github.com/ironcore-dev/gardener-extension-provider-ironcore@v0.3.2-0.20240314231816-8336447fb9a0/cmd/gardener-extension-provider-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  	druidv1alpha1 "github.com/gardener/etcd-druid/api/v1alpha1"
    12  	extensionscontroller "github.com/gardener/gardener/extensions/pkg/controller"
    13  	controllercmd "github.com/gardener/gardener/extensions/pkg/controller/cmd"
    14  	"github.com/gardener/gardener/extensions/pkg/controller/controlplane/genericactuator"
    15  	"github.com/gardener/gardener/extensions/pkg/controller/heartbeat"
    16  	heartbeatcmd "github.com/gardener/gardener/extensions/pkg/controller/heartbeat/cmd"
    17  	"github.com/gardener/gardener/extensions/pkg/util"
    18  	webhookcmd "github.com/gardener/gardener/extensions/pkg/webhook/cmd"
    19  	"github.com/gardener/gardener/pkg/client/kubernetes"
    20  	gardenerhealthz "github.com/gardener/gardener/pkg/healthz"
    21  	machinev1alpha1 "github.com/gardener/machine-controller-manager/pkg/apis/machine/v1alpha1"
    22  	"github.com/spf13/cobra"
    23  	corev1 "k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	autoscalingv1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
    26  	"k8s.io/component-base/version/verflag"
    27  	"sigs.k8s.io/controller-runtime/pkg/client"
    28  	"sigs.k8s.io/controller-runtime/pkg/cluster"
    29  	"sigs.k8s.io/controller-runtime/pkg/healthz"
    30  	"sigs.k8s.io/controller-runtime/pkg/manager"
    31  
    32  	ironcoreinstall "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/apis/ironcore/install"
    33  	ironcorecmd "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/cmd"
    34  	backupbucketcontroller "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/controller/backupbucket"
    35  	backupentrycontroller "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/controller/backupentry"
    36  	bastioncontroller "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/controller/bastion"
    37  	ironcorecontrolplane "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/controller/controlplane"
    38  	"github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/controller/healthcheck"
    39  	infrastructurecontroller "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/controller/infrastructure"
    40  	workercontroller "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/controller/worker"
    41  	ironcore "github.com/ironcore-dev/gardener-extension-provider-ironcore/pkg/ironcore"
    42  )
    43  
    44  // NewControllerManagerCommand creates a new command for running a ironcore provider controller.
    45  func NewControllerManagerCommand(ctx context.Context) *cobra.Command {
    46  	var (
    47  		generalOpts = &controllercmd.GeneralOptions{}
    48  		restOpts    = &controllercmd.RESTOptions{}
    49  		mgrOpts     = &controllercmd.ManagerOptions{
    50  			LeaderElection:          true,
    51  			LeaderElectionID:        controllercmd.LeaderElectionNameID(ironcore.ProviderName),
    52  			LeaderElectionNamespace: os.Getenv("LEADER_ELECTION_NAMESPACE"),
    53  			WebhookServerPort:       443,
    54  			WebhookCertDir:          "/tmp/gardener-extensions-cert",
    55  			MetricsBindAddress:      ":8080",
    56  			HealthBindAddress:       ":8081",
    57  		}
    58  		configFileOpts = &ironcorecmd.ConfigOptions{}
    59  
    60  		// options for the backupbucket controller
    61  		backupBucketCtrlOpts = &controllercmd.ControllerOptions{
    62  			MaxConcurrentReconciles: 5,
    63  		}
    64  
    65  		// options for the backupentry controller
    66  		backupEntryCtrlOpts = &controllercmd.ControllerOptions{
    67  			MaxConcurrentReconciles: 5,
    68  		}
    69  
    70  		// options for the health care controller
    71  		healthCheckCtrlOpts = &controllercmd.ControllerOptions{
    72  			MaxConcurrentReconciles: 5,
    73  		}
    74  
    75  		// options for the heartbeat controller
    76  		heartbeatCtrlOpts = &heartbeatcmd.Options{
    77  			ExtensionName:        ironcore.ProviderName,
    78  			RenewIntervalSeconds: 30,
    79  			Namespace:            os.Getenv("LEADER_ELECTION_NAMESPACE"),
    80  		}
    81  
    82  		// options for the controlplane controller
    83  		controlPlaneCtrlOpts = &controllercmd.ControllerOptions{
    84  			MaxConcurrentReconciles: 5,
    85  		}
    86  
    87  		// options for the infrastructure controller
    88  		infraCtrlOpts = &controllercmd.ControllerOptions{
    89  			MaxConcurrentReconciles: 5,
    90  		}
    91  		reconcileOpts = &controllercmd.ReconcilerOptions{}
    92  
    93  		// options for the worker controller
    94  		workerCtrlOpts = &controllercmd.ControllerOptions{
    95  			MaxConcurrentReconciles: 5,
    96  		}
    97  
    98  		// options for the webhook server
    99  		webhookServerOptions = &webhookcmd.ServerOptions{
   100  			Namespace: os.Getenv("WEBHOOK_CONFIG_NAMESPACE"),
   101  		}
   102  
   103  		// options for the bastion controller
   104  		bastionCtrlOpts = &controllercmd.ControllerOptions{
   105  			MaxConcurrentReconciles: 5,
   106  		}
   107  
   108  		controllerSwitches = ironcorecmd.ControllerSwitchOptions()
   109  		webhookSwitches    = ironcorecmd.WebhookSwitchOptions()
   110  		webhookOptions     = webhookcmd.NewAddToManagerOptions(
   111  			ironcore.ProviderName,
   112  			genericactuator.ShootWebhooksResourceName,
   113  			genericactuator.ShootWebhookNamespaceSelector(ironcore.Type),
   114  			webhookServerOptions,
   115  			webhookSwitches,
   116  		)
   117  
   118  		aggOption = controllercmd.NewOptionAggregator(
   119  			generalOpts,
   120  			restOpts,
   121  			mgrOpts,
   122  			controllercmd.PrefixOption("controlplane-", controlPlaneCtrlOpts),
   123  			controllercmd.PrefixOption("infrastructure-", infraCtrlOpts),
   124  			controllercmd.PrefixOption("worker-", workerCtrlOpts),
   125  			controllercmd.PrefixOption("healthcheck-", healthCheckCtrlOpts),
   126  			controllercmd.PrefixOption("heartbeat-", heartbeatCtrlOpts),
   127  			controllercmd.PrefixOption("bastion-", bastionCtrlOpts),
   128  			controllercmd.PrefixOption("backupbucket-", backupBucketCtrlOpts),
   129  			controllercmd.PrefixOption("backupentry-", backupEntryCtrlOpts),
   130  			configFileOpts,
   131  			controllerSwitches,
   132  			reconcileOpts,
   133  			webhookOptions,
   134  		)
   135  	)
   136  
   137  	cmd := &cobra.Command{
   138  		Use: fmt.Sprintf("%s-controller-manager", ironcore.ProviderName),
   139  
   140  		RunE: func(cmd *cobra.Command, args []string) error {
   141  			verflag.PrintAndExitIfRequested()
   142  
   143  			if err := aggOption.Complete(); err != nil {
   144  				return fmt.Errorf("error completing options: %w", err)
   145  			}
   146  
   147  			if err := heartbeatCtrlOpts.Validate(); err != nil {
   148  				return err
   149  			}
   150  
   151  			util.ApplyClientConnectionConfigurationToRESTConfig(configFileOpts.Completed().Config.ClientConnection, restOpts.Completed().Config)
   152  
   153  			mopts := mgrOpts.Completed().Options()
   154  			mopts.Client = client.Options{
   155  				Cache: &client.CacheOptions{
   156  					DisableFor: []client.Object{
   157  						&corev1.Secret{},
   158  					},
   159  				},
   160  			}
   161  			mgr, err := manager.New(restOpts.Completed().Config, mopts)
   162  			if err != nil {
   163  				return fmt.Errorf("could not instantiate manager: %w", err)
   164  			}
   165  
   166  			scheme := mgr.GetScheme()
   167  			if err := extensionscontroller.AddToScheme(scheme); err != nil {
   168  				return fmt.Errorf("could not update manager scheme: %w", err)
   169  			}
   170  			if err := ironcoreinstall.AddToScheme(scheme); err != nil {
   171  				return fmt.Errorf("could not update manager scheme: %w", err)
   172  			}
   173  			if err := druidv1alpha1.AddToScheme(scheme); err != nil {
   174  				return fmt.Errorf("could not update manager scheme: %w", err)
   175  			}
   176  			if err := autoscalingv1.AddToScheme(scheme); err != nil {
   177  				return fmt.Errorf("could not update manager scheme: %w", err)
   178  			}
   179  			if err := machinev1alpha1.AddToScheme(scheme); err != nil {
   180  				return fmt.Errorf("could not update manager scheme: %w", err)
   181  			}
   182  
   183  			// add common meta types to schema for controller-runtime to use v1.ListOptions
   184  			metav1.AddToGroupVersion(scheme, machinev1alpha1.SchemeGroupVersion)
   185  
   186  			log := mgr.GetLogger()
   187  			log.Info("Getting rest config for garden")
   188  			gardenRESTConfig, err := kubernetes.RESTConfigFromKubeconfigFile(os.Getenv("GARDEN_KUBECONFIG"), kubernetes.AuthTokenFile)
   189  			if err != nil {
   190  				return err
   191  			}
   192  
   193  			log.Info("Setting up cluster object for garden")
   194  			gardenCluster, err := cluster.New(gardenRESTConfig, func(opts *cluster.Options) {
   195  				opts.Scheme = kubernetes.GardenScheme
   196  				opts.Logger = log
   197  			})
   198  			if err != nil {
   199  				return fmt.Errorf("failed creating garden cluster object: %w", err)
   200  			}
   201  
   202  			log.Info("Adding garden cluster to manager")
   203  			if err := mgr.Add(gardenCluster); err != nil {
   204  				return fmt.Errorf("failed adding garden cluster to manager: %w", err)
   205  			}
   206  
   207  			configFileOpts.Completed().ApplyHealthCheckConfig(&healthcheck.DefaultAddOptions.HealthCheckConfig)
   208  			healthCheckCtrlOpts.Completed().Apply(&healthcheck.DefaultAddOptions.Controller)
   209  			configFileOpts.Completed().ApplyBastionConfig(&bastioncontroller.DefaultAddOptions.BastionConfig)
   210  			heartbeatCtrlOpts.Completed().Apply(&heartbeat.DefaultAddOptions)
   211  			infraCtrlOpts.Completed().Apply(&infrastructurecontroller.DefaultAddOptions.Controller)
   212  			workerCtrlOpts.Completed().Apply(&workercontroller.DefaultAddOptions.Controller)
   213  			configFileOpts.Completed().ApplyBackupbucketConfig(&backupbucketcontroller.DefaultAddOptions.BackupBucketConfig)
   214  			bastionCtrlOpts.Completed().Apply(&bastioncontroller.DefaultAddOptions.Controller)
   215  			backupBucketCtrlOpts.Completed().Apply(&backupbucketcontroller.DefaultAddOptions.Controller)
   216  			backupEntryCtrlOpts.Completed().Apply(&backupentrycontroller.DefaultAddOptions.Controller)
   217  			reconcileOpts.Completed().Apply(&bastioncontroller.DefaultAddOptions.IgnoreOperationAnnotation)
   218  			reconcileOpts.Completed().Apply(&infrastructurecontroller.DefaultAddOptions.IgnoreOperationAnnotation)
   219  			reconcileOpts.Completed().Apply(&workercontroller.DefaultAddOptions.IgnoreOperationAnnotation)
   220  			reconcileOpts.Completed().Apply(&backupbucketcontroller.DefaultAddOptions.IgnoreOperationAnnotation)
   221  			reconcileOpts.Completed().Apply(&backupentrycontroller.DefaultAddOptions.IgnoreOperationAnnotation)
   222  			workercontroller.DefaultAddOptions.GardenCluster = gardenCluster
   223  
   224  			if _, err := webhookOptions.Completed().AddToManager(ctx, mgr, nil); err != nil {
   225  				return fmt.Errorf("could not add webhooks to manager: %w", err)
   226  			}
   227  			ironcorecontrolplane.DefaultAddOptions.WebhookServerNamespace = webhookOptions.Server.Namespace
   228  
   229  			if err := controllerSwitches.Completed().AddToManager(ctx, mgr); err != nil {
   230  				return fmt.Errorf("could not add controllers to manager: %w", err)
   231  			}
   232  
   233  			if err := mgr.AddReadyzCheck("informer-sync", gardenerhealthz.NewCacheSyncHealthz(mgr.GetCache())); err != nil {
   234  				return fmt.Errorf("could not add readycheck for informers: %w", err)
   235  			}
   236  
   237  			if err := mgr.AddHealthzCheck("ping", healthz.Ping); err != nil {
   238  				return fmt.Errorf("could not add health check to manager: %w", err)
   239  			}
   240  
   241  			if err := mgr.AddReadyzCheck("webhook-server", mgr.GetWebhookServer().StartedChecker()); err != nil {
   242  				return fmt.Errorf("could not add ready check for webhook server to manager: %w", err)
   243  			}
   244  
   245  			if err := mgr.Start(ctx); err != nil {
   246  				return fmt.Errorf("error running manager: %w", err)
   247  			}
   248  
   249  			return nil
   250  		},
   251  	}
   252  
   253  	verflag.AddFlags(cmd.Flags())
   254  	aggOption.AddFlags(cmd.Flags())
   255  
   256  	return cmd
   257  }