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