-
Notifications
You must be signed in to change notification settings - Fork 102
/
apply.go
135 lines (114 loc) · 3.64 KB
/
apply.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
134
135
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package tfe
import (
"context"
"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://developer.hashicorp.com/terraform/cloud-docs/api-docs/applies
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 Applies interface.
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"`
ResourceImports int `jsonapi:"attr,resource-imports"`
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 `jsonapi:"attr,canceled-at,rfc3339"`
ErroredAt time.Time `jsonapi:"attr,errored-at,rfc3339"`
FinishedAt time.Time `jsonapi:"attr,finished-at,rfc3339"`
ForceCanceledAt time.Time `jsonapi:"attr,force-canceled-at,rfc3339"`
QueuedAt time.Time `jsonapi:"attr,queued-at,rfc3339"`
StartedAt time.Time `jsonapi:"attr,started-at,rfc3339"`
}
// Read an apply by its ID.
func (s *applies) Read(ctx context.Context, applyID string) (*Apply, error) {
if !validStringID(&applyID) {
return nil, ErrInvalidApplyID
}
u := fmt.Sprintf("applies/%s", url.PathEscape(applyID))
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
a := &Apply{}
err = req.Do(ctx, 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, ErrInvalidApplyID
}
// 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: %w", 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
}