add credentials to module.Storage

Provide a way to pass in credentials to be used by the module.Storage
when contacting registries.

Remove the mockTLSServer and use a static discovery map pointing to the
http url for tests.
This commit is contained in:
James Bardin 2017-10-27 15:07:24 -04:00
parent 3a495ffe56
commit 4e8fe97556
7 changed files with 145 additions and 189 deletions

View File

@ -16,7 +16,6 @@ import (
version "github.com/hashicorp/go-version" version "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform/registry/regsrc" "github.com/hashicorp/terraform/registry/regsrc"
"github.com/hashicorp/terraform/registry/response" "github.com/hashicorp/terraform/registry/response"
"github.com/hashicorp/terraform/svchost/disco"
) )
// Map of module names and location of test modules. // Map of module names and location of test modules.
@ -162,7 +161,7 @@ func mockRegHandler() http.Handler {
mux.HandleFunc("/.well-known/terraform.json", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/.well-known/terraform.json", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
io.WriteString(w, `{"modules.v1":"/v1/modules/"}`) io.WriteString(w, `{"modules.v1":"http://localhost/v1/modules/"}`)
}) })
return mux return mux
} }
@ -174,26 +173,16 @@ func mockRegistry() *httptest.Server {
return server return server
} }
func mockTLSRegistry() *httptest.Server {
server := httptest.NewTLSServer(mockRegHandler())
return server
}
// GitHub archives always contain the module source in a single subdirectory, // GitHub archives always contain the module source in a single subdirectory,
// so the registry will return a path with with a `//*` suffix. We need to make // so the registry will return a path with with a `//*` suffix. We need to make
// sure this doesn't intefere with our internal handling of `//` subdir. // sure this doesn't intefere with our internal handling of `//` subdir.
func TestRegistryGitHubArchive(t *testing.T) { func TestRegistryGitHubArchive(t *testing.T) {
server := mockTLSRegistry() server := mockRegistry()
defer server.Close() defer server.Close()
d := regDisco
regDisco = disco.NewDisco() disco := testDisco(server)
regDisco.Transport = mockTransport(server) storage := testStorage(t, disco)
defer func() {
regDisco = d
}()
storage := testStorage(t)
tree := NewTree("", testConfig(t, "registry-tar-subdir")) tree := NewTree("", testConfig(t, "registry-tar-subdir"))
storage.Mode = GetModeGet storage.Mode = GetModeGet
@ -232,17 +221,11 @@ func TestRegistryGitHubArchive(t *testing.T) {
// Test that the //subdir notation can be used with registry modules // Test that the //subdir notation can be used with registry modules
func TestRegisryModuleSubdir(t *testing.T) { func TestRegisryModuleSubdir(t *testing.T) {
server := mockTLSRegistry() server := mockRegistry()
defer server.Close() defer server.Close()
d := regDisco disco := testDisco(server)
regDisco = disco.NewDisco() storage := testStorage(t, disco)
regDisco.Transport = mockTransport(server)
defer func() {
regDisco = d
}()
storage := testStorage(t)
tree := NewTree("", testConfig(t, "registry-subdir")) tree := NewTree("", testConfig(t, "registry-subdir"))
storage.Mode = GetModeGet storage.Mode = GetModeGet
@ -277,7 +260,8 @@ func TestAccRegistryDiscover(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
loc, err := lookupModuleLocation(nil, module, "") s := NewStorage("/tmp", nil, nil)
loc, err := s.lookupModuleLocation(module, "")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -301,7 +285,7 @@ func TestAccRegistryLoad(t *testing.T) {
t.Skip("skipping ACC test") t.Skip("skipping ACC test")
} }
storage := testStorage(t) storage := testStorage(t, nil)
tree := NewTree("", testConfig(t, "registry-load")) tree := NewTree("", testConfig(t, "registry-load"))
storage.Mode = GetModeGet storage.Mode = GetModeGet

View File

@ -1,13 +1,17 @@
package module package module
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http/httptest"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/svchost"
"github.com/hashicorp/terraform/svchost/disco"
) )
func init() { func init() {
@ -41,7 +45,22 @@ func testConfig(t *testing.T, n string) *config.Config {
return c return c
} }
func testStorage(t *testing.T) *Storage { func testStorage(t *testing.T, d *disco.Disco) *Storage {
t.Helper() t.Helper()
return &Storage{StorageDir: tempDir(t)} return NewStorage(tempDir(t), d, nil)
}
// test discovery maps registry.terraform.io, localhost, localhost.localdomain,
// and example.com to the test server.
func testDisco(s *httptest.Server) *disco.Disco {
services := map[string]interface{}{
"modules.v1": fmt.Sprintf("%s/v1/modules/", s.URL),
}
d := disco.NewDisco()
d.ForceHostServices(svchost.Hostname("registry.terraform.io"), services)
d.ForceHostServices(svchost.Hostname("localhost"), services)
d.ForceHostServices(svchost.Hostname("localhost.localdomain"), services)
d.ForceHostServices(svchost.Hostname("example.com"), services)
return d
} }

View File

@ -16,7 +16,6 @@ import (
"github.com/hashicorp/terraform/registry/regsrc" "github.com/hashicorp/terraform/registry/regsrc"
"github.com/hashicorp/terraform/registry/response" "github.com/hashicorp/terraform/registry/response"
"github.com/hashicorp/terraform/svchost" "github.com/hashicorp/terraform/svchost"
"github.com/hashicorp/terraform/svchost/disco"
"github.com/hashicorp/terraform/version" "github.com/hashicorp/terraform/version"
) )
@ -33,7 +32,6 @@ const (
var ( var (
httpClient *http.Client httpClient *http.Client
tfVersion = version.String() tfVersion = version.String()
regDisco = disco.NewDisco()
) )
func init() { func init() {
@ -47,16 +45,8 @@ func (e errModuleNotFound) Error() string {
return `module "` + string(e) + `" not found` return `module "` + string(e) + `" not found`
} }
func discoverRegURL(d *disco.Disco, module *regsrc.Module) *url.URL { func (s *Storage) discoverRegURL(module *regsrc.Module) *url.URL {
if d == nil { regURL := s.Services.DiscoverServiceURL(svchost.Hostname(module.RawHost.Normalized()), serviceID)
d = regDisco
}
if module.RawHost == nil {
module.RawHost = regsrc.NewFriendlyHost(defaultRegistry)
}
regURL := d.DiscoverServiceURL(svchost.Hostname(module.RawHost.Normalized()), serviceID)
if regURL == nil { if regURL == nil {
regURL = &url.URL{ regURL = &url.URL{
Scheme: "https", Scheme: "https",
@ -72,9 +62,29 @@ func discoverRegURL(d *disco.Disco, module *regsrc.Module) *url.URL {
return regURL return regURL
} }
func (s *Storage) addRequestCreds(host svchost.Hostname, req *http.Request) {
if s.Creds == nil {
return
}
creds, err := s.Creds.ForHost(host)
if err != nil {
log.Printf("[WARNING] Failed to get credentials for %s: %s (ignoring)", host, err)
return
}
if creds != nil {
creds.PrepareRequest(req)
}
}
// Lookup module versions in the registry. // Lookup module versions in the registry.
func lookupModuleVersions(d *disco.Disco, module *regsrc.Module) (*response.ModuleVersions, error) { func (s *Storage) lookupModuleVersions(module *regsrc.Module) (*response.ModuleVersions, error) {
service := discoverRegURL(d, module) if module.RawHost == nil {
module.RawHost = regsrc.NewFriendlyHost(defaultRegistry)
}
service := s.discoverRegURL(module)
p, err := url.Parse(path.Join(module.Module(), "versions")) p, err := url.Parse(path.Join(module.Module(), "versions"))
if err != nil { if err != nil {
@ -90,22 +100,10 @@ func lookupModuleVersions(d *disco.Disco, module *regsrc.Module) (*response.Modu
return nil, err return nil, err
} }
s.addRequestCreds(svchost.Hostname(module.RawHost.Normalized()), req)
req.Header.Set(xTerraformVersion, tfVersion) req.Header.Set(xTerraformVersion, tfVersion)
if d == nil { resp, err := httpClient.Do(req)
d = regDisco
}
// if discovery required a custom transport, then we should use that too
client := httpClient
if d.Transport != nil {
client = &http.Client{
Transport: d.Transport,
Timeout: requestTimeout,
}
}
resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -131,8 +129,12 @@ func lookupModuleVersions(d *disco.Disco, module *regsrc.Module) (*response.Modu
} }
// lookup the location of a specific module version in the registry // lookup the location of a specific module version in the registry
func lookupModuleLocation(d *disco.Disco, module *regsrc.Module, version string) (string, error) { func (s *Storage) lookupModuleLocation(module *regsrc.Module, version string) (string, error) {
service := discoverRegURL(d, module) if module.RawHost == nil {
module.RawHost = regsrc.NewFriendlyHost(defaultRegistry)
}
service := s.discoverRegURL(module)
var p *url.URL var p *url.URL
var err error var err error
@ -155,16 +157,7 @@ func lookupModuleLocation(d *disco.Disco, module *regsrc.Module, version string)
req.Header.Set(xTerraformVersion, tfVersion) req.Header.Set(xTerraformVersion, tfVersion)
// if discovery required a custom transport, then we should use that too resp, err := httpClient.Do(req)
client := httpClient
if regDisco.Transport != nil {
client = &http.Client{
Transport: regDisco.Transport,
Timeout: requestTimeout,
}
}
resp, err := client.Do(req)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -1,75 +1,19 @@
package module package module
import ( import (
"context"
"net"
"net/http"
"net/http/httptest"
"net/url"
"os" "os"
"testing" "testing"
"time"
cleanhttp "github.com/hashicorp/go-cleanhttp"
version "github.com/hashicorp/go-version" version "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform/registry/regsrc" "github.com/hashicorp/terraform/registry/regsrc"
"github.com/hashicorp/terraform/svchost/disco" "github.com/hashicorp/terraform/svchost/disco"
) )
// Return a transport to use for this test server.
// This not only loads the tls.Config from the test server for proper cert
// validation, but also inserts a Dialer that resolves localhost and
// example.com to 127.0.0.1 with the correct port, since 127.0.0.1 on its own
// isn't a valid registry hostname.
// TODO: cert validation not working here, so we use don't verify for now.
func mockTransport(server *httptest.Server) *http.Transport {
u, _ := url.Parse(server.URL)
_, port, _ := net.SplitHostPort(u.Host)
transport := cleanhttp.DefaultTransport()
transport.TLSClientConfig = server.TLS
transport.TLSClientConfig.InsecureSkipVerify = true
transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
host, _, _ := net.SplitHostPort(addr)
switch host {
case "example.com", "localhost", "localhost.localdomain", "registry.terraform.io":
addr = "127.0.0.1"
if port != "" {
addr += ":" + port
}
}
return (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext(ctx, network, addr)
}
return transport
}
func TestMockDiscovery(t *testing.T) {
server := mockTLSRegistry()
defer server.Close()
regDisco := disco.NewDisco()
regDisco.Transport = mockTransport(server)
regURL := regDisco.DiscoverServiceURL("example.com", serviceID)
if regURL == nil {
t.Fatal("no registry service discovered")
}
if regURL.Host != "example.com" {
t.Fatal("expected registry host example.com, got:", regURL.Host)
}
}
func TestLookupModuleVersions(t *testing.T) { func TestLookupModuleVersions(t *testing.T) {
server := mockTLSRegistry() server := mockRegistry()
defer server.Close() defer server.Close()
regDisco := disco.NewDisco()
regDisco.Transport = mockTransport(server) regDisco := testDisco(server)
// test with and without a hostname // test with and without a hostname
for _, src := range []string{ for _, src := range []string{
@ -81,7 +25,8 @@ func TestLookupModuleVersions(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
resp, err := lookupModuleVersions(regDisco, modsrc) s := &Storage{Services: regDisco}
resp, err := s.lookupModuleVersions(modsrc)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -125,7 +70,10 @@ func TestAccLookupModuleVersions(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
resp, err := lookupModuleVersions(regDisco, modsrc) s := &Storage{
Services: regDisco,
}
resp, err := s.lookupModuleVersions(modsrc)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -10,6 +10,7 @@ import (
getter "github.com/hashicorp/go-getter" getter "github.com/hashicorp/go-getter"
"github.com/hashicorp/terraform/registry/regsrc" "github.com/hashicorp/terraform/registry/regsrc"
"github.com/hashicorp/terraform/svchost/auth"
"github.com/hashicorp/terraform/svchost/disco" "github.com/hashicorp/terraform/svchost/disco"
"github.com/mitchellh/cli" "github.com/mitchellh/cli"
) )
@ -55,26 +56,44 @@ type moduleRecord struct {
registry bool registry bool
} }
// Storage implements methods to record and fetch metadata about the // Storage implements methods to manage the storage of modules.
// modules that have been fetched and stored locally. The getter.Storgae // This is used by Tree.Load to query registries, authenticate requests, and
// abstraction doesn't provide the information needed to know which versions of // store modules locally.
// a module have been stored, or their location.
type Storage struct { type Storage struct {
// StorageDir is the directory where all modules will be stored. // StorageDir is the full path to the directory where all modules will be
// stored.
StorageDir string StorageDir string
// Services is a *Disco which ay have services and credentials pre-loaded. // Services is a required *disco.Disco, which may have services and
// credentials pre-loaded.
Services *disco.Disco Services *disco.Disco
// Creds optionally provides credentials for communicating with service
// providers.
Creds auth.CredentialsSource
// Ui is an optional cli.Ui for user output // Ui is an optional cli.Ui for user output
Ui cli.Ui Ui cli.Ui
// Mode is the GetMode that will be used for various operations. // Mode is the GetMode that will be used for various operations.
Mode GetMode Mode GetMode
} }
func NewStorage(dir string, services *disco.Disco, creds auth.CredentialsSource) *Storage {
s := &Storage{
StorageDir: dir,
Services: services,
Creds: creds,
}
// make sure this isn't nil
if s.Services == nil {
s.Services = disco.NewDisco()
}
return s
}
// loadManifest returns the moduleManifest file from the parent directory. // loadManifest returns the moduleManifest file from the parent directory.
func (m Storage) loadManifest() (moduleManifest, error) { func (s Storage) loadManifest() (moduleManifest, error) {
manifest := moduleManifest{} manifest := moduleManifest{}
manifestPath := filepath.Join(m.StorageDir, manifestName) manifestPath := filepath.Join(s.StorageDir, manifestName)
data, err := ioutil.ReadFile(manifestPath) data, err := ioutil.ReadFile(manifestPath)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return manifest, err return manifest, err
@ -94,8 +113,8 @@ func (m Storage) loadManifest() (moduleManifest, error) {
// root directory. The storage method loads the entire file and rewrites it // root directory. The storage method loads the entire file and rewrites it
// each time. This is only done a few times during init, so efficiency is // each time. This is only done a few times during init, so efficiency is
// not a concern. // not a concern.
func (m Storage) recordModule(rec moduleRecord) error { func (s Storage) recordModule(rec moduleRecord) error {
manifest, err := m.loadManifest() manifest, err := s.loadManifest()
if err != nil { if err != nil {
// if there was a problem with the file, we will attempt to write a new // if there was a problem with the file, we will attempt to write a new
// one. Any non-data related error should surface there. // one. Any non-data related error should surface there.
@ -124,15 +143,15 @@ func (m Storage) recordModule(rec moduleRecord) error {
panic(err) panic(err)
} }
manifestPath := filepath.Join(m.StorageDir, manifestName) manifestPath := filepath.Join(s.StorageDir, manifestName)
return ioutil.WriteFile(manifestPath, js, 0644) return ioutil.WriteFile(manifestPath, js, 0644)
} }
// load the manifest from dir, and return all module versions matching the // load the manifest from dir, and return all module versions matching the
// provided source. Records with no version info will be skipped, as they need // provided source. Records with no version info will be skipped, as they need
// to be uniquely identified by other means. // to be uniquely identified by other means.
func (m Storage) moduleVersions(source string) ([]moduleRecord, error) { func (s Storage) moduleVersions(source string) ([]moduleRecord, error) {
manifest, err := m.loadManifest() manifest, err := s.loadManifest()
if err != nil { if err != nil {
return manifest.Modules, err return manifest.Modules, err
} }
@ -148,8 +167,8 @@ func (m Storage) moduleVersions(source string) ([]moduleRecord, error) {
return matching, nil return matching, nil
} }
func (m Storage) moduleDir(key string) (string, error) { func (s Storage) moduleDir(key string) (string, error) {
manifest, err := m.loadManifest() manifest, err := s.loadManifest()
if err != nil { if err != nil {
return "", err return "", err
} }
@ -164,8 +183,8 @@ func (m Storage) moduleDir(key string) (string, error) {
} }
// return only the root directory of the module stored in dir. // return only the root directory of the module stored in dir.
func (m Storage) getModuleRoot(dir string) (string, error) { func (s Storage) getModuleRoot(dir string) (string, error) {
manifest, err := m.loadManifest() manifest, err := s.loadManifest()
if err != nil { if err != nil {
return "", err return "", err
} }
@ -179,33 +198,32 @@ func (m Storage) getModuleRoot(dir string) (string, error) {
} }
// record only the Root directory for the module stored at dir. // record only the Root directory for the module stored at dir.
// TODO: remove this compatibility function to store the full moduleRecord. func (s Storage) recordModuleRoot(dir, root string) error {
func (m Storage) recordModuleRoot(dir, root string) error {
rec := moduleRecord{ rec := moduleRecord{
Dir: dir, Dir: dir,
Root: root, Root: root,
} }
return m.recordModule(rec) return s.recordModule(rec)
} }
func (m Storage) getStorage(key string, src string) (string, bool, error) { func (s Storage) getStorage(key string, src string) (string, bool, error) {
storage := &getter.FolderStorage{ storage := &getter.FolderStorage{
StorageDir: m.StorageDir, StorageDir: s.StorageDir,
} }
if m.Ui != nil { if s.Ui != nil {
update := "" update := ""
if m.Mode == GetModeUpdate { if s.Mode == GetModeUpdate {
update = " (update)" update = " (update)"
} }
m.Ui.Output(fmt.Sprintf("Get: %s%s", src, update)) s.Ui.Output(fmt.Sprintf("Get: %s%s", src, update))
} }
// Get the module with the level specified if we were told to. // Get the module with the level specified if we were told to.
if m.Mode > GetModeNone { if s.Mode > GetModeNone {
log.Printf("[DEBUG] fetching %q with key %q", src, key) log.Printf("[DEBUG] fetching %q with key %q", src, key)
if err := storage.Get(key, src, m.Mode == GetModeUpdate); err != nil { if err := storage.Get(key, src, s.Mode == GetModeUpdate); err != nil {
return "", false, err return "", false, err
} }
} }
@ -217,16 +235,16 @@ func (m Storage) getStorage(key string, src string) (string, bool, error) {
} }
// find a stored module that's not from a registry // find a stored module that's not from a registry
func (m Storage) findModule(key string) (string, error) { func (s Storage) findModule(key string) (string, error) {
if m.Mode == GetModeUpdate { if s.Mode == GetModeUpdate {
return "", nil return "", nil
} }
return m.moduleDir(key) return s.moduleDir(key)
} }
// find a registry module // find a registry module
func (m Storage) findRegistryModule(mSource, constraint string) (moduleRecord, error) { func (s Storage) findRegistryModule(mSource, constraint string) (moduleRecord, error) {
rec := moduleRecord{ rec := moduleRecord{
Source: mSource, Source: mSource,
} }
@ -244,7 +262,7 @@ func (m Storage) findRegistryModule(mSource, constraint string) (moduleRecord, e
log.Printf("[TRACE] %q is a registry module", mod.Module()) log.Printf("[TRACE] %q is a registry module", mod.Module())
versions, err := m.moduleVersions(mod.String()) versions, err := s.moduleVersions(mod.String())
if err != nil { if err != nil {
log.Printf("[ERROR] error looking up versions for %q: %s", mod.Module(), err) log.Printf("[ERROR] error looking up versions for %q: %s", mod.Module(), err)
return rec, err return rec, err
@ -252,7 +270,6 @@ func (m Storage) findRegistryModule(mSource, constraint string) (moduleRecord, e
match, err := newestRecord(versions, constraint) match, err := newestRecord(versions, constraint)
if err != nil { if err != nil {
// TODO: does this allow previously unversioned modules?
log.Printf("[INFO] no matching version for %q<%s>, %s", mod.Module(), constraint, err) log.Printf("[INFO] no matching version for %q<%s>, %s", mod.Module(), constraint, err)
} }
@ -262,8 +279,8 @@ func (m Storage) findRegistryModule(mSource, constraint string) (moduleRecord, e
// we need to lookup available versions // we need to lookup available versions
// Only on Get if it's not found, on unconditionally on Update // Only on Get if it's not found, on unconditionally on Update
if (m.Mode == GetModeGet && !found) || (m.Mode == GetModeUpdate) { if (s.Mode == GetModeGet && !found) || (s.Mode == GetModeUpdate) {
resp, err := lookupModuleVersions(nil, mod) resp, err := s.lookupModuleVersions(mod)
if err != nil { if err != nil {
return rec, err return rec, err
} }
@ -283,7 +300,7 @@ func (m Storage) findRegistryModule(mSource, constraint string) (moduleRecord, e
rec.Version = match.Version rec.Version = match.Version
rec.url, err = lookupModuleLocation(nil, mod, rec.Version) rec.url, err = s.lookupModuleLocation(mod, rec.Version)
if err != nil { if err != nil {
return rec, err return rec, err
} }

View File

@ -8,7 +8,7 @@ import (
) )
func TestTreeEncodeDecodeGob(t *testing.T) { func TestTreeEncodeDecodeGob(t *testing.T) {
storage := testStorage(t) storage := testStorage(t, nil)
tree := NewTree("", testConfig(t, "basic")) tree := NewTree("", testConfig(t, "basic"))
// This should get things // This should get things

View File

@ -20,7 +20,7 @@ func TestTreeChild(t *testing.T) {
t.Fatal("child should be nil") t.Fatal("child should be nil")
} }
storage := testStorage(t) storage := testStorage(t, nil)
storage.Mode = GetModeGet storage.Mode = GetModeGet
tree := NewTree("", testConfig(t, "child")) tree := NewTree("", testConfig(t, "child"))
if err := tree.Load(storage); err != nil { if err := tree.Load(storage); err != nil {
@ -65,7 +65,7 @@ func TestTreeChild(t *testing.T) {
} }
func TestTreeLoad(t *testing.T) { func TestTreeLoad(t *testing.T) {
storage := testStorage(t) storage := testStorage(t, nil)
tree := NewTree("", testConfig(t, "basic")) tree := NewTree("", testConfig(t, "basic"))
if tree.Loaded() { if tree.Loaded() {
@ -105,7 +105,7 @@ func TestTreeLoad(t *testing.T) {
} }
func TestTreeLoad_duplicate(t *testing.T) { func TestTreeLoad_duplicate(t *testing.T) {
storage := testStorage(t) storage := testStorage(t, nil)
tree := NewTree("", testConfig(t, "dup")) tree := NewTree("", testConfig(t, "dup"))
if tree.Loaded() { if tree.Loaded() {
@ -184,7 +184,7 @@ func TestTreeLoad_copyable(t *testing.T) {
} }
func TestTreeLoad_parentRef(t *testing.T) { func TestTreeLoad_parentRef(t *testing.T) {
storage := testStorage(t) storage := testStorage(t, nil)
tree := NewTree("", testConfig(t, "basic-parent")) tree := NewTree("", testConfig(t, "basic-parent"))
if tree.Loaded() { if tree.Loaded() {
@ -228,17 +228,12 @@ func TestTreeLoad_subdir(t *testing.T) {
fixtures := []string{ fixtures := []string{
"basic-subdir", "basic-subdir",
"basic-tar-subdir", "basic-tar-subdir",
// Passing a subpath to go getter extracts only this subpath. The old
// internal code would keep the entire directory structure, allowing a
// top-level module to reference others through its parent directory.
// TODO: this can be removed as a breaking change in a major release.
"tar-subdir-to-parent", "tar-subdir-to-parent",
} }
for _, tc := range fixtures { for _, tc := range fixtures {
t.Run(tc, func(t *testing.T) { t.Run(tc, func(t *testing.T) {
storage := testStorage(t) storage := testStorage(t, nil)
tree := NewTree("", testConfig(t, tc)) tree := NewTree("", testConfig(t, tc))
if tree.Loaded() { if tree.Loaded() {
@ -404,7 +399,7 @@ func TestTreeValidate_table(t *testing.T) {
for i, tc := range cases { for i, tc := range cases {
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
tree := NewTree("", testConfig(t, tc.Fixture)) tree := NewTree("", testConfig(t, tc.Fixture))
storage := testStorage(t) storage := testStorage(t, nil)
storage.Mode = GetModeGet storage.Mode = GetModeGet
if err := tree.Load(storage); err != nil { if err := tree.Load(storage); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -427,7 +422,7 @@ func TestTreeValidate_table(t *testing.T) {
func TestTreeValidate_badChild(t *testing.T) { func TestTreeValidate_badChild(t *testing.T) {
tree := NewTree("", testConfig(t, "validate-child-bad")) tree := NewTree("", testConfig(t, "validate-child-bad"))
storage := testStorage(t) storage := testStorage(t, nil)
storage.Mode = GetModeGet storage.Mode = GetModeGet
if err := tree.Load(storage); err != nil { if err := tree.Load(storage); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -441,7 +436,7 @@ func TestTreeValidate_badChild(t *testing.T) {
func TestTreeValidate_badChildOutput(t *testing.T) { func TestTreeValidate_badChildOutput(t *testing.T) {
tree := NewTree("", testConfig(t, "validate-bad-output")) tree := NewTree("", testConfig(t, "validate-bad-output"))
storage := testStorage(t) storage := testStorage(t, nil)
storage.Mode = GetModeGet storage.Mode = GetModeGet
if err := tree.Load(storage); err != nil { if err := tree.Load(storage); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -455,7 +450,7 @@ func TestTreeValidate_badChildOutput(t *testing.T) {
func TestTreeValidate_badChildOutputToModule(t *testing.T) { func TestTreeValidate_badChildOutputToModule(t *testing.T) {
tree := NewTree("", testConfig(t, "validate-bad-output-to-module")) tree := NewTree("", testConfig(t, "validate-bad-output-to-module"))
storage := testStorage(t) storage := testStorage(t, nil)
storage.Mode = GetModeGet storage.Mode = GetModeGet
if err := tree.Load(storage); err != nil { if err := tree.Load(storage); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -469,7 +464,7 @@ func TestTreeValidate_badChildOutputToModule(t *testing.T) {
func TestTreeValidate_badChildVar(t *testing.T) { func TestTreeValidate_badChildVar(t *testing.T) {
tree := NewTree("", testConfig(t, "validate-bad-var")) tree := NewTree("", testConfig(t, "validate-bad-var"))
storage := testStorage(t) storage := testStorage(t, nil)
storage.Mode = GetModeGet storage.Mode = GetModeGet
if err := tree.Load(storage); err != nil { if err := tree.Load(storage); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -483,7 +478,7 @@ func TestTreeValidate_badChildVar(t *testing.T) {
func TestTreeValidate_badRoot(t *testing.T) { func TestTreeValidate_badRoot(t *testing.T) {
tree := NewTree("", testConfig(t, "validate-root-bad")) tree := NewTree("", testConfig(t, "validate-root-bad"))
storage := testStorage(t) storage := testStorage(t, nil)
storage.Mode = GetModeGet storage.Mode = GetModeGet
if err := tree.Load(storage); err != nil { if err := tree.Load(storage); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -497,7 +492,7 @@ func TestTreeValidate_badRoot(t *testing.T) {
func TestTreeValidate_good(t *testing.T) { func TestTreeValidate_good(t *testing.T) {
tree := NewTree("", testConfig(t, "validate-child-good")) tree := NewTree("", testConfig(t, "validate-child-good"))
storage := testStorage(t) storage := testStorage(t, nil)
storage.Mode = GetModeGet storage.Mode = GetModeGet
if err := tree.Load(storage); err != nil { if err := tree.Load(storage); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -519,7 +514,7 @@ func TestTreeValidate_notLoaded(t *testing.T) {
func TestTreeValidate_requiredChildVar(t *testing.T) { func TestTreeValidate_requiredChildVar(t *testing.T) {
tree := NewTree("", testConfig(t, "validate-required-var")) tree := NewTree("", testConfig(t, "validate-required-var"))
storage := testStorage(t) storage := testStorage(t, nil)
storage.Mode = GetModeGet storage.Mode = GetModeGet
if err := tree.Load(storage); err != nil { if err := tree.Load(storage); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -542,7 +537,7 @@ func TestTreeValidate_requiredChildVar(t *testing.T) {
func TestTreeValidate_unknownModule(t *testing.T) { func TestTreeValidate_unknownModule(t *testing.T) {
tree := NewTree("", testConfig(t, "validate-module-unknown")) tree := NewTree("", testConfig(t, "validate-module-unknown"))
storage := testStorage(t) storage := testStorage(t, nil)
storage.Mode = GetModeNone storage.Mode = GetModeNone
if err := tree.Load(storage); err != nil { if err := tree.Load(storage); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -554,7 +549,7 @@ func TestTreeValidate_unknownModule(t *testing.T) {
} }
func TestTreeProviders_basic(t *testing.T) { func TestTreeProviders_basic(t *testing.T) {
storage := testStorage(t) storage := testStorage(t, nil)
tree := NewTree("", testConfig(t, "basic-parent-providers")) tree := NewTree("", testConfig(t, "basic-parent-providers"))
storage.Mode = GetModeGet storage.Mode = GetModeGet
@ -617,7 +612,7 @@ func TestTreeProviders_basic(t *testing.T) {
} }
func TestTreeProviders_implicit(t *testing.T) { func TestTreeProviders_implicit(t *testing.T) {
storage := testStorage(t) storage := testStorage(t, nil)
tree := NewTree("", testConfig(t, "implicit-parent-providers")) tree := NewTree("", testConfig(t, "implicit-parent-providers"))
storage.Mode = GetModeGet storage.Mode = GetModeGet
@ -658,7 +653,7 @@ func TestTreeProviders_implicit(t *testing.T) {
} }
func TestTreeProviders_implicitMultiLevel(t *testing.T) { func TestTreeProviders_implicitMultiLevel(t *testing.T) {
storage := testStorage(t) storage := testStorage(t, nil)
tree := NewTree("", testConfig(t, "implicit-grandparent-providers")) tree := NewTree("", testConfig(t, "implicit-grandparent-providers"))
storage.Mode = GetModeGet storage.Mode = GetModeGet
@ -728,7 +723,7 @@ func TestTreeProviders_implicitMultiLevel(t *testing.T) {
} }
func TestTreeLoad_conflictingSubmoduleNames(t *testing.T) { func TestTreeLoad_conflictingSubmoduleNames(t *testing.T) {
storage := testStorage(t) storage := testStorage(t, nil)
tree := NewTree("", testConfig(t, "conficting-submodule-names")) tree := NewTree("", testConfig(t, "conficting-submodule-names"))
storage.Mode = GetModeGet storage.Mode = GetModeGet