9 "github.com/hashicorp/errwrap"
12 // configurable is an interface that must be implemented by any configuration
13 // formats of Terraform in order to return a *Config.
14 type configurable interface {
15 Config() (*Config, error)
18 // importTree is the result of the first-pass load of the configuration
19 // files. It is a tree of raw configurables and then any children (their
22 // An importTree can be turned into a configTree.
23 type importTree struct {
26 Children []*importTree
29 // This is the function type that must be implemented by the configuration
30 // file loader to turn a single file into a configurable and any additional
32 type fileLoaderFunc func(path string) (configurable, []string, error)
34 // Set this to a non-empty value at link time to enable the HCL2 experiment.
35 // This is not currently enabled for release builds.
38 // go install -ldflags="-X github.com/hashicorp/terraform/config.enableHCL2Experiment=true" github.com/hashicorp/terraform
39 var enableHCL2Experiment = ""
41 // loadTree takes a single file and loads the entire importTree for that
42 // file. This function detects what kind of configuration file it is an
43 // executes the proper fileLoaderFunc.
44 func loadTree(root string) (*importTree, error) {
47 // HCL2 experiment is currently activated at build time via the linker.
48 // See the comment on this variable for more information.
49 if enableHCL2Experiment == "" {
50 // Main-line behavior: always use the original HCL parser
52 case ".tf", ".tf.json":
57 // Experimental behavior: use the HCL2 parser if the opt-in comment
61 // We need to sniff the file for the opt-in comment line to decide
62 // if the file is participating in the HCL2 experiment.
63 cf, err := os.Open(root)
67 sc := bufio.NewScanner(cf)
69 if sc.Text() == "#terraform:hcl2" {
70 f = globalHCL2Loader.loadFile
83 return nil, fmt.Errorf(
84 "%s: unknown configuration format. Use '.tf' or '.tf.json' extension",
88 c, imps, err := f(root)
93 children := make([]*importTree, len(imps))
94 for i, imp := range imps {
95 t, err := loadTree(imp)
110 // Close releases any resources we might be holding open for the importTree.
112 // This can safely be called even while ConfigTree results are alive. The
113 // importTree is not bound to these.
114 func (t *importTree) Close() error {
115 if c, ok := t.Raw.(io.Closer); ok {
118 for _, ct := range t.Children {
125 // ConfigTree traverses the importTree and turns each node into a *Config
126 // object, ultimately returning a *configTree.
127 func (t *importTree) ConfigTree() (*configTree, error) {
128 config, err := t.Raw.Config()
130 return nil, errwrap.Wrapf(fmt.Sprintf("Error loading %s: {{err}}", t.Path), err)
134 result := &configTree{
139 // Build the config trees for the children
140 result.Children = make([]*configTree, len(t.Children))
141 for i, ct := range t.Children {
142 t, err := ct.ConfigTree()
147 result.Children[i] = t