1package command 2 3import ( 4 "context" 5 "fmt" 6 "log" 7 "strings" 8 9 "github.com/hashicorp/hcl/v2" 10 "github.com/hashicorp/terraform-config-inspect/tfconfig" 11 svchost "github.com/hashicorp/terraform-svchost" 12 "github.com/posener/complete" 13 "github.com/zclconf/go-cty/cty" 14 15 "github.com/hashicorp/terraform/internal/addrs" 16 "github.com/hashicorp/terraform/internal/backend" 17 backendInit "github.com/hashicorp/terraform/internal/backend/init" 18 "github.com/hashicorp/terraform/internal/configs" 19 "github.com/hashicorp/terraform/internal/configs/configschema" 20 "github.com/hashicorp/terraform/internal/getproviders" 21 "github.com/hashicorp/terraform/internal/providercache" 22 "github.com/hashicorp/terraform/internal/states" 23 "github.com/hashicorp/terraform/internal/terraform" 24 "github.com/hashicorp/terraform/internal/tfdiags" 25 tfversion "github.com/hashicorp/terraform/version" 26) 27 28// InitCommand is a Command implementation that takes a Terraform 29// module and clones it to the working directory. 30type InitCommand struct { 31 Meta 32} 33 34func (c *InitCommand) Run(args []string) int { 35 var flagFromModule, flagLockfile string 36 var flagBackend, flagGet, flagUpgrade bool 37 var flagPluginPath FlagStringSlice 38 flagConfigExtra := newRawFlags("-backend-config") 39 40 args = c.Meta.process(args) 41 cmdFlags := c.Meta.extendedFlagSet("init") 42 cmdFlags.BoolVar(&flagBackend, "backend", true, "") 43 cmdFlags.Var(flagConfigExtra, "backend-config", "") 44 cmdFlags.StringVar(&flagFromModule, "from-module", "", "copy the source of the given module into the directory before init") 45 cmdFlags.BoolVar(&flagGet, "get", true, "") 46 cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data") 47 cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") 48 cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") 49 cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure") 50 cmdFlags.BoolVar(&c.migrateState, "migrate-state", false, "migrate state") 51 cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "") 52 cmdFlags.Var(&flagPluginPath, "plugin-dir", "plugin directory") 53 cmdFlags.StringVar(&flagLockfile, "lockfile", "", "Set a dependency lockfile mode") 54 cmdFlags.BoolVar(&c.Meta.ignoreRemoteVersion, "ignore-remote-version", false, "continue even if remote and local Terraform versions are incompatible") 55 cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } 56 if err := cmdFlags.Parse(args); err != nil { 57 return 1 58 } 59 60 if c.migrateState && c.reconfigure { 61 c.Ui.Error("The -migrate-state and -reconfigure options are mutually-exclusive") 62 return 1 63 } 64 65 // Copying the state only happens during backend migration, so setting 66 // -force-copy implies -migrate-state 67 if c.forceInitCopy { 68 c.migrateState = true 69 } 70 71 var diags tfdiags.Diagnostics 72 73 if len(flagPluginPath) > 0 { 74 c.pluginPath = flagPluginPath 75 } 76 77 // Validate the arg count and get the working directory 78 args = cmdFlags.Args() 79 path, err := ModulePath(args) 80 if err != nil { 81 c.Ui.Error(err.Error()) 82 return 1 83 } 84 85 if err := c.storePluginPath(c.pluginPath); err != nil { 86 c.Ui.Error(fmt.Sprintf("Error saving -plugin-path values: %s", err)) 87 return 1 88 } 89 90 // This will track whether we outputted anything so that we know whether 91 // to output a newline before the success message 92 var header bool 93 94 if flagFromModule != "" { 95 src := flagFromModule 96 97 empty, err := configs.IsEmptyDir(path) 98 if err != nil { 99 c.Ui.Error(fmt.Sprintf("Error validating destination directory: %s", err)) 100 return 1 101 } 102 if !empty { 103 c.Ui.Error(strings.TrimSpace(errInitCopyNotEmpty)) 104 return 1 105 } 106 107 c.Ui.Output(c.Colorize().Color(fmt.Sprintf( 108 "[reset][bold]Copying configuration[reset] from %q...", src, 109 ))) 110 header = true 111 112 hooks := uiModuleInstallHooks{ 113 Ui: c.Ui, 114 ShowLocalPaths: false, // since they are in a weird location for init 115 } 116 117 initDiags := c.initDirFromModule(path, src, hooks) 118 diags = diags.Append(initDiags) 119 if initDiags.HasErrors() { 120 c.showDiagnostics(diags) 121 return 1 122 } 123 124 c.Ui.Output("") 125 } 126 127 // If our directory is empty, then we're done. We can't get or set up 128 // the backend with an empty directory. 129 empty, err := configs.IsEmptyDir(path) 130 if err != nil { 131 diags = diags.Append(fmt.Errorf("Error checking configuration: %s", err)) 132 c.showDiagnostics(diags) 133 return 1 134 } 135 if empty { 136 c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputInitEmpty))) 137 return 0 138 } 139 140 // For Terraform v0.12 we introduced a special loading mode where we would 141 // use the 0.11-syntax-compatible "earlyconfig" package as a heuristic to 142 // identify situations where it was likely that the user was trying to use 143 // 0.11-only syntax that the upgrade tool might help with. 144 // 145 // However, as the language has moved on that is no longer a suitable 146 // heuristic in Terraform 0.13 and later: other new additions to the 147 // language can cause the main loader to disagree with earlyconfig, which 148 // would lead us to give poor advice about how to respond. 149 // 150 // For that reason, we no longer use a different error message in that 151 // situation, but for now we still use both codepaths because some of our 152 // initialization functionality remains built around "earlyconfig" and 153 // so we need to still load the module via that mechanism anyway until we 154 // can do some more invasive refactoring here. 155 rootModEarly, earlyConfDiags := c.loadSingleModuleEarly(path) 156 // If _only_ the early loader encountered errors then that's unusual 157 // (it should generally be a superset of the normal loader) but we'll 158 // return those errors anyway since otherwise we'll probably get 159 // some weird behavior downstream. Errors from the early loader are 160 // generally not as high-quality since it has less context to work with. 161 if earlyConfDiags.HasErrors() { 162 c.Ui.Error(c.Colorize().Color(strings.TrimSpace(errInitConfigError))) 163 // Errors from the early loader are generally not as high-quality since 164 // it has less context to work with. 165 166 // TODO: It would be nice to check the version constraints in 167 // rootModEarly.RequiredCore and print out a hint if the module is 168 // declaring that it's not compatible with this version of Terraform, 169 // and that may be what caused earlyconfig to fail. 170 diags = diags.Append(earlyConfDiags) 171 c.showDiagnostics(diags) 172 return 1 173 } 174 175 if flagGet { 176 modsOutput, modsDiags := c.getModules(path, rootModEarly, flagUpgrade) 177 diags = diags.Append(modsDiags) 178 if modsDiags.HasErrors() { 179 c.showDiagnostics(diags) 180 return 1 181 } 182 if modsOutput { 183 header = true 184 } 185 } 186 187 // With all of the modules (hopefully) installed, we can now try to load the 188 // whole configuration tree. 189 config, confDiags := c.loadConfig(path) 190 // configDiags will be handled after the version constraint check, since an 191 // incorrect version of terraform may be producing errors for configuration 192 // constructs added in later versions. 193 194 // Before we go further, we'll check to make sure none of the modules in 195 // the configuration declare that they don't support this Terraform 196 // version, so we can produce a version-related error message rather than 197 // potentially-confusing downstream errors. 198 versionDiags := terraform.CheckCoreVersionRequirements(config) 199 if versionDiags.HasErrors() { 200 c.showDiagnostics(versionDiags) 201 return 1 202 } 203 204 diags = diags.Append(confDiags) 205 if confDiags.HasErrors() { 206 c.Ui.Error(strings.TrimSpace(errInitConfigError)) 207 c.showDiagnostics(diags) 208 return 1 209 } 210 211 var back backend.Backend 212 if flagBackend { 213 214 be, backendOutput, backendDiags := c.initBackend(config.Module, flagConfigExtra) 215 diags = diags.Append(backendDiags) 216 if backendDiags.HasErrors() { 217 c.showDiagnostics(diags) 218 return 1 219 } 220 if backendOutput { 221 header = true 222 } 223 back = be 224 } else { 225 // load the previously-stored backend config 226 be, backendDiags := c.Meta.backendFromState() 227 diags = diags.Append(backendDiags) 228 if backendDiags.HasErrors() { 229 c.showDiagnostics(diags) 230 return 1 231 } 232 back = be 233 } 234 235 if back == nil { 236 // If we didn't initialize a backend then we'll try to at least 237 // instantiate one. This might fail if it wasn't already initialized 238 // by a previous run, so we must still expect that "back" may be nil 239 // in code that follows. 240 var backDiags tfdiags.Diagnostics 241 back, backDiags = c.Backend(nil) 242 if backDiags.HasErrors() { 243 // This is fine. We'll proceed with no backend, then. 244 back = nil 245 } 246 } 247 248 var state *states.State 249 250 // If we have a functional backend (either just initialized or initialized 251 // on a previous run) we'll use the current state as a potential source 252 // of provider dependencies. 253 if back != nil { 254 c.ignoreRemoteBackendVersionConflict(back) 255 workspace, err := c.Workspace() 256 if err != nil { 257 c.Ui.Error(fmt.Sprintf("Error selecting workspace: %s", err)) 258 return 1 259 } 260 sMgr, err := back.StateMgr(workspace) 261 if err != nil { 262 c.Ui.Error(fmt.Sprintf("Error loading state: %s", err)) 263 return 1 264 } 265 266 if err := sMgr.RefreshState(); err != nil { 267 c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) 268 return 1 269 } 270 271 state = sMgr.State() 272 } 273 274 // Now that we have loaded all modules, check the module tree for missing providers. 275 providersOutput, providersAbort, providerDiags := c.getProviders(config, state, flagUpgrade, flagPluginPath, flagLockfile) 276 diags = diags.Append(providerDiags) 277 if providersAbort || providerDiags.HasErrors() { 278 c.showDiagnostics(diags) 279 return 1 280 } 281 if providersOutput { 282 header = true 283 } 284 285 // If we outputted information, then we need to output a newline 286 // so that our success message is nicely spaced out from prior text. 287 if header { 288 c.Ui.Output("") 289 } 290 291 // If we accumulated any warnings along the way that weren't accompanied 292 // by errors then we'll output them here so that the success message is 293 // still the final thing shown. 294 c.showDiagnostics(diags) 295 c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputInitSuccess))) 296 if !c.RunningInAutomation { 297 // If we're not running in an automation wrapper, give the user 298 // some more detailed next steps that are appropriate for interactive 299 // shell usage. 300 c.Ui.Output(c.Colorize().Color(strings.TrimSpace(outputInitSuccessCLI))) 301 } 302 return 0 303} 304 305func (c *InitCommand) getModules(path string, earlyRoot *tfconfig.Module, upgrade bool) (output bool, diags tfdiags.Diagnostics) { 306 if len(earlyRoot.ModuleCalls) == 0 { 307 // Nothing to do 308 return false, nil 309 } 310 311 if upgrade { 312 c.Ui.Output(c.Colorize().Color("[reset][bold]Upgrading modules...")) 313 } else { 314 c.Ui.Output(c.Colorize().Color("[reset][bold]Initializing modules...")) 315 } 316 317 hooks := uiModuleInstallHooks{ 318 Ui: c.Ui, 319 ShowLocalPaths: true, 320 } 321 instDiags := c.installModules(path, upgrade, hooks) 322 diags = diags.Append(instDiags) 323 324 // Since module installer has modified the module manifest on disk, we need 325 // to refresh the cache of it in the loader. 326 if c.configLoader != nil { 327 if err := c.configLoader.RefreshModules(); err != nil { 328 // Should never happen 329 diags = diags.Append(tfdiags.Sourceless( 330 tfdiags.Error, 331 "Failed to read module manifest", 332 fmt.Sprintf("After installing modules, Terraform could not re-read the manifest of installed modules. This is a bug in Terraform. %s.", err), 333 )) 334 } 335 } 336 337 return true, diags 338} 339 340func (c *InitCommand) initBackend(root *configs.Module, extraConfig rawFlags) (be backend.Backend, output bool, diags tfdiags.Diagnostics) { 341 c.Ui.Output(c.Colorize().Color("\n[reset][bold]Initializing the backend...")) 342 343 var backendConfig *configs.Backend 344 var backendConfigOverride hcl.Body 345 if root.Backend != nil { 346 backendType := root.Backend.Type 347 bf := backendInit.Backend(backendType) 348 if bf == nil { 349 diags = diags.Append(&hcl.Diagnostic{ 350 Severity: hcl.DiagError, 351 Summary: "Unsupported backend type", 352 Detail: fmt.Sprintf("There is no backend type named %q.", backendType), 353 Subject: &root.Backend.TypeRange, 354 }) 355 return nil, true, diags 356 } 357 358 b := bf() 359 backendSchema := b.ConfigSchema() 360 backendConfig = root.Backend 361 362 var overrideDiags tfdiags.Diagnostics 363 backendConfigOverride, overrideDiags = c.backendConfigOverrideBody(extraConfig, backendSchema) 364 diags = diags.Append(overrideDiags) 365 if overrideDiags.HasErrors() { 366 return nil, true, diags 367 } 368 } else { 369 // If the user supplied a -backend-config on the CLI but no backend 370 // block was found in the configuration, it's likely - but not 371 // necessarily - a mistake. Return a warning. 372 if !extraConfig.Empty() { 373 diags = diags.Append(tfdiags.Sourceless( 374 tfdiags.Warning, 375 "Missing backend configuration", 376 `-backend-config was used without a "backend" block in the configuration. 377 378If you intended to override the default local backend configuration, 379no action is required, but you may add an explicit backend block to your 380configuration to clear this warning: 381 382terraform { 383 backend "local" {} 384} 385 386However, if you intended to override a defined backend, please verify that 387the backend configuration is present and valid. 388`, 389 )) 390 } 391 } 392 393 opts := &BackendOpts{ 394 Config: backendConfig, 395 ConfigOverride: backendConfigOverride, 396 Init: true, 397 } 398 399 back, backDiags := c.Backend(opts) 400 diags = diags.Append(backDiags) 401 return back, true, diags 402} 403 404// Load the complete module tree, and fetch any missing providers. 405// This method outputs its own Ui. 406func (c *InitCommand) getProviders(config *configs.Config, state *states.State, upgrade bool, pluginDirs []string, flagLockfile string) (output, abort bool, diags tfdiags.Diagnostics) { 407 // Dev overrides cause the result of "terraform init" to be irrelevant for 408 // any overridden providers, so we'll warn about it to avoid later 409 // confusion when Terraform ends up using a different provider than the 410 // lock file called for. 411 diags = diags.Append(c.providerDevOverrideInitWarnings()) 412 413 // First we'll collect all the provider dependencies we can see in the 414 // configuration and the state. 415 reqs, hclDiags := config.ProviderRequirements() 416 diags = diags.Append(hclDiags) 417 if hclDiags.HasErrors() { 418 return false, true, diags 419 } 420 if state != nil { 421 stateReqs := state.ProviderRequirements() 422 reqs = reqs.Merge(stateReqs) 423 } 424 425 for providerAddr := range reqs { 426 if providerAddr.IsLegacy() { 427 diags = diags.Append(tfdiags.Sourceless( 428 tfdiags.Error, 429 "Invalid legacy provider address", 430 fmt.Sprintf( 431 "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.", 432 providerAddr.Type, 433 ), 434 )) 435 } 436 } 437 438 previousLocks, moreDiags := c.lockedDependencies() 439 diags = diags.Append(moreDiags) 440 441 if diags.HasErrors() { 442 return false, true, diags 443 } 444 445 var inst *providercache.Installer 446 if len(pluginDirs) == 0 { 447 // By default we use a source that looks for providers in all of the 448 // standard locations, possibly customized by the user in CLI config. 449 inst = c.providerInstaller() 450 } else { 451 // If the user passes at least one -plugin-dir then that circumvents 452 // the usual sources and forces Terraform to consult only the given 453 // directories. Anything not available in one of those directories 454 // is not available for installation. 455 source := c.providerCustomLocalDirectorySource(pluginDirs) 456 inst = c.providerInstallerCustomSource(source) 457 458 // The default (or configured) search paths are logged earlier, in provider_source.go 459 // Log that those are being overridden by the `-plugin-dir` command line options 460 log.Println("[DEBUG] init: overriding provider plugin search paths") 461 log.Printf("[DEBUG] will search for provider plugins in %s", pluginDirs) 462 } 463 464 // Installation can be aborted by interruption signals 465 ctx, done := c.InterruptibleContext() 466 defer done() 467 468 // Because we're currently just streaming a series of events sequentially 469 // into the terminal, we're showing only a subset of the events to keep 470 // things relatively concise. Later it'd be nice to have a progress UI 471 // where statuses update in-place, but we can't do that as long as we 472 // are shimming our vt100 output to the legacy console API on Windows. 473 evts := &providercache.InstallerEvents{ 474 PendingProviders: func(reqs map[addrs.Provider]getproviders.VersionConstraints) { 475 c.Ui.Output(c.Colorize().Color( 476 "\n[reset][bold]Initializing provider plugins...", 477 )) 478 }, 479 ProviderAlreadyInstalled: func(provider addrs.Provider, selectedVersion getproviders.Version) { 480 c.Ui.Info(fmt.Sprintf("- Using previously-installed %s v%s", provider.ForDisplay(), selectedVersion)) 481 }, 482 BuiltInProviderAvailable: func(provider addrs.Provider) { 483 c.Ui.Info(fmt.Sprintf("- %s is built in to Terraform", provider.ForDisplay())) 484 }, 485 BuiltInProviderFailure: func(provider addrs.Provider, err error) { 486 diags = diags.Append(tfdiags.Sourceless( 487 tfdiags.Error, 488 "Invalid dependency on built-in provider", 489 fmt.Sprintf("Cannot use %s: %s.", provider.ForDisplay(), err), 490 )) 491 }, 492 QueryPackagesBegin: func(provider addrs.Provider, versionConstraints getproviders.VersionConstraints, locked bool) { 493 if locked { 494 c.Ui.Info(fmt.Sprintf("- Reusing previous version of %s from the dependency lock file", provider.ForDisplay())) 495 } else { 496 if len(versionConstraints) > 0 { 497 c.Ui.Info(fmt.Sprintf("- Finding %s versions matching %q...", provider.ForDisplay(), getproviders.VersionConstraintsString(versionConstraints))) 498 } else { 499 c.Ui.Info(fmt.Sprintf("- Finding latest version of %s...", provider.ForDisplay())) 500 } 501 } 502 }, 503 LinkFromCacheBegin: func(provider addrs.Provider, version getproviders.Version, cacheRoot string) { 504 c.Ui.Info(fmt.Sprintf("- Using %s v%s from the shared cache directory", provider.ForDisplay(), version)) 505 }, 506 FetchPackageBegin: func(provider addrs.Provider, version getproviders.Version, location getproviders.PackageLocation) { 507 c.Ui.Info(fmt.Sprintf("- Installing %s v%s...", provider.ForDisplay(), version)) 508 }, 509 QueryPackagesFailure: func(provider addrs.Provider, err error) { 510 switch errorTy := err.(type) { 511 case getproviders.ErrProviderNotFound: 512 sources := errorTy.Sources 513 displaySources := make([]string, len(sources)) 514 for i, source := range sources { 515 displaySources[i] = fmt.Sprintf(" - %s", source) 516 } 517 diags = diags.Append(tfdiags.Sourceless( 518 tfdiags.Error, 519 "Failed to query available provider packages", 520 fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s\n\n%s", 521 provider.ForDisplay(), err, strings.Join(displaySources, "\n"), 522 ), 523 )) 524 case getproviders.ErrRegistryProviderNotKnown: 525 // We might be able to suggest an alternative provider to use 526 // instead of this one. 527 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 terraform providers", provider.ForDisplay()) 528 alternative := getproviders.MissingProviderSuggestion(ctx, provider, inst.ProviderSource(), reqs) 529 if alternative != provider { 530 suggestion = fmt.Sprintf( 531 "\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 terraform providers", 532 alternative.ForDisplay(), provider.ForDisplay(), 533 ) 534 } 535 536 diags = diags.Append(tfdiags.Sourceless( 537 tfdiags.Error, 538 "Failed to query available provider packages", 539 fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s%s", 540 provider.ForDisplay(), err, suggestion, 541 ), 542 )) 543 case getproviders.ErrHostNoProviders: 544 switch { 545 case errorTy.Hostname == svchost.Hostname("github.com") && !errorTy.HasOtherVersion: 546 // If a user copies the URL of a GitHub repository into 547 // the source argument and removes the schema to make it 548 // provider-address-shaped then that's one way we can end up 549 // here. We'll use a specialized error message in anticipation 550 // of that mistake. We only do this if github.com isn't a 551 // provider registry, to allow for the (admittedly currently 552 // rather unlikely) possibility that github.com starts being 553 // a real Terraform provider registry in the future. 554 diags = diags.Append(tfdiags.Sourceless( 555 tfdiags.Error, 556 "Invalid provider registry host", 557 fmt.Sprintf("The given source address %q specifies a GitHub repository rather than a Terraform provider. Refer to the documentation of the provider to find the correct source address to use.", 558 provider.String(), 559 ), 560 )) 561 562 case errorTy.HasOtherVersion: 563 diags = diags.Append(tfdiags.Sourceless( 564 tfdiags.Error, 565 "Invalid provider registry host", 566 fmt.Sprintf("The host %q given in in provider source address %q does not offer a Terraform provider registry that is compatible with this Terraform version, but it may be compatible with a different Terraform version.", 567 errorTy.Hostname, provider.String(), 568 ), 569 )) 570 571 default: 572 diags = diags.Append(tfdiags.Sourceless( 573 tfdiags.Error, 574 "Invalid provider registry host", 575 fmt.Sprintf("The host %q given in in provider source address %q does not offer a Terraform provider registry.", 576 errorTy.Hostname, provider.String(), 577 ), 578 )) 579 } 580 581 case getproviders.ErrRequestCanceled: 582 // We don't attribute cancellation to any particular operation, 583 // but rather just emit a single general message about it at 584 // the end, by checking ctx.Err(). 585 586 default: 587 diags = diags.Append(tfdiags.Sourceless( 588 tfdiags.Error, 589 "Failed to query available provider packages", 590 fmt.Sprintf("Could not retrieve the list of available versions for provider %s: %s", 591 provider.ForDisplay(), err, 592 ), 593 )) 594 } 595 596 }, 597 QueryPackagesWarning: func(provider addrs.Provider, warnings []string) { 598 displayWarnings := make([]string, len(warnings)) 599 for i, warning := range warnings { 600 displayWarnings[i] = fmt.Sprintf("- %s", warning) 601 } 602 603 diags = diags.Append(tfdiags.Sourceless( 604 tfdiags.Warning, 605 "Additional provider information from registry", 606 fmt.Sprintf("The remote registry returned warnings for %s:\n%s", 607 provider.String(), 608 strings.Join(displayWarnings, "\n"), 609 ), 610 )) 611 }, 612 LinkFromCacheFailure: func(provider addrs.Provider, version getproviders.Version, err error) { 613 diags = diags.Append(tfdiags.Sourceless( 614 tfdiags.Error, 615 "Failed to install provider from shared cache", 616 fmt.Sprintf("Error while importing %s v%s from the shared cache directory: %s.", provider.ForDisplay(), version, err), 617 )) 618 }, 619 FetchPackageFailure: func(provider addrs.Provider, version getproviders.Version, err error) { 620 const summaryIncompatible = "Incompatible provider version" 621 switch err := err.(type) { 622 case getproviders.ErrProtocolNotSupported: 623 closestAvailable := err.Suggestion 624 switch { 625 case closestAvailable == getproviders.UnspecifiedVersion: 626 diags = diags.Append(tfdiags.Sourceless( 627 tfdiags.Error, 628 summaryIncompatible, 629 fmt.Sprintf(errProviderVersionIncompatible, provider.String()), 630 )) 631 case version.GreaterThan(closestAvailable): 632 diags = diags.Append(tfdiags.Sourceless( 633 tfdiags.Error, 634 summaryIncompatible, 635 fmt.Sprintf(providerProtocolTooNew, provider.ForDisplay(), 636 version, tfversion.String(), closestAvailable, closestAvailable, 637 getproviders.VersionConstraintsString(reqs[provider]), 638 ), 639 )) 640 default: // version is less than closestAvailable 641 diags = diags.Append(tfdiags.Sourceless( 642 tfdiags.Error, 643 summaryIncompatible, 644 fmt.Sprintf(providerProtocolTooOld, provider.ForDisplay(), 645 version, tfversion.String(), closestAvailable, closestAvailable, 646 getproviders.VersionConstraintsString(reqs[provider]), 647 ), 648 )) 649 } 650 case getproviders.ErrPlatformNotSupported: 651 switch { 652 case err.MirrorURL != nil: 653 // If we're installing from a mirror then it may just be 654 // the mirror lacking the package, rather than it being 655 // unavailable from upstream. 656 diags = diags.Append(tfdiags.Sourceless( 657 tfdiags.Error, 658 summaryIncompatible, 659 fmt.Sprintf( 660 "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 Terraform 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.", 661 err.MirrorURL, err.Provider, err.Version, err.Platform, 662 err.Provider.Hostname, 663 ), 664 )) 665 default: 666 diags = diags.Append(tfdiags.Sourceless( 667 tfdiags.Error, 668 summaryIncompatible, 669 fmt.Sprintf( 670 "Provider %s v%s does not have a package available for your current platform, %s.\n\nProvider releases are separate from Terraform CLI releases, so not all providers are available for all platforms. Other versions of this provider may have different platforms supported.", 671 err.Provider, err.Version, err.Platform, 672 ), 673 )) 674 } 675 676 case getproviders.ErrRequestCanceled: 677 // We don't attribute cancellation to any particular operation, 678 // but rather just emit a single general message about it at 679 // the end, by checking ctx.Err(). 680 681 default: 682 // We can potentially end up in here under cancellation too, 683 // in spite of our getproviders.ErrRequestCanceled case above, 684 // because not all of the outgoing requests we do under the 685 // "fetch package" banner are source metadata requests. 686 // In that case we will emit a redundant error here about 687 // the request being cancelled, but we'll still detect it 688 // as a cancellation after the installer returns and do the 689 // normal cancellation handling. 690 691 diags = diags.Append(tfdiags.Sourceless( 692 tfdiags.Error, 693 "Failed to install provider", 694 fmt.Sprintf("Error while installing %s v%s: %s", provider.ForDisplay(), version, err), 695 )) 696 } 697 }, 698 FetchPackageSuccess: func(provider addrs.Provider, version getproviders.Version, localDir string, authResult *getproviders.PackageAuthenticationResult) { 699 var keyID string 700 if authResult != nil && authResult.ThirdPartySigned() { 701 keyID = authResult.KeyID 702 } 703 if keyID != "" { 704 keyID = c.Colorize().Color(fmt.Sprintf(", key ID [reset][bold]%s[reset]", keyID)) 705 } 706 707 c.Ui.Info(fmt.Sprintf("- Installed %s v%s (%s%s)", provider.ForDisplay(), version, authResult, keyID)) 708 }, 709 ProvidersFetched: func(authResults map[addrs.Provider]*getproviders.PackageAuthenticationResult) { 710 thirdPartySigned := false 711 for _, authResult := range authResults { 712 if authResult.ThirdPartySigned() { 713 thirdPartySigned = true 714 break 715 } 716 } 717 if thirdPartySigned { 718 c.Ui.Info(fmt.Sprintf("\nPartner and community providers are signed by their developers.\n" + 719 "If you'd like to know more about provider signing, you can read about it here:\n" + 720 "https://www.terraform.io/docs/cli/plugins/signing.html")) 721 } 722 }, 723 HashPackageFailure: func(provider addrs.Provider, version getproviders.Version, err error) { 724 diags = diags.Append(tfdiags.Sourceless( 725 tfdiags.Error, 726 "Failed to validate installed provider", 727 fmt.Sprintf( 728 "Validating provider %s v%s failed: %s", 729 provider.ForDisplay(), 730 version, 731 err, 732 ), 733 )) 734 }, 735 } 736 ctx = evts.OnContext(ctx) 737 738 mode := providercache.InstallNewProvidersOnly 739 if upgrade { 740 if flagLockfile == "readonly" { 741 c.Ui.Error("The -upgrade flag conflicts with -lockfile=readonly.") 742 return true, true, diags 743 } 744 745 mode = providercache.InstallUpgrades 746 } 747 newLocks, err := inst.EnsureProviderVersions(ctx, previousLocks, reqs, mode) 748 if ctx.Err() == context.Canceled { 749 c.showDiagnostics(diags) 750 c.Ui.Error("Provider installation was canceled by an interrupt signal.") 751 return true, true, diags 752 } 753 if err != nil { 754 // The errors captured in "err" should be redundant with what we 755 // received via the InstallerEvents callbacks above, so we'll 756 // just return those as long as we have some. 757 if !diags.HasErrors() { 758 diags = diags.Append(err) 759 } 760 761 return true, true, diags 762 } 763 764 // If the provider dependencies have changed since the last run then we'll 765 // say a little about that in case the reader wasn't expecting a change. 766 // (When we later integrate module dependencies into the lock file we'll 767 // probably want to refactor this so that we produce one lock-file related 768 // message for all changes together, but this is here for now just because 769 // it's the smallest change relative to what came before it, which was 770 // a hidden JSON file specifically for tracking providers.) 771 if !newLocks.Equal(previousLocks) { 772 // if readonly mode 773 if flagLockfile == "readonly" { 774 // check if required provider dependences change 775 if !newLocks.EqualProviderAddress(previousLocks) { 776 diags = diags.Append(tfdiags.Sourceless( 777 tfdiags.Error, 778 `Provider dependency changes detected`, 779 `Changes to the required provider dependencies were detected, but the lock file is read-only. To use and record these requirements, run "terraform init" without the "-lockfile=readonly" flag.`, 780 )) 781 return true, true, diags 782 } 783 784 // suppress updating the file to record any new information it learned, 785 // such as a hash using a new scheme. 786 diags = diags.Append(tfdiags.Sourceless( 787 tfdiags.Warning, 788 `Provider lock file not updated`, 789 `Changes to the provider selections were detected, but not saved in the .terraform.lock.hcl file. To record these selections, run "terraform init" without the "-lockfile=readonly" flag.`, 790 )) 791 return true, false, diags 792 } 793 794 if previousLocks.Empty() { 795 // A change from empty to non-empty is special because it suggests 796 // we're running "terraform init" for the first time against a 797 // new configuration. In that case we'll take the opportunity to 798 // say a little about what the dependency lock file is, for new 799 // users or those who are upgrading from a previous Terraform 800 // version that didn't have dependency lock files. 801 c.Ui.Output(c.Colorize().Color(` 802Terraform has created a lock file [bold].terraform.lock.hcl[reset] to record the provider 803selections it made above. Include this file in your version control repository 804so that Terraform can guarantee to make the same selections by default when 805you run "terraform init" in the future.`)) 806 } else { 807 c.Ui.Output(c.Colorize().Color(` 808Terraform has made some changes to the provider dependency selections recorded 809in the .terraform.lock.hcl file. Review those changes and commit them to your 810version control system if they represent changes you intended to make.`)) 811 } 812 813 moreDiags = c.replaceLockedDependencies(newLocks) 814 diags = diags.Append(moreDiags) 815 } 816 817 return true, false, diags 818} 819 820// backendConfigOverrideBody interprets the raw values of -backend-config 821// arguments into a hcl Body that should override the backend settings given 822// in the configuration. 823// 824// If the result is nil then no override needs to be provided. 825// 826// If the returned diagnostics contains errors then the returned body may be 827// incomplete or invalid. 828func (c *InitCommand) backendConfigOverrideBody(flags rawFlags, schema *configschema.Block) (hcl.Body, tfdiags.Diagnostics) { 829 items := flags.AllItems() 830 if len(items) == 0 { 831 return nil, nil 832 } 833 834 var ret hcl.Body 835 var diags tfdiags.Diagnostics 836 synthVals := make(map[string]cty.Value) 837 838 mergeBody := func(newBody hcl.Body) { 839 if ret == nil { 840 ret = newBody 841 } else { 842 ret = configs.MergeBodies(ret, newBody) 843 } 844 } 845 flushVals := func() { 846 if len(synthVals) == 0 { 847 return 848 } 849 newBody := configs.SynthBody("-backend-config=...", synthVals) 850 mergeBody(newBody) 851 synthVals = make(map[string]cty.Value) 852 } 853 854 if len(items) == 1 && items[0].Value == "" { 855 // Explicitly remove all -backend-config options. 856 // We do this by setting an empty but non-nil ConfigOverrides. 857 return configs.SynthBody("-backend-config=''", synthVals), diags 858 } 859 860 for _, item := range items { 861 eq := strings.Index(item.Value, "=") 862 863 if eq == -1 { 864 // The value is interpreted as a filename. 865 newBody, fileDiags := c.loadHCLFile(item.Value) 866 diags = diags.Append(fileDiags) 867 if fileDiags.HasErrors() { 868 continue 869 } 870 // Generate an HCL body schema for the backend block. 871 var bodySchema hcl.BodySchema 872 for name := range schema.Attributes { 873 // We intentionally ignore the `Required` attribute here 874 // because backend config override files can be partial. The 875 // goal is to make sure we're not loading a file with 876 // extraneous attributes or blocks. 877 bodySchema.Attributes = append(bodySchema.Attributes, hcl.AttributeSchema{ 878 Name: name, 879 }) 880 } 881 for name, block := range schema.BlockTypes { 882 var labelNames []string 883 if block.Nesting == configschema.NestingMap { 884 labelNames = append(labelNames, "key") 885 } 886 bodySchema.Blocks = append(bodySchema.Blocks, hcl.BlockHeaderSchema{ 887 Type: name, 888 LabelNames: labelNames, 889 }) 890 } 891 // Verify that the file body matches the expected backend schema. 892 _, schemaDiags := newBody.Content(&bodySchema) 893 diags = diags.Append(schemaDiags) 894 if schemaDiags.HasErrors() { 895 continue 896 } 897 flushVals() // deal with any accumulated individual values first 898 mergeBody(newBody) 899 } else { 900 name := item.Value[:eq] 901 rawValue := item.Value[eq+1:] 902 attrS := schema.Attributes[name] 903 if attrS == nil { 904 diags = diags.Append(tfdiags.Sourceless( 905 tfdiags.Error, 906 "Invalid backend configuration argument", 907 fmt.Sprintf("The backend configuration argument %q given on the command line is not expected for the selected backend type.", name), 908 )) 909 continue 910 } 911 value, valueDiags := configValueFromCLI(item.String(), rawValue, attrS.Type) 912 diags = diags.Append(valueDiags) 913 if valueDiags.HasErrors() { 914 continue 915 } 916 synthVals[name] = value 917 } 918 } 919 920 flushVals() 921 922 return ret, diags 923} 924 925func (c *InitCommand) AutocompleteArgs() complete.Predictor { 926 return complete.PredictDirs("") 927} 928 929func (c *InitCommand) AutocompleteFlags() complete.Flags { 930 return complete.Flags{ 931 "-backend": completePredictBoolean, 932 "-backend-config": complete.PredictFiles("*.tfvars"), // can also be key=value, but we can't "predict" that 933 "-force-copy": complete.PredictNothing, 934 "-from-module": completePredictModuleSource, 935 "-get": completePredictBoolean, 936 "-input": completePredictBoolean, 937 "-lock": completePredictBoolean, 938 "-lock-timeout": complete.PredictAnything, 939 "-no-color": complete.PredictNothing, 940 "-plugin-dir": complete.PredictDirs(""), 941 "-reconfigure": complete.PredictNothing, 942 "-migrate-state": complete.PredictNothing, 943 "-upgrade": completePredictBoolean, 944 } 945} 946 947func (c *InitCommand) Help() string { 948 helpText := ` 949Usage: terraform [global options] init [options] 950 951 Initialize a new or existing Terraform working directory by creating 952 initial files, loading any remote state, downloading modules, etc. 953 954 This is the first command that should be run for any new or existing 955 Terraform configuration per machine. This sets up all the local data 956 necessary to run Terraform that is typically not committed to version 957 control. 958 959 This command is always safe to run multiple times. Though subsequent runs 960 may give errors, this command will never delete your configuration or 961 state. Even so, if you have important information, please back it up prior 962 to running this command, just in case. 963 964Options: 965 966 -backend=false Disable backend initialization for this configuration 967 and use the previously initialized backend instead. 968 969 -backend-config=path This can be either a path to an HCL file with key/value 970 assignments (same format as terraform.tfvars) or a 971 'key=value' format. This is merged with what is in the 972 configuration file. This can be specified multiple 973 times. The backend type must be in the configuration 974 itself. 975 976 -force-copy Suppress prompts about copying state data. This is 977 equivalent to providing a "yes" to all confirmation 978 prompts. 979 980 -from-module=SOURCE Copy the contents of the given module into the target 981 directory before initialization. 982 983 -get=false Disable downloading modules for this configuration. 984 985 -input=false Disable prompting for missing backend configuration 986 values. This will result in an error if the backend 987 configuration is not fully specified. 988 989 -lock=false Don't hold a state lock during backend migration. 990 This is dangerous if others might concurrently run 991 commands against the same workspace. 992 993 -lock-timeout=0s Duration to retry a state lock. 994 995 -no-color If specified, output won't contain any color. 996 997 -plugin-dir Directory containing plugin binaries. This overrides all 998 default search paths for plugins, and prevents the 999 automatic installation of plugins. This flag can be used 1000 multiple times. 1001 1002 -reconfigure Reconfigure the backend, ignoring any saved 1003 configuration. 1004 1005 -migrate-state Reconfigure the backend, and attempt to migrate any 1006 existing state. 1007 1008 -upgrade Install the latest module and provider versions 1009 allowed within configured constraints, overriding the 1010 default behavior of selecting exactly the version 1011 recorded in the dependency lockfile. 1012 1013 -lockfile=MODE Set a dependency lockfile mode. 1014 Currently only "readonly" is valid. 1015 1016 -ignore-remote-version A rare option used for the remote backend only. See 1017 the remote backend documentation for more information. 1018 1019` 1020 return strings.TrimSpace(helpText) 1021} 1022 1023func (c *InitCommand) Synopsis() string { 1024 return "Prepare your working directory for other commands" 1025} 1026 1027const errInitConfigError = ` 1028[reset]There are some problems with the configuration, described below. 1029 1030The Terraform configuration must be valid before initialization so that 1031Terraform can determine which modules and providers need to be installed. 1032` 1033 1034const errInitCopyNotEmpty = ` 1035The working directory already contains files. The -from-module option requires 1036an empty directory into which a copy of the referenced module will be placed. 1037 1038To initialize the configuration already in this working directory, omit the 1039-from-module option. 1040` 1041 1042const outputInitEmpty = ` 1043[reset][bold]Terraform initialized in an empty directory![reset] 1044 1045The directory has no Terraform configuration files. You may begin working 1046with Terraform immediately by creating Terraform configuration files. 1047` 1048 1049const outputInitSuccess = ` 1050[reset][bold][green]Terraform has been successfully initialized![reset][green] 1051` 1052 1053const outputInitSuccessCLI = `[reset][green] 1054You may now begin working with Terraform. Try running "terraform plan" to see 1055any changes that are required for your infrastructure. All Terraform commands 1056should now work. 1057 1058If you ever set or change modules or backend configuration for Terraform, 1059rerun this command to reinitialize your working directory. If you forget, other 1060commands will detect it and remind you to do so if necessary. 1061` 1062 1063// providerProtocolTooOld is a message sent to the CLI UI if the provider's 1064// supported protocol versions are too old for the user's version of terraform, 1065// but a newer version of the provider is compatible. 1066const providerProtocolTooOld = `Provider %q v%s is not compatible with Terraform %s. 1067Provider version %s is the latest compatible version. Select it with the following version constraint: 1068 version = %q 1069 1070Terraform checked all of the plugin versions matching the given constraint: 1071 %s 1072 1073Consult the documentation for this provider for more information on compatibility between provider and Terraform versions. 1074` 1075 1076// providerProtocolTooNew is a message sent to the CLI UI if the provider's 1077// supported protocol versions are too new for the user's version of terraform, 1078// and the user could either upgrade terraform or choose an older version of the 1079// provider. 1080const providerProtocolTooNew = `Provider %q v%s is not compatible with Terraform %s. 1081You need to downgrade to v%s or earlier. Select it with the following constraint: 1082 version = %q 1083 1084Terraform checked all of the plugin versions matching the given constraint: 1085 %s 1086 1087Consult the documentation for this provider for more information on compatibility between provider and Terraform versions. 1088Alternatively, upgrade to the latest version of Terraform for compatibility with newer provider releases. 1089` 1090 1091// No version of the provider is compatible. 1092const errProviderVersionIncompatible = `No compatible versions of provider %s were found.` 1093