@@ -5,10 +5,10 @@ package command
5
5
6
6
import (
7
7
"context"
8
- "errors"
9
8
"fmt"
10
9
"log"
11
10
"maps"
11
+ "os"
12
12
"reflect"
13
13
"slices"
14
14
"sort"
@@ -19,13 +19,11 @@ import (
19
19
"github.com/posener/complete"
20
20
"github.com/zclconf/go-cty/cty"
21
21
"go.opentelemetry.io/otel/attribute"
22
- "go.opentelemetry.io/otel/codes"
23
22
"go.opentelemetry.io/otel/trace"
24
23
25
24
"github.com/hashicorp/terraform/internal/addrs"
26
25
"github.com/hashicorp/terraform/internal/backend"
27
26
backendInit "github.com/hashicorp/terraform/internal/backend/init"
28
- "github.com/hashicorp/terraform/internal/cloud"
29
27
"github.com/hashicorp/terraform/internal/command/arguments"
30
28
"github.com/hashicorp/terraform/internal/command/views"
31
29
"github.com/hashicorp/terraform/internal/configs"
@@ -34,7 +32,6 @@ import (
34
32
"github.com/hashicorp/terraform/internal/getproviders"
35
33
"github.com/hashicorp/terraform/internal/providercache"
36
34
"github.com/hashicorp/terraform/internal/states"
37
- "github.com/hashicorp/terraform/internal/terraform"
38
35
"github.com/hashicorp/terraform/internal/tfdiags"
39
36
tfversion "github.com/hashicorp/terraform/version"
40
37
)
@@ -58,282 +55,20 @@ func (c *InitCommand) Run(args []string) int {
58
55
return 1
59
56
}
60
57
61
- c .forceInitCopy = initArgs .ForceInitCopy
62
- c .Meta .stateLock = initArgs .StateLock
63
- c .Meta .stateLockTimeout = initArgs .StateLockTimeout
64
- c .reconfigure = initArgs .Reconfigure
65
- c .migrateState = initArgs .MigrateState
66
- c .Meta .ignoreRemoteVersion = initArgs .IgnoreRemoteVersion
67
- c .Meta .input = initArgs .InputEnabled
68
- c .Meta .targetFlags = initArgs .TargetFlags
69
- c .Meta .compactWarnings = initArgs .CompactWarnings
70
-
71
- varArgs := initArgs .Vars .All ()
72
- items := make ([]arguments.FlagNameValue , len (varArgs ))
73
- for i := range varArgs {
74
- items [i ].Name = varArgs [i ].Name
75
- items [i ].Value = varArgs [i ].Value
58
+ // The else condition below invokes the original logic of the init command.
59
+ // An experimental version of the init code will be used if:
60
+ // > The user uses an experimental version of TF (alpha or built from source)
61
+ // > Either the flag -enable-pluggable-state-storage-experiment is passed to the init command.
62
+ // > Or, the environment variable TF_ENABLE_PLUGGABLE_STATE_STORAGE is set to any value.
63
+ if v := os .Getenv ("TF_ENABLE_PLUGGABLE_STATE_STORAGE" ); v != "" {
64
+ initArgs .EnablePssExperiment = true
76
65
}
77
- c .Meta .variableArgs = arguments.FlagNameValueSlice {Items : & items }
78
-
79
- // Copying the state only happens during backend migration, so setting
80
- // -force-copy implies -migrate-state
81
- if c .forceInitCopy {
82
- c .migrateState = true
83
- }
84
-
85
- if len (initArgs .PluginPath ) > 0 {
86
- c .pluginPath = initArgs .PluginPath
87
- }
88
-
89
- // Validate the arg count and get the working directory
90
- path , err := ModulePath (initArgs .Args )
91
- if err != nil {
92
- diags = diags .Append (err )
93
- view .Diagnostics (diags )
94
- return 1
95
- }
96
-
97
- if err := c .storePluginPath (c .pluginPath ); err != nil {
98
- diags = diags .Append (fmt .Errorf ("Error saving -plugin-dir to workspace directory: %s" , err ))
99
- view .Diagnostics (diags )
100
- return 1
101
- }
102
-
103
- // Initialization can be aborted by interruption signals
104
- ctx , done := c .InterruptibleContext (c .CommandContext ())
105
- defer done ()
106
-
107
- // This will track whether we outputted anything so that we know whether
108
- // to output a newline before the success message
109
- var header bool
110
-
111
- if initArgs .FromModule != "" {
112
- src := initArgs .FromModule
113
-
114
- empty , err := configs .IsEmptyDir (path , initArgs .TestsDirectory )
115
- if err != nil {
116
- diags = diags .Append (fmt .Errorf ("Error validating destination directory: %s" , err ))
117
- view .Diagnostics (diags )
118
- return 1
119
- }
120
- if ! empty {
121
- diags = diags .Append (errors .New (strings .TrimSpace (errInitCopyNotEmpty )))
122
- view .Diagnostics (diags )
123
- return 1
124
- }
125
-
126
- view .Output (views .CopyingConfigurationMessage , src )
127
- header = true
128
-
129
- hooks := uiModuleInstallHooks {
130
- Ui : c .Ui ,
131
- ShowLocalPaths : false , // since they are in a weird location for init
132
- View : view ,
133
- }
134
-
135
- ctx , span := tracer .Start (ctx , "-from-module=..." , trace .WithAttributes (
136
- attribute .String ("module_source" , src ),
137
- ))
138
-
139
- initDirFromModuleAbort , initDirFromModuleDiags := c .initDirFromModule (ctx , path , src , hooks )
140
- diags = diags .Append (initDirFromModuleDiags )
141
- if initDirFromModuleAbort || initDirFromModuleDiags .HasErrors () {
142
- view .Diagnostics (diags )
143
- span .SetStatus (codes .Error , "module installation failed" )
144
- span .End ()
145
- return 1
146
- }
147
- span .End ()
148
-
149
- view .Output (views .EmptyMessage )
150
- }
151
-
152
- // If our directory is empty, then we're done. We can't get or set up
153
- // the backend with an empty directory.
154
- empty , err := configs .IsEmptyDir (path , initArgs .TestsDirectory )
155
- if err != nil {
156
- diags = diags .Append (fmt .Errorf ("Error checking configuration: %s" , err ))
157
- view .Diagnostics (diags )
158
- return 1
159
- }
160
- if empty {
161
- view .Output (views .OutputInitEmptyMessage )
162
- return 0
163
- }
164
-
165
- // Load just the root module to begin backend and module initialization
166
- rootModEarly , earlyConfDiags := c .loadSingleModuleWithTests (path , initArgs .TestsDirectory )
167
-
168
- // There may be parsing errors in config loading but these will be shown later _after_
169
- // checking for core version requirement errors. Not meeting the version requirement should
170
- // be the first error displayed if that is an issue, but other operations are required
171
- // before being able to check core version requirements.
172
- if rootModEarly == nil {
173
- diags = diags .Append (errors .New (view .PrepareMessage (views .InitConfigError )), earlyConfDiags )
174
- view .Diagnostics (diags )
175
-
176
- return 1
177
- }
178
-
179
- var back backend.Backend
180
-
181
- // There may be config errors or backend init errors but these will be shown later _after_
182
- // checking for core version requirement errors.
183
- var backDiags tfdiags.Diagnostics
184
- var backendOutput bool
185
-
186
- switch {
187
- case initArgs .Cloud && rootModEarly .CloudConfig != nil :
188
- back , backendOutput , backDiags = c .initCloud (ctx , rootModEarly , initArgs .BackendConfig , initArgs .ViewType , view )
189
- case initArgs .Backend :
190
- back , backendOutput , backDiags = c .initBackend (ctx , rootModEarly , initArgs .BackendConfig , initArgs .ViewType , view )
191
- default :
192
- // load the previously-stored backend config
193
- back , backDiags = c .Meta .backendFromState (ctx )
194
- }
195
- if backendOutput {
196
- header = true
197
- }
198
-
199
- var state * states.State
200
-
201
- // If we have a functional backend (either just initialized or initialized
202
- // on a previous run) we'll use the current state as a potential source
203
- // of provider dependencies.
204
- if back != nil {
205
- c .ignoreRemoteVersionConflict (back )
206
- workspace , err := c .Workspace ()
207
- if err != nil {
208
- diags = diags .Append (fmt .Errorf ("Error selecting workspace: %s" , err ))
209
- view .Diagnostics (diags )
210
- return 1
211
- }
212
- sMgr , err := back .StateMgr (workspace )
213
- if err != nil {
214
- diags = diags .Append (fmt .Errorf ("Error loading state: %s" , err ))
215
- view .Diagnostics (diags )
216
- return 1
217
- }
218
-
219
- if err := sMgr .RefreshState (); err != nil {
220
- diags = diags .Append (fmt .Errorf ("Error refreshing state: %s" , err ))
221
- view .Diagnostics (diags )
222
- return 1
223
- }
224
-
225
- state = sMgr .State ()
226
- }
227
-
228
- if initArgs .Get {
229
- modsOutput , modsAbort , modsDiags := c .getModules (ctx , path , initArgs .TestsDirectory , rootModEarly , initArgs .Upgrade , view )
230
- diags = diags .Append (modsDiags )
231
- if modsAbort || modsDiags .HasErrors () {
232
- view .Diagnostics (diags )
233
- return 1
234
- }
235
- if modsOutput {
236
- header = true
237
- }
238
- }
239
-
240
- // With all of the modules (hopefully) installed, we can now try to load the
241
- // whole configuration tree.
242
- config , confDiags := c .loadConfigWithTests (path , initArgs .TestsDirectory )
243
- // configDiags will be handled after the version constraint check, since an
244
- // incorrect version of terraform may be producing errors for configuration
245
- // constructs added in later versions.
246
-
247
- // Before we go further, we'll check to make sure none of the modules in
248
- // the configuration declare that they don't support this Terraform
249
- // version, so we can produce a version-related error message rather than
250
- // potentially-confusing downstream errors.
251
- versionDiags := terraform .CheckCoreVersionRequirements (config )
252
- if versionDiags .HasErrors () {
253
- view .Diagnostics (versionDiags )
254
- return 1
255
- }
256
-
257
- // We've passed the core version check, now we can show errors from the
258
- // configuration and backend initialisation.
259
-
260
- // Now, we can check the diagnostics from the early configuration and the
261
- // backend.
262
- diags = diags .Append (earlyConfDiags )
263
- diags = diags .Append (backDiags )
264
- if earlyConfDiags .HasErrors () {
265
- diags = diags .Append (errors .New (view .PrepareMessage (views .InitConfigError )))
266
- view .Diagnostics (diags )
267
- return 1
268
- }
269
-
270
- // Now, we can show any errors from initializing the backend, but we won't
271
- // show the InitConfigError preamble as we didn't detect problems with
272
- // the early configuration.
273
- if backDiags .HasErrors () {
274
- view .Diagnostics (diags )
275
- return 1
276
- }
277
-
278
- // If everything is ok with the core version check and backend initialization,
279
- // show other errors from loading the full configuration tree.
280
- diags = diags .Append (confDiags )
281
- if confDiags .HasErrors () {
282
- diags = diags .Append (errors .New (view .PrepareMessage (views .InitConfigError )))
283
- view .Diagnostics (diags )
284
- return 1
285
- }
286
-
287
- if cb , ok := back .(* cloud.Cloud ); ok {
288
- if c .RunningInAutomation {
289
- if err := cb .AssertImportCompatible (config ); err != nil {
290
- diags = diags .Append (tfdiags .Sourceless (tfdiags .Error , "Compatibility error" , err .Error ()))
291
- view .Diagnostics (diags )
292
- return 1
293
- }
294
- }
295
- }
296
-
297
- // Now that we have loaded all modules, check the module tree for missing providers.
298
- providersOutput , providersAbort , providerDiags := c .getProviders (ctx , config , state , initArgs .Upgrade , initArgs .PluginPath , initArgs .Lockfile , view )
299
- diags = diags .Append (providerDiags )
300
- if providersAbort || providerDiags .HasErrors () {
301
- view .Diagnostics (diags )
302
- return 1
303
- }
304
- if providersOutput {
305
- header = true
306
- }
307
-
308
- // If we outputted information, then we need to output a newline
309
- // so that our success message is nicely spaced out from prior text.
310
- if header {
311
- view .Output (views .EmptyMessage )
312
- }
313
-
314
- // If we accumulated any warnings along the way that weren't accompanied
315
- // by errors then we'll output them here so that the success message is
316
- // still the final thing shown.
317
- view .Diagnostics (diags )
318
- _ , cloud := back .(* cloud.Cloud )
319
- output := views .OutputInitSuccessMessage
320
- if cloud {
321
- output = views .OutputInitSuccessCloudMessage
322
- }
323
-
324
- view .Output (output )
325
-
326
- if ! c .RunningInAutomation {
327
- // If we're not running in an automation wrapper, give the user
328
- // some more detailed next steps that are appropriate for interactive
329
- // shell usage.
330
- output = views .OutputInitSuccessCLIMessage
331
- if cloud {
332
- output = views .OutputInitSuccessCLICloudMessage
333
- }
334
- view .Output (output )
66
+ if c .Meta .AllowExperimentalFeatures && initArgs .EnablePssExperiment {
67
+ // TODO(SarahFrench/radeksimko): Remove forked init logic once feature is no longer experimental
68
+ panic ("This experiment is not available yet" )
69
+ } else {
70
+ return c .run (initArgs , view )
335
71
}
336
- return 0
337
72
}
338
73
339
74
func (c * InitCommand ) getModules (ctx context.Context , path , testsDir string , earlyRoot * configs.Module , upgrade bool , view views.Init ) (output bool , abort bool , diags tfdiags.Diagnostics ) {
0 commit comments