Commit 8562f8a7 authored by Glenn Sarti's avatar Glenn Sarti
Browse files

Add Pre-Plan Run Tasks to Terraform CLI

Prevously the cloud backend would only render post-plan run tasks. Now
that pre-plan tasks are in beta, this commit updates the plan phase to
render pre-plan run tasks.  This commit also moves some common code to
the common backend as it will be used by other task stages in the
future.
parent b6a478b6
Showing with 40 additions and 27 deletions
+40 -27
...@@ -36,6 +36,7 @@ ENHANCEMENTS: ...@@ -36,6 +36,7 @@ ENHANCEMENTS:
* The COS backend now supports global acceleration. ([#31425](https://github.com/hashicorp/terraform/issues/31425)) * The COS backend now supports global acceleration. ([#31425](https://github.com/hashicorp/terraform/issues/31425))
* providercache: include host in provider installation error ([#31524](https://github.com/hashicorp/terraform/issues/31524)) * providercache: include host in provider installation error ([#31524](https://github.com/hashicorp/terraform/issues/31524))
* refactoring: `moved` blocks can now be used to move resources to and from external modules ([#31556](https://github.com/hashicorp/terraform/issues/31556)) * refactoring: `moved` blocks can now be used to move resources to and from external modules ([#31556](https://github.com/hashicorp/terraform/issues/31556))
* When showing the progress of a remote operation running in Terraform Cloud, Terraform CLI will include information about pre-plan run tasks ([#31617](https://github.com/hashicorp/terraform/issues/31617))
BUG FIXES: BUG FIXES:
...@@ -54,7 +55,7 @@ EXPERIMENTS: ...@@ -54,7 +55,7 @@ EXPERIMENTS:
* The built-in `defaults` function, previously used to meet the use-case of replacing null values with default values, will not graduate to stable and has been removed. Use the second argument of `optional` inline in your type constraint to declare default values instead. * The built-in `defaults` function, previously used to meet the use-case of replacing null values with default values, will not graduate to stable and has been removed. Use the second argument of `optional` inline in your type constraint to declare default values instead.
If you have any experimental modules that were participating in this experiment, you will need to remove the experiment opt-in and adopt the new syntax for declaring default values in order to migrate your existing module to the stablized version of this feature. If you are writing a shared module for others to use, we recommend declaring that your module requires Terraform v1.3.0 or later to give specific feedback when using the new feature on older Terraform versions, in place of the previous declaration to use the experimental form of this feature: If you have any experimental modules that were participating in this experiment, you will need to remove the experiment opt-in and adopt the new syntax for declaring default values in order to migrate your existing module to the stablized version of this feature. If you are writing a shared module for others to use, we recommend declaring that your module requires Terraform v1.3.0 or later to give specific feedback when using the new feature on older Terraform versions, in place of the previous declaration to use the experimental form of this feature:
```hcl ```hcl
terraform { terraform {
required_version = ">= 1.3.0" required_version = ">= 1.3.0"
......
...@@ -199,6 +199,17 @@ func (b *Cloud) waitForRun(stopCtx, cancelCtx context.Context, op *backend.Opera ...@@ -199,6 +199,17 @@ func (b *Cloud) waitForRun(stopCtx, cancelCtx context.Context, op *backend.Opera
} }
} }
func (b *Cloud) waitTaskStage(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run, stageID string, outputTitle string) error {
integration := &IntegrationContext{
B: b,
StopContext: stopCtx,
CancelContext: cancelCtx,
Op: op,
Run: r,
}
return b.runTasks(integration, integration.BeginOutput(outputTitle), stageID)
}
func (b *Cloud) costEstimate(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run) error { func (b *Cloud) costEstimate(stopCtx, cancelCtx context.Context, op *backend.Operation, r *tfe.Run) error {
if r.CostEstimate == nil { if r.CostEstimate == nil {
return nil return nil
......
...@@ -291,6 +291,28 @@ in order to capture the filesystem context the remote workspace expects: ...@@ -291,6 +291,28 @@ in order to capture the filesystem context the remote workspace expects:
runHeader, b.hostname, b.organization, op.Workspace, r.ID)) + "\n")) runHeader, b.hostname, b.organization, op.Workspace, r.ID)) + "\n"))
} }
// Retrieve the run to get task stages.
// Task Stages are calculated upfront so we only need to call this once for the run.
taskStages := make([]*tfe.TaskStage, 0)
result, err := b.client.Runs.ReadWithOptions(stopCtx, r.ID, &tfe.RunReadOptions{
Include: []tfe.RunIncludeOpt{tfe.RunTaskStages},
})
if err == nil {
taskStages = result.TaskStages
} else {
// This error would be expected for older versions of TFE that do not allow
// fetching task_stages.
if !strings.HasSuffix(err.Error(), "Invalid include parameter") {
return r, generalError("Failed to retrieve run", err)
}
}
if stageID := getTaskStageIDByName(taskStages, tfe.PrePlan); stageID != nil {
if err := b.waitTaskStage(stopCtx, cancelCtx, op, r, *stageID, "Pre-plan Tasks"); err != nil {
return r, err
}
}
r, err = b.waitForRun(stopCtx, cancelCtx, op, "plan", r, w) r, err = b.waitForRun(stopCtx, cancelCtx, op, "plan", r, w)
if err != nil { if err != nil {
return r, err return r, err
...@@ -324,20 +346,9 @@ in order to capture the filesystem context the remote workspace expects: ...@@ -324,20 +346,9 @@ in order to capture the filesystem context the remote workspace expects:
} }
// Retrieve the run to get its current status. // Retrieve the run to get its current status.
runID := r.ID r, err = b.client.Runs.Read(stopCtx, r.ID)
r, err = b.client.Runs.ReadWithOptions(stopCtx, runID, &tfe.RunReadOptions{
Include: []tfe.RunIncludeOpt{tfe.RunTaskStages},
})
if err != nil { if err != nil {
// This error would be expected for older versions of TFE that do not allow return r, generalError("Failed to retrieve run", err)
// fetching task_stages.
if strings.HasSuffix(err.Error(), "Invalid include parameter") {
r, err = b.client.Runs.Read(stopCtx, runID)
}
if err != nil {
return r, generalError("Failed to retrieve run", err)
}
} }
// If the run is canceled or errored, we still continue to the // If the run is canceled or errored, we still continue to the
...@@ -346,18 +357,8 @@ in order to capture the filesystem context the remote workspace expects: ...@@ -346,18 +357,8 @@ in order to capture the filesystem context the remote workspace expects:
// status of the run will be "errored", but there is still policy // status of the run will be "errored", but there is still policy
// information which should be shown. // information which should be shown.
// Await post-plan run tasks if stageID := getTaskStageIDByName(taskStages, tfe.PostPlan); stageID != nil {
integration := &IntegrationContext{ if err := b.waitTaskStage(stopCtx, cancelCtx, op, r, *stageID, "Post-plan Tasks"); err != nil {
B: b,
StopContext: stopCtx,
CancelContext: cancelCtx,
Op: op,
Run: r,
}
if stageID := getTaskStageIDByName(r.TaskStages, tfe.PostPlan); stageID != nil {
err = b.runTasks(integration, integration.BeginOutput("Run Tasks (post-plan)"), *stageID)
if err != nil {
return r, err return r, err
} }
} }
......
...@@ -52,7 +52,7 @@ func (b *Cloud) runTasksWithTaskResults(context *IntegrationContext, output Inte ...@@ -52,7 +52,7 @@ func (b *Cloud) runTasksWithTaskResults(context *IntegrationContext, output Inte
stage, err := fetchTaskStage(b, context.StopContext) stage, err := fetchTaskStage(b, context.StopContext)
if err != nil { if err != nil {
return false, generalError("Failed to retrieve pre-apply task stage", err) return false, generalError("Failed to retrieve task stage", err)
} }
summary := summarizeTaskResults(stage.TaskResults) summary := summarizeTaskResults(stage.TaskResults)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment