Lokasi ngalangkungan proxy:   [ UP ]  
[Ngawartoskeun bug]   [Panyetelan cookie]                
Skip to content

Commit a35a926

Browse files
committed
config/module: detectors, some more work on Tree
1 parent 799ffbb commit a35a926

7 files changed

Lines changed: 196 additions & 7 deletions

File tree

config/module/detect.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package module
2+
3+
import (
4+
"fmt"
5+
"net/url"
6+
)
7+
8+
// Detector defines the interface that an invalid URL or a URL with a blank
9+
// scheme is passed through in order to determine if its shorthand for
10+
// something else well-known.
11+
type Detector interface {
12+
// Detect will detect whether the string matches a known pattern to
13+
// turn it into a proper URL.
14+
Detect(string, string) (string, bool, error)
15+
}
16+
17+
// Detectors is the list of detectors that are tried on an invalid URL.
18+
// This is also the order they're tried (index 0 is first).
19+
var Detectors []Detector
20+
21+
func init() {
22+
Detectors = []Detector{
23+
new(FileDetector),
24+
}
25+
}
26+
27+
// Detect turns a source string into another source string if it is
28+
// detected to be of a known pattern.
29+
//
30+
// This is safe to be called with an already valid source string: Detect
31+
// will just return it.
32+
func Detect(src string, pwd string) (string, error) {
33+
u, err := url.Parse(src)
34+
if err == nil && u.Scheme != "" {
35+
// Valid URL
36+
return src, nil
37+
}
38+
39+
for _, d := range Detectors {
40+
result, ok, err := d.Detect(src, pwd)
41+
if err != nil {
42+
return "", err
43+
}
44+
if ok {
45+
return result, nil
46+
}
47+
}
48+
49+
return "", fmt.Errorf("invalid source string: %s", src)
50+
}

config/module/detect_file.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package module
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
)
7+
8+
// FileDetector implements Detector to detect file paths.
9+
type FileDetector struct{}
10+
11+
func (d *FileDetector) Detect(src, pwd string) (string, bool, error) {
12+
if len(src) == 0 {
13+
return "", false, nil
14+
}
15+
16+
// Make sure we're using "/" even on Windows. URLs are "/"-based.
17+
src = filepath.ToSlash(src)
18+
if !filepath.IsAbs(src) {
19+
src = filepath.Join(pwd, src)
20+
}
21+
22+
// Make sure that we don't start with "/" since we add that below
23+
if src[0] == '/' {
24+
src = src[1:]
25+
}
26+
27+
return fmt.Sprintf("file:///%s", src), true, nil
28+
}

config/module/detect_file_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package module
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestFileDetector(t *testing.T) {
8+
cases := []struct {
9+
Input string
10+
Output string
11+
}{
12+
{"./foo", "file:///pwd/foo"},
13+
{"foo", "file:///pwd/foo"},
14+
{"/foo", "file:///foo"},
15+
}
16+
17+
pwd := "/pwd"
18+
f := new(FileDetector)
19+
for i, tc := range cases {
20+
output, ok, err := f.Detect(tc.Input, pwd)
21+
if err != nil {
22+
t.Fatalf("err: %s", err)
23+
}
24+
if !ok {
25+
t.Fatal("not ok")
26+
}
27+
28+
if output != tc.Output {
29+
t.Fatalf("%d: bad: %#v", i, output)
30+
}
31+
}
32+
}

config/module/module.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ package module
44
type Module struct {
55
Name string
66
Source string
7+
Dir string
78
}

config/module/module_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,7 @@ func testModule(n string) string {
4545
url.Path = p
4646
return url.String()
4747
}
48+
49+
func testStorage(t *testing.T) Storage {
50+
return &FolderStorage{StorageDir: tempDir(t)}
51+
}

config/module/tree.go

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package module
22

33
import (
4+
"fmt"
5+
"sync"
6+
47
"github.com/hashicorp/terraform/config"
58
)
69

