Skip to content

Commit db1927b

Browse files
committed
Add -create-default-workspace flag to init command, for overriding behaviour when TF isn't in interactive mode
This required refactoring the signature of the initBackend method; pass in all init args instead of a subset
1 parent 94ca12c commit db1927b

File tree

4 files changed

+59
-31
lines changed

4 files changed

+59
-31
lines changed

internal/command/arguments/init.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ type Init struct {
7878
// TODO(SarahFrench/radeksimko): Remove this once the feature is no longer
7979
// experimental
8080
EnablePssExperiment bool
81+
82+
// CreateDefaultWorkspace indicates whether the default workspace should be created by
83+
// Terraform when initializing a state store for the first time.
84+
CreateDefaultWorkspace bool
8185
}
8286

8387
// ParseInit processes CLI arguments, returning an Init value and errors.
@@ -111,7 +115,7 @@ func ParseInit(args []string) (*Init, tfdiags.Diagnostics) {
111115
cmdFlags.BoolVar(&init.Json, "json", false, "json")
112116
cmdFlags.Var(&init.BackendConfig, "backend-config", "")
113117
cmdFlags.Var(&init.PluginPath, "plugin-dir", "plugin directory")
114-
118+
cmdFlags.BoolVar(&init.CreateDefaultWorkspace, "create-default-workspace", true, "when -input=false, use this flag to block creation of the default workspace")
115119
// Used for enabling experimental code that's invoked before configuration is parsed.
116120
cmdFlags.BoolVar(&init.EnablePssExperiment, "enable-pluggable-state-storage-experiment", false, "Enable the pluggable state storage experiment")
117121

@@ -139,6 +143,14 @@ func ParseInit(args []string) (*Init, tfdiags.Diagnostics) {
139143
))
140144
}
141145

146+
if init.InputEnabled && !init.CreateDefaultWorkspace {
147+
diags = diags.Append(tfdiags.Sourceless(
148+
tfdiags.Warning,
149+
"Invalid init options",
150+
"The flag -create-default-workspace=false is ignored when Terraform is configured to ask users for input. Instead, add -input=false or remove the -create-default-workspace flag",
151+
))
152+
}
153+
142154
init.Args = cmdFlags.Args()
143155

144156
backendFlagSet := FlagIsSet(cmdFlags, "backend")

