@@ -5,10 +5,10 @@ package command
55
66import (
77 "context"
8- "errors"
98 "fmt"
109 "log"
1110 "maps"
11+ "os"
1212 "reflect"
1313 "slices"
1414 "sort"
@@ -19,13 +19,11 @@ import (
1919 "github.com/posener/complete"
2020 "github.com/zclconf/go-cty/cty"
2121 "go.opentelemetry.io/otel/attribute"
22- "go.opentelemetry.io/otel/codes"
2322 "go.opentelemetry.io/otel/trace"
2423
2524 "github.com/hashicorp/terraform/internal/addrs"
2625 "github.com/hashicorp/terraform/internal/backend"
2726 backendInit "github.com/hashicorp/terraform/internal/backend/init"
28- "github.com/hashicorp/terraform/internal/cloud"
2927 "github.com/hashicorp/terraform/internal/command/arguments"
3028 "github.com/hashicorp/terraform/internal/command/views"
3129 "github.com/hashicorp/terraform/internal/configs"
@@ -34,7 +32,6 @@ import (
3432 "github.com/hashicorp/terraform/internal/getproviders"
3533 "github.com/hashicorp/terraform/internal/providercache"
3634 "github.com/hashicorp/terraform/internal/states"
37- "github.com/hashicorp/terraform/internal/terraform"
3835 "github.com/hashicorp/terraform/internal/tfdiags"
3936 tfversion "github.com/hashicorp/terraform/version"
4037)
@@ -58,282 +55,20 @@ func (c *InitCommand) Run(args []string) int {
5855 return 1
5956 }
6057
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
7665 }
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 )
33571 }
336- return 0
33772}
33873
33974func (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