Skip to content
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

[feat]: Set metadata for LinodeMachines, disable swap #74

Merged
merged 4 commits into from
Feb 1, 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
64 changes: 59 additions & 5 deletions cloud/scope/machine.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
package scope

import (
"context"
"errors"
"fmt"

infrav1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1"
"github.com/linode/linodego"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
"sigs.k8s.io/cluster-api/util/patch"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

infrav1alpha1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1"
)

type MachineScopeParams struct {
Client client.Client
Cluster *clusterv1.Cluster
Machine *clusterv1.Machine
LinodeCluster *infrav1.LinodeCluster
LinodeMachine *infrav1.LinodeMachine
LinodeCluster *infrav1alpha1.LinodeCluster
LinodeMachine *infrav1alpha1.LinodeMachine
}

type MachineScope struct {
Expand All @@ -26,8 +31,8 @@ type MachineScope struct {
Cluster *clusterv1.Cluster
Machine *clusterv1.Machine
LinodeClient *linodego.Client
LinodeCluster *infrav1.LinodeCluster
LinodeMachine *infrav1.LinodeMachine
LinodeCluster *infrav1alpha1.LinodeCluster
LinodeMachine *infrav1alpha1.LinodeMachine
}

func validateMachineScopeParams(params MachineScopeParams) error {
Expand Down Expand Up @@ -69,3 +74,52 @@ func NewMachineScope(apiKey string, params MachineScopeParams) (*MachineScope, e
LinodeMachine: params.LinodeMachine,
}, nil
}

// PatchObject persists the machine configuration and status.
func (s *MachineScope) PatchObject(ctx context.Context) error {
return s.PatchHelper.Patch(ctx, s.LinodeMachine)
}

// Close closes the current scope persisting the machine configuration and status.
func (s *MachineScope) Close(ctx context.Context) error {
return s.PatchObject(ctx)
}

// AddFinalizer adds a finalizer and immediately patches the object to avoid any race conditions
func (s *MachineScope) AddFinalizer(ctx context.Context) error {
controllerutil.AddFinalizer(s.LinodeMachine, infrav1alpha1.GroupVersion.String())

return s.Close(ctx)
}

// GetBootstrapData returns the bootstrap data from the secret in the Machine's bootstrap.dataSecretName.
func (m *MachineScope) GetBootstrapData(ctx context.Context) ([]byte, error) {
if m.Machine.Spec.Bootstrap.DataSecretName == nil {
return []byte{}, fmt.Errorf(
"bootstrap data secret is nil for LinodeMachine %s/%s",
m.LinodeMachine.Namespace,
m.LinodeMachine.Name,
)
}

secret := &corev1.Secret{}
key := types.NamespacedName{Namespace: m.LinodeMachine.Namespace, Name: *m.Machine.Spec.Bootstrap.DataSecretName}
if err := m.client.Get(ctx, key, secret); err != nil {
return []byte{}, fmt.Errorf(
"failed to retrieve bootstrap data secret for LinodeMachine %s/%s",
m.LinodeMachine.Namespace,
m.LinodeMachine.Name,
)
}

value, ok := secret.Data["value"]
if !ok {
return []byte{}, fmt.Errorf(
"bootstrap data secret value key is missing for LinodeMachine %s/%s",
m.LinodeMachine.Namespace,
m.LinodeMachine.Name,
)
}

return value, nil
}
11 changes: 11 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,19 @@ rules:
- events
verbs:
- create
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- cluster.x-k8s.io
resources:
Expand Down
3 changes: 1 addition & 2 deletions controller/linodecluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,12 @@ func (r *LinodeClusterReconciler) reconcile(
if err := r.reconcileCreate(ctx, logger, clusterScope); err != nil {
return res, err
}
r.Recorder.Event(clusterScope.LinodeCluster, corev1.EventTypeNormal, string(clusterv1.ReadyCondition), "Load balancer is ready")
}

clusterScope.LinodeCluster.Status.Ready = true
conditions.MarkTrue(clusterScope.LinodeCluster, clusterv1.ReadyCondition)

r.Recorder.Event(clusterScope.LinodeCluster, corev1.EventTypeNormal, string(clusterv1.ReadyCondition), "Load balancer is ready")

return res, nil
}

Expand Down
42 changes: 32 additions & 10 deletions controller/linodemachine_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package controller

