github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/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":
    37  		fallthrough
    38  	case ".tf.json":
    39  		f = loadFileHcl
    40  	default:
    41  	}
    42  
    43  	if f == nil {
    44  		return nil, fmt.Errorf(
    45  			"%s: unknown configuration format. Use '.tf' or '.tf.json' extension",
    46  			root)
    47  	}
    48  
    49  	c, imps, err := f(root)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	children := make([]*importTree, len(imps))
    55  	for i, imp := range imps {
    56  		t, err := loadTree(imp)
    57  		if err != nil {
    58  			return nil, err
    59  		}
    60  
    61  		children[i] = t
    62  	}
    63  
    64  	return &importTree{
    65  		Path:     root,
    66  		Raw:      c,
    67  		Children: children,
    68  	}, nil
    69  }
    70  
    71  // Close releases any resources we might be holding open for the importTree.
    72  //
    73  // This can safely be called even while ConfigTree results are alive. The
    74  // importTree is not bound to these.
    75  func (t *importTree) Close() error {
    76  	if c, ok := t.Raw.(io.Closer); ok {
    77  		c.Close()
    78  	}
    79  	for _, ct := range t.Children {
    80  		ct.Close()
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  // ConfigTree traverses the importTree and turns each node into a *Config
    87  // object, ultimately returning a *configTree.
    88  func (t *importTree) ConfigTree() (*configTree, error) {
    89  	config, err := t.Raw.Config()
    90  	if err != nil {
    91  		return nil, fmt.Errorf(
    92  			"Error loading %s: %s",
    93  			t.Path,
    94  			err)
    95  	}
    96  
    97  	// Build our result
    98  	result := &configTree{
    99  		Path:   t.Path,
   100  		Config: config,
   101  	}
   102  
   103  	// Build the config trees for the children
   104  	result.Children = make([]*configTree, len(t.Children))
   105  	for i, ct := range t.Children {
   106  		t, err := ct.ConfigTree()
   107  		if err != nil {
   108  			return nil, err
   109  		}
   110  
   111  		result.Children[i] = t
   112  	}
   113  
   114  	return result, nil
   115  }