@@ -244,6 +244,237 @@ func TestNoOpUpdateSameResourceVersion(t *testing.T) {
244
244
}
245
245
}
246
246
247
+ // TestNoOpApplyWithEmptyMap
248
+ func TestNoOpApplyWithEmptyMap (t * testing.T ) {
249
+ client , closeFn := setup (t )
250
+ defer closeFn ()
251
+
252
+ deploymentName := "no-op"
253
+ deploymentsResource := "deployments"
254
+ deploymentBytes := []byte (`{
255
+ "apiVersion": "apps/v1",
256
+ "kind": "Deployment",
257
+ "metadata": {
258
+ "name": "` + deploymentName + `",
259
+ "labels": {
260
+ "app": "nginx"
261
+ }
262
+ },
263
+ "spec": {
264
+ "replicas": 1,
265
+ "selector": {
266
+ "matchLabels": {
267
+ "app": "nginx"
268
+ }
269
+ },
270
+ "template": {
271
+ "metadata": {
272
+ "annotations": {},
273
+ "labels": {
274
+ "app": "nginx"
275
+ }
276
+ },
277
+ "spec": {
278
+ "containers": [{
279
+ "name": "nginx",
280
+ "image": "nginx:1.14.2",
281
+ "ports": [{
282
+ "containerPort": 80
283
+ }]
284
+ }]
285
+ }
286
+ }
287
+ }
288
+ }` )
289
+
290
+ _ , err := client .AppsV1 ().RESTClient ().Patch (types .ApplyPatchType ).
291
+ Namespace ("default" ).
292
+ Param ("fieldManager" , "apply_test" ).
293
+ Resource (deploymentsResource ).
294
+ Name (deploymentName ).
295
+ Body (deploymentBytes ).
296
+ Do (context .TODO ()).
297
+ Get ()
298
+ if err != nil {
299
+ t .Fatalf ("Failed to create object: %v" , err )
300
+ }
301
+
302
+ // This sleep is necessary to consistently produce different timestamps because the time field in managedFields has
303
+ // 1 second granularity and if both apply requests happen during the same second, this test would flake.
304
+ time .Sleep (1 * time .Second )
305
+
306
+ createdObject , err := client .AppsV1 ().RESTClient ().Get ().Namespace ("default" ).Resource (deploymentsResource ).Name (deploymentName ).Do (context .TODO ()).Get ()
307
+ if err != nil {
308
+ t .Fatalf ("Failed to retrieve created object: %v" , err )
309
+ }
310
+
311
+ createdAccessor , err := meta .Accessor (createdObject )
312
+ if err != nil {
313
+ t .Fatalf ("Failed to get meta accessor for created object: %v" , err )
314
+ }
315
+
316
+ createdBytes , err := json .MarshalIndent (createdObject , "\t " , "\t " )
317
+ if err != nil {
318
+ t .Fatalf ("Failed to marshal created object: %v" , err )
319
+ }
320
+
321
+ // Test that we can apply the same object and don't change the RV
322
+ _ , err = client .AppsV1 ().RESTClient ().Patch (types .ApplyPatchType ).
323
+ Namespace ("default" ).
324
+ Param ("fieldManager" , "apply_test" ).
325
+ Resource (deploymentsResource ).
326
+ Name (deploymentName ).
327
+ Body (deploymentBytes ).
328
+ Do (context .TODO ()).
329
+ Get ()
330
+ if err != nil {
331
+ t .Fatalf ("Failed to create object: %v" , err )
332
+ }
333
+
334
+ updatedObject , err := client .AppsV1 ().RESTClient ().Get ().Namespace ("default" ).Resource (deploymentsResource ).Name (deploymentName ).Do (context .TODO ()).Get ()
335
+ if err != nil {
336
+ t .Fatalf ("Failed to retrieve updated object: %v" , err )
337
+ }
338
+
339
+ updatedAccessor , err := meta .Accessor (updatedObject )
340
+ if err != nil {
341
+ t .Fatalf ("Failed to get meta accessor for updated object: %v" , err )
342
+ }
343
+
344
+ updatedBytes , err := json .MarshalIndent (updatedObject , "\t " , "\t " )
345
+ if err != nil {
346
+ t .Fatalf ("Failed to marshal updated object: %v" , err )
347
+ }
348
+
349
+ if createdAccessor .GetResourceVersion () != updatedAccessor .GetResourceVersion () {
350
+ t .Fatalf ("Expected same resource version to be %v but got: %v\n old object:\n %v\n new object:\n %v" ,
351
+ createdAccessor .GetResourceVersion (),
352
+ updatedAccessor .GetResourceVersion (),
353
+ string (createdBytes ),
354
+ string (updatedBytes ),
355
+ )
356
+ }
357
+ }
358
+
359
+ // TestApplyEmptyMarkerStructDifferentFromNil
360
+ func TestApplyEmptyMarkerStructDifferentFromNil (t * testing.T ) {
361
+ client , closeFn := setup (t )
362
+ defer closeFn ()
363
+
364
+ podName := "pod-with-empty-dir"
365
+ podsResource := "pods"
366
+ podBytesWithEmptyDir := []byte (`{
367
+ "apiVersion": "v1",
368
+ "kind": "Pod",
369
+ "metadata": {
370
+ "name": "` + podName + `"
371
+ },
372
+ "spec": {
373
+ "containers": [{
374
+ "name": "test-container-a",
375
+ "image": "test-image-one",
376
+ "volumeMounts": [{
377
+ "mountPath": "/cache",
378
+ "name": "cache-volume"
379
+ }],
380
+ }],
381
+ "volumes": [{
382
+ "name": "cache-volume",
383
+ "emptyDir": {}
384
+ }]
385
+ }
386
+ }` )
387
+
388
+ _ , err := client .CoreV1 ().RESTClient ().Patch (types .ApplyPatchType ).
389
+ Namespace ("default" ).
390
+ Param ("fieldManager" , "apply_test" ).
391
+ Resource (podsResource ).
392
+ Name (podName ).
393
+ Body (podBytesWithEmptyDir ).
394
+ Do (context .TODO ()).
395
+ Get ()
396
+ if err != nil {
397
+ t .Fatalf ("Failed to create object: %v" , err )
398
+ }
399
+
400
+ // This sleep is necessary to consistently produce different timestamps because the time field in managedFields has
401
+ // 1 second granularity and if both apply requests happen during the same second, this test would flake.
402
+ time .Sleep (1 * time .Second )
403
+
404
+ createdObject , err := client .CoreV1 ().RESTClient ().Get ().Namespace ("default" ).Resource (podsResource ).Name (podName ).Do (context .TODO ()).Get ()
405
+ if err != nil {
406
+ t .Fatalf ("Failed to retrieve created object: %v" , err )
407
+ }
408
+
409
+ createdAccessor , err := meta .Accessor (createdObject )
410
+ if err != nil {
411
+ t .Fatalf ("Failed to get meta accessor for created object: %v" , err )
412
+ }
413
+
414
+ createdBytes , err := json .MarshalIndent (createdObject , "\t " , "\t " )
415
+ if err != nil {
416
+ t .Fatalf ("Failed to marshal created object: %v" , err )
417
+ }
418
+
419
+ podBytesNoEmptyDir := []byte (`{
420
+ "apiVersion": "v1",
421
+ "kind": "Pod",
422
+ "metadata": {
423
+ "name": "` + podName + `"
424
+ },
425
+ "spec": {
426
+ "containers": [{
427
+ "name": "test-container-a",
428
+ "image": "test-image-one",
429
+ "volumeMounts": [{
430
+ "mountPath": "/cache",
431
+ "name": "cache-volume"
432
+ }],
433
+ }],
434
+ "volumes": [{
435
+ "name": "cache-volume"
436
+ }]
437
+ }
438
+ }` )
439
+
440
+ // Test that an apply with no emptyDir is recognized as distinct from an empty marker struct emptyDir.
441
+ _ , err = client .CoreV1 ().RESTClient ().Patch (types .ApplyPatchType ).
442
+ Namespace ("default" ).
443
+ Param ("fieldManager" , "apply_test" ).
444
+ Resource (podsResource ).
445
+ Name (podName ).
446
+ Body (podBytesNoEmptyDir ).
447
+ Do (context .TODO ()).
448
+ Get ()
449
+ if err != nil {
450
+ t .Fatalf ("Failed to create object: %v" , err )
451
+ }
452
+
453
+ updatedObject , err := client .CoreV1 ().RESTClient ().Get ().Namespace ("default" ).Resource (podsResource ).Name (podName ).Do (context .TODO ()).Get ()
454
+ if err != nil {
455
+ t .Fatalf ("Failed to retrieve updated object: %v" , err )
456
+ }
457
+
458
+ updatedAccessor , err := meta .Accessor (updatedObject )
459
+ if err != nil {
460
+ t .Fatalf ("Failed to get meta accessor for updated object: %v" , err )
461
+ }
462
+
463
+ updatedBytes , err := json .MarshalIndent (updatedObject , "\t " , "\t " )
464
+ if err != nil {
465
+ t .Fatalf ("Failed to marshal updated object: %v" , err )
466
+ }
467
+
468
+ if createdAccessor .GetResourceVersion () == updatedAccessor .GetResourceVersion () {
469
+ t .Fatalf ("Expected different resource version to be %v but got: %v\n old object:\n %v\n new object:\n %v" ,
470
+ createdAccessor .GetResourceVersion (),
471
+ updatedAccessor .GetResourceVersion (),
472
+ string (createdBytes ),
473
+ string (updatedBytes ),
474
+ )
475
+ }
476
+ }
477
+
247
478
func getRV (obj runtime.Object ) (string , error ) {
248
479
acc , err := meta .Accessor (obj )
249
480
if err != nil {
0 commit comments