github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/cmd/manager/main.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package main 21 22 import ( 23 "encoding/json" 24 "flag" 25 "fmt" 26 "os" 27 "strings" 28 "time" 29 30 // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) 31 // to ensure that exec-entrypoint and run can make use of them. 32 "github.com/fsnotify/fsnotify" 33 snapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v3/apis/volumesnapshot/v1beta1" 34 snapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" 35 "github.com/spf13/pflag" 36 corev1 "k8s.io/api/core/v1" 37 "k8s.io/apimachinery/pkg/runtime" 38 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 39 discoverycli "k8s.io/client-go/discovery" 40 clientgoscheme "k8s.io/client-go/kubernetes/scheme" 41 _ "k8s.io/client-go/plugin/pkg/client/auth" 42 ctrl "sigs.k8s.io/controller-runtime" 43 "sigs.k8s.io/controller-runtime/pkg/healthz" 44 "sigs.k8s.io/controller-runtime/pkg/log/zap" 45 46 // +kubebuilder:scaffold:imports 47 48 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 49 dpv1alpha1 "github.com/1aal/kubeblocks/apis/dataprotection/v1alpha1" 50 extensionsv1alpha1 "github.com/1aal/kubeblocks/apis/extensions/v1alpha1" 51 storagev1alpha1 "github.com/1aal/kubeblocks/apis/storage/v1alpha1" 52 workloadsv1alpha1 "github.com/1aal/kubeblocks/apis/workloads/v1alpha1" 53 appscontrollers "github.com/1aal/kubeblocks/controllers/apps" 54 "github.com/1aal/kubeblocks/controllers/apps/configuration" 55 extensionscontrollers "github.com/1aal/kubeblocks/controllers/extensions" 56 k8scorecontrollers "github.com/1aal/kubeblocks/controllers/k8score" 57 storagecontrollers "github.com/1aal/kubeblocks/controllers/storage" 58 workloadscontrollers "github.com/1aal/kubeblocks/controllers/workloads" 59 "github.com/1aal/kubeblocks/pkg/constant" 60 "github.com/1aal/kubeblocks/pkg/controller/rsm" 61 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 62 viper "github.com/1aal/kubeblocks/pkg/viperx" 63 ) 64 65 // added lease.coordination.k8s.io for leader election 66 // +kubebuilder:rbac:groups=coordination.k8s.io,resources=leases,verbs=get;list;watch;create;update;patch 67 68 const ( 69 appName = "kubeblocks" 70 ) 71 72 var ( 73 scheme = runtime.NewScheme() 74 setupLog = ctrl.Log.WithName("setup") 75 ) 76 77 func init() { 78 utilruntime.Must(clientgoscheme.AddToScheme(scheme)) 79 80 utilruntime.Must(appsv1alpha1.AddToScheme(scheme)) 81 utilruntime.Must(dpv1alpha1.AddToScheme(scheme)) 82 utilruntime.Must(snapshotv1.AddToScheme(scheme)) 83 utilruntime.Must(snapshotv1beta1.AddToScheme(scheme)) 84 utilruntime.Must(extensionsv1alpha1.AddToScheme(scheme)) 85 utilruntime.Must(workloadsv1alpha1.AddToScheme(scheme)) 86 utilruntime.Must(storagev1alpha1.AddToScheme(scheme)) 87 // +kubebuilder:scaffold:scheme 88 89 viper.SetConfigName("config") // name of config file (without extension) 90 viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name 91 viper.AddConfigPath(fmt.Sprintf("/etc/%s/", appName)) // path to look for the config file in 92 viper.AddConfigPath(fmt.Sprintf("$HOME/.%s", appName)) // call multiple times to append search path 93 viper.AddConfigPath(".") // optionally look for config in the working directory 94 viper.AutomaticEnv() 95 96 viper.SetDefault(constant.CfgKeyCtrlrReconcileRetryDurationMS, 1000) 97 viper.SetDefault("CERT_DIR", "/tmp/k8s-webhook-server/serving-certs") 98 viper.SetDefault(constant.EnableRBACManager, true) 99 viper.SetDefault("VOLUMESNAPSHOT_API_BETA", false) 100 viper.SetDefault(constant.KBToolsImage, "apecloud/kubeblocks-tools:latest") 101 viper.SetDefault("PROBE_SERVICE_HTTP_PORT", 3501) 102 viper.SetDefault("PROBE_SERVICE_GRPC_PORT", 50001) 103 viper.SetDefault("PROBE_SERVICE_LOG_LEVEL", "info") 104 viper.SetDefault("KUBEBLOCKS_SERVICEACCOUNT_NAME", "kubeblocks") 105 viper.SetDefault("CONFIG_MANAGER_GRPC_PORT", 9901) 106 viper.SetDefault("CONFIG_MANAGER_LOG_LEVEL", "info") 107 viper.SetDefault(constant.CfgKeyCtrlrMgrNS, "default") 108 viper.SetDefault(constant.FeatureGateReplicatedStateMachine, true) 109 viper.SetDefault(constant.KBDataScriptClientsImage, "apecloud/kubeblocks-datascript:latest") 110 viper.SetDefault(constant.KubernetesClusterDomainEnv, constant.DefaultDNSDomain) 111 viper.SetDefault(rsm.FeatureGateRSMCompatibilityMode, true) 112 } 113 114 type flagName string 115 116 const ( 117 probeAddrFlagKey flagName = "health-probe-bind-address" 118 metricsAddrFlagKey flagName = "metrics-bind-address" 119 leaderElectFlagKey flagName = "leader-elect" 120 leaderElectIDFlagKey flagName = "leader-elect-id" 121 122 // switch flags key for API groups 123 appsFlagKey flagName = "apps" 124 extensionsFlagKey flagName = "extensions" 125 workloadsFlagKey flagName = "workloads" 126 storageFlagKey flagName = "storage" 127 ) 128 129 func (r flagName) String() string { 130 return string(r) 131 } 132 133 func (r flagName) viperName() string { 134 return strings.ReplaceAll(r.String(), "-", "_") 135 } 136 137 func validateRequiredToParseConfigs() error { 138 validateTolerations := func(val string) error { 139 if val == "" { 140 return nil 141 } 142 var tolerations []corev1.Toleration 143 return json.Unmarshal([]byte(val), &tolerations) 144 } 145 146 validateAffinity := func(val string) error { 147 if val == "" { 148 return nil 149 } 150 affinity := corev1.Affinity{} 151 return json.Unmarshal([]byte(val), &affinity) 152 } 153 154 if jobTTL := viper.GetString(constant.CfgKeyAddonJobTTL); jobTTL != "" { 155 if _, err := time.ParseDuration(jobTTL); err != nil { 156 return err 157 } 158 } 159 if err := validateTolerations(viper.GetString(constant.CfgKeyCtrlrMgrTolerations)); err != nil { 160 return err 161 } 162 if err := validateAffinity(viper.GetString(constant.CfgKeyCtrlrMgrAffinity)); err != nil { 163 return err 164 } 165 if cmNodeSelector := viper.GetString(constant.CfgKeyCtrlrMgrNodeSelector); cmNodeSelector != "" { 166 nodeSelector := map[string]string{} 167 if err := json.Unmarshal([]byte(cmNodeSelector), &nodeSelector); err != nil { 168 return err 169 } 170 } 171 if err := validateTolerations(viper.GetString(constant.CfgKeyDataPlaneTolerations)); err != nil { 172 return err 173 } 174 if err := validateAffinity(viper.GetString(constant.CfgKeyDataPlaneAffinity)); err != nil { 175 return err 176 } 177 return nil 178 } 179 180 func main() { 181 var metricsAddr string 182 var enableLeaderElection bool 183 var enableLeaderElectionID string 184 var probeAddr string 185 flag.String(metricsAddrFlagKey.String(), ":8080", "The address the metric endpoint binds to.") 186 flag.String(probeAddrFlagKey.String(), ":8081", "The address the probe endpoint binds to.") 187 flag.Bool(leaderElectFlagKey.String(), false, 188 "Enable leader election for controller manager. "+ 189 "Enabling this will ensure there is only one active controller manager.") 190 191 flag.String(leaderElectIDFlagKey.String(), "001c317f", 192 "The leader election ID prefix for controller manager. "+ 193 "This ID must be unique to controller manager.") 194 195 flag.Bool(appsFlagKey.String(), true, 196 "Enable the apps controller manager.") 197 flag.Bool(extensionsFlagKey.String(), true, 198 "Enable the extensions controller manager. ") 199 flag.Bool(workloadsFlagKey.String(), true, 200 "Enable the workloads controller manager. ") 201 flag.Bool(storageFlagKey.String(), true, 202 "Enable the storage controller manager. ") 203 204 opts := zap.Options{ 205 Development: true, 206 } 207 opts.BindFlags(flag.CommandLine) 208 pflag.CommandLine.AddGoFlagSet(flag.CommandLine) 209 pflag.Parse() 210 211 // set normalizeFunc to replace flag name to viper name 212 normalizeFunc := pflag.CommandLine.GetNormalizeFunc() 213 pflag.CommandLine.SetNormalizeFunc(func(fs *pflag.FlagSet, name string) pflag.NormalizedName { 214 result := normalizeFunc(fs, name) 215 name = strings.ReplaceAll(string(result), "-", "_") 216 return pflag.NormalizedName(name) 217 }) 218 219 if err := viper.BindPFlags(pflag.CommandLine); err != nil { 220 setupLog.Error(err, "unable to bind flags") 221 os.Exit(1) 222 } 223 224 // NOTES: 225 // zap is "Blazing fast, structured, leveled logging in Go.", DON'T event try 226 // to refactor this logging lib to anything else. Check FAQ - https://github.com/uber-go/zap/blob/master/FAQ.md 227 ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) 228 229 // Find and read the config file 230 if err := viper.ReadInConfig(); err != nil { // Handle errors reading the config file 231 setupLog.Info("unable to read in config, errors ignored") 232 } 233 setupLog.Info(fmt.Sprintf("config file: %s", viper.GetViper().ConfigFileUsed())) 234 viper.OnConfigChange(func(e fsnotify.Event) { 235 setupLog.Info(fmt.Sprintf("config file changed: %s", e.Name)) 236 }) 237 viper.WatchConfig() 238 239 metricsAddr = viper.GetString(metricsAddrFlagKey.viperName()) 240 probeAddr = viper.GetString(probeAddrFlagKey.viperName()) 241 enableLeaderElection = viper.GetBool(leaderElectFlagKey.viperName()) 242 enableLeaderElectionID = viper.GetString(leaderElectIDFlagKey.viperName()) 243 244 setupLog.Info(fmt.Sprintf("config settings: %v", viper.AllSettings())) 245 if err := validateRequiredToParseConfigs(); err != nil { 246 setupLog.Error(err, "config value error") 247 os.Exit(1) 248 } 249 250 mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ 251 Scheme: scheme, 252 MetricsBindAddress: metricsAddr, 253 Port: 9443, 254 HealthProbeBindAddress: probeAddr, 255 LeaderElection: enableLeaderElection, 256 // NOTES: 257 // following LeaderElectionID is generated via hash/fnv (FNV-1 and FNV-1a), in 258 // pattern of '{{ hashFNV .Repo }}.{{ .Domain }}', make sure regenerate this ID 259 // if you have forked from this project template. 260 LeaderElectionID: enableLeaderElectionID + ".kubeblocks.io", 261 262 // NOTES: 263 // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily 264 // when the Manager ends. This requires the binary to immediately end when the 265 // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly 266 // speeds up voluntary leader transitions as the new leader doesn't have to wait 267 // LeaseDuration time first. 268 // 269 // In the default scaffold provided, the program ends immediately after 270 // the manager stops, so would be fine to enable this option. However, 271 // if you are doing or intending to do any operation such as performing cleanups 272 // after the manager stops then its usage might be unsafe. 273 LeaderElectionReleaseOnCancel: true, 274 275 CertDir: viper.GetString("cert_dir"), 276 ClientDisableCacheFor: intctrlutil.GetUncachedObjects(), 277 }) 278 if err != nil { 279 setupLog.Error(err, "unable to start manager") 280 os.Exit(1) 281 } 282 283 if viper.GetBool(appsFlagKey.viperName()) { 284 if err = (&appscontrollers.ClusterReconciler{ 285 Client: mgr.GetClient(), 286 Scheme: mgr.GetScheme(), 287 Recorder: mgr.GetEventRecorderFor("cluster-controller"), 288 }).SetupWithManager(mgr); err != nil { 289 setupLog.Error(err, "unable to create controller", "controller", "Cluster") 290 os.Exit(1) 291 } 292 293 if err = (&appscontrollers.ClusterDefinitionReconciler{ 294 Client: mgr.GetClient(), 295 Scheme: mgr.GetScheme(), 296 Recorder: mgr.GetEventRecorderFor("cluster-definition-controller"), 297 }).SetupWithManager(mgr); err != nil { 298 setupLog.Error(err, "unable to create controller", "controller", "ClusterDefinition") 299 os.Exit(1) 300 } 301 302 if err = (&appscontrollers.ClusterVersionReconciler{ 303 Client: mgr.GetClient(), 304 Scheme: mgr.GetScheme(), 305 Recorder: mgr.GetEventRecorderFor("cluster-version-controller"), 306 }).SetupWithManager(mgr); err != nil { 307 setupLog.Error(err, "unable to create controller", "controller", "ClusterVersion") 308 os.Exit(1) 309 } 310 311 if err = (&appscontrollers.OpsRequestReconciler{ 312 Client: mgr.GetClient(), 313 Scheme: mgr.GetScheme(), 314 Recorder: mgr.GetEventRecorderFor("ops-request-controller"), 315 }).SetupWithManager(mgr); err != nil { 316 setupLog.Error(err, "unable to create controller", "controller", "OpsRequest") 317 os.Exit(1) 318 } 319 320 if err = (&configuration.ConfigConstraintReconciler{ 321 Client: mgr.GetClient(), 322 Scheme: mgr.GetScheme(), 323 Recorder: mgr.GetEventRecorderFor("config-constraint-controller"), 324 }).SetupWithManager(mgr); err != nil { 325 setupLog.Error(err, "unable to create controller", "controller", "ConfigConstraint") 326 os.Exit(1) 327 } 328 329 if err = (&configuration.ReconfigureReconciler{ 330 Client: mgr.GetClient(), 331 Scheme: mgr.GetScheme(), 332 Recorder: mgr.GetEventRecorderFor("reconfigure-controller"), 333 }).SetupWithManager(mgr); err != nil { 334 setupLog.Error(err, "unable to create controller", "controller", "ReconfigureRequest") 335 os.Exit(1) 336 } 337 338 if err = (&configuration.ConfigurationReconciler{ 339 Client: mgr.GetClient(), 340 Scheme: mgr.GetScheme(), 341 Recorder: mgr.GetEventRecorderFor("configuration-controller"), 342 }).SetupWithManager(mgr); err != nil { 343 setupLog.Error(err, "unable to create controller", "controller", "Configuration") 344 os.Exit(1) 345 } 346 347 if err = (&appscontrollers.SystemAccountReconciler{ 348 Client: mgr.GetClient(), 349 Scheme: mgr.GetScheme(), 350 Recorder: mgr.GetEventRecorderFor("system-account-controller"), 351 }).SetupWithManager(mgr); err != nil { 352 setupLog.Error(err, "unable to create controller", "controller", "SystemAccount") 353 os.Exit(1) 354 } 355 356 if err = (&k8scorecontrollers.EventReconciler{ 357 Client: mgr.GetClient(), 358 Scheme: mgr.GetScheme(), 359 Recorder: mgr.GetEventRecorderFor("event-controller"), 360 }).SetupWithManager(mgr); err != nil { 361 setupLog.Error(err, "unable to create controller", "controller", "Event") 362 os.Exit(1) 363 } 364 365 if err = (&appscontrollers.ComponentClassReconciler{ 366 Client: mgr.GetClient(), 367 Scheme: mgr.GetScheme(), 368 Recorder: mgr.GetEventRecorderFor("class-controller"), 369 }).SetupWithManager(mgr); err != nil { 370 setupLog.Error(err, "unable to create controller", "controller", "Class") 371 os.Exit(1) 372 } 373 374 if err = (&appscontrollers.ServiceDescriptorReconciler{ 375 Client: mgr.GetClient(), 376 Scheme: mgr.GetScheme(), 377 }).SetupWithManager(mgr); err != nil { 378 setupLog.Error(err, "unable to create controller", "controller", "ServiceDescriptor") 379 os.Exit(1) 380 } 381 382 if err = (&appscontrollers.BackupPolicyTemplateReconciler{ 383 Client: mgr.GetClient(), 384 Scheme: mgr.GetScheme(), 385 }).SetupWithManager(mgr); err != nil { 386 setupLog.Error(err, "unable to create controller", "controller", "BackupPolicyTemplate") 387 os.Exit(1) 388 } 389 } 390 391 if viper.GetBool(extensionsFlagKey.viperName()) { 392 if err = (&extensionscontrollers.AddonReconciler{ 393 Client: mgr.GetClient(), 394 Scheme: mgr.GetScheme(), 395 Recorder: mgr.GetEventRecorderFor("addon-controller"), 396 RestConfig: mgr.GetConfig(), 397 }).SetupWithManager(mgr); err != nil { 398 setupLog.Error(err, "unable to create controller", "controller", "Addon") 399 os.Exit(1) 400 } 401 } 402 403 if viper.GetBool(workloadsFlagKey.viperName()) { 404 if err = (&workloadscontrollers.ReplicatedStateMachineReconciler{ 405 Client: mgr.GetClient(), 406 Scheme: mgr.GetScheme(), 407 Recorder: mgr.GetEventRecorderFor("replicated-state-machine-controller"), 408 }).SetupWithManager(mgr); err != nil { 409 setupLog.Error(err, "unable to create controller", "controller", "ReplicatedStateMachine") 410 os.Exit(1) 411 } 412 } 413 414 if viper.GetBool(storageFlagKey.viperName()) { 415 if err = (&storagecontrollers.StorageProviderReconciler{ 416 Client: mgr.GetClient(), 417 Scheme: mgr.GetScheme(), 418 Recorder: mgr.GetEventRecorderFor("storage-provider-controller"), 419 }).SetupWithManager(mgr); err != nil { 420 setupLog.Error(err, "unable to create controller", "controller", "StorageProvider") 421 os.Exit(1) 422 } 423 } 424 // +kubebuilder:scaffold:builder 425 426 if viper.GetBool("enable_webhooks") { 427 428 appsv1alpha1.RegisterWebhookManager(mgr) 429 430 if err = (&appsv1alpha1.Cluster{}).SetupWebhookWithManager(mgr); err != nil { 431 setupLog.Error(err, "unable to create webhook", "webhook", "Cluster") 432 os.Exit(1) 433 } 434 435 if err = (&appsv1alpha1.ClusterDefinition{}).SetupWebhookWithManager(mgr); err != nil { 436 setupLog.Error(err, "unable to create webhook", "webhook", "ClusterDefinition") 437 os.Exit(1) 438 } 439 440 if err = (&appsv1alpha1.ClusterVersion{}).SetupWebhookWithManager(mgr); err != nil { 441 setupLog.Error(err, "unable to create webhook", "webhook", "ClusterVersion") 442 os.Exit(1) 443 } 444 445 if err = (&appsv1alpha1.OpsRequest{}).SetupWebhookWithManager(mgr); err != nil { 446 setupLog.Error(err, "unable to create webhook", "webhook", "OpsRequest") 447 os.Exit(1) 448 } 449 450 if err = (&workloadsv1alpha1.ReplicatedStateMachine{}).SetupWebhookWithManager(mgr); err != nil { 451 setupLog.Error(err, "unable to create webhook", "webhook", "ReplicatedStateMachine") 452 os.Exit(1) 453 } 454 455 if err = (&appsv1alpha1.ServiceDescriptor{}).SetupWebhookWithManager(mgr); err != nil { 456 setupLog.Error(err, "unable to create webhook", "webhook", "ServiceDescriptor") 457 os.Exit(1) 458 } 459 } 460 461 if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { 462 setupLog.Error(err, "unable to set up health check") 463 os.Exit(1) 464 } 465 if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { 466 setupLog.Error(err, "unable to set up ready check") 467 os.Exit(1) 468 } 469 470 cli, err := discoverycli.NewDiscoveryClientForConfig(mgr.GetConfig()) 471 if err != nil { 472 setupLog.Error(err, "unable to create discovery client") 473 os.Exit(1) 474 } 475 476 ver, err := cli.ServerVersion() 477 if err != nil { 478 setupLog.Error(err, "unable to discover version info") 479 os.Exit(1) 480 } 481 viper.SetDefault(constant.CfgKeyServerInfo, *ver) 482 483 setupLog.Info("starting manager") 484 if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { 485 setupLog.Error(err, "problem running manager") 486 os.Exit(1) 487 } 488 }