diff --git a/command/apply.go b/command/apply.go index 859d0515f3..2a348f2cdf 100644 --- a/command/apply.go +++ b/command/apply.go @@ -8,6 +8,7 @@ import ( "sort" "strings" + "github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/terraform" ) @@ -35,6 +36,12 @@ func (c *ApplyCommand) Run(args []string) int { return 1 } + pwd, err := os.Getwd() + if err != nil { + c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err)) + return 1 + } + var configPath string args = cmdFlags.Args() if len(args) > 1 { @@ -44,11 +51,7 @@ func (c *ApplyCommand) Run(args []string) int { } 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)) - } + configPath = pwd } // Prepare the extra hooks to count resources @@ -67,6 +70,24 @@ func (c *ApplyCommand) Run(args []string) int { backupPath = stateOutPath + DefaultBackupExtention } + // Do a detect to determine if we need to do an init + apply. + if detected, err := module.Detect(configPath, pwd); err != nil { + c.Ui.Error(fmt.Sprintf( + "Invalid path: %s", err)) + return 1 + } else if !strings.HasPrefix(detected, "file") { + // If this isn't a file URL then we're doing an init + + // apply. + var init InitCommand + init.Meta = c.Meta + if code := init.Run([]string{detected}); code != 0 { + return code + } + + // Change the config path to be the cwd + configPath = pwd + } + // Build the context based on the arguments given ctx, planned, err := c.Context(contextOpts{ Path: configPath, diff --git a/command/apply_test.go b/command/apply_test.go index f745016d2f..4423b3b2ec 100644 --- a/command/apply_test.go +++ b/command/apply_test.go @@ -3,6 +3,9 @@ package command import ( "fmt" "io/ioutil" + "net" + "net/http" + "net/url" "os" "path/filepath" "reflect" @@ -196,6 +199,73 @@ func TestApply_error(t *testing.T) { } } +func TestApply_init(t *testing.T) { + // Change to the temporary directory + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("err: %s", err) + } + dir := tempDir(t) + if err := os.MkdirAll(dir, 0755); err != nil { + t.Fatalf("err: %s", err) + } + if err := os.Chdir(dir); err != nil { + t.Fatalf("err: %s", err) + } + defer os.Chdir(cwd) + + // Create the test fixtures + statePath := testTempFile(t) + ln := testHttpServer(t) + defer ln.Close() + + // Initialize the command + p := testProvider() + ui := new(cli.MockUi) + c := &ApplyCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + // Build the URL to the init + var u url.URL + u.Scheme = "http" + u.Host = ln.Addr().String() + u.Path = "/header" + + args := []string{ + "-state", statePath, + u.String(), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + if _, err := os.Stat("hello.tf"); err != nil { + t.Fatalf("err: %s", err) + } + + 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_noArgs(t *testing.T) { cwd, err := os.Getwd() if err != nil { @@ -942,6 +1012,31 @@ func TestApply_disableBackup(t *testing.T) { } } +func testHttpServer(t *testing.T) net.Listener { + ln, err := net.Listen("tcp", ":0") + if err != nil { + t.Fatalf("err: %s", err) + } + + mux := http.NewServeMux() + mux.HandleFunc("/header", testHttpHandlerHeader) + + var server http.Server + server.Handler = mux + go server.Serve(ln) + + return ln +} + +func testHttpHandlerHeader(w http.ResponseWriter, r *http.Request) { + var url url.URL + url.Scheme = "file" + url.Path = testFixturePath("init") + + w.Header().Add("X-Terraform-Get", url.String()) + w.WriteHeader(200) +} + const applyVarFile = ` foo = "bar" `