github.com/ggriffiths/terraform@v0.9.0-beta1.0.20170222213024-79c4935604cb/command/meta_backend.go (about) 1 package command 2 3 // This file contains all the Backend-related function calls on Meta, 4 // exported and private. 5 6 import ( 7 "errors" 8 "fmt" 9 "io/ioutil" 10 "log" 11 "os" 12 "path/filepath" 13 "strings" 14 15 "github.com/hashicorp/errwrap" 16 "github.com/hashicorp/go-multierror" 17 "github.com/hashicorp/hcl" 18 "github.com/hashicorp/terraform/backend" 19 clistate "github.com/hashicorp/terraform/command/state" 20 "github.com/hashicorp/terraform/config" 21 "github.com/hashicorp/terraform/state" 22 "github.com/hashicorp/terraform/terraform" 23 "github.com/mitchellh/mapstructure" 24 25 backendlegacy "github.com/hashicorp/terraform/backend/legacy" 26 backendlocal "github.com/hashicorp/terraform/backend/local" 27 backendconsul "github.com/hashicorp/terraform/backend/remote-state/consul" 28 backendinmem "github.com/hashicorp/terraform/backend/remote-state/inmem" 29 ) 30 31 // BackendOpts are the options used to initialize a backend.Backend. 32 type BackendOpts struct { 33 // ConfigPath is a path to a file or directory containing the backend 34 // configuration (declaration). 35 ConfigPath string 36 37 // ConfigFile is a path to a file that contains configuration that 38 // is merged directly into the backend configuration when loaded 39 // from a file. 40 ConfigFile string 41 42 // Plan is a plan that is being used. If this is set, the backend 43 // configuration and output configuration will come from this plan. 44 Plan *terraform.Plan 45 46 // Init should be set to true if initialization is allowed. If this is 47 // false, then any configuration that requires configuration will show 48 // an error asking the user to reinitialize. 49 Init bool 50 51 // ForceLocal will force a purely local backend, including state. 52 // You probably don't want to set this. 53 ForceLocal bool 54 } 55 56 // Backend initializes and returns the backend for this CLI session. 57 // 58 // The backend is used to perform the actual Terraform operations. This 59 // abstraction enables easily sliding in new Terraform behavior such as 60 // remote state storage, remote operations, etc. while allowing the CLI 61 // to remain mostly identical. 62 // 63 // This will initialize a new backend for each call, which can carry some 64 // overhead with it. Please reuse the returned value for optimal behavior. 65 // 66 // Only one backend should be used per Meta. This function is stateful 67 // and is unsafe to create multiple backends used at once. This function 68 // can be called multiple times with each backend being "live" (usable) 69 // one at a time. 70 func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, error) { 71 // If no opts are set, then initialize 72 if opts == nil { 73 opts = &BackendOpts{} 74 } 75 76 // Setup the local state paths 77 statePath := m.statePath 78 stateOutPath := m.stateOutPath 79 backupPath := m.backupPath 80 if statePath == "" { 81 statePath = DefaultStateFilename 82 } 83 if stateOutPath == "" { 84 stateOutPath = statePath 85 } 86 if backupPath == "" { 87 backupPath = stateOutPath + DefaultBackupExtension 88 } 89 if backupPath == "-" { 90 // The local backend expects an empty string for not taking backups. 91 backupPath = "" 92 } 93 94 // Initialize a backend from the config unless we're forcing a purely 95 // local operation. 96 var b backend.Backend 97 if !opts.ForceLocal { 98 var err error 99 100 // If we have a plan then, we get the the backend from there. Otherwise, 101 // the backend comes from the configuration. 102 if opts.Plan != nil { 103 b, err = m.backendFromPlan(opts) 104 } else { 105 b, err = m.backendFromConfig(opts) 106 } 107 if err != nil { 108 return nil, err 109 } 110 111 log.Printf("[INFO] command: backend initialized: %T", b) 112 } 113 114 // If the result of loading the backend is an enhanced backend, 115 // then return that as-is. This works even if b == nil (it will be !ok). 116 if enhanced, ok := b.(backend.Enhanced); ok { 117 return enhanced, nil 118 } 119 120 // We either have a non-enhanced backend or no backend configured at 121 // all. In either case, we use local as our enhanced backend and the 122 // non-enhanced (if any) as the state backend. 123 124 if !opts.ForceLocal { 125 log.Printf("[INFO] command: backend %T is not enhanced, wrapping in local", b) 126 } 127 128 // Build the local backend 129 return &backendlocal.Local{ 130 CLI: m.Ui, 131 CLIColor: m.Colorize(), 132 StatePath: statePath, 133 StateOutPath: stateOutPath, 134 StateBackupPath: backupPath, 135 ContextOpts: m.contextOpts(), 136 OpInput: m.Input(), 137 OpValidation: true, 138 Backend: b, 139 }, nil 140 } 141 142 // Operation initializes a new backend.Operation struct. 143 // 144 // This prepares the operation. After calling this, the caller is expected 145 // to modify fields of the operation such as Sequence to specify what will 146 // be called. 147 func (m *Meta) Operation() *backend.Operation { 148 return &backend.Operation{ 149 PlanOutBackend: m.backendState, 150 Targets: m.targets, 151 UIIn: m.UIInput(), 152 } 153 } 154 155 // backendConfig returns the local configuration for the backend 156 func (m *Meta) backendConfig(opts *BackendOpts) (*config.Backend, error) { 157 // If no explicit path was given then it is okay for there to be 158 // no backend configuration found. 159 emptyOk := opts.ConfigPath == "" 160 161 // Determine the path to the configuration. 162 path := opts.ConfigPath 163 164 // If we had no path set, it is an error. We can't initialize unset 165 if path == "" { 166 path = "." 167 } 168 169 // Expand the path 170 if !filepath.IsAbs(path) { 171 var err error 172 path, err = filepath.Abs(path) 173 if err != nil { 174 return nil, fmt.Errorf( 175 "Error expanding path to backend config %q: %s", path, err) 176 } 177 } 178 179 log.Printf("[DEBUG] command: loading backend config file: %s", path) 180 181 // We first need to determine if we're loading a file or a directory. 182 fi, err := os.Stat(path) 183 if err != nil { 184 if os.IsNotExist(err) && emptyOk { 185 log.Printf( 186 "[INFO] command: backend config not found, returning nil: %s", 187 path) 188 return nil, nil 189 } 190 191 return nil, err 192 } 193 194 var f func(string) (*config.Config, error) = config.LoadFile 195 if fi.IsDir() { 196 f = config.LoadDir 197 } 198 199 // Load the configuration 200 c, err := f(path) 201 if err != nil { 202 // Check for the error where we have no config files and return nil 203 // as the configuration type. 204 if errwrap.ContainsType(err, new(config.ErrNoConfigsFound)) { 205 log.Printf( 206 "[INFO] command: backend config not found, returning nil: %s", 207 path) 208 return nil, nil 209 } 210 211 return nil, err 212 } 213 214 // If there is no Terraform configuration block, no backend config 215 if c.Terraform == nil { 216 return nil, nil 217 } 218 219 // Get the configuration for the backend itself. 220 backend := c.Terraform.Backend 221 if backend == nil { 222 return nil, nil 223 } 224 225 // If we have a config file set, load that and merge. 226 if opts.ConfigFile != "" { 227 log.Printf( 228 "[DEBUG] command: loading extra backend config from: %s", 229 opts.ConfigFile) 230 rc, err := m.backendConfigFile(opts.ConfigFile) 231 if err != nil { 232 return nil, fmt.Errorf( 233 "Error loading extra configuration file for backend: %s", err) 234 } 235 236 // Merge in the configuration 237 backend.RawConfig = backend.RawConfig.Merge(rc) 238 } 239 240 // Return the configuration which may or may not be set 241 return backend, nil 242 } 243 244 // backendConfigFile loads the extra configuration to merge with the 245 // backend configuration from an extra file if specified by 246 // BackendOpts.ConfigFile. 247 func (m *Meta) backendConfigFile(path string) (*config.RawConfig, error) { 248 // Read the file 249 d, err := ioutil.ReadFile(path) 250 if err != nil { 251 return nil, err 252 } 253 254 // Parse it 255 hclRoot, err := hcl.Parse(string(d)) 256 if err != nil { 257 return nil, err 258 } 259 260 // Decode it 261 var c map[string]interface{} 262 if err := hcl.DecodeObject(&c, hclRoot); err != nil { 263 return nil, err 264 } 265 266 return config.NewRawConfig(c) 267 } 268 269 // backendFromConfig returns the initialized (not configured) backend 270 // directly from the config/state.. 271 // 272 // This function handles any edge cases around backend config loading. For 273 // example: legacy remote state, new config changes, backend type changes, 274 // etc. 275 // 276 // This function may query the user for input unless input is disabled, in 277 // which case this function will error. 278 func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) { 279 // Get the local backend configuration. 280 c, err := m.backendConfig(opts) 281 if err != nil { 282 return nil, fmt.Errorf("Error loading backend config: %s", err) 283 } 284 285 // Get the path to where we store a local cache of backend configuration 286 // if we're using a remote backend. This may not yet exist which means 287 // we haven't used a non-local backend before. That is okay. 288 statePath := filepath.Join(m.DataDir(), DefaultStateFilename) 289 sMgr := &state.LocalState{Path: statePath} 290 if err := sMgr.RefreshState(); err != nil { 291 return nil, fmt.Errorf("Error loading state: %s", err) 292 } 293 294 // Load the state, it must be non-nil for the tests below but can be empty 295 s := sMgr.State() 296 if s == nil { 297 log.Printf("[DEBUG] command: no data state file found for backend config") 298 s = terraform.NewState() 299 } 300 301 // Upon return, we want to set the state we're using in-memory so that 302 // we can access it for commands. 303 m.backendState = nil 304 defer func() { 305 if s := sMgr.State(); s != nil && !s.Backend.Empty() { 306 m.backendState = s.Backend 307 } 308 }() 309 310 // This giant switch statement covers all eight possible combinations 311 // of state settings between: configuring new backends, saved (previously- 312 // configured) backends, and legacy remote state. 313 switch { 314 // No configuration set at all. Pure local state. 315 case c == nil && s.Remote.Empty() && s.Backend.Empty(): 316 return nil, nil 317 318 // We're unsetting a backend (moving from backend => local) 319 case c == nil && s.Remote.Empty() && !s.Backend.Empty(): 320 if !opts.Init { 321 initReason := fmt.Sprintf( 322 "Unsetting the previously set backend %q", 323 s.Backend.Type) 324 m.backendInitRequired(initReason) 325 return nil, errBackendInitRequired 326 } 327 328 return m.backend_c_r_S(c, sMgr, true) 329 330 // We have a legacy remote state configuration but no new backend config 331 case c == nil && !s.Remote.Empty() && s.Backend.Empty(): 332 return m.backend_c_R_s(c, sMgr) 333 334 // We have a legacy remote state configuration simultaneously with a 335 // saved backend configuration while at the same time disabling backend 336 // configuration. 337 // 338 // This is a naturally impossible case: Terraform will never put you 339 // in this state, though it is theoretically possible through manual edits 340 case c == nil && !s.Remote.Empty() && !s.Backend.Empty(): 341 if !opts.Init { 342 initReason := fmt.Sprintf( 343 "Unsetting the previously set backend %q", 344 s.Backend.Type) 345 m.backendInitRequired(initReason) 346 return nil, errBackendInitRequired 347 } 348 349 return m.backend_c_R_S(c, sMgr) 350 351 // Configuring a backend for the first time. 352 case c != nil && s.Remote.Empty() && s.Backend.Empty(): 353 if !opts.Init { 354 initReason := fmt.Sprintf( 355 "Initial configuration of the requested backend %q", 356 c.Type) 357 m.backendInitRequired(initReason) 358 return nil, errBackendInitRequired 359 } 360 361 return m.backend_C_r_s(c, sMgr) 362 363 // Potentially changing a backend configuration 364 case c != nil && s.Remote.Empty() && !s.Backend.Empty(): 365 // If our configuration is the same, then we're just initializing 366 // a previously configured remote backend. 367 if !s.Backend.Empty() && s.Backend.Hash == c.Hash { 368 return m.backend_C_r_S_unchanged(c, sMgr) 369 } 370 371 if !opts.Init { 372 initReason := fmt.Sprintf( 373 "Backend configuration changed for %q", 374 c.Type) 375 m.backendInitRequired(initReason) 376 return nil, errBackendInitRequired 377 } 378 379 log.Printf( 380 "[WARN] command: backend config change! saved: %d, new: %d", 381 s.Backend.Hash, c.Hash) 382 return m.backend_C_r_S_changed(c, sMgr, true) 383 384 // Configuring a backend for the first time while having legacy 385 // remote state. This is very possible if a Terraform user configures 386 // a backend prior to ever running Terraform on an old state. 387 case c != nil && !s.Remote.Empty() && s.Backend.Empty(): 388 if !opts.Init { 389 initReason := fmt.Sprintf( 390 "Initial configuration for backend %q", 391 c.Type) 392 m.backendInitRequired(initReason) 393 return nil, errBackendInitRequired 394 } 395 396 return m.backend_C_R_s(c, sMgr) 397 398 // Configuring a backend with both a legacy remote state set 399 // and a pre-existing backend saved. 400 case c != nil && !s.Remote.Empty() && !s.Backend.Empty(): 401 // If the hashes are the same, we have a legacy remote state with 402 // an unchanged stored backend state. 403 if s.Backend.Hash == c.Hash { 404 if !opts.Init { 405 initReason := fmt.Sprintf( 406 "Legacy remote state found with configured backend %q", 407 c.Type) 408 m.backendInitRequired(initReason) 409 return nil, errBackendInitRequired 410 } 411 412 return m.backend_C_R_S_unchanged(c, sMgr, true) 413 } 414 415 if !opts.Init { 416 initReason := fmt.Sprintf( 417 "Reconfiguring the backend %q", 418 c.Type) 419 m.backendInitRequired(initReason) 420 return nil, errBackendInitRequired 421 } 422 423 // We have change in all three 424 return m.backend_C_R_S_changed(c, sMgr) 425 default: 426 // This should be impossible since all state possibilties are 427 // tested above, but we need a default case anyways and we should 428 // protect against the scenario where a case is somehow removed. 429 return nil, fmt.Errorf( 430 "Unhandled backend configuration state. This is a bug. Please\n"+ 431 "report this error with the following information.\n\n"+ 432 "Config Nil: %v\n"+ 433 "Saved Backend Empty: %v\n"+ 434 "Legacy Remote Empty: %v\n", 435 c == nil, s.Backend.Empty(), s.Remote.Empty()) 436 } 437 } 438 439 // backendFromPlan loads the backend from a given plan file. 440 func (m *Meta) backendFromPlan(opts *BackendOpts) (backend.Backend, error) { 441 // Precondition check 442 if opts.Plan == nil { 443 panic("plan should not be nil") 444 } 445 446 // We currently don't allow "-state" to be specified. 447 if m.statePath != "" { 448 return nil, fmt.Errorf( 449 "State path cannot be specified with a plan file. The plan itself contains\n" + 450 "the state to use. If you wish to change that, please create a new plan\n" + 451 "and specify the state path when creating the plan.") 452 } 453 454 planState := opts.Plan.State 455 if planState == nil { 456 // The state can be nil, we just have to make it empty for the logic 457 // in this function. 458 planState = terraform.NewState() 459 } 460 461 // Validation only for non-local plans 462 local := planState.Remote.Empty() && planState.Backend.Empty() 463 if !local { 464 // We currently don't allow "-state-out" to be specified. 465 if m.stateOutPath != "" { 466 return nil, fmt.Errorf(strings.TrimSpace(errBackendPlanStateFlag)) 467 } 468 } 469 470 /* 471 // Determine the path where we'd be writing state 472 path := DefaultStateFilename 473 if !planState.Remote.Empty() || !planState.Backend.Empty() { 474 path = filepath.Join(m.DataDir(), DefaultStateFilename) 475 } 476 477 // If the path exists, then we need to verify we're writing the same 478 // state lineage. If the path doesn't exist that's okay. 479 _, err := os.Stat(path) 480 if err != nil && !os.IsNotExist(err) { 481 return nil, fmt.Errorf("Error checking state destination: %s", err) 482 } 483 if err == nil { 484 // The file exists, we need to read it and compare 485 if err := m.backendFromPlan_compareStates(state, path); err != nil { 486 return nil, err 487 } 488 } 489 */ 490 491 // If we have a stateOutPath, we must also specify it as the 492 // input path so we can check it properly. We restore it after this 493 // function exits. 494 original := m.statePath 495 m.statePath = m.stateOutPath 496 defer func() { m.statePath = original }() 497 498 var b backend.Backend 499 var err error 500 switch { 501 // No remote state at all, all local 502 case planState.Remote.Empty() && planState.Backend.Empty(): 503 // Get the local backend 504 b, err = m.Backend(&BackendOpts{ForceLocal: true}) 505 506 // New backend configuration set 507 case planState.Remote.Empty() && !planState.Backend.Empty(): 508 b, err = m.backendInitFromSaved(planState.Backend) 509 510 // Legacy remote state set 511 case !planState.Remote.Empty() && planState.Backend.Empty(): 512 // Write our current state to an inmemory state just so that we 513 // have it in the format of state.State 514 inmem := &state.InmemState{} 515 inmem.WriteState(planState) 516 517 // Get the backend through the normal means of legacy state 518 b, err = m.backend_c_R_s(nil, inmem) 519 520 // Both set, this can't happen in a plan. 521 case !planState.Remote.Empty() && !planState.Backend.Empty(): 522 return nil, fmt.Errorf(strings.TrimSpace(errBackendPlanBoth)) 523 } 524 525 // If we had an error, return that 526 if err != nil { 527 return nil, err 528 } 529 530 // Get the state so we can determine the effect of using this plan 531 realMgr, err := b.State() 532 if err != nil { 533 return nil, fmt.Errorf("Error reading state: %s", err) 534 } 535 536 // Lock the state if we can 537 lockInfo := state.NewLockInfo() 538 lockInfo.Operation = "backend from plan" 539 540 lockID, err := clistate.Lock(realMgr, lockInfo, m.Ui, m.Colorize()) 541 if err != nil { 542 return nil, fmt.Errorf("Error locking state: %s", err) 543 } 544 defer clistate.Unlock(realMgr, lockID, m.Ui, m.Colorize()) 545 546 if err := realMgr.RefreshState(); err != nil { 547 return nil, fmt.Errorf("Error reading state: %s", err) 548 } 549 real := realMgr.State() 550 if real != nil { 551 // If they're not the same lineage, don't allow this 552 if !real.SameLineage(planState) { 553 return nil, fmt.Errorf(strings.TrimSpace(errBackendPlanLineageDiff)) 554 } 555 556 // Compare ages 557 comp, err := real.CompareAges(planState) 558 if err != nil { 559 return nil, fmt.Errorf("Error comparing state ages for safety: %s", err) 560 } 561 switch comp { 562 case terraform.StateAgeEqual: 563 // State ages are equal, this is perfect 564 565 case terraform.StateAgeReceiverOlder: 566 // Real state is somehow older, this is okay. 567 568 case terraform.StateAgeReceiverNewer: 569 // If we have an older serial it is a problem but if we have a 570 // differing serial but are still identical, just let it through. 571 if real.Equal(planState) { 572 log.Printf( 573 "[WARN] command: state in plan has older serial, but Equal is true") 574 break 575 } 576 577 // The real state is newer, this is not allowed. 578 return nil, fmt.Errorf( 579 strings.TrimSpace(errBackendPlanOlder), 580 planState.Serial, real.Serial) 581 } 582 } 583 584 // Write the state 585 newState := opts.Plan.State.DeepCopy() 586 if newState != nil { 587 newState.Remote = nil 588 newState.Backend = nil 589 } 590 591 // realMgr locked above 592 if err := realMgr.WriteState(newState); err != nil { 593 return nil, fmt.Errorf("Error writing state: %s", err) 594 } 595 if err := realMgr.PersistState(); err != nil { 596 return nil, fmt.Errorf("Error writing state: %s", err) 597 } 598 599 return b, nil 600 } 601 602 //------------------------------------------------------------------- 603 // Backend Config Scenarios 604 // 605 // The functions below cover handling all the various scenarios that 606 // can exist when loading a backend. They are named in the format of 607 // "backend_C_R_S" where C, R, S may be upper or lowercase. Lowercase 608 // means it is false, uppercase means it is true. The full set of eight 609 // possible cases is handled. 610 // 611 // The fields are: 612 // 613 // * C - Backend configuration is set and changed in TF files 614 // * R - Legacy remote state is set 615 // * S - Backend configuration is set in the state 616 // 617 //------------------------------------------------------------------- 618 619 // Unconfiguring a backend (moving from backend => local). 620 func (m *Meta) backend_c_r_S( 621 c *config.Backend, sMgr state.State, output bool) (backend.Backend, error) { 622 s := sMgr.State() 623 624 // Get the backend type for output 625 backendType := s.Backend.Type 626 627 // Confirm with the user that the copy should occur 628 copy, err := m.confirm(&terraform.InputOpts{ 629 Id: "backend-migrate-to-local", 630 Query: fmt.Sprintf("Do you want to copy the state from %q?", s.Backend.Type), 631 Description: fmt.Sprintf( 632 strings.TrimSpace(inputBackendMigrateLocal), s.Backend.Type), 633 }) 634 if err != nil { 635 return nil, fmt.Errorf( 636 "Error asking for state copy action: %s", err) 637 } 638 639 // If we're copying, perform the migration 640 if copy { 641 // Grab a purely local backend to get the local state if it exists 642 localB, err := m.Backend(&BackendOpts{ForceLocal: true}) 643 if err != nil { 644 return nil, fmt.Errorf(strings.TrimSpace(errBackendLocalRead), err) 645 } 646 localState, err := localB.State() 647 if err != nil { 648 return nil, fmt.Errorf(strings.TrimSpace(errBackendLocalRead), err) 649 } 650 if err := localState.RefreshState(); err != nil { 651 return nil, fmt.Errorf(strings.TrimSpace(errBackendLocalRead), err) 652 } 653 654 // Initialize the configured backend 655 b, err := m.backend_C_r_S_unchanged(c, sMgr) 656 if err != nil { 657 return nil, fmt.Errorf( 658 strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err) 659 } 660 backendState, err := b.State() 661 if err != nil { 662 return nil, fmt.Errorf( 663 strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err) 664 } 665 if err := backendState.RefreshState(); err != nil { 666 return nil, fmt.Errorf( 667 strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err) 668 } 669 670 // Perform the migration 671 err = m.backendMigrateState(&backendMigrateOpts{ 672 OneType: s.Backend.Type, 673 TwoType: "local", 674 One: backendState, 675 Two: localState, 676 }) 677 if err != nil { 678 return nil, err 679 } 680 } 681 682 // Remove the stored metadata 683 s.Backend = nil 684 if err := sMgr.WriteState(s); err != nil { 685 return nil, fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err) 686 } 687 if err := sMgr.PersistState(); err != nil { 688 return nil, fmt.Errorf(strings.TrimSpace(errBackendClearSaved), err) 689 } 690 691 if output { 692 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 693 "[reset][green]\n\n"+ 694 strings.TrimSpace(successBackendUnset), backendType))) 695 } 696 697 // Return no backend 698 return nil, nil 699 } 700 701 // Legacy remote state 702 func (m *Meta) backend_c_R_s( 703 c *config.Backend, sMgr state.State) (backend.Backend, error) { 704 s := sMgr.State() 705 706 // Warn the user 707 m.Ui.Warn(strings.TrimSpace(warnBackendLegacy) + "\n") 708 709 // We need to convert the config to map[string]interface{} since that 710 // is what the backends expect. 711 var configMap map[string]interface{} 712 if err := mapstructure.Decode(s.Remote.Config, &configMap); err != nil { 713 return nil, fmt.Errorf("Error configuring remote state: %s", err) 714 } 715 716 // Create the config 717 rawC, err := config.NewRawConfig(configMap) 718 if err != nil { 719 return nil, fmt.Errorf("Error configuring remote state: %s", err) 720 } 721 config := terraform.NewResourceConfig(rawC) 722 723 // Initialize the legacy remote backend 724 b := &backendlegacy.Backend{Type: s.Remote.Type} 725 726 // Configure 727 if err := b.Configure(config); err != nil { 728 return nil, fmt.Errorf(errBackendLegacyConfig, err) 729 } 730 731 return b, nil 732 } 733 734 // Unsetting backend, saved backend, legacy remote state 735 func (m *Meta) backend_c_R_S( 736 c *config.Backend, sMgr state.State) (backend.Backend, error) { 737 // Notify the user 738 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 739 "[reset]%s\n\n", 740 strings.TrimSpace(outputBackendUnsetWithLegacy)))) 741 742 // Get the backend type for later 743 backendType := sMgr.State().Backend.Type 744 745 // First, perform the configured => local tranasition 746 if _, err := m.backend_c_r_S(c, sMgr, false); err != nil { 747 return nil, err 748 } 749 750 // Grab a purely local backend 751 localB, err := m.Backend(&BackendOpts{ForceLocal: true}) 752 if err != nil { 753 return nil, fmt.Errorf(errBackendLocalRead, err) 754 } 755 localState, err := localB.State() 756 if err != nil { 757 return nil, fmt.Errorf(errBackendLocalRead, err) 758 } 759 if err := localState.RefreshState(); err != nil { 760 return nil, fmt.Errorf(errBackendLocalRead, err) 761 } 762 763 // Grab the state 764 s := sMgr.State() 765 766 // Ask the user if they want to migrate their existing remote state 767 copy, err := m.confirm(&terraform.InputOpts{ 768 Id: "backend-migrate-to-new", 769 Query: fmt.Sprintf( 770 "Do you want to copy the legacy remote state from %q?", 771 s.Remote.Type), 772 Description: strings.TrimSpace(inputBackendMigrateLegacyLocal), 773 }) 774 if err != nil { 775 return nil, fmt.Errorf( 776 "Error asking for state copy action: %s", err) 777 } 778 779 // If the user wants a copy, copy! 780 if copy { 781 // Initialize the legacy backend 782 oldB, err := m.backendInitFromLegacy(s.Remote) 783 if err != nil { 784 return nil, err 785 } 786 oldState, err := oldB.State() 787 if err != nil { 788 return nil, fmt.Errorf( 789 strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err) 790 } 791 if err := oldState.RefreshState(); err != nil { 792 return nil, fmt.Errorf( 793 strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err) 794 } 795 796 // Perform the migration 797 err = m.backendMigrateState(&backendMigrateOpts{ 798 OneType: s.Remote.Type, 799 TwoType: "local", 800 One: oldState, 801 Two: localState, 802 }) 803 if err != nil { 804 return nil, err 805 } 806 } 807 808 // Unset the remote state 809 s = sMgr.State() 810 if s == nil { 811 s = terraform.NewState() 812 } 813 s.Remote = nil 814 if err := sMgr.WriteState(s); err != nil { 815 return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err) 816 } 817 if err := sMgr.PersistState(); err != nil { 818 return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err) 819 } 820 821 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 822 "[reset][green]\n\n"+ 823 strings.TrimSpace(successBackendUnset), backendType))) 824 825 return nil, nil 826 } 827 828 // Configuring a backend for the first time with legacy remote state. 829 func (m *Meta) backend_C_R_s( 830 c *config.Backend, sMgr state.State) (backend.Backend, error) { 831 // Notify the user 832 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 833 "[reset]%s\n\n", 834 strings.TrimSpace(outputBackendConfigureWithLegacy)))) 835 836 // First, configure the new backend 837 b, err := m.backendInitFromConfig(c) 838 if err != nil { 839 return nil, err 840 } 841 842 // Next, save the new configuration. This will not overwrite our 843 // legacy remote state. We'll handle that after. 844 s := sMgr.State() 845 if s == nil { 846 s = terraform.NewState() 847 } 848 s.Backend = &terraform.BackendState{ 849 Type: c.Type, 850 Config: c.RawConfig.Raw, 851 Hash: c.Hash, 852 } 853 if err := sMgr.WriteState(s); err != nil { 854 return nil, fmt.Errorf(errBackendWriteSaved, err) 855 } 856 if err := sMgr.PersistState(); err != nil { 857 return nil, fmt.Errorf(errBackendWriteSaved, err) 858 } 859 860 // I don't know how this is possible but if we don't have remote 861 // state config anymore somehow, just return the backend. This 862 // shouldn't be possible, though. 863 if s.Remote.Empty() { 864 return b, nil 865 } 866 867 // Finally, ask the user if they want to copy the state from 868 // their old remote state location. 869 copy, err := m.confirm(&terraform.InputOpts{ 870 Id: "backend-migrate-to-new", 871 Query: fmt.Sprintf( 872 "Do you want to copy the legacy remote state from %q?", 873 s.Remote.Type), 874 Description: strings.TrimSpace(inputBackendMigrateLegacy), 875 }) 876 if err != nil { 877 return nil, fmt.Errorf( 878 "Error asking for state copy action: %s", err) 879 } 880 881 // If the user wants a copy, copy! 882 if copy { 883 // Initialize the legacy backend 884 oldB, err := m.backendInitFromLegacy(s.Remote) 885 if err != nil { 886 return nil, err 887 } 888 oldState, err := oldB.State() 889 if err != nil { 890 return nil, fmt.Errorf( 891 strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err) 892 } 893 if err := oldState.RefreshState(); err != nil { 894 return nil, fmt.Errorf( 895 strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err) 896 } 897 898 // Get the new state 899 newState, err := b.State() 900 if err != nil { 901 return nil, fmt.Errorf(strings.TrimSpace(errBackendNewRead), err) 902 } 903 if err := newState.RefreshState(); err != nil { 904 return nil, fmt.Errorf(strings.TrimSpace(errBackendNewRead), err) 905 } 906 907 // Perform the migration 908 err = m.backendMigrateState(&backendMigrateOpts{ 909 OneType: s.Remote.Type, 910 TwoType: c.Type, 911 One: oldState, 912 Two: newState, 913 }) 914 if err != nil { 915 return nil, err 916 } 917 } 918 919 // Unset the remote state 920 s = sMgr.State() 921 if s == nil { 922 s = terraform.NewState() 923 } 924 s.Remote = nil 925 if err := sMgr.WriteState(s); err != nil { 926 return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err) 927 } 928 if err := sMgr.PersistState(); err != nil { 929 return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err) 930 } 931 932 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 933 "[reset][green]\n\n"+ 934 strings.TrimSpace(successBackendSet), s.Backend.Type))) 935 936 return b, nil 937 } 938 939 // Configuring a backend for the first time. 940 func (m *Meta) backend_C_r_s( 941 c *config.Backend, sMgr state.State) (backend.Backend, error) { 942 // Get the backend 943 b, err := m.backendInitFromConfig(c) 944 if err != nil { 945 return nil, err 946 } 947 948 // Grab a purely local backend to get the local state if it exists 949 localB, err := m.Backend(&BackendOpts{ForceLocal: true}) 950 if err != nil { 951 return nil, fmt.Errorf(errBackendLocalRead, err) 952 } 953 localState, err := localB.State() 954 if err != nil { 955 return nil, fmt.Errorf(errBackendLocalRead, err) 956 } 957 if err := localState.RefreshState(); err != nil { 958 return nil, fmt.Errorf(errBackendLocalRead, err) 959 } 960 961 // If the local state is not empty, we need to potentially do a 962 // state migration to the new backend (with user permission). 963 if localS := localState.State(); !localS.Empty() { 964 backendState, err := b.State() 965 if err != nil { 966 return nil, fmt.Errorf(errBackendRemoteRead, err) 967 } 968 if err := backendState.RefreshState(); err != nil { 969 return nil, fmt.Errorf(errBackendRemoteRead, err) 970 } 971 972 // Perform the migration 973 err = m.backendMigrateState(&backendMigrateOpts{ 974 OneType: "local", 975 TwoType: c.Type, 976 One: localState, 977 Two: backendState, 978 }) 979 if err != nil { 980 return nil, err 981 } 982 983 // We always delete the local state 984 if err := localState.WriteState(nil); err != nil { 985 return nil, fmt.Errorf(errBackendMigrateLocalDelete, err) 986 } 987 if err := localState.PersistState(); err != nil { 988 return nil, fmt.Errorf(errBackendMigrateLocalDelete, err) 989 } 990 } 991 992 // Lock the state if we can 993 lockInfo := state.NewLockInfo() 994 lockInfo.Operation = "backend from config" 995 996 lockID, err := clistate.Lock(sMgr, lockInfo, m.Ui, m.Colorize()) 997 if err != nil { 998 return nil, fmt.Errorf("Error locking state: %s", err) 999 } 1000 defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize()) 1001 1002 // Store the metadata in our saved state location 1003 s := sMgr.State() 1004 if s == nil { 1005 s = terraform.NewState() 1006 } 1007 s.Backend = &terraform.BackendState{ 1008 Type: c.Type, 1009 Config: c.RawConfig.Raw, 1010 Hash: c.Hash, 1011 } 1012 1013 if err := sMgr.WriteState(s); err != nil { 1014 return nil, fmt.Errorf(errBackendWriteSaved, err) 1015 } 1016 if err := sMgr.PersistState(); err != nil { 1017 return nil, fmt.Errorf(errBackendWriteSaved, err) 1018 } 1019 1020 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 1021 "[reset][green]\n\n"+ 1022 strings.TrimSpace(successBackendSet), s.Backend.Type))) 1023 1024 // Return the backend 1025 return b, nil 1026 } 1027 1028 // Changing a previously saved backend. 1029 func (m *Meta) backend_C_r_S_changed( 1030 c *config.Backend, sMgr state.State, output bool) (backend.Backend, error) { 1031 if output { 1032 // Notify the user 1033 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 1034 "[reset]%s\n\n", 1035 strings.TrimSpace(outputBackendReconfigure)))) 1036 } 1037 1038 // Get the old state 1039 s := sMgr.State() 1040 1041 // Get the backend 1042 b, err := m.backendInitFromConfig(c) 1043 if err != nil { 1044 return nil, fmt.Errorf( 1045 "Error initializing new backend: %s", err) 1046 } 1047 1048 // Check with the user if we want to migrate state 1049 copy, err := m.confirm(&terraform.InputOpts{ 1050 Id: "backend-migrate-to-new", 1051 Query: fmt.Sprintf("Do you want to copy the state from %q?", c.Type), 1052 Description: strings.TrimSpace(fmt.Sprintf(inputBackendMigrateChange, c.Type, s.Backend.Type)), 1053 }) 1054 if err != nil { 1055 return nil, fmt.Errorf( 1056 "Error asking for state copy action: %s", err) 1057 } 1058 1059 // If we are, then we need to initialize the old backend and 1060 // perform the copy. 1061 if copy { 1062 // Grab the existing backend 1063 oldB, err := m.backend_C_r_S_unchanged(c, sMgr) 1064 if err != nil { 1065 return nil, fmt.Errorf( 1066 "Error loading previously configured backend: %s", err) 1067 } 1068 1069 oldState, err := oldB.State() 1070 if err != nil { 1071 return nil, fmt.Errorf( 1072 strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err) 1073 } 1074 if err := oldState.RefreshState(); err != nil { 1075 return nil, fmt.Errorf( 1076 strings.TrimSpace(errBackendSavedUnsetConfig), s.Backend.Type, err) 1077 } 1078 1079 // Get the new state 1080 newState, err := b.State() 1081 if err != nil { 1082 return nil, fmt.Errorf(strings.TrimSpace(errBackendNewRead), err) 1083 } 1084 if err := newState.RefreshState(); err != nil { 1085 return nil, fmt.Errorf(strings.TrimSpace(errBackendNewRead), err) 1086 } 1087 1088 // Perform the migration 1089 err = m.backendMigrateState(&backendMigrateOpts{ 1090 OneType: s.Backend.Type, 1091 TwoType: c.Type, 1092 One: oldState, 1093 Two: newState, 1094 }) 1095 if err != nil { 1096 return nil, err 1097 } 1098 } 1099 1100 // Lock the state if we can 1101 lockInfo := state.NewLockInfo() 1102 lockInfo.Operation = "backend from config" 1103 1104 lockID, err := clistate.Lock(sMgr, lockInfo, m.Ui, m.Colorize()) 1105 if err != nil { 1106 return nil, fmt.Errorf("Error locking state: %s", err) 1107 } 1108 defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize()) 1109 1110 // Update the backend state 1111 s = sMgr.State() 1112 if s == nil { 1113 s = terraform.NewState() 1114 } 1115 s.Backend = &terraform.BackendState{ 1116 Type: c.Type, 1117 Config: c.RawConfig.Raw, 1118 Hash: c.Hash, 1119 } 1120 1121 if err := sMgr.WriteState(s); err != nil { 1122 return nil, fmt.Errorf(errBackendWriteSaved, err) 1123 } 1124 if err := sMgr.PersistState(); err != nil { 1125 return nil, fmt.Errorf(errBackendWriteSaved, err) 1126 } 1127 1128 if output { 1129 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 1130 "[reset][green]\n\n"+ 1131 strings.TrimSpace(successBackendSet), s.Backend.Type))) 1132 } 1133 1134 return b, nil 1135 } 1136 1137 // Initiailizing an unchanged saved backend 1138 func (m *Meta) backend_C_r_S_unchanged( 1139 c *config.Backend, sMgr state.State) (backend.Backend, error) { 1140 s := sMgr.State() 1141 1142 // Create the config. We do this from the backend state since this 1143 // has the complete configuration data whereas the config itself 1144 // may require input. 1145 rawC, err := config.NewRawConfig(s.Backend.Config) 1146 if err != nil { 1147 return nil, fmt.Errorf("Error configuring backend: %s", err) 1148 } 1149 config := terraform.NewResourceConfig(rawC) 1150 1151 // Get the backend 1152 f, ok := Backends[s.Backend.Type] 1153 if !ok { 1154 return nil, fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type) 1155 } 1156 b := f() 1157 1158 // Configure 1159 if err := b.Configure(config); err != nil { 1160 return nil, fmt.Errorf(errBackendSavedConfig, s.Backend.Type, err) 1161 } 1162 1163 return b, nil 1164 } 1165 1166 // Initiailizing a changed saved backend with legacy remote state. 1167 func (m *Meta) backend_C_R_S_changed( 1168 c *config.Backend, sMgr state.State) (backend.Backend, error) { 1169 // Notify the user 1170 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 1171 "[reset]%s\n\n", 1172 strings.TrimSpace(outputBackendSavedWithLegacyChanged)))) 1173 1174 // Reconfigure the backend first 1175 if _, err := m.backend_C_r_S_changed(c, sMgr, false); err != nil { 1176 return nil, err 1177 } 1178 1179 // Handle the case where we have all set but unchanged 1180 b, err := m.backend_C_R_S_unchanged(c, sMgr, false) 1181 if err != nil { 1182 return nil, err 1183 } 1184 1185 // Output success message 1186 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 1187 "[reset][green]\n\n"+ 1188 strings.TrimSpace(successBackendReconfigureWithLegacy), c.Type))) 1189 1190 return b, nil 1191 } 1192 1193 // Initiailizing an unchanged saved backend with legacy remote state. 1194 func (m *Meta) backend_C_R_S_unchanged( 1195 c *config.Backend, sMgr state.State, output bool) (backend.Backend, error) { 1196 if output { 1197 // Notify the user 1198 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 1199 "[reset]%s\n\n", 1200 strings.TrimSpace(outputBackendSavedWithLegacy)))) 1201 } 1202 1203 // Load the backend from the state 1204 s := sMgr.State() 1205 b, err := m.backendInitFromSaved(s.Backend) 1206 if err != nil { 1207 return nil, err 1208 } 1209 1210 // Ask if the user wants to move their legacy remote state 1211 copy, err := m.confirm(&terraform.InputOpts{ 1212 Id: "backend-migrate-to-new", 1213 Query: fmt.Sprintf( 1214 "Do you want to copy the legacy remote state from %q?", 1215 s.Remote.Type), 1216 Description: strings.TrimSpace(inputBackendMigrateLegacy), 1217 }) 1218 if err != nil { 1219 return nil, fmt.Errorf( 1220 "Error asking for state copy action: %s", err) 1221 } 1222 1223 // If the user wants a copy, copy! 1224 if copy { 1225 // Initialize the legacy backend 1226 oldB, err := m.backendInitFromLegacy(s.Remote) 1227 if err != nil { 1228 return nil, err 1229 } 1230 oldState, err := oldB.State() 1231 if err != nil { 1232 return nil, fmt.Errorf( 1233 strings.TrimSpace(errBackendSavedUnsetConfig), s.Remote.Type, err) 1234 } 1235 if err := oldState.RefreshState(); err != nil { 1236 return nil, fmt.Errorf( 1237 strings.TrimSpace(errBackendSavedUnsetConfig), s.Remote.Type, err) 1238 } 1239 1240 // Get the new state 1241 newState, err := b.State() 1242 if err != nil { 1243 return nil, fmt.Errorf(strings.TrimSpace(errBackendNewRead), err) 1244 } 1245 if err := newState.RefreshState(); err != nil { 1246 return nil, fmt.Errorf(strings.TrimSpace(errBackendNewRead), err) 1247 } 1248 1249 // Perform the migration 1250 err = m.backendMigrateState(&backendMigrateOpts{ 1251 OneType: s.Remote.Type, 1252 TwoType: s.Backend.Type, 1253 One: oldState, 1254 Two: newState, 1255 }) 1256 if err != nil { 1257 return nil, err 1258 } 1259 } 1260 1261 // Lock the state if we can 1262 lockInfo := state.NewLockInfo() 1263 lockInfo.Operation = "backend from config" 1264 1265 lockID, err := clistate.Lock(sMgr, lockInfo, m.Ui, m.Colorize()) 1266 if err != nil { 1267 return nil, fmt.Errorf("Error locking state: %s", err) 1268 } 1269 defer clistate.Unlock(sMgr, lockID, m.Ui, m.Colorize()) 1270 1271 // Unset the remote state 1272 s = sMgr.State() 1273 if s == nil { 1274 s = terraform.NewState() 1275 } 1276 s.Remote = nil 1277 1278 if err := sMgr.WriteState(s); err != nil { 1279 return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err) 1280 } 1281 if err := sMgr.PersistState(); err != nil { 1282 return nil, fmt.Errorf(strings.TrimSpace(errBackendClearLegacy), err) 1283 } 1284 1285 if output { 1286 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 1287 "[reset][green]\n\n"+ 1288 strings.TrimSpace(successBackendLegacyUnset), s.Backend.Type))) 1289 } 1290 1291 return b, nil 1292 } 1293 1294 //------------------------------------------------------------------- 1295 // Reusable helper functions for backend management 1296 //------------------------------------------------------------------- 1297 1298 func (m *Meta) backendInitFromConfig(c *config.Backend) (backend.Backend, error) { 1299 // Create the config. 1300 config := terraform.NewResourceConfig(c.RawConfig) 1301 1302 // Get the backend 1303 f, ok := Backends[c.Type] 1304 if !ok { 1305 return nil, fmt.Errorf(strings.TrimSpace(errBackendNewUnknown), c.Type) 1306 } 1307 b := f() 1308 1309 // TODO: test 1310 // Ask for input if we have input enabled 1311 if m.Input() { 1312 var err error 1313 config, err = b.Input(m.UIInput(), config) 1314 if err != nil { 1315 return nil, fmt.Errorf( 1316 "Error asking for input to configure the backend %q: %s", 1317 c.Type, err) 1318 } 1319 } 1320 1321 // Validate 1322 warns, errs := b.Validate(config) 1323 if len(errs) > 0 { 1324 return nil, fmt.Errorf( 1325 "Error configuring the backend %q: %s", 1326 c.Type, multierror.Append(nil, errs...)) 1327 } 1328 if len(warns) > 0 { 1329 // TODO: warnings are currently ignored 1330 } 1331 1332 // Configure 1333 if err := b.Configure(config); err != nil { 1334 return nil, fmt.Errorf(errBackendNewConfig, c.Type, err) 1335 } 1336 1337 return b, nil 1338 } 1339 1340 func (m *Meta) backendInitFromLegacy(s *terraform.RemoteState) (backend.Backend, error) { 1341 // We need to convert the config to map[string]interface{} since that 1342 // is what the backends expect. 1343 var configMap map[string]interface{} 1344 if err := mapstructure.Decode(s.Config, &configMap); err != nil { 1345 return nil, fmt.Errorf("Error configuring remote state: %s", err) 1346 } 1347 1348 // Create the config 1349 rawC, err := config.NewRawConfig(configMap) 1350 if err != nil { 1351 return nil, fmt.Errorf("Error configuring remote state: %s", err) 1352 } 1353 config := terraform.NewResourceConfig(rawC) 1354 1355 // Initialize the legacy remote backend 1356 b := &backendlegacy.Backend{Type: s.Type} 1357 1358 // Configure 1359 if err := b.Configure(config); err != nil { 1360 return nil, fmt.Errorf(errBackendLegacyConfig, err) 1361 } 1362 1363 return b, nil 1364 } 1365 1366 func (m *Meta) backendInitFromSaved(s *terraform.BackendState) (backend.Backend, error) { 1367 // Create the config. We do this from the backend state since this 1368 // has the complete configuration data whereas the config itself 1369 // may require input. 1370 rawC, err := config.NewRawConfig(s.Config) 1371 if err != nil { 1372 return nil, fmt.Errorf("Error configuring backend: %s", err) 1373 } 1374 config := terraform.NewResourceConfig(rawC) 1375 1376 // Get the backend 1377 f, ok := Backends[s.Type] 1378 if !ok { 1379 return nil, fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Type) 1380 } 1381 b := f() 1382 1383 // Configure 1384 if err := b.Configure(config); err != nil { 1385 return nil, fmt.Errorf(errBackendSavedConfig, s.Type, err) 1386 } 1387 1388 return b, nil 1389 } 1390 1391 func (m *Meta) backendInitRequired(reason string) { 1392 m.Ui.Output(m.Colorize().Color(fmt.Sprintf( 1393 "[reset]"+strings.TrimSpace(errBackendInit)+"\n", reason))) 1394 } 1395 1396 //------------------------------------------------------------------- 1397 // Output constants and initialization code 1398 //------------------------------------------------------------------- 1399 1400 // Backends is the list of available backends. This is currently a hardcoded 1401 // list that can't be modified without recompiling Terraform. This is done 1402 // because the API for backends uses complex structures and supporting that 1403 // over the plugin system is currently prohibitively difficult. For those 1404 // wanting to implement a custom backend, recompilation should not be a 1405 // high barrier. 1406 var Backends map[string]func() backend.Backend 1407 1408 func init() { 1409 // Our hardcoded backends 1410 Backends = map[string]func() backend.Backend{ 1411 "local": func() backend.Backend { return &backendlocal.Local{} }, 1412 "consul": func() backend.Backend { return backendconsul.New() }, 1413 "inmem": func() backend.Backend { return backendinmem.New() }, 1414 } 1415 1416 // Add the legacy remote backends that haven't yet been convertd to 1417 // the new backend API. 1418 backendlegacy.Init(Backends) 1419 } 1420 1421 // errBackendInitRequired is the final error message shown when reinit 1422 // is required for some reason. The error message includes the reason. 1423 var errBackendInitRequired = errors.New( 1424 "Initialization required. Please see the error message above.") 1425 1426 const errBackendLegacyConfig = ` 1427 One or more errors occurred while configuring the legacy remote state. 1428 If fixing these errors requires changing your remote state configuration, 1429 you must switch your configuration to the new remote backend configuration. 1430 You can learn more about remote backends at the URL below: 1431 1432 TODO: URL 1433 1434 The error(s) configuring the legacy remote state: 1435 1436 %s 1437 ` 1438 1439 const errBackendLocalRead = ` 1440 Error reading local state: %s 1441 1442 Terraform is trying to read your local state to determine if there is 1443 state to migrate to your newly configured backend. Terraform can't continue 1444 without this check because that would risk losing state. Please resolve the 1445 error above and try again. 1446 ` 1447 1448 const errBackendMigrateLocalDelete = ` 1449 Error deleting local state after migration: %s 1450 1451 Your local state is deleted after successfully migrating it to the newly 1452 configured backend. As part of the deletion process, a backup is made at 1453 the standard backup path unless explicitly asked not to. To cleanly operate 1454 with a backend, we must delete the local state file. Please resolve the 1455 issue above and retry the command. 1456 ` 1457 1458 const errBackendMigrateNew = ` 1459 Error migrating local state to backend: %s 1460 1461 Your local state remains intact and unmodified. Please resolve the error 1462 above and try again. 1463 ` 1464 1465 const errBackendNewConfig = ` 1466 Error configuring the backend %q: %s 1467 1468 Please update the configuration in your Terraform files to fix this error 1469 then run this command again. 1470 ` 1471 1472 const errBackendNewRead = ` 1473 Error reading newly configured backend state: %s 1474 1475 Terraform is trying to read the state from your newly configured backend 1476 to determine the copy process for your existing state. Backends are expected 1477 to not error even if there is no state yet written. Please resolve the 1478 error above and try again. 1479 ` 1480 1481 const errBackendNewUnknown = ` 1482 The backend %q could not be found. 1483 1484 This is the backend specified in your Terraform configuration file. 1485 This error could be a simple typo in your configuration, but it can also 1486 be caused by using a Terraform version that doesn't support the specified 1487 backend type. Please check your configuration and your Terraform version. 1488 1489 If you'd like to run Terraform and store state locally, you can fix this 1490 error by removing the backend configuration from your configuration. 1491 ` 1492 1493 const errBackendRemoteRead = ` 1494 Error reading backend state: %s 1495 1496 Terraform is trying to read the state from your configured backend to 1497 determine if there is any migration steps necessary. Terraform can't continue 1498 without this check because that would risk losing state. Please resolve the 1499 error above and try again. 1500 ` 1501 1502 const errBackendSavedConfig = ` 1503 Error configuring the backend %q: %s 1504 1505 Please update the configuration in your Terraform files to fix this error. 1506 If you'd like to update the configuration interactively without storing 1507 the values in your configuration, run "terraform init". 1508 ` 1509 1510 const errBackendSavedUnsetConfig = ` 1511 Error configuring the existing backend %q: %s 1512 1513 Terraform must configure the existing backend in order to copy the state 1514 from the existing backend, as requested. Please resolve the error and try 1515 again. If you choose to not copy the existing state, Terraform will not 1516 configure the backend. If the configuration is invalid, please update your 1517 Terraform configuration with proper configuration for this backend first 1518 before unsetting the backend. 1519 ` 1520 1521 const errBackendSavedUnknown = ` 1522 The backend %q could not be found. 1523 1524 This is the backend that this Terraform environment is configured to use 1525 both in your configuration and saved locally as your last-used backend. 1526 If it isn't found, it could mean an alternate version of Terraform was 1527 used with this configuration. Please use the proper version of Terraform that 1528 contains support for this backend. 1529 1530 If you'd like to force remove this backend, you must update your configuration 1531 to not use the backend and run "terraform init" (or any other command) again. 1532 ` 1533 1534 const errBackendClearLegacy = ` 1535 Error clearing the legacy remote state configuration: %s 1536 1537 Terraform completed configuring your backend. It is now safe to remove 1538 the legacy remote state configuration, but an error occurred while trying 1539 to do so. Please look at the error above, resolve it, and try again. 1540 ` 1541 1542 const errBackendClearSaved = ` 1543 Error clearing the backend configuration: %s 1544 1545 Terraform removes the saved backend configuration when you're removing a 1546 configured backend. This must be done so future Terraform runs know to not 1547 use the backend configuration. Please look at the error above, resolve it, 1548 and try again. 1549 ` 1550 1551 const errBackendInit = ` 1552 [reset][bold][yellow]Backend reinitialization required. Please run "terraform init".[reset] 1553 [yellow]Reason: %s 1554 1555 The "backend" is the interface that Terraform uses to store state, 1556 perform operations, etc. If this message is showing up, it means that the 1557 Terraform configuration you're using is using a custom configuration for 1558 the Terraform backend. 1559 1560 Changes to backend configurations require reinitialization. This allows 1561 Terraform to setup the new configuration, copy existing state, etc. This is 1562 only done during "terraform init". Please run that command now then try again. 1563 1564 If the change reason above is incorrect, please verify your configuration 1565 hasn't changed and try again. At this point, no changes to your existing 1566 configuration or state have been made. 1567 ` 1568 1569 const errBackendWriteSaved = ` 1570 Error saving the backend configuration: %s 1571 1572 Terraform saves the complete backend configuration in a local file for 1573 configuring the backend on future operations. This cannot be disabled. Errors 1574 are usually due to simple file permission errors. Please look at the error 1575 above, resolve it, and try again. 1576 ` 1577 1578 const errBackendPlanBoth = ` 1579 The plan file contained both a legacy remote state and backend configuration. 1580 This is not allowed. Please recreate the plan file with the latest version of 1581 Terraform. 1582 ` 1583 1584 const errBackendPlanLineageDiff = ` 1585 The plan file contains a state with a differing lineage than the current 1586 state. By continuing, your current state would be overwritten by the state 1587 in the plan. Please either update the plan with the latest state or delete 1588 your current state and try again. 1589 1590 "Lineage" is a unique identifier generated only once on the creation of 1591 a new, empty state. If these values differ, it means they were created new 1592 at different times. Therefore, Terraform must assume that they're completely 1593 different states. 1594 1595 The most common cause of seeing this error is using a plan that was 1596 created against a different state. Perhaps the plan is very old and the 1597 state has since been recreated, or perhaps the plan was against a competely 1598 different infrastructure. 1599 ` 1600 1601 const errBackendPlanStateFlag = ` 1602 The -state and -state-out flags cannot be set with a plan that has a remote 1603 state. The plan itself contains the configuration for the remote backend to 1604 store state. The state will be written there for consistency. 1605 1606 If you wish to change this behavior, please create a plan from local state. 1607 You may use the state flags with plans from local state to affect where 1608 the final state is written. 1609 ` 1610 1611 const errBackendPlanOlder = ` 1612 This plan was created against an older state than is current. Please create 1613 a new plan file against the latest state and try again. 1614 1615 Terraform doesn't allow you to run plans that were created from older 1616 states since it doesn't properly represent the latest changes Terraform 1617 may have made, and can result in unsafe behavior. 1618 1619 Plan Serial: %[1]d 1620 Current Serial: %[2]d 1621 ` 1622 1623 const inputBackendMigrateChange = ` 1624 Would you like to copy the state from your prior backend %q to the 1625 newly configured %q backend? If you're reconfiguring the same backend, 1626 answering "yes" or "no" shouldn't make a difference. Please answer exactly 1627 "yes" or "no". 1628 ` 1629 1630 const inputBackendMigrateLegacy = ` 1631 Terraform can copy the existing state in your legacy remote state 1632 backend to your newly configured backend. Please answer "yes" or "no". 1633 ` 1634 1635 const inputBackendMigrateLegacyLocal = ` 1636 Terraform can copy the existing state in your legacy remote state 1637 backend to your local state. Please answer "yes" or "no". 1638 ` 1639 1640 const inputBackendMigrateLocal = ` 1641 Terraform has detected you're unconfiguring your previously set backend. 1642 Would you like to copy the state from %q to local state? Please answer 1643 "yes" or "no". If you answer "no", you will start with a blank local state. 1644 ` 1645 1646 const outputBackendConfigureWithLegacy = ` 1647 [reset][bold]New backend configuration detected with legacy remote state![reset] 1648 1649 Terraform has detected that you're attempting to configure a new backend. 1650 At the same time, legacy remote state configuration was found. Terraform will 1651 first configure the new backend, and then ask if you'd like to migrate 1652 your remote state to the new backend. 1653 ` 1654 1655 const outputBackendReconfigure = ` 1656 [reset][bold]Backend configuration changed![reset] 1657 1658 Terraform has detected that the configuration specified for the backend 1659 has changed. Terraform will now reconfigure for this backend. If you didn't 1660 intend to reconfigure your backend please undo any changes to the "backend" 1661 section in your Terraform configuration. 1662 ` 1663 1664 const outputBackendSavedWithLegacy = ` 1665 [reset][bold]Legacy remote state was detected![reset] 1666 1667 Terraform has detected you still have legacy remote state enabled while 1668 also having a backend configured. Terraform will now ask if you want to 1669 migrate your legacy remote state data to the configured backend. 1670 ` 1671 1672 const outputBackendSavedWithLegacyChanged = ` 1673 [reset][bold]Legacy remote state was detected while also changing your current backend!reset] 1674 1675 Terraform has detected that you have legacy remote state, a configured 1676 current backend, and you're attempting to reconfigure your backend. To handle 1677 all of these changes, Terraform will first reconfigure your backend. After 1678 this, Terraform will handle optionally copying your legacy remote state 1679 into the newly configured backend. 1680 ` 1681 1682 const outputBackendUnsetWithLegacy = ` 1683 [reset][bold]Detected a request to unset the backend with legacy remote state present![reset] 1684 1685 Terraform has detected that you're attempting to unset a previously configured 1686 backend (by not having the "backend" configuration set in your Terraform files). 1687 At the same time, legacy remote state was detected. To handle this complex 1688 scenario, Terraform will first unset your configured backend, and then 1689 ask you how to handle the legacy remote state. This will be multi-step 1690 process. 1691 ` 1692 1693 const successBackendLegacyUnset = ` 1694 Terraform has successfully migrated from legacy remote state to your 1695 configured remote state. 1696 ` 1697 1698 const successBackendReconfigureWithLegacy = ` 1699 Terraform has successfully reconfigured your backend and migrate 1700 from legacy remote state to the new backend. 1701 ` 1702 1703 const successBackendUnset = ` 1704 Successfully unset the backend %q. Terraform will now operate locally. 1705 ` 1706 1707 const successBackendSet = ` 1708 Successfully configured the backend %q! Terraform will automatically 1709 use this backend unless the backend configuration changes. 1710 ` 1711 1712 const warnBackendLegacy = ` 1713 Deprecation warning: This environment is configured to use legacy remote state. 1714 Remote state changed significantly in Terraform 0.9. Please update your remote 1715 state configuration to use the new 'backend' settings. For now, Terraform 1716 will continue to use your existing settings. Legacy remote state support 1717 will be removed in Terraform 0.11. 1718 `