2017-04-19 19:04:09 -05:00
|
|
|
package discovery
|
|
|
|
|
2017-05-24 17:03:36 -05:00
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
)
|
|
|
|
|
2019-01-16 13:00:08 -06:00
|
|
|
// PluginInstallProtocolVersion is the protocol version TF-core
|
|
|
|
// supports to communicate with servers, and is used to resolve
|
|
|
|
// plugin discovery with terraform registry, in addition to
|
|
|
|
// any specified plugin version constraints
|
|
|
|
const PluginInstallProtocolVersion = 5
|
|
|
|
|
2017-04-19 19:04:09 -05:00
|
|
|
// PluginRequirements describes a set of plugins (assumed to be of a consistent
|
|
|
|
// kind) that are required to exist and have versions within the given
|
|
|
|
// corresponding sets.
|
2017-05-24 17:03:36 -05:00
|
|
|
type PluginRequirements map[string]*PluginConstraints
|
|
|
|
|
|
|
|
// PluginConstraints represents an element of PluginRequirements describing
|
|
|
|
// the constraints for a single plugin.
|
|
|
|
type PluginConstraints struct {
|
|
|
|
// Specifies that the plugin's version must be within the given
|
|
|
|
// constraints.
|
|
|
|
Versions Constraints
|
|
|
|
|
|
|
|
// If non-nil, the hash of the on-disk plugin executable must exactly
|
|
|
|
// match the SHA256 hash given here.
|
|
|
|
SHA256 []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allows returns true if the given version is within the receiver's version
|
|
|
|
// constraints.
|
|
|
|
func (s *PluginConstraints) Allows(v Version) bool {
|
|
|
|
return s.Versions.Allows(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AcceptsSHA256 returns true if the given executable SHA256 hash is acceptable,
|
|
|
|
// either because it matches the constraint or because there is no such
|
|
|
|
// constraint.
|
|
|
|
func (s *PluginConstraints) AcceptsSHA256(digest []byte) bool {
|
|
|
|
if s.SHA256 == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return bytes.Equal(s.SHA256, digest)
|
|
|
|
}
|
2017-04-19 19:04:09 -05:00
|
|
|
|
|
|
|
// Merge takes the contents of the receiver and the other given requirements
|
|
|
|
// object and merges them together into a single requirements structure
|
|
|
|
// that satisfies both sets of requirements.
|
2017-05-24 17:03:36 -05:00
|
|
|
//
|
|
|
|
// Note that it doesn't make sense to merge two PluginRequirements with
|
|
|
|
// differing required plugin SHA256 hashes, since the result will never
|
|
|
|
// match any plugin.
|
2017-04-19 19:04:09 -05:00
|
|
|
func (r PluginRequirements) Merge(other PluginRequirements) PluginRequirements {
|
|
|
|
ret := make(PluginRequirements)
|
2017-05-24 17:03:36 -05:00
|
|
|
for n, c := range r {
|
|
|
|
ret[n] = &PluginConstraints{
|
|
|
|
Versions: Constraints{}.Append(c.Versions),
|
|
|
|
SHA256: c.SHA256,
|
|
|
|
}
|
2017-04-19 19:04:09 -05:00
|
|
|
}
|
2017-05-24 17:03:36 -05:00
|
|
|
for n, c := range other {
|
2017-04-19 19:04:09 -05:00
|
|
|
if existing, exists := ret[n]; exists {
|
2017-05-24 17:03:36 -05:00
|
|
|
ret[n].Versions = ret[n].Versions.Append(c.Versions)
|
|
|
|
|
|
|
|
if existing.SHA256 != nil {
|
|
|
|
if c.SHA256 != nil && !bytes.Equal(c.SHA256, existing.SHA256) {
|
|
|
|
// If we've been asked to merge two constraints with
|
|
|
|
// different SHA256 hashes then we'll produce a dummy value
|
|
|
|
// that can never match anything. This is a silly edge case
|
|
|
|
// that no reasonable caller should hit.
|
|
|
|
ret[n].SHA256 = []byte(invalidProviderHash)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret[n].SHA256 = c.SHA256 // might still be nil
|
|
|
|
}
|
2017-04-19 19:04:09 -05:00
|
|
|
} else {
|
2017-05-24 17:03:36 -05:00
|
|
|
ret[n] = &PluginConstraints{
|
|
|
|
Versions: Constraints{}.Append(c.Versions),
|
|
|
|
SHA256: c.SHA256,
|
|
|
|
}
|
2017-04-19 19:04:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
2017-05-24 17:03:36 -05:00
|
|
|
|
|
|
|
// LockExecutables applies additional constraints to the receiver that
|
|
|
|
// require plugin executables with specific SHA256 digests. This modifies
|
|
|
|
// the receiver in-place, since it's intended to be applied after
|
|
|
|
// version constraints have been resolved.
|
|
|
|
//
|
|
|
|
// The given map must include a key for every plugin that is already
|
|
|
|
// required. If not, any missing keys will cause the corresponding plugin
|
|
|
|
// to never match, though the direct caller doesn't necessarily need to
|
|
|
|
// guarantee this as long as the downstream code _applying_ these constraints
|
|
|
|
// is able to deal with the non-match in some way.
|
|
|
|
func (r PluginRequirements) LockExecutables(sha256s map[string][]byte) {
|
|
|
|
for name, cons := range r {
|
|
|
|
digest := sha256s[name]
|
|
|
|
|
|
|
|
if digest == nil {
|
|
|
|
// Prevent any match, which will then presumably cause the
|
|
|
|
// downstream consumer of this requirements to report an error.
|
|
|
|
cons.SHA256 = []byte(invalidProviderHash)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
cons.SHA256 = digest
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const invalidProviderHash = "<invalid>"
|