diff --git a/command/init.go b/command/init.go index c2fab48112..4da1ea0494 100644 --- a/command/init.go +++ b/command/init.go @@ -213,7 +213,14 @@ func (c *InitCommand) getProviders(path string, state *terraform.State, upgrade return err } - available := c.providerPluginSet() + var available discovery.PluginMetaSet + if upgrade { + // If we're in upgrade mode, we ignore any auto-installed plugins + // in "available", causing us to reinstall and possibly upgrade them. + available = c.providerPluginManuallyInstalledSet() + } else { + available = c.providerPluginSet() + } requirements := terraform.ModuleTreeDependencies(mod, state).AllPluginRequirements() missing := c.missingPlugins(available, requirements) @@ -253,6 +260,20 @@ func (c *InitCommand) getProviders(path string, state *terraform.State, upgrade return err } + if upgrade { + // Purge any auto-installed plugins that aren't being used. + purged, err := c.providerInstaller.PurgeUnused(chosen) + if err != nil { + // Failure to purge old plugins is not a fatal error + c.Ui.Warn(fmt.Sprintf("failed to purge unused plugins: %s", err)) + } + if purged != nil { + for meta := range purged { + log.Printf("[DEBUG] Purged unused %s plugin %s", meta.Name, meta.Path) + } + } + } + // If any providers have "floating" versions (completely unconstrained) // we'll suggest the user constrain with a pessimistic constraint to // avoid implicitly adopting a later major release. diff --git a/command/init_test.go b/command/init_test.go index e5a548717f..c51e8a4a60 100644 --- a/command/init_test.go +++ b/command/init_test.go @@ -5,7 +5,9 @@ import ( "io/ioutil" "os" "path/filepath" + "reflect" "runtime" + "sort" "strings" "testing" @@ -492,6 +494,98 @@ func TestInit_getProvider(t *testing.T) { } } +func TestInit_getUpgradePlugins(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + copy.CopyDir(testFixturePath("init-get-providers"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + ui := new(cli.MockUi) + m := Meta{ + testingOverrides: metaOverridesForProvider(testProvider()), + Ui: ui, + } + + installer := &mockProviderInstaller{ + Providers: map[string][]string{ + // looking for an exact version + "exact": []string{"1.2.3"}, + // config requires >= 2.3.3 + "greater_than": []string{"2.3.4", "2.3.3", "2.3.0"}, + // config specifies + "between": []string{"3.4.5", "2.3.4", "1.2.3"}, + }, + + Dir: m.pluginDir(), + } + + err := os.MkdirAll(m.pluginDir(), os.ModePerm) + if err != nil { + t.Fatal(err) + } + exactUnwanted := filepath.Join(m.pluginDir(), installer.FileName("exact", "0.0.1")) + err = ioutil.WriteFile(exactUnwanted, []byte{}, os.ModePerm) + if err != nil { + t.Fatal(err) + } + greaterThanUnwanted := filepath.Join(m.pluginDir(), installer.FileName("greater_than", "2.3.3")) + err = ioutil.WriteFile(greaterThanUnwanted, []byte{}, os.ModePerm) + if err != nil { + t.Fatal(err) + } + betweenOverride := installer.FileName("between", "2.3.4") // intentionally directly in cwd, and should override auto-install + err = ioutil.WriteFile(betweenOverride, []byte{}, os.ModePerm) + if err != nil { + t.Fatal(err) + } + + c := &InitCommand{ + Meta: m, + providerInstaller: installer, + } + + args := []string{ + "-upgrade=true", + } + if code := c.Run(args); code != 0 { + t.Fatalf("command did not complete successfully:\n%s", ui.ErrorWriter.String()) + } + + files, err := ioutil.ReadDir(m.pluginDir()) + if err != nil { + t.Fatal(err) + } + + if !installer.PurgeUnusedCalled { + t.Errorf("init -upgrade didn't purge providers, but should have") + } + + gotFilenames := make([]string, len(files)) + for i, info := range files { + gotFilenames[i] = info.Name() + } + sort.Strings(gotFilenames) + + wantFilenames := []string{ + "lock.json", + + // no "between" because the file in cwd overrides it + + // The mock PurgeUnused doesn't actually purge anything, so the dir + // includes both our old and new versions. + "terraform-provider-exact_v0.0.1_x4", + "terraform-provider-exact_v1.2.3_x4", + "terraform-provider-greater_than_v2.3.3_x4", + "terraform-provider-greater_than_v2.3.4_x4", + } + + if !reflect.DeepEqual(gotFilenames, wantFilenames) { + t.Errorf("wrong directory contents after upgrade\ngot: %#v\nwant: %#v", gotFilenames, wantFilenames) + } + +} + func TestInit_getProviderMissing(t *testing.T) { // Create a temporary working directory that is empty td := tempDir(t)