Git Product home page Git Product logo

function-patch-and-transform's Introduction

function-patch-and-transform

CI GitHub release (latest SemVer)

This composition function does everything Crossplane's built-in patch & transform (P&T) composition does. Instead of specifying spec.resources in your Composition, you can use this function.

Using this function, P&T looks like this:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: example
spec:
  # Omitted for brevity.
  mode: Pipeline
  pipeline:
  - step: patch-and-transform
    functionRef:
      name: function-patch-and-transform
    input:
      apiVersion: pt.fn.crossplane.io/v1beta1
      kind: Resources
      resources:
      - name: bucket
        base:
          apiVersion: s3.aws.upbound.io/v1beta1
          kind: Bucket
          spec:
            forProvider:
              region: us-east-2
        patches:
        - type: FromCompositeFieldPath
          fromFieldPath: "spec.location"
          toFieldPath: "spec.forProvider.region"
          transforms:
          - type: map
            map: 
              EU: "eu-north-1"
              US: "us-east-2"

Notice that it looks very similar to native P&T. The difference is that everything is under spec.pipeline[0].input.resources, not spec.resources. This is the Function's input.

Okay, but why?

There are a lot of good reasons to use a function to use a function to do P&T composition. In fact, it's so compelling that the Crossplane maintainers are considering deprecating native P&T. See Crossplane issue #4746 for details.

Mix and match P&T with other functions

With this function you can use P&T with other functions. For example you can create a desired resource using the Go Templating function, then patch the result using this function.

To include results from previous functions, simply provide a resources entry for each and specify a name field that matches the name of the resource from the previous function. Also, do not specify any value for the base field of each resource.

It's not just patches either. You can use P&T to derive composite resource connection details from a resource produced by another function, or use it to determine whether a resource produced by another function is ready.

A straightforward example for multistep mix and match pipeline with function-patch-and-transform and function-go-templating can be found here

Decouple P&T development from Crossplane core

When P&T development happens in a function, it's not coupled to the Crossplane release cycle. The maintainers of this function can cut releases more frequently to add new features to P&T.

It also becomes easier to fork. You could fork this function, add a new kind of transform and try it out for a few weeks before sending a PR upstream. Or, if your new feature is controversial, it's now a lot less work to maintain your own fork long term.

Test P&T locally using the Crossplane CLI

You can use the Crossplane CLI to run any function locally and see what composed resources it would create. This only works with functions - not native P&T.

For example, using the files in the example directory:

$ crossplane beta render xr.yaml composition.yaml functions.yaml

Produces the following output, showing what resources Crossplane would compose:

---
apiVersion: example.crossplane.io/v1
kind: XR
metadata:
  name: example-xr
---
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  annotations:
    crossplane.io/composition-resource-name: bucket
  generateName: example-xr-
  labels:
    crossplane.io/composite: example-xr
  ownerReferences:
    # Omitted for brevity
spec:
  forProvider:
    region: us-east-2

See the composition functions documentation to learn how to use crossplane beta render.

Differences from the native implementation

This function has a few small, intentional breaking changes compared to the native implementation.

New Required fields

These fields are now required. This makes P&T configuration less ambiguous:

  • resources[i].name
  • resources[i].connectionDetails[i].name
  • resources[i].connectionDetails[i].type
  • resources[i].patches[i].transforms[i].string.type
  • resources[i].patches[i].transforms[i].math.type

mergeOptions replaced by toFieldPath

Also, the resources[i].patches[i].policy.mergeOptions field is no longer supported. This functionality has been replaced by the resources[i].patches[i].policy.toFieldPath field. The table below outlines previous behavior that was possible with mergeOptions and how to achieve it with the new toFieldPath field:

# mergeOptions appendSlice keepMapValues toFieldPath
1 nil N/A N/A nil which defaults to Replace
2 non-nil nil or false true MergeObjects
3 non-nil true nil or false ForceMergeObjectsAppendArrays
4 non-nil nil or false nil or false ForceMergeObjects
5 non-nil true true MergeObjectsAppendArrays

As an example, a previous configuration using the no longer supported mergeOptions:

policy:
  mergeOptions:
    appendSlice: true
    keepMapValues: true

Should be replaced with:

policy:
  toFieldPath: MergeObjectsAppendArrays

Starting with Crossplane v1.16.0, the convert command in the Crossplane CLI will automatically convert mergeOptions to toFieldPath for you.

Developing this function

This function uses Go, Docker, and the Crossplane CLI to build functions.

# Run code generation - see input/generate.go
$ go generate ./...

# Run tests - see fn_test.go
$ go test ./...

# Build the function's runtime image - see Dockerfile
$ docker build . --tag=runtime

# Build a function package - see package/crossplane.yaml
$ crossplane xpkg build -f package --embed-runtime-image=runtime

function-patch-and-transform's People

Contributors

jbw976 avatar mistermx avatar negz avatar phisco avatar ravilr avatar renovate[bot] avatar stevendborrelli avatar ytsarev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

function-patch-and-transform's Issues

Patch existing resources by matching labels, not resource template name

One neat thing about function-patch-and-transform is that you can patch composed resources that are produced by another Function, earlier in the pipeline. For example here we P&T some resources produced by function-go-templates:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: dynamo-with-bucket
spec:
  compositeTypeRef:
    apiVersion: database.example.com/v1alpha1
    kind: NoSQL
  # This pipeliene renders some Go templates, then passes them to P&T
  pipeline:
  - step: render-go-templates
    functionRef:
      name: function-go-templates
    input: {} # Omitted for brevity :)
  - step: patch-and-transform
    functionRef:
      name: function-patch-and-transform
    input:
      apiVersion: pt.fn.crossplane.io/v1beta1
      kind: Resources
      resources:
        # Notice that my-cool-bucket doesn't have a base template. As long as
        # the render-go-templates step above rendered a composed resource with
        # this name, this Function will patch it.
      - name: my-cool-bucket
        patches:
        - type: FromCompositeFieldPath
          fromFieldPath: "location"
          toFieldPath: "spec.forProvider.region"
          transforms:
          - type: map
            map: 
              EU: "eu-north-1"
              US: "us-east-2"

One shortcoming with this is that you have to know the names (as in composition template names, not object metadata names) of the composed resources some other function produced in order to patch them.

@vfarcic noted that it would be useful to be able to patch resources without knowing their name in advance. Consider for example a Function that produced an arbitrary number of resources, where the number of resources was derived from an XR field. It wouldn't be possible to know what (or more specifically how many) names that Function would produce.

I think this would be pretty easy to implement if we can find a good API to describe it.

