Skip to content

Commit 7d886e3

Browse files
Reconcile on libvirt domain events
1 parent c16be82 commit 7d886e3

File tree

7 files changed

+334
-352
lines changed

7 files changed

+334
-352
lines changed

internal/controller/hypervisor_controller.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,16 @@ import (
2525
"time"
2626

2727
kvmv1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
28+
golibvirt "github.com/digitalocean/go-libvirt"
2829
"k8s.io/apimachinery/pkg/api/meta"
2930
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3031
"k8s.io/apimachinery/pkg/runtime"
3132
ctrl "sigs.k8s.io/controller-runtime"
3233
"sigs.k8s.io/controller-runtime/pkg/client"
34+
"sigs.k8s.io/controller-runtime/pkg/event"
35+
"sigs.k8s.io/controller-runtime/pkg/handler"
3336
logger "sigs.k8s.io/controller-runtime/pkg/log"
37+
"sigs.k8s.io/controller-runtime/pkg/source"
3438

3539
"github.com/cobaltcore-dev/kvm-node-agent/internal/certificates"
3640
"github.com/cobaltcore-dev/kvm-node-agent/internal/evacuation"
@@ -48,6 +52,9 @@ type HypervisorReconciler struct {
4852

4953
osDescriptor *systemd.Descriptor
5054
evacuateOnReboot bool
55+
56+
// Channel that can be used to trigger reconcile events.
57+
reconcileCh chan event.GenericEvent
5158
}
5259

5360
const (
@@ -287,6 +294,47 @@ func (r *HypervisorReconciler) Reconcile(ctx context.Context, req ctrl.Request)
287294
return ctrl.Result{RequeueAfter: 1 * time.Minute}, nil
288295
}
289296

297+
// Trigger a reconcile event for the managed hypervisor through the
298+
// event channel which is watched by the controller manager.
299+
func (r *HypervisorReconciler) triggerReconcile() {
300+
r.reconcileCh <- event.GenericEvent{
301+
Object: &kvmv1.Hypervisor{
302+
TypeMeta: metav1.TypeMeta{
303+
Kind: "Hypervisor",
304+
APIVersion: "kvm.cloud.sap/v1",
305+
},
306+
ObjectMeta: metav1.ObjectMeta{
307+
Name: sys.Hostname,
308+
Namespace: sys.Namespace,
309+
},
310+
},
311+
}
312+
}
313+
314+
// Start is called when the manager starts. It starts the libvirt
315+
// event subscription to receive events when the hypervisor needs to be
316+
// reconciled.
317+
func (r *HypervisorReconciler) Start(ctx context.Context) error {
318+
log := logger.FromContext(ctx, "controller", "hypervisor")
319+
log.Info("starting libvirt event subscription")
320+
321+
// Ensure we're connected to libvirt.
322+
if err := r.Libvirt.Connect(); err != nil {
323+
log.Error(err, "unable to connect to libvirt")
324+
return err
325+
}
326+
327+
// Domain lifecycle events impact the list of active/inactive domains,
328+
// as well as the allocation of resources on the hypervisor.
329+
r.Libvirt.WatchDomainChanges(
330+
golibvirt.DomainEventIDLifecycle,
331+
"reconcile-on-domain-lifecycle",
332+
func(_ context.Context, _ any) { r.triggerReconcile() },
333+
)
334+
335+
return nil
336+
}
337+
290338
// SetupWithManager sets up the controller with the Manager.
291339
func (r *HypervisorReconciler) SetupWithManager(mgr ctrl.Manager) error {
292340
ctx := context.Background()
@@ -296,7 +344,16 @@ func (r *HypervisorReconciler) SetupWithManager(mgr ctrl.Manager) error {
296344
return fmt.Errorf("unable to get Systemd hostname describe(): %w", err)
297345
}
298346

347+
// Prepare an event channel that will trigger a reconcile event.
348+
r.reconcileCh = make(chan event.GenericEvent)
349+
src := source.Channel(r.reconcileCh, &handler.EnqueueRequestForObject{})
350+
// Run the Start(ctx context.Context) method when the manager starts.
351+
if err := mgr.Add(r); err != nil {
352+
return err
353+
}
354+
299355
return ctrl.NewControllerManagedBy(mgr).
300356
For(&kvmv1.Hypervisor{}).
357+
WatchesRawSource(src).
301358
Complete(r)
302359
}

internal/libvirt/interface.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,36 @@ limitations under the License.
2020
package libvirt
2121

2222
import (
23+
"context"
24+
2325
v1 "github.com/cobaltcore-dev/openstack-hypervisor-operator/api/v1"
26+
"github.com/digitalocean/go-libvirt"
2427
)
2528

2629
type Interface interface {
2730
// Connect connects to the libvirt daemon.
31+
//
32+
// This function also run a loop which listens for new events on the
33+
// subscribed libvirt event channels and distributes them to the subscribed
34+
// listeners (see the `Watch` method).
2835
Connect() error
2936

3037
// Close closes the connection to the libvirt daemon.
3138
Close() error
3239

40+
// Watch libvirt domain changes and notify the provided handler.
41+
//
42+
// The provided handlerId should be unique per handler, and is used to
43+
// disambiguate multiple handlers for the same eventId.
44+
//
45+
// Note that the handler is called in a blocking manner, so long-running handlers
46+
// should spawn goroutines if needed.
47+
WatchDomainChanges(
48+
eventId libvirt.DomainEventID,
49+
handlerId string,
50+
handler func(context.Context, any),
51+
)
52+
3353
// Add information extracted from the libvirt socket to the hypervisor instance.
3454
// If an error occurs, the instance is returned unmodified. The libvirt
3555
// connection needs to be established before calling this function.

internal/libvirt/interface_mock.go

Lines changed: 58 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)