github.com/ironcore-dev/gardener-extension-provider-ironcore@v0.3.2-0.20240314231816-8336447fb9a0/cmd/gardener-extension-admission-ironcore/app/app.go (about)

     1  // SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and IronCore contributors
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package app
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"os"
    10  
    11  	controllercmd "github.com/gardener/gardener/extensions/pkg/controller/cmd"
    12  	"github.com/gardener/gardener/extensions/pkg/util"
    13  	webhookcmd "github.com/gardener/gardener/extensions/pkg/webhook/cmd"
    14  	"github.com/gardener/gardener/pkg/apis/core/install"
    15  	v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
    16  	gardenerhealthz "github.com/gardener/gardener/pkg/healthz"
    17  	"github.com/spf13/cobra"
    18  	corev1 "k8s.io/api/core/v1"
    19  	"k8s.io/client-go/rest"
    20  	"k8s.io/client-go/tools/clientcmd"
    21  	componentbaseconfig "k8s.io/component-base/config"
    22  	"k8s.io/component-base/version/verflag"
    23  	"sigs.k8s.io/controller-runtime/pkg/cache"
    24  	"sigs.k8s.io/controller-runtime/pkg/client"
    25  	"sigs.k8s.io/controller-runtime/pkg/cluster"
    26  	"sigs.k8s.io/controller-runtime/pkg/healthz"
    27  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    28  	"sigs.k8s.io/controller-runtime/pkg/manager"
    29  
    30  	admissioncmd "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/admission/cmd"
    31  	ironcoreinstall "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/apis/ironcore/install"
    32  	providerironcore "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/ironcore"
    33  )
    34  
    35  // AdmissionName is the name of the admission component.
    36  const AdmissionName = "admission-ironcore"
    37  
    38  var log = logf.Log.WithName("gardener-extension-admission-ironcore")
    39  
    40  // NewAdmissionCommand creates a new command for running an ironcore gardener-extension-admission-ironcore webhook.
    41  func NewAdmissionCommand(ctx context.Context) *cobra.Command {
    42  	var (
    43  		restOpts = &controllercmd.RESTOptions{}
    44  		mgrOpts  = &controllercmd.ManagerOptions{
    45  			LeaderElection:          true,
    46  			LeaderElectionID:        controllercmd.LeaderElectionNameID(AdmissionName),
    47  			LeaderElectionNamespace: os.Getenv("LEADER_ELECTION_NAMESPACE"),
    48  			WebhookServerPort:       443,
    49  			MetricsBindAddress:      ":8080",
    50  			HealthBindAddress:       ":8081",
    51  			WebhookCertDir:          "/tmp/admission-aws-cert",
    52  		}
    53  		// options for the webhook server
    54  		webhookServerOptions = &webhookcmd.ServerOptions{
    55  			Namespace: os.Getenv("WEBHOOK_CONFIG_NAMESPACE"),
    56  		}
    57  		webhookSwitches = admissioncmd.GardenWebhookSwitchOptions()
    58  		webhookOptions  = webhookcmd.NewAddToManagerOptions(
    59  			AdmissionName,
    60  			"",
    61  			nil,
    62  			webhookServerOptions,
    63  			webhookSwitches,
    64  		)
    65  
    66  		aggOption = controllercmd.NewOptionAggregator(
    67  			restOpts,
    68  			mgrOpts,
    69  			webhookOptions,
    70  		)
    71  	)
    72  
    73  	cmd := &cobra.Command{
    74  		Use: fmt.Sprintf("admission-%s", providerironcore.Type),
    75  
    76  		RunE: func(cmd *cobra.Command, args []string) error {
    77  			verflag.PrintAndExitIfRequested()
    78  
    79  			if err := aggOption.Complete(); err != nil {
    80  				return fmt.Errorf("error completing options: %v", err)
    81  			}
    82  
    83  			util.ApplyClientConnectionConfigurationToRESTConfig(&componentbaseconfig.ClientConnectionConfiguration{
    84  				QPS:   100.0,
    85  				Burst: 130,
    86  			}, restOpts.Completed().Config)
    87  
    88  			managerOptions := mgrOpts.Completed().Options()
    89  
    90  			// Operators can enable the source cluster option via SOURCE_CLUSTER environment variable.
    91  			// In-cluster config will be used if no SOURCE_KUBECONFIG is specified.
    92  			//
    93  			// The source cluster is for instance used by Gardener's certificate controller, to maintain certificate
    94  			// secrets in a different cluster ('runtime-garden') than the cluster where the webhook configurations
    95  			// are maintained ('virtual-garden').
    96  			var sourceClusterConfig *rest.Config
    97  			if sourceClusterEnabled := os.Getenv("SOURCE_CLUSTER"); sourceClusterEnabled != "" {
    98  				log.Info("Configuring source cluster option")
    99  				var err error
   100  				sourceClusterConfig, err = clientcmd.BuildConfigFromFlags("", os.Getenv("SOURCE_KUBECONFIG"))
   101  				if err != nil {
   102  					return err
   103  				}
   104  				managerOptions.LeaderElectionConfig = sourceClusterConfig
   105  			} else {
   106  				// Restrict the cache for secrets to the configured namespace to avoid the need for cluster-wide list/watch permissions.
   107  				managerOptions.Cache = cache.Options{
   108  					ByObject: map[client.Object]cache.ByObject{
   109  						&corev1.Secret{}: {Namespaces: map[string]cache.Config{webhookOptions.Server.Completed().Namespace: {}}},
   110  					},
   111  				}
   112  			}
   113  
   114  			mgr, err := manager.New(restOpts.Completed().Config, mgrOpts.Completed().Options())
   115  			if err != nil {
   116  				return fmt.Errorf("could not instantiate manager: %v", err)
   117  			}
   118  
   119  			install.Install(mgr.GetScheme())
   120  
   121  			if err := ironcoreinstall.AddToScheme(mgr.GetScheme()); err != nil {
   122  				return fmt.Errorf("could not update manager scheme: %v", err)
   123  			}
   124  
   125  			var sourceCluster cluster.Cluster
   126  			if sourceClusterConfig != nil {
   127  				sourceCluster, err = cluster.New(sourceClusterConfig, func(opts *cluster.Options) {
   128  					opts.Logger = log
   129  					opts.Cache.DefaultNamespaces = map[string]cache.Config{v1beta1constants.GardenNamespace: {}}
   130  				})
   131  				if err != nil {
   132  					return err
   133  				}
   134  
   135  				if err := mgr.AddReadyzCheck("source-informer-sync", gardenerhealthz.NewCacheSyncHealthz(sourceCluster.GetCache())); err != nil {
   136  					return err
   137  				}
   138  
   139  				if err = mgr.Add(sourceCluster); err != nil {
   140  					return err
   141  				}
   142  			}
   143  
   144  			log.Info("Setting up webhook server")
   145  			if _, err := webhookOptions.Completed().AddToManager(ctx, mgr, sourceCluster); err != nil {
   146  				return err
   147  			}
   148  
   149  			if err := mgr.AddReadyzCheck("informer-sync", gardenerhealthz.NewCacheSyncHealthz(mgr.GetCache())); err != nil {
   150  				return fmt.Errorf("could not add readycheck for informers: %w", err)
   151  			}
   152  
   153  			if err := mgr.AddHealthzCheck("ping", healthz.Ping); err != nil {
   154  				return fmt.Errorf("could not add healthcheck: %w", err)
   155  			}
   156  
   157  			if err := mgr.AddReadyzCheck("webhook-server", mgr.GetWebhookServer().StartedChecker()); err != nil {
   158  				return fmt.Errorf("could not add readycheck of webhook to manager: %w", err)
   159  			}
   160  
   161  			return mgr.Start(ctx)
   162  		},
   163  	}
   164  
   165  	verflag.AddFlags(cmd.Flags())
   166  	aggOption.AddFlags(cmd.Flags())
   167  
   168  	return cmd
   169  }