command/apply: default state path, only one optional arg

This commit is contained in:
Mitchell Hashimoto 2014-07-11 21:30:40 -07:00
parent 7a01e781ab
commit abc6df2a7d
3 changed files with 84 additions and 22 deletions

View File

@ -20,36 +20,39 @@ type ApplyCommand struct {
func (c *ApplyCommand) Run(args []string) int { func (c *ApplyCommand) Run(args []string) int {
var init bool var init bool
var stateOutPath string var statePath, stateOutPath string
cmdFlags := flag.NewFlagSet("apply", flag.ContinueOnError) cmdFlags := flag.NewFlagSet("apply", flag.ContinueOnError)
cmdFlags.BoolVar(&init, "init", false, "init") cmdFlags.BoolVar(&init, "init", false, "init")
cmdFlags.StringVar(&stateOutPath, "out", "", "path") cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path")
cmdFlags.StringVar(&stateOutPath, "state-out", "", "path")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil { if err := cmdFlags.Parse(args); err != nil {
return 1 return 1
} }
args = cmdFlags.Args() args = cmdFlags.Args()
if len(args) != 2 { if len(args) > 1 {
c.Ui.Error("The apply command expects two arguments.\n") c.Ui.Error("The apply command expacts at most one argument.")
cmdFlags.Usage() cmdFlags.Usage()
return 1 return 1
} }
configPath := args[0]
statePath := args[0] // If we don't specify an output path, default to out normal state
configPath := args[1] // path.
if stateOutPath == "" { if stateOutPath == "" {
stateOutPath = statePath stateOutPath = statePath
} }
// The state path to use to generate a plan. If we're initializing
// a new infrastructure, then we don't use a state path.
planStatePath := statePath planStatePath := statePath
if init { if init {
planStatePath = "" planStatePath = ""
} }
// Initialize Terraform right away // Build the context based on the arguments given
c.ContextOpts.Hooks = append(c.ContextOpts.Hooks, &UiHook{Ui: c.Ui}) c.ContextOpts.Hooks = append(c.ContextOpts.Hooks, &UiHook{Ui: c.Ui})
ctx, err := ContextArg(configPath, planStatePath, c.ContextOpts) ctx, err := ContextArg(configPath, planStatePath, c.ContextOpts)
if err != nil { if err != nil {
@ -113,18 +116,24 @@ func (c *ApplyCommand) Run(args []string) int {
func (c *ApplyCommand) Help() string { func (c *ApplyCommand) Help() string {
helpText := ` helpText := `
Usage: terraform apply [options] STATE PATH Usage: terraform apply [options] [dir]
Builds or changes infrastructure according to the Terraform configuration Builds or changes infrastructure according to Terraform configuration
file. files .
Options: Options:
-init If specified, it is okay to build brand new -init If specified, new infrastructure can be built (no
infrastructure (with no state file specified). previous state). This is just a safety switch
to prevent accidentally spinning up a new
infrastructure.
-out=file.tfstate Path to save the new state. If not specified, the -state=path Path to read and save state (unless state-out
state path argument will be used. is specified). Defaults to "terraform.tfstate".
-state-out=path Path to write state to that is different than
"-state". This can be used to preserve the old
state.
` `
return strings.TrimSpace(helpText) return strings.TrimSpace(helpText)

View File

@ -2,7 +2,9 @@ package command
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath"
"reflect" "reflect"
"sync" "sync"
"testing" "testing"
@ -25,7 +27,7 @@ func TestApply(t *testing.T) {
args := []string{ args := []string{
"-init", "-init",
statePath, "-state", statePath,
testFixturePath("apply"), testFixturePath("apply"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
@ -61,7 +63,7 @@ func TestApply_configInvalid(t *testing.T) {
args := []string{ args := []string{
"-init", "-init",
testTempFile(t), "-state", testTempFile(t),
testFixturePath("apply-config-invalid"), testFixturePath("apply-config-invalid"),
} }
if code := c.Run(args); code != 1 { if code := c.Run(args); code != 1 {
@ -69,6 +71,57 @@ func TestApply_configInvalid(t *testing.T) {
} }
} }
func TestApply_defaultState(t *testing.T) {
td, err := ioutil.TempDir("", "tf")
if err != nil {
t.Fatalf("err: %s", err)
}
statePath := filepath.Join(td, DefaultStateFilename)
// Change to the temporary directory
cwd, err := os.Getwd()
if err != nil {
t.Fatalf("err: %s", err)
}
if err := os.Chdir(filepath.Dir(statePath)); err != nil {
t.Fatalf("err: %s", err)
}
defer os.Chdir(cwd)
p := testProvider()
ui := new(cli.MockUi)
c := &ApplyCommand{
ContextOpts: testCtxConfig(p),
Ui: ui,
}
args := []string{
"-init",
testFixturePath("apply"),
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
if _, err := os.Stat(statePath); err != nil {
t.Fatalf("err: %s", err)
}
f, err := os.Open(statePath)
if err != nil {
t.Fatalf("err: %s", err)
}
defer f.Close()
state, err := terraform.ReadState(f)
if err != nil {
t.Fatalf("err: %s", err)
}
if state == nil {
t.Fatal("state should not be nil")
}
}
func TestApply_error(t *testing.T) { func TestApply_error(t *testing.T) {
statePath := testTempFile(t) statePath := testTempFile(t)
@ -108,7 +161,7 @@ func TestApply_error(t *testing.T) {
args := []string{ args := []string{
"-init", "-init",
statePath, "-state", statePath,
testFixturePath("apply-error"), testFixturePath("apply-error"),
} }
if code := c.Run(args); code != 1 { if code := c.Run(args); code != 1 {
@ -151,7 +204,7 @@ func TestApply_plan(t *testing.T) {
} }
args := []string{ args := []string{
statePath, "-state", statePath,
planPath, planPath,
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
@ -235,7 +288,7 @@ func TestApply_shutdown(t *testing.T) {
args := []string{ args := []string{
"-init", "-init",
statePath, "-state", statePath,
testFixturePath("apply-shutdown"), testFixturePath("apply-shutdown"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {
@ -294,7 +347,7 @@ func TestApply_state(t *testing.T) {
// Run the apply command pointing to our existing state // Run the apply command pointing to our existing state
args := []string{ args := []string{
statePath, "-state", statePath,
testFixturePath("apply"), testFixturePath("apply"),
} }
if code := c.Run(args); code != 0 { if code := c.Run(args); code != 0 {

View File

@ -55,7 +55,7 @@ func ContextArg(
} }
} }
config, err := config.Load(path) config, err := config.LoadDir(path)
if err != nil { if err != nil {
return nil, fmt.Errorf("Error loading config: %s", err) return nil, fmt.Errorf("Error loading config: %s", err)
} }