config/module: allow registry download sources to be relative paths

It's not always easy or convenient for a web application to determine its
own absolute URL to return, so here we pragmatically allow the download
source string from a registry to be a path relative to the download
endpoint.

Since X-Terraform-Get is a go-getter string, not all valid values are
valid URLs and so we sniff for certain relative-path-looking prefixes
in order to decide whether to apply the relative lookup transform.
This commit is contained in:
Martin Atkins 2017-10-30 08:59:01 -07:00
parent 4a01bf0b97
commit 288f7c00e4
3 changed files with 48 additions and 1 deletions

View File

@ -49,6 +49,10 @@ var testMods = map[string][]testMod{
location: "file:///registry/exists",
version: "0.2.0",
}},
"relative/foo/bar": {{ // There is an exception for the "relative/" prefix in the test registry server
location: "/relative-path",
version: "0.2.0",
}},
"test-versions/name/provider": {
{version: "2.2.0"},
{version: "2.1.1"},
@ -103,7 +107,7 @@ func mockRegHandler() http.Handler {
mod := versions[0]
location := mod.location
if !strings.HasPrefix(location, "file:///") {
if !strings.HasPrefix(matches[0], "relative/") && !strings.HasPrefix(location, "file:///") {
// we can't use filepath.Abs because it will clean `//`
wd, _ := os.Getwd()
location = fmt.Sprintf("file://%s/%s", wd, location)

View File

@ -186,5 +186,25 @@ func (s *Storage) lookupModuleLocation(module *regsrc.Module, version string) (s
return "", fmt.Errorf("failed to get download URL for %q: %s resp:%s", module, resp.Status, body)
}
// If location looks like it's trying to be a relative URL, treat it as
// one.
//
// We don't do this for just _any_ location, since the X-Terraform-Get
// header is a go-getter location rather than a URL, and so not all
// possible values will parse reasonably as URLs.)
//
// When used in conjunction with go-getter we normally require this header
// to be an absolute URL, but we are more liberal here because third-party
// registry implementations may not "know" their own absolute URLs if
// e.g. they are running behind a reverse proxy frontend, or such.
if strings.HasPrefix(location, "/") || strings.HasPrefix(location, "./") || strings.HasPrefix(location, "../") {
locationURL, err := url.Parse(location)
if err != nil {
return "", fmt.Errorf("invalid relative URL for %q: %s", module, err)
}
locationURL = download.ResolveReference(locationURL)
location = locationURL.String()
}
return location, nil
}

View File

@ -93,7 +93,30 @@ func TestRegistryAuth(t *testing.T) {
}
}
func TestLookupModuleLocationRelative(t *testing.T) {
server := mockRegistry()
defer server.Close()
regDisco := testDisco(server)
storage := testStorage(t, regDisco)
src := "relative/foo/bar"
mod, err := regsrc.ParseModuleSource(src)
if err != nil {
t.Fatal(err)
}
got, err := storage.lookupModuleLocation(mod, "0.2.0")
if err != nil {
t.Fatal(err)
}
want := server.URL + "/relative-path"
if got != want {
t.Errorf("wrong location %s; want %s", got, want)
}
}
func TestAccLookupModuleVersions(t *testing.T) {
if os.Getenv("TF_ACC") == "" {
t.Skip()