sigs.k8s.io/cluster-api@v1.7.1/internal/controllers/topology/cluster/patches/patch_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 patches 18 19 import ( 20 "testing" 21 22 . "github.com/onsi/gomega" 23 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 24 25 "sigs.k8s.io/cluster-api/internal/contract" 26 ) 27 28 func TestCopySpec(t *testing.T) { 29 tests := []struct { 30 name string 31 input copySpecInput 32 want *unstructured.Unstructured 33 wantErr bool 34 }{ 35 { 36 name: "Field both in src and dest, no-op when equal", 37 input: copySpecInput{ 38 src: &unstructured.Unstructured{ 39 Object: map[string]interface{}{ 40 "spec": map[string]interface{}{ 41 "A": "A", 42 }, 43 }, 44 }, 45 dest: &unstructured.Unstructured{ 46 Object: map[string]interface{}{ 47 "spec": map[string]interface{}{ 48 "A": "A", 49 }, 50 }, 51 }, 52 srcSpecPath: "spec", 53 destSpecPath: "spec", 54 }, 55 want: &unstructured.Unstructured{ 56 Object: map[string]interface{}{ 57 "spec": map[string]interface{}{ 58 "A": "A", 59 }, 60 }, 61 }, 62 }, 63 { 64 name: "Field both in src and dest, overwrite dest when different", 65 input: copySpecInput{ 66 src: &unstructured.Unstructured{ 67 Object: map[string]interface{}{ 68 "spec": map[string]interface{}{ 69 "A": "A", 70 }, 71 }, 72 }, 73 dest: &unstructured.Unstructured{ 74 Object: map[string]interface{}{ 75 "spec": map[string]interface{}{ 76 "A": "A-different", 77 }, 78 }, 79 }, 80 srcSpecPath: "spec", 81 destSpecPath: "spec", 82 }, 83 want: &unstructured.Unstructured{ 84 Object: map[string]interface{}{ 85 "spec": map[string]interface{}{ 86 "A": "A", 87 }, 88 }, 89 }, 90 }, 91 { 92 name: "Nested field both in src and dest, no-op when equal", 93 input: copySpecInput{ 94 src: &unstructured.Unstructured{ 95 Object: map[string]interface{}{ 96 "spec": map[string]interface{}{ 97 "template": map[string]interface{}{ 98 "spec": map[string]interface{}{ 99 "A": "A", 100 }, 101 }, 102 }, 103 }, 104 }, 105 dest: &unstructured.Unstructured{ 106 Object: map[string]interface{}{ 107 "spec": map[string]interface{}{ 108 "template": map[string]interface{}{ 109 "spec": map[string]interface{}{ 110 "A": "A", 111 }, 112 }, 113 }, 114 }, 115 }, 116 srcSpecPath: "spec", 117 destSpecPath: "spec", 118 }, 119 want: &unstructured.Unstructured{ 120 Object: map[string]interface{}{ 121 "spec": map[string]interface{}{ 122 "template": map[string]interface{}{ 123 "spec": map[string]interface{}{ 124 "A": "A", 125 }, 126 }, 127 }, 128 }, 129 }, 130 }, 131 { 132 name: "Nested field both in src and dest, overwrite dest when different", 133 input: copySpecInput{ 134 src: &unstructured.Unstructured{ 135 Object: map[string]interface{}{ 136 "spec": map[string]interface{}{ 137 "template": map[string]interface{}{ 138 "spec": map[string]interface{}{ 139 "A": "A", 140 }, 141 }, 142 }, 143 }, 144 }, 145 dest: &unstructured.Unstructured{ 146 Object: map[string]interface{}{ 147 "spec": map[string]interface{}{ 148 "template": map[string]interface{}{ 149 "spec": map[string]interface{}{ 150 "A": "A-different", 151 }, 152 }, 153 }, 154 }, 155 }, 156 srcSpecPath: "spec", 157 destSpecPath: "spec", 158 }, 159 want: &unstructured.Unstructured{ 160 Object: map[string]interface{}{ 161 "spec": map[string]interface{}{ 162 "template": map[string]interface{}{ 163 "spec": map[string]interface{}{ 164 "A": "A", 165 }, 166 }, 167 }, 168 }, 169 }, 170 }, 171 { 172 name: "Field only in src, copy to dest", 173 input: copySpecInput{ 174 src: &unstructured.Unstructured{ 175 Object: map[string]interface{}{ 176 "spec": map[string]interface{}{ 177 "foo": "bar", 178 }, 179 }, 180 }, 181 dest: &unstructured.Unstructured{ 182 Object: map[string]interface{}{}, 183 }, 184 srcSpecPath: "spec", 185 destSpecPath: "spec", 186 }, 187 want: &unstructured.Unstructured{ 188 Object: map[string]interface{}{ 189 "spec": map[string]interface{}{ 190 "foo": "bar", 191 }, 192 }, 193 }, 194 }, 195 { 196 name: "Nested field only in src, copy to dest", 197 input: copySpecInput{ 198 src: &unstructured.Unstructured{ 199 Object: map[string]interface{}{ 200 "spec": map[string]interface{}{ 201 "template": map[string]interface{}{ 202 "spec": map[string]interface{}{ 203 "A": "A", 204 }, 205 }, 206 }, 207 }, 208 }, 209 dest: &unstructured.Unstructured{ 210 Object: map[string]interface{}{}, 211 }, 212 srcSpecPath: "spec", 213 destSpecPath: "spec", 214 }, 215 want: &unstructured.Unstructured{ 216 Object: map[string]interface{}{ 217 "spec": map[string]interface{}{ 218 "template": map[string]interface{}{ 219 "spec": map[string]interface{}{ 220 "A": "A", 221 }, 222 }, 223 }, 224 }, 225 }, 226 }, 227 { 228 name: "Copy field from spec.template.spec in src to spec in dest", 229 input: copySpecInput{ 230 src: &unstructured.Unstructured{ 231 Object: map[string]interface{}{ 232 "spec": map[string]interface{}{ 233 "template": map[string]interface{}{ 234 "spec": map[string]interface{}{ 235 "A": "A", 236 }, 237 }, 238 }, 239 }, 240 }, 241 dest: &unstructured.Unstructured{ 242 Object: map[string]interface{}{}, 243 }, 244 srcSpecPath: "spec.template.spec", 245 destSpecPath: "spec", 246 }, 247 want: &unstructured.Unstructured{ 248 Object: map[string]interface{}{ 249 "spec": map[string]interface{}{ 250 "A": "A", 251 }, 252 }, 253 }, 254 }, 255 { 256 name: "Copy field from spec.template.spec in src to spec in dest (overwrite when different)", 257 input: copySpecInput{ 258 src: &unstructured.Unstructured{ 259 Object: map[string]interface{}{ 260 "spec": map[string]interface{}{ 261 "template": map[string]interface{}{ 262 "spec": map[string]interface{}{ 263 "A": "A", 264 }, 265 }, 266 }, 267 }, 268 }, 269 dest: &unstructured.Unstructured{ 270 Object: map[string]interface{}{ 271 "spec": map[string]interface{}{ 272 "A": "A-different", 273 }, 274 }, 275 }, 276 srcSpecPath: "spec.template.spec", 277 destSpecPath: "spec", 278 }, 279 want: &unstructured.Unstructured{ 280 Object: map[string]interface{}{ 281 "spec": map[string]interface{}{ 282 "A": "A", 283 }, 284 }, 285 }, 286 }, 287 { 288 name: "Field both in src and dest, overwrite when different and preserve fields", 289 input: copySpecInput{ 290 src: &unstructured.Unstructured{ 291 Object: map[string]interface{}{ 292 "spec": map[string]interface{}{ 293 "template": map[string]interface{}{ 294 "spec": map[string]interface{}{ 295 "machineTemplate": map[string]interface{}{ 296 "infrastructureRef": map[string]interface{}{ 297 "apiVersion": "invalid", 298 "kind": "invalid", 299 "namespace": "invalid", 300 "name": "invalid", 301 }, 302 }, 303 "replicas": float64(10), 304 "version": "v1.15.0", 305 "A": "A", 306 }, 307 }, 308 }, 309 }, 310 }, 311 dest: &unstructured.Unstructured{ 312 Object: map[string]interface{}{ 313 "spec": map[string]interface{}{ 314 "machineTemplate": map[string]interface{}{ 315 "infrastructureRef": map[string]interface{}{ 316 "apiVersion": "v1", 317 "kind": "kind", 318 "namespace": "namespace", 319 "name": "name", 320 }, 321 }, 322 "replicas": float64(3), 323 "version": "v1.22.0", 324 "A": "A-different", 325 }, 326 }, 327 }, 328 srcSpecPath: "spec.template.spec", 329 destSpecPath: "spec", 330 fieldsToPreserve: []contract.Path{ 331 {"spec", "machineTemplate", "infrastructureRef"}, 332 {"spec", "replicas"}, 333 {"spec", "version"}, 334 }, 335 }, 336 want: &unstructured.Unstructured{ 337 Object: map[string]interface{}{ 338 "spec": map[string]interface{}{ 339 "machineTemplate": map[string]interface{}{ 340 "infrastructureRef": map[string]interface{}{ 341 "apiVersion": "v1", 342 "kind": "kind", 343 "namespace": "namespace", 344 "name": "name", 345 }, 346 }, 347 "replicas": float64(3), 348 "version": "v1.22.0", 349 "A": "A", 350 }, 351 }, 352 }, 353 }, 354 } 355 356 for _, tt := range tests { 357 t.Run(tt.name, func(t *testing.T) { 358 g := NewWithT(t) 359 360 err := copySpec(tt.input) 361 if tt.wantErr { 362 g.Expect(err).To(HaveOccurred()) 363 return 364 } 365 g.Expect(err).ToNot(HaveOccurred()) 366 367 g.Expect(tt.input.dest).To(BeComparableTo(tt.want)) 368 }) 369 } 370 }