github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/configs/parser_config_dir.go (about)

     1  package configs
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/hcl2/hcl"
     9  )
    10  
    11  // LoadConfigDir reads the .tf and .tf.json files in the given directory
    12  // as config files (using LoadConfigFile) and then combines these files into
    13  // a single Module.
    14  //
    15  // If this method returns nil, that indicates that the given directory does not
    16  // exist at all or could not be opened for some reason. Callers may wish to
    17  // detect this case and ignore the returned diagnostics so that they can
    18  // produce a more context-aware error message in that case.
    19  //
    20  // If this method returns a non-nil module while error diagnostics are returned
    21  // then the module may be incomplete but can be used carefully for static
    22  // analysis.
    23  //
    24  // This file does not consider a directory with no files to be an error, and
    25  // will simply return an empty module in that case. Callers should first call
    26  // Parser.IsConfigDir if they wish to recognize that situation.
    27  //
    28  // .tf files are parsed using the HCL native syntax while .tf.json files are
    29  // parsed using the HCL JSON syntax.
    30  func (p *Parser) LoadConfigDir(path string) (*Module, hcl.Diagnostics) {
    31  	primaryPaths, overridePaths, diags := p.dirFiles(path)
    32  	if diags.HasErrors() {
    33  		return nil, diags
    34  	}
    35  
    36  	primary, fDiags := p.loadFiles(primaryPaths, false)
    37  	diags = append(diags, fDiags...)
    38  	override, fDiags := p.loadFiles(overridePaths, true)
    39  	diags = append(diags, fDiags...)
    40  
    41  	mod, modDiags := NewModule(primary, override)
    42  	diags = append(diags, modDiags...)
    43  
    44  	return mod, diags
    45  }
    46  
    47  // IsConfigDir determines whether the given path refers to a directory that
    48  // exists and contains at least one Terraform config file (with a .tf or
    49  // .tf.json extension.)
    50  func (p *Parser) IsConfigDir(path string) bool {
    51  	primaryPaths, overridePaths, _ := p.dirFiles(path)
    52  	return (len(primaryPaths) + len(overridePaths)) > 0
    53  }
    54  
    55  func (p *Parser) loadFiles(paths []string, override bool) ([]*File, hcl.Diagnostics) {
    56  	var files []*File
    57  	var diags hcl.Diagnostics
    58  
    59  	for _, path := range paths {
    60  		var f *File
    61  		var fDiags hcl.Diagnostics
    62  		if override {
    63  			f, fDiags = p.LoadConfigFileOverride(path)
    64  		} else {
    65  			f, fDiags = p.LoadConfigFile(path)
    66  		}
    67  		diags = append(diags, fDiags...)
    68  		if f != nil {
    69  			files = append(files, f)
    70  		}
    71  	}
    72  
    73  	return files, diags
    74  }
    75  
    76  func (p *Parser) dirFiles(dir string) (primary, override []string, diags hcl.Diagnostics) {
    77  	infos, err := p.fs.ReadDir(dir)
    78  	if err != nil {
    79  		diags = append(diags, &hcl.Diagnostic{
    80  			Severity: hcl.DiagError,
    81  			Summary:  "Failed to read module directory",
    82  			Detail:   fmt.Sprintf("Module directory %s does not exist or cannot be read.", dir),
    83  		})
    84  		return
    85  	}
    86  
    87  	for _, info := range infos {
    88  		if info.IsDir() {
    89  			// We only care about files
    90  			continue
    91  		}
    92  
    93  		name := info.Name()
    94  		ext := fileExt(name)
    95  		if ext == "" || IsIgnoredFile(name) {
    96  			continue
    97  		}
    98  
    99  		baseName := name[:len(name)-len(ext)] // strip extension
   100  		isOverride := baseName == "override" || strings.HasSuffix(baseName, "_override")
   101  
   102  		fullPath := filepath.Join(dir, name)
   103  		if isOverride {
   104  			override = append(override, fullPath)
   105  		} else {
   106  			primary = append(primary, fullPath)
   107  		}
   108  	}
   109  
   110  	return
   111  }
   112  
   113  // fileExt returns the Terraform configuration extension of the given
   114  // path, or a blank string if it is not a recognized extension.
   115  func fileExt(path string) string {
   116  	if strings.HasSuffix(path, ".tf") {
   117  		return ".tf"
   118  	} else if strings.HasSuffix(path, ".tf.json") {
   119  		return ".tf.json"
   120  	} else {
   121  		return ""
   122  	}
   123  }
   124  
   125  // IsIgnoredFile returns true if the given filename (which must not have a
   126  // directory path ahead of it) should be ignored as e.g. an editor swap file.
   127  func IsIgnoredFile(name string) bool {
   128  	return strings.HasPrefix(name, ".") || // Unix-like hidden files
   129  		strings.HasSuffix(name, "~") || // vim
   130  		strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#") // emacs
   131  }