From 57c1a8dada2990829a4fbb96461f7b244c60ffc0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 26 Sep 2014 15:49:51 -0700 Subject: [PATCH 01/10] config: add IsEmptyDir --- config/loader.go | 121 ++++++++++++++++++++++++------------------ config/loader_test.go | 20 +++++++ 2 files changed, 88 insertions(+), 53 deletions(-) diff --git a/config/loader.go b/config/loader.go index 5cc27e998c..da74e97621 100644 --- a/config/loader.go +++ b/config/loader.go @@ -42,62 +42,10 @@ func Load(path string) (*Config, error) { // // Files are loaded in lexical order. func LoadDir(root string) (*Config, error) { - var files, overrides []string - - f, err := os.Open(root) - if err != nil { - return nil, err - } - - fi, err := f.Stat() + files, overrides, err := dirFiles(root) if err != nil { return nil, err } - if !fi.IsDir() { - return nil, fmt.Errorf( - "configuration path must be a directory: %s", - root) - } - - err = nil - for err != io.EOF { - var fis []os.FileInfo - fis, err = f.Readdir(128) - if err != nil && err != io.EOF { - f.Close() - return nil, err - } - - for _, fi := range fis { - // Ignore directories - if fi.IsDir() { - continue - } - - // Only care about files that are valid to load - name := fi.Name() - extValue := ext(name) - if extValue == "" { - continue - } - - // Determine if we're dealing with an override - nameNoExt := name[:len(name)-len(extValue)] - override := nameNoExt == "override" || - strings.HasSuffix(nameNoExt, "_override") - - path := filepath.Join(root, name) - if override { - overrides = append(overrides, path) - } else { - files = append(files, path) - } - } - } - - // Close the directory, we're done with it - f.Close() - if len(files) == 0 { return nil, fmt.Errorf( "No Terraform configuration files found in directory: %s", @@ -152,6 +100,17 @@ func LoadDir(root string) (*Config, error) { return result, nil } +// IsEmptyDir returns true if the directory given has no Terraform +// configuration files. +func IsEmptyDir(root string) (bool, error) { + fs, os, err := dirFiles(root) + if err != nil { + return false, err + } + + return len(fs) == 0 && len(os) == 0, nil +} + // Ext returns the Terraform configuration extension of the given // path, or a blank string if it is an invalid function. func ext(path string) string { @@ -163,3 +122,59 @@ func ext(path string) string { return "" } } + +func dirFiles(dir string) ([]string, []string, error) { + f, err := os.Open(dir) + if err != nil { + return nil, nil, err + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + return nil, nil, err + } + if !fi.IsDir() { + return nil, nil, fmt.Errorf( + "configuration path must be a directory: %s", + dir) + } + + var files, overrides []string + err = nil + for err != io.EOF { + var fis []os.FileInfo + fis, err = f.Readdir(128) + if err != nil && err != io.EOF { + return nil, nil, err + } + + for _, fi := range fis { + // Ignore directories + if fi.IsDir() { + continue + } + + // Only care about files that are valid to load + name := fi.Name() + extValue := ext(name) + if extValue == "" { + continue + } + + // Determine if we're dealing with an override + nameNoExt := name[:len(name)-len(extValue)] + override := nameNoExt == "override" || + strings.HasSuffix(nameNoExt, "_override") + + path := filepath.Join(dir, name) + if override { + overrides = append(overrides, path) + } else { + files = append(files, path) + } + } + } + + return files, overrides, nil +} diff --git a/config/loader_test.go b/config/loader_test.go index f95235d66e..da4a2dd8ff 100644 --- a/config/loader_test.go +++ b/config/loader_test.go @@ -6,6 +6,26 @@ import ( "testing" ) +func TestIsEmptyDir(t *testing.T) { + val, err := IsEmptyDir(fixtureDir) + if err != nil { + t.Fatalf("err: %s", err) + } + if val { + t.Fatal("should not be empty") + } +} + +func TestIsEmptyDir_noConfigs(t *testing.T) { + val, err := IsEmptyDir(filepath.Join(fixtureDir, "dir-empty")) + if err != nil { + t.Fatalf("err: %s", err) + } + if !val { + t.Fatal("should be empty") + } +} + func TestLoad_badType(t *testing.T) { _, err := Load(filepath.Join(fixtureDir, "bad_type.tf.nope")) if err == nil { From 615192a6c4b99a1f52c69a2f4c66615e11ad257a Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 26 Sep 2014 16:03:39 -0700 Subject: [PATCH 02/10] command: init command start --- command/init.go | 102 +++++++++++++++++++++++++++++++++++++++++++ command/init_test.go | 67 ++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 command/init.go create mode 100644 command/init_test.go diff --git a/command/init.go b/command/init.go new file mode 100644 index 0000000000..dc7afda3a9 --- /dev/null +++ b/command/init.go @@ -0,0 +1,102 @@ +package command + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/hashicorp/terraform/config" + "github.com/hashicorp/terraform/config/module" +) + +// InitCommand is a Command implementation that takes a Terraform +// module and clones it to the working directory. +type InitCommand struct { + Meta +} + +func (c *InitCommand) Run(args []string) int { + args = c.Meta.process(args, false) + + cmdFlags := flag.NewFlagSet("init", flag.ContinueOnError) + cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } + if err := cmdFlags.Parse(args); err != nil { + return 1 + } + + var path string + args = cmdFlags.Args() + if len(args) > 2 { + c.Ui.Error("The init command expects at most two arguments.\n") + cmdFlags.Usage() + return 1 + } else if len(args) < 1 { + c.Ui.Error("The init command expects at least one arguments.\n") + cmdFlags.Usage() + return 1 + } + + if len(args) == 2 { + path = args[1] + } else { + var err error + path, err = os.Getwd() + if err != nil { + c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err)) + } + } + + source := args[0] + + // Verify the directory is empty + if empty, err := config.IsEmptyDir(path); err != nil { + c.Ui.Error(fmt.Sprintf( + "Error checking on destination path: %s", err)) + return 1 + } else if !empty { + c.Ui.Error( + "The destination path has Terraform configuration files. The\n" + + "init command can only be used on a directory without existing Terraform\n" + + "files.") + return 1 + } + + // Create a temporary directory to store our module + td, err := ioutil.TempDir("", "tf") + if err != nil { + c.Ui.Error(fmt.Sprintf( + "Error creating temporary directory: %s", err)) + return 1 + } + defer os.RemoveAll(td) + + // Get it! + if err := module.Get(td, source); err != nil { + c.Ui.Error(err.Error()) + return 1 + } + + return 0 +} + +func (c *InitCommand) Help() string { + helpText := ` +Usage: terraform init [options] SOURCE [PATH] + + Downloads the module given by SOURCE into the PATH. The PATH defaults + to the working directory. PATH must be empty of any Terraform files. + Any conflicting non-Terraform files will be overwritten. + + The module downloaded is a copy. If you're downloading a module from + Git, it will not preserve the Git history, it will only copy the + latest files. + +` + return strings.TrimSpace(helpText) +} + +func (c *InitCommand) Synopsis() string { + return "Initializes Terraform configuration from a module" +} diff --git a/command/init_test.go b/command/init_test.go new file mode 100644 index 0000000000..7238bc6ce7 --- /dev/null +++ b/command/init_test.go @@ -0,0 +1,67 @@ +package command + +import ( + "testing" + + "github.com/mitchellh/cli" +) + +/* +func TestGet(t *testing.T) { + ui := new(cli.MockUi) + c := &GetCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + } + + args := []string{ + testFixturePath("get"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + + output := ui.OutputWriter.String() + if !strings.Contains(output, "Get: file://") { + t.Fatalf("doesn't look like get: %s", output) + } + if strings.Contains(output, "(update)") { + t.Fatalf("doesn't look like get: %s", output) + } +} +*/ + +func TestInit_multipleArgs(t *testing.T) { + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + } + + args := []string{ + "bad", + "bad", + } + if code := c.Run(args); code != 1 { + t.Fatalf("bad: \n%s", ui.OutputWriter.String()) + } +} + +func TestInit_noArgs(t *testing.T) { + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + } + + args := []string{} + if code := c.Run(args); code != 1 { + t.Fatalf("bad: \n%s", ui.OutputWriter.String()) + } +} From c91fd76fe8dc9349026c25369e4aba64c590838f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 26 Sep 2014 16:11:13 -0700 Subject: [PATCH 03/10] config/module: Get can support subdirs --- config/module/copy_dir.go | 9 ++++++++- config/module/get.go | 33 ++++++++++++++++++++++++++++++++- config/module/get_test.go | 14 ++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/config/module/copy_dir.go b/config/module/copy_dir.go index 6d4cb82017..6408913e77 100644 --- a/config/module/copy_dir.go +++ b/config/module/copy_dir.go @@ -4,6 +4,7 @@ import ( "io" "os" "path/filepath" + "strings" ) // copyDir copies the src directory contents into dst. Both directories @@ -14,7 +15,13 @@ func copyDir(dst, src string) error { return err } - dstPath := filepath.Join(dst, filepath.Base(path)) + basePath := filepath.Base(path) + if strings.HasPrefix(basePath, ".") { + // Skip any dot files + return nil + } + + dstPath := filepath.Join(dst, basePath) // If we have a directory, make that subdirectory, then continue // the walk. diff --git a/config/module/get.go b/config/module/get.go index cd553f2671..bb811d63e6 100644 --- a/config/module/get.go +++ b/config/module/get.go @@ -3,8 +3,11 @@ package module import ( "bytes" "fmt" + "io/ioutil" "net/url" + "os" "os/exec" + "path/filepath" "regexp" "strings" "syscall" @@ -51,6 +54,21 @@ func Get(dst, src string) error { var force string force, src = getForcedGetter(src) + // If there is a subdir component, then we download the root separately + // and then copy over the proper subdir. + var realDst string + src, subDir := getDirSubdir(src) + if subDir != "" { + tmpDir, err := ioutil.TempDir("", "tf") + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + realDst = dst + dst = subDir + } + u, err := url.Parse(src) if err != nil { return err @@ -68,9 +86,22 @@ func Get(dst, src string) error { err = g.Get(dst, u) if err != nil { err = fmt.Errorf("error downloading module '%s': %s", src, err) + return err + } + + // If we have a subdir, copy that over + if subDir != "" { + if err := os.RemoveAll(realDst); err != nil { + return err + } + if err := os.MkdirAll(realDst, 0755); err != nil { + return err + } + + return copyDir(realDst, filepath.Join(dst, subDir)) } - return err + return nil } // getRunCommand is a helper that will run a command and capture the output diff --git a/config/module/get_test.go b/config/module/get_test.go index 85488577db..8721694823 100644 --- a/config/module/get_test.go +++ b/config/module/get_test.go @@ -46,6 +46,20 @@ func TestGet_fileForced(t *testing.T) { } } +func TestGet_fileSubdir(t *testing.T) { + dst := tempDir(t) + u := testModule("basic//subdir") + + if err := Get(dst, u); err != nil { + t.Fatalf("err: %s", err) + } + + mainPath := filepath.Join(dst, "sub.tf") + if _, err := os.Stat(mainPath); err != nil { + t.Fatalf("err: %s", err) + } +} + func TestGetDirSubdir(t *testing.T) { cases := []struct { Input string From 9689a34b285c999610fad3bf3069f90e470d9dda Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 26 Sep 2014 16:21:33 -0700 Subject: [PATCH 04/10] config/module: GetCopy --- config/module/copy_dir.go | 8 ++++++++ config/module/get.go | 33 +++++++++++++++++++++++++++++++++ config/module/get_test.go | 14 ++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/config/module/copy_dir.go b/config/module/copy_dir.go index 6408913e77..981bba1ba7 100644 --- a/config/module/copy_dir.go +++ b/config/module/copy_dir.go @@ -10,10 +10,18 @@ import ( // copyDir copies the src directory contents into dst. Both directories // should already exist. func copyDir(dst, src string) error { + src, err := filepath.EvalSymlinks(src) + if err != nil { + return err + } + walkFn := func(path string, info os.FileInfo, err error) error { if err != nil { return err } + if path == src { + return nil + } basePath := filepath.Base(path) if strings.HasPrefix(basePath, ".") { diff --git a/config/module/get.go b/config/module/get.go index bb811d63e6..14f105548c 100644 --- a/config/module/get.go +++ b/config/module/get.go @@ -63,6 +63,9 @@ func Get(dst, src string) error { if err != nil { return err } + if err := os.RemoveAll(tmpDir); err != nil { + return err + } defer os.RemoveAll(tmpDir) realDst = dst @@ -104,6 +107,36 @@ func Get(dst, src string) error { return nil } +// GetCopy is the same as Get except that it downloads a copy of the +// module represented by source. +// +// This copy will omit and dot-prefixed files (such as .git/, .hg/) and +// can't be updated on its own. +func GetCopy(dst, src string) error { + // Create the temporary directory to do the real Get to + tmpDir, err := ioutil.TempDir("", "tf") + if err != nil { + return err + } + if err := os.RemoveAll(tmpDir); err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + // Get to that temporary dir + if err := Get(tmpDir, src); err != nil { + return err + } + + // Make sure the destination exists + if err := os.MkdirAll(dst, 0755); err != nil { + return err + } + + // Copy to the final location + return copyDir(dst, tmpDir) +} + // getRunCommand is a helper that will run a command and capture the output // in the case an error happens. func getRunCommand(cmd *exec.Cmd) error { diff --git a/config/module/get_test.go b/config/module/get_test.go index 8721694823..cf34f1ae8a 100644 --- a/config/module/get_test.go +++ b/config/module/get_test.go @@ -60,6 +60,20 @@ func TestGet_fileSubdir(t *testing.T) { } } +func TestGetCopy_file(t *testing.T) { + dst := tempDir(t) + u := testModule("basic") + + if err := GetCopy(dst, u); err != nil { + t.Fatalf("err: %s", err) + } + + mainPath := filepath.Join(dst, "main.tf") + if _, err := os.Stat(mainPath); err != nil { + t.Fatalf("err: %s", err) + } +} + func TestGetDirSubdir(t *testing.T) { cases := []struct { Input string From 6cbadf14df5f3e6019cf97b3bc8b9ff7ea5c630d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 26 Sep 2014 16:28:18 -0700 Subject: [PATCH 05/10] config: IsEmptyDir is true if dir doesn't exist --- config/loader.go | 4 ++++ config/loader_test.go | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/config/loader.go b/config/loader.go index da74e97621..740d0ec5f6 100644 --- a/config/loader.go +++ b/config/loader.go @@ -103,6 +103,10 @@ func LoadDir(root string) (*Config, error) { // IsEmptyDir returns true if the directory given has no Terraform // configuration files. func IsEmptyDir(root string) (bool, error) { + if _, err := os.Stat(root); err != nil && os.IsNotExist(err) { + return true, nil + } + fs, os, err := dirFiles(root) if err != nil { return false, err diff --git a/config/loader_test.go b/config/loader_test.go index da4a2dd8ff..70dba0a166 100644 --- a/config/loader_test.go +++ b/config/loader_test.go @@ -16,6 +16,16 @@ func TestIsEmptyDir(t *testing.T) { } } +func TestIsEmptyDir_noExist(t *testing.T) { + val, err := IsEmptyDir(filepath.Join(fixtureDir, "nopenopenope")) + if err != nil { + t.Fatalf("err: %s", err) + } + if !val { + t.Fatal("should be empty") + } +} + func TestIsEmptyDir_noConfigs(t *testing.T) { val, err := IsEmptyDir(filepath.Join(fixtureDir, "dir-empty")) if err != nil { From f4cc2c066fa0385e974adab13cfb415175b552ea Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 26 Sep 2014 16:30:49 -0700 Subject: [PATCH 06/10] command: init works at a basic level --- command/init.go | 18 ++++++++++++------ command/init_test.go | 21 ++++++++++----------- command/test-fixtures/init/hello.tf | 0 3 files changed, 22 insertions(+), 17 deletions(-) create mode 100644 command/test-fixtures/init/hello.tf diff --git a/command/init.go b/command/init.go index dc7afda3a9..2a9c6f45d9 100644 --- a/command/init.go +++ b/command/init.go @@ -3,7 +3,6 @@ package command import ( "flag" "fmt" - "io/ioutil" "os" "strings" @@ -50,6 +49,14 @@ func (c *InitCommand) Run(args []string) int { source := args[0] + // Get our pwd since we need it + pwd, err := os.Getwd() + if err != nil { + c.Ui.Error(fmt.Sprintf( + "Error reading working directory: %s", err)) + return 1 + } + // Verify the directory is empty if empty, err := config.IsEmptyDir(path); err != nil { c.Ui.Error(fmt.Sprintf( @@ -63,17 +70,16 @@ func (c *InitCommand) Run(args []string) int { return 1 } - // Create a temporary directory to store our module - td, err := ioutil.TempDir("", "tf") + // Detect + source, err = module.Detect(source, pwd) if err != nil { c.Ui.Error(fmt.Sprintf( - "Error creating temporary directory: %s", err)) + "Error with module source: %s", err)) return 1 } - defer os.RemoveAll(td) // Get it! - if err := module.Get(td, source); err != nil { + if err := module.GetCopy(path, source); err != nil { c.Ui.Error(err.Error()) return 1 } diff --git a/command/init_test.go b/command/init_test.go index 7238bc6ce7..c1cc56a37e 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -1,15 +1,18 @@ package command import ( + "os" + "path/filepath" "testing" "github.com/mitchellh/cli" ) -/* -func TestGet(t *testing.T) { +func TestInit(t *testing.T) { + dir := tempDir(t) + ui := new(cli.MockUi) - c := &GetCommand{ + c := &InitCommand{ Meta: Meta{ ContextOpts: testCtxConfig(testProvider()), Ui: ui, @@ -17,21 +20,17 @@ func TestGet(t *testing.T) { } args := []string{ - testFixturePath("get"), + testFixturePath("init"), + dir, } if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) } - output := ui.OutputWriter.String() - if !strings.Contains(output, "Get: file://") { - t.Fatalf("doesn't look like get: %s", output) - } - if strings.Contains(output, "(update)") { - t.Fatalf("doesn't look like get: %s", output) + if _, err := os.Stat(filepath.Join(dir, "hello.tf")); err != nil { + t.Fatalf("err: %s", err) } } -*/ func TestInit_multipleArgs(t *testing.T) { ui := new(cli.MockUi) diff --git a/command/test-fixtures/init/hello.tf b/command/test-fixtures/init/hello.tf new file mode 100644 index 0000000000..e69de29bb2 From e041a52dab1d1a248170eaea66683395b921731b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 27 Sep 2014 09:23:02 -0700 Subject: [PATCH 07/10] command: test init --- command/init_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/command/init_test.go b/command/init_test.go index c1cc56a37e..2db54b0477 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -32,6 +32,42 @@ func TestInit(t *testing.T) { } } +func TestInit_cwd(t *testing.T) { + dir := tempDir(t) + if err := os.MkdirAll(dir, 0755); err != nil { + t.Fatalf("err: %s", err) + } + + // Change to the temporary directory + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("err: %s", err) + } + if err := os.Chdir(dir); err != nil { + t.Fatalf("err: %s", err) + } + defer os.Chdir(cwd) + + ui := new(cli.MockUi) + c := &InitCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + } + + args := []string{ + testFixturePath("init"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) + } + + if _, err := os.Stat("hello.tf"); err != nil { + t.Fatalf("err: %s", err) + } +} + func TestInit_multipleArgs(t *testing.T) { ui := new(cli.MockUi) c := &InitCommand{ From 0a2d06268cc234fe95464315f0497ddd8233b606 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 27 Sep 2014 09:29:12 -0700 Subject: [PATCH 08/10] config/module: detect supports subdirs --- config/module/detect.go | 23 +++++++++++++++++++++++ config/module/detect_test.go | 19 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/config/module/detect.go b/config/module/detect.go index 99b2af0a73..bdf54f9f80 100644 --- a/config/module/detect.go +++ b/config/module/detect.go @@ -3,6 +3,7 @@ package module import ( "fmt" "net/url" + "path/filepath" ) // Detector defines the interface that an invalid URL or a URL with a blank @@ -34,6 +35,9 @@ func init() { func Detect(src string, pwd string) (string, error) { getForce, getSrc := getForcedGetter(src) + // Separate out the subdir if there is one, we don't pass that to detect + getSrc, subDir := getDirSubdir(getSrc) + u, err := url.Parse(getSrc) if err == nil && u.Scheme != "" { // Valid URL @@ -51,6 +55,25 @@ func Detect(src string, pwd string) (string, error) { var detectForce string detectForce, result = getForcedGetter(result) + result, detectSubdir := getDirSubdir(result) + + // If we have a subdir from the detection, then prepend it to our + // requested subdir. + if detectSubdir != "" { + if subDir != "" { + subDir = filepath.Join(detectSubdir, subDir) + } else { + subDir = detectSubdir + } + } + if subDir != "" { + u, err := url.Parse(result) + if err != nil { + return "", fmt.Errorf("Error parsing URL: %s", err) + } + u.Path += "//" + subDir + result = u.String() + } // Preserve the forced getter if it exists. We try to use the // original set force first, followed by any force set by the diff --git a/config/module/detect_test.go b/config/module/detect_test.go index 8f62f6618b..69a5a6fef7 100644 --- a/config/module/detect_test.go +++ b/config/module/detect_test.go @@ -13,7 +13,24 @@ func TestDetect(t *testing.T) { }{ {"./foo", "/foo", "file:///foo/foo", false}, {"git::./foo", "/foo", "git::file:///foo/foo", false}, - {"git::github.com/hashicorp/foo", "", "git::https://github.com/hashicorp/foo.git", false}, + { + "git::github.com/hashicorp/foo", + "", + "git::https://github.com/hashicorp/foo.git", + false, + }, + { + "./foo//bar", + "/foo", + "file:///foo/foo//bar", + false, + }, + { + "git::github.com/hashicorp/foo//bar", + "", + "git::https://github.com/hashicorp/foo.git//bar", + false, + }, } for i, tc := range cases { From f760c143325156eed4bcc1c30bbfc153f4aba594 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 27 Sep 2014 12:28:07 -0700 Subject: [PATCH 09/10] website: document init --- .../source/docs/commands/init.html.markdown | 24 +++++++++++++++++++ website/source/layouts/docs.erb | 4 ++++ 2 files changed, 28 insertions(+) create mode 100644 website/source/docs/commands/init.html.markdown diff --git a/website/source/docs/commands/init.html.markdown b/website/source/docs/commands/init.html.markdown new file mode 100644 index 0000000000..a157a047a7 --- /dev/null +++ b/website/source/docs/commands/init.html.markdown @@ -0,0 +1,24 @@ +--- +layout: "docs" +page_title: "Command: init" +sidebar_current: "docs-commands-init" +--- + +# Command: init + +The `terraform init` command is used to initialize a Terraform configuration +using another +[module](/docs/modules/index.html) +as a skeleton. + +## Usage + +Usage: `terraform init [options] SOURCE [DIR]` + +Init will download the module from SOURCE and copy it into the DIR +(which defaults to the current working directory). Version control +information from the module (such as Git history) will not be copied. + +The directory being initialized must be empty of all Terraform configurations. +If the module has other files which conflict with what is already in the +directory, they _will be overwritten_. diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 3b1c92fe60..fa60aac790 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -59,6 +59,10 @@ get + > + init + + > graph From fe4f53eb5bf3ea7392ab227d46d3c2dd1082fa76 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 27 Sep 2014 12:31:38 -0700 Subject: [PATCH 10/10] enable init command --- commands.go | 6 ++++++ website/source/layouts/docs.erb | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/commands.go b/commands.go index fa9294df37..a8251e547c 100644 --- a/commands.go +++ b/commands.go @@ -52,6 +52,12 @@ func init() { }, nil }, + "init": func() (cli.Command, error) { + return &command.InitCommand{ + Meta: meta, + }, nil + }, + "output": func() (cli.Command, error) { return &command.OutputCommand{ Meta: meta, diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index fa60aac790..9536b29406 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -59,14 +59,14 @@ get - > - init - - > graph + > + init + + > output