github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/getproviders/http_mirror_source_test.go (about)

     1  package getproviders
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"net/url"
     9  	"testing"
    10  
    11  	"github.com/google/go-cmp/cmp"
    12  	svchost "github.com/hashicorp/terraform-svchost"
    13  	svcauth "github.com/hashicorp/terraform-svchost/auth"
    14  
    15  	"github.com/hashicorp/terraform/internal/addrs"
    16  )
    17  
    18  func TestHTTPMirrorSource(t *testing.T) {
    19  	// For mirrors we require a HTTPS server, so we'll use httptest to create
    20  	// one. However, that means we need to instantiate the source in an unusual
    21  	// way to force it to use the test client that is configured to trust the
    22  	// test server.
    23  	httpServer := httptest.NewTLSServer(http.HandlerFunc(testHTTPMirrorSourceHandler))
    24  	defer httpServer.Close()
    25  	httpClient := httpServer.Client()
    26  	baseURL, err := url.Parse(httpServer.URL)
    27  	if err != nil {
    28  		t.Fatalf("httptest.NewTLSServer returned a server with an invalid URL")
    29  	}
    30  	creds := svcauth.StaticCredentialsSource(map[svchost.Hostname]map[string]interface{}{
    31  		svchost.Hostname(baseURL.Host): {
    32  			"token": "placeholder-token",
    33  		},
    34  	})
    35  	source := newHTTPMirrorSourceWithHTTPClient(baseURL, creds, httpClient)
    36  
    37  	existingProvider := addrs.MustParseProviderSourceString("terraform.io/test/exists")
    38  	missingProvider := addrs.MustParseProviderSourceString("terraform.io/test/missing")
    39  	failingProvider := addrs.MustParseProviderSourceString("terraform.io/test/fails")
    40  	redirectingProvider := addrs.MustParseProviderSourceString("terraform.io/test/redirects")
    41  	redirectLoopProvider := addrs.MustParseProviderSourceString("terraform.io/test/redirect-loop")
    42  	tosPlatform := Platform{OS: "tos", Arch: "m68k"}
    43  
    44  	t.Run("AvailableVersions for provider that exists", func(t *testing.T) {
    45  		got, _, err := source.AvailableVersions(context.Background(), existingProvider)
    46  		if err != nil {
    47  			t.Fatalf("unexpected error: %s", err)
    48  		}
    49  		want := VersionList{
    50  			MustParseVersion("1.0.0"),
    51  			MustParseVersion("1.0.1"),
    52  			MustParseVersion("1.0.2-beta.1"),
    53  		}
    54  		if diff := cmp.Diff(want, got); diff != "" {
    55  			t.Errorf("wrong result\n%s", diff)
    56  		}
    57  	})
    58  	t.Run("AvailableVersions for provider that doesn't exist", func(t *testing.T) {
    59  		_, _, err := source.AvailableVersions(context.Background(), missingProvider)
    60  		switch err := err.(type) {
    61  		case ErrProviderNotFound:
    62  			if got, want := err.Provider, missingProvider; got != want {
    63  				t.Errorf("wrong provider in error\ngot:  %s\nwant: %s", got, want)
    64  			}
    65  		default:
    66  			t.Fatalf("wrong error type %T; want ErrProviderNotFound", err)
    67  		}
    68  	})
    69  	t.Run("AvailableVersions without required credentials", func(t *testing.T) {
    70  		unauthSource := newHTTPMirrorSourceWithHTTPClient(baseURL, nil, httpClient)
    71  		_, _, err := unauthSource.AvailableVersions(context.Background(), existingProvider)
    72  		switch err := err.(type) {
    73  		case ErrUnauthorized:
    74  			if got, want := string(err.Hostname), baseURL.Host; got != want {
    75  				t.Errorf("wrong hostname in error\ngot:  %s\nwant: %s", got, want)
    76  			}
    77  		default:
    78  			t.Fatalf("wrong error type %T; want ErrUnauthorized", err)
    79  		}
    80  	})
    81  	t.Run("AvailableVersions when the response is a server error", func(t *testing.T) {
    82  		_, _, err := source.AvailableVersions(context.Background(), failingProvider)
    83  		switch err := err.(type) {
    84  		case ErrQueryFailed:
    85  			if got, want := err.Provider, failingProvider; got != want {
    86  				t.Errorf("wrong provider in error\ngot:  %s\nwant: %s", got, want)
    87  			}
    88  			if err.MirrorURL != source.baseURL {
    89  				t.Errorf("error does not refer to the mirror URL")
    90  			}
    91  		default:
    92  			t.Fatalf("wrong error type %T; want ErrQueryFailed", err)
    93  		}
    94  	})
    95  	t.Run("AvailableVersions for provider that redirects", func(t *testing.T) {
    96  		got, _, err := source.AvailableVersions(context.Background(), redirectingProvider)
    97  		if err != nil {
    98  			t.Fatalf("unexpected error: %s", err)
    99  		}
   100  		want := VersionList{
   101  			MustParseVersion("1.0.0"),
   102  		}
   103  		if diff := cmp.Diff(want, got); diff != "" {
   104  			t.Errorf("wrong result\n%s", diff)
   105  		}
   106  	})
   107  	t.Run("AvailableVersions for provider that redirects too much", func(t *testing.T) {
   108  		_, _, err := source.AvailableVersions(context.Background(), redirectLoopProvider)
   109  		if err == nil {
   110  			t.Fatalf("succeeded; expected error")
   111  		}
   112  	})
   113  	t.Run("PackageMeta for a version that exists and has a hash", func(t *testing.T) {
   114  		version := MustParseVersion("1.0.0")
   115  		got, err := source.PackageMeta(context.Background(), existingProvider, version, tosPlatform)
   116  		if err != nil {
   117  			t.Fatalf("unexpected error: %s", err)
   118  		}
   119  
   120  		want := PackageMeta{
   121  			Provider:       existingProvider,
   122  			Version:        version,
   123  			TargetPlatform: tosPlatform,
   124  			Filename:       "terraform-provider-test_v1.0.0_tos_m68k.zip",
   125  			Location:       PackageHTTPURL(httpServer.URL + "/terraform.io/test/exists/terraform-provider-test_v1.0.0_tos_m68k.zip"),
   126  			Authentication: packageHashAuthentication{
   127  				RequiredHashes: []Hash{"h1:placeholder-hash"},
   128  				AllHashes:      []Hash{"h1:placeholder-hash", "h0:unacceptable-hash"},
   129  				Platform:       Platform{"tos", "m68k"},
   130  			},
   131  		}
   132  		if diff := cmp.Diff(want, got); diff != "" {
   133  			t.Errorf("wrong result\n%s", diff)
   134  		}
   135  
   136  		gotHashes := got.AcceptableHashes()
   137  		wantHashes := []Hash{"h1:placeholder-hash", "h0:unacceptable-hash"}
   138  		if diff := cmp.Diff(wantHashes, gotHashes); diff != "" {
   139  			t.Errorf("wrong acceptable hashes\n%s", diff)
   140  		}
   141  	})
   142  	t.Run("PackageMeta for a version that exists and has no hash", func(t *testing.T) {
   143  		version := MustParseVersion("1.0.1")
   144  		got, err := source.PackageMeta(context.Background(), existingProvider, version, tosPlatform)
   145  		if err != nil {
   146  			t.Fatalf("unexpected error: %s", err)
   147  		}
   148  
   149  		want := PackageMeta{
   150  			Provider:       existingProvider,
   151  			Version:        version,
   152  			TargetPlatform: tosPlatform,
   153  			Filename:       "terraform-provider-test_v1.0.1_tos_m68k.zip",
   154  			Location:       PackageHTTPURL(httpServer.URL + "/terraform.io/test/exists/terraform-provider-test_v1.0.1_tos_m68k.zip"),
   155  			Authentication: nil,
   156  		}
   157  		if diff := cmp.Diff(want, got); diff != "" {
   158  			t.Errorf("wrong result\n%s", diff)
   159  		}
   160  	})
   161  	t.Run("PackageMeta for a version that exists but has no archives", func(t *testing.T) {
   162  		version := MustParseVersion("1.0.2-beta.1")
   163  		_, err := source.PackageMeta(context.Background(), existingProvider, version, tosPlatform)
   164  		switch err := err.(type) {
   165  		case ErrPlatformNotSupported:
   166  			if got, want := err.Provider, existingProvider; got != want {
   167  				t.Errorf("wrong provider in error\ngot:  %s\nwant: %s", got, want)
   168  			}
   169  			if got, want := err.Platform, tosPlatform; got != want {
   170  				t.Errorf("wrong platform in error\ngot:  %s\nwant: %s", got, want)
   171  			}
   172  			if err.MirrorURL != source.baseURL {
   173  				t.Errorf("error does not contain the mirror URL")
   174  			}
   175  		default:
   176  			t.Fatalf("wrong error type %T; want ErrPlatformNotSupported", err)
   177  		}
   178  	})
   179  	t.Run("PackageMeta with redirect to a version that exists", func(t *testing.T) {
   180  		version := MustParseVersion("1.0.0")
   181  		got, err := source.PackageMeta(context.Background(), redirectingProvider, version, tosPlatform)
   182  		if err != nil {
   183  			t.Fatalf("unexpected error: %s", err)
   184  		}
   185  
   186  		want := PackageMeta{
   187  			Provider:       redirectingProvider,
   188  			Version:        version,
   189  			TargetPlatform: tosPlatform,
   190  			Filename:       "terraform-provider-test.zip",
   191  
   192  			// NOTE: The final URL is interpreted relative to the redirect
   193  			// target, not relative to what we originally requested.
   194  			Location: PackageHTTPURL(httpServer.URL + "/redirect-target/terraform-provider-test.zip"),
   195  		}
   196  		if diff := cmp.Diff(want, got); diff != "" {
   197  			t.Errorf("wrong result\n%s", diff)
   198  		}
   199  	})
   200  	t.Run("PackageMeta when the response is a server error", func(t *testing.T) {
   201  		version := MustParseVersion("1.0.0")
   202  		_, err := source.PackageMeta(context.Background(), failingProvider, version, tosPlatform)
   203  		switch err := err.(type) {
   204  		case ErrQueryFailed:
   205  			if got, want := err.Provider, failingProvider; got != want {
   206  				t.Errorf("wrong provider in error\ngot:  %s\nwant: %s", got, want)
   207  			}
   208  			if err.MirrorURL != source.baseURL {
   209  				t.Errorf("error does not contain the mirror URL")
   210  			}
   211  		default:
   212  			t.Fatalf("wrong error type %T; want ErrQueryFailed", err)
   213  		}
   214  	})
   215  }
   216  
   217  func testHTTPMirrorSourceHandler(resp http.ResponseWriter, req *http.Request) {
   218  	if auth := req.Header.Get("authorization"); auth != "Bearer placeholder-token" {
   219  		resp.WriteHeader(401)
   220  		fmt.Fprintln(resp, "incorrect auth token")
   221  	}
   222  
   223  	switch req.URL.Path {
   224  	case "/terraform.io/test/exists/index.json":
   225  		resp.Header().Add("Content-Type", "application/json; ignored=yes")
   226  		resp.WriteHeader(200)
   227  		fmt.Fprint(resp, `
   228  			{
   229  				"versions": {
   230  					"1.0.0": {},
   231  					"1.0.1": {},
   232  					"1.0.2-beta.1": {}
   233  				}
   234  			}
   235  		`)
   236  
   237  	case "/terraform.io/test/fails/index.json", "/terraform.io/test/fails/1.0.0.json":
   238  		resp.WriteHeader(500)
   239  		fmt.Fprint(resp, "server error")
   240  
   241  	case "/terraform.io/test/exists/1.0.0.json":
   242  		resp.Header().Add("Content-Type", "application/json; ignored=yes")
   243  		resp.WriteHeader(200)
   244  		fmt.Fprint(resp, `
   245  			{
   246  				"archives": {
   247  					"tos_m68k": {
   248  						"url": "terraform-provider-test_v1.0.0_tos_m68k.zip",
   249  						"hashes": [
   250  							"h1:placeholder-hash",
   251  							"h0:unacceptable-hash"
   252  						]
   253  					}
   254  				}
   255  			}
   256  		`)
   257  
   258  	case "/terraform.io/test/exists/1.0.1.json":
   259  		resp.Header().Add("Content-Type", "application/json; ignored=yes")
   260  		resp.WriteHeader(200)
   261  		fmt.Fprint(resp, `
   262  			{
   263  				"archives": {
   264  					"tos_m68k": {
   265  						"url": "terraform-provider-test_v1.0.1_tos_m68k.zip"
   266  					}
   267  				}
   268  			}
   269  		`)
   270  
   271  	case "/terraform.io/test/exists/1.0.2-beta.1.json":
   272  		resp.Header().Add("Content-Type", "application/json; ignored=yes")
   273  		resp.WriteHeader(200)
   274  		fmt.Fprint(resp, `
   275  			{
   276  				"archives": {}
   277  			}
   278  		`)
   279  
   280  	case "/terraform.io/test/redirects/index.json":
   281  		resp.Header().Add("location", "/redirect-target/index.json")
   282  		resp.WriteHeader(301)
   283  		fmt.Fprint(resp, "redirect")
   284  
   285  	case "/redirect-target/index.json":
   286  		resp.Header().Add("Content-Type", "application/json")
   287  		resp.WriteHeader(200)
   288  		fmt.Fprint(resp, `
   289  			{
   290  				"versions": {
   291  					"1.0.0": {}
   292  				}
   293  			}
   294  		`)
   295  
   296  	case "/terraform.io/test/redirects/1.0.0.json":
   297  		resp.Header().Add("location", "/redirect-target/1.0.0.json")
   298  		resp.WriteHeader(301)
   299  		fmt.Fprint(resp, "redirect")
   300  
   301  	case "/redirect-target/1.0.0.json":
   302  		resp.Header().Add("Content-Type", "application/json")
   303  		resp.WriteHeader(200)
   304  		fmt.Fprint(resp, `
   305  			{
   306  				"archives": {
   307  					"tos_m68k": {
   308  						"url": "terraform-provider-test.zip"
   309  					}
   310  				}
   311  			}
   312  		`)
   313  
   314  	case "/terraform.io/test/redirect-loop/index.json":
   315  		// This is intentionally redirecting to itself, to create a loop.
   316  		resp.Header().Add("location", req.URL.Path)
   317  		resp.WriteHeader(301)
   318  		fmt.Fprint(resp, "redirect loop")
   319  
   320  	default:
   321  		resp.WriteHeader(404)
   322  		fmt.Fprintln(resp, "not found")
   323  	}
   324  }