]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/internal/earlyconfig/config_build.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / internal / earlyconfig / config_build.go
1 package earlyconfig
2
3 import (
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.
16 func 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
26 func 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.
98 type 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.
134 type 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.
140 type ModuleWalkerFunc func(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics)
141
142 func (f ModuleWalkerFunc) LoadModule(req *ModuleRequest) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) {
143 return f(req)
144 }