Skip to content

Commit 013036b

Browse files
committed
feat: cluster lifecycle
1 parent 4ea7a46 commit 013036b

37 files changed

Lines changed: 5582 additions & 175 deletions

api/v1beta2/cloudscalecluster_types.go

Lines changed: 203 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,171 @@ limitations under the License.
1717
package v1beta2
1818

1919
import (
20+
corev1 "k8s.io/api/core/v1"
2021
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
23+
"sigs.k8s.io/cluster-api/util/conditions"
2124
)
2225

23-
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
24-
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
26+
const (
27+
// ClusterFinalizer allows cleanup of resources before removal from the API.
28+
ClusterFinalizer = "cloudscalecluster.infrastructure.cluster.x-k8s.io"
29+
)
2530

2631
// CloudscaleClusterSpec defines the desired state of CloudscaleCluster
2732
type CloudscaleClusterSpec struct {
28-
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
29-
// Important: Run "make" to regenerate code after modifying this file
30-
// The following markers will use OpenAPI v3 schema to validate the value
31-
// More info: https://book.kubebuilder.io/reference/markers/crd-validation.html
33+
// Region is the cloudscale.ch region (e.g., "rma", "lpg").
34+
// +kubebuilder:validation:Required
35+
// +kubebuilder:validation:Enum=rma;lpg
36+
Region string `json:"region"`
37+
38+
// CredentialsRef references the Secret containing the cloudscale.ch API token.
39+
// +kubebuilder:validation:Required
40+
CredentialsRef CloudscaleCredentialsReference `json:"credentialsRef"`
41+
42+
// ControlPlaneEndpoint represents the endpoint to communicate with the control plane.
43+
// This is set automatically from the load balancer's VIP address.
44+
// +optional
45+
ControlPlaneEndpoint clusterv1.APIEndpoint `json:"controlPlaneEndpoint,omitzero"`
46+
47+
// Network contains network configuration for the cluster.
48+
// +optional
49+
Network NetworkSpec `json:"network,omitzero"`
50+
51+
// ControlPlaneLoadBalancer configures the load balancer for the control plane.
52+
// +optional
53+
ControlPlaneLoadBalancer LoadBalancerSpec `json:"controlPlaneLoadBalancer,omitzero"`
54+
}
55+
56+
// CloudscaleCredentialsReference references a Secret containing the API token.
57+
type CloudscaleCredentialsReference struct {
58+
// Name is the name of the Secret.
59+
// +kubebuilder:validation:Required
60+
Name string `json:"name"`
61+
62+
// Namespace is the namespace of the Secret. Defaults to the cluster namespace.
63+
// +optional
64+
Namespace string `json:"namespace,omitempty"`
65+
}
66+
67+
// NetworkSpec defines the network configuration.
68+
type NetworkSpec struct {
69+
// Zone is the cloudscale.ch zone for the network (e.g., "rma1", "lpg1").
70+
// Defaults to region + "1" if not specified.
71+
// +optional
72+
Zone string `json:"zone,omitempty"`
73+
74+
// CIDR is the CIDR block for the private network subnet.
75+
// +kubebuilder:default="10.0.0.0/24"
76+
// +optional
77+
CIDR string `json:"cidr,omitempty"`
78+
79+
// GatewayAddress is the gateway IP address for the subnet.
80+
// By default, no gateway is configured on the private network subnet. This ensures
81+
// that outbound internet traffic uses the public network interface, which is required
82+
// for the Cloud Controller Manager to reach the cloudscale.ch API.
83+
// Set this to a specific IP address (e.g., "10.0.0.1") only if you have configured
84+
// a NAT gateway or similar infrastructure on the private network.
85+
// +optional
86+
GatewayAddress *string `json:"gatewayAddress,omitempty"`
87+
}
88+
89+
// LoadBalancerSpec defines the load balancer configuration for the control plane.
90+
type LoadBalancerSpec struct {
91+
// Enabled controls whether a load balancer is created for the control plane.
92+
// Set to false for external control planes (e.g., hosted control plane) where the endpoint
93+
// is provided externally.
94+
// +kubebuilder:default=true
95+
// +optional
96+
Enabled *bool `json:"enabled,omitempty"`
97+
98+
// Algorithm is the load balancing algorithm.
99+
// +kubebuilder:validation:Enum=round_robin;least_connections;source_ip
100+
// +kubebuilder:default="round_robin"
101+
// +optional
102+
Algorithm string `json:"algorithm,omitempty"`
32103

33-
// foo is an example field of CloudscaleCluster. Edit cloudscalecluster_types.go to remove/update
104+
// Flavor is the load balancer flavor (size).
105+
// +kubebuilder:default="lb-standard"
34106
// +optional
35-
Foo *string `json:"foo,omitempty"`
107+
Flavor string `json:"flavor,omitempty"`
108+
109+
// APIServerPort is the port for the Kubernetes API server.
110+
// +kubebuilder:default=6443
111+
// +kubebuilder:validation:Minimum=1
112+
// +kubebuilder:validation:Maximum=65535
113+
// +optional
114+
APIServerPort int32 `json:"apiServerPort,omitempty"`
115+
116+
// HealthMonitor configures the load balancer health monitor.
117+
// +optional
118+
HealthMonitor HealthMonitorSpec `json:"healthMonitor,omitempty"`
119+
}
120+
121+
// HealthMonitorSpec configures the load balancer health monitor.
122+
type HealthMonitorSpec struct {
123+
// DelayS is the interval between health checks in seconds.
124+
// +kubebuilder:default=5
125+
// +kubebuilder:validation:Minimum=1
126+
// +kubebuilder:validation:Maximum=300
127+
// +optional
128+
DelayS int `json:"delayS,omitempty"`
129+
130+
// TimeoutS is the health check timeout in seconds.
131+
// +kubebuilder:default=3
132+
// +kubebuilder:validation:Minimum=1
133+
// +kubebuilder:validation:Maximum=300
134+
// +optional
135+
TimeoutS int `json:"timeoutS,omitempty"`
136+
137+
// UpThreshold is the number of successful checks to mark healthy.
138+
// +kubebuilder:default=2
139+
// +kubebuilder:validation:Minimum=1
140+
// +kubebuilder:validation:Maximum=10
141+
// +optional
142+
UpThreshold int `json:"upThreshold,omitempty"`
143+
144+
// DownThreshold is the number of failed checks to mark unhealthy.
145+
// +kubebuilder:default=3
146+
// +kubebuilder:validation:Minimum=1
147+
// +kubebuilder:validation:Maximum=10
148+
// +optional
149+
DownThreshold int `json:"downThreshold,omitempty"`
36150
}
37151

