Skip to content

Commit b09a59f

Browse files
authored
Merge pull request #724 from hashicorp/nf/jun23-save-plan-run-attr
Add support for the `save-plan` run attr and `save_plan` operation filter
2 parents e9a6e05 + cae98cb commit b09a59f

File tree

3 files changed

+82
-33
lines changed

3 files changed

+82
-33
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## Enhancements
44
* Adds `RunPreApplyCompleted` run status by @uk1288 [#727](https://github.com/hashicorp/go-tfe/pull/727)
5+
* Added BETA support for saved plan runs, by @nfagerlund [#724](https://github.com/hashicorp/go-tfe/pull/724)
6+
* New `SavePlan` fields in `Run` and `RunCreateOptions`
7+
* New `RunPlannedAndSaved` `RunStatus` value
8+
* New `PlannedAndSavedAt` field in `RunStatusTimestamps`
9+
* New `RunOperationSavePlan` constant for run list filters
510

611
# v1.28.0
712

run.go

+46-33
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const (
7070
RunPending RunStatus = "pending"
7171
RunPlanned RunStatus = "planned"
7272
RunPlannedAndFinished RunStatus = "planned_and_finished"
73+
RunPlannedAndSaved RunStatus = "planned_and_saved" // Note: This status is in BETA.
7374
RunPlanning RunStatus = "planning"
7475
RunPlanQueued RunStatus = "plan_queued"
7576
RunPolicyChecked RunStatus = "policy_checked"
@@ -107,6 +108,8 @@ const (
107108
RunOperationRefreshOnly RunOperation = "refresh_only"
108109
RunOperationDestroy RunOperation = "destroy"
109110
RunOperationEmptyApply RunOperation = "empty_apply"
111+
// **Note: This operation type is still in BETA and subject to change.**
112+
RunOperationSavePlan RunOperation = "save_plan"
110113
)
111114

112115
// RunList represents a list of runs.
@@ -117,28 +120,30 @@ type RunList struct {
117120

118121
// Run represents a Terraform Enterprise run.
119122
type Run struct {
120-
ID string `jsonapi:"primary,runs"`
121-
Actions *RunActions `jsonapi:"attr,actions"`
122-
AutoApply bool `jsonapi:"attr,auto-apply,omitempty"`
123-
AllowConfigGeneration *bool `jsonapi:"attr,allow-config-generation,omitempty"`
124-
AllowEmptyApply bool `jsonapi:"attr,allow-empty-apply"`
125-
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
126-
ForceCancelAvailableAt time.Time `jsonapi:"attr,force-cancel-available-at,iso8601"`
127-
HasChanges bool `jsonapi:"attr,has-changes"`
128-
IsDestroy bool `jsonapi:"attr,is-destroy"`
129-
Message string `jsonapi:"attr,message"`
130-
Permissions *RunPermissions `jsonapi:"attr,permissions"`
131-
PositionInQueue int `jsonapi:"attr,position-in-queue"`
132-
PlanOnly bool `jsonapi:"attr,plan-only"`
133-
Refresh bool `jsonapi:"attr,refresh"`
134-
RefreshOnly bool `jsonapi:"attr,refresh-only"`
135-
ReplaceAddrs []string `jsonapi:"attr,replace-addrs,omitempty"`
136-
Source RunSource `jsonapi:"attr,source"`
137-
Status RunStatus `jsonapi:"attr,status"`
138-
StatusTimestamps *RunStatusTimestamps `jsonapi:"attr,status-timestamps"`
139-
TargetAddrs []string `jsonapi:"attr,target-addrs,omitempty"`
140-
TerraformVersion string `jsonapi:"attr,terraform-version"`
141-
Variables []*RunVariableAttr `jsonapi:"attr,variables"`
123+
ID string `jsonapi:"primary,runs"`
124+
Actions *RunActions `jsonapi:"attr,actions"`
125+
AutoApply bool `jsonapi:"attr,auto-apply,omitempty"`
126+
AllowConfigGeneration *bool `jsonapi:"attr,allow-config-generation,omitempty"`
127+
AllowEmptyApply bool `jsonapi:"attr,allow-empty-apply"`
128+
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
129+
ForceCancelAvailableAt time.Time `jsonapi:"attr,force-cancel-available-at,iso8601"`
130+
HasChanges bool `jsonapi:"attr,has-changes"`
131+
IsDestroy bool `jsonapi:"attr,is-destroy"`
132+
Message string `jsonapi:"attr,message"`
133+
Permissions *RunPermissions `jsonapi:"attr,permissions"`
134+
PositionInQueue int `jsonapi:"attr,position-in-queue"`
135+
PlanOnly bool `jsonapi:"attr,plan-only"`
136+
Refresh bool `jsonapi:"attr,refresh"`
137+
RefreshOnly bool `jsonapi:"attr,refresh-only"`
138+
ReplaceAddrs []string `jsonapi:"attr,replace-addrs,omitempty"`
139+
// **Note: This field is still in BETA and subject to change.**
140+
SavePlan bool `jsonapi:"attr,save-plan,omitempty"`
141+
Source RunSource `jsonapi:"attr,source"`
142+
Status RunStatus `jsonapi:"attr,status"`
143+
StatusTimestamps *RunStatusTimestamps `jsonapi:"attr,status-timestamps"`
144+
TargetAddrs []string `jsonapi:"attr,target-addrs,omitempty"`
145+
TerraformVersion string `jsonapi:"attr,terraform-version"`
146+
Variables []*RunVariableAttr `jsonapi:"attr,variables"`
142147

143148
// Relations
144149
Apply *Apply `jsonapi:"relation,apply"`
@@ -184,17 +189,19 @@ type RunStatusTimestamps struct {
184189
FetchingAt time.Time `jsonapi:"attr,fetching-at,rfc3339"`
185190
ForceCanceledAt time.Time `jsonapi:"attr,force-canceled-at,rfc3339"`
186191
PlannedAndFinishedAt time.Time `jsonapi:"attr,planned-and-finished-at,rfc3339"`
187-
PlannedAt time.Time `jsonapi:"attr,planned-at,rfc3339"`
188-
PlanningAt time.Time `jsonapi:"attr,planning-at,rfc3339"`
189-
PlanQueueableAt time.Time `jsonapi:"attr,plan-queueable-at,rfc3339"`
190-
PlanQueuedAt time.Time `jsonapi:"attr,plan-queued-at,rfc3339"`
191-
PolicyCheckedAt time.Time `jsonapi:"attr,policy-checked-at,rfc3339"`
192-
PolicySoftFailedAt time.Time `jsonapi:"attr,policy-soft-failed-at,rfc3339"`
193-
PostPlanCompletedAt time.Time `jsonapi:"attr,post-plan-completed-at,rfc3339"`
194-
PostPlanRunningAt time.Time `jsonapi:"attr,post-plan-running-at,rfc3339"`
195-
PrePlanCompletedAt time.Time `jsonapi:"attr,pre-plan-completed-at,rfc3339"`
196-
PrePlanRunningAt time.Time `jsonapi:"attr,pre-plan-running-at,rfc3339"`
197-
QueuingAt time.Time `jsonapi:"attr,queuing-at,rfc3339"`
192+
// **Note: This field is still in BETA and subject to change.**
193+
PlannedAndSavedAt time.Time `jsonapi:"attr,planned-and-saved-at,rfc3339"`
194+
PlannedAt time.Time `jsonapi:"attr,planned-at,rfc3339"`
195+
PlanningAt time.Time `jsonapi:"attr,planning-at,rfc3339"`
196+
PlanQueueableAt time.Time `jsonapi:"attr,plan-queueable-at,rfc3339"`
197+
PlanQueuedAt time.Time `jsonapi:"attr,plan-queued-at,rfc3339"`
198+
PolicyCheckedAt time.Time `jsonapi:"attr,policy-checked-at,rfc3339"`
199+
PolicySoftFailedAt time.Time `jsonapi:"attr,policy-soft-failed-at,rfc3339"`
200+
PostPlanCompletedAt time.Time `jsonapi:"attr,post-plan-completed-at,rfc3339"`
201+
PostPlanRunningAt time.Time `jsonapi:"attr,post-plan-running-at,rfc3339"`
202+
PrePlanCompletedAt time.Time `jsonapi:"attr,pre-plan-completed-at,rfc3339"`
203+
PrePlanRunningAt time.Time `jsonapi:"attr,pre-plan-running-at,rfc3339"`
204+
QueuingAt time.Time `jsonapi:"attr,queuing-at,rfc3339"`
198205
}
199206

200207
// RunIncludeOpt represents the available options for include query params.
@@ -290,6 +297,12 @@ type RunCreateOptions struct {
290297
// and refresh the state only
291298
RefreshOnly *bool `jsonapi:"attr,refresh-only,omitempty"`
292299

300+
// SavePlan determines whether this should be a saved-plan run. Saved-plan
301+
// runs perform their plan and checks immediately, but won't lock the
302+
// workspace and become its current run until they are confirmed for apply.
303+
// **Note: This field is still in BETA and subject to change.**
304+
SavePlan *bool `jsonapi:"attr,save-plan,omitempty"`
305+
293306
// Specifies the message to be associated with this run.
294307
Message *string `jsonapi:"attr,message,omitempty"`
295308

run_integration_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,31 @@ func TestRunsListQueryParams(t *testing.T) {
149149
},
150150
}
151151

152+
betaTestCases := []testCase{
153+
{
154+
description: "with operation of save_plan parameter",
155+
options: &RunListOptions{Operation: string(RunOperationSavePlan), Include: []RunIncludeOpt{RunWorkspace}},
156+
assertion: func(tc testCase, rl *RunList, err error) {
157+
require.NoError(t, err)
158+
assert.Equal(t, 0, len(rl.Items))
159+
},
160+
},
161+
}
162+
152163
for _, testCase := range testCases {
153164
t.Run(testCase.description, func(t *testing.T) {
154165
runs, err := client.Runs.List(ctx, workspaceTest.ID, testCase.options)
155166
testCase.assertion(testCase, runs, err)
156167
})
157168
}
169+
170+
for _, testCase := range betaTestCases {
171+
t.Run(testCase.description, func(t *testing.T) {
172+
skipUnlessBeta(t)
173+
runs, err := client.Runs.List(ctx, workspaceTest.ID, testCase.options)
174+
testCase.assertion(testCase, runs, err)
175+
})
176+
}
158177
}
159178

160179
func TestRunsCreate(t *testing.T) {
@@ -203,6 +222,18 @@ func TestRunsCreate(t *testing.T) {
203222
assert.Equal(t, true, r.AllowEmptyApply)
204223
})
205224

225+
t.Run("with save-plan", func(t *testing.T) {
226+
skipUnlessBeta(t)
227+
options := RunCreateOptions{
228+
Workspace: wTest,
229+
SavePlan: Bool(true),
230+
}
231+
232+
r, err := client.Runs.Create(ctx, options)
233+
require.NoError(t, err)
234+
assert.Equal(t, true, r.SavePlan)
235+
})
236+
206237
t.Run("with terraform version and plan only", func(t *testing.T) {
207238
options := RunCreateOptions{
208239
Workspace: wTest,

0 commit comments

Comments
 (0)