From 320a21cc64a5ae6adb95a5591b8501202e502330 Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Mon, 21 Oct 2024 15:42:16 +0200 Subject: [PATCH 1/5] chore: refactor admission controller --- .changelog/59.txt | 7 + .goreleaser.yaml | 60 ----- Makefile | 29 ++- api/v1alpha1/image_models.go | 18 +- cmd/admission-controller/certificate.go | 222 ------------------ cmd/admission-controller/main.go | 122 ---------- .../webhook-configuration.go | 105 --------- cmd/admission-controller/webhook.go | 220 ----------------- cmd/operator/main.go | 17 ++ config/webhook/manifests.yaml | 10 +- docs/advanced/image-status.md | 35 --- docs/advanced/image-status.md.tmpl | 15 -- docs/advanced/metrics.md | 2 +- docs/advanced/metrics.md.tmpl | 2 +- docs/crd/image.md | 17 ++ docs/crd/image.md.tmpl | 81 +++++++ docs/crd/kimup.md | 8 +- docs/getting-started/howto.md | 16 +- docs/getting-started/install.md | 12 +- .../logical-pod-creation-dark.png | Bin 0 -> 71839 bytes .../logical-pod-creation-light.png | Bin 0 -> 71156 bytes docs/getting-started/scope.md | 58 +++++ docs/index.md | 5 +- go.mod | 15 +- go.sum | 24 +- internal/controller/image_tag_mutator.go | 83 +++++++ internal/httpserver/httpserver.go | 1 + internal/kubeclient/client.go | 6 +- internal/kubeclient/image.go | 2 +- internal/kubeclient/mutating.go | 103 ++++++++ internal/kubeclient/mutatingMatchCondition.go | 60 +++++ internal/models/mutator.go | 14 ++ .../crd/kimup.cloudavenue.io_images.yaml | 12 + .../crd/kimup.cloudavenue.io_kimups.yaml | 136 +++++++++-- manifests/operator/deployment.yaml | 54 +++++ manifests/operator/kustomization.yaml | 3 + manifests/operator/service.yaml | 14 ++ manifests/operator/webhook-certificate.yaml | 21 ++ mkdocs.yml | 7 +- test/mocks/fakekubeclient/kubeclient.go | 4 + ...ge-status.go => generate-doc-crd-image.go} | 12 +- tools/doc/generate-doc.go | 4 +- tools/env-dev/pod-operator.yaml | 31 +++ tools/env-dev/whoami-deployment.yaml | 50 ++++ tools/mutator/main.go | 49 ++++ 45 files changed, 892 insertions(+), 874 deletions(-) create mode 100644 .changelog/59.txt delete mode 100644 cmd/admission-controller/certificate.go delete mode 100644 cmd/admission-controller/main.go delete mode 100644 cmd/admission-controller/webhook-configuration.go delete mode 100644 cmd/admission-controller/webhook.go delete mode 100644 docs/advanced/image-status.md delete mode 100644 docs/advanced/image-status.md.tmpl create mode 100644 docs/crd/image.md.tmpl create mode 100644 docs/getting-started/logical-pod-creation-dark.png create mode 100644 docs/getting-started/logical-pod-creation-light.png create mode 100644 docs/getting-started/scope.md create mode 100644 internal/controller/image_tag_mutator.go create mode 100644 internal/kubeclient/mutating.go create mode 100644 internal/kubeclient/mutatingMatchCondition.go create mode 100644 internal/models/mutator.go create mode 100644 manifests/operator/deployment.yaml create mode 100644 manifests/operator/service.yaml create mode 100644 manifests/operator/webhook-certificate.yaml rename tools/doc/{generate-doc-image-status.go => generate-doc-crd-image.go} (80%) create mode 100644 tools/env-dev/pod-operator.yaml create mode 100644 tools/env-dev/whoami-deployment.yaml create mode 100644 tools/mutator/main.go diff --git a/.changelog/59.txt b/.changelog/59.txt new file mode 100644 index 0000000..9de9417 --- /dev/null +++ b/.changelog/59.txt @@ -0,0 +1,7 @@ +```release-note:breaking-change +`kimup-admission-controller` - Remove component admission controller. This component is moved to `kimup-operator`, the `kimup-admission-controller` is no longer available. +``` + +```release-note:feature +`kimup-operator` - Now the `kimup-operator` allow to set scope of the image-tag mutation. The scope can be set to `Namespaced` or `Pod`. +``` \ No newline at end of file diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 7fc10bf..c5ad4b1 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -37,17 +37,6 @@ builds: - arm64 env: - CGO_ENABLED=0 - - id: "kimup-admission-controller" - binary: kimup-admission-controller - main: ./cmd/admission-controller - goos: - - linux - - darwin - goarch: - - amd64 - - arm64 - env: - - CGO_ENABLED=0 dockers: # * KIMUP @@ -128,45 +117,6 @@ dockers: - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - --label=org.opencontainers.image.revision={{ .FullCommit }} - # * KIMUP-ADMISSION-CONTROLLER - - goarch: amd64 - image_templates: - - "ghcr.io/orange-cloudavenue/{{.ProjectName}}-admission-controller:v{{ .Version }}-amd64" - dockerfile: Dockerfile - use: buildx - ids: - - kimup-admission-controller - build_flag_templates: - - --platform=linux/amd64 - - "--build-arg=BINNAME=kimup-admission-controller" - - --pull - - --label=org.opencontainers.image.title="kimup-admission-controller" - - --label=org.opencontainers.image.description="kube-image-updater-admission-controller" - - --label=org.opencontainers.image.url=https://github.com/orange-cloudavenue/kube-image-updater-admission-controller - - --label=org.opencontainers.image.source=https://github.com/orange-cloudavenue/kube-image-updater-admission-controller - - --label=org.opencontainers.image.version={{ .Version }} - - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - - --label=org.opencontainers.image.revision={{ .FullCommit }} - - - goarch: arm64 - image_templates: - - "ghcr.io/orange-cloudavenue/{{ .ProjectName }}-admission-controller:v{{ .Version }}-arm64v8" - dockerfile: Dockerfile - use: buildx - ids: - - kimup-admission-controller - build_flag_templates: - - --platform=linux/arm64/v8 - - "--build-arg=BINNAME=kimup-admission-controller" - - --pull - - --label=org.opencontainers.image.title="kimup-admission-controller" - - --label=org.opencontainers.image.description="kube-image-updater-admission-controller" - - --label=org.opencontainers.image.url=https://github.com/orange-cloudavenue/kube-image-updater-admission-controller - - --label=org.opencontainers.image.source=https://github.com/orange-cloudavenue/kube-image-updater-admission-controller - - --label=org.opencontainers.image.version={{ .Version }} - - --label=org.opencontainers.image.created={{ time "2006-01-02T15:04:05Z07:00" }} - - --label=org.opencontainers.image.revision={{ .FullCommit }} - docker_manifests: # * KIMUP - name_template: "ghcr.io/orange-cloudavenue/{{ .ProjectName }}-controller:v{{ .Version }}" @@ -187,13 +137,3 @@ docker_manifests: image_templates: - "ghcr.io/orange-cloudavenue/{{ .ProjectName }}-operator:v{{ .Version }}-amd64" - "ghcr.io/orange-cloudavenue/{{ .ProjectName }}-operator:v{{ .Version }}-arm64v8" - -# * KIMUP-ADMISSION-CONTROLLER -- name_template: "ghcr.io/orange-cloudavenue/{{ .ProjectName }}-admission-controller:v{{ .Version }}" - image_templates: - - "ghcr.io/orange-cloudavenue/{{ .ProjectName }}-admission-controller:v{{ .Version }}-amd64" - - "ghcr.io/orange-cloudavenue/{{ .ProjectName }}-admission-controller:v{{ .Version }}-arm64v8" -- name_template: "ghcr.io/orange-cloudavenue/{{ .ProjectName }}-admission-controller:latest" - image_templates: - - "ghcr.io/orange-cloudavenue/{{ .ProjectName }}-admission-controller:v{{ .Version }}-amd64" - - "ghcr.io/orange-cloudavenue/{{ .ProjectName }}-admission-controller:v{{ .Version }}-arm64v8" diff --git a/Makefile b/Makefile index c90884a..ae8895a 100644 --- a/Makefile +++ b/Makefile @@ -82,28 +82,39 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes build: manifests generate fmt vet ## Build manager binary. go build -o bin/operator cmd/operator/main.go go build -o bin/kimup cmd/kimup/* - go build -o bin/admission-controller cmd/admission-controller/* - -.PHONY: build -build-admission-controller: manifests generate fmt vet - go build -o bin/admission-controller cmd/admission-controller/* .PHONY: build-kimup build-kimup: manifests generate fmt vet go build -o bin/kimup cmd/kimup/* +.PHONY: generate-mutating-config +generate-mutating-config: ## Generate the mutating webhook configuration. + go run ./tools/admission-controller/main.go + .PHONY: run-operator run-operator: manifests generate fmt vet ## Run a controller from your host. go run ./cmd/operator/main.go +.PHONY: run-operator-in-cluster +run-operator-in-cluster: manifests generate fmt vet ## Run a controller from your host. + kubectl get node docker-desktop + kubectl -n kimup-operator delete po kimup-operator || true + kubectl create ns kimup-operator || true + kubectl apply -k manifests/crd + kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.1/cert-manager.yaml --wait=true + kubectl -n kimup-operator apply --filename="manifests/operator/role.yaml,manifests/operator/role_binding.yaml,manifests/operator/service_account.yaml,manifests/operator/webhook-certificate.yaml,manifests/operator/service.yaml" --wait=true + kubectl wait --for=condition=Available deployment/cert-manager -n cert-manager --timeout=300s + kubectl wait --for=condition=Ready certificate/kimup-webhook-serving-cert -n kimup-operator + kurun apply -f tools/env-dev/pod-operator.yaml + kubectl wait --for=condition=Ready pod/kimup-operator -n kimup-operator + kubectl apply -f tools/env-dev/whoami-deployment.yaml || true + kubectl -n kimup-operator logs kimup-operator -f + kubectl -n kimup-operator delete po kimup-operator + .PHONY: run-kimup run-kimup: manifests generate fmt vet ## Run the image updater from your host. go run ./cmd/kimup -.PHONY: run-admission-controller -run-admission-controller: manifests generate fmt vet ## Run the admission-controller from your host. - go run ./cmd/admission-controller/ - .PHONY: run-mkdocs run-mkdocs: ## Run mkdocs to serve the documentation locally. mkdocs serve diff --git a/api/v1alpha1/image_models.go b/api/v1alpha1/image_models.go index f1ac7d7..7c43295 100644 --- a/api/v1alpha1/image_models.go +++ b/api/v1alpha1/image_models.go @@ -5,30 +5,30 @@ type ( ) var ( - // ImageStatusLastSyncSuccess is the status of the image when it is last sync success. + // Status of the image when it is last sync success. ImageStatusLastSyncSuccess ImageStatusLastSync = "Success" - // ImageStatusLastSyncScheduled is the status of the image when it is last sync is scheduled. + // Status of the image when it is last sync is scheduled. ImageStatusLastSyncScheduled ImageStatusLastSync = "Scheduled" - // ImageStatusError is the status of the image when an error occurred. + // Status of the image when an error occurred. ImageStatusLastSyncError ImageStatusLastSync = "Error" - // ImageStatusLastSyncGetError is the status of the image when it is last sync get error. + // Status of the image when it is last sync get error. ImageStatusLastSyncErrorGetImage ImageStatusLastSync = "GetImageError" - // ImageStatusLastSyncErrorSecrets is the status of the image when it is last sync error secrets. + // Status of the image when it is last sync error secrets. ImageStatusLastSyncErrorPullSecrets ImageStatusLastSync = "PullSecretsError" - // ImageStatusLastSyncErrorRegistry is the status of the image when it is last sync error registry. + // Status of the image when it is last sync error registry. ImageStatusLastSyncErrorRegistry ImageStatusLastSync = "RegistryError" - // ImageStatusLastSyncErrorTags is the status of the image when it is last sync error tags. + // Status of the image when it is last sync error tags. ImageStatusLastSyncErrorTags ImageStatusLastSync = "TagsError" - // ImageStatusLastSyncErrorGetRule is the status of the image when it is last sync error get rule. + // Status of the image when it is last sync error get rule. ImageStatusLastSyncErrorGetRule ImageStatusLastSync = "GetRuleError" - // ImageStatusLastSyncErrorAction is the status of the image when it is last sync error action. + // Status of the image when it is last sync error action. ImageStatusLastSyncErrorAction ImageStatusLastSync = "ActionError" ) diff --git a/cmd/admission-controller/certificate.go b/cmd/admission-controller/certificate.go deleted file mode 100644 index 324bc51..0000000 --- a/cmd/admission-controller/certificate.go +++ /dev/null @@ -1,222 +0,0 @@ -package main - -import ( - "bufio" - "bytes" - "context" - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/base64" - "encoding/pem" - "math/big" - "os" - "strings" - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - v1 "k8s.io/client-go/applyconfigurations/admissionregistration/v1" - - "github.com/orange-cloudavenue/kube-image-updater/internal/kubeclient" -) - -// generateTLS generates a self-signed certificate for the webhook server -// and returns the certificate and the CA certificate -// The certificate is generated with the following DNS names: -// - webhookServiceName -// - webhookServiceName.webhookNamespace -// - webhookServiceName.webhookNamespace.svc -func generateTLS() (keyPair tls.Certificate, caPEM *bytes.Buffer, err error) { - // generate dns names - dnsNames := []string{ - webhookServiceName, - webhookServiceName + "." + webhookNamespace, - webhookServiceName + "." + webhookNamespace + ".svc", - // webhookServiceName + "." + webhookNamespace + ".svc" + ".cluster.local", - } - commonName := webhookServiceName + "." + webhookNamespace + ".svc" - - caPEM, certPEM, certKeyPEM, err := generateCert([]string{webhookBase}, dnsNames, commonName) - if err != nil { - return - } - - keyPair, err = tls.X509KeyPair(certPEM.Bytes(), certKeyPEM.Bytes()) - if err != nil { - return - } - return -} - -// generateCert generates a self-signed certificate with the given organizations, DNS names, and common name -// The certificate is valid for 1 year -// The certificate is signed by the CA certificate -// The CA certificate is generated with the given organizations -// it resurns the CA, certificate and private key in PEM format. -func generateCert(orgs, dnsNames []string, commonName string) (caPEM, newCertPEM, newPrivateKeyPEM *bytes.Buffer, err error) { - // init CA config - ca := &x509.Certificate{ - SerialNumber: big.NewInt(2022), - Subject: pkix.Name{Organization: orgs}, - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(1, 0, 0), // expired in 1 year - IsCA: true, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - BasicConstraintsValid: true, - } - - // generate private key for CA - caPrivateKey, err := rsa.GenerateKey(rand.Reader, 4096) - if err != nil { - return nil, nil, nil, err - } - - // create the CA certificate - caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivateKey.PublicKey, caPrivateKey) - if err != nil { - return nil, nil, nil, err - } - - // CA certificate with PEM encoded - caPEM = new(bytes.Buffer) - err = pem.Encode(caPEM, &pem.Block{ - Type: "CERTIFICATE", - Bytes: caBytes, - }) - if err != nil { - return nil, nil, nil, err - } - - // print CA certificate if insideCluster is false - if !insideCluster { - writeNewCA(caPEM, manifestWebhookPath) - time.Sleep(2 * time.Second) - applyManifest(manifestWebhookPath) - } - - // new certificate config - newCert := &x509.Certificate{ - DNSNames: dnsNames, - SerialNumber: big.NewInt(1024), - Subject: pkix.Name{ - CommonName: commonName, - Organization: orgs, - }, - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(1, 0, 0), // expired in 1 year - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: x509.KeyUsageDigitalSignature, - } - - // generate new private key - newPrivateKey, err := rsa.GenerateKey(rand.Reader, 4096) - if err != nil { - return nil, nil, nil, err - } - - // sign the new certificate - newCertBytes, err := x509.CreateCertificate(rand.Reader, newCert, ca, &newPrivateKey.PublicKey, caPrivateKey) - if err != nil { - return nil, nil, nil, err - } - - // new certificate with PEM encoded - newCertPEM = new(bytes.Buffer) - err = pem.Encode(newCertPEM, &pem.Block{ - Type: "CERTIFICATE", - Bytes: newCertBytes, - }) - if err != nil { - return nil, nil, nil, err - } - - // new private key with PEM encoded - newPrivateKeyPEM = new(bytes.Buffer) - err = pem.Encode(newPrivateKeyPEM, &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(newPrivateKey), - }) - if err != nil { - return nil, nil, nil, err - } - - return caPEM, newCertPEM, newPrivateKeyPEM, nil -} - -func writeNewCA(caPEM *bytes.Buffer, filePath string) { - newCABundle := base64.StdEncoding.EncodeToString(caPEM.Bytes()) - - // Lire le fichier - file, err := os.Open(filePath) - if err != nil { - return - } - defer file.Close() - - var lines []string - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - if strings.Contains(line, "caBundle:") { - line = " caBundle: " + "\"" + newCABundle + "\"" - } - lines = append(lines, line) - } - - if err := scanner.Err(); err != nil { - return - } - - // Écrire les modifications dans le fichier - file, err = os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0o644) - if err != nil { - return - } - defer file.Close() - - writer := bufio.NewWriter(file) - for _, line := range lines { - _, err := writer.WriteString(line + "\n") - if err != nil { - return - } - } - writer.Flush() -} - -func applyManifest(file string) { - // read the manifest file - manifestBytes, err := os.ReadFile(file) - if err != nil { - return - } - - // decode the manifest to unstructured object - decoder := serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer() - obj := &unstructured.Unstructured{} - _, _, err = decoder.Decode(manifestBytes, nil, obj) - if err != nil { - return - } - - // convert the unstructured object to typed object - mutatingWebhookConfiguration, err := kubeclient.DecodeUnstructured[v1.MutatingWebhookConfigurationApplyConfiguration](obj) - if err != nil { - return - } - - // apply the manifest - if _, err := kubeClient.AdmissionregistrationV1().MutatingWebhookConfigurations().Apply( - context.TODO(), - &mutatingWebhookConfiguration, - metav1.ApplyOptions{Force: true, FieldManager: "kumi-webhook"}, - ); err != nil { - return - } -} diff --git a/cmd/admission-controller/main.go b/cmd/admission-controller/main.go deleted file mode 100644 index fa747a6..0000000 --- a/cmd/admission-controller/main.go +++ /dev/null @@ -1,122 +0,0 @@ -package main - -import ( - "context" - "crypto/tls" - "flag" - "net" - "os" - "os/signal" - "syscall" - "time" - - "github.com/sirupsen/logrus" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - - "github.com/orange-cloudavenue/kube-image-updater/internal/httpserver" - client "github.com/orange-cloudavenue/kube-image-updater/internal/kubeclient" - "github.com/orange-cloudavenue/kube-image-updater/internal/log" - "github.com/orange-cloudavenue/kube-image-updater/internal/metrics" -) - -var ( - insideCluster bool = true // running inside k8s cluster - - webhookNamespace string = "nip.io" - webhookServiceName string = "192-168-1-30" - webhookConfigName string = "mutating-webhook-configuration" - webhookPathMutate string = "/mutate" - webhookPort string = ":8443" - webhookBase = webhookServiceName + "." + webhookNamespace - - runtimeScheme = runtime.NewScheme() - codecs = serializer.NewCodecFactory(runtimeScheme) - deserializer = codecs.UniversalDeserializer() - - kubeClient client.Interface - manifestWebhookPath string = "./examples/mutatingWebhookConfiguration.yaml" -) - -func init() { - // Init Metrics - metrics.AdmissionController() - - // webhook server running namespace (default to "default") - if os.Getenv("POD_NAMESPACE") != "" { - webhookNamespace = os.Getenv("POD_NAMESPACE") - } - // init flags - flag.StringVar(&webhookPort, "webhook-port", webhookPort, "Webhook server port.ex: :8443") - flag.StringVar(&webhookNamespace, "namespace", webhookNamespace, "Kimup Webhook Mutating namespace.") - flag.StringVar(&webhookServiceName, "service-name", webhookServiceName, "Kimup Webhook Mutating service name.") - flag.BoolVar(&insideCluster, "inside-cluster", true, "True if running inside k8s cluster.") - flag.Parse() -} - -// Start http server for webhook -func main() { - var err error - - // -- Context -- // - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // -- OS signal handling -- // - signalChan := make(chan os.Signal, 1) - signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGKILL) - - // kubernetes golang library provide flag "kubeconfig" to specify the path to the kubeconfig file - kubeClient, err = client.New(flag.Lookup("kubeconfig").Value.String(), client.ComponentAdmissionController) - if err != nil { - log.WithError(err).Panicf("Error creating kubeclient") - } - - // * Webhook server - // generate cert for webhook - pair, caPEM, err := generateTLS() - if err != nil { - log.WithError(err).Fatal("Failed to generate TLS") - } - tlsC := &tls.Config{ - Certificates: []tls.Certificate{pair}, - MinVersion: tls.VersionTLS12, - // InsecureSkipVerify: true, //nolint:gosec - } - - // create or update the mutatingwebhookconfiguration - err = createOrUpdateMutatingWebhookConfiguration(caPEM, webhookServiceName, webhookNamespace, kubeClient) - if err != nil { - log.WithError(err).Error("Failed to create or update the mutating webhook configuration") - signalChan <- os.Interrupt - } - - // * Config the webhook server - a, waitHTTP := httpserver.Init(ctx, httpserver.WithCustomHandlerForHealth( - func() (bool, error) { - _, err := net.DialTimeout("tcp", webhookPort, 5*time.Second) - if err != nil { - return false, err - } - return true, nil - })) - - s, err := a.Add("webhook", httpserver.WithTLS(tlsC), httpserver.WithAddr(webhookPort)) - if err != nil { - log. - WithError(err). - WithFields(logrus.Fields{ - "address": webhookPort, - }).Fatal("Failed to create the server") - } - s.Config.Post(webhookPathMutate, ServeHandler) - if err := a.Run(); err != nil { - log.WithError(err).Fatal("Failed to start HTTP servers") - } - - // !-- OS signal handling --! // - <-signalChan - // cancel the context - cancel() - waitHTTP() -} diff --git a/cmd/admission-controller/webhook-configuration.go b/cmd/admission-controller/webhook-configuration.go deleted file mode 100644 index a49921b..0000000 --- a/cmd/admission-controller/webhook-configuration.go +++ /dev/null @@ -1,105 +0,0 @@ -package main - -import ( - "bytes" - "context" - "reflect" - - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - client "github.com/orange-cloudavenue/kube-image-updater/internal/kubeclient" - "github.com/orange-cloudavenue/kube-image-updater/internal/log" -) - -// createOrUpdateMutatingWebhookConfiguration creates or updates the mutating webhook configuration -// for the webhook service. The CA is generated and used for the webhook. -// This function create the request to the Kubernetes API server to create or update the mutating webhook configuration. -func createOrUpdateMutatingWebhookConfiguration(caPEM *bytes.Buffer, webhookService, webhookNamespace string, k client.Interface) error { - mutatingWebhookConfigV1Client := k.AdmissionregistrationV1() - - var clientConfig admissionregistrationv1.WebhookClientConfig - switch insideCluster { - case true: - clientConfig = admissionregistrationv1.WebhookClientConfig{ - Service: &admissionregistrationv1.ServiceReference{ - Name: webhookService, - Namespace: webhookNamespace, - Path: &webhookPathMutate, - }, - } - case false: - // the webhook is running outside the cluster - // Please note that the webhook service must be accessible from the Kubernetes cluster. - // Each time you change webhook service name, namespace, or port, you need to update the MutatingWebhookConfiguration - // Also you need to modifiy the manifest MutatingWebhookConfiguration to push new caPEM to allow client to trust the webhook - // The caPEM is generated and printed to the logs when the webhook starts for outside cluster - url := "https://" + webhookService + "." + webhookNamespace + webhookPort + webhookPathMutate - clientConfig = admissionregistrationv1.WebhookClientConfig{ - CABundle: caPEM.Bytes(), - URL: &url, - } - } - log.Debug("Creating or updating the mutatingwebhookconfiguration") - fail := admissionregistrationv1.Fail - sideEffect := admissionregistrationv1.SideEffectClassNone - mutatingWebhookConfig := &admissionregistrationv1.MutatingWebhookConfiguration{ - ObjectMeta: metav1.ObjectMeta{ - Name: webhookConfigName, - }, - Webhooks: []admissionregistrationv1.MutatingWebhook{{ - Name: webhookService + "." + webhookNamespace, - AdmissionReviewVersions: []string{"v1", "v1beta1"}, - SideEffects: &sideEffect, - ClientConfig: clientConfig, - - Rules: []admissionregistrationv1.RuleWithOperations{ - { - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Update, - admissionregistrationv1.Create, - }, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{""}, - APIVersions: []string{"v1"}, - Resources: []string{"pods"}, - // TODO - add namespace scope - // Scope: "*", - }, - }, - }, - FailurePolicy: &fail, - }}, - } - - // check if the mutatingwebhookconfiguration already exists - foundWebhookConfig, err := mutatingWebhookConfigV1Client.MutatingWebhookConfigurations().Get(context.TODO(), webhookConfigName, metav1.GetOptions{}) - switch { - case err != nil && apierrors.IsNotFound(err): - if _, err := mutatingWebhookConfigV1Client.MutatingWebhookConfigurations().Create(context.TODO(), mutatingWebhookConfig, metav1.CreateOptions{}); err != nil { - return err - } - case err != nil: - return err - default: - // there is an existing mutatingWebhookConfiguration - if len(foundWebhookConfig.Webhooks) != len(mutatingWebhookConfig.Webhooks) || - !(foundWebhookConfig.Webhooks[0].Name == mutatingWebhookConfig.Webhooks[0].Name && - reflect.DeepEqual(foundWebhookConfig.Webhooks[0].AdmissionReviewVersions, mutatingWebhookConfig.Webhooks[0].AdmissionReviewVersions) && - reflect.DeepEqual(foundWebhookConfig.Webhooks[0].SideEffects, mutatingWebhookConfig.Webhooks[0].SideEffects) && - reflect.DeepEqual(foundWebhookConfig.Webhooks[0].FailurePolicy, mutatingWebhookConfig.Webhooks[0].FailurePolicy) && - reflect.DeepEqual(foundWebhookConfig.Webhooks[0].Rules, mutatingWebhookConfig.Webhooks[0].Rules) && - // reflect.DeepEqual(foundWebhookConfig.Webhooks[0].NamespaceSelector, mutatingWebhookConfig.Webhooks[0].NamespaceSelector) && - reflect.DeepEqual(foundWebhookConfig.Webhooks[0].ClientConfig.CABundle, mutatingWebhookConfig.Webhooks[0].ClientConfig.CABundle) && - // reflect.DeepEqual(foundWebhookConfig.Webhooks[0].ClientConfig.Service, mutatingWebhookConfig.Webhooks[0].ClientConfig.Service) && - reflect.DeepEqual(foundWebhookConfig.Webhooks[0].ClientConfig.URL, mutatingWebhookConfig.Webhooks[0].ClientConfig.URL)) { - mutatingWebhookConfig.ObjectMeta.ResourceVersion = foundWebhookConfig.ObjectMeta.ResourceVersion - if _, err := mutatingWebhookConfigV1Client.MutatingWebhookConfigurations().Update(context.TODO(), mutatingWebhookConfig, metav1.UpdateOptions{}); err != nil { - return err - } - } - } - - return nil -} diff --git a/cmd/admission-controller/webhook.go b/cmd/admission-controller/webhook.go deleted file mode 100644 index 64a350c..0000000 --- a/cmd/admission-controller/webhook.go +++ /dev/null @@ -1,220 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - - "github.com/sirupsen/logrus" - admissionv1 "k8s.io/api/admission/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/orange-cloudavenue/kube-image-updater/api/v1alpha1" - "github.com/orange-cloudavenue/kube-image-updater/internal/annotations" - "github.com/orange-cloudavenue/kube-image-updater/internal/log" - "github.com/orange-cloudavenue/kube-image-updater/internal/metrics" - "github.com/orange-cloudavenue/kube-image-updater/internal/patch" - "github.com/orange-cloudavenue/kube-image-updater/internal/utils" -) - -// func serveHandler -func ServeHandler(w http.ResponseWriter, r *http.Request) { - // Prometheus metrics - metrics.AdmissionController().RequestTotal.Inc() - timeAC := metrics.AdmissionController().RequestDuration.NewTimer() - defer timeAC.ObserveDuration() - - var body []byte - if r.Body != nil { - if data, err := io.ReadAll(r.Body); err == nil { - body = data - } - } - if len(body) == 0 { - // increment the total number of errors - metrics.AdmissionController().RequestErrorTotal.Inc() - - log.Error("empty body") - http.Error(w, "empty body", http.StatusBadRequest) - return - } - - // verify the content type is accurate - contentType := r.Header.Get("Content-Type") - if contentType != "application/json" { - // increment the total number of errors - metrics.AdmissionController().RequestErrorTotal.Inc() - - http.Error(w, "invalid Content-Type, expect `application/json`", http.StatusUnsupportedMediaType) - return - } - - var admissionResponse *admissionv1.AdmissionResponse - ar := admissionv1.AdmissionReview{} - if _, _, err := deserializer.Decode(body, nil, &ar); err != nil { - // increment the total number of errors - metrics.AdmissionController().RequestErrorTotal.Inc() - - log.WithError(err).Warn("Can't decode body") - admissionResponse = &admissionv1.AdmissionResponse{ - Result: &metav1.Status{ - Message: err.Error(), - }, - } - } else { - admissionResponse = mutate(r.Context(), &ar) - } - - admissionReview := admissionv1.AdmissionReview{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "admission.k8s.io/v1", - Kind: "AdmissionReview", - }, - } - if admissionResponse != nil { - admissionReview.Response = admissionResponse - if ar.Request != nil { - admissionReview.Response.UID = ar.Request.UID - } - } - - resp, err := json.Marshal(admissionReview) - if err != nil { - // increment the total number of errors - metrics.AdmissionController().RequestErrorTotal.Inc() - - http.Error(w, fmt.Sprintf("could not encode response: %v", err), http.StatusInternalServerError) - } - if _, err := w.Write(resp); err != nil { - // increment the total number of errors - metrics.AdmissionController().RequestErrorTotal.Inc() - - http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError) - } -} - -// func mutate the request -func mutate(ctx context.Context, ar *admissionv1.AdmissionReview) *admissionv1.AdmissionResponse { - req := ar.Request - var pod corev1.Pod - if err := json.Unmarshal(req.Object.Raw, &pod); err != nil { - return &admissionv1.AdmissionResponse{ - Result: &metav1.Status{ - Message: err.Error(), - }, - } - } - - log.WithFields(logrus.Fields{ - "Kind": req.Kind, - "Namespace": req.Namespace, - "Name": req.Name, - "UID": req.UID, - "Operation": req.Operation, - "UserInfo": req.UserInfo, - }).Info("AdmissionReview") - - // create patch - patchBytes, err := createPatch(ctx, &pod) - if err != nil { - return &admissionv1.AdmissionResponse{ - Result: &metav1.Status{ - Message: err.Error(), - }, - } - } - return &admissionv1.AdmissionResponse{ - Allowed: true, - Patch: patchBytes, - PatchType: func() *admissionv1.PatchType { - pt := admissionv1.PatchTypeJSONPatch - return &pt - }(), - } -} - -// create mutation patch for pod. -func createPatch(ctx context.Context, pod *corev1.Pod) ([]byte, error) { - // Metrics - increment the total number of patch - metrics.AdmissionController().PatchTotal.Inc() - timePatch := metrics.AdmissionController().PatchDuration.NewTimer() - defer timePatch.ObserveDuration() - - var err error - // find annotation enabled - an := annotations.New(ctx, pod) - if !an.Enabled().Get() { - // increment the total number of errors - metrics.AdmissionController().PatchErrorTotal.Inc() - - return nil, fmt.Errorf("annotation not enabled") - } - - // var patch []patchOperation - p := patch.NewBuilder() - - log. - WithFields(logrus.Fields{ - "Namespace": pod.Namespace, - "Name": pod.Name, - }).Info("Generate Patch") - - for i, container := range pod.Spec.Containers { - imageP := utils.ImageParser(container.Image) - - // TODO Why is this not used? Annotation is never set. - crdName, _ := an.Images().Get(imageP.GetImageWithoutTag()) - - // If crdName is empty, it means that we need to find it - var image v1alpha1.Image - if crdName == "" { - // find the image associated with the pod - image, err = kubeClient.Image().Find(ctx, pod.Namespace, imageP.GetImageWithoutTag()) - if err != nil { - // increment the total number of errors - metrics.AdmissionController().PatchErrorTotal.Inc() - - log. - WithFields(logrus.Fields{ - "Namespace": pod.Namespace, - "Name": pod.Name, - "Container": container.Name, - "ContainerImage": imageP.GetImageWithoutTag(), - }). - WithError(err).Error("Failed to find kind Image") - continue - } - } else { - image, err = kubeClient.Image().Get(ctx, pod.Namespace, crdName) - if err != nil { - // increment the total number of errors - metrics.AdmissionController().PatchErrorTotal.Inc() - - log. - WithFields(logrus.Fields{ - "Namespace": pod.Namespace, - "Name": pod.Name, - "Container": container.Name, - "ContainerImage": crdName, - }).WithError(err).Error("Failed to get kind Image") - continue - } - } - - // Set the image to the pod - if image.ImageIsEqual(container.Image) { - p.AddPatch(patch.OpReplace, fmt.Sprintf("/spec/containers/%d/image", i), image.GetImageWithTag()) - } - - // Annotations - an.Containers().Set(container.Name, image.Name) - } - - // update the annotation - p.AddRawPatches(an.Containers().BuildPatches()) - - return p.Generate() -} diff --git a/cmd/operator/main.go b/cmd/operator/main.go index 83d5b99..f9118a3 100644 --- a/cmd/operator/main.go +++ b/cmd/operator/main.go @@ -32,6 +32,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" kimupv1alpha1 "github.com/orange-cloudavenue/kube-image-updater/api/v1alpha1" "github.com/orange-cloudavenue/kube-image-updater/internal/controller" @@ -68,6 +69,8 @@ func main() { ctrl.SetLogger(logrusr.New(log.GetLogger())) + webhook := webhook.NewServer(webhook.Options{}) + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, Metrics: metricsserver.Options{ @@ -78,6 +81,7 @@ func main() { HealthProbeBindAddress: "0", // disable health probe service LeaderElection: enableLeaderElection, LeaderElectionID: "71be4586.cloudavenue.io", + WebhookServer: webhook, }) if err != nil { log.WithError(err).Error("unable to start manager") @@ -90,6 +94,19 @@ func main() { c <- syscall.SIGINT } + // ! Mutator + + if err := (&controller.ImageTagMutator{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + KubeAPIClient: kubeAPIClient, + }).SetupWebhookWithManager(mgr); err != nil { + log.WithError(err).Error("unable to create webhook", "webhook", "ImageTagMutator") + c <- syscall.SIGINT + } + + // ! Reconcilers + if err = (&controller.ImageReconciler{ Client: mgr.GetClient(), KubeAPIClient: kubeAPIClient, diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 8a3d6f2..0c88da3 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -10,17 +10,17 @@ webhooks: service: name: webhook-service namespace: system - path: /mutate-kimup-cloudavenue-io-v1alpha1-image + path: /mutate/image-tag failurePolicy: Fail - name: mimage.kb.io + name: mutator.kimup.cloudavenue.io rules: - apiGroups: - - kimup.cloudavenue.io + - "" apiVersions: - - v1alpha1 + - v1 operations: - CREATE - UPDATE resources: - - images + - pods sideEffects: None diff --git a/docs/advanced/image-status.md b/docs/advanced/image-status.md deleted file mode 100644 index e52df5c..0000000 --- a/docs/advanced/image-status.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -hide: - - toc ---- - -# CRD Image - -Custom Resource Definition (CRD) to manage images. - -## Status - -The following status can be set on an image: - -| Last-Sync | Description | -| --------- | ----------- | -| ImageStatusLastSyncError | ImageStatusError is the status of the image when an error occurred. - | -| ImageStatusLastSyncErrorAction | ImageStatusLastSyncErrorAction is the status of the image when it is last sync error action. - | -| ImageStatusLastSyncErrorGetImage | ImageStatusLastSyncGetError is the status of the image when it is last sync get error. - | -| ImageStatusLastSyncErrorGetRule | ImageStatusLastSyncErrorGetRule is the status of the image when it is last sync error get rule. - | -| ImageStatusLastSyncErrorPullSecrets | ImageStatusLastSyncErrorSecrets is the status of the image when it is last sync error secrets. - | -| ImageStatusLastSyncErrorRegistry | ImageStatusLastSyncErrorRegistry is the status of the image when it is last sync error registry. - | -| ImageStatusLastSyncErrorTags | ImageStatusLastSyncErrorTags is the status of the image when it is last sync error tags. - | -| ImageStatusLastSyncScheduled | ImageStatusLastSyncScheduled is the status of the image when it is last sync is scheduled. - | -| ImageStatusLastSyncSuccess | ImageStatusLastSyncSuccess is the status of the image when it is last sync success. - | - - diff --git a/docs/advanced/image-status.md.tmpl b/docs/advanced/image-status.md.tmpl deleted file mode 100644 index 77efb1d..0000000 --- a/docs/advanced/image-status.md.tmpl +++ /dev/null @@ -1,15 +0,0 @@ ---- -hide: - - toc ---- - -# CRD Image - -Custom Resource Definition (CRD) to manage images. - -## Status - -The following status can be set on an image: - -{{ imageStatusLastSync }} - diff --git a/docs/advanced/metrics.md b/docs/advanced/metrics.md index ad62184..eb17ec3 100644 --- a/docs/advanced/metrics.md +++ b/docs/advanced/metrics.md @@ -9,7 +9,7 @@ kimup exposes metrics to monitor the performance. The metrics are exposed in the ## Settings -The following arguments can be used to configure the metrics *(Available in kimup-operator, kimup-controller and kimup-admission-controller)*: +The following arguments can be used to configure the metrics *(Available in kimup-operator and kimup-controller)*: | Flag | Default | Description | | -------------- | -------- | ------------------------- | diff --git a/docs/advanced/metrics.md.tmpl b/docs/advanced/metrics.md.tmpl index efdb891..0d82663 100644 --- a/docs/advanced/metrics.md.tmpl +++ b/docs/advanced/metrics.md.tmpl @@ -9,7 +9,7 @@ kimup exposes metrics to monitor the performance. The metrics are exposed in the ## Settings -The following arguments can be used to configure the metrics *(Available in kimup-operator, kimup-controller and kimup-admission-controller)*: +The following arguments can be used to configure the metrics *(Available in kimup-operator and kimup-controller)*: {{ tableSettings }} diff --git a/docs/crd/image.md b/docs/crd/image.md index 8897f23..7aa0f06 100644 --- a/docs/crd/image.md +++ b/docs/crd/image.md @@ -73,3 +73,20 @@ spec: - - ``` +## Status + +The following status can be set on an image: + +| Last-Sync state | Description | +| --------------- | ----------- | +| `ImageStatusLastSyncErrorAction` | Status of the image when it is last sync error action. | +| `ImageStatusLastSyncErrorGetImage` | Status of the image when it is last sync get error. | +| `ImageStatusLastSyncErrorGetRule` | Status of the image when it is last sync error get rule. | +| `ImageStatusLastSyncErrorPullSecrets` | Status of the image when it is last sync error secrets. | +| `ImageStatusLastSyncErrorRegistry` | Status of the image when it is last sync error registry. | +| `ImageStatusLastSyncErrorTags` | Status of the image when it is last sync error tags. | +| `ImageStatusLastSyncError` | Status of the image when an error occurred. | +| `ImageStatusLastSyncScheduled` | Status of the image when it is last sync is scheduled. | +| `ImageStatusLastSyncSuccess` | Status of the image when it is last sync success. | + + diff --git a/docs/crd/image.md.tmpl b/docs/crd/image.md.tmpl new file mode 100644 index 0000000..bbb6755 --- /dev/null +++ b/docs/crd/image.md.tmpl @@ -0,0 +1,81 @@ +--- +hide: + - toc +--- + +# Custom Resource Definition `Image` + +This is a custom resource definition for an image. It is used to store information about an image. +`Image` is a namespaced resource. + +## Basic example + +```yaml +apiVersion: kimup.cloudavenue.io/v1alpha1 +kind: Image +metadata: + name: image-sample +spec: + image: alpine + baseTag: v1.0.0 + triggers: + - + - + rules: + - + - +``` + +## Configuration + +Kimup Operator uses a dedicated kimup CRD to create and manage image resources. The CRD allows various configurations to define the behaviour of the image. See [docs.crds.dev](https://doc.crds.dev/github.com/orange-cloudavenue/kube-image-updater/kimup.cloudavenue.io/Image/v1alpha1) for more information about the Image CRD. + +## Advanced + +### Use authenticated registry + +Use the `imagePullSecrets` field to specify the name of the secret to use to authenticate with the registry. + +```yaml +apiVersion: kimup.cloudavenue.io/v1alpha1 +kind: Image +metadata: + name: image-sample +spec: + image: custom-registry.io/image + baseTag: v1.0.0 + imagePullSecrets: + - name: registry-local + triggers: + - + - + rules: + - + - +``` + +### Self-signed certificate + +Use the `insecureSkipTLSVerify` field to skip the verification of the TLS certificate. + +```yaml +kind: Image +metadata: + name: image-sample +spec: + image: custom-registry.io/image + baseTag: v1.0.0 + insecureSkipTLSVerify: true + triggers: + - + - + rules: + - + - +``` +## Status + +The following status can be set on an image: + +{{ imageStatusLastSync }} + diff --git a/docs/crd/kimup.md b/docs/crd/kimup.md index be5f829..d6588c1 100644 --- a/docs/crd/kimup.md +++ b/docs/crd/kimup.md @@ -5,7 +5,7 @@ hide: # Custom Resource Definition `Kimup` -This is a custom resource definition for a Kimup. It is used to manage a deployment of a Kimup (kimup-admission-controller and kimup-controller). +This is a custom resource definition for a Kimup. It is used to manage a deployment of a kimup-controller. ## Basic example @@ -20,11 +20,9 @@ spec: controller: name: demo logLevel: info - admissionController: - name: demo - logLevel: info + ``` ## Configuration -Kimup Operator uses a dedicated kimup CRD to create and manage kimup resources. The CRD allows various configurations to define the behaviour of the kimup admission controller and the kimup controller. See [docs.crds.dev](https://doc.crds.dev/github.com/orange-cloudavenue/kube-image-updater/kimup.cloudavenue.io/Kimup/v1alpha1) for more information about the Kimup CRD. +Kimup Operator uses a dedicated kimup CRD to create and manage kimup resources. The CRD allows various configurations to define the behaviour of the kimup controller. See [docs.crds.dev](https://doc.crds.dev/github.com/orange-cloudavenue/kube-image-updater/kimup.cloudavenue.io/Kimup/v1alpha1) for more information about the Kimup CRD. diff --git a/docs/getting-started/howto.md b/docs/getting-started/howto.md index 3c70bc5..8eec7ba 100644 --- a/docs/getting-started/howto.md +++ b/docs/getting-started/howto.md @@ -31,21 +31,21 @@ spec: - type: apply ``` +In this example the image `{{dockerImages.whoami}}` will be updated every 12 hours with the latest minor version. + 2 - Apply the `Image` resource: ```bash kubectl apply -f image.yaml ``` -In this example the image `{{dockerImages.whoami}}` will be updated every 12 hours with the latest minor version. - 3 - Check the Image TAG: ```bash -kubectl get image demo' +kubectl get image demo -NAME IMAGE TAG -demo {{dockerImages.whoami}} +NAME IMAGE TAG LAST-RESULT LAST-SYNC +demo {{dockerImages.whoami}} ``` But you can force the update by running the following command: @@ -57,8 +57,10 @@ kubectl annotate image demo kimup.cloudavenue.io/action=refresh The Image TAG is now updated: ```bash -NAME IMAGE TAG -demo {{dockerImages.whoami}} v1.10.0 +kubectl get image demo + +NAME IMAGE TAG LAST-RESULT LAST-SYNC +demo {{dockerImages.whoami}} v1.10.0 Success 15s ``` 4 - Make a deployment with the image: diff --git a/docs/getting-started/install.md b/docs/getting-started/install.md index 5302ce3..3d1b1ef 100644 --- a/docs/getting-started/install.md +++ b/docs/getting-started/install.md @@ -5,7 +5,7 @@ hide: # Install Kimup Operator -Kimup Operator is a Kubernetes operator used to manage images and their lifecycle, manage kimup-admission-controller and kimup-webhook deployments. The operator is required for the functioning of the Kimup. +Kimup Operator is a Kubernetes operator used to manage images and their lifecycle, manage kimup-controller deployments. The operator is required for the functioning of the Kimup. Resources managed by Kimup Operator are: @@ -34,9 +34,12 @@ kubectl apply -k "https://github.com/orange-cloudavenue/kube-image-updater/manif By default, Kimup Operator is installed in the `kimup-operator` namespace. -### Deploy `kimup-admission-controller` and `kimup-controller` +!!! warning "Namespace" + For the moment only the `kimup-operator` namespace is supported. -For deploying `kimup-admission-controller` and `kimup-controller`, create a `Kimup` resource: +### Deploy `kimup-controller` + +For deploying `kimup-controller`, create a `Kimup` resource: ```yaml apiVersion: kimup.cloudavenue.io/v1alpha1 @@ -49,7 +52,4 @@ spec: controller: name: demo logLevel: info - admissionController: - name: demo - logLevel: info ``` diff --git a/docs/getting-started/logical-pod-creation-dark.png b/docs/getting-started/logical-pod-creation-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..fed100542e69b589153626b4d7beb2c141da7027 GIT binary patch literal 71839 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/getting-started/logical-pod-creation-light.png b/docs/getting-started/logical-pod-creation-light.png new file mode 100644 index 0000000000000000000000000000000000000000..7a7697284f43ca6a122a7b5a13d2becae3e2a1a2 GIT binary patch literal 71156 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 github.com/distribution/reference v0.5.0 @@ -51,7 +53,8 @@ require ( github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.1 // indirect + github.com/evanphx/json-patch v4.12.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fatih/color v1.15.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -95,7 +98,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/common v0.57.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect @@ -111,7 +114,7 @@ require ( golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.17.0 // indirect - golang.org/x/time v0.3.0 // indirect + golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.34.2 // indirect @@ -121,8 +124,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2 // indirect + k8s.io/utils v0.0.0-20240821151609-f90d01438635 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 3274c4b..0ca383c 100644 --- a/go.sum +++ b/go.sum @@ -58,10 +58,10 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= +github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= @@ -187,8 +187,8 @@ github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+ github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.57.0 h1:Ro/rKjwdq9mZn1K5QPctzh+MA4Lp0BuYk5ZZEVhoNcY= +github.com/prometheus/common v0.57.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/reugn/go-quartz v0.13.0 h1:0eMxvj28Qu1npIDdN9Mzg9hwyksGH6XJt4Cz0QB8EUk= @@ -268,8 +268,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -310,10 +310,10 @@ k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= -k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2 h1:GKE9U8BH16uynoxQii0auTjmmmuZ3O0LFMN6S0lPPhI= +k8s.io/kube-openapi v0.0.0-20240827152857-f7e401e7b4c2/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA= +k8s.io/utils v0.0.0-20240821151609-f90d01438635 h1:2wThSvJoW/Ncn9TmQEYXRnevZXi2duqHWf5OX9S3zjI= +k8s.io/utils v0.0.0-20240821151609-f90d01438635/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/internal/controller/image_tag_mutator.go b/internal/controller/image_tag_mutator.go new file mode 100644 index 0000000..f4b6f02 --- /dev/null +++ b/internal/controller/image_tag_mutator.go @@ -0,0 +1,83 @@ +package controller + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + "github.com/orange-cloudavenue/kube-image-updater/internal/kubeclient" + "github.com/orange-cloudavenue/kube-image-updater/internal/metrics" + "github.com/orange-cloudavenue/kube-image-updater/internal/utils" +) + +func (i *ImageTagMutator) SetupWebhookWithManager(mgr ctrl.Manager) error { + mgr.GetWebhookServer().Register("/mutate/image-tag", &webhook.Admission{Handler: i.SetupHandler()}) + return nil +} + +func (i *ImageTagMutator) SetupHandler() admission.Handler { + i.decoder = admission.NewDecoder(i.Scheme) + return i +} + +// +kubebuilder:webhook:path=/mutate/image-tag,mutating=true,failurePolicy=fail,groups="",resources=pods,sideEffects=None,verbs=create;update,versions=v1,name=mutator.kimup.cloudavenue.io,admissionReviewVersions=v1 + +var _ admission.Handler = &ImageTagMutator{} + +// podAnnotator annotates Pods +type ImageTagMutator struct { + client.Client + KubeAPIClient *kubeclient.Client + Scheme *runtime.Scheme + decoder admission.Decoder +} + +func (i *ImageTagMutator) Handle(ctx context.Context, req admission.Request) admission.Response { + log := logf.FromContext(ctx) + + pod := &corev1.Pod{} + + err := i.decoder.Decode(req, pod) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + + for c, container := range pod.Spec.Containers { + imageP := utils.ImageParser(container.Image) + + // find the image associated with the pod + image, err := i.KubeAPIClient.Image().Find(ctx, pod.Namespace, imageP.GetImageWithoutTag()) + if err != nil { + // increment the total number of errors + metrics.AdmissionController().PatchErrorTotal.Inc() + + log.Error(err, "Failed to find kind Image") + continue + } + + log.Info(fmt.Sprintf("Mutating container %s with image %s to %s", container.Name, container.Image, image.GetImageWithTag())) + + // Set the image to the pod + if image.ImageIsEqual(container.Image) { + pod.Spec.Containers[c].Image = image.GetImageWithTag() + } + } + + // Marshal the pod and return a patch response + marshaledPod, err := json.Marshal(pod) + if err != nil { + log.Error(err, "Failed to mutate the pod") + return admission.Errored(http.StatusInternalServerError, err) + } + + return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod) +} diff --git a/internal/httpserver/httpserver.go b/internal/httpserver/httpserver.go index b491eb2..3fcf12a 100644 --- a/internal/httpserver/httpserver.go +++ b/internal/httpserver/httpserver.go @@ -150,6 +150,7 @@ func (a *app) createMetrics() *server { func (a *app) new(opts ...Option) *server { // create a new router r := chi.NewRouter() + r.Use(middleware.Recoverer) r.Use(middleware.Logger) // create a new server with default parameters diff --git a/internal/kubeclient/client.go b/internal/kubeclient/client.go index f92d1b4..5b8e3e4 100644 --- a/internal/kubeclient/client.go +++ b/internal/kubeclient/client.go @@ -43,15 +43,15 @@ type ( InterfaceKimup interface { Image() *ImageObj Alert() *AlertObj + Mutator() *MutatorObj } component string ) const ( - ComponentOperator component = "kimup-operator" - ComponentController component = "kimup-controller" - ComponentAdmissionController component = "kimup-admission-controller" + ComponentOperator component = "kimup-operator" + ComponentController component = "kimup-controller" ) func init() { diff --git a/internal/kubeclient/image.go b/internal/kubeclient/image.go index ae69c6f..4428824 100644 --- a/internal/kubeclient/image.go +++ b/internal/kubeclient/image.go @@ -196,7 +196,7 @@ func (i *ImageObj) UpdateStatus(ctx context.Context, image v1alpha1.Image) error return err } - _, err = i.imageClient.Namespace(image.Namespace).UpdateStatus(ctx, u, v1.UpdateOptions{}) + _, err = i.imageClient.Namespace(image.Namespace).UpdateStatus(ctx, u, metav1.UpdateOptions{}) if err != nil { return err } diff --git a/internal/kubeclient/mutating.go b/internal/kubeclient/mutating.go new file mode 100644 index 0000000..76e5267 --- /dev/null +++ b/internal/kubeclient/mutating.go @@ -0,0 +1,103 @@ +package kubeclient + +import ( + "context" + + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/orange-cloudavenue/kube-image-updater/internal/annotations" + "github.com/orange-cloudavenue/kube-image-updater/internal/utils" +) + +type ( + MutatorObj struct { + InterfaceKubernetes + } +) + +// Mutator returns an Mutator object +func (c *Client) Mutator() *MutatorObj { + return NewMutator(c) +} + +func NewMutator(k InterfaceKubernetes) *MutatorObj { + return &MutatorObj{ + InterfaceKubernetes: k, + } +} + +func (a *MutatorObj) GetMutatingConfiguration(ctx context.Context, name string) (*admissionregistrationv1.MutatingWebhookConfiguration, error) { + return a.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(ctx, name, metav1.GetOptions{}) +} + +func (a *MutatorObj) CreateOrUpdateMutatingConfiguration(ctx context.Context, name string, svc admissionregistrationv1.ServiceReference, policy admissionregistrationv1.FailurePolicyType) (*admissionregistrationv1.MutatingWebhookConfiguration, error) { + // Get All Namespaces with the "enabled" label + nsList, err := a.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, err + } + + mwc, err := a.GetMutatingConfiguration(ctx, name) + if err != nil { + if apierrors.IsNotFound(err) { + mwc = &admissionregistrationv1.MutatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Annotations: map[string]string{ + "cert-manager.io/inject-ca-from": "kimup-operator/kimup-webhook-serving-cert", + }, + }, + } + } + } + + // reset webhooks settings + mwc.Webhooks = []admissionregistrationv1.MutatingWebhook{} + + for _, ns := range nsList.Items { + nsAnnotation := annotations.New(ctx, &ns) + if !nsAnnotation.Enabled().Get() { + continue + } + + mwc.Webhooks = append(mwc.Webhooks, a.buildMutatingWebhookConfiguration(svc, policy, &namespaceMatchConditionBuilder{namespace: ns.Name})) + } + + // Add the default matchCondition (All pods with annotation enabled == true) + mwc.Webhooks = append(mwc.Webhooks, a.buildMutatingWebhookConfiguration(svc, policy, &defaultMatchConditionBuilder{})) + + if mwc.UID == "" { + return a.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(ctx, mwc, metav1.CreateOptions{}) + } + + return a.AdmissionregistrationV1().MutatingWebhookConfigurations().Update(ctx, mwc, metav1.UpdateOptions{}) +} + +func (a *MutatorObj) buildMutatingWebhookConfiguration(svc admissionregistrationv1.ServiceReference, policy admissionregistrationv1.FailurePolicyType, matchConditionBuilder matchConditionBuilderInterface) admissionregistrationv1.MutatingWebhook { + return admissionregistrationv1.MutatingWebhook{ + Name: matchConditionBuilder.getName() + ".image-tag.kimup.cloudavenue.io", + AdmissionReviewVersions: []string{"v1", "v1beta1"}, + SideEffects: utils.ToPTR(admissionregistrationv1.SideEffectClassNone), + ClientConfig: admissionregistrationv1.WebhookClientConfig{ + Service: &svc, + }, + Rules: []admissionregistrationv1.RuleWithOperations{ + { + Operations: []admissionregistrationv1.OperationType{ + admissionregistrationv1.Update, + admissionregistrationv1.Create, + }, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{"v1"}, + Resources: []string{"pods"}, + Scope: utils.ToPTR(admissionregistrationv1.NamespacedScope), + }, + }, + }, + MatchConditions: matchConditionBuilder.buildMatchCondition(), + FailurePolicy: utils.ToPTR(policy), + } +} diff --git a/internal/kubeclient/mutatingMatchCondition.go b/internal/kubeclient/mutatingMatchCondition.go new file mode 100644 index 0000000..74a5044 --- /dev/null +++ b/internal/kubeclient/mutatingMatchCondition.go @@ -0,0 +1,60 @@ +package kubeclient + +import ( + "fmt" + + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + + "github.com/orange-cloudavenue/kube-image-updater/internal/annotations" +) + +type ( + matchConditionBuilderInterface interface { + buildMatchCondition() []admissionregistrationv1.MatchCondition + getName() string + } + + namespaceMatchConditionBuilder struct { + namespace string + } + + defaultMatchConditionBuilder struct{} +) + +// defaultMatchConditionBuilder + +var _ matchConditionBuilderInterface = &defaultMatchConditionBuilder{} + +func (m *defaultMatchConditionBuilder) buildMatchCondition() []admissionregistrationv1.MatchCondition { + return []admissionregistrationv1.MatchCondition{ + { + Name: "annotation-is-true", + Expression: fmt.Sprintf("object.metadata.?annotations['%s'].orValue('false') == 'true'", annotations.KeyEnabled), + }, + } +} + +func (m *defaultMatchConditionBuilder) getName() string { + return "default" +} + +// * namespaceMatchConditionBuilder + +var _ matchConditionBuilderInterface = &namespaceMatchConditionBuilder{} + +func (n *namespaceMatchConditionBuilder) buildMatchCondition() []admissionregistrationv1.MatchCondition { + return []admissionregistrationv1.MatchCondition{ + { + Name: "annotation-is-not-false", + Expression: fmt.Sprintf("object.metadata.?annotations['%s'].orValue('') != 'false'", annotations.KeyEnabled), + }, + { + Name: fmt.Sprintf("namespace-%s-match", n.namespace), + Expression: fmt.Sprintf("object.metadata.namespace == '%s'", n.namespace), + }, + } +} + +func (n *namespaceMatchConditionBuilder) getName() string { + return n.namespace + ".ns" +} diff --git a/internal/models/mutator.go b/internal/models/mutator.go new file mode 100644 index 0000000..1c8b88e --- /dev/null +++ b/internal/models/mutator.go @@ -0,0 +1,14 @@ +package models + +import "fmt" + +var ( + MutatorDefaultPort int32 = 8443 + MutatorDefaultAddr = fmt.Sprintf(":%d", MutatorDefaultPort) + + MutatorMutatingWebhookConfigurationName = "kimup-admission-controller-mutating" + MutatorMutatingWebhookName = "image-tag.kimup.io" + MutatorServiceName = MutatorMutatingWebhookConfigurationName + + MutatorWebhookPathMutateImageTag = "/mutate/image-tag" +) diff --git a/manifests/crd/kimup.cloudavenue.io_images.yaml b/manifests/crd/kimup.cloudavenue.io_images.yaml index b3e6fd5..12355ea 100644 --- a/manifests/crd/kimup.cloudavenue.io_images.yaml +++ b/manifests/crd/kimup.cloudavenue.io_images.yaml @@ -21,6 +21,12 @@ spec: - jsonPath: .status.tag name: Tag type: string + - jsonPath: .status.result + name: Last-Result + type: string + - jsonPath: .status.time + name: Last-Sync + type: date name: v1alpha1 schema: openAPIV3Schema: @@ -213,13 +219,19 @@ spec: status: description: ImageStatus defines the observed state of Image properties: + result: + type: string tag: description: |- INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file type: string + time: + type: string required: + - result - tag + - time type: object type: object served: true diff --git a/manifests/crd/kimup.cloudavenue.io_kimups.yaml b/manifests/crd/kimup.cloudavenue.io_kimups.yaml index 33cc211..c590855 100644 --- a/manifests/crd/kimup.cloudavenue.io_kimups.yaml +++ b/manifests/crd/kimup.cloudavenue.io_kimups.yaml @@ -24,7 +24,8 @@ spec: name: v1alpha1 schema: openAPIV3Schema: - description: Kimup is the Schema for the kimups API + description: Kimup is the Schema for the kimups API. Permit to manage the + Kimup instances. (Controller and AdmissionController) properties: apiVersion: description: |- @@ -44,12 +45,16 @@ spec: metadata: type: object spec: - description: KimupSpec defines the desired state of Kimup + description: Spec defines the desired state of Kimup properties: admissionController: + description: AdmissionController is a map of settings that will be + used to configure the admissionController. If not set, the admissionController + will not be deployed. properties: affinity: - description: Affinity is a group of affinity scheduling rules. + description: Affinity is a map of affinity settings that will + be added to the Kimup pods. properties: nodeAffinity: description: Describes node affinity scheduling rules for @@ -978,6 +983,8 @@ spec: annotations: additionalProperties: type: string + description: Annotations is a key value map that will be added + to the Kimup pods. type: object deploymentType: default: Deployment @@ -986,6 +993,8 @@ spec: - DaemonSet type: string env: + description: Env is a list of key value pairs that will be added + to the Kimup pods. items: description: EnvVar represents an environment variable present in a Container. @@ -1107,56 +1116,95 @@ spec: healthz: default: enabled: true + description: Healthz is a map of settings that will be used to + configure the healthz probe. If not set, the probe will be enabled. properties: enabled: default: true + description: Enabled is a boolean that enables or disables + the probe. If not set, the probe will be enabled. type: boolean path: + description: Path is the path where the probe will be exposed. + If not set, the default path will be used. See https://pkg.go.dev/github.com/orange-cloudavenue/kube-image-updater@v0.0.1/internal/models#pkg-variables. type: string port: + description: Port is the port number where the probe will + be exposed. If not set, the default port will be used. See + https://pkg.go.dev/github.com/orange-cloudavenue/kube-image-updater@v0.0.1/internal/models#pkg-variables. format: int32 type: integer type: object image: + description: Image of the Kimup container. If not set, the default + image will be used. type: string labels: additionalProperties: type: string + description: Labels is a key value map that will be added to the + Kimup pods. type: object logLevel: default: info + description: LogLevel is a string that will be used to configure + the log level of the Kimup instance. If not set, the info log + level will be used. enum: - debug - info - warn - error + - fatal + - panic + - trace type: string metrics: default: enabled: true + description: Metrics is a map of settings that will be used to + configure the metrics probe. If not set, the probe will be enabled. properties: enabled: default: true + description: Enabled is a boolean that enables or disables + the probe. If not set, the probe will be enabled. type: boolean path: + description: Path is the path where the probe will be exposed. + If not set, the default path will be used. See https://pkg.go.dev/github.com/orange-cloudavenue/kube-image-updater@v0.0.1/internal/models#pkg-variables. type: string port: + description: Port is the port number where the probe will + be exposed. If not set, the default port will be used. See + https://pkg.go.dev/github.com/orange-cloudavenue/kube-image-updater@v0.0.1/internal/models#pkg-variables. format: int32 type: integer type: object + name: + description: The name of the Kimup instance in the suffix of the + resource names. + type: string nodeSelector: additionalProperties: type: string + description: NodeSelector is a map of node selector settings that + will be added to the Kimup pods. type: object priorityClassName: + description: PriorityClassName is the name of the priority class + that will be used by the Kimup pods. type: string replicas: default: 3 + description: Replicas is the number of replicas that will be used + by the admissionController deployment. If not set, 3 replicas + will be used. (Only for Deployment) format: int32 type: integer resources: - description: ResourceRequirements describes the compute resource - requirements. + description: Resources is a map of resource requirements that + will be added to the Kimup pods. properties: claims: description: |- @@ -1216,8 +1264,12 @@ spec: type: object serviceAccountName: default: kimup + description: ServiceAccountName is the name of the service account + that will be used by the Kimup pods. type: string tolerations: + description: Tolerations is a list of tolerations that will be + added to the Kimup pods. items: description: |- The pod this Toleration is attached to tolerates any taint that matches @@ -1256,6 +1308,8 @@ spec: type: object type: array topologySpreadConstraints: + description: TopologySpreadConstraints is a list of constraints + that will be added to the Kimup pods. items: description: TopologySpreadConstraint specifies how to spread matching pods among the given topology. @@ -1430,11 +1484,17 @@ spec: - whenUnsatisfiable type: object type: array + required: + - name type: object controller: + description: Controller is a map of settings that will be used to + configure the controller. If not set, the controller will not be + deployed. properties: affinity: - description: Affinity is a group of affinity scheduling rules. + description: Affinity is a map of affinity settings that will + be added to the Kimup pods. properties: nodeAffinity: description: Describes node affinity scheduling rules for @@ -2363,8 +2423,12 @@ spec: annotations: additionalProperties: type: string + description: Annotations is a key value map that will be added + to the Kimup pods. type: object env: + description: Env is a list of key value pairs that will be added + to the Kimup pods. items: description: EnvVar represents an environment variable present in a Container. @@ -2486,52 +2550,88 @@ spec: healthz: default: enabled: true + description: Healthz is a map of settings that will be used to + configure the healthz probe. If not set, the probe will be enabled. properties: enabled: default: true + description: Enabled is a boolean that enables or disables + the probe. If not set, the probe will be enabled. type: boolean path: + description: Path is the path where the probe will be exposed. + If not set, the default path will be used. See https://pkg.go.dev/github.com/orange-cloudavenue/kube-image-updater@v0.0.1/internal/models#pkg-variables. type: string port: + description: Port is the port number where the probe will + be exposed. If not set, the default port will be used. See + https://pkg.go.dev/github.com/orange-cloudavenue/kube-image-updater@v0.0.1/internal/models#pkg-variables. format: int32 type: integer type: object image: + description: Image of the Kimup container. If not set, the default + image will be used. type: string labels: additionalProperties: type: string + description: Labels is a key value map that will be added to the + Kimup pods. type: object logLevel: default: info + description: LogLevel is a string that will be used to configure + the log level of the Kimup instance. If not set, the info log + level will be used. enum: - debug - info - warn - error + - fatal + - panic + - trace type: string metrics: default: enabled: true + description: Metrics is a map of settings that will be used to + configure the metrics probe. If not set, the probe will be enabled. properties: enabled: default: true + description: Enabled is a boolean that enables or disables + the probe. If not set, the probe will be enabled. type: boolean path: + description: Path is the path where the probe will be exposed. + If not set, the default path will be used. See https://pkg.go.dev/github.com/orange-cloudavenue/kube-image-updater@v0.0.1/internal/models#pkg-variables. type: string port: + description: Port is the port number where the probe will + be exposed. If not set, the default port will be used. See + https://pkg.go.dev/github.com/orange-cloudavenue/kube-image-updater@v0.0.1/internal/models#pkg-variables. format: int32 type: integer type: object + name: + description: The name of the Kimup instance in the suffix of the + resource names. + type: string nodeSelector: additionalProperties: type: string + description: NodeSelector is a map of node selector settings that + will be added to the Kimup pods. type: object priorityClassName: + description: PriorityClassName is the name of the priority class + that will be used by the Kimup pods. type: string resources: - description: ResourceRequirements describes the compute resource - requirements. + description: Resources is a map of resource requirements that + will be added to the Kimup pods. properties: claims: description: |- @@ -2591,8 +2691,12 @@ spec: type: object serviceAccountName: default: kimup + description: ServiceAccountName is the name of the service account + that will be used by the Kimup pods. type: string tolerations: + description: Tolerations is a list of tolerations that will be + added to the Kimup pods. items: description: |- The pod this Toleration is attached to tolerates any taint that matches @@ -2631,6 +2735,8 @@ spec: type: object type: array topologySpreadConstraints: + description: TopologySpreadConstraints is a list of constraints + that will be added to the Kimup pods. items: description: TopologySpreadConstraint specifies how to spread matching pods among the given topology. @@ -2805,17 +2911,16 @@ spec: - whenUnsatisfiable type: object type: array + required: + - name type: object type: object status: - description: KimupStatus defines the observed state of Kimup + description: Status defines the observed state of Kimup properties: admissionController: + description: AdmissionController status properties: - isRollingUpdate: - description: IsRollingUpdate is true if the kimup instance is - being updated - type: boolean state: description: |- Status of the Kimup Instance @@ -2825,11 +2930,8 @@ spec: type: string type: object controller: + description: Controller status properties: - isRollingUpdate: - description: IsRollingUpdate is true if the kimup instance is - being updated - type: boolean state: description: |- Status of the Kimup Instance diff --git a/manifests/operator/deployment.yaml b/manifests/operator/deployment.yaml new file mode 100644 index 0000000..13a9bd3 --- /dev/null +++ b/manifests/operator/deployment.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kimup-operator + namespace: kimup-operator + labels: + app.kubernetes.io/name: kube-image-updater + app.kubernetes.io/instance: kimup-operator + app.kubernetes.io/component: controller +spec: + replicas: 3 + selector: + matchLabels: + app.kubernetes.io/name: kube-image-updater + app.kubernetes.io/instance: kimup-operator + app.kubernetes.io/component: controller + template: + metadata: + labels: + app.kubernetes.io/name: kube-image-updater + app.kubernetes.io/instance: kimup-operator + app.kubernetes.io/component: controller + spec: + serviceAccountName: kimup + containers: + - name: operator + image: "ghcr.io/orange-cloudavenue/kimup-operator:latest" + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8443 + name: webhook-server + protocol: TCP + - containerPort: 8080 + name: metrics + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-cert + readOnly: true + # readinessProbe: + # httpGet: + # path: /readyz + # port: 8081 + # livenessProbe: + # httpGet: + # path: /healthz + # port: 8081 + # resources: + + volumes: + - name: webhook-cert + secret: + defaultMode: 420 + secretName: kimup-webhook-serving-cert diff --git a/manifests/operator/kustomization.yaml b/manifests/operator/kustomization.yaml index 5700af7..734e3bc 100644 --- a/manifests/operator/kustomization.yaml +++ b/manifests/operator/kustomization.yaml @@ -6,3 +6,6 @@ resources: - role.yaml - role_binding.yaml - service_account.yaml + - webhook-certificate.yaml + - deployment.yaml + - service.yaml \ No newline at end of file diff --git a/manifests/operator/service.yaml b/manifests/operator/service.yaml new file mode 100644 index 0000000..1f1df84 --- /dev/null +++ b/manifests/operator/service.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Service +metadata: + name: mutator + namespace: kimup-operator + labels: + app.kubernetes.io/name: kube-image-updater +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app.kubernetes.io/name: kube-image-updater + app.kubernetes.io/instance: kimup-operator \ No newline at end of file diff --git a/manifests/operator/webhook-certificate.yaml b/manifests/operator/webhook-certificate.yaml new file mode 100644 index 0000000..7e49fb8 --- /dev/null +++ b/manifests/operator/webhook-certificate.yaml @@ -0,0 +1,21 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: kimup-webhook-serving-cert + namespace: kimup-operator +spec: + dnsNames: + - mutator.kimup-operator.svc + - mutator.kimup-operator.svc.cluster.local + secretName: kimup-webhook-serving-cert + issuerRef: + kind: Issuer + name: kimup-selfsigned-issuer +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: kimup-selfsigned-issuer + namespace: kimup-operator +spec: + selfSigned: {} \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 435db2a..b0bf789 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,7 +6,9 @@ nav: - Overview: "index.md" - Getting Started: - Installation: getting-started/install.md - - Howto: getting-started/howto.md + - Howto: + - Basic Usage: getting-started/howto.md + - Scope : getting-started/scope.md - Custom Resources: - Kimup: crd/kimup.md - Image: crd/image.md @@ -25,8 +27,7 @@ nav: - Discord: actions/alerts/discord.md - Email: actions/alerts/email.md - Advanced: - - metrics: advanced/metrics.md - - CRD Images: advanced/crd-images.md + - Metrics: advanced/metrics.md # ! Other settings diff --git a/test/mocks/fakekubeclient/kubeclient.go b/test/mocks/fakekubeclient/kubeclient.go index 721351c..699309f 100644 --- a/test/mocks/fakekubeclient/kubeclient.go +++ b/test/mocks/fakekubeclient/kubeclient.go @@ -56,6 +56,10 @@ func (f *FakeKubeClient) Alert() *kubeclient.AlertObj { return kubeclient.NewAlert(f) } +func (f *FakeKubeClient) Mutator() *kubeclient.MutatorObj { + return kubeclient.NewMutator(f) +} + func (f *FakeKubeClient) CreateFakeImage(image v1alpha1.Image) error { u, err := kubeclient.EncodeUnstructured(image) if err != nil { diff --git a/tools/doc/generate-doc-image-status.go b/tools/doc/generate-doc-crd-image.go similarity index 80% rename from tools/doc/generate-doc-image-status.go rename to tools/doc/generate-doc-crd-image.go index 6e0e4d2..e1eef05 100644 --- a/tools/doc/generate-doc-image-status.go +++ b/tools/doc/generate-doc-crd-image.go @@ -1,17 +1,19 @@ package main import ( + "fmt" "go/ast" "go/parser" "go/token" "html/template" "log" "os" + "strings" "github.com/fbiville/markdown-table-formatter/pkg/markdown" ) -func generateDocImageStatus() { +func generateDocImageCRD() { tmplFuncs := template.FuncMap{ "imageStatusLastSync": func() string { fset := token.NewFileSet() @@ -27,7 +29,7 @@ func generateDocImageStatus() { for _, spec := range decl.(*ast.GenDecl).Specs { if _, ok := spec.(*ast.ValueSpec); ok { for _, ident := range spec.(*ast.ValueSpec).Names { - imgStatusSlice = append(imgStatusSlice, []string{ident.Name, ident.Obj.Decl.(*ast.ValueSpec).Doc.Text()}) + imgStatusSlice = append(imgStatusSlice, []string{fmt.Sprintf("`%s`", ident.Name), strings.TrimSuffix(ident.Obj.Decl.(*ast.ValueSpec).Doc.Text(), "\n")}) } } } @@ -38,7 +40,7 @@ func generateDocImageStatus() { prettyPrintedTable, err := markdown. NewTableFormatterBuilder(). WithAlphabeticalSortIn(markdown.ASCENDING_ORDER). - Build("Last-Sync", "Description"). + Build("Last-Sync state", "Description"). Format(imgStatusSlice) if err != nil { panic(err) @@ -49,7 +51,7 @@ func generateDocImageStatus() { } // os read file - file, err := os.ReadFile("docs/advanced/image-status.md.tmpl") + file, err := os.ReadFile("docs/crd/image.md.tmpl") if err != nil { log.Default().Printf("Failed to open file: %v", err) os.Exit(1) @@ -58,7 +60,7 @@ func generateDocImageStatus() { tmpl := template.Must(template.New("image-status").Funcs(tmplFuncs).Parse(string(file))) // write template to file - f, err := os.Create("docs/advanced/image-status.md") + f, err := os.Create("docs/crd/image.md") defer f.Close() if err != nil { log.Default().Printf("Failed to create file: %v", err) diff --git a/tools/doc/generate-doc.go b/tools/doc/generate-doc.go index 8b9ca22..012e960 100644 --- a/tools/doc/generate-doc.go +++ b/tools/doc/generate-doc.go @@ -5,6 +5,6 @@ import "log" func main() { log.Default().Printf("Generating metrics documentation") generateDocMetrics() - log.Default().Printf("Generating image status documentation") - generateDocImageStatus() + log.Default().Printf("Generating image CRD documentation") + generateDocImageCRD() } diff --git a/tools/env-dev/pod-operator.yaml b/tools/env-dev/pod-operator.yaml new file mode 100644 index 0000000..952d3ee --- /dev/null +++ b/tools/env-dev/pod-operator.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: Pod +metadata: + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: kimup-operator + app.kubernetes.io/name: kube-image-updater + name: kimup-operator + namespace: kimup-operator +spec: + containers: + - image: kurun://cmd/operator/main.go + name: operator + ports: + - containerPort: 9443 + name: webhook-server + protocol: TCP + - containerPort: 8080 + name: metrics + protocol: TCP + volumeMounts: + - mountPath: /tmp/k8s-webhook-server/serving-certs + name: webhook-cert + readOnly: true + serviceAccount: kimup + serviceAccountName: kimup + volumes: + - name: webhook-cert + secret: + defaultMode: 420 + secretName: kimup-webhook-serving-cert \ No newline at end of file diff --git a/tools/env-dev/whoami-deployment.yaml b/tools/env-dev/whoami-deployment.yaml new file mode 100644 index 0000000..1117a5d --- /dev/null +++ b/tools/env-dev/whoami-deployment.yaml @@ -0,0 +1,50 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: dev-kube-image-updater + annotations: + kimup.cloudavenue.io/enabled: "true" +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whoami + namespace: dev-kube-image-updater + labels: + app.kubernetes.io/name: whoami +spec: + replicas: 3 + selector: + matchLabels: + app.kubernetes.io/name: whoami + template: + metadata: + # annotations: + # kimup.cloudavenue.io/enabled: "false" + labels: + app.kubernetes.io/name: whoami + spec: + containers: + - name: whoami + image: "traefik/whoami:latest" + imagePullPolicy: IfNotPresent + +--- +apiVersion: kimup.cloudavenue.io/v1alpha1 +kind: Image +metadata: + labels: + app.kubernetes.io/name: whoami + name: traefik-whoami + namespace: dev-kube-image-updater +spec: + image: traefik/whoami + baseTag: v1.10.0 + triggers: + - type: crontab + value: "00 00 */12 * * *" + rules: + - name: Automatic update semver patch + type: semver-patch + actions: + - type: apply diff --git a/tools/mutator/main.go b/tools/mutator/main.go new file mode 100644 index 0000000..44aeb9e --- /dev/null +++ b/tools/mutator/main.go @@ -0,0 +1,49 @@ +// This tool is used to create mutating configuration for the admission controller webhook. + +package main + +import ( + "context" + "flag" + "os" + + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + + "github.com/orange-cloudavenue/kube-image-updater/internal/kubeclient" + "github.com/orange-cloudavenue/kube-image-updater/internal/log" + "github.com/orange-cloudavenue/kube-image-updater/internal/models" +) + +func main() { + kubeconfig := flag.Lookup("kubeconfig").Value.String() + + if kubeconfig == "" { + // Get home directory + home, err := os.UserHomeDir() + if err != nil { + panic(err) + } + + kubeconfig = home + "/.kube/config" + } + + // kubernetes golang library provide flag "kubeconfig" to specify the path to the kubeconfig file + k, err := kubeclient.New(kubeconfig, kubeclient.ComponentOperator) + if err != nil { + log.WithError(err).Panic("Error creating kubeclient") + } + + _, err = k.Mutator().CreateOrUpdateMutatingConfiguration( + context.Background(), + models.MutatorMutatingWebhookConfigurationName, + admissionregistrationv1.ServiceReference{ + Name: "mutator", + Namespace: "kimup-operator", + Path: &models.MutatorWebhookPathMutateImageTag, + }, + admissionregistrationv1.Fail, + ) + if err != nil { + log.WithError(err).Panic("Error creating or updating mutating configuration") + } +} From c0295f3bad597252a5362c5f208c618cfbb756ee Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Tue, 29 Oct 2024 12:02:28 +0100 Subject: [PATCH 2/5] chore: try netlify From 4b89d0dd16f94982be3ac1d1408908da9989f390 Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Tue, 29 Oct 2024 12:04:47 +0100 Subject: [PATCH 3/5] ci: fix mkdocs settings --- mkdocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index b0bf789..d05e5b0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -69,7 +69,6 @@ repo_name: orange-cloudavenue/kube-image-updater plugins: - search - - mkdocs-video - macros: on_undefined: silent From face30fcb2d75a0f8fe82da1391a603f4c2d5164 Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Tue, 29 Oct 2024 12:10:42 +0100 Subject: [PATCH 4/5] chore: try netlify From 97911ac694abcc85d680dc7aee2b315f13b4bb1f Mon Sep 17 00:00:00 2001 From: Mickael Stanislas Date: Tue, 29 Oct 2024 12:16:16 +0100 Subject: [PATCH 5/5] chore: try netlify