Skip to content

Commit 838a2a9

Browse files
PCP-5482: Propagate HCP gating events (#280)
1 parent 0e653c5 commit 838a2a9

File tree

3 files changed

+76
-10
lines changed

3 files changed

+76
-10
lines changed

controllers/host_maintenance_controller.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2525
"k8s.io/apimachinery/pkg/runtime"
2626
"k8s.io/apimachinery/pkg/types"
27+
"k8s.io/client-go/tools/record"
2728
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
2829
ctrl "sigs.k8s.io/controller-runtime"
2930
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -41,8 +42,9 @@ import (
4142
// HMCMaintenanceReconciler handles host maintenance operations via ConfigMap triggers
4243
type HMCMaintenanceReconciler struct {
4344
client.Client
44-
Log logr.Logger
45-
Scheme *runtime.Scheme
45+
Log logr.Logger
46+
Scheme *runtime.Scheme
47+
Recorder record.EventRecorder
4648
// Namespace is the controller namespace (namespaced deployment)
4749
Namespace string
4850
// GenericEventChannel allows external triggers to enqueue reconcile requests
@@ -242,7 +244,7 @@ func (r *HMCMaintenanceReconciler) reconcileMaasMachine(ctx context.Context, maa
242244
log.Info("Maintenance tags ensured", "host", hostSystemID, "opId", st.OpID)
243245

244246
// Create host maintenance service
245-
hmcService, err := NewHostMaintenanceService(r.Client, maasMachine.Namespace)
247+
hmcService, err := NewHostMaintenanceService(r.Client, maasMachine.Namespace, r.Recorder)
246248
if err != nil {
247249
log.Error(err, "failed to create host maintenance service")
248250
return ctrl.Result{RequeueAfter: 10 * time.Second}, err
@@ -307,6 +309,10 @@ func (r *HMCMaintenanceReconciler) SetupWithManager(ctx context.Context, mgr ctr
307309
recover := true
308310
options.RecoverPanic = &recover
309311

312+
if r.Recorder == nil {
313+
r.Recorder = mgr.GetEventRecorderFor("hmc-controller")
314+
}
315+
310316
if r.GenericEventChannel == nil {
311317
r.GenericEventChannel = make(chan event.GenericEvent)
312318
}

controllers/host_maintenance_service.go

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323
"github.com/go-logr/logr"
2424
infrav1beta1 "github.com/spectrocloud/cluster-api-provider-maas/api/v1beta1"
2525
maint "github.com/spectrocloud/cluster-api-provider-maas/pkg/maas/maintenance"
26+
corev1 "k8s.io/api/core/v1"
27+
"k8s.io/client-go/tools/record"
2628
"sigs.k8s.io/controller-runtime/pkg/client"
2729
)
2830

@@ -40,10 +42,11 @@ type HostMaintenanceService struct {
4042
namespace string
4143
tagService maint.TagService
4244
inventoryService maint.InventoryService
45+
recorder record.EventRecorder
4346
}
4447

4548
// NewHostMaintenanceService creates a new host maintenance service
46-
func NewHostMaintenanceService(k8sClient client.Client, namespace string) (*HostMaintenanceService, error) {
49+
func NewHostMaintenanceService(k8sClient client.Client, namespace string, recorder record.EventRecorder) (*HostMaintenanceService, error) {
4750
maasClient, err := maint.NewMAASClient(k8sClient, namespace)
4851
if err != nil {
4952
return nil, fmt.Errorf("failed to create MAAS client: %w", err)
@@ -53,6 +56,7 @@ func NewHostMaintenanceService(k8sClient client.Client, namespace string) (*Host
5356
namespace: namespace,
5457
tagService: maint.NewTagService(maasClient),
5558
inventoryService: maint.NewInventoryService(maasClient),
59+
recorder: recorder,
5660
}, nil
5761
}
5862

@@ -79,13 +83,51 @@ func (s *HostMaintenanceService) CheckEvacuationGates(ctx context.Context, maasM
7983
hostSystemID := *maasMachine.Spec.SystemID
8084

8185
// Gate 1: Check if host is empty (no VMs running)
82-
hostEmpty, err := s.isHostEmpty(ctx, hostSystemID, log)
86+
// Get VMs list first so we can include names in event if host is not empty
87+
vms, err := s.inventoryService.ListHostVMs(hostSystemID)
8388
if err != nil {
84-
return false, fmt.Errorf("failed to check if host is empty: %w", err)
89+
return false, fmt.Errorf("failed to list host VMs: %w", err)
8590
}
8691

87-
if !hostEmpty {
88-
log.Info("Host not empty, evacuation blocked", "host", hostSystemID)
92+
hostEmpty := len(vms) == 0
93+
if hostEmpty {
94+
log.Info("Host is empty (no VMs)", "host", hostSystemID)
95+
} else {
96+
// Get host details to use hostname in event
97+
hostDetails, err := s.inventoryService.GetHost(hostSystemID)
98+
hostName := hostSystemID // fallback to systemID
99+
if err == nil {
100+
if hostDetails.FQDN != "" {
101+
hostName = hostDetails.FQDN
102+
} else if hostDetails.Hostname != "" {
103+
hostName = hostDetails.Hostname
104+
}
105+
}
106+
107+
// Build list of VM names/identifiers for the event
108+
vmNames := make([]string, 0, len(vms))
109+
for _, vm := range vms {
110+
// Prefer FQDN or Hostname, fallback to SystemID
111+
vmName := vm.FQDN
112+
if vmName == "" {
113+
vmName = vm.Hostname
114+
}
115+
if vmName == "" {
116+
vmName = vm.SystemID
117+
}
118+
vmNames = append(vmNames, vmName)
119+
}
120+
121+
log.Info("Host not empty, evacuation blocked", "host", hostSystemID, "vmCount", len(vms), "vms", vmNames)
122+
123+
// Emit Kubernetes event with VM names
124+
if s.recorder != nil {
125+
vmNamesStr := strings.Join(vmNames, ", ")
126+
s.recorder.Eventf(maasMachine, corev1.EventTypeWarning, "EvacuationBlocked",
127+
"Host evacuation blocked: %d VM(s) still present on host %s: %s",
128+
len(vms), hostName, vmNamesStr)
129+
}
130+
89131
return false, nil
90132
}
91133

@@ -96,7 +138,25 @@ func (s *HostMaintenanceService) CheckEvacuationGates(ctx context.Context, maasM
96138
}
97139

98140
if !wlcReady {
141+
// Get host details to use hostname in event
142+
hostDetails, err := s.inventoryService.GetHost(hostSystemID)
143+
hostName := hostSystemID // fallback to systemID
144+
if err == nil {
145+
if hostDetails.FQDN != "" {
146+
hostName = hostDetails.FQDN
147+
} else if hostDetails.Hostname != "" {
148+
hostName = hostDetails.Hostname
149+
}
150+
}
151+
99152
log.Info("WLC ready tags not met, evacuation blocked", "host", hostSystemID)
153+
154+
// Emit Kubernetes event
155+
if s.recorder != nil {
156+
s.recorder.Eventf(maasMachine, corev1.EventTypeWarning, "WLCReplacementPending",
157+
"WLC evacuation blocked: waiting for replacement VMs on host %s", hostName)
158+
}
159+
100160
return false, nil
101161
}
102162

pkg/maas/scope/machine.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,10 +314,10 @@ func (m *MachineScope) GetStaticIP() string {
314314
// GetStaticIPConfig returns the full static IP configuration if configured
315315
func (m *MachineScope) GetStaticIPConfig() *infrav1beta1.StaticIPConfig {
316316
if m.MaasMachine.Spec.StaticIP == nil {
317-
m.Info("StaticIPConfig: StaticIP config is nil")
317+
m.V(1).Info("StaticIPConfig: StaticIP config is nil")
318318
return nil
319319
}
320320

321-
m.Info("StaticIPConfig: returning config", "ip", m.MaasMachine.Spec.StaticIP.IP)
321+
m.V(1).Info("StaticIPConfig: returning config", "ip", m.MaasMachine.Spec.StaticIP.IP)
322322
return m.MaasMachine.Spec.StaticIP
323323
}

0 commit comments

Comments
 (0)