func (c *RemoteCommand) Run(args []string) int { args = c.Meta.process(args, false) var address, accessToken, name, path 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.StringVar(&address, "address", "", "") cmdFlags.StringVar(&accessToken, "access-token", "", "") cmdFlags.StringVar(&name, "name", "", "") cmdFlags.StringVar(&path, "path", "", "") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 } // Show help if given no inputs if !c.conf.disableRemote && c.remoteConf.Type == "atlas" && name == "" && accessToken == "" { cmdFlags.Usage() return 1 } // Populate the various configurations c.remoteConf.Config = map[string]string{ "address": address, "access_token": accessToken, "name": name, "path": path, } // Check if have an existing local state file haveLocal, err := remote.HaveLocalState() if err != nil { c.Ui.Error(fmt.Sprintf("Failed to check for local state: %v", err)) return 1 } // Check if we have the non-managed state file haveNonManaged, err := remote.ExistsFile(c.conf.statePath) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to check for state file: %v", err)) return 1 } // Check if remote state is being disabled if c.conf.disableRemote { if !haveLocal { c.Ui.Error(fmt.Sprintf("Remote state management not enabled! Aborting.")) return 1 } if haveNonManaged { 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 switch { case haveLocal && haveNonManaged: c.Ui.Error(fmt.Sprintf("Remote state is enabled, but non-managed state file '%s' is also present!", c.conf.statePath)) return 1 case !haveLocal && !haveNonManaged: // If we don't have either state file, initialize a blank state file return c.initBlankState() case haveLocal && !haveNonManaged: // Update the remote state target potentially return c.updateRemoteConfig() case !haveLocal && haveNonManaged: // Enable remote state management return c.enableRemoteState() default: panic("unhandled case") } return 0 }
// Test disabling remote management without pulling func TestRemote_disable_noPull(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) // Create remote state file, this should be pulled s := terraform.NewState() s.Serial = 10 conf, srv := testRemoteState(t, s, 200) defer srv.Close() // Persist local remote state s = terraform.NewState() s.Serial = 5 s.Remote = conf if err := remote.EnsureDirectory(); err != nil { t.Fatalf("err: %v", err) } if err := remote.PersistState(s); err != nil { t.Fatalf("err: %v", err) } ui := new(cli.MockUi) c := &RemoteCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, }, } args := []string{"-disable", "-pull=false"} if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } // Local state file should be removed haveLocal, err := remote.HaveLocalState() if err != nil { t.Fatalf("err: %v", err) } if haveLocal { t.Fatalf("should be disabled") } // New state file should be installed exists, err := remote.ExistsFile(DefaultStateFilename) if err != nil { t.Fatalf("err: %v", err) } if !exists { t.Fatalf("failed to make state file") } // Check that the state file was updated raw, _ := ioutil.ReadFile(DefaultStateFilename) newState, err := terraform.ReadState(bytes.NewReader(raw)) if err != nil { t.Fatalf("err: %v", err) } // Ensure we DIDNT updated // TODO: Should be 5, but WriteState currently increments // this which is incorrect. if newState.Serial != 7 { t.Fatalf("state file updated: %#v", newState) } if newState.Remote != nil { t.Fatalf("remote configuration not removed") } }
func (c *RefreshCommand) Run(args []string) int { args = c.Meta.process(args, true) cmdFlags := c.Meta.flagSet("refresh") cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path") cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 } var configPath string args = cmdFlags.Args() if len(args) > 1 { c.Ui.Error("The apply command expacts at most one argument.") cmdFlags.Usage() return 1 } else if len(args) == 1 { configPath = args[0] } else { var err error configPath, err = os.Getwd() if err != nil { c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err)) } } // Check if remote state is enabled remoteEnabled, err := remote.HaveLocalState() if err != nil { c.Ui.Error(fmt.Sprintf("Failed to check for remote state: %v", err)) return 1 } // Verify that the state path exists. The "ContextArg" function below // will actually do this, but we want to provide a richer error message // if possible. if !remoteEnabled { if _, err := os.Stat(c.Meta.statePath); err != nil { if os.IsNotExist(err) { c.Ui.Error(fmt.Sprintf( "The Terraform state file for your infrastructure does not\n"+ "exist. The 'refresh' command only works and only makes sense\n"+ "when there is existing state that Terraform is managing. Please\n"+ "double-check the value given below and try again. If you\n"+ "haven't created infrastructure with Terraform yet, use the\n"+ "'terraform apply' command.\n\n"+ "Path: %s", c.Meta.statePath)) return 1 } c.Ui.Error(fmt.Sprintf( "There was an error reading the Terraform state that is needed\n"+ "for refreshing. The path and error are shown below.\n\n"+ "Path: %s\n\nError: %s", c.Meta.statePath, err)) return 1 } } // Build the context based on the arguments given ctx, _, err := c.Context(contextOpts{ Path: configPath, StatePath: c.Meta.statePath, }) if err != nil { c.Ui.Error(err.Error()) return 1 } if !validateContext(ctx, c.Ui) { return 1 } if err := ctx.Input(c.InputMode()); err != nil { c.Ui.Error(fmt.Sprintf("Error configuring: %s", err)) return 1 } state, err := ctx.Refresh() if err != nil { c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err)) return 1 } log.Printf("[INFO] Writing state output to: %s", c.Meta.StateOutPath()) if err := c.Meta.PersistState(state); err != nil { c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err)) return 1 } return 0 }