Skip to content

[feat] templates: add Flatcar template #394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/v1alpha1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions api/v1alpha2/linodemachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ type LinodeMachineSpec struct {
// +optional
CredentialsRef *corev1.SecretReference `json:"credentialsRef,omitempty"`

// Configuration is the Akamai instance configuration OS,
// if not specified this defaults to the default configuration associated to the instance.
Configuration *InstanceConfiguration `json:"configuration,omitempty"`

// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
// +optional
// PlacementGroupRef is a reference to a placement group object. This makes the linode to be launched in that specific group.
Expand All @@ -110,6 +114,12 @@ type InstanceMetadataOptions struct {
UserData string `json:"userData,omitempty"`
}

// InstanceConfiguration defines the instance configuration
type InstanceConfiguration struct {
// Kernel is a Kernel ID to boot a Linode with. (e.g linode/latest-64bit)
Kernel string `json:"kernel,omitempty"`
}

// InstanceConfigInterfaceCreateOptions defines network interface config
type InstanceConfigInterfaceCreateOptions struct {
IPAMAddress string `json:"ipamAddress,omitempty"`
Expand Down
20 changes: 20 additions & 0 deletions api/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,16 @@ spec:
x-kubernetes-validations:
- message: Value is immutable
rule: self == oldSelf
configuration:
description: |-
Configuration is the Akamai instance configuration OS,
if not specified this defaults to the default configuration associated to the instance.
properties:
kernel:
description: Kernel is a Kernel ID to boot a Linode with. (e.g
linode/latest-64bit)
type: string
type: object
credentialsRef:
description: |-
CredentialsRef is a reference to a Secret that contains the credentials
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,16 @@ spec:
x-kubernetes-validations:
- message: Value is immutable
rule: self == oldSelf
configuration:
description: |-
Configuration is the Akamai instance configuration OS,
if not specified this defaults to the default configuration associated to the instance.
properties:
kernel:
description: Kernel is a Kernel ID to boot a Linode with.
(e.g linode/latest-64bit)
type: string
type: object
credentialsRef:
description: |-
CredentialsRef is a reference to a Secret that contains the credentials
Expand Down
36 changes: 30 additions & 6 deletions controller/linodemachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@
return r.reconcileInstanceCreate(ctx, logger, machineScope, linodeInstance)
}

//nolint:cyclop,gocognit // It is ok for the moment but need larger refactor.
func (r *LinodeMachineReconciler) reconcileInstanceCreate(
ctx context.Context,
logger logr.Logger,
Expand All @@ -357,6 +358,18 @@
conditions.MarkTrue(machineScope.LinodeMachine, ConditionPreflightConfigured)
}

if machineScope.LinodeMachine.Spec.Configuration != nil && machineScope.LinodeMachine.Spec.Configuration.Kernel != "" {
instanceConfig, err := r.getDefaultInstanceConfig(ctx, machineScope, linodeInstance.ID)
if err != nil {
logger.Error(err, "Failed to get default instance configuration")
return ctrl.Result{}, err

Check warning on line 365 in controller/linodemachine_controller.go

View check run for this annotation

Codecov / codecov/patch

controller/linodemachine_controller.go#L362-L365

Added lines #L362 - L365 were not covered by tests
}

if _, err := machineScope.LinodeClient.UpdateInstanceConfig(ctx, linodeInstance.ID, instanceConfig.ID, linodego.InstanceConfigUpdateOptions{Kernel: machineScope.LinodeMachine.Spec.Configuration.Kernel}); err != nil {
return ctrl.Result{}, err

Check warning on line 369 in controller/linodemachine_controller.go

View check run for this annotation

Codecov / codecov/patch

controller/linodemachine_controller.go#L368-L369

Added lines #L368 - L369 were not covered by tests
}
}

