Skip to content

Commit f1f423c

Browse files
committed
Add gate to registration logic, seperate it to initializer and put grace period. Code cleanup.
1 parent 81e5fea commit f1f423c

File tree

6 files changed

+461
-143
lines changed

6 files changed

+461
-143
lines changed

controllers/lxd_initializer_ds.go

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import (
66
"strings"
77

88
appsv1 "k8s.io/api/apps/v1"
9+
corev1 "k8s.io/api/core/v1"
910
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
11+
"k8s.io/apimachinery/pkg/labels"
12+
"k8s.io/apimachinery/pkg/runtime/schema"
1013
"sigs.k8s.io/controller-runtime/pkg/client"
1114
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1215
"sigs.k8s.io/yaml"
@@ -44,7 +47,106 @@ func (r *MaasClusterReconciler) ensureLXDInitializerDS(ctx context.Context, clus
4447
return fmt.Errorf("failed to get target cluster client: %v", err)
4548
}
4649

47-
// First clean up any existing DaemonSets in this namespace
50+
// If feature is off or cluster is being deleted, we're done
51+
if !clusterScope.IsLXDHostEnabled() || !cluster.ObjectMeta.DeletionTimestamp.IsZero() {
52+
return nil
53+
}
54+
55+
// Gate: ensure pivot completed. Require mgmt namespace to have clusterEnv=target
56+
mgmtNS := &corev1.Namespace{}
57+
if err := r.Client.Get(ctx, client.ObjectKey{Name: dsNamespace}, mgmtNS); err != nil {
58+
// Namespace not readable yet on management cluster; skip for now
59+
return nil
60+
}
61+
if v := strings.TrimSpace(mgmtNS.Annotations["clusterEnv"]); v != "target" {
62+
r.Log.Info("Namespace not marked as target; deferring LXD initializer", "namespace", dsNamespace, "clusterEnv", v)
63+
return nil
64+
}
65+
66+
// Gate: derive desired CP count from MaasCloudConfig; fallback to KCP
67+
desiredCP := int32(1)
68+
readyByKCP := int32(0)
69+
// Prefer MaasCloudConfig.spec.machinePoolConfig[].size where isControlPlane=true (sum)
70+
{
71+
mccList := &unstructured.UnstructuredList{}
72+
mccList.SetGroupVersionKind(schema.GroupVersionKind{Group: "cluster.spectrocloud.com", Version: "v1alpha1", Kind: "MaasCloudConfigList"})
73+
if err := r.Client.List(ctx, mccList, client.InNamespace(dsNamespace)); err == nil {
74+
var sum int64
75+
for _, it := range mccList.Items {
76+
owned := false
77+
for _, or := range it.GetOwnerReferences() {
78+
if or.Name == cluster.Name {
79+
owned = true
80+
break
81+
}
82+
}
83+
if !owned && !strings.HasSuffix(it.GetName(), "-maas-config") {
84+
continue
85+
}
86+
pools, found, _ := unstructured.NestedSlice(it.Object, "spec", "machinePoolConfig")
87+
if !found {
88+
continue
89+
}
90+
for _, p := range pools {
91+
if mp, ok := p.(map[string]interface{}); ok {
92+
isCP, _, _ := unstructured.NestedBool(mp, "isControlPlane")
93+
if !isCP {
94+
continue
95+
}
96+
if v, foundSz, _ := unstructured.NestedInt64(mp, "size"); foundSz && v > 0 {
97+
sum += v
98+
}
99+
}
100+
}
101+
if sum > 0 {
102+
desiredCP = int32(sum)
103+
break
104+
}
105+
}
106+
}
107+
}
108+
// Fallback: use KCP if MCC not found
109+
if desiredCP == 1 {
110+
kcpList := &unstructured.UnstructuredList{}
111+
kcpList.SetGroupVersionKind(schema.GroupVersionKind{Group: "controlplane.cluster.x-k8s.io", Version: "v1beta1", Kind: "KubeadmControlPlaneList"})
112+
if err := r.Client.List(ctx, kcpList, client.InNamespace(dsNamespace), client.MatchingLabels{
113+
"cluster.x-k8s.io/cluster-name": cluster.Name,
114+
}); err == nil {
115+
if len(kcpList.Items) > 0 {
116+
item := kcpList.Items[0]
117+
if v, found, _ := unstructured.NestedInt64(item.Object, "spec", "replicas"); found && v > 0 {
118+
desiredCP = int32(v)
119+
}
120+
if v, found, _ := unstructured.NestedInt64(item.Object, "status", "readyReplicas"); found && v >= 0 {
121+
readyByKCP = int32(v)
122+
}
123+
}
124+
}
125+
}
126+
127+
nodeList := &corev1.NodeList{}
128+
cpSelector := labels.SelectorFromSet(labels.Set{
129+
"node-role.kubernetes.io/control-plane": "",
130+
})
131+
if err := remoteClient.List(ctx, nodeList, &client.ListOptions{LabelSelector: cpSelector}); err == nil {
132+
ready := 0
133+
for _, n := range nodeList.Items {
134+
for _, c := range n.Status.Conditions {
135+
if c.Type == corev1.NodeReady && c.Status == corev1.ConditionTrue {
136+
ready++
137+
break
138+
}
139+
}
140+
}
141+
// Proceed when CP nodes are present and Ready, regardless of KCP readyReplicas
142+
if int32(len(nodeList.Items)) < desiredCP || int32(ready) < desiredCP {
143+
r.Log.Info("Not enough control-plane nodes present/ready yet; skipping DS for now", "desiredCP", desiredCP, "readyByKCP", readyByKCP, "nodeList", len(nodeList.Items), "ready", ready)
144+
// Not enough control-plane nodes present/ready yet; skip DS for now
145+
return nil
146+
}
147+
}
148+
149+
// Clean up any existing DaemonSets in this namespace (old naming/labels)
48150
dsList := &appsv1.DaemonSetList{}
49151
if err := remoteClient.List(ctx, dsList, client.InNamespace(dsNamespace), client.MatchingLabels{
50152
"app": "lxd-initializer",
@@ -59,11 +161,6 @@ func (r *MaasClusterReconciler) ensureLXDInitializerDS(ctx context.Context, clus
59161
}
60162
}
61163

62-
// If feature is off or cluster is being deleted, we're done after cleanup
63-
if !clusterScope.IsLXDHostEnabled() || !cluster.ObjectMeta.DeletionTimestamp.IsZero() {
64-
return nil
65-
}
66-
67164
// Ensure RBAC resources are created on target cluster
68165
if err := r.ensureLXDInitializerRBACOnTarget(ctx, remoteClient, dsNamespace); err != nil {
69166
return fmt.Errorf("failed to ensure LXD initializer RBAC: %v", err)

controllers/maasmachine_controller.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"strings"
2424
"time"
2525

26+
"github.com/spectrocloud/maas-client-go/maasclient"
2627
"k8s.io/apimachinery/pkg/runtime"
2728

2829
"github.com/go-logr/logr"
@@ -50,7 +51,6 @@ import (
5051
lxd "github.com/spectrocloud/cluster-api-provider-maas/pkg/maas/lxd"
5152
maasmachine "github.com/spectrocloud/cluster-api-provider-maas/pkg/maas/machine"
5253
"github.com/spectrocloud/cluster-api-provider-maas/pkg/maas/scope"
53-
"github.com/spectrocloud/maas-client-go/maasclient"
5454
)
5555

5656
var ErrRequeueDNS = errors.New("need to requeue DNS")
@@ -226,8 +226,7 @@ func (r *MaasMachineReconciler) reconcileDelete(_ context.Context, machineScope
226226
// If MAAS requires VM host removal first, attempt best-effort unregister and retry once
227227
if isVMHostRemovalRequiredError(err) {
228228
api := clusterScope.GetMaasClientIdentity()
229-
// choose ExternalIP first, then InternalIP
230-
nodeIP := getNodeIP(m.Addresses)
229+
231230
// For control-plane BM that backs an LXD VM host, force-delete guest VMs to unblock release
232231
if clusterScope.IsLXDHostEnabled() && machineScope.IsControlPlane() {
233232
ctx := context.Background()
@@ -243,7 +242,9 @@ func (r *MaasMachineReconciler) reconcileDelete(_ context.Context, machineScope
243242
}
244243
// Fetch details to confirm and delete
245244
if gm, ge := client.Machines().Machine(gid).Get(ctx); ge == nil {
246-
_ = client.Machines().Machine(gm.SystemID()).Delete(ctx)
245+
if derr := client.Machines().Machine(gm.SystemID()).Delete(ctx); derr != nil {
246+
machineScope.Error(derr, "failed to delete guest VM during host release cleanup", "guestSystemID", gm.SystemID())
247+
}
247248
}
248249
}
249250
}
@@ -252,6 +253,9 @@ func (r *MaasMachineReconciler) reconcileDelete(_ context.Context, machineScope
252253
}
253254
}
254255
}
256+
257+
// choose ExternalIP first, then InternalIP
258+
nodeIP := getNodeIP(m.Addresses)
255259
if nodeIP != "" {
256260
if uerr := lxd.UnregisterLXDHostWithMaasClient(api.Token, api.URL, nodeIP); uerr != nil {
257261
machineScope.Error(uerr, "failed to unregister LXD VM host prior to release")

controllers/templates/lxd_initializer_ds.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ spec:
7373
mountPropagation: HostToContainer
7474
containers:
7575
- name: lxd-initializer
76-
image: us-east1-docker.pkg.dev/spectro-images/dev/amit/cluster-api/lxd-initializer:v0.6.1-spectro-4.0.0-dev-11102025-03
76+
image: us-east1-docker.pkg.dev/spectro-images/dev/amit/cluster-api/lxd-initializer:v0.6.1-spectro-4.0.0-dev-15102025-01
77+
imagePullPolicy: Always
7778
securityContext:
7879
privileged: true
7980
env:

controllers/templates/lxd_initializer_rbac.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ metadata:
1717
rules:
1818
- apiGroups: [""]
1919
resources: ["nodes"]
20-
verbs: ["get", "patch", "update"]
20+
verbs: ["get", "list", "watch", "patch", "update"]
2121
- apiGroups: [""]
2222
resources: ["secrets"]
2323
verbs: ["list"]

lxd-initializer/lxd-initializer-daemonset.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ spec:
2020
hostPID: true
2121
containers:
2222
- name: lxd-initializer
23-
image: us-east1-docker.pkg.dev/spectro-images/dev/amit/cluster-api/lxd-initializer:v0.6.1-spectro-4.0.0-dev-11102025-03
23+
image: us-east1-docker.pkg.dev/spectro-images/dev/amit/cluster-api/lxd-initializer:v0.6.1-spectro-4.0.0-dev-15102025-01
2424
imagePullPolicy: Always
2525
securityContext:
2626
privileged: true

0 commit comments

Comments
 (0)