Patching from a non-existent 'from' field deletes the 'to' field

Background

I plan to include #81 in the v0.3.0 release of this function.

#81 changes how this function handles patch errors. This includes how the function handles patching from a field path that doesn't exist, which is a kind of patch error.

Most patch errors now cause the function to return a fatal result. A fatal result stops the entire function pipeline. The fatal result will appear as an event associated with the XR. For example a type mismatch like patching from an integer field to an array field would cause a fatal error.

We treat errors that occur due to missing 'from' field paths specially. Each patch can configure whether the 'from' field path is optional (the default) or required. This is because more than any other kind of error, a missing field path is likely to fix itself eventually.

Consider for example a composition that patches from MR A's status to the XR's spec, then patches that XR spec field to MR B's spec. MR B's spec is derived from MR A's status. It's normal for the patches to fail due to a missing field path until the status field is populated from the external system.

Sometimes it's fine for MR B to be created, then have its spec field derived from MR A's status once that status becomes available. You use an optional field path patch for this.

Other times MR B should not be created at all until the data from MR A's status is available. You use a required field path patch for this.

After #81 this function behaves per this table:

Patch Policy Patch From Patch To Result
Required XR New composed resource Emit an event. Don't create the composed resource.
Required XR Existing composed resource Emit an event. Skip the patch.
Required Env New composed resource Emit an event. Don't create the composed resource.
Required Env Existing composed resource Emit an event. Skip the patch.
Required * XR Emit an event. Skip the patch.
Required * Env Emit an event. Skip the patch.
Optional XR New composed resource Skip the patch. Create the composed resource.
Optional XR Existing composed resource Skip the patch.
Optional Env New composed resource Skip the patch. Create the composed resource.
Optional Env Existing composed resource Skip the patch.
Optional * XR Skip the patch.
Optional * Env Skip the patch.

Another way to look at this is:

  • The only purpose of a required 'from' field path is to block creation of the new 'to' resource.
  • If the 'to' resource already exists, we treat a required 'from' field path like optional, but emit a warning.

The Issue

This function computes desired state similarly to native P&T, but applies it differently. Functions use server-side apply (SSA), while native P&T uses client-side apply. One of the benefits of server-side apply is that it supports deleting fields. Our client-side apply implementation does not. This leads to issues like crossplane/crossplane#4162.

This means that when native P&T skips a patch, the field it's patching to just remains how it was.

Because SSA interprets omitting a field from desired state as a desired to delete that field, when this functions skips a patch the field will be deleted.

This means that:

  • If you're patching to a resource that exists (e.g. the XR or an existing composed resource), and...
  • The patch has previously succeeded in patching from the 'from' field path to the 'to' field path, and...
  • The patch begins to fail because the 'to' field path no longer exists

This function will omit the 'to' field path from its desired state, causing it to be deleted.

Example code is broken

example/composition.yaml:
fromFieldPath: "location"

should be (as in README.md)
fromFieldPath: "spec.location"

Premature Kubectl Wait Return For Composite Resource Readiness

When waiting for a composite resource to assume the Ready state, kubectl wait may return prematurely. This could imply that the Ready state is briefly set and then revoked.

Example:
kubectl-v1.27.3 wait vault.sec.upbound.io configuration-vault --for=condition=Ready --timeout 5m

After kubectl wait returned, crossplane beta trace vault.sec.upbound.io configuration-vault shows that the resource is not yet ready.

NAME                                                                       SYNCED   READY   STATUS
Vault/configuration-vault (default)                                        True     False   Waiting: ...resource claim is waiting for composite resource to become Ready
└─ XVault/configuration-vault-mhtj5                                        True     False   Creating: Unready resources: xVaultAuth, xVaultPolicies, and xVaultSecrets
   ├─ XAuth/configuration-vault-mhtj5-g2cbg                                True     False   Creating: Unready resources: generic-endpoint, userpass-auth-backend
   │  ├─ Backend/configuration-vault-auth-backend-userpass                 True     True    Available
   │  └─ Endpoint/configuration-vault-auth-generic-endpoint                True     False   Creating

nested compositions not working

nested compositions not working:

cannot compose resources: cannot update composite resource spec.resourceRefs:
      XNetwork.aws.caas.upbound.io "aws-spoke-02-8htxm-p2lmd" is invalid: spec.parameters:
      Required value
kubectl get xnetwork aws-spoke-02-8htxm-p2lmd -o yaml
apiVersion: aws.caas.upbound.io/v1alpha1
kind: XNetwork
metadata:
  annotations:
    crossplane.io/composition-resource-name: compositeNetworkEKS
  creationTimestamp: "2023-10-01T07:40:35Z"
  finalizers:
  - composite.apiextensions.crossplane.io
  generateName: aws-spoke-02-8htxm-
  generation: 4
  labels:
    crossplane.io/claim-name: aws-spoke-02
    crossplane.io/claim-namespace: default
    crossplane.io/composite: aws-spoke-02-8htxm
  name: aws-spoke-02-8htxm-p2lmd
  ownerReferences:
  - apiVersion: aws.caas.upbound.io/v1alpha1
    blockOwnerDeletion: true
    controller: true
    kind: XCluster
    name: aws-spoke-02-8htxm
    uid: 9c2c8129-0f9e-4adc-bdef-1d729e228705
  resourceVersion: "166117"
  uid: 3137a719-2d79-4ed4-9e90-7bd7a52e4f2e
spec:
  compositionRef:
    name: xnetworks.aws.caas.upbound.io
  compositionRevisionRef:
    name: xnetworks.aws.caas.upbound.io-11e7ac6
  compositionSelector:
    matchLabels:
      type: basic
  compositionUpdatePolicy: Automatic
  parameters:
    deletionPolicy: Delete
    id: aws-spoke-02
    providerConfigName: default
    region: eu-central-1
  writeConnectionSecretToRef:
    name: 3137a719-2d79-4ed4-9e90-7bd7a52e4f2e
    namespace: upbound-system
status:
  conditions:
  - lastTransitionTime: "2023-10-01T07:40:35Z"
    message: 'cannot compose resources: cannot update composite resource spec.resourceRefs:
      XNetwork.aws.caas.upbound.io "aws-spoke-02-8htxm-p2lmd" is invalid: spec.parameters:
      Required value'
    reason: ReconcileError
    status: "False"
    type: Synced

Composite showing ready status when one of the resources cannot render

In porting over platform-ref-aws to P&T, I noticed that the Helm releases were unhealthy, but the overall composite showed healthy:

kubectl get composite 
xeks.aws.platformref.upbound.io/borrelli-function-test-9pq6z-62qdh   True     True    xeks.aws.platformref.upbound.io   70m