38152
// CloudscaleClusterStatus defines the observed state of CloudscaleCluster.
39153
type CloudscaleClusterStatus struct {
40-
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
41-
// Important: Run "make" to regenerate code after modifying this file
154+
// Initialization contains v1beta2 initialization tracking.
155+
// +optional
156+
Initialization *ClusterInitializationStatus `json:"initialization,omitempty"`
157+
158+
// NetworkID is the cloudscale.ch network UUID.
159+
// +optional
160+
NetworkID string `json:"networkID,omitempty"`
42161

43-
// For Kubernetes API conventions, see:
44-
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
162+
// SubnetID is the cloudscale.ch subnet UUID.
163+
// +optional
164+
SubnetID string `json:"subnetID,omitempty"`
165+
166+
// LoadBalancerID is the cloudscale.ch load balancer UUID.
167+
// +optional
168+
LoadBalancerID string `json:"loadBalancerID,omitempty"`
169+
170+
// LoadBalancerPoolID is the cloudscale.ch load balancer pool UUID for the API server.
171+
// +optional
172+
LoadBalancerPoolID string `json:"loadBalancerPoolID,omitempty"`
173+
174+
// LoadBalancerListenerID is the cloudscale.ch load balancer listener UUID for the API server.
175+
// +optional
176+
LoadBalancerListenerID string `json:"loadBalancerListenerID,omitempty"`
177+
178+
// LoadBalancerHealthMonitorID is the cloudscale.ch load balancer health monitor UUID.
179+
// +optional
180+
LoadBalancerHealthMonitorID string `json:"loadBalancerHealthMonitorID,omitempty"`
181+
182+
// LoadBalancerMemberIDs are the list of nodes attached to the loadBalancer.
183+
// +optional
184+
LoadBalancerMemberIDs []string `json:"LoadBalancerMemberIDs,omitempty"`
45185

46186
// conditions represent the current state of the CloudscaleCluster resource.
47187
// Each condition has a unique type and reflects the status of a specific aspect of the resource.
@@ -58,8 +198,21 @@ type CloudscaleClusterStatus struct {
58198
Conditions []metav1.Condition `json:"conditions,omitempty"`
59199
}
60200

201+
// ClusterInitializationStatus contains v1beta2 initialization tracking for CloudscaleCluster.
202+
type ClusterInitializationStatus struct {
203+
// Provisioned indicates that all cluster infrastructure has been provisioned.
204+
// True when Network, Subnet, Load Balancer, and Control Plane Endpoint are ready.
205+
// +optional
206+
Provisioned *bool `json:"provisioned,omitempty"`
207+
}
208+
61209
// +kubebuilder:object:root=true
62210
// +kubebuilder:subresource:status
211+
// +kubebuilder:resource:path=cloudscaleclusters,scope=Namespaced,categories=cluster-api
212+
// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster"
213+
// +kubebuilder:printcolumn:name="Provisioned",type="string",JSONPath=".status.initialization.provisioned",description="Infrastructure provisioned"
214+
// +kubebuilder:printcolumn:name="Region",type="string",JSONPath=".spec.region",description="cloudscale.ch region"
215+
// +kubebuilder:printcolumn:name="Endpoint",type="string",JSONPath=".spec.controlPlaneEndpoint.host",description="Control plane endpoint"
63216

64217
// CloudscaleCluster is the Schema for the cloudscaleclusters API
65218
type CloudscaleCluster struct {
@@ -78,6 +231,40 @@ type CloudscaleCluster struct {
78231
Status CloudscaleClusterStatus `json:"status,omitzero"`
79232
}
80233

234+
// ensures CloudscaleCluster implements conditions.Setter
235+
var _ conditions.Setter = &CloudscaleCluster{}
236+
237+
// GetConditions returns the conditions for the CloudscaleCluster.
238+
// This implements the conditions.Getter interface from CAPI util/conditions.
239+
func (c *CloudscaleCluster) GetConditions() []metav1.Condition {
240+
return c.Status.Conditions
241+
}
242+
243+
// SetConditions sets the conditions for the CloudscaleCluster.
244+
// This implements the conditions.Setter interface from CAPI util/conditions.
245+
func (c *CloudscaleCluster) SetConditions(conds []metav1.Condition) {
246+
c.Status.Conditions = conds
247+
}
248+
249+
// CredentialsSecretRef returns an ObjectReference to the credentials Secret.
250+
func (c *CloudscaleCluster) CredentialsSecretRef() corev1.ObjectReference {
251+
ns := c.Spec.CredentialsRef.Namespace
252+
if ns == "" {
253+
ns = c.Namespace
254+
}
255+
return corev1.ObjectReference{
256+
APIVersion: "v1",
257+
Kind: "Secret",
258+
Name: c.Spec.CredentialsRef.Name,
259+
Namespace: ns,
260+
}
261+
}
262+
263+
// ClusterTagKey generates the key for resources associated with a cluster.
264+
func (c *CloudscaleCluster) ClusterTagKey() string {
265+
return NameCloudscaleProviderOwned + c.Name
266+
}
267+
81268
// +kubebuilder:object:root=true
82269

83270
// CloudscaleClusterList contains a list of CloudscaleCluster
@@ -88,5 +275,8 @@ type CloudscaleClusterList struct {
88275
}
89276

90277
func init() {
91-
SchemeBuilder.Register(&CloudscaleCluster{}, &CloudscaleClusterList{})
278+
objectTypes = append(objectTypes,
279+
&CloudscaleCluster{},
280+
&CloudscaleClusterList{},
281+
)
92282
}

api/v1beta2/cloudscalemachine_types.go

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,65 @@ package v1beta2
1818

1919
import (
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
2122
)
2223

23-
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
24-
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
25-
2624
// CloudscaleMachineSpec defines the desired state of CloudscaleMachine
2725
type CloudscaleMachineSpec struct {
28-
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
29-
// Important: Run "make" to regenerate code after modifying this file
30-
// The following markers will use OpenAPI v3 schema to validate the value
31-
// More info: https://book.kubebuilder.io/reference/markers/crd-validation.html
26+
// ProviderID is the unique identifier as specified by the cloud provider.
27+
// Format: cloudscale://<server-uuid>
28+
// +optional
29+
ProviderID *string `json:"providerID,omitempty"`
30+
31+
// Flavor is the cloudscale.ch server flavor (e.g., "flex-8-4").
32+
// +kubebuilder:validation:Required
33+
// +kubebuilder:validation:MinLength=1
34+
Flavor string `json:"flavor"`
3235

33-
// foo is an example field of CloudscaleMachine. Edit cloudscalemachine_types.go to remove/update
36+
// Image is the OS image slug (e.g., "ubuntu-24.04") or custom image UUID.
37+
// +kubebuilder:validation:Required
38+
// +kubebuilder:validation:MinLength=1
39+
Image string `json:"image"`
40+
41+
// RootVolumeSize is the root volume size in GB.
42+
// +kubebuilder:validation:Minimum=10
3443
// +optional
35-
Foo *string `json:"foo,omitempty"`
44+
RootVolumeSize int `json:"rootVolumeSize,omitempty"`
45+
46+
// SSHKeys is a list of SSH public keys to install on the server.
47+
// +optional
48+
SSHKeys []string `json:"sshKeys,omitempty"`
49+
50+
// Tags are key-value pairs to apply to the server.
51+
// +optional
52+
Tags map[string]string `json:"tags,omitempty"`
53+
}
54+
55+
// MachineInitializationStatus contains v1beta2 initialization tracking for CloudscaleMachine.
56+
type MachineInitializationStatus struct {
57+
// Provisioned indicates that the machine infrastructure has been provisioned.
58+
// True when the server is running and ready.
59+
// +optional
60+
Provisioned *bool `json:"provisioned,omitempty"`
3661
}
3762

3863
// CloudscaleMachineStatus defines the observed state of CloudscaleMachine.
3964
type CloudscaleMachineStatus struct {
40-
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
41-
// Important: Run "make" to regenerate code after modifying this file
65+
// Initialization contains v1beta2 initialization tracking.
66+
// +optional
67+
Initialization *MachineInitializationStatus `json:"initialization,omitempty"`
68+
69+
// ServerID is the cloudscale.ch server UUID.
70+
// +optional
71+
ServerID string `json:"serverID,omitempty"`
72+
73+
// FailureDomain is the actual zone where the server is running.
74+
// +optional
75+
FailureDomain string `json:"failureDomain,omitempty"`
4276

43-
// For Kubernetes API conventions, see:
44-
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties
77+
// Addresses contains the machine's addresses.
78+
// +optional
79+
Addresses []clusterv1.MachineAddress `json:"addresses,omitempty"`
4580

4681
// conditions represent the current state of the CloudscaleMachine resource.
4782
// Each condition has a unique type and reflects the status of a specific aspect of the resource.
@@ -60,6 +95,11 @@ type CloudscaleMachineStatus struct {
6095

6196
// +kubebuilder:object:root=true
6297
// +kubebuilder:subresource:status
98+
// +kubebuilder:resource:path=cloudscalemachines,scope=Namespaced,categories=cluster-api
99+
// +kubebuilder:printcolumn:name="Cluster",type="string",JSONPath=".metadata.labels.cluster\\.x-k8s\\.io/cluster-name",description="Cluster"
100+
// +kubebuilder:printcolumn:name="Provisioned",type="string",JSONPath=".status.initialization.provisioned",description="Machine provisioned"
101+
// +kubebuilder:printcolumn:name="ProviderID",type="string",JSONPath=".spec.providerID",description="cloudscale.ch server ID"
102+
// +kubebuilder:printcolumn:name="Machine",type="string",JSONPath=".metadata.ownerReferences[?(@.kind==\"Machine\")].name",description="Machine object"
63103

64104
// CloudscaleMachine is the Schema for the cloudscalemachines API
65105
type CloudscaleMachine struct {
@@ -88,5 +128,8 @@ type CloudscaleMachineList struct {
88128
}
89129

90130
func init() {
91-
SchemeBuilder.Register(&CloudscaleMachine{}, &CloudscaleMachineList{})
131+
objectTypes = append(objectTypes,
132+
&CloudscaleMachine{},
133+
&CloudscaleMachineList{},
134+
)
92135
}

api/v1beta2/cloudscalemachinetemplate_types.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,8 @@ type CloudscaleMachineTemplateList struct {
8888
}
8989

9090
func init() {
91-
SchemeBuilder.Register(&CloudscaleMachineTemplate{}, &CloudscaleMachineTemplateList{})
91+
objectTypes = append(objectTypes,
92+
&CloudscaleMachineTemplate{},
93+
&CloudscaleMachineTemplateList{},
94+
)
9295
}

0 commit comments

Comments
 (0)