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.
This commit is contained in:
Martin Atkins 2017-06-21 10:32:13 -07:00
parent 3dc7d618f7
commit 40e2fbb8e9
2 changed files with 38 additions and 15 deletions

View File

@ -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)

View File

@ -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())
}