From 40e2fbb8e968d76191a524dd1245c34912930708 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Wed, 21 Jun 2017 10:32:13 -0700 Subject: [PATCH] command: init to allow plugin init without backend init Previously init would crash if given these options: -backend=false -get-plugins=true This is because the state is used as a source of provider dependency information, and we need to instantiate the backend to get the state. To avoid the crash, we now use the following adjusted behavior: - if -backend=true, we behave as before - if -backend=false, we instead try to instantiate the backend the same way any other command would, without modifying its configuration - if we're able to instantiate the backend, we use it to fetch state for dependency resolution purposes - if the backend is not instantiable then we assume it's not yet configured and proceed with a nil state, which may cause us to see an incomplete picture of the dependencies but still allows the install to succeed. Subsequently running "terraform plan" will not work until the backend is (re-)initialized, so the incomplete picture of required plugins is safe. --- command/init.go | 49 +++++++++++++++++++++++++++++++------------- command/init_test.go | 4 +++- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/command/init.go b/command/init.go index fbfbcd080..7b75bf263 100644 --- a/command/init.go +++ b/command/init.go @@ -172,21 +172,42 @@ func (c *InitCommand) Run(args []string) int { } } + if back == nil { + // If we didn't initialize a backend then we'll try to at least + // instantiate one. This might fail if it wasn't already initalized + // by a previous run, so we must still expect that "back" may be nil + // in code that follows. + back, err = c.Backend(nil) + if err != nil { + // This is fine. We'll proceed with no backend, then. + back = nil + } + } + + var state *terraform.State + + // If we have a functional backend (either just initialized or initialized + // on a previous run) we'll use the current state as a potential source + // of provider dependencies. + if back != nil { + sMgr, err := back.State(c.Workspace()) + if err != nil { + c.Ui.Error(fmt.Sprintf( + "Error loading state: %s", err)) + return 1 + } + + if err := sMgr.RefreshState(); err != nil { + c.Ui.Error(fmt.Sprintf( + "Error refreshing state: %s", err)) + return 1 + } + + state = sMgr.State() + } + // Now that we have loaded all modules, check the module tree for missing providers. - sMgr, err := back.State(c.Workspace()) - if err != nil { - c.Ui.Error(fmt.Sprintf( - "Error loading state: %s", err)) - return 1 - } - - if err := sMgr.RefreshState(); err != nil { - c.Ui.Error(fmt.Sprintf( - "Error refreshing state: %s", err)) - return 1 - } - - err = c.getProviders(path, sMgr.State(), flagUpgrade) + err = c.getProviders(path, state, flagUpgrade) if err != nil { // this function provides its own output log.Printf("[ERROR] %s", err) diff --git a/command/init_test.go b/command/init_test.go index 069f54676..e9403e1a5 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -474,7 +474,9 @@ func TestInit_getProvider(t *testing.T) { providerInstaller: installer, } - args := []string{} + args := []string{ + "-backend=false", // should be possible to install plugins without backend init + } if code := c.Run(args); code != 0 { t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) }