sigs.k8s.io/cluster-api@v1.7.1/internal/controllers/topology/cluster/structuredmerge/dryrun_test.go (about) 1 /* 2 Copyright 2022 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 structuredmerge 18 19 import ( 20 "testing" 21 "time" 22 23 . "github.com/onsi/gomega" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 26 27 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 28 "sigs.k8s.io/cluster-api/util/conversion" 29 ) 30 31 func Test_cleanupManagedFieldsAndAnnotation(t *testing.T) { 32 rawManagedFieldWithAnnotation := `{"f:metadata":{"f:annotations":{"f:topology.cluster.x-k8s.io/dry-run":{},"f:cluster.x-k8s.io/conversion-data":{}}}}` 33 rawManagedFieldWithAnnotationSpecLabels := `{"f:metadata":{"f:annotations":{"f:topology.cluster.x-k8s.io/dry-run":{},"f:cluster.x-k8s.io/conversion-data":{}},"f:labels":{}},"f:spec":{"f:foo":{}}}` 34 rawManagedFieldWithSpecLabels := `{"f:metadata":{"f:labels":{}},"f:spec":{"f:foo":{}}}` 35 36 tests := []struct { 37 name string 38 obj *unstructured.Unstructured 39 wantErr bool 40 want *unstructured.Unstructured 41 }{ 42 { 43 name: "no-op", 44 obj: newObjectBuilder().Build(), 45 wantErr: false, 46 }, 47 { 48 name: "filter out dry-run annotation", 49 obj: newObjectBuilder(). 50 WithAnnotation(clusterv1.TopologyDryRunAnnotation, ""). 51 Build(), 52 wantErr: false, 53 want: newObjectBuilder(). 54 Build(), 55 }, 56 { 57 name: "filter out conversion annotation", 58 obj: newObjectBuilder(). 59 WithAnnotation(conversion.DataAnnotation, ""). 60 Build(), 61 wantErr: false, 62 want: newObjectBuilder(). 63 Build(), 64 }, 65 { 66 name: "managedFields: should drop managed fields of other manager", 67 obj: newObjectBuilder(). 68 WithManagedFieldsEntry("other", "", metav1.ManagedFieldsOperationApply, []byte(`{}`), nil). 69 Build(), 70 wantErr: false, 71 want: newObjectBuilder(). 72 Build(), 73 }, 74 { 75 name: "managedFields: should drop managed fields of a subresource", 76 obj: newObjectBuilder(). 77 WithManagedFieldsEntry(TopologyManagerName, "status", metav1.ManagedFieldsOperationApply, []byte(`{}`), nil). 78 Build(), 79 wantErr: false, 80 want: newObjectBuilder(). 81 Build(), 82 }, 83 { 84 name: "managedFields: should drop managed fields of another operation", 85 obj: newObjectBuilder(). 86 WithManagedFieldsEntry(TopologyManagerName, "", metav1.ManagedFieldsOperationUpdate, []byte(`{}`), nil). 87 Build(), 88 wantErr: false, 89 want: newObjectBuilder(). 90 Build(), 91 }, 92 { 93 name: "managedFields: cleanup up the managed field entry", 94 obj: newObjectBuilder(). 95 WithManagedFieldsEntry(TopologyManagerName, "", metav1.ManagedFieldsOperationApply, []byte(rawManagedFieldWithAnnotation), nil). 96 Build(), 97 wantErr: false, 98 want: newObjectBuilder(). 99 WithManagedFieldsEntry(TopologyManagerName, "", metav1.ManagedFieldsOperationApply, []byte(`{}`), nil). 100 Build(), 101 }, 102 { 103 name: "managedFields: cleanup the managed field entry and preserve other ownership", 104 obj: newObjectBuilder(). 105 WithManagedFieldsEntry(TopologyManagerName, "", metav1.ManagedFieldsOperationApply, []byte(rawManagedFieldWithAnnotationSpecLabels), nil). 106 Build(), 107 wantErr: false, 108 want: newObjectBuilder(). 109 WithManagedFieldsEntry(TopologyManagerName, "", metav1.ManagedFieldsOperationApply, []byte(rawManagedFieldWithSpecLabels), nil). 110 Build(), 111 }, 112 { 113 name: "managedFields: remove time", 114 obj: newObjectBuilder(). 115 WithManagedFieldsEntry(TopologyManagerName, "", metav1.ManagedFieldsOperationApply, []byte(`{}`), &metav1.Time{Time: time.Time{}}). 116 Build(), 117 wantErr: false, 118 want: newObjectBuilder(). 119 WithManagedFieldsEntry(TopologyManagerName, "", metav1.ManagedFieldsOperationApply, []byte(`{}`), nil). 120 Build(), 121 }, 122 } 123 for _, tt := range tests { 124 t.Run(tt.name, func(t *testing.T) { 125 g := NewWithT(t) 126 127 if err := cleanupManagedFieldsAndAnnotation(tt.obj); (err != nil) != tt.wantErr { 128 t.Errorf("cleanupManagedFieldsAndAnnotation() error = %v, wantErr %v", err, tt.wantErr) 129 } 130 if tt.want != nil { 131 g.Expect(tt.obj).To(BeEquivalentTo(tt.want)) 132 } 133 }) 134 } 135 } 136 137 type objectBuilder struct { 138 u *unstructured.Unstructured 139 } 140 141 func newObjectBuilder() objectBuilder { 142 u := &unstructured.Unstructured{Object: map[string]interface{}{}} 143 u.SetName("test") 144 u.SetNamespace("default") 145 146 return objectBuilder{ 147 u: u, 148 } 149 } 150 151 func (b objectBuilder) DeepCopy() objectBuilder { 152 return objectBuilder{b.u.DeepCopy()} 153 } 154 155 func (b objectBuilder) Build() *unstructured.Unstructured { 156 // Setting an empty managed field array if no managed field is set so there is 157 // no difference between an object which never had a managed field and one from 158 // which all managed field entries were removed. 159 if b.u.GetManagedFields() == nil { 160 b.u.SetManagedFields([]metav1.ManagedFieldsEntry{}) 161 } 162 return b.u.DeepCopy() 163 } 164 165 func (b objectBuilder) WithAnnotation(k, v string) objectBuilder { 166 annotations := b.u.GetAnnotations() 167 if annotations == nil { 168 annotations = map[string]string{} 169 } 170 annotations[k] = v 171 b.u.SetAnnotations(annotations) 172 return b 173 } 174 175 func (b objectBuilder) WithManagedFieldsEntry(manager, subresource string, operation metav1.ManagedFieldsOperationType, fieldsV1 []byte, time *metav1.Time) objectBuilder { 176 managedFields := append(b.u.GetManagedFields(), metav1.ManagedFieldsEntry{ 177 Manager: manager, 178 Operation: operation, 179 Subresource: subresource, 180 FieldsV1: &metav1.FieldsV1{Raw: fieldsV1}, 181 Time: time, 182 }) 183 b.u.SetManagedFields(managedFields) 184 return b 185 }