github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/plugin/discovery/get_test.go (about)

     1  package discovery
     2  
     3  import (
     4  	"archive/zip"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"log"
    10  	"net"
    11  	"net/http"
    12  	"net/http/httptest"
    13  	"os"
    14  	"path/filepath"
    15  	"reflect"
    16  	"runtime"
    17  	"strings"
    18  	"testing"
    19  
    20  	svchost "github.com/hashicorp/terraform-svchost"
    21  	"github.com/hashicorp/terraform-svchost/disco"
    22  	"github.com/hashicorp/terraform/addrs"
    23  	"github.com/hashicorp/terraform/httpclient"
    24  	"github.com/hashicorp/terraform/registry"
    25  	"github.com/hashicorp/terraform/registry/response"
    26  	"github.com/hashicorp/terraform/version"
    27  	"github.com/mitchellh/cli"
    28  )
    29  
    30  const testProviderFile = "test provider binary"
    31  
    32  func TestMain(m *testing.M) {
    33  	server := testReleaseServer()
    34  	l, err := net.Listen("tcp", "127.0.0.1:8080")
    35  	if err != nil {
    36  		log.Fatal(err)
    37  	}
    38  
    39  	// NewUnstartedServer creates a listener. Close that listener and replace
    40  	// with the one we created.
    41  	server.Listener.Close()
    42  	server.Listener = l
    43  	server.Start()
    44  	defer server.Close()
    45  
    46  	os.Exit(m.Run())
    47  }
    48  
    49  // return the directory listing for the "test" provider
    50  func testListingHandler(w http.ResponseWriter, r *http.Request) {
    51  	parts := strings.Split(r.URL.Path, "/")
    52  	if len(parts) != 6 {
    53  		http.Error(w, "not found", http.StatusNotFound)
    54  		return
    55  	}
    56  	provider := parts[4]
    57  	if provider == "test" {
    58  		js, err := json.Marshal(versionList)
    59  		if err != nil {
    60  			http.Error(w, err.Error(), http.StatusInternalServerError)
    61  			return
    62  		}
    63  		w.Write(js)
    64  	}
    65  	http.Error(w, ErrorNoSuchProvider.Error(), http.StatusNotFound)
    66  	return
    67  
    68  }
    69  
    70  // return the download URLs for the "test" provider
    71  func testDownloadHandler(w http.ResponseWriter, r *http.Request) {
    72  	js, err := json.Marshal(downloadURLs)
    73  	if err != nil {
    74  		http.Error(w, err.Error(), http.StatusInternalServerError)
    75  		return
    76  	}
    77  	w.Write(js)
    78  }
    79  
    80  func testChecksumHandler(w http.ResponseWriter, r *http.Request) {
    81  	// this exact plugin has a signature and checksum file
    82  	if r.URL.Path == "/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS" {
    83  		http.ServeFile(w, r, "testdata/terraform-provider-template_0.1.0_SHA256SUMS")
    84  		return
    85  	}
    86  	if r.URL.Path == "/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.sig" {
    87  		http.ServeFile(w, r, "testdata/terraform-provider-template_0.1.0_SHA256SUMS.sig")
    88  		return
    89  	}
    90  	if r.URL.Path == "/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.72D7468F.sig" {
    91  		http.ServeFile(w, r, "testdata/terraform-provider-template_0.1.0_SHA256SUMS.72D7468F.sig")
    92  		return
    93  	}
    94  
    95  	// this this checksum file is corrupt and doesn't match the sig
    96  	if r.URL.Path == "/terraform-provider-badsig/0.1.0/terraform-provider-badsig_0.1.0_SHA256SUMS" {
    97  		http.ServeFile(w, r, "testdata/terraform-provider-badsig_0.1.0_SHA256SUMS")
    98  		return
    99  	}
   100  	if r.URL.Path == "/terraform-provider-badsig/0.1.0/terraform-provider-badsig_0.1.0_SHA256SUMS.sig" {
   101  		http.ServeFile(w, r, "testdata/terraform-provider-badsig_0.1.0_SHA256SUMS.sig")
   102  		return
   103  	}
   104  	if r.URL.Path == "/terraform-provider-badsig/0.1.0/terraform-provider-badsig_0.1.0_SHA256SUMS.72D7468F.sig" {
   105  		http.ServeFile(w, r, "testdata/terraform-provider-badsig_0.1.0_SHA256SUMS.72D7468F.sig")
   106  		return
   107  	}
   108  
   109  	http.Error(w, "signtaure files not found", http.StatusNotFound)
   110  }
   111  
   112  // returns a 200 for a valid provider url, using the patch number for the
   113  // plugin protocol version.
   114  func testHandler(w http.ResponseWriter, r *http.Request) {
   115  	if strings.HasSuffix(r.URL.Path, "/versions") {
   116  		testListingHandler(w, r)
   117  		return
   118  	}
   119  
   120  	if strings.Contains(r.URL.Path, "/download") {
   121  		testDownloadHandler(w, r)
   122  		return
   123  	}
   124  
   125  	parts := strings.Split(r.URL.Path, "/")
   126  	if len(parts) != 7 {
   127  		http.Error(w, "not found", http.StatusNotFound)
   128  		return
   129  	}
   130  
   131  	// write a dummy file
   132  	z := zip.NewWriter(w)
   133  	fn := fmt.Sprintf("%s_v%s", parts[4], parts[5])
   134  	f, err := z.Create(fn)
   135  	if err != nil {
   136  		panic(err)
   137  	}
   138  	io.WriteString(f, testProviderFile)
   139  	z.Close()
   140  }
   141  
   142  func testReleaseServer() *httptest.Server {
   143  	handler := http.NewServeMux()
   144  	handler.HandleFunc("/v1/providers/-/", testHandler)
   145  	handler.HandleFunc("/v1/providers/terraform-providers/", testHandler)
   146  	handler.HandleFunc("/terraform-provider-template/", testChecksumHandler)
   147  	handler.HandleFunc("/terraform-provider-badsig/", testChecksumHandler)
   148  	handler.HandleFunc("/.well-known/terraform.json", func(w http.ResponseWriter, r *http.Request) {
   149  		w.Header().Set("Content-Type", "application/json")
   150  		io.WriteString(w, `{"modules.v1":"http://localhost/v1/modules/", "providers.v1":"http://localhost/v1/providers/"}`)
   151  	})
   152  
   153  	return httptest.NewUnstartedServer(handler)
   154  }
   155  
   156  func TestVersionListing(t *testing.T) {
   157  	server := testReleaseServer()
   158  	server.Start()
   159  	defer server.Close()
   160  
   161  	i := newProviderInstaller(server)
   162  
   163  	allVersions, err := i.listProviderVersions(addrs.Provider{Type: "test"})
   164  
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  
   169  	var versions []*response.TerraformProviderVersion
   170  
   171  	for _, v := range allVersions.Versions {
   172  		versions = append(versions, v)
   173  	}
   174  
   175  	response.ProviderVersionCollection(versions).Sort()
   176  
   177  	expected := []*response.TerraformProviderVersion{
   178  		{Version: "1.2.4"},
   179  		{Version: "1.2.3"},
   180  		{Version: "1.2.1"},
   181  	}
   182  
   183  	if len(versions) != len(expected) {
   184  		t.Fatalf("Received wrong number of versions. expected: %#v, got: %#v", expected, versions)
   185  	}
   186  
   187  	for i, v := range versions {
   188  		if v.Version != expected[i].Version {
   189  			t.Fatalf("incorrect version: %#v, expected %#v", v, expected[i])
   190  		}
   191  	}
   192  }
   193  
   194  func TestCheckProtocolVersions(t *testing.T) {
   195  	tests := []struct {
   196  		VersionMeta *response.TerraformProviderVersion
   197  		Err         bool
   198  	}{
   199  		{
   200  			&response.TerraformProviderVersion{
   201  				Protocols: []string{"1", "2"},
   202  			},
   203  			true,
   204  		},
   205  		{
   206  			&response.TerraformProviderVersion{
   207  				Protocols: []string{"4"},
   208  			},
   209  			false,
   210  		},
   211  		{
   212  			&response.TerraformProviderVersion{
   213  				Protocols: []string{"4.2"},
   214  			},
   215  			false,
   216  		},
   217  		{
   218  			&response.TerraformProviderVersion{
   219  				Protocols: []string{"4.0", "5.2"},
   220  			},
   221  			false,
   222  		},
   223  		{
   224  			&response.TerraformProviderVersion{
   225  				Protocols: []string{"5.0", "6.1"},
   226  			},
   227  			true,
   228  		},
   229  	}
   230  
   231  	server := testReleaseServer()
   232  	server.Start()
   233  	defer server.Close()
   234  	i := newProviderInstaller(server)
   235  
   236  	for _, test := range tests {
   237  		err := i.checkPluginProtocol(test.VersionMeta)
   238  		if test.Err {
   239  			if err == nil {
   240  				t.Fatal("succeeded; want error")
   241  			}
   242  		} else if err != nil {
   243  			t.Fatalf("unexpected error: %s", err)
   244  		}
   245  	}
   246  }
   247  
   248  func TestFindClosestProtocolCompatibleVersion(t *testing.T) {
   249  	testCases := []struct {
   250  		Name                  string
   251  		PluginProtocolVersion uint
   252  		ProviderVersions      []*response.TerraformProviderVersion
   253  		ExpectedVersion       string
   254  		Err                   bool
   255  	}{
   256  		{
   257  			"no compatible version",
   258  			5,
   259  			[]*response.TerraformProviderVersion{
   260  				&response.TerraformProviderVersion{
   261  					Version:   "1.0.0",
   262  					Protocols: []string{"4.0"},
   263  				},
   264  			},
   265  			"",
   266  			true,
   267  		}, {
   268  			"equal, suggests latest",
   269  			4,
   270  			[]*response.TerraformProviderVersion{
   271  				&response.TerraformProviderVersion{
   272  					Version:   "1.0.0",
   273  					Protocols: []string{"4.0"},
   274  				},
   275  				&response.TerraformProviderVersion{
   276  					Version:   "1.5.0",
   277  					Protocols: []string{"4.0"},
   278  				},
   279  			},
   280  			"1.5.0",
   281  			false,
   282  		}, {
   283  			"provider protocol too old, suggests earliest",
   284  			5,
   285  			[]*response.TerraformProviderVersion{
   286  				&response.TerraformProviderVersion{
   287  					Version:   "1.0.0",
   288  					Protocols: []string{"4.0"},
   289  				},
   290  				&response.TerraformProviderVersion{
   291  					Version:   "2.0.0",
   292  					Protocols: []string{"4.0", "5.0"},
   293  				},
   294  				&response.TerraformProviderVersion{
   295  					Version:   "2.5.0",
   296  					Protocols: []string{"4.0", "5.0"},
   297  				},
   298  				&response.TerraformProviderVersion{
   299  					Version:   "3.0.0",
   300  					Protocols: []string{"5.0"},
   301  				},
   302  			},
   303  			"2.0.0",
   304  			false,
   305  		}, {
   306  			"provider protocol too new, suggests latest",
   307  			4,
   308  			[]*response.TerraformProviderVersion{
   309  				&response.TerraformProviderVersion{
   310  					Version:   "1.0.0",
   311  					Protocols: []string{"4.0"},
   312  				},
   313  				&response.TerraformProviderVersion{
   314  					Version:   "2.0.0",
   315  					Protocols: []string{"4.0", "5.0"},
   316  				},
   317  				&response.TerraformProviderVersion{
   318  					Version:   "2.5.0",
   319  					Protocols: []string{"4.0", "5.0"},
   320  				},
   321  				&response.TerraformProviderVersion{
   322  					Version:   "3.0.0",
   323  					Protocols: []string{"5.0"},
   324  				},
   325  			},
   326  			"2.5.0",
   327  			false,
   328  		}, {
   329  			"compatible prereleses are filtered",
   330  			5,
   331  			[]*response.TerraformProviderVersion{
   332  				&response.TerraformProviderVersion{
   333  					Version:   "2.0.0-alpha",
   334  					Protocols: []string{"4.0", "5.0"},
   335  				},
   336  			},
   337  			"",
   338  			true,
   339  		}, {
   340  			"suggests latest non-prerelease",
   341  			4,
   342  			[]*response.TerraformProviderVersion{
   343  				&response.TerraformProviderVersion{
   344  					Version:   "2.0.0-alpha",
   345  					Protocols: []string{"4.0", "5.0"},
   346  				},
   347  				&response.TerraformProviderVersion{
   348  					Version:   "2.0.0",
   349  					Protocols: []string{"4.0", "5.0"},
   350  				},
   351  				&response.TerraformProviderVersion{
   352  					Version:   "2.5.0-pre",
   353  					Protocols: []string{"4.0", "5.0"},
   354  				},
   355  				&response.TerraformProviderVersion{
   356  					Version:   "2.5.0",
   357  					Protocols: []string{"4.0", "5.0"},
   358  				},
   359  			},
   360  			"2.5.0",
   361  			false,
   362  		}, {
   363  			"suggests earliest non-prerelease",
   364  			5,
   365  			[]*response.TerraformProviderVersion{
   366  				&response.TerraformProviderVersion{
   367  					Version:   "2.0.0-alpha",
   368  					Protocols: []string{"4.0", "5.0"},
   369  				},
   370  				&response.TerraformProviderVersion{
   371  					Version:   "2.0.0",
   372  					Protocols: []string{"4.0", "5.0"},
   373  				},
   374  				&response.TerraformProviderVersion{
   375  					Version:   "2.6.0",
   376  					Protocols: []string{"4.0", "5.0"},
   377  				},
   378  				&response.TerraformProviderVersion{
   379  					Version:   "3.0.0",
   380  					Protocols: []string{"5.0"},
   381  				},
   382  			},
   383  			"2.0.0",
   384  			false,
   385  		},
   386  	}
   387  
   388  	for _, tc := range testCases {
   389  		t.Run(tc.Name, func(t *testing.T) {
   390  			i := ProviderInstaller{
   391  				Ui:                    cli.NewMockUi(),
   392  				PluginProtocolVersion: tc.PluginProtocolVersion,
   393  			}
   394  
   395  			closestMatch, err := i.findClosestProtocolCompatibleVersion(tc.ProviderVersions)
   396  			if err != nil {
   397  				if !tc.Err {
   398  					t.Fatalf("unexpected error: %q", err)
   399  				}
   400  				return
   401  			}
   402  			if tc.ExpectedVersion != closestMatch.Version {
   403  				t.Errorf("Expected %q, got %q", tc.ExpectedVersion, closestMatch.Version)
   404  			}
   405  		})
   406  	}
   407  }
   408  
   409  func TestProviderInstallerGet(t *testing.T) {
   410  	server := testReleaseServer()
   411  	server.Start()
   412  	defer server.Close()
   413  
   414  	tmpDir, err := ioutil.TempDir("", "tf-plugin")
   415  	if err != nil {
   416  		t.Fatal(err)
   417  	}
   418  
   419  	defer os.RemoveAll(tmpDir)
   420  
   421  	// attempt to use an incompatible protocol version
   422  	i := &ProviderInstaller{
   423  		Dir:                   tmpDir,
   424  		PluginProtocolVersion: 5,
   425  		SkipVerify:            true,
   426  		Ui:                    cli.NewMockUi(),
   427  		registry:              registry.NewClient(Disco(server), nil),
   428  	}
   429  
   430  	_, _, err = i.Get(addrs.NewLegacyProvider("test"), AllVersions)
   431  
   432  	if err != ErrorNoVersionCompatibleWithPlatform {
   433  		t.Fatal("want error for incompatible version")
   434  	}
   435  
   436  	i = &ProviderInstaller{
   437  		Dir:                   tmpDir,
   438  		PluginProtocolVersion: 4,
   439  		SkipVerify:            true,
   440  		Ui:                    cli.NewMockUi(),
   441  		registry:              registry.NewClient(Disco(server), nil),
   442  		OS:                    "mockos",
   443  		Arch:                  "mockarch",
   444  	}
   445  
   446  	{
   447  		_, _, err := i.Get(addrs.NewLegacyProvider("test"), ConstraintStr(">9.0.0").MustParse())
   448  		if err != ErrorNoSuitableVersion {
   449  			t.Fatal("want error for mismatching constraints")
   450  		}
   451  	}
   452  
   453  	{
   454  		provider := addrs.NewLegacyProvider("nonexist")
   455  		_, _, err := i.Get(provider, AllVersions)
   456  		if err != ErrorNoSuchProvider {
   457  			t.Fatal("want error for no such provider")
   458  		}
   459  	}
   460  
   461  	gotMeta, _, err := i.Get(addrs.NewLegacyProvider("test"), AllVersions)
   462  	if err != nil {
   463  		t.Fatal(err)
   464  	}
   465  
   466  	// we should have version 1.2.4
   467  	dest := filepath.Join(tmpDir, "terraform-provider-test_v1.2.4")
   468  
   469  	wantMeta := PluginMeta{
   470  		Name:    "test",
   471  		Version: VersionStr("1.2.4"),
   472  		Path:    dest,
   473  	}
   474  	if !reflect.DeepEqual(gotMeta, wantMeta) {
   475  		t.Errorf("wrong result meta\ngot:  %#v\nwant: %#v", gotMeta, wantMeta)
   476  	}
   477  
   478  	f, err := ioutil.ReadFile(dest)
   479  	if err != nil {
   480  		t.Fatal(err)
   481  	}
   482  
   483  	// provider should have been unzipped
   484  	if string(f) != testProviderFile {
   485  		t.Fatalf("test provider contains: %q", f)
   486  	}
   487  
   488  }
   489  
   490  // test that the provider installer can install plugins from a plugin cache dir
   491  // into a target directory that does not exist.
   492  //  https://github.com/hashicorp/terraform/issues/20532
   493  func TestProviderInstallerGet_cache(t *testing.T) {
   494  	server := testReleaseServer()
   495  	server.Start()
   496  	defer server.Close()
   497  
   498  	tmpDir, err := ioutil.TempDir("", "tf-plugin")
   499  	if err != nil {
   500  		t.Fatal(err)
   501  	}
   502  
   503  	cache := NewLocalPluginCache(filepath.Join(tmpDir, "cache"))
   504  	targetDir := filepath.Join(tmpDir, "non-existant-dir")
   505  
   506  	defer os.RemoveAll(tmpDir)
   507  
   508  	i := &ProviderInstaller{
   509  		Dir:                   targetDir,
   510  		Cache:                 cache,
   511  		PluginProtocolVersion: 4,
   512  		SkipVerify:            true,
   513  		Ui:                    cli.NewMockUi(),
   514  		registry:              registry.NewClient(Disco(server), nil),
   515  		OS:                    "mockos",
   516  		Arch:                  "mockarch",
   517  	}
   518  
   519  	gotMeta, _, err := i.Get(addrs.NewLegacyProvider("test"), AllVersions)
   520  	if err != nil {
   521  		t.Fatal(err)
   522  	}
   523  
   524  	// we should have version 1.2.4
   525  	dest := filepath.Join(targetDir, "terraform-provider-test_v1.2.4")
   526  
   527  	wantMeta := PluginMeta{
   528  		Name:    "test",
   529  		Version: VersionStr("1.2.4"),
   530  		Path:    dest,
   531  	}
   532  	if !reflect.DeepEqual(gotMeta, wantMeta) {
   533  		t.Errorf("wrong result meta\ngot:  %#v\nwant: %#v", gotMeta, wantMeta)
   534  	}
   535  
   536  	f, err := ioutil.ReadFile(dest)
   537  	if err != nil {
   538  		t.Fatal(err)
   539  	}
   540  
   541  	// provider should have been unzipped
   542  	if string(f) != testProviderFile {
   543  		t.Fatalf("test provider contains: %q", f)
   544  	}
   545  }
   546  
   547  func TestProviderInstallerPurgeUnused(t *testing.T) {
   548  	server := testReleaseServer()
   549  	defer server.Close()
   550  
   551  	tmpDir, err := ioutil.TempDir("", "tf-plugin")
   552  	if err != nil {
   553  		t.Fatal(err)
   554  	}
   555  
   556  	defer os.RemoveAll(tmpDir)
   557  
   558  	unwantedPath := filepath.Join(tmpDir, "terraform-provider-test_v0.0.1_x2")
   559  	wantedPath := filepath.Join(tmpDir, "terraform-provider-test_v1.2.3_x3")
   560  
   561  	f, err := os.Create(unwantedPath)
   562  	if err != nil {
   563  		t.Fatal(err)
   564  	}
   565  	f.Close()
   566  	f, err = os.Create(wantedPath)
   567  	if err != nil {
   568  		t.Fatal(err)
   569  	}
   570  	f.Close()
   571  
   572  	i := &ProviderInstaller{
   573  		Dir:                   tmpDir,
   574  		PluginProtocolVersion: 3,
   575  		SkipVerify:            true,
   576  		Ui:                    cli.NewMockUi(),
   577  		registry:              registry.NewClient(Disco(server), nil),
   578  	}
   579  	purged, err := i.PurgeUnused(map[string]PluginMeta{
   580  		"test": PluginMeta{
   581  			Name:    "test",
   582  			Version: VersionStr("1.2.3"),
   583  			Path:    wantedPath,
   584  		},
   585  	})
   586  	if err != nil {
   587  		t.Fatal(err)
   588  	}
   589  
   590  	if got, want := purged.Count(), 1; got != want {
   591  		t.Errorf("wrong purged count %d; want %d", got, want)
   592  	}
   593  	if got, want := purged.Newest().Path, unwantedPath; got != want {
   594  		t.Errorf("wrong purged path %s; want %s", got, want)
   595  	}
   596  
   597  	files, err := ioutil.ReadDir(tmpDir)
   598  	if err != nil {
   599  		t.Fatal(err)
   600  	}
   601  
   602  	gotFilenames := make([]string, len(files))
   603  	for i, info := range files {
   604  		gotFilenames[i] = info.Name()
   605  	}
   606  	wantFilenames := []string{"terraform-provider-test_v1.2.3_x3"}
   607  
   608  	if !reflect.DeepEqual(gotFilenames, wantFilenames) {
   609  		t.Errorf("wrong filenames after purge\ngot:  %#v\nwant: %#v", gotFilenames, wantFilenames)
   610  	}
   611  }
   612  
   613  // Test fetching a provider's checksum file while verifying its signature.
   614  func TestProviderChecksum(t *testing.T) {
   615  	hashicorpKey, err := ioutil.ReadFile("testdata/hashicorp.asc")
   616  	if err != nil {
   617  		t.Fatal(err)
   618  	}
   619  
   620  	tests := []struct {
   621  		Name string
   622  		Resp *response.TerraformProviderPlatformLocation
   623  		Err  bool
   624  	}{
   625  		{
   626  			"good",
   627  			&response.TerraformProviderPlatformLocation{
   628  				Filename:            "terraform-provider-template_0.1.0_darwin_amd64.zip",
   629  				Shasum:              "95ac46d12c737be2f69913db36ddf557bd4fdc298d763d184fe679f915e8540d",
   630  				ShasumsURL:          "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS",
   631  				ShasumsSignatureURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.72D7468F.sig",
   632  				SigningKeys: response.SigningKeyList{
   633  					GPGKeys: []*response.GPGKey{
   634  						&response.GPGKey{
   635  							ASCIIArmor: string(hashicorpKey),
   636  						},
   637  					},
   638  				},
   639  			},
   640  			false,
   641  		},
   642  		{
   643  			"bad",
   644  			&response.TerraformProviderPlatformLocation{
   645  				Filename:            "terraform-provider-template_0.1.0_darwin_amd64.zip",
   646  				ShasumsURL:          "http://127.0.0.1:8080/terraform-provider-badsig/0.1.0/terraform-provider-badsig_0.1.0_SHA256SUMS",
   647  				ShasumsSignatureURL: "http://127.0.0.1:8080/terraform-provider-badsig/0.1.0/terraform-provider-badsig_0.1.0_SHA256SUMS.72D7468F.sig",
   648  				SigningKeys: response.SigningKeyList{
   649  					GPGKeys: []*response.GPGKey{
   650  						&response.GPGKey{
   651  							ASCIIArmor: string(hashicorpKey),
   652  						},
   653  					},
   654  				},
   655  			},
   656  			true,
   657  		},
   658  		{
   659  			"no keys",
   660  			&response.TerraformProviderPlatformLocation{
   661  				Filename:            "terraform-provider-template_0.1.0_darwin_amd64.zip",
   662  				ShasumsURL:          "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS",
   663  				ShasumsSignatureURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.72D7468F.sig",
   664  				SigningKeys: response.SigningKeyList{
   665  					GPGKeys: []*response.GPGKey{},
   666  				},
   667  			},
   668  			true,
   669  		},
   670  		{
   671  			"mismatch checksum",
   672  			&response.TerraformProviderPlatformLocation{
   673  				Filename:            "terraform-provider-template_0.1.0_darwin_amd64.zip",
   674  				Shasum:              "force mismatch",
   675  				ShasumsURL:          "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS",
   676  				ShasumsSignatureURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.72D7468F.sig",
   677  				SigningKeys: response.SigningKeyList{
   678  					GPGKeys: []*response.GPGKey{
   679  						&response.GPGKey{
   680  							ASCIIArmor: string(hashicorpKey),
   681  						},
   682  					},
   683  				},
   684  			},
   685  			true,
   686  		},
   687  		{
   688  			"missing checksum for file",
   689  			&response.TerraformProviderPlatformLocation{
   690  				Filename:            "terraform-provider-template_0.1.0_darwin_amd64_missing_checksum.zip",
   691  				Shasum:              "checksum",
   692  				ShasumsURL:          "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS",
   693  				ShasumsSignatureURL: "http://127.0.0.1:8080/terraform-provider-template/0.1.0/terraform-provider-template_0.1.0_SHA256SUMS.72D7468F.sig",
   694  				SigningKeys: response.SigningKeyList{
   695  					GPGKeys: []*response.GPGKey{
   696  						&response.GPGKey{
   697  							ASCIIArmor: string(hashicorpKey),
   698  						},
   699  					},
   700  				},
   701  			},
   702  			true,
   703  		},
   704  	}
   705  
   706  	i := ProviderInstaller{}
   707  
   708  	for _, test := range tests {
   709  		t.Run(test.Name, func(t *testing.T) {
   710  			sha256sum, err := i.getProviderChecksum(test.Resp)
   711  			if test.Err {
   712  				if err == nil {
   713  					t.Fatal("succeeded; want error")
   714  				}
   715  				return
   716  			} else if err != nil {
   717  				t.Fatalf("unexpected error: %s", err)
   718  			}
   719  
   720  			// get the expected checksum for our os/arch
   721  			sumData, err := ioutil.ReadFile("testdata/terraform-provider-template_0.1.0_SHA256SUMS")
   722  			if err != nil {
   723  				t.Fatal(err)
   724  			}
   725  
   726  			expected := checksumForFile(sumData, test.Resp.Filename)
   727  
   728  			if sha256sum != expected {
   729  				t.Fatalf("expected: %s\ngot %s\n", sha256sum, expected)
   730  			}
   731  		})
   732  	}
   733  }
   734  
   735  // newProviderInstaller returns a minimally-initialized ProviderInstaller
   736  func newProviderInstaller(s *httptest.Server) ProviderInstaller {
   737  	return ProviderInstaller{
   738  		registry:              registry.NewClient(Disco(s), nil),
   739  		OS:                    runtime.GOOS,
   740  		Arch:                  runtime.GOARCH,
   741  		PluginProtocolVersion: 4,
   742  	}
   743  }
   744  
   745  // Disco return a *disco.Disco mapping registry.terraform.io, localhost,
   746  // localhost.localdomain, and example.com to the test server.
   747  func Disco(s *httptest.Server) *disco.Disco {
   748  	services := map[string]interface{}{
   749  		// Note that both with and without trailing slashes are supported behaviours
   750  		"modules.v1":   fmt.Sprintf("%s/v1/modules", s.URL),
   751  		"providers.v1": fmt.Sprintf("%s/v1/providers", s.URL),
   752  	}
   753  	d := disco.New()
   754  	d.SetUserAgent(httpclient.TerraformUserAgent(version.String()))
   755  
   756  	d.ForceHostServices(svchost.Hostname("registry.terraform.io"), services)
   757  	d.ForceHostServices(svchost.Hostname("localhost"), services)
   758  	d.ForceHostServices(svchost.Hostname("localhost.localdomain"), services)
   759  	d.ForceHostServices(svchost.Hostname("example.com"), services)
   760  	return d
   761  }
   762  
   763  var versionList = response.TerraformProvider{
   764  	ID: "terraform-providers/test",
   765  	Versions: []*response.TerraformProviderVersion{
   766  		{Version: "1.2.1"},
   767  		{Version: "1.2.3"},
   768  		{
   769  			Version:   "1.2.4",
   770  			Protocols: []string{"4"},
   771  			Platforms: []*response.TerraformProviderPlatform{
   772  				{
   773  					OS:   "mockos",
   774  					Arch: "mockarch",
   775  				},
   776  			},
   777  		},
   778  	},
   779  }
   780  
   781  var downloadURLs = response.TerraformProviderPlatformLocation{
   782  	ShasumsURL:          "https://registry.terraform.io/terraform-provider-template/1.2.4/terraform-provider-test_1.2.4_SHA256SUMS",
   783  	ShasumsSignatureURL: "https://registry.terraform.io/terraform-provider-template/1.2.4/terraform-provider-test_1.2.4_SHA256SUMS.sig",
   784  	Filename:            "terraform-provider-template_1.2.4_darwin_amd64.zip",
   785  	DownloadURL:         "http://127.0.0.1:8080/v1/providers/terraform-providers/terraform-provider-test/1.2.4/terraform-provider-test_1.2.4_darwin_amd64.zip",
   786  }