Skip to content

Commit 460022f

Browse files
author
Eric Stroczynski
authored
pkg/k8sutil/crd*.go: add kubernetes-style version sorter (#1781)
* internal/util/k8sutil: Sort implementation for Kubernetes-style version sorting, CRDVersions for sorting CRD spec.versions, and unit tests for both * internal/pkg/scaffold/olm-catalog: sort both owned and required CRDDescriptions
1 parent 6cda969 commit 460022f

File tree

5 files changed

+105
-4
lines changed

5 files changed

+105
-4
lines changed

internal/scaffold/crd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"io"
2020
"os"
2121
"path/filepath"
22+
"sort"
2223
"sync"
2324

2425
"github.com/operator-framework/operator-sdk/internal/scaffold/input"
@@ -153,6 +154,7 @@ func (s *CRD) CustomRender() ([]byte, error) {
153154
if err := checkCRDVersions(crd); err != nil {
154155
return nil, err
155156
}
157+
sort.Sort(k8sutil.CRDVersions(crd.Spec.Versions))
156158
return k8sutil.GetObjectBytes(crd, yaml.Marshal)
157159
}
158160

internal/scaffold/olm-catalog/csv_updaters.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package catalog
1717
import (
1818
"bytes"
1919
"encoding/json"
20+
"sort"
2021
"strings"
2122

2223
"github.com/operator-framework/operator-sdk/pkg/k8sutil"
@@ -31,6 +32,7 @@ import (
3132
rbacv1 "k8s.io/api/rbac/v1"
3233
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
3334
"k8s.io/apimachinery/pkg/runtime/schema"
35+
"k8s.io/apimachinery/pkg/version"
3436
)
3537

3638
// CSVUpdater is an interface for any data that can be in a CSV, which will be
@@ -244,6 +246,20 @@ type CustomResourceDefinitionsUpdate struct {
244246
crIDs map[string]struct{}
245247
}
246248

249+
type descSorter []olmapiv1alpha1.CRDDescription
250+
251+
func (descs descSorter) Len() int { return len(descs) }
252+
func (descs descSorter) Less(i, j int) bool {
253+
if descs[i].Name == descs[j].Name {
254+
if descs[i].Kind == descs[j].Kind {
255+
return version.CompareKubeAwareVersionStrings(descs[i].Version, descs[j].Version) > 0
256+
}
257+
return descs[i].Kind < descs[j].Kind
258+
}
259+
return descs[i].Name < descs[j].Name
260+
}
261+
func (descs descSorter) Swap(i, j int) { descs[i], descs[j] = descs[j], descs[i] }
262+
247263
func (store *updaterStore) AddOwnedCRD(yamlDoc []byte) error {
248264
crd := &apiextv1beta1.CustomResourceDefinition{}
249265
if err := yaml.Unmarshal(yamlDoc, crd); err != nil {
@@ -317,6 +333,8 @@ func (u *CustomResourceDefinitionsUpdate) Apply(csv *olmapiv1alpha1.ClusterServi
317333
}
318334
}
319335
csv.Spec.CustomResourceDefinitions.Owned = newDescs
336+
sort.Sort(descSorter(csv.Spec.CustomResourceDefinitions.Owned))
337+
sort.Sort(descSorter(csv.Spec.CustomResourceDefinitions.Required))
320338
return nil
321339
}
322340

internal/scaffold/olm-catalog/testdata/deploy/olm-catalog/app-operator/0.1.0/app-operator.v0.1.0.clusterserviceversion.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ spec:
1010
apiservicedefinitions: {}
1111
customresourcedefinitions:
1212
owned:
13-
- displayName: some display name
14-
kind: AppService2
15-
name: appservice2.example.com
16-
version: v1alpha2
1713
- description: some description
1814
kind: AppService
1915
name: appservice.example.com
2016
version: v1alpha1
17+
- displayName: some display name
18+
kind: AppService2
19+
name: appservice2.example.com
20+
version: v1alpha2
2121
required:
2222
- description: Represents a cluster of etcd nodes.
2323
displayName: etcd Cluster

internal/util/k8sutil/crd.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
yaml "github.com/ghodss/yaml"
2626
"github.com/pkg/errors"
2727
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
28+
"k8s.io/apimachinery/pkg/version"
2829
)
2930

3031
// GetCRDs parses all CRD manifests in the directory crdsDir and all of its subdirectories.
@@ -152,3 +153,11 @@ func CreateFQAPIs(pkg string, gvs map[string][]string) (apis []string) {
152153
}
153154
return apis
154155
}
156+
157+
type CRDVersions []apiextv1beta1.CustomResourceDefinitionVersion
158+
159+
func (vs CRDVersions) Len() int { return len(vs) }
160+
func (vs CRDVersions) Less(i, j int) bool {
161+
return version.CompareKubeAwareVersionStrings(vs[i].Name, vs[j].Name) > 0
162+
}
163+
func (vs CRDVersions) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }

internal/util/k8sutil/crd_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2019 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package k8sutil
16+
17+
import (
18+
"reflect"
19+
"sort"
20+
"testing"
21+
22+
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
23+
)
24+
25+
func TestSortVersions(t *testing.T) {
26+
cases := []struct {
27+
inputVersions []string
28+
expected []string
29+
}{
30+
{[]string{""}, []string{""}},
31+
{[]string{"v1"}, []string{"v1"}},
32+
{[]string{"v1alpha1"}, []string{"v1alpha1"}},
33+
{[]string{"v1alpha1", "v1"}, []string{"v1", "v1alpha1"}},
34+
{
35+
[]string{"foo1", "foo10", "foo2", "foo13", "foo52", "foo23", "foo32", "foo33", "foo100"},
36+
[]string{"foo1", "foo10", "foo100", "foo13", "foo2", "foo23", "foo32", "foo33", "foo52"},
37+
},
38+
{
39+
[]string{"v1alpha10", "v1alpha1", "v1alpha2000", "v1alpha3", "v1alpha2", "v1alpha300"},
40+
[]string{"v1alpha2000", "v1alpha300", "v1alpha10", "v1alpha3", "v1alpha2", "v1alpha1"},
41+
},
42+
{
43+
[]string{"v3beta1", "v12alpha1", "v12alpha2", "v10beta3", "v1", "v11alpha2", "foo1", "v10", "v2", "foo10", "v11beta2"},
44+
[]string{"v10", "v2", "v1", "v11beta2", "v10beta3", "v3beta1", "v12alpha2", "v12alpha1", "v11alpha2", "foo1", "foo10"},
45+
},
46+
}
47+
48+
for _, c := range cases {
49+
cvs := stringsToCRDVersions(c.inputVersions)
50+
sort.Sort(cvs)
51+
vs := crdVersionsToStrings(cvs)
52+
if !reflect.DeepEqual(vs, c.expected) {
53+
t.Errorf("Output not sorted as expected:\noutput: %+q\nexpected: %+q", vs, c.expected)
54+
}
55+
}
56+
}
57+
58+
func stringsToCRDVersions(vs []string) (cvs CRDVersions) {
59+
for _, v := range vs {
60+
cvs = append(cvs, apiextv1beta1.CustomResourceDefinitionVersion{
61+
Name: v,
62+
})
63+
}
64+
return cvs
65+
}
66+
67+
func crdVersionsToStrings(cvs []apiextv1beta1.CustomResourceDefinitionVersion) (vs []string) {
68+
for _, v := range cvs {
69+
vs = append(vs, v.Name)
70+
}
71+
return vs
72+
}

0 commit comments

Comments
 (0)