From afde43500f3271c18dfab7a91733ae9a5dae5528 Mon Sep 17 00:00:00 2001 From: Jared Watts Date: Wed, 17 Sep 2025 10:08:26 -0700 Subject: [PATCH] update conformance-testdata-configuration for v2 - convert to function pipeline from native patch and transform - use cluster scoped v2 XRD - use latest provider-nop with ClusterNopResource - add more tests and cleanup to verify new function dependencies succeed Signed-off-by: Jared Watts --- testdata/configuration/composition.yaml | 46 ++++++++++++++--------- testdata/configuration/crossplane.yaml | 6 ++- testdata/configuration/definition.yaml | 8 +--- tests/configuration_test.go | 49 +++++++++++++++++++++---- 4 files changed, 75 insertions(+), 34 deletions(-) diff --git a/testdata/configuration/composition.yaml b/testdata/configuration/composition.yaml index 7fee9e2..228e10c 100644 --- a/testdata/configuration/composition.yaml +++ b/testdata/configuration/composition.yaml @@ -9,21 +9,31 @@ spec: writeConnectionSecretsToNamespace: crossplane-system compositeTypeRef: apiVersion: test.crossplane.io/v1alpha1 - kind: Conformance - resources: - - name: nop - base: - apiVersion: nop.crossplane.io/v1alpha1 - kind: NopResource - spec: - forProvider: {} - writeConnectionSecretToRef: - namespace: crossplane-conformance - name: nop-example-resource - patches: - - fromFieldPath: spec.parameters.coolness - toFieldPath: metadata.annotations[nop.crossplane.io/coolness] - transforms: - - type: string - string: - fmt: "%d" + kind: ClusterConformance + mode: Pipeline + pipeline: + - step: patch-and-transform + functionRef: + name: crossplane-contrib-function-patch-and-transform + input: + apiVersion: pt.fn.crossplane.io/v1beta1 + kind: Resources + resources: + - name: nop + base: + apiVersion: nop.crossplane.io/v1alpha1 + kind: ClusterNopResource + spec: + forProvider: {} + writeConnectionSecretToRef: + namespace: crossplane-conformance + name: nop-example-resource + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.coolness + toFieldPath: metadata.annotations[nop.crossplane.io/coolness] + transforms: + - type: string + string: + type: Format + fmt: "%.0f" diff --git a/testdata/configuration/crossplane.yaml b/testdata/configuration/crossplane.yaml index 576e626..8a318ae 100644 --- a/testdata/configuration/crossplane.yaml +++ b/testdata/configuration/crossplane.yaml @@ -6,7 +6,9 @@ metadata: crossplane.io/test: crossplane-conformance spec: crossplane: - version: ">=v1.15.0" + version: ">=v2.0.0" dependsOn: - provider: xpkg.crossplane.io/crossplane-contrib/provider-nop - version: "v0.4.0" + version: "v0.5.0" + - function: xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform + version: "v0.9.0" diff --git a/testdata/configuration/definition.yaml b/testdata/configuration/definition.yaml index f9fc11e..8de8e01 100644 --- a/testdata/configuration/definition.yaml +++ b/testdata/configuration/definition.yaml @@ -1,20 +1,16 @@ --- -apiVersion: apiextensions.crossplane.io/v1 +apiVersion: apiextensions.crossplane.io/v2 kind: CompositeResourceDefinition metadata: name: clusterconformances.test.crossplane.io labels: crossplane.io/test: crossplane-conformance spec: + scope: Cluster group: test.crossplane.io names: kind: ClusterConformance plural: clusterconformances - claimNames: - kind: Conformance - plural: conformances - connectionSecretKeys: - - test versions: - name: v1alpha1 served: true diff --git a/tests/configuration_test.go b/tests/configuration_test.go index 1cef754..f8b4ed5 100644 --- a/tests/configuration_test.go +++ b/tests/configuration_test.go @@ -55,8 +55,10 @@ func TestConfiguration(t *testing.T) { }, } - // The crossplane-conformance provider depends on xpkg.crossplane.io/crossplane-contrib/provider-nop. + // The crossplane-conformance provider depends on xpkg.crossplane.io/crossplane-contrib/provider-nop + // and xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform prv := &pkgv1.Provider{ObjectMeta: metav1.ObjectMeta{Name: "crossplane-contrib-provider-nop"}} + fnc := &pkgv1.Function{ObjectMeta: metav1.ObjectMeta{Name: "crossplane-contrib-function-patch-and-transform"}} if err := kube.Create(ctx, cfg); err != nil { t.Fatalf("Create configuration %q: %v", cfg.GetName(), err) @@ -74,6 +76,17 @@ func TestConfiguration(t *testing.T) { t.Logf("Deleted provider %q", prv.GetName()) }) + t.Cleanup(func() { + t.Logf("Cleaning up function %q.", fnc.GetName()) + if err := kube.Get(ctx, types.NamespacedName{Name: fnc.GetName()}, fnc); resource.IgnoreNotFound(err) != nil { + t.Fatalf("Get function %q: %v", fnc.GetName(), err) + } + if err := kube.Delete(ctx, fnc); resource.IgnoreNotFound(err) != nil { + t.Fatalf("Delete function %q: %v", fnc.GetName(), err) + } + t.Logf("Deleted function %q", fnc.GetName()) + }) + t.Cleanup(func() { t.Logf("Cleaning up configuration %q.", cfg.GetName()) if err := kube.Get(ctx, types.NamespacedName{Name: cfg.GetName()}, cfg); resource.IgnoreNotFound(err) != nil { @@ -131,12 +144,7 @@ func TestConfiguration(t *testing.T) { return false, nil } - if rev.GetCondition(pkgv1.TypeRuntimeHealthy).Status != corev1.ConditionTrue { - t.Logf("Revision %q is not yet RuntimeHealthy", rev.GetName()) - return false, nil - } - - t.Logf("Revision %q is RevisionHealthy and RuntimeHealthy", rev.GetName()) + t.Logf("Revision %q is RevisionHealthy", rev.GetName()) // We expect the revision to deploy two objects; an XRD // and a Composition. @@ -170,7 +178,7 @@ func TestConfiguration(t *testing.T) { } }) - t.Run("DependencyBecomesInstalledAndHealthy", func(t *testing.T) { + t.Run("DependenciesBecomesInstalledAndHealthy", func(t *testing.T) { t.Log("Testing that the configuration's dependencies' Healthy and Installed status conditions become 'True'.") if err := wait.PollUntilContextTimeout(ctx, 10*time.Second, 90*time.Second, true, func(ctx context.Context) (done bool, err error) { if err := kube.Get(ctx, types.NamespacedName{Name: prv.GetName()}, prv); err != nil { @@ -196,5 +204,30 @@ func TestConfiguration(t *testing.T) { }); err != nil { t.Errorf("Provider %q never became Healthy and Installed: %v", prv.GetName(), err) } + + if err := wait.PollUntilContextTimeout(ctx, 10*time.Second, 90*time.Second, true, func(ctx context.Context) (done bool, err error) { + if err := kube.Get(ctx, types.NamespacedName{Name: fnc.GetName()}, fnc); err != nil { + // Most likely the function hasn't been created yet. + if kerrors.IsNotFound(err) { + return false, nil + } + return false, err + } + + if fnc.GetCondition(pkgv1.TypeHealthy).Status != corev1.ConditionTrue { + t.Logf("Function %q is not yet Healthy", fnc.GetName()) + return false, nil + } + + if fnc.GetCondition(pkgv1.TypeInstalled).Status != corev1.ConditionTrue { + t.Logf("Function %q is not yet Installed", fnc.GetName()) + return false, nil + } + + t.Logf("Function %q is Healthy and Installed", fnc.GetName()) + return true, nil + }); err != nil { + t.Errorf("Function %q never became Healthy and Installed: %v", fnc.GetName(), err) + } }) }