aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/internal/earlyconfig
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/internal/earlyconfig')
-rw-r--r--vendor/github.com/hashicorp/terraform/internal/earlyconfig/config.go123
-rw-r--r--vendor/github.com/hashicorp/terraform/internal/earlyconfig/config_build.go144
-rw-r--r--vendor/github.com/hashicorp/terraform/internal/earlyconfig/diagnostics.go78
-rw-r--r--vendor/github.com/hashicorp/terraform/internal/earlyconfig/doc.go20
-rw-r--r--vendor/github.com/hashicorp/terraform/internal/earlyconfig/module.go13
5 files changed, 378 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config.go b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config.go
new file mode 100644
index 0000000..a9b8f98
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config.go
@@ -0,0 +1,123 @@
1package earlyconfig
2
3import (
4 "fmt"
5 "sort"
6
7 version "github.com/hashicorp/go-version"
8 "github.com/hashicorp/terraform-config-inspect/tfconfig"
9 "github.com/hashicorp/terraform/addrs"
10 "github.com/hashicorp/terraform/moduledeps"
11 "github.com/hashicorp/terraform/plugin/discovery"
12 "github.com/hashicorp/terraform/tfdiags"
13)
14
15// A Config is a node in the tree of modules within a configuration.
16//
17// The module tree is constructed by following ModuleCall instances recursively
18// through the root module transitively into descendent modules.
19type Config struct {
20 // RootModule points to the Config for the root module within the same
21 // module tree as this module. If this module _is_ the root module then
22 // this is self-referential.
23 Root *Config
24
25 // ParentModule points to the Config for the module that directly calls
26 // this module. If this is the root module then this field is nil.
27 Parent *Config
28
29 // Path is a sequence of module logical names that traverse from the root
30 // module to this config. Path is empty for the root module.
31 //
32 // This should only be used to display paths to the end-user in rare cases
33 // where we are talking about the static module tree, before module calls
34 // have been resolved. In most cases, an addrs.ModuleInstance describing
35 // a node in the dynamic module tree is better, since it will then include
36 // any keys resulting from evaluating "count" and "for_each" arguments.
37 Path addrs.Module
38
39 // ChildModules points to the Config for each of the direct child modules
40 // called from this module. The keys in this map match the keys in
41 // Module.ModuleCalls.
42 Children map[string]*Config
43
44 // Module points to the object describing the configuration for the
45 // various elements (variables, resources, etc) defined by this module.
46 Module *tfconfig.Module
47
48 // CallPos is the source position for the header of the module block that
49 // requested this module.
50 //
51 // This field is meaningless for the root module, where its contents are undefined.
52 CallPos tfconfig.SourcePos
53
54 // SourceAddr is the source address that the referenced module was requested
55 // from, as specified in configuration.
56 //
57 // This field is meaningless for the root module, where its contents are undefined.
58 SourceAddr string
59
60 // Version is the specific version that was selected for this module,
61 // based on version constraints given in configuration.
62 //
63 // This field is nil if the module was loaded from a non-registry source,
64 // since versions are not supported for other sources.
65 //
66 // This field is meaningless for the root module, where it will always
67 // be nil.
68 Version *version.Version
69}
70
71// ProviderDependencies returns the provider dependencies for the recieving
72// config, including all of its descendent modules.
73func (c *Config) ProviderDependencies() (*moduledeps.Module, tfdiags.Diagnostics) {
74 var diags tfdiags.Diagnostics
75
76 var name string
77 if len(c.Path) > 0 {
78 name = c.Path[len(c.Path)-1]
79 }
80
81 ret := &moduledeps.Module{
82 Name: name,
83 }
84
85 providers := make(moduledeps.Providers)
86 for name, reqs := range c.Module.RequiredProviders {
87 inst := moduledeps.ProviderInstance(name)
88 var constraints version.Constraints
89 for _, reqStr := range reqs {
90 if reqStr != "" {
91 constraint, err := version.NewConstraint(reqStr)
92 if err != nil {
93 diags = diags.Append(wrapDiagnostic(tfconfig.Diagnostic{
94 Severity: tfconfig.DiagError,
95 Summary: "Invalid provider version constraint",
96 Detail: fmt.Sprintf("Invalid version constraint %q for provider %s.", reqStr, name),
97 }))
98 continue
99 }
100 constraints = append(constraints, constraint...)
101 }
102 }
103 providers[inst] = moduledeps.ProviderDependency{
104 Constraints: discovery.NewConstraints(constraints),
105 Reason: moduledeps.ProviderDependencyExplicit,
106 }
107 }
108 ret.Providers = providers
109
110 childNames := make([]string, 0, len(c.Children))
111 for name := range c.Children {
112 childNames = append(childNames, name)
113 }
114 sort.Strings(childNames)
115
116 for _, name := range childNames {
117 child, childDiags := c.Children[name].ProviderDependencies()
118 ret.Children = append(ret.Children, child)
119 diags = diags.Append(childDiags)
120 }
121
122 return ret, diags
123}
diff --git a/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config_build.go b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config_build.go
new file mode 100644
index 0000000..770d5df
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/config_build.go
@@ -0,0 +1,144 @@
1package earlyconfig
2
3import (
4 "fmt"
5 "sort"
6 "strings"
7
8 version "github.com/hashicorp/go-version"
9 "github.com/hashicorp/terraform-config-inspect/tfconfig"
10 "github.com/hashicorp/terraform/addrs"
11 "github.com/hashicorp/terraform/tfdiags"
12)
13
14// BuildConfig constructs a Config from a root module by loading all of its
15// descendent modules via the given ModuleWalker.
16func BuildConfig(root *tfconfig.Module, walker ModuleWalker) (*Config, tfdiags.Diagnostics) {
17 var diags tfdiags.Diagnostics
18 cfg := &Config{
19 Module: root,
20 }
21 cfg.Root = cfg // Root module is self-referential.
22 cfg.Children, diags = buildChildModules(cfg, walker)
23 return cfg, diags
24}
25
26func buildChildModules(parent *Config, walker ModuleWalker) (map[string]*Config, tfdiags.Diagnostics) {
27 var diags tfdiags.Diagnostics
28 ret := map[string]*Config{}
29 calls := parent.Module.ModuleCalls
30
31 // We'll sort the calls by their local names so that they'll appear in a
32 // predictable order in any logging that's produced during the walk.
33 callNames := make([]string, 0, len(calls))
34 for k := range calls {
35 callNames = append(callNames, k)
36 }
37 sort.Strings(callNames)
38
39 for _, callName := range callNames {
40 call := calls[callName]
41 path := make([]string, len(parent.Path)+1)
42 copy(path, parent.Path)
43 path[len(path)-1] = call.Name
44
45 var vc version.Constraints
46 if strings.TrimSpace(call.Version) != "" {
47 var err error
48 vc, err = version.NewConstraint(call.Version)
49 if err != nil {
50 diags = diags.Append(wrapDiagnostic(tfconfig.Diagnostic{
51 Severity: tfconfig.DiagError,
52 Summary: "Invalid version constraint",
53 Detail: fmt.Sprintf("Module %q (declared at %s line %d) has invalid version constraint %q: %s.", callName, call.Pos.Filename, call.Pos.Line, call.Version, err),
54 }))
55 continue
56 }
57 }
58
59 req := ModuleRequest{
60 Name: call.Name,
61 Path: path,
62 SourceAddr: call.Source,
63 VersionConstraints: vc,
64 Parent: parent,
65 CallPos: call.Pos,
66 }
67
68 mod, ver, modDiags := walker.LoadModule(&req)
69 diags = append(diags, modDiags...)
70 if mod == nil {
71 // nil can be returned if the source address was invalid and so
72 // nothing could be loaded whatsoever. LoadModule should've
73 // returned at least one error diagnostic in that case.
74 continue
75 }
76
77 child := &Config{
78 Parent: parent,
79 Root: parent.Root,
80 Path: path,
81 Module: mod,
82 CallPos: call.Pos,
83 SourceAddr: call.Source,
84 Version: ver,
85 }
86
87 child.Children, modDiags = buildChildModules(child, walker)
88 diags = diags.Append(modDiags)
89
90 ret[call.Name] = child
91 }
92
93 return ret, diags
94}
95
96// ModuleRequest is used as part of the ModuleWalker interface used with
97// function BuildConfig.
98type ModuleRequest struct {
99 // Name is the "logical name" of the module call within configuration.
100 // This is provided in case the name is used as part of a storage key
101 // for the module, but implementations must otherwise treat it as an
102 // opaque string. It is guaranteed to have already been validated as an
103 // HCL identifier and UTF-8 encoded.
104 Name string
105
106 // Path is a list of logical names that traverse from the root module to
107 // this module. This can be used, for example, to form a lookup key for
108 // each distinct module call in a configuration, allowing for multiple
109 // calls with the same name at different points in the tree.
110 Path addrs.Module
111
112 // SourceAddr is the source address string provided by the user in
113 // configuration.
114 SourceAddr string
115
116 // VersionConstraint is the version constraint applied to the module in
117 // configuration.
118 VersionConstraints version.Constraints
119
120 // Parent is the partially-constructed module tree node that the loaded
121 // module will be added to. Callers may refer to any field of this
122 // structure except Children, which is still under construction when
123 // ModuleRequest objects are created and thus has undefined content.
124 // The main reason this is provided is so that full module paths can
125 // be constructed for uniqueness.
126 Parent *Config
127
128 // CallRange is the source position for the header of the "module" block
129 // in configuration that prompted this request.
130 CallPos tfconfig.SourcePos
131}
132
133// ModuleWalker is an interface used with BuildConfig.
134type ModuleWalker interface {
135 LoadModule(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics)
136}
137
138// ModuleWalkerFunc is an implementation of ModuleWalker that directly wraps
139// a callback function, for more convenient use of that interface.
140type ModuleWalkerFunc func(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics)
141
142func (f ModuleWalkerFunc) LoadModule(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) {
143 return f(req)
144}
diff --git a/vendor/github.com/hashicorp/terraform/internal/earlyconfig/diagnostics.go b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/diagnostics.go
new file mode 100644
index 0000000..9b2fd7f
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/diagnostics.go
@@ -0,0 +1,78 @@
1package earlyconfig
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/terraform-config-inspect/tfconfig"
7 "github.com/hashicorp/terraform/tfdiags"
8)
9
10func wrapDiagnostics(diags tfconfig.Diagnostics) tfdiags.Diagnostics {
11 ret := make(tfdiags.Diagnostics, len(diags))
12 for i, diag := range diags {
13 ret[i] = wrapDiagnostic(diag)
14 }
15 return ret
16}
17
18func wrapDiagnostic(diag tfconfig.Diagnostic) tfdiags.Diagnostic {
19 return wrappedDiagnostic{
20 d: diag,
21 }
22}
23
24type wrappedDiagnostic struct {
25 d tfconfig.Diagnostic
26}
27
28func (d wrappedDiagnostic) Severity() tfdiags.Severity {
29 switch d.d.Severity {
30 case tfconfig.DiagError:
31 return tfdiags.Error
32 case tfconfig.DiagWarning:
33 return tfdiags.Warning
34 default:
35 // Should never happen since there are no other severities
36 return 0
37 }
38}
39
40func (d wrappedDiagnostic) Description() tfdiags.Description {
41 // Since the inspect library doesn't produce precise source locations,
42 // we include the position information as part of the error message text.
43 // See the comment inside method "Source" for more information.
44 switch {
45 case d.d.Pos == nil:
46 return tfdiags.Description{
47 Summary: d.d.Summary,
48 Detail: d.d.Detail,
49 }
50 case d.d.Detail != "":
51 return tfdiags.Description{
52 Summary: d.d.Summary,
53 Detail: fmt.Sprintf("On %s line %d: %s", d.d.Pos.Filename, d.d.Pos.Line, d.d.Detail),
54 }
55 default:
56 return tfdiags.Description{
57 Summary: fmt.Sprintf("%s (on %s line %d)", d.d.Summary, d.d.Pos.Filename, d.d.Pos.Line),
58 }
59 }
60}
61
62func (d wrappedDiagnostic) Source() tfdiags.Source {
63 // Since the inspect library is constrained by the lowest common denominator
64 // between legacy HCL and modern HCL, it only returns ranges at whole-line
65 // granularity, and that isn't sufficient to populate a tfdiags.Source
66 // and so we'll just omit ranges altogether and include the line number in
67 // the Description text.
68 //
69 // Callers that want to return nicer errors should consider reacting to
70 // earlyconfig errors by attempting a follow-up parse with the normal
71 // config loader, which can produce more precise source location
72 // information.
73 return tfdiags.Source{}
74}
75
76func (d wrappedDiagnostic) FromExpr() *tfdiags.FromExpr {
77 return nil
78}
diff --git a/vendor/github.com/hashicorp/terraform/internal/earlyconfig/doc.go b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/doc.go
new file mode 100644
index 0000000..a9cf10f
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/doc.go
@@ -0,0 +1,20 @@
1// Package earlyconfig is a specialized alternative to the top-level "configs"
2// package that does only shallow processing of configuration and is therefore
3// able to be much more liberal than the full config loader in what it accepts.
4//
5// In particular, it can accept both current and legacy HCL syntax, and it
6// ignores top-level blocks that it doesn't recognize. These two characteristics
7// make this package ideal for dependency-checking use-cases so that we are
8// more likely to be able to return an error message about an explicit
9// incompatibility than to return a less-actionable message about a construct
10// not being supported.
11//
12// However, its liberal approach also means it should be used sparingly. It
13// exists primarily for "terraform init", so that it is able to detect
14// incompatibilities more robustly when installing dependencies. For most
15// other use-cases, use the "configs" and "configs/configload" packages.
16//
17// Package earlyconfig is a wrapper around the terraform-config-inspect
18// codebase, adding to it just some helper functionality for Terraform's own
19// use-cases.
20package earlyconfig
diff --git a/vendor/github.com/hashicorp/terraform/internal/earlyconfig/module.go b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/module.go
new file mode 100644
index 0000000..d2d6287
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/internal/earlyconfig/module.go
@@ -0,0 +1,13 @@
1package earlyconfig
2
3import (
4 "github.com/hashicorp/terraform-config-inspect/tfconfig"
5 "github.com/hashicorp/terraform/tfdiags"
6)
7
8// LoadModule loads some top-level metadata for the module in the given
9// directory.
10func LoadModule(dir string) (*tfconfig.Module, tfdiags.Diagnostics) {
11 mod, diags := tfconfig.LoadModule(dir)
12 return mod, wrapDiagnostics(diags)
13}