Skip to content

Commit fcb5719

Browse files
committed
Bug 1732914: Operator upgrades fail when versions field is not set
1. Fix the ensureCRDVersions to account for `version` field 2. Add test cases for `version` scenarios Signed-off-by: Vu Dinh <[email protected]>
1 parent 26e2cc3 commit fcb5719

File tree

3 files changed

+92
-15
lines changed

3 files changed

+92
-15
lines changed

pkg/controller/operators/catalog/operator.go

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,23 +1064,35 @@ func (o *Operator) ResolvePlan(plan *v1alpha1.InstallPlan) error {
10641064
return nil
10651065
}
10661066

1067-
// Ensure all existing versions are present in new CRD
1067+
// Ensure all existing served versions are present in new CRD
10681068
func ensureCRDVersions(oldCRD *v1beta1ext.CustomResourceDefinition, newCRD *v1beta1ext.CustomResourceDefinition) error {
1069+
newCRDVersions := map[string]struct{}{}
1070+
1071+
for _, newVersion := range newCRD.Spec.Versions {
1072+
newCRDVersions[newVersion.Name] = struct{}{}
1073+
}
1074+
if newCRD.Spec.Version != "" {
1075+
newCRDVersions[newCRD.Spec.Version] = struct{}{}
1076+
}
1077+
10691078
for _, oldVersion := range oldCRD.Spec.Versions {
1070-
var versionPresent bool
1071-
for _, newVersion := range newCRD.Spec.Versions {
1072-
if oldVersion.Name == newVersion.Name {
1073-
versionPresent = true
1079+
if oldVersion.Served {
1080+
_, ok := newCRDVersions[oldVersion.Name]
1081+
if !ok {
1082+
return fmt.Errorf("New CRD (%s) must contain existing served versions (%s)", oldCRD.Name, oldVersion.Name)
10741083
}
10751084
}
1076-
if !versionPresent {
1077-
return fmt.Errorf("not allowing CRD (%v) update with unincluded version %v", newCRD.GetName(), oldVersion)
1085+
}
1086+
if oldCRD.Spec.Version != "" {
1087+
_, ok := newCRDVersions[oldCRD.Spec.Version]
1088+
if !ok {
1089+
return fmt.Errorf("New CRD (%s) must contain existing version (%s)", oldCRD.Name, oldCRD.Spec.Version)
10781090
}
10791091
}
1080-
10811092
return nil
10821093
}
10831094

1095+
// Validate all existing served versions against new CRD's validation (if changed)
10841096
func (o *Operator) validateCustomResourceDefinition(oldCRD *v1beta1ext.CustomResourceDefinition, newCRD *v1beta1ext.CustomResourceDefinition) error {
10851097
o.logger.Debugf("Comparing %#v to %#v", oldCRD.Spec.Validation, newCRD.Spec.Validation)
10861098
// If validation schema is unchanged, return right away
@@ -1091,11 +1103,13 @@ func (o *Operator) validateCustomResourceDefinition(oldCRD *v1beta1ext.CustomRes
10911103
if err := v1beta1ext.Convert_v1beta1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(newCRD, convertedCRD, nil); err != nil {
10921104
return err
10931105
}
1094-
for _, oldVersion := range oldCRD.Spec.Versions {
1095-
gvr := schema.GroupVersionResource{Group: oldCRD.Spec.Group, Version: oldVersion.Name, Resource: oldCRD.Spec.Names.Plural}
1096-
err := o.validateExistingCRs(gvr, convertedCRD)
1097-
if err != nil {
1098-
return err
1106+
for _, version := range oldCRD.Spec.Versions {
1107+
if !version.Served {
1108+
gvr := schema.GroupVersionResource{Group: oldCRD.Spec.Group, Version: version.Name, Resource: oldCRD.Spec.Names.Plural}
1109+
err := o.validateExistingCRs(gvr, convertedCRD)
1110+
if err != nil {
1111+
return err
1112+
}
10991113
}
11001114
}
11011115

@@ -1106,7 +1120,7 @@ func (o *Operator) validateCustomResourceDefinition(oldCRD *v1beta1ext.CustomRes
11061120
return err
11071121
}
11081122
}
1109-
1123+
o.logger.Debugf("Successfully validated CRD %s\n", newCRD.Name)
11101124
return nil
11111125
}
11121126

@@ -1198,7 +1212,8 @@ func (o *Operator) ExecutePlan(plan *v1alpha1.InstallPlan) error {
11981212
} else if len(matchedCSV) > 1 {
11991213
o.logger.Debugf("Found multiple owners for CRD %v", crd)
12001214

1201-
if err := ensureCRDVersions(currentCRD, &crd); err != nil {
1215+
err := ensureCRDVersions(currentCRD, &crd)
1216+
if err != nil {
12021217
return errorwrap.Wrapf(err, "error missing existing CRD version(s) in new CRD: %s", step.Resource.Name)
12031218
}
12041219

pkg/controller/operators/catalog/operator_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,35 @@ func TestEnsureCRDVersions(t *testing.T) {
195195
}(),
196196
expectedFailure: true,
197197
},
198+
{
199+
name: "missing version in new CRD",
200+
oldCRD: func() v1beta1.CustomResourceDefinition {
201+
oldCRD := crd(mainCRDPlural)
202+
oldCRD.Spec.Version = "v1alpha1"
203+
return oldCRD
204+
}(),
205+
newCRD: func() v1beta1.CustomResourceDefinition {
206+
newCRD := crd(mainCRDPlural)
207+
newCRD.Spec.Version = "v1alpha2"
208+
return newCRD
209+
}(),
210+
expectedFailure: true,
211+
},
212+
{
213+
name: "existing version is present in new CRD's versions",
214+
oldCRD: func() v1beta1.CustomResourceDefinition {
215+
oldCRD := crd(mainCRDPlural)
216+
oldCRD.Spec.Version = "v1alpha1"
217+
return oldCRD
218+
}(),
219+
newCRD: func() v1beta1.CustomResourceDefinition {
220+
newCRD := crd(mainCRDPlural)
221+
newCRD.Spec.Version = ""
222+
newCRD.Spec.Versions = addedVersions
223+
return newCRD
224+
}(),
225+
expectedFailure: false,
226+
},
198227
}
199228

200229
for _, tt := range tests {

test/e2e/installplan_e2e_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,39 @@ func TestInstallPlanWithCRDSchemaChange(t *testing.T) {
823823
return &newCRD
824824
}(),
825825
},
826+
{
827+
name: "existing version is present in new CRD (deprecated field)",
828+
expectedPhase: v1alpha1.InstallPlanPhaseComplete,
829+
oldCRD: func() *apiextensions.CustomResourceDefinition {
830+
oldCRD := newCRD(mainCRDPlural + "d")
831+
oldCRD.Spec.Version = "v1alpha1"
832+
return &oldCRD
833+
}(),
834+
newCRD: func() *apiextensions.CustomResourceDefinition {
835+
newCRD := newCRD(mainCRDPlural + "d")
836+
newCRD.Spec.Version = "v1alpha1"
837+
newCRD.Spec.Validation = &apiextensions.CustomResourceValidation{
838+
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
839+
Type: "object",
840+
Properties: map[string]apiextensions.JSONSchemaProps{
841+
"spec": {
842+
Type: "object",
843+
Description: "Spec of a test object.",
844+
Properties: map[string]apiextensions.JSONSchemaProps{
845+
"scalar": {
846+
Type: "number",
847+
Description: "Scalar value that should have a min and max.",
848+
Minimum: &min,
849+
Maximum: &max,
850+
},
851+
},
852+
},
853+
},
854+
},
855+
}
856+
return &newCRD
857+
}(),
858+
},
826859
}
827860

828861
for _, tt := range tests {

0 commit comments

Comments
 (0)