mirror of
https://github.com/letic/terraform-provider-google.git
synced 2024-09-19 09:39:59 +00:00
131 lines
3.9 KiB
Go
131 lines
3.9 KiB
Go
package tfconfig
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/hcl2/hcl"
|
|
)
|
|
|
|
// LoadModule reads the directory at the given path and attempts to interpret
|
|
// it as a Terraform module.
|
|
func LoadModule(dir string) (*Module, Diagnostics) {
|
|
|
|
// For broad compatibility here we actually have two separate loader
|
|
// codepaths. The main one uses the new HCL parser and API and is intended
|
|
// for configurations from Terraform 0.12 onwards (though will work for
|
|
// many older configurations too), but we'll also fall back on one that
|
|
// uses the _old_ HCL implementation so we can deal with some edge-cases
|
|
// that are not valid in new HCL.
|
|
|
|
module, diags := loadModule(dir)
|
|
if diags.HasErrors() {
|
|
// Try using the legacy HCL parser and see if we fare better.
|
|
legacyModule, legacyDiags := loadModuleLegacyHCL(dir)
|
|
if !legacyDiags.HasErrors() {
|
|
legacyModule.init(legacyDiags)
|
|
return legacyModule, legacyDiags
|
|
}
|
|
}
|
|
|
|
module.init(diags)
|
|
return module, diags
|
|
}
|
|
|
|
// IsModuleDir checks if the given path contains terraform configuration files.
|
|
// This allows the caller to decide how to handle directories that do not have tf files.
|
|
func IsModuleDir(dir string) bool {
|
|
primaryPaths, _ := dirFiles(dir)
|
|
if len(primaryPaths) == 0 {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (m *Module) init(diags Diagnostics) {
|
|
// Fill in any additional provider requirements that are implied by
|
|
// resource configurations, to avoid the caller from needing to apply
|
|
// this logic itself. Implied requirements don't have version constraints,
|
|
// but we'll make sure the requirement value is still non-nil in this
|
|
// case so callers can easily recognize it.
|
|
for _, r := range m.ManagedResources {
|
|
if _, exists := m.RequiredProviders[r.Provider.Name]; !exists {
|
|
m.RequiredProviders[r.Provider.Name] = []string{}
|
|
}
|
|
}
|
|
for _, r := range m.DataResources {
|
|
if _, exists := m.RequiredProviders[r.Provider.Name]; !exists {
|
|
m.RequiredProviders[r.Provider.Name] = []string{}
|
|
}
|
|
}
|
|
|
|
// We redundantly also reference the diagnostics from inside the module
|
|
// object, primarily so that we can easily included in JSON-serialized
|
|
// versions of the module object.
|
|
m.Diagnostics = diags
|
|
}
|
|
|
|
func dirFiles(dir string) (primary []string, diags hcl.Diagnostics) {
|
|
infos, err := ioutil.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
|
|
}
|
|
|
|
var override []string
|
|
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)
|
|
}
|
|
}
|
|
|
|
// We are assuming that any _override files will be logically named,
|
|
// and processing the files in alphabetical order. Primaries first, then overrides.
|
|
primary = append(primary, override...)
|
|
|
|
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
|
|
}
|