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