This helm ProviderConfig and KubernetesClusterAuth not generating was due to a patch that needed a field Type added to it:

        transforms:
                - type: string
                  string:
                    type: Format # missing
                    fmt: "%s-ekscluster"
  Warning  ComposeResources         4m48s (x43 over 70m)  defined/compositeresourcedefinition.apiextensions.crossplane.io  Pipeline step "patch-and-transform": cannot render FromComposite patches for composed resource "kubernetesClusterAuth": cannot apply the "FromCompositeFieldPath" patch at index 1: transform at index 0 returned error: string transform could not resolve: type  is not supported for string transform type

I believe this composite should have shown that it could not render a resource:

Status:
  Conditions:
    Last Transition Time:  2023-09-30T07:11:06Z
    Reason:                ReconcileSuccess
    Status:                True
    Type:                  Synced
    Last Transition Time:  2023-09-30T08:19:07Z
    Reason:                Available
    Status:                True
    Type:                  Ready

Tags are not being merged as expected

Given a CompositeResourceDefinition with the following property.

tags:
  x-kubernetes-map-type: granular
  additionalProperties:
    type: string
  description: Key-value map of resource tags.
  type: object

And a xr with the following field.

tags:
      my-label: test

And P&T func configured like the following

    - step: patch-and-transform
      functionRef:
        name: function-patch-and-transform
      input:
        apiVersion: pt.fn.crossplane.io/v1beta1
        kind: Resources
        resources:
          - name: my-resource
            base: <redact>
            patches:
              - type: FromCompositeFieldPath
                fromFieldPath: spec.parameters.tags
                toFieldPath: spec.initProvider.tags

The tags are being overwritten where I am expecting/hoping they will be merged.

Please advise.

Confusing error message when string transform is missing type

I had a string transform like this:

            transforms:
              - type: string
                string:
                  fmt: "%s-aws-auth"

In this Function (unlike native P&T) the type field is required, so it should be:

            transforms:
              - type: string
                string:
                  type: Format
                  fmt: "%s-aws-auth"

When it's missing, I get this confusing error:

Warning  ComposeResources         3m12s (x29 over 18m)  defined/compositeresourcedefinition.apiextensions.crossplane.io  Pipeline step "patch-and-transform": cannot render FromComposite patches for composed resource "aws-auth": cannot apply the "FromCompositeFieldPath" patch at index 1: transform at index 0 returned error: string transform could not resolve: type  is not supported for string transform type

Unable to install this function

I wanted to give this a try and am unable to install this function using the following manifest

apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
  name: function-patch-and-transform
spec:
  package: xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.2.1

When I deploy this resources It shows the following failed conditions

apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
  name: function-patch-and-transform
status:
  conditions:
    - lastTransitionTime: '2023-11-05T17:56:48Z'
      message: >-
        cannot unpack package: failed to fetch package digest from remote:
        failed to fetch package descriptor with a GET request after a previous
        HEAD request failure: HEAD
        https://xpkg.crossplane.io/v2/crossplane-contrib/function-patch-and-transform/manifests/v0.2.1:
        unexpected status code 404 Not Found (HEAD responses have no body, use
        GET for details): GET
        https://xpkg.crossplane.io/v2/crossplane-contrib/function-patch-and-transform/manifests/v0.2.1:
        NAME_UNKNOWN: repository name not known to registry; map[]
      reason: UnpackingPackage
      status: 'False'
      type: Installed
spec:
  ignoreCrossplaneConstraints: false
  package: xpkg.crossplane.io/crossplane-contrib/function-patch-and-transform:v0.2.1
  packagePullPolicy: IfNotPresent
  revisionActivationPolicy: Automatic
  revisionHistoryLimit: 1
  runtimeConfigRef:
    apiVersion: pkg.crossplane.io/v1beta1
    kind: DeploymentRuntimeConfig
    name: default
  skipDependencyResolution: false

I wasn't able to find any additional documentation on running this from the crossplane docs or release notes. Am I missing something here?

Reusable Package

Any chance of moving the patching logic to an importable package? I would like to reuse the patching logic in a function that I am working on, but cannot because it is part of the main package. I could work on a PR if this is doable.

keepMapValues policy option doesn't keep existing values.

Composition migrated through the crossplane-migrator . The original composition works.

...
        resources:
          - name: cluster-addons
            base:
              apiVersion: intenthq.com/v1alpha1
              kind: XClusterAddon
              spec:
                compositionSelector:
                  matchLabels:
                    xclusteraddons.intenthq.com/type: main
                deletionPolicy: Delete
                parameters:
                  main:
                    helm:
                      values:
                        crossplane:
                          enabled: true
            patches:
              - type: FromCompositeFieldPath
                fromFieldPath: spec.parameters.clusterAddons.values
                toFieldPath: spec.parameters.main.helm.values
                policy:
                  mergeOptions:
                    keepMapValues: true

and resource with

spec:
  parameters:
    clusterAddons:
      values:
        crossplane:
          helm:
            chart:
              version: 1.14.5

it's rendered as

apiVersion: intenthq.com/v1alpha1
kind: XClusterAddon
...
spec:
  parameters:
    main:
      helm:
        values:
          crossplane:
            helm:
              chart:
                version: 1.14.5

missing the default values created in the composition

Required fromFieldPath patch policy can cause incorrectly ready XRs and unintentional deletion of managed resources

When the Required fromFieldPath patch policy is used a missing field causes the affected resource not to be rendered in the result from the function. Since the affected resource is not rendered crossplane will behave as if it does not exist. If the missing field is a result of updating a composition this will lead to existing managed resources being deleted. This is a change in behaviour from native patch and transform.

This is similar to #59, however that issue deals with genuine errors in the patch, which perhaps should be fatal, while making missing required fields fatal would probably be undesirable (since they may be the result of waiting on a ToCompositeFieldPath patch from the status of another resource.)

A simple test case is attached:
testcase.zip

Using native patch and transform the behaviour is either:
A) if the composite already contains the Required policy when you create the XR then the failing ManagedResource is repeatedly recreated, and the XR does not become ready.
B) if you are updating the composite to add the Required policy then the existing MR remains in place, and the XR becomes not Synced. The external resource is not deleted.

With function-patch-and-transform the behaviour is that the resource is not rendered, the XR is Ready and Synced, and any existing MR (in case B) is deleted. A warning is logged as an event to the XR, but this does not affect the XR status.

Cannot patch with CombineFromEnvironment

