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