]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package config |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "io" | |
6 | ) | |
7 | ||
8 | // configurable is an interface that must be implemented by any configuration | |
9 | // formats of Terraform in order to return a *Config. | |
10 | type configurable interface { | |
11 | Config() (*Config, error) | |
12 | } | |
13 | ||
14 | // importTree is the result of the first-pass load of the configuration | |
15 | // files. It is a tree of raw configurables and then any children (their | |
16 | // imports). | |
17 | // | |
18 | // An importTree can be turned into a configTree. | |
19 | type importTree struct { | |
20 | Path string | |
21 | Raw configurable | |
22 | Children []*importTree | |
23 | } | |
24 | ||
25 | // This is the function type that must be implemented by the configuration | |
26 | // file loader to turn a single file into a configurable and any additional | |
27 | // imports. | |
28 | type fileLoaderFunc func(path string) (configurable, []string, error) | |
29 | ||
30 | // loadTree takes a single file and loads the entire importTree for that | |
31 | // file. This function detects what kind of configuration file it is an | |
32 | // executes the proper fileLoaderFunc. | |
33 | func loadTree(root string) (*importTree, error) { | |
34 | var f fileLoaderFunc | |
35 | switch ext(root) { | |
36 | case ".tf", ".tf.json": | |
37 | f = loadFileHcl | |
38 | default: | |
39 | } | |
40 | ||
41 | if f == nil { | |
42 | return nil, fmt.Errorf( | |
43 | "%s: unknown configuration format. Use '.tf' or '.tf.json' extension", | |
44 | root) | |
45 | } | |
46 | ||
47 | c, imps, err := f(root) | |
48 | if err != nil { | |
49 | return nil, err | |
50 | } | |
51 | ||
52 | children := make([]*importTree, len(imps)) | |
53 | for i, imp := range imps { | |
54 | t, err := loadTree(imp) | |
55 | if err != nil { | |
56 | return nil, err | |
57 | } | |
58 | ||
59 | children[i] = t | |
60 | } | |
61 | ||
62 | return &importTree{ | |
63 | Path: root, | |
64 | Raw: c, | |
65 | Children: children, | |
66 | }, nil | |
67 | } | |
68 | ||
69 | // Close releases any resources we might be holding open for the importTree. | |
70 | // | |
71 | // This can safely be called even while ConfigTree results are alive. The | |
72 | // importTree is not bound to these. | |
73 | func (t *importTree) Close() error { | |
74 | if c, ok := t.Raw.(io.Closer); ok { | |
75 | c.Close() | |
76 | } | |
77 | for _, ct := range t.Children { | |
78 | ct.Close() | |
79 | } | |
80 | ||
81 | return nil | |
82 | } | |
83 | ||
84 | // ConfigTree traverses the importTree and turns each node into a *Config | |
85 | // object, ultimately returning a *configTree. | |
86 | func (t *importTree) ConfigTree() (*configTree, error) { | |
87 | config, err := t.Raw.Config() | |
88 | if err != nil { | |
89 | return nil, fmt.Errorf( | |
90 | "Error loading %s: %s", | |
91 | t.Path, | |
92 | err) | |
93 | } | |
94 | ||
95 | // Build our result | |
96 | result := &configTree{ | |
97 | Path: t.Path, | |
98 | Config: config, | |
99 | } | |
100 | ||
101 | // Build the config trees for the children | |
102 | result.Children = make([]*configTree, len(t.Children)) | |
103 | for i, ct := range t.Children { | |
104 | t, err := ct.ConfigTree() | |
105 | if err != nil { | |
106 | return nil, err | |
107 | } | |
108 | ||
109 | result.Children[i] = t | |
110 | } | |
111 | ||
112 | return result, nil | |
113 | } |