I've migrated from a resource composition to a pipeline and have been able to get everything to work except the patching of the annotation, so it defaults to the generated name preventing the resource from being created because it is not valid for the provider

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: storage-pipeline
spec:
  compositeTypeRef:
    apiVersion: xxx.xxx.xxx/v1alpha1
    kind: xStorage
  environment:
    environmentConfigs:
    - type: Reference
      ref:
        name: global
    - type: Reference
      ref:
        name: organization
    - type: Reference
      ref:
        name: subscription
    - type: Reference
      ref:
        name: product
    - type: Reference
      ref:
        name: environment
    patches:
      - type: FromCompositeFieldPath
        fromFieldPath: spec.identifier            
        toFieldPath: data.instance.identifier

  mode: Pipeline
  pipeline:
  - step: create-storage
    functionRef:
      name: function-patch-and-transform
    input:
      apiVersion: pt.fn.crossplane.io/v1beta1
      kind: Resources
      resources:
      - name: storage
        base:
          apiVersion: storage.azure.upbound.io/v1beta1
          kind: Account
              
        patches:

          - type: CombineFromEnvironment
            combine:
              strategy: string
              variables:
              - fromFieldPath: organization.identifier
              - fromFieldPath: subscription.identifier
              - fromFieldPath: environment.code
              - fromFieldPath: location.short
              - fromFieldPath: product.identifier
              - fromFieldPath: data.instance.identifier
              string: 
                fmt: "%s%sst%s%s%s%s"
            toFieldPath: metadata.annotations[crossplane.io/external-name]

          - type: FromEnvironmentFieldPath
            fromFieldPath: storage.accountReplicationType
            toFieldPath: spec.forProvider.accountReplicationType
          - type: FromCompositeFieldPath
            fromFieldPath: spec.accountReplicationType
            toFieldPath: spec.forProvider.accountReplicationType

          - type: FromEnvironmentFieldPath
            fromFieldPath: storage.accountTier
            toFieldPath: spec.forProvider.accountTier
          - type: FromCompositeFieldPath
            fromFieldPath: spec.accountTier
            toFieldPath: spec.forProvider.accountTier

          - type: FromEnvironmentFieldPath
            fromFieldPath: storage.accountKind
            toFieldPath: spec.forProvider.accountKind
          - type: FromCompositeFieldPath
            fromFieldPath: spec.accountKind
            toFieldPath: spec.forProvider.accountKind

          - type: FromEnvironmentFieldPath
            fromFieldPath: storage.allowNestedItemsToBePublic
            toFieldPath: spec.forProvider.allowNestedItemsToBePublic
          - type: FromCompositeFieldPath
            fromFieldPath: spec.allowNestedItemsToBePublic
            toFieldPath: spec.forProvider.allowNestedItemsToBePublic

          - type: FromEnvironmentFieldPath
            fromFieldPath: storage.publicNetworkAccessEnabled
            toFieldPath: spec.forProvider.publicNetworkAccessEnabled
          - type: FromCompositeFieldPath
            fromFieldPath: spec.publicNetworkAccessEnabled
            toFieldPath: spec.forProvider.publicNetworkAccessEnabled

          - type: FromEnvironmentFieldPath
            fromFieldPath: location.long
            toFieldPath: spec.forProvider.location

          - type: FromEnvironmentFieldPath
            fromFieldPath: product.resourceGroupName
            toFieldPath: spec.forProvider.resourceGroupName
        
          - type: ToCompositeFieldPath
            fromFieldPath: status.atProvider.id
            toFieldPath: status.id

Everything else appears to patch correctly. I also tried patching the same thing to "metadata.name" and it also does not patch.

When I run the same from a resource composition it patches as expected.

How to enable DEBUG log level for Composite Function?

Hi there,

In the Crossplane v1.13, it was possible to enable debugging for Composite Functions when enabling them.

helm install crossplane --namespace crossplane-system crossplane-stable/crossplane \
    --create-namespace \
    --set "args='{--debug,--enable-composition-functions}'" \
    --set "xfn.enabled=true" \
    --set "xfn.args='{--debug}'"

https://web.archive.org/web/20230924000034/https://docs.crossplane.io/v1.13/concepts/composition-functions/

This does not seem to apply for Crossplane from v1.14 onwards.

      - args:
          - --enable-composition-revisions
          - --enable-environment-configs
          - --enable-composition-webhook-schema-validation
          - --enable-usages
          - --enable-ssa-claims
          # - --poll-interval=15s
          # - --enable-realtime-compositions
          - --enable-composition-functions=true # default
          - --enable-composition-functions-extra-resources=true # default
          - --debug
      - xfn:
          args:
            - --debug

Is there a way how to enable it again?

We would like to see DEBUG level in the logs.
k logs -n crossplane-system function-patch-and-transform-3160a4debc89-6df6c75d89-s628t -f

Thank you,
Jindra

Issue with ToCompositeFieldPath

Testing [platform-ref-aws], I got the following error with a ToCompositeFieldPath patch:

{"level":"info","ts":1696006454.791182,"caller":"fn/fn.go:170","msg":"Cannot render ToComposite patches for composed resource","xr-version":"aws.platformref.upbound.io/v1alpha1","xr-kind":"XNetwork","xr-name":"borrelli-function-test-hvgm6-s6xm5","resource-template-name":"subnet-private-west-2a","warning":"cannot apply the \"ToCompositeFieldPath\" patch at index 1: Object 'Kind' is missing in '{\"status\":{\"subnetIds\":[\"subnet-07699c2984add28ac\",\"subnet-0e37fb31d56a8f64a\",\"subnet-01302552ad7ffa489\"]}}'"}

Merge patch behavior not compatible with Native P&T

What happened?

Seeing some compatibility issues in merge patch behavior between Native P&T and the latest function-p-and-t:v0.4.0. Looking for guidance on how to make the migration of compositions using native P&T to function-p-and-t seamless.

See repro below. To summarize as per my understanding/observation from below resources:

  1. function-p-and-t don't have an equivalent of policy.mergeOptions.keepMapValues: false. the function-p&t default toFieldPath: Replace will replace the dst with src. the newly added toFieldPath: MergeObject which is equivalent to keepMapValues: true isn't what is needed here. with keepMapValues: false, mergo's overwrite configuration gets enabled in native P&T https://github.com/darccio/mergo/blob/v1.0.0/merge.go#L322, which isn't possible in functions-p-and-t today..

  2. the AppendArray merge option in functions-p-and-t isn't resulting in de-duplication of slice items, resulting in duplicate slice items after merge. whereas in Native P&T, appendSlice: true results in deduplication of slice items, so no duplicate in the destination after merge.. Any idea, why do we see this difference in behavior b/w function and native ?

