From 6a2221a675ac6287c9651d7cf4448a4f3a315f69 Mon Sep 17 00:00:00 2001 From: Francesco Pantano Date: Sat, 4 Nov 2023 14:04:48 +0100 Subject: [PATCH] Add missing GlanceAPI webhook In general it is not allowed to create a glanceAPI instance without defining the top-level Glance CR. However, if for any, unknown reason a glanceAPI CR is created, it will always fail because of the required .spec.ContainerImage field that appears to be empty or simply not defined. This patch introduces the glanceAPI missing webhook that covers at the glanceAPI level the ContainerImage propagationa/definition. Signed-off-by: Francesco Pantano --- api/v1beta1/common.go | 10 +++ api/v1beta1/glanceapi_webhook.go | 93 ++++++++++++++++++++ api/v1beta1/zz_generated.deepcopy.go | 15 ++++ config/samples/glance_v1beta1_glanceapi.yaml | 1 - config/webhook/manifests.yaml | 40 +++++++++ hack/clean_local_webhook.sh | 2 + hack/configure_local_webhook.sh | 56 ++++++++++++ main.go | 4 + pkg/glance/pvc.go | 2 +- pkg/glanceapi/statefulset.go | 2 +- test/functional/suite_test.go | 2 + 11 files changed, 224 insertions(+), 3 deletions(-) create mode 100644 api/v1beta1/glanceapi_webhook.go diff --git a/api/v1beta1/common.go b/api/v1beta1/common.go index cd63e0f9..86d1939e 100644 --- a/api/v1beta1/common.go +++ b/api/v1beta1/common.go @@ -120,6 +120,16 @@ func SetupDefaults() { SetupGlanceDefaults(glanceDefaults) } +// SetupAPIDefaults - initializes any CRD field defaults based on environment variables (the defaulting mechanism itself is implemented via webhooks) +func SetupAPIDefaults() { + // Acquire environmental defaults and initialize GlanceAPI defaults with them + glanceAPIDefaults := GlanceAPIDefaults{ + ContainerImageURL: util.GetEnvVar("RELATED_IMAGE_GLANCE_API_IMAGE_URL_DEFAULT", GlanceAPIContainerImage), + } + + SetupGlanceAPIDefaults(glanceAPIDefaults) +} + // GetAdminServiceClient - get an admin serviceClient for the Glance instance func GetAdminServiceClient( ctx context.Context, diff --git a/api/v1beta1/glanceapi_webhook.go b/api/v1beta1/glanceapi_webhook.go new file mode 100644 index 00000000..dab0b6d0 --- /dev/null +++ b/api/v1beta1/glanceapi_webhook.go @@ -0,0 +1,93 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" +) + +// GlanceAPIDefaults - +type GlanceAPIDefaults struct { + ContainerImageURL string +} + +var glanceAPIDefaults GlanceAPIDefaults + +// log is for logging in this package. +var glanceapilog = logf.Log.WithName("glanceapi-resource") + +// SetupGlanceAPIDefaults - initialize GlanceAPI spec defaults for use with either internal or external webhooks +func SetupGlanceAPIDefaults(defaults GlanceAPIDefaults) { + glanceAPIDefaults = defaults + glancelog.Info("Glance defaults initialized", "defaults", defaults) +} + +// SetupWebhookWithManager sets up the webhook with the Manager +func (r *GlanceAPI) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +//+kubebuilder:webhook:path=/mutate-glance-openstack-org-v1beta1-glanceapi,mutating=true,failurePolicy=fail,sideEffects=None,groups=glance.openstack.org,resources=glanceapis,verbs=create;update,versions=v1beta1,name=mglanceapi.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &GlanceAPI{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *GlanceAPI) Default() { + glancelog.Info("default", "name", r.Name) + + r.Spec.Default() +} + +// Default - set defaults for this Glance spec +func (spec *GlanceAPISpec) Default() { + if spec.GlanceAPITemplate.ContainerImage == "" { + spec.GlanceAPITemplate.ContainerImage = glanceAPIDefaults.ContainerImageURL + } +} + +//+kubebuilder:webhook:path=/validate-glance-openstack-org-v1beta1-glanceapi,mutating=false,failurePolicy=fail,sideEffects=None,groups=glance.openstack.org,resources=glanceapis,verbs=create;update,versions=v1beta1,name=vglanceapi.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &GlanceAPI{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *GlanceAPI) ValidateCreate() error { + glancelog.Info("validate create", "name", r.Name) + + // TODO(user): fill in your validation logic upon object creation. + return nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *GlanceAPI) ValidateUpdate(old runtime.Object) error { + glancelog.Info("validate update", "name", r.Name) + + // TODO(user): fill in your validation logic upon object update. + return nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *GlanceAPI) ValidateDelete() error { + glancelog.Info("validate delete", "name", r.Name) + + // TODO(user): fill in your validation logic upon object deletion. + return nil +} diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 29c46b42..67dae803 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -117,6 +117,21 @@ func (in *GlanceAPIDebug) DeepCopy() *GlanceAPIDebug { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GlanceAPIDefaults) DeepCopyInto(out *GlanceAPIDefaults) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GlanceAPIDefaults. +func (in *GlanceAPIDefaults) DeepCopy() *GlanceAPIDefaults { + if in == nil { + return nil + } + out := new(GlanceAPIDefaults) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GlanceAPIList) DeepCopyInto(out *GlanceAPIList) { *out = *in diff --git a/config/samples/glance_v1beta1_glanceapi.yaml b/config/samples/glance_v1beta1_glanceapi.yaml index 7831ec6a..e9517a4d 100644 --- a/config/samples/glance_v1beta1_glanceapi.yaml +++ b/config/samples/glance_v1beta1_glanceapi.yaml @@ -5,7 +5,6 @@ metadata: spec: serviceUser: glance serviceAccount: glance - containerImage: quay.io/podified-antelope-centos9/openstack-glance-api:current-podified customServiceConfig: | [DEFAULT] debug = true diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 9cc3d915..45295f39 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -25,6 +25,26 @@ webhooks: resources: - glances sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /mutate-glance-openstack-org-v1beta1-glanceapi + failurePolicy: Fail + name: mglanceapi.kb.io + rules: + - apiGroups: + - glance.openstack.org + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - glanceapis + sideEffects: None --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration @@ -52,3 +72,23 @@ webhooks: resources: - glances sideEffects: None +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-glance-openstack-org-v1beta1-glanceapi + failurePolicy: Fail + name: vglanceapi.kb.io + rules: + - apiGroups: + - glance.openstack.org + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - glanceapis + sideEffects: None diff --git a/hack/clean_local_webhook.sh b/hack/clean_local_webhook.sh index 0d61129f..ad680a58 100755 --- a/hack/clean_local_webhook.sh +++ b/hack/clean_local_webhook.sh @@ -3,3 +3,5 @@ set -ex oc delete validatingwebhookconfiguration/vglance.kb.io --ignore-not-found oc delete mutatingwebhookconfiguration/mglance.kb.io --ignore-not-found +oc delete validatingwebhookconfiguration/vglanceapi.kb.io --ignore-not-found +oc delete mutatingwebhookconfiguration/mglanceapi.kb.io --ignore-not-found diff --git a/hack/configure_local_webhook.sh b/hack/configure_local_webhook.sh index e1a0676a..52f63670 100755 --- a/hack/configure_local_webhook.sh +++ b/hack/configure_local_webhook.sh @@ -83,6 +83,62 @@ webhooks: scope: '*' sideEffects: None timeoutSeconds: 10 +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: vglanceapi.kb.io +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: ${CA_BUNDLE} + url: https://${CRC_IP}:9443/validate-glance-openstack-org-v1beta1-glanceapi + failurePolicy: Fail + matchPolicy: Equivalent + name: vglanceapi.kb.io + objectSelector: {} + rules: + - apiGroups: + - glance.openstack.org + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - glanceapis + scope: '*' + sideEffects: None + timeoutSeconds: 10 +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: mglanceapi.kb.io +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + caBundle: ${CA_BUNDLE} + url: https://${CRC_IP}:9443/mutate-glance-openstack-org-v1beta1-glanceapi + failurePolicy: Fail + matchPolicy: Equivalent + name: mglanceapi.kb.io + objectSelector: {} + rules: + - apiGroups: + - glance.openstack.org + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - glanceapis + scope: '*' + sideEffects: None + timeoutSeconds: 10 EOF_CAT oc apply -n openstack -f ${TMPDIR}/patch_webhook_configurations.yaml diff --git a/main.go b/main.go index 383be426..51576ab3 100644 --- a/main.go +++ b/main.go @@ -144,6 +144,10 @@ func main() { setupLog.Error(err, "unable to create webhook", "webhook", "Glance") os.Exit(1) } + if err = (&glancev1.GlanceAPI{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "GlanceAPI") + os.Exit(1) + } checker = mgr.GetWebhookServer().StartedChecker() } //+kubebuilder:scaffold:builder diff --git a/pkg/glance/pvc.go b/pkg/glance/pvc.go index 1659f566..6f234e84 100644 --- a/pkg/glance/pvc.go +++ b/pkg/glance/pvc.go @@ -8,7 +8,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// Pvc - creates and returns a PVC object for a backing store +// GetPvc - creates and returns a PVC object for a backing store func GetPvc(api *glancev1.GlanceAPI, labels map[string]string, pvcType PvcType) corev1.PersistentVolumeClaim { // By default we point to a local storage pvc request // that will be customized in case the pvc is requested diff --git a/pkg/glanceapi/statefulset.go b/pkg/glanceapi/statefulset.go index 287f5a1e..e6a811a6 100644 --- a/pkg/glanceapi/statefulset.go +++ b/pkg/glanceapi/statefulset.go @@ -37,7 +37,7 @@ const ( GlanceAPIHttpdCommand = "/usr/sbin/httpd -DFOREGROUND" ) -// Deployment func +// StatefulSet func func StatefulSet( instance *glancev1.GlanceAPI, configHash string, diff --git a/test/functional/suite_test.go b/test/functional/suite_test.go index 278cd877..42d6a2b2 100644 --- a/test/functional/suite_test.go +++ b/test/functional/suite_test.go @@ -196,6 +196,8 @@ var _ = BeforeSuite(func() { err = (&glancev1.Glance{}).SetupWebhookWithManager(k8sManager) Expect(err).NotTo(HaveOccurred()) + err = (&glancev1.GlanceAPI{}).SetupWebhookWithManager(k8sManager) + Expect(err).NotTo(HaveOccurred()) go func() { defer GinkgoRecover() err = k8sManager.Start(ctx)