Skip to content

Commit 59d34b9

Browse files
author
Richard Kovacs
committed
Requeue non empty vpc deletion
1 parent 98aa3a0 commit 59d34b9

9 files changed

+94
-89
lines changed

api/v1alpha1/linodemachine_types.go

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,38 +80,28 @@ type LinodeMachineSpec struct {
8080
// InstanceMetadataOptions defines metadata of instance
8181
type InstanceMetadataOptions struct {
8282
// UserData expects a Base64-encoded string
83-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
8483
UserData string `json:"userData,omitempty"`
8584
}
8685

8786
// InstanceConfigInterfaceCreateOptions defines network interface config
8887
type InstanceConfigInterfaceCreateOptions struct {
89-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
9088
IPAMAddress string `json:"ipamAddress,omitempty"`
9189
// +kubebuilder:validation:MinLength=3
9290
// +kubebuilder:validation:MaxLength=63
93-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
9491
// +optional
95-
Label string `json:"label,omitempty"`
96-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
92+
Label string `json:"label,omitempty"`
9793
Purpose linodego.ConfigInterfacePurpose `json:"purpose,omitempty"`
98-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
99-
Primary bool `json:"primary,omitempty"`
100-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
94+
Primary bool `json:"primary,omitempty"`
10195
// +optional
10296
SubnetID *int `json:"subnetId,omitempty"`
103-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
10497
// +optional
105-
IPv4 *VPCIPv4 `json:"ipv4,omitempty"`
106-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
98+
IPv4 *VPCIPv4 `json:"ipv4,omitempty"`
10799
IPRanges []string `json:"ipRanges,omitempty"`
108100
}
109101

110102
// VPCIPv4 defines VPC IPV4 settings
111103
type VPCIPv4 struct {
112-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
113-
VPC string `json:"vpc,omitempty"`
114-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
104+
VPC string `json:"vpc,omitempty"`
115105
NAT1To1 string `json:"nat1to1,omitempty"`
116106
}
117107

api/v1alpha1/linodevpc_types.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@ type LinodeVPCSpec struct {
4747
type VPCSubnetCreateOptions struct {
4848
// +kubebuilder:validation:MinLength=3
4949
// +kubebuilder:validation:MaxLength=63
50-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
5150
// +optional
5251
Label string `json:"label,omitempty"`
53-
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
5452
// +optional
5553
IPv4 string `json:"ipv4,omitempty"`
5654
}

config/crd/bases/infrastructure.cluster.x-k8s.io_linodemachines.yaml

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -85,55 +85,28 @@ spec:
8585
items:
8686
type: string
8787
type: array
88-
x-kubernetes-validations:
89-
- message: Value is immutable
90-
rule: self == oldSelf
9188
ipamAddress:
9289
type: string
93-
x-kubernetes-validations:
94-
- message: Value is immutable
95-
rule: self == oldSelf
9690
ipv4:
9791
description: VPCIPv4 defines VPC IPV4 settings
9892
properties:
9993
nat1to1:
10094
type: string
101-
x-kubernetes-validations:
102-
- message: Value is immutable
103-
rule: self == oldSelf
10495
vpc:
10596
type: string
106-
x-kubernetes-validations:
107-
- message: Value is immutable
108-
rule: self == oldSelf
10997
type: object
110-
x-kubernetes-validations:
111-
- message: Value is immutable
112-
rule: self == oldSelf
11398
label:
11499
maxLength: 63
115100
minLength: 3
116101
type: string
117-
x-kubernetes-validations:
118-
- message: Value is immutable
119-
rule: self == oldSelf
120102
primary:
121103
type: boolean
122-
x-kubernetes-validations:
123-
- message: Value is immutable
124-
rule: self == oldSelf
125104
purpose:
126105
description: ConfigInterfacePurpose options start with InterfacePurpose
127106
and include all known interface purpose types
128107
type: string
129-
x-kubernetes-validations:
130-
- message: Value is immutable
131-
rule: self == oldSelf
132108
subnetId:
133109
type: integer
134-
x-kubernetes-validations:
135-
- message: Value is immutable
136-
rule: self == oldSelf
137110
type: object
138111
type: array
139112
x-kubernetes-validations:
@@ -152,9 +125,6 @@ spec:
152125
userData:
153126
description: UserData expects a Base64-encoded string
154127
type: string
155-
x-kubernetes-validations:
156-
- message: Value is immutable
157-
rule: self == oldSelf
158128
type: object
159129
x-kubernetes-validations:
160130
- message: Value is immutable

config/crd/bases/infrastructure.cluster.x-k8s.io_linodevpcs.yaml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,10 @@ spec:
5454
properties:
5555
ipv4:
5656
type: string
57-
x-kubernetes-validations:
58-
- message: Value is immutable
59-
rule: self == oldSelf
6057
label:
6158
maxLength: 63
6259
minLength: 3
6360
type: string
64-
x-kubernetes-validations:
65-
- message: Value is immutable
66-
rule: self == oldSelf
6761
type: object
6862
type: array
6963
x-kubernetes-validations:

controller/linodemachine_controller.go

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ var skippedMachinePhases = map[string]bool{
5454
}
5555

5656
var skippedInstanceStatuses = map[linodego.InstanceStatus]bool{
57-
linodego.InstanceOffline: true,
5857
linodego.InstanceShuttingDown: true,
5958
linodego.InstanceDeleting: true,
6059
}
6160

6261
var requeueInstanceStatuses = map[linodego.InstanceStatus]bool{
62+
linodego.InstanceOffline: true,
6363
linodego.InstanceBooting: true,
6464
linodego.InstanceRebooting: true,
6565
linodego.InstanceProvisioning: true,
@@ -154,7 +154,7 @@ func (r *LinodeMachineReconciler) Reconcile(ctx context.Context, req ctrl.Reques
154154
Name: cluster.Spec.InfrastructureRef.Name,
155155
}
156156

157-
if err := r.Client.Get(ctx, linodeClusterKey, linodeCluster); err != nil {
157+
if err = r.Client.Get(ctx, linodeClusterKey, linodeCluster); err != nil {
158158
log.Error(err, "Failed to fetch Linode cluster")
159159

160160
return ctrl.Result{}, client.IgnoreNotFound(err)
@@ -248,6 +248,8 @@ func (r *LinodeMachineReconciler) reconcile(
248248
}
249249

250250
func (r *LinodeMachineReconciler) reconcileCreate(ctx context.Context, machineScope *scope.MachineScope, logger logr.Logger) (*linodego.Instance, error) {
251+
logger.Info("creating machine")
252+
251253
tags := []string{string(machineScope.LinodeCluster.UID), string(machineScope.LinodeMachine.UID)}
252254

253255
linodeInstances, err := machineScope.LinodeClient.ListInstances(ctx, linodego.NewListOptions(1, util.CreateLinodeAPIFilter("", tags)))
@@ -327,21 +329,20 @@ func (r *LinodeMachineReconciler) reconcileCreate(ctx context.Context, machineSc
327329
}
328330

329331
func (r *LinodeMachineReconciler) reconcileUpdate(ctx context.Context, logger logr.Logger, machineScope *scope.MachineScope) (res reconcile.Result, linodeInstance *linodego.Instance, err error) {
330-
if machineScope.LinodeMachine.Spec.InstanceID == nil {
331-
err = errors.New("missing instance ID")
332-
333-
return
334-
}
332+
logger.Info("updating machine")
335333

336334
res = ctrl.Result{}
337335

338-
if linodeInstance, err = machineScope.LinodeClient.GetInstance(ctx, *machineScope.LinodeMachine.Spec.InstanceID); err != nil {
339-
logger.Error(err, "Failed to get Linode machine instance")
336+
if machineScope.LinodeMachine.Spec.InstanceID == nil {
337+
return res, nil, errors.New("missing instance ID")
338+
}
340339

341-
// Not found is not an error
342-
apiErr := linodego.Error{Code: http.StatusNotFound}
343-
if apiErr.Is(err) {
344-
err = nil
340+
if linodeInstance, err = machineScope.LinodeClient.GetInstance(ctx, *machineScope.LinodeMachine.Spec.InstanceID); err != nil {
341+
err = util.IgnoreLinodeAPIError(err, http.StatusNotFound)
342+
if err != nil {
343+
logger.Error(err, "Failed to get Linode machine instance")
344+
} else {
345+
logger.Info("Instance not found, let's create a new one")
345346

346347
// Create new machine
347348
machineScope.LinodeMachine.Spec.ProviderID = nil
@@ -350,44 +351,46 @@ func (r *LinodeMachineReconciler) reconcileUpdate(ctx context.Context, logger lo
350351
conditions.MarkFalse(machineScope.LinodeMachine, clusterv1.ReadyCondition, string("missing"), clusterv1.ConditionSeverityWarning, "instance not found")
351352
}
352353

353-
return
354+
return res, linodeInstance, err
354355
}
355356

356357
if _, ok := requeueInstanceStatuses[linodeInstance.Status]; ok {
357358
if linodeInstance.Updated.Add(reconciler.DefaultMachineControllerWaitForRunningTimeout).After(time.Now()) {
358359
logger.Info("Instance has one operaton running, re-queuing reconciliation", "status", linodeInstance.Status)
359360

360-
res = ctrl.Result{RequeueAfter: reconciler.DefaultMachineControllerWaitForRunningDelay}
361-
} else {
362-
logger.Info("Instance has one operaton long running, skipping reconciliation", "status", linodeInstance.Status)
361+
return ctrl.Result{RequeueAfter: reconciler.DefaultMachineControllerWaitForRunningDelay}, linodeInstance, nil
363362
}
364363

365-
return
364+
logger.Info("Instance has one operaton long running, skipping reconciliation", "status", linodeInstance.Status)
365+
366+
conditions.MarkFalse(machineScope.LinodeMachine, clusterv1.ReadyCondition, string(linodeInstance.Status), clusterv1.ConditionSeverityInfo, "skipped due to long running operation")
367+
368+
return res, linodeInstance, nil
366369
} else if _, ok := skippedInstanceStatuses[linodeInstance.Status]; ok || linodeInstance.Status != linodego.InstanceRunning {
367370
logger.Info("Instance has incompatible status, skipping reconciliation", "status", linodeInstance.Status)
368371

369372
conditions.MarkFalse(machineScope.LinodeMachine, clusterv1.ReadyCondition, string(linodeInstance.Status), clusterv1.ConditionSeverityInfo, "incompatible status")
370373

371-
return
374+
return res, linodeInstance, nil
372375
}
373376

377+
machineScope.LinodeMachine.Status.Ready = true
378+
374379
conditions.MarkTrue(machineScope.LinodeMachine, clusterv1.ReadyCondition)
375380

376381
r.Recorder.Event(machineScope.LinodeMachine, corev1.EventTypeNormal, string(clusterv1.ReadyCondition), "instance is running")
377382

378-
return
383+
return res, linodeInstance, nil
379384
}
380385

381386
func (r *LinodeMachineReconciler) reconcileDelete(ctx context.Context, logger logr.Logger, machineScope *scope.MachineScope) error {
382387
logger.Info("deleting machine")
383388

384389
if machineScope.LinodeMachine.Spec.InstanceID != nil {
385390
if err := machineScope.LinodeClient.DeleteInstance(ctx, *machineScope.LinodeMachine.Spec.InstanceID); err != nil {
386-
logger.Info("Failed to delete Linode machine instance")
391+
if util.IgnoreLinodeAPIError(err, http.StatusNotFound) != nil {
392+
logger.Error(err, "Failed to delete Linode machine instance")
387393

388-
// Not found is not an error
389-
apiErr := linodego.Error{Code: http.StatusNotFound}
390-
if !apiErr.Is(err) {
391394
return err
392395
}
393396
}

controller/linodevpc_controller.go

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ import (
3232
ctrl "sigs.k8s.io/controller-runtime"
3333
"sigs.k8s.io/controller-runtime/pkg/client"
3434
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
35+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3536

3637
"github.com/go-logr/logr"
3738
infrav1 "github.com/linode/cluster-api-provider-linode/api/v1alpha1"
3839
"github.com/linode/cluster-api-provider-linode/cloud/scope"
3940
"github.com/linode/cluster-api-provider-linode/util"
4041
"github.com/linode/cluster-api-provider-linode/util/reconciler"
41-
"github.com/linode/linodego"
4242
)
4343

4444
// LinodeVPCReconciler reconciles a LinodeVPC object
@@ -129,7 +129,7 @@ func (r *LinodeVPCReconciler) reconcile(
129129
if !vpcScope.LinodeVPC.ObjectMeta.DeletionTimestamp.IsZero() {
130130
failureReason = infrav1.DeleteVPCError
131131

132-
err = r.reconcileDelete(ctx, logger, vpcScope)
132+
res, err = r.reconcileDelete(ctx, logger, vpcScope)
133133

134134
return
135135
}
@@ -156,6 +156,8 @@ func (r *LinodeVPCReconciler) reconcile(
156156
}
157157

158158
func (r *LinodeVPCReconciler) reconcileCreate(ctx context.Context, vpcScope *scope.VPCScope, logger logr.Logger) error {
159+
logger.Info("creating vpc")
160+
159161
if err := r.reconcileVPC(ctx, vpcScope, logger); err != nil {
160162
logger.Error(err, "Failed to create VPC")
161163

@@ -171,6 +173,9 @@ func (r *LinodeVPCReconciler) reconcileCreate(ctx context.Context, vpcScope *sco
171173
}
172174

173175
func (r *LinodeVPCReconciler) reconcileUpdate(ctx context.Context, logger logr.Logger, vpcScope *scope.VPCScope) error {
176+
logger.Info("updating vpc")
177+
178+
// Update is not supported at the moment
174179
if err := r.reconcileVPC(ctx, vpcScope, logger); err != nil {
175180
logger.Error(err, "Failed to update VPC")
176181

@@ -185,17 +190,46 @@ func (r *LinodeVPCReconciler) reconcileUpdate(ctx context.Context, logger logr.L
185190
return nil
186191
}
187192

188-
func (r *LinodeVPCReconciler) reconcileDelete(ctx context.Context, logger logr.Logger, vpcScope *scope.VPCScope) error {
193+
//nolint:nestif // As simple as possible.
194+
func (r *LinodeVPCReconciler) reconcileDelete(ctx context.Context, logger logr.Logger, vpcScope *scope.VPCScope) (reconcile.Result, error) {
189195
logger.Info("deleting VPC")
190196

197+
res := ctrl.Result{}
198+
191199
if vpcScope.LinodeVPC.Spec.VPCID != nil {
192-
if err := vpcScope.LinodeClient.DeleteVPC(ctx, *vpcScope.LinodeVPC.Spec.VPCID); err != nil {
193-
logger.Error(err, "Failed to delete VPC")
200+
vpc, err := vpcScope.LinodeClient.GetVPC(ctx, *vpcScope.LinodeVPC.Spec.VPCID)
201+
if util.IgnoreLinodeAPIError(err, http.StatusNotFound) != nil {
202+
logger.Error(err, "Failed to fetch VPC")
203+
204+
return res, err
205+
} else if vpc == nil {
206+
return res, errors.New("failed to fetch VPC")
207+
}
208+
209+
if vpc != nil {
210+
for i := range vpc.Subnets {
211+
if len(vpc.Subnets[i].Linodes) == 0 {
212+
continue
213+
}
214+
215+
if vpc.Updated.Add(reconciler.DefaultVPCControllerWaitForHasNodesTimeout).After(time.Now()) {
216+
logger.Info("VPC has node(s) attached, re-queuing reconciliation")
194217

195-
// Not found is not an error
196-
apiErr := linodego.Error{Code: http.StatusNotFound}
197-
if !apiErr.Is(err) {
198-
return err
218+
return ctrl.Result{RequeueAfter: reconciler.DefaultVPCControllerWaitForHasNodesDelay}, nil
219+
}
220+
221+
logger.Info("VPC has node(s) attached for long, skipping reconciliation")
222+
223+
conditions.MarkFalse(vpcScope.LinodeVPC, clusterv1.ReadyCondition, clusterv1.DeletionFailedReason, clusterv1.ConditionSeverityInfo, "skipped due to node(s) attached")
224+
225+
return res, nil
226+
}
227+
228+
err = vpcScope.LinodeClient.DeleteVPC(ctx, *vpcScope.LinodeVPC.Spec.VPCID)
229+
if util.IgnoreLinodeAPIError(err, http.StatusNotFound) != nil {
230+
logger.Error(err, "Failed to delete VPC")
231+
232+
return res, err
199233
}
200234
}
201235
} else {
@@ -209,7 +243,7 @@ func (r *LinodeVPCReconciler) reconcileDelete(ctx context.Context, logger logr.L
209243
vpcScope.LinodeVPC.Spec.VPCID = nil
210244
controllerutil.RemoveFinalizer(vpcScope.LinodeVPC, infrav1.GroupVersion.String())
211245

212-
return nil
246+
return res, nil
213247
}
214248

215249
// SetupWithManager sets up the controller with the Manager.

controller/linodevpc_controller_helpers.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,20 @@ func (r *LinodeVPCReconciler) reconcileVPC(ctx context.Context, vpcScope *scope.
5454
return nil
5555
}
5656

57-
linodeVPC, err := vpcScope.LinodeClient.CreateVPC(ctx, *createConfig)
57+
vpc, err := vpcScope.LinodeClient.CreateVPC(ctx, *createConfig)
5858
if err != nil {
5959
logger.Error(err, "Failed to create VPC")
6060

6161
return err
62-
} else if linodeVPC == nil {
62+
} else if vpc == nil {
6363
err = errors.New("missing VPC")
6464

6565
logger.Error(err, "Panic! Failed to create VPC")
6666

6767
return err
6868
}
6969

70-
vpcScope.LinodeVPC.Spec.VPCID = &linodeVPC.ID
70+
vpcScope.LinodeVPC.Spec.VPCID = &vpc.ID
7171

7272
return nil
7373
}

0 commit comments

Comments
 (0)