How can we reproduce it?

  • Apply the below XRD spec and the Composition and Claim resources targeting Native P&T and the same targeting function-p&t.
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-nop
spec:
  package: xpkg.upbound.io/crossplane-contrib/provider-nop:v0.2.1
  ignoreCrossplaneConstraints: true
---
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
  name: function-patch-and-transform
spec:
  ignoreCrossplaneConstraints: false
  package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.4.0
  packagePullPolicy: IfNotPresent
  skipDependencyResolution: true
---
# XRD spec
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
  name: xnopresources.nop.example.org
spec:
  group: nop.example.org
  names:
    kind: XNopResource
    plural: xnopresources
  claimNames:
    kind: NopResource
    plural: nopresources
  connectionSecretKeys:
  - test
  versions:
  - name: v1alpha1
    served: true
    referenceable: true
    schema:
     openAPIV3Schema:
       type: object
       properties:
        spec:
          type: object
          properties:
            coolField:
              type: string
            testConfig:
              type: object
              description: Test Map Configuration
              x-kubernetes-preserve-unknown-fields: true
          required:
          - coolField
---
# Native P&T Composition           
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xnopresources-nativept.nop.example.org
  labels:
    xr-template-source: native-p-and-t
spec:
  compositeTypeRef:
    apiVersion: nop.example.org/v1alpha1
    kind: XNopResource
  resources:
  - name: nop-resource-1
    base:
     apiVersion: nop.crossplane.io/v1alpha1
     kind: NopResource
     spec:
      forProvider:
        conditionAfter:
        - conditionType: Ready
          conditionStatus: "False"
          time: 0s
        - conditionType: Ready
          conditionStatus: "True"
          time: 1s
        fields:
          environment: prod
          eks_version: "1.28"
          region: ""
      
          cilium:
            enabled: true
            mode: hybrid

          flux:
            enabled: true

          identity_provider:
            enabled: true
            groups:
              - 12345678

    patches:
      - fromFieldPath: spec.testConfig
        toFieldPath: spec.forProvider.fields
        policy:
          fromFieldPath: Required
          mergeOptions:
            keepMapValues: false
        type: FromCompositeFieldPath

      - fromFieldPath: spec.testConfig.identity_provider.groups
        toFieldPath: spec.forProvider.fields.identity_provider.groups
        type: FromCompositeFieldPath
        policy:
          mergeOptions:
            appendSlice: true
---
# A Claim targeting native P&T
apiVersion: nop.example.org/v1alpha1
kind: NopResource
metadata:
  namespace: default
  name: lifecycle-upgrade-1
spec:
  compositionSelector:
    matchLabels:
      xr-template-source: native-p-and-t
  coolField: "cool"
  compositeDeletePolicy: Foreground
  testConfig:
    environment: dev
    eks_version: "1.29"
    region: "use1"

    cilium:
      enabled: true
      mode: full

    identity_provider:
      enabled: true
      groups:
        - 12345678
---
# Same Composition converted to use functions P&T pipeline
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xnopresources-xfn.nop.example.org
  labels:
    xr-template-source: function-p-and-t
spec:
  compositeTypeRef:
    apiVersion: nop.example.org/v1alpha1
    kind: XNopResource
  mode: Pipeline
  pipeline:
  - functionRef:
      name: function-patch-and-transform
    input:
      apiVersion: pt.fn.crossplane.io/v1beta1
      environment: null
      kind: Resources
      patchSets: []
      resources:
      - base:
          apiVersion: nop.crossplane.io/v1alpha1
          kind: NopResource
          spec:
            forProvider:
              conditionAfter:
              - conditionStatus: "False"
                conditionType: Ready
                time: 0s
              - conditionStatus: "True"
                conditionType: Ready
                time: 1s
              fields:
                environment: prod
                eks_version: "1.28"
                region: ""
            
                cilium:
                  enabled: true
                  mode: hybrid

                flux:
                  enabled: true

                identity_provider:
                  enabled: true
                  groups:
                    - 12345678              

        name: nop-resource-1
        patches:
        - fromFieldPath: spec.testConfig
          policy:
            fromFieldPath: Required
          toFieldPath: spec.forProvider.fields
          type: FromCompositeFieldPath
        - fromFieldPath: spec.testConfig.identity_provider.groups
          policy:
            toFieldPath: AppendArray
          toFieldPath: spec.forProvider.fields.identity_provider.groups
          type: FromCompositeFieldPath
    step: patch-and-transform
---
# A claim targetting function P&T Composition
apiVersion: nop.example.org/v1alpha1
kind: NopResource
metadata:
  namespace: default
  name: lifecycle-upgrade-2
spec:
  compositionSelector:
    matchLabels:
      xr-template-source: function-p-and-t
  coolField: "cool"
  compositeDeletePolicy: Foreground
  testConfig:
    environment: dev
    eks_version: "1.29"
    region: "use1"

    cilium:
      enabled: true
      mode: full

    identity_provider:
      enabled: true
      groups:
        - 12345678

the rendered desired MR resource by function-P&T isn't the same as the one rendered by Native P&T. the vimdiff in below image:
Screenshot 2024-03-26 at 4 29 12 PM

  • the default Replace toField merge in function-p&t isn't equivalent to Native P&T's keepMapValues: false, the non-overlapping map attributes from destination are lost with Replace.
  • AppendArray in function-p&t doesn't seem to de-duplicate slices on merge, like the Native P&T does.

What environment did it happen in?

Crossplane version: v1.15.1
function-patch-and-transform: v0.4.0

Document release process

Currently there is no documentation about how to cut a new release, we should document it and automate if needed.

Make patch type required

See discussion at #16 (comment).

This would almost certainly make porting to function-patch-and-transform more of a pain for most folks, but it may be worth it to make P&T Compositions more explicit and readable in future.

Patches without type are ignored

It seems the new code added in #55 (specifically RenderEnvironmentPatches() and RenderComposedPatches(), although possibly elsewhere as well) switches on p.Type (which may be empty) instead of p.GetType() (which returns the default if empty). As a result, patches without their type field set are silently ignored.

Missing Environment-related Patch types shown in CRDs

Patches defined at resources.patches are missing CombineToEnvironment,CombineFromEnvironment,ToEnvironmentFieldPath,FromEnvironmentFieldPathtypes in the generated CRD, this is not a blocker as we don't actually validate them, but it's still misleading and could become an issue if we start validating inputs from Crossplane.

We should update the relevant annotation, but probably also split Patches from EnvironmentPatches as we are doing in the upstream implementation, given that the latter support only a subset of types w.r.t. the former.

