1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
use kube::CustomResource;

use super::*;

pub use common::*;
pub use condition::*;
pub use ip_family::ClusterIpFamily;
pub use ip_family::InvalidIpFamily;
pub use phase::ClusterPhase;

mod common;
mod condition;
mod impls;
mod ip_family;
mod phase;

/// ClusterFinalizer is the finalizer used by the cluster controller to
/// cleanup the cluster resources when a Cluster is being deleted.
pub const CLUSTER_FINALIZER: &str = "cluster.cluster.x-k8s.io";

/// ClusterSpec defines the desired state of Cluster.
///
#[skip_serializing_none]
#[derive(Clone, Debug, Default, Serialize, Deserialize, CustomResource)]
#[serde(rename_all = "camelCase")]
#[kube(
    group = "cluster.x-k8s.io",
    version = "v1beta1",
    kind = "Cluster",
    plural = "clusters",
    shortname = "cl",
    status = "ClusterStatus"
)]
#[kube(namespaced)]
#[kube(schema = "disabled")]
pub struct ClusterSpec {
    /// Paused can be used to prevent controllers from processing the Cluster and all its associated objects.
    pub paused: Option<bool>,

    /// Cluster network configuration.
    pub cluster_network: Option<ClusterNetwork>,

    /// ControlPlaneEndpoint represents the endpoint used to communicate with the control plane.
    pub control_plane_endpoint: Option<ApiEndpoint>,

    /// ControlPlaneRef is an optional reference to a provider-specific resource that holds
    /// the details for provisioning the Control Plane for a Cluster.
    pub control_plane_ref: Option<corev1::ObjectReference>,

    /// InfrastructureRef is a reference to a provider-specific resource that holds the details
    /// for provisioning infrastructure for a cluster in said provider.
    pub infrastructure_ref: Option<corev1::ObjectReference>,

    /// This encapsulates the topology for the cluster.
    /// NOTE: It is required to enable the ClusterTopology
    /// feature gate flag to activate managed topologies support;
    /// this feature is highly experimental, and parts of it might still be not implemented.
    pub topology: Option<Topology>,
}

/// Topology encapsulates the information of the managed resources.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Topology {
    /// The name of the ClusterClass object to create the topology.
    pub class: String,

    /// The Kubernetes version of the cluster.
    pub version: String,

    /// RolloutAfter performs a rollout of the entire cluster one component at a time,
    /// control plane first and then machine deployments.
    pub rollout_after: Option<metav1::Time>,

    /// ControlPlane describes the cluster control plane.
    pub control_plane: Option<ControlPlaneTopology>,

    /// Workers encapsulates the different constructs that form the worker nodes
    /// for the cluster.
    pub workers: Option<WorkersTopology>,

    /// Variables can be used to customize the Cluster through
    /// patches. They must comply to the corresponding
    /// VariableClasses defined in the ClusterClass.
    pub variables: Option<Vec<ClusterVariable>>,
}

/// ControlPlaneTopology specifies the parameters for the control plane nodes in the cluster.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ControlPlaneTopology {
    /// Metadata is the metadata applied to the machines of the ControlPlane.
    /// At runtime this metadata is merged with the corresponding metadata from the ClusterClass.
    ///
    /// This field is supported if and only if the control plane provider template
    /// referenced in the ClusterClass is Machine based.
    pub metadata: Option<ObjectMeta>,

    /// Replicas is the number of control plane nodes.
    /// If the value is nil, the ControlPlane object is created without the number of Replicas
    /// and it's assumed that the control plane controller does not implement support for this field.
    /// When specified against a control plane provider that lacks support for this field, this value will be ignored.
    pub replicas: Option<i32>,
}

/// WorkersTopology represents the different sets of worker nodes in the cluster.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkersTopology {
    /// MachineDeployments is a list of machine deployments in the cluster.
    pub machine_deployments: Option<Vec<MachineDeploymentTopology>>,
}

/// MachineDeploymentTopology specifies the different parameters for a set of worker nodes in the topology.
/// This set of nodes is managed by a MachineDeployment object whose lifecycle is managed by the Cluster controller.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct MachineDeploymentTopology {
    /// Metadata is the metadata applied to the machines of the MachineDeployment.
    /// At runtime this metadata is merged with the corresponding metadata from the ClusterClass.
    pub metadata: Option<ObjectMeta>,

    /// Class is the name of the MachineDeploymentClass used to create the set of worker nodes.
    /// This should match one of the deployment classes defined in the ClusterClass object
    /// mentioned in the `Cluster.Spec.Class` field.
    pub class: String,

    /// Name is the unique identifier for this MachineDeploymentTopology.
    /// The value is used with other unique identifiers to create a MachineDeployment's Name
    /// (e.g. cluster's name, etc). In case the name is greater than the allowed maximum length,
    /// the values are hashed together.
    pub name: String,

    /// Replicas is the number of worker nodes belonging to this set.
    /// If the value is nil, the MachineDeployment is created without the number of Replicas (defaulting to zero)
    /// and it's assumed that an external entity (like cluster autoscaler) is responsible for the management
    /// of this value.
    pub replicas: Option<i32>,
}

