Skip to content

Commit 99866da

Browse files
authored
šŸ› test: filter cluster-wide objects asserted in ResourceVersion tests to exclude objects of parallel tests (#10560)
* test: filter cluster-wide objects asserted in ResourceVersion tests to exclude objects of parallel tests * review fixes * review fixes
1 parent cbd1f23 commit 99866da

7 files changed

+85
-39
lines changed

ā€Žcmd/clusterctl/client/cluster/ownergraph.go

+31-3
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,35 @@ type OwnerGraphNode struct {
3939
Owners []metav1.OwnerReference
4040
}
4141

42+
// GetOwnerGraphFilterFunction allows filtering the objects returned by GetOwnerGraph.
43+
// The function has to return true for objects which should be kept.
44+
// NOTE: this function signature is exposed to allow implementation of E2E tests; there is
45+
// no guarantee about the stability of this API.
46+
type GetOwnerGraphFilterFunction func(u unstructured.Unstructured) bool
47+
48+
// FilterClusterObjectsWithNameFilter is used in e2e tests where the owner graph
49+
// gets queried to filter out cluster-wide objects which don't have the s in their
50+
// object name. This avoids assertions on objects which are part of in-parallel
51+
// running tests like ExtensionConfig.
52+
// NOTE: this function signature is exposed to allow implementation of E2E tests; there is
53+
// no guarantee about the stability of this API.
54+
func FilterClusterObjectsWithNameFilter(s string) func(u unstructured.Unstructured) bool {
55+
return func(u unstructured.Unstructured) bool {
56+
// Ignore cluster-wide objects which don't have the clusterName in their object
57+
// name to avoid asserting on cluster-wide objects which get created or deleted
58+
// by tests which run in-parallel (e.g. ExtensionConfig).
59+
if u.GetNamespace() == "" && !strings.Contains(u.GetName(), s) {
60+
return false
61+
}
62+
return true
63+
}
64+
}
65+
4266
// GetOwnerGraph returns a graph with all the objects considered by clusterctl move as nodes and the OwnerReference relationship between those objects as edges.
4367
// NOTE: this data structure is exposed to allow implementation of E2E tests verifying that CAPI can properly rebuild its
4468
// own owner references; there is no guarantee about the stability of this API. Using this test with providers may require
4569
// a custom implementation of this function, or the OwnerGraph it returns.
46-
func GetOwnerGraph(ctx context.Context, namespace, kubeconfigPath string) (OwnerGraph, error) {
70+
func GetOwnerGraph(ctx context.Context, namespace, kubeconfigPath string, filterFn GetOwnerGraphFilterFunction) (OwnerGraph, error) {
4771
p := newProxy(Kubeconfig{Path: kubeconfigPath, Context: ""})
4872
invClient := newInventoryClient(p, nil)
4973

@@ -57,14 +81,14 @@ func GetOwnerGraph(ctx context.Context, namespace, kubeconfigPath string) (Owner
5781

5882
// graph.Discovery can not be used here as it will use the latest APIVersion for ownerReferences - not those
5983
// present in the object `metadata.ownerReferences`.
60-
owners, err := discoverOwnerGraph(ctx, namespace, graph)
84+
owners, err := discoverOwnerGraph(ctx, namespace, graph, filterFn)
6185
if err != nil {
6286
return OwnerGraph{}, errors.Wrap(err, "failed to discovery ownerGraph types")
6387
}
6488
return owners, nil
6589
}
6690

67-
func discoverOwnerGraph(ctx context.Context, namespace string, o *objectGraph) (OwnerGraph, error) {
91+
func discoverOwnerGraph(ctx context.Context, namespace string, o *objectGraph, filterFn GetOwnerGraphFilterFunction) (OwnerGraph, error) {
6892
selectors := []client.ListOption{}
6993
if namespace != "" {
7094
selectors = append(selectors, client.InNamespace(namespace))
@@ -102,6 +126,10 @@ func discoverOwnerGraph(ctx context.Context, namespace string, o *objectGraph) (
102126
}
103127
}
104128
for _, obj := range objList.Items {
129+
// Exclude the objects via the filter function.
130+
if filterFn != nil && !filterFn(obj) {
131+
continue
132+
}
105133
// Exclude the kube-root-ca.crt ConfigMap from the owner graph.
106134
if obj.GetKind() == "ConfigMap" && obj.GetName() == "kube-root-ca.crt" {
107135
continue

ā€Žtest/e2e/cluster_upgrade_runtimesdk.go

+9
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ type clusterUpgradeWithRuntimeSDKSpecInput struct {
8585
// Allows to inject a function to be run after test namespace is created.
8686
// If not specified, this is a no-op.
8787
PostNamespaceCreated func(managementClusterProxy framework.ClusterProxy, workloadClusterNamespace string)
88+
89+
// Allows to inject a function to be run after the cluster is upgraded.
90+
// If not specified, this is a no-op.
91+
PostUpgrade func(managementClusterProxy framework.ClusterProxy, workloadClusterNamespace, workloadClusterName string)
8892
}
8993

9094
// clusterUpgradeWithRuntimeSDKSpec implements a spec that upgrades a cluster and runs the Kubernetes conformance suite.
@@ -260,6 +264,11 @@ func clusterUpgradeWithRuntimeSDKSpec(ctx context.Context, inputGetter func() cl
260264
WaitForNodesReady: input.E2EConfig.GetIntervals(specName, "wait-nodes-ready"),
261265
})
262266

267+
if input.PostUpgrade != nil {
268+
log.Logf("Calling PostMachinesProvisioned for cluster %s", klog.KRef(namespace.Name, clusterResources.Cluster.Name))
269+
input.PostUpgrade(input.BootstrapClusterProxy, namespace.Name, clusterResources.Cluster.Name)
270+
}
271+
263272
By("Dumping resources and deleting the workload cluster; deletion waits for BeforeClusterDeleteHook to gate the operation")
264273
dumpAndDeleteCluster(ctx, input.BootstrapClusterProxy, namespace.Name, clusterName, input.ArtifactFolder)
265274

ā€Žtest/e2e/cluster_upgrade_runtimesdk_test.go

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import (
2424
. "github.com/onsi/ginkgo/v2"
2525
. "github.com/onsi/gomega"
2626
"k8s.io/utils/ptr"
27+
28+
clusterctlcluster "sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
29+
"sigs.k8s.io/cluster-api/test/framework"
2730
)
2831

2932
var _ = Describe("When upgrading a workload cluster using ClusterClass with RuntimeSDK [ClusterClass]", func() {
@@ -41,6 +44,11 @@ var _ = Describe("When upgrading a workload cluster using ClusterClass with Runt
4144
ArtifactFolder: artifactFolder,
4245
SkipCleanup: skipCleanup,
4346
InfrastructureProvider: ptr.To("docker"),
47+
PostUpgrade: func(proxy framework.ClusterProxy, namespace, clusterName string) {
48+
// This check ensures that the resourceVersions are stable, i.e. it verifies there are no
49+
// continuous reconciles when everything should be stable.
50+
framework.ValidateResourceVersionStable(ctx, proxy, namespace, clusterctlcluster.FilterClusterObjectsWithNameFilter(clusterName))
51+
},
4452
// "upgrades" is the same as the "topology" flavor but with an additional MachinePool.
4553
Flavor: ptr.To("upgrades-runtimesdk"),
4654
}

ā€Žtest/e2e/quick_start_test.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
. "github.com/onsi/gomega"
2525
"k8s.io/utils/ptr"
2626

27+
clusterctlcluster "sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
2728
"sigs.k8s.io/cluster-api/test/framework"
2829
"sigs.k8s.io/cluster-api/test/framework/kubetest"
2930
)
@@ -39,7 +40,7 @@ var _ = Describe("When following the Cluster API quick-start", func() {
3940
InfrastructureProvider: ptr.To("docker"),
4041
PostMachinesProvisioned: func(proxy framework.ClusterProxy, namespace, clusterName string) {
4142
// This check ensures that owner references are resilient - i.e. correctly re-reconciled - when removed.
42-
framework.ValidateOwnerReferencesResilience(ctx, proxy, namespace, clusterName,
43+
framework.ValidateOwnerReferencesResilience(ctx, proxy, namespace, clusterName, clusterctlcluster.FilterClusterObjectsWithNameFilter(clusterName),
4344
framework.CoreOwnerReferenceAssertion,
4445
framework.ExpOwnerReferenceAssertions,
4546
framework.DockerInfraOwnerReferenceAssertions,
@@ -48,7 +49,7 @@ var _ = Describe("When following the Cluster API quick-start", func() {
4849
framework.KubernetesReferenceAssertions,
4950
)
5051
// This check ensures that owner references are correctly updated to the correct apiVersion.
51-
framework.ValidateOwnerReferencesOnUpdate(ctx, proxy, namespace, clusterName,
52+
framework.ValidateOwnerReferencesOnUpdate(ctx, proxy, namespace, clusterName, clusterctlcluster.FilterClusterObjectsWithNameFilter(clusterName),
5253
framework.CoreOwnerReferenceAssertion,
5354
framework.ExpOwnerReferenceAssertions,
5455
framework.DockerInfraOwnerReferenceAssertions,
@@ -57,15 +58,15 @@ var _ = Describe("When following the Cluster API quick-start", func() {
5758
framework.KubernetesReferenceAssertions,
5859
)
5960
// This check ensures that finalizers are resilient - i.e. correctly re-reconciled - when removed.
60-
framework.ValidateFinalizersResilience(ctx, proxy, namespace, clusterName,
61+
framework.ValidateFinalizersResilience(ctx, proxy, namespace, clusterName, clusterctlcluster.FilterClusterObjectsWithNameFilter(clusterName),
6162
framework.CoreFinalizersAssertion,
6263
framework.KubeadmControlPlaneFinalizersAssertion,
6364
framework.ExpFinalizersAssertion,
6465
framework.DockerInfraFinalizersAssertion,
6566
)
6667
// This check ensures that the resourceVersions are stable, i.e. it verifies there are no
6768
// continuous reconciles when everything should be stable.
68-
framework.ValidateResourceVersionStable(ctx, proxy, namespace)
69+
framework.ValidateResourceVersionStable(ctx, proxy, namespace, clusterctlcluster.FilterClusterObjectsWithNameFilter(clusterName))
6970
},
7071
}
7172
})
@@ -83,7 +84,7 @@ var _ = Describe("When following the Cluster API quick-start with ClusterClass [
8384
InfrastructureProvider: ptr.To("docker"),
8485
// This check ensures that owner references are resilient - i.e. correctly re-reconciled - when removed.
8586
PostMachinesProvisioned: func(proxy framework.ClusterProxy, namespace, clusterName string) {
86-
framework.ValidateOwnerReferencesResilience(ctx, proxy, namespace, clusterName,
87+
framework.ValidateOwnerReferencesResilience(ctx, proxy, namespace, clusterName, clusterctlcluster.FilterClusterObjectsWithNameFilter(clusterName),
8788
framework.CoreOwnerReferenceAssertion,
8889
framework.ExpOwnerReferenceAssertions,
8990
framework.DockerInfraOwnerReferenceAssertions,
@@ -92,7 +93,7 @@ var _ = Describe("When following the Cluster API quick-start with ClusterClass [
9293
framework.KubernetesReferenceAssertions,
9394
)
9495
// This check ensures that owner references are correctly updated to the correct apiVersion.
95-
framework.ValidateOwnerReferencesOnUpdate(ctx, proxy, namespace, clusterName,
96+
framework.ValidateOwnerReferencesOnUpdate(ctx, proxy, namespace, clusterName, clusterctlcluster.FilterClusterObjectsWithNameFilter(clusterName),
9697
framework.CoreOwnerReferenceAssertion,
9798
framework.ExpOwnerReferenceAssertions,
9899
framework.DockerInfraOwnerReferenceAssertions,
@@ -101,15 +102,15 @@ var _ = Describe("When following the Cluster API quick-start with ClusterClass [
101102
framework.KubernetesReferenceAssertions,
102103
)
103104
// This check ensures that finalizers are resilient - i.e. correctly re-reconciled - when removed.
104-
framework.ValidateFinalizersResilience(ctx, proxy, namespace, clusterName,
105+
framework.ValidateFinalizersResilience(ctx, proxy, namespace, clusterName, clusterctlcluster.FilterClusterObjectsWithNameFilter(clusterName),
105106
framework.CoreFinalizersAssertion,
106107
framework.KubeadmControlPlaneFinalizersAssertion,
107108
framework.ExpFinalizersAssertion,
108109
framework.DockerInfraFinalizersAssertion,
109110
)
110111
// This check ensures that the resourceVersions are stable, i.e. it verifies there are no
111112
// continuous reconciles when everything should be stable.
112-
framework.ValidateResourceVersionStable(ctx, proxy, namespace)
113+
framework.ValidateResourceVersionStable(ctx, proxy, namespace, clusterctlcluster.FilterClusterObjectsWithNameFilter(clusterName))
113114
},
114115
}
115116
})

ā€Žtest/framework/finalizers_helpers.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ var KubeadmControlPlaneFinalizersAssertion = map[string][]string{
6464
}
6565

6666
// ValidateFinalizersResilience checks that expected finalizers are in place, deletes them, and verifies that expected finalizers are properly added again.
67-
func ValidateFinalizersResilience(ctx context.Context, proxy ClusterProxy, namespace, clusterName string, finalizerAssertions ...map[string][]string) {
67+
func ValidateFinalizersResilience(ctx context.Context, proxy ClusterProxy, namespace, clusterName string, ownerGraphFilterFunction clusterctlcluster.GetOwnerGraphFilterFunction, finalizerAssertions ...map[string][]string) {
6868
clusterKey := client.ObjectKey{Namespace: namespace, Name: clusterName}
6969
allFinalizerAssertions, err := concatenateFinalizerAssertions(finalizerAssertions...)
7070
Expect(err).ToNot(HaveOccurred())
7171

7272
// Collect all objects where finalizers were initially set
73-
objectsWithFinalizers := getObjectsWithFinalizers(ctx, proxy, namespace, allFinalizerAssertions)
73+
objectsWithFinalizers := getObjectsWithFinalizers(ctx, proxy, namespace, allFinalizerAssertions, ownerGraphFilterFunction)
7474

7575
// Setting the paused property on the Cluster resource will pause reconciliations, thereby having no effect on Finalizers.
7676
// This also makes debugging easier.
@@ -79,18 +79,18 @@ func ValidateFinalizersResilience(ctx context.Context, proxy ClusterProxy, names
7979
// We are testing the worst-case scenario, i.e. all finalizers are deleted.
8080
// Once all Clusters are paused remove all the Finalizers from all objects in the graph.
8181
// The reconciliation loop should be able to recover from this, by adding the required Finalizers back.
82-
removeFinalizers(ctx, proxy, namespace)
82+
removeFinalizers(ctx, proxy, namespace, ownerGraphFilterFunction)
8383

8484
// Unpause the cluster.
8585
setClusterPause(ctx, proxy.GetClient(), clusterKey, false)
8686

8787
// Check that the Finalizers are as expected after further reconciliations.
88-
assertFinalizersExist(ctx, proxy, namespace, objectsWithFinalizers, allFinalizerAssertions)
88+
assertFinalizersExist(ctx, proxy, namespace, objectsWithFinalizers, allFinalizerAssertions, ownerGraphFilterFunction)
8989
}
9090

9191
// removeFinalizers removes all Finalizers from objects in the owner graph.
92-
func removeFinalizers(ctx context.Context, proxy ClusterProxy, namespace string) {
93-
graph, err := clusterctlcluster.GetOwnerGraph(ctx, namespace, proxy.GetKubeconfigPath())
92+
func removeFinalizers(ctx context.Context, proxy ClusterProxy, namespace string, ownerGraphFilterFunction clusterctlcluster.GetOwnerGraphFilterFunction) {
93+
graph, err := clusterctlcluster.GetOwnerGraph(ctx, namespace, proxy.GetKubeconfigPath(), ownerGraphFilterFunction)
9494
Expect(err).ToNot(HaveOccurred())
9595
for _, object := range graph {
9696
ref := object.Object
@@ -107,8 +107,8 @@ func removeFinalizers(ctx context.Context, proxy ClusterProxy, namespace string)
107107
}
108108
}
109109

110-
func getObjectsWithFinalizers(ctx context.Context, proxy ClusterProxy, namespace string, allFinalizerAssertions map[string][]string) map[string]*unstructured.Unstructured {
111-
graph, err := clusterctlcluster.GetOwnerGraph(ctx, namespace, proxy.GetKubeconfigPath())
110+
func getObjectsWithFinalizers(ctx context.Context, proxy ClusterProxy, namespace string, allFinalizerAssertions map[string][]string, ownerGraphFilterFunction clusterctlcluster.GetOwnerGraphFilterFunction) map[string]*unstructured.Unstructured {
111+
graph, err := clusterctlcluster.GetOwnerGraph(ctx, namespace, proxy.GetKubeconfigPath(), ownerGraphFilterFunction)
112112
Expect(err).ToNot(HaveOccurred())
113113

114114
objsWithFinalizers := map[string]*unstructured.Unstructured{}
@@ -134,10 +134,10 @@ func getObjectsWithFinalizers(ctx context.Context, proxy ClusterProxy, namespace
134134
}
135135

136136
// assertFinalizersExist ensures that current Finalizers match those in the initialObjectsWithFinalizers.
137-
func assertFinalizersExist(ctx context.Context, proxy ClusterProxy, namespace string, initialObjsWithFinalizers map[string]*unstructured.Unstructured, allFinalizerAssertions map[string][]string) {
137+
func assertFinalizersExist(ctx context.Context, proxy ClusterProxy, namespace string, initialObjsWithFinalizers map[string]*unstructured.Unstructured, allFinalizerAssertions map[string][]string, ownerGraphFilterFunction clusterctlcluster.GetOwnerGraphFilterFunction) {
138138
Eventually(func() error {
139139
var allErrs []error
140-
finalObjsWithFinalizers := getObjectsWithFinalizers(ctx, proxy, namespace, allFinalizerAssertions)
140+
finalObjsWithFinalizers := getObjectsWithFinalizers(ctx, proxy, namespace, allFinalizerAssertions, ownerGraphFilterFunction)
141141

142142
for objKindNamespacedName, obj := range initialObjsWithFinalizers {
143143
// verify if finalizers for this resource were set on reconcile

0 commit comments

Comments
Ā (0)