]>
Commit | Line | Data |
---|---|---|
1 | package discovery | |
2 | ||
3 | import ( | |
4 | "bytes" | |
5 | ) | |
6 | ||
7 | // PluginInstallProtocolVersion is the protocol version TF-core | |
8 | // supports to communicate with servers, and is used to resolve | |
9 | // plugin discovery with terraform registry, in addition to | |
10 | // any specified plugin version constraints | |
11 | const PluginInstallProtocolVersion = 5 | |
12 | ||
13 | // PluginRequirements describes a set of plugins (assumed to be of a consistent | |
14 | // kind) that are required to exist and have versions within the given | |
15 | // corresponding sets. | |
16 | type PluginRequirements map[string]*PluginConstraints | |
17 | ||
18 | // PluginConstraints represents an element of PluginRequirements describing | |
19 | // the constraints for a single plugin. | |
20 | type PluginConstraints struct { | |
21 | // Specifies that the plugin's version must be within the given | |
22 | // constraints. | |
23 | Versions Constraints | |
24 | ||
25 | // If non-nil, the hash of the on-disk plugin executable must exactly | |
26 | // match the SHA256 hash given here. | |
27 | SHA256 []byte | |
28 | } | |
29 | ||
30 | // Allows returns true if the given version is within the receiver's version | |
31 | // constraints. | |
32 | func (s *PluginConstraints) Allows(v Version) bool { | |
33 | return s.Versions.Allows(v) | |
34 | } | |
35 | ||
36 | // AcceptsSHA256 returns true if the given executable SHA256 hash is acceptable, | |
37 | // either because it matches the constraint or because there is no such | |
38 | // constraint. | |
39 | func (s *PluginConstraints) AcceptsSHA256(digest []byte) bool { | |
40 | if s.SHA256 == nil { | |
41 | return true | |
42 | } | |
43 | return bytes.Equal(s.SHA256, digest) | |
44 | } | |
45 | ||
46 | // Merge takes the contents of the receiver and the other given requirements | |
47 | // object and merges them together into a single requirements structure | |
48 | // that satisfies both sets of requirements. | |
49 | // | |
50 | // Note that it doesn't make sense to merge two PluginRequirements with | |
51 | // differing required plugin SHA256 hashes, since the result will never | |
52 | // match any plugin. | |
53 | func (r PluginRequirements) Merge(other PluginRequirements) PluginRequirements { | |
54 | ret := make(PluginRequirements) | |
55 | for n, c := range r { | |
56 | ret[n] = &PluginConstraints{ | |
57 | Versions: Constraints{}.Append(c.Versions), | |
58 | SHA256: c.SHA256, | |
59 | } | |
60 | } | |
61 | for n, c := range other { | |
62 | if existing, exists := ret[n]; exists { | |
63 | ret[n].Versions = ret[n].Versions.Append(c.Versions) | |
64 | ||
65 | if existing.SHA256 != nil { | |
66 | if c.SHA256 != nil && !bytes.Equal(c.SHA256, existing.SHA256) { | |
67 | // If we've been asked to merge two constraints with | |
68 | // different SHA256 hashes then we'll produce a dummy value | |
69 | // that can never match anything. This is a silly edge case | |
70 | // that no reasonable caller should hit. | |
71 | ret[n].SHA256 = []byte(invalidProviderHash) | |
72 | } | |
73 | } else { | |
74 | ret[n].SHA256 = c.SHA256 // might still be nil | |
75 | } | |
76 | } else { | |
77 | ret[n] = &PluginConstraints{ | |
78 | Versions: Constraints{}.Append(c.Versions), | |
79 | SHA256: c.SHA256, | |
80 | } | |
81 | } | |
82 | } | |
83 | return ret | |
84 | } | |
85 | ||
86 | // LockExecutables applies additional constraints to the receiver that | |
87 | // require plugin executables with specific SHA256 digests. This modifies | |
88 | // the receiver in-place, since it's intended to be applied after | |
89 | // version constraints have been resolved. | |
90 | // | |
91 | // The given map must include a key for every plugin that is already | |
92 | // required. If not, any missing keys will cause the corresponding plugin | |
93 | // to never match, though the direct caller doesn't necessarily need to | |
94 | // guarantee this as long as the downstream code _applying_ these constraints | |
95 | // is able to deal with the non-match in some way. | |
96 | func (r PluginRequirements) LockExecutables(sha256s map[string][]byte) { | |
97 | for name, cons := range r { | |
98 | digest := sha256s[name] | |
99 | ||
100 | if digest == nil { | |
101 | // Prevent any match, which will then presumably cause the | |
102 | // downstream consumer of this requirements to report an error. | |
103 | cons.SHA256 = []byte(invalidProviderHash) | |
104 | continue | |
105 | } | |
106 | ||
107 | cons.SHA256 = digest | |
108 | } | |
109 | } | |
110 | ||
111 | const invalidProviderHash = "<invalid>" |