aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/registry/regsrc/module.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/registry/regsrc/module.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/registry/regsrc/module.go205
1 files changed, 205 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/registry/regsrc/module.go b/vendor/github.com/hashicorp/terraform/registry/regsrc/module.go
new file mode 100644
index 0000000..325706e
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/registry/regsrc/module.go
@@ -0,0 +1,205 @@
1package regsrc
2
3import (
4 "errors"
5 "fmt"
6 "regexp"
7 "strings"
8
9 "github.com/hashicorp/terraform/svchost"
10)
11
12var (
13 ErrInvalidModuleSource = errors.New("not a valid registry module source")
14
15 // nameSubRe is the sub-expression that matches a valid module namespace or
16 // name. It's strictly a super-set of what GitHub allows for user/org and
17 // repo names respectively, but more restrictive than our original repo-name
18 // regex which allowed periods but could cause ambiguity with hostname
19 // prefixes. It does not anchor the start or end so it can be composed into
20 // more complex RegExps below. Alphanumeric with - and _ allowed in non
21 // leading or trailing positions. Max length 64 chars. (GitHub username is
22 // 38 max.)
23 nameSubRe = "[0-9A-Za-z](?:[0-9A-Za-z-_]{0,62}[0-9A-Za-z])?"
24
25 // providerSubRe is the sub-expression that matches a valid provider. It
26 // does not anchor the start or end so it can be composed into more complex
27 // RegExps below. Only lowercase chars and digits are supported in practice.
28 // Max length 64 chars.
29 providerSubRe = "[0-9a-z]{1,64}"
30
31 // moduleSourceRe is a regular expression that matches the basic
32 // namespace/name/provider[//...] format for registry sources. It assumes
33 // any FriendlyHost prefix has already been removed if present.
34 moduleSourceRe = regexp.MustCompile(
35 fmt.Sprintf("^(%s)\\/(%s)\\/(%s)(?:\\/\\/(.*))?$",
36 nameSubRe, nameSubRe, providerSubRe))
37
38 // NameRe is a regular expression defining the format allowed for namespace
39 // or name fields in module registry implementations.
40 NameRe = regexp.MustCompile("^" + nameSubRe + "$")
41
42 // ProviderRe is a regular expression defining the format allowed for
43 // provider fields in module registry implementations.
44 ProviderRe = regexp.MustCompile("^" + providerSubRe + "$")
45
46 // these hostnames are not allowed as registry sources, because they are
47 // already special case module sources in terraform.
48 disallowed = map[string]bool{
49 "github.com": true,
50 "bitbucket.org": true,
51 }
52)
53
54// Module describes a Terraform Registry Module source.
55type Module struct {
56 // RawHost is the friendly host prefix if one was present. It might be nil
57 // if the original source had no host prefix which implies
58 // PublicRegistryHost but is distinct from having an actual pointer to
59 // PublicRegistryHost since it encodes the fact the original string didn't
60 // include a host prefix at all which is significant for recovering actual
61 // input not just normalized form. Most callers should access it with Host()
62 // which will return public registry host instance if it's nil.
63 RawHost *FriendlyHost
64 RawNamespace string
65 RawName string
66 RawProvider string
67 RawSubmodule string
68}
69
70// NewModule construct a new module source from separate parts. Pass empty
71// string if host or submodule are not needed.
72func NewModule(host, namespace, name, provider, submodule string) (*Module, error) {
73 m := &Module{
74 RawNamespace: namespace,
75 RawName: name,
76 RawProvider: provider,
77 RawSubmodule: submodule,
78 }
79 if host != "" {
80 h := NewFriendlyHost(host)
81 if h != nil {
82 fmt.Println("HOST:", h)
83 if !h.Valid() || disallowed[h.Display()] {
84 return nil, ErrInvalidModuleSource
85 }
86 }
87 m.RawHost = h
88 }
89 return m, nil
90}
91
92// ParseModuleSource attempts to parse source as a Terraform registry module
93// source. If the string is not found to be in a valid format,
94// ErrInvalidModuleSource is returned. Note that this can only be used on
95// "input" strings, e.g. either ones supplied by the user or potentially
96// normalised but in Display form (unicode). It will fail to parse a source with
97// a punycoded domain since this is not permitted input from a user. If you have
98// an already normalized string internally, you can compare it without parsing
99// by comparing with the normalized version of the subject with the normal
100// string equality operator.
101func ParseModuleSource(source string) (*Module, error) {
102 // See if there is a friendly host prefix.
103 host, rest := ParseFriendlyHost(source)
104 if host != nil {
105 if !host.Valid() || disallowed[host.Display()] {
106 return nil, ErrInvalidModuleSource
107 }
108 }
109
110 matches := moduleSourceRe.FindStringSubmatch(rest)
111 if len(matches) < 4 {
112 return nil, ErrInvalidModuleSource
113 }
114
115 m := &Module{
116 RawHost: host,
117 RawNamespace: matches[1],
118 RawName: matches[2],
119 RawProvider: matches[3],
120 }
121
122 if len(matches) == 5 {
123 m.RawSubmodule = matches[4]
124 }
125
126 return m, nil
127}
128
129// Display returns the source formatted for display to the user in CLI or web
130// output.
131func (m *Module) Display() string {
132 return m.formatWithPrefix(m.normalizedHostPrefix(m.Host().Display()), false)
133}
134
135// Normalized returns the source formatted for internal reference or comparison.
136func (m *Module) Normalized() string {
137 return m.formatWithPrefix(m.normalizedHostPrefix(m.Host().Normalized()), false)
138}
139
140// String returns the source formatted as the user originally typed it assuming
141// it was parsed from user input.
142func (m *Module) String() string {
143 // Don't normalize public registry hostname - leave it exactly like the user
144 // input it.
145 hostPrefix := ""
146 if m.RawHost != nil {
147 hostPrefix = m.RawHost.String() + "/"
148 }
149 return m.formatWithPrefix(hostPrefix, true)
150}
151
152// Equal compares the module source against another instance taking
153// normalization into account.
154func (m *Module) Equal(other *Module) bool {
155 return m.Normalized() == other.Normalized()
156}
157
158// Host returns the FriendlyHost object describing which registry this module is
159// in. If the original source string had not host component this will return the
160// PublicRegistryHost.
161func (m *Module) Host() *FriendlyHost {
162 if m.RawHost == nil {
163 return PublicRegistryHost
164 }
165 return m.RawHost
166}
167
168func (m *Module) normalizedHostPrefix(host string) string {
169 if m.Host().Equal(PublicRegistryHost) {
170 return ""
171 }
172 return host + "/"
173}
174
175func (m *Module) formatWithPrefix(hostPrefix string, preserveCase bool) string {
176 suffix := ""
177 if m.RawSubmodule != "" {
178 suffix = "//" + m.RawSubmodule
179 }
180 str := fmt.Sprintf("%s%s/%s/%s%s", hostPrefix, m.RawNamespace, m.RawName,
181 m.RawProvider, suffix)
182
183 // lower case by default
184 if !preserveCase {
185 return strings.ToLower(str)
186 }
187 return str
188}
189
190// Module returns just the registry ID of the module, without a hostname or
191// suffix.
192func (m *Module) Module() string {
193 return fmt.Sprintf("%s/%s/%s", m.RawNamespace, m.RawName, m.RawProvider)
194}
195
196// SvcHost returns the svchost.Hostname for this module. Since FriendlyHost may
197// contain an invalid hostname, this also returns an error indicating if it
198// could be converted to a svchost.Hostname. If no host is specified, the
199// default PublicRegistryHost is returned.
200func (m *Module) SvcHost() (svchost.Hostname, error) {
201 if m.RawHost == nil {
202 return svchost.ForComparison(PublicRegistryHost.Raw)
203 }
204 return svchost.ForComparison(m.RawHost.Raw)
205}