terraform/vendor/github.com/hashicorp/go-tfe/plan.go

134 lines
3.4 KiB
Go

package tfe
import (
"context"
"errors"
"fmt"
"io"
"net/url"
"time"
)
// Compile-time proof of interface implementation.
var _ Plans = (*plans)(nil)
// Plans describes all the plan related methods that the Terraform Enterprise
// API supports.
//
// TFE API docs: https://www.terraform.io/docs/enterprise/api/plan.html
type Plans interface {
// Read a plan by its ID.
Read(ctx context.Context, planID string) (*Plan, error)
// Logs retrieves the logs of a plan.
Logs(ctx context.Context, planID string) (io.Reader, error)
}
// plans implements Plans.
type plans struct {
client *Client
}
// PlanStatus represents a plan state.
type PlanStatus string
//List all available plan statuses.
const (
PlanCanceled PlanStatus = "canceled"
PlanCreated PlanStatus = "created"
PlanErrored PlanStatus = "errored"
PlanFinished PlanStatus = "finished"
PlanMFAWaiting PlanStatus = "mfa_waiting"
PlanPending PlanStatus = "pending"
PlanQueued PlanStatus = "queued"
PlanRunning PlanStatus = "running"
PlanUnreachable PlanStatus = "unreachable"
)
// Plan represents a Terraform Enterprise plan.
type Plan struct {
ID string `jsonapi:"primary,plans"`
HasChanges bool `jsonapi:"attr,has-changes"`
LogReadURL string `jsonapi:"attr,log-read-url"`
ResourceAdditions int `jsonapi:"attr,resource-additions"`
ResourceChanges int `jsonapi:"attr,resource-changes"`
ResourceDestructions int `jsonapi:"attr,resource-destructions"`
Status PlanStatus `jsonapi:"attr,status"`
StatusTimestamps *PlanStatusTimestamps `jsonapi:"attr,status-timestamps"`
}
// PlanStatusTimestamps holds the timestamps for individual plan statuses.
type PlanStatusTimestamps struct {
CanceledAt time.Time `json:"canceled-at"`
ErroredAt time.Time `json:"errored-at"`
FinishedAt time.Time `json:"finished-at"`
ForceCanceledAt time.Time `json:"force-canceled-at"`
QueuedAt time.Time `json:"queued-at"`
StartedAt time.Time `json:"started-at"`
}
// Read a plan by its ID.
func (s *plans) Read(ctx context.Context, planID string) (*Plan, error) {
if !validStringID(&planID) {
return nil, errors.New("invalid value for plan ID")
}
u := fmt.Sprintf("plans/%s", url.QueryEscape(planID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
p := &Plan{}
err = s.client.do(ctx, req, p)
if err != nil {
return nil, err
}
return p, nil
}
// Logs retrieves the logs of a plan.
func (s *plans) Logs(ctx context.Context, planID string) (io.Reader, error) {
if !validStringID(&planID) {
return nil, errors.New("invalid value for plan ID")
}
// Get the plan to make sure it exists.
p, err := s.Read(ctx, planID)
if err != nil {
return nil, err
}
// Return an error if the log URL is empty.
if p.LogReadURL == "" {
return nil, fmt.Errorf("plan %s does not have a log URL", planID)
}
u, err := url.Parse(p.LogReadURL)
if err != nil {
return nil, fmt.Errorf("invalid log URL: %v", err)
}
done := func() (bool, error) {
p, err := s.Read(ctx, p.ID)
if err != nil {
return false, err
}
switch p.Status {
case PlanCanceled, PlanErrored, PlanFinished, PlanUnreachable:
return true, nil
default:
return false, nil
}
}
return &LogReader{
client: s.client,
ctx: ctx,
done: done,
logURL: u,
}, nil
}