github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/plugin/discovery/requirements.go (about) 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>"