Merge pull request #337 from hashicorp/f-apply-init
`terraform apply MODULE` shorthand
This commit is contained in:
commit
d996959691
|
@ -8,6 +8,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/config/module"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,6 +36,12 @@ func (c *ApplyCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
var configPath string
|
var configPath string
|
||||||
args = cmdFlags.Args()
|
args = cmdFlags.Args()
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
|
@ -44,11 +51,7 @@ func (c *ApplyCommand) Run(args []string) int {
|
||||||
} else if len(args) == 1 {
|
} else if len(args) == 1 {
|
||||||
configPath = args[0]
|
configPath = args[0]
|
||||||
} else {
|
} else {
|
||||||
var err error
|
configPath = pwd
|
||||||
configPath, err = os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the extra hooks to count resources
|
// Prepare the extra hooks to count resources
|
||||||
|
@ -67,6 +70,24 @@ func (c *ApplyCommand) Run(args []string) int {
|
||||||
backupPath = stateOutPath + DefaultBackupExtention
|
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
|
// Build the context based on the arguments given
|
||||||
ctx, planned, err := c.Context(contextOpts{
|
ctx, planned, err := c.Context(contextOpts{
|
||||||
Path: configPath,
|
Path: configPath,
|
||||||
|
@ -229,10 +250,16 @@ func (c *ApplyCommand) Run(args []string) int {
|
||||||
|
|
||||||
func (c *ApplyCommand) Help() string {
|
func (c *ApplyCommand) Help() string {
|
||||||
helpText := `
|
helpText := `
|
||||||
Usage: terraform apply [options] [dir]
|
Usage: terraform apply [options] [DIR]
|
||||||
|
|
||||||
Builds or changes infrastructure according to Terraform configuration
|
Builds or changes infrastructure according to Terraform configuration
|
||||||
files .
|
files in DIR.
|
||||||
|
|
||||||
|
DIR can also be a SOURCE as given to the "init" command. In this case,
|
||||||
|
apply behaves as though "init" was called followed by "apply". This only
|
||||||
|
works for sources that aren't files, and only if the current working
|
||||||
|
directory is empty of Terraform files. This is a shortcut for getting
|
||||||
|
started.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@ package command
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"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) {
|
func TestApply_noArgs(t *testing.T) {
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
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 = `
|
const applyVarFile = `
|
||||||
foo = "bar"
|
foo = "bar"
|
||||||
`
|
`
|
||||||
|
|
|
@ -19,6 +19,11 @@ and applies the changes appropriately. However, a path to another configuration
|
||||||
or an execution plan can be provided. Execution plans can be used to only
|
or an execution plan can be provided. Execution plans can be used to only
|
||||||
execute a pre-determined set of actions.
|
execute a pre-determined set of actions.
|
||||||
|
|
||||||
|
The `dir` argument can also be a [module source](/docs/modules/index.html).
|
||||||
|
In this case, `apply` behaves as though `init` were called with that
|
||||||
|
argument followed by an `apply` in the current directory. This is meant
|
||||||
|
as a shortcut for getting started.
|
||||||
|
|
||||||
The command-line flags are all optional. The list of available flags are:
|
The command-line flags are all optional. The list of available flags are:
|
||||||
|
|
||||||
* `-backup=path` - Path to the backup file. Defaults to `-state-out` with
|
* `-backup=path` - Path to the backup file. Defaults to `-state-out` with
|
||||||
|
|
Loading…
Reference in New Issue