@@ -28,23 +28,19 @@ import (
2828 "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/aggregates"
2929 "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/services"
3030 "github.com/gophercloud/gophercloud/v2/openstack/placement/v1/resourceproviders"
31- corev1 "k8s.io/api/core/v1"
3231 "k8s.io/apimachinery/pkg/api/meta"
3332 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3433 "k8s.io/apimachinery/pkg/runtime"
35- "k8s.io/client-go/util/retry"
3634 ctrl "sigs.k8s.io/controller-runtime"
3735 k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
38- "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3936 logger "sigs.k8s.io/controller-runtime/pkg/log"
4037
4138 kvmv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
4239 "github.com/cobaltcore-dev/openstack-hypervisor-operator/internal/openstack"
4340)
4441
4542const (
46- decommissionFinalizerName = "cobaltcore.cloud.sap/decommission-hypervisor"
47- DecommissionControllerName = "nodeDecommission"
43+ DecommissionControllerName = "offboarding"
4844)
4945
5046type NodeDecommissionReconciler struct {
@@ -57,66 +53,37 @@ type NodeDecommissionReconciler struct {
5753// The counter-side in gardener is here:
5854// https://github.com/gardener/machine-controller-manager/blob/rel-v0.56/pkg/util/provider/machinecontroller/machine.go#L646
5955
60- // +kubebuilder:rbac:groups="",resources=nodes,verbs=get;list;watch;patch;update
61- // +kubebuilder:rbac:groups="",resources=nodes/finalizers,verbs=update
6256// +kubebuilder:rbac:groups=kvm.cloud.sap,resources=hypervisors,verbs=get;list;watch
6357// +kubebuilder:rbac:groups=kvm.cloud.sap,resources=hypervisors/status,verbs=get;list;watch;update;patch
6458func (r * NodeDecommissionReconciler ) Reconcile (ctx context.Context , req ctrl.Request ) (ctrl.Result , error ) {
6559 hostname := req .Name
6660 log := logger .FromContext (ctx ).WithName (req .Name ).WithValues ("hostname" , hostname )
6761 ctx = logger .IntoContext (ctx , log )
6862
69- node := & corev1.Node {}
70- if err := r .Get (ctx , req .NamespacedName , node ); err != nil {
71- return ctrl.Result {}, k8sclient .IgnoreNotFound (err )
72- }
73-
74- // Fetch HV to check if lifecycle management is enabled
7563 hv := & kvmv1.Hypervisor {}
7664 if err := r .Get (ctx , k8sclient.ObjectKey {Name : hostname }, hv ); err != nil {
7765 // ignore not found errors, could be deleted
7866 return ctrl.Result {}, k8sclient .IgnoreNotFound (err )
7967 }
80- if ! hv .Spec .LifecycleEnabled {
81- // Get out of the way
82- return r .removeFinalizer (ctx , node )
83- }
84-
85- if ! controllerutil .ContainsFinalizer (node , decommissionFinalizerName ) {
86- return ctrl.Result {}, retry .RetryOnConflict (retry .DefaultRetry , func () error {
87- patch := k8sclient .MergeFrom (node .DeepCopy ())
88- controllerutil .AddFinalizer (node , decommissionFinalizerName )
89- if err := r .Patch (ctx , node , patch ); err != nil {
90- return fmt .Errorf ("failed to add finalizer due to %w" , err )
91- }
92- log .Info ("Added finalizer" )
93- return nil
94- })
95- }
9668
97- // Not yet deleting hv, nothing more to do
98- if node .DeletionTimestamp .IsZero () {
69+ if ! hv .Spec .LifecycleEnabled || hv .Spec .Maintenance != kvmv1 .MaintenanceTermination {
9970 return ctrl.Result {}, nil
10071 }
10172
102- // Someone is just deleting the hv, without going through termination
103- // See: https://github.com/gardener/machine-controller-manager/blob/rel-v0.56/pkg/util/provider/machinecontroller/machine.go#L658-L659
104- if ! IsNodeConditionTrue (node .Status .Conditions , "Terminating" ) {
105- log .Info ("removing finalizer since not terminating" )
106- // So we just get out of the way for now
107- return r .removeFinalizer (ctx , node )
108- }
109-
11073 if meta .IsStatusConditionTrue (hv .Status .Conditions , kvmv1 .ConditionTypeReady ) {
11174 return r .setDecommissioningCondition (ctx , hv , "Node is being decommissioned, removing host from nova" )
11275 }
11376
77+ if meta .IsStatusConditionTrue (hv .Status .Conditions , kvmv1 .ConditionTypeOffboarded ) {
78+ return ctrl.Result {}, nil
79+ }
80+
11481 log .Info ("removing host from nova" )
11582
11683 hypervisor , err := openstack .GetHypervisorByName (ctx , r .computeClient , hostname , true )
11784 if errors .Is (err , openstack .ErrNoHypervisor ) {
11885 // We are (hopefully) done
119- return r . removeFinalizer (ctx , node )
86+ return ctrl. Result {}, r . markOffboarded (ctx , hv )
12087 }
12188
12289 // TODO: remove since RunningVMs is only available until micro-version 2.87, and also is updated asynchronously
@@ -141,7 +108,7 @@ func (r *NodeDecommissionReconciler) Reconcile(ctx context.Context, req ctrl.Req
141108 return r .setDecommissioningCondition (ctx , hv , fmt .Sprintf ("cannot list aggregates due to %v" , err ))
142109 }
143110
144- host := node .Name
111+ host := hv .Name
145112 for name , aggregate := range aggs {
146113 if slices .Contains (aggregate .Hosts , host ) {
147114 opts := aggregates.RemoveHostOpts {Host : host }
@@ -168,19 +135,7 @@ func (r *NodeDecommissionReconciler) Reconcile(ctx context.Context, req ctrl.Req
168135 return r .setDecommissioningCondition (ctx , hv , fmt .Sprintf ("cannot clean up resource provider: %v" , err ))
169136 }
170137
171- return r .removeFinalizer (ctx , node )
172- }
173-
174- func (r * NodeDecommissionReconciler ) removeFinalizer (ctx context.Context , node * corev1.Node ) (ctrl.Result , error ) {
175- if ! controllerutil .ContainsFinalizer (node , decommissionFinalizerName ) {
176- return ctrl.Result {}, nil
177- }
178-
179- nodeBase := node .DeepCopy ()
180- controllerutil .RemoveFinalizer (node , decommissionFinalizerName )
181- err := r .Patch (ctx , node , k8sclient .MergeFromWithOptions (nodeBase ,
182- k8sclient.MergeFromWithOptimisticLock {}), k8sclient .FieldOwner (DecommissionControllerName ))
183- return ctrl.Result {}, err
138+ return ctrl.Result {}, r .markOffboarded (ctx , hv )
184139}
185140
186141func (r * NodeDecommissionReconciler ) setDecommissioningCondition (ctx context.Context , hv * kvmv1.Hypervisor , message string ) (ctrl.Result , error ) {
@@ -195,7 +150,22 @@ func (r *NodeDecommissionReconciler) setDecommissioningCondition(ctx context.Con
195150 k8sclient.MergeFromWithOptimisticLock {}), k8sclient .FieldOwner (DecommissionControllerName )); err != nil {
196151 return ctrl.Result {}, fmt .Errorf ("cannot update hypervisor status due to %w" , err )
197152 }
198- return ctrl.Result {RequeueAfter : shortRetryTime }, nil
153+ return ctrl.Result {}, nil
154+ }
155+
156+ func (r * NodeDecommissionReconciler ) markOffboarded (ctx context.Context , hv * kvmv1.Hypervisor ) error {
157+ base := hv .DeepCopy ()
158+ meta .SetStatusCondition (& hv .Status .Conditions , metav1.Condition {
159+ Type : kvmv1 .ConditionTypeOffboarded ,
160+ Status : metav1 .ConditionTrue ,
161+ Reason : "Offboarded" ,
162+ Message : "Offboarding successful" ,
163+ })
164+ if err := r .Status ().Patch (ctx , hv , k8sclient .MergeFromWithOptions (base ,
165+ k8sclient.MergeFromWithOptimisticLock {}), k8sclient .FieldOwner (DecommissionControllerName )); err != nil {
166+ return fmt .Errorf ("cannot update hypervisor status due to %w" , err )
167+ }
168+ return nil
199169}
200170
201171// SetupWithManager sets up the controller with the Manager.
@@ -217,6 +187,6 @@ func (r *NodeDecommissionReconciler) SetupWithManager(mgr ctrl.Manager) error {
217187
218188 return ctrl .NewControllerManagedBy (mgr ).
219189 Named (DecommissionControllerName ).
220- For (& corev1. Node {}).
190+ For (& kvmv1. Hypervisor {}).
221191 Complete (r )
222192}
0 commit comments