internal/getproviders: Package URL should always be absolute

Registries backed by static files are likely to use relative paths to
their archives for simplicity's sake, but we'll normalize them to be
absolute before returning because the caller wouldn't otherwise know what
to resolve the URLs relative to.
This commit is contained in:
Martin Atkins 2020-01-08 16:47:49 -08:00
parent c8f7223adb
commit a77bc59c44
3 changed files with 22 additions and 19 deletions

View File

@ -201,6 +201,15 @@ func (c *registryClient) PackageMeta(provider addrs.Provider, version Version, t
} }
protoVersions.Sort() protoVersions.Sort()
downloadURL, err := url.Parse(body.DownloadURL)
if err != nil {
return PackageMeta{}, fmt.Errorf("registry response includes invalid download URL: %s", err)
}
downloadURL = resp.Request.URL.ResolveReference(downloadURL)
if downloadURL.Scheme != "http" && downloadURL.Scheme != "https" {
return PackageMeta{}, fmt.Errorf("registry response includes invalid download URL: must use http or https scheme")
}
ret := PackageMeta{ ret := PackageMeta{
ProtocolVersions: protoVersions, ProtocolVersions: protoVersions,
TargetPlatform: Platform{ TargetPlatform: Platform{
@ -208,7 +217,7 @@ func (c *registryClient) PackageMeta(provider addrs.Provider, version Version, t
Arch: body.Arch, Arch: body.Arch,
}, },
Filename: body.Filename, Filename: body.Filename,
Location: PackageHTTPURL(body.DownloadURL), Location: PackageHTTPURL(downloadURL.String()),
// SHA256Sum is populated below // SHA256Sum is populated below
} }

View File

@ -25,10 +25,10 @@ import (
// The second return value is a function to call at the end of a test function // The second return value is a function to call at the end of a test function
// to shut down the test server. After you call that function, the discovery // to shut down the test server. After you call that function, the discovery
// object becomes useless. // object becomes useless.
func testServices(t *testing.T) (*disco.Disco, func()) { func testServices(t *testing.T) (services *disco.Disco, baseURL string, cleanup func()) {
server := httptest.NewServer(http.HandlerFunc(fakeRegistryHandler)) server := httptest.NewServer(http.HandlerFunc(fakeRegistryHandler))
services := disco.New() services = disco.New()
services.ForceHostServices(svchost.Hostname("example.com"), map[string]interface{}{ services.ForceHostServices(svchost.Hostname("example.com"), map[string]interface{}{
"providers.v1": server.URL + "/providers/v1/", "providers.v1": server.URL + "/providers/v1/",
}) })
@ -42,7 +42,7 @@ func testServices(t *testing.T) (*disco.Disco, func()) {
"providers.v1": server.URL + "/fails-immediately/", "providers.v1": server.URL + "/fails-immediately/",
}) })
return services, func() { return services, server.URL, func() {
server.Close() server.Close()
} }
} }
@ -53,10 +53,10 @@ func testServices(t *testing.T) (*disco.Disco, func()) {
// //
// As with testServices, the second return value is a function to call at the end // As with testServices, the second return value is a function to call at the end
// of your test in order to shut down the test server. // of your test in order to shut down the test server.
func testRegistrySource(t *testing.T) (*RegistrySource, func()) { func testRegistrySource(t *testing.T) (source *RegistrySource, baseURL string, cleanup func()) {
services, close := testServices(t) services, baseURL, close := testServices(t)
source := NewRegistrySource(services) source = NewRegistrySource(services)
return source, close return source, baseURL, close
} }
func fakeRegistryHandler(resp http.ResponseWriter, req *http.Request) { func fakeRegistryHandler(resp http.ResponseWriter, req *http.Request) {

View File

@ -14,7 +14,7 @@ import (
) )
func TestSourceAvailableVersions(t *testing.T) { func TestSourceAvailableVersions(t *testing.T) {
source, close := testRegistrySource(t) source, baseURL, close := testRegistrySource(t)
defer close() defer close()
tests := []struct { tests := []struct {
@ -52,15 +52,10 @@ func TestSourceAvailableVersions(t *testing.T) {
{ {
"fails.example.com/foo/bar", "fails.example.com/foo/bar",
nil, nil,
`could not query provider registry for fails.example.com/foo/bar: Get http://placeholder-origin/fails-immediately/foo/bar/versions: EOF`, `could not query provider registry for fails.example.com/foo/bar: Get ` + baseURL + `/fails-immediately/foo/bar/versions: EOF`,
}, },
} }
// Sometimes error messages contain specific HTTP endpoint URLs, but
// since our test server is on a random port we'd not be able to
// consistently match those. Instead, we'll normalize the URLs.
urlPattern := regexp.MustCompile(`http://[^/]+/`)
for _, test := range tests { for _, test := range tests {
t.Run(test.provider, func(t *testing.T) { t.Run(test.provider, func(t *testing.T) {
// TEMP: We don't yet have a function for parsing provider // TEMP: We don't yet have a function for parsing provider
@ -78,8 +73,7 @@ func TestSourceAvailableVersions(t *testing.T) {
if test.wantErr == "" { if test.wantErr == "" {
t.Fatalf("wrong error\ngot: %s\nwant: <nil>", err.Error()) t.Fatalf("wrong error\ngot: %s\nwant: <nil>", err.Error())
} }
gotErr := urlPattern.ReplaceAllLiteralString(err.Error(), "http://placeholder-origin/") if got, want := err.Error(), test.wantErr; got != want {
if got, want := gotErr, test.wantErr; got != want {
t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want) t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want)
} }
return return
@ -106,7 +100,7 @@ func TestSourceAvailableVersions(t *testing.T) {
} }
func TestSourcePackageMeta(t *testing.T) { func TestSourcePackageMeta(t *testing.T) {
source, close := testRegistrySource(t) source, baseURL, close := testRegistrySource(t)
defer close() defer close()
tests := []struct { tests := []struct {
@ -126,7 +120,7 @@ func TestSourcePackageMeta(t *testing.T) {
ProtocolVersions: VersionList{versions.MustParseVersion("5.0.0")}, ProtocolVersions: VersionList{versions.MustParseVersion("5.0.0")},
TargetPlatform: Platform{"linux", "amd64"}, TargetPlatform: Platform{"linux", "amd64"},
Filename: "happycloud_1.2.0.zip", Filename: "happycloud_1.2.0.zip",
Location: PackageHTTPURL("/pkg/happycloud_1.2.0.zip"), Location: PackageHTTPURL(baseURL + "/pkg/happycloud_1.2.0.zip"),
SHA256Sum: [32]uint8{30: 0xf0, 31: 0x0d}, // fake registry uses a memorable sum SHA256Sum: [32]uint8{30: 0xf0, 31: 0x0d}, // fake registry uses a memorable sum
}, },
``, ``,