All environment patch types failing validation

I am attempting to convert some compositions from Resource mode to Pipeline mode, but all of my environment patches are failing validation.

I'm using xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.2.1 and rendering locally with the crossplane cli v1.14.5.

Here is a sample spec containing an environment patch:

spec:
  pipeline:
    - step: patch-and-transform
      functionRef:
        name: function-patch-and-transform
      input:
        apiVersion: pt.fn.crossplane.io/v1beta1
        environment:
          patches:
            - type: FromCompositeFieldPath
              fromFieldPath: foo
              toFieldPath: bar

This function (

func ValidateEnvironment(e *v1beta1.Environment) *field.Error {
) seems like it ought to return nil (no error) in this case, but no matter what patch type (or any string actually...) I specify I always get:

crossplane: error: cannot render composite resource: pipeline step "patch-and-transform" returned a fatal result: invalid Function input: environment.patches[0][type]: Invalid value: "<TYPE>": invalid environment patch type

Function cannot patch a desired.composed resource

I may misunderstood how the function pipeline should work, but I assumed the following composition would create an S3 bucket:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: xbuckets.aws.platformref.upbound.io
spec:
  writeConnectionSecretsToNamespace: crossplane-system
  compositeTypeRef:
    apiVersion: aws.platformref.upbound.io/v1alpha1
    kind: XBucket
  mode: Pipeline
  pipeline:
    - step: create-base
      functionRef:
        name: function-patch-and-transform
      input:
        apiVersion: pt.fn.crossplane.io/v1beta1
        kind: Resources
        resources:
          - name: ezgi-cool-bucket
            base:
              apiVersion: s3.aws.upbound.io/v1beta1
              kind: Bucket
              metadata:
                name: ezgi-cool-bucket
              spec:
                forProvider:
                  tags:
                    Name: ezgi-cool-bucket
    - step: patch-and-transform
      functionRef:
        name: function-patch-and-transform
      input:
        apiVersion: pt.fn.crossplane.io/v1beta1
        kind: Resources
        resources:
          - name: ezgi-cool-bucket
            patches:
              - type: FromCompositeFieldPath
                fromFieldPath: "location"
                toFieldPath: "spec.forProvider.region"
                transforms:
                  - type: map
                    map:
                      EU: "eu-north-1"
                      US: "us-east-2"
    - step: ready
      functionRef:
        name: function-auto-ready

The composition above has two pipeline steps where the first step puts an S3 bucket resource without a required field in the desired state and the second step patches that required field to the resource.

When I create the claim, I get the following error:

  Warning  ComposeResources  54s (x31265 over 115m)  defined/compositeresourcedefinition.apiextensions.crossplane.io  (combined from similar events): cannot compose resources: cannot apply composed resource "ezgi-cool-bucket": Bucket.s3.aws.upbound.io "ezgi-bucket-cqm9c-scgrn" is invalid: spec.forProvider.region: Required value

Environment patches ordering is different from that of Crossplane v1.13

Same as crossplane/crossplane#5050.

functions.yaml
---
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-patch-and-transform
spec:
package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.2.1
composition.yaml
---
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: test-composition
spec:
  compositeTypeRef:
    apiVersion: example.com/v1alpha1
    kind: XTest
  mode: Pipeline
  pipeline:
  - step: patch-and-transform
    functionRef:
      name: function-patch-and-transform
    input:
      apiVersion: pt.fn.crossplane.io/v1beta1
      environment:
        environmentConfigs:
        - ref:
            name: test
          type: Reference
      kind: Resources
      resources:
      - name: test
        base:
          apiVersion: apiextensions.crossplane.io/v1alpha1
          kind: EnvironmentConfig
          metadata:
            name: result
          data:
            value: null
        patches:
        - fromFieldPath: value
          toFieldPath: data.value
          type: FromEnvironmentFieldPath
        - fromFieldPath: spec.testValue
          toFieldPath: data.value
          type: FromCompositeFieldPath
xr.yaml
---
apiVersion: example.com/v1alpha1
kind: XTest
metadata:
  name: test
spec:
  testValue: "From Composite"
$ crossplane beta render xr.yaml composition.yaml functions.yaml --context-values=apiextensions.crossplane.io/environment='{"value": "From environment"}'
---
apiVersion: example.com/v1alpha1
kind: XTest
metadata:
  name: test
---
apiVersion: apiextensions.crossplane.io/v1alpha1
kind: EnvironmentConfig
metadata:
  annotations:
    crossplane.io/composition-resource-name: test
  generateName: test-
  labels:
    crossplane.io/composite: test
  ownerReferences:
  - apiVersion: example.com/v1alpha1
    blockOwnerDeletion: true
    controller: true
    kind: XTest
    name: test
    uid: ""
data:
  value: From environment    # <= this should have been "From Compositite"

MergeObject does not honour the same behaviour as Native P&T

When using a patch such as:

        - type: FromCompositeFieldPath
          fromFieldPath: spec.parameters.tags
          toFieldPath: spec.forProvider.tags
          policy:
            toFieldPath: MergeObject

on a resource that looks like:

      - name: public-subnet
        base:
          apiVersion: ec2.aws.upbound.io/v1beta1
          kind: Subnet
          metadata:
            labels:
              type: subnet
            name: # patched
          spec:
            forProvider:
              availabilityZone: # patched
              cidrBlock: # patched
              mapPublicIpOnLaunch: false
              tags:
                kubernetes.io/role/elb: "1"

I expected this to merge the existing tag (kubernetes.io/role/elb: "1") to be merged with the object on spec.parameters.tags. However, this is not being the case and that static tag is simply lost.

This behaves differently when compared to Native P&T and:

    policy:
      mergeOptions:
        keepMapValues: true

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

dockerfile
Dockerfile
  • docker/dockerfile 1
  • golang 1
github-actions
.github/workflows/ci.yml
  • actions/checkout v4
  • actions/setup-go v5
  • golangci/golangci-lint-action v6
  • actions/checkout v4
  • actions/setup-go v5
  • docker/setup-qemu-action v3
  • docker/setup-buildx-action v3
  • actions/checkout v4
  • docker/build-push-action v5
  • actions/upload-artifact v4
  • actions/checkout v4
  • actions/download-artifact v4
  • docker/login-action v3
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
  • ubuntu 22.04
gomod
go.mod
  • go 1.21
  • go 1.22.3
  • github.com/alecthomas/kong v0.9.0
  • github.com/crossplane/crossplane-runtime v1.15.1
  • github.com/crossplane/function-sdk-go v0.2.0
  • github.com/google/go-cmp v0.6.0
  • github.com/pkg/errors v0.9.1
  • google.golang.org/protobuf v1.34.1
  • k8s.io/api v0.29.3
  • k8s.io/apiextensions-apiserver v0.29.3
  • k8s.io/apimachinery v0.29.3
  • k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0@fe8a2dddb1d0
  • sigs.k8s.io/controller-tools v0.14.0

  • Check this box to trigger a request for Renovate to run again on this repository

wrong patch(field not in schema) can leading to unintentional deletion of managed resource

I've come across a peculiar issue related to the patch and transform function. Here's the scenario:

  • Deployed an XR successfully.
  • All managed resources are in a ready state.
  • Attempted to add a new patch, but encountered an issue.
  • The problem arises when the field I intend to patch is not present in the schema.
  • As a consequence, the operation results in the deletion of the managed resource.
Events:
  Type    Reason                   Age    From                                                                       Message
  ----    ------                   ----   ----                                                                       -------
  Normal  DeletedExternalResource  4m49s  managed/containerservice.azure.upbound.io/v1beta1, kind=kubernetescluster  Successfully requested deletion of external resource

Steps to Reproduce:

  • Deploy an XR.
  • Ensure all managed resources are ready.
  • Attempt to apply a patch to a field not present in the schema.

Are FromEnvironmentFieldPath and CombineFromEnvironment Supported

I'm having a problem where it seems like it is not patching from EnvironmentConfig in function-patch-and-transform.

I have a resources pipeline that basically does the same thing and want to change to use a Pipeline so that I can do some other functions and the issues I am having indicate it is not patching.

  • All of my spec values are pulled from either EnvironmentConfig or patches. Omitting spec.forProvider fails with required fields being missing even though they should be patched with valid values (and this works in a resources composition)
  • The external name doesn't get set

Not sure if in the pipeline the from or to field paths need to be different, it didn't seem like it from what I was reading.

Below is basically what I have:

apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
  name: storage-pipeline
spec:
  compositeTypeRef:
    apiVersion: xxx.xxx.xxx/v1alpha1
    kind: xStorage
  mode: Pipeline
  pipeline:
  - step: create-storage
    functionRef:
      name: function-patch-and-transform
    input:
      apiVersion: pt.fn.crossplane.io/v1beta1
      kind: Resources
      resources:
      - name: storage
        base:
          apiVersion: storage.azure.upbound.io/v1beta1
          kind: Account
          metadata:
            name: storagename
        patches:
        - type: FromEnvironmentFieldPath
          fromFieldPath: "storage.accountReplicationType"
          toFieldPath: spec.forProvider.accountReplicationType
        - type: FromCompositeFieldPath
          fromFieldPath: "spec.accountReplicationType"
          toFieldPath: spec.forProvider.accountReplicationType

        - type: FromEnvironmentFieldPath
          fromFieldPath: "storage.accountTier"
          toFieldPath: spec.forProvider.accountTier
        - type: FromCompositeFieldPath
          fromFieldPath: "spec.accountTier"
          toFieldPath: spec.forProvider.accountTier

        - type: FromEnvironmentFieldPath
          fromFieldPath: "storage.accountKind"
          toFieldPath: spec.forProvider.accountKind
        - type: FromCompositeFieldPath
          fromFieldPath: "spec.accountKind"
          toFieldPath: spec.forProvider.accountKind

        - type: FromEnvironmentFieldPath
          fromFieldPath: "storage.allowNestedItemsToBePublic"
          toFieldPath: spec.forProvider.allowNestedItemsToBePublic
        - type: FromCompositeFieldPath
          fromFieldPath: "spec.allowNestedItemsToBePublic"
          toFieldPath:  spec.forProvider.allowNestedItemsToBePublic

        - type: FromEnvironmentFieldPath
          fromFieldPath: "storage.publicNetworkAccessEnabled"
          toFieldPath: spec.forProvider.publicNetworkAccessEnabled
        - type: FromCompositeFieldPath
          fromFieldPath: "spec.publicNetworkAccessEnabled"
          toFieldPath: spec.forProvider.publicNetworkAccessEnabled

        - type: FromEnvironmentFieldPath
          fromFieldPath: "location.long"
          toFieldPath: spec.forProvider.location

        - type: FromEnvironmentFieldPath
          fromFieldPath: "product.resourceGroupName"
          toFieldPath: spec.forProvider.resourceGroupName
      
        - type: CombineFromEnvironment
          combine:
            strategy: string
            variables:
            - fromFieldPath: "organization.identifier"
            - fromFieldPath: "subscription.identifier"
            - fromFieldPath: "environment.code"
            - fromFieldPath: "location.short"
            - fromFieldPath: "product.identifier"
            - fromFieldPath: "data.instance.identifier"
            string: 
              fmt: "%s%sst%s%s%s%s"
          toFieldPath: "metadata.annotations[crossplane.io/external-name]"

The above works fine in a composition that is Resource mode, just not in Pipeline using function-patch-and-transform.

Just trying to validate that function-patch-and-transform handles EnvironmentConfig patches and if it does, is there something just wrong with the paths that is different in function-patch-and-transform?

Thanks.

Dave

Cannot retrieve data from an environmentConfig

Hey,

I'm openning this issue following a conversation in slack: I converted an existing composition using crossplane-migrator but I'm not able to read the data from the environment config anymore.

My composition is here and I followed @phisco and @haarchri advise and added data. to the fromEnvironmentFielPath block.

              - fromFieldPath: data.privateSubnetIds
                toFieldPath: spec.forProvider.subnetIds
                type: FromEnvironmentFieldPath

But I still get this error

kubectl get subnetgroup.rds.aws.upbound.io/xplane-harbor-vqqnq-rr8hc -o yaml | grep -A 30 ^status:
status:
  atProvider: {}
  conditions:
  - lastTransitionTime: "2023-11-24T15:11:44Z"
    reason: Creating
    status: "False"
    type: Ready
  - lastTransitionTime: "2023-11-24T15:11:44Z"
    reason: ReconcileSuccess
    status: "True"
    type: Synced
  - lastTransitionTime: "2023-11-24T15:31:11Z"
    message: "async create failed: failed to create the resource: [{0 creating RDS
      DB Subnet Group (xplane-harbor-vqqnq-rr8hc): InvalidSubnet: Subnet IDs are required.\n\tstatus
      code: 400, request id: 701186a6-b34a-450d-b3de-f782f4781c07  []}]"
    reason: AsyncCreateFailure
    status: "False"
    type: LastAsyncOperation

