Ejemplo n.º 1
0
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
}
Ejemplo n.º 2
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")
	}
}
Ejemplo n.º 3
0
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
}