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>"