|
| 1 | +/* |
| 2 | +Copyright 2020 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 internal |
| 18 | + |
| 19 | +import ( |
| 20 | + "github.com/go-logr/logr" |
| 21 | + corev1 "k8s.io/api/core/v1" |
| 22 | + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 23 | + "k8s.io/apiserver/pkg/storage/names" |
| 24 | + clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3" |
| 25 | + bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3" |
| 26 | + controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1alpha3" |
| 27 | + "sigs.k8s.io/cluster-api/controlplane/kubeadm/internal/hash" |
| 28 | + "sigs.k8s.io/cluster-api/controlplane/kubeadm/internal/machinefilters" |
| 29 | +) |
| 30 | + |
| 31 | +// ControlPlane holds business logic around control planes. |
| 32 | +// It should never need to connect to a service, that responsibility lies outside of this struct. |
| 33 | +type ControlPlane struct { |
| 34 | + KCP *controlplanev1.KubeadmControlPlane |
| 35 | + Cluster *clusterv1.Cluster |
| 36 | + Machines FilterableMachineCollection |
| 37 | +} |
| 38 | + |
| 39 | +// NewControlPlane returns an instantiated ControlPlane. |
| 40 | +func NewControlPlane(cluster *clusterv1.Cluster, kcp *controlplanev1.KubeadmControlPlane, ownedMachines FilterableMachineCollection) *ControlPlane { |
| 41 | + return &ControlPlane{ |
| 42 | + KCP: kcp, |
| 43 | + Cluster: cluster, |
| 44 | + Machines: ownedMachines, |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +// Logger returns a logger with useful context. |
| 49 | +func (c *ControlPlane) Logger() logr.Logger { |
| 50 | + return Log.WithValues("namespace", c.KCP.Namespace, "name", c.KCP.Name, "cluster-nanme", c.Cluster.Name) |
| 51 | +} |
| 52 | + |
| 53 | +// Version returns the KubeadmControlPlane's version. |
| 54 | +func (c *ControlPlane) Version() *string { |
| 55 | + return &c.KCP.Spec.Version |
| 56 | +} |
| 57 | + |
| 58 | +// InfrastructureTemplate returns the KubeadmControlPlane's infrastructure template. |
| 59 | +func (c *ControlPlane) InfrastructureTemplate() *corev1.ObjectReference { |
| 60 | + return &c.KCP.Spec.InfrastructureTemplate |
| 61 | +} |
| 62 | + |
| 63 | +// ConfigurationHash returns the hash of the KubeadmControlPlane spec. |
| 64 | +func (c *ControlPlane) ConfigurationHash() string { |
| 65 | + return hash.Compute(&c.KCP.Spec) |
| 66 | +} |
| 67 | + |
| 68 | +// AsOwnerReference returns an owner reference to the KubeadmControlPlane. |
| 69 | +func (c *ControlPlane) AsOwnerReference() *metav1.OwnerReference { |
| 70 | + return &metav1.OwnerReference{ |
| 71 | + APIVersion: controlplanev1.GroupVersion.String(), |
| 72 | + Kind: "KubeadmControlPlane", |
| 73 | + Name: c.KCP.Name, |
| 74 | + UID: c.KCP.UID, |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +// EtcdImageData returns the etcd image data embedded in the ClusterConfiguration or empty strings if none are defined. |
| 79 | +func (c *ControlPlane) EtcdImageData() (string, string) { |
| 80 | + if c.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration != nil && c.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local != nil { |
| 81 | + meta := c.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.Etcd.Local.ImageMeta |
| 82 | + return meta.ImageRepository, meta.ImageTag |
| 83 | + } |
| 84 | + return "", "" |
| 85 | +} |
| 86 | + |
| 87 | +// MachinesNeedingUpgrade return a list of machines that need to be upgraded. |
| 88 | +func (c *ControlPlane) MachinesNeedingUpgrade() FilterableMachineCollection { |
| 89 | + now := metav1.Now() |
| 90 | + var requireUpgrade FilterableMachineCollection |
| 91 | + if c.KCP.Spec.UpgradeAfter != nil && c.KCP.Spec.UpgradeAfter.Before(&now) { |
| 92 | + requireUpgrade = c.Machines.AnyFilter( |
| 93 | + machinefilters.Not(machinefilters.MatchesConfigurationHash(hash.Compute(&c.KCP.Spec))), |
| 94 | + machinefilters.OlderThan(c.KCP.Spec.UpgradeAfter), |
| 95 | + ) |
| 96 | + } else { |
| 97 | + requireUpgrade = c.Machines.Filter( |
| 98 | + machinefilters.Not(machinefilters.MatchesConfigurationHash(hash.Compute(&c.KCP.Spec))), |
| 99 | + ) |
| 100 | + } |
| 101 | + return requireUpgrade |
| 102 | +} |
| 103 | + |
| 104 | +// FailureDomainWithMost returns the failure domain with the most number of machines. |
| 105 | +// Used when scaling down. |
| 106 | +func (c *ControlPlane) FailureDomainWithMost() *string { |
| 107 | + // See if there are any Machines that are not in currently defined failure domains first. |
| 108 | + notInFailureDomains := c.Machines.Filter( |
| 109 | + machinefilters.Not(machinefilters.InFailureDomains(c.Cluster.Status.FailureDomains.FilterControlPlane().GetIDs()...)), |
| 110 | + ) |
| 111 | + if len(notInFailureDomains) > 0 { |
| 112 | + // return the failure domain for the oldest Machine not in the current list of failure domains |
| 113 | + // this could be either nil (no failure domain defined) or a failure domain that is no longer defined |
| 114 | + // in the cluster status. |
| 115 | + return notInFailureDomains.Oldest().Spec.FailureDomain |
| 116 | + } |
| 117 | + |
| 118 | + // Otherwise pick the currently known failure domain with the most Machines |
| 119 | + return PickMost(c.Cluster.Status.FailureDomains.FilterControlPlane(), c.Machines) |
| 120 | +} |
| 121 | + |
| 122 | +// FailureDomainWithFewest returns the failure domain with the fewest number of machines. |
| 123 | +// Used when scaling up. |
| 124 | +func (c *ControlPlane) FailureDomainWithFewest() *string { |
| 125 | + if len(c.Cluster.Status.FailureDomains.FilterControlPlane()) == 0 { |
| 126 | + return nil |
| 127 | + } |
| 128 | + return PickFewest(c.Cluster.Status.FailureDomains.FilterControlPlane(), c.Machines) |
| 129 | +} |
| 130 | + |
| 131 | +// InitialControlPlaneConfig returns a new KubeadmConfigSpec that is to be used for an initializing control plane. |
| 132 | +func (c *ControlPlane) InitialControlPlaneConfig() *bootstrapv1.KubeadmConfigSpec { |
| 133 | + bootstrapSpec := c.KCP.Spec.KubeadmConfigSpec.DeepCopy() |
| 134 | + bootstrapSpec.JoinConfiguration = nil |
| 135 | + return bootstrapSpec |
| 136 | +} |
| 137 | + |
| 138 | +// JoinControlPlaneConfig returns a new KubeadmConfigSpec that is to be used for joining control planes. |
| 139 | +func (c *ControlPlane) JoinControlPlaneConfig() *bootstrapv1.KubeadmConfigSpec { |
| 140 | + bootstrapSpec := c.KCP.Spec.KubeadmConfigSpec.DeepCopy() |
| 141 | + bootstrapSpec.InitConfiguration = nil |
| 142 | + bootstrapSpec.ClusterConfiguration = nil |
| 143 | + return bootstrapSpec |
| 144 | +} |
| 145 | + |
| 146 | +// GenerateKubeadmConfig generates a new kubeadm config for creating new control plane nodes. |
| 147 | +func (c *ControlPlane) GenerateKubeadmConfig(spec *bootstrapv1.KubeadmConfigSpec) *bootstrapv1.KubeadmConfig { |
| 148 | + // Create an owner reference without a controller reference because the owning controller is the machine controller |
| 149 | + owner := metav1.OwnerReference{ |
| 150 | + APIVersion: controlplanev1.GroupVersion.String(), |
| 151 | + Kind: "KubeadmControlPlane", |
| 152 | + Name: c.KCP.Name, |
| 153 | + UID: c.KCP.UID, |
| 154 | + } |
| 155 | + |
| 156 | + bootstrapConfig := &bootstrapv1.KubeadmConfig{ |
| 157 | + ObjectMeta: metav1.ObjectMeta{ |
| 158 | + Name: names.SimpleNameGenerator.GenerateName(c.KCP.Name + "-"), |
| 159 | + Namespace: c.KCP.Namespace, |
| 160 | + Labels: ControlPlaneLabelsForClusterWithHash(c.Cluster.Name, c.ConfigurationHash()), |
| 161 | + OwnerReferences: []metav1.OwnerReference{owner}, |
| 162 | + }, |
| 163 | + Spec: *spec, |
| 164 | + } |
| 165 | + return bootstrapConfig |
| 166 | +} |
| 167 | + |
| 168 | +// NewMachine returns a machine configured to be a part of the control plane. |
| 169 | +func (c *ControlPlane) NewMachine(infraRef, bootstrapRef *corev1.ObjectReference, failureDomain *string) *clusterv1.Machine { |
| 170 | + return &clusterv1.Machine{ |
| 171 | + ObjectMeta: metav1.ObjectMeta{ |
| 172 | + Name: names.SimpleNameGenerator.GenerateName(c.KCP.Name + "-"), |
| 173 | + Namespace: c.KCP.Namespace, |
| 174 | + Labels: ControlPlaneLabelsForClusterWithHash(c.Cluster.Name, c.ConfigurationHash()), |
| 175 | + OwnerReferences: []metav1.OwnerReference{ |
| 176 | + *metav1.NewControllerRef(c.KCP, controlplanev1.GroupVersion.WithKind("KubeadmControlPlane")), |
| 177 | + }, |
| 178 | + }, |
| 179 | + Spec: clusterv1.MachineSpec{ |
| 180 | + ClusterName: c.Cluster.Name, |
| 181 | + Version: c.Version(), |
| 182 | + InfrastructureRef: *infraRef, |
| 183 | + Bootstrap: clusterv1.Bootstrap{ |
| 184 | + ConfigRef: bootstrapRef, |
| 185 | + }, |
| 186 | + FailureDomain: failureDomain, |
| 187 | + }, |
| 188 | + } |
| 189 | +} |
| 190 | + |
| 191 | +// NeedsReplacementNode determines if the control plane needs to create a replacement node during upgrade. |
| 192 | +func (c *ControlPlane) NeedsReplacementNode() bool { |
| 193 | + // Can't do anything with an unknown number of desired replicas. |
| 194 | + if c.KCP.Spec.Replicas == nil { |
| 195 | + return false |
| 196 | + } |
| 197 | + // if the number of existing machines is exactly 1 > than the number of replicas. |
| 198 | + return len(c.Machines)+1 == int(*c.KCP.Spec.Replicas) |
| 199 | +} |
| 200 | + |
| 201 | +// HasDeletingMachine returns true if any machine in the control plane is in the process of being deleted. |
| 202 | +func (c *ControlPlane) HasDeletingMachine() bool { |
| 203 | + return len(c.Machines.Filter(machinefilters.HasDeletionTimestamp)) > 0 |
| 204 | +} |
0 commit comments