diff --git a/internal/cloud/backend_common.go b/internal/cloud/backend_common.go index a5082f923d..9b0e42d099 100644 --- a/internal/cloud/backend_common.go +++ b/internal/cloud/backend_common.go @@ -450,7 +450,7 @@ func (b *Cloud) confirm(stopCtx context.Context, op *backend.Operation, opts *te switch keyword { case "override": - if r.Status != tfe.RunPolicyOverride { + if r.Status != tfe.RunPolicyOverride && r.Status != tfe.RunPostPlanAwaitingDecision { if r.Status == tfe.RunDiscarded { err = errRunDiscarded } else { diff --git a/internal/cloud/backend_taskStages.go b/internal/cloud/backend_taskStages.go index 3729d16602..2b288c8b15 100644 --- a/internal/cloud/backend_taskStages.go +++ b/internal/cloud/backend_taskStages.go @@ -6,10 +6,16 @@ import ( "strings" tfe "github.com/hashicorp/go-tfe" + "github.com/hashicorp/terraform/internal/terraform" ) type taskStages map[tfe.Stage]*tfe.TaskStage +const taskStageHeader = ` +To view this run in a browser, visit: +https://%s/app/%s/%s/runs/%s +` + type taskStageSummarizer interface { // Summarize takes an IntegrationContext, IntegrationOutputWriter for // writing output and a pointer to a tfe.TaskStage object as arguments. @@ -104,7 +110,6 @@ func (b *Cloud) runTaskStage(ctx *IntegrationContext, output IntegrationOutputWr } } case tfe.TaskStageAwaitingOverride: - // TODO: Add override functionality for _, s := range summarizers { cont, msg, err := s.Summarize(ctx, output, stage) if cont { @@ -121,6 +126,12 @@ func (b *Cloud) runTaskStage(ctx *IntegrationContext, output IntegrationOutputWr errs.Append(err) } } + cont, err := b.processStageOverrides(ctx, output, stage.ID) + if err != nil { + errs.Append(err) + } else { + return cont, nil + } case tfe.TaskStageUnreachable: return false, nil default: @@ -133,3 +144,27 @@ func (b *Cloud) runTaskStage(ctx *IntegrationContext, output IntegrationOutputWr return false, nil }) } + +func (b *Cloud) processStageOverrides(context *IntegrationContext, output IntegrationOutputWriter, taskStageID string) (bool, error) { + opts := &terraform.InputOpts{ + Id: fmt.Sprintf("%c%c [bold]Override", Arrow, Arrow), + Query: "\nDo you want to override the failed policy check?", + Description: "Only 'override' will be accepted to override.", + } + runUrl := fmt.Sprintf(taskStageHeader, b.hostname, b.organization, context.Op.Workspace, context.Run.ID) + err := b.confirm(context.StopContext, context.Op, opts, context.Run, "override") + if err != nil && err != errRunOverridden { + return false, fmt.Errorf( + fmt.Sprintf("Failed to override: %s\n%s\n", err.Error(), runUrl), + ) + } + + if err != errRunOverridden { + if _, err = b.client.TaskStages.Override(context.StopContext, taskStageID, tfe.TaskStageOverrideOptions{}); err != nil { + return false, generalError(fmt.Sprintf("Failed to override policy check.\n%s", runUrl), err) + } + } else { + output.Output(fmt.Sprintf("The run needs to be manually overridden or discarded.\n%s\n", runUrl)) + } + return false, nil +}