Skip to content

Commit 1c49829

Browse files
authored
[improvement] : add conditions to check if linodevpc and linodefirewall are ready (#535)
* add conditions to check if linodevpc and linodefirewall are ready * fix lint errors * fix unittests * add unittests for new conditions added
1 parent fb042c1 commit 1c49829

File tree

4 files changed

+273
-8
lines changed

4 files changed

+273
-8
lines changed

controller/linodecluster_controller.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"github.com/go-logr/logr"
2727
corev1 "k8s.io/api/core/v1"
2828
apierrors "k8s.io/apimachinery/pkg/api/errors"
29+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2930
utilerrors "k8s.io/apimachinery/pkg/util/errors"
3031
"k8s.io/client-go/tools/record"
3132
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -49,7 +50,11 @@ import (
4950
"github.com/linode/cluster-api-provider-linode/util/reconciler"
5051
)
5152

52-
const lbTypeDNS string = "dns"
53+
const (
54+
lbTypeDNS string = "dns"
55+
56+
ConditionPreflightLinodeVPCReady clusterv1.ConditionType = "PreflightLinodeVPCReady"
57+
)
5358

5459
// LinodeClusterReconciler reconciles a LinodeCluster object
5560
type LinodeClusterReconciler struct {
@@ -112,6 +117,7 @@ func (r *LinodeClusterReconciler) Reconcile(ctx context.Context, req ctrl.Reques
112117
return r.reconcile(ctx, clusterScope, logger)
113118
}
114119

120+
//nolint:cyclop // can't make it simpler with existing API
115121
func (r *LinodeClusterReconciler) reconcile(
116122
ctx context.Context,
117123
clusterScope *scope.ClusterScope,
@@ -163,6 +169,17 @@ func (r *LinodeClusterReconciler) reconcile(
163169
}
164170

165171
// Create
172+
if clusterScope.LinodeCluster.Spec.VPCRef != nil {
173+
if !reconciler.ConditionTrue(clusterScope.LinodeCluster, ConditionPreflightLinodeVPCReady) {
174+
res, err := r.reconcilePreflightLinodeVPCCheck(ctx, logger, clusterScope)
175+
if err != nil || !res.IsZero() {
176+
conditions.MarkFalse(clusterScope.LinodeCluster, ConditionPreflightLinodeVPCReady, string("linode vpc not yet available"), clusterv1.ConditionSeverityError, "")
177+
return res, err
178+
}
179+
}
180+
conditions.MarkTrue(clusterScope.LinodeCluster, ConditionPreflightLinodeVPCReady)
181+
}
182+
166183
if clusterScope.LinodeCluster.Spec.ControlPlaneEndpoint.Host == "" {
167184
if err := r.reconcileCreate(ctx, logger, clusterScope); err != nil {
168185
if !reconciler.HasConditionSeverity(clusterScope.LinodeCluster, clusterv1.ReadyCondition, clusterv1.ConditionSeverityError) {
@@ -192,6 +209,34 @@ func (r *LinodeClusterReconciler) reconcile(
192209
return res, nil
193210
}
194211

212+
func (r *LinodeClusterReconciler) reconcilePreflightLinodeVPCCheck(ctx context.Context, logger logr.Logger, clusterScope *scope.ClusterScope) (ctrl.Result, error) {
213+
name := clusterScope.LinodeCluster.Spec.VPCRef.Name
214+
namespace := clusterScope.LinodeCluster.Spec.VPCRef.Namespace
215+
if namespace == "" {
216+
namespace = clusterScope.LinodeCluster.Namespace
217+
}
218+
linodeVPC := infrav1alpha2.LinodeVPC{
219+
ObjectMeta: metav1.ObjectMeta{
220+
Namespace: namespace,
221+
Name: name,
222+
},
223+
}
224+
if err := clusterScope.Client.Get(ctx, client.ObjectKeyFromObject(&linodeVPC), &linodeVPC); err != nil {
225+
logger.Error(err, "Failed to fetch LinodeVPC")
226+
if reconciler.RecordDecayingCondition(clusterScope.LinodeCluster,
227+
ConditionPreflightLinodeVPCReady, string(cerrs.CreateClusterError), err.Error(),
228+
reconciler.DefaultTimeout(r.ReconcileTimeout, reconciler.DefaultClusterControllerReconcileTimeout)) {
229+
return ctrl.Result{}, err
230+
}
231+
return ctrl.Result{RequeueAfter: reconciler.DefaultClusterControllerReconcileDelay}, nil
232+
} else if !linodeVPC.Status.Ready {
233+
logger.Info("LinodeVPC is not yet available")
234+
return ctrl.Result{RequeueAfter: reconciler.DefaultClusterControllerReconcileDelay}, nil
235+
}
236+
r.Recorder.Event(clusterScope.LinodeCluster, corev1.EventTypeNormal, string(clusterv1.ReadyCondition), "LinodeVPC is now available")
237+
return ctrl.Result{}, nil
238+
}
239+
195240
func setFailureReason(clusterScope *scope.ClusterScope, failureReason cerrs.ClusterStatusError, err error, lcr *LinodeClusterReconciler) {
196241
clusterScope.LinodeCluster.Status.FailureReason = util.Pointer(failureReason)
197242
clusterScope.LinodeCluster.Status.FailureMessage = util.Pointer(err.Error())

controller/linodecluster_controller_test.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/go-logr/logr"
2424
"github.com/linode/linodego"
2525
"go.uber.org/mock/gomock"
26+
corev1 "k8s.io/api/core/v1"
2627
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728
"k8s.io/utils/ptr"
2829
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -63,6 +64,19 @@ var _ = Describe("cluster-lifecycle", Ordered, Label("cluster", "cluster-lifecyc
6364
ObjectMeta: metadata,
6465
Spec: infrav1alpha2.LinodeClusterSpec{
6566
Region: "us-ord",
67+
VPCRef: &corev1.ObjectReference{Name: "vpctest"},
68+
},
69+
}
70+
linodeVPC := infrav1alpha2.LinodeVPC{
71+
ObjectMeta: metav1.ObjectMeta{
72+
Name: "vpctest",
73+
Namespace: defaultNamespace,
74+
},
75+
Spec: infrav1alpha2.LinodeVPCSpec{
76+
Region: "us-ord",
77+
Subnets: []infrav1alpha2.VPCSubnetCreateOptions{
78+
{Label: "subnet1", IPv4: "10.0.0.0/8"},
79+
},
6680
},
6781
}
6882

@@ -91,9 +105,38 @@ var _ = Describe("cluster-lifecycle", Ordered, Label("cluster", "cluster-lifecyc
91105

92106
ctlrSuite.Run(
93107
OneOf(
108+
Path(
109+
Call("vpc doesn't exist", func(ctx context.Context, mck Mock) {
110+
}),
111+
OneOf(
112+
Path(Result("", func(ctx context.Context, mck Mock) {
113+
reconciler.Client = k8sClient
114+
_, err := reconciler.reconcile(ctx, cScope, mck.Logger())
115+
Expect(err).NotTo(HaveOccurred())
116+
Expect(rec.ConditionTrue(&linodeCluster, ConditionPreflightLinodeVPCReady)).To(BeFalse())
117+
})),
118+
),
119+
),
120+
Path(
121+
Call("vpc present but not ready", func(ctx context.Context, mck Mock) {
122+
Expect(k8sClient.Create(ctx, &linodeVPC)).To(Succeed())
123+
linodeVPC.Status.Ready = false
124+
k8sClient.Status().Update(ctx, &linodeVPC)
125+
}),
126+
OneOf(
127+
Path(Result("", func(ctx context.Context, mck Mock) {
128+
reconciler.Client = k8sClient
129+
_, err := reconciler.reconcile(ctx, cScope, mck.Logger())
130+
Expect(err).NotTo(HaveOccurred())
131+
Expect(rec.ConditionTrue(&linodeCluster, ConditionPreflightLinodeVPCReady)).To(BeFalse())
132+
})),
133+
),
134+
),
94135
Path(
95136
Call("cluster is not created because there was an error creating nb", func(ctx context.Context, mck Mock) {
96137
cScope.LinodeClient = mck.LinodeClient
138+
linodeVPC.Status.Ready = true
139+
k8sClient.Status().Update(ctx, &linodeVPC)
97140
mck.LinodeClient.EXPECT().CreateNodeBalancer(gomock.Any(), gomock.Any()).
98141
Return(nil, errors.New("failed to ensure nodebalancer"))
99142
}),
@@ -207,8 +250,9 @@ var _ = Describe("cluster-lifecycle", Ordered, Label("cluster", "cluster-lifecyc
207250
clusterKey := client.ObjectKeyFromObject(&linodeCluster)
208251
Expect(k8sClient.Get(ctx, clusterKey, &linodeCluster)).To(Succeed())
209252
Expect(linodeCluster.Status.Ready).To(BeTrue())
210-
Expect(linodeCluster.Status.Conditions).To(HaveLen(1))
253+
Expect(linodeCluster.Status.Conditions).To(HaveLen(2))
211254
Expect(linodeCluster.Status.Conditions[0].Type).To(Equal(clusterv1.ReadyCondition))
255+
Expect(linodeCluster.Status.Conditions[1].Type).To(Equal(ConditionPreflightLinodeVPCReady))
212256

213257
By("checking NB id")
214258
Expect(linodeCluster.Spec.Network.NodeBalancerID).To(Equal(&nodebalancerID))

controller/linodemachine_controller.go

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/linode/linodego"
3030
corev1 "k8s.io/api/core/v1"
3131
apierrors "k8s.io/apimachinery/pkg/api/errors"
32+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3233
utilerrors "k8s.io/apimachinery/pkg/util/errors"
3334
"k8s.io/client-go/tools/record"
3435
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -59,6 +60,8 @@ const (
5960
defaultDiskFilesystem = string(linodego.FilesystemExt4)
6061

6162
// conditions for preflight instance creation
63+
ConditionPreflightBootstrapDataSecretReady clusterv1.ConditionType = "PreflightBootstrapDataSecretReady"
64+
ConditionPreflightLinodeFirewallReady clusterv1.ConditionType = "PreflightLinodeFirewallReady"
6265
ConditionPreflightMetadataSupportConfigured clusterv1.ConditionType = "PreflightMetadataSupportConfigured"
6366
ConditionPreflightCreated clusterv1.ConditionType = "PreflightCreated"
6467
ConditionPreflightRootDiskResizing clusterv1.ConditionType = "PreflightRootDiskResizing"
@@ -212,10 +215,12 @@ func (r *LinodeMachineReconciler) reconcile(ctx context.Context, logger logr.Log
212215
}
213216

214217
// Make sure bootstrap data is available and populated.
215-
if machineScope.Machine.Spec.Bootstrap.DataSecretName == nil {
218+
if !reconciler.ConditionTrue(machineScope.LinodeMachine, ConditionPreflightBootstrapDataSecretReady) && machineScope.Machine.Spec.Bootstrap.DataSecretName == nil {
216219
logger.Info("Bootstrap data secret is not yet available")
217-
conditions.MarkFalse(machineScope.LinodeMachine, ConditionPreflightMetadataSupportConfigured, WaitingForBootstrapDataReason, clusterv1.ConditionSeverityInfo, "")
220+
conditions.MarkFalse(machineScope.LinodeMachine, ConditionPreflightBootstrapDataSecretReady, WaitingForBootstrapDataReason, clusterv1.ConditionSeverityInfo, "")
218221
return ctrl.Result{}, nil
222+
} else {
223+
conditions.MarkTrue(machineScope.LinodeMachine, ConditionPreflightBootstrapDataSecretReady)
219224
}
220225

221226
// Update
@@ -229,7 +234,7 @@ func (r *LinodeMachineReconciler) reconcile(ctx context.Context, logger logr.Log
229234
return r.reconcileCreate(ctx, logger, machineScope)
230235
}
231236

232-
//nolint:cyclop // can't make it simpler with existing API
237+
//nolint:cyclop,gocognit // can't make it simpler with existing API
233238
func (r *LinodeMachineReconciler) reconcileCreate(
234239
ctx context.Context,
235240
logger logr.Logger,
@@ -242,6 +247,16 @@ func (r *LinodeMachineReconciler) reconcileCreate(
242247
return ctrl.Result{}, err
243248
}
244249

250+
if machineScope.LinodeMachine.Spec.FirewallRef != nil {
251+
if !reconciler.ConditionTrue(machineScope.LinodeMachine, ConditionPreflightLinodeFirewallReady) && machineScope.LinodeMachine.Spec.ProviderID == nil {
252+
res, err := r.reconcilePreflightLinodeFirewallCheck(ctx, logger, machineScope)
253+
if err != nil || !res.IsZero() {
254+
conditions.MarkFalse(machineScope.LinodeMachine, ConditionPreflightLinodeFirewallReady, string("linode firewall not yet available"), clusterv1.ConditionSeverityError, "")
255+
return res, err
256+
}
257+
}
258+
}
259+
245260
if !reconciler.ConditionTrue(machineScope.LinodeMachine, ConditionPreflightMetadataSupportConfigured) && machineScope.LinodeMachine.Spec.ProviderID == nil {
246261
res, err := r.reconcilePreflightMetadataSupportConfigure(ctx, logger, machineScope)
247262
if err != nil || !res.IsZero() {
@@ -287,6 +302,34 @@ func (r *LinodeMachineReconciler) reconcileCreate(
287302
return ctrl.Result{}, nil
288303
}
289304

305+
func (r *LinodeMachineReconciler) reconcilePreflightLinodeFirewallCheck(ctx context.Context, logger logr.Logger, machineScope *scope.MachineScope) (ctrl.Result, error) {
306+
name := machineScope.LinodeMachine.Spec.FirewallRef.Name
307+
namespace := machineScope.LinodeMachine.Spec.FirewallRef.Namespace
308+
if namespace == "" {
309+
namespace = machineScope.LinodeMachine.Namespace
310+
}
311+
linodeFirewall := infrav1alpha2.LinodeFirewall{
312+
ObjectMeta: metav1.ObjectMeta{
313+
Namespace: namespace,
314+
Name: name,
315+
},
316+
}
317+
if err := machineScope.Client.Get(ctx, client.ObjectKeyFromObject(&linodeFirewall), &linodeFirewall); err != nil {
318+
logger.Error(err, "Failed to find linode Firewall")
319+
if reconciler.RecordDecayingCondition(machineScope.LinodeMachine,
320+
ConditionPreflightLinodeFirewallReady, string(cerrs.CreateMachineError), err.Error(),
321+
reconciler.DefaultTimeout(r.ReconcileTimeout, reconciler.DefaultMachineControllerWaitForPreflightTimeout)) {
322+
return ctrl.Result{}, err
323+
}
324+
return ctrl.Result{RequeueAfter: reconciler.DefaultMachineControllerRetryDelay}, nil
325+
} else if !linodeFirewall.Status.Ready {
326+
logger.Info("Linode firewall not yet ready")
327+
return ctrl.Result{RequeueAfter: reconciler.DefaultMachineControllerRetryDelay}, nil
328+
}
329+
conditions.MarkTrue(machineScope.LinodeMachine, ConditionPreflightLinodeFirewallReady)
330+
return ctrl.Result{}, nil
331+
}
332+
290333
func (r *LinodeMachineReconciler) reconcilePreflightMetadataSupportConfigure(ctx context.Context, logger logr.Logger, machineScope *scope.MachineScope) (ctrl.Result, error) {
291334
region, err := machineScope.LinodeClient.GetRegion(ctx, machineScope.LinodeMachine.Spec.Region)
292335
if err != nil {

0 commit comments

Comments
 (0)