Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌱 Improve error output of ValidateResourceVersionStable #11012

Merged
merged 1 commit into from
Aug 6, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 68 additions & 7 deletions test/framework/resourceversion_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ package framework
import (
"context"
"fmt"
"strings"
"time"

"github.com/google/go-cmp/cmp"
. "github.com/onsi/gomega"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"

clusterctlcluster "sigs.k8s.io/cluster-api/cmd/clusterctl/client/cluster"
)
Expand All @@ -33,13 +37,15 @@ func ValidateResourceVersionStable(ctx context.Context, proxy ClusterProxy, name
// Wait until resource versions are stable for a bit.
byf("Check Resource versions are stable")
var previousResourceVersions map[string]string
var previousObjects map[string]client.Object
Eventually(func(g Gomega) {
objectsWithResourceVersion, err := getObjectsWithResourceVersion(ctx, proxy, namespace, ownerGraphFilterFunction)
objectsWithResourceVersion, objects, err := getObjectsWithResourceVersion(ctx, proxy, namespace, ownerGraphFilterFunction)
g.Expect(err).ToNot(HaveOccurred())

defer func() {
// Set current resource versions as previous resource versions for the next try.
previousResourceVersions = objectsWithResourceVersion
previousObjects = objects
}()
// This is intentionally failing on the first run.
g.Expect(objectsWithResourceVersion).To(BeComparableTo(previousResourceVersions))
Expand All @@ -48,19 +54,73 @@ func ValidateResourceVersionStable(ctx context.Context, proxy ClusterProxy, name
// Verify resource versions are stable for a while.
byf("Check Resource versions remain stable")
Consistently(func(g Gomega) {
objectsWithResourceVersion, err := getObjectsWithResourceVersion(ctx, proxy, namespace, ownerGraphFilterFunction)
objectsWithResourceVersion, objects, err := getObjectsWithResourceVersion(ctx, proxy, namespace, ownerGraphFilterFunction)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(objectsWithResourceVersion).To(BeComparableTo(previousResourceVersions))
g.Expect(objectsWithResourceVersion).To(BeComparableTo(previousResourceVersions), printObjectDiff(previousObjects, objects))
}, 2*time.Minute, 15*time.Second).Should(Succeed(), "Resource versions didn't stay stable")
}

func getObjectsWithResourceVersion(ctx context.Context, proxy ClusterProxy, namespace string, ownerGraphFilterFunction clusterctlcluster.GetOwnerGraphFilterFunction) (map[string]string, error) {
func printObjectDiff(previousObjects, newObjects map[string]client.Object) func() string {
return func() string {
createdObjects := objectIDs(newObjects).Difference(objectIDs(previousObjects))
deletedObjects := objectIDs(previousObjects).Difference(objectIDs(newObjects))
preservedObjects := objectIDs(previousObjects).Intersection(objectIDs(newObjects))

var output strings.Builder

if len(createdObjects) > 0 {
output.WriteString("\nDetected new objects\n")
for objID := range createdObjects {
obj := newObjects[objID]
resourceYAML, _ := yaml.Marshal(obj)
output.WriteString(fmt.Sprintf("\nNew object %s:\n%s\n", objID, resourceYAML))
}
}
if len(deletedObjects) > 0 {
output.WriteString("\nDetected deleted objects\n")
for objID := range deletedObjects {
obj := previousObjects[objID]
resourceYAML, _ := yaml.Marshal(obj)
output.WriteString(fmt.Sprintf("\nDeleted object %s:\n%s\n", objID, resourceYAML))
}
}

if len(preservedObjects) > 0 {
output.WriteString("\nDetected objects with changed resourceVersion\n")
for objID := range preservedObjects {
previousObj := previousObjects[objID]
newObj := newObjects[objID]

if previousObj.GetResourceVersion() == newObj.GetResourceVersion() {
continue
}

previousResourceYAML, _ := yaml.Marshal(previousObj)
newResourceYAML, _ := yaml.Marshal(newObj)
output.WriteString(fmt.Sprintf("\nObject with changed resourceVersion %s:\n%s\n", objID, cmp.Diff(string(previousResourceYAML), string(newResourceYAML))))
}
}

return output.String()
}
}

func objectIDs(objs map[string]client.Object) sets.Set[string] {
ret := sets.Set[string]{}
for id := range objs {
ret.Insert(id)
}
return ret
}

func getObjectsWithResourceVersion(ctx context.Context, proxy ClusterProxy, namespace string, ownerGraphFilterFunction clusterctlcluster.GetOwnerGraphFilterFunction) (map[string]string, map[string]client.Object, error) {
graph, err := clusterctlcluster.GetOwnerGraph(ctx, namespace, proxy.GetKubeconfigPath(), ownerGraphFilterFunction)
if err != nil {
return nil, err
return nil, nil, err
}

objectsWithResourceVersion := map[string]string{}
objects := map[string]client.Object{}
for _, node := range graph {
nodeNamespacedName := client.ObjectKey{Namespace: node.Object.Namespace, Name: node.Object.Name}
obj := &metav1.PartialObjectMetadata{
Expand All @@ -70,9 +130,10 @@ func getObjectsWithResourceVersion(ctx context.Context, proxy ClusterProxy, name
},
}
if err := proxy.GetClient().Get(ctx, nodeNamespacedName, obj); err != nil {
return nil, err
return nil, nil, err
}
objectsWithResourceVersion[fmt.Sprintf("%s/%s/%s", node.Object.Kind, node.Object.Namespace, node.Object.Name)] = obj.ResourceVersion
objects[fmt.Sprintf("%s/%s/%s", node.Object.Kind, node.Object.Namespace, node.Object.Name)] = obj
}
return objectsWithResourceVersion, nil
return objectsWithResourceVersion, objects, nil
}
Loading