From d1379d0f8c2aae58f3c1c4c20f818e521bc7cf1a Mon Sep 17 00:00:00 2001 From: Spolti Date: Tue, 22 Aug 2023 13:19:17 -0300 Subject: [PATCH] [KOGITO-7840] - Add validation on Workflow Metadata with admission webhooks Signed-off-by: Spolti --- Makefile | 33 ++-- PROJECT | 4 + api/go.mod | 13 +- api/go.sum | 2 +- api/v1alpha08/sonataflow_webhook.go | 101 ++++++++++ api/v1alpha08/webhook_suite_test.go | 181 ++++++++++++++++++ ...w-operator-webhook-service_v1_service.yaml | 21 ++ ...taflow-operator.clusterserviceversion.yaml | 86 ++++++--- .../sonataflow.org_sonataflowbuilds.yaml | 1 + .../sonataflow.org_sonataflowplatforms.yaml | 11 ++ .../manifests/sonataflow.org_sonataflows.yaml | 1 + config/certmanager/certificate.yaml | 39 ++++ config/certmanager/kustomization.yaml | 5 + config/certmanager/kustomizeconfig.yaml | 16 ++ config/crd/kustomization.yaml | 12 +- .../patches/cainjection_in_sonataflows.yaml | 2 +- .../crd/patches/webhook_in_sonataflows.yaml | 2 +- config/default/kustomization.yaml | 60 +++--- config/default/manager_webhook_patch.yaml | 23 +++ config/default/webhookcainjection_patch.yaml | 30 +++ config/manager/kustomization.yaml | 11 +- config/manifests/kustomization.yaml | 32 ++-- config/webhook/kustomization.yaml | 6 + config/webhook/kustomizeconfig.yaml | 25 +++ config/webhook/manifests.yaml | 27 +++ config/webhook/service.yaml | 19 ++ docs/CONTRIBUTING.md | 17 ++ go.work.sum | 9 + hack/local/cert-manager.sh | 56 ++++++ main.go | 16 ++ operator.yaml | 170 +++++++++++++--- 31 files changed, 904 insertions(+), 127 deletions(-) create mode 100644 api/v1alpha08/sonataflow_webhook.go create mode 100644 api/v1alpha08/webhook_suite_test.go create mode 100644 bundle/manifests/sonataflow-operator-webhook-service_v1_service.yaml create mode 100644 config/certmanager/certificate.yaml create mode 100644 config/certmanager/kustomization.yaml create mode 100644 config/certmanager/kustomizeconfig.yaml create mode 100644 config/default/manager_webhook_patch.yaml create mode 100644 config/default/webhookcainjection_patch.yaml create mode 100644 config/webhook/kustomization.yaml create mode 100644 config/webhook/kustomizeconfig.yaml create mode 100644 config/webhook/manifests.yaml create mode 100644 config/webhook/service.yaml create mode 100755 hack/local/cert-manager.sh diff --git a/Makefile b/Makefile index 2303a36f4..5a5b4b808 100644 --- a/Makefile +++ b/Makefile @@ -139,9 +139,10 @@ build: generate ## Build manager binary. build-4-debug: generate ## Build manager binary with debug options. go build -gcflags="all=-N -l" -o bin/manager main.go +ENABLE_WEBHOOKS ?= false .PHONY: run run: manifests generate ## Run a controller from your host. - go run ./main.go + ENABLE_WEBHOOKS=${ENABLE_WEBHOOKS} go run ./main.go .PHONY: debug debug: build-4-debug ## Run a controller from your host from binary @@ -159,6 +160,10 @@ podman-build: test ## Build container image with the manager. docker-push: ## Push docker image with the manager. docker push ${IMG} +.PHONY: podman-push +podman-push: ## Push container image with the manager. + podman push ${PODMAN_PUSH_PARAMS} ${IMG} + # This is currently done directly into the CI # PLATFORMS defines the target platforms for the manager image be build to provide support to multiple # architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: @@ -177,10 +182,6 @@ docker-buildx: test ## Build and push docker image for the manager for cross-pla - docker buildx rm project-v3-builder rm Dockerfile.cross -.PHONY: podman-push -podman-push: ## Push container image with the manager. - podman push ${PODMAN_PUSH_PARAMS} ${IMG} - .PHONY: container-build container-build: test ## Build the container image cekit -v --descriptor image.yaml build ${build_options} $(BUILDER) @@ -207,19 +208,19 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - .PHONY: deploy -deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. +deploy: manifests kustomize install-cert-manager ## Deploy controller to the K8s cluster specified in ~/.kube/config. cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} $(KUSTOMIZE) build config/default | kubectl apply -f - +.PHONY: undeploy +undeploy: uninstall-cert-manager ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - + .PHONY: generate-deploy generate-deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} $(KUSTOMIZE) build config/default > operator.yaml -.PHONY: undeploy -undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - - ##@ Build Dependencies ## Location to install dependencies to @@ -265,7 +266,7 @@ bundle-build: ## Build the bundle image. .PHONY: bundle-push bundle-push: ## Push the bundle image. - $(MAKE) contianer-push IMG=$(BUNDLE_IMG) + $(MAKE) container-push IMG=$(BUNDLE_IMG) .PHONY: opm OPM = ./bin/opm @@ -333,4 +334,12 @@ test-e2e: install-operator-sdk go test ./test/e2e/* -v -ginkgo.v .PHONY: before-pr -before-pr: test generate-all \ No newline at end of file +before-pr: test generate-all + +.PHONY: install-cert-manager +install-cert-manager: + ./hack/local/cert-manager.sh install + +.PHONY: uninstall-cert-manager +uninstall-cert-manager: + ./hack/local/cert-manager.sh uninstall \ No newline at end of file diff --git a/PROJECT b/PROJECT index 0dc6a090f..0fdacec75 100644 --- a/PROJECT +++ b/PROJECT @@ -16,6 +16,10 @@ resources: kind: SonataFlow path: github.com/kiegroup/kogito-serverless-operator/api/v1alpha08 version: v1alpha08 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 - api: crdVersion: v1 namespaced: true diff --git a/api/go.mod b/api/go.mod index 76565e68a..ee73f483f 100644 --- a/api/go.mod +++ b/api/go.mod @@ -3,9 +3,13 @@ module github.com/kiegroup/kogito-serverless-operator/api go 1.19 require ( + github.com/onsi/ginkgo/v2 v2.9.5 + github.com/onsi/gomega v1.27.7 github.com/serverlessworkflow/sdk-go/v2 v2.2.3 k8s.io/api v0.27.2 k8s.io/apimachinery v0.27.2 + k8s.io/client-go v0.27.2 + k8s.io/klog/v2 v2.100.1 knative.dev/pkg v0.0.0-20230525143525-9bda38b21643 sigs.k8s.io/controller-runtime v0.15.0 sigs.k8s.io/yaml v1.3.0 @@ -20,18 +24,21 @@ require ( github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/zapr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.1 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.11.1 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect + github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -52,15 +59,15 @@ require ( github.com/stretchr/testify v1.8.2 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.9.0 // indirect + go.uber.org/zap v1.24.0 // indirect golang.org/x/crypto v0.8.0 // indirect - golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/oauth2 v0.7.0 // indirect - golang.org/x/sync v0.2.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.9.1 // indirect gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect @@ -68,9 +75,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.27.2 // indirect - k8s.io/client-go v0.27.2 // indirect k8s.io/component-base v0.27.2 // indirect - k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/api/go.sum b/api/go.sum index 03f6be6e0..5ed87ff6a 100644 --- a/api/go.sum +++ b/api/go.sum @@ -1,5 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -160,7 +161,6 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/api/v1alpha08/sonataflow_webhook.go b/api/v1alpha08/sonataflow_webhook.go new file mode 100644 index 000000000..eb030671a --- /dev/null +++ b/api/v1alpha08/sonataflow_webhook.go @@ -0,0 +1,101 @@ +// Copyright 2023 Red Hat, Inc. and/or its affiliates +// +// 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 v1alpha08 + +import ( + "fmt" + + cncfvalidator "github.com/serverlessworkflow/sdk-go/v2/validator" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/klog/v2" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/kiegroup/kogito-serverless-operator/api/metadata" + "github.com/kiegroup/kogito-serverless-operator/log" +) + +func (r *SonataFlow) SetupWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(r). + Complete() +} + +// Uncomment to enable the mutating webhook. +// Uncomment also the mutating webhook in the config/default/webhookcainjection_patch.yaml file. +// //+kubebuilder:webhook:path=/mutate-sonataflow-org-v1alpha08-sonataflow,mutating=true,failurePolicy=fail,sideEffects=None,groups=sonataflow.org,resources=sonataflows,verbs=create;update,versions=v1alpha08,name=msonataflow.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &SonataFlow{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (s *SonataFlow) Default() { + klog.V(log.I).InfoS("Applying default values for ", "name", s.Name) + // Add defaults + if len(s.ObjectMeta.Annotations[metadata.Version]) == 0 { + s.ObjectMeta.Annotations[metadata.Key] = metadata.SpecVersion + } + klog.V(log.I).InfoS("BBBBBBB Applying default values for ", "specVersion", s.ObjectMeta.Annotations[metadata.Key]) +} + +// change verbs to "verbs=create;update;delete" if you want to enable deletion validation. +//+kubebuilder:webhook:path=/validate-sonataflow-org-v1alpha08-sonataflow,mutating=false,failurePolicy=fail,sideEffects=None,groups=sonataflow.org,resources=sonataflows,verbs=create;update,versions=v1alpha08,name=vsonataflow.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &SonataFlow{} + +var requiredMetadataFields = [2]string{metadata.Description, metadata.Version} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *SonataFlow) ValidateCreate() (admission.Warnings, error) { + klog.V(log.I).InfoS("validate create", "name", r.Name) + return nil, validate(r) +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (r *SonataFlow) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + klog.V(log.I).InfoS("validate update", "name", r.Name) + return nil, validate(r) +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (r *SonataFlow) ValidateDelete() (admission.Warnings, error) { + klog.V(log.I).InfoS("validate delete", "name", r.Name) + // TODO + return nil, nil +} + +func validate(r *SonataFlow) error { + // validate the required metadata + response := "Field metadata.annotation.%s.%s not set." + var missingAnnotations []string + for _, field := range requiredMetadataFields { + if len(r.Annotations[field]) == 0 { + missingAnnotations = append(missingAnnotations, fmt.Sprintf(response, metadata.Domain, field)) + } + } + if len(missingAnnotations) > 0 { + return fmt.Errorf("%+v", missingAnnotations) + } + + klog.V(log.I).InfoS("Validating workflow", "flow", r.Spec.Flow) + + validator := cncfvalidator.GetValidator() + fmt.Printf("validator: %+v", r.Spec.Flow) + if err := validator.Struct(r.Spec.Flow); err != nil { + return err + } + + return nil +} diff --git a/api/v1alpha08/webhook_suite_test.go b/api/v1alpha08/webhook_suite_test.go new file mode 100644 index 000000000..38a56479a --- /dev/null +++ b/api/v1alpha08/webhook_suite_test.go @@ -0,0 +1,181 @@ +// Copyright 2023 Red Hat, Inc. and/or its affiliates +// +// 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 v1alpha08 + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "path/filepath" + "testing" + "time" + + "github.com/kiegroup/kogito-serverless-operator/api/metadata" + + "github.com/serverlessworkflow/sdk-go/v2/model" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + admissionv1beta1 "k8s.io/api/admission/v1beta1" + //+kubebuilder:scaffold:imports + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment +var ctx context.Context +var cancel context.CancelFunc + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Webhook Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: false, + WebhookInstallOptions: envtest.WebhookInstallOptions{ + Paths: []string{filepath.Join("..", "..", "config", "webhook")}, + }, + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + scheme := runtime.NewScheme() + err = AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + err = admissionv1beta1.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + // start webhook server using Manager + webhookInstallOptions := &testEnv.WebhookInstallOptions + mgr, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme, + Host: webhookInstallOptions.LocalServingHost, + Port: webhookInstallOptions.LocalServingPort, + CertDir: webhookInstallOptions.LocalServingCertDir, + LeaderElection: false, + MetricsBindAddress: "0", + }) + Expect(err).NotTo(HaveOccurred()) + + err = (&SonataFlow{}).SetupWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:webhook + + go func() { + defer GinkgoRecover() + err = mgr.Start(ctx) + Expect(err).NotTo(HaveOccurred()) + }() + + // wait for the webhook server to get ready + dialer := &net.Dialer{Timeout: time.Second} + addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort) + Eventually(func() error { + conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) + if err != nil { + return err + } + conn.Close() + return nil + }).Should(Succeed()) + +}) + +var _ = AfterSuite(func() { + cancel() + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) + +var _ = Describe("Test Serverless Workflow Validating Webhook", func() { + Context("basic workflow validations", func() { + + var swf SonataFlow + swf.ObjectMeta.Name = "greeting-fail-no-version" + swf.ObjectMeta.Namespace = "default" + annotations := make(map[string]string) + annotations[metadata.Description] = "Greeting example on k8s!" + swf.ObjectMeta.Annotations = annotations + + swf.Spec.Flow = Flow{ + Start: &model.Start{ + StateName: "ChooseOnLanguage", + }, + } + + state := &model.States{ + model.State{ + BaseState: model.BaseState{ + Name: "ChooseOnLanguage", + Type: "sleep", + End: &model.End{ + Terminate: true, + }, + }, + SleepState: &model.SleepState{ + Duration: "PTasd10S", + }, + }, + } + swf.Spec.Flow.States = *state + + It("should return validation error on missing version annotation", func() { + err := k8sClient.Create(context.TODO(), &swf) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("admission webhook \"vsonataflow.kb.io\" denied the request: [Field metadata.annotation.sonataflow.org.sonataflow.org/version not set.]")) + }) + + It("should return validation error on the workflow definition", func() { + swf.ObjectMeta.Annotations[metadata.Version] = "0.0.1" + err := k8sClient.Create(context.TODO(), &swf) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("admission webhook \"vsonataflow.kb.io\" denied the request: Key: 'Flow.States[0].SleepState.Duration' Error:Field validation for 'Duration' failed on the 'iso8601duration' tag")) + }) + }) +}) diff --git a/bundle/manifests/sonataflow-operator-webhook-service_v1_service.yaml b/bundle/manifests/sonataflow-operator-webhook-service_v1_service.yaml new file mode 100644 index 000000000..3f5623554 --- /dev/null +++ b/bundle/manifests/sonataflow-operator-webhook-service_v1_service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: sonataflow + app.kubernetes.io/instance: webhook-service + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: service + app.kubernetes.io/part-of: sonataflow + name: sonataflow-operator-webhook-service +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager +status: + loadBalancer: {} diff --git a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml index 2fc42f029..eddbf5b7f 100644 --- a/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml +++ b/bundle/manifests/sonataflow-operator.clusterserviceversion.yaml @@ -412,31 +412,6 @@ spec: control-plane: controller-manager spec: containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - seccompProfile: - type: RuntimeDefault - args: - --health-probe-bind-address=:8081 - --metrics-bind-address=127.0.0.1:8080 @@ -457,6 +432,10 @@ spec: initialDelaySeconds: 15 periodSeconds: 20 name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP readinessProbe: httpGet: path: /readyz @@ -475,6 +454,31 @@ spec: capabilities: drop: - ALL + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault securityContext: runAsNonRoot: true seccompProfile: @@ -541,3 +545,35 @@ spec: provider: name: Red Hat version: 2.0.0-snapshot + webhookdefinitions: + - admissionReviewVersions: + - v1 + containerPort: 443 + conversionCRDs: + - sonataflowplatforms.sonataflow.org + deploymentName: sonataflow-operator-controller-manager + generateName: csonataflowplatforms.kb.io + sideEffects: None + targetPort: 9443 + type: ConversionWebhook + webhookPath: /convert + - admissionReviewVersions: + - v1 + containerPort: 443 + deploymentName: sonataflow-operator-controller-manager + failurePolicy: Fail + generateName: vsonataflow.kb.io + rules: + - apiGroups: + - sonataflow.org + apiVersions: + - v1alpha08 + operations: + - CREATE + - UPDATE + resources: + - sonataflows + sideEffects: None + targetPort: 9443 + type: ValidatingAdmissionWebhook + webhookPath: /validate-sonataflow-org-v1alpha08-sonataflow diff --git a/bundle/manifests/sonataflow.org_sonataflowbuilds.yaml b/bundle/manifests/sonataflow.org_sonataflowbuilds.yaml index fe5c96060..3f0c2c2bb 100644 --- a/bundle/manifests/sonataflow.org_sonataflowbuilds.yaml +++ b/bundle/manifests/sonataflow.org_sonataflowbuilds.yaml @@ -2,6 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: sonataflow-operator-system/sonataflow-operator-serving-cert controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: sonataflowbuilds.sonataflow.org diff --git a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml index 5d417a84b..9b0e042be 100644 --- a/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml +++ b/bundle/manifests/sonataflow.org_sonataflowplatforms.yaml @@ -2,10 +2,21 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: sonataflow-operator-system/sonataflow-operator-serving-cert controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: sonataflowplatforms.sonataflow.org spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: sonataflow-operator-webhook-service + namespace: sonataflow-operator-system + path: /convert + conversionReviewVersions: + - v1 group: sonataflow.org names: kind: SonataFlowPlatform diff --git a/bundle/manifests/sonataflow.org_sonataflows.yaml b/bundle/manifests/sonataflow.org_sonataflows.yaml index e70b1a87d..8e7487128 100644 --- a/bundle/manifests/sonataflow.org_sonataflows.yaml +++ b/bundle/manifests/sonataflow.org_sonataflows.yaml @@ -2,6 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: sonataflow-operator-system/sonataflow-operator-serving-cert controller-gen.kubebuilder.io/version: v0.9.2 creationTimestamp: null name: sonataflows.sonataflow.org diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml new file mode 100644 index 000000000..6759ea148 --- /dev/null +++ b/config/certmanager/certificate.yaml @@ -0,0 +1,39 @@ +# The following manifests contain a self-signed issuer CR and a certificate CR. +# More document can be found at https://docs.cert-manager.io +# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/name: issuer + app.kubernetes.io/instance: selfsigned-issuer + app.kubernetes.io/component: certificate + app.kubernetes.io/created-by: sonataflow + app.kubernetes.io/part-of: sonataflow + app.kubernetes.io/managed-by: kustomize + name: selfsigned-issuer + namespace: system +spec: + selfSigned: {} +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/name: certificate + app.kubernetes.io/instance: serving-cert + app.kubernetes.io/component: certificate + app.kubernetes.io/created-by: sonataflow + app.kubernetes.io/part-of: sonataflow + app.kubernetes.io/managed-by: kustomize + name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml + namespace: system +spec: + # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize + dnsNames: + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc + - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local + issuerRef: + kind: Issuer + name: selfsigned-issuer + secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize \ No newline at end of file diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml new file mode 100644 index 000000000..bbaca30d1 --- /dev/null +++ b/config/certmanager/kustomization.yaml @@ -0,0 +1,5 @@ +resources: + - certificate.yaml + +configurations: + - kustomizeconfig.yaml \ No newline at end of file diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml new file mode 100644 index 000000000..5d0f125b4 --- /dev/null +++ b/config/certmanager/kustomizeconfig.yaml @@ -0,0 +1,16 @@ +# This configuration is for teaching kustomize how to update name ref and var substitution +nameReference: + - kind: Issuer + group: cert-manager.io + fieldSpecs: + - kind: Certificate + group: cert-manager.io + path: spec/issuerRef/name + +varReference: + - kind: Certificate + group: cert-manager.io + path: spec/commonName + - kind: Certificate + group: cert-manager.io + path: spec/dnsNames \ No newline at end of file diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index a07d24bb8..46fbc88b1 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -10,16 +10,16 @@ resources: patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD -#- patches/webhook_in_workflows.yaml -#- patches/webhook_in_sonataflows.yaml -#- patches/webhook_in_sonataflowplatforms.yaml +- patches/cainjection_in_sonataflows.yaml +- patches/cainjection_in_sonataflowbuilds.yaml +- patches/webhook_in_sonataflowplatforms.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD -#- patches/cainjection_in_workflows.yaml -#- patches/cainjection_in_sonataflowworkflows.yaml -#- patches/cainjection_in_sonataflowplatforms.yaml +- patches/cainjection_in_sonataflows.yaml +- patches/cainjection_in_sonataflowbuilds.yaml +- patches/cainjection_in_sonataflowplatforms.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_sonataflows.yaml b/config/crd/patches/cainjection_in_sonataflows.yaml index 0f2efd86b..4b9aa140c 100644 --- a/config/crd/patches/cainjection_in_sonataflows.yaml +++ b/config/crd/patches/cainjection_in_sonataflows.yaml @@ -4,4 +4,4 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: sonataflowworkflows.sonataflow.org + name: sonataflows.sonataflow.org diff --git a/config/crd/patches/webhook_in_sonataflows.yaml b/config/crd/patches/webhook_in_sonataflows.yaml index d8550d047..9e9a98d35 100644 --- a/config/crd/patches/webhook_in_sonataflows.yaml +++ b/config/crd/patches/webhook_in_sonataflows.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: sonataflowworkflows.sonataflow.org + name: sonataflows.sonataflow.org spec: conversion: strategy: Webhook diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 613c7e3b3..6cf1c3a0b 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -18,9 +18,9 @@ bases: - ../manager # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml -#- ../webhook +- ../webhook # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager +- ../certmanager # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus @@ -36,39 +36,39 @@ patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml -#- manager_webhook_patch.yaml +- manager_webhook_patch.yaml # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. # 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml +- webhookcainjection_patch.yaml # the following config is for teaching kustomize how to do var substitution vars: # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldref: -# fieldpath: metadata.namespace -#- name: CERTIFICATE_NAME -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -#- name: SERVICE_NAMESPACE # namespace of the service -# objref: -# kind: Service -# version: v1 -# name: webhook-service -# fieldref: -# fieldpath: metadata.namespace -#- name: SERVICE_NAME -# objref: -# kind: Service -# version: v1 -# name: webhook-service +- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR + objref: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert # this name should match the one in certificate.yaml + fieldref: + fieldpath: metadata.namespace +- name: CERTIFICATE_NAME + objref: + kind: Certificate + group: cert-manager.io + version: v1 + name: serving-cert # this name should match the one in certificate.yaml +- name: SERVICE_NAMESPACE # namespace of the service + objref: + kind: Service + version: v1 + name: webhook-service + fieldref: + fieldpath: metadata.namespace +- name: SERVICE_NAME + objref: + kind: Service + version: v1 + name: webhook-service diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml new file mode 100644 index 000000000..716560fc4 --- /dev/null +++ b/config/default/manager_webhook_patch.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system +spec: + template: + spec: + containers: + - name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert \ No newline at end of file diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml new file mode 100644 index 000000000..a8bc00be6 --- /dev/null +++ b/config/default/webhookcainjection_patch.yaml @@ -0,0 +1,30 @@ +# This patch add annotation to admission webhook config and +# the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. +# Uncomment to enable the mudating webhook +#apiVersion: admissionregistration.k8s.io/v1 +#kind: MutatingWebhookConfiguration +#metadata: +# labels: +# app.kubernetes.io/name: msonataflow.kb.io +# app.kubernetes.io/instance: mutating-webhook-configuration +# app.kubernetes.io/component: webhook +# app.kubernetes.io/created-by: sonataflow +# app.kubernetes.io/part-of: sonataflow +# app.kubernetes.io/managed-by: kustomize +# name: mutating-webhook-configuration +# annotations: +# cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + labels: + app.kubernetes.io/name: vsonataflow.kb.io + app.kubernetes.io/instance: validating-webhook-configuration + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: sonataflow + app.kubernetes.io/part-of: sonataflow + app.kubernetes.io/managed-by: kustomize + name: validating-webhook-configuration + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) \ No newline at end of file diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 494861437..203314bab 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -19,9 +19,14 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: quay.io/kiegroup/kogito-serverless-operator-nightly - newTag: latest -# Patching the manager deployment file to add an env var with the operator namespace in + newName: quay.io/spolti/kogito-serverless-operator + newTag: test5 + # Patching the manager deployment file to add an env var with the operator namespace in + # - op: add + # path: /spec/template/spec/containers/0/env + # value: + # - name: ENABLE_WEBHOOKS + # value: "false" patchesJson6902: - patch: |- - op: add diff --git a/config/manifests/kustomization.yaml b/config/manifests/kustomization.yaml index 64d054689..135226490 100644 --- a/config/manifests/kustomization.yaml +++ b/config/manifests/kustomization.yaml @@ -9,19 +9,19 @@ resources: # [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix. # Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager. # These patches remove the unnecessary "cert" volume and its manager container volumeMount. -#patchesJson6902: -#- target: -# group: apps -# version: v1 -# kind: Deployment -# name: controller-manager -# namespace: system -# patch: |- -# # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs. -# # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment. -# - op: remove -# path: /spec/template/spec/containers/1/volumeMounts/0 -# # Remove the "cert" volume, since OLM will create and mount a set of certs. -# # Update the indices in this path if adding or removing volumes in the manager's Deployment. -# - op: remove -# path: /spec/template/spec/volumes/0 +patchesJson6902: +- target: + group: apps + version: v1 + kind: Deployment + name: controller-manager + namespace: system + patch: |- + # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs. + # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment. + - op: remove + path: /spec/template/spec/containers/0/volumeMounts/0 + # Remove the "cert" volume, since OLM will create and mount a set of certs. + # Update the indices in this path if adding or removing volumes in the manager's Deployment. + - op: remove + path: /spec/template/spec/volumes/0 diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml new file mode 100644 index 000000000..fbd74f349 --- /dev/null +++ b/config/webhook/kustomization.yaml @@ -0,0 +1,6 @@ +resources: + - manifests.yaml + - service.yaml + +configurations: + - kustomizeconfig.yaml \ No newline at end of file diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml new file mode 100644 index 000000000..15185025d --- /dev/null +++ b/config/webhook/kustomizeconfig.yaml @@ -0,0 +1,25 @@ +# the following config is for teaching kustomize where to look at when substituting vars. +# It requires kustomize v2.1.0 or newer to work properly. +nameReference: + - kind: Service + version: v1 + fieldSpecs: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/name + +namespace: + - kind: MutatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + - kind: ValidatingWebhookConfiguration + group: admissionregistration.k8s.io + path: webhooks/clientConfig/service/namespace + create: true + +varReference: + - path: metadata/annotations \ No newline at end of file diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml new file mode 100644 index 000000000..ffa576364 --- /dev/null +++ b/config/webhook/manifests.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: system + path: /validate-sonataflow-org-v1alpha08-sonataflow + failurePolicy: Fail + name: vsonataflow.kb.io + rules: + - apiGroups: + - sonataflow.org + apiVersions: + - v1alpha08 + operations: + - CREATE + - UPDATE + resources: + - sonataflows + sideEffects: None diff --git a/config/webhook/service.yaml b/config/webhook/service.yaml new file mode 100644 index 000000000..ccf809991 --- /dev/null +++ b/config/webhook/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: service + app.kubernetes.io/instance: webhook-service + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: sonataflow + app.kubernetes.io/part-of: sonataflow + app.kubernetes.io/managed-by: kustomize + name: webhook-service + namespace: system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager \ No newline at end of file diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index d5736b0a8..3a302429e 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -108,6 +108,14 @@ make run > **NOTE:** Run `make help` for more information on all potential `make` targets +> **NOTE:** The Webhook is disabled by default when running it locally, however, if you need to test it, just set +the ENABLE_WEBHOOKS env to true, like this example: +> ```bash +> make run ENABLE_WEBHOOKS=true +> ``` +> For this, please follow the steps described in this [link](https://book.kubebuilder.io/cronjob-tutorial/running.html#running-webhooks-locally). + + More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) ### How-tos @@ -128,6 +136,15 @@ make container-build #### Deploy +When not using OLM it is required to have certificates in place in order for the webhook to properly work. +It is recommended to use the CertManager operator to handle this task. +It is automatically done when executing the `make deploy` command. Or, manually, as described +[here](https://book.kubebuilder.io/cronjob-tutorial/running-webhook.html). + +The make goal `deploy` will check if the CertManager Operator is installed, if not, it will be installed for you, and, +when you're done, it will be cleaned up as part of the `undeploy` make goal. + + ```sh make deploy ``` diff --git a/go.work.sum b/go.work.sum index 6491617b7..7b77e3c6a 100644 --- a/go.work.sum +++ b/go.work.sum @@ -82,6 +82,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -412,6 +413,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= @@ -461,11 +463,14 @@ go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/A go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0= go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= @@ -488,6 +493,7 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -501,6 +507,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -559,6 +566,7 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -666,6 +674,7 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= diff --git a/hack/local/cert-manager.sh b/hack/local/cert-manager.sh new file mode 100755 index 000000000..dc8a82d1f --- /dev/null +++ b/hack/local/cert-manager.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Copyright 2023 Red Hat, Inc. and/or its affiliates +# +# 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. + +CERT_MANAGER_INSTALLER="https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml" + +# Verify if there is any cert-manager operator installed +function verify() { + # get any pod that has the status different than Running + kubectl -n cert-manager rollout status deploy/cert-manager-webhook --timeout=120s + result_running=$(exec kubectl get pods --namespace cert-manager --field-selector=status.phase==Running --no-headers | wc -l | tr -d ' ') + if [ "${result_running}" != 3 ]; then + echo "cert manager is not ready, please verify." + kubectl get pods --namespace cert-manager + fi + echo "ready" +} + +case "$1" in + "install") + kubectl apply -f ${CERT_MANAGER_INSTALLER} + max_attempts=10 + counter=0 + while [ $counter -lt $max_attempts ]; do + is_ready=$(verify) + if [ "${is_ready}" == "ready" ]; then + break + else + counter=$((counter+1)) + # wait 3 seconds + sleep 3 + fi + done + ;; + "uninstall") + kubectl delete -f ${CERT_MANAGER_INSTALLER} + ;; + "verify") + verify + ;; + *) + echo "Option not recognized, allowed values are [un]install and verify." + exit 1 + ;; +esac diff --git a/main.go b/main.go index 14f3ac113..68a2548d7 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ package main import ( "flag" "os" + "strconv" "k8s.io/klog/v2/klogr" @@ -46,6 +47,8 @@ var ( scheme = runtime.NewScheme() ) +const enableWebhookEnv = "ENABLE_WEBHOOKS" + func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(operatorapi.AddToScheme(scheme)) @@ -100,6 +103,19 @@ func main() { os.Exit(1) } + // Setup webhooks + enable := true + env := os.Getenv(enableWebhookEnv) + if len(env) > 0 { + enable, _ = strconv.ParseBool(env) + } + if enable { + if err = (&operatorapi.SonataFlow{}).SetupWebhookWithManager(mgr); err != nil { + klog.V(log.E).ErrorS(err, "unable to create webhook", "webhook", "SonataFlow") + os.Exit(1) + } + } + if err = (&controllers.SonataFlowPlatformReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), diff --git a/operator.yaml b/operator.yaml index 1b68e11b2..fdf767794 100644 --- a/operator.yaml +++ b/operator.yaml @@ -9,8 +9,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: sonataflow-operator-system/sonataflow-operator-serving-cert controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null name: sonataflowbuilds.sonataflow.org spec: group: sonataflow.org @@ -145,10 +145,20 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: sonataflow-operator-system/sonataflow-operator-serving-cert controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null name: sonataflowplatforms.sonataflow.org spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + name: sonataflow-operator-webhook-service + namespace: sonataflow-operator-system + path: /convert + conversionReviewVersions: + - v1 group: sonataflow.org names: kind: SonataFlowPlatform @@ -384,8 +394,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: + cert-manager.io/inject-ca-from: sonataflow-operator-system/sonataflow-operator-serving-cert controller-gen.kubebuilder.io/version: v0.9.2 - creationTimestamp: null name: sonataflows.sonataflow.org spec: group: sonataflow.org @@ -3070,6 +3080,26 @@ spec: selector: control-plane: controller-manager --- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: sonataflow + app.kubernetes.io/instance: webhook-service + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: service + app.kubernetes.io/part-of: sonataflow + name: sonataflow-operator-webhook-service + namespace: sonataflow-operator-system +spec: + ports: + - port: 443 + protocol: TCP + targetPort: 9443 + selector: + control-plane: controller-manager +--- apiVersion: apps/v1 kind: Deployment metadata: @@ -3090,31 +3120,6 @@ spec: control-plane: controller-manager spec: containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - seccompProfile: - type: RuntimeDefault - args: - --health-probe-bind-address=:8081 - --metrics-bind-address=127.0.0.1:8080 @@ -3135,6 +3140,10 @@ spec: initialDelaySeconds: 15 periodSeconds: 20 name: manager + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP readinessProbe: httpGet: path: /readyz @@ -3153,9 +3162,114 @@ spec: capabilities: drop: - ALL + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: cert + readOnly: true + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --logtostderr=true + - --v=0 + image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0 + name: kube-rbac-proxy + ports: + - containerPort: 8443 + name: https + protocol: TCP + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 5m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault serviceAccountName: sonataflow-operator-controller-manager terminationGracePeriodSeconds: 10 + volumes: + - name: cert + secret: + defaultMode: 420 + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + labels: + app.kubernetes.io/component: certificate + app.kubernetes.io/created-by: sonataflow + app.kubernetes.io/instance: serving-cert + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: certificate + app.kubernetes.io/part-of: sonataflow + name: sonataflow-operator-serving-cert + namespace: sonataflow-operator-system +spec: + dnsNames: + - sonataflow-operator-webhook-service.sonataflow-operator-system.svc + - sonataflow-operator-webhook-service.sonataflow-operator-system.svc.cluster.local + issuerRef: + kind: Issuer + name: sonataflow-operator-selfsigned-issuer + secretName: webhook-server-cert +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + labels: + app.kubernetes.io/component: certificate + app.kubernetes.io/created-by: sonataflow + app.kubernetes.io/instance: selfsigned-issuer + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: issuer + app.kubernetes.io/part-of: sonataflow + name: sonataflow-operator-selfsigned-issuer + namespace: sonataflow-operator-system +spec: + selfSigned: {} +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + annotations: + cert-manager.io/inject-ca-from: sonataflow-operator-system/sonataflow-operator-serving-cert + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/created-by: sonataflow + app.kubernetes.io/instance: validating-webhook-configuration + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: vsonataflow.kb.io + app.kubernetes.io/part-of: sonataflow + name: sonataflow-operator-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: sonataflow-operator-webhook-service + namespace: sonataflow-operator-system + path: /validate-sonataflow-org-v1alpha08-sonataflow + failurePolicy: Fail + name: vsonataflow.kb.io + rules: + - apiGroups: + - sonataflow.org + apiVersions: + - v1alpha08 + operations: + - CREATE + - UPDATE + resources: + - sonataflows + sideEffects: None