kudobuilder/kuttl

Kuttl should be able to match resource on label if metadata.name is not provided

Closed this issue · 4 comments

jtucci commented

What would you like to be added:
I would like to be able to match a resource based on labels when no name is provided.

For example, the below resource will have an autogenerated uuid attached to the the name making it impossible to select. I want kuttl to fallback to selecting the resource based on labels. In this case, it would find a ResourceGroup with the label resourceGroupDev and then make the assertion.

apiVersion: azure.upbound.io/v1beta1
kind: ResourceGroup
metadata:
  labels:
    name: "resourceGroupDev"
spec:
  forProvider:
    location: westeurope

Why is this needed:
As of now, the only way to match a specific resource is by metadata.name, however many k8s resources use autogenerated names, for example, pods created by replica sets. Because of this, there is no way to match a specific pod and Kuttl will often select the wrong one which results in a failure.

This would be a great feature to add!

Running into the same, would definitely make things easier to work with.

Looking at existing issues, #121 seems to suggest this is already supported. But it sounds like reality is that this is only the case if a single resource of given kind exists in the namespace?

Looking at existing issues, #121 seems to suggest this is already supported. But it sounds like reality is that this is only the case if a single resource of given kind exists in the namespace?

The current list function which is called when no name is specified is not selecting on labels at all.

func list(cl client.Client, gvk schema.GroupVersionKind, namespace string) ([]unstructured.Unstructured, error) {
	list := unstructured.UnstructuredList{}
	list.SetGroupVersionKind(gvk)

	listOptions := []client.ListOption{}
	if namespace != "" {
		listOptions = append(listOptions, client.InNamespace(namespace))
	}

	if err := cl.List(context.TODO(), &list, listOptions...); err != nil {
		return []unstructured.Unstructured{}, err
	}

	return list.Items, nil
}

When CheckResource is comparing actual vs expected, it is taking the label values into consideration. However, without actually selecting on labels you end up only seeing the diff of whatever the last object it tried to compare it against with the same gvk. for example if the following resources exist in the cluster:

apiVersion: azure.upbound.io/v1beta1
kind: ResourceGroup
metadata:
  labels:
    name: "resourceGroup1"
spec:
  forProvider:
    key: val1 
    location: westeurope
 ---
 apiVersion: azure.upbound.io/v1beta1
kind: ResourceGroup
metadata:
  labels:
    name: "resourceGroup2"
spec:
  forProvider:
    key: val2 
    location: westeurope 
---
 apiVersion: azure.upbound.io/v1beta1
kind: ResourceGroup
metadata:
  labels:
    name: "resourceGroup3"
spec:
  forProvider:
    key: val3
    location: westeurope       

If I want to assert against an expected value

apiVersion: azure.upbound.io/v1beta1
kind: ResourceGroup
metadata:
  labels:
    name: "resourceGroup1"
spec:
  forProvider:
    key: valx
    location: westeurope

It will loop over actuals (All 3 resource group objects) and attempt to compare against the expected object. In reality, we only want to compare it against the resource with the same label resourceGroup1. If no object matches, the only error returned is from the last comparison which would be the following resource.

 apiVersion: azure.upbound.io/v1beta1
kind: ResourceGroup
metadata:
  labels:
    name: "resourceGroup3"
spec:
  forProvider:
    key: val3
    location: westeurope       

imo, this leads to pretty confusing results.

if we use label selectors for the list function, its only going to be comparing against the resource which has matching labels, providing a more accurate error message as to why the resource comparison failed.