1package tfe 2 3import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "io" 9 "net/url" 10 "time" 11) 12 13// Compile-time proof of interface implementation. 14var _ CostEstimates = (*costEstimates)(nil) 15 16// CostEstimates describes all the costEstimate related methods that 17// the Terraform Enterprise API supports. 18// 19// TFE API docs: https://www.terraform.io/docs/enterprise/api/ (TBD) 20type CostEstimates interface { 21 // Read a costEstimate by its ID. 22 Read(ctx context.Context, costEstimateID string) (*CostEstimate, error) 23 24 // Logs retrieves the logs of a costEstimate. 25 Logs(ctx context.Context, costEstimateID string) (io.Reader, error) 26} 27 28// costEstimates implements CostEstimates. 29type costEstimates struct { 30 client *Client 31} 32 33// CostEstimateStatus represents a costEstimate state. 34type CostEstimateStatus string 35 36// List all available costEstimate statuses. 37const ( 38 CostEstimateCanceled CostEstimateStatus = "canceled" 39 CostEstimateErrored CostEstimateStatus = "errored" 40 CostEstimateFinished CostEstimateStatus = "finished" 41 CostEstimatePending CostEstimateStatus = "pending" 42 CostEstimateQueued CostEstimateStatus = "queued" 43 CostEstimateSkippedDueToTargeting CostEstimateStatus = "skipped_due_to_targeting" 44) 45 46// CostEstimate represents a Terraform Enterprise costEstimate. 47type CostEstimate struct { 48 ID string `jsonapi:"primary,cost-estimates"` 49 DeltaMonthlyCost string `jsonapi:"attr,delta-monthly-cost"` 50 ErrorMessage string `jsonapi:"attr,error-message"` 51 MatchedResourcesCount int `jsonapi:"attr,matched-resources-count"` 52 PriorMonthlyCost string `jsonapi:"attr,prior-monthly-cost"` 53 ProposedMonthlyCost string `jsonapi:"attr,proposed-monthly-cost"` 54 ResourcesCount int `jsonapi:"attr,resources-count"` 55 Status CostEstimateStatus `jsonapi:"attr,status"` 56 StatusTimestamps *CostEstimateStatusTimestamps `jsonapi:"attr,status-timestamps"` 57 UnmatchedResourcesCount int `jsonapi:"attr,unmatched-resources-count"` 58} 59 60// CostEstimateStatusTimestamps holds the timestamps for individual costEstimate statuses. 61type CostEstimateStatusTimestamps struct { 62 CanceledAt time.Time `json:"canceled-at"` 63 ErroredAt time.Time `json:"errored-at"` 64 FinishedAt time.Time `json:"finished-at"` 65 PendingAt time.Time `json:"pending-at"` 66 QueuedAt time.Time `json:"queued-at"` 67 SkippedDueToTargetingAt time.Time `json:"skipped-due-to-targeting-at"` 68} 69 70// Read a costEstimate by its ID. 71func (s *costEstimates) Read(ctx context.Context, costEstimateID string) (*CostEstimate, error) { 72 if !validStringID(&costEstimateID) { 73 return nil, errors.New("invalid value for cost estimate ID") 74 } 75 76 u := fmt.Sprintf("cost-estimates/%s", url.QueryEscape(costEstimateID)) 77 req, err := s.client.newRequest("GET", u, nil) 78 if err != nil { 79 return nil, err 80 } 81 82 ce := &CostEstimate{} 83 err = s.client.do(ctx, req, ce) 84 if err != nil { 85 return nil, err 86 } 87 88 return ce, nil 89} 90 91// Logs retrieves the logs of a costEstimate. 92func (s *costEstimates) Logs(ctx context.Context, costEstimateID string) (io.Reader, error) { 93 if !validStringID(&costEstimateID) { 94 return nil, errors.New("invalid value for cost estimate ID") 95 } 96 97 // Loop until the context is canceled or the cost estimate is finished 98 // running. The cost estimate logs are not streamed and so only available 99 // once the estimate is finished. 100 for { 101 // Get the costEstimate to make sure it exists. 102 ce, err := s.Read(ctx, costEstimateID) 103 if err != nil { 104 return nil, err 105 } 106 107 switch ce.Status { 108 case CostEstimateQueued: 109 select { 110 case <-ctx.Done(): 111 return nil, ctx.Err() 112 case <-time.After(1000 * time.Millisecond): 113 continue 114 } 115 } 116 117 u := fmt.Sprintf("cost-estimates/%s/output", url.QueryEscape(costEstimateID)) 118 req, err := s.client.newRequest("GET", u, nil) 119 if err != nil { 120 return nil, err 121 } 122 123 logs := bytes.NewBuffer(nil) 124 err = s.client.do(ctx, req, logs) 125 if err != nil { 126 return nil, err 127 } 128 129 return logs, nil 130 } 131} 132