import (
"context"
b64 "encoding/base64"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -81,13 +82,14 @@ type LinodeMachineReconciler struct {
ReconcileTimeout time.Duration
}

//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=linodemachines,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=linodemachines/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=linodemachines/finalizers,verbs=update
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=linodemachines,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=linodemachines/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=linodemachines/finalizers,verbs=update

//+kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters,verbs=get;watch;list
//+kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machines,verbs=get;watch;list
//+kubebuilder:rbac:groups="",resources=events,verbs=create;update;patch
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=clusters,verbs=get;watch;list
// +kubebuilder:rbac:groups=cluster.x-k8s.io,resources=machines,verbs=get;watch;list
// +kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch
// +kubebuilder:rbac:groups="",resources=secrets;,verbs=get;list;watch

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
Expand Down Expand Up @@ -118,7 +120,6 @@ func (r *LinodeMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques

return ctrl.Result{}, nil
case skippedMachinePhases[machine.Status.Phase]:
log.Info("Machine phase is not the one we are looking for, skipping reconciliation", "phase", machine.Status.Phase)

return ctrl.Result{}, nil
default:
Expand Down Expand Up @@ -202,13 +203,19 @@ func (r *LinodeMachineReconciler) reconcile(
r.Recorder.Event(machineScope.LinodeMachine, corev1.EventTypeWarning, string(failureReason), err.Error())
}

if patchErr := machineScope.PatchHelper.Patch(ctx, machineScope.LinodeMachine); patchErr != nil && client.IgnoreNotFound(patchErr) != nil {
// Always close the scope when exiting this function so we can persist any LinodeMachine changes.
if patchErr := machineScope.Close(ctx); patchErr != nil && client.IgnoreNotFound(patchErr) != nil {
logger.Error(patchErr, "failed to patch LinodeMachine")

err = errors.Join(err, patchErr)
}
}()

// Add the finalizer if not already there
if err := machineScope.AddFinalizer(ctx); err != nil {
return res, err
}

// Delete
if !machineScope.LinodeMachine.ObjectMeta.DeletionTimestamp.IsZero() {
failureReason = cerrs.DeleteMachineError
Expand All @@ -218,8 +225,6 @@ func (r *LinodeMachineReconciler) reconcile(
return
}

controllerutil.AddFinalizer(machineScope.LinodeMachine, infrav1.GroupVersion.String())

var linodeInstance *linodego.Instance
defer func() {
machineScope.LinodeMachine.Status.InstanceState = util.Pointer(linodego.InstanceOffline)
Expand All @@ -241,7 +246,12 @@ func (r *LinodeMachineReconciler) reconcile(

// Create
failureReason = cerrs.CreateMachineError
// Make sure bootstrap data is available and populated.
if machineScope.Machine.Spec.Bootstrap.DataSecretName == nil {
logger.Info("Bootstrap data secret is not yet available")

return ctrl.Result{RequeueAfter: reconciler.DefaultMachineControllerWaitForBootstrapDelay}, nil
}
linodeInstance, err = r.reconcileCreate(ctx, machineScope, logger)

return
Expand Down Expand Up @@ -280,6 +290,18 @@ func (*LinodeMachineReconciler) reconcileCreate(ctx context.Context, machineScop
return nil, err
}
createConfig.Tags = tags
createConfig.SwapSize = util.Pointer(0)

// get the bootstrap data for the Linode instance and set it for create config
bootstrapData, err := machineScope.GetBootstrapData(ctx)
if err != nil {
logger.Info("Failed to get bootstrap data", "error", err.Error())

return nil, err
}
createConfig.Metadata = &linodego.InstanceMetadataOptions{
UserData: b64.StdEncoding.EncodeToString(bootstrapData),
}

if linodeInstance, err = machineScope.LinodeClient.CreateInstance(ctx, *createConfig); err != nil {
logger.Info("Failed to create Linode machine instance", "error", err.Error())
Expand Down
2 changes: 2 additions & 0 deletions util/reconciler/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const (
// DefaultMappingTimeout is the default timeout for a controller request mapping func.
DefaultMappingTimeout = 60 * time.Second

// DefaultMachineControllerWaitForBootstrapDelay is the default requeue delay if bootstrap data is not ready.
DefaultMachineControllerWaitForBootstrapDelay = 5 * time.Second
// DefaultMachineControllerWaitForRunningDelay is the default requeue delay if instance is not running.
DefaultMachineControllerWaitForRunningDelay = 5 * time.Second
// DefaultMachineControllerWaitForRunningTimeout is the default timeout if instance is not running.
Expand Down