From e1a45d08f9bcbba3d3c5b3822533dd3f1682db8c Mon Sep 17 00:00:00 2001 From: Anatolii Bazko Date: Mon, 19 Sep 2022 15:46:07 +0300 Subject: [PATCH] feat: Add ValidatingWebhookConfiguration to ensure only one CheCluster is created (#1518) Signed-off-by: Anatolii Bazko --- Makefile | 3 ++ PROJECT | 1 + api/v2/checluster_webhook.go | 46 +++++++++++++++++-- .../che-operator.clusterserviceversion.yaml | 21 +++++++++ config/kubernetes/kustomization.yaml | 1 + .../patches/cainjection_in_checlusters.yaml | 2 +- .../patches/cainjection_in_webhook.yaml | 18 ++++++++ config/openshift/kustomization.yaml | 1 + .../patches/cainjection_in_webhook.yaml | 18 ++++++++ config/webhook/kustomization.yaml | 1 + config/webhook/webhooks.yaml | 43 +++++++++++++++++ deploy/deployment/kubernetes/combined.yaml | 34 ++++++++++++++ ...se.che.ValidatingWebhookConfiguration.yaml | 45 ++++++++++++++++++ deploy/deployment/openshift/combined.yaml | 34 ++++++++++++++ ...se.che.ValidatingWebhookConfiguration.yaml | 45 ++++++++++++++++++ ...se.che.ValidatingWebhookConfiguration.yaml | 45 ++++++++++++++++++ main.go | 8 ++-- pkg/common/k8s-helper/k8s_helper.go | 16 +++++++ 18 files changed, 375 insertions(+), 7 deletions(-) create mode 100644 config/kubernetes/patches/cainjection_in_webhook.yaml create mode 100644 config/openshift/patches/cainjection_in_webhook.yaml create mode 100644 config/webhook/webhooks.yaml create mode 100644 deploy/deployment/kubernetes/objects/org.eclipse.che.ValidatingWebhookConfiguration.yaml create mode 100644 deploy/deployment/openshift/objects/org.eclipse.che.ValidatingWebhookConfiguration.yaml create mode 100644 helmcharts/next/templates/org.eclipse.che.ValidatingWebhookConfiguration.yaml diff --git a/Makefile b/Makefile index 16516c8c65..806c822b00 100644 --- a/Makefile +++ b/Makefile @@ -282,6 +282,9 @@ gen-chectl-tmpl: ## Generate Eclipse Che k8s deployment resources used by chectl cp $${src}/$${TARGET_PLATFORM}/objects/che-operator.Role.yaml $${dst}/$${TARGET_PLATFORM}/role.yaml cp $${src}/$${TARGET_PLATFORM}/objects/che-operator-service.Service.yaml $${dst}/$${TARGET_PLATFORM}/webhook-service.yaml + if [[ -f $${src}/org.eclipse.che.ValidatingWebhookConfiguration.yaml ]]; then + cp $${src}/org.eclipse.che.ValidatingWebhookConfiguration.yaml $${dst}/org.eclipse.che.ValidatingWebhookConfiguration.yaml + fi if [[ $${TARGET_PLATFORM} == "kubernetes" ]]; then cp $${src}/$${TARGET_PLATFORM}/objects/che-operator-serving-cert.Certificate.yaml $${dst}/$${TARGET_PLATFORM}/serving-cert.yaml diff --git a/PROJECT b/PROJECT index c1ee02aeeb..643ae9fc22 100644 --- a/PROJECT +++ b/PROJECT @@ -26,5 +26,6 @@ resources: version: v2 webhooks: conversion: true + validation: true webhookVersion: v1 version: "3" diff --git a/api/v2/checluster_webhook.go b/api/v2/checluster_webhook.go index 7d25d00db9..9ac3dcb3fc 100644 --- a/api/v2/checluster_webhook.go +++ b/api/v2/checluster_webhook.go @@ -13,12 +13,20 @@ package v2 import ( + "context" + "fmt" + + k8shelper "github.com/eclipse-che/che-operator/pkg/common/k8s-helper" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" ctrl "sigs.k8s.io/controller-runtime" - logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" ) // log is for logging in this package. -var checlusterlog = logf.Log.WithName("checluster-resource") +var ( + logger = ctrl.Log.WithName("webhook") +) func (r *CheCluster) SetupWebhookWithManager(mgr ctrl.Manager) error { return ctrl.NewWebhookManagedBy(mgr). @@ -26,4 +34,36 @@ func (r *CheCluster) SetupWebhookWithManager(mgr ctrl.Manager) error { Complete() } -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +var _ webhook.Validator = &CheCluster{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *CheCluster) ValidateCreate() error { + return ensureSingletonCheCluster() +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *CheCluster) ValidateUpdate(old runtime.Object) error { + return nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *CheCluster) ValidateDelete() error { + return nil +} + +func ensureSingletonCheCluster() error { + client := k8shelper.New().GetClient() + utilruntime.Must(AddToScheme(client.Scheme())) + + che := &CheClusterList{} + err := client.List(context.TODO(), che) + if err != nil { + logger.Error(err, "Failed to list CheCluster Custom Resources.") + } + + if len(che.Items) != 0 { + return fmt.Errorf("only one CheCluster is allowed") + } + + return nil +} diff --git a/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml b/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml index d4b3dddac1..ce489983a2 100644 --- a/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml +++ b/bundle/next/eclipse-che-preview-openshift/manifests/che-operator.clusterserviceversion.yaml @@ -1393,6 +1393,27 @@ spec: name: Eclipse Foundation version: 7.52.0-644.next webhookdefinitions: + - admissionReviewVersions: + - v1 + - v1beta1 + containerPort: 443 + deploymentName: che-operator + failurePolicy: Fail + generateName: vchecluster.kb.io + rules: + - apiGroups: + - org.eclipse.che + apiVersions: + - v2 + operations: + - CREATE + - UPDATE + resources: + - checlusters + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-org-eclipse-che-v2-checluster - admissionReviewVersions: - v1 - v2 diff --git a/config/kubernetes/kustomization.yaml b/config/kubernetes/kustomization.yaml index 62e1c0a927..1c49b131fe 100644 --- a/config/kubernetes/kustomization.yaml +++ b/config/kubernetes/kustomization.yaml @@ -21,6 +21,7 @@ resources: # Kubernetes platform specific patches patchesStrategicMerge: - patches/cainjection_in_checlusters.yaml + - patches/cainjection_in_webhook.yaml - patches/manager_pod_security_context.yaml vars: diff --git a/config/kubernetes/patches/cainjection_in_checlusters.yaml b/config/kubernetes/patches/cainjection_in_checlusters.yaml index fb167f83d5..9542f8c105 100644 --- a/config/kubernetes/patches/cainjection_in_checlusters.yaml +++ b/config/kubernetes/patches/cainjection_in_checlusters.yaml @@ -14,6 +14,6 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: + name: checlusters.org.eclipse.che annotations: cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: checlusters.org.eclipse.che diff --git a/config/kubernetes/patches/cainjection_in_webhook.yaml b/config/kubernetes/patches/cainjection_in_webhook.yaml new file mode 100644 index 0000000000..3d167bec5c --- /dev/null +++ b/config/kubernetes/patches/cainjection_in_webhook.yaml @@ -0,0 +1,18 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: org.eclipse.che + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) \ No newline at end of file diff --git a/config/openshift/kustomization.yaml b/config/openshift/kustomization.yaml index 85315bb754..207896f9b2 100644 --- a/config/openshift/kustomization.yaml +++ b/config/openshift/kustomization.yaml @@ -19,4 +19,5 @@ resources: patchesStrategicMerge: - patches/cainjection_in_checlusters.yaml + - patches/cainjection_in_webhook.yaml - patches/service_cert_patch.yaml \ No newline at end of file diff --git a/config/openshift/patches/cainjection_in_webhook.yaml b/config/openshift/patches/cainjection_in_webhook.yaml new file mode 100644 index 0000000000..1749062932 --- /dev/null +++ b/config/openshift/patches/cainjection_in_webhook.yaml @@ -0,0 +1,18 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: org.eclipse.che + annotations: + service.beta.openshift.io/inject-cabundle: "true" \ No newline at end of file diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml index d65efe4545..e283c69b00 100644 --- a/config/webhook/kustomization.yaml +++ b/config/webhook/kustomization.yaml @@ -11,6 +11,7 @@ # resources: +- webhooks.yaml - service.yaml configurations: diff --git a/config/webhook/webhooks.yaml b/config/webhook/webhooks.yaml new file mode 100644 index 0000000000..0fdc7504e7 --- /dev/null +++ b/config/webhook/webhooks.yaml @@ -0,0 +1,43 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: org.eclipse.che + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org +webhooks: + - admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /validate-org-eclipse-che-v2-checluster + failurePolicy: Fail + name: vchecluster.kb.io + rules: + - apiGroups: + - org.eclipse.che + apiVersions: + - v2 + operations: + - CREATE + - UPDATE + resources: + - checlusters + sideEffects: None \ No newline at end of file diff --git a/deploy/deployment/kubernetes/combined.yaml b/deploy/deployment/kubernetes/combined.yaml index 899e8427fd..31c84b79cf 100644 --- a/deploy/deployment/kubernetes/combined.yaml +++ b/deploy/deployment/kubernetes/combined.yaml @@ -6200,3 +6200,37 @@ metadata: namespace: eclipse-che spec: selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: eclipse-che/che-operator-serving-cert + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: org.eclipse.che +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /validate-org-eclipse-che-v2-checluster + failurePolicy: Fail + name: vchecluster.kb.io + rules: + - apiGroups: + - org.eclipse.che + apiVersions: + - v2 + operations: + - CREATE + - UPDATE + resources: + - checlusters + sideEffects: None diff --git a/deploy/deployment/kubernetes/objects/org.eclipse.che.ValidatingWebhookConfiguration.yaml b/deploy/deployment/kubernetes/objects/org.eclipse.che.ValidatingWebhookConfiguration.yaml new file mode 100644 index 0000000000..4d566bd6e5 --- /dev/null +++ b/deploy/deployment/kubernetes/objects/org.eclipse.che.ValidatingWebhookConfiguration.yaml @@ -0,0 +1,45 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: eclipse-che/che-operator-serving-cert + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: org.eclipse.che +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /validate-org-eclipse-che-v2-checluster + failurePolicy: Fail + name: vchecluster.kb.io + rules: + - apiGroups: + - org.eclipse.che + apiVersions: + - v2 + operations: + - CREATE + - UPDATE + resources: + - checlusters + sideEffects: None diff --git a/deploy/deployment/openshift/combined.yaml b/deploy/deployment/openshift/combined.yaml index 5fbff35b54..66ff2c63ef 100644 --- a/deploy/deployment/openshift/combined.yaml +++ b/deploy/deployment/openshift/combined.yaml @@ -6167,3 +6167,37 @@ spec: secret: defaultMode: 420 secretName: che-operator-webhook-server-cert +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + service.beta.openshift.io/inject-cabundle: "true" + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: org.eclipse.che +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /validate-org-eclipse-che-v2-checluster + failurePolicy: Fail + name: vchecluster.kb.io + rules: + - apiGroups: + - org.eclipse.che + apiVersions: + - v2 + operations: + - CREATE + - UPDATE + resources: + - checlusters + sideEffects: None diff --git a/deploy/deployment/openshift/objects/org.eclipse.che.ValidatingWebhookConfiguration.yaml b/deploy/deployment/openshift/objects/org.eclipse.che.ValidatingWebhookConfiguration.yaml new file mode 100644 index 0000000000..4e82930c00 --- /dev/null +++ b/deploy/deployment/openshift/objects/org.eclipse.che.ValidatingWebhookConfiguration.yaml @@ -0,0 +1,45 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + service.beta.openshift.io/inject-cabundle: "true" + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: org.eclipse.che +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /validate-org-eclipse-che-v2-checluster + failurePolicy: Fail + name: vchecluster.kb.io + rules: + - apiGroups: + - org.eclipse.che + apiVersions: + - v2 + operations: + - CREATE + - UPDATE + resources: + - checlusters + sideEffects: None diff --git a/helmcharts/next/templates/org.eclipse.che.ValidatingWebhookConfiguration.yaml b/helmcharts/next/templates/org.eclipse.che.ValidatingWebhookConfiguration.yaml new file mode 100644 index 0000000000..4d566bd6e5 --- /dev/null +++ b/helmcharts/next/templates/org.eclipse.che.ValidatingWebhookConfiguration.yaml @@ -0,0 +1,45 @@ +# +# Copyright (c) 2019-2021 Red Hat, Inc. +# This program and the accompanying materials are made +# available under the terms of the Eclipse Public License 2.0 +# which is available at https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Red Hat, Inc. - initial API and implementation +# + +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: eclipse-che/che-operator-serving-cert + labels: + app.kubernetes.io/component: che-operator + app.kubernetes.io/instance: che + app.kubernetes.io/name: che + app.kubernetes.io/part-of: che.eclipse.org + name: org.eclipse.che +webhooks: +- admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + name: che-operator-service + namespace: eclipse-che + path: /validate-org-eclipse-che-v2-checluster + failurePolicy: Fail + name: vchecluster.kb.io + rules: + - apiGroups: + - org.eclipse.che + apiVersions: + - v2 + operations: + - CREATE + - UPDATE + resources: + - checlusters + sideEffects: None diff --git a/main.go b/main.go index d0a45bbf09..663170537e 100644 --- a/main.go +++ b/main.go @@ -318,9 +318,11 @@ func main() { }) } - if err = (&chev2.CheCluster{}).SetupWebhookWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create webhook", "webhook", "CheCluster") - os.Exit(1) + if os.Getenv("ENABLE_WEBHOOKS") != "false" { + if err = (&chev2.CheCluster{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "CheCluster") + os.Exit(1) + } } // +kubebuilder:scaffold:builder diff --git a/pkg/common/k8s-helper/k8s_helper.go b/pkg/common/k8s-helper/k8s_helper.go index c4c49c3eaa..19a42918f7 100644 --- a/pkg/common/k8s-helper/k8s_helper.go +++ b/pkg/common/k8s-helper/k8s_helper.go @@ -18,6 +18,9 @@ import ( "io" "os" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "k8s.io/client-go/kubernetes/fake" "github.com/sirupsen/logrus" @@ -27,10 +30,12 @@ import ( "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/tools/remotecommand" "sigs.k8s.io/controller-runtime/pkg/client/config" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" ) type K8sHelper struct { clientset kubernetes.Interface + client client.Client } var ( @@ -53,6 +58,10 @@ func (cl *K8sHelper) GetClientset() kubernetes.Interface { return cl.clientset } +func (cl *K8sHelper) GetClient() client.Client { + return cl.client +} + func (cl *K8sHelper) ExecIntoPod( deploymentName string, command string, @@ -159,6 +168,7 @@ func (cl *K8sHelper) RunExec(command []string, podName, namespace string, stdin func initializeForTesting() *K8sHelper { k8sHelper = &K8sHelper{ clientset: fake.NewSimpleClientset(), + client: fakeclient.NewClientBuilder().Build(), } return k8sHelper @@ -175,8 +185,14 @@ func initialize() *K8sHelper { logrus.Fatalf("Failed to initialized Kubernetes client: %v", err) } + client, err := client.New(cfg, client.Options{Scheme: runtime.NewScheme()}) + if err != nil { + logrus.Fatalf("Failed to initialized Kubernetes client: %v", err) + } + k8sHelper = &K8sHelper{ clientset: clientSet, + client: client, } return k8sHelper