1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
package tfe
import (
"context"
"errors"
"fmt"
"io"
"net/url"
"time"
)
// Compile-time proof of interface implementation.
var _ Applies = (*applies)(nil)
// Applies describes all the apply related methods that the Terraform
// Enterprise API supports.
//
// TFE API docs: https://www.terraform.io/docs/enterprise/api/apply.html
type Applies interface {
// Read an apply by its ID.
Read(ctx context.Context, applyID string) (*Apply, error)
// Logs retrieves the logs of an apply.
Logs(ctx context.Context, applyID string) (io.Reader, error)
}
// applies implements Applys.
type applies struct {
client *Client
}
// ApplyStatus represents an apply state.
type ApplyStatus string
//List all available apply statuses.
const (
ApplyCanceled ApplyStatus = "canceled"
ApplyCreated ApplyStatus = "created"
ApplyErrored ApplyStatus = "errored"
ApplyFinished ApplyStatus = "finished"
ApplyMFAWaiting ApplyStatus = "mfa_waiting"
ApplyPending ApplyStatus = "pending"
ApplyQueued ApplyStatus = "queued"
ApplyRunning ApplyStatus = "running"
ApplyUnreachable ApplyStatus = "unreachable"
)
// Apply represents a Terraform Enterprise apply.
type Apply struct {
ID string `jsonapi:"primary,applies"`
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 ApplyStatus `jsonapi:"attr,status"`
StatusTimestamps *ApplyStatusTimestamps `jsonapi:"attr,status-timestamps"`
}
// ApplyStatusTimestamps holds the timestamps for individual apply statuses.
type ApplyStatusTimestamps 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 an apply by its ID.
func (s *applies) Read(ctx context.Context, applyID string) (*Apply, error) {
if !validStringID(&applyID) {
return nil, errors.New("invalid value for apply ID")
}
u := fmt.Sprintf("applies/%s", url.QueryEscape(applyID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
a := &Apply{}
err = s.client.do(ctx, req, a)
if err != nil {
return nil, err
}
return a, nil
}
// Logs retrieves the logs of an apply.
func (s *applies) Logs(ctx context.Context, applyID string) (io.Reader, error) {
if !validStringID(&applyID) {
return nil, errors.New("invalid value for apply ID")
}
// Get the apply to make sure it exists.
a, err := s.Read(ctx, applyID)
if err != nil {
return nil, err
}
// Return an error if the log URL is empty.
if a.LogReadURL == "" {
return nil, fmt.Errorf("apply %s does not have a log URL", applyID)
}
u, err := url.Parse(a.LogReadURL)
if err != nil {
return nil, fmt.Errorf("invalid log URL: %v", err)
}
done := func() (bool, error) {
a, err := s.Read(ctx, a.ID)
if err != nil {
return false, err
}
switch a.Status {
case ApplyCanceled, ApplyErrored, ApplyFinished, ApplyUnreachable:
return true, nil
default:
return false, nil
}
}
return &LogReader{
client: s.client,
ctx: ctx,
done: done,
logURL: u,
}, nil
}
|