config/module: detectors, some more work on Tree
This commit is contained in:
parent
799ffbb3ac
commit
a35a9262d4
|
@ -0,0 +1,50 @@
|
|||
package module
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Detector defines the interface that an invalid URL or a URL with a blank
|
||||
// scheme is passed through in order to determine if its shorthand for
|
||||
// something else well-known.
|
||||
type Detector interface {
|
||||
// Detect will detect whether the string matches a known pattern to
|
||||
// turn it into a proper URL.
|
||||
Detect(string, string) (string, bool, error)
|
||||
}
|
||||
|
||||
// Detectors is the list of detectors that are tried on an invalid URL.
|
||||
// This is also the order they're tried (index 0 is first).
|
||||
var Detectors []Detector
|
||||
|
||||
func init() {
|
||||
Detectors = []Detector{
|
||||
new(FileDetector),
|
||||
}
|
||||
}
|
||||
|
||||
// Detect turns a source string into another source string if it is
|
||||
// detected to be of a known pattern.
|
||||
//
|
||||
// This is safe to be called with an already valid source string: Detect
|
||||
// will just return it.
|
||||
func Detect(src string, pwd string) (string, error) {
|
||||
u, err := url.Parse(src)
|
||||
if err == nil && u.Scheme != "" {
|
||||
// Valid URL
|
||||
return src, nil
|
||||
}
|
||||
|
||||
for _, d := range Detectors {
|
||||
result, ok, err := d.Detect(src, pwd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if ok {
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("invalid source string: %s", src)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package module
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FileDetector implements Detector to detect file paths.
|
||||
type FileDetector struct{}
|
||||
|
||||
func (d *FileDetector) Detect(src, pwd string) (string, bool, error) {
|
||||
if len(src) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
// Make sure we're using "/" even on Windows. URLs are "/"-based.
|
||||
src = filepath.ToSlash(src)
|
||||
if !filepath.IsAbs(src) {
|
||||
src = filepath.Join(pwd, src)
|
||||
}
|
||||
|
||||
// Make sure that we don't start with "/" since we add that below
|
||||
if src[0] == '/' {
|
||||
src = src[1:]
|
||||
}
|
||||
|
||||
return fmt.Sprintf("file:///%s", src), true, nil
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package module
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFileDetector(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Output string
|
||||
}{
|
||||
{"./foo", "file:///pwd/foo"},
|
||||
{"foo", "file:///pwd/foo"},
|
||||
{"/foo", "file:///foo"},
|
||||
}
|
||||
|
||||
pwd := "/pwd"
|
||||
f := new(FileDetector)
|
||||
for i, tc := range cases {
|
||||
output, ok, err := f.Detect(tc.Input, pwd)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatal("not ok")
|
||||
}
|
||||
|
||||
if output != tc.Output {
|
||||
t.Fatalf("%d: bad: %#v", i, output)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,4 +4,5 @@ package module
|
|||
type Module struct {
|
||||
Name string
|
||||
Source string
|
||||
Dir string
|
||||
}
|
||||
|
|
|
@ -45,3 +45,7 @@ func testModule(n string) string {
|
|||
url.Path = p
|
||||
return url.String()
|
||||
}
|
||||
|
||||
func testStorage(t *testing.T) Storage {
|
||||
return &FolderStorage{StorageDir: tempDir(t)}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package module
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
)
|
||||
|
||||
|
@ -10,8 +13,9 @@ import (
|
|||
// all the modules without getting, flatten the tree into something
|
||||
// Terraform can use, etc.
|
||||
type Tree struct {
|
||||
Config *config.Config
|
||||
Children []*Tree
|
||||
config *config.Config
|
||||
children []*Tree
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// GetMode is an enum that describes how modules are loaded.
|
||||
|
@ -35,7 +39,15 @@ const (
|
|||
|
||||
// NewTree returns a new Tree for the given config structure.
|
||||
func NewTree(c *config.Config) *Tree {
|
||||
return &Tree{Config: c}
|
||||
return &Tree{config: c}
|
||||
}
|
||||
|
||||
// Children returns the children of this tree (the modules that are
|
||||
// imported by this root).
|
||||
//
|
||||
// This will only return a non-nil value after Load is called.
|
||||
func (t *Tree) Children() []*Tree {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flatten takes the entire module tree and flattens it into a single
|
||||
|
@ -54,10 +66,10 @@ func (t *Tree) Flatten() (*config.Config, error) {
|
|||
// This is only the imports of _this_ level of the tree. To retrieve the
|
||||
// full nested imports, you'll have to traverse the tree.
|
||||
func (t *Tree) Modules() []*Module {
|
||||
result := make([]*Module, len(t.Config.Modules))
|
||||
for i, m := range t.Config.Modules {
|
||||
result := make([]*Module, len(t.config.Modules))
|
||||
for i, m := range t.config.Modules {
|
||||
result[i] = &Module{
|
||||
Name: m.Name,
|
||||
Name: m.Name,
|
||||
Source: m.Source,
|
||||
}
|
||||
}
|
||||
|
@ -70,11 +82,66 @@ func (t *Tree) Modules() []*Module {
|
|||
// The parameters are used to tell the tree where to find modules and
|
||||
// whether it can download/update modules along the way.
|
||||
//
|
||||
// Calling this multiple times will reload the tree.
|
||||
//
|
||||
// Various semantic-like checks are made along the way of loading since
|
||||
// module trees inherently require the configuration to be in a reasonably
|
||||
// sane state: no circular dependencies, proper module sources, etc. A full
|
||||
// suite of validations can be done by running Validate (after loading).
|
||||
func (t *Tree) Load(s Storage, mode GetMode) error {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
// Reset the children if we have any
|
||||
t.children = nil
|
||||
|
||||
modules := t.Modules()
|
||||
children := make([]*Tree, len(modules))
|
||||
|
||||
// Go through all the modules and get the directory for them.
|
||||
update := mode == GetModeUpdate
|
||||
for i, m := range modules {
|
||||
source, err := Detect(m.Source, m.Dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("module %s: %s", m.Name, err)
|
||||
}
|
||||
|
||||
if mode > GetModeNone {
|
||||
// Get the module since we specified we should
|
||||
if err := s.Get(source, update); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Get the directory where this module is so we can load it
|
||||
dir, ok, err := s.Dir(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf(
|
||||
"module %s: not found, may need to be downloaded", m.Name)
|
||||
}
|
||||
|
||||
// Load the configuration
|
||||
c, err := config.LoadDir(dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"module %s: %s", m.Name, err)
|
||||
}
|
||||
children[i] = NewTree(c)
|
||||
}
|
||||
|
||||
// Go through all the children and load them.
|
||||
for _, c := range children {
|
||||
if err := c.Load(s, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Set our tree up
|
||||
t.children = children
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,14 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestTree(t *testing.T) {
|
||||
func TestTree_Load(t *testing.T) {
|
||||
tree := NewTree(testConfig(t, "basic"))
|
||||
if err := tree.Load(testStorage(t), GetModeGet); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTree_Modules(t *testing.T) {
|
||||
tree := NewTree(testConfig(t, "basic"))
|
||||
actual := tree.Modules()
|
||||
|
||||
|
|
Loading…
Reference in New Issue