Make validation errors useful

Currently an invalid resources array will return an error like this:

$ xrender claim.yaml composition.yaml functions.yaml
xrender: error: main.CLI.Run(): cannot render composite resource: pipeline step "patch-and-transform" returned a fatal result: invalid Function input: resources: Invalid value: v1beta1.ComposedTemplate{Name:"composite-cluster-eks", B
ase:(*runtime.RawExtension)(0x40000cf470), Patches:[]v1beta1.Patch{v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312c80), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312ca0), PatchSetName:(*string)(nil), Trans
forms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312cc0), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312ce0), PatchSetName:(*string)(nil), Tra
nsforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312d20), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312d40), PatchSetName:(*string)(nil), T
ransforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312d60), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312d80), PatchSetName:(*string)(nil),
 Transforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312da0), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312dc0), PatchSetName:(*string)(nil
), Transforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312de0), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312e00), PatchSetName:(*string)(n
il), Transforms:[]v1beta1.Transform{v1beta1.Transform{Type:"string", Math:(*v1beta1.MathTransform)(nil), Map:(*v1beta1.MapTransform)(nil), Match:(*v1beta1.MatchTransform)(nil), String:(*v1beta1.StringTransform)(0x40000cf4a0), Convert
:(*v1beta1.ConvertTransform)(nil)}}, Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312e40), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312e60), PatchSetName:(*string)(nil)
, Transforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312e80), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312ea0), PatchSetName:(*string)(ni
l), Transforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312ec0), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312ee0), PatchSetName:(*string)(
nil), Transforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312f00), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312f20), PatchSetName:(*string
)(nil), Transforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312f40), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312f60), PatchSetName:(*stri
ng)(nil), Transforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312f80), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000312fa0), PatchSetName:(*st
ring)(nil), Transforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(nil)}, v1beta1.Patch{Type:"", FromFieldPath:(*string)(0x4000312fc0), Combine:(*v1beta1.Combine)(nil), ToFieldPath:(*string)(0x4000313000), PatchSetName:(*
string)(nil), Transforms:[]v1beta1.Transform(nil), Policy:(*v1beta1.PatchPolicy)(0x40004901f0)}}, ConnectionDetails:[]v1beta1.ConnectionDetail{v1beta1.ConnectionDetail{Name:"", Type:"", FromConnectionSecretKey:(*string)(0x4000312c60)
, FromFieldPath:(*string)(nil), Value:(*string)(nil)}}, ReadinessChecks:[]v1beta1.ReadinessCheck(nil)}: invalid resource