@@ -10,8 +13,9 @@ import (
1013
// all the modules without getting, flatten the tree into something
1114
// Terraform can use, etc.
1215
type Tree struct {
13-
Config *config.Config
14-
Children []*Tree
16+
config *config.Config
17+
children []*Tree
18+
lock sync.Mutex
1519
}
1620

1721
// GetMode is an enum that describes how modules are loaded.
@@ -35,7 +39,15 @@ const (
3539

3640
// NewTree returns a new Tree for the given config structure.
3741
func NewTree(c *config.Config) *Tree {
38-
return &Tree{Config: c}
42+
return &Tree{config: c}
43+
}
44+
45+
// Children returns the children of this tree (the modules that are
46+
// imported by this root).
47+
//
48+
// This will only return a non-nil value after Load is called.
49+
func (t *Tree) Children() []*Tree {
50+
return nil
3951
}
4052

4153
// Flatten takes the entire module tree and flattens it into a single
@@ -54,10 +66,10 @@ func (t *Tree) Flatten() (*config.Config, error) {
5466
// This is only the imports of _this_ level of the tree. To retrieve the
5567
// full nested imports, you'll have to traverse the tree.
5668
func (t *Tree) Modules() []*Module {
57-
result := make([]*Module, len(t.Config.Modules))
58-
for i, m := range t.Config.Modules {
69+
result := make([]*Module, len(t.config.Modules))
70+
for i, m := range t.config.Modules {
5971
result[i] = &Module{
60-
Name: m.Name,
72+
Name: m.Name,
6173
Source: m.Source,
6274
}
6375
}
@@ -70,11 +82,66 @@ func (t *Tree) Modules() []*Module {
7082
// The parameters are used to tell the tree where to find modules and
7183
// whether it can download/update modules along the way.
7284
//
85+
// Calling this multiple times will reload the tree.
86+
//
7387
// Various semantic-like checks are made along the way of loading since
7488
// module trees inherently require the configuration to be in a reasonably
7589
// sane state: no circular dependencies, proper module sources, etc. A full
7690
// suite of validations can be done by running Validate (after loading).
7791
func (t *Tree) Load(s Storage, mode GetMode) error {
92+
t.lock.Lock()
93+
defer t.lock.Unlock()
94+
95+
// Reset the children if we have any
96+
t.children = nil
97+
98+
modules := t.Modules()
99+
children := make([]*Tree, len(modules))
100+
101+
// Go through all the modules and get the directory for them.
102+
update := mode == GetModeUpdate
103+
for i, m := range modules {
104+
source, err := Detect(m.Source, m.Dir)
105+
if err != nil {
106+
return fmt.Errorf("module %s: %s", m.Name, err)
107+
}
108+
109+
if mode > GetModeNone {
110+
// Get the module since we specified we should
111+
if err := s.Get(source, update); err != nil {
112+
return err
113+
}
114+
}
115+
116+
// Get the directory where this module is so we can load it
117+
dir, ok, err := s.Dir(source)
118+
if err != nil {
119+
return err
120+
}
121+
if !ok {
122+
return fmt.Errorf(
123+
"module %s: not found, may need to be downloaded", m.Name)
124+
}
125+
126+
// Load the configuration
127+
c, err := config.LoadDir(dir)
128+
if err != nil {
129+
return fmt.Errorf(
130+
"module %s: %s", m.Name, err)
131+
}
132+
children[i] = NewTree(c)
133+
}
134+
135+
// Go through all the children and load them.
136+
for _, c := range children {
137+
if err := c.Load(s, mode); err != nil {
138+
return err
139+
}
140+
}
141+
142+
// Set our tree up
143+
t.children = children
144+
78145
return nil
79146
}
80147

config/module/tree_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import (
55
"testing"
66
)
77

8-
func TestTree(t *testing.T) {
8+
func TestTree_Load(t *testing.T) {
9+
tree := NewTree(testConfig(t, "basic"))
10+
if err := tree.Load(testStorage(t), GetModeGet); err != nil {
11+
t.Fatalf("err: %s", err)
12+
}
13+
}
14+
15+
func TestTree_Modules(t *testing.T) {
916
tree := NewTree(testConfig(t, "basic"))
1017
actual := tree.Modules()
1118

0 commit comments

Comments
 (0)