if !reconciler.ConditionTrue(machineScope.LinodeMachine, ConditionPreflightBootTriggered) {
if err := machineScope.LinodeClient.BootInstance(ctx, linodeInstance.ID, 0); err != nil && !strings.HasSuffix(err.Error(), "already booted.") {
logger.Error(err, "Failed to boot instance")
Expand Down Expand Up @@ -529,16 +542,14 @@
if reconciler.ConditionTrue(machineScope.LinodeMachine, ConditionPreflightRootDiskResized) {
return nil
}
// get the default instance config
configs, err := machineScope.LinodeClient.ListInstanceConfigs(ctx, linodeInstanceID, &linodego.ListOptions{})
if err != nil || len(configs) == 0 {
logger.Error(err, "Failed to list instance configs")

conditions.MarkFalse(machineScope.LinodeMachine, ConditionPreflightRootDiskResized, string(cerrs.CreateMachineError), clusterv1.ConditionSeverityWarning, err.Error())
instanceConfig, err := r.getDefaultInstanceConfig(ctx, machineScope, linodeInstanceID)
if err != nil {
logger.Error(err, "Failed to get default instance configuration")

Check warning on line 548 in controller/linodemachine_controller.go

View check run for this annotation

Codecov / codecov/patch

controller/linodemachine_controller.go#L548

Added line #L548 was not covered by tests

conditions.MarkFalse(machineScope.LinodeMachine, ConditionPreflightRootDiskResized, string(cerrs.CreateMachineError), clusterv1.ConditionSeverityWarning, err.Error())

Check warning on line 550 in controller/linodemachine_controller.go

View check run for this annotation

Codecov / codecov/patch

controller/linodemachine_controller.go#L550

Added line #L550 was not covered by tests
return err
}
instanceConfig := configs[0]

if instanceConfig.Devices.SDA == nil {
conditions.MarkFalse(machineScope.LinodeMachine, ConditionPreflightRootDiskResized, string(cerrs.CreateMachineError), clusterv1.ConditionSeverityWarning, "root disk not yet ready")
Expand Down Expand Up @@ -779,3 +790,16 @@
func (r *LinodeMachineReconciler) TracedClient() client.Client {
return wrappedruntimeclient.NewRuntimeClientWithTracing(r.Client, wrappedruntimeclient.DefaultDecorator())
}

func (r *LinodeMachineReconciler) getDefaultInstanceConfig(
ctx context.Context,
machineScope *scope.MachineScope,
linodeInstanceID int,
) (linodego.InstanceConfig, error) {
configs, err := machineScope.LinodeClient.ListInstanceConfigs(ctx, linodeInstanceID, &linodego.ListOptions{})
if err != nil || len(configs) == 0 {
return linodego.InstanceConfig{}, fmt.Errorf("failing to list instance configurations: %w", err)

Check warning on line 801 in controller/linodemachine_controller.go

View check run for this annotation

Codecov / codecov/patch

controller/linodemachine_controller.go#L801

Added line #L801 was not covered by tests
}

return configs[0], nil
}
56 changes: 56 additions & 0 deletions docs/src/topics/flavors/flatcar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Flatcar

This flavor supports provisioning k8s clusters outside of VPC using [Flatcar][flatcar] as a base OS. It uses kubeadm for
setting up control plane and uses cilium with VXLAN for pod networking.

## Specification
| Supported Control Plane | CNI | Default OS | Installs ClusterClass | IPv4 | IPv6 |
|-------------------------|--------|--------------|-----------------------|------|------|
| kubeadm | Cilium | Flatcar | No | Yes | No |

## Notes
This flavor is identical to the default flavor with the exception that it provisions
k8s clusters without VPC using [Flatcar][flatcar] as a base OS. Since it runs outside of VPC, native routing is not
supported in this flavor and it uses VXLAN for pod to pod communication.

## Usage

### Initialization

Before generating the cluster configuration, it is required to initialize the management cluster with [Ignition][ignition] support to provision Flatcar nodes:

```bash
export EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION=true
clusterctl init --infrastructure linode-linode --addon helm
```

### Import the Flatcar image

Flatcar is not officially provided by Akamai/Linode so it is required to import a Flatcar image. Akamai support is available on Flatcar since the release [4012.0.0][release-4012]: all releases equal or greater than this major release will fit.

To import the image, it is recommended to follow this documentation: https://www.flatcar.org/docs/latest/installing/community-platforms/akamai/#importing-an-image

By following this import step, you will get the Flatcar image ID stored into `IMAGE_ID`.

### Configure and deploy the workload cluster

1. Set the Flatcar image name from the previous step:
```bash
export FLATCAR_IMAGE_NAME="${IMAGE_ID}"
```

2. Generate cluster yaml
```bash
clusterctl generate cluster test-cluster \
--kubernetes-version v1.29.1 \
--infrastructure linode-linode \
--flavor kubeadm-flatcar > test-cluster.yaml
```
2. Apply cluster yaml
```bash
kubectl apply -f test-cluster.yaml
```

[flatcar]: https://www.flatcar.org/
[ignition]: https://coreos.github.io/ignition/
[release-4012]: https://www.flatcar.org/releases#release-4012.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: capi-controller-manager
namespace: capi-system
status:
availableReplicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: capl-controller-manager
namespace: capl-system
status:
availableReplicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: capi-kubeadm-bootstrap-controller-manager
namespace: kubeadm-bootstrap-system
status:
availableReplicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: capi-kubeadm-control-plane-controller-manager
namespace: kubeadm-control-plane-system
status:
availableReplicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: caaph-controller-manager
namespace: caaph-system
status:
availableReplicas: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ccm-linode
namespace: kube-system
status:
currentNumberScheduled: 1
desiredNumberScheduled: 1
numberAvailable: 1
numberMisscheduled: 0
numberReady: 1
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: cilium
namespace: kube-system
status:
currentNumberScheduled: 2
desiredNumberScheduled: 2
numberAvailable: 2
numberMisscheduled: 0
numberReady: 2
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: csi-linode-node
namespace: kube-system
status:
currentNumberScheduled: 2
desiredNumberScheduled: 2
numberAvailable: 2
numberMisscheduled: 0
numberReady: 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: cilium-operator
namespace: kube-system
status:
availableReplicas: 2
readyReplicas: 2
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/part-of: cilium
namespace: kube-system
status:
readyReplicas: 1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
status:
availableReplicas: 2
readyReplicas: 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
apiVersion: infrastructure.cluster.x-k8s.io/v1alpha1
kind: LinodeMachine
metadata:
labels:
cluster.x-k8s.io/cluster-name: ($cluster)
spec:
region: (env('LINODE_REGION'))
type: g6-standard-2
status:
ready: true
instanceState: running
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: Machine
metadata:
labels:
cluster.x-k8s.io/cluster-name: ($cluster)
spec:
clusterName: ($cluster)
status:
bootstrapReady: true
infrastructureReady: true
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
labels:
cluster.x-k8s.io/cluster-name: ($cluster)
spec:
clusterName: ($cluster)
replicas: 1
status:
readyReplicas: 1
unavailableReplicas: 0
availableReplicas: 1
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: KubeadmControlPlane
metadata:
labels:
cluster.x-k8s.io/cluster-name: ($cluster)
status:
readyReplicas: 1
unavailableReplicas: 0
ready: true
---
apiVersion: addons.cluster.x-k8s.io/v1alpha1
kind: HelmReleaseProxy
metadata:
labels:
cluster.x-k8s.io/cluster-name: ($cluster)
status:
conditions:
- type: Ready
status: "True"
- type: ClusterAvailable
status: "True"
- type: HelmReleaseReady
status: "True"
status: deployed
Loading
Loading