From b3fe6a8c4a2af1d92e580bc17fcd136c3ba4e87e Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Tue, 17 Feb 2026 23:49:20 +0300 Subject: [PATCH] fix(crdinstall): hardcode CRD GVK, add timeout test, document dual install - Use explicit apiextensions.k8s.io/v1 CRD GVK in waitForCRDsEstablished instead of fragile objects[0].GroupVersionKind() - Add TestInstall_crdNotEstablished for context timeout path - Add --recursive to diff in verify-crds Makefile target - Document why both crds/ and --install-crds exist in deployment template Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- Makefile | 2 +- internal/crdinstall/install.go | 7 ++- internal/crdinstall/install_test.go | 46 +++++++++++++++++++ .../templates/cozystack-operator.yaml | 3 ++ 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 85b48659..9707ade4 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ test: make -C packages/core/testing test verify-crds: - @diff packages/core/installer/crds/ internal/crdinstall/manifests/ --exclude=.gitattributes \ + @diff --recursive packages/core/installer/crds/ internal/crdinstall/manifests/ --exclude=.gitattributes \ || (echo "ERROR: CRD manifests out of sync. Run 'make generate' to fix." && exit 1) unit-tests: helm-unit-tests verify-crds diff --git a/internal/crdinstall/install.go b/internal/crdinstall/install.go index 62615f68..315de612 100644 --- a/internal/crdinstall/install.go +++ b/internal/crdinstall/install.go @@ -28,6 +28,7 @@ import ( "time" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" k8syaml "k8s.io/apimachinery/pkg/util/yaml" "sigs.k8s.io/controller-runtime/pkg/client" @@ -124,7 +125,11 @@ func waitForCRDsEstablished(ctx context.Context, k8sClient client.Client, object allEstablished := true for _, name := range crdNames { crd := &unstructured.Unstructured{} - crd.SetGroupVersionKind(objects[0].GroupVersionKind()) + crd.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "apiextensions.k8s.io", + Version: "v1", + Kind: "CustomResourceDefinition", + }) if err := k8sClient.Get(ctx, types.NamespacedName{Name: name}, crd); err != nil { allEstablished = false break diff --git a/internal/crdinstall/install_test.go b/internal/crdinstall/install_test.go index ca49db51..2fd200bc 100644 --- a/internal/crdinstall/install_test.go +++ b/internal/crdinstall/install_test.go @@ -354,6 +354,52 @@ func TestInstall_writeManifestsFails(t *testing.T) { } } +func TestInstall_crdNotEstablished(t *testing.T) { + log.SetLogger(zap.New(zap.UseDevMode(true))) + + scheme := runtime.NewScheme() + if err := apiextensionsv1.AddToScheme(scheme); err != nil { + t.Fatalf("failed to add apiextensions to scheme: %v", err) + } + + // No interceptor: CRDs will never get Established condition + fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build() + + writeManifests := func(dir string) error { + crd := `apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: packages.cozystack.io +spec: + group: cozystack.io + names: + kind: Package + plural: packages + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object +` + return os.WriteFile(filepath.Join(dir, "crd.yaml"), []byte(crd), 0600) + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + ctx = log.IntoContext(ctx, log.FromContext(context.Background())) + + err := Install(ctx, fakeClient, writeManifests) + if err == nil { + t.Fatal("Install() expected error when CRDs never become established, got nil") + } + if !strings.Contains(err.Error(), "CRDs not established") { + t.Errorf("Install() error = %v, want error containing 'CRDs not established'", err) + } +} + func TestWriteEmbeddedManifests_filePermissions(t *testing.T) { tmpDir := t.TempDir() diff --git a/packages/core/installer/templates/cozystack-operator.yaml b/packages/core/installer/templates/cozystack-operator.yaml index 359fb40c..1153ec4d 100644 --- a/packages/core/installer/templates/cozystack-operator.yaml +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -53,6 +53,9 @@ spec: args: - --leader-elect=true - --install-flux=true + # CRDs are also in crds/ for initial helm install, but Helm never updates + # them on upgrade. The operator applies embedded CRDs via server-side apply + # on every startup, ensuring they stay up to date. - --install-crds=true - --metrics-bind-address=0 - --health-probe-bind-address=