This is obviously not very helpful. Notably it:

  • Prints the (possibly large) Go object
  • Doesn't actually surface the validation error that it's essentially "wrapping".

This is probably due to my misunderstanding of how to use the k8s.io/apimachinery/pkg/util/validation/field errors I copied over from the c/c native implementation of P&T.

Panic due to nil map assignment

@stevendborrelli observed this crash when running function-patch-and-transform.

kubectl logs -n crossplane-system function-patch-and-transform-2a8093bacea0-7cfdb7774b-lzwzz
{"level":"info","ts":1695993145.073164,"caller":"fn/fn.go:29","msg":"Running Function","tag":""}
panic: assignment to entry in nil map

goroutine 21 [running]:
main.(*Function).RunFunction(0x400051f430, {0x14f0ae0, 0x400015e030}, 0x40001080a0)
        /fn/fn.go:140 +0x1468
github.com/crossplane/function-sdk-go/proto/v1beta1._FunctionRunnerService_RunFunction_Handler({0x10c8120?, 0x400051f430}, {0x14f0ae0, 0x400015e030}, 0x4000350000, 0x0)
        /go/pkg/mod/github.com/crossplane/[email protected]/proto/v1beta1/run_function_grpc.pb.go:104 +0x164
google.golang.org/grpc.(*Server).processUnaryRPC(0x40005661e0, {0x14f5f38, 0x4000503040}, 0x40003f0b40, 0x4000599170, 0x20213f0, 0x0)
        /go/pkg/mod/google.golang.org/[email protected]/server.go:1376 +0xb90
google.golang.org/grpc.(*Server).handleStream(0x40005661e0, {0x14f5f38, 0x4000503040}, 0x40003f0b40, 0x0)
        /go/pkg/mod/google.golang.org/[email protected]/server.go:1753 +0x80c
google.golang.org/grpc.(*Server).serveStreams.func1.1()
        /go/pkg/mod/google.golang.org/[email protected]/server.go:998 +0x84
created by google.golang.org/grpc.(*Server).serveStreams.func1
        /go/pkg/mod/google.golang.org/[email protected]/server.go:996 +0x164

It only seems to happen in a real Crossplane deployment, not using xrender. He mentioned he believes it was related to this resource:

   - name: RDSInstanceSmall
          base:
            apiVersion: rds.aws.upbound.io/v1beta1
            kind: Instance
            spec:
              forProvider:
                region: us-west-2
                dbSubnetGroupNameSelector:
                  matchControllerRef: true
                instanceClass: db.t3.micro
                username: masteruser
                skipFinalSnapshot: true
                publiclyAccessible: false
                dbName: upbound # create custom database within the instance by default
              deletionPolicy: Delete
          patches:
            - fromFieldPath: "metadata.uid"
              toFieldPath: "spec.writeConnectionSecretToRef.name"
              transforms:
              - type: string
                string:
                  type: Format
                  fmt: "%s-sql" #change

Unknown toFieldPathPolicy error when MergeObjects policy is used

Hi! I'm trying to use the new MergeObjects policy option and I get the following error. The same composition without the affected patch works well.

crossplane beta render xencryptionkey-function.yaml ../apis/encryptionkey/aws.yaml functions.yaml
crossplane: error: cannot render composite resource: pipeline step "patch-and-transform" returned a fatal result: invalid Function input: resources[0].patches[1].policy.toFieldPathPolicy: Invalid value: "MergeObjects": unknown toFieldPathPolicy

In this composition resource definition

        resources:
          - base:
              apiVersion: kms.aws.upbound.io/v1beta1
              kind: Key
              spec:
                deletionPolicy: Orphan
                forProvider: {}
                providerConfigRef:
                  name: default
            name: key
            patches:
              - patchSetName: common-fields
                type: PatchSet
              - fromFieldPath: metadata.labels
                policy:
                  toFieldPath: MergeObjects
                toFieldPath: metadata.labels
                type: FromCompositeFieldPath

I'm pretty sure that I've followed the documentation but I don't know why my policy.toFieldPath attribute gets translated into toFieldPathPolicy

Any idea why? am I doing something wrong?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.