@@ -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,40 @@ 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 )
117- if errors .Is (err , openstack .ErrNoHypervisor ) {
118- // We are (hopefully) done
119- return r .removeFinalizer (ctx , node )
84+ if err != nil {
85+ if errors .Is (err , openstack .ErrNoHypervisor ) {
86+ // We are (hopefully) done
87+ return ctrl.Result {}, r .markOffboarded (ctx , hv )
88+ }
89+ return r .setDecommissioningCondition (ctx , hv , fmt .Sprintf ("cannot get hypervisor by name %s due to %s" , hostname , err ))
12090 }
12191
12292 // TODO: remove since RunningVMs is only available until micro-version 2.87, and also is updated asynchronously
@@ -141,7 +111,7 @@ func (r *NodeDecommissionReconciler) Reconcile(ctx context.Context, req ctrl.Req
141111 return r .setDecommissioningCondition (ctx , hv , fmt .Sprintf ("cannot list aggregates due to %v" , err ))
142112 }
143113
144- host := node .Name
114+ host := hv .Name
145115 for name , aggregate := range aggs {
146116 if slices .Contains (aggregate .Hosts , host ) {
147117 opts := aggregates.RemoveHostOpts {Host : host }
@@ -168,19 +138,7 @@ func (r *NodeDecommissionReconciler) Reconcile(ctx context.Context, req ctrl.Req
168138 return r .setDecommissioningCondition (ctx , hv , fmt .Sprintf ("cannot clean up resource provider: %v" , err ))
169139 }
170140
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
141+ return ctrl.Result {}, r .markOffboarded (ctx , hv )
184142}
185143
186144func (r * NodeDecommissionReconciler ) setDecommissioningCondition (ctx context.Context , hv * kvmv1.Hypervisor , message string ) (ctrl.Result , error ) {
@@ -195,7 +153,22 @@ func (r *NodeDecommissionReconciler) setDecommissioningCondition(ctx context.Con
195153 k8sclient.MergeFromWithOptimisticLock {}), k8sclient .FieldOwner (DecommissionControllerName )); err != nil {
196154 return ctrl.Result {}, fmt .Errorf ("cannot update hypervisor status due to %w" , err )
197155 }
198- return ctrl.Result {RequeueAfter : shortRetryTime }, nil
156+ return ctrl.Result {}, nil
157+ }
158+
159+ func (r * NodeDecommissionReconciler ) markOffboarded (ctx context.Context , hv * kvmv1.Hypervisor ) error {
160+ base := hv .DeepCopy ()
161+ meta .SetStatusCondition (& hv .Status .Conditions , metav1.Condition {
162+ Type : kvmv1 .ConditionTypeOffboarded ,
163+ Status : metav1 .ConditionTrue ,
164+ Reason : "Offboarded" ,
165+ Message : "Offboarding successful" ,
166+ })
167+ if err := r .Status ().Patch (ctx , hv , k8sclient .MergeFromWithOptions (base ,
168+ k8sclient.MergeFromWithOptimisticLock {}), k8sclient .FieldOwner (DecommissionControllerName )); err != nil {
169+ return fmt .Errorf ("cannot update hypervisor status due to %w" , err )
170+ }
171+ return nil
199172}
200173
201174// SetupWithManager sets up the controller with the Manager.
@@ -217,6 +190,6 @@ func (r *NodeDecommissionReconciler) SetupWithManager(mgr ctrl.Manager) error {
217190
218191 return ctrl .NewControllerManagedBy (mgr ).
219192 Named (DecommissionControllerName ).
220- For (& corev1. Node {}).
193+ For (& kvmv1. Hypervisor {}).
221194 Complete (r )
222195}
0 commit comments