github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/config/import_tree.go (about)

     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  }