diff --git a/.gitignore b/.gitignore index 90fa1bb42e..0fb0171579 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ cmd/cli/kubectl-kyverno/kubectl-kyverno cmd/initContainer/kyvernopre cmd/kyverno/kyverno cmd/cleanup-controller/cleanup-controller +cmd/reports-controller/reports-controller /release .DS_Store .tools diff --git a/Makefile b/Makefile index 3c45f96bb7..b7d3890223 100644 --- a/Makefile +++ b/Makefile @@ -26,10 +26,12 @@ KYVERNOPRE_IMAGE := kyvernopre KYVERNO_IMAGE := kyverno CLI_IMAGE := kyverno-cli CLEANUP_IMAGE := cleanup-controller +REPORTS_IMAGE := reports-controller REPO_KYVERNOPRE := $(REGISTRY)/$(REPO)/$(KYVERNOPRE_IMAGE) REPO_KYVERNO := $(REGISTRY)/$(REPO)/$(KYVERNO_IMAGE) REPO_CLI := $(REGISTRY)/$(REPO)/$(CLI_IMAGE) REPO_CLEANUP := $(REGISTRY)/$(REPO)/$(CLEANUP_IMAGE) +REPO_REPORTS := $(REGISTRY)/$(REPO)/$(REPORTS_IMAGE) USE_CONFIG ?= standard ######### @@ -136,10 +138,12 @@ KYVERNO_DIR := $(CMD_DIR)/kyverno KYVERNOPRE_DIR := $(CMD_DIR)/initContainer CLI_DIR := $(CMD_DIR)/cli/kubectl-kyverno CLEANUP_DIR := $(CMD_DIR)/cleanup-controller +REPORTS_DIR := $(CMD_DIR)/reports-controller KYVERNO_BIN := $(KYVERNO_DIR)/kyverno KYVERNOPRE_BIN := $(KYVERNOPRE_DIR)/kyvernopre CLI_BIN := $(CLI_DIR)/kubectl-kyverno CLEANUP_BIN := $(CLEANUP_DIR)/cleanup-controller +REPORTS_BIN := $(REPORTS_DIR)/reports-controller PACKAGE ?= github.com/kyverno/kyverno CGO_ENABLED ?= 0 LD_FLAGS = "-s -w -X $(PACKAGE)/pkg/version.BuildVersion=$(GIT_VERSION) -X $(PACKAGE)/pkg/version.BuildHash=$(GIT_HASH) -X $(PACKAGE)/pkg/version.BuildTime=$(TIMESTAMP)" @@ -199,6 +203,10 @@ $(CLEANUP_BIN): fmt vet @echo Build cleanup controller binary... >&2 @CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) go build -o $(CLEANUP_BIN) -ldflags=$(LD_FLAGS) $(CLEANUP_DIR) +$(REPORTS_BIN): fmt vet + @echo Build reports controller binary... >&2 + @CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) go build -o $(REPORTS_BIN) -ldflags=$(LD_FLAGS) $(REPORTS_DIR) + .PHONY: build-kyvernopre build-kyvernopre: $(KYVERNOPRE_BIN) ## Build kyvernopre binary @@ -211,7 +219,10 @@ build-cli: $(CLI_BIN) ## Build cli binary .PHONY: build-cleanup-controller build-cleanup-controller: $(CLEANUP_BIN) ## Build cleanup controller binary -build-all: build-kyvernopre build-kyverno build-cli build-cleanup-controller ## Build all binaries +.PHONY: build-reports-controller +build-reports-controller: $(REPORTS_BIN) ## Build reports controller binary + +build-all: build-kyvernopre build-kyverno build-cli build-cleanup-controller build-reports-controller ## Build all binaries ############## # BUILD (KO) # @@ -242,8 +253,13 @@ ko-build-cleanup-controller: $(KO) ## Build cleanup controller local image (with @echo Build cleanup controller local image with ko... >&2 @LD_FLAGS=$(LD_FLAGS_DEV) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=ko.local $(KO) build $(CLEANUP_DIR) --preserve-import-paths --tags=$(KO_TAGS_DEV) --platform=$(LOCAL_PLATFORM) +.PHONY: ko-build-reports-controller +ko-build-reports-controller: $(KO) ## Build reports controller local image (with ko) + @echo Build reports controller local image with ko... >&2 + @LD_FLAGS=$(LD_FLAGS_DEV) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=ko.local $(KO) build $(REPORTS_DIR) --preserve-import-paths --tags=$(KO_TAGS_DEV) --platform=$(LOCAL_PLATFORM) + .PHONY: ko-build-all -ko-build-all: ko-build-kyvernopre ko-build-kyverno ko-build-cli ko-build-cleanup-controller ## Build all local images (with ko) +ko-build-all: ko-build-kyvernopre ko-build-kyverno ko-build-cli ko-build-cleanup-controller ko-build-reports-controller ## Build all local images (with ko) ################ # PUBLISH (KO) # @@ -253,6 +269,7 @@ REGISTRY_USERNAME ?= dummy KO_KYVERNOPRE_IMAGE := ko.local/github.com/kyverno/kyverno/cmd/initcontainer KO_KYVERNO_IMAGE := ko.local/github.com/kyverno/kyverno/cmd/kyverno KO_CLEANUP_IMAGE := ko.local/github.com/kyverno/kyverno/cmd/cleanup-controller +KO_REPORTS_IMAGE := ko.local/github.com/kyverno/kyverno/cmd/reports-controller .PHONY: ko-login ko-login: $(KO) @@ -274,6 +291,10 @@ ko-publish-cli: ko-login ## Build and publish cli image (with ko) ko-publish-cleanup-controller: ko-login ## Build and publish cleanup controller image (with ko) @LD_FLAGS=$(LD_FLAGS) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(REPO_CLEANUP) $(KO) build $(CLEANUP_DIR) --bare --tags=$(KO_TAGS) --platform=$(PLATFORMS) +.PHONY: ko-publish-reports-controller +ko-publish-reports-controller: ko-login ## Build and publish reports controller image (with ko) + @LD_FLAGS=$(LD_FLAGS) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(REPO_REPORTS) $(KO) build $(REPORTS_DIR) --bare --tags=$(KO_TAGS) --platform=$(PLATFORMS) + .PHONY: ko-publish-kyvernopre-dev ko-publish-kyvernopre-dev: ko-login ## Build and publish kyvernopre dev image (with ko) @LD_FLAGS=$(LD_FLAGS_DEV) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(REPO_KYVERNOPRE) $(KO) build $(KYVERNOPRE_DIR) --bare --tags=$(KO_TAGS_DEV) --platform=$(PLATFORMS) @@ -290,11 +311,15 @@ ko-publish-cli-dev: ko-login ## Build and publish cli dev image (with ko) ko-publish-cleanup-controller-dev: ko-login ## Build and publish cleanup controller dev image (with ko) @LD_FLAGS=$(LD_FLAGS_DEV) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(REPO_CLEANUP) $(KO) build $(CLEANUP_DIR) --bare --tags=$(KO_TAGS_DEV) --platform=$(PLATFORMS) +.PHONY: ko-publish-reports-controller-dev +ko-publish-reports-controller-dev: ko-login ## Build and publish reports controller dev image (with ko) + @LD_FLAGS=$(LD_FLAGS_DEV) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(REPO_REPORTS) $(KO) build $(REPORTS_DIR) --bare --tags=$(KO_TAGS_DEV) --platform=$(PLATFORMS) + .PHONY: ko-publish-all -ko-publish-all: ko-publish-kyvernopre ko-publish-kyverno ko-publish-cli ko-publish-cleanup-controller ## Build and publish all images (with ko) +ko-publish-all: ko-publish-kyvernopre ko-publish-kyverno ko-publish-cli ko-publish-cleanup-controller ko-publish-reports-controller ## Build and publish all images (with ko) .PHONY: ko-publish-all-dev -ko-publish-all-dev: ko-publish-kyvernopre-dev ko-publish-kyverno-dev ko-publish-cli-dev ko-publish-cleanup-controller-dev ## Build and publish all dev images (with ko) +ko-publish-all-dev: ko-publish-kyvernopre-dev ko-publish-kyverno-dev ko-publish-cli-dev ko-publish-cleanup-controller-dev ko-publish-reports-controller-dev ## Build and publish all dev images (with ko) ################# # BUILD (IMAGE) # @@ -303,6 +328,7 @@ ko-publish-all-dev: ko-publish-kyvernopre-dev ko-publish-kyverno-dev ko-publish- LOCAL_KYVERNOPRE_IMAGE := $($(shell echo $(BUILD_WITH) | tr '[:lower:]' '[:upper:]')_KYVERNOPRE_IMAGE) LOCAL_KYVERNO_IMAGE := $($(shell echo $(BUILD_WITH) | tr '[:lower:]' '[:upper:]')_KYVERNO_IMAGE) LOCAL_CLEANUP_IMAGE := $($(shell echo $(BUILD_WITH) | tr '[:lower:]' '[:upper:]')_CLEANUP_IMAGE) +LOCAL_REPORTS_IMAGE := $($(shell echo $(BUILD_WITH) | tr '[:lower:]' '[:upper:]')_REPORTS_IMAGE) .PHONY: image-build-kyvernopre image-build-kyvernopre: $(BUILD_WITH)-build-kyvernopre @@ -316,6 +342,9 @@ image-build-cli: $(BUILD_WITH)-build-cli .PHONY: image-build-cleanup-controller image-build-cleanup-controller: $(BUILD_WITH)-build-cleanup-controller +.PHONY: image-build-reports-controller +image-build-reports-controller: $(BUILD_WITH)-build-reports-controller + .PHONY: image-build-all image-build-all: $(BUILD_WITH)-build-all @@ -726,8 +755,13 @@ kind-load-cleanup-controller: $(KIND) image-build-cleanup-controller ## Build cl @echo Load cleanup controller image... >&2 @$(KIND) load docker-image --name $(KIND_NAME) $(LOCAL_CLEANUP_IMAGE):$(IMAGE_TAG_DEV) +.PHONY: kind-load-reports-controller +kind-load-reports-controller: $(KIND) image-build-reports-controller ## Build reports controller image and load it in kind cluster + @echo Load reports controller image... >&2 + @$(KIND) load docker-image --name $(KIND_NAME) $(LOCAL_REPORTS_IMAGE):$(IMAGE_TAG_DEV) + .PHONY: kind-load-all -kind-load-all: kind-load-kyvernopre kind-load-kyverno kind-load-cleanup-controller ## Build images and load them in kind cluster +kind-load-all: kind-load-kyvernopre kind-load-kyverno kind-load-cleanup-controller kind-load-reports-controller ## Build images and load them in kind cluster .PHONY: kind-deploy-kyverno kind-deploy-kyverno: $(HELM) kind-load-all ## Build images, load them in kind cluster and deploy kyverno helm chart diff --git a/cmd/reports-controller/main.go b/cmd/reports-controller/main.go new file mode 100644 index 0000000000..b1742ca883 --- /dev/null +++ b/cmd/reports-controller/main.go @@ -0,0 +1,363 @@ +package main + +import ( + "context" + "errors" + "flag" + "os" + "strings" + "sync" + "time" + + "github.com/go-logr/logr" + "github.com/kyverno/kyverno/cmd/internal" + "github.com/kyverno/kyverno/pkg/client/clientset/versioned" + kyvernoinformer "github.com/kyverno/kyverno/pkg/client/informers/externalversions" + "github.com/kyverno/kyverno/pkg/clients/dclient" + dynamicclient "github.com/kyverno/kyverno/pkg/clients/dynamic" + kubeclient "github.com/kyverno/kyverno/pkg/clients/kube" + kyvernoclient "github.com/kyverno/kyverno/pkg/clients/kyverno" + metadataclient "github.com/kyverno/kyverno/pkg/clients/metadata" + "github.com/kyverno/kyverno/pkg/config" + admissionreportcontroller "github.com/kyverno/kyverno/pkg/controllers/report/admission" + aggregatereportcontroller "github.com/kyverno/kyverno/pkg/controllers/report/aggregate" + backgroundscancontroller "github.com/kyverno/kyverno/pkg/controllers/report/background" + resourcereportcontroller "github.com/kyverno/kyverno/pkg/controllers/report/resource" + "github.com/kyverno/kyverno/pkg/cosign" + "github.com/kyverno/kyverno/pkg/engine/context/resolvers" + "github.com/kyverno/kyverno/pkg/event" + "github.com/kyverno/kyverno/pkg/leaderelection" + "github.com/kyverno/kyverno/pkg/logging" + "github.com/kyverno/kyverno/pkg/metrics" + "github.com/kyverno/kyverno/pkg/registryclient" + kubeinformers "k8s.io/client-go/informers" + corev1listers "k8s.io/client-go/listers/core/v1" + metadatainformers "k8s.io/client-go/metadata/metadatainformer" + kyamlopenapi "sigs.k8s.io/kustomize/kyaml/openapi" +) + +const ( + resyncPeriod = 15 * time.Minute +) + +func setupRegistryClient(ctx context.Context, logger logr.Logger, lister corev1listers.SecretNamespaceLister, imagePullSecrets string, allowInsecureRegistry bool) (registryclient.Client, error) { + logger = logger.WithName("registry-client") + logger.Info("setup registry client...", "secrets", imagePullSecrets, "insecure", allowInsecureRegistry) + registryOptions := []registryclient.Option{ + registryclient.WithTracing(), + } + secrets := strings.Split(imagePullSecrets, ",") + if imagePullSecrets != "" && len(secrets) > 0 { + registryOptions = append(registryOptions, registryclient.WithKeychainPullSecrets(ctx, lister, secrets...)) + } + if allowInsecureRegistry { + registryOptions = append(registryOptions, registryclient.WithAllowInsecureRegistry()) + } + return registryclient.New(registryOptions...) +} + +func setupCosign(logger logr.Logger, imageSignatureRepository string) { + logger = logger.WithName("cosign") + logger.Info("setup cosign...", "repository", imageSignatureRepository) + if imageSignatureRepository != "" { + cosign.ImageSignatureRepository = imageSignatureRepository + } +} + +func createReportControllers( + backgroundScan bool, + admissionReports bool, + reportsChunkSize int, + backgroundScanWorkers int, + client dclient.Interface, + kyvernoClient versioned.Interface, + rclient registryclient.Client, + metadataFactory metadatainformers.SharedInformerFactory, + kubeInformer kubeinformers.SharedInformerFactory, + kyvernoInformer kyvernoinformer.SharedInformerFactory, + configMapResolver resolvers.ConfigmapResolver, + backgroundScanInterval time.Duration, + configuration config.Configuration, + eventGenerator event.Interface, +) ([]internal.Controller, func(context.Context) error) { + var ctrls []internal.Controller + var warmups []func(context.Context) error + kyvernoV1 := kyvernoInformer.Kyverno().V1() + kyvernoV2Alpha1 := kyvernoInformer.Kyverno().V2alpha1() + if backgroundScan || admissionReports { + resourceReportController := resourcereportcontroller.NewController( + client, + kyvernoV1.Policies(), + kyvernoV1.ClusterPolicies(), + ) + warmups = append(warmups, func(ctx context.Context) error { + return resourceReportController.Warmup(ctx) + }) + ctrls = append(ctrls, internal.NewController( + resourcereportcontroller.ControllerName, + resourceReportController, + resourcereportcontroller.Workers, + )) + ctrls = append(ctrls, internal.NewController( + aggregatereportcontroller.ControllerName, + aggregatereportcontroller.NewController( + kyvernoClient, + metadataFactory, + kyvernoV1.Policies(), + kyvernoV1.ClusterPolicies(), + resourceReportController, + reportsChunkSize, + ), + aggregatereportcontroller.Workers, + )) + if admissionReports { + ctrls = append(ctrls, internal.NewController( + admissionreportcontroller.ControllerName, + admissionreportcontroller.NewController( + kyvernoClient, + metadataFactory, + resourceReportController, + ), + admissionreportcontroller.Workers, + )) + } + if backgroundScan { + ctrls = append(ctrls, internal.NewController( + backgroundscancontroller.ControllerName, + backgroundscancontroller.NewController( + client, + kyvernoClient, + rclient, + metadataFactory, + kyvernoV1.Policies(), + kyvernoV1.ClusterPolicies(), + kubeInformer.Core().V1().Namespaces(), + kyvernoV2Alpha1.PolicyExceptions(), + resourceReportController, + configMapResolver, + backgroundScanInterval, + configuration, + eventGenerator, + ), + backgroundScanWorkers, + )) + } + } + return ctrls, func(ctx context.Context) error { + for _, warmup := range warmups { + if err := warmup(ctx); err != nil { + return err + } + } + return nil + } +} + +func createrLeaderControllers( + backgroundScan bool, + admissionReports bool, + reportsChunkSize int, + backgroundScanWorkers int, + kubeInformer kubeinformers.SharedInformerFactory, + kyvernoInformer kyvernoinformer.SharedInformerFactory, + metadataInformer metadatainformers.SharedInformerFactory, + kyvernoClient versioned.Interface, + dynamicClient dclient.Interface, + rclient registryclient.Client, + configuration config.Configuration, + eventGenerator event.Interface, + configMapResolver resolvers.ConfigmapResolver, + backgroundScanInterval time.Duration, +) ([]internal.Controller, func(context.Context) error, error) { + reportControllers, warmup := createReportControllers( + backgroundScan, + admissionReports, + reportsChunkSize, + backgroundScanWorkers, + dynamicClient, + kyvernoClient, + rclient, + metadataInformer, + kubeInformer, + kyvernoInformer, + configMapResolver, + backgroundScanInterval, + configuration, + eventGenerator, + ) + return reportControllers, warmup, nil +} + +func main() { + var ( + leaderElectionRetryPeriod time.Duration + imagePullSecrets string + imageSignatureRepository string + allowInsecureRegistry bool + backgroundScan bool + admissionReports bool + reportsChunkSize int + backgroundScanWorkers int + backgroundScanInterval time.Duration + maxQueuedEvents int + ) + flagset := flag.NewFlagSet("reports-controller", flag.ExitOnError) + flagset.DurationVar(&leaderElectionRetryPeriod, "leaderElectionRetryPeriod", leaderelection.DefaultRetryPeriod, "Configure leader election retry period.") + flagset.StringVar(&imagePullSecrets, "imagePullSecrets", "", "Secret resource names for image registry access credentials.") + flagset.StringVar(&imageSignatureRepository, "imageSignatureRepository", "", "Alternate repository for image signatures. Can be overridden per rule via `verifyImages.Repository`.") + flagset.BoolVar(&allowInsecureRegistry, "allowInsecureRegistry", false, "Whether to allow insecure connections to registries. Don't use this for anything but testing.") + flagset.BoolVar(&backgroundScan, "backgroundScan", true, "Enable or disable backgound scan.") + flagset.BoolVar(&admissionReports, "admissionReports", true, "Enable or disable admission reports.") + flagset.IntVar(&reportsChunkSize, "reportsChunkSize", 1000, "Max number of results in generated reports, reports will be split accordingly if there are more results to be stored.") + flagset.IntVar(&backgroundScanWorkers, "backgroundScanWorkers", backgroundscancontroller.Workers, "Configure the number of background scan workers.") + flagset.DurationVar(&backgroundScanInterval, "backgroundScanInterval", time.Hour, "Configure background scan interval.") + flagset.IntVar(&maxQueuedEvents, "maxQueuedEvents", 1000, "Maximum events to be queued.") + // config + appConfig := internal.NewConfiguration( + internal.WithProfiling(), + internal.WithMetrics(), + internal.WithTracing(), + internal.WithKubeconfig(), + internal.WithFlagSets(flagset), + ) + // parse flags + internal.ParseFlags(appConfig) + // setup logger + // show version + // start profiling + // setup signals + // setup maxprocs + // setup metrics + ctx, logger, metricsConfig, sdown := internal.Setup() + defer sdown() + // create instrumented clients + kubeClient := internal.CreateKubernetesClient(logger, kubeclient.WithMetrics(metricsConfig, metrics.KubeClient), kubeclient.WithTracing()) + leaderElectionClient := internal.CreateKubernetesClient(logger, kubeclient.WithMetrics(metricsConfig, metrics.KubeClient), kubeclient.WithTracing()) + kyvernoClient := internal.CreateKyvernoClient(logger, kyvernoclient.WithMetrics(metricsConfig, metrics.KyvernoClient), kyvernoclient.WithTracing()) + metadataClient := internal.CreateMetadataClient(logger, metadataclient.WithMetrics(metricsConfig, metrics.KyvernoClient), metadataclient.WithTracing()) + dynamicClient := internal.CreateDynamicClient(logger, dynamicclient.WithMetrics(metricsConfig, metrics.KyvernoClient), dynamicclient.WithTracing()) + dClient, err := dclient.NewClient(ctx, dynamicClient, kubeClient, 15*time.Minute) + if err != nil { + logger.Error(err, "failed to create dynamic client") + os.Exit(1) + } + // THIS IS AN UGLY FIX + // ELSE KYAML IS NOT THREAD SAFE + kyamlopenapi.Schema() + // informer factories + kubeKyvernoInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, resyncPeriod, kubeinformers.WithNamespace(config.KyvernoNamespace())) + kyvernoInformer := kyvernoinformer.NewSharedInformerFactory(kyvernoClient, resyncPeriod) + cacheInformer, err := resolvers.GetCacheInformerFactory(kubeClient, resyncPeriod) + if err != nil { + logger.Error(err, "failed to create cache informer factory") + os.Exit(1) + } + secretLister := kubeKyvernoInformer.Core().V1().Secrets().Lister().Secrets(config.KyvernoNamespace()) + // setup registry client + rclient, err := setupRegistryClient(ctx, logger, secretLister, imagePullSecrets, allowInsecureRegistry) + if err != nil { + logger.Error(err, "failed to setup registry client") + os.Exit(1) + } + // setup cosign + setupCosign(logger, imageSignatureRepository) + informerBasedResolver, err := resolvers.NewInformerBasedResolver(cacheInformer.Core().V1().ConfigMaps().Lister()) + if err != nil { + logger.Error(err, "failed to create informer based resolver") + os.Exit(1) + } + clientBasedResolver, err := resolvers.NewClientBasedResolver(kubeClient) + if err != nil { + logger.Error(err, "failed to create client based resolver") + os.Exit(1) + } + configMapResolver, err := resolvers.NewResolverChain(informerBasedResolver, clientBasedResolver) + if err != nil { + logger.Error(err, "failed to create config map resolver") + os.Exit(1) + } + configuration, err := config.NewConfiguration(kubeClient) + if err != nil { + logger.Error(err, "failed to initialize configuration") + os.Exit(1) + } + eventGenerator := event.NewEventGenerator( + dClient, + kyvernoInformer.Kyverno().V1().ClusterPolicies(), + kyvernoInformer.Kyverno().V1().Policies(), + maxQueuedEvents, + logging.WithName("EventGenerator"), + ) + // setup leader election + le, err := leaderelection.New( + logger.WithName("leader-election"), + "kyverno-reports-controller", + config.KyvernoNamespace(), + leaderElectionClient, + config.KyvernoPodName(), + leaderElectionRetryPeriod, + func(ctx context.Context) { + logger := logger.WithName("leader") + // create leader factories + kubeInformer := kubeinformers.NewSharedInformerFactory(kubeClient, resyncPeriod) + kubeKyvernoInformer := kubeinformers.NewSharedInformerFactoryWithOptions(kubeClient, resyncPeriod, kubeinformers.WithNamespace(config.KyvernoNamespace())) + kyvernoInformer := kyvernoinformer.NewSharedInformerFactory(kyvernoClient, resyncPeriod) + metadataInformer := metadatainformers.NewSharedInformerFactory(metadataClient, 15*time.Minute) + // create leader controllers + leaderControllers, warmup, err := createrLeaderControllers( + backgroundScan, + admissionReports, + reportsChunkSize, + backgroundScanWorkers, + kubeInformer, + kyvernoInformer, + metadataInformer, + kyvernoClient, + dClient, + rclient, + configuration, + eventGenerator, + configMapResolver, + backgroundScanInterval, + ) + if err != nil { + logger.Error(err, "failed to create leader controllers") + os.Exit(1) + } + // start informers and wait for cache sync + if !internal.StartInformersAndWaitForCacheSync(ctx, kyvernoInformer, kubeInformer, kubeKyvernoInformer) { + logger.Error(errors.New("failed to wait for cache sync"), "failed to wait for cache sync") + os.Exit(1) + } + internal.StartInformers(ctx, metadataInformer) + if !internal.CheckCacheSync(metadataInformer.WaitForCacheSync(ctx.Done())) { + logger.Error(errors.New("failed to wait for cache sync"), "failed to wait for cache sync") + os.Exit(1) + } + if err := warmup(ctx); err != nil { + logger.Error(err, "failed to run warmup") + os.Exit(1) + } + // start leader controllers + var wg sync.WaitGroup + for _, controller := range leaderControllers { + controller.Run(ctx, logger.WithName("controllers"), &wg) + } + // wait all controllers shut down + wg.Wait() + }, + nil, + ) + if err != nil { + logger.Error(err, "failed to initialize leader election") + os.Exit(1) + } + for { + select { + case <-ctx.Done(): + return + default: + le.Run(ctx) + } + } +}