diff --git a/controllers/fipsetup/fipsetup.go b/controllers/fipsetup/fipsetup.go index 69d4b47..0c30feb 100644 --- a/controllers/fipsetup/fipsetup.go +++ b/controllers/fipsetup/fipsetup.go @@ -11,6 +11,7 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" @@ -27,6 +28,7 @@ var log = logf.Log.WithName("fipsetup") type Reconciler struct { client.Client v1alpha1.Config + recorder record.EventRecorder } func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { @@ -180,6 +182,7 @@ func (r *Reconciler) deleteJob(ctx context.Context, job *batchv1.Job) error { // SetupWithManager sets up the controller with the Manager. func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error { + r.recorder = mgr.GetEventRecorderFor("fipsetup") preds := []predicate.Predicate{ predicate.Or( predicate.AnnotationChangedPredicate{}, diff --git a/e2e/e2e_test.go b/e2e/e2e_test.go index f2a2fcf..4e2d241 100644 --- a/e2e/e2e_test.go +++ b/e2e/e2e_test.go @@ -18,9 +18,9 @@ import ( ) const ( - defaultTimeout = 5 * time.Minute - pollInterval = 5 * time.Second - logInterval = 30 * time.Second + defaultTimeout = 5 * time.Minute + pollInterval = 5 * time.Second + logInterval = 30 * time.Second nodeReadyTimeout = 5 * time.Minute ) @@ -82,6 +82,10 @@ func TestFloatingIPAssignment(t *testing.T) { assignCtx, assignCancel := context.WithTimeout(ctx, cfg.timeout) nodeName := waitForAssignment(assignCtx, t, kubeClient, hcloudClient, fip.ID, cfg.labelKey, "") assignCancel() + + eventCtx, eventCancel := context.WithTimeout(ctx, cfg.timeout) + waitForEvent(eventCtx, t, kubeClient, cfg.namespace, "FloatingIPAssigned", fmt.Sprintf("%s assigned to Node", fip.IP.String())) + eventCancel() assignments[fip.ID] = nodeName } @@ -437,9 +441,9 @@ func createFloatingIPs(ctx context.Context, t *testing.T, client *hcloud.Client, for i := 0; i < count; i++ { description := fmt.Sprintf("hcloud-fip-k8s e2e %d", time.Now().UnixNano()) opts := hcloud.FloatingIPCreateOpts{ - Type: hcloud.FloatingIPTypeIPv4, - Description: &description, - Labels: labels, + Type: hcloud.FloatingIPTypeIPv4, + Description: &description, + Labels: labels, } if location != nil { opts.HomeLocation = location @@ -673,6 +677,43 @@ func logAssignmentWait(t *testing.T, start time.Time, lastLog *time.Time, msg, d } } +func waitForEvent(ctx context.Context, t *testing.T, kube *kubernetes.Clientset, namespace, reason, message string) { + t.Helper() + if namespace == "" { + t.Fatalf("event namespace must be set") + } + start := time.Now() + lastLog := time.Now() + logDetail := message + if logDetail == "" { + logDetail = reason + } + err := poll(ctx, func() (bool, error) { + list, err := kube.CoreV1().Events(namespace).List(ctx, metav1.ListOptions{}) + if err != nil { + return false, err + } + for _, event := range list.Items { + if reason != "" && event.Reason != reason { + continue + } + if message != "" && !strings.Contains(event.Message, message) { + continue + } + if reason == "" && message == "" { + continue + } + t.Logf("found event %s/%s: reason=%s message=%s", event.Namespace, event.Name, event.Reason, event.Message) + return true, nil + } + logAssignmentWait(t, start, &lastLog, "waiting for event", logDetail) + return false, nil + }) + if err != nil { + t.Fatalf("failed waiting for event reason %q message %q in namespace %s: %v", reason, message, namespace, err) + } +} + func waitForAction(ctx context.Context, t *testing.T, client *hcloud.Client, action *hcloud.Action, desc string) { t.Helper() if action == nil {