6capiextensions: handle CRD conflict errs in integration tests

pull/8/head
Dr. Stefan Schimanski 2018-05-09 12:14:44 +02:00
parent 0949cefd7b
commit 5b78c3a41d
3 changed files with 44 additions and 58 deletions

View File

@ -22,7 +22,6 @@ import (
"sort"
"strings"
"testing"
"time"
autoscaling "k8s.io/api/autoscaling/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
@ -30,7 +29,6 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/client-go/dynamic"
@ -392,9 +390,8 @@ func TestValidateOnlyStatus(t *testing.T) {
// UpdateStatus should validate only status
// 1. create a crd with max value of .spec.num = 10 and .status.num = 10
// 2. create a cr with .spec.num = 10 and .status.num = 10 (valid)
// 3. update the crd so that max value of .spec.num = 5 and .status.num = 10
// 4. update the status of the cr with .status.num = 5 (spec is invalid)
// validation passes becauses spec is not validated
// 3. update the spec of the cr with .spec.num = 15 (spec is invalid), expect no error
// 4. update the spec of the cr with .spec.num = 15 (spec is invalid), expect error
// max value of spec.num = 10 and status.num = 10
schema := &apiextensionsv1beta1.JSONSchemaProps{
@ -443,58 +440,31 @@ func TestValidateOnlyStatus(t *testing.T) {
t.Fatalf("unable to create noxu instance: %v", err)
}
gottenCRD, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get("noxus.mygroup.example.com", metav1.GetOptions{})
// update the spec with .spec.num = 15, expecting no error
err = unstructured.SetNestedField(createdNoxuInstance.Object, int64(15), "spec", "num")
if err != nil {
t.Fatal(err)
t.Fatalf("unexpected error setting .spec.num: %v", err)
}
// update the crd so that max value of spec.num = 5 and status.num = 10
gottenCRD.Spec.Validation.OpenAPIV3Schema = &apiextensionsv1beta1.JSONSchemaProps{
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
"spec": {
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
"num": {
Type: "integer",
Maximum: float64Ptr(5),
},
},
},
"status": {
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
"num": {
Type: "integer",
Maximum: float64Ptr(10),
},
},
},
},
}
if _, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(gottenCRD); err != nil {
t.Fatal(err)
}
// update the status with .status.num = 5
err = unstructured.SetNestedField(createdNoxuInstance.Object, int64(5), "status", "num")
createdNoxuInstance, err = noxuStatusResourceClient.Update(createdNoxuInstance)
if err != nil {
t.Fatalf("unexpected error: %v", err)
t.Errorf("unexpected error: %v", err)
}
// cr is updated even though spec is invalid
err = wait.Poll(500*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
_, err := noxuStatusResourceClient.Update(createdNoxuInstance)
if statusError, isStatus := err.(*apierrors.StatusError); isStatus {
if strings.Contains(statusError.Error(), "is invalid") {
return false, nil
}
}
if err != nil {
return false, err
}
return true, nil
})
// update with .status.num = 15, expecting an error
err = unstructured.SetNestedField(createdNoxuInstance.Object, int64(15), "status", "num")
if err != nil {
t.Fatal(err)
t.Fatalf("unexpected error setting .status.num: %v", err)
}
createdNoxuInstance, err = noxuStatusResourceClient.Update(createdNoxuInstance)
if err == nil {
t.Fatal("expected error, but got none")
}
statusError, isStatus := err.(*apierrors.StatusError)
if !isStatus || statusError == nil {
t.Fatalf("expected status error, got %T: %v", err, err)
}
if !strings.Contains(statusError.Error(), "Invalid value") {
t.Fatalf("expected 'Invalid value' in error, got: %v", err)
}
}

View File

@ -274,6 +274,25 @@ func checkForWatchCachePrimed(crd *apiextensionsv1beta1.CustomResourceDefinition
}
}
// UpdateCustomResourceDefinition updates a CRD, retrying up to 5 times on version conflict errors.
func UpdateCustomResourceDefinition(client clientset.Interface, name string, update func(*apiextensionsv1beta1.CustomResourceDefinition)) (*apiextensionsv1beta1.CustomResourceDefinition, error) {
for i := 0; i < 5; i++ {
crd, err := client.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get CustomResourceDefinition %q: %v", name, err)
}
update(crd)
crd, err = client.ApiextensionsV1beta1().CustomResourceDefinitions().Update(crd)
if err == nil {
return crd, nil
}
if !errors.IsConflict(err) {
return nil, fmt.Errorf("failed to update CustomResourceDefinition %q: %v", name, err)
}
}
return nil, fmt.Errorf("too many retries after conflicts updating CustomResourceDefinition %q", name)
}
func DeleteCustomResourceDefinition(crd *apiextensionsv1beta1.CustomResourceDefinition, apiExtensionsClient clientset.Interface) error {
if err := apiExtensionsClient.Apiextensions().CustomResourceDefinitions().Delete(crd.Name, nil); err != nil {
return err

View File

@ -348,14 +348,11 @@ func TestCRValidationOnCRDUpdate(t *testing.T) {
t.Fatalf("unexpected non-error: CR should be rejected")
}
gottenCRD, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get("noxus.mygroup.example.com", metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
// update the CRD to a less stricter schema
gottenCRD.Spec.Validation.OpenAPIV3Schema.Required = []string{"alpha", "beta"}
if _, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(gottenCRD); err != nil {
_, err = testserver.UpdateCustomResourceDefinition(apiExtensionClient, "noxus.mygroup.example.com", func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
crd.Spec.Validation.OpenAPIV3Schema.Required = []string{"alpha", "beta"}
})
if err != nil {
t.Fatal(err)
}