sigs.k8s.io/cluster-api@v1.6.3/internal/controllers/topology/cluster/patches/variables/variables.go (about) 1 /* 2 Copyright 2021 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 variables calculates variables for patching. 18 package variables 19 20 import ( 21 "encoding/json" 22 23 "github.com/pkg/errors" 24 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 25 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 26 "k8s.io/utils/pointer" 27 28 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 29 expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1" 30 runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1" 31 "sigs.k8s.io/cluster-api/internal/contract" 32 ) 33 34 const ( 35 // BuiltinsName is the name of the builtin variable. 36 BuiltinsName = "builtin" 37 // emptyDefinitionFrom may be supplied in variable values. 38 emptyDefinitionFrom = "" 39 ) 40 41 // Builtins represents builtin variables exposed through patches. 42 type Builtins struct { 43 Cluster *ClusterBuiltins `json:"cluster,omitempty"` 44 ControlPlane *ControlPlaneBuiltins `json:"controlPlane,omitempty"` 45 MachineDeployment *MachineDeploymentBuiltins `json:"machineDeployment,omitempty"` 46 MachinePool *MachinePoolBuiltins `json:"machinePool,omitempty"` 47 } 48 49 // ClusterBuiltins represents builtin cluster variables. 50 type ClusterBuiltins struct { 51 // Name is the name of the cluster. 52 Name string `json:"name,omitempty"` 53 54 // Namespace is the namespace of the cluster. 55 Namespace string `json:"namespace,omitempty"` 56 57 // Topology represents the cluster topology variables. 58 Topology *ClusterTopologyBuiltins `json:"topology,omitempty"` 59 60 // Network represents the cluster network variables. 61 Network *ClusterNetworkBuiltins `json:"network,omitempty"` 62 } 63 64 // ClusterTopologyBuiltins represents builtin cluster topology variables. 65 type ClusterTopologyBuiltins struct { 66 // Version is the Kubernetes version of the Cluster. 67 // NOTE: Please note that this version might temporarily differ from the version 68 // of the ControlPlane or workers while an upgrade process is being orchestrated. 69 Version string `json:"version,omitempty"` 70 71 // Class is the name of the ClusterClass of the Cluster. 72 Class string `json:"class,omitempty"` 73 } 74 75 // ClusterNetworkBuiltins represents builtin cluster network variables. 76 type ClusterNetworkBuiltins struct { 77 // ServiceDomain is the domain name for services. 78 ServiceDomain *string `json:"serviceDomain,omitempty"` 79 // Services is the network ranges from which service VIPs are allocated. 80 Services []string `json:"services,omitempty"` 81 // Pods is the network ranges from which Pod networks are allocated. 82 Pods []string `json:"pods,omitempty"` 83 // IPFamily is the IPFamily the Cluster is operating in. One of Invalid, IPv4, IPv6, DualStack. 84 // Note: IPFamily is not a concept in Kubernetes. It was originally introduced in CAPI for CAPD. 85 // IPFamily may be dropped in a future release. More details at https://github.com/kubernetes-sigs/cluster-api/issues/7521 86 IPFamily string `json:"ipFamily,omitempty"` 87 } 88 89 // ControlPlaneBuiltins represents builtin ControlPlane variables. 90 // NOTE: These variables are only set for templates belonging to the ControlPlane object. 91 type ControlPlaneBuiltins struct { 92 // Version is the Kubernetes version of the ControlPlane object. 93 // NOTE: Please note that this version is the version we are currently reconciling towards. 94 // It can differ from the current version of the ControlPlane while an upgrade process is 95 // being orchestrated. 96 Version string `json:"version,omitempty"` 97 98 // Name is the name of the ControlPlane, 99 // to which the current template belongs to. 100 Name string `json:"name,omitempty"` 101 102 // Replicas is the value of the replicas field of the ControlPlane object. 103 Replicas *int64 `json:"replicas,omitempty"` 104 105 // MachineTemplate is the value of the .spec.machineTemplate field of the ControlPlane object. 106 MachineTemplate *ControlPlaneMachineTemplateBuiltins `json:"machineTemplate,omitempty"` 107 } 108 109 // ControlPlaneMachineTemplateBuiltins is the value of the .spec.machineTemplate field of the ControlPlane object. 110 type ControlPlaneMachineTemplateBuiltins struct { 111 // InfrastructureRef is the value of the infrastructureRef field of ControlPlane.spec.machineTemplate. 112 InfrastructureRef ControlPlaneMachineTemplateInfrastructureRefBuiltins `json:"infrastructureRef,omitempty"` 113 } 114 115 // ControlPlaneMachineTemplateInfrastructureRefBuiltins is the value of the infrastructureRef field of 116 // ControlPlane.spec.machineTemplate. 117 type ControlPlaneMachineTemplateInfrastructureRefBuiltins struct { 118 // Name of the infrastructureRef. 119 Name string `json:"name,omitempty"` 120 } 121 122 // MachineDeploymentBuiltins represents builtin MachineDeployment variables. 123 // NOTE: These variables are only set for templates belonging to a MachineDeployment. 124 type MachineDeploymentBuiltins struct { 125 // Version is the Kubernetes version of the MachineDeployment, 126 // to which the current template belongs to. 127 // NOTE: Please note that this version is the version we are currently reconciling towards. 128 // It can differ from the current version of the MachineDeployment machines while an upgrade process is 129 // being orchestrated. 130 Version string `json:"version,omitempty"` 131 132 // Class is the class name of the MachineDeployment, 133 // to which the current template belongs to. 134 Class string `json:"class,omitempty"` 135 136 // Name is the name of the MachineDeployment, 137 // to which the current template belongs to. 138 Name string `json:"name,omitempty"` 139 140 // TopologyName is the topology name of the MachineDeployment, 141 // to which the current template belongs to. 142 TopologyName string `json:"topologyName,omitempty"` 143 144 // Replicas is the value of the replicas field of the MachineDeployment, 145 // to which the current template belongs to. 146 Replicas *int64 `json:"replicas,omitempty"` 147 148 // Bootstrap is the value of the .spec.template.spec.bootstrap field of the MachineDeployment. 149 Bootstrap *MachineBootstrapBuiltins `json:"bootstrap,omitempty"` 150 151 // InfrastructureRef is the value of the .spec.template.spec.infrastructureRef field of the MachineDeployment. 152 InfrastructureRef *MachineInfrastructureRefBuiltins `json:"infrastructureRef,omitempty"` 153 } 154 155 // MachinePoolBuiltins represents builtin MachinePool variables. 156 // NOTE: These variables are only set for templates belonging to a MachinePool. 157 type MachinePoolBuiltins struct { 158 // Version is the Kubernetes version of the MachinePool, 159 // to which the current template belongs to. 160 // NOTE: Please note that this version is the version we are currently reconciling towards. 161 // It can differ from the current version of the MachinePool machines while an upgrade process is 162 // being orchestrated. 163 Version string `json:"version,omitempty"` 164 165 // Class is the class name of the MachinePool, 166 // to which the current template belongs to. 167 Class string `json:"class,omitempty"` 168 169 // Name is the name of the MachinePool, 170 // to which the current template belongs to. 171 Name string `json:"name,omitempty"` 172 173 // TopologyName is the topology name of the MachinePool, 174 // to which the current template belongs to. 175 TopologyName string `json:"topologyName,omitempty"` 176 177 // Replicas is the value of the replicas field of the MachinePool, 178 // to which the current template belongs to. 179 Replicas *int64 `json:"replicas,omitempty"` 180 181 // Bootstrap is the value of the .spec.template.spec.bootstrap field of the MachinePool. 182 Bootstrap *MachineBootstrapBuiltins `json:"bootstrap,omitempty"` 183 184 // InfrastructureRef is the value of the .spec.template.spec.infrastructureRef field of the MachinePool. 185 InfrastructureRef *MachineInfrastructureRefBuiltins `json:"infrastructureRef,omitempty"` 186 } 187 188 // MachineBootstrapBuiltins is the value of the .spec.template.spec.bootstrap field 189 // of the MachineDeployment or MachinePool. 190 type MachineBootstrapBuiltins struct { 191 // ConfigRef is the value of the .spec.template.spec.bootstrap.configRef field of the MachineDeployment. 192 ConfigRef *MachineBootstrapConfigRefBuiltins `json:"configRef,omitempty"` 193 } 194 195 // MachineBootstrapConfigRefBuiltins is the value of the .spec.template.spec.bootstrap.configRef 196 // field of the MachineDeployment or MachinePool. 197 type MachineBootstrapConfigRefBuiltins struct { 198 // Name of the bootstrap.configRef. 199 Name string `json:"name,omitempty"` 200 } 201 202 // MachineInfrastructureRefBuiltins is the value of the .spec.template.spec.infrastructureRef field 203 // of the MachineDeployment or MachinePool. 204 type MachineInfrastructureRefBuiltins struct { 205 // Name of the infrastructureRef. 206 Name string `json:"name,omitempty"` 207 } 208 209 // Global returns variables that apply to all the templates, including user provided variables 210 // and builtin variables for the Cluster object. 211 func Global(clusterTopology *clusterv1.Topology, cluster *clusterv1.Cluster, definitionFrom string, patchVariableDefinitions map[string]bool) ([]runtimehooksv1.Variable, error) { 212 variables := []runtimehooksv1.Variable{} 213 214 // Add user defined variables from Cluster.spec.topology.variables. 215 for _, variable := range clusterTopology.Variables { 216 // Don't add user-defined "builtin" variable. 217 if variable.Name == BuiltinsName { 218 continue 219 } 220 // Add the variable if it is defined for the current patch or it is defined for all the patches. 221 if variable.DefinitionFrom == emptyDefinitionFrom || variable.DefinitionFrom == definitionFrom { 222 // Add the variable if it has a definition from this patch in the ClusterClass. 223 if _, ok := patchVariableDefinitions[variable.Name]; ok { 224 variables = append(variables, runtimehooksv1.Variable{Name: variable.Name, Value: variable.Value}) 225 } 226 } 227 } 228 229 // Construct builtin variable. 230 builtin := Builtins{ 231 Cluster: &ClusterBuiltins{ 232 Name: cluster.Name, 233 Namespace: cluster.Namespace, 234 Topology: &ClusterTopologyBuiltins{ 235 Version: cluster.Spec.Topology.Version, 236 Class: cluster.Spec.Topology.Class, 237 }, 238 }, 239 } 240 if cluster.Spec.ClusterNetwork != nil { 241 clusterNetworkIPFamily, _ := cluster.GetIPFamily() 242 builtin.Cluster.Network = &ClusterNetworkBuiltins{ 243 IPFamily: ipFamilyToString(clusterNetworkIPFamily), 244 } 245 if cluster.Spec.ClusterNetwork.ServiceDomain != "" { 246 builtin.Cluster.Network.ServiceDomain = &cluster.Spec.ClusterNetwork.ServiceDomain 247 } 248 if cluster.Spec.ClusterNetwork.Services != nil && cluster.Spec.ClusterNetwork.Services.CIDRBlocks != nil { 249 builtin.Cluster.Network.Services = cluster.Spec.ClusterNetwork.Services.CIDRBlocks 250 } 251 if cluster.Spec.ClusterNetwork.Pods != nil && cluster.Spec.ClusterNetwork.Pods.CIDRBlocks != nil { 252 builtin.Cluster.Network.Pods = cluster.Spec.ClusterNetwork.Pods.CIDRBlocks 253 } 254 } 255 256 // Add builtin variables derived from the cluster object. 257 variable, err := toVariable(BuiltinsName, builtin) 258 if err != nil { 259 return nil, err 260 } 261 variables = append(variables, *variable) 262 263 return variables, nil 264 } 265 266 // ControlPlane returns variables that apply to templates belonging to the ControlPlane. 267 func ControlPlane(cpTopology *clusterv1.ControlPlaneTopology, cp, cpInfrastructureMachineTemplate *unstructured.Unstructured) ([]runtimehooksv1.Variable, error) { 268 variables := []runtimehooksv1.Variable{} 269 270 // Construct builtin variable. 271 builtin := Builtins{ 272 ControlPlane: &ControlPlaneBuiltins{ 273 Name: cp.GetName(), 274 }, 275 } 276 277 // If it is required to manage the number of replicas for the ControlPlane, set the corresponding variable. 278 // NOTE: If the Cluster.spec.topology.controlPlane.replicas field is nil, the topology reconciler won't set 279 // the replicas field on the ControlPlane. This happens either when the ControlPlane provider does 280 // not implement support for this field or the default value of the ControlPlane is used. 281 if cpTopology.Replicas != nil { 282 replicas, err := contract.ControlPlane().Replicas().Get(cp) 283 if err != nil { 284 return nil, errors.Wrap(err, "failed to get spec.replicas from the ControlPlane") 285 } 286 builtin.ControlPlane.Replicas = replicas 287 } 288 289 version, err := contract.ControlPlane().Version().Get(cp) 290 if err != nil { 291 return nil, errors.Wrap(err, "failed to get spec.version from the ControlPlane") 292 } 293 builtin.ControlPlane.Version = *version 294 295 if cpInfrastructureMachineTemplate != nil { 296 builtin.ControlPlane.MachineTemplate = &ControlPlaneMachineTemplateBuiltins{ 297 InfrastructureRef: ControlPlaneMachineTemplateInfrastructureRefBuiltins{ 298 Name: cpInfrastructureMachineTemplate.GetName(), 299 }, 300 } 301 } 302 303 variable, err := toVariable(BuiltinsName, builtin) 304 if err != nil { 305 return nil, err 306 } 307 variables = append(variables, *variable) 308 309 return variables, nil 310 } 311 312 // MachineDeployment returns variables that apply to templates belonging to a MachineDeployment. 313 func MachineDeployment(mdTopology *clusterv1.MachineDeploymentTopology, md *clusterv1.MachineDeployment, mdBootstrapTemplate, mdInfrastructureMachineTemplate *unstructured.Unstructured, definitionFrom string, patchVariableDefinitions map[string]bool) ([]runtimehooksv1.Variable, error) { 314 variables := []runtimehooksv1.Variable{} 315 316 // Add variables overrides for the MachineDeployment. 317 if mdTopology.Variables != nil { 318 for _, variable := range mdTopology.Variables.Overrides { 319 // Add the variable if it is defined for the current patch or it is defined for all the patches. 320 if variable.DefinitionFrom == emptyDefinitionFrom || variable.DefinitionFrom == definitionFrom { 321 // Add the variable if it has a definition from this patch in the ClusterClass. 322 if _, ok := patchVariableDefinitions[variable.Name]; ok { 323 variables = append(variables, runtimehooksv1.Variable{Name: variable.Name, Value: variable.Value}) 324 } 325 } 326 } 327 } 328 329 // Construct builtin variable. 330 builtin := Builtins{ 331 MachineDeployment: &MachineDeploymentBuiltins{ 332 Version: *md.Spec.Template.Spec.Version, 333 Class: mdTopology.Class, 334 Name: md.Name, 335 TopologyName: mdTopology.Name, 336 }, 337 } 338 if md.Spec.Replicas != nil { 339 builtin.MachineDeployment.Replicas = pointer.Int64(int64(*md.Spec.Replicas)) 340 } 341 342 if mdBootstrapTemplate != nil { 343 builtin.MachineDeployment.Bootstrap = &MachineBootstrapBuiltins{ 344 ConfigRef: &MachineBootstrapConfigRefBuiltins{ 345 Name: mdBootstrapTemplate.GetName(), 346 }, 347 } 348 } 349 350 if mdInfrastructureMachineTemplate != nil { 351 builtin.MachineDeployment.InfrastructureRef = &MachineInfrastructureRefBuiltins{ 352 Name: mdInfrastructureMachineTemplate.GetName(), 353 } 354 } 355 356 variable, err := toVariable(BuiltinsName, builtin) 357 if err != nil { 358 return nil, err 359 } 360 variables = append(variables, *variable) 361 362 return variables, nil 363 } 364 365 // MachinePool returns variables that apply to templates belonging to a MachinePool. 366 func MachinePool(mpTopology *clusterv1.MachinePoolTopology, mp *expv1.MachinePool, mpBootstrapObject, mpInfrastructureMachinePool *unstructured.Unstructured, definitionFrom string, patchVariableDefinitions map[string]bool) ([]runtimehooksv1.Variable, error) { 367 variables := []runtimehooksv1.Variable{} 368 369 // Add variables overrides for the MachinePool. 370 if mpTopology.Variables != nil { 371 for _, variable := range mpTopology.Variables.Overrides { 372 // Add the variable if it is defined for the current patch or it is defined for all the patches. 373 if variable.DefinitionFrom == emptyDefinitionFrom || variable.DefinitionFrom == definitionFrom { 374 // Add the variable if it has a definition from this patch in the ClusterClass. 375 if _, ok := patchVariableDefinitions[variable.Name]; ok { 376 variables = append(variables, runtimehooksv1.Variable{Name: variable.Name, Value: variable.Value}) 377 } 378 } 379 } 380 } 381 382 // Construct builtin variable. 383 builtin := Builtins{ 384 MachinePool: &MachinePoolBuiltins{ 385 Version: *mp.Spec.Template.Spec.Version, 386 Class: mpTopology.Class, 387 Name: mp.Name, 388 TopologyName: mpTopology.Name, 389 }, 390 } 391 if mp.Spec.Replicas != nil { 392 builtin.MachinePool.Replicas = pointer.Int64(int64(*mp.Spec.Replicas)) 393 } 394 395 if mpBootstrapObject != nil { 396 builtin.MachinePool.Bootstrap = &MachineBootstrapBuiltins{ 397 ConfigRef: &MachineBootstrapConfigRefBuiltins{ 398 Name: mpBootstrapObject.GetName(), 399 }, 400 } 401 } 402 403 if mpInfrastructureMachinePool != nil { 404 builtin.MachinePool.InfrastructureRef = &MachineInfrastructureRefBuiltins{ 405 Name: mpInfrastructureMachinePool.GetName(), 406 } 407 } 408 409 variable, err := toVariable(BuiltinsName, builtin) 410 if err != nil { 411 return nil, err 412 } 413 variables = append(variables, *variable) 414 415 return variables, nil 416 } 417 418 // toVariable converts name and value to a variable. 419 func toVariable(name string, value interface{}) (*runtimehooksv1.Variable, error) { 420 marshalledValue, err := json.Marshal(value) 421 if err != nil { 422 return nil, errors.Wrapf(err, "failed to set variable %q: error marshalling", name) 423 } 424 425 return &runtimehooksv1.Variable{ 426 Name: name, 427 Value: apiextensionsv1.JSON{Raw: marshalledValue}, 428 }, nil 429 } 430 431 func ipFamilyToString(ipFamily clusterv1.ClusterIPFamily) string { 432 switch ipFamily { 433 case clusterv1.DualStackIPFamily: 434 return "DualStack" 435 case clusterv1.IPv4IPFamily: 436 return "IPv4" 437 case clusterv1.IPv6IPFamily: 438 return "IPv6" 439 default: 440 return "Invalid" 441 } 442 } 443 444 // ToMap converts a list of Variables to a map of JSON (name is the map key). 445 func ToMap(variables []runtimehooksv1.Variable) map[string]apiextensionsv1.JSON { 446 variablesMap := map[string]apiextensionsv1.JSON{} 447 for i := range variables { 448 variablesMap[variables[i].Name] = variables[i].Value 449 } 450 return variablesMap 451 }