sigs.k8s.io/cluster-api@v1.7.1/exp/addons/internal/controllers/clusterresourceset_scope_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 controllers 18 19 import ( 20 "context" 21 "testing" 22 23 . "github.com/onsi/gomega" 24 corev1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 27 "sigs.k8s.io/controller-runtime/pkg/client" 28 "sigs.k8s.io/controller-runtime/pkg/client/fake" 29 30 addonsv1 "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1" 31 ) 32 33 func TestReconcileStrategyScopeNeedsApply(t *testing.T) { 34 tests := []struct { 35 name string 36 scope *reconcileStrategyScope 37 want bool 38 }{ 39 { 40 name: "no ResourceBinding", 41 scope: &reconcileStrategyScope{ 42 baseResourceReconcileScope: baseResourceReconcileScope{ 43 resourceSetBinding: &addonsv1.ResourceSetBinding{}, 44 resourceRef: addonsv1.ResourceRef{ 45 Name: "cp", 46 Kind: "ConfigMap", 47 }, 48 }, 49 }, 50 want: true, 51 }, 52 { 53 name: "not applied ResourceBinding", 54 scope: &reconcileStrategyScope{ 55 baseResourceReconcileScope: baseResourceReconcileScope{ 56 resourceSetBinding: &addonsv1.ResourceSetBinding{ 57 Resources: []addonsv1.ResourceBinding{ 58 { 59 ResourceRef: addonsv1.ResourceRef{ 60 Name: "cp", 61 Kind: "ConfigMap", 62 }, 63 Applied: false, 64 }, 65 }, 66 }, 67 resourceRef: addonsv1.ResourceRef{ 68 Name: "cp", 69 Kind: "ConfigMap", 70 }, 71 }, 72 }, 73 want: true, 74 }, 75 { 76 name: "applied ResourceBinding and different hash", 77 scope: &reconcileStrategyScope{ 78 baseResourceReconcileScope: baseResourceReconcileScope{ 79 resourceSetBinding: &addonsv1.ResourceSetBinding{ 80 Resources: []addonsv1.ResourceBinding{ 81 { 82 ResourceRef: addonsv1.ResourceRef{ 83 Name: "cp", 84 Kind: "ConfigMap", 85 }, 86 Applied: true, 87 Hash: "111", 88 }, 89 }, 90 }, 91 resourceRef: addonsv1.ResourceRef{ 92 Name: "cp", 93 Kind: "ConfigMap", 94 }, 95 computedHash: "222", 96 }, 97 }, 98 want: true, 99 }, 100 { 101 name: "applied ResourceBinding and same hash", 102 scope: &reconcileStrategyScope{ 103 baseResourceReconcileScope: baseResourceReconcileScope{ 104 resourceSetBinding: &addonsv1.ResourceSetBinding{ 105 Resources: []addonsv1.ResourceBinding{ 106 { 107 ResourceRef: addonsv1.ResourceRef{ 108 Name: "cp", 109 Kind: "ConfigMap", 110 }, 111 Applied: true, 112 Hash: "111", 113 }, 114 }, 115 }, 116 resourceRef: addonsv1.ResourceRef{ 117 Name: "cp", 118 Kind: "ConfigMap", 119 }, 120 computedHash: "111", 121 }, 122 }, 123 want: false, 124 }, 125 } 126 for _, tt := range tests { 127 t.Run(tt.name, func(t *testing.T) { 128 gs := NewWithT(t) 129 gs.Expect(tt.scope.needsApply()).To(Equal(tt.want)) 130 }) 131 } 132 } 133 134 func TestReconcileApplyOnceScopeNeedsApply(t *testing.T) { 135 tests := []struct { 136 name string 137 scope *reconcileApplyOnceScope 138 want bool 139 }{ 140 { 141 name: "not applied ResourceBinding", 142 scope: &reconcileApplyOnceScope{ 143 baseResourceReconcileScope: baseResourceReconcileScope{ 144 resourceSetBinding: &addonsv1.ResourceSetBinding{ 145 Resources: []addonsv1.ResourceBinding{ 146 { 147 ResourceRef: addonsv1.ResourceRef{ 148 Name: "cp", 149 Kind: "ConfigMap", 150 }, 151 Applied: false, 152 }, 153 }, 154 }, 155 resourceRef: addonsv1.ResourceRef{ 156 Name: "cp", 157 Kind: "ConfigMap", 158 }, 159 }, 160 }, 161 want: true, 162 }, 163 { 164 name: "applied ResourceBinding", 165 scope: &reconcileApplyOnceScope{ 166 baseResourceReconcileScope: baseResourceReconcileScope{ 167 resourceSetBinding: &addonsv1.ResourceSetBinding{ 168 Resources: []addonsv1.ResourceBinding{ 169 { 170 ResourceRef: addonsv1.ResourceRef{ 171 Name: "cp", 172 Kind: "ConfigMap", 173 }, 174 Applied: true, 175 }, 176 }, 177 }, 178 resourceRef: addonsv1.ResourceRef{ 179 Name: "cp", 180 Kind: "ConfigMap", 181 }, 182 }, 183 }, 184 want: false, 185 }, 186 } 187 for _, tt := range tests { 188 t.Run(tt.name, func(t *testing.T) { 189 gs := NewWithT(t) 190 gs.Expect(tt.scope.needsApply()).To(Equal(tt.want)) 191 }) 192 } 193 } 194 195 func TestReconcileApplyOnceScopeApplyObj(t *testing.T) { 196 tests := []struct { 197 name string 198 existingObjs []client.Object 199 obj *unstructured.Unstructured 200 wantErr string 201 }{ 202 { 203 name: "object doesn't exist", 204 obj: &unstructured.Unstructured{ 205 Object: map[string]interface{}{ 206 "apiVersion": "v1", 207 "kind": "ConfigMap", 208 "metadata": map[string]interface{}{ 209 "name": "my-cm", 210 "namespace": "that-ns", 211 }, 212 }, 213 }, 214 }, 215 { 216 name: "object exists", 217 existingObjs: []client.Object{ 218 &corev1.ConfigMap{ 219 ObjectMeta: metav1.ObjectMeta{ 220 Name: "my-cm", 221 Namespace: "that-ns", 222 }, 223 }, 224 }, 225 obj: &unstructured.Unstructured{ 226 Object: map[string]interface{}{ 227 "apiVersion": "v1", 228 "kind": "ConfigMap", 229 "metadata": map[string]interface{}{ 230 "name": "my-cm", 231 "namespace": "that-ns", 232 }, 233 }, 234 }, 235 }, 236 } 237 for _, tt := range tests { 238 t.Run(tt.name, func(t *testing.T) { 239 gs := NewWithT(t) 240 ctx := context.Background() 241 client := fake.NewClientBuilder().WithObjects(tt.existingObjs...).Build() 242 scope := &reconcileApplyOnceScope{} 243 err := scope.applyObj(ctx, client, tt.obj) 244 if tt.wantErr == "" { 245 gs.Expect(err).ToNot(HaveOccurred()) 246 } else { 247 gs.Expect(err).To(MatchError(ContainSubstring(tt.wantErr))) 248 } 249 }) 250 } 251 }