package controller import ( "context" "fmt" "sync/atomic" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/reconcile" terdutv1alpha1 "git.ryuvia.com/niklas/terdut-operator/api/v1alpha1" "git.ryuvia.com/niklas/terdut-operator/internal/tdclient" ) // Shared fixture values -- a bring-your-own DSN TerdutServer spec, reused // across terdutserver_controller_test.go, terdutteam_controller_test.go and // this file, so there's exactly one definition to keep consistent. const ( testImageRepo = "example.invalid/terdut-server" testImageTag = "test" testDSN = "postgres://terdut@test-postgres:5432/terdut?sslmode=require" ) // bootstrapReadyTerdutServer creates a TerdutServer with a bring-your-own // DSN and drives it to Ready against fakeURL, the same three-pass sequence // terdutserver_controller_test.go's own happy-path test exercises directly // -- shared here so TerdutTeam's tests (which need a real, Ready // TerdutServer to resolve against) don't duplicate it. func bootstrapReadyTerdutServer(ctx context.Context, namespace, name, fakeURL string) *terdutv1alpha1.TerdutServer { GinkgoHelper() reconciler := &TerdutServerReconciler{ Client: k8sClient, Scheme: k8sClient.Scheme(), OperatorNamespace: namespace, NewClient: func(string) *tdclient.Client { return tdclient.New(fakeURL) }, } objKey := types.NamespacedName{Name: name, Namespace: namespace} srv := &terdutv1alpha1.TerdutServer{ ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}, Spec: terdutv1alpha1.TerdutServerSpec{ Image: terdutv1alpha1.ImageSpec{Repository: testImageRepo, Tag: testImageTag}, Networking: terdutv1alpha1.NetworkingSpec{Hostname: "terdut.example.invalid", ServicePort: 8080}, Database: terdutv1alpha1.DatabaseSpec{DSN: testDSN}, }, } Expect(k8sClient.Create(ctx, srv)).To(Succeed()) _, err := reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: objKey}) // finalizer Expect(err).NotTo(HaveOccurred()) _, err = reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: objKey}) // Deployment/Service Expect(err).NotTo(HaveOccurred()) var deploy appsv1.Deployment Expect(k8sClient.Get(ctx, objKey, &deploy)).To(Succeed()) deploy.Status.ReadyReplicas = 1 deploy.Status.Replicas = 1 Expect(k8sClient.Status().Update(ctx, &deploy)).To(Succeed()) _, err = reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: objKey}) // bootstrap Expect(err).NotTo(HaveOccurred()) Expect(k8sClient.Get(ctx, objKey, srv)).To(Succeed()) Expect(srv.Status.CredentialsSecretRef).NotTo(BeNil(), "test setup: TerdutServer %s/%s did not reach Bootstrapped", namespace, name) return srv } // uniqueNameCounter backs uniqueName. GinkgoRandomSeed()/GinkgoParallelProcess() // are constants for the whole suite run, not per-spec -- an earlier version // of this helper used them and collided across every spec that called it // more than once; an atomic counter is genuinely unique per call instead. var uniqueNameCounter atomic.Int64 // uniqueName returns a Kubernetes-object-safe name unique to this call. func uniqueName(prefix string) string { return fmt.Sprintf("%s-%d", prefix, uniqueNameCounter.Add(1)) }