-
Notifications
You must be signed in to change notification settings - Fork 102
/
cost_estimate.go
133 lines (113 loc) · 4.32 KB
/
cost_estimate.go
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
133
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package tfe
import (
"bytes"
"context"
"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://developer.hashicorp.com/terraform/cloud-docs/api-docs/cost-estimates
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 `jsonapi:"attr,canceled-at,rfc3339"`
ErroredAt time.Time `jsonapi:"attr,errored-at,rfc3339"`
FinishedAt time.Time `jsonapi:"attr,finished-at,rfc3339"`
PendingAt time.Time `jsonapi:"attr,pending-at,rfc3339"`
QueuedAt time.Time `jsonapi:"attr,queued-at,rfc3339"`
SkippedDueToTargetingAt time.Time `jsonapi:"attr,skipped-due-to-targeting-at,rfc3339"`
}
// Read a costEstimate by its ID.
func (s *costEstimates) Read(ctx context.Context, costEstimateID string) (*CostEstimate, error) {
if !validStringID(&costEstimateID) {
return nil, ErrInvalidCostEstimateID
}
u := fmt.Sprintf("cost-estimates/%s", url.PathEscape(costEstimateID))
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
ce := &CostEstimate{}
err = req.Do(ctx, 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, ErrInvalidCostEstimateID
}
// 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.PathEscape(costEstimateID))
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
logs := bytes.NewBuffer(nil)
err = req.Do(ctx, logs)
if err != nil {
return nil, err
}
return logs, nil
}
}