crossplane-contrib / function-kcl

Crossplane Composition Functions using KCL Programming Language
Apache License 2.0
25 stars 10 forks source link

Unable to match desired object #112

Closed mproffitt closed 3 weeks ago

mproffitt commented 4 weeks ago

What happened?

I currently have a composition whereby function-patch-and-transform creates a DB Cluster. I then pass this to an instantiation of function-kcl with target: PatchDesired to further patch the cluster with more dynamic properties before handing it back to be created.

This then results in the following condition on the XR:

  Conditions:
    Last Transition Time:  2024-06-09T14:07:16Z
    Message:               cannot compose resources: pipeline step "function-kcl-patch-cluster" returned a fatal result: cannot process xr and state with the pipeline output in *v1beta1.RunFunctionResponse: failed to match all resources, found 0 / 1 patches
    Reason:                ReconcileError
    Status:                False
    Type:                  Synced

It would appear that this is related to the following revert commit but in reality I think both sides of the diff were wrong: https://github.com/crossplane-contrib/function-kcl/commit/1faa3149779b2a6d2a9ff1f3a61c68063196563d

Reading through this, the call resource.Name(d.GetName()) retrieves the metadata.name from the unstructured object and converts that to resouce.Name but this relies on metadata.name being set, and matching exactly the resource name in desired-composed.

This is invalid as the resource name often has no reference to metadata.name which may, or may not be set

A better behaviour was almost there on the left hand side of the diff which relied on the annotation, and if not found, fell back to GVK+Name.

A closer matching would potentially look something like this, allowing the fallback to metadata.name if the AnnotationKeyCompositionResourceName is not set.

func getName(o *unstructured.Unstructured) resource.Name {
    name, found := o.GetAnnotations()[AnnotationKeyCompositionResourceName]
    if !found {
        name = o.GetName()
    }
    return resource.Name(name)
}

This, (I think) would allow the desired resource to be targeted as follows:

_dcds = option("params").dcds
_prePatchCluster = _dcds["rds-cluster"]

_cluster {
    **_prePatchCluster
    **{
        metadata = {
            annotations = {
                "krm.kcl.dev/composition-resource-name" = "rds-cluster"
            }
        }
        spec = {
            forProvider = {
                # dynamic patching
            }
        }
    }
}

A better option might be to have getName return an error if the annotation is not found rather than falling back to unstructured.Unstructured.GetName(), this is how function patch-and-transform appears to handle it

How can we reproduce it?

What environment did it happen in?

Function version: 0.9.0

mproffitt commented 4 weeks ago

I tested the change by building the function myself and for sure it looks like I mis-understood how the PatchDesired target works although it did get to the point of telling me I was wrong which was a definite improvement

Peefy commented 4 weeks ago

Hello, I've added more examples about the PatchDesired target and can this help you?

https://github.com/crossplane-contrib/function-kcl/pull/113

mproffitt commented 4 weeks ago

This actually makes a lot more sense now, thank you.

mproffitt commented 3 weeks ago

Closing as working as expected