diff --git a/charts/gardener-extension-admission-calico/charts/runtime/templates/_helpers.tpl b/charts/gardener-extension-admission-calico/charts/runtime/templates/_helpers.tpl index f7ffa2d60..38dd6ae79 100644 --- a/charts/gardener-extension-admission-calico/charts/runtime/templates/_helpers.tpl +++ b/charts/gardener-extension-admission-calico/charts/runtime/templates/_helpers.tpl @@ -21,3 +21,7 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- printf "%s:%s" .repository .tag }} {{- end }} {{- end }} + +{{- define "leaderelectionid" -}} +gardener-extension-admission-calico +{{- end -}} diff --git a/charts/gardener-extension-admission-calico/charts/runtime/templates/deplyoment.yaml b/charts/gardener-extension-admission-calico/charts/runtime/templates/deplyoment.yaml index 80a0e83a0..132c312c9 100644 --- a/charts/gardener-extension-admission-calico/charts/runtime/templates/deplyoment.yaml +++ b/charts/gardener-extension-admission-calico/charts/runtime/templates/deplyoment.yaml @@ -48,6 +48,7 @@ spec: - --kubeconfig={{ required ".Values.global.projectedKubeconfig.baseMountPath is required" .Values.global.projectedKubeconfig.baseMountPath }}/kubeconfig {{- end }} - --health-bind-address=:{{ .Values.global.healthPort }} + - --leader-election-id={{ include "leaderelectionid" . }} {{- if .Values.global.virtualGarden.enabled }} env: - name: SOURCE_CLUSTER diff --git a/charts/gardener-extension-admission-calico/charts/runtime/templates/rbac.yaml b/charts/gardener-extension-admission-calico/charts/runtime/templates/rbac.yaml index 3acffa702..86ef3ebba 100644 --- a/charts/gardener-extension-admission-calico/charts/runtime/templates/rbac.yaml +++ b/charts/gardener-extension-admission-calico/charts/runtime/templates/rbac.yaml @@ -18,6 +18,32 @@ rules: - update - patch - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - list + - watch +- apiGroups: + - coordination.k8s.io + resources: + - leases + resourceNames: + - {{ include "leaderelectionid" . }} + verbs: + - update + - get +- apiGroups: + - "" + - events.k8s.io + resources: + - events + verbs: + - create + - patch + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding diff --git a/cmd/gardener-extension-admission-calico/app/app.go b/cmd/gardener-extension-admission-calico/app/app.go index 889cc9f0e..498a24b08 100644 --- a/cmd/gardener-extension-admission-calico/app/app.go +++ b/cmd/gardener-extension-admission-calico/app/app.go @@ -27,11 +27,14 @@ import ( gardenerhealthz "github.com/gardener/gardener/pkg/healthz" "github.com/gardener/gardener/pkg/logger" "github.com/spf13/cobra" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" componentbaseconfig "k8s.io/component-base/config" "k8s.io/component-base/version" "k8s.io/component-base/version/verflag" "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/cluster" "sigs.k8s.io/controller-runtime/pkg/healthz" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -50,9 +53,12 @@ func NewControllerManagerCommand(ctx context.Context) *cobra.Command { var ( restOpts = &controllercmd.RESTOptions{} mgrOpts = &controllercmd.ManagerOptions{ - WebhookServerPort: 443, - HealthBindAddress: ":8081", - WebhookCertDir: "/tmp/admission-calico-cert", + LeaderElection: true, + LeaderElectionID: controllercmd.LeaderElectionNameID(Name), + LeaderElectionNamespace: os.Getenv("LEADER_ELECTION_NAMESPACE"), + WebhookServerPort: 443, + HealthBindAddress: ":8081", + WebhookCertDir: "/tmp/admission-calico-cert", } // options for the webhook server webhookServerOptions = &webhookcmd.ServerOptions{ @@ -60,7 +66,7 @@ func NewControllerManagerCommand(ctx context.Context) *cobra.Command { } webhookSwitches = admissioncmd.GardenWebhookSwitchOptions() webhookOptions = webhookcmd.NewAddToManagerOptions( - "admission-calico", + Name, "", nil, webhookServerOptions, @@ -96,16 +102,7 @@ func NewControllerManagerCommand(ctx context.Context) *cobra.Command { Burst: 130, }, restOpts.Completed().Config) - mgr, err := manager.New(restOpts.Completed().Config, mgrOpts.Completed().Options()) - if err != nil { - return fmt.Errorf("could not instantiate manager: %w", err) - } - - install.Install(mgr.GetScheme()) - - if err := calicoinstall.AddToScheme(mgr.GetScheme()); err != nil { - return fmt.Errorf("could not update manager scheme: %w", err) - } + managerOptions := mgrOpts.Completed().Options() // Operators can enable the source cluster option via SOURCE_CLUSTER environment variable. // In-cluster config will be used if no SOURCE_KUBECONFIG is specified. @@ -113,15 +110,38 @@ func NewControllerManagerCommand(ctx context.Context) *cobra.Command { // The source cluster is for instance used by Gardener's certificate controller, to maintain certificate // secrets in a different cluster ('runtime-garden') than the cluster where the webhook configurations // are maintained ('virtual-garden'). - var sourceCluster cluster.Cluster + var sourceClusterConfig *rest.Config if sourceClusterEnabled := os.Getenv("SOURCE_CLUSTER"); sourceClusterEnabled != "" { - log.Info("Configuring source cluster option") - config, err := clientcmd.BuildConfigFromFlags("", os.Getenv("SOURCE_KUBECONFIG")) + var err error + sourceClusterConfig, err = clientcmd.BuildConfigFromFlags("", os.Getenv("SOURCE_KUBECONFIG")) if err != nil { return err } + managerOptions.LeaderElectionConfig = sourceClusterConfig + } else { + // Restrict the cache for secrets to the configured namespace to avoid the need for cluster-wide list/watch permissions. + managerOptions.Cache = cache.Options{ + ByObject: map[client.Object]cache.ByObject{ + &corev1.Secret{}: {Namespaces: map[string]cache.Config{webhookOptions.Server.Completed().Namespace: {}}}, + }, + } + } - sourceCluster, err = cluster.New(config, func(opts *cluster.Options) { + mgr, err := manager.New(restOpts.Completed().Config, managerOptions) + if err != nil { + return fmt.Errorf("could not instantiate manager: %w", err) + } + + install.Install(mgr.GetScheme()) + + if err := calicoinstall.AddToScheme(mgr.GetScheme()); err != nil { + return fmt.Errorf("could not update manager scheme: %w", err) + } + + var sourceCluster cluster.Cluster + if sourceClusterConfig != nil { + log.Info("Configuring source cluster option") + sourceCluster, err = cluster.New(sourceClusterConfig, func(opts *cluster.Options) { opts.Logger = log opts.Cache.DefaultNamespaces = map[string]cache.Config{v1beta1constants.GardenNamespace: {}} })