github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/command/plugins_test.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/hashicorp/terraform/addrs"
    11  	"github.com/hashicorp/terraform/configs/configschema"
    12  	"github.com/hashicorp/terraform/plugin/discovery"
    13  	"github.com/hashicorp/terraform/providers"
    14  	"github.com/hashicorp/terraform/terraform"
    15  	"github.com/hashicorp/terraform/tfdiags"
    16  )
    17  
    18  func TestMultiVersionProviderResolver(t *testing.T) {
    19  	available := make(discovery.PluginMetaSet)
    20  	available.Add(discovery.PluginMeta{
    21  		Name:    "plugin",
    22  		Version: "1.0.0",
    23  		Path:    "testdata/empty-file",
    24  	})
    25  
    26  	resolver := &multiVersionProviderResolver{
    27  		Internal: map[addrs.Provider]providers.Factory{
    28  			addrs.NewLegacyProvider("internal"): providers.FactoryFixed(
    29  				&terraform.MockProvider{
    30  					GetSchemaReturn: &terraform.ProviderSchema{
    31  						ResourceTypes: map[string]*configschema.Block{
    32  							"internal_foo": {},
    33  						},
    34  					},
    35  				},
    36  			),
    37  		},
    38  		Available: available,
    39  	}
    40  
    41  	t.Run("plugin matches", func(t *testing.T) {
    42  		reqd := discovery.PluginRequirements{
    43  			"plugin": &discovery.PluginConstraints{
    44  				Versions: discovery.ConstraintStr("1.0.0").MustParse(),
    45  			},
    46  		}
    47  		got, err := resolver.ResolveProviders(reqd)
    48  		if err != nil {
    49  			t.Fatalf("unexpected error: %s", err)
    50  		}
    51  		if ct := len(got); ct != 1 {
    52  			t.Errorf("wrong number of results %d; want 1", ct)
    53  		}
    54  		if _, exists := got[addrs.NewLegacyProvider("plugin")]; !exists {
    55  			t.Errorf("provider \"plugin\" not in result")
    56  		}
    57  	})
    58  	t.Run("plugin doesn't match", func(t *testing.T) {
    59  		reqd := discovery.PluginRequirements{
    60  			"plugin": &discovery.PluginConstraints{
    61  				Versions: discovery.ConstraintStr("2.0.0").MustParse(),
    62  			},
    63  		}
    64  		_, err := resolver.ResolveProviders(reqd)
    65  		if err == nil {
    66  			t.Errorf("resolved successfully, but want error")
    67  		}
    68  	})
    69  	t.Run("internal matches", func(t *testing.T) {
    70  		reqd := discovery.PluginRequirements{
    71  			"internal": &discovery.PluginConstraints{
    72  				Versions: discovery.AllVersions,
    73  			},
    74  		}
    75  		got, err := resolver.ResolveProviders(reqd)
    76  		if err != nil {
    77  			t.Fatalf("unexpected error: %s", err)
    78  		}
    79  		if ct := len(got); ct != 1 {
    80  			t.Errorf("wrong number of results %d; want 1", ct)
    81  		}
    82  		if _, exists := got[addrs.NewLegacyProvider("internal")]; !exists {
    83  			t.Errorf("provider \"internal\" not in result")
    84  		}
    85  	})
    86  	t.Run("internal with version constraint", func(t *testing.T) {
    87  		// Version constraints are not permitted for internal providers
    88  		reqd := discovery.PluginRequirements{
    89  			"internal": &discovery.PluginConstraints{
    90  				Versions: discovery.ConstraintStr("2.0.0").MustParse(),
    91  			},
    92  		}
    93  		_, err := resolver.ResolveProviders(reqd)
    94  		if err == nil {
    95  			t.Errorf("resolved successfully, but want error")
    96  		}
    97  	})
    98  }
    99  
   100  func TestPluginPath(t *testing.T) {
   101  	td := testTempDir(t)
   102  	defer os.RemoveAll(td)
   103  	defer testChdir(t, td)()
   104  
   105  	pluginPath := []string{"a", "b", "c"}
   106  
   107  	m := Meta{}
   108  	if err := m.storePluginPath(pluginPath); err != nil {
   109  		t.Fatal(err)
   110  	}
   111  
   112  	restoredPath, err := m.loadPluginPath()
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  
   117  	if !reflect.DeepEqual(pluginPath, restoredPath) {
   118  		t.Fatalf("expected plugin path %#v, got %#v", pluginPath, restoredPath)
   119  	}
   120  }
   121  
   122  func TestInternalProviders(t *testing.T) {
   123  	m := Meta{}
   124  	internal := m.internalProviders()
   125  	tfProvider, err := internal[addrs.NewLegacyProvider("terraform")]()
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  
   130  	schema := tfProvider.GetSchema()
   131  	_, found := schema.DataSources["terraform_remote_state"]
   132  	if !found {
   133  		t.Errorf("didn't find terraform_remote_state in internal \"terraform\" provider")
   134  	}
   135  }
   136  
   137  // mockProviderInstaller is a discovery.PluginInstaller implementation that
   138  // is a mock for discovery.ProviderInstaller.
   139  type mockProviderInstaller struct {
   140  	// A map of provider names to available versions.
   141  	// The tests expect the versions to be in order from newest to oldest.
   142  	Providers map[string][]string
   143  
   144  	Dir               string
   145  	PurgeUnusedCalled bool
   146  }
   147  
   148  func (i *mockProviderInstaller) FileName(provider, version string) string {
   149  	return fmt.Sprintf("terraform-provider-%s_v%s_x4", provider, version)
   150  }
   151  
   152  func (i *mockProviderInstaller) Get(provider addrs.Provider, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) {
   153  	var diags tfdiags.Diagnostics
   154  	noMeta := discovery.PluginMeta{}
   155  	versions := i.Providers[provider.Type]
   156  	if len(versions) == 0 {
   157  		return noMeta, diags, fmt.Errorf("provider %q not found", provider)
   158  	}
   159  
   160  	err := os.MkdirAll(i.Dir, 0755)
   161  	if err != nil {
   162  		return noMeta, diags, fmt.Errorf("error creating plugins directory: %s", err)
   163  	}
   164  
   165  	for _, v := range versions {
   166  		version, err := discovery.VersionStr(v).Parse()
   167  		if err != nil {
   168  			panic(err)
   169  		}
   170  
   171  		if req.Allows(version) {
   172  			// provider filename
   173  			name := i.FileName(provider.Type, v)
   174  			path := filepath.Join(i.Dir, name)
   175  			f, err := os.Create(path)
   176  			if err != nil {
   177  				return noMeta, diags, fmt.Errorf("error fetching provider: %s", err)
   178  			}
   179  			f.Close()
   180  			return discovery.PluginMeta{
   181  				Name:    provider.Type,
   182  				Version: discovery.VersionStr(v),
   183  				Path:    path,
   184  			}, diags, nil
   185  		}
   186  	}
   187  
   188  	return noMeta, diags, fmt.Errorf("no suitable version for provider %q found with constraints %s", provider, req)
   189  }
   190  
   191  func (i *mockProviderInstaller) PurgeUnused(map[string]discovery.PluginMeta) (discovery.PluginMetaSet, error) {
   192  	i.PurgeUnusedCalled = true
   193  	ret := make(discovery.PluginMetaSet)
   194  	ret.Add(discovery.PluginMeta{
   195  		Name:    "test",
   196  		Version: "0.0.0",
   197  		Path:    "mock-test",
   198  	})
   199  	return ret, nil
   200  }
   201  
   202  type callbackPluginInstaller func(provider string, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error)
   203  
   204  func (cb callbackPluginInstaller) Get(provider addrs.Provider, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) {
   205  	return cb(provider.Type, req)
   206  }
   207  
   208  func (cb callbackPluginInstaller) PurgeUnused(map[string]discovery.PluginMeta) (discovery.PluginMetaSet, error) {
   209  	// does nothing
   210  	return make(discovery.PluginMetaSet), nil
   211  }