package command import ( "fmt" "os" "path/filepath" "reflect" "testing" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/plugin/discovery" "github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/tfdiags" ) func TestMultiVersionProviderResolver(t *testing.T) { available := make(discovery.PluginMetaSet) available.Add(discovery.PluginMeta{ Name: "plugin", Version: "1.0.0", Path: "testdata/empty-file", }) resolver := &multiVersionProviderResolver{ Internal: map[string]providers.Factory{ "internal": providers.FactoryFixed( &terraform.MockProvider{ GetSchemaReturn: &terraform.ProviderSchema{ ResourceTypes: map[string]*configschema.Block{ "internal_foo": {}, }, }, }, ), }, Available: available, } t.Run("plugin matches", func(t *testing.T) { reqd := discovery.PluginRequirements{ "plugin": &discovery.PluginConstraints{ Versions: discovery.ConstraintStr("1.0.0").MustParse(), }, } got, err := resolver.ResolveProviders(reqd) if err != nil { t.Fatalf("unexpected error: %s", err) } if ct := len(got); ct != 1 { t.Errorf("wrong number of results %d; want 1", ct) } if _, exists := got["plugin"]; !exists { t.Errorf("provider \"plugin\" not in result") } }) t.Run("plugin doesn't match", func(t *testing.T) { reqd := discovery.PluginRequirements{ "plugin": &discovery.PluginConstraints{ Versions: discovery.ConstraintStr("2.0.0").MustParse(), }, } _, err := resolver.ResolveProviders(reqd) if err == nil { t.Errorf("resolved successfully, but want error") } }) t.Run("internal matches", func(t *testing.T) { reqd := discovery.PluginRequirements{ "internal": &discovery.PluginConstraints{ Versions: discovery.AllVersions, }, } got, err := resolver.ResolveProviders(reqd) if err != nil { t.Fatalf("unexpected error: %s", err) } if ct := len(got); ct != 1 { t.Errorf("wrong number of results %d; want 1", ct) } if _, exists := got["internal"]; !exists { t.Errorf("provider \"internal\" not in result") } }) t.Run("internal with version constraint", func(t *testing.T) { // Version constraints are not permitted for internal providers reqd := discovery.PluginRequirements{ "internal": &discovery.PluginConstraints{ Versions: discovery.ConstraintStr("2.0.0").MustParse(), }, } _, err := resolver.ResolveProviders(reqd) if err == nil { t.Errorf("resolved successfully, but want error") } }) } func TestPluginPath(t *testing.T) { td := testTempDir(t) defer testChdir(t, td)() pluginPath := []string{"a", "b", "c"} m := Meta{} if err := m.storePluginPath(pluginPath); err != nil { t.Fatal(err) } restoredPath, err := m.loadPluginPath() if err != nil { t.Fatal(err) } if !reflect.DeepEqual(pluginPath, restoredPath) { t.Fatalf("expected plugin path %#v, got %#v", pluginPath, restoredPath) } } func TestInternalProviders(t *testing.T) { m := Meta{} internal := m.internalProviders() tfProvider, err := internal["terraform"]() if err != nil { t.Fatal(err) } schema := tfProvider.GetSchema() _, found := schema.DataSources["terraform_remote_state"] if !found { t.Errorf("didn't find terraform_remote_state in internal \"terraform\" provider") } } // mockProviderInstaller is a discovery.PluginInstaller implementation that // is a mock for discovery.ProviderInstaller. type mockProviderInstaller struct { // A map of provider names to available versions. // The tests expect the versions to be in order from newest to oldest. Providers map[string][]string Dir string PurgeUnusedCalled bool } func (i *mockProviderInstaller) FileName(provider, version string) string { return fmt.Sprintf("terraform-provider-%s_v%s_x4", provider, version) } func (i *mockProviderInstaller) Get(provider addrs.ProviderType, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) { var diags tfdiags.Diagnostics noMeta := discovery.PluginMeta{} versions := i.Providers[provider.Type] if len(versions) == 0 { return noMeta, diags, fmt.Errorf("provider %q not found", provider) } err := os.MkdirAll(i.Dir, 0755) if err != nil { return noMeta, diags, fmt.Errorf("error creating plugins directory: %s", err) } for _, v := range versions { version, err := discovery.VersionStr(v).Parse() if err != nil { panic(err) } if req.Allows(version) { // provider filename name := i.FileName(provider.Type, v) path := filepath.Join(i.Dir, name) f, err := os.Create(path) if err != nil { return noMeta, diags, fmt.Errorf("error fetching provider: %s", err) } f.Close() return discovery.PluginMeta{ Name: provider.Type, Version: discovery.VersionStr(v), Path: path, }, diags, nil } } return noMeta, diags, fmt.Errorf("no suitable version for provider %q found with constraints %s", provider, req) } func (i *mockProviderInstaller) PurgeUnused(map[string]discovery.PluginMeta) (discovery.PluginMetaSet, error) { i.PurgeUnusedCalled = true ret := make(discovery.PluginMetaSet) ret.Add(discovery.PluginMeta{ Name: "test", Version: "0.0.0", Path: "mock-test", }) return ret, nil } type callbackPluginInstaller func(provider string, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) func (cb callbackPluginInstaller) Get(provider addrs.ProviderType, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) { return cb(provider.Type, req) } func (cb callbackPluginInstaller) PurgeUnused(map[string]discovery.PluginMeta) (discovery.PluginMetaSet, error) { // does nothing return make(discovery.PluginMetaSet), nil }