k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/apiserver/apply/apply_crd_beta_test.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package apiserver 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 "testing" 24 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 27 apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" 28 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 29 "k8s.io/apiextensions-apiserver/test/integration/fixtures" 30 apierrors "k8s.io/apimachinery/pkg/api/errors" 31 "k8s.io/apimachinery/pkg/types" 32 "k8s.io/client-go/dynamic" 33 apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" 34 "k8s.io/kubernetes/test/integration/framework" 35 ) 36 37 // TestApplyCRDNoSchema tests that CRDs and CRs can both be applied to with a PATCH request with the apply content type 38 // when there is no validation field provided. 39 func TestApplyCRDNoSchema(t *testing.T) { 40 server, err := apiservertesting.StartTestServer(t, apiservertesting.NewDefaultTestServerOptions(), nil, framework.SharedEtcd()) 41 if err != nil { 42 t.Fatal(err) 43 } 44 defer server.TearDownFn() 45 config := server.ClientConfig 46 47 apiExtensionClient, err := clientset.NewForConfig(config) 48 if err != nil { 49 t.Fatal(err) 50 } 51 dynamicClient, err := dynamic.NewForConfig(config) 52 if err != nil { 53 t.Fatal(err) 54 } 55 56 noxuBetaDefinition := nearlyRemovedBetaMultipleVersionNoxuCRD(apiextensionsv1beta1.ClusterScoped) 57 58 noxuDefinition, err := fixtures.CreateCRDUsingRemovedAPI(server.EtcdClient, server.EtcdStoragePrefix, noxuBetaDefinition, apiExtensionClient, dynamicClient) 59 if err != nil { 60 t.Fatal(err) 61 } 62 63 kind := noxuDefinition.Spec.Names.Kind 64 apiVersion := noxuDefinition.Spec.Group + "/" + noxuDefinition.Spec.Versions[0].Name 65 name := "mytest" 66 67 rest := apiExtensionClient.Discovery().RESTClient() 68 yamlBody := []byte(fmt.Sprintf(` 69 apiVersion: %s 70 kind: %s 71 metadata: 72 name: %s 73 spec: 74 replicas: 1`, apiVersion, kind, name)) 75 result, err := rest.Patch(types.ApplyPatchType). 76 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural). 77 Name(name). 78 Param("fieldManager", "apply_test"). 79 Body(yamlBody). 80 DoRaw(context.TODO()) 81 if err != nil { 82 t.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(result)) 83 } 84 verifyReplicas(t, result, 1) 85 86 // Patch object to change the number of replicas 87 result, err = rest.Patch(types.MergePatchType). 88 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural). 89 Name(name). 90 Body([]byte(`{"spec":{"replicas": 5}}`)). 91 DoRaw(context.TODO()) 92 if err != nil { 93 t.Fatalf("failed to update number of replicas with merge patch: %v:\n%v", err, string(result)) 94 } 95 verifyReplicas(t, result, 5) 96 97 // Re-apply, we should get conflicts now, since the number of replicas was changed. 98 result, err = rest.Patch(types.ApplyPatchType). 99 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural). 100 Name(name). 101 Param("fieldManager", "apply_test"). 102 Body(yamlBody). 103 DoRaw(context.TODO()) 104 if err == nil { 105 t.Fatalf("Expecting to get conflicts when applying object after updating replicas, got no error: %s", result) 106 } 107 status, ok := err.(*apierrors.StatusError) 108 if !ok { 109 t.Fatalf("Expecting to get conflicts as API error") 110 } 111 if len(status.Status().Details.Causes) != 1 { 112 t.Fatalf("Expecting to get one conflict when applying object after updating replicas, got: %v", status.Status().Details.Causes) 113 } 114 115 // Re-apply with force, should work fine. 116 result, err = rest.Patch(types.ApplyPatchType). 117 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural). 118 Name(name). 119 Param("force", "true"). 120 Param("fieldManager", "apply_test"). 121 Body(yamlBody). 122 DoRaw(context.TODO()) 123 if err != nil { 124 t.Fatalf("failed to apply object with force after updating replicas: %v:\n%v", err, string(result)) 125 } 126 verifyReplicas(t, result, 1) 127 128 // Try to set managed fields using a subresource and verify that it has no effect 129 existingManagedFields, err := getManagedFields(result) 130 if err != nil { 131 t.Fatalf("failed to get managedFields from response: %v", err) 132 } 133 updateBytes := []byte(`{ 134 "metadata": { 135 "managedFields": [{ 136 "manager":"testing", 137 "operation":"Update", 138 "apiVersion":"v1", 139 "fieldsType":"FieldsV1", 140 "fieldsV1":{ 141 "f:spec":{ 142 "f:containers":{ 143 "k:{\"name\":\"testing\"}":{ 144 ".":{}, 145 "f:image":{}, 146 "f:name":{} 147 } 148 } 149 } 150 } 151 }] 152 } 153 }`) 154 result, err = rest.Patch(types.MergePatchType). 155 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural). 156 SubResource("status"). 157 Name(name). 158 Param("fieldManager", "subresource_test"). 159 Body(updateBytes). 160 DoRaw(context.TODO()) 161 if err != nil { 162 t.Fatalf("Error updating subresource: %v ", err) 163 } 164 newManagedFields, err := getManagedFields(result) 165 if err != nil { 166 t.Fatalf("failed to get managedFields from response: %v", err) 167 } 168 if !reflect.DeepEqual(existingManagedFields, newManagedFields) { 169 t.Fatalf("Expected managed fields to not have changed when trying manually setting them via subresoures.\n\nExpected: %#v\n\nGot: %#v", existingManagedFields, newManagedFields) 170 } 171 172 // However, it is possible to modify managed fields using the main resource 173 result, err = rest.Patch(types.MergePatchType). 174 AbsPath("/apis", noxuDefinition.Spec.Group, noxuDefinition.Spec.Versions[0].Name, noxuDefinition.Spec.Names.Plural). 175 Name(name). 176 Param("fieldManager", "subresource_test"). 177 Body([]byte(`{"metadata":{"managedFields":[{}]}}`)). 178 DoRaw(context.TODO()) 179 if err != nil { 180 t.Fatalf("Error updating managed fields of the main resource: %v ", err) 181 } 182 newManagedFields, err = getManagedFields(result) 183 if err != nil { 184 t.Fatalf("failed to get managedFields from response: %v", err) 185 } 186 187 if len(newManagedFields) != 0 { 188 t.Fatalf("Expected managed fields to have been reset, but got: %v", newManagedFields) 189 } 190 } 191 192 func nearlyRemovedBetaMultipleVersionNoxuCRD(scope apiextensionsv1beta1.ResourceScope) *apiextensionsv1beta1.CustomResourceDefinition { 193 return &apiextensionsv1beta1.CustomResourceDefinition{ 194 ObjectMeta: metav1.ObjectMeta{Name: "noxus.mygroup.example.com"}, 195 Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ 196 Group: "mygroup.example.com", 197 Version: "v1beta1", 198 Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ 199 Plural: "noxus", 200 Singular: "nonenglishnoxu", 201 Kind: "WishIHadChosenNoxu", 202 ShortNames: []string{"foo", "bar", "abc", "def"}, 203 ListKind: "NoxuItemList", 204 Categories: []string{"all"}, 205 }, 206 Scope: scope, 207 Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{ 208 { 209 Name: "v1beta1", 210 Served: true, 211 Storage: false, 212 }, 213 { 214 Name: "v1beta2", 215 Served: true, 216 Storage: true, 217 }, 218 { 219 Name: "v0", 220 Served: false, 221 Storage: false, 222 }, 223 }, 224 Subresources: &apiextensionsv1beta1.CustomResourceSubresources{ 225 Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{}, 226 }, 227 }, 228 } 229 }