sigs.k8s.io/cluster-api@v1.7.1/api/v1beta1/cluster_types.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 v1beta1
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  	"strings"
    23  
    24  	"github.com/pkg/errors"
    25  	corev1 "k8s.io/api/core/v1"
    26  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/utils/ptr"
    29  
    30  	capierrors "sigs.k8s.io/cluster-api/errors"
    31  )
    32  
    33  const (
    34  	// ClusterFinalizer is the finalizer used by the cluster controller to
    35  	// cleanup the cluster resources when a Cluster is being deleted.
    36  	ClusterFinalizer = "cluster.cluster.x-k8s.io"
    37  
    38  	// ClusterKind represents the Kind of Cluster.
    39  	ClusterKind = "Cluster"
    40  )
    41  
    42  // ANCHOR: ClusterSpec
    43  
    44  // ClusterSpec defines the desired state of Cluster.
    45  type ClusterSpec struct {
    46  	// Paused can be used to prevent controllers from processing the Cluster and all its associated objects.
    47  	// +optional
    48  	Paused bool `json:"paused,omitempty"`
    49  
    50  	// Cluster network configuration.
    51  	// +optional
    52  	ClusterNetwork *ClusterNetwork `json:"clusterNetwork,omitempty"`
    53  
    54  	// ControlPlaneEndpoint represents the endpoint used to communicate with the control plane.
    55  	// +optional
    56  	ControlPlaneEndpoint APIEndpoint `json:"controlPlaneEndpoint,omitempty"`
    57  
    58  	// ControlPlaneRef is an optional reference to a provider-specific resource that holds
    59  	// the details for provisioning the Control Plane for a Cluster.
    60  	// +optional
    61  	ControlPlaneRef *corev1.ObjectReference `json:"controlPlaneRef,omitempty"`
    62  
    63  	// InfrastructureRef is a reference to a provider-specific resource that holds the details
    64  	// for provisioning infrastructure for a cluster in said provider.
    65  	// +optional
    66  	InfrastructureRef *corev1.ObjectReference `json:"infrastructureRef,omitempty"`
    67  
    68  	// This encapsulates the topology for the cluster.
    69  	// NOTE: It is required to enable the ClusterTopology
    70  	// feature gate flag to activate managed topologies support;
    71  	// this feature is highly experimental, and parts of it might still be not implemented.
    72  	// +optional
    73  	Topology *Topology `json:"topology,omitempty"`
    74  }
    75  
    76  // Topology encapsulates the information of the managed resources.
    77  type Topology struct {
    78  	// The name of the ClusterClass object to create the topology.
    79  	Class string `json:"class"`
    80  
    81  	// The Kubernetes version of the cluster.
    82  	Version string `json:"version"`
    83  
    84  	// RolloutAfter performs a rollout of the entire cluster one component at a time,
    85  	// control plane first and then machine deployments.
    86  	//
    87  	// Deprecated: This field has no function and is going to be removed in the next apiVersion.
    88  	//
    89  	// +optional
    90  	RolloutAfter *metav1.Time `json:"rolloutAfter,omitempty"`
    91  
    92  	// ControlPlane describes the cluster control plane.
    93  	// +optional
    94  	ControlPlane ControlPlaneTopology `json:"controlPlane,omitempty"`
    95  
    96  	// Workers encapsulates the different constructs that form the worker nodes
    97  	// for the cluster.
    98  	// +optional
    99  	Workers *WorkersTopology `json:"workers,omitempty"`
   100  
   101  	// Variables can be used to customize the Cluster through
   102  	// patches. They must comply to the corresponding
   103  	// VariableClasses defined in the ClusterClass.
   104  	// +optional
   105  	Variables []ClusterVariable `json:"variables,omitempty"`
   106  }
   107  
   108  // ControlPlaneTopology specifies the parameters for the control plane nodes in the cluster.
   109  type ControlPlaneTopology struct {
   110  	// Metadata is the metadata applied to the ControlPlane and the Machines of the ControlPlane
   111  	// if the ControlPlaneTemplate referenced by the ClusterClass is machine based. If not, it
   112  	// is applied only to the ControlPlane.
   113  	// At runtime this metadata is merged with the corresponding metadata from the ClusterClass.
   114  	// +optional
   115  	Metadata ObjectMeta `json:"metadata,omitempty"`
   116  
   117  	// Replicas is the number of control plane nodes.
   118  	// If the value is nil, the ControlPlane object is created without the number of Replicas
   119  	// and it's assumed that the control plane controller does not implement support for this field.
   120  	// When specified against a control plane provider that lacks support for this field, this value will be ignored.
   121  	// +optional
   122  	Replicas *int32 `json:"replicas,omitempty"`
   123  
   124  	// MachineHealthCheck allows to enable, disable and override
   125  	// the MachineHealthCheck configuration in the ClusterClass for this control plane.
   126  	// +optional
   127  	MachineHealthCheck *MachineHealthCheckTopology `json:"machineHealthCheck,omitempty"`
   128  
   129  	// NodeDrainTimeout is the total amount of time that the controller will spend on draining a node.
   130  	// The default value is 0, meaning that the node can be drained without any time limitations.
   131  	// NOTE: NodeDrainTimeout is different from `kubectl drain --timeout`
   132  	// +optional
   133  	NodeDrainTimeout *metav1.Duration `json:"nodeDrainTimeout,omitempty"`
   134  
   135  	// NodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes
   136  	// to be detached. The default value is 0, meaning that the volumes can be detached without any time limitations.
   137  	// +optional
   138  	NodeVolumeDetachTimeout *metav1.Duration `json:"nodeVolumeDetachTimeout,omitempty"`
   139  
   140  	// NodeDeletionTimeout defines how long the controller will attempt to delete the Node that the Machine
   141  	// hosts after the Machine is marked for deletion. A duration of 0 will retry deletion indefinitely.
   142  	// Defaults to 10 seconds.
   143  	// +optional
   144  	NodeDeletionTimeout *metav1.Duration `json:"nodeDeletionTimeout,omitempty"`
   145  }
   146  
   147  // WorkersTopology represents the different sets of worker nodes in the cluster.
   148  type WorkersTopology struct {
   149  	// MachineDeployments is a list of machine deployments in the cluster.
   150  	// +optional
   151  	MachineDeployments []MachineDeploymentTopology `json:"machineDeployments,omitempty"`
   152  
   153  	// MachinePools is a list of machine pools in the cluster.
   154  	// +optional
   155  	MachinePools []MachinePoolTopology `json:"machinePools,omitempty"`
   156  }
   157  
   158  // MachineDeploymentTopology specifies the different parameters for a set of worker nodes in the topology.
   159  // This set of nodes is managed by a MachineDeployment object whose lifecycle is managed by the Cluster controller.
   160  type MachineDeploymentTopology struct {
   161  	// Metadata is the metadata applied to the MachineDeployment and the machines of the MachineDeployment.
   162  	// At runtime this metadata is merged with the corresponding metadata from the ClusterClass.
   163  	// +optional
   164  	Metadata ObjectMeta `json:"metadata,omitempty"`
   165  
   166  	// Class is the name of the MachineDeploymentClass used to create the set of worker nodes.
   167  	// This should match one of the deployment classes defined in the ClusterClass object
   168  	// mentioned in the `Cluster.Spec.Class` field.
   169  	Class string `json:"class"`
   170  
   171  	// Name is the unique identifier for this MachineDeploymentTopology.
   172  	// The value is used with other unique identifiers to create a MachineDeployment's Name
   173  	// (e.g. cluster's name, etc). In case the name is greater than the allowed maximum length,
   174  	// the values are hashed together.
   175  	Name string `json:"name"`
   176  
   177  	// FailureDomain is the failure domain the machines will be created in.
   178  	// Must match a key in the FailureDomains map stored on the cluster object.
   179  	// +optional
   180  	FailureDomain *string `json:"failureDomain,omitempty"`
   181  
   182  	// Replicas is the number of worker nodes belonging to this set.
   183  	// If the value is nil, the MachineDeployment is created without the number of Replicas (defaulting to 1)
   184  	// and it's assumed that an external entity (like cluster autoscaler) is responsible for the management
   185  	// of this value.
   186  	// +optional
   187  	Replicas *int32 `json:"replicas,omitempty"`
   188  
   189  	// MachineHealthCheck allows to enable, disable and override
   190  	// the MachineHealthCheck configuration in the ClusterClass for this MachineDeployment.
   191  	// +optional
   192  	MachineHealthCheck *MachineHealthCheckTopology `json:"machineHealthCheck,omitempty"`
   193  
   194  	// NodeDrainTimeout is the total amount of time that the controller will spend on draining a node.
   195  	// The default value is 0, meaning that the node can be drained without any time limitations.
   196  	// NOTE: NodeDrainTimeout is different from `kubectl drain --timeout`
   197  	// +optional
   198  	NodeDrainTimeout *metav1.Duration `json:"nodeDrainTimeout,omitempty"`
   199  
   200  	// NodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes
   201  	// to be detached. The default value is 0, meaning that the volumes can be detached without any time limitations.
   202  	// +optional
   203  	NodeVolumeDetachTimeout *metav1.Duration `json:"nodeVolumeDetachTimeout,omitempty"`
   204  
   205  	// NodeDeletionTimeout defines how long the controller will attempt to delete the Node that the Machine
   206  	// hosts after the Machine is marked for deletion. A duration of 0 will retry deletion indefinitely.
   207  	// Defaults to 10 seconds.
   208  	// +optional
   209  	NodeDeletionTimeout *metav1.Duration `json:"nodeDeletionTimeout,omitempty"`
   210  
   211  	// Minimum number of seconds for which a newly created machine should
   212  	// be ready.
   213  	// Defaults to 0 (machine will be considered available as soon as it
   214  	// is ready)
   215  	// +optional
   216  	MinReadySeconds *int32 `json:"minReadySeconds,omitempty"`
   217  
   218  	// The deployment strategy to use to replace existing machines with
   219  	// new ones.
   220  	// +optional
   221  	Strategy *MachineDeploymentStrategy `json:"strategy,omitempty"`
   222  
   223  	// Variables can be used to customize the MachineDeployment through patches.
   224  	// +optional
   225  	Variables *MachineDeploymentVariables `json:"variables,omitempty"`
   226  }
   227  
   228  // MachineHealthCheckTopology defines a MachineHealthCheck for a group of machines.
   229  type MachineHealthCheckTopology struct {
   230  	// Enable controls if a MachineHealthCheck should be created for the target machines.
   231  	//
   232  	// If false: No MachineHealthCheck will be created.
   233  	//
   234  	// If not set(default): A MachineHealthCheck will be created if it is defined here or
   235  	//  in the associated ClusterClass. If no MachineHealthCheck is defined then none will be created.
   236  	//
   237  	// If true: A MachineHealthCheck is guaranteed to be created. Cluster validation will
   238  	// block if `enable` is true and no MachineHealthCheck definition is available.
   239  	// +optional
   240  	Enable *bool `json:"enable,omitempty"`
   241  
   242  	// MachineHealthCheckClass defines a MachineHealthCheck for a group of machines.
   243  	// If specified (any field is set), it entirely overrides the MachineHealthCheckClass defined in ClusterClass.
   244  	MachineHealthCheckClass `json:",inline"`
   245  }
   246  
   247  // MachinePoolTopology specifies the different parameters for a pool of worker nodes in the topology.
   248  // This pool of nodes is managed by a MachinePool object whose lifecycle is managed by the Cluster controller.
   249  type MachinePoolTopology struct {
   250  	// Metadata is the metadata applied to the MachinePool.
   251  	// At runtime this metadata is merged with the corresponding metadata from the ClusterClass.
   252  	// +optional
   253  	Metadata ObjectMeta `json:"metadata,omitempty"`
   254  
   255  	// Class is the name of the MachinePoolClass used to create the pool of worker nodes.
   256  	// This should match one of the deployment classes defined in the ClusterClass object
   257  	// mentioned in the `Cluster.Spec.Class` field.
   258  	Class string `json:"class"`
   259  
   260  	// Name is the unique identifier for this MachinePoolTopology.
   261  	// The value is used with other unique identifiers to create a MachinePool's Name
   262  	// (e.g. cluster's name, etc). In case the name is greater than the allowed maximum length,
   263  	// the values are hashed together.
   264  	Name string `json:"name"`
   265  
   266  	// FailureDomains is the list of failure domains the machine pool will be created in.
   267  	// Must match a key in the FailureDomains map stored on the cluster object.
   268  	// +optional
   269  	FailureDomains []string `json:"failureDomains,omitempty"`
   270  
   271  	// NodeDrainTimeout is the total amount of time that the controller will spend on draining a node.
   272  	// The default value is 0, meaning that the node can be drained without any time limitations.
   273  	// NOTE: NodeDrainTimeout is different from `kubectl drain --timeout`
   274  	// +optional
   275  	NodeDrainTimeout *metav1.Duration `json:"nodeDrainTimeout,omitempty"`
   276  
   277  	// NodeVolumeDetachTimeout is the total amount of time that the controller will spend on waiting for all volumes
   278  	// to be detached. The default value is 0, meaning that the volumes can be detached without any time limitations.
   279  	// +optional
   280  	NodeVolumeDetachTimeout *metav1.Duration `json:"nodeVolumeDetachTimeout,omitempty"`
   281  
   282  	// NodeDeletionTimeout defines how long the controller will attempt to delete the Node that the MachinePool
   283  	// hosts after the MachinePool is marked for deletion. A duration of 0 will retry deletion indefinitely.
   284  	// Defaults to 10 seconds.
   285  	// +optional
   286  	NodeDeletionTimeout *metav1.Duration `json:"nodeDeletionTimeout,omitempty"`
   287  
   288  	// Minimum number of seconds for which a newly created machine pool should
   289  	// be ready.
   290  	// Defaults to 0 (machine will be considered available as soon as it
   291  	// is ready)
   292  	// +optional
   293  	MinReadySeconds *int32 `json:"minReadySeconds,omitempty"`
   294  
   295  	// Replicas is the number of nodes belonging to this pool.
   296  	// If the value is nil, the MachinePool is created without the number of Replicas (defaulting to 1)
   297  	// and it's assumed that an external entity (like cluster autoscaler) is responsible for the management
   298  	// of this value.
   299  	// +optional
   300  	Replicas *int32 `json:"replicas,omitempty"`
   301  
   302  	// Variables can be used to customize the MachinePool through patches.
   303  	// +optional
   304  	Variables *MachinePoolVariables `json:"variables,omitempty"`
   305  }
   306  
   307  // ClusterVariable can be used to customize the Cluster through patches. Each ClusterVariable is associated with a
   308  // Variable definition in the ClusterClass `status` variables.
   309  type ClusterVariable struct {
   310  	// Name of the variable.
   311  	Name string `json:"name"`
   312  
   313  	// DefinitionFrom specifies where the definition of this Variable is from. DefinitionFrom is `inline` when the
   314  	// definition is from the ClusterClass `.spec.variables` or the name of a patch defined in the ClusterClass
   315  	// `.spec.patches` where the patch is external and provides external variables.
   316  	// This field is mandatory if the variable has `DefinitionsConflict: true` in ClusterClass `status.variables[]`
   317  	// +optional
   318  	DefinitionFrom string `json:"definitionFrom,omitempty"`
   319  
   320  	// Value of the variable.
   321  	// Note: the value will be validated against the schema of the corresponding ClusterClassVariable
   322  	// from the ClusterClass.
   323  	// Note: We have to use apiextensionsv1.JSON instead of a custom JSON type, because controller-tools has a
   324  	// hard-coded schema for apiextensionsv1.JSON which cannot be produced by another type via controller-tools,
   325  	// i.e. it is not possible to have no type field.
   326  	// Ref: https://github.com/kubernetes-sigs/controller-tools/blob/d0e03a142d0ecdd5491593e941ee1d6b5d91dba6/pkg/crd/known_types.go#L106-L111
   327  	Value apiextensionsv1.JSON `json:"value"`
   328  }
   329  
   330  // MachineDeploymentVariables can be used to provide variables for a specific MachineDeployment.
   331  type MachineDeploymentVariables struct {
   332  	// Overrides can be used to override Cluster level variables.
   333  	// +optional
   334  	Overrides []ClusterVariable `json:"overrides,omitempty"`
   335  }
   336  
   337  // MachinePoolVariables can be used to provide variables for a specific MachinePool.
   338  type MachinePoolVariables struct {
   339  	// Overrides can be used to override Cluster level variables.
   340  	// +optional
   341  	Overrides []ClusterVariable `json:"overrides,omitempty"`
   342  }
   343  
   344  // ANCHOR_END: ClusterSpec
   345  
   346  // ANCHOR: ClusterNetwork
   347  
   348  // ClusterNetwork specifies the different networking
   349  // parameters for a cluster.
   350  type ClusterNetwork struct {
   351  	// APIServerPort specifies the port the API Server should bind to.
   352  	// Defaults to 6443.
   353  	// +optional
   354  	APIServerPort *int32 `json:"apiServerPort,omitempty"`
   355  
   356  	// The network ranges from which service VIPs are allocated.
   357  	// +optional
   358  	Services *NetworkRanges `json:"services,omitempty"`
   359  
   360  	// The network ranges from which Pod networks are allocated.
   361  	// +optional
   362  	Pods *NetworkRanges `json:"pods,omitempty"`
   363  
   364  	// Domain name for services.
   365  	// +optional
   366  	ServiceDomain string `json:"serviceDomain,omitempty"`
   367  }
   368  
   369  // ANCHOR_END: ClusterNetwork
   370  
   371  // ANCHOR: NetworkRanges
   372  
   373  // NetworkRanges represents ranges of network addresses.
   374  type NetworkRanges struct {
   375  	CIDRBlocks []string `json:"cidrBlocks"`
   376  }
   377  
   378  func (n NetworkRanges) String() string {
   379  	if len(n.CIDRBlocks) == 0 {
   380  		return ""
   381  	}
   382  	return strings.Join(n.CIDRBlocks, ",")
   383  }
   384  
   385  // ANCHOR_END: NetworkRanges
   386  
   387  // ANCHOR: ClusterStatus
   388  
   389  // ClusterStatus defines the observed state of Cluster.
   390  type ClusterStatus struct {
   391  	// FailureDomains is a slice of failure domain objects synced from the infrastructure provider.
   392  	// +optional
   393  	FailureDomains FailureDomains `json:"failureDomains,omitempty"`
   394  
   395  	// FailureReason indicates that there is a fatal problem reconciling the
   396  	// state, and will be set to a token value suitable for
   397  	// programmatic interpretation.
   398  	// +optional
   399  	FailureReason *capierrors.ClusterStatusError `json:"failureReason,omitempty"`
   400  
   401  	// FailureMessage indicates that there is a fatal problem reconciling the
   402  	// state, and will be set to a descriptive error message.
   403  	// +optional
   404  	FailureMessage *string `json:"failureMessage,omitempty"`
   405  
   406  	// Phase represents the current phase of cluster actuation.
   407  	// E.g. Pending, Running, Terminating, Failed etc.
   408  	// +optional
   409  	Phase string `json:"phase,omitempty"`
   410  
   411  	// InfrastructureReady is the state of the infrastructure provider.
   412  	// +optional
   413  	InfrastructureReady bool `json:"infrastructureReady"`
   414  
   415  	// ControlPlaneReady defines if the control plane is ready.
   416  	// +optional
   417  	ControlPlaneReady bool `json:"controlPlaneReady"`
   418  
   419  	// Conditions defines current service state of the cluster.
   420  	// +optional
   421  	Conditions Conditions `json:"conditions,omitempty"`
   422  
   423  	// ObservedGeneration is the latest generation observed by the controller.
   424  	// +optional
   425  	ObservedGeneration int64 `json:"observedGeneration,omitempty"`
   426  }
   427  
   428  // ANCHOR_END: ClusterStatus
   429  
   430  // SetTypedPhase sets the Phase field to the string representation of ClusterPhase.
   431  func (c *ClusterStatus) SetTypedPhase(p ClusterPhase) {
   432  	c.Phase = string(p)
   433  }
   434  
   435  // GetTypedPhase attempts to parse the Phase field and return
   436  // the typed ClusterPhase representation as described in `machine_phase_types.go`.
   437  func (c *ClusterStatus) GetTypedPhase() ClusterPhase {
   438  	switch phase := ClusterPhase(c.Phase); phase {
   439  	case
   440  		ClusterPhasePending,
   441  		ClusterPhaseProvisioning,
   442  		ClusterPhaseProvisioned,
   443  		ClusterPhaseDeleting,
   444  		ClusterPhaseFailed:
   445  		return phase
   446  	default:
   447  		return ClusterPhaseUnknown
   448  	}
   449  }
   450  
   451  // ANCHOR: APIEndpoint
   452  
   453  // APIEndpoint represents a reachable Kubernetes API endpoint.
   454  type APIEndpoint struct {
   455  	// The hostname on which the API server is serving.
   456  	Host string `json:"host"`
   457  
   458  	// The port on which the API server is serving.
   459  	Port int32 `json:"port"`
   460  }
   461  
   462  // IsZero returns true if both host and port are zero values.
   463  func (v APIEndpoint) IsZero() bool {
   464  	return v.Host == "" && v.Port == 0
   465  }
   466  
   467  // IsValid returns true if both host and port are non-zero values.
   468  func (v APIEndpoint) IsValid() bool {
   469  	return v.Host != "" && v.Port != 0
   470  }
   471  
   472  // String returns a formatted version HOST:PORT of this APIEndpoint.
   473  func (v APIEndpoint) String() string {
   474  	return net.JoinHostPort(v.Host, fmt.Sprintf("%d", v.Port))
   475  }
   476  
   477  // ANCHOR_END: APIEndpoint
   478  
   479  // +kubebuilder:object:root=true
   480  // +kubebuilder:resource:path=clusters,shortName=cl,scope=Namespaced,categories=cluster-api
   481  // +kubebuilder:storageversion
   482  // +kubebuilder:subresource:status
   483  // +kubebuilder:printcolumn:name="ClusterClass",type="string",JSONPath=".spec.topology.class",description="ClusterClass of this Cluster, empty if the Cluster is not using a ClusterClass"
   484  // +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="Cluster status such as Pending/Provisioning/Provisioned/Deleting/Failed"
   485  // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of Cluster"
   486  // +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.topology.version",description="Kubernetes version associated with this Cluster"
   487  
   488  // Cluster is the Schema for the clusters API.
   489  type Cluster struct {
   490  	metav1.TypeMeta   `json:",inline"`
   491  	metav1.ObjectMeta `json:"metadata,omitempty"`
   492  
   493  	Spec   ClusterSpec   `json:"spec,omitempty"`
   494  	Status ClusterStatus `json:"status,omitempty"`
   495  }
   496  
   497  // GetConditions returns the set of conditions for this object.
   498  func (c *Cluster) GetConditions() Conditions {
   499  	return c.Status.Conditions
   500  }
   501  
   502  // SetConditions sets the conditions on this object.
   503  func (c *Cluster) SetConditions(conditions Conditions) {
   504  	c.Status.Conditions = conditions
   505  }
   506  
   507  // GetIPFamily returns a ClusterIPFamily from the configuration provided.
   508  // Note: IPFamily is not a concept in Kubernetes. It was originally introduced in CAPI for CAPD.
   509  // IPFamily may be dropped in a future release. More details at https://github.com/kubernetes-sigs/cluster-api/issues/7521
   510  func (c *Cluster) GetIPFamily() (ClusterIPFamily, error) {
   511  	var podCIDRs, serviceCIDRs []string
   512  	if c.Spec.ClusterNetwork != nil {
   513  		if c.Spec.ClusterNetwork.Pods != nil {
   514  			podCIDRs = c.Spec.ClusterNetwork.Pods.CIDRBlocks
   515  		}
   516  		if c.Spec.ClusterNetwork.Services != nil {
   517  			serviceCIDRs = c.Spec.ClusterNetwork.Services.CIDRBlocks
   518  		}
   519  	}
   520  	if len(podCIDRs) == 0 && len(serviceCIDRs) == 0 {
   521  		return IPv4IPFamily, nil
   522  	}
   523  
   524  	podsIPFamily, err := ipFamilyForCIDRStrings(podCIDRs)
   525  	if err != nil {
   526  		return InvalidIPFamily, fmt.Errorf("pods: %s", err)
   527  	}
   528  	if len(serviceCIDRs) == 0 {
   529  		return podsIPFamily, nil
   530  	}
   531  
   532  	servicesIPFamily, err := ipFamilyForCIDRStrings(serviceCIDRs)
   533  	if err != nil {
   534  		return InvalidIPFamily, fmt.Errorf("services: %s", err)
   535  	}
   536  	if len(podCIDRs) == 0 {
   537  		return servicesIPFamily, nil
   538  	}
   539  
   540  	if podsIPFamily == DualStackIPFamily {
   541  		return DualStackIPFamily, nil
   542  	} else if podsIPFamily != servicesIPFamily {
   543  		return InvalidIPFamily, errors.New("pods and services IP family mismatch")
   544  	}
   545  
   546  	return podsIPFamily, nil
   547  }
   548  
   549  func ipFamilyForCIDRStrings(cidrs []string) (ClusterIPFamily, error) {
   550  	if len(cidrs) > 2 {
   551  		return InvalidIPFamily, errors.New("too many CIDRs specified")
   552  	}
   553  	var foundIPv4 bool
   554  	var foundIPv6 bool
   555  	for _, cidr := range cidrs {
   556  		ip, _, err := net.ParseCIDR(cidr)
   557  		if err != nil {
   558  			return InvalidIPFamily, fmt.Errorf("could not parse CIDR: %s", err)
   559  		}
   560  		if ip.To4() != nil {
   561  			foundIPv4 = true
   562  		} else {
   563  			foundIPv6 = true
   564  		}
   565  	}
   566  	switch {
   567  	case foundIPv4 && foundIPv6:
   568  		return DualStackIPFamily, nil
   569  	case foundIPv4:
   570  		return IPv4IPFamily, nil
   571  	case foundIPv6:
   572  		return IPv6IPFamily, nil
   573  	default:
   574  		return InvalidIPFamily, nil
   575  	}
   576  }
   577  
   578  // ClusterIPFamily defines the types of supported IP families.
   579  type ClusterIPFamily int
   580  
   581  // Define the ClusterIPFamily constants.
   582  const (
   583  	InvalidIPFamily ClusterIPFamily = iota
   584  	IPv4IPFamily
   585  	IPv6IPFamily
   586  	DualStackIPFamily
   587  )
   588  
   589  func (f ClusterIPFamily) String() string {
   590  	return [...]string{"InvalidIPFamily", "IPv4IPFamily", "IPv6IPFamily", "DualStackIPFamily"}[f]
   591  }
   592  
   593  // +kubebuilder:object:root=true
   594  
   595  // ClusterList contains a list of Cluster.
   596  type ClusterList struct {
   597  	metav1.TypeMeta `json:",inline"`
   598  	metav1.ListMeta `json:"metadata,omitempty"`
   599  	Items           []Cluster `json:"items"`
   600  }
   601  
   602  func init() {
   603  	objectTypes = append(objectTypes, &Cluster{}, &ClusterList{})
   604  }
   605  
   606  // FailureDomains is a slice of FailureDomains.
   607  type FailureDomains map[string]FailureDomainSpec
   608  
   609  // FilterControlPlane returns a FailureDomain slice containing only the domains suitable to be used
   610  // for control plane nodes.
   611  func (in FailureDomains) FilterControlPlane() FailureDomains {
   612  	res := make(FailureDomains)
   613  	for id, spec := range in {
   614  		if spec.ControlPlane {
   615  			res[id] = spec
   616  		}
   617  	}
   618  	return res
   619  }
   620  
   621  // GetIDs returns a slice containing the ids for failure domains.
   622  func (in FailureDomains) GetIDs() []*string {
   623  	ids := make([]*string, 0, len(in))
   624  	for id := range in {
   625  		ids = append(ids, ptr.To(id))
   626  	}
   627  	return ids
   628  }
   629  
   630  // FailureDomainSpec is the Schema for Cluster API failure domains.
   631  // It allows controllers to understand how many failure domains a cluster can optionally span across.
   632  type FailureDomainSpec struct {
   633  	// ControlPlane determines if this failure domain is suitable for use by control plane machines.
   634  	// +optional
   635  	ControlPlane bool `json:"controlPlane,omitempty"`
   636  
   637  	// Attributes is a free form map of attributes an infrastructure provider might use or require.
   638  	// +optional
   639  	Attributes map[string]string `json:"attributes,omitempty"`
   640  }