@@ -20,8 +20,11 @@ package controller
2020import (
2121 "context"
2222 "fmt"
23+ "slices"
24+ "strings"
2325
2426 corev1 "k8s.io/api/core/v1"
27+ "k8s.io/apimachinery/pkg/api/errors"
2528 "k8s.io/apimachinery/pkg/api/meta"
2629 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2730 "k8s.io/apimachinery/pkg/labels"
@@ -36,7 +39,9 @@ import (
3639)
3740
3841const (
39- labelLifecycleMode = "cobaltcore.cloud.sap/node-hypervisor-lifecycle"
42+ labelLifecycleMode = "cobaltcore.cloud.sap/node-hypervisor-lifecycle"
43+ annotationAggregates = "nova.openstack.cloud.sap/aggregates"
44+ annotationCustomTraits = "nova.openstack.cloud.sap/custom-traits"
4045)
4146
4247var transferLabels = []string {
@@ -80,24 +85,14 @@ func (hv *HypervisorController) Reconcile(ctx context.Context, req ctrl.Request)
8085 },
8186 }
8287
83- // Ensure corresponding hypervisor exists or update it
84- op , err := controllerutil .CreateOrPatch (ctx , hv .Client , hypervisor , func () error {
85- // Transfer Labels
86- for _ , label := range transferLabels {
87- if nodeLabels .Has (label ) {
88- hypervisor .Labels [label ] = nodeLabels .Get (label )
89- }
90- }
91-
92- if nodeLabels .Has (labelLifecycleMode ) {
93- hypervisor .Spec .LifecycleEnabled = true
94- hypervisor .Spec .SkipTests = nodeLabels .Get (labelLifecycleMode ) == "skip-tests"
88+ // Check if hypervisor already exists
89+ if err := hv .Get (ctx , k8sclient .ObjectKeyFromObject (hypervisor ), hypervisor ); err != nil {
90+ if ! errors .IsNotFound (err ) {
91+ return ctrl.Result {}, fmt .Errorf ("failed to get hypervisor: %w" , err )
9592 }
96-
97- if err := controllerutil .SetControllerReference (node , hypervisor , hv .Scheme ); err != nil {
98- return fmt .Errorf ("failed setting controller reference: %w" , err )
99- }
100-
93+ // continue with creation
94+ } else {
95+ // update Status if needed
10196 nodeTerminationCondition := FindNodeStatusCondition (node .Status .Conditions , "Terminating" )
10297 if nodeTerminationCondition != nil && nodeTerminationCondition .Status == corev1 .ConditionTrue {
10398 // Node might be terminating, propagate condition to hypervisor
@@ -113,16 +108,60 @@ func (hv *HypervisorController) Reconcile(ctx context.Context, req ctrl.Request)
113108 Reason : nodeTerminationCondition .Reason ,
114109 Message : nodeTerminationCondition .Message ,
115110 })
111+ return ctrl.Result {}, hv .Status ().Update (ctx , hypervisor )
116112 }
117113
118- return nil
119- })
120- if err != nil {
121- log .Error (err , "Hypervisor reconcile failed" )
122- } else if op != controllerutil .OperationResultNone {
123- log .Info ("Hypervisor successfully reconciled" , "operation" , op )
114+ return ctrl.Result {}, nil
115+ }
116+
117+ // transfer labels
118+ for _ , label := range transferLabels {
119+ if nodeLabels .Has (label ) {
120+ hypervisor .Labels [label ] = nodeLabels .Get (label )
121+ }
122+ }
123+
124+ // transport lifecycle label to hypervisor spec
125+ if nodeLabels .Has (labelLifecycleMode ) {
126+ hypervisor .Spec .LifecycleEnabled = true
127+ hypervisor .Spec .SkipTests = nodeLabels .Get (labelLifecycleMode ) == "skip-tests"
128+ }
129+
130+ // transport aggregates annotation to hypervisor spec
131+ if aggregates , found := node .Annotations [annotationAggregates ]; found {
132+ // split aggregates string
133+ hypervisor .Spec .Aggregates = slices .Collect (func (yield func (string ) bool ) {
134+ for _ , agg := range strings .Split (aggregates , "," ) {
135+ trimmed := strings .TrimSpace (agg )
136+ if trimmed != "" && yield (trimmed ) {
137+ return
138+ }
139+ }
140+ })
141+ }
142+
143+ // transport custom traits annotation to hypervisor spec
144+ if customTraits , found := node .Annotations [annotationCustomTraits ]; found {
145+ // split custom traits string
146+ hypervisor .Spec .CustomTraits = slices .Collect (func (yield func (string ) bool ) {
147+ for _ , trait := range strings .Split (customTraits , "," ) {
148+ trimmed := strings .TrimSpace (trait )
149+ if trimmed != "" && yield (trimmed ) {
150+ return
151+ }
152+ }
153+ })
154+ }
155+
156+ if err := controllerutil .SetControllerReference (node , hypervisor , hv .Scheme ); err != nil {
157+ return ctrl.Result {}, fmt .Errorf ("failed setting controller reference: %w" , err )
158+ }
159+
160+ if err := hv .Create (ctx , hypervisor ); err != nil {
161+ return ctrl.Result {}, err
124162 }
125163
164+ log .Info ("Created Hypervisor resource" , "hypervisor" , hypervisor .Name )
126165 return ctrl.Result {}, nil
127166}
128167
0 commit comments