/// ClusterVariable can be used to customize the Cluster through
/// patches. It must comply to the corresponding
/// ClusterClassVariable defined in the ClusterClass.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ClusterVariable {
    /// Name of the variable.
    pub name: String,

    /// Value of the variable.
    /// Note: the value will be validated against the schema of the corresponding ClusterClassVariable
    /// from the ClusterClass.
    /// Note: We have to use apiextensionsv1.JSON instead of a custom JSON type, because controller-tools has a
    /// hard-coded schema for apiextensionsv1.JSON which cannot be produced by another type via controller-tools,
    /// i.e. it's not possible to have no type field.
    /// Ref: https://github.com/kubernetes-sigs/controller-tools/blob/d0e03a142d0ecdd5491593e941ee1d6b5d91dba6/pkg/crd/known_types.go#L106-L111
    pub value: apiextensionsv1::JSON,
}

// ANCHOR_END: ClusterSpec

// ANCHOR: ClusterNetwork

/// ClusterNetwork specifies the different networking
/// parameters for a cluster.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ClusterNetwork {
    /// APIServerPort specifies the port the API Server should bind to.
    /// Defaults to 6443.
    pub api_server_port: Option<i32>,

    /// The network ranges from which service VIPs are allocated.
    pub services: Option<NetworkRanges>,

    /// The network ranges from which Pod networks are allocated.
    pub pods: Option<NetworkRanges>,

    /// Domain name for services.
    pub service_domain: Option<String>,
}

// ANCHOR_END: ClusterNetwork

// ANCHOR: NetworkRanges

/// NetworkRanges represents ranges of network addresses.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NetworkRanges {
    pub cidr_blocks: Vec<String>,
}

// ANCHOR_END: NetworkRanges

// ANCHOR: ClusterStatus

/// ClusterStatus defines the observed state of Cluster.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ClusterStatus {
    /// FailureDomains is a slice of failure domain objects synced from the infrastructure provider.
    pub failure_domains: Option<FailureDomains>,

    /// FailureReason indicates that there is a fatal problem reconciling the
    /// state, and will be set to a token value suitable for
    /// programmatic interpretation.
    pub failure_reason: Option<capierrors::ClusterStatusError>,

    /// FailureMessage indicates that there is a fatal problem reconciling the
    /// state, and will be set to a descriptive error message.
    pub failure_message: Option<String>,

    /// Phase represents the current phase of cluster actuation.
    /// E.g. Pending, Running, Terminating, Failed etc.
    pub phase: Option<ClusterPhase>,

    /// InfrastructureReady is the state of the infrastructure provider.
    // +optional
    pub infrastructure_ready: Option<bool>,

    /// ControlPlaneReady defines if the control plane is ready.
    // +optional
    pub control_plane_ready: Option<bool>,

    /// Conditions defines current service state of the cluster.
    // +optional
    pub conditions: Option<Conditions>,

    /// ObservedGeneration is the latest generation observed by the controller.
    // +optional
    pub observed_generation: i64,
}

// ANCHOR_END: ClusterStatus

// ANCHOR: APIEndpoint

/// APIEndpoint represents a reachable Kubernetes API endpoint.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ApiEndpoint {
    /// The hostname on which the API server is serving.
    pub host: String,

    /// The port on which the API server is serving.
    pub port: i32,
}

// ANCHOR_END: APIEndpoint

/* ############

// +kubebuilder:object:root=true
// +kubebuilder:resource:path=clusters,shortName=cl,scope=Namespaced,categories=cluster-api
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="Cluster status such as Pending/Provisioning/Provisioned/Deleting/Failed"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of Cluster"
// +kubebuilder:printcolumn:name="Version",type="string",JSONPath=".spec.topology.version",description="Kubernetes version associated with this Cluster"



// +kubebuilder:object:root=true

// ClusterList contains a list of Cluster.
type ClusterList struct {
    metav1.TypeMeta `json:",inline"`
    metav1.ListMeta `json:"metadata,omitempty"`
    Items           []Cluster `json:"items"`
}

func init() {
    SchemeBuilder.Register(&Cluster{}, &ClusterList{})
}

############ */

/// FailureDomains is a slice of FailureDomains.
pub type FailureDomains = BTreeMap<String, FailureDomainSpec>;

/* ############
// FilterControlPlane returns a FailureDomain slice containing only the domains suitable to be used
// for control plane nodes.
func (in FailureDomains) FilterControlPlane() FailureDomains {
    res := make(FailureDomains)
    for id, spec := range in {
        if spec.ControlPlane {
            res[id] = spec
        }
    }
    return res
}

// GetIDs returns a slice containing the ids for failure domains.
func (in FailureDomains) GetIDs() []*string {
    ids := make([]*string, 0, len(in))
    for id := range in {
        ids = append(ids, pointer.StringPtr(id))
    }
    return ids
}

############ */

/// FailureDomainSpec is the Schema for Cluster API failure domains.
/// It allows controllers to understand how many failure domains a cluster can optionally span across.
#[skip_serializing_none]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct FailureDomainSpec {
    /// ControlPlane determines if this failure domain is suitable for use by control plane machines.
    pub control_plane: Option<bool>,

    /// Attributes is a free form map of attributes an infrastructure provider might use or require.
    pub attributes: Option<BTreeMap<String, String>>,
}

#[cfg(test)]
mod tests;