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 {
var init bool
var stateOutPath string
var statePath, stateOutPath string
cmdFlags := flag.NewFlagSet("apply", flag.ContinueOnError)
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()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
}
args = cmdFlags.Args()
if len(args) != 2 {
c.Ui.Error("The apply command expects two arguments.\n")
if len(args) > 1 {
c.Ui.Error("The apply command expacts at most one argument.")
cmdFlags.Usage()
return 1
}
configPath := args[0]
statePath := args[0]
configPath := args[1]
// If we don't specify an output path, default to out normal state
// path.
if stateOutPath == "" {
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
if init {
planStatePath = ""
}
// Initialize Terraform right away
// Build the context based on the arguments given
c.ContextOpts.Hooks = append(c.ContextOpts.Hooks, &UiHook{Ui: c.Ui})
ctx, err := ContextArg(configPath, planStatePath, c.ContextOpts)
if err != nil {
@ -113,18 +116,24 @@ func (c *ApplyCommand) Run(args []string) int {
func (c *ApplyCommand) Help() string {
helpText := `
Usage: terraform apply [options] STATE PATH
Usage: terraform apply [options] [dir]
Builds or changes infrastructure according to the Terraform configuration
file.
Builds or changes infrastructure according to Terraform configuration
files .
Options:
-init If specified, it is okay to build brand new
infrastructure (with no state file specified).
-init If specified, new infrastructure can be built (no
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 argument will be used.
-state=path Path to read and save state (unless state-out
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)

View File

@ -2,7 +2,9 @@ package command
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"sync"
"testing"
@ -25,7 +27,7 @@ func TestApply(t *testing.T) {
args := []string{
"-init",
statePath,
"-state", statePath,
testFixturePath("apply"),
}
if code := c.Run(args); code != 0 {
@ -61,7 +63,7 @@ func TestApply_configInvalid(t *testing.T) {
args := []string{
"-init",
testTempFile(t),
"-state", testTempFile(t),
testFixturePath("apply-config-invalid"),
}
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) {
statePath := testTempFile(t)
@ -108,7 +161,7 @@ func TestApply_error(t *testing.T) {
args := []string{
"-init",
statePath,
"-state", statePath,
testFixturePath("apply-error"),
}
if code := c.Run(args); code != 1 {
@ -151,7 +204,7 @@ func TestApply_plan(t *testing.T) {
}
args := []string{
statePath,
"-state", statePath,
planPath,
}
if code := c.Run(args); code != 0 {
@ -235,7 +288,7 @@ func TestApply_shutdown(t *testing.T) {
args := []string{
"-init",
statePath,
"-state", statePath,
testFixturePath("apply-shutdown"),
}
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
args := []string{
statePath,
"-state", statePath,
testFixturePath("apply"),
}
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 {
return nil, fmt.Errorf("Error loading config: %s", err)
}