package providerhealth import ( "context" "os" "path/filepath" "strings" "testing" "time" "unicode/utf8" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" crawlv1alpha1 "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/api/v1alpha1" "gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/provider" ) // TestMonitor_envtestRoundTrip writes through a real API server, which is // the only thing that validates the generated schema: cluster scope, the // status subresource, required fields, list-map keys and the message limit. func TestMonitor_envtestRoundTrip(t *testing.T) { if testing.Short() { t.Skip("envtest skipped under -short") } env := &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: true, BinaryAssetsDirectory: envtestBinaryDir(), } cfg, err := env.Start() if err != nil { t.Fatalf("starting envtest (run `make setup-envtest` first): %v", err) } t.Cleanup(func() { _ = env.Stop() }) scheme := runtime.NewScheme() if err := crawlv1alpha1.AddToScheme(scheme); err != nil { t.Fatal(err) } c, err := client.New(cfg, client.Options{Scheme: scheme}) if err != nil { t.Fatal(err) } ctx := context.Background() m := &Monitor{Client: c, Reader: c} first := Result{Provider: "kvm-home", Type: "kvm", Supported: true, CheckedAt: t0, Targets: []provider.TargetStatus{ {Name: "nas1", Address: "nas1:22", Ready: true, Reason: provider.ReasonReachable, Latency: 42 * time.Millisecond, Details: map[string]string{"libvirtVersion": "10.2.0"}}, {Name: "nas2", Address: "nas2:22", Reason: provider.ReasonUnreachable, Message: strings.Repeat("x", 2000)}, }} if err := m.publish(ctx, first); err != nil { t.Fatalf("first publish: %v", err) } got := getHealth(t, c, "kvm-home") if got.Status.TargetsReady != "1/2" || got.Status.Targets[0].Details["libvirtVersion"] != "10.2.0" || got.Status.Targets[0].LatencyMillis != 42 || utf8.RuneCountInString(got.Status.Targets[1].Message) != maxMessageLen { t.Errorf("status after the first round = %+v", got.Status) } second := Result{Provider: "kvm-home", Type: "kvm", Supported: true, CheckedAt: t1, Targets: []provider.TargetStatus{up("nas1"), up("nas2")}} if err := m.publish(ctx, second); err != nil { t.Fatalf("second publish: %v", err) } got = getHealth(t, c, "kvm-home") if cond := readyCondition(got); cond == nil || cond.Reason != reasonAllTargetsReady { t.Errorf("Ready after recovery = %+v, want %s", cond, reasonAllTargetsReady) } if lt := got.Status.Targets[0].LastTransitionTime.Time; !lt.Equal(t0) { t.Errorf("nas1 lastTransitionTime = %s, want %s", lt, t0) } if lt := got.Status.Targets[1].LastTransitionTime.Time; !lt.Equal(t1) { t.Errorf("nas2 lastTransitionTime = %s, want %s", lt, t1) } m.cleanup(ctx, nil) var gone crawlv1alpha1.ProviderHealth if err := c.Get(ctx, client.ObjectKey{Name: "kvm-home"}, &gone); !apierrors.IsNotFound(err) { t.Errorf("Get after cleanup = %v, want NotFound", err) } } // envtestBinaryDir finds the binaries `make setup-envtest` downloads, so // the test also runs outside make (which sets KUBEBUILDER_ASSETS instead). func envtestBinaryDir() string { base := filepath.Join("..", "..", "bin", "k8s") entries, err := os.ReadDir(base) if err != nil { return "" } for _, e := range entries { if e.IsDir() { return filepath.Join(base, e.Name()) } } return "" }