github.com/verrazzano/verrazzano@v1.7.1/cluster-operator/apis/clusters/v1alpha1/verrazzanomanagedcluster_webhook_test.go (about) 1 // Copyright (c) 2021, 2022, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package v1alpha1 5 6 import ( 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 11 "github.com/verrazzano/verrazzano/platform-operator/constants" 12 corev1 "k8s.io/api/core/v1" 13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 "sigs.k8s.io/controller-runtime/pkg/client" 15 "sigs.k8s.io/controller-runtime/pkg/client/fake" 16 ) 17 18 const testNameDefaultVZ = "defaultVz" 19 const testNameRancherEnabled = "rancher explicitly enabled in VZ" 20 const testNameRancherDisabled = "rancher disabled in VZ" 21 22 var falseValue = false 23 var trueValue = true 24 25 // List of Verrazzano resources that have status InstallComplete 26 27 // verrazzanoList contains Verrazzano with defaults and InstallComplete status condition 28 var verrazzanoList = &v1beta1.VerrazzanoList{ 29 Items: []v1beta1.Verrazzano{ 30 { 31 ObjectMeta: metav1.ObjectMeta{ 32 Name: "my-verrazzano", 33 Namespace: "default", 34 }, 35 Status: v1beta1.VerrazzanoStatus{ 36 Conditions: []v1beta1.Condition{ 37 { 38 Type: v1beta1.CondInstallComplete, 39 }, 40 }, 41 }, 42 }, 43 }, 44 } 45 46 // verrazzanoListRancherDisabled contains Verrazzano with Rancher disabled and InstallComplete status condition 47 var verrazzanoListRancherDisabled = &v1beta1.VerrazzanoList{ 48 Items: []v1beta1.Verrazzano{ 49 { 50 ObjectMeta: verrazzanoList.Items[0].ObjectMeta, 51 Status: verrazzanoList.Items[0].Status, 52 Spec: v1beta1.VerrazzanoSpec{ 53 Components: v1beta1.ComponentSpec{ 54 Rancher: &v1beta1.RancherComponent{Enabled: &falseValue}, 55 }, 56 }, 57 }, 58 }, 59 } 60 61 // verrazzanoListRancherEnabled contains Verrazzano with Rancher explicitly enabled and InstallComplete status condition 62 var verrazzanoListRancherEnabled = &v1beta1.VerrazzanoList{ 63 Items: []v1beta1.Verrazzano{ 64 { 65 ObjectMeta: verrazzanoList.Items[0].ObjectMeta, 66 Status: verrazzanoList.Items[0].Status, 67 Spec: v1beta1.VerrazzanoSpec{ 68 Components: v1beta1.ComponentSpec{ 69 Rancher: &v1beta1.RancherComponent{Enabled: &trueValue}, 70 }, 71 }, 72 }, 73 }, 74 } 75 76 // TestCreateWithSecretAndConfigMap tests the validation of a valid VerrazzanoManagedCluster secret and valid verrazzano-admin-cluster configmap 77 // GIVEN a call validate VerrazzanoManagedCluster 78 // WHEN the VerrazzanoManagedCluster has valid secret specified and verrazzano-admin-cluster configmap is valid 79 // THEN the validation should succeed in all cases 80 func TestCreateWithSecretAndConfigMap(t *testing.T) { 81 const secretName = "mySecret" 82 tests := []struct { 83 name string 84 verrazzanos *v1beta1.VerrazzanoList 85 }{ 86 {testNameDefaultVZ, verrazzanoList}, 87 {testNameRancherDisabled, verrazzanoListRancherDisabled}, 88 {testNameRancherEnabled, verrazzanoListRancherEnabled}, 89 } 90 for _, test := range tests { 91 t.Run(test.name, func(t *testing.T) { 92 93 // fake client needed to get secret 94 getClientFunc = func() (client.Client, error) { 95 return fake.NewClientBuilder().WithScheme(newScheme()).WithObjects( 96 &corev1.Secret{ 97 ObjectMeta: metav1.ObjectMeta{ 98 Name: secretName, 99 Namespace: constants.VerrazzanoMultiClusterNamespace, 100 }, 101 }, 102 &corev1.ConfigMap{ 103 ObjectMeta: metav1.ObjectMeta{ 104 Name: constants.AdminClusterConfigMapName, 105 Namespace: constants.VerrazzanoMultiClusterNamespace, 106 }, 107 Data: map[string]string{ 108 constants.ServerDataKey: "https://testUrl", 109 }, 110 }).WithLists(verrazzanoList).Build(), nil 111 } 112 defer func() { getClientFunc = getClient }() 113 114 // VMC to be validated 115 vz := VerrazzanoManagedCluster{ 116 TypeMeta: metav1.TypeMeta{}, 117 ObjectMeta: metav1.ObjectMeta{ 118 Name: "testMC", 119 Namespace: constants.VerrazzanoMultiClusterNamespace, 120 }, 121 Spec: VerrazzanoManagedClusterSpec{ 122 CASecret: secretName, 123 }, 124 } 125 err := vz.ValidateCreate() 126 assert.NoError(t, err, "Error validating VerrazzanoMultiCluster resource") 127 }) 128 } 129 } 130 131 // TestCreateNoConfigMap tests the validation of missing verrazzano-admin-cluster configmap 132 // GIVEN a call validate VerrazzanoManagedCluster 133 // WHEN the verrazzano-admin-cluster configmap doesn't exist 134 // THEN the validation should fail if Rancher is disabled, succeed otherwise 135 func TestCreateNoConfigMap(t *testing.T) { 136 const secretName = "mySecret" 137 138 tests := []struct { 139 name string 140 verrazzanos *v1beta1.VerrazzanoList 141 errorExpected bool 142 errMsgExpected string 143 }{ 144 {testNameDefaultVZ, verrazzanoList, false, ""}, 145 {testNameRancherDisabled, verrazzanoListRancherDisabled, true, "The ConfigMap verrazzano-admin-cluster does not exist in namespace verrazzano-mc"}, 146 {testNameRancherEnabled, verrazzanoListRancherEnabled, false, ""}, 147 } 148 for _, test := range tests { 149 t.Run(test.name, func(t *testing.T) { 150 // fake client needed to get secret 151 getClientFunc = func() (client.Client, error) { 152 return fake.NewClientBuilder().WithScheme(newScheme()).WithObjects( 153 &corev1.Secret{ 154 ObjectMeta: metav1.ObjectMeta{ 155 Name: secretName, 156 Namespace: constants.VerrazzanoMultiClusterNamespace, 157 }, 158 }).WithLists(test.verrazzanos).Build(), nil 159 } 160 defer func() { getClientFunc = getClient }() 161 162 // VMC to be validated 163 vz := VerrazzanoManagedCluster{ 164 TypeMeta: metav1.TypeMeta{}, 165 ObjectMeta: metav1.ObjectMeta{ 166 Name: "testMC", 167 Namespace: constants.VerrazzanoMultiClusterNamespace, 168 }, 169 Spec: VerrazzanoManagedClusterSpec{ 170 CASecret: secretName, 171 }, 172 } 173 err := vz.ValidateCreate() 174 if test.errorExpected { 175 assert.EqualError(t, err, test.errMsgExpected, "Expected correct error message") 176 } else { 177 assert.NoError(t, err, "Error validating VerrazzanoMultiCluster resource - should be able to create VMC without verrazzano-admin-cluster existing") 178 } 179 }) 180 } 181 } 182 183 // TestCreateWithSecretConfigMapMissingServer tests the validation of verrazzano-admin-cluster configmap with missing server data 184 // GIVEN a call validate VerrazzanoManagedCluster 185 // WHEN the verrazzano-admin-cluster configmap is missing server data 186 // THEN the validation should fail if Rancher is disabled, succeed otherwise 187 func TestCreateWithSecretConfigMapMissingServer(t *testing.T) { 188 const secretName = "mySecret" 189 190 tests := []struct { 191 name string 192 verrazzanos *v1beta1.VerrazzanoList 193 errorExpected bool 194 errMsgExpected string 195 }{ 196 {testNameDefaultVZ, verrazzanoList, false, ""}, 197 {testNameRancherDisabled, verrazzanoListRancherDisabled, true, "Data with key \"server\" contains invalid url \"\" in the ConfigMap verrazzano-admin-cluster namespace verrazzano-mc"}, 198 {testNameRancherEnabled, verrazzanoListRancherEnabled, false, ""}, 199 } 200 for _, test := range tests { 201 t.Run(test.name, func(t *testing.T) { 202 // fake client needed to get secret 203 getClientFunc = func() (client.Client, error) { 204 return fake.NewClientBuilder().WithScheme(newScheme()).WithObjects( 205 &corev1.Secret{ 206 ObjectMeta: metav1.ObjectMeta{ 207 Name: secretName, 208 Namespace: constants.VerrazzanoMultiClusterNamespace, 209 }, 210 }, 211 &corev1.ConfigMap{ 212 ObjectMeta: metav1.ObjectMeta{ 213 Name: constants.AdminClusterConfigMapName, 214 Namespace: constants.VerrazzanoMultiClusterNamespace, 215 }, 216 }).WithLists(test.verrazzanos).Build(), nil 217 } 218 defer func() { getClientFunc = getClient }() 219 220 // VMC to be validated 221 vz := VerrazzanoManagedCluster{ 222 TypeMeta: metav1.TypeMeta{}, 223 ObjectMeta: metav1.ObjectMeta{ 224 Name: "testMC", 225 Namespace: constants.VerrazzanoMultiClusterNamespace, 226 }, 227 Spec: VerrazzanoManagedClusterSpec{ 228 CASecret: secretName, 229 }, 230 } 231 err := vz.ValidateCreate() 232 if test.errorExpected { 233 assert.EqualError(t, err, test.errMsgExpected, 234 "Expected correct error message") 235 } else { 236 assert.NoError(t, err) 237 } 238 }) 239 } 240 } 241 242 // TestCreateMissingSecretName tests the validation of a VerrazzanoManagedCluster with a missing secret name 243 // GIVEN a call validate VerrazzanoManagedCluster 244 // WHEN the VerrazzanoManagedCluster is missing the secret name 245 // THEN the validation should succeed and default to a well-known CA 246 func TestCreateMissingSecretName(t *testing.T) { 247 getClientFunc = func() (client.Client, error) { 248 return fake.NewClientBuilder().WithScheme(newScheme()).WithObjects( 249 &corev1.ConfigMap{ 250 ObjectMeta: metav1.ObjectMeta{ 251 Name: constants.AdminClusterConfigMapName, 252 Namespace: constants.VerrazzanoMultiClusterNamespace, 253 }, 254 Data: map[string]string{ 255 constants.ServerDataKey: "https://testUrl", 256 }, 257 }).WithLists(verrazzanoList).Build(), nil 258 } 259 defer func() { getClientFunc = getClient }() 260 vz := VerrazzanoManagedCluster{ 261 TypeMeta: metav1.TypeMeta{}, 262 ObjectMeta: metav1.ObjectMeta{ 263 Name: "test", 264 Namespace: constants.VerrazzanoMultiClusterNamespace, 265 }, 266 } 267 err := vz.ValidateCreate() 268 assert.NoError(t, err, "Error validating VerrazzanoMultiCluster resource with well-known CA") 269 } 270 271 // TestCreateMissingSecret tests the validation of a missing Prometheus secret in the MC namespace 272 // GIVEN a call validate VerrazzanoManagedCluster 273 // WHEN the multi-cluster namespace is missing the secret 274 // THEN the validation should fail 275 func TestCreateMissingSecret(t *testing.T) { 276 const secretName = "mySecret" 277 tests := []struct { 278 name string 279 verrazzanos *v1beta1.VerrazzanoList 280 errorExpected bool 281 errMsgExpected string 282 }{ 283 {testNameDefaultVZ, verrazzanoList, false, ""}, 284 {testNameRancherDisabled, verrazzanoListRancherDisabled, true, "The CA secret mySecret does not exist in namespace verrazzano-mc"}, 285 {testNameRancherEnabled, verrazzanoListRancherEnabled, false, ""}, 286 } 287 for _, test := range tests { 288 t.Run(test.name, func(t *testing.T) { 289 getClientFunc = func() (client.Client, error) { 290 return fake.NewClientBuilder().WithScheme(newScheme()).WithLists(test.verrazzanos).Build(), nil 291 } 292 defer func() { getClientFunc = getClient }() 293 294 vz := VerrazzanoManagedCluster{ 295 TypeMeta: metav1.TypeMeta{}, 296 ObjectMeta: metav1.ObjectMeta{ 297 Name: "testMC", 298 Namespace: constants.VerrazzanoMultiClusterNamespace, 299 }, 300 Spec: VerrazzanoManagedClusterSpec{ 301 CASecret: secretName, 302 }, 303 } 304 err := vz.ValidateCreate() 305 if test.errorExpected { 306 assert.EqualError(t, err, test.errMsgExpected, 307 "Expected correct error message for missing secret") 308 } else { 309 assert.NoError(t, err) 310 } 311 }) 312 } 313 } 314 315 // TestCreateVerrazzanoNotInstalled tests the validation of a Verrazzano being installed 316 // GIVEN a call validate VerrazzanoManagedCluster 317 // WHEN the a Verrazzano install has not completed 318 // THEN the validation should fail 319 func TestCreateVerrazzanoNotInstalled(t *testing.T) { 320 const secretName = "mySecret" 321 322 var notInstalledList = &v1beta1.VerrazzanoList{ 323 Items: []v1beta1.Verrazzano{ 324 { 325 ObjectMeta: metav1.ObjectMeta{ 326 Name: "my-verrazzano", 327 Namespace: "default", 328 }, 329 Status: v1beta1.VerrazzanoStatus{ 330 Conditions: []v1beta1.Condition{ 331 { 332 Type: v1beta1.CondInstallStarted, 333 }, 334 }, 335 }, 336 }, 337 }, 338 } 339 340 // fake client needed to validate create 341 getClientFunc = func() (client.Client, error) { 342 return fake.NewClientBuilder().WithScheme(newScheme()).WithLists(notInstalledList).Build(), nil 343 } 344 defer func() { getClientFunc = getClient }() 345 346 // VMC to be validated 347 vz := VerrazzanoManagedCluster{ 348 TypeMeta: metav1.TypeMeta{}, 349 ObjectMeta: metav1.ObjectMeta{ 350 Name: "testMC", 351 Namespace: constants.VerrazzanoMultiClusterNamespace, 352 }, 353 Spec: VerrazzanoManagedClusterSpec{ 354 CASecret: secretName, 355 }, 356 } 357 err := vz.ValidateCreate() 358 assert.Error(t, err) 359 assert.Contains(t, err.Error(), "the Verrazzano install must successfully complete") 360 }