github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/managedfields/extract_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 managedfields 18 19 import ( 20 "testing" 21 22 "github.com/google/go-cmp/cmp" 23 "sigs.k8s.io/structured-merge-diff/v4/typed" 24 25 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/api/equality" 26 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/api/meta" 27 metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1" 28 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1/unstructured" 29 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime" 30 runtimeschema "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema" 31 ) 32 33 func TestExtractInto(t *testing.T) { 34 one := int32(1) 35 parser, err := typed.NewParser(schemaYAML) 36 if err != nil { 37 t.Fatalf("Failed to parse schema: %v", err) 38 } 39 cases := []struct { 40 name string 41 obj runtime.Object 42 objType typed.ParseableType 43 managedFields []metav1.ManagedFieldsEntry // written to object before test is run 44 fieldManager string 45 expectedOut interface{} 46 subresource string 47 }{ 48 { 49 name: "unstructured, no matching manager", 50 obj: &unstructured.Unstructured{Object: map[string]interface{}{"spec": map[string]interface{}{"replicas": 1}}}, 51 objType: parser.Type("io.k8s.api.apps.v1.Deployment"), 52 managedFields: []metav1.ManagedFieldsEntry{ 53 applyFieldsEntry("mgr999", `{ "f:spec": { "f:replicas": {}}}`, ""), 54 }, 55 fieldManager: "mgr1", 56 expectedOut: map[string]interface{}{}, 57 }, 58 { 59 name: "unstructured, one manager", 60 obj: &unstructured.Unstructured{Object: map[string]interface{}{"spec": map[string]interface{}{"replicas": 1}}}, 61 objType: parser.Type("io.k8s.api.apps.v1.Deployment"), 62 managedFields: []metav1.ManagedFieldsEntry{ 63 applyFieldsEntry("mgr1", `{ "f:spec": { "f:replicas": {}}}`, ""), 64 }, 65 fieldManager: "mgr1", 66 expectedOut: map[string]interface{}{"spec": map[string]interface{}{"replicas": 1}}, 67 }, 68 { 69 name: "unstructured, multiple manager", 70 obj: &unstructured.Unstructured{Object: map[string]interface{}{"spec": map[string]interface{}{"paused": true}}}, 71 objType: parser.Type("io.k8s.api.apps.v1.Deployment"), 72 managedFields: []metav1.ManagedFieldsEntry{ 73 applyFieldsEntry("mgr1", `{ "f:spec": { "f:replicas": {}}}`, ""), 74 applyFieldsEntry("mgr2", `{ "f:spec": { "f:paused": {}}}`, ""), 75 }, 76 fieldManager: "mgr2", 77 expectedOut: map[string]interface{}{"spec": map[string]interface{}{"paused": true}}, 78 }, 79 { 80 name: "structured, no matching manager", 81 obj: &fakeDeployment{Spec: fakeDeploymentSpec{Replicas: &one}}, 82 objType: parser.Type("io.k8s.api.apps.v1.Deployment"), 83 managedFields: []metav1.ManagedFieldsEntry{ 84 applyFieldsEntry("mgr999", `{ "f:spec": { "f:replicas": {}}}`, ""), 85 }, 86 fieldManager: "mgr1", 87 expectedOut: map[string]interface{}{}, 88 }, 89 { 90 name: "structured, one manager", 91 obj: &fakeDeployment{Spec: fakeDeploymentSpec{Replicas: &one}}, 92 objType: parser.Type("io.k8s.api.apps.v1.Deployment"), 93 managedFields: []metav1.ManagedFieldsEntry{ 94 applyFieldsEntry("mgr1", `{ "f:spec": { "f:replicas": {}}}`, ""), 95 }, 96 fieldManager: "mgr1", 97 expectedOut: map[string]interface{}{"spec": map[string]interface{}{"replicas": int64(1)}}, 98 }, 99 { 100 name: "structured, multiple manager", 101 obj: &fakeDeployment{Spec: fakeDeploymentSpec{Replicas: &one, Paused: true}}, 102 objType: parser.Type("io.k8s.api.apps.v1.Deployment"), 103 managedFields: []metav1.ManagedFieldsEntry{ 104 applyFieldsEntry("mgr1", `{ "f:spec": { "f:replicas": {}}}`, ""), 105 applyFieldsEntry("mgr2", `{ "f:spec": { "f:paused": {}}}`, ""), 106 }, 107 fieldManager: "mgr2", 108 expectedOut: map[string]interface{}{"spec": map[string]interface{}{"paused": true}}, 109 }, 110 { 111 name: "subresource", 112 obj: &fakeDeployment{Status: fakeDeploymentStatus{Replicas: &one}}, 113 objType: parser.Type("io.k8s.api.apps.v1.Deployment"), 114 managedFields: []metav1.ManagedFieldsEntry{ 115 applyFieldsEntry("mgr1", `{ "f:status": { "f:replicas": {}}}`, "status"), 116 }, 117 fieldManager: "mgr1", 118 expectedOut: map[string]interface{}{"status": map[string]interface{}{"replicas": int64(1)}}, 119 subresource: "status", 120 }, 121 } 122 for _, tc := range cases { 123 t.Run(tc.name, func(t *testing.T) { 124 out := map[string]interface{}{} 125 accessor, err := meta.Accessor(tc.obj) 126 if err != nil { 127 t.Fatalf("Error accessing object: %v", err) 128 } 129 accessor.SetManagedFields(tc.managedFields) 130 err = ExtractInto(tc.obj, tc.objType, tc.fieldManager, &out, tc.subresource) 131 if err != nil { 132 t.Fatalf("Unexpected extract error: %v", err) 133 } 134 if !equality.Semantic.DeepEqual(out, tc.expectedOut) { 135 t.Fatalf("Expected output did not match actual output: %s", cmp.Diff(out, tc.expectedOut)) 136 } 137 }) 138 } 139 } 140 141 func applyFieldsEntry(fieldManager string, fieldsJSON string, subresource string) metav1.ManagedFieldsEntry { 142 return metav1.ManagedFieldsEntry{ 143 Manager: fieldManager, 144 Operation: metav1.ManagedFieldsOperationApply, 145 APIVersion: "v1", 146 FieldsType: "FieldsV1", 147 FieldsV1: &metav1.FieldsV1{Raw: []byte(fieldsJSON)}, 148 Subresource: subresource, 149 } 150 } 151 152 type fakeDeployment struct { 153 metav1.ObjectMeta `json:"metadata,omitempty"` 154 Spec fakeDeploymentSpec `json:"spec"` 155 Status fakeDeploymentStatus `json:"status"` 156 } 157 158 type fakeDeploymentSpec struct { 159 Replicas *int32 `json:"replicas"` 160 Paused bool `json:"paused,omitempty"` 161 } 162 163 type fakeDeploymentStatus struct { 164 Replicas *int32 `json:"replicas"` 165 } 166 167 func (o *fakeDeployment) GetObjectMeta() metav1.ObjectMeta { 168 return o.ObjectMeta 169 } 170 func (o *fakeDeployment) GetObjectKind() runtimeschema.ObjectKind { 171 return runtimeschema.EmptyObjectKind 172 } 173 func (o *fakeDeployment) DeepCopyObject() runtime.Object { 174 return o 175 } 176 177 // trimmed up schema for test purposes 178 const schemaYAML = typed.YAMLObject(`types: 179 - name: io.k8s.api.apps.v1.Deployment 180 map: 181 fields: 182 - name: apiVersion 183 type: 184 scalar: string 185 - name: kind 186 type: 187 scalar: string 188 - name: metadata 189 type: 190 namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta 191 - name: spec 192 type: 193 namedType: io.k8s.api.apps.v1.DeploymentSpec 194 - name: status 195 type: 196 namedType: io.k8s.api.apps.v1.DeploymentStatus 197 - name: io.k8s.api.apps.v1.DeploymentSpec 198 map: 199 fields: 200 - name: paused 201 type: 202 scalar: boolean 203 - name: replicas 204 type: 205 scalar: numeric 206 - name: io.k8s.api.apps.v1.DeploymentStatus 207 map: 208 fields: 209 - name: replicas 210 type: 211 scalar: numeric 212 - name: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta 213 map: 214 fields: 215 - name: creationTimestamp 216 type: 217 namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time 218 - name: managedFields 219 type: 220 list: 221 elementType: 222 namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry 223 elementRelationship: atomic 224 - name: io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry 225 map: 226 fields: 227 - name: apiVersion 228 type: 229 scalar: string 230 - name: fieldsType 231 type: 232 scalar: string 233 - name: fieldsV1 234 type: 235 namedType: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1 236 - name: manager 237 type: 238 scalar: string 239 - name: operation 240 type: 241 scalar: string 242 - name: time 243 type: 244 namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time 245 - name: subresource 246 type: 247 scalar: string 248 - name: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1 249 map: 250 elementType: 251 scalar: untyped 252 list: 253 elementType: 254 namedType: __untyped_atomic_ 255 elementRelationship: atomic 256 map: 257 elementType: 258 namedType: __untyped_atomic_ 259 elementRelationship: atomic 260 - name: io.k8s.apimachinery.pkg.apis.meta.v1.Time 261 scalar: untyped 262 - name: __untyped_atomic_ 263 scalar: untyped 264 list: 265 elementType: 266 namedType: __untyped_atomic_ 267 elementRelationship: atomic 268 map: 269 elementType: 270 namedType: __untyped_atomic_ 271 elementRelationship: atomic 272 `)