github.com/verrazzano/verrazzano@v1.7.0/platform-operator/internal/operatorinit/update_webhooks_test.go (about) 1 // Copyright (c) 2020, 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 operatorinit 5 6 import ( 7 "bytes" 8 "context" 9 "errors" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/verrazzano/verrazzano/pkg/constants" 14 "github.com/verrazzano/verrazzano/platform-operator/internal/k8s/certificate" 15 adminv1 "k8s.io/api/admissionregistration/v1" 16 v1 "k8s.io/api/core/v1" 17 v12 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 18 fakeapiExt "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" 19 errors2 "k8s.io/apimachinery/pkg/api/errors" 20 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 "k8s.io/apimachinery/pkg/runtime" 22 "k8s.io/client-go/kubernetes/fake" 23 fake2 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1/fake" 24 testing2 "k8s.io/client-go/testing" 25 controllerruntime "sigs.k8s.io/controller-runtime" 26 ) 27 28 // TestUpdateValidatingnWebhookConfiguration tests that the CA Bundle is updated in the verrazzano-platform-operator 29 // validatingWebhookConfiguration resource. 30 // GIVEN a validatingWebhookConfiguration resource with the CA Bundle set 31 // 32 // WHEN I call updateValidatingWebhookConfiguration 33 // THEN the validatingWebhookConfiguration resource set the CA Bundle as expected 34 func TestUpdateValidatingnWebhookConfiguration(t *testing.T) { 35 asserts := assert.New(t) 36 37 kubeClient := fake.NewSimpleClientset() 38 39 _, caCert, err := createExpectedCASecret(kubeClient) 40 asserts.Nilf(err, "Unexpected error creating expected CA secret", err) 41 42 wh, err := createExpectedValidatingWebhook(kubeClient, certificate.OperatorName) 43 asserts.Nilf(err, "error should not be returned creating validation webhook configuration: %v", err) 44 asserts.NotEmpty(wh) 45 46 err = updateValidatingWebhookConfiguration(kubeClient, certificate.OperatorName) 47 asserts.Nilf(err, "error should not be returned updating validation webhook configuration: %v", err) 48 49 updatedWebhook, _ := kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Get(context.TODO(), "verrazzano-platform-operator-webhook", metav1.GetOptions{}) 50 asserts.Equal(caCert.Bytes(), updatedWebhook.Webhooks[0].ClientConfig.CABundle, "Expected CA bundle name did not match") 51 } 52 53 // TestUpdateValidatingnWebhookConfigurationFail tests that the CA Bundle is not updated in the 54 // verrazzano-platform-operator validatingWebhookConfiguration resource. 55 // GIVEN an invalid validatingWebhookConfiguration resource with the CA Bundle set 56 // 57 // WHEN I call updateValidatingWebhookConfiguration 58 // THEN the validatingWebhookConfiguration resource will fail to be updated 59 func TestUpdateValidatingnWebhookConfigurationFail(t *testing.T) { 60 asserts := assert.New(t) 61 62 kubeClient := fake.NewSimpleClientset() 63 64 _, _, err := createExpectedCASecret(kubeClient) 65 asserts.Nilf(err, "Unexpected error creating expected CA secret", err) 66 67 _, err = createInvalidExpectedValidatingWebhook(kubeClient, certificate.OperatorName) 68 asserts.Nil(err, "error should not be returned creating validation webhook configuration") 69 70 err = updateValidatingWebhookConfiguration(kubeClient, certificate.OperatorName) 71 asserts.Error(err, "error should be returned updating validation webhook configuration") 72 } 73 74 // TestUpdateConversionWebhookConfiguration tests that the CA Bundle is updated in the verrazzano-platform-operator 75 // ConversionWebhookConfiguration resource. 76 // GIVEN a call to updateConversionWebhookConfiguration 77 // 78 // WHEN the webhook CA bundle is present 79 // THEN the CRD is updated with a Webhook converter configuration for the v1beta1 review versions with the correct CA Bundle 80 func TestUpdateConversionWebhookConfiguration(t *testing.T) { 81 asserts := assert.New(t) 82 83 kubeClient := fake.NewSimpleClientset() 84 85 _, caCert, err := createExpectedCASecret(kubeClient) 86 asserts.Nilf(err, "Unexpected error creating expected CA secret", err) 87 88 apiExtClient := fakeapiExt.NewSimpleClientset().ApiextensionsV1() 89 _, err = apiExtClient.CustomResourceDefinitions().Create(context.TODO(), &v12.CustomResourceDefinition{ 90 ObjectMeta: controllerruntime.ObjectMeta{Name: certificate.CRDName}, 91 }, metav1.CreateOptions{}) 92 asserts.Nilf(err, "Unexpected error creating mock CRD: %v", err) 93 94 err = updateConversionWebhookConfiguration(apiExtClient, kubeClient) 95 asserts.Nilf(err, "Unexpected error returned updating validation webhook configuration: %v", err) 96 97 updatedCRD, err := apiExtClient.CustomResourceDefinitions().Get(context.TODO(), certificate.CRDName, metav1.GetOptions{}) 98 asserts.Nilf(err, "Unexpected error getting updated CRD: %v", err) 99 100 asserts.Equal(caCert.Bytes(), updatedCRD.Spec.Conversion.Webhook.ClientConfig.CABundle, "Expected CA bundle name did not match") 101 asserts.Equal(certificate.OperatorName, updatedCRD.Spec.Conversion.Webhook.ClientConfig.Service.Name) 102 asserts.Equal(certificate.OperatorNamespace, updatedCRD.Spec.Conversion.Webhook.ClientConfig.Service.Namespace) 103 asserts.Equal("/convert", *updatedCRD.Spec.Conversion.Webhook.ClientConfig.Service.Path) 104 asserts.Equal(int32(443), *updatedCRD.Spec.Conversion.Webhook.ClientConfig.Service.Port) 105 asserts.Equal(v12.WebhookConverter, updatedCRD.Spec.Conversion.Strategy) 106 asserts.Equal([]string{"v1beta1"}, updatedCRD.Spec.Conversion.Webhook.ConversionReviewVersions) 107 } 108 109 // TestUpdateMutatingWebhookConfiguration tests that the CA Bundle is updated the specified MutatingWebhook configuration 110 // GIVEN a call to updateMutatingWebhookConfiguration 111 // 112 // WHEN with the webhook CA bundle secret exists 113 // THEN the MutatingWebhook configuration sets the CA bundle on all webhook client configurations 114 func TestUpdateMutatingWebhookConfiguration(t *testing.T) { 115 asserts := assert.New(t) 116 117 kubeClient := fake.NewSimpleClientset() 118 119 _, caCert, err := createExpectedCASecret(kubeClient) 120 asserts.Nilf(err, "Unexpected error creating expected CA secret", err) 121 122 _, err = createExpectedMutatingWebhook(kubeClient) 123 asserts.Nilf(err, "Unexpected error creating expected webhook configuration") 124 125 err = updateMutatingWebhookConfiguration(kubeClient, constants.MysqlBackupMutatingWebhookName) 126 asserts.Nilf(err, "Unexpected error returned from updateMutatingWebhookConfiguration: %v", err) 127 128 updatedWebhook, _ := kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(context.TODO(), constants.MysqlBackupMutatingWebhookName, metav1.GetOptions{}) 129 asserts.Equal(caCert.Bytes(), updatedWebhook.Webhooks[0].ClientConfig.CABundle, "Expected CA bundle name did not match") 130 asserts.Equal(caCert.Bytes(), updatedWebhook.Webhooks[1].ClientConfig.CABundle, "Expected CA bundle name did not match") 131 } 132 133 // TestUpdateMutatingWebhookConfigurationNoCASecret tests that 134 // GIVEN a call to updateMutatingWebhookConfiguration 135 // 136 // WHEN with the webhook CA bundle secret does not exist but the webhook does 137 // THEN an error is returned 138 func TestUpdateMutatingWebhookConfigurationNoCASecret(t *testing.T) { 139 asserts := assert.New(t) 140 141 kubeClient := fake.NewSimpleClientset() 142 143 _, err := createExpectedMutatingWebhook(kubeClient) 144 asserts.Nilf(err, "Unexpected error creating expected webhook configuration", err) 145 146 err = updateMutatingWebhookConfiguration(kubeClient, constants.MysqlBackupMutatingWebhookName) 147 asserts.NotNil(err, "No error returned when webhook doesn't exist") 148 } 149 150 // TestUpdateMutatingWebhookConfigurationNoWebhook tests that 151 // GIVEN a call to updateMutatingWebhookConfiguration 152 // 153 // WHEN with the webhook CA bundle secret exists but the webhook does not 154 // THEN an error is returned 155 func TestUpdateMutatingWebhookConfigurationNoWebhook(t *testing.T) { 156 asserts := assert.New(t) 157 158 kubeClient := fake.NewSimpleClientset() 159 160 _, _, err := createExpectedCASecret(kubeClient) 161 asserts.Nilf(err, "Unexpected error creating expected CA secret", err) 162 163 err = updateMutatingWebhookConfiguration(kubeClient, constants.MysqlBackupMutatingWebhookName) 164 asserts.NotNil(err, "No error returned when webhook doesn't exist") 165 } 166 167 // TestDeleteValidatingWebhookConfiguration tests that 168 // GIVEN a call to deleteValidatingWebhookConfiguration 169 // 170 // WHEN the webhook does exist 171 // THEN no error is returned 172 func TestDeleteValidatingWebhookConfiguration(t *testing.T) { 173 asserts := assert.New(t) 174 175 kubeClient := fake.NewSimpleClientset() 176 177 const webhookName = "foo" 178 _, err := createExpectedValidatingWebhook(kubeClient, webhookName) 179 asserts.Nilf(err, "Unexpected error creating expected webhook configuration") 180 181 err = deleteValidatingWebhookConfiguration(kubeClient, webhookName) 182 asserts.Nilf(err, "Unexpected error when deleting webhook", err) 183 184 wh, err := kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Get(context.TODO(), webhookName, metav1.GetOptions{}) 185 asserts.NotNil(err, "Did not get expected error after delete") 186 asserts.True(errors2.IsNotFound(err), "Did not get IsNotFound error after delete") 187 asserts.Nilf(wh, "Webhook reference should be nil after delete") 188 } 189 190 // TestDeleteValidatingWebhookConfigurationDoesNotExist tests that 191 // GIVEN a call to deleteValidatingWebhookConfiguration 192 // 193 // WHEN the webhook does NOT exist 194 // THEN no error is returned 195 func TestDeleteValidatingWebhookConfigurationDoesNotExist(t *testing.T) { 196 asserts := assert.New(t) 197 kubeClient := fake.NewSimpleClientset() 198 err := deleteValidatingWebhookConfiguration(kubeClient, "foo") 199 asserts.Nilf(err, "Unexpected error when deleting webhook that does not exist", err) 200 } 201 202 // TestDeleteValidatingWebhookConfigurationErrorOnGet tests that 203 // GIVEN a call to deleteValidatingWebhookConfiguration 204 // 205 // WHEN the webhook Get() operation returns an unexpected error 206 // THEN that error is returned 207 func TestDeleteValidatingWebhookConfigurationErrorOnGet(t *testing.T) { 208 asserts := assert.New(t) 209 210 kubeClient := fake.NewSimpleClientset() 211 kubeClient.AdmissionregistrationV1().(*fake2.FakeAdmissionregistrationV1). 212 PrependReactor("get", "validatingwebhookconfigurations", 213 func(action testing2.Action) (handled bool, ret runtime.Object, err error) { 214 return true, nil, errors.New("error deleting validating webhook") 215 }) 216 217 err := deleteValidatingWebhookConfiguration(kubeClient, "foo") 218 asserts.NotNilf(err, "No expected error returned when deleting webhook", err) 219 } 220 221 func createExpectedCASecret(kubeClient *fake.Clientset) (*v1.Secret, bytes.Buffer, error) { 222 var caCert bytes.Buffer 223 caCert.WriteString("Fake CABundle") 224 225 caSecret := v1.Secret{} 226 caSecret.Name = certificate.OperatorCA 227 caSecret.Type = v1.SecretTypeTLS 228 caSecret.Namespace = certificate.OperatorNamespace 229 caSecret.Data = make(map[string][]byte) 230 caSecret.Data[certificate.CertKey] = caCert.Bytes() 231 caSecret.Data[certificate.PrivKey] = caCert.Bytes() 232 233 newSecret, err := kubeClient.CoreV1().Secrets(certificate.OperatorNamespace).Create(context.TODO(), &caSecret, metav1.CreateOptions{}) 234 return newSecret, caCert, err 235 } 236 237 func createExpectedMutatingWebhook(kubeClient *fake.Clientset) (*adminv1.MutatingWebhookConfiguration, error) { 238 webhook := adminv1.MutatingWebhookConfiguration{ 239 TypeMeta: metav1.TypeMeta{}, 240 ObjectMeta: metav1.ObjectMeta{ 241 Name: constants.MysqlBackupMutatingWebhookName, 242 }, 243 Webhooks: []adminv1.MutatingWebhook{ 244 {Name: "webhook1"}, 245 {Name: "webhook2"}, 246 }, 247 } 248 return kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(context.TODO(), &webhook, metav1.CreateOptions{}) 249 } 250 251 func createExpectedValidatingWebhook(kubeClient *fake.Clientset, whName string) (*adminv1.ValidatingWebhookConfiguration, error) { 252 pathInstall := "/validate-install-verrazzano-io-v1alpha1-verrazzano" 253 serviceInstall := adminv1.ServiceReference{ 254 Name: whName, 255 Namespace: certificate.OperatorNamespace, 256 Path: &pathInstall, 257 } 258 259 webhook := adminv1.ValidatingWebhookConfiguration{ 260 TypeMeta: metav1.TypeMeta{}, 261 ObjectMeta: metav1.ObjectMeta{ 262 Name: whName, 263 }, 264 Webhooks: []adminv1.ValidatingWebhook{ 265 { 266 Name: "install.verrazzano.io", 267 ClientConfig: adminv1.WebhookClientConfig{ 268 Service: &serviceInstall, 269 }, 270 }, 271 { 272 Name: "install.verrazzano.io.v1beta", 273 ClientConfig: adminv1.WebhookClientConfig{ 274 Service: &serviceInstall, 275 }, 276 }, 277 }, 278 } 279 return kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(context.TODO(), &webhook, metav1.CreateOptions{}) 280 } 281 282 func createInvalidExpectedValidatingWebhook(kubeClient *fake.Clientset, whName string) (*adminv1.ValidatingWebhookConfiguration, error) { 283 path := "/validate-install-verrazzano-io-v1alpha1-verrazzano" 284 service := adminv1.ServiceReference{ 285 Name: whName, 286 Namespace: certificate.OperatorNamespace, 287 Path: &path, 288 } 289 webhook := adminv1.ValidatingWebhookConfiguration{ 290 TypeMeta: metav1.TypeMeta{}, 291 ObjectMeta: metav1.ObjectMeta{ 292 Name: "InvalidName", 293 }, 294 Webhooks: []adminv1.ValidatingWebhook{ 295 { 296 Name: "install.verrazzano.io", 297 ClientConfig: adminv1.WebhookClientConfig{ 298 Service: &service, 299 }, 300 }, 301 }, 302 } 303 return kubeClient.AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(context.TODO(), &webhook, metav1.CreateOptions{}) 304 }