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