2016-12-28 01:45:11 +00:00
|
|
|
// Copyright 2016 clair authors
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
// Package versionfmt exposes functions to dynamically register formats used to
|
|
|
|
// parse Feature Versions.
|
|
|
|
package versionfmt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"sync"
|
2017-07-26 23:22:29 +00:00
|
|
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
2016-12-28 01:45:11 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// MinVersion is a special package version which is always sorted first.
|
|
|
|
MinVersion = "#MINV#"
|
|
|
|
|
2017-01-03 21:00:20 +00:00
|
|
|
// MaxVersion is a special package version which is always sorted last.
|
2016-12-28 01:45:11 +00:00
|
|
|
MaxVersion = "#MAXV#"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrUnknownVersionFormat is returned when a function does not have enough
|
|
|
|
// context to determine the format of a version.
|
|
|
|
ErrUnknownVersionFormat = errors.New("unknown version format")
|
|
|
|
|
|
|
|
// ErrInvalidVersion is returned when a function needs to validate a version,
|
|
|
|
// but should return an error in the case where the version is invalid.
|
|
|
|
ErrInvalidVersion = errors.New("invalid version")
|
|
|
|
|
|
|
|
parsersM sync.Mutex
|
|
|
|
parsers = make(map[string]Parser)
|
|
|
|
)
|
|
|
|
|
|
|
|
// Parser represents any format that can compare two version strings.
|
|
|
|
type Parser interface {
|
|
|
|
// Valid attempts to parse a version string and returns its success.
|
|
|
|
Valid(string) bool
|
|
|
|
|
|
|
|
// Compare parses two different version strings.
|
|
|
|
// Returns 0 when equal, -1 when a < b, 1 when b < a.
|
|
|
|
Compare(a, b string) (int, error)
|
2017-07-26 23:22:29 +00:00
|
|
|
|
|
|
|
// InRange computes if a is in range of b
|
|
|
|
//
|
|
|
|
// NOTE(Sida): For legacy version formats, rangeB is a version and
|
|
|
|
// always use if versionA < rangeB as threshold.
|
|
|
|
InRange(versionA, rangeB string) (bool, error)
|
|
|
|
|
|
|
|
// GetFixedIn computes a fixed in version for a certain version range.
|
|
|
|
//
|
|
|
|
// NOTE(Sida): For legacy version formats, rangeA is a version and
|
|
|
|
// be returned directly becuase it was considered fixed in version.
|
|
|
|
GetFixedIn(rangeA string) (string, error)
|
2016-12-28 01:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterParser provides a way to dynamically register an implementation of a
|
|
|
|
// Parser.
|
|
|
|
//
|
|
|
|
// If RegisterParser is called twice with the same name, the name is blank, or
|
|
|
|
// if the provided Parser is nil, this function panics.
|
|
|
|
func RegisterParser(name string, p Parser) {
|
|
|
|
if name == "" {
|
2017-01-13 07:33:19 +00:00
|
|
|
panic("versionfmt: could not register a Parser with an empty name")
|
2016-12-28 01:45:11 +00:00
|
|
|
}
|
2017-01-13 07:33:19 +00:00
|
|
|
|
2016-12-28 01:45:11 +00:00
|
|
|
if p == nil {
|
2017-01-13 07:33:19 +00:00
|
|
|
panic("versionfmt: could not register a nil Parser")
|
2016-12-28 01:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
parsersM.Lock()
|
|
|
|
defer parsersM.Unlock()
|
|
|
|
|
2017-01-13 07:33:19 +00:00
|
|
|
if _, dup := parsers[name]; dup {
|
|
|
|
panic("versionfmt: RegisterParser called twice for " + name)
|
2016-12-28 01:45:11 +00:00
|
|
|
}
|
2017-01-13 07:33:19 +00:00
|
|
|
|
2016-12-28 01:45:11 +00:00
|
|
|
parsers[name] = p
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetParser returns the registered Parser with a provided name.
|
|
|
|
func GetParser(name string) (p Parser, exists bool) {
|
|
|
|
parsersM.Lock()
|
|
|
|
defer parsersM.Unlock()
|
|
|
|
|
|
|
|
p, exists = parsers[name]
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Valid is a helper function that will return an error if the version fails to
|
|
|
|
// validate with a given format.
|
|
|
|
func Valid(format, version string) error {
|
|
|
|
versionParser, exists := GetParser(format)
|
|
|
|
if !exists {
|
|
|
|
return ErrUnknownVersionFormat
|
|
|
|
}
|
|
|
|
|
|
|
|
if !versionParser.Valid(version) {
|
|
|
|
return ErrInvalidVersion
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compare is a helper function that will compare two versions with a given
|
|
|
|
// format and return an error if there are any failures.
|
|
|
|
func Compare(format, versionA, versionB string) (int, error) {
|
|
|
|
versionParser, exists := GetParser(format)
|
|
|
|
if !exists {
|
|
|
|
return 0, ErrUnknownVersionFormat
|
|
|
|
}
|
|
|
|
|
|
|
|
return versionParser.Compare(versionA, versionB)
|
|
|
|
}
|
2017-07-26 23:22:29 +00:00
|
|
|
|
|
|
|
// InRange is a helper function that checks if `versionA` is in `rangeB`
|
|
|
|
func InRange(format, version, versionRange string) (bool, error) {
|
|
|
|
versionParser, exists := GetParser(format)
|
|
|
|
if !exists {
|
|
|
|
return false, ErrUnknownVersionFormat
|
|
|
|
}
|
|
|
|
|
|
|
|
in, err := versionParser.InRange(version, versionRange)
|
|
|
|
if err != nil {
|
|
|
|
log.WithFields(log.Fields{"Format": format, "Version": version, "Range": versionRange}).Error(err)
|
|
|
|
}
|
|
|
|
return in, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetFixedIn is a helper function that computes the next fixed in version given
|
|
|
|
// a affected version range `rangeA`.
|
|
|
|
func GetFixedIn(format, rangeA string) (string, error) {
|
|
|
|
versionParser, exists := GetParser(format)
|
|
|
|
if !exists {
|
|
|
|
return "", ErrUnknownVersionFormat
|
|
|
|
}
|
|
|
|
|
|
|
|
return versionParser.GetFixedIn(rangeA)
|
|
|
|
}
|