Merge pull request #182 from fluxcd/use-pkg-version

Switch to Masterminds/semver and pkg/version libs
This commit is contained in:
Hidde Beydals 2020-10-28 12:37:53 +01:00 committed by GitHub
commit 53554fbf98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 65 additions and 53 deletions

View File

@ -216,11 +216,18 @@ var _ = Describe("GitRepositoryReconciler", func() {
expectStatus: corev1.ConditionTrue, expectStatus: corev1.ConditionTrue,
expectRevision: "0.2.0", expectRevision: "0.2.0",
}), }),
Entry("mixed semver range", refTestCase{
reference: &sourcev1.GitRepositoryRef{SemVer: ">=0.1.0 <1.0.0"},
createRefs: []string{"refs/tags/0.1.0", "refs/tags/v0.1.1", "refs/tags/v0.2.0", "refs/tags/1.0.0"},
waitForReason: sourcev1.GitOperationSucceedReason,
expectStatus: corev1.ConditionTrue,
expectRevision: "v0.2.0",
}),
Entry("semver invalid", refTestCase{ Entry("semver invalid", refTestCase{
reference: &sourcev1.GitRepositoryRef{SemVer: "v1.0.0"}, reference: &sourcev1.GitRepositoryRef{SemVer: "1.2.3.4"},
waitForReason: sourcev1.GitOperationFailedReason, waitForReason: sourcev1.GitOperationFailedReason,
expectStatus: corev1.ConditionFalse, expectStatus: corev1.ConditionFalse,
expectMessage: "semver parse range error", expectMessage: "semver parse range error: improper constraint: 1.2.3.4",
}), }),
Entry("semver no match", refTestCase{ Entry("semver no match", refTestCase{
reference: &sourcev1.GitRepositoryRef{SemVer: "1.0.0"}, reference: &sourcev1.GitRepositoryRef{SemVer: "1.0.0"},

3
go.mod
View File

@ -5,7 +5,7 @@ go 1.15
replace github.com/fluxcd/source-controller/api => ./api replace github.com/fluxcd/source-controller/api => ./api
require ( require (
github.com/blang/semver/v4 v4.0.0 github.com/Masterminds/semver/v3 v3.1.0
github.com/fluxcd/pkg/apis/meta v0.0.2 github.com/fluxcd/pkg/apis/meta v0.0.2
github.com/fluxcd/pkg/gittestserver v0.0.2 github.com/fluxcd/pkg/gittestserver v0.0.2
github.com/fluxcd/pkg/helmtestserver v0.0.1 github.com/fluxcd/pkg/helmtestserver v0.0.1
@ -13,6 +13,7 @@ require (
github.com/fluxcd/pkg/runtime v0.1.0 github.com/fluxcd/pkg/runtime v0.1.0
github.com/fluxcd/pkg/ssh v0.0.5 github.com/fluxcd/pkg/ssh v0.0.5
github.com/fluxcd/pkg/untar v0.0.5 github.com/fluxcd/pkg/untar v0.0.5
github.com/fluxcd/pkg/version v0.0.1
github.com/fluxcd/source-controller/api v0.1.1 github.com/fluxcd/source-controller/api v0.1.1
github.com/go-git/go-billy/v5 v5.0.0 github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.1.0 github.com/go-git/go-git/v5 v5.1.0

4
go.sum
View File

@ -87,8 +87,6 @@ github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngE
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs= github.com/blang/semver v3.5.0+incompatible h1:CGxCgetQ64DKk7rdZ++Vfnb1+ogGNnB17OJKJXD2Cfs=
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA= github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA=
@ -220,6 +218,8 @@ github.com/fluxcd/pkg/testserver v0.0.2 h1:SoaMtO9cE5p/wl2zkGudzflnEHd9mk68CGjZO
github.com/fluxcd/pkg/testserver v0.0.2/go.mod h1:pgUZTh9aQ44FSTQo+5NFlh7YMbUfdz1B80DalW7k96Y= github.com/fluxcd/pkg/testserver v0.0.2/go.mod h1:pgUZTh9aQ44FSTQo+5NFlh7YMbUfdz1B80DalW7k96Y=
github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk= github.com/fluxcd/pkg/untar v0.0.5 h1:UGI3Ch1UIEIaqQvMicmImL1s9npQa64DJ/ozqHKB7gk=
github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw= github.com/fluxcd/pkg/untar v0.0.5/go.mod h1:O6V9+rtl8c1mHBafgqFlJN6zkF1HS5SSYn7RpQJ/nfw=
github.com/fluxcd/pkg/version v0.0.1 h1:/8asQoDXSThz3csiwi4Qo8Zb6blAxLXbtxNgeMJ9bCg=
github.com/fluxcd/pkg/version v0.0.1/go.mod h1:WAF4FEEA9xyhngF8TDxg3UPu5fA1qhEYV8Pmi2Il01Q=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=

View File

@ -25,10 +25,12 @@ import (
"sort" "sort"
"strings" "strings"
"github.com/blang/semver/v4" "github.com/Masterminds/semver/v3"
"helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/getter"
"helm.sh/helm/v3/pkg/repo" "helm.sh/helm/v3/pkg/repo"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"github.com/fluxcd/pkg/version"
) )
// ChartRepository represents a Helm chart repository, and the configuration // ChartRepository represents a Helm chart repository, and the configuration
@ -63,7 +65,7 @@ func NewChartRepository(repositoryURL string, providers getter.Providers, opts [
// Get returns the repo.ChartVersion for the given name, the version is expected // Get returns the repo.ChartVersion for the given name, the version is expected
// to be a semver.Constraints compatible string. If version is empty, the latest // to be a semver.Constraints compatible string. If version is empty, the latest
// stable version will be returned and prerelease versions will be ignored. // stable version will be returned and prerelease versions will be ignored.
func (r *ChartRepository) Get(name, version string) (*repo.ChartVersion, error) { func (r *ChartRepository) Get(name, ver string) (*repo.ChartVersion, error) {
cvs, ok := r.Index.Entries[name] cvs, ok := r.Index.Entries[name]
if !ok { if !ok {
return nil, repo.ErrNoChartName return nil, repo.ErrNoChartName
@ -73,71 +75,69 @@ func (r *ChartRepository) Get(name, version string) (*repo.ChartVersion, error)
} }
// Check for exact matches first // Check for exact matches first
if len(version) != 0 { if len(ver) != 0 {
for _, cv := range cvs { for _, cv := range cvs {
if version == cv.Version { if ver == cv.Version {
return cv, nil return cv, nil
} }
} }
} }
// Continue to look for a (semantic) version match // Continue to look for a (semantic) version match
latestStable := len(version) == 0 || version == "*" verConstraint, err := semver.NewConstraint("*")
var match semver.Range if err != nil {
return nil, err
}
latestStable := len(ver) == 0 || ver == "*"
if !latestStable { if !latestStable {
rng, err := semver.ParseRange(version) verConstraint, err = semver.NewConstraint(ver)
if err != nil { if err != nil {
return nil, err return nil, err
} }
match = rng
} }
var filteredVersions semver.Versions
lookup := make(map[string]*repo.ChartVersion) // Filter out chart versions that doesn't satisfy constraints if any,
// parse semver and build a lookup table
var matchedVersions semver.Collection
lookup := make(map[*semver.Version]*repo.ChartVersion)
for _, cv := range cvs { for _, cv := range cvs {
v, err := semver.ParseTolerant(cv.Version) v, err := version.ParseVersion(cv.Version)
if err != nil { if err != nil {
continue continue
} }
if match != nil && !match(v) { if !verConstraint.Check(v) {
continue continue
} }
filteredVersions = append(filteredVersions, v)
lookup[v.String()] = cv matchedVersions = append(matchedVersions, v)
lookup[v] = cv
} }
if len(filteredVersions) == 0 { if len(matchedVersions) == 0 {
return nil, fmt.Errorf("no chart version found for %s-%s", name, version) return nil, fmt.Errorf("no chart version found for %s-%s", name, ver)
} }
// Sort versions // Sort versions
sort.SliceStable(filteredVersions, func(i, j int) bool { sort.SliceStable(matchedVersions, func(i, j int) bool {
// Reverse // Reverse
return !(func() bool { return !(func() bool {
left := filteredVersions[i] left := matchedVersions[i]
right := filteredVersions[j] right := matchedVersions[j]
if !left.EQ(right) { if !left.Equal(right) {
return left.LT(right) return left.LessThan(right)
} }
// Having chart creation timestamp at our disposal, we put package with the // Having chart creation timestamp at our disposal, we put package with the
// same version into a chronological order. This is especially important for // same version into a chronological order. This is especially important for
// versions that differ only by build metadata, because it is not considered // versions that differ only by build metadata, because it is not considered
// a part of the comparable version in Semver // a part of the comparable version in Semver
return lookup[left.String()].Created.Before(lookup[right.String()].Created) return lookup[left].Created.Before(lookup[right].Created)
})() })()
}) })
latest := filteredVersions[0] latest := matchedVersions[0]
if latestStable { return lookup[latest], nil
for _, v := range filteredVersions {
if len(v.Pre) == 0 {
latest = v
break
}
}
}
return lookup[latest.String()], nil
} }
// DownloadChart confirms the given repo.ChartVersion has a downloadable URL, // DownloadChart confirms the given repo.ChartVersion has a downloadable URL,

View File

@ -22,12 +22,14 @@ import (
"sort" "sort"
"time" "time"
"github.com/blang/semver/v4" "github.com/Masterminds/semver/v3"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/plumbing/transport"
"github.com/fluxcd/pkg/version"
sourcev1 "github.com/fluxcd/source-controller/api/v1beta1" sourcev1 "github.com/fluxcd/source-controller/api/v1beta1"
) )
@ -166,7 +168,7 @@ type CheckoutSemVer struct {
} }
func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth transport.AuthMethod) (*object.Commit, string, error) { func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth transport.AuthMethod) (*object.Commit, string, error) {
rng, err := semver.ParseRange(c.semVer) verConstraint, err := semver.NewConstraint(c.semVer)
if err != nil { if err != nil {
return nil, "", fmt.Errorf("semver parse range error: %w", err) return nil, "", fmt.Errorf("semver parse range error: %w", err)
} }
@ -208,26 +210,28 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth tr
return nil return nil
}) })
svTags := make(map[string]string) var matchedVersions semver.Collection
var svers []semver.Version
for tag, _ := range tags { for tag, _ := range tags {
v, _ := semver.ParseTolerant(tag) v, err := version.ParseVersion(tag)
if rng(v) { if err != nil {
svers = append(svers, v) continue
svTags[v.String()] = tag
} }
if !verConstraint.Check(v) {
continue
}
matchedVersions = append(matchedVersions, v)
} }
if len(svers) == 0 { if len(matchedVersions) == 0 {
return nil, "", fmt.Errorf("no match found for semver: %s", c.semVer) return nil, "", fmt.Errorf("no match found for semver: %s", c.semVer)
} }
// Sort versions // Sort versions
sort.SliceStable(svers, func(i, j int) bool { sort.SliceStable(matchedVersions, func(i, j int) bool {
left := svers[i] left := matchedVersions[i]
right := svers[j] right := matchedVersions[j]
if !left.EQ(right) { if !left.Equal(right) {
return left.LT(right) return left.LessThan(right)
} }
// Having tag target timestamps at our disposal, we further try to sort // Having tag target timestamps at our disposal, we further try to sort
@ -236,8 +240,8 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, auth tr
// a part of the comparable version in Semver // a part of the comparable version in Semver
return tagTimestamps[left.String()].Before(tagTimestamps[right.String()]) return tagTimestamps[left.String()].Before(tagTimestamps[right.String()])
}) })
v := svers[len(svers)-1] v := matchedVersions[len(matchedVersions)-1]
t := svTags[v.String()] t := v.Original()
w, err := repo.Worktree() w, err := repo.Worktree()
if err != nil { if err != nil {