diff --git a/internal/backend/backendrun/operation.go b/internal/backend/backendrun/operation.go index 7a2ce6086f..3ce59a9f4f 100644 --- a/internal/backend/backendrun/operation.go +++ b/internal/backend/backendrun/operation.go @@ -124,6 +124,13 @@ type Operation struct { // the variables set in the plan are used instead, and they must be valid. AllowUnsetVariables bool + // DeferralAllowed enables experimental support for automatically performing + // a partial plan if some objects are not yet plannable. + // + // IMPORTANT: When configuring an Operation, you should only set a value for + // this field if Terraform was built with experimental features enabled. + DeferralAllowed bool + // View implements the logic for all UI interactions. View views.Operation diff --git a/internal/backend/local/backend_local.go b/internal/backend/local/backend_local.go index 5f1811a840..d34d76f769 100644 --- a/internal/backend/local/backend_local.go +++ b/internal/backend/local/backend_local.go @@ -204,6 +204,7 @@ func (b *Local) localRunDirect(op *backendrun.Operation, run *backendrun.LocalRu SetVariables: variables, SkipRefresh: op.Type != backendrun.OperationTypeRefresh && !op.PlanRefresh, GenerateConfigPath: op.GenerateConfigOut, + DeferralAllowed: op.DeferralAllowed, } run.PlanOpts = planOpts diff --git a/internal/command/apply.go b/internal/command/apply.go index b6a0f3a05d..ebf29a27a2 100644 --- a/internal/command/apply.go +++ b/internal/command/apply.go @@ -280,6 +280,20 @@ func (c *ApplyCommand) OperationRequest( opReq.Type = backendrun.OperationTypeApply opReq.View = view.Operation() + // EXPERIMENTAL: maybe enable deferred actions + if c.AllowExperimentalFeatures { + opReq.DeferralAllowed = args.DeferralAllowed + } else if args.DeferralAllowed { + // Belated flag parse error, since we don't know about experiments + // support at actual parse time. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + "The -allow-deferral flag is only valid in experimental builds of Terraform.", + )) + return nil, diags + } + var err error opReq.ConfigLoader, err = c.initConfigLoader() if err != nil { diff --git a/internal/command/arguments/extended.go b/internal/command/arguments/extended.go index abc90fd82a..6e9aadea2a 100644 --- a/internal/command/arguments/extended.go +++ b/internal/command/arguments/extended.go @@ -78,6 +78,18 @@ type Operation struct { // learn a use-case for broader matching. ForceReplace []addrs.AbsResourceInstance + // DeferralAllowed enables experimental support for automatically performing + // a partial plan if some objects are not yet plannable (due to unknown + // values in count/for_each, or due to other missing dependencies that can't + // be resolved in a single plan/apply cycle). + // + // IMPORTANT: This feature should only be available when Terraform is built + // with experimental features enabled. Since extendedFlagSet can't currently + // test whether experimental features are enabled, the check needs to happen + // when _reading_ these Operation arguments and transferring values to the + // backendrun.Operation struct. + DeferralAllowed bool + // These private fields are used only temporarily during decoding. Use // method Parse to populate the exported fields from these, validating // the raw values in the process. @@ -223,6 +235,7 @@ func extendedFlagSet(name string, state *State, operation *Operation, vars *Vars if operation != nil { f.IntVar(&operation.Parallelism, "parallelism", DefaultParallelism, "parallelism") + f.BoolVar(&operation.DeferralAllowed, "allow-deferral", false, "allow-deferral") f.BoolVar(&operation.Refresh, "refresh", true, "refresh") f.BoolVar(&operation.destroyRaw, "destroy", false, "destroy") f.BoolVar(&operation.refreshOnlyRaw, "refresh-only", false, "refresh-only") diff --git a/internal/command/plan.go b/internal/command/plan.go index f9423c79bf..056dfd3b97 100644 --- a/internal/command/plan.go +++ b/internal/command/plan.go @@ -165,6 +165,20 @@ func (c *PlanCommand) OperationRequest( opReq.Type = backendrun.OperationTypePlan opReq.View = view.Operation() + // EXPERIMENTAL: maybe enable deferred actions + if c.AllowExperimentalFeatures { + opReq.DeferralAllowed = args.DeferralAllowed + } else if args.DeferralAllowed { + // Belated flag parse error, since we don't know about experiments + // support at actual parse time. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + "The -allow-deferral flag is only valid in experimental builds of Terraform.", + )) + return nil, diags + } + var err error opReq.ConfigLoader, err = c.initConfigLoader() if err != nil { diff --git a/internal/command/refresh.go b/internal/command/refresh.go index 18bab4f25e..15160f98d0 100644 --- a/internal/command/refresh.go +++ b/internal/command/refresh.go @@ -147,6 +147,20 @@ func (c *RefreshCommand) OperationRequest(be backendrun.OperationsBackend, view opReq.Type = backendrun.OperationTypeRefresh opReq.View = view.Operation() + // EXPERIMENTAL: maybe enable deferred actions + if c.AllowExperimentalFeatures { + opReq.DeferralAllowed = args.DeferralAllowed + } else if args.DeferralAllowed { + // Belated flag parse error, since we don't know about experiments + // support at actual parse time. + diags = diags.Append(tfdiags.Sourceless( + tfdiags.Error, + "Failed to parse command-line flags", + "The -allow-deferral flag is only valid in experimental builds of Terraform.", + )) + return nil, diags + } + var err error opReq.ConfigLoader, err = c.initConfigLoader() if err != nil {