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