From 5107c33119ef1efad25f849407bd79041a9ee194 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 2 Nov 2016 10:30:28 -0700 Subject: [PATCH] command/import: load configurations and allow empty config dirs Fixes #7774 This modifies the `import` command to load configuration files from the pwd. This also augments the configuration loading section for the CLI to have a new option (default false, same as old behavior) to allow directories with no Terraform configurations. For import, we allow directories with no Terraform configurations so this option is set to true. --- command/import.go | 9 +++ command/import_test.go | 57 +++++++++++++++++++ command/meta.go | 20 ++++++- command/test-fixtures/import-provider/main.tf | 3 + config/loader.go | 16 +++++- config/loader_test.go | 4 ++ 6 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 command/test-fixtures/import-provider/main.tf diff --git a/command/import.go b/command/import.go index 015610da5..cc9792872 100644 --- a/command/import.go +++ b/command/import.go @@ -3,6 +3,7 @@ package command import ( "fmt" "log" + "os" "strings" "github.com/hashicorp/terraform/terraform" @@ -34,8 +35,16 @@ func (c *ImportCommand) 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 + } + // Build the context based on the arguments given ctx, _, err := c.Context(contextOpts{ + Path: pwd, + PathEmptyOk: true, StatePath: c.Meta.statePath, Parallelism: c.Meta.parallelism, }) diff --git a/command/import_test.go b/command/import_test.go index 1341fa5ea..319388057 100644 --- a/command/import_test.go +++ b/command/import_test.go @@ -1,6 +1,7 @@ package command import ( + "fmt" "testing" "github.com/hashicorp/terraform/terraform" @@ -45,6 +46,62 @@ func TestImport(t *testing.T) { testStateOutput(t, statePath, testImportStr) } +func TestImport_providerConfig(t *testing.T) { + defer testChdir(t, testFixturePath("import-provider"))() + + statePath := testTempFile(t) + + p := testProvider() + ui := new(cli.MockUi) + c := &ImportCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + p.ImportStateFn = nil + p.ImportStateReturn = []*terraform.InstanceState{ + &terraform.InstanceState{ + ID: "yay", + Ephemeral: terraform.EphemeralState{ + Type: "test_instance", + }, + }, + } + + configured := false + p.ConfigureFn = func(c *terraform.ResourceConfig) error { + configured = true + + if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { + return fmt.Errorf("bad value: %#v", v) + } + + return nil + } + + args := []string{ + "-state", statePath, + "test_instance.foo", + "bar", + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + // Verify that we were called + if !configured { + t.Fatal("Configure should be called") + } + + if !p.ImportStateCalled { + t.Fatal("ImportState should be called") + } + + testStateOutput(t, statePath, testImportStr) +} + /* func TestRefresh_badState(t *testing.T) { p := testProvider() diff --git a/command/meta.go b/command/meta.go index 4e142585d..b2416abd5 100644 --- a/command/meta.go +++ b/command/meta.go @@ -5,11 +5,14 @@ import ( "flag" "fmt" "io" + "log" "os" "path/filepath" "strconv" + "github.com/hashicorp/errwrap" "github.com/hashicorp/go-getter" + "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/helper/experiment" "github.com/hashicorp/terraform/state" @@ -163,6 +166,17 @@ func (m *Meta) Context(copts contextOpts) (*terraform.Context, bool, error) { var mod *module.Tree if copts.Path != "" { mod, err = module.NewTreeModule("", copts.Path) + + // Check for the error where we have no config files but + // allow that. If that happens, clear the error. + if errwrap.ContainsType(err, new(config.ErrNoConfigsFound)) && + copts.PathEmptyOk { + log.Printf( + "[WARN] Empty configuration dir, ignoring: %s", copts.Path) + err = nil + mod = module.NewEmptyTree() + } + if err != nil { return nil, false, fmt.Errorf("Error loading config: %s", err) } @@ -495,7 +509,11 @@ func (m *Meta) outputShadowError(err error, output bool) bool { // contextOpts are the options used to load a context from a command. type contextOpts struct { // Path to the directory where the root module is. - Path string + // + // PathEmptyOk, when set, will allow paths that have no Terraform + // configurations. The result in that case will be an empty module. + Path string + PathEmptyOk bool // StatePath is the path to the state file. If this is empty, then // no state will be loaded. It is also okay for this to be a path to diff --git a/command/test-fixtures/import-provider/main.tf b/command/test-fixtures/import-provider/main.tf new file mode 100644 index 000000000..dd4a7556c --- /dev/null +++ b/command/test-fixtures/import-provider/main.tf @@ -0,0 +1,3 @@ +provider "test" { + foo = "bar" +} diff --git a/config/loader.go b/config/loader.go index c9a1295fe..0bfa89c25 100644 --- a/config/loader.go +++ b/config/loader.go @@ -12,6 +12,18 @@ import ( "github.com/hashicorp/hcl" ) +// ErrNoConfigsFound is the error returned by LoadDir if no +// Terraform configuration files were found in the given directory. +type ErrNoConfigsFound struct { + Dir string +} + +func (e ErrNoConfigsFound) Error() string { + return fmt.Sprintf( + "No Terraform configuration files found in directory: %s", + e.Dir) +} + // LoadJSON loads a single Terraform configuration from a given JSON document. // // The document must be a complete Terraform configuration. This function will @@ -69,9 +81,7 @@ func LoadDir(root string) (*Config, error) { return nil, err } if len(files) == 0 { - return nil, fmt.Errorf( - "No Terraform configuration files found in directory: %s", - root) + return nil, &ErrNoConfigsFound{Dir: root} } // Determine the absolute path to the directory. diff --git a/config/loader_test.go b/config/loader_test.go index dfc79f623..73b09a6fe 100644 --- a/config/loader_test.go +++ b/config/loader_test.go @@ -8,6 +8,10 @@ import ( "testing" ) +func TestErrNoConfigsFound_impl(t *testing.T) { + var _ error = new(ErrNoConfigsFound) +} + func TestIsEmptyDir(t *testing.T) { val, err := IsEmptyDir(fixtureDir) if err != nil {