796758b8a5
The existing version constraint uses logic optimized for package managers, not schedulers, when checking prereleases: - 1.3.0-beta1 will *not* satisfy ">= 0.6.1" - 1.7.0-rc1 will *not* satisfy ">= 1.6.0-beta1" This is due to package managers wishing to favor final releases over prereleases. In a scheduler versions more often represent the earliest release all required features/APIs are available in a system. Whether the constraint or the version being evaluated are prereleases has no impact on ordering. This commit adds a new constraint - `semver` - which will use Semver v2.0 ordering when evaluating constraints. Given the above examples: - 1.3.0-beta1 satisfies ">= 0.6.1" using `semver` - 1.7.0-rc1 satisfies ">= 1.6.0-beta1" using `semver` Since existing jobspecs may rely on the old behavior, a new constraint was added and the implicit Consul Connect and Vault constraints were updated to use it.
148 lines
3.4 KiB
Go
148 lines
3.4 KiB
Go
// semver is a Semver Constraints package copied from
|
|
// github.com/hashicorp/go-version @ 2046c9d0f0b03c779670f5186a2a4b2c85493a71
|
|
//
|
|
// Unlike Constraints in go-version, Semver constraints use Semver 2.0 ordering
|
|
// rules and only accept properly formatted Semver versions.
|
|
package semver
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/go-version"
|
|
)
|
|
|
|
// Constraint represents a single constraint for a version, such as ">=
|
|
// 1.0".
|
|
type Constraint struct {
|
|
f constraintFunc
|
|
check *version.Version
|
|
original string
|
|
}
|
|
|
|
// Constraints is a slice of constraints. We make a custom type so that
|
|
// we can add methods to it.
|
|
type Constraints []*Constraint
|
|
|
|
type constraintFunc func(v, c *version.Version) bool
|
|
|
|
var constraintOperators map[string]constraintFunc
|
|
|
|
var constraintRegexp *regexp.Regexp
|
|
|
|
func init() {
|
|
constraintOperators = map[string]constraintFunc{
|
|
"": constraintEqual,
|
|
"=": constraintEqual,
|
|
"!=": constraintNotEqual,
|
|
">": constraintGreaterThan,
|
|
"<": constraintLessThan,
|
|
">=": constraintGreaterThanEqual,
|
|
"<=": constraintLessThanEqual,
|
|
}
|
|
|
|
ops := make([]string, 0, len(constraintOperators))
|
|
for k := range constraintOperators {
|
|
ops = append(ops, regexp.QuoteMeta(k))
|
|
}
|
|
|
|
constraintRegexp = regexp.MustCompile(fmt.Sprintf(
|
|
`^\s*(%s)\s*(%s)\s*$`,
|
|
strings.Join(ops, "|"),
|
|
version.SemverRegexpRaw))
|
|
}
|
|
|
|
// NewConstraint will parse one or more constraints from the given
|
|
// constraint string. The string must be a comma-separated list of constraints.
|
|
func NewConstraint(v string) (Constraints, error) {
|
|
vs := strings.Split(v, ",")
|
|
result := make([]*Constraint, len(vs))
|
|
for i, single := range vs {
|
|
c, err := parseSingle(single)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result[i] = c
|
|
}
|
|
|
|
return Constraints(result), nil
|
|
}
|
|
|
|
// Check tests if a version satisfies all the constraints.
|
|
func (cs Constraints) Check(v *version.Version) bool {
|
|
for _, c := range cs {
|
|
if !c.Check(v) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Returns the string format of the constraints
|
|
func (cs Constraints) String() string {
|
|
csStr := make([]string, len(cs))
|
|
for i, c := range cs {
|
|
csStr[i] = c.String()
|
|
}
|
|
|
|
return strings.Join(csStr, ",")
|
|
}
|
|
|
|
// Check tests if a constraint is validated by the given version.
|
|
func (c *Constraint) Check(v *version.Version) bool {
|
|
return c.f(v, c.check)
|
|
}
|
|
|
|
func (c *Constraint) String() string {
|
|
return c.original
|
|
}
|
|
|
|
func parseSingle(v string) (*Constraint, error) {
|
|
matches := constraintRegexp.FindStringSubmatch(v)
|
|
if matches == nil {
|
|
return nil, fmt.Errorf("Malformed constraint: %s", v)
|
|
}
|
|
|
|
check, err := version.NewSemver(matches[2])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Constraint{
|
|
f: constraintOperators[matches[1]],
|
|
check: check,
|
|
original: v,
|
|
}, nil
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
// Constraint functions
|
|
//-------------------------------------------------------------------
|
|
|
|
func constraintEqual(v, c *version.Version) bool {
|
|
return v.Equal(c)
|
|
}
|
|
|
|
func constraintNotEqual(v, c *version.Version) bool {
|
|
return !v.Equal(c)
|
|
}
|
|
|
|
func constraintGreaterThan(v, c *version.Version) bool {
|
|
return v.Compare(c) == 1
|
|
}
|
|
|
|
func constraintLessThan(v, c *version.Version) bool {
|
|
return v.Compare(c) == -1
|
|
}
|
|
|
|
func constraintGreaterThanEqual(v, c *version.Version) bool {
|
|
return v.Compare(c) >= 0
|
|
}
|
|
|
|
func constraintLessThanEqual(v, c *version.Version) bool {
|
|
return v.Compare(c) <= 0
|
|
}
|