func (c *RemoteConfigCommand) Run(args []string) int { args = c.Meta.process(args, false) config := make(map[string]string) cmdFlags := flag.NewFlagSet("remote", flag.ContinueOnError) cmdFlags.BoolVar(&c.conf.disableRemote, "disable", false, "") cmdFlags.BoolVar(&c.conf.pullOnDisable, "pull", true, "") cmdFlags.StringVar(&c.conf.statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&c.conf.backupPath, "backup", "", "path") cmdFlags.StringVar(&c.remoteConf.Type, "backend", "atlas", "") cmdFlags.Var((*FlagKV)(&config), "backend-config", "config") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { c.Ui.Error(fmt.Sprintf("\nError parsing CLI flags: %s", err)) return 1 } // Lowercase the type c.remoteConf.Type = strings.ToLower(c.remoteConf.Type) // Set the local state path c.statePath = c.conf.statePath // Populate the various configurations c.remoteConf.Config = config // Get the state information. We specifically request the cache only // for the remote state here because it is possible the remote state // is invalid and we don't want to error. stateOpts := c.StateOpts() stateOpts.RemoteCacheOnly = true if _, err := c.StateRaw(stateOpts); err != nil { c.Ui.Error(fmt.Sprintf("Error loading local state: %s", err)) return 1 } // Get the local and remote [cached] state localState := c.stateResult.Local.State() var remoteState *terraform.State if remote := c.stateResult.Remote; remote != nil { remoteState = remote.State() } // Check if remote state is being disabled if c.conf.disableRemote { if !remoteState.IsRemote() { c.Ui.Error(fmt.Sprintf("Remote state management not enabled! Aborting.")) return 1 } if !localState.Empty() { c.Ui.Error(fmt.Sprintf("State file already exists at '%s'. Aborting.", c.conf.statePath)) return 1 } return c.disableRemoteState() } // Ensure there is no conflict, and then do the correct operation var result int haveCache := !remoteState.Empty() haveLocal := !localState.Empty() switch { case haveCache && haveLocal: c.Ui.Error(fmt.Sprintf("Remote state is enabled, but non-managed state file '%s' is also present!", c.conf.statePath)) result = 1 case !haveCache && !haveLocal: // If we don't have either state file, initialize a blank state file result = c.initBlankState() case haveCache && !haveLocal: // Update the remote state target potentially result = c.updateRemoteConfig() case !haveCache && haveLocal: // Enable remote state management result = c.enableRemoteState() } // If there was an error, return right away if result != 0 { return result } // If we're not pulling, then do nothing if !c.conf.pullOnDisable { return result } // Otherwise, refresh the state stateResult, err := c.StateRaw(c.StateOpts()) if err != nil { c.Ui.Error(fmt.Sprintf( "Error while performing the initial pull. The error message is shown\n"+ "below. Note that remote state was properly configured, so you don't\n"+ "need to reconfigure. You can now use `push` and `pull` directly.\n"+ "\n%s", err)) return 1 } state := stateResult.State if err := state.RefreshState(); err != nil { c.Ui.Error(fmt.Sprintf( "Error while performing the initial pull. The error message is shown\n"+ "below. Note that remote state was properly configured, so you don't\n"+ "need to reconfigure. You can now use `push` and `pull` directly.\n"+ "\n%s", err)) return 1 } c.Ui.Output(c.Colorize().Color(fmt.Sprintf( "[reset][bold][green]Remote state configured and pulled."))) return 0 }
// Test performs an acceptance test on a resource. // // Tests are not run unless an environmental variable "TF_ACC" is // set to some non-empty value. This is to avoid test cases surprising // a user by creating real resources. // // Tests will fail unless the verbose flag (`go test -v`, or explicitly // the "-test.v" flag) is set. Because some acceptance tests take quite // long, we require the verbose flag so users are able to see progress // output. func Test(t TestT, c TestCase) { // We only run acceptance tests if an env var is set because they're // slow and generally require some outside configuration. if os.Getenv(TestEnvVar) == "" { t.Skip(fmt.Sprintf( "Acceptance tests skipped unless env '%s' set", TestEnvVar)) return } isUnitTest := (os.Getenv(TestEnvVar) == UnitTestOverride) logWriter, err := logging.LogOutput() if err != nil { t.Error(fmt.Errorf("error setting up logging: %s", err)) } log.SetOutput(logWriter) // We require verbose mode so that the user knows what is going on. if !testTesting && !testing.Verbose() && !isUnitTest { t.Fatal("Acceptance tests must be run with the -v flag on tests") return } // Run the PreCheck if we have it if c.PreCheck != nil { c.PreCheck() } // Build our context options that we can ctxProviders := c.ProviderFactories if ctxProviders == nil { ctxProviders = make(map[string]terraform.ResourceProviderFactory) for k, p := range c.Providers { ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p) } } opts := terraform.ContextOpts{Providers: ctxProviders} // A single state variable to track the lifecycle, starting with no state var state *terraform.State // Go through each step and run it var idRefreshCheck *terraform.ResourceState idRefresh := c.IDRefreshName != "" errored := false for i, step := range c.Steps { var err error log.Printf("[WARN] Test: Executing step %d", i) state, err = testStep(opts, state, step) if err != nil { errored = true t.Error(fmt.Sprintf( "Step %d error: %s", i, err)) break } // If we've never checked an id-only refresh and our state isn't // empty, find the first resource and test it. if idRefresh && idRefreshCheck == nil && !state.Empty() { // Find the first non-nil resource in the state for _, m := range state.Modules { if len(m.Resources) > 0 { if v, ok := m.Resources[c.IDRefreshName]; ok { idRefreshCheck = v } break } } // If we have an instance to check for refreshes, do it // immediately. We do it in the middle of another test // because it shouldn't affect the overall state (refresh // is read-only semantically) and we want to fail early if // this fails. If refresh isn't read-only, then this will have // caught a different bug. if idRefreshCheck != nil { log.Printf( "[WARN] Test: Running ID-only refresh check on %s", idRefreshCheck.Primary.ID) if err := testIDOnlyRefresh(c, opts, step, idRefreshCheck); err != nil { log.Printf("[ERROR] Test: ID-only test failed: %s", err) t.Error(fmt.Sprintf( "ID-Only refresh test failure: %s", err)) break } } } } // If we never checked an id-only refresh, it is a failure. if idRefresh { if !errored && len(c.Steps) > 0 && idRefreshCheck == nil { t.Error("ID-only refresh check never ran.") } } // If we have a state, then run the destroy if state != nil { destroyStep := TestStep{ Config: c.Steps[len(c.Steps)-1].Config, Check: c.CheckDestroy, Destroy: true, } log.Printf("[WARN] Test: Executing destroy step") state, err := testStep(opts, state, destroyStep) if err != nil { t.Error(fmt.Sprintf( "Error destroying resource! WARNING: Dangling resources\n"+ "may exist. The full state and error is shown below.\n\n"+ "Error: %s\n\nState: %s", err, state)) } } else { log.Printf("[WARN] Skipping destroy test since there is no state.") } }