sigs.k8s.io/cluster-api/bootstrap/kubeadm@v0.0.0-20191016155141-23a891785b60/controllers/kubeadmconfig_controller.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package controllers 18 19 import ( 20 "context" 21 "fmt" 22 "strings" 23 "time" 24 25 "github.com/go-logr/logr" 26 "github.com/pkg/errors" 27 apierrors "k8s.io/apimachinery/pkg/api/errors" 28 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" 30 bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha2" 31 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/cloudinit" 32 internalcluster "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cluster" 33 kubeadmv1beta1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/kubeadm/v1beta1" 34 clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha2" 35 capierrors "sigs.k8s.io/cluster-api/errors" 36 "sigs.k8s.io/cluster-api/util" 37 "sigs.k8s.io/cluster-api/util/patch" 38 "sigs.k8s.io/cluster-api/util/secret" 39 ctrl "sigs.k8s.io/controller-runtime" 40 "sigs.k8s.io/controller-runtime/pkg/client" 41 "sigs.k8s.io/controller-runtime/pkg/handler" 42 "sigs.k8s.io/controller-runtime/pkg/source" 43 ) 44 45 // InitLocker is a lock that is used around kubeadm init 46 type InitLocker interface { 47 Lock(ctx context.Context, cluster *clusterv1.Cluster, machine *clusterv1.Machine) bool 48 Unlock(ctx context.Context, cluster *clusterv1.Cluster) bool 49 } 50 51 // SecretsClientFactory define behaviour for creating a secrets client 52 type SecretsClientFactory interface { 53 // NewSecretsClient returns a new client supporting SecretInterface 54 NewSecretsClient(client.Client, *clusterv1.Cluster) (typedcorev1.SecretInterface, error) 55 } 56 57 // +kubebuilder:rbac:groups=bootstrap.cluster.x-k8s.io,resources=kubeadmconfigs;kubeadmconfigs/status,verbs=get;list;watch;create;update;patch;delete 58 // +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters;clusters/status;machines;machines/status,verbs=get;list;watch 59 // +kubebuilder:rbac:groups="",resources=secrets;events;configmaps,verbs=get;list;watch;create;update;patch;delete 60 61 // KubeadmConfigReconciler reconciles a KubeadmConfig object 62 type KubeadmConfigReconciler struct { 63 client.Client 64 SecretsClientFactory SecretsClientFactory 65 KubeadmInitLock InitLocker 66 Log logr.Logger 67 } 68 69 // SetupWithManager sets up the reconciler with the Manager. 70 func (r *KubeadmConfigReconciler) SetupWithManager(mgr ctrl.Manager) error { 71 return ctrl.NewControllerManagedBy(mgr). 72 For(&bootstrapv1.KubeadmConfig{}). 73 Watches( 74 &source.Kind{Type: &clusterv1.Machine{}}, 75 &handler.EnqueueRequestsFromMapFunc{ 76 ToRequests: handler.ToRequestsFunc(r.MachineToBootstrapMapFunc), 77 }, 78 ). 79 Watches( 80 &source.Kind{Type: &clusterv1.Cluster{}}, 81 &handler.EnqueueRequestsFromMapFunc{ 82 ToRequests: handler.ToRequestsFunc(r.ClusterToKubeadmConfigs), 83 }, 84 ). 85 Complete(r) 86 } 87 88 // Reconcile handles KubeadmConfig events 89 func (r *KubeadmConfigReconciler) Reconcile(req ctrl.Request) (_ ctrl.Result, rerr error) { 90 ctx := context.Background() 91 log := r.Log.WithValues("kubeadmconfig", req.NamespacedName) 92 93 // Lookup the kubeadm config 94 config := &bootstrapv1.KubeadmConfig{} 95 if err := r.Get(ctx, req.NamespacedName, config); err != nil { 96 if apierrors.IsNotFound(err) { 97 return ctrl.Result{}, nil 98 } 99 log.Error(err, "failed to get config") 100 return ctrl.Result{}, err 101 } 102 103 // Look up the Machine that owns this KubeConfig if there is one 104 machine, err := util.GetOwnerMachine(ctx, r.Client, config.ObjectMeta) 105 if err != nil { 106 log.Error(err, "could not get owner machine") 107 return ctrl.Result{}, err 108 } 109 if machine == nil { 110 log.Info("Waiting for Machine Controller to set OwnerRef on the KubeadmConfig") 111 return ctrl.Result{}, nil 112 } 113 log = log.WithValues("machine-name", machine.Name) 114 115 // Lookup the cluster the machine is associated with 116 cluster, err := util.GetClusterFromMetadata(ctx, r.Client, machine.ObjectMeta) 117 if err != nil { 118 if errors.Cause(err) == util.ErrNoCluster { 119 log.Info("Machine does not belong to a cluster yet, waiting until its part of a cluster") 120 return ctrl.Result{}, nil 121 } 122 123 if apierrors.IsNotFound(err) { 124 log.Info("Cluster does not exist yet , waiting until it is created") 125 return ctrl.Result{}, nil 126 } 127 log.Error(err, "could not get cluster by machine metadata") 128 return ctrl.Result{}, err 129 } 130 131 switch { 132 // Wait patiently for the infrastructure to be ready 133 case !cluster.Status.InfrastructureReady: 134 log.Info("Infrastructure is not ready, waiting until ready.") 135 return ctrl.Result{}, nil 136 // bail super early if it's already ready 137 case config.Status.Ready && machine.Status.InfrastructureReady: 138 log.Info("ignoring config for an already ready machine") 139 return ctrl.Result{}, nil 140 // Reconcile status for machines that have already copied bootstrap data 141 case machine.Spec.Bootstrap.Data != nil && !config.Status.Ready: 142 config.Status.Ready = true 143 // Initialize the patch helper 144 patchHelper, err := patch.NewHelper(config, r) 145 if err != nil { 146 return ctrl.Result{}, err 147 } 148 err = patchHelper.Patch(ctx, config) 149 return ctrl.Result{}, err 150 // If we've already embedded a time-limited join token into a config, but are still waiting for the token to be used, refresh it 151 case config.Status.Ready && (config.Spec.JoinConfiguration != nil && config.Spec.JoinConfiguration.Discovery.BootstrapToken != nil): 152 token := config.Spec.JoinConfiguration.Discovery.BootstrapToken.Token 153 154 // gets the remote secret interface client for the current cluster 155 secretsClient, err := r.SecretsClientFactory.NewSecretsClient(r.Client, cluster) 156 if err != nil { 157 return ctrl.Result{}, err 158 } 159 160 log.Info("refreshing token until the infrastructure has a chance to consume it") 161 err = refreshToken(secretsClient, token) 162 if err != nil { 163 // It would be nice to re-create the bootstrap token if the error was "not found", but we have no way to update the Machine's bootstrap data 164 return ctrl.Result{}, errors.Wrapf(err, "failed to refresh bootstrap token") 165 } 166 // NB: this may not be sufficient to keep the token live if we don't see it before it expires, but when we generate a config we will set the status to "ready" which should generate an update event 167 return ctrl.Result{ 168 RequeueAfter: DefaultTokenTTL / 2, 169 }, nil 170 } 171 172 // Initialize the patch helper 173 patchHelper, err := patch.NewHelper(config, r) 174 if err != nil { 175 return ctrl.Result{}, err 176 } 177 // Attempt to Patch the KubeadmConfig object and status after each reconciliation if no error occurs. 178 defer func() { 179 if rerr == nil { 180 if rerr = patchHelper.Patch(ctx, config); rerr != nil { 181 log.Error(rerr, "failed to patch config") 182 } 183 } 184 }() 185 186 if !cluster.Status.ControlPlaneInitialized { 187 // if it's NOT a control plane machine, requeue 188 if !util.IsControlPlaneMachine(machine) { 189 log.Info(fmt.Sprintf("Machine is not a control plane. If it should be a control plane, add `%s: true` as a label to the Machine", clusterv1.MachineControlPlaneLabelName)) 190 return ctrl.Result{RequeueAfter: 30 * time.Second}, nil 191 } 192 193 // if the machine has not ClusterConfiguration and InitConfiguration, requeue 194 if config.Spec.InitConfiguration == nil && config.Spec.ClusterConfiguration == nil { 195 log.Info("Control plane is not ready, requeing joining control planes until ready.") 196 return ctrl.Result{RequeueAfter: 30 * time.Second}, nil 197 } 198 199 // acquire the init lock so that only the first machine configured 200 // as control plane get processed here 201 // if not the first, requeue 202 if !r.KubeadmInitLock.Lock(ctx, cluster, machine) { 203 log.Info("A control plane is already being initialized, requeing until control plane is ready") 204 return ctrl.Result{RequeueAfter: 30 * time.Second}, nil 205 } 206 207 defer func() { 208 if rerr != nil { 209 r.KubeadmInitLock.Unlock(ctx, cluster) 210 } 211 }() 212 213 log.Info("Creating BootstrapData for the init control plane") 214 215 // Nb. in this case JoinConfiguration should not be defined by users, but in case of misconfigurations, CABPK simply ignore it 216 217 // get both of ClusterConfiguration and InitConfiguration strings to pass to the cloud init control plane generator 218 // kubeadm allows one of these values to be empty; CABPK replace missing values with an empty config, so the cloud init generation 219 // should not handle special cases. 220 221 if config.Spec.InitConfiguration == nil { 222 config.Spec.InitConfiguration = &kubeadmv1beta1.InitConfiguration{ 223 TypeMeta: v1.TypeMeta{ 224 APIVersion: "kubeadm.k8s.io/v1beta1", 225 Kind: "InitConfiguration", 226 }, 227 } 228 } 229 initdata, err := kubeadmv1beta1.ConfigurationToYAML(config.Spec.InitConfiguration) 230 if err != nil { 231 log.Error(err, "failed to marshal init configuration") 232 return ctrl.Result{}, err 233 } 234 235 if config.Spec.ClusterConfiguration == nil { 236 config.Spec.ClusterConfiguration = &kubeadmv1beta1.ClusterConfiguration{ 237 TypeMeta: v1.TypeMeta{ 238 APIVersion: "kubeadm.k8s.io/v1beta1", 239 Kind: "ClusterConfiguration", 240 }, 241 } 242 } 243 244 // injects into config.ClusterConfiguration values from top level object 245 r.reconcileTopLevelObjectSettings(cluster, machine, config) 246 247 clusterdata, err := kubeadmv1beta1.ConfigurationToYAML(config.Spec.ClusterConfiguration) 248 if err != nil { 249 log.Error(err, "failed to marshal cluster configuration") 250 return ctrl.Result{}, err 251 } 252 253 certificates := internalcluster.NewCertificatesForInitialControlPlane(config.Spec.ClusterConfiguration) 254 if err := certificates.LookupOrGenerate(ctx, r.Client, cluster, config); err != nil { 255 log.Error(err, "unable to lookup or create cluster certificates") 256 return ctrl.Result{}, err 257 } 258 259 cloudInitData, err := cloudinit.NewInitControlPlane(&cloudinit.ControlPlaneInput{ 260 BaseUserData: cloudinit.BaseUserData{ 261 AdditionalFiles: config.Spec.Files, 262 NTP: config.Spec.NTP, 263 PreKubeadmCommands: config.Spec.PreKubeadmCommands, 264 PostKubeadmCommands: config.Spec.PostKubeadmCommands, 265 Users: config.Spec.Users, 266 }, 267 InitConfiguration: initdata, 268 ClusterConfiguration: clusterdata, 269 Certificates: certificates, 270 }) 271 if err != nil { 272 log.Error(err, "failed to generate cloud init for bootstrap control plane") 273 return ctrl.Result{}, err 274 } 275 276 config.Status.BootstrapData = cloudInitData 277 config.Status.Ready = true 278 279 return ctrl.Result{}, nil 280 } 281 282 // Every other case it's a join scenario 283 // Nb. in this case ClusterConfiguration and InitConfiguration should not be defined by users, but in case of misconfigurations, CABPK simply ignore them 284 285 // Unlock any locks that might have been set during init process 286 r.KubeadmInitLock.Unlock(ctx, cluster) 287 288 // if the JoinConfiguration is missing, create a default one 289 if config.Spec.JoinConfiguration == nil { 290 log.Info("Creating default JoinConfiguration") 291 config.Spec.JoinConfiguration = &kubeadmv1beta1.JoinConfiguration{} 292 } 293 294 // it's a control plane join 295 if util.IsControlPlaneMachine(machine) { 296 if config.Spec.JoinConfiguration.ControlPlane == nil { 297 config.Spec.JoinConfiguration.ControlPlane = &kubeadmv1beta1.JoinControlPlane{} 298 } 299 300 certificates := internalcluster.NewCertificatesForJoiningControlPlane() 301 if err := certificates.Lookup(ctx, r.Client, cluster); err != nil { 302 log.Error(err, "unable to lookup cluster certificates") 303 return ctrl.Result{}, err 304 } 305 if err := certificates.EnsureAllExist(); err != nil { 306 return ctrl.Result{}, err 307 } 308 309 // ensure that joinConfiguration.Discovery is properly set for joining node on the current cluster 310 if err := r.reconcileDiscovery(cluster, config, certificates); err != nil { 311 if requeueErr, ok := errors.Cause(err).(capierrors.HasRequeueAfterError); ok { 312 log.Info(err.Error()) 313 return ctrl.Result{RequeueAfter: requeueErr.GetRequeueAfter()}, nil 314 } 315 return ctrl.Result{}, err 316 } 317 318 joinData, err := kubeadmv1beta1.ConfigurationToYAML(config.Spec.JoinConfiguration) 319 if err != nil { 320 log.Error(err, "failed to marshal join configuration") 321 return ctrl.Result{}, err 322 } 323 324 log.Info("Creating BootstrapData for the join control plane") 325 cloudJoinData, err := cloudinit.NewJoinControlPlane(&cloudinit.ControlPlaneJoinInput{ 326 JoinConfiguration: joinData, 327 Certificates: certificates, 328 BaseUserData: cloudinit.BaseUserData{ 329 AdditionalFiles: config.Spec.Files, 330 NTP: config.Spec.NTP, 331 PreKubeadmCommands: config.Spec.PreKubeadmCommands, 332 PostKubeadmCommands: config.Spec.PostKubeadmCommands, 333 Users: config.Spec.Users, 334 }, 335 }) 336 if err != nil { 337 log.Error(err, "failed to create a control plane join configuration") 338 return ctrl.Result{}, err 339 } 340 341 config.Status.BootstrapData = cloudJoinData 342 config.Status.Ready = true 343 return ctrl.Result{}, nil 344 } 345 346 // It's a worker join 347 certificates := internalcluster.NewCertificatesForWorker(config.Spec.JoinConfiguration.CACertPath) 348 if err := certificates.Lookup(ctx, r.Client, cluster); err != nil { 349 log.Error(err, "unable to lookup cluster certificates") 350 return ctrl.Result{}, err 351 } 352 if err := certificates.EnsureAllExist(); err != nil { 353 log.Error(err, "Missing certificates") 354 return ctrl.Result{}, err 355 } 356 357 // ensure that joinConfiguration.Discovery is properly set for joining node on the current cluster 358 if err := r.reconcileDiscovery(cluster, config, certificates); err != nil { 359 if requeueErr, ok := errors.Cause(err).(capierrors.HasRequeueAfterError); ok { 360 log.Info(err.Error()) 361 return ctrl.Result{RequeueAfter: requeueErr.GetRequeueAfter()}, nil 362 } 363 return ctrl.Result{}, err 364 } 365 366 joinData, err := kubeadmv1beta1.ConfigurationToYAML(config.Spec.JoinConfiguration) 367 if err != nil { 368 log.Error(err, "failed to marshal join configuration") 369 return ctrl.Result{}, err 370 } 371 372 if config.Spec.JoinConfiguration.ControlPlane != nil { 373 return ctrl.Result{}, errors.New("Machine is a Worker, but JoinConfiguration.ControlPlane is set in the KubeadmConfig object") 374 } 375 376 log.Info("Creating BootstrapData for the worker node") 377 378 cloudJoinData, err := cloudinit.NewNode(&cloudinit.NodeInput{ 379 BaseUserData: cloudinit.BaseUserData{ 380 AdditionalFiles: config.Spec.Files, 381 NTP: config.Spec.NTP, 382 PreKubeadmCommands: config.Spec.PreKubeadmCommands, 383 PostKubeadmCommands: config.Spec.PostKubeadmCommands, 384 Users: config.Spec.Users, 385 }, 386 JoinConfiguration: joinData, 387 }) 388 if err != nil { 389 log.Error(err, "failed to create a worker join configuration") 390 return ctrl.Result{}, err 391 } 392 config.Status.BootstrapData = cloudJoinData 393 config.Status.Ready = true 394 return ctrl.Result{}, nil 395 } 396 397 // ClusterToKubeadmConfigs is a handler.ToRequestsFunc to be used to enqeue 398 // requests for reconciliation of KubeadmConfigs. 399 func (r *KubeadmConfigReconciler) ClusterToKubeadmConfigs(o handler.MapObject) []ctrl.Request { 400 result := []ctrl.Request{} 401 402 c, ok := o.Object.(*clusterv1.Cluster) 403 if !ok { 404 r.Log.Error(errors.Errorf("expected a Cluster but got a %T", o.Object), "failed to get Machine for Cluster") 405 return nil 406 } 407 408 selectors := []client.ListOption{ 409 client.InNamespace(c.Namespace), 410 client.MatchingLabels{ 411 clusterv1.MachineClusterLabelName: c.Name, 412 }, 413 } 414 415 machineList := &clusterv1.MachineList{} 416 if err := r.List(context.Background(), machineList, selectors...); err != nil { 417 r.Log.Error(err, "failed to list Machines", "Cluster", c.Name, "Namespace", c.Namespace) 418 return nil 419 } 420 421 for _, m := range machineList.Items { 422 if m.Spec.Bootstrap.ConfigRef != nil && 423 m.Spec.Bootstrap.ConfigRef.GroupVersionKind() == bootstrapv1.GroupVersion.WithKind("KubeadmConfig") { 424 name := client.ObjectKey{Namespace: m.Namespace, Name: m.Spec.Bootstrap.ConfigRef.Name} 425 result = append(result, ctrl.Request{NamespacedName: name}) 426 } 427 } 428 429 return result 430 } 431 432 // MachineToBootstrapMapFunc is a handler.ToRequestsFunc to be used to enqeue 433 // request for reconciliation of KubeadmConfig. 434 func (r *KubeadmConfigReconciler) MachineToBootstrapMapFunc(o handler.MapObject) []ctrl.Request { 435 result := []ctrl.Request{} 436 437 m, ok := o.Object.(*clusterv1.Machine) 438 if !ok { 439 return nil 440 } 441 if m.Spec.Bootstrap.ConfigRef != nil && m.Spec.Bootstrap.ConfigRef.GroupVersionKind() == bootstrapv1.GroupVersion.WithKind("KubeadmConfig") { 442 name := client.ObjectKey{Namespace: m.Namespace, Name: m.Spec.Bootstrap.ConfigRef.Name} 443 result = append(result, ctrl.Request{NamespacedName: name}) 444 } 445 return result 446 } 447 448 // reconcileDiscovery ensures that config.JoinConfiguration.Discovery is properly set for the joining node. 449 // The implementation func respect user provided discovery configurations, but in case some of them are missing, a valid BootstrapToken object 450 // is automatically injected into config.JoinConfiguration.Discovery. 451 // This allows to simplify configuration UX, by providing the option to delegate to CABPK the configuration of kubeadm join discovery. 452 func (r *KubeadmConfigReconciler) reconcileDiscovery(cluster *clusterv1.Cluster, config *bootstrapv1.KubeadmConfig, certificates internalcluster.Certificates) error { 453 log := r.Log.WithValues("kubeadmconfig", fmt.Sprintf("%s/%s", config.Namespace, config.Name)) 454 455 // if config already contains a file discovery configuration, respect it without further validations 456 if config.Spec.JoinConfiguration.Discovery.File != nil { 457 return nil 458 } 459 460 // otherwise it is necessary to ensure token discovery is properly configured 461 if config.Spec.JoinConfiguration.Discovery.BootstrapToken == nil { 462 config.Spec.JoinConfiguration.Discovery.BootstrapToken = &kubeadmv1beta1.BootstrapTokenDiscovery{} 463 } 464 465 // calculate the ca cert hashes if they are not already set 466 if len(config.Spec.JoinConfiguration.Discovery.BootstrapToken.CACertHashes) == 0 { 467 hashes, err := certificates.GetByPurpose(secret.ClusterCA).Hashes() 468 if err != nil { 469 log.Error(err, "Unable to generate Cluster CA certificate hashes") 470 return err 471 } 472 config.Spec.JoinConfiguration.Discovery.BootstrapToken.CACertHashes = hashes 473 } 474 475 // if BootstrapToken already contains an APIServerEndpoint, respect it; otherwise inject the APIServerEndpoint endpoint defined in cluster status 476 apiServerEndpoint := config.Spec.JoinConfiguration.Discovery.BootstrapToken.APIServerEndpoint 477 if apiServerEndpoint == "" { 478 if len(cluster.Status.APIEndpoints) == 0 { 479 return errors.Wrap(&capierrors.RequeueAfterError{RequeueAfter: 10 * time.Second}, "Waiting for Cluster Controller to set cluster.Status.APIEndpoints") 480 } 481 482 // NB. CABPK only uses the first APIServerEndpoint defined in cluster status if there are multiple defined. 483 apiServerEndpoint = fmt.Sprintf("%s:%d", cluster.Status.APIEndpoints[0].Host, cluster.Status.APIEndpoints[0].Port) 484 config.Spec.JoinConfiguration.Discovery.BootstrapToken.APIServerEndpoint = apiServerEndpoint 485 log.Info("Altering JoinConfiguration.Discovery.BootstrapToken", "APIServerEndpoint", apiServerEndpoint) 486 } 487 488 // if BootstrapToken already contains a token, respect it; otherwise create a new bootstrap token for the node to join 489 if config.Spec.JoinConfiguration.Discovery.BootstrapToken.Token == "" { 490 // gets the remote secret interface client for the current cluster 491 secretsClient, err := r.SecretsClientFactory.NewSecretsClient(r.Client, cluster) 492 if err != nil { 493 return err 494 } 495 496 token, err := createToken(secretsClient) 497 if err != nil { 498 return errors.Wrapf(err, "failed to create new bootstrap token") 499 } 500 501 config.Spec.JoinConfiguration.Discovery.BootstrapToken.Token = token 502 log.Info("Altering JoinConfiguration.Discovery.BootstrapToken", "Token", token) 503 } 504 505 // If the BootstrapToken does not contain any CACertHashes then force skip CA Verification 506 if len(config.Spec.JoinConfiguration.Discovery.BootstrapToken.CACertHashes) == 0 { 507 log.Info("No CAs were provided. Falling back to insecure discover method by skipping CA Cert validation") 508 config.Spec.JoinConfiguration.Discovery.BootstrapToken.UnsafeSkipCAVerification = true 509 } 510 511 return nil 512 } 513 514 // reconcileTopLevelObjectSettings injects into config.ClusterConfiguration values from top level objects like cluster and machine. 515 // The implementation func respect user provided config values, but in case some of them are missing, values from top level objects are used. 516 func (r *KubeadmConfigReconciler) reconcileTopLevelObjectSettings(cluster *clusterv1.Cluster, machine *clusterv1.Machine, config *bootstrapv1.KubeadmConfig) { 517 log := r.Log.WithValues("kubeadmconfig", fmt.Sprintf("%s/%s", config.Namespace, config.Name)) 518 519 // If there are no ControlPlaneEndpoint defined in ClusterConfiguration but there are APIEndpoints defined at cluster level (e.g. the load balancer endpoint), 520 // then use cluster APIEndpoints as a control plane endpoint for the K8s cluster 521 if config.Spec.ClusterConfiguration.ControlPlaneEndpoint == "" && len(cluster.Status.APIEndpoints) > 0 { 522 // NB. CABPK only uses the first APIServerEndpoint defined in cluster status if there are multiple defined. 523 config.Spec.ClusterConfiguration.ControlPlaneEndpoint = fmt.Sprintf("%s:%d", cluster.Status.APIEndpoints[0].Host, cluster.Status.APIEndpoints[0].Port) 524 log.Info("Altering ClusterConfiguration", "ControlPlaneEndpoint", config.Spec.ClusterConfiguration.ControlPlaneEndpoint) 525 } 526 527 // If there are no ClusterName defined in ClusterConfiguration, use Cluster.Name 528 if config.Spec.ClusterConfiguration.ClusterName == "" { 529 config.Spec.ClusterConfiguration.ClusterName = cluster.Name 530 log.Info("Altering ClusterConfiguration", "ClusterName", config.Spec.ClusterConfiguration.ClusterName) 531 } 532 533 // If there are no Network settings defined in ClusterConfiguration, use ClusterNetwork settings, if defined 534 if cluster.Spec.ClusterNetwork != nil { 535 if config.Spec.ClusterConfiguration.Networking.DNSDomain == "" && cluster.Spec.ClusterNetwork.ServiceDomain != "" { 536 config.Spec.ClusterConfiguration.Networking.DNSDomain = cluster.Spec.ClusterNetwork.ServiceDomain 537 log.Info("Altering ClusterConfiguration", "DNSDomain", config.Spec.ClusterConfiguration.Networking.DNSDomain) 538 } 539 if config.Spec.ClusterConfiguration.Networking.ServiceSubnet == "" && 540 cluster.Spec.ClusterNetwork.Services != nil && 541 len(cluster.Spec.ClusterNetwork.Services.CIDRBlocks) > 0 { 542 config.Spec.ClusterConfiguration.Networking.ServiceSubnet = strings.Join(cluster.Spec.ClusterNetwork.Services.CIDRBlocks, "") 543 log.Info("Altering ClusterConfiguration", "ServiceSubnet", config.Spec.ClusterConfiguration.Networking.ServiceSubnet) 544 } 545 if config.Spec.ClusterConfiguration.Networking.PodSubnet == "" && 546 cluster.Spec.ClusterNetwork.Pods != nil && 547 len(cluster.Spec.ClusterNetwork.Pods.CIDRBlocks) > 0 { 548 config.Spec.ClusterConfiguration.Networking.PodSubnet = strings.Join(cluster.Spec.ClusterNetwork.Pods.CIDRBlocks, "") 549 log.Info("Altering ClusterConfiguration", "PodSubnet", config.Spec.ClusterConfiguration.Networking.PodSubnet) 550 } 551 } 552 553 // If there are no KubernetesVersion settings defined in ClusterConfiguration, use Version from machine, if defined 554 if config.Spec.ClusterConfiguration.KubernetesVersion == "" && machine.Spec.Version != nil { 555 config.Spec.ClusterConfiguration.KubernetesVersion = *machine.Spec.Version 556 log.Info("Altering ClusterConfiguration", "KubernetesVersion", config.Spec.ClusterConfiguration.KubernetesVersion) 557 } 558 }