diff --git a/Makefile b/Makefile index 05c2c1b599..d3e2befb33 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,7 @@ codegen: cp scripts/generate_internal_groups.sh vendor/k8s.io/code-generator/generate_internal_groups.sh mkdir -p vendor/k8s.io/code-generator/hack cp boilerplate.go.txt vendor/k8s.io/code-generator/hack/boilerplate.go.txt - go run vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go --logtostderr -i ./vendor/k8s.io/apimachinery/pkg/runtime,./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./vendor/k8s.io/apimachinery/pkg/version,./pkg/package-server/apis/operators/v1,./pkg/package-server/apis/apps/v1alpha1,./pkg/api/apis/operators/v1alpha1 -p $(PKG)/pkg/package-server/apis/openapi -O zz_generated.openapi -h boilerplate.go.txt -r /dev/null + go run vendor/k8s.io/kube-openapi/cmd/openapi-gen/openapi-gen.go --logtostderr -i ./vendor/k8s.io/apimachinery/pkg/runtime,./vendor/k8s.io/apimachinery/pkg/apis/meta/v1,./vendor/k8s.io/apimachinery/pkg/version,./pkg/package-server/apis/operators/v1,./pkg/package-server/apis/apps/v1alpha1,./pkg/api/apis/operators/v1alpha1,./pkg/lib/version -p $(PKG)/pkg/package-server/apis/openapi -O zz_generated.openapi -h boilerplate.go.txt -r /dev/null $(CODEGEN) all $(PKG)/pkg/api/client $(PKG)/pkg/api/apis "operators:v1alpha1,v1" $(CODEGEN_INTERNAL) all $(PKG)/pkg/package-server/client $(PKG)/pkg/package-server/apis $(PKG)/pkg/package-server/apis "operators:v1 apps:v1alpha1" diff --git a/go.mod b/go.mod index 26fdc02694..52e7131059 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/blang/semver v3.5.1+incompatible github.com/coreos/etcd v3.3.12+incompatible // indirect github.com/coreos/go-semver v0.2.0 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect @@ -38,6 +39,7 @@ require ( github.com/sirupsen/logrus v1.4.1 github.com/spf13/cobra v0.0.3 github.com/stretchr/testify v1.2.2 + go.uber.org/zap v1.10.0 // indirect golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5 // indirect golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db // indirect golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 diff --git a/go.sum b/go.sum index 7cc1dd1b8c..d6acee6cd1 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzs github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -266,6 +268,8 @@ go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go b/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go index cda3791bd4..6854c9dc8d 100644 --- a/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go +++ b/pkg/api/apis/operators/v1alpha1/clusterserviceversion_types.go @@ -7,8 +7,9 @@ import ( "fmt" "sort" - "github.com/coreos/go-semver/semver" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" ) const ( @@ -136,7 +137,7 @@ type APIServiceDefinitions struct { // that can manage apps for a given version. type ClusterServiceVersionSpec struct { InstallStrategy NamedInstallStrategy `json:"install"` - Version semver.Version `json:"version,omitempty"` + Version version.OperatorVersion `json:"version,omitempty"` Maturity string `json:"maturity,omitempty"` CustomResourceDefinitions CustomResourceDefinitions `json:"customresourcedefinitions,omitempty"` APIServiceDefinitions APIServiceDefinitions `json:"apiservicedefinitions,omitempty"` diff --git a/pkg/api/apis/operators/v1alpha1/zz_generated.deepcopy.go b/pkg/api/apis/operators/v1alpha1/zz_generated.deepcopy.go index 4eed2474f6..22f3d810bc 100644 --- a/pkg/api/apis/operators/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/api/apis/operators/v1alpha1/zz_generated.deepcopy.go @@ -397,7 +397,7 @@ func (in *ClusterServiceVersionList) DeepCopyObject() runtime.Object { func (in *ClusterServiceVersionSpec) DeepCopyInto(out *ClusterServiceVersionSpec) { *out = *in in.InstallStrategy.DeepCopyInto(&out.InstallStrategy) - out.Version = in.Version + in.Version.DeepCopyInto(&out.Version) in.CustomResourceDefinitions.DeepCopyInto(&out.CustomResourceDefinitions) in.APIServiceDefinitions.DeepCopyInto(&out.APIServiceDefinitions) if in.NativeAPIs != nil { diff --git a/pkg/controller/operators/catalog/operator.go b/pkg/controller/operators/catalog/operator.go index 880b289c23..d800414c00 100644 --- a/pkg/controller/operators/catalog/operator.go +++ b/pkg/controller/operators/catalog/operator.go @@ -779,7 +779,7 @@ func (o *Operator) ensureSubscriptionCSVState(logger *logrus.Entry, sub *v1alpha return sub, false, nil } - _, err := o.client.OperatorsV1alpha1().ClusterServiceVersions(sub.GetNamespace()).Get(sub.Status.CurrentCSV, metav1.GetOptions{}) + csv, err := o.client.OperatorsV1alpha1().ClusterServiceVersions(sub.GetNamespace()).Get(sub.Status.CurrentCSV, metav1.GetOptions{}) out := sub.DeepCopy() if err != nil { logger.WithError(err).WithField("currentCSV", sub.Status.CurrentCSV).Debug("error fetching csv listed in subscription status") @@ -789,7 +789,7 @@ func (o *Operator) ensureSubscriptionCSVState(logger *logrus.Entry, sub *v1alpha if err := querier.Queryable(); err != nil { return nil, false, err } - bundle, _, _ := querier.FindReplacement(sub.Status.CurrentCSV, sub.Spec.Package, sub.Spec.Channel, resolver.CatalogKey{sub.Spec.CatalogSource, sub.Spec.CatalogSourceNamespace}) + bundle, _, _ := querier.FindReplacement(&csv.Spec.Version.Version, sub.Status.CurrentCSV, sub.Spec.Package, sub.Spec.Channel, resolver.CatalogKey{sub.Spec.CatalogSource, sub.Spec.CatalogSourceNamespace}) if bundle != nil { out.Status.State = v1alpha1.SubscriptionStateUpgradeAvailable } else { diff --git a/pkg/controller/operators/olm/operator.go b/pkg/controller/operators/olm/operator.go index 1166fea047..cc7282b8ce 100644 --- a/pkg/controller/operators/olm/operator.go +++ b/pkg/controller/operators/olm/operator.go @@ -752,7 +752,7 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v return } - operatorSurface, err := resolver.NewOperatorFromCSV(out) + operatorSurface, err := resolver.NewOperatorFromV1Alpha1CSV(out) if err != nil { // TODO: Add failure status to CSV syncError = err diff --git a/pkg/controller/operators/olm/operatorgroup.go b/pkg/controller/operators/olm/operatorgroup.go index ef423a6f1c..28a1b1bdcf 100644 --- a/pkg/controller/operators/olm/operatorgroup.go +++ b/pkg/controller/operators/olm/operatorgroup.go @@ -171,7 +171,7 @@ func (a *Operator) providedAPIsFromCSVs(group *v1.OperatorGroup, logger *logrus. // TODO: Throw out CSVs that aren't members of the group due to group related failures? // Union the providedAPIsFromCSVs from existing members of the group - operatorSurface, err := resolver.NewOperatorFromCSV(csv) + operatorSurface, err := resolver.NewOperatorFromV1Alpha1CSV(csv) if err != nil { logger.WithError(err).Warn("could not create OperatorSurface from csv") continue @@ -763,7 +763,7 @@ func (a *Operator) findCSVsThatProvideAnyOf(provide resolver.APISet) ([]*v1alpha continue } - operatorSurface, err := resolver.NewOperatorFromCSV(csv) + operatorSurface, err := resolver.NewOperatorFromV1Alpha1CSV(csv) if err != nil { continue } diff --git a/pkg/controller/registry/resolver/evolver.go b/pkg/controller/registry/resolver/evolver.go index 5eb9ef9632..e6a4ce0642 100644 --- a/pkg/controller/registry/resolver/evolver.go +++ b/pkg/controller/registry/resolver/evolver.go @@ -57,7 +57,7 @@ func (e *NamespaceGenerationEvolver) checkForUpdates() error { continue } - bundle, key, err := e.querier.FindReplacement(op.Identifier(), op.SourceInfo().Package, op.SourceInfo().Channel, op.SourceInfo().Catalog) + bundle, key, err := e.querier.FindReplacement(op.Version(), op.Identifier(), op.SourceInfo().Package, op.SourceInfo().Channel, op.SourceInfo().Catalog) if err != nil || bundle == nil { continue } diff --git a/pkg/controller/registry/resolver/generation.go b/pkg/controller/registry/resolver/generation.go index 5ceec6eea1..cc4262490a 100644 --- a/pkg/controller/registry/resolver/generation.go +++ b/pkg/controller/registry/resolver/generation.go @@ -47,7 +47,7 @@ func NewGenerationFromCluster(csvs []*v1alpha1.ClusterServiceVersion, subs []*v1 } } for _, csv := range csvs { - op, err := NewOperatorFromCSV(csv) + op, err := NewOperatorFromV1Alpha1CSV(csv) if err != nil { return nil, err } diff --git a/pkg/controller/registry/resolver/generation_test.go b/pkg/controller/registry/resolver/generation_test.go index 167fc26992..c927710da8 100644 --- a/pkg/controller/registry/resolver/generation_test.go +++ b/pkg/controller/registry/resolver/generation_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "github.com/blang/semver" "github.com/operator-framework/operator-registry/pkg/registry" opregistry "github.com/operator-framework/operator-registry/pkg/registry" "github.com/stretchr/testify/require" @@ -12,6 +13,8 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" ) +var NoVersion = semver.MustParse("0.0.0") + func TestNewGenerationFromCSVs(t *testing.T) { type args struct { csvs []*v1alpha1.ClusterServiceVersion @@ -82,6 +85,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { }, requiredAPIs: EmptyAPISet(), sourceInfo: &ExistingOperator, + version: &NoVersion, }, {Group: "g", Version: "v1", Kind: "CRDKind", Plural: "crdkinds"}: &Operator{ name: "operator.v1", @@ -91,6 +95,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { }, requiredAPIs: EmptyAPISet(), sourceInfo: &ExistingOperator, + version: &NoVersion, }, }, requiredAPIs: EmptyAPIMultiOwnerSet(), @@ -142,6 +147,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { {Group: "g", Version: "v1", Kind: "CRDKind", Plural: "crdkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &NoVersion, }, }, {Group: "g", Version: "v1", Kind: "CRDKind", Plural: "crdkinds"}: map[string]OperatorSurface{ @@ -153,6 +159,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { {Group: "g", Version: "v1", Kind: "CRDKind", Plural: "crdkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &NoVersion, }, }, }, @@ -170,6 +177,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { {Group: "g", Version: "v1", Kind: "CRDKind", Plural: "crdkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &NoVersion, }, }, {Group: "g", Version: "v1", Kind: "CRDKind", Plural: "crdkinds"}: map[string]OperatorSurface{ @@ -181,6 +189,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { {Group: "g", Version: "v1", Kind: "CRDKind", Plural: "crdkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &NoVersion, }, }, }, @@ -246,6 +255,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { {Group: "g2", Version: "v1", Kind: "CRDReqKind", Plural: "crdreqkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &NoVersion, }, {Group: "g", Version: "v1", Kind: "CRDOwnedKind", Plural: "crdownedkinds"}: &Operator{ name: "operator.v1", @@ -258,6 +268,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { {Group: "g2", Version: "v1", Kind: "CRDReqKind", Plural: "crdreqkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &NoVersion, }, }, requiredAPIs: map[opregistry.APIKey]OperatorSet{ @@ -273,6 +284,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { {Group: "g2", Version: "v1", Kind: "CRDReqKind", Plural: "crdreqkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &NoVersion, }, }, {Group: "g2", Version: "v1", Kind: "CRDReqKind", Plural: "crdreqkinds"}: map[string]OperatorSurface{ @@ -287,6 +299,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { {Group: "g2", Version: "v1", Kind: "CRDReqKind", Plural: "crdreqkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &NoVersion, }, }, }, @@ -307,6 +320,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { {Group: "g2", Version: "v1", Kind: "CRDReqKind", Plural: "crdreqkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &NoVersion, }, }, {Group: "g2", Version: "v1", Kind: "CRDReqKind", Plural: "crdreqkinds"}: map[string]OperatorSurface{ @@ -321,6 +335,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { {Group: "g2", Version: "v1", Kind: "CRDReqKind", Plural: "crdreqkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &NoVersion, }, }, }, @@ -334,7 +349,7 @@ func TestNewGenerationFromCSVs(t *testing.T) { operatorSet := EmptyOperatorSet() for _, csv := range tt.args.csvs { // there's a separate unit test for this constructor - op, err := NewOperatorFromCSV(csv) + op, err := NewOperatorFromV1Alpha1CSV(csv) require.NoError(t, err) operatorSet[op.Identifier()] = op } diff --git a/pkg/controller/registry/resolver/operators.go b/pkg/controller/registry/resolver/operators.go index b1b22d93fc..a8a3b777a7 100644 --- a/pkg/controller/registry/resolver/operators.go +++ b/pkg/controller/registry/resolver/operators.go @@ -6,9 +6,11 @@ import ( "sort" "strings" - "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" + "github.com/blang/semver" opregistry "github.com/operator-framework/operator-registry/pkg/registry" "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" ) type CatalogKey struct { @@ -215,6 +217,7 @@ type OperatorSurface interface { RequiredAPIs() APISet Identifier() string Replaces() string + Version() *semver.Version SourceInfo() *OperatorSourceInfo Bundle() *opregistry.Bundle } @@ -224,6 +227,7 @@ type Operator struct { replaces string providedAPIs APISet requiredAPIs APISet + version *semver.Version bundle *opregistry.Bundle sourceInfo *OperatorSourceInfo } @@ -246,6 +250,7 @@ func NewOperatorFromBundle(bundle *opregistry.Bundle, startingCSV string, source return &Operator{ name: csv.GetName(), replaces: csv.Spec.Replaces, + version: &csv.Spec.Version.Version, providedAPIs: providedAPIs, requiredAPIs: requiredAPIs, bundle: bundle, @@ -258,7 +263,7 @@ func NewOperatorFromBundle(bundle *opregistry.Bundle, startingCSV string, source }, nil } -func NewOperatorFromCSV(csv *v1alpha1.ClusterServiceVersion) (*Operator, error) { +func NewOperatorFromV1Alpha1CSV(csv *v1alpha1.ClusterServiceVersion) (*Operator, error) { providedAPIs := EmptyAPISet() for _, crdDef := range csv.Spec.CustomResourceDefinitions.Owned { parts := strings.SplitN(crdDef.Name, ".", 2) @@ -285,6 +290,7 @@ func NewOperatorFromCSV(csv *v1alpha1.ClusterServiceVersion) (*Operator, error) return &Operator{ name: csv.GetName(), + version: &csv.Spec.Version.Version, replaces: csv.Spec.Replaces, providedAPIs: providedAPIs, requiredAPIs: requiredAPIs, @@ -319,3 +325,7 @@ func (o *Operator) SourceInfo() *OperatorSourceInfo { func (o *Operator) Bundle() *opregistry.Bundle { return o.bundle } + +func (o *Operator) Version() *semver.Version { + return o.version +} diff --git a/pkg/controller/registry/resolver/operators_test.go b/pkg/controller/registry/resolver/operators_test.go index 5ed5739b35..09766ec674 100644 --- a/pkg/controller/registry/resolver/operators_test.go +++ b/pkg/controller/registry/resolver/operators_test.go @@ -3,6 +3,7 @@ package resolver import ( "testing" + "github.com/blang/semver" opregistry "github.com/operator-framework/operator-registry/pkg/registry" "github.com/stretchr/testify/require" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" @@ -11,6 +12,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" + opver "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" ) func TestGVKStringToProvidedAPISet(t *testing.T) { @@ -885,6 +887,7 @@ func TestOperatorSourceInfo_String(t *testing.T) { } func TestNewOperatorFromBundle(t *testing.T) { + version := opver.OperatorVersion{semver.MustParse("0.1.0-abc")} csv := v1alpha1.ClusterServiceVersion{ TypeMeta: metav1.TypeMeta{ Kind: v1alpha1.ClusterServiceVersionKind, @@ -903,6 +906,7 @@ func TestNewOperatorFromBundle(t *testing.T) { Owned: []v1alpha1.APIServiceDescription{}, Required: []v1alpha1.APIServiceDescription{}, }, + Version: version, }, } csvUnst, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&csv) @@ -993,6 +997,7 @@ func TestNewOperatorFromBundle(t *testing.T) { }, want: &Operator{ name: "testCSV", + version: &version.Version, providedAPIs: EmptyAPISet(), requiredAPIs: EmptyAPISet(), bundle: bundleNoAPIs, @@ -1010,7 +1015,8 @@ func TestNewOperatorFromBundle(t *testing.T) { sourceKey: CatalogKey{Name: "source", Namespace: "testNamespace"}, }, want: &Operator{ - name: "testCSV", + name: "testCSV", + version: &version.Version, providedAPIs: APISet{ opregistry.APIKey{ Group: "crd.group.com", @@ -1058,6 +1064,7 @@ func TestNewOperatorFromBundle(t *testing.T) { } func TestNewOperatorFromCSV(t *testing.T) { + version := opver.OperatorVersion{semver.MustParse("0.1.0-abc")} type args struct { csv *v1alpha1.ClusterServiceVersion } @@ -1074,6 +1081,9 @@ func TestNewOperatorFromCSV(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "operator.v1", }, + Spec: v1alpha1.ClusterServiceVersionSpec{ + Version: version, + }, }, }, want: &Operator{ @@ -1081,6 +1091,7 @@ func TestNewOperatorFromCSV(t *testing.T) { providedAPIs: EmptyAPISet(), requiredAPIs: EmptyAPISet(), sourceInfo: &ExistingOperator, + version: &version.Version, }, }, { @@ -1091,6 +1102,7 @@ func TestNewOperatorFromCSV(t *testing.T) { Name: "operator.v1", }, Spec: v1alpha1.ClusterServiceVersionSpec{ + Version: version, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ { @@ -1121,6 +1133,7 @@ func TestNewOperatorFromCSV(t *testing.T) { }, requiredAPIs: EmptyAPISet(), sourceInfo: &ExistingOperator, + version: &version.Version, }, }, { @@ -1131,6 +1144,7 @@ func TestNewOperatorFromCSV(t *testing.T) { Name: "operator.v1", }, Spec: v1alpha1.ClusterServiceVersionSpec{ + Version: version, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Required: []v1alpha1.CRDDescription{ { @@ -1161,6 +1175,7 @@ func TestNewOperatorFromCSV(t *testing.T) { {Group: "g", Version: "v1", Kind: "CRDKind", Plural: "crdkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &version.Version, }, }, { @@ -1171,6 +1186,7 @@ func TestNewOperatorFromCSV(t *testing.T) { Name: "operator.v1", }, Spec: v1alpha1.ClusterServiceVersionSpec{ + Version: version, CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ Owned: []v1alpha1.CRDDescription{ { @@ -1219,12 +1235,13 @@ func TestNewOperatorFromCSV(t *testing.T) { {Group: "g2", Version: "v1", Kind: "CRDReqKind", Plural: "crdreqkinds"}: {}, }, sourceInfo: &ExistingOperator, + version: &version.Version, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := NewOperatorFromCSV(tt.args.csv) + got, err := NewOperatorFromV1Alpha1CSV(tt.args.csv) require.Equal(t, tt.wantErr, err) require.Equal(t, tt.want, got) }) diff --git a/pkg/controller/registry/resolver/querier.go b/pkg/controller/registry/resolver/querier.go index 26b924ad7b..ff8c64b20e 100644 --- a/pkg/controller/registry/resolver/querier.go +++ b/pkg/controller/registry/resolver/querier.go @@ -5,11 +5,15 @@ import ( "context" "fmt" + "github.com/blang/semver" "github.com/operator-framework/operator-registry/pkg/client" opregistry "github.com/operator-framework/operator-registry/pkg/registry" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/errors" ) +const SkipPackageAnnotationKey = "olm.skipRange" + type SourceRef struct { Address string Client client.Interface @@ -21,7 +25,7 @@ type SourceQuerier interface { FindProvider(api opregistry.APIKey) (*opregistry.Bundle, *CatalogKey, error) FindBundle(pkgName, channelName, bundleName string, initialSource CatalogKey) (*opregistry.Bundle, *CatalogKey, error) FindLatestBundle(pkgName, channelName string, initialSource CatalogKey) (*opregistry.Bundle, *CatalogKey, error) - FindReplacement(bundleName, pkgName, channelName string, initialSource CatalogKey) (*opregistry.Bundle, *CatalogKey, error) + FindReplacement(currentVersion *semver.Version, bundleName, pkgName, channelName string, initialSource CatalogKey) (*opregistry.Bundle, *CatalogKey, error) Queryable() error } @@ -102,24 +106,84 @@ func (q *NamespaceSourceQuerier) FindLatestBundle(pkgName, channelName string, i return nil, nil, fmt.Errorf("%s/%s not found in any available CatalogSource", pkgName, channelName) } -func (q *NamespaceSourceQuerier) FindReplacement(bundleName, pkgName, channelName string, initialSource CatalogKey) (*opregistry.Bundle, *CatalogKey, error) { +func (q *NamespaceSourceQuerier) FindReplacement(currentVersion *semver.Version, bundleName, pkgName, channelName string, initialSource CatalogKey) (*opregistry.Bundle, *CatalogKey, error) { + errs := []error{} + if initialSource.Name != "" && initialSource.Namespace != "" { source, ok := q.sources[initialSource] if !ok { return nil, nil, fmt.Errorf("CatalogSource %s not found", initialSource.Name) } - bundle, err := source.GetReplacementBundleInPackageChannel(context.TODO(), bundleName, pkgName, channelName) + + bundle, err := q.findChannelHead(currentVersion, pkgName, channelName, source) + if bundle != nil { + return bundle, &initialSource, nil + } if err != nil { - return nil, nil, err + errs = append(errs, err) } - return bundle, &initialSource, nil + + bundle, err = source.GetReplacementBundleInPackageChannel(context.TODO(), bundleName, pkgName, channelName) + if bundle != nil { + return bundle, &initialSource, nil + } + if err != nil { + errs = append(errs, err) + } + + return nil, nil, errors.NewAggregate(errs) } for key, source := range q.sources { - bundle, err := source.GetReplacementBundleInPackageChannel(context.TODO(), bundleName, pkgName, channelName) - if err == nil { + bundle, err := q.findChannelHead(currentVersion, pkgName, channelName, source) + if bundle != nil { + return bundle, &initialSource, nil + } + if err != nil { + errs = append(errs, err) + } + + bundle, err = source.GetReplacementBundleInPackageChannel(context.TODO(), bundleName, pkgName, channelName) + if bundle != nil { return bundle, &key, nil } + if err != nil { + errs = append(errs, err) + } } - return nil, nil, fmt.Errorf("%s/%s not found in any available CatalogSource", pkgName, channelName) + return nil, nil, errors.NewAggregate(errs) +} + +func (q *NamespaceSourceQuerier) findChannelHead(currentVersion *semver.Version, pkgName, channelName string, source client.Interface) (*opregistry.Bundle, error) { + if currentVersion == nil { + return nil, nil + } + + latest, err := source.GetBundleInPackageChannel(context.TODO(), pkgName, channelName) + if err != nil { + return nil, err + } + + csv, err := latest.ClusterServiceVersion() + if err != nil { + return nil, err + } + if csv == nil { + return nil, nil + } + + skipRange, ok := csv.GetAnnotations()[SkipPackageAnnotationKey] + if !ok { + return nil, nil + } + + r, err := semver.ParseRange(skipRange) + if err != nil { + return nil, err + } + + if r(*currentVersion) { + return latest, nil + } + return nil, nil } diff --git a/pkg/controller/registry/resolver/querier_test.go b/pkg/controller/registry/resolver/querier_test.go index 8d14a7fd99..be1548da00 100644 --- a/pkg/controller/registry/resolver/querier_test.go +++ b/pkg/controller/registry/resolver/querier_test.go @@ -5,11 +5,17 @@ import ( "fmt" "testing" + "github.com/blang/semver" "github.com/operator-framework/operator-registry/pkg/client" opregistry "github.com/operator-framework/operator-registry/pkg/registry" "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/fakes" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" ) func TestNewNamespaceSourceQuerier(t *testing.T) { @@ -273,3 +279,178 @@ func TestNamespaceSourceQuerier_FindPackage(t *testing.T) { }) } } + +func TestNamespaceSourceQuerier_FindReplacement(t *testing.T) { + // TODO: clean up this test setup + initialSource := fakes.FakeInterface{} + otherSource := fakes.FakeInterface{} + replacementSource := fakes.FakeInterface{} + replacementAndLatestSource := fakes.FakeInterface{} + replacementAndNoAnnotationLatestSource := fakes.FakeInterface{} + + latestVersion := semver.MustParse("1.0.0-1556661308") + csv := v1alpha1.ClusterServiceVersion{ + TypeMeta: metav1.TypeMeta{ + Kind: v1alpha1.ClusterServiceVersionKind, + APIVersion: v1alpha1.GroupVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "latest", + Namespace: "placeholder", + Annotations: map[string]string{ + "olm.skipRange": ">= 1.0.0-0 < 1.0.0-1556661308", + }, + }, + Spec: v1alpha1.ClusterServiceVersionSpec{ + CustomResourceDefinitions: v1alpha1.CustomResourceDefinitions{ + Owned: []v1alpha1.CRDDescription{}, + Required: []v1alpha1.CRDDescription{}, + }, + APIServiceDefinitions: v1alpha1.APIServiceDefinitions{ + Owned: []v1alpha1.APIServiceDescription{}, + Required: []v1alpha1.APIServiceDescription{}, + }, + Version: version.OperatorVersion{latestVersion}, + }, + } + csvUnst, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&csv) + require.NoError(t, err) + + nextBundle := opregistry.NewBundle("test.v1", "testPkg", "testChannel") + latestBundle := opregistry.NewBundle("latest", "testPkg", "testChannel", &unstructured.Unstructured{Object: csvUnst}) + + csv.SetAnnotations(map[string]string{}) + csvUnstNoAnnotation, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&csv) + require.NoError(t, err) + latestBundleNoAnnotation := opregistry.NewBundle("latest", "testPkg", "testChannel", &unstructured.Unstructured{Object: csvUnstNoAnnotation}) + + initialSource.GetReplacementBundleInPackageChannelStub = func(ctx context.Context, bundleName, pkgName, channelName string) (*opregistry.Bundle, error) { + return nil, fmt.Errorf("not found") + } + replacementSource.GetReplacementBundleInPackageChannelStub = func(ctx context.Context, bundleName, pkgName, channelName string) (*opregistry.Bundle, error) { + return nextBundle, nil + } + initialSource.GetBundleInPackageChannelStub = func(ctx context.Context, pkgName, channelName string) (*opregistry.Bundle, error) { + if pkgName != latestBundle.Package { + return nil, fmt.Errorf("not found") + } + return latestBundle, nil + } + otherSource.GetBundleInPackageChannelStub = func(ctx context.Context, pkgName, channelName string) (*opregistry.Bundle, error) { + if pkgName != latestBundle.Package { + return nil, fmt.Errorf("not found") + } + return latestBundle, nil + } + replacementAndLatestSource.GetReplacementBundleInPackageChannelStub = func(ctx context.Context, bundleName, pkgName, channelName string) (*opregistry.Bundle, error) { + return nextBundle, nil + } + replacementAndLatestSource.GetBundleInPackageChannelStub = func(ctx context.Context, pkgName, channelName string) (*opregistry.Bundle, error) { + return latestBundle, nil + } + replacementAndNoAnnotationLatestSource.GetReplacementBundleInPackageChannelStub = func(ctx context.Context, bundleName, pkgName, channelName string) (*opregistry.Bundle, error) { + return nextBundle, nil + } + replacementAndNoAnnotationLatestSource.GetBundleInPackageChannelStub = func(ctx context.Context, pkgName, channelName string) (*opregistry.Bundle, error) { + return latestBundleNoAnnotation, nil + } + + initialKey := CatalogKey{"initial", "ns"} + otherKey := CatalogKey{"other", "other"} + replacementKey := CatalogKey{"replacement", "ns"} + replacementAndLatestKey := CatalogKey{"replat", "ns"} + replacementAndNoAnnotationLatestKey := CatalogKey{"replatbad", "ns"} + + sources := map[CatalogKey]client.Interface{ + initialKey: &initialSource, + otherKey: &otherSource, + replacementKey: &replacementSource, + replacementAndLatestKey: &replacementAndLatestSource, + replacementAndNoAnnotationLatestKey: &replacementAndNoAnnotationLatestSource, + } + + startVersion := semver.MustParse("1.0.0-0") + notInRange := semver.MustParse("1.0.0-1556661347") + + type fields struct { + sources map[CatalogKey]client.Interface + } + type args struct { + currentVersion *semver.Version + pkgName string + channelName string + bundleName string + initialSource CatalogKey + } + type out struct { + bundle *opregistry.Bundle + key *CatalogKey + err error + } + tests := []struct { + name string + fields fields + args args + out out + }{ + { + name: "FindsLatestInPrimaryCatalog", + fields: fields{sources: sources}, + args: args{&startVersion, "testPkg", "testChannel", "test.v1", initialKey}, + out: out{bundle: latestBundle, key: &initialKey, err: nil}, + }, + { + name: "FindsLatestInSecondaryCatalog", + fields: fields{sources: sources}, + args: args{&startVersion, "testPkg", "testChannel", "test.v1", otherKey}, + out: out{bundle: latestBundle, key: &otherKey, err: nil}, + }, + { + name: "PrefersLatestToReplaced/SameCatalog", + fields: fields{sources: sources}, + args: args{&startVersion, "testPkg", "testChannel", "test.v1", replacementAndLatestKey}, + out: out{bundle: latestBundle, key: &replacementAndLatestKey, err: nil}, + }, + { + name: "PrefersLatestToReplaced/OtherCatalog", + fields: fields{sources: sources}, + args: args{&startVersion, "testPkg", "testChannel", "test.v1", initialKey}, + out: out{bundle: latestBundle, key: &initialKey, err: nil}, + }, + { + name: "IgnoresLatestWithoutAnnotation", + fields: fields{sources: sources}, + args: args{&startVersion, "testPkg", "testChannel", "test.v1", replacementAndNoAnnotationLatestKey}, + out: out{bundle: nextBundle, key: &replacementAndNoAnnotationLatestKey, err: nil}, + }, + { + name: "IgnoresLatestNotInRange", + fields: fields{sources: sources}, + args: args{¬InRange, "testPkg", "testChannel", "test.v1", replacementAndLatestKey}, + out: out{bundle: nextBundle, key: &replacementAndLatestKey, err: nil}, + }, + { + name: "IgnoresLatestAtLatest", + fields: fields{sources: sources}, + args: args{&latestVersion, "testPkg", "testChannel", "test.v1", otherKey}, + out: out{bundle: nil, key: nil, err: nil}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + q := &NamespaceSourceQuerier{ + sources: tt.fields.sources, + } + var got *opregistry.Bundle + var key *CatalogKey + var err error + got, key, err = q.FindReplacement(tt.args.currentVersion, tt.args.bundleName, tt.args.pkgName, tt.args.channelName, tt.args.initialSource) + if err != nil { + t.Log(err.Error()) + } + require.Equal(t, tt.out.err, err) + require.Equal(t, tt.out.bundle, got) + require.Equal(t, tt.out.key, key) + }) + } +} diff --git a/pkg/controller/registry/resolver/util_test.go b/pkg/controller/registry/resolver/util_test.go index 094fb10af4..f7080c8bac 100644 --- a/pkg/controller/registry/resolver/util_test.go +++ b/pkg/controller/registry/resolver/util_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/blang/semver" "github.com/operator-framework/operator-registry/pkg/client" opregistry "github.com/operator-framework/operator-registry/pkg/registry" "github.com/stretchr/testify/require" @@ -51,6 +52,7 @@ func NewFakeOperatorSurface(name, pkg, channel, replaces, src, startingCSV strin requiredCRDAPISet := EmptyAPISet() providedAPIServiceAPISet := EmptyAPISet() requiredAPIServiceAPISet := EmptyAPISet() + version := semver.MustParse("0.0.0") for _, p := range providedCRDs { providedCRDAPISet[p] = struct{}{} @@ -72,11 +74,13 @@ func NewFakeOperatorSurface(name, pkg, channel, replaces, src, startingCSV strin // force bundle cache to fill _, _ = b.ClusterServiceVersion() _, _ = b.CustomResourceDefinitions() + return &Operator{ providedAPIs: providedAPISet, requiredAPIs: requiredAPISet, name: name, replaces: replaces, + version: &version, sourceInfo: &OperatorSourceInfo{ Package: pkg, Channel: channel, @@ -207,6 +211,7 @@ func crd(key opregistry.APIKey) *v1beta1.CustomResourceDefinition { } func u(object runtime.Object) *unstructured.Unstructured { + fmt.Println(object) unst, err := runtime.DefaultUnstructuredConverter.ToUnstructured(object) if err != nil { panic(err) diff --git a/pkg/lib/version/version.go b/pkg/lib/version/version.go new file mode 100644 index 0000000000..51e1fac659 --- /dev/null +++ b/pkg/lib/version/version.go @@ -0,0 +1,66 @@ +package version + +import ( + "encoding/json" + + "github.com/blang/semver" +) + +// +k8s:openapi-gen=true +// OperatorVersion is a wrapper around semver.Version which supports correct +// marshaling to YAML and JSON. +type OperatorVersion struct { + semver.Version +} + +// DeepCopyInto creates a deep-copy of the Version value. +func (v *OperatorVersion) DeepCopyInto(out *OperatorVersion) { + out.Major = v.Major + out.Minor = v.Minor + out.Patch = v.Patch + + if v.Pre != nil { + pre := make([]semver.PRVersion, len(v.Pre)) + copy(pre, v.Pre) + out.Pre = pre + } + + if v.Build != nil { + build := make([]string, len(v.Build)) + copy(build, v.Build) + out.Build = build + } +} + +// MarshalJSON implements the encoding/json.Marshaler interface. +func (v OperatorVersion) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +// UnmarshalJSON implements the encoding/json.Unmarshaler interface. +func (v *OperatorVersion) UnmarshalJSON(data []byte) (err error) { + var versionString string + + if err = json.Unmarshal(data, &versionString); err != nil { + return + } + + version := semver.Version{} + version, err = semver.ParseTolerant(versionString) + if err != nil { + return err + } + v.Version = version + return +} + +// OpenAPISchemaType is used by the kube-openapi generator when constructing +// the OpenAPI spec of this type. +// +// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators +func (_ OperatorVersion) OpenAPISchemaType() []string { return []string{"string"} } + +// OpenAPISchemaFormat is used by the kube-openapi generator when constructing +// the OpenAPI spec of this type. +// "semver" is not a standard openapi format but tooling may use the value regardless +func (_ OperatorVersion) OpenAPISchemaFormat() string { return "semver" } diff --git a/pkg/lib/version/version_test.go b/pkg/lib/version/version_test.go new file mode 100644 index 0000000000..e6c14f7b48 --- /dev/null +++ b/pkg/lib/version/version_test.go @@ -0,0 +1,77 @@ +package version + +import ( + "encoding/json" + "testing" + + "github.com/blang/semver" + "github.com/stretchr/testify/require" +) + +func TestOperatorVersionMarshal(t *testing.T) { + tests := []struct { + name string + in OperatorVersion + out []byte + err error + }{ + { + name: "MMP", + in: OperatorVersion{semver.MustParse("1.2.3")}, + out: []byte(`"1.2.3"`), + }, + { + name: "empty", + in: OperatorVersion{semver.Version{}}, + out: []byte(`"0.0.0"`), + }, + { + name: "with-timestamp", + in: OperatorVersion{semver.MustParse("1.2.3-1556715351")}, + out: []byte(`"1.2.3-1556715351"`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m, err := tt.in.MarshalJSON() + require.Equal(t, tt.out, m, string(m)) + require.Equal(t, tt.err, err) + }) + } +} + +func TestOperatorVersionUnmarshal(t *testing.T) { + type TestStruct struct { + Version OperatorVersion `json:"v"` + } + tests := []struct { + name string + in []byte + out TestStruct + err error + }{ + { + name: "MMP", + in: []byte(`{"v": "1.2.3"}`), + out: TestStruct{Version: OperatorVersion{semver.MustParse("1.2.3")}}, + }, + { + name: "empty", + in: []byte(`{"v": "0.0.0"}`), + out: TestStruct{Version: OperatorVersion{semver.Version{Major: 0, Minor: 0, Patch: 0}}}, + }, + { + name: "with-timestamp", + in: []byte(`{"v": "1.2.3-1556715351"}`), + out: TestStruct{OperatorVersion{semver.MustParse("1.2.3-1556715351")}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := TestStruct{} + err := json.Unmarshal(tt.in, &s) + require.Equal(t, tt.out, s) + require.Equal(t, tt.err, err) + }) + } +} diff --git a/pkg/package-server/apis/apps/v1alpha1/packagemanifest_types.go b/pkg/package-server/apis/apps/v1alpha1/packagemanifest_types.go index ee92f75963..0a072c02a4 100644 --- a/pkg/package-server/apis/apps/v1alpha1/packagemanifest_types.go +++ b/pkg/package-server/apis/apps/v1alpha1/packagemanifest_types.go @@ -1,10 +1,10 @@ package v1alpha1 import ( - "github.com/coreos/go-semver/semver" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" operatorv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" ) // PackageManifestList is a list of PackageManifest objects. @@ -91,8 +91,7 @@ type CSVDescription struct { Icon []Icon `json:"icon,omitempty"` // Version is the CSV's semantic version - // +k8s:openapi-gen=false - Version semver.Version `json:"version,omitempty"` + Version version.OperatorVersion `json:"version,omitempty"` // Provider is the CSV's provider Provider AppLink `json:"provider,omitempty"` diff --git a/pkg/package-server/apis/apps/v1alpha1/zz_generated.deepcopy.go b/pkg/package-server/apis/apps/v1alpha1/zz_generated.deepcopy.go index 72f3a59f85..d02eb3e7c8 100644 --- a/pkg/package-server/apis/apps/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/package-server/apis/apps/v1alpha1/zz_generated.deepcopy.go @@ -49,7 +49,7 @@ func (in *CSVDescription) DeepCopyInto(out *CSVDescription) { *out = make([]Icon, len(*in)) copy(*out, *in) } - out.Version = in.Version + in.Version.DeepCopyInto(&out.Version) out.Provider = in.Provider if in.Annotations != nil { in, out := &in.Annotations, &out.Annotations diff --git a/pkg/package-server/apis/openapi/zz_generated.openapi.go b/pkg/package-server/apis/openapi/zz_generated.openapi.go index a2ef5f6718..68c430ab40 100644 --- a/pkg/package-server/apis/openapi/zz_generated.openapi.go +++ b/pkg/package-server/apis/openapi/zz_generated.openapi.go @@ -24,6 +24,7 @@ package openapi import ( spec "github.com/go-openapi/spec" + version "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" common "k8s.io/kube-openapi/pkg/common" ) @@ -31,6 +32,7 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1.InstallMode": schema_api_apis_operators_v1alpha1_InstallMode(ref), + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version.OperatorVersion": schema_operator_lifecycle_manager_pkg_lib_version_OperatorVersion(ref), "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/apps/v1alpha1.AppLink": schema_package_server_apis_apps_v1alpha1_AppLink(ref), "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/apps/v1alpha1.CSVDescription": schema_package_server_apis_apps_v1alpha1_CSVDescription(ref), "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/apps/v1alpha1.Icon": schema_package_server_apis_apps_v1alpha1_Icon(ref), @@ -119,6 +121,18 @@ func schema_api_apis_operators_v1alpha1_InstallMode(ref common.ReferenceCallback } } +func schema_operator_lifecycle_manager_pkg_lib_version_OperatorVersion(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "OperatorVersion is a wrapper around semver.Version which supports correct marshaling to YAML and JSON.", + Type: version.OperatorVersion{}.OpenAPISchemaType(), + Format: version.OperatorVersion{}.OpenAPISchemaFormat(), + }, + }, + } +} + func schema_package_server_apis_apps_v1alpha1_AppLink(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -170,6 +184,12 @@ func schema_package_server_apis_apps_v1alpha1_CSVDescription(ref common.Referenc }, }, }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "Version is the CSV's semantic version", + Ref: ref("github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version.OperatorVersion"), + }, + }, "provider": { SchemaProps: spec.SchemaProps{ Description: "Provider is the CSV's provider", @@ -213,7 +233,7 @@ func schema_package_server_apis_apps_v1alpha1_CSVDescription(ref common.Referenc }, }, Dependencies: []string{ - "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1.InstallMode", "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/apps/v1alpha1.AppLink", "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/apps/v1alpha1.Icon"}, + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1.InstallMode", "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version.OperatorVersion", "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/apps/v1alpha1.AppLink", "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/apps/v1alpha1.Icon"}, } } @@ -503,6 +523,12 @@ func schema_package_server_apis_operators_v1_CSVDescription(ref common.Reference }, }, }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "Version is the CSV's semantic version", + Ref: ref("github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version.OperatorVersion"), + }, + }, "provider": { SchemaProps: spec.SchemaProps{ Description: "Provider is the CSV's provider", @@ -546,7 +572,7 @@ func schema_package_server_apis_operators_v1_CSVDescription(ref common.Reference }, }, Dependencies: []string{ - "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1.InstallMode", "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.AppLink", "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.Icon"}, + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1.InstallMode", "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version.OperatorVersion", "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.AppLink", "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1.Icon"}, } } diff --git a/pkg/package-server/apis/operators/packagemanifest_types.go b/pkg/package-server/apis/operators/packagemanifest_types.go index aec1ef3745..38df55e790 100644 --- a/pkg/package-server/apis/operators/packagemanifest_types.go +++ b/pkg/package-server/apis/operators/packagemanifest_types.go @@ -1,10 +1,10 @@ package operators import ( - "github.com/coreos/go-semver/semver" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" operatorv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" ) // PackageManifestList is a list of PackageManifest objects. @@ -91,8 +91,7 @@ type CSVDescription struct { Icon []Icon // Version is the CSV's semantic version - // +k8s:openapi-gen=false - Version semver.Version + Version version.OperatorVersion // Provider is the CSV's provider Provider AppLink diff --git a/pkg/package-server/apis/operators/v1/packagemanifest_types.go b/pkg/package-server/apis/operators/v1/packagemanifest_types.go index 03ce64d486..4a910e695c 100644 --- a/pkg/package-server/apis/operators/v1/packagemanifest_types.go +++ b/pkg/package-server/apis/operators/v1/packagemanifest_types.go @@ -1,10 +1,10 @@ package v1 import ( - "github.com/coreos/go-semver/semver" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" operatorv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" ) // PackageManifestList is a list of PackageManifest objects. @@ -91,8 +91,7 @@ type CSVDescription struct { Icon []Icon `json:"icon,omitempty"` // Version is the CSV's semantic version - // +k8s:openapi-gen=false - Version semver.Version `json:"version,omitempty"` + Version version.OperatorVersion `json:"version,omitempty"` // Provider is the CSV's provider Provider AppLink `json:"provider,omitempty"` diff --git a/pkg/package-server/apis/operators/v1/zz_generated.deepcopy.go b/pkg/package-server/apis/operators/v1/zz_generated.deepcopy.go index 0c4534473b..3273ee1637 100644 --- a/pkg/package-server/apis/operators/v1/zz_generated.deepcopy.go +++ b/pkg/package-server/apis/operators/v1/zz_generated.deepcopy.go @@ -49,7 +49,7 @@ func (in *CSVDescription) DeepCopyInto(out *CSVDescription) { *out = make([]Icon, len(*in)) copy(*out, *in) } - out.Version = in.Version + in.Version.DeepCopyInto(&out.Version) out.Provider = in.Provider if in.Annotations != nil { in, out := &in.Annotations, &out.Annotations diff --git a/pkg/package-server/apis/operators/zz_generated.deepcopy.go b/pkg/package-server/apis/operators/zz_generated.deepcopy.go index 719847ddeb..350acceb53 100644 --- a/pkg/package-server/apis/operators/zz_generated.deepcopy.go +++ b/pkg/package-server/apis/operators/zz_generated.deepcopy.go @@ -49,7 +49,7 @@ func (in *CSVDescription) DeepCopyInto(out *CSVDescription) { *out = make([]Icon, len(*in)) copy(*out, *in) } - out.Version = in.Version + in.Version.DeepCopyInto(&out.Version) out.Provider = in.Provider if in.Annotations != nil { in, out := &in.Annotations, &out.Annotations diff --git a/pkg/version/version.go b/pkg/version/version.go index 190b80f438..3fe30acafb 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -13,7 +13,7 @@ func String() string { return fmt.Sprintf("OLM version: %s\ngit commit: %s\n", OLMVersion, GitCommit) } -// Full returns a hypenated concatenation of just OLMVersion and GitCommit +// Full returns a hyphenated concatenation of just OLMVersion and GitCommit func Full() string { return fmt.Sprintf("%s-%s", OLMVersion, GitCommit) } diff --git a/test/e2e/catalog_e2e_test.go b/test/e2e/catalog_e2e_test.go index 47d6b6bb49..42c83439fc 100644 --- a/test/e2e/catalog_e2e_test.go +++ b/test/e2e/catalog_e2e_test.go @@ -6,7 +6,7 @@ import ( "fmt" "testing" - "github.com/coreos/go-semver/semver" + "github.com/blang/semver" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -43,7 +43,7 @@ func TestCatalogLoadingBetweenRestarts(t *testing.T) { crdPlural := genName("ins") crd := newCRD(crdPlural) namedStrategy := newNginxInstallStrategy(genName("dep-"), nil, nil) - csv := newCSV(packageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + csv := newCSV(packageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) c := newKubeClient(t) crc := newCRClient(t) @@ -117,8 +117,8 @@ func TestConfigMapUpdateTriggersRegistryPodRollout(t *testing.T) { crdPlural := genName("ins-") dependentCRD := newCRD(crdPlural) - mainCSV := newCSV(mainPackageStable, testNamespace, "", *semver.New("0.1.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) - dependentCSV := newCSV(dependentPackageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) + mainCSV := newCSV(mainPackageStable, testNamespace, "", semver.MustParse("0.1.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) + dependentCSV := newCSV(dependentPackageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) c := newKubeClient(t) crc := newCRClient(t) @@ -234,8 +234,8 @@ func TestConfigMapReplaceTriggersRegistryPodRollout(t *testing.T) { crdPlural := genName("ins-") dependentCRD := newCRD(crdPlural) - mainCSV := newCSV(mainPackageStable, testNamespace, "", *semver.New("0.1.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) - dependentCSV := newCSV(dependentPackageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) + mainCSV := newCSV(mainPackageStable, testNamespace, "", semver.MustParse("0.1.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) + dependentCSV := newCSV(dependentPackageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) c := newKubeClient(t) crc := newCRClient(t) @@ -325,9 +325,9 @@ func TestGrpcAddressCatalogSource(t *testing.T) { crdPlural := genName("ins-") dependentCRD := newCRD(crdPlural) - mainCSV := newCSV(mainPackageStable, testNamespace, "", *semver.New("0.1.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) - replacementCSV := newCSV(mainPackageReplacement, testNamespace, mainPackageStable, *semver.New("0.2.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) - dependentCSV := newCSV(dependentPackageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) + mainCSV := newCSV(mainPackageStable, testNamespace, "", semver.MustParse("0.1.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) + replacementCSV := newCSV(mainPackageReplacement, testNamespace, mainPackageStable, semver.MustParse("0.2.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) + dependentCSV := newCSV(dependentPackageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) c := newKubeClient(t) crc := newCRClient(t) @@ -454,7 +454,7 @@ func TestDeleteRegistryPodTriggersRecreation(t *testing.T) { namedStrategy := newNginxInstallStrategy(genName("dep-"), nil, nil) catalogName := genName("catsrc-") crd := newCRD(genName("ins-")) - csv := newCSV(packageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + csv := newCSV(packageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) manifests := []registry.PackageManifest{ { PackageName: packageName, diff --git a/test/e2e/gc_e2e_test.go b/test/e2e/gc_e2e_test.go index 3c17569e5b..afd0fe893c 100644 --- a/test/e2e/gc_e2e_test.go +++ b/test/e2e/gc_e2e_test.go @@ -3,7 +3,7 @@ package e2e import ( "testing" - "github.com/coreos/go-semver/semver" + "github.com/blang/semver" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -22,8 +22,8 @@ import ( func TestOwnerReferenceGCBehavior(t *testing.T) { defer cleaner.NotifyTestComplete(t, true) - ownerA := newCSV("ownera", testNamespace, "", *semver.New("0.0.0"), nil, nil, newNginxInstallStrategy("dep-", nil, nil)) - ownerB := newCSV("ownerb", testNamespace, "", *semver.New("0.0.0"), nil, nil, newNginxInstallStrategy("dep-", nil, nil)) + ownerA := newCSV("ownera", testNamespace, "", semver.MustParse("0.0.0"), nil, nil, newNginxInstallStrategy("dep-", nil, nil)) + ownerB := newCSV("ownerb", testNamespace, "", semver.MustParse("0.0.0"), nil, nil, newNginxInstallStrategy("dep-", nil, nil)) // create all owners c := newKubeClient(t) diff --git a/test/e2e/installplan_e2e_test.go b/test/e2e/installplan_e2e_test.go index 8fd180b76a..bcd1e59a2a 100644 --- a/test/e2e/installplan_e2e_test.go +++ b/test/e2e/installplan_e2e_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/coreos/go-semver/semver" + "github.com/blang/semver" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -21,6 +21,7 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" + opver "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" ) type checkInstallPlanFunc func(fip *v1alpha1.InstallPlan) bool @@ -150,7 +151,7 @@ func newCSV(name, namespace, replaces string, version semver.Version, owned []ap }, Spec: v1alpha1.ClusterServiceVersionSpec{ Replaces: replaces, - Version: version, + Version: opver.OperatorVersion{version}, MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { @@ -221,8 +222,8 @@ func TestInstallPlanWithCSVsAcrossMultipleCatalogSources(t *testing.T) { crdPlural := genName("ins-") dependentCRD := newCRD(crdPlural) - mainCSV := newCSV(mainPackageStable, testNamespace, "", *semver.New("0.1.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) - dependentCSV := newCSV(dependentPackageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) + mainCSV := newCSV(mainPackageStable, testNamespace, "", semver.MustParse("0.1.0"), nil, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) + dependentCSV := newCSV(dependentPackageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) c := newKubeClient(t) crc := newCRClient(t) @@ -374,10 +375,10 @@ func TestCreateInstallPlanWithPreExistingCRDOwners(t *testing.T) { dependentCRD := newCRD(dependentCRDPlural) // Create new CSVs - mainStableCSV := newCSV(mainPackageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{mainCRD}, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) - mainBetaCSV := newCSV(mainPackageBeta, testNamespace, mainPackageStable, *semver.New("0.2.0"), []apiextensions.CustomResourceDefinition{mainCRD}, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) - dependentStableCSV := newCSV(dependentPackageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) - dependentBetaCSV := newCSV(dependentPackageBeta, testNamespace, dependentPackageStable, *semver.New("0.2.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) + mainStableCSV := newCSV(mainPackageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{mainCRD}, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) + mainBetaCSV := newCSV(mainPackageBeta, testNamespace, mainPackageStable, semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{mainCRD}, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) + dependentStableCSV := newCSV(dependentPackageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) + dependentBetaCSV := newCSV(dependentPackageBeta, testNamespace, dependentPackageStable, semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) c := newKubeClient(t) crc := newCRClient(t) @@ -499,10 +500,10 @@ func TestCreateInstallPlanWithPreExistingCRDOwners(t *testing.T) { dependentCRD := newCRD(dependentCRDPlural) // Create new CSVs - mainStableCSV := newCSV(mainPackageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{mainCRD}, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) - mainBetaCSV := newCSV(mainPackageBeta, testNamespace, mainPackageStable, *semver.New("0.2.0"), []apiextensions.CustomResourceDefinition{mainCRD}, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) - dependentStableCSV := newCSV(dependentPackageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) - dependentBetaCSV := newCSV(dependentPackageBeta, testNamespace, dependentPackageStable, *semver.New("0.2.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) + mainStableCSV := newCSV(mainPackageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{mainCRD}, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) + mainBetaCSV := newCSV(mainPackageBeta, testNamespace, mainPackageStable, semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{mainCRD}, []apiextensions.CustomResourceDefinition{dependentCRD}, mainNamedStrategy) + dependentStableCSV := newCSV(dependentPackageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) + dependentBetaCSV := newCSV(dependentPackageBeta, testNamespace, dependentPackageStable, semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{dependentCRD}, nil, dependentNamedStrategy) c := newKubeClient(t) crc := newCRClient(t) @@ -654,7 +655,7 @@ func TestCreateInstallPlanWithPermissions(t *testing.T) { namedStrategy := newNginxInstallStrategy(genName("dep-"), permissions, clusterPermissions) // Create new CSVs - stableCSV := newCSV(stableCSVName, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + stableCSV := newCSV(stableCSVName, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) c := newKubeClient(t) crc := newCRClient(t) @@ -796,7 +797,7 @@ func TestInstallPlanCRDValidation(t *testing.T) { stableChannel := "stable" packageNameStable := packageName + "-" + stableChannel namedStrategy := newNginxInstallStrategy(genName("dep-"), nil, nil) - csv := newCSV(packageNameStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + csv := newCSV(packageNameStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) // Create PackageManifests manifests := []registry.PackageManifest{ @@ -828,7 +829,7 @@ func TestInstallPlanCRDValidation(t *testing.T) { require.NoError(t, err) require.NotNil(t, subscription) - installPlanName := subscription.Status.Install.Name + installPlanName := subscription.Status.InstallPlanRef.Name // Wait for InstallPlan to be status: Complete before checking resource presence fetchedInstallPlan, err := fetchInstallPlan(t, crc, installPlanName, buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseComplete, v1alpha1.InstallPlanPhaseFailed)) diff --git a/test/e2e/operator_groups_e2e_test.go b/test/e2e/operator_groups_e2e_test.go index 2a468a29fa..7a14981653 100644 --- a/test/e2e/operator_groups_e2e_test.go +++ b/test/e2e/operator_groups_e2e_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/coreos/go-semver/semver" + "github.com/blang/semver" "github.com/stretchr/testify/require" authorizationv1 "k8s.io/api/authorization/v1" corev1 "k8s.io/api/core/v1" @@ -215,7 +215,7 @@ func TestOperatorGroup(t *testing.T) { deploymentName := genName("operator-deployment") namedStrategy := newNginxInstallStrategy(deploymentName, permissions, nil) - aCSV := newCSV(csvName, opGroupNamespace, "", *semver.New("0.0.0"), []apiextensions.CustomResourceDefinition{mainCRD}, nil, namedStrategy) + aCSV := newCSV(csvName, opGroupNamespace, "", semver.MustParse("0.0.0"), []apiextensions.CustomResourceDefinition{mainCRD}, nil, namedStrategy) createdCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Create(&aCSV) require.NoError(t, err) @@ -464,7 +464,7 @@ func TestOperatorGroupRoleAggregation(t *testing.T) { // Generate csvA in namespaceA with all installmodes supported crd := newCRD(genName("a")) namedStrategy := newNginxInstallStrategy(genName("dep-"), nil, nil) - csvA := newCSV("nginx-a", nsA, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + csvA := newCSV("nginx-a", nsA, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) _, err = crc.OperatorsV1alpha1().ClusterServiceVersions(nsA).Create(&csvA) require.NoError(t, err) defer func() { @@ -561,7 +561,7 @@ func TestOperatorGroupInstallModeSupport(t *testing.T) { // Generate csvA in namespaceA with no supported InstallModes crd := newCRD(genName("b")) namedStrategy := newNginxInstallStrategy(genName("dep-"), nil, nil) - csv := newCSV("nginx-a", nsA, "", *semver.New("0.1.0"), nil, []apiextensions.CustomResourceDefinition{crd}, namedStrategy) + csv := newCSV("nginx-a", nsA, "", semver.MustParse("0.1.0"), nil, []apiextensions.CustomResourceDefinition{crd}, namedStrategy) csvA := &csv csvA.Spec.InstallModes = []v1alpha1.InstallMode{ { @@ -787,9 +787,9 @@ func TestOperatorGroupIntersection(t *testing.T) { kvgA := fmt.Sprintf("%s.%s.%s", crdA.Spec.Names.Kind, crdA.Spec.Version, crdA.Spec.Group) kvgB := fmt.Sprintf("%s.%s.%s", crdB.Spec.Names.Kind, crdB.Spec.Version, crdB.Spec.Group) kvgD := fmt.Sprintf("%s.%s.%s", crdD.Spec.Names.Kind, crdD.Spec.Version, crdD.Spec.Group) - csvA := newCSV(pkgAStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crdA}, nil, strategyA) - csvB := newCSV(pkgBStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crdA, crdB}, nil, strategyB) - csvD := newCSV(pkgDStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crdD}, nil, strategyD) + csvA := newCSV(pkgAStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crdA}, nil, strategyA) + csvB := newCSV(pkgBStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crdA, crdB}, nil, strategyB) + csvD := newCSV(pkgDStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crdD}, nil, strategyD) // Create namespaces nsA, nsB, nsC, nsD, nsE := genName("a-"), genName("b-"), genName("c-"), genName("d-"), genName("e-") @@ -1044,8 +1044,8 @@ func TestStaticProviderOperatorGroup(t *testing.T) { crdB := newCRD(genName(pkgB)) kvgA := fmt.Sprintf("%s.%s.%s", crdA.Spec.Names.Kind, crdA.Spec.Version, crdA.Spec.Group) kvgB := fmt.Sprintf("%s.%s.%s", crdB.Spec.Names.Kind, crdB.Spec.Version, crdB.Spec.Group) - csvA := newCSV(pkgAStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crdA}, nil, strategyA) - csvB := newCSV(pkgBStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crdB}, nil, strategyB) + csvA := newCSV(pkgAStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crdA}, nil, strategyA) + csvB := newCSV(pkgBStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crdB}, nil, strategyB) // Create namespaces nsA, nsB, nsC, nsD := genName("a-"), genName("b-"), genName("c-"), genName("d-") @@ -1327,7 +1327,7 @@ func TestCSVCopyWatchingAllNamespaces(t *testing.T) { deploymentName := genName("operator-deployment") namedStrategy := newNginxInstallStrategy(deploymentName, permissions, nil) - aCSV := newCSV(csvName, opGroupNamespace, "", *semver.New("0.0.0"), []apiextensions.CustomResourceDefinition{mainCRD}, nil, namedStrategy) + aCSV := newCSV(csvName, opGroupNamespace, "", semver.MustParse("0.0.0"), []apiextensions.CustomResourceDefinition{mainCRD}, nil, namedStrategy) aCSV.Labels = map[string]string{"label": t.Name()} createdCSV, err := crc.OperatorsV1alpha1().ClusterServiceVersions(opGroupNamespace).Create(&aCSV) require.NoError(t, err) diff --git a/test/e2e/packagemanifest_e2e_test.go b/test/e2e/packagemanifest_e2e_test.go index 91570f7997..de6e659f0f 100644 --- a/test/e2e/packagemanifest_e2e_test.go +++ b/test/e2e/packagemanifest_e2e_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/coreos/go-semver/semver" + "github.com/blang/semver" "github.com/stretchr/testify/require" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apimachinery/pkg/api/errors" @@ -65,7 +65,7 @@ func TestPackageManifestLoading(t *testing.T) { crd := newCRD(crdPlural) catalogSourceName := genName("mock-ocs") namedStrategy := newNginxInstallStrategy(genName("dep-"), nil, nil) - csv := newCSV(packageStable, testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + csv := newCSV(packageStable, testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) c := newKubeClient(t) crc := newCRClient(t) diff --git a/test/e2e/subscription_e2e_test.go b/test/e2e/subscription_e2e_test.go index 7d778e3555..7914c8d342 100644 --- a/test/e2e/subscription_e2e_test.go +++ b/test/e2e/subscription_e2e_test.go @@ -7,7 +7,7 @@ import ( "sync" "testing" - "github.com/coreos/go-semver/semver" + "github.com/blang/semver" "github.com/ghodss/yaml" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" @@ -21,7 +21,9 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/version" ) var doubleInstance = int32(2) @@ -77,7 +79,7 @@ var ( }, Spec: v1alpha1.ClusterServiceVersionSpec{ Replaces: "", - Version: *semver.New("0.1.0"), + Version: version.OperatorVersion{semver.MustParse("0.1.0")}, MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { @@ -107,7 +109,7 @@ var ( }, Spec: v1alpha1.ClusterServiceVersionSpec{ Replaces: outdated, - Version: *semver.New("0.2.0"), + Version: version.OperatorVersion{semver.MustParse("0.2.0")}, MinKubeVersion: "0.0.0", InstallModes: []v1alpha1.InstallMode{ { @@ -137,7 +139,7 @@ var ( }, Spec: v1alpha1.ClusterServiceVersionSpec{ Replaces: stable, - Version: *semver.New("0.1.1"), + Version: version.OperatorVersion{semver.MustParse("0.1.1")}, InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -166,7 +168,7 @@ var ( }, Spec: v1alpha1.ClusterServiceVersionSpec{ Replaces: beta, - Version: *semver.New("0.3.0"), + Version: version.OperatorVersion{semver.MustParse("0.3.0")}, InstallModes: []v1alpha1.InstallMode{ { Type: v1alpha1.InstallModeTypeOwnNamespace, @@ -464,6 +466,70 @@ func TestCreateNewSubscriptionExistingCSV(t *testing.T) { compareResources(t, subscription, sameSubscription) } +func TestSubscriptionSkipRange(t *testing.T) { + defer cleaner.NotifyTestComplete(t, true) + + mainPackageName := genName("nginx-") + mainPackageStable := fmt.Sprintf("%s-stable", mainPackageName) + updatedPackageStable := fmt.Sprintf("%s-updated", mainPackageName) + stableChannel := "stable" + mainNamedStrategy := newNginxInstallStrategy(genName("dep-"), nil, nil) + mainCSV := newCSV(mainPackageStable, testNamespace, "", semver.MustParse("0.1.0-1556661347"), nil, nil, mainNamedStrategy) + updatedCSV := newCSV(updatedPackageStable, testNamespace, "", semver.MustParse("0.1.0-1556661832"), nil, nil, mainNamedStrategy) + updatedCSV.SetAnnotations(map[string]string{resolver.SkipPackageAnnotationKey: ">=0.1.0-1556661347 <0.1.0-1556661832"}) + + c := newKubeClient(t) + crc := newCRClient(t) + defer func() { + require.NoError(t, crc.OperatorsV1alpha1().Subscriptions(testNamespace).DeleteCollection(&metav1.DeleteOptions{}, metav1.ListOptions{})) + }() + + mainCatalogName := genName("mock-ocs-main-") + + // Create separate manifests for each CatalogSource + mainManifests := []registry.PackageManifest{ + { + PackageName: mainPackageName, + Channels: []registry.PackageChannel{ + {Name: stableChannel, CurrentCSVName: mainPackageStable}, + }, + DefaultChannelName: stableChannel, + }, + } + updatedManifests := []registry.PackageManifest{ + { + PackageName: mainPackageName, + Channels: []registry.PackageChannel{ + {Name: stableChannel, CurrentCSVName: updatedPackageStable}, + }, + DefaultChannelName: stableChannel, + }, + } + + // Create catalog source + _, cleanupMainCatalogSource := createInternalCatalogSource(t, c, crc, mainCatalogName, testNamespace, mainManifests, nil, []v1alpha1.ClusterServiceVersion{mainCSV}) + defer cleanupMainCatalogSource() + // Attempt to get the catalog source before creating subscription + _, err := fetchCatalogSource(t, crc, mainCatalogName, testNamespace, catalogSourceRegistryPodSynced) + require.NoError(t, err) + + // Create a subscription + subscriptionName := genName("sub-nginx-") + subscriptionCleanup := createSubscriptionForCatalog(t, crc, testNamespace, subscriptionName, mainCatalogName, mainPackageName, stableChannel, "", v1alpha1.ApprovalAutomatic) + defer subscriptionCleanup() + + // Wait for csv to install + _, err = awaitCSV(t, crc, testNamespace, mainCSV.GetName(), csvSucceededChecker) + require.NoError(t, err) + + // Update catalog with a new csv in the channel with a skip range + updateInternalCatalog(t, c, crc, mainCatalogName, testNamespace, nil, []v1alpha1.ClusterServiceVersion{updatedCSV}, updatedManifests) + + // Wait for csv to update + _, err = awaitCSV(t, crc, testNamespace, updatedCSV.GetName(), csvSucceededChecker) + require.NoError(t, err) +} + // If installPlanApproval is set to manual, the installplans created should be created with approval: manual func TestCreateNewSubscriptionManualApproval(t *testing.T) { defer cleaner.NotifyTestComplete(t, true) @@ -529,8 +595,8 @@ func TestSusbcriptionWithStartingCSV(t *testing.T) { stableChannel := "stable" namedStrategy := newNginxInstallStrategy(genName("dep-"), nil, nil) - csvA := newCSV("nginx-a", testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) - csvB := newCSV("nginx-b", testNamespace, "nginx-a", *semver.New("0.2.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + csvA := newCSV("nginx-a", testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + csvB := newCSV("nginx-b", testNamespace, "nginx-a", semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) // Create PackageManifests manifests := []registry.PackageManifest{ @@ -668,9 +734,9 @@ func TestSubscriptionUpdatesMultipleIntermediates(t *testing.T) { stableChannel := "stable" namedStrategy := newNginxInstallStrategy(genName("dep-"), nil, nil) - csvA := newCSV("nginx-a", testNamespace, "", *semver.New("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) - csvB := newCSV("nginx-b", testNamespace, "nginx-a", *semver.New("0.2.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) - csvC := newCSV("nginx-c", testNamespace, "nginx-b", *semver.New("0.3.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + csvA := newCSV("nginx-a", testNamespace, "", semver.MustParse("0.1.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + csvB := newCSV("nginx-b", testNamespace, "nginx-a", semver.MustParse("0.2.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) + csvC := newCSV("nginx-c", testNamespace, "nginx-b", semver.MustParse("0.3.0"), []apiextensions.CustomResourceDefinition{crd}, nil, namedStrategy) // Create PackageManifests manifests := []registry.PackageManifest{ diff --git a/vendor/github.com/blang/semver/.travis.yml b/vendor/github.com/blang/semver/.travis.yml new file mode 100644 index 0000000000..102fb9a691 --- /dev/null +++ b/vendor/github.com/blang/semver/.travis.yml @@ -0,0 +1,21 @@ +language: go +matrix: + include: + - go: 1.4.3 + - go: 1.5.4 + - go: 1.6.3 + - go: 1.7 + - go: tip + allow_failures: + - go: tip +install: +- go get golang.org/x/tools/cmd/cover +- go get github.com/mattn/goveralls +script: +- echo "Test and track coverage" ; $HOME/gopath/bin/goveralls -package "." -service=travis-ci + -repotoken $COVERALLS_TOKEN +- echo "Build examples" ; cd examples && go build +- echo "Check if gofmt'd" ; diff -u <(echo -n) <(gofmt -d -s .) +env: + global: + secure: HroGEAUQpVq9zX1b1VIkraLiywhGbzvNnTZq2TMxgK7JHP8xqNplAeF1izrR2i4QLL9nsY+9WtYss4QuPvEtZcVHUobw6XnL6radF7jS1LgfYZ9Y7oF+zogZ2I5QUMRLGA7rcxQ05s7mKq3XZQfeqaNts4bms/eZRefWuaFZbkw= diff --git a/vendor/github.com/blang/semver/LICENSE b/vendor/github.com/blang/semver/LICENSE new file mode 100644 index 0000000000..5ba5c86fcb --- /dev/null +++ b/vendor/github.com/blang/semver/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2014 Benedikt Lang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/github.com/blang/semver/README.md b/vendor/github.com/blang/semver/README.md new file mode 100644 index 0000000000..08b2e4a3d7 --- /dev/null +++ b/vendor/github.com/blang/semver/README.md @@ -0,0 +1,194 @@ +semver for golang [![Build Status](https://travis-ci.org/blang/semver.svg?branch=master)](https://travis-ci.org/blang/semver) [![GoDoc](https://godoc.org/github.com/blang/semver?status.png)](https://godoc.org/github.com/blang/semver) [![Coverage Status](https://img.shields.io/coveralls/blang/semver.svg)](https://coveralls.io/r/blang/semver?branch=master) +====== + +semver is a [Semantic Versioning](http://semver.org/) library written in golang. It fully covers spec version `2.0.0`. + +Usage +----- +```bash +$ go get github.com/blang/semver +``` +Note: Always vendor your dependencies or fix on a specific version tag. + +```go +import github.com/blang/semver +v1, err := semver.Make("1.0.0-beta") +v2, err := semver.Make("2.0.0-beta") +v1.Compare(v2) +``` + +Also check the [GoDocs](http://godoc.org/github.com/blang/semver). + +Why should I use this lib? +----- + +- Fully spec compatible +- No reflection +- No regex +- Fully tested (Coverage >99%) +- Readable parsing/validation errors +- Fast (See [Benchmarks](#benchmarks)) +- Only Stdlib +- Uses values instead of pointers +- Many features, see below + + +Features +----- + +- Parsing and validation at all levels +- Comparator-like comparisons +- Compare Helper Methods +- InPlace manipulation +- Ranges `>=1.0.0 <2.0.0 || >=3.0.0 !3.0.1-beta.1` +- Wildcards `>=1.x`, `<=2.5.x` +- Sortable (implements sort.Interface) +- database/sql compatible (sql.Scanner/Valuer) +- encoding/json compatible (json.Marshaler/Unmarshaler) + +Ranges +------ + +A `Range` is a set of conditions which specify which versions satisfy the range. + +A condition is composed of an operator and a version. The supported operators are: + +- `<1.0.0` Less than `1.0.0` +- `<=1.0.0` Less than or equal to `1.0.0` +- `>1.0.0` Greater than `1.0.0` +- `>=1.0.0` Greater than or equal to `1.0.0` +- `1.0.0`, `=1.0.0`, `==1.0.0` Equal to `1.0.0` +- `!1.0.0`, `!=1.0.0` Not equal to `1.0.0`. Excludes version `1.0.0`. + +Note that spaces between the operator and the version will be gracefully tolerated. + +A `Range` can link multiple `Ranges` separated by space: + +Ranges can be linked by logical AND: + + - `>1.0.0 <2.0.0` would match between both ranges, so `1.1.1` and `1.8.7` but not `1.0.0` or `2.0.0` + - `>1.0.0 <3.0.0 !2.0.3-beta.2` would match every version between `1.0.0` and `3.0.0` except `2.0.3-beta.2` + +Ranges can also be linked by logical OR: + + - `<2.0.0 || >=3.0.0` would match `1.x.x` and `3.x.x` but not `2.x.x` + +AND has a higher precedence than OR. It's not possible to use brackets. + +Ranges can be combined by both AND and OR + + - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` + +Range usage: + +``` +v, err := semver.Parse("1.2.3") +range, err := semver.ParseRange(">1.0.0 <2.0.0 || >=3.0.0") +if range(v) { + //valid +} + +``` + +Example +----- + +Have a look at full examples in [examples/main.go](examples/main.go) + +```go +import github.com/blang/semver + +v, err := semver.Make("0.0.1-alpha.preview+123.github") +fmt.Printf("Major: %d\n", v.Major) +fmt.Printf("Minor: %d\n", v.Minor) +fmt.Printf("Patch: %d\n", v.Patch) +fmt.Printf("Pre: %s\n", v.Pre) +fmt.Printf("Build: %s\n", v.Build) + +// Prerelease versions array +if len(v.Pre) > 0 { + fmt.Println("Prerelease versions:") + for i, pre := range v.Pre { + fmt.Printf("%d: %q\n", i, pre) + } +} + +// Build meta data array +if len(v.Build) > 0 { + fmt.Println("Build meta data:") + for i, build := range v.Build { + fmt.Printf("%d: %q\n", i, build) + } +} + +v001, err := semver.Make("0.0.1") +// Compare using helpers: v.GT(v2), v.LT, v.GTE, v.LTE +v001.GT(v) == true +v.LT(v001) == true +v.GTE(v) == true +v.LTE(v) == true + +// Or use v.Compare(v2) for comparisons (-1, 0, 1): +v001.Compare(v) == 1 +v.Compare(v001) == -1 +v.Compare(v) == 0 + +// Manipulate Version in place: +v.Pre[0], err = semver.NewPRVersion("beta") +if err != nil { + fmt.Printf("Error parsing pre release version: %q", err) +} + +fmt.Println("\nValidate versions:") +v.Build[0] = "?" + +err = v.Validate() +if err != nil { + fmt.Printf("Validation failed: %s\n", err) +} +``` + + +Benchmarks +----- + + BenchmarkParseSimple-4 5000000 390 ns/op 48 B/op 1 allocs/op + BenchmarkParseComplex-4 1000000 1813 ns/op 256 B/op 7 allocs/op + BenchmarkParseAverage-4 1000000 1171 ns/op 163 B/op 4 allocs/op + BenchmarkStringSimple-4 20000000 119 ns/op 16 B/op 1 allocs/op + BenchmarkStringLarger-4 10000000 206 ns/op 32 B/op 2 allocs/op + BenchmarkStringComplex-4 5000000 324 ns/op 80 B/op 3 allocs/op + BenchmarkStringAverage-4 5000000 273 ns/op 53 B/op 2 allocs/op + BenchmarkValidateSimple-4 200000000 9.33 ns/op 0 B/op 0 allocs/op + BenchmarkValidateComplex-4 3000000 469 ns/op 0 B/op 0 allocs/op + BenchmarkValidateAverage-4 5000000 256 ns/op 0 B/op 0 allocs/op + BenchmarkCompareSimple-4 100000000 11.8 ns/op 0 B/op 0 allocs/op + BenchmarkCompareComplex-4 50000000 30.8 ns/op 0 B/op 0 allocs/op + BenchmarkCompareAverage-4 30000000 41.5 ns/op 0 B/op 0 allocs/op + BenchmarkSort-4 3000000 419 ns/op 256 B/op 2 allocs/op + BenchmarkRangeParseSimple-4 2000000 850 ns/op 192 B/op 5 allocs/op + BenchmarkRangeParseAverage-4 1000000 1677 ns/op 400 B/op 10 allocs/op + BenchmarkRangeParseComplex-4 300000 5214 ns/op 1440 B/op 30 allocs/op + BenchmarkRangeMatchSimple-4 50000000 25.6 ns/op 0 B/op 0 allocs/op + BenchmarkRangeMatchAverage-4 30000000 56.4 ns/op 0 B/op 0 allocs/op + BenchmarkRangeMatchComplex-4 10000000 153 ns/op 0 B/op 0 allocs/op + +See benchmark cases at [semver_test.go](semver_test.go) + + +Motivation +----- + +I simply couldn't find any lib supporting the full spec. Others were just wrong or used reflection and regex which i don't like. + + +Contribution +----- + +Feel free to make a pull request. For bigger changes create a issue first to discuss about it. + + +License +----- + +See [LICENSE](LICENSE) file. diff --git a/vendor/github.com/blang/semver/json.go b/vendor/github.com/blang/semver/json.go new file mode 100644 index 0000000000..a74bf7c449 --- /dev/null +++ b/vendor/github.com/blang/semver/json.go @@ -0,0 +1,23 @@ +package semver + +import ( + "encoding/json" +) + +// MarshalJSON implements the encoding/json.Marshaler interface. +func (v Version) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} + +// UnmarshalJSON implements the encoding/json.Unmarshaler interface. +func (v *Version) UnmarshalJSON(data []byte) (err error) { + var versionString string + + if err = json.Unmarshal(data, &versionString); err != nil { + return + } + + *v, err = Parse(versionString) + + return +} diff --git a/vendor/github.com/blang/semver/package.json b/vendor/github.com/blang/semver/package.json new file mode 100644 index 0000000000..1cf8ebdd9c --- /dev/null +++ b/vendor/github.com/blang/semver/package.json @@ -0,0 +1,17 @@ +{ + "author": "blang", + "bugs": { + "URL": "https://github.com/blang/semver/issues", + "url": "https://github.com/blang/semver/issues" + }, + "gx": { + "dvcsimport": "github.com/blang/semver" + }, + "gxVersion": "0.10.0", + "language": "go", + "license": "MIT", + "name": "semver", + "releaseCmd": "git commit -a -m \"gx publish $VERSION\"", + "version": "3.5.1" +} + diff --git a/vendor/github.com/blang/semver/range.go b/vendor/github.com/blang/semver/range.go new file mode 100644 index 0000000000..fca406d479 --- /dev/null +++ b/vendor/github.com/blang/semver/range.go @@ -0,0 +1,416 @@ +package semver + +import ( + "fmt" + "strconv" + "strings" + "unicode" +) + +type wildcardType int + +const ( + noneWildcard wildcardType = iota + majorWildcard wildcardType = 1 + minorWildcard wildcardType = 2 + patchWildcard wildcardType = 3 +) + +func wildcardTypefromInt(i int) wildcardType { + switch i { + case 1: + return majorWildcard + case 2: + return minorWildcard + case 3: + return patchWildcard + default: + return noneWildcard + } +} + +type comparator func(Version, Version) bool + +var ( + compEQ comparator = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == 0 + } + compNE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) != 0 + } + compGT = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == 1 + } + compGE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) >= 0 + } + compLT = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) == -1 + } + compLE = func(v1 Version, v2 Version) bool { + return v1.Compare(v2) <= 0 + } +) + +type versionRange struct { + v Version + c comparator +} + +// rangeFunc creates a Range from the given versionRange. +func (vr *versionRange) rangeFunc() Range { + return Range(func(v Version) bool { + return vr.c(v, vr.v) + }) +} + +// Range represents a range of versions. +// A Range can be used to check if a Version satisfies it: +// +// range, err := semver.ParseRange(">1.0.0 <2.0.0") +// range(semver.MustParse("1.1.1") // returns true +type Range func(Version) bool + +// OR combines the existing Range with another Range using logical OR. +func (rf Range) OR(f Range) Range { + return Range(func(v Version) bool { + return rf(v) || f(v) + }) +} + +// AND combines the existing Range with another Range using logical AND. +func (rf Range) AND(f Range) Range { + return Range(func(v Version) bool { + return rf(v) && f(v) + }) +} + +// ParseRange parses a range and returns a Range. +// If the range could not be parsed an error is returned. +// +// Valid ranges are: +// - "<1.0.0" +// - "<=1.0.0" +// - ">1.0.0" +// - ">=1.0.0" +// - "1.0.0", "=1.0.0", "==1.0.0" +// - "!1.0.0", "!=1.0.0" +// +// A Range can consist of multiple ranges separated by space: +// Ranges can be linked by logical AND: +// - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0" +// - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2 +// +// Ranges can also be linked by logical OR: +// - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x" +// +// AND has a higher precedence than OR. It's not possible to use brackets. +// +// Ranges can be combined by both AND and OR +// +// - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1` +func ParseRange(s string) (Range, error) { + parts := splitAndTrim(s) + orParts, err := splitORParts(parts) + if err != nil { + return nil, err + } + expandedParts, err := expandWildcardVersion(orParts) + if err != nil { + return nil, err + } + var orFn Range + for _, p := range expandedParts { + var andFn Range + for _, ap := range p { + opStr, vStr, err := splitComparatorVersion(ap) + if err != nil { + return nil, err + } + vr, err := buildVersionRange(opStr, vStr) + if err != nil { + return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err) + } + rf := vr.rangeFunc() + + // Set function + if andFn == nil { + andFn = rf + } else { // Combine with existing function + andFn = andFn.AND(rf) + } + } + if orFn == nil { + orFn = andFn + } else { + orFn = orFn.OR(andFn) + } + + } + return orFn, nil +} + +// splitORParts splits the already cleaned parts by '||'. +// Checks for invalid positions of the operator and returns an +// error if found. +func splitORParts(parts []string) ([][]string, error) { + var ORparts [][]string + last := 0 + for i, p := range parts { + if p == "||" { + if i == 0 { + return nil, fmt.Errorf("First element in range is '||'") + } + ORparts = append(ORparts, parts[last:i]) + last = i + 1 + } + } + if last == len(parts) { + return nil, fmt.Errorf("Last element in range is '||'") + } + ORparts = append(ORparts, parts[last:]) + return ORparts, nil +} + +// buildVersionRange takes a slice of 2: operator and version +// and builds a versionRange, otherwise an error. +func buildVersionRange(opStr, vStr string) (*versionRange, error) { + c := parseComparator(opStr) + if c == nil { + return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, "")) + } + v, err := Parse(vStr) + if err != nil { + return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err) + } + + return &versionRange{ + v: v, + c: c, + }, nil + +} + +// inArray checks if a byte is contained in an array of bytes +func inArray(s byte, list []byte) bool { + for _, el := range list { + if el == s { + return true + } + } + return false +} + +// splitAndTrim splits a range string by spaces and cleans whitespaces +func splitAndTrim(s string) (result []string) { + last := 0 + var lastChar byte + excludeFromSplit := []byte{'>', '<', '='} + for i := 0; i < len(s); i++ { + if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) { + if last < i-1 { + result = append(result, s[last:i]) + } + last = i + 1 + } else if s[i] != ' ' { + lastChar = s[i] + } + } + if last < len(s)-1 { + result = append(result, s[last:]) + } + + for i, v := range result { + result[i] = strings.Replace(v, " ", "", -1) + } + + // parts := strings.Split(s, " ") + // for _, x := range parts { + // if s := strings.TrimSpace(x); len(s) != 0 { + // result = append(result, s) + // } + // } + return +} + +// splitComparatorVersion splits the comparator from the version. +// Input must be free of leading or trailing spaces. +func splitComparatorVersion(s string) (string, string, error) { + i := strings.IndexFunc(s, unicode.IsDigit) + if i == -1 { + return "", "", fmt.Errorf("Could not get version from string: %q", s) + } + return strings.TrimSpace(s[0:i]), s[i:], nil +} + +// getWildcardType will return the type of wildcard that the +// passed version contains +func getWildcardType(vStr string) wildcardType { + parts := strings.Split(vStr, ".") + nparts := len(parts) + wildcard := parts[nparts-1] + + possibleWildcardType := wildcardTypefromInt(nparts) + if wildcard == "x" { + return possibleWildcardType + } + + return noneWildcard +} + +// createVersionFromWildcard will convert a wildcard version +// into a regular version, replacing 'x's with '0's, handling +// special cases like '1.x.x' and '1.x' +func createVersionFromWildcard(vStr string) string { + // handle 1.x.x + vStr2 := strings.Replace(vStr, ".x.x", ".x", 1) + vStr2 = strings.Replace(vStr2, ".x", ".0", 1) + parts := strings.Split(vStr2, ".") + + // handle 1.x + if len(parts) == 2 { + return vStr2 + ".0" + } + + return vStr2 +} + +// incrementMajorVersion will increment the major version +// of the passed version +func incrementMajorVersion(vStr string) (string, error) { + parts := strings.Split(vStr, ".") + i, err := strconv.Atoi(parts[0]) + if err != nil { + return "", err + } + parts[0] = strconv.Itoa(i + 1) + + return strings.Join(parts, "."), nil +} + +// incrementMajorVersion will increment the minor version +// of the passed version +func incrementMinorVersion(vStr string) (string, error) { + parts := strings.Split(vStr, ".") + i, err := strconv.Atoi(parts[1]) + if err != nil { + return "", err + } + parts[1] = strconv.Itoa(i + 1) + + return strings.Join(parts, "."), nil +} + +// expandWildcardVersion will expand wildcards inside versions +// following these rules: +// +// * when dealing with patch wildcards: +// >= 1.2.x will become >= 1.2.0 +// <= 1.2.x will become < 1.3.0 +// > 1.2.x will become >= 1.3.0 +// < 1.2.x will become < 1.2.0 +// != 1.2.x will become < 1.2.0 >= 1.3.0 +// +// * when dealing with minor wildcards: +// >= 1.x will become >= 1.0.0 +// <= 1.x will become < 2.0.0 +// > 1.x will become >= 2.0.0 +// < 1.0 will become < 1.0.0 +// != 1.x will become < 1.0.0 >= 2.0.0 +// +// * when dealing with wildcards without +// version operator: +// 1.2.x will become >= 1.2.0 < 1.3.0 +// 1.x will become >= 1.0.0 < 2.0.0 +func expandWildcardVersion(parts [][]string) ([][]string, error) { + var expandedParts [][]string + for _, p := range parts { + var newParts []string + for _, ap := range p { + if strings.Index(ap, "x") != -1 { + opStr, vStr, err := splitComparatorVersion(ap) + if err != nil { + return nil, err + } + + versionWildcardType := getWildcardType(vStr) + flatVersion := createVersionFromWildcard(vStr) + + var resultOperator string + var shouldIncrementVersion bool + switch opStr { + case ">": + resultOperator = ">=" + shouldIncrementVersion = true + case ">=": + resultOperator = ">=" + case "<": + resultOperator = "<" + case "<=": + resultOperator = "<" + shouldIncrementVersion = true + case "", "=", "==": + newParts = append(newParts, ">="+flatVersion) + resultOperator = "<" + shouldIncrementVersion = true + case "!=", "!": + newParts = append(newParts, "<"+flatVersion) + resultOperator = ">=" + shouldIncrementVersion = true + } + + var resultVersion string + if shouldIncrementVersion { + switch versionWildcardType { + case patchWildcard: + resultVersion, _ = incrementMinorVersion(flatVersion) + case minorWildcard: + resultVersion, _ = incrementMajorVersion(flatVersion) + } + } else { + resultVersion = flatVersion + } + + ap = resultOperator + resultVersion + } + newParts = append(newParts, ap) + } + expandedParts = append(expandedParts, newParts) + } + + return expandedParts, nil +} + +func parseComparator(s string) comparator { + switch s { + case "==": + fallthrough + case "": + fallthrough + case "=": + return compEQ + case ">": + return compGT + case ">=": + return compGE + case "<": + return compLT + case "<=": + return compLE + case "!": + fallthrough + case "!=": + return compNE + } + + return nil +} + +// MustParseRange is like ParseRange but panics if the range cannot be parsed. +func MustParseRange(s string) Range { + r, err := ParseRange(s) + if err != nil { + panic(`semver: ParseRange(` + s + `): ` + err.Error()) + } + return r +} diff --git a/vendor/github.com/blang/semver/semver.go b/vendor/github.com/blang/semver/semver.go new file mode 100644 index 0000000000..8ee0842e6a --- /dev/null +++ b/vendor/github.com/blang/semver/semver.go @@ -0,0 +1,418 @@ +package semver + +import ( + "errors" + "fmt" + "strconv" + "strings" +) + +const ( + numbers string = "0123456789" + alphas = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" + alphanum = alphas + numbers +) + +// SpecVersion is the latest fully supported spec version of semver +var SpecVersion = Version{ + Major: 2, + Minor: 0, + Patch: 0, +} + +// Version represents a semver compatible version +type Version struct { + Major uint64 + Minor uint64 + Patch uint64 + Pre []PRVersion + Build []string //No Precendence +} + +// Version to string +func (v Version) String() string { + b := make([]byte, 0, 5) + b = strconv.AppendUint(b, v.Major, 10) + b = append(b, '.') + b = strconv.AppendUint(b, v.Minor, 10) + b = append(b, '.') + b = strconv.AppendUint(b, v.Patch, 10) + + if len(v.Pre) > 0 { + b = append(b, '-') + b = append(b, v.Pre[0].String()...) + + for _, pre := range v.Pre[1:] { + b = append(b, '.') + b = append(b, pre.String()...) + } + } + + if len(v.Build) > 0 { + b = append(b, '+') + b = append(b, v.Build[0]...) + + for _, build := range v.Build[1:] { + b = append(b, '.') + b = append(b, build...) + } + } + + return string(b) +} + +// Equals checks if v is equal to o. +func (v Version) Equals(o Version) bool { + return (v.Compare(o) == 0) +} + +// EQ checks if v is equal to o. +func (v Version) EQ(o Version) bool { + return (v.Compare(o) == 0) +} + +// NE checks if v is not equal to o. +func (v Version) NE(o Version) bool { + return (v.Compare(o) != 0) +} + +// GT checks if v is greater than o. +func (v Version) GT(o Version) bool { + return (v.Compare(o) == 1) +} + +// GTE checks if v is greater than or equal to o. +func (v Version) GTE(o Version) bool { + return (v.Compare(o) >= 0) +} + +// GE checks if v is greater than or equal to o. +func (v Version) GE(o Version) bool { + return (v.Compare(o) >= 0) +} + +// LT checks if v is less than o. +func (v Version) LT(o Version) bool { + return (v.Compare(o) == -1) +} + +// LTE checks if v is less than or equal to o. +func (v Version) LTE(o Version) bool { + return (v.Compare(o) <= 0) +} + +// LE checks if v is less than or equal to o. +func (v Version) LE(o Version) bool { + return (v.Compare(o) <= 0) +} + +// Compare compares Versions v to o: +// -1 == v is less than o +// 0 == v is equal to o +// 1 == v is greater than o +func (v Version) Compare(o Version) int { + if v.Major != o.Major { + if v.Major > o.Major { + return 1 + } + return -1 + } + if v.Minor != o.Minor { + if v.Minor > o.Minor { + return 1 + } + return -1 + } + if v.Patch != o.Patch { + if v.Patch > o.Patch { + return 1 + } + return -1 + } + + // Quick comparison if a version has no prerelease versions + if len(v.Pre) == 0 && len(o.Pre) == 0 { + return 0 + } else if len(v.Pre) == 0 && len(o.Pre) > 0 { + return 1 + } else if len(v.Pre) > 0 && len(o.Pre) == 0 { + return -1 + } + + i := 0 + for ; i < len(v.Pre) && i < len(o.Pre); i++ { + if comp := v.Pre[i].Compare(o.Pre[i]); comp == 0 { + continue + } else if comp == 1 { + return 1 + } else { + return -1 + } + } + + // If all pr versions are the equal but one has further prversion, this one greater + if i == len(v.Pre) && i == len(o.Pre) { + return 0 + } else if i == len(v.Pre) && i < len(o.Pre) { + return -1 + } else { + return 1 + } + +} + +// Validate validates v and returns error in case +func (v Version) Validate() error { + // Major, Minor, Patch already validated using uint64 + + for _, pre := range v.Pre { + if !pre.IsNum { //Numeric prerelease versions already uint64 + if len(pre.VersionStr) == 0 { + return fmt.Errorf("Prerelease can not be empty %q", pre.VersionStr) + } + if !containsOnly(pre.VersionStr, alphanum) { + return fmt.Errorf("Invalid character(s) found in prerelease %q", pre.VersionStr) + } + } + } + + for _, build := range v.Build { + if len(build) == 0 { + return fmt.Errorf("Build meta data can not be empty %q", build) + } + if !containsOnly(build, alphanum) { + return fmt.Errorf("Invalid character(s) found in build meta data %q", build) + } + } + + return nil +} + +// New is an alias for Parse and returns a pointer, parses version string and returns a validated Version or error +func New(s string) (vp *Version, err error) { + v, err := Parse(s) + vp = &v + return +} + +// Make is an alias for Parse, parses version string and returns a validated Version or error +func Make(s string) (Version, error) { + return Parse(s) +} + +// ParseTolerant allows for certain version specifications that do not strictly adhere to semver +// specs to be parsed by this library. It does so by normalizing versions before passing them to +// Parse(). It currently trims spaces, removes a "v" prefix, and adds a 0 patch number to versions +// with only major and minor components specified +func ParseTolerant(s string) (Version, error) { + s = strings.TrimSpace(s) + s = strings.TrimPrefix(s, "v") + + // Split into major.minor.(patch+pr+meta) + parts := strings.SplitN(s, ".", 3) + if len(parts) < 3 { + if strings.ContainsAny(parts[len(parts)-1], "+-") { + return Version{}, errors.New("Short version cannot contain PreRelease/Build meta data") + } + for len(parts) < 3 { + parts = append(parts, "0") + } + s = strings.Join(parts, ".") + } + + return Parse(s) +} + +// Parse parses version string and returns a validated Version or error +func Parse(s string) (Version, error) { + if len(s) == 0 { + return Version{}, errors.New("Version string empty") + } + + // Split into major.minor.(patch+pr+meta) + parts := strings.SplitN(s, ".", 3) + if len(parts) != 3 { + return Version{}, errors.New("No Major.Minor.Patch elements found") + } + + // Major + if !containsOnly(parts[0], numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in major number %q", parts[0]) + } + if hasLeadingZeroes(parts[0]) { + return Version{}, fmt.Errorf("Major number must not contain leading zeroes %q", parts[0]) + } + major, err := strconv.ParseUint(parts[0], 10, 64) + if err != nil { + return Version{}, err + } + + // Minor + if !containsOnly(parts[1], numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in minor number %q", parts[1]) + } + if hasLeadingZeroes(parts[1]) { + return Version{}, fmt.Errorf("Minor number must not contain leading zeroes %q", parts[1]) + } + minor, err := strconv.ParseUint(parts[1], 10, 64) + if err != nil { + return Version{}, err + } + + v := Version{} + v.Major = major + v.Minor = minor + + var build, prerelease []string + patchStr := parts[2] + + if buildIndex := strings.IndexRune(patchStr, '+'); buildIndex != -1 { + build = strings.Split(patchStr[buildIndex+1:], ".") + patchStr = patchStr[:buildIndex] + } + + if preIndex := strings.IndexRune(patchStr, '-'); preIndex != -1 { + prerelease = strings.Split(patchStr[preIndex+1:], ".") + patchStr = patchStr[:preIndex] + } + + if !containsOnly(patchStr, numbers) { + return Version{}, fmt.Errorf("Invalid character(s) found in patch number %q", patchStr) + } + if hasLeadingZeroes(patchStr) { + return Version{}, fmt.Errorf("Patch number must not contain leading zeroes %q", patchStr) + } + patch, err := strconv.ParseUint(patchStr, 10, 64) + if err != nil { + return Version{}, err + } + + v.Patch = patch + + // Prerelease + for _, prstr := range prerelease { + parsedPR, err := NewPRVersion(prstr) + if err != nil { + return Version{}, err + } + v.Pre = append(v.Pre, parsedPR) + } + + // Build meta data + for _, str := range build { + if len(str) == 0 { + return Version{}, errors.New("Build meta data is empty") + } + if !containsOnly(str, alphanum) { + return Version{}, fmt.Errorf("Invalid character(s) found in build meta data %q", str) + } + v.Build = append(v.Build, str) + } + + return v, nil +} + +// MustParse is like Parse but panics if the version cannot be parsed. +func MustParse(s string) Version { + v, err := Parse(s) + if err != nil { + panic(`semver: Parse(` + s + `): ` + err.Error()) + } + return v +} + +// PRVersion represents a PreRelease Version +type PRVersion struct { + VersionStr string + VersionNum uint64 + IsNum bool +} + +// NewPRVersion creates a new valid prerelease version +func NewPRVersion(s string) (PRVersion, error) { + if len(s) == 0 { + return PRVersion{}, errors.New("Prerelease is empty") + } + v := PRVersion{} + if containsOnly(s, numbers) { + if hasLeadingZeroes(s) { + return PRVersion{}, fmt.Errorf("Numeric PreRelease version must not contain leading zeroes %q", s) + } + num, err := strconv.ParseUint(s, 10, 64) + + // Might never be hit, but just in case + if err != nil { + return PRVersion{}, err + } + v.VersionNum = num + v.IsNum = true + } else if containsOnly(s, alphanum) { + v.VersionStr = s + v.IsNum = false + } else { + return PRVersion{}, fmt.Errorf("Invalid character(s) found in prerelease %q", s) + } + return v, nil +} + +// IsNumeric checks if prerelease-version is numeric +func (v PRVersion) IsNumeric() bool { + return v.IsNum +} + +// Compare compares two PreRelease Versions v and o: +// -1 == v is less than o +// 0 == v is equal to o +// 1 == v is greater than o +func (v PRVersion) Compare(o PRVersion) int { + if v.IsNum && !o.IsNum { + return -1 + } else if !v.IsNum && o.IsNum { + return 1 + } else if v.IsNum && o.IsNum { + if v.VersionNum == o.VersionNum { + return 0 + } else if v.VersionNum > o.VersionNum { + return 1 + } else { + return -1 + } + } else { // both are Alphas + if v.VersionStr == o.VersionStr { + return 0 + } else if v.VersionStr > o.VersionStr { + return 1 + } else { + return -1 + } + } +} + +// PreRelease version to string +func (v PRVersion) String() string { + if v.IsNum { + return strconv.FormatUint(v.VersionNum, 10) + } + return v.VersionStr +} + +func containsOnly(s string, set string) bool { + return strings.IndexFunc(s, func(r rune) bool { + return !strings.ContainsRune(set, r) + }) == -1 +} + +func hasLeadingZeroes(s string) bool { + return len(s) > 1 && s[0] == '0' +} + +// NewBuildVersion creates a new valid build version +func NewBuildVersion(s string) (string, error) { + if len(s) == 0 { + return "", errors.New("Buildversion is empty") + } + if !containsOnly(s, alphanum) { + return "", fmt.Errorf("Invalid character(s) found in build meta data %q", s) + } + return s, nil +} diff --git a/vendor/github.com/blang/semver/sort.go b/vendor/github.com/blang/semver/sort.go new file mode 100644 index 0000000000..e18f880826 --- /dev/null +++ b/vendor/github.com/blang/semver/sort.go @@ -0,0 +1,28 @@ +package semver + +import ( + "sort" +) + +// Versions represents multiple versions. +type Versions []Version + +// Len returns length of version collection +func (s Versions) Len() int { + return len(s) +} + +// Swap swaps two versions inside the collection by its indices +func (s Versions) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// Less checks if version at index i is less than version at index j +func (s Versions) Less(i, j int) bool { + return s[i].LT(s[j]) +} + +// Sort sorts a slice of versions +func Sort(versions []Version) { + sort.Sort(Versions(versions)) +} diff --git a/vendor/github.com/blang/semver/sql.go b/vendor/github.com/blang/semver/sql.go new file mode 100644 index 0000000000..eb4d802666 --- /dev/null +++ b/vendor/github.com/blang/semver/sql.go @@ -0,0 +1,30 @@ +package semver + +import ( + "database/sql/driver" + "fmt" +) + +// Scan implements the database/sql.Scanner interface. +func (v *Version) Scan(src interface{}) (err error) { + var str string + switch src := src.(type) { + case string: + str = src + case []byte: + str = string(src) + default: + return fmt.Errorf("Version.Scan: cannot convert %T to string.", src) + } + + if t, err := Parse(str); err == nil { + *v = t + } + + return +} + +// Value implements the database/sql/driver.Valuer interface. +func (v Version) Value() (driver.Value, error) { + return v.String(), nil +} diff --git a/vendor/k8s.io/code-generator/generate_groups.sh b/vendor/k8s.io/code-generator/generate_groups.sh new file mode 100755 index 0000000000..713a340943 --- /dev/null +++ b/vendor/k8s.io/code-generator/generate_groups.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +# Copyright 2017 The Kubernetes 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. + +set -o errexit +set -o nounset +set -o pipefail + +# generate-groups generates everything for a project with external types only, e.g. a project based +# on CustomResourceDefinitions. + +if [ "$#" -lt 4 ] || [ "${1}" == "--help" ]; then + cat < ... + + the generators comma separated to run (deepcopy,defaulter,client,lister,informer) or "all". + the output package name (e.g. github.com/example/project/pkg/generated). + the external types dir (e.g. github.com/example/api or github.com/example/project/pkg/apis). + the groups and their versions in the format "groupA:v1,v2 groupB:v1 groupC:v2", relative + to . + ... arbitrary flags passed to all generator binaries. + + +Examples: + $(basename $0) all github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1" + $(basename $0) deepcopy,client github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1" +EOF + exit 0 +fi + +GENS="$1" +OUTPUT_PKG="$2" +APIS_PKG="$3" +GROUPS_WITH_VERSIONS="$4" +shift 4 + +go install ./vendor/k8s.io/code-generator/cmd/{defaulter-gen,client-gen,lister-gen,informer-gen,deepcopy-gen} +function codegen::join() { local IFS="$1"; shift; echo "$*"; } + +# enumerate group versions +FQ_APIS=() # e.g. k8s.io/api/apps/v1 +for GVs in ${GROUPS_WITH_VERSIONS}; do + IFS=: read G Vs <<<"${GVs}" + + # enumerate versions + for V in ${Vs//,/ }; do + FQ_APIS+=(${APIS_PKG}/${G}/${V}) + done +done + +if [ "${GENS}" = "all" ] || grep -qw "deepcopy" <<<"${GENS}"; then + echo "Generating deepcopy funcs" + ${GOPATH}/bin/deepcopy-gen --input-dirs $(codegen::join , "${FQ_APIS[@]}") -O zz_generated.deepcopy --bounding-dirs ${APIS_PKG} "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "client" <<<"${GENS}"; then + echo "Generating clientset for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/clientset" + ${GOPATH}/bin/client-gen --clientset-name versioned --input-base "" --input $(codegen::join , "${FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/clientset "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "lister" <<<"${GENS}"; then + echo "Generating listers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/listers" + ${GOPATH}/bin/lister-gen --input-dirs $(codegen::join , "${FQ_APIS[@]}") --output-package ${OUTPUT_PKG}/listers "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "informer" <<<"${GENS}"; then + echo "Generating informers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/informers" + ${GOPATH}/bin/informer-gen \ + --input-dirs $(codegen::join , "${FQ_APIS[@]}") \ + --versioned-clientset-package ${OUTPUT_PKG}/clientset/versioned \ + --listers-package ${OUTPUT_PKG}/listers \ + --output-package ${OUTPUT_PKG}/informers \ + "$@" +fi diff --git a/vendor/k8s.io/code-generator/generate_internal_groups.sh b/vendor/k8s.io/code-generator/generate_internal_groups.sh new file mode 100755 index 0000000000..258b53b56f --- /dev/null +++ b/vendor/k8s.io/code-generator/generate_internal_groups.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash + +# Copyright 2017 The Kubernetes 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. + +set -o errexit +set -o nounset +set -o pipefail + +# generate-internal-groups generates everything for a project with internal types, e.g. an +# user-provided API server based on k8s.io/apiserver. + +if [ "$#" -lt 5 ] || [ "${1}" == "--help" ]; then + cat < ... + + the generators comma separated to run (deepcopy,defaulter,conversion,client,lister,informer) or "all". + the output package name (e.g. github.com/example/project/pkg/generated). + the internal types dir (e.g. github.com/example/project/pkg/apis). + the external types dir (e.g. github.com/example/project/pkg/apis or githubcom/example/apis). + the groups and their versions in the format "groupA:v1,v2 groupB:v1 groupC:v2", relative + to . + ... arbitrary flags passed to all generator binaries. + +Examples: + $(basename "$0") all github.com/example/project/pkg/client github.com/example/project/pkg/apis github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1" + $(basename "$0") deepcopy,defaulter,conversion github.com/example/project/pkg/client github.com/example/project/pkg/apis github.com/example/project/apis "foo:v1 bar:v1alpha1,v1beta1" +EOF + exit 0 +fi + +GENS="$1" +OUTPUT_PKG="$2" +INT_APIS_PKG="$3" +EXT_APIS_PKG="$4" +GROUPS_WITH_VERSIONS="$5" +shift 5 + +go install ./"$(dirname "${0}")"/cmd/{defaulter-gen,conversion-gen,client-gen,lister-gen,informer-gen,deepcopy-gen} +function codegen::join() { local IFS="$1"; shift; echo "$*"; } + +# enumerate group versions +ALL_FQ_APIS=() # e.g. k8s.io/kubernetes/pkg/apis/apps k8s.io/api/apps/v1 +INT_FQ_APIS=() # e.g. k8s.io/kubernetes/pkg/apis/apps +EXT_FQ_APIS=() # e.g. k8s.io/api/apps/v1 +for GVs in ${GROUPS_WITH_VERSIONS}; do + IFS=: read -r G Vs <<<"${GVs}" + + if [ -n "${INT_APIS_PKG}" ]; then + ALL_FQ_APIS+=("${INT_APIS_PKG}/${G}") + INT_FQ_APIS+=("${INT_APIS_PKG}/${G}") + fi + + # enumerate versions + for V in ${Vs//,/ }; do + ALL_FQ_APIS+=("${EXT_APIS_PKG}/${G}/${V}") + EXT_FQ_APIS+=("${EXT_APIS_PKG}/${G}/${V}") + done +done + +if [ "${GENS}" = "all" ] || grep -qw "deepcopy" <<<"${GENS}"; then + echo "Generating deepcopy funcs" + "${GOPATH}/bin/deepcopy-gen" --input-dirs "$(codegen::join , "${ALL_FQ_APIS[@]}")" -O zz_generated.deepcopy --bounding-dirs "${INT_APIS_PKG},${EXT_APIS_PKG}" "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "defaulter" <<<"${GENS}"; then + echo "Generating defaulters" + "${GOPATH}/bin/defaulter-gen" --input-dirs "$(codegen::join , "${EXT_FQ_APIS[@]}")" -O zz_generated.defaults "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "conversion" <<<"${GENS}"; then + echo "Generating conversions" + "${GOPATH}/bin/conversion-gen" --input-dirs "$(codegen::join , "${ALL_FQ_APIS[@]}")" -O zz_generated.conversion "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "client" <<<"${GENS}"; then + echo "Generating clientset for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}" + if [ -n "${INT_APIS_PKG}" ]; then + IFS=" " read -r -a APIS <<< "$(printf '%s/ ' "${INT_FQ_APIS[@]}")" + "${GOPATH}/bin/client-gen" --clientset-name "${CLIENTSET_NAME_INTERNAL:-internalversion}" --input-base "" --input "$(codegen::join , "${APIS[@]}")" --output-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}" "$@" + fi + "${GOPATH}/bin/client-gen" --clientset-name "${CLIENTSET_NAME_VERSIONED:-versioned}" --input-base "" --input "$(codegen::join , "${EXT_FQ_APIS[@]}")" --output-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}" "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "lister" <<<"${GENS}"; then + echo "Generating listers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/listers" + "${GOPATH}/bin/lister-gen" --input-dirs "$(codegen::join , "${ALL_FQ_APIS[@]}")" --output-package "${OUTPUT_PKG}/listers" "$@" +fi + +if [ "${GENS}" = "all" ] || grep -qw "informer" <<<"${GENS}"; then + echo "Generating informers for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}/informers" + "${GOPATH}/bin/informer-gen" \ + --input-dirs "$(codegen::join , "${ALL_FQ_APIS[@]}")" \ + --versioned-clientset-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_VERSIONED:-versioned}" \ + --internal-clientset-package "${OUTPUT_PKG}/${CLIENTSET_PKG_NAME:-clientset}/${CLIENTSET_NAME_INTERNAL:-internalversion}" \ + --listers-package "${OUTPUT_PKG}/listers" \ + --output-package "${OUTPUT_PKG}/informers" \ + "$@" +fi diff --git a/vendor/k8s.io/code-generator/hack/boilerplate.go.txt b/vendor/k8s.io/code-generator/hack/boilerplate.go.txt new file mode 100644 index 0000000000..dc48e7ab7e --- /dev/null +++ b/vendor/k8s.io/code-generator/hack/boilerplate.go.txt @@ -0,0 +1,15 @@ +/* +Copyright YEAR Red Hat, Inc. + +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. +*/ diff --git a/vendor/modules.txt b/vendor/modules.txt index 11f0dd7cb3..35289e282e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -13,6 +13,8 @@ github.com/PuerkitoBio/urlesc github.com/asaskevich/govalidator # github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 github.com/beorn7/perks/quantile +# github.com/blang/semver v3.5.1+incompatible +github.com/blang/semver # github.com/coreos/etcd v3.3.12+incompatible github.com/coreos/etcd/client github.com/coreos/etcd/clientv3