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  }