github.com/haklop/terraform@v0.3.6/config/loader.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"sort"
     9  	"strings"
    10  )
    11  
    12  // Load loads the Terraform configuration from a given file.
    13  //
    14  // This file can be any format that Terraform recognizes, and import any
    15  // other format that Terraform recognizes.
    16  func Load(path string) (*Config, error) {
    17  	importTree, err := loadTree(path)
    18  	if err != nil {
    19  		return nil, err
    20  	}
    21  
    22  	configTree, err := importTree.ConfigTree()
    23  
    24  	// Close the importTree now so that we can clear resources as quickly
    25  	// as possible.
    26  	importTree.Close()
    27  
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	return configTree.Flatten()
    33  }
    34  
    35  // LoadDir loads all the Terraform configuration files in a single
    36  // directory and appends them together.
    37  //
    38  // Special files known as "override files" can also be present, which
    39  // are merged into the loaded configuration. That is, the non-override
    40  // files are loaded first to create the configuration. Then, the overrides
    41  // are merged into the configuration to create the final configuration.
    42  //
    43  // Files are loaded in lexical order.
    44  func LoadDir(root string) (*Config, error) {
    45  	files, overrides, err := dirFiles(root)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	if len(files) == 0 {
    50  		return nil, fmt.Errorf(
    51  			"No Terraform configuration files found in directory: %s",
    52  			root)
    53  	}
    54  
    55  	// Determine the absolute path to the directory.
    56  	rootAbs, err := filepath.Abs(root)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	var result *Config
    62  
    63  	// Sort the files and overrides so we have a deterministic order
    64  	sort.Strings(files)
    65  	sort.Strings(overrides)
    66  
    67  	// Load all the regular files, append them to each other.
    68  	for _, f := range files {
    69  		c, err := Load(f)
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  
    74  		if result != nil {
    75  			result, err = Append(result, c)
    76  			if err != nil {
    77  				return nil, err
    78  			}
    79  		} else {
    80  			result = c
    81  		}
    82  	}
    83  
    84  	// Load all the overrides, and merge them into the config
    85  	for _, f := range overrides {
    86  		c, err := Load(f)
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  
    91  		result, err = Merge(result, c)
    92  		if err != nil {
    93  			return nil, err
    94  		}
    95  	}
    96  
    97  	// Mark the directory
    98  	result.Dir = rootAbs
    99  
   100  	return result, nil
   101  }
   102  
   103  // IsEmptyDir returns true if the directory given has no Terraform
   104  // configuration files.
   105  func IsEmptyDir(root string) (bool, error) {
   106  	if _, err := os.Stat(root); err != nil && os.IsNotExist(err) {
   107  		return true, nil
   108  	}
   109  
   110  	fs, os, err := dirFiles(root)
   111  	if err != nil {
   112  		return false, err
   113  	}
   114  
   115  	return len(fs) == 0 && len(os) == 0, nil
   116  }
   117  
   118  // Ext returns the Terraform configuration extension of the given
   119  // path, or a blank string if it is an invalid function.
   120  func ext(path string) string {
   121  	if strings.HasSuffix(path, ".tf") {
   122  		return ".tf"
   123  	} else if strings.HasSuffix(path, ".tf.json") {
   124  		return ".tf.json"
   125  	} else {
   126  		return ""
   127  	}
   128  }
   129  
   130  func dirFiles(dir string) ([]string, []string, error) {
   131  	f, err := os.Open(dir)
   132  	if err != nil {
   133  		return nil, nil, err
   134  	}
   135  	defer f.Close()
   136  
   137  	fi, err := f.Stat()
   138  	if err != nil {
   139  		return nil, nil, err
   140  	}
   141  	if !fi.IsDir() {
   142  		return nil, nil, fmt.Errorf(
   143  			"configuration path must be a directory: %s",
   144  			dir)
   145  	}
   146  
   147  	var files, overrides []string
   148  	err = nil
   149  	for err != io.EOF {
   150  		var fis []os.FileInfo
   151  		fis, err = f.Readdir(128)
   152  		if err != nil && err != io.EOF {
   153  			return nil, nil, err
   154  		}
   155  
   156  		for _, fi := range fis {
   157  			// Ignore directories
   158  			if fi.IsDir() {
   159  				continue
   160  			}
   161  
   162  			// Only care about files that are valid to load
   163  			name := fi.Name()
   164  			extValue := ext(name)
   165  			if extValue == "" || isTemporaryFile(name) {
   166  				continue
   167  			}
   168  
   169  			// Determine if we're dealing with an override
   170  			nameNoExt := name[:len(name)-len(extValue)]
   171  			override := nameNoExt == "override" ||
   172  				strings.HasSuffix(nameNoExt, "_override")
   173  
   174  			path := filepath.Join(dir, name)
   175  			if override {
   176  				overrides = append(overrides, path)
   177  			} else {
   178  				files = append(files, path)
   179  			}
   180  		}
   181  	}
   182  
   183  	return files, overrides, nil
   184  }
   185  
   186  // isTemporaryFile returns true or false depending on whether the
   187  // provided file name is a temporary file for the following editors:
   188  // emacs or vim.
   189  func isTemporaryFile(name string) bool {
   190  	return strings.HasSuffix(name, "~") || // vim
   191  		strings.HasPrefix(name, ".#") || // emacs
   192  		(strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#")) // emacs
   193  }