github.com/opentofu/opentofu@v1.7.1/internal/command/init.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package command 7 8 import ( 9 "context" 10 "fmt" 11 "log" 12 "reflect" 13 "sort" 14 "strings" 15 16 "github.com/hashicorp/hcl/v2" 17 svchost "github.com/hashicorp/terraform-svchost" 18 "github.com/posener/complete" 19 "github.com/zclconf/go-cty/cty" 20 "go.opentelemetry.io/otel/attribute" 21 "go.opentelemetry.io/otel/codes" 22 "go.opentelemetry.io/otel/trace" 23 24 "github.com/opentofu/opentofu/internal/addrs" 25 "github.com/opentofu/opentofu/internal/backend" 26 backendInit "github.com/opentofu/opentofu/internal/backend/init" 27 "github.com/opentofu/opentofu/internal/cloud" 28 "github.com/opentofu/opentofu/internal/command/arguments" 29 "github.com/opentofu/opentofu/internal/command/views" 30 "github.com/opentofu/opentofu/internal/configs" 31 "github.com/opentofu/opentofu/internal/configs/configschema" 32 "github.com/opentofu/opentofu/internal/encryption" 33 "github.com/opentofu/opentofu/internal/getproviders" 34 "github.com/opentofu/opentofu/internal/providercache" 35 "github.com/opentofu/opentofu/internal/states" 36 "github.com/opentofu/opentofu/internal/tfdiags" 37 "github.com/opentofu/opentofu/internal/tofu" 38 "github.com/opentofu/opentofu/internal/tofumigrate" 39 tfversion "github.com/opentofu/opentofu/version" 40 ) 41 42 // InitCommand is a Command implementation that takes a Terraform 43 // module and clones it to the working directory. 44 type InitCommand struct { 45 Meta 46 } 47 48 func (c *InitCommand) Run(args []string) int { 49 var flagFromModule, flagLockfile, testsDirectory string 50 var flagBackend, flagCloud, flagGet, flagUpgrade bool 51 var flagPluginPath FlagStringSlice 52 flagConfigExtra := newRawFlags("-backend-config") 53 54 args = c.Meta.process(args) 55 cmdFlags := c.Meta.extendedFlagSet("init") 56 cmdFlags.BoolVar(&flagBackend, "backend", true, "") 57 cmdFlags.BoolVar(&flagCloud, "cloud", true, "") 58 cmdFlags.Var(flagConfigExtra, "backend-config", "") 59 cmdFlags.StringVar(&flagFromModule, "from-module", "", "copy the source of the given module into the directory before init") 60 cmdFlags.BoolVar(&flagGet, "get", true, "") 61 cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data") 62 cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") 63 cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") 64 cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure") 65 cmdFlags.BoolVar(&c.migrateState, "migrate-state", false, "migrate state") 66 cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "") 67 cmdFlags.Var(&flagPluginPath, "plugin-dir", "plugin directory") 68 cmdFlags.StringVar(&flagLockfile, "lockfile", "", "Set a dependency lockfile mode") 69 cmdFlags.BoolVar(&c.Meta.ignoreRemoteVersion, "ignore-remote-version", false, "continue even if remote and local OpenTofu versions are incompatible") 70 cmdFlags.StringVar(&testsDirectory, "test-directory", "tests", "test-directory") 71 cmdFlags.BoolVar(&c.outputInJSON, "json", false, "json") 72 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 73 if err := cmdFlags.Parse(args); err != nil { 74 return 1 75 } 76 77 if c.outputInJSON { 78 c.Meta.color = false 79 c.Meta.Color = false 80 c.oldUi = c.Ui 81 c.Ui = &WrappedUi{ 82 cliUi: c.oldUi, 83 jsonView: views.NewJSONView(c.View), 84 outputInJSON: true, 85 } 86 } 87 88 backendFlagSet := arguments.FlagIsSet(cmdFlags, "backend") 89 cloudFlagSet := arguments.FlagIsSet(cmdFlags, "cloud") 90 91 switch { 92 case backendFlagSet && cloudFlagSet: 93 c.Ui.Error("The -backend and -cloud options are aliases of one another and mutually-exclusive in their use") 94 return 1 95 case backendFlagSet: 96 flagCloud = flagBackend 97 case cloudFlagSet: 98 flagBackend = flagCloud 99 } 100 101 if c.migrateState && c.reconfigure { 102 c.Ui.Error("The -migrate-state and -reconfigure options are mutually-exclusive") 103 return 1 104 } 105 106 // Copying the state only happens during backend migration, so setting 107 // -force-copy implies -migrate-state 108 if c.forceInitCopy { 109 c.migrateState = true 110 } 111 112 var diags tfdiags.Diagnostics 113 114 if len(flagPluginPath) > 0 { 115 c.pluginPath = flagPluginPath 116 } 117 118 // Validate the arg count and get the working directory 119 args = cmdFlags.Args() 120 path, err := modulePath(args) 121 if err != nil { 122 c.Ui.Error(err.Error()) 123 return 1 124 } 125 126 if err := c.storePluginPath(c.pluginPath); err != nil { 127 c.Ui.Error(fmt.Sprintf("Error saving -plugin-path values: %s", err)) 128 return 1 129 } 130 131 // Initialization can be aborted by interruption signals 132 ctx, done := c.InterruptibleContext(c.CommandContext()) 133 defer done() 134 135 // This will track whether we outputted anything so that we know whether 136 // to output a newline before the success message 137 var header bool 138 139 if flagFromModule != "" { 140 src := flagFromModule 141 142 empty, err := configs.IsEmptyDir(path) 143 if err != nil { 144 c.Ui.Error(fmt.Sprintf("Error validating destination directory: %s", err)) 145 return 1 146 } 147 if !empty { 148 c.Ui.Error(strings.TrimSpace(errInitCopyNotEmpty)) 149 return 1 150 } 151 152 c.Ui.Output(c.Colorize().Color(fmt.Sprintf( 153 "[reset][bold]Copying configuration[reset] from %q...", src, 154 ))) 155 header = true 156 157 hooks := uiModuleInstallHooks{ 158 Ui: c.Ui, 159 ShowLocalPaths: false, // since they are in a weird location for init 160 } 161 162 ctx, span := tracer.Start(ctx, "-from-module=...", trace.WithAttributes( 163 attribute.String("module_source", src), 164 )) 165 166 initDirFromModuleAbort, initDirFromModuleDiags := c.initDirFromModule(ctx, path, src, hooks) 167 diags = diags.Append(initDirFromModuleDiags) 168 if initDirFromModuleAbort || initDirFromModuleDiags.HasErrors() { 169 c.showDiagnostics(diags) 170 span.SetStatus(codes.Error, "module installation failed") 171 span.End() 172 return 1 173 } 174 span.End() 175 176 c.Ui.Output("") 177 } 178 179 // If our directory is empty, then we're done. We can't get or set up 180 // the backend with an empty directory. 181 empty, err := configs.IsEmptyDir(path) 182 if err != nil { 183 diags = diags.Append(fmt.Errorf("Error checking configuration: %w", err)) 184 c.showDiagnostics(diags) 185 return 1 186 } 187 if empty { 188 c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputInitEmpty))) 189 return 0 190 } 191 192 // Load just the root module to begin backend and module initialization 193 rootModEarly, earlyConfDiags := c.loadSingleModuleWithTests(path, testsDirectory) 194 195 // There may be parsing errors in config loading but these will be shown later _after_ 196 // checking for core version requirement errors. Not meeting the version requirement should 197 // be the first error displayed if that is an issue, but other operations are required 198 // before being able to check core version requirements. 199 if rootModEarly == nil { 200 c.Ui.Error(c.Colorize().Color(strings.TrimSpace(errInitConfigError))) 201 diags = diags.Append(earlyConfDiags) 202 c.showDiagnostics(diags) 203 204 return 1 205 } 206 207 // Load the encryption configuration 208 enc, encDiags := c.EncryptionFromModule(rootModEarly) 209 diags = diags.Append(encDiags) 210 if encDiags.HasErrors() { 211 c.showDiagnostics(diags) 212 return 1 213 } 214 215 var back backend.Backend 216 217 // There may be config errors or backend init errors but these will be shown later _after_ 218 // checking for core version requirement errors. 219 var backDiags tfdiags.Diagnostics 220 var backendOutput bool 221 222 switch { 223 case flagCloud && rootModEarly.CloudConfig != nil: 224 back, backendOutput, backDiags = c.initCloud(ctx, rootModEarly, flagConfigExtra, enc) 225 case flagBackend: 226 back, backendOutput, backDiags = c.initBackend(ctx, rootModEarly, flagConfigExtra, enc) 227 default: 228 // load the previously-stored backend config 229 back, backDiags = c.Meta.backendFromState(ctx, enc.State()) 230 } 231 if backendOutput { 232 header = true 233 } 234 235 var state *states.State 236 237 // If we have a functional backend (either just initialized or initialized 238 // on a previous run) we'll use the current state as a potential source 239 // of provider dependencies. 240 if back != nil { 241 c.ignoreRemoteVersionConflict(back) 242 workspace, err := c.Workspace() 243 if err != nil { 244 c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err)) 245 return 1 246 } 247 sMgr, err := back.StateMgr(workspace) 248 if err != nil { 249 c.Ui.Error(fmt.Sprintf("Error loading state: %s", err)) 250 return 1 251 } 252 253 if err := sMgr.RefreshState(); err != nil { 254 c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) 255 return 1 256 } 257 258 state = sMgr.State() 259 } 260 261 if flagGet { 262 modsOutput, modsAbort, modsDiags := c.getModules(ctx, path, testsDirectory, rootModEarly, flagUpgrade) 263 diags = diags.Append(modsDiags) 264 if modsAbort || modsDiags.HasErrors() { 265 c.showDiagnostics(diags) 266 return 1 267 } 268 if modsOutput { 269 header = true 270 } 271 } 272 273 // With all of the modules (hopefully) installed, we can now try to load the 274 // whole configuration tree. 275 config, confDiags := c.loadConfigWithTests(path, testsDirectory) 276 // configDiags will be handled after the version constraint check, since an 277 // incorrect version of tofu may be producing errors for configuration 278 // constructs added in later versions. 279 280 // Before we go further, we'll check to make sure none of the modules in 281 // the configuration declare that they don't support this OpenTofu 282 // version, so we can produce a version-related error message rather than 283 // potentially-confusing downstream errors. 284 versionDiags := tofu.CheckCoreVersionRequirements(config) 285 if versionDiags.HasErrors() { 286 c.showDiagnostics(versionDiags) 287 return 1 288 } 289 290 // We've passed the core version check, now we can show errors from the 291 // configuration and backend initialization. 292 293 // Now, we can check the diagnostics from the early configuration and the 294 // backend. 295 diags = diags.Append(earlyConfDiags) 296 diags = diags.Append(backDiags) 297 if earlyConfDiags.HasErrors() { 298 c.Ui.Error(strings.TrimSpace(errInitConfigError)) 299 c.showDiagnostics(diags) 300 return 1 301 } 302 303 // Now, we can show any errors from initializing the backend, but we won't 304 // show the errInitConfigError preamble as we didn't detect problems with 305 // the early configuration. 306 if backDiags.HasErrors() { 307 c.showDiagnostics(diags) 308 return 1 309 } 310 311 // If everything is ok with the core version check and backend initialization, 312 // show other errors from loading the full configuration tree. 313 diags = diags.Append(confDiags) 314 if confDiags.HasErrors() { 315 c.Ui.Error(strings.TrimSpace(errInitConfigError)) 316 c.showDiagnostics(diags) 317 return 1 318 } 319 320 if cb, ok := back.(*cloud.Cloud); ok { 321 if c.RunningInAutomation { 322 if err := cb.AssertImportCompatible(config); err != nil { 323 diags = diags.Append(tfdiags.Sourceless(tfdiags.Error, "Compatibility error", err.Error())) 324 c.showDiagnostics(diags) 325 return 1 326 } 327 } 328 } 329 330 if state != nil { 331 // Since we now have the full configuration loaded, we can use it to migrate the in-memory state view 332 // prior to fetching providers. 333 migratedState, migrateDiags := tofumigrate.MigrateStateProviderAddresses(config, state) 334 diags = diags.Append(migrateDiags) 335 if migrateDiags.HasErrors() { 336 c.showDiagnostics(diags) 337 return 1 338 } 339 state = migratedState 340 } 341 342 // Now that we have loaded all modules, check the module tree for missing providers. 343 providersOutput, providersAbort, providerDiags := c.getProviders(ctx, config, state, flagUpgrade, flagPluginPath, flagLockfile) 344 diags = diags.Append(providerDiags) 345 if providersAbort || providerDiags.HasErrors() { 346 c.showDiagnostics(diags) 347 return 1 348 } 349 if providersOutput { 350 header = true 351 } 352 353 // If we outputted information, then we need to output a newline 354 // so that our success message is nicely spaced out from prior text. 355 if header { 356 c.Ui.Output("") 357 } 358 359 // If we accumulated any warnings along the way that weren't accompanied 360 // by errors then we'll output them here so that the success message is 361 // still the final thing shown. 362 c.showDiagnostics(diags) 363 _, cloud := back.(*cloud.Cloud) 364 output := outputInitSuccess 365 if cloud { 366 output = outputInitSuccessCloud 367 } 368 369 c.Ui.Output(c.Colorize().Color(strings.TrimSpace(output))) 370 371 if !c.RunningInAutomation { 372 // If we're not running in an automation wrapper, give the user 373 // some more detailed next steps that are appropriate for interactive 374 // shell usage. 375 output = outputInitSuccessCLI 376 if cloud { 377 output = outputInitSuccessCLICloud 378 } 379 c.Ui.Output(c.Colorize().Color(strings.TrimSpace(output))) 380 } 381 return 0 382 } 383 384 func (c *InitCommand) getModules(ctx context.Context, path, testsDir string, earlyRoot *configs.Module, upgrade bool) (output bool, abort bool, diags tfdiags.Diagnostics) { 385 testModules := false // We can also have modules buried in test files. 386 for _, file := range earlyRoot.Tests { 387 for _, run := range file.Runs { 388 if run.Module != nil { 389 testModules = true 390 } 391 } 392 } 393 394 if len(earlyRoot.ModuleCalls) == 0 && !testModules { 395 // Nothing to do 396 return false, false, nil 397 } 398 399 ctx, span := tracer.Start(ctx, "install modules", trace.WithAttributes( 400 attribute.Bool("upgrade", upgrade), 401 )) 402 defer span.End() 403 404 if upgrade { 405 c.Ui.Output(c.Colorize().Color("[reset][bold]Upgrading modules...")) 406 } else { 407 c.Ui.Output(c.Colorize().Color("[reset][bold]Initializing modules...")) 408 } 409 410 hooks := uiModuleInstallHooks{ 411 Ui: c.Ui, 412 ShowLocalPaths: true, 413 } 414 415 installAbort, installDiags := c.installModules(ctx, path, testsDir, upgrade, false, hooks) 416 diags = diags.Append(installDiags) 417 418 // At this point, installModules may have generated error diags or been 419 // aborted by SIGINT. In any case we continue and the manifest as best 420 // we can. 421 422 // Since module installer has modified the module manifest on disk, we need 423 // to refresh the cache of it in the loader. 424 if c.configLoader != nil { 425 if err := c.configLoader.RefreshModules(); err != nil { 426 // Should never happen 427 diags = diags.Append(tfdiags.Sourceless( 428 tfdiags.Error, 429 "Failed to read module manifest", 430 fmt.Sprintf("After installing modules, OpenTofu could not re-read the manifest of installed modules. This is a bug in OpenTofu. %s.", err), 431 )) 432 } 433 } 434 435 return true, installAbort, diags 436 } 437 438 func (c *InitCommand) initCloud(ctx context.Context, root *configs.Module, extraConfig rawFlags, enc encryption.Encryption) (be backend.Backend, output bool, diags tfdiags.Diagnostics) { 439 ctx, span := tracer.Start(ctx, "initialize cloud backend") 440 _ = ctx // prevent staticcheck from complaining to avoid a maintenence hazard of having the wrong ctx in scope here 441 defer span.End() 442 443 c.Ui.Output(c.Colorize().Color("\n[reset][bold]Initializing cloud backend...")) 444 445 if len(extraConfig.AllItems()) != 0 { 446 diags = diags.Append(tfdiags.Sourceless( 447 tfdiags.Error, 448 "Invalid command-line option", 449 "The -backend-config=... command line option is only for state backends, and is not applicable to cloud backend-based configurations.\n\nTo change the set of workspaces associated with this configuration, edit the Cloud configuration block in the root module.", 450 )) 451 return nil, true, diags 452 } 453 454 backendConfig := root.CloudConfig.ToBackendConfig() 455 456 opts := &BackendOpts{ 457 Config: &backendConfig, 458 Init: true, 459 } 460 461 back, backDiags := c.Backend(opts, enc.State()) 462 diags = diags.Append(backDiags) 463 return back, true, diags 464 } 465 466 func (c *InitCommand) initBackend(ctx context.Context, root *configs.Module, extraConfig rawFlags, enc encryption.Encryption) (be backend.Backend, output bool, diags tfdiags.Diagnostics) { 467 ctx, span := tracer.Start(ctx, "initialize backend") 468 _ = ctx // prevent staticcheck from complaining to avoid a maintenence hazard of having the wrong ctx in scope here 469 defer span.End() 470 471 c.Ui.Output(c.Colorize().Color("\n[reset][bold]Initializing the backend...")) 472 473 var backendConfig *configs.Backend 474 var backendConfigOverride hcl.Body 475 if root.Backend != nil { 476 backendType := root.Backend.Type 477 if backendType == "cloud" { 478 diags = diags.Append(&hcl.Diagnostic{ 479 Severity: hcl.DiagError, 480 Summary: "Unsupported backend type", 481 Detail: fmt.Sprintf("There is no explicit backend type named %q. To configure cloud backend, declare a 'cloud' block instead.", backendType), 482 Subject: &root.Backend.TypeRange, 483 }) 484 return nil, true, diags 485 } 486 487 bf := backendInit.Backend(backendType) 488 if bf == nil { 489 detail := fmt.Sprintf("There is no backend type named %q.", backendType) 490 if msg, removed := backendInit.RemovedBackends[backendType]; removed { 491 detail = msg 492 } 493 494 diags = diags.Append(&hcl.Diagnostic{ 495 Severity: hcl.DiagError, 496 Summary: "Unsupported backend type", 497 Detail: detail, 498 Subject: &root.Backend.TypeRange, 499 }) 500 return nil, true, diags 501 } 502 503 b := bf(nil) // This is only used to get the schema, encryption should panic if attempted 504 backendSchema := b.ConfigSchema() 505 backendConfig = root.Backend 506 507 var overrideDiags tfdiags.Diagnostics 508 backendConfigOverride, overrideDiags = c.backendConfigOverrideBody(extraConfig, backendSchema) 509 diags = diags.Append(overrideDiags) 510 if overrideDiags.HasErrors() { 511 return nil, true, diags 512 } 513 } else { 514 // If the user supplied a -backend-config on the CLI but no backend 515 // block was found in the configuration, it's likely - but not 516 // necessarily - a mistake. Return a warning. 517 if !extraConfig.Empty() { 518 diags = diags.Append(tfdiags.Sourceless( 519 tfdiags.Warning, 520 "Missing backend configuration", 521 `-backend-config was used without a "backend" block in the configuration. 522 523 If you intended to override the default local backend configuration, 524 no action is required, but you may add an explicit backend block to your 525 configuration to clear this warning: 526 527 terraform { 528 backend "local" {} 529 } 530 531 However, if you intended to override a defined backend, please verify that 532 the backend configuration is present and valid. 533 `, 534 )) 535 } 536 } 537 538 opts := &BackendOpts{ 539 Config: backendConfig, 540 ConfigOverride: backendConfigOverride, 541 Init: true, 542 } 543 544 back, backDiags := c.Backend(opts, enc.State()) 545 diags = diags.Append(backDiags) 546 return back, true, diags 547 } 548 549 // Load the complete module tree, and fetch any missing providers. 550 // This method outputs its own Ui. 551 func (c *InitCommand) getProviders(ctx context.Context, config *configs.Config, state *states.State, upgrade bool, pluginDirs []string, flagLockfile string) (output, abort bool, diags tfdiags.Diagnostics) { 552 ctx, span := tracer.Start(ctx, "install providers") 553 defer span.End() 554 555 // Dev overrides cause the result of "tofu init" to be irrelevant for 556 // any overridden providers, so we'll warn about it to avoid later 557 // confusion when OpenTofu ends up using a different provider than the 558 // lock file called for. 559 diags = diags.Append(c.providerDevOverrideInitWarnings()) 560 561 // First we'll collect all the provider dependencies we can see in the 562 // configuration and the state. 563 reqs, hclDiags := config.ProviderRequirements() 564 diags = diags.Append(hclDiags) 565 if hclDiags.HasErrors() { 566 return false, true, diags 567 } 568 if state != nil { 569 stateReqs := state.ProviderRequirements() 570 reqs = reqs.Merge(stateReqs) 571 } 572 573 potentialProviderConflicts := make(map[string][]string) 574 575 for providerAddr := range reqs { 576 if providerAddr.Namespace == "hashicorp" || providerAddr.Namespace == "opentofu" { 577 potentialProviderConflicts[providerAddr.Type] = append(potentialProviderConflicts[providerAddr.Type], providerAddr.ForDisplay()) 578 } 579 580 if providerAddr.IsLegacy() { 581 diags = diags.Append(tfdiags.Sourceless( 582 tfdiags.Error, 583 "Invalid legacy provider address", 584 fmt.Sprintf( 585 "This configuration or its associated state refers to the unqualified provider %q.\n\nYou must complete the Terraform 0.13 upgrade process before upgrading to later versions.", 586 providerAddr.Type, 587 ), 588 )) 589 } 590 } 591 592 for name, addrs := range potentialProviderConflicts { 593 if len(addrs) > 1 { 594 diags = diags.Append(tfdiags.Sourceless( 595 tfdiags.Warning, 596 "Potential provider misconfiguration", 597 fmt.Sprintf( 598 "OpenTofu has detected multiple providers of type %s (%s) which may be a misconfiguration.\n\nIf this is intentional you can ignore this warning", 599 name, 600 strings.Join(addrs, ", "), 601 ), 602 )) 603 } 604 } 605 606 previousLocks, moreDiags := c.lockedDependencies() 607 diags = diags.Append(moreDiags) 608 609 if diags.HasErrors() { 610 return false, true, diags 611 } 612 613 var inst *providercache.Installer 614 if len(pluginDirs) == 0 { 615 // By default we use a source that looks for providers in all of the 616 // standard locations, possibly customized by the user in CLI config. 617 inst = c.providerInstaller() 618 } else { 619 // If the user passes at least one -plugin-dir then that circumvents 620 // the usual sources and forces OpenTofu to consult only the given 621 // directories. Anything not available in one of those directories 622 // is not available for installation. 623 source := c.providerCustomLocalDirectorySource(pluginDirs) 624 inst = c.providerInstallerCustomSource(source) 625 626 // The default (or configured) search paths are logged earlier, in provider_source.go 627 // Log that those are being overridden by the `-plugin-dir` command line options 628 log.Println("[DEBUG] init: overriding provider plugin search paths") 629 log.Printf("[DEBUG] will search for provider plugins in %s", pluginDirs) 630 } 631 632 // We want to print out a nice warning if we don't manage to pull 633 // checksums for all our providers. This is tracked via callbacks 634 // and incomplete providers are stored here for later analysis. 635 var incompleteProviders []string 636 637 // Because we're currently just streaming a series of events sequentially 638 // into the terminal, we're showing only a subset of the events to keep 639 // things relatively concise. Later it'd be nice to have a progress UI 640 // where statuses update in-place, but we can't do that as long as we 641 // are shimming our vt100 output to the legacy console API on Windows. 642 evts := &providercache.InstallerEvents{ 643 PendingProviders: func(reqs map[addrs.Provider]getproviders.VersionConstraints) { 644 c.Ui.Output(c.Colorize().Color( 645 "\n[reset][bold]Initializing provider plugins...", 646 )) 647 }, 648 ProviderAlreadyInstalled: func(provider addrs.Provider, selectedVersion getproviders.Version) { 649 c.Ui.Info(fmt.Sprintf("- Using previously-installed %s v%s", provider.ForDisplay(), selectedVersion)) 650 }, 651 BuiltInProviderAvailable: func(provider addrs.Provider) { 652 c.Ui.Info(fmt.Sprintf("- %s is built in to OpenTofu", provider.ForDisplay())) 653 }, 654 BuiltInProviderFailure: func(provider addrs.Provider, err error) { 655 diags = diags.Append(tfdiags.Sourceless( 656 tfdiags.Error, 657 "Invalid dependency on built-in provider", 658 fmt.Sprintf("Cannot use %s: %s.", provider.ForDisplay(), err), 659 )) 660 }, 661 QueryPackagesBegin: func(provider addrs.Provider, versionConstraints getproviders.VersionConstraints, locked bool) { 662 if locked { 663 c.Ui.Info(fmt.Sprintf("- Reusing previous version of %s from the dependency lock file", provider.ForDisplay())) 664 } else { 665 if len(versionConstraints) > 0 { 666 c.Ui.Info(fmt.Sprintf("- Finding %s versions matching %q...", provider.ForDisplay(), getproviders.VersionConstraintsString(versionConstraints))) 667 } else { 668 c.Ui.Info(fmt.Sprintf("- Finding latest version of %s...", provider.ForDisplay())) 669 } 670 } 671 }, 672 LinkFromCacheBegin: func(provider addrs.Provider, version getproviders.Version, cacheRoot string) { 673 c.Ui.Info(fmt.Sprintf("- Using %s v%s from the shared cache directory", provider.ForDisplay(), version)) 674 }, 675 FetchPackageBegin: func(provider addrs.Provider, version getproviders.Version, location getproviders.PackageLocation) { 676 c.Ui.Info(fmt.Sprintf("- Installing %s v%s...", provider.ForDisplay(), version)) 677 }, 678 QueryPackagesFailure: func(provider addrs.Provider, err error) { 679 switch errorTy := err.(type) { 680 case getproviders.ErrProviderNotFound: 681 sources := errorTy.Sources 682 displaySources := make([]string, len(sources)) 683 for i, source := range sources { 684 displaySources[i] = fmt.Sprintf(" - %s", source) 685 } 686 diags = diags.Append(tfdiags.Sourceless( 687 tfdiags.Error, 688 "Failed to query available provider packages", 689 fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s\n\n%s", 690 provider.ForDisplay(), err, strings.Join(displaySources, "\n"), 691 ), 692 )) 693 case getproviders.ErrRegistryProviderNotKnown: 694 // We might be able to suggest an alternative provider to use 695 // instead of this one. 696 suggestion := fmt.Sprintf("\n\nAll modules should specify their required_providers so that external consumers will get the correct providers when using a module. To see which modules are currently depending on %s, run the following command:\n tofu providers", provider.ForDisplay()) 697 alternative := getproviders.MissingProviderSuggestion(ctx, provider, inst.ProviderSource(), reqs) 698 if alternative != provider { 699 suggestion = fmt.Sprintf( 700 "\n\nDid you intend to use %s? If so, you must specify that source address in each module which requires that provider. To see which modules are currently depending on %s, run the following command:\n tofu providers", 701 alternative.ForDisplay(), provider.ForDisplay(), 702 ) 703 } 704 705 if provider.Hostname == addrs.DefaultProviderRegistryHost { 706 suggestion += "\n\nIf you believe this provider is missing from the registry, please submit a issue on the OpenTofu Registry https://github.com/opentofu/registry/issues/" 707 } 708 709 diags = diags.Append(tfdiags.Sourceless( 710 tfdiags.Error, 711 "Failed to query available provider packages", 712 fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s%s", 713 provider.ForDisplay(), err, suggestion, 714 ), 715 )) 716 case getproviders.ErrHostNoProviders: 717 switch { 718 case errorTy.Hostname == svchost.Hostname("github.com") && !errorTy.HasOtherVersion: 719 // If a user copies the URL of a GitHub repository into 720 // the source argument and removes the schema to make it 721 // provider-address-shaped then that's one way we can end up 722 // here. We'll use a specialized error message in anticipation 723 // of that mistake. We only do this if github.com isn't a 724 // provider registry, to allow for the (admittedly currently 725 // rather unlikely) possibility that github.com starts being 726 // a real Terraform provider registry in the future. 727 diags = diags.Append(tfdiags.Sourceless( 728 tfdiags.Error, 729 "Invalid provider registry host", 730 fmt.Sprintf("The given source address %q specifies a GitHub repository rather than a OpenTofu provider. Refer to the documentation of the provider to find the correct source address to use.", 731 provider.String(), 732 ), 733 )) 734 735 case errorTy.HasOtherVersion: 736 diags = diags.Append(tfdiags.Sourceless( 737 tfdiags.Error, 738 "Invalid provider registry host", 739 fmt.Sprintf("The host %q given in provider source address %q does not offer a OpenTofu provider registry that is compatible with this OpenTofu version, but it may be compatible with a different OpenTofu version.", 740 errorTy.Hostname, provider.String(), 741 ), 742 )) 743 744 default: 745 diags = diags.Append(tfdiags.Sourceless( 746 tfdiags.Error, 747 "Invalid provider registry host", 748 fmt.Sprintf("The host %q given in provider source address %q does not offer a OpenTofu provider registry.", 749 errorTy.Hostname, provider.String(), 750 ), 751 )) 752 } 753 754 case getproviders.ErrRequestCanceled: 755 // We don't attribute cancellation to any particular operation, 756 // but rather just emit a single general message about it at 757 // the end, by checking ctx.Err(). 758 759 default: 760 diags = diags.Append(tfdiags.Sourceless( 761 tfdiags.Error, 762 "Failed to resolve provider packages", 763 fmt.Sprintf("Could not resolve provider %s: %s", 764 provider.ForDisplay(), err, 765 ), 766 )) 767 } 768 769 }, 770 QueryPackagesWarning: func(provider addrs.Provider, warnings []string) { 771 displayWarnings := make([]string, len(warnings)) 772 for i, warning := range warnings { 773 displayWarnings[i] = fmt.Sprintf("- %s", warning) 774 } 775 776 diags = diags.Append(tfdiags.Sourceless( 777 tfdiags.Warning, 778 "Additional provider information from registry", 779 fmt.Sprintf("The remote registry returned warnings for %s:\n%s", 780 provider.String(), 781 strings.Join(displayWarnings, "\n"), 782 ), 783 )) 784 }, 785 LinkFromCacheFailure: func(provider addrs.Provider, version getproviders.Version, err error) { 786 diags = diags.Append(tfdiags.Sourceless( 787 tfdiags.Error, 788 "Failed to install provider from shared cache", 789 fmt.Sprintf("Error while importing %s v%s from the shared cache directory: %s.", provider.ForDisplay(), version, err), 790 )) 791 }, 792 FetchPackageFailure: func(provider addrs.Provider, version getproviders.Version, err error) { 793 const summaryIncompatible = "Incompatible provider version" 794 switch err := err.(type) { 795 case getproviders.ErrProtocolNotSupported: 796 closestAvailable := err.Suggestion 797 switch { 798 case closestAvailable == getproviders.UnspecifiedVersion: 799 diags = diags.Append(tfdiags.Sourceless( 800 tfdiags.Error, 801 summaryIncompatible, 802 fmt.Sprintf(errProviderVersionIncompatible, provider.String()), 803 )) 804 case version.GreaterThan(closestAvailable): 805 diags = diags.Append(tfdiags.Sourceless( 806 tfdiags.Error, 807 summaryIncompatible, 808 fmt.Sprintf(providerProtocolTooNew, provider.ForDisplay(), 809 version, tfversion.String(), closestAvailable, closestAvailable, 810 getproviders.VersionConstraintsString(reqs[provider]), 811 ), 812 )) 813 default: // version is less than closestAvailable 814 diags = diags.Append(tfdiags.Sourceless( 815 tfdiags.Error, 816 summaryIncompatible, 817 fmt.Sprintf(providerProtocolTooOld, provider.ForDisplay(), 818 version, tfversion.String(), closestAvailable, closestAvailable, 819 getproviders.VersionConstraintsString(reqs[provider]), 820 ), 821 )) 822 } 823 case getproviders.ErrPlatformNotSupported: 824 switch { 825 case err.MirrorURL != nil: 826 // If we're installing from a mirror then it may just be 827 // the mirror lacking the package, rather than it being 828 // unavailable from upstream. 829 diags = diags.Append(tfdiags.Sourceless( 830 tfdiags.Error, 831 summaryIncompatible, 832 fmt.Sprintf( 833 "Your chosen provider mirror at %s does not have a %s v%s package available for your current platform, %s.\n\nProvider releases are separate from OpenTofu CLI releases, so this provider might not support your current platform. Alternatively, the mirror itself might have only a subset of the plugin packages available in the origin registry, at %s.", 834 err.MirrorURL, err.Provider, err.Version, err.Platform, 835 err.Provider.Hostname, 836 ), 837 )) 838 default: 839 diags = diags.Append(tfdiags.Sourceless( 840 tfdiags.Error, 841 summaryIncompatible, 842 fmt.Sprintf( 843 "Provider %s v%s does not have a package available for your current platform, %s.\n\nProvider releases are separate from OpenTofu CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.", 844 err.Provider, err.Version, err.Platform, 845 ), 846 )) 847 } 848 849 case getproviders.ErrRequestCanceled: 850 // We don't attribute cancellation to any particular operation, 851 // but rather just emit a single general message about it at 852 // the end, by checking ctx.Err(). 853 854 default: 855 // We can potentially end up in here under cancellation too, 856 // in spite of our getproviders.ErrRequestCanceled case above, 857 // because not all of the outgoing requests we do under the 858 // "fetch package" banner are source metadata requests. 859 // In that case we will emit a redundant error here about 860 // the request being cancelled, but we'll still detect it 861 // as a cancellation after the installer returns and do the 862 // normal cancellation handling. 863 864 diags = diags.Append(tfdiags.Sourceless( 865 tfdiags.Error, 866 "Failed to install provider", 867 fmt.Sprintf("Error while installing %s v%s: %s", provider.ForDisplay(), version, err), 868 )) 869 } 870 }, 871 FetchPackageSuccess: func(provider addrs.Provider, version getproviders.Version, localDir string, authResult *getproviders.PackageAuthenticationResult) { 872 var keyID string 873 if authResult != nil && authResult.Signed() { 874 keyID = authResult.KeyID 875 } 876 if keyID != "" { 877 keyID = c.Colorize().Color(fmt.Sprintf(", key ID [reset][bold]%s[reset]", keyID)) 878 } 879 880 if authResult != nil && authResult.SigningSkipped() { 881 c.Ui.Warn(fmt.Sprintf("- Installed %s v%s. Signature validation was skipped due to the registry not containing GPG keys for this provider", provider.ForDisplay(), version)) 882 } else { 883 c.Ui.Info(fmt.Sprintf("- Installed %s v%s (%s%s)", provider.ForDisplay(), version, authResult, keyID)) 884 } 885 }, 886 ProvidersLockUpdated: func(provider addrs.Provider, version getproviders.Version, localHashes []getproviders.Hash, signedHashes []getproviders.Hash, priorHashes []getproviders.Hash) { 887 // We're going to use this opportunity to track if we have any 888 // "incomplete" installs of providers. An incomplete install is 889 // when we are only going to write the local hashes into our lock 890 // file which means a `tofu init` command will fail in future 891 // when used on machines of a different architecture. 892 // 893 // We want to print a warning about this. 894 895 if len(signedHashes) > 0 { 896 // If we have any signedHashes hashes then we don't worry - as 897 // we know we retrieved all available hashes for this version 898 // anyway. 899 return 900 } 901 902 // If local hashes and prior hashes are exactly the same then 903 // it means we didn't record any signed hashes previously, and 904 // we know we're not adding any extra in now (because we already 905 // checked the signedHashes), so that's a problem. 906 // 907 // In the actual check here, if we have any priorHashes and those 908 // hashes are not the same as the local hashes then we're going to 909 // accept that this provider has been configured correctly. 910 if len(priorHashes) > 0 && !reflect.DeepEqual(localHashes, priorHashes) { 911 return 912 } 913 914 // Now, either signedHashes is empty, or priorHashes is exactly the 915 // same as our localHashes which means we never retrieved the 916 // signedHashes previously. 917 // 918 // Either way, this is bad. Let's complain/warn. 919 incompleteProviders = append(incompleteProviders, provider.ForDisplay()) 920 }, 921 ProvidersFetched: func(authResults map[addrs.Provider]*getproviders.PackageAuthenticationResult) { 922 thirdPartySigned := false 923 for _, authResult := range authResults { 924 if authResult.Signed() { 925 thirdPartySigned = true 926 break 927 } 928 } 929 if thirdPartySigned { 930 c.Ui.Info(fmt.Sprintf("\nProviders are signed by their developers.\n" + 931 "If you'd like to know more about provider signing, you can read about it here:\n" + 932 "https://opentofu.org/docs/cli/plugins/signing/")) 933 } 934 }, 935 } 936 ctx = evts.OnContext(ctx) 937 938 mode := providercache.InstallNewProvidersOnly 939 if upgrade { 940 if flagLockfile == "readonly" { 941 c.Ui.Error("The -upgrade flag conflicts with -lockfile=readonly.") 942 return true, true, diags 943 } 944 945 mode = providercache.InstallUpgrades 946 } 947 newLocks, err := inst.EnsureProviderVersions(ctx, previousLocks, reqs, mode) 948 if ctx.Err() == context.Canceled { 949 c.showDiagnostics(diags) 950 c.Ui.Error("Provider installation was canceled by an interrupt signal.") 951 return true, true, diags 952 } 953 if err != nil { 954 // The errors captured in "err" should be redundant with what we 955 // received via the InstallerEvents callbacks above, so we'll 956 // just return those as long as we have some. 957 if !diags.HasErrors() { 958 diags = diags.Append(err) 959 } 960 961 return true, true, diags 962 } 963 964 // If the provider dependencies have changed since the last run then we'll 965 // say a little about that in case the reader wasn't expecting a change. 966 // (When we later integrate module dependencies into the lock file we'll 967 // probably want to refactor this so that we produce one lock-file related 968 // message for all changes together, but this is here for now just because 969 // it's the smallest change relative to what came before it, which was 970 // a hidden JSON file specifically for tracking providers.) 971 if !newLocks.Equal(previousLocks) { 972 // if readonly mode 973 if flagLockfile == "readonly" { 974 // check if required provider dependences change 975 if !newLocks.EqualProviderAddress(previousLocks) { 976 diags = diags.Append(tfdiags.Sourceless( 977 tfdiags.Error, 978 `Provider dependency changes detected`, 979 `Changes to the required provider dependencies were detected, but the lock file is read-only. To use and record these requirements, run "tofu init" without the "-lockfile=readonly" flag.`, 980 )) 981 return true, true, diags 982 } 983 984 // suppress updating the file to record any new information it learned, 985 // such as a hash using a new scheme. 986 diags = diags.Append(tfdiags.Sourceless( 987 tfdiags.Warning, 988 `Provider lock file not updated`, 989 `Changes to the provider selections were detected, but not saved in the .terraform.lock.hcl file. To record these selections, run "tofu init" without the "-lockfile=readonly" flag.`, 990 )) 991 return true, false, diags 992 } 993 994 // Jump in here and add a warning if any of the providers are incomplete. 995 if len(incompleteProviders) > 0 { 996 // We don't really care about the order here, we just want the 997 // output to be deterministic. 998 sort.Slice(incompleteProviders, func(i, j int) bool { 999 return incompleteProviders[i] < incompleteProviders[j] 1000 }) 1001 diags = diags.Append(tfdiags.Sourceless( 1002 tfdiags.Warning, 1003 incompleteLockFileInformationHeader, 1004 fmt.Sprintf( 1005 incompleteLockFileInformationBody, 1006 strings.Join(incompleteProviders, "\n - "), 1007 getproviders.CurrentPlatform.String()))) 1008 } 1009 1010 if previousLocks.Empty() { 1011 // A change from empty to non-empty is special because it suggests 1012 // we're running "tofu init" for the first time against a 1013 // new configuration. In that case we'll take the opportunity to 1014 // say a little about what the dependency lock file is, for new 1015 // users or those who are upgrading from a previous Terraform 1016 // version that didn't have dependency lock files. 1017 c.Ui.Output(c.Colorize().Color(` 1018 OpenTofu has created a lock file [bold].terraform.lock.hcl[reset] to record the provider 1019 selections it made above. Include this file in your version control repository 1020 so that OpenTofu can guarantee to make the same selections by default when 1021 you run "tofu init" in the future.`)) 1022 } else { 1023 c.Ui.Output(c.Colorize().Color(` 1024 OpenTofu has made some changes to the provider dependency selections recorded 1025 in the .terraform.lock.hcl file. Review those changes and commit them to your 1026 version control system if they represent changes you intended to make.`)) 1027 } 1028 1029 moreDiags = c.replaceLockedDependencies(newLocks) 1030 diags = diags.Append(moreDiags) 1031 } 1032 1033 return true, false, diags 1034 } 1035 1036 // backendConfigOverrideBody interprets the raw values of -backend-config 1037 // arguments into a hcl Body that should override the backend settings given 1038 // in the configuration. 1039 // 1040 // If the result is nil then no override needs to be provided. 1041 // 1042 // If the returned diagnostics contains errors then the returned body may be 1043 // incomplete or invalid. 1044 func (c *InitCommand) backendConfigOverrideBody(flags rawFlags, schema *configschema.Block) (hcl.Body, tfdiags.Diagnostics) { 1045 items := flags.AllItems() 1046 if len(items) == 0 { 1047 return nil, nil 1048 } 1049 1050 var ret hcl.Body 1051 var diags tfdiags.Diagnostics 1052 synthVals := make(map[string]cty.Value) 1053 1054 mergeBody := func(newBody hcl.Body) { 1055 if ret == nil { 1056 ret = newBody 1057 } else { 1058 ret = configs.MergeBodies(ret, newBody) 1059 } 1060 } 1061 flushVals := func() { 1062 if len(synthVals) == 0 { 1063 return 1064 } 1065 newBody := configs.SynthBody("-backend-config=...", synthVals) 1066 mergeBody(newBody) 1067 synthVals = make(map[string]cty.Value) 1068 } 1069 1070 if len(items) == 1 && items[0].Value == "" { 1071 // Explicitly remove all -backend-config options. 1072 // We do this by setting an empty but non-nil ConfigOverrides. 1073 return configs.SynthBody("-backend-config=''", synthVals), diags 1074 } 1075 1076 for _, item := range items { 1077 eq := strings.Index(item.Value, "=") 1078 1079 if eq == -1 { 1080 // The value is interpreted as a filename. 1081 newBody, fileDiags := c.loadHCLFile(item.Value) 1082 diags = diags.Append(fileDiags) 1083 if fileDiags.HasErrors() { 1084 continue 1085 } 1086 // Generate an HCL body schema for the backend block. 1087 var bodySchema hcl.BodySchema 1088 for name := range schema.Attributes { 1089 // We intentionally ignore the `Required` attribute here 1090 // because backend config override files can be partial. The 1091 // goal is to make sure we're not loading a file with 1092 // extraneous attributes or blocks. 1093 bodySchema.Attributes = append(bodySchema.Attributes, hcl.AttributeSchema{ 1094 Name: name, 1095 }) 1096 } 1097 for name, block := range schema.BlockTypes { 1098 var labelNames []string 1099 if block.Nesting == configschema.NestingMap { 1100 labelNames = append(labelNames, "key") 1101 } 1102 bodySchema.Blocks = append(bodySchema.Blocks, hcl.BlockHeaderSchema{ 1103 Type: name, 1104 LabelNames: labelNames, 1105 }) 1106 } 1107 // Verify that the file body matches the expected backend schema. 1108 _, schemaDiags := newBody.Content(&bodySchema) 1109 diags = diags.Append(schemaDiags) 1110 if schemaDiags.HasErrors() { 1111 continue 1112 } 1113 flushVals() // deal with any accumulated individual values first 1114 mergeBody(newBody) 1115 } else { 1116 name := item.Value[:eq] 1117 rawValue := item.Value[eq+1:] 1118 attrS := schema.Attributes[name] 1119 if attrS == nil { 1120 diags = diags.Append(tfdiags.Sourceless( 1121 tfdiags.Error, 1122 "Invalid backend configuration argument", 1123 fmt.Sprintf("The backend configuration argument %q given on the command line is not expected for the selected backend type.", name), 1124 )) 1125 continue 1126 } 1127 value, valueDiags := configValueFromCLI(item.String(), rawValue, attrS.Type) 1128 diags = diags.Append(valueDiags) 1129 if valueDiags.HasErrors() { 1130 continue 1131 } 1132 synthVals[name] = value 1133 } 1134 } 1135 1136 flushVals() 1137 1138 return ret, diags 1139 } 1140 1141 func (c *InitCommand) AutocompleteArgs() complete.Predictor { 1142 return complete.PredictDirs("") 1143 } 1144 1145 func (c *InitCommand) AutocompleteFlags() complete.Flags { 1146 return complete.Flags{ 1147 "-backend": completePredictBoolean, 1148 "-cloud": completePredictBoolean, 1149 "-backend-config": complete.PredictFiles("*.tfvars"), // can also be key=value, but we can't "predict" that 1150 "-force-copy": complete.PredictNothing, 1151 "-from-module": completePredictModuleSource, 1152 "-get": completePredictBoolean, 1153 "-input": completePredictBoolean, 1154 "-lock": completePredictBoolean, 1155 "-lock-timeout": complete.PredictAnything, 1156 "-no-color": complete.PredictNothing, 1157 "-plugin-dir": complete.PredictDirs(""), 1158 "-reconfigure": complete.PredictNothing, 1159 "-migrate-state": complete.PredictNothing, 1160 "-upgrade": completePredictBoolean, 1161 } 1162 } 1163 1164 func (c *InitCommand) Help() string { 1165 helpText := ` 1166 Usage: tofu [global options] init [options] 1167 1168 Initialize a new or existing OpenTofu working directory by creating 1169 initial files, loading any remote state, downloading modules, etc. 1170 1171 This is the first command that should be run for any new or existing 1172 OpenTofu configuration per machine. This sets up all the local data 1173 necessary to run OpenTofu that is typically not committed to version 1174 control. 1175 1176 This command is always safe to run multiple times. Though subsequent runs 1177 may give errors, this command will never delete your configuration or 1178 state. Even so, if you have important information, please back it up prior 1179 to running this command, just in case. 1180 1181 Options: 1182 1183 -backend=false Disable backend or cloud backend initialization 1184 for this configuration and use what was previously 1185 initialized instead. 1186 1187 aliases: -cloud=false 1188 1189 -backend-config=path Configuration to be merged with what is in the 1190 configuration file's 'backend' block. This can be 1191 either a path to an HCL file with key/value 1192 assignments (same format as terraform.tfvars) or a 1193 'key=value' format, and can be specified multiple 1194 times. The backend type must be in the configuration 1195 itself. 1196 1197 -force-copy Suppress prompts about copying state data when 1198 initializating a new state backend. This is 1199 equivalent to providing a "yes" to all confirmation 1200 prompts. 1201 1202 -from-module=SOURCE Copy the contents of the given module into the target 1203 directory before initialization. 1204 1205 -get=false Disable downloading modules for this configuration. 1206 1207 -input=false Disable interactive prompts. Note that some actions may 1208 require interactive prompts and will error if input is 1209 disabled. 1210 1211 -lock=false Don't hold a state lock during backend migration. 1212 This is dangerous if others might concurrently run 1213 commands against the same workspace. 1214 1215 -lock-timeout=0s Duration to retry a state lock. 1216 1217 -no-color If specified, output won't contain any color. 1218 1219 -plugin-dir Directory containing plugin binaries. This overrides all 1220 default search paths for plugins, and prevents the 1221 automatic installation of plugins. This flag can be used 1222 multiple times. 1223 1224 -reconfigure Reconfigure a backend, ignoring any saved 1225 configuration. 1226 1227 -migrate-state Reconfigure a backend, and attempt to migrate any 1228 existing state. 1229 1230 -upgrade Install the latest module and provider versions 1231 allowed within configured constraints, overriding the 1232 default behavior of selecting exactly the version 1233 recorded in the dependency lockfile. 1234 1235 -lockfile=MODE Set a dependency lockfile mode. 1236 Currently only "readonly" is valid. 1237 1238 -ignore-remote-version A rare option used for cloud backend and the remote backend 1239 only. Set this to ignore checking that the local and remote 1240 OpenTofu versions use compatible state representations, making 1241 an operation proceed even when there is a potential mismatch. 1242 See the documentation on configuring OpenTofu with 1243 cloud backend for more information. 1244 1245 -test-directory=path Set the OpenTofu test directory, defaults to "tests". When set, the 1246 test command will search for test files in the current directory and 1247 in the one specified by the flag. 1248 1249 -json Produce output in a machine-readable JSON format, 1250 suitable for use in text editor integrations and other 1251 automated systems. Always disables color. 1252 1253 ` 1254 return strings.TrimSpace(helpText) 1255 } 1256 1257 func (c *InitCommand) Synopsis() string { 1258 return "Prepare your working directory for other commands" 1259 } 1260 1261 const errInitConfigError = ` 1262 [reset]OpenTofu encountered problems during initialization, including problems 1263 with the configuration, described below. 1264 1265 The OpenTofu configuration must be valid before initialization so that 1266 OpenTofu can determine which modules and providers need to be installed. 1267 ` 1268 1269 const errInitCopyNotEmpty = ` 1270 The working directory already contains files. The -from-module option requires 1271 an empty directory into which a copy of the referenced module will be placed. 1272 1273 To initialize the configuration already in this working directory, omit the 1274 -from-module option. 1275 ` 1276 1277 const outputInitEmpty = ` 1278 [reset][bold]OpenTofu initialized in an empty directory![reset] 1279 1280 The directory has no OpenTofu configuration files. You may begin working 1281 with OpenTofu immediately by creating OpenTofu configuration files. 1282 ` 1283 1284 const outputInitSuccess = ` 1285 [reset][bold][green]OpenTofu has been successfully initialized![reset][green] 1286 ` 1287 1288 const outputInitSuccessCloud = ` 1289 [reset][bold][green]Cloud backend has been successfully initialized![reset][green] 1290 ` 1291 1292 const outputInitSuccessCLI = `[reset][green] 1293 You may now begin working with OpenTofu. Try running "tofu plan" to see 1294 any changes that are required for your infrastructure. All OpenTofu commands 1295 should now work. 1296 1297 If you ever set or change modules or backend configuration for OpenTofu, 1298 rerun this command to reinitialize your working directory. If you forget, other 1299 commands will detect it and remind you to do so if necessary. 1300 ` 1301 1302 const outputInitSuccessCLICloud = `[reset][green] 1303 You may now begin working with cloud backend. Try running "tofu plan" to 1304 see any changes that are required for your infrastructure. 1305 1306 If you ever set or change modules or OpenTofu Settings, run "tofu init" 1307 again to reinitialize your working directory. 1308 ` 1309 1310 // providerProtocolTooOld is a message sent to the CLI UI if the provider's 1311 // supported protocol versions are too old for the user's version of tofu, 1312 // but a newer version of the provider is compatible. 1313 const providerProtocolTooOld = `Provider %q v%s is not compatible with OpenTofu %s. 1314 Provider version %s is the latest compatible version. Select it with the following version constraint: 1315 version = %q 1316 1317 OpenTofu checked all of the plugin versions matching the given constraint: 1318 %s 1319 1320 Consult the documentation for this provider for more information on compatibility between provider and OpenTofu versions. 1321 ` 1322 1323 // providerProtocolTooNew is a message sent to the CLI UI if the provider's 1324 // supported protocol versions are too new for the user's version of tofu, 1325 // and the user could either upgrade tofu or choose an older version of the 1326 // provider. 1327 const providerProtocolTooNew = `Provider %q v%s is not compatible with OpenTofu %s. 1328 You need to downgrade to v%s or earlier. Select it with the following constraint: 1329 version = %q 1330 1331 OpenTofu checked all of the plugin versions matching the given constraint: 1332 %s 1333 1334 Consult the documentation for this provider for more information on compatibility between provider and OpenTofu versions. 1335 Alternatively, upgrade to the latest version of OpenTofu for compatibility with newer provider releases. 1336 ` 1337 1338 // No version of the provider is compatible. 1339 const errProviderVersionIncompatible = `No compatible versions of provider %s were found.` 1340 1341 // incompleteLockFileInformationHeader is the summary displayed to users when 1342 // the lock file has only recorded local hashes. 1343 const incompleteLockFileInformationHeader = `Incomplete lock file information for providers` 1344 1345 // incompleteLockFileInformationBody is the body of text displayed to users when 1346 // the lock file has only recorded local hashes. 1347 const incompleteLockFileInformationBody = `Due to your customized provider installation methods, OpenTofu was forced to calculate lock file checksums locally for the following providers: 1348 - %s 1349 1350 The current .terraform.lock.hcl file only includes checksums for %s, so OpenTofu running on another platform will fail to install these providers. 1351 1352 To calculate additional checksums for another platform, run: 1353 tofu providers lock -platform=linux_amd64 1354 (where linux_amd64 is the platform to generate)`