internal/command/init.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (c *InitCommand) Run(args []string) int {
5151
args = c.Meta.process(args)
5252
initArgs, initDiags := arguments.ParseInit(args)
5353

54-
view := views.NewInit(initArgs.ViewType, c.View)
54+
view := views.NewInit(viewType, c.View)
5555

5656
if initDiags.HasErrors() {
5757
diags = diags.Append(initDiags)
@@ -246,12 +246,13 @@ func (c *InitCommand) initBackend(ctx context.Context, root *configs.Module, ext
246246
}
247247

248248
opts = &BackendOpts{
249-
StateStoreConfig: root.StateStore,
250-
Locks: locks,
251-
ProviderFactory: factory,
252-
ConfigOverride: configOverride,
253-
Init: true,
254-
ViewType: viewType,
249+
StateStoreConfig: root.StateStore,
250+
Locks: locks,
251+
ProviderFactory: factory,
252+
CreateDefaultWorkspace: initArgs.CreateDefaultWorkspace,
253+
ConfigOverride: configOverride,
254+
Init: true,
255+
ViewType: viewType,
255256
}
256257

257258
case root.Backend != nil:

internal/command/init_run_experiment.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func (c *InitCommand) runPssInit(initArgs *arguments.Init, view views.Init) int
208208
// This handles case when config contains either backend or state_store blocks.
209209
// This is valid as either can be implementations of backend.Backend, which is what we
210210
// obtain here.
211-
back, backendOutput, backDiags = c.initBackend(ctx, rootModEarly, initArgs.BackendConfig, initArgs.ViewType, configLocks, view)
211+
back, backendOutput, backDiags = c.initBackend(ctx, rootModEarly, initArgs, configLocks, view)
212212
default:
213213
// load the previously-stored backend config
214214
back, backDiags = c.Meta.backendFromState(ctx)

internal/command/meta_backend.go

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ type BackendOpts struct {
8989
// ViewType will set console output format for the
9090
// initialization operation (JSON or human-readable).
9191
ViewType arguments.ViewType
92+
93+
// CreateDefaultWorkspace signifies whether the operations backend should create
94+
// the default workspace or not
95+
CreateDefaultWorkspace bool
9296
}
9397

9498
// BackendWithRemoteTerraformVersion is a shared interface between the 'remote' and 'cloud' backends
@@ -1671,32 +1675,43 @@ func (m *Meta) stateStore_C_s(c *configs.StateStore, cHash int, backendSMgr *cli
16711675
return nil, diags
16721676

16731677
case ws == backend.DefaultStateName:
1674-
// TODO: do we want to prompt for input here (m.Input()), or create automatically unless -readonly flag present?
1675-
// input := m.UIInput()
1676-
// desc := fmt.Sprintf("Terraform will create the %q workspace via %q.\n"+
1677-
// "Only 'yes' will be accepted to approve.", backend.DefaultStateName, c.Type)
1678-
// v, err := input.Input(context.Background(), &terraform.InputOpts{
1679-
// Id: "approve",
1680-
// Query: fmt.Sprintf("Workspace %q does not exit, would you like to create one?", backend.DefaultStateName),
1681-
// Description: desc,
1682-
// })
1683-
// if err != nil {
1684-
// diags = diags.Append(fmt.Errorf("Failed to confirm default workspace creation: %w", err))
1685-
// return nil, diags
1686-
// }
1687-
// if v != "yes" {
1688-
// diags = diags.Append(errors.New("Failed to create default workspace"))
1689-
// return nil, diags
1690-
// }
1691-
1692-
// TODO: Confirm if defaulting to creation on first use (rather than error) is a good idea
1693-
// Make the default workspace. All other workspaces are user-created via the workspace commands.
1694-
m.createDefaultWorkspace(c, b)
1678+
// Should we create the default state after prompting the user, or not?
1679+
if m.Input() {
1680+
// If input is enabled, we prompt the user before creating the default workspace.
1681+
input := m.UIInput()
1682+
desc := fmt.Sprintf("Terraform will create the %q workspace via state store %q.\n"+
1683+
"Only 'yes' will be accepted to approve.", backend.DefaultStateName, c.Type)
1684+
v, err := input.Input(context.Background(), &terraform.InputOpts{
1685+
Id: "approve",
1686+
Query: fmt.Sprintf("Workspace the %s workspace does not exit, would you like to create it?", backend.DefaultStateName),
1687+
Description: desc,
1688+
})
1689+
if err != nil {
1690+
diags = diags.Append(fmt.Errorf("Failed to confirm %s workspace creation: %w", backend.DefaultStateName, err))
1691+
return nil, diags
1692+
}
1693+
if v != "yes" {
1694+
diags = diags.Append(fmt.Errorf("Cancelled creation of the %s workspace", backend.DefaultStateName))
1695+
return nil, diags
1696+
}
1697+
m.createDefaultWorkspace(c, b)
1698+
} else {
1699+
// If input is disabled, we don't prompt before creating the default workspace.
1700+
// However this can be blocked with other flags present.
1701+
if opts.CreateDefaultWorkspace {
1702+
m.createDefaultWorkspace(c, b)
1703+
} else {
1704+
diags = diags.Append(&hcl.Diagnostic{
1705+
Severity: hcl.DiagWarning,
1706+
Summary: "The default workspace does not exist",
1707+
Detail: "Terraform has been configured to skip creation of the default workspace in the state store. This may cause issues in subsequent Terraform operations",
1708+
})
1709+
}
1710+
}
16951711
default:
16961712
diags = diags.Append(err)
16971713
return nil, diags
16981714
}
1699-
// TODO: handle if input is not enabled
17001715
}
17011716
}
17021717
if diags.HasErrors() {

0 commit comments

Comments
 (0)