diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a2d48d2..39bae40 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,7 +12,8 @@ jobs: if: "!contains(github.event.head_commit.message, '[ci skip]')" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - name: Checkout + uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version-file: go.mod @@ -34,3 +35,4 @@ jobs: - name: Run e2e tests run: | make deploy + kubectl rollout status deploy/kcl-webhook-server --timeout=1m diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 27d3eb9..5a75cca 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,7 +8,7 @@ on: permissions: contents: write jobs: - image-amd64: + webhook-init-image-amd64: runs-on: ubuntu-latest steps: - name: Checkout @@ -26,6 +26,10 @@ jobs: version: v0.11.2 install: true + - name: Copy and edit amd4 arch for Dockerfile + run: | + cp docker/amd64/Dockerfile.init Dockerfile + # <--- Login, build and push image to Docker Hub ---> - name: Login to Docker Hub uses: docker/login-action@v3 @@ -37,7 +41,7 @@ jobs: id: meta uses: docker/metadata-action@v5 with: - images: kcllang/webhookserver + images: kcllang/webhook-init - name: Build and push Docker image uses: docker/build-push-action@v6 with: @@ -47,7 +51,98 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - image-arm64: + webhook-server-image-amd64: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + version: v0.11.2 + install: true + + - name: Copy and edit amd4 arch for Dockerfile + run: | + cp docker/amd64/Dockerfile.server Dockerfile + + # <--- Login, build and push image to Docker Hub ---> + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: kcllang/webhook-server + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + webhook-init-image-arm64: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Setup QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: all + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + version: v0.11.2 + install: true + + # <--- Login, build and push image to Docker Hub ---> + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Copy and edit arm64 arch for Dockerfile + run: | + cp docker/amd64/Dockerfile.init Dockerfile + sed -i 's/kcllang/kcl-arm64\/kcllang/kcl/g' Dockerfile + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: kcllang/webhook-init-arm64 + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + webhook-server-image-arm64: runs-on: ubuntu-latest steps: - name: Checkout @@ -76,17 +171,16 @@ jobs: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Edit arm64 arch for Dockerfile + - name: Copy and edit arm64 arch for Dockerfile run: | - sed -i 's/ubuntu:22.04/arm64v8\/ubuntu:20.04/g' Dockerfile + cp docker/amd64/Dockerfile.server Dockerfile sed -i 's/kcllang/kcl-arm64\/kcllang/kcl/g' Dockerfile - echo "ENV KCL_FAST_EVAL=1" >> Dockerfile - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@v5 with: - images: kcllang/webhookserver-arm64 + images: kcllang/webhook-server-arm64 - name: Build and push Docker image uses: docker/build-push-action@v6 with: diff --git a/Makefile b/Makefile index c638578..5014eee 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ VERSION ?= 0.2.0 # Image URL to use all building/pushing image targets -IMG ?= kcllang/webhookserver +IMG ?= kcllang/webhook-server # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.30.0 @@ -68,7 +68,7 @@ help: ## Display this help. .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + $(CONTROLLER_GEN) rbac:roleName=webhook-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. @@ -90,36 +90,38 @@ test: manifests generate fmt vet envtest ## Run tests. .PHONY: build build: ## Build binaries. - make manager + make webhook .PHONY: build build-linux: ## Build binaries. - make manager-linux + make webhook-linux -.PHONY: manager -manager: manifests generate fmt vet ## Build manager binary - go build -o bin/manager main.go +.PHONY: webhook +webhook: manifests generate fmt vet ## Build webhook server and init container binary + go build -o bin/wehbook-server cmd/webhook-server/main.go + go build -o bin/wehbook-init cmd/webhook-init/main.go -.PHONY: manager-linux -manager-linux: generate fmt vet - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o build/bin/manager main.go +.PHONY: webook-linux +webhook-linux: generate fmt vet + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o bin/wehbook-server cmd/webhook-server/main.go + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o bin/wehbook-init cmd/webhook-init/main.go .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. go run ./main.go -# If you wish built the manager image targeting other platforms you can use the --platform flag. +# If you wish built the webhook image targeting other platforms you can use the --platform flag. # (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ .PHONY: docker-build -docker-build: ## Build docker image with the manager. +docker-build: ## Build docker image with the webhook. docker build -t $(IMG) . .PHONY: docker-push -docker-push: ## Push docker image with the manager. +docker-push: ## Push docker image with the webhook. docker push ${IMG} -# PLATFORMS defines the target platforms for the manager image be build to provide support to multiple +# PLATFORMS defines the target platforms for the webhook 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: # - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ # - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ @@ -127,7 +129,7 @@ docker-push: ## Push docker image with the manager. # To properly provided solutions that supports more than one platform you should use this option. PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le .PHONY: docker-buildx -docker-buildx: test ## Build and push docker image for the manager for cross-platform support +docker-buildx: test ## Build and push docker image for the webhook for cross-platform support # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross - docker buildx create --name project-v3-builder diff --git a/api/kclrun/v1alpha1/zz_generated.defaults.go b/api/kclrun/v1alpha1/zz_generated.defaults.go deleted file mode 100644 index 188226e..0000000 --- a/api/kclrun/v1alpha1/zz_generated.defaults.go +++ /dev/null @@ -1,32 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -/* -Copyright 2023. - -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. -*/ -// Code generated by defaulter-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// RegisterDefaults adds defaulters functions to the given scheme. -// Public to allow building arbitrary schemes. -// All generated defaulters are covering - they call all nested defaulters. -func RegisterDefaults(scheme *runtime.Scheme) error { - return nil -} diff --git a/cmd/webhook-init/main.go b/cmd/webhook-init/main.go new file mode 100644 index 0000000..10c8dfa --- /dev/null +++ b/cmd/webhook-init/main.go @@ -0,0 +1,194 @@ +/* +Copyright 2024 The KCL Authors +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 main + +import ( + "bytes" + "context" + cryptorand "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "os" + "time" + + log "github.com/sirupsen/logrus" + "k8s.io/client-go/kubernetes" + + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" +) + +func main() { + var caPEM, serverCertPEM, serverPrivKeyPEM *bytes.Buffer + // CA config + ca := &x509.Certificate{ + SerialNumber: big.NewInt(2020), + Subject: pkix.Name{ + Organization: []string{"kcl-lang.io"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 0, 0), + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + + // CA private key + caPrivKey, err := rsa.GenerateKey(cryptorand.Reader, 4096) + if err != nil { + fmt.Println(err) + } + + // Self signed CA certificate + caBytes, err := x509.CreateCertificate(cryptorand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey) + if err != nil { + fmt.Println(err) + } + + // PEM encode CA cert + caPEM = new(bytes.Buffer) + _ = pem.Encode(caPEM, &pem.Block{ + Type: "CERTIFICATE", + Bytes: caBytes, + }) + + dnsNames := []string{"webhook-service", + "webhook-service.default", "webhook-service.default.svc"} + commonName := "webhook-service.default.svc" + + // server cert config + cert := &x509.Certificate{ + DNSNames: dnsNames, + SerialNumber: big.NewInt(1658), + Subject: pkix.Name{ + CommonName: commonName, + Organization: []string{"krm.kcl.dev"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(1, 0, 0), + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + } + + // server private key + serverPrivKey, err := rsa.GenerateKey(cryptorand.Reader, 4096) + if err != nil { + fmt.Println(err) + } + + // sign the server cert + serverCertBytes, err := x509.CreateCertificate(cryptorand.Reader, cert, ca, &serverPrivKey.PublicKey, caPrivKey) + if err != nil { + fmt.Println(err) + } + + // PEM encode the server cert and key + serverCertPEM = new(bytes.Buffer) + _ = pem.Encode(serverCertPEM, &pem.Block{ + Type: "CERTIFICATE", + Bytes: serverCertBytes, + }) + + serverPrivKeyPEM = new(bytes.Buffer) + _ = pem.Encode(serverPrivKeyPEM, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(serverPrivKey), + }) + + err = os.MkdirAll("/etc/webhook/certs/", 0666) + if err != nil { + log.Panic(err) + } + err = WriteFile("/etc/webhook/certs/tls.crt", serverCertPEM) + if err != nil { + log.Panic(err) + } + + err = WriteFile("/etc/webhook/certs/tls.key", serverPrivKeyPEM) + if err != nil { + log.Panic(err) + } + // Create `MutatingWebhookConfiguration` resources + createMutationConfig(serverCertPEM) +} + +// WriteFile writes data in the file at the given path +func WriteFile(filepath string, sCert *bytes.Buffer) error { + f, err := os.Create(filepath) + if err != nil { + return err + } + defer f.Close() + + _, err = f.Write(sCert.Bytes()) + if err != nil { + return err + } + return nil +} + +func createMutationConfig(caCert *bytes.Buffer) { + var ( + webhookNamespace, _ = os.LookupEnv("WEBHOOK_NAMESPACE") + mutationCfgName, _ = os.LookupEnv("MUTATE_CONFIG") + webhookService, _ = os.LookupEnv("WEBHOOK_SERVICE") + ) + config := ctrl.GetConfigOrDie() + kubeClient, err := kubernetes.NewForConfig(config) + if err != nil { + panic("failed to set go-client") + } + + path := "/mutate" + fail := admissionregistrationv1.Fail + + mutateconfig := &admissionregistrationv1.MutatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: mutationCfgName, + }, + Webhooks: []admissionregistrationv1.MutatingWebhook{{ + Name: "kcl-webhook-server.krm.kcl.dev", + ClientConfig: admissionregistrationv1.WebhookClientConfig{ + CABundle: caCert.Bytes(), // CA bundle created earlier + Service: &admissionregistrationv1.ServiceReference{ + Name: webhookService, + Namespace: webhookNamespace, + Path: &path, + }, + }, + Rules: []admissionregistrationv1.RuleWithOperations{{Operations: []admissionregistrationv1.OperationType{ + admissionregistrationv1.Create}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{""}, + APIVersions: []string{"v1"}, + Resources: []string{"deployments", "pods"}, + }, + }}, + FailurePolicy: &fail, + }}, + } + + if _, err := kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(context.Background(), mutateconfig, metav1.CreateOptions{}); err != nil { + panic(err) + } +} diff --git a/main.go b/cmd/webhook-server/main.go similarity index 97% rename from main.go rename to cmd/webhook-server/main.go index 30f6598..dcb6853 100644 --- a/main.go +++ b/cmd/webhook-server/main.go @@ -1,9 +1,11 @@ /* -Copyright 2023. +Copyright 2024 The KCL Authors 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 + + 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. diff --git a/config/all.yaml b/config/all.yaml index 1a77914..7fbd88f 100644 --- a/config/all.yaml +++ b/config/all.yaml @@ -76,7 +76,7 @@ metadata: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: manager-role + name: webhook-role rules: - apiGroups: - krm.kcl.dev @@ -108,32 +108,22 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: manager-rolebinding + name: webhook-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: manager-role + name: webhook-role subjects: - kind: ServiceAccount name: default namespace: default --- apiVersion: v1 -data: - cert.pem: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQ1akNDQXM2Z0F3SUJBZ0lVSk03UjJPZ1JpcUN4dGZpaDRhZHpMbnF3aTVJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZTXhDekFKQmdOVkJBWVRBa05PTVJBd0RnWURWUVFJRXdkQ1pXbHFhVzVuTVJBd0RnWURWUVFIRXdkQwpaV2xxYVc1bk1SUXdFZ1lEVlFRS0V3dFZibWwwWldSVGRHRmphekVQTUEwR0ExVUVDeE1HUkdWMmIzQnpNU2t3Ckp3WURWUVFERXlCd2IyUXRZVzV1YjNSaGRHVXRkMlZpYUc5dmF5NWtaV1poZFd4MExuTjJZekFnRncweU16QTQKTXpFd01qVXdNemRhR0E4ek1ESXpNREV3TVRBeU5UQXpOMW93Z1lNeEN6QUpCZ05WQkFZVEFrTk9NUkF3RGdZRApWUVFJRXdkQ1pXbHFhVzVuTVJBd0RnWURWUVFIRXdkQ1pXbHFhVzVuTVJRd0VnWURWUVFLRXd0VmJtbDBaV1JUCmRHRmphekVQTUEwR0ExVUVDeE1HUkdWMmIzQnpNU2t3SndZRFZRUURFeUJ3YjJRdFlXNXViM1JoZEdVdGQyVmkKYUc5dmF5NWtaV1poZFd4MExuTjJZekNDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQgpBTXVrYTFBS0tYNko2UWhrQmQ2clNyRzh0a3ZsbG5lcW5PTE5ya0FZMGV0VXNtS3hlZnJoYUhjNlhjdTJYcGFZCitGRWxTeEU0ZGNKa09YWmpGTDg4c3RleXRqOWFHaG5uOHN3WnozMWFNUU5iMWJERjIvTTdVWm16bjVKdG1IV2MKKzdnUUJORDN5Myt6SFZyaWNYVnNxb2ppSFhoN0huUC8vVlRNellrTUhtUk9ER0ZZTUlRVmowcDl3VFVYMnE3NQphSS9zMTY2Vi9CRURIbHNlc29SbnVjU0h4R2JtMzF3TnNQSFQzOWpIRHEvV3AvNytzNnpjT3hZekt1MzdIOWN1CkIycDhRNUp6NWdtazFuU01JZU1rUGhveStWUXYrRHR2TnNnN2tWOGhvTERKOWdBdW5obDFUbVNLQmU2elM1SUsKS3RaWFNENE1NQjc3VC9CTXBmU3VLQ01DQXdFQUFhTk9NRXd3S3dZRFZSMFJCQ1F3SW9JZ2NHOWtMV0Z1Ym05MApZWFJsTFhkbFltaHZiMnN1WkdWbVlYVnNkQzV6ZG1Nd0hRWURWUjBPQkJZRUZMZGgycnVhYjAxSERSN2xGWlBHClRUbEU1ZXg0TUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFESEJONmNNdmZ1OTJpWjZEM1ZLUDdFbnIzWHF4cXYKUE9CdTdMRnlxRVBySlZ4L2E0ME5zUXQzYlk4NWUzOXM5aERHRklYcTVwcXpQRFl3WUxnUW8za2IyVjJCVVVmcwpBZmtjVE9DbExsK3dDK2kzTm9mZzNLZC9CckUxelRhUGc1UmFkRU5HUVZmbjZ6a3NYNEVBaXJhUmdhVW9uQmFPCk5WdUFLZ1phSVovUXJjQ1RjSGZMNC9yeVNWT0hTSzRZa05RWFVWZnhscGJCaU9HRUg3N2NqS3g2eEpjSHZTYnoKVXJtZlFHQ25IS25aWUJhSVJ2ZTErWGtEbEllUkFlTlB1SWh2YjRKZm0yNmxYbnFVcXpoeEd4b29wQi81eHN6NwoyeEpmWFdwdzhVZWpudGYxRzRIYytyVStJL1dnRnM2WTRFb3pTUzVKOWtCVHNYSkdFb011cVFhbAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - key.pem: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktZd2dnU2lBZ0VBQW9JQkFRRExwR3RRQ2lsK2lla0kKWkFYZXEwcXh2TFpMNVpaM3Fweml6YTVBR05IclZMSmlzWG42NFdoM09sM0x0bDZXbVBoUkpVc1JPSFhDWkRsMgpZeFMvUExMWHNyWS9XaG9aNS9MTUdjOTlXakVEVzlXd3hkdnpPMUdaczUrU2JaaDFuUHU0RUFUUTk4dC9zeDFhCjRuRjFiS3FJNGgxNGV4NXovLzFVek0ySkRCNWtUZ3hoV0RDRUZZOUtmY0UxRjlxdStXaVA3TmV1bGZ3UkF4NWIKSHJLRVo3bkVoOFJtNXQ5Y0RiRHgwOS9ZeHc2djFxZisvck9zM0RzV015cnQreC9YTGdkcWZFT1NjK1lKcE5aMApqQ0hqSkQ0YU12bFVML2c3YnpiSU81RmZJYUN3eWZZQUxwNFpkVTVraWdYdXMwdVNDaXJXVjBnK0REQWUrMC93ClRLWDByaWdqQWdNQkFBRUNnZ0VBTDRpaCtlMWRVU21jeVBZYi9xVktDcUhMaFh6Q09nMkxrRTlGVUxYYWJnMTMKbEJ6c0paanUyd0czT2lGSUErd1I3bEwvcWpYZEd2SnREVXJFWVgwb0c3d2QyVHpOWWVXbjRXZFc3T0ljRmJZZQpjNlJEbW90cW1TOTUzR0tUbDBSODV6SnFCV01KejdWOThTUnhLbUVpajFBRVpBYU5EYk9mck9jZlJOR2MySG1LCmR3RXhWZDMxeTFpUkhNS05ndjZZZmMwZ1lNaGFmc3lNMC9oS1dQMVFlcHRCRnNRYktlcFBSNmhSLzRiT0ZYeUEKeXdBNVZWZ1p4VWF0MElWbCt2UnZXUWE3SUdOQTFSVmxNK0lEWW9HaE1wVGg2OGl4ajdaL3ZIbVpBOVJxTDl3NQpMajFHKzJldUtvYzdJY0dZRkwzT1MvdFpqSm1lVTlaNDdqaTBqM0t6UFFLQmdRRDJrdXh3Ym5SWVo0Tzl0eTQ3CnlZa3dDNFh3VFpXampwdmR6VjRSWlFCRXd4UlF4OTVPV0RoU1U3d3o2MHczZkprUTFPUnFycGx4QytraVMxMmkKRHo2ZFN3eDZ0M01NWTh4dGZXNFMrVFVrMUN4ZHpUQVVBdFRENThYRlRZdVVGTjdlZFlrZmZMbkxkbUd0TU5USwovUVlOV0F4UWM2NGkraTFDSXF1bDcwMXVid0tCZ1FEVGJWa3BJczJ0QzYrdHREajBwUFVnNDdJeTZhdVdnREdPCmVwS0xmeVk3M2J3dm5vTHNnT2pXVkloOGd5Umc3a1JPQ0pNRlZWdXAzSDdRUzFjQkpyMHlZMmwwejhCUEZCQ20KbUVMY3BHcmtCUjVqT0creThubXNyLzJkMjF1NmRCbDJtcUQ5UGJuOHhjbG1PTVAwam9aZW5LRnBLMW5UdUJ1TQpVSldoNlJkN2pRS0JnRkp0K1piNGhmS0w0SEhLekN6MmllTkM1dXJYdFAreGpBL1JPUEpOdHBKR09RTnNYYmdKCkxBTHh0VWdTRUMrNGVwOHkzSTJCZ3hCVXNBSmorWkJVMGxUWkl2bmZYQm0zUHo4WElIWTlVM1BWYm1PSlZkcEsKdkFBbCttcWtLdFk5UitoTW1LT3JHWjJZSEwwK1J0VDVVMDJnc3JVdWh2ZHdkVExYemxReHRNd0hBb0dBVCtXYwpzZnppK0tmWDVhNmJiMmYrOGtUWmpIL2RSZXgvemJYb055ci9pZFFMVDN0NVFtS2NtcElyV2RJOUp4d2pWOUhKCnJWUktaWEJidzk4VXEreTF4cHJtdVN2aElvRVVvY0FjVkVFVjI4RkxjSGRkVHFSdThxRTNHRFpTL2F6dUNuSDQKc0hwcEZHcXg1eDFBZitSMFJzQ3VCbzVKVGNiZk1qRXB1cHZaTWVrQ2dZQmdmK3Bzc1dVc2p0ejg5bXBTQWlyQwovMG5ncXA3K1dHWWI4QzMxQmRBSGNsWER0ZzBwZHk4QkhjQndIWHBTMlpVZk5rN1pieVZDMzRxYndCa1FrcXc2CjZ3SnI1bWJheDNZbDdJTXVKQlRWdmpxN0JBU1dKS21ZUERBT2JsTGYyMW1SK3oxYXBFSjZkbUE5aHBtNDB6V3oKOWV3T0NjRWJFaGw0Wm9nREJjc1kwdz09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K -kind: Secret -metadata: - creationTimestamp: null - name: kcl-operator-webhook-certs - namespace: default ---- -apiVersion: v1 kind: Service metadata: labels: - app: kcl-operator-webhook - name: kcl-operator-webhook + app: kcl-webhook-server + name: kcl-webhook-server namespace: default spec: ports: @@ -141,66 +131,51 @@ spec: protocol: TCP targetPort: 8081 selector: - app: kcl-operator-webhook + app: kcl-webhook-server --- apiVersion: apps/v1 kind: Deployment metadata: labels: - app: kcl-operator-webhook - name: kcl-operator-webhook + app: kcl-webhook-server + name: kcl-webhook-server namespace: default spec: replicas: 1 selector: matchLabels: - app: kcl-operator-webhook + app: kcl-webhook-server template: metadata: labels: - app: kcl-operator-webhook + app: kcl-webhook-server spec: containers: - args: - - -tls-cert-file=/etc/webhook/certs/cert.pem - - -tls-key-file=/etc/webhook/certs/key.pem + - -tls-cert-file=/etc/webhook/certs/tls.crt + - -tls-key-file=/etc/webhook/certs/tls.key - -addr=:8081 - image: kcllang/webhookserver + image: kcllang/webhook-server imagePullPolicy: Always - name: kcl-operator-webhook + name: kcl-webhook-server volumeMounts: - mountPath: /etc/webhook/certs name: webhook-certs readOnly: true + initContainers: + - env: + - name: MUTATE_CONFIG + value: kcl-webhook-server + - name: WEBHOOK_SERVICE + value: kcl-webhook-server + - name: WEBHOOK_NAMESPACE + value: default + image: kcllang/webhook-init + imagePullPolicy: IfNotPresent + name: kcl-webhook-init + volumeMounts: + - mountPath: /etc/webhook/certs + name: webhook-certs volumes: - - name: webhook-certs - secret: - secretName: kcl-operator-webhook-certs ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - labels: - app: kcl-operator-webhook - kind: mutator - name: kcl-operator-webhook -webhooks: -- admissionReviewVersions: - - v1beta1 - clientConfig: - caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQ1akNDQXM2Z0F3SUJBZ0lVSk03UjJPZ1JpcUN4dGZpaDRhZHpMbnF3aTVJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZTXhDekFKQmdOVkJBWVRBa05PTVJBd0RnWURWUVFJRXdkQ1pXbHFhVzVuTVJBd0RnWURWUVFIRXdkQwpaV2xxYVc1bk1SUXdFZ1lEVlFRS0V3dFZibWwwWldSVGRHRmphekVQTUEwR0ExVUVDeE1HUkdWMmIzQnpNU2t3Ckp3WURWUVFERXlCd2IyUXRZVzV1YjNSaGRHVXRkMlZpYUc5dmF5NWtaV1poZFd4MExuTjJZekFnRncweU16QTQKTXpFd01qVXdNemRhR0E4ek1ESXpNREV3TVRBeU5UQXpOMW93Z1lNeEN6QUpCZ05WQkFZVEFrTk9NUkF3RGdZRApWUVFJRXdkQ1pXbHFhVzVuTVJBd0RnWURWUVFIRXdkQ1pXbHFhVzVuTVJRd0VnWURWUVFLRXd0VmJtbDBaV1JUCmRHRmphekVQTUEwR0ExVUVDeE1HUkdWMmIzQnpNU2t3SndZRFZRUURFeUJ3YjJRdFlXNXViM1JoZEdVdGQyVmkKYUc5dmF5NWtaV1poZFd4MExuTjJZekNDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQgpBTXVrYTFBS0tYNko2UWhrQmQ2clNyRzh0a3ZsbG5lcW5PTE5ya0FZMGV0VXNtS3hlZnJoYUhjNlhjdTJYcGFZCitGRWxTeEU0ZGNKa09YWmpGTDg4c3RleXRqOWFHaG5uOHN3WnozMWFNUU5iMWJERjIvTTdVWm16bjVKdG1IV2MKKzdnUUJORDN5Myt6SFZyaWNYVnNxb2ppSFhoN0huUC8vVlRNellrTUhtUk9ER0ZZTUlRVmowcDl3VFVYMnE3NQphSS9zMTY2Vi9CRURIbHNlc29SbnVjU0h4R2JtMzF3TnNQSFQzOWpIRHEvV3AvNytzNnpjT3hZekt1MzdIOWN1CkIycDhRNUp6NWdtazFuU01JZU1rUGhveStWUXYrRHR2TnNnN2tWOGhvTERKOWdBdW5obDFUbVNLQmU2elM1SUsKS3RaWFNENE1NQjc3VC9CTXBmU3VLQ01DQXdFQUFhTk9NRXd3S3dZRFZSMFJCQ1F3SW9JZ2NHOWtMV0Z1Ym05MApZWFJsTFhkbFltaHZiMnN1WkdWbVlYVnNkQzV6ZG1Nd0hRWURWUjBPQkJZRUZMZGgycnVhYjAxSERSN2xGWlBHClRUbEU1ZXg0TUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFESEJONmNNdmZ1OTJpWjZEM1ZLUDdFbnIzWHF4cXYKUE9CdTdMRnlxRVBySlZ4L2E0ME5zUXQzYlk4NWUzOXM5aERHRklYcTVwcXpQRFl3WUxnUW8za2IyVjJCVVVmcwpBZmtjVE9DbExsK3dDK2kzTm9mZzNLZC9CckUxelRhUGc1UmFkRU5HUVZmbjZ6a3NYNEVBaXJhUmdhVW9uQmFPCk5WdUFLZ1phSVovUXJjQ1RjSGZMNC9yeVNWT0hTSzRZa05RWFVWZnhscGJCaU9HRUg3N2NqS3g2eEpjSHZTYnoKVXJtZlFHQ25IS25aWUJhSVJ2ZTErWGtEbEllUkFlTlB1SWh2YjRKZm0yNmxYbnFVcXpoeEd4b29wQi81eHN6NwoyeEpmWFdwdzhVZWpudGYxRzRIYytyVStJL1dnRnM2WTRFb3pTUzVKOWtCVHNYSkdFb011cVFhbAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - service: - name: kcl-operator-webhook - namespace: default - path: /mutate - name: kcl-operator-webhook.slok.dev - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - resources: - - pods - sideEffects: None + - emptyDir: {} + name: webhook-certs diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index e922472..10df539 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: manager-role + name: webhook-role rules: - apiGroups: - krm.kcl.dev diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index 8f26587..768da9b 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -1,11 +1,11 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: - name: manager-rolebinding + name: webhook-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: manager-role + name: webhook-role subjects: - kind: ServiceAccount name: default diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml index b80552c..8a81591 100644 --- a/config/webhook/kustomization.yaml +++ b/config/webhook/kustomization.yaml @@ -1,9 +1,7 @@ resources: -- webhook-certs.yaml - webhook.yaml -- webhook-registration.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: kcllang/webhookserver + newName: kcllang/webhook-server diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml deleted file mode 100644 index d298a0c..0000000 --- a/config/webhook/manifests.yaml +++ /dev/null @@ -1,26 +0,0 @@ ---- -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: mutating-webhook-configuration -webhooks: -- admissionReviewVersions: - - v1beta1 - clientConfig: - service: - name: webhook-service - namespace: system - path: /mutate - failurePolicy: Fail - name: kcl-operator-webhook.slok.dev - rules: - - apiGroups: - - "" - apiVersions: - - v1 - operations: - - CREATE - - UPDATE - resources: - - pods - sideEffects: None diff --git a/config/webhook/webhook-certs.yaml b/config/webhook/webhook-certs.yaml deleted file mode 100644 index 72802a2..0000000 --- a/config/webhook/webhook-certs.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -data: - cert.pem: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQ1akNDQXM2Z0F3SUJBZ0lVSk03UjJPZ1JpcUN4dGZpaDRhZHpMbnF3aTVJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZTXhDekFKQmdOVkJBWVRBa05PTVJBd0RnWURWUVFJRXdkQ1pXbHFhVzVuTVJBd0RnWURWUVFIRXdkQwpaV2xxYVc1bk1SUXdFZ1lEVlFRS0V3dFZibWwwWldSVGRHRmphekVQTUEwR0ExVUVDeE1HUkdWMmIzQnpNU2t3Ckp3WURWUVFERXlCd2IyUXRZVzV1YjNSaGRHVXRkMlZpYUc5dmF5NWtaV1poZFd4MExuTjJZekFnRncweU16QTQKTXpFd01qVXdNemRhR0E4ek1ESXpNREV3TVRBeU5UQXpOMW93Z1lNeEN6QUpCZ05WQkFZVEFrTk9NUkF3RGdZRApWUVFJRXdkQ1pXbHFhVzVuTVJBd0RnWURWUVFIRXdkQ1pXbHFhVzVuTVJRd0VnWURWUVFLRXd0VmJtbDBaV1JUCmRHRmphekVQTUEwR0ExVUVDeE1HUkdWMmIzQnpNU2t3SndZRFZRUURFeUJ3YjJRdFlXNXViM1JoZEdVdGQyVmkKYUc5dmF5NWtaV1poZFd4MExuTjJZekNDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQgpBTXVrYTFBS0tYNko2UWhrQmQ2clNyRzh0a3ZsbG5lcW5PTE5ya0FZMGV0VXNtS3hlZnJoYUhjNlhjdTJYcGFZCitGRWxTeEU0ZGNKa09YWmpGTDg4c3RleXRqOWFHaG5uOHN3WnozMWFNUU5iMWJERjIvTTdVWm16bjVKdG1IV2MKKzdnUUJORDN5Myt6SFZyaWNYVnNxb2ppSFhoN0huUC8vVlRNellrTUhtUk9ER0ZZTUlRVmowcDl3VFVYMnE3NQphSS9zMTY2Vi9CRURIbHNlc29SbnVjU0h4R2JtMzF3TnNQSFQzOWpIRHEvV3AvNytzNnpjT3hZekt1MzdIOWN1CkIycDhRNUp6NWdtazFuU01JZU1rUGhveStWUXYrRHR2TnNnN2tWOGhvTERKOWdBdW5obDFUbVNLQmU2elM1SUsKS3RaWFNENE1NQjc3VC9CTXBmU3VLQ01DQXdFQUFhTk9NRXd3S3dZRFZSMFJCQ1F3SW9JZ2NHOWtMV0Z1Ym05MApZWFJsTFhkbFltaHZiMnN1WkdWbVlYVnNkQzV6ZG1Nd0hRWURWUjBPQkJZRUZMZGgycnVhYjAxSERSN2xGWlBHClRUbEU1ZXg0TUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFESEJONmNNdmZ1OTJpWjZEM1ZLUDdFbnIzWHF4cXYKUE9CdTdMRnlxRVBySlZ4L2E0ME5zUXQzYlk4NWUzOXM5aERHRklYcTVwcXpQRFl3WUxnUW8za2IyVjJCVVVmcwpBZmtjVE9DbExsK3dDK2kzTm9mZzNLZC9CckUxelRhUGc1UmFkRU5HUVZmbjZ6a3NYNEVBaXJhUmdhVW9uQmFPCk5WdUFLZ1phSVovUXJjQ1RjSGZMNC9yeVNWT0hTSzRZa05RWFVWZnhscGJCaU9HRUg3N2NqS3g2eEpjSHZTYnoKVXJtZlFHQ25IS25aWUJhSVJ2ZTErWGtEbEllUkFlTlB1SWh2YjRKZm0yNmxYbnFVcXpoeEd4b29wQi81eHN6NwoyeEpmWFdwdzhVZWpudGYxRzRIYytyVStJL1dnRnM2WTRFb3pTUzVKOWtCVHNYSkdFb011cVFhbAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - key.pem: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2QUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktZd2dnU2lBZ0VBQW9JQkFRRExwR3RRQ2lsK2lla0kKWkFYZXEwcXh2TFpMNVpaM3Fweml6YTVBR05IclZMSmlzWG42NFdoM09sM0x0bDZXbVBoUkpVc1JPSFhDWkRsMgpZeFMvUExMWHNyWS9XaG9aNS9MTUdjOTlXakVEVzlXd3hkdnpPMUdaczUrU2JaaDFuUHU0RUFUUTk4dC9zeDFhCjRuRjFiS3FJNGgxNGV4NXovLzFVek0ySkRCNWtUZ3hoV0RDRUZZOUtmY0UxRjlxdStXaVA3TmV1bGZ3UkF4NWIKSHJLRVo3bkVoOFJtNXQ5Y0RiRHgwOS9ZeHc2djFxZisvck9zM0RzV015cnQreC9YTGdkcWZFT1NjK1lKcE5aMApqQ0hqSkQ0YU12bFVML2c3YnpiSU81RmZJYUN3eWZZQUxwNFpkVTVraWdYdXMwdVNDaXJXVjBnK0REQWUrMC93ClRLWDByaWdqQWdNQkFBRUNnZ0VBTDRpaCtlMWRVU21jeVBZYi9xVktDcUhMaFh6Q09nMkxrRTlGVUxYYWJnMTMKbEJ6c0paanUyd0czT2lGSUErd1I3bEwvcWpYZEd2SnREVXJFWVgwb0c3d2QyVHpOWWVXbjRXZFc3T0ljRmJZZQpjNlJEbW90cW1TOTUzR0tUbDBSODV6SnFCV01KejdWOThTUnhLbUVpajFBRVpBYU5EYk9mck9jZlJOR2MySG1LCmR3RXhWZDMxeTFpUkhNS05ndjZZZmMwZ1lNaGFmc3lNMC9oS1dQMVFlcHRCRnNRYktlcFBSNmhSLzRiT0ZYeUEKeXdBNVZWZ1p4VWF0MElWbCt2UnZXUWE3SUdOQTFSVmxNK0lEWW9HaE1wVGg2OGl4ajdaL3ZIbVpBOVJxTDl3NQpMajFHKzJldUtvYzdJY0dZRkwzT1MvdFpqSm1lVTlaNDdqaTBqM0t6UFFLQmdRRDJrdXh3Ym5SWVo0Tzl0eTQ3CnlZa3dDNFh3VFpXampwdmR6VjRSWlFCRXd4UlF4OTVPV0RoU1U3d3o2MHczZkprUTFPUnFycGx4QytraVMxMmkKRHo2ZFN3eDZ0M01NWTh4dGZXNFMrVFVrMUN4ZHpUQVVBdFRENThYRlRZdVVGTjdlZFlrZmZMbkxkbUd0TU5USwovUVlOV0F4UWM2NGkraTFDSXF1bDcwMXVid0tCZ1FEVGJWa3BJczJ0QzYrdHREajBwUFVnNDdJeTZhdVdnREdPCmVwS0xmeVk3M2J3dm5vTHNnT2pXVkloOGd5Umc3a1JPQ0pNRlZWdXAzSDdRUzFjQkpyMHlZMmwwejhCUEZCQ20KbUVMY3BHcmtCUjVqT0creThubXNyLzJkMjF1NmRCbDJtcUQ5UGJuOHhjbG1PTVAwam9aZW5LRnBLMW5UdUJ1TQpVSldoNlJkN2pRS0JnRkp0K1piNGhmS0w0SEhLekN6MmllTkM1dXJYdFAreGpBL1JPUEpOdHBKR09RTnNYYmdKCkxBTHh0VWdTRUMrNGVwOHkzSTJCZ3hCVXNBSmorWkJVMGxUWkl2bmZYQm0zUHo4WElIWTlVM1BWYm1PSlZkcEsKdkFBbCttcWtLdFk5UitoTW1LT3JHWjJZSEwwK1J0VDVVMDJnc3JVdWh2ZHdkVExYemxReHRNd0hBb0dBVCtXYwpzZnppK0tmWDVhNmJiMmYrOGtUWmpIL2RSZXgvemJYb055ci9pZFFMVDN0NVFtS2NtcElyV2RJOUp4d2pWOUhKCnJWUktaWEJidzk4VXEreTF4cHJtdVN2aElvRVVvY0FjVkVFVjI4RkxjSGRkVHFSdThxRTNHRFpTL2F6dUNuSDQKc0hwcEZHcXg1eDFBZitSMFJzQ3VCbzVKVGNiZk1qRXB1cHZaTWVrQ2dZQmdmK3Bzc1dVc2p0ejg5bXBTQWlyQwovMG5ncXA3K1dHWWI4QzMxQmRBSGNsWER0ZzBwZHk4QkhjQndIWHBTMlpVZk5rN1pieVZDMzRxYndCa1FrcXc2CjZ3SnI1bWJheDNZbDdJTXVKQlRWdmpxN0JBU1dKS21ZUERBT2JsTGYyMW1SK3oxYXBFSjZkbUE5aHBtNDB6V3oKOWV3T0NjRWJFaGw0Wm9nREJjc1kwdz09Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K -kind: Secret -metadata: - creationTimestamp: null - name: kcl-operator-webhook-certs diff --git a/config/webhook/webhook-registration.yaml b/config/webhook/webhook-registration.yaml deleted file mode 100644 index 761bed3..0000000 --- a/config/webhook/webhook-registration.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: admissionregistration.k8s.io/v1 -kind: MutatingWebhookConfiguration -metadata: - name: kcl-operator-webhook - labels: - app: kcl-operator-webhook - kind: mutator -webhooks: - - name: kcl-operator-webhook.slok.dev - clientConfig: - service: - name: kcl-operator-webhook - namespace: default - path: "/mutate" - caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQ1akNDQXM2Z0F3SUJBZ0lVSk03UjJPZ1JpcUN4dGZpaDRhZHpMbnF3aTVJd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZTXhDekFKQmdOVkJBWVRBa05PTVJBd0RnWURWUVFJRXdkQ1pXbHFhVzVuTVJBd0RnWURWUVFIRXdkQwpaV2xxYVc1bk1SUXdFZ1lEVlFRS0V3dFZibWwwWldSVGRHRmphekVQTUEwR0ExVUVDeE1HUkdWMmIzQnpNU2t3Ckp3WURWUVFERXlCd2IyUXRZVzV1YjNSaGRHVXRkMlZpYUc5dmF5NWtaV1poZFd4MExuTjJZekFnRncweU16QTQKTXpFd01qVXdNemRhR0E4ek1ESXpNREV3TVRBeU5UQXpOMW93Z1lNeEN6QUpCZ05WQkFZVEFrTk9NUkF3RGdZRApWUVFJRXdkQ1pXbHFhVzVuTVJBd0RnWURWUVFIRXdkQ1pXbHFhVzVuTVJRd0VnWURWUVFLRXd0VmJtbDBaV1JUCmRHRmphekVQTUEwR0ExVUVDeE1HUkdWMmIzQnpNU2t3SndZRFZRUURFeUJ3YjJRdFlXNXViM1JoZEdVdGQyVmkKYUc5dmF5NWtaV1poZFd4MExuTjJZekNDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQgpBTXVrYTFBS0tYNko2UWhrQmQ2clNyRzh0a3ZsbG5lcW5PTE5ya0FZMGV0VXNtS3hlZnJoYUhjNlhjdTJYcGFZCitGRWxTeEU0ZGNKa09YWmpGTDg4c3RleXRqOWFHaG5uOHN3WnozMWFNUU5iMWJERjIvTTdVWm16bjVKdG1IV2MKKzdnUUJORDN5Myt6SFZyaWNYVnNxb2ppSFhoN0huUC8vVlRNellrTUhtUk9ER0ZZTUlRVmowcDl3VFVYMnE3NQphSS9zMTY2Vi9CRURIbHNlc29SbnVjU0h4R2JtMzF3TnNQSFQzOWpIRHEvV3AvNytzNnpjT3hZekt1MzdIOWN1CkIycDhRNUp6NWdtazFuU01JZU1rUGhveStWUXYrRHR2TnNnN2tWOGhvTERKOWdBdW5obDFUbVNLQmU2elM1SUsKS3RaWFNENE1NQjc3VC9CTXBmU3VLQ01DQXdFQUFhTk9NRXd3S3dZRFZSMFJCQ1F3SW9JZ2NHOWtMV0Z1Ym05MApZWFJsTFhkbFltaHZiMnN1WkdWbVlYVnNkQzV6ZG1Nd0hRWURWUjBPQkJZRUZMZGgycnVhYjAxSERSN2xGWlBHClRUbEU1ZXg0TUEwR0NTcUdTSWIzRFFFQkN3VUFBNElCQVFESEJONmNNdmZ1OTJpWjZEM1ZLUDdFbnIzWHF4cXYKUE9CdTdMRnlxRVBySlZ4L2E0ME5zUXQzYlk4NWUzOXM5aERHRklYcTVwcXpQRFl3WUxnUW8za2IyVjJCVVVmcwpBZmtjVE9DbExsK3dDK2kzTm9mZzNLZC9CckUxelRhUGc1UmFkRU5HUVZmbjZ6a3NYNEVBaXJhUmdhVW9uQmFPCk5WdUFLZ1phSVovUXJjQ1RjSGZMNC9yeVNWT0hTSzRZa05RWFVWZnhscGJCaU9HRUg3N2NqS3g2eEpjSHZTYnoKVXJtZlFHQ25IS25aWUJhSVJ2ZTErWGtEbEllUkFlTlB1SWh2YjRKZm0yNmxYbnFVcXpoeEd4b29wQi81eHN6NwoyeEpmWFdwdzhVZWpudGYxRzRIYytyVStJL1dnRnM2WTRFb3pTUzVKOWtCVHNYSkdFb011cVFhbAotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== - rules: - - operations: [ "CREATE" ] - apiGroups: [""] - apiVersions: ["v1"] - resources: ["pods"] - sideEffects: None - admissionReviewVersions: ["v1beta1"] diff --git a/config/webhook/webhook.yaml b/config/webhook/webhook.yaml index ac1e115..dda55b3 100644 --- a/config/webhook/webhook.yaml +++ b/config/webhook/webhook.yaml @@ -1,26 +1,40 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: kcl-operator-webhook + name: kcl-webhook-server labels: - app: kcl-operator-webhook + app: kcl-webhook-server spec: replicas: 1 selector: matchLabels: - app: kcl-operator-webhook + app: kcl-webhook-server template: metadata: labels: - app: kcl-operator-webhook + app: kcl-webhook-server spec: + initContainers: + - name: kcl-webhook-init + image: kcllang/webhook-init + imagePullPolicy: IfNotPresent + volumeMounts: + - mountPath: /etc/webhook/certs + name: webhook-certs + env: + - name: MUTATE_CONFIG + value: kcl-webhook-server + - name: WEBHOOK_SERVICE + value: kcl-webhook-server + - name: WEBHOOK_NAMESPACE + value: default containers: - - name: kcl-operator-webhook - image: kcllang/webhookserver + - name: kcl-webhook-server + image: kcllang/webhook-server imagePullPolicy: Always args: - - -tls-cert-file=/etc/webhook/certs/cert.pem - - -tls-key-file=/etc/webhook/certs/key.pem + - -tls-cert-file=/etc/webhook/certs/tls.crt + - -tls-key-file=/etc/webhook/certs/tls.key - -addr=:8081 volumeMounts: - name: webhook-certs @@ -28,19 +42,18 @@ spec: readOnly: true volumes: - name: webhook-certs - secret: - secretName: kcl-operator-webhook-certs + emptyDir: {} --- apiVersion: v1 kind: Service metadata: - name: kcl-operator-webhook + name: kcl-webhook-server labels: - app: kcl-operator-webhook + app: kcl-webhook-server spec: ports: - port: 443 protocol: TCP targetPort: 8081 selector: - app: kcl-operator-webhook + app: kcl-webhook-server diff --git a/Dockerfile b/docker/amd64/Dockerfile.init similarity index 71% rename from Dockerfile rename to docker/amd64/Dockerfile.init index 2a824ef..aac78a5 100644 --- a/Dockerfile +++ b/docker/amd64/Dockerfile.init @@ -1,4 +1,4 @@ -FROM --platform=${BUILDPLATFORM} golang:1.22 AS builder +FROM --platform=${BUILDPLATFORM} golang:1.23 AS builder # The TARGETOS and TARGETARCH args are set by docker. We set GOOS and GOARCH to # these values to ask Go to compile a binary for these architectures. If @@ -14,14 +14,14 @@ WORKDIR / COPY . . -RUN GOOS=linux GOARCH=amd64 go build -o manager +RUN GOOS=linux GOARCH=amd64 go build -o webhook-server cmd/wehbook-init FROM kcllang/kcl WORKDIR / -COPY --from=builder /manager . +COPY --from=builder /webhook-server . ENV KCL_FAST_EVAL=1 ENV LANG="en_US.UTF-8" -ENTRYPOINT ["/manager"] +ENTRYPOINT ["/webhook-init"] diff --git a/docker/amd64/Dockerfile.server b/docker/amd64/Dockerfile.server new file mode 100644 index 0000000..ca2f8c8 --- /dev/null +++ b/docker/amd64/Dockerfile.server @@ -0,0 +1,27 @@ +FROM --platform=${BUILDPLATFORM} golang:1.23 AS builder + +# The TARGETOS and TARGETARCH args are set by docker. We set GOOS and GOARCH to +# these values to ask Go to compile a binary for these architectures. If +# TARGETOS and TARGETOS are different from BUILDPLATFORM, Go will cross compile +# for us (e.g. compile a linux/amd64 binary on a linux/arm64 build machine). +ARG TARGETOS +ARG TARGETARCH + +ENV GO111MODULE=on \ + GOPROXY=https://goproxy.cn,direct + +WORKDIR / + +COPY . . + +RUN GOOS=linux GOARCH=amd64 go build -o webhook-server cmd/wehbook-server + +FROM kcllang/kcl + +WORKDIR / +COPY --from=builder /webhook-server . + +ENV KCL_FAST_EVAL=1 +ENV LANG="en_US.UTF-8" + +ENTRYPOINT ["/webhook-server"] diff --git a/go.mod b/go.mod index a30c572..ebc137d 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.23.0 require ( github.com/sirupsen/logrus v1.9.3 github.com/slok/kubewebhook/v2 v2.7.0 + k8s.io/api v0.31.0 k8s.io/apimachinery v0.31.0 k8s.io/client-go v0.31.0 kcl-lang.io/kcl-go v0.9.3 @@ -186,11 +187,11 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.31.0 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/apiserver v0.31.0 // indirect k8s.io/component-base v0.31.0 // indirect diff --git a/pkg/webhook/handler/mutation.go b/pkg/webhook/handler/mutation.go index 079b4ed..3b75940 100644 --- a/pkg/webhook/handler/mutation.go +++ b/pkg/webhook/handler/mutation.go @@ -22,7 +22,6 @@ import ( //+kubebuilder:rbac:groups=krm.kcl.dev,resources=kclruns,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=krm.kcl.dev,resources=kclruns/status,verbs=get;update;patch //+kubebuilder:rbac:groups=krm.kcl.dev,resources=kclruns/finalizers,verbs=update -//+kubebuilder:webhook:admissionReviewVersions=v1beta1,path=/mutate,mutating=true,failurePolicy=fail,groups="",resources=pods,verbs=create;update,versions=v1,sideEffects=none,name=kcl-operator-webhook.slok.dev // MutationHandler validates Kubernetes resources using the KCL source. type MutationHandler struct { @@ -74,10 +73,11 @@ func (v *MutationHandler) Mutate(ctx context.Context, r *kwhmodel.AdmissionRevie v.Logger.Errorf("Data decode error %v", err) return &kwhmutating.MutatorResult{}, err } - v.Logger.Infof("Mutate using KCL finished") + v.Logger.Infof("Mutate using KCL finished.") return &kwhmutating.MutatorResult{ MutatedObject: unstructuredObj, }, nil } + v.Logger.Infof("No KCL resource found") return &kwhmutating.MutatorResult{}, nil } diff --git a/pkg/webhook/handler/mutation_test.go b/pkg/webhook/handler/mutation_test.go new file mode 100644 index 0000000..1a5e519 --- /dev/null +++ b/pkg/webhook/handler/mutation_test.go @@ -0,0 +1,165 @@ +package handler + +import ( + "context" + "testing" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + krmkcldevv1alpha1 "kcl-lang.io/kcl-operator/api/kclrun/v1alpha1" + "kcl-lang.io/krm-kcl/pkg/api" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/yaml" + + "github.com/sirupsen/logrus" + kwhlogrus "github.com/slok/kubewebhook/v2/pkg/log/logrus" + kwhmodel "github.com/slok/kubewebhook/v2/pkg/model" + kwhmutating "github.com/slok/kubewebhook/v2/pkg/webhook/mutating" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestMutationHandler_Mutate(t *testing.T) { + tests := []struct { + name string + kclRunList *krmkcldevv1alpha1.KCLRunList + admissionReview *kwhmodel.AdmissionReview + expectedResult *kwhmutating.MutatorResult + expectedError bool + }{ + { + name: "successful mutation", + kclRunList: &krmkcldevv1alpha1.KCLRunList{ + Items: []krmkcldevv1alpha1.KCLRun{ + { + TypeMeta: metav1.TypeMeta{ + APIVersion: "krm.kcl.dev/v1alpha1", + Kind: api.KCLRunKind, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "set-annotation", + Namespace: "default", + }, + Spec: krmkcldevv1alpha1.KCLRunSpec{ + Source: ` +items = [item | { + metadata.annotations: {"managed-by" = "kcl-operator"} +} for item in option("items")] +`, + }, + }, + }, + }, + admissionReview: &kwhmodel.AdmissionReview{ + Namespace: "default", + NewObjectRaw: func() []byte { + r, _ := yaml.Marshal(map[string]interface{}{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": map[string]interface{}{ + "name": "nginx", + "annotations": map[string]interface{}{ + "app": "nginx", + }, + }, + "spec": map[string]interface{}{ + "containers": []map[string]interface{}{ + { + "name": "nginx", + "image": "nginx:1.14.2", + "ports": []map[string]interface{}{ + { + "containerPort": 80, + }, + }, + }, + }, + }, + }, + ) + return r + }(), + }, + expectedResult: &kwhmutating.MutatorResult{ + MutatedObject: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "v1", + "kind": "Pod", + "metadata": map[string]interface{}{ + "name": "nginx", + "annotations": map[string]interface{}{ + "app": "nginx", + "managed-by": "kcl-operator", + }, + }, + "spec": map[string]interface{}{ + "containers": []map[string]interface{}{ + { + "name": "nginx", + "image": "nginx:1.14.2", + "ports": []map[string]interface{}{ + { + "containerPort": 80, + }, + }, + }, + }, + }, + }, + }, + }, + expectedError: false, + }, + { + name: "no KCLRun found", + kclRunList: &krmkcldevv1alpha1.KCLRunList{}, + admissionReview: &kwhmodel.AdmissionReview{Namespace: "default"}, + expectedResult: &kwhmutating.MutatorResult{}, + expectedError: false, + }, + } + + logrusLogEntry := logrus.NewEntry(logrus.New()) + logrusLogEntry.Logger.SetLevel(logrus.DebugLevel) + logger := kwhlogrus.NewLogrus(logrusLogEntry) + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + scheme := runtime.NewScheme() + krmkcldevv1alpha1.AddToScheme(scheme) + client := fake.NewClientBuilder().WithScheme(scheme).WithLists(test.kclRunList).Build() + mutationHandler := &MutationHandler{ + Client: client, + Reader: client, + Scheme: scheme, + Logger: logger, + } + result, err := mutationHandler.Mutate(context.Background(), test.admissionReview, nil) + if (err != nil) != test.expectedError { + t.Errorf("Unexpected error: %v", err) + } + if a, b, r := equalMutatorResults(result, test.expectedResult); !r { + t.Errorf("Unexpected result:\nGot:%v\nExpected:\n%v", string(a), string(b)) + } + }) + } +} + +func equalMutatorResults(a, b *kwhmutating.MutatorResult) (aMutatedObj []byte, bMutatedObj []byte, result bool) { + if a == nil && b == nil { + return + } + if a == nil || b == nil { + return + } + aMutatedObj, err := yaml.Marshal(a.MutatedObject) + if err != nil { + return + } + bMutatedObj, err = yaml.Marshal(b.MutatedObject) + if err != nil { + return + } + if string(aMutatedObj) != string(bMutatedObj) { + return + } + return aMutatedObj, bMutatedObj, true +}