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
|
package tfe
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/url"
"time"
)
// Compile-time proof of interface implementation.
var _ CostEstimates = (*costEstimates)(nil)
// CostEstimates describes all the costEstimate related methods that
// the Terraform Enterprise API supports.
//
// TFE API docs: https://www.terraform.io/docs/enterprise/api/ (TBD)
type CostEstimates interface {
// Read a costEstimate by its ID.
Read(ctx context.Context, costEstimateID string) (*CostEstimate, error)
// Logs retrieves the logs of a costEstimate.
Logs(ctx context.Context, costEstimateID string) (io.Reader, error)
}
// costEstimates implements CostEstimates.
type costEstimates struct {
client *Client
}
// CostEstimateStatus represents a costEstimate state.
type CostEstimateStatus string
// List all available costEstimate statuses.
const (
CostEstimateCanceled CostEstimateStatus = "canceled"
CostEstimateErrored CostEstimateStatus = "errored"
CostEstimateFinished CostEstimateStatus = "finished"
CostEstimatePending CostEstimateStatus = "pending"
CostEstimateQueued CostEstimateStatus = "queued"
CostEstimateSkippedDueToTargeting CostEstimateStatus = "skipped_due_to_targeting"
)
// CostEstimate represents a Terraform Enterprise costEstimate.
type CostEstimate struct {
ID string `jsonapi:"primary,cost-estimates"`
DeltaMonthlyCost string `jsonapi:"attr,delta-monthly-cost"`
ErrorMessage string `jsonapi:"attr,error-message"`
MatchedResourcesCount int `jsonapi:"attr,matched-resources-count"`
PriorMonthlyCost string `jsonapi:"attr,prior-monthly-cost"`
ProposedMonthlyCost string `jsonapi:"attr,proposed-monthly-cost"`
ResourcesCount int `jsonapi:"attr,resources-count"`
Status CostEstimateStatus `jsonapi:"attr,status"`
StatusTimestamps *CostEstimateStatusTimestamps `jsonapi:"attr,status-timestamps"`
UnmatchedResourcesCount int `jsonapi:"attr,unmatched-resources-count"`
}
// CostEstimateStatusTimestamps holds the timestamps for individual costEstimate statuses.
type CostEstimateStatusTimestamps struct {
CanceledAt time.Time `json:"canceled-at"`
ErroredAt time.Time `json:"errored-at"`
FinishedAt time.Time `json:"finished-at"`
PendingAt time.Time `json:"pending-at"`
QueuedAt time.Time `json:"queued-at"`
SkippedDueToTargetingAt time.Time `json:"skipped-due-to-targeting-at"`
}
// Read a costEstimate by its ID.
func (s *costEstimates) Read(ctx context.Context, costEstimateID string) (*CostEstimate, error) {
if !validStringID(&costEstimateID) {
return nil, errors.New("invalid value for cost estimate ID")
}
u := fmt.Sprintf("cost-estimates/%s", url.QueryEscape(costEstimateID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
ce := &CostEstimate{}
err = s.client.do(ctx, req, ce)
if err != nil {
return nil, err
}
return ce, nil
}
// Logs retrieves the logs of a costEstimate.
func (s *costEstimates) Logs(ctx context.Context, costEstimateID string) (io.Reader, error) {
if !validStringID(&costEstimateID) {
return nil, errors.New("invalid value for cost estimate ID")
}
// Loop until the context is canceled or the cost estimate is finished
// running. The cost estimate logs are not streamed and so only available
// once the estimate is finished.
for {
// Get the costEstimate to make sure it exists.
ce, err := s.Read(ctx, costEstimateID)
if err != nil {
return nil, err
}
switch ce.Status {
case CostEstimateQueued:
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(1000 * time.Millisecond):
continue
}
}
u := fmt.Sprintf("cost-estimates/%s/output", url.QueryEscape(costEstimateID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
logs := bytes.NewBuffer(nil)
err = s.client.do(ctx, req, logs)
if err != nil {
return nil, err
}
return logs, nil
}
}
|