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 }