Skip to content

Commit

Permalink
Merge pull request #724 from hashicorp/nf/jun23-save-plan-run-attr
Browse files Browse the repository at this point in the history
Add support for the `save-plan` run attr and `save_plan` operation filter
  • Loading branch information
nfagerlund authored Jun 27, 2023
2 parents e9a6e05 + cae98cb commit b09a59f
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 33 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

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

# v1.28.0

Expand Down
79 changes: 46 additions & 33 deletions run.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const (
RunPending RunStatus = "pending"
RunPlanned RunStatus = "planned"
RunPlannedAndFinished RunStatus = "planned_and_finished"
RunPlannedAndSaved RunStatus = "planned_and_saved" // Note: This status is in BETA.
RunPlanning RunStatus = "planning"
RunPlanQueued RunStatus = "plan_queued"
RunPolicyChecked RunStatus = "policy_checked"
Expand Down Expand Up @@ -107,6 +108,8 @@ const (
RunOperationRefreshOnly RunOperation = "refresh_only"
RunOperationDestroy RunOperation = "destroy"
RunOperationEmptyApply RunOperation = "empty_apply"
// **Note: This operation type is still in BETA and subject to change.**
RunOperationSavePlan RunOperation = "save_plan"
)

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

// Run represents a Terraform Enterprise run.
type Run struct {
ID string `jsonapi:"primary,runs"`
Actions *RunActions `jsonapi:"attr,actions"`
AutoApply bool `jsonapi:"attr,auto-apply,omitempty"`
AllowConfigGeneration *bool `jsonapi:"attr,allow-config-generation,omitempty"`
AllowEmptyApply bool `jsonapi:"attr,allow-empty-apply"`
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
ForceCancelAvailableAt time.Time `jsonapi:"attr,force-cancel-available-at,iso8601"`
HasChanges bool `jsonapi:"attr,has-changes"`
IsDestroy bool `jsonapi:"attr,is-destroy"`
Message string `jsonapi:"attr,message"`
Permissions *RunPermissions `jsonapi:"attr,permissions"`
PositionInQueue int `jsonapi:"attr,position-in-queue"`
PlanOnly bool `jsonapi:"attr,plan-only"`
Refresh bool `jsonapi:"attr,refresh"`
RefreshOnly bool `jsonapi:"attr,refresh-only"`
ReplaceAddrs []string `jsonapi:"attr,replace-addrs,omitempty"`
Source RunSource `jsonapi:"attr,source"`
Status RunStatus `jsonapi:"attr,status"`
StatusTimestamps *RunStatusTimestamps `jsonapi:"attr,status-timestamps"`
TargetAddrs []string `jsonapi:"attr,target-addrs,omitempty"`
TerraformVersion string `jsonapi:"attr,terraform-version"`
Variables []*RunVariableAttr `jsonapi:"attr,variables"`
ID string `jsonapi:"primary,runs"`
Actions *RunActions `jsonapi:"attr,actions"`
AutoApply bool `jsonapi:"attr,auto-apply,omitempty"`
AllowConfigGeneration *bool `jsonapi:"attr,allow-config-generation,omitempty"`
AllowEmptyApply bool `jsonapi:"attr,allow-empty-apply"`
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
ForceCancelAvailableAt time.Time `jsonapi:"attr,force-cancel-available-at,iso8601"`
HasChanges bool `jsonapi:"attr,has-changes"`
IsDestroy bool `jsonapi:"attr,is-destroy"`
Message string `jsonapi:"attr,message"`
Permissions *RunPermissions `jsonapi:"attr,permissions"`
PositionInQueue int `jsonapi:"attr,position-in-queue"`
PlanOnly bool `jsonapi:"attr,plan-only"`
Refresh bool `jsonapi:"attr,refresh"`
RefreshOnly bool `jsonapi:"attr,refresh-only"`
ReplaceAddrs []string `jsonapi:"attr,replace-addrs,omitempty"`
// **Note: This field is still in BETA and subject to change.**
SavePlan bool `jsonapi:"attr,save-plan,omitempty"`
Source RunSource `jsonapi:"attr,source"`
Status RunStatus `jsonapi:"attr,status"`
StatusTimestamps *RunStatusTimestamps `jsonapi:"attr,status-timestamps"`
TargetAddrs []string `jsonapi:"attr,target-addrs,omitempty"`
TerraformVersion string `jsonapi:"attr,terraform-version"`
Variables []*RunVariableAttr `jsonapi:"attr,variables"`

// Relations
Apply *Apply `jsonapi:"relation,apply"`
Expand Down Expand Up @@ -184,17 +189,19 @@ type RunStatusTimestamps struct {
FetchingAt time.Time `jsonapi:"attr,fetching-at,rfc3339"`
ForceCanceledAt time.Time `jsonapi:"attr,force-canceled-at,rfc3339"`
PlannedAndFinishedAt time.Time `jsonapi:"attr,planned-and-finished-at,rfc3339"`
PlannedAt time.Time `jsonapi:"attr,planned-at,rfc3339"`
PlanningAt time.Time `jsonapi:"attr,planning-at,rfc3339"`
PlanQueueableAt time.Time `jsonapi:"attr,plan-queueable-at,rfc3339"`
PlanQueuedAt time.Time `jsonapi:"attr,plan-queued-at,rfc3339"`
PolicyCheckedAt time.Time `jsonapi:"attr,policy-checked-at,rfc3339"`
PolicySoftFailedAt time.Time `jsonapi:"attr,policy-soft-failed-at,rfc3339"`
PostPlanCompletedAt time.Time `jsonapi:"attr,post-plan-completed-at,rfc3339"`
PostPlanRunningAt time.Time `jsonapi:"attr,post-plan-running-at,rfc3339"`
PrePlanCompletedAt time.Time `jsonapi:"attr,pre-plan-completed-at,rfc3339"`
PrePlanRunningAt time.Time `jsonapi:"attr,pre-plan-running-at,rfc3339"`
QueuingAt time.Time `jsonapi:"attr,queuing-at,rfc3339"`
// **Note: This field is still in BETA and subject to change.**
PlannedAndSavedAt time.Time `jsonapi:"attr,planned-and-saved-at,rfc3339"`
PlannedAt time.Time `jsonapi:"attr,planned-at,rfc3339"`
PlanningAt time.Time `jsonapi:"attr,planning-at,rfc3339"`
PlanQueueableAt time.Time `jsonapi:"attr,plan-queueable-at,rfc3339"`
PlanQueuedAt time.Time `jsonapi:"attr,plan-queued-at,rfc3339"`
PolicyCheckedAt time.Time `jsonapi:"attr,policy-checked-at,rfc3339"`
PolicySoftFailedAt time.Time `jsonapi:"attr,policy-soft-failed-at,rfc3339"`
PostPlanCompletedAt time.Time `jsonapi:"attr,post-plan-completed-at,rfc3339"`
PostPlanRunningAt time.Time `jsonapi:"attr,post-plan-running-at,rfc3339"`
PrePlanCompletedAt time.Time `jsonapi:"attr,pre-plan-completed-at,rfc3339"`
PrePlanRunningAt time.Time `jsonapi:"attr,pre-plan-running-at,rfc3339"`
QueuingAt time.Time `jsonapi:"attr,queuing-at,rfc3339"`
}

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

// SavePlan determines whether this should be a saved-plan run. Saved-plan
// runs perform their plan and checks immediately, but won't lock the
// workspace and become its current run until they are confirmed for apply.
// **Note: This field is still in BETA and subject to change.**
SavePlan *bool `jsonapi:"attr,save-plan,omitempty"`

// Specifies the message to be associated with this run.
Message *string `jsonapi:"attr,message,omitempty"`

Expand Down
31 changes: 31 additions & 0 deletions run_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,31 @@ func TestRunsListQueryParams(t *testing.T) {
},
}

betaTestCases := []testCase{
{
description: "with operation of save_plan parameter",
options: &RunListOptions{Operation: string(RunOperationSavePlan), Include: []RunIncludeOpt{RunWorkspace}},
assertion: func(tc testCase, rl *RunList, err error) {
require.NoError(t, err)
assert.Equal(t, 0, len(rl.Items))
},
},
}

for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
runs, err := client.Runs.List(ctx, workspaceTest.ID, testCase.options)
testCase.assertion(testCase, runs, err)
})
}

for _, testCase := range betaTestCases {
t.Run(testCase.description, func(t *testing.T) {
skipUnlessBeta(t)
runs, err := client.Runs.List(ctx, workspaceTest.ID, testCase.options)
testCase.assertion(testCase, runs, err)
})
}
}

func TestRunsCreate(t *testing.T) {
Expand Down Expand Up @@ -203,6 +222,18 @@ func TestRunsCreate(t *testing.T) {
assert.Equal(t, true, r.AllowEmptyApply)
})

t.Run("with save-plan", func(t *testing.T) {
skipUnlessBeta(t)
options := RunCreateOptions{
Workspace: wTest,
SavePlan: Bool(true),
}

r, err := client.Runs.Create(ctx, options)
require.NoError(t, err)
assert.Equal(t, true, r.SavePlan)
})

t.Run("with terraform version and plan only", func(t *testing.T) {
options := RunCreateOptions{
Workspace: wTest,
Expand Down

0 comments on commit b09a59f

Please sign in to comment.