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

Marshal MR structs in a (more) server-side apply compatible way #70

Merged
merged 3 commits into from
Oct 30, 2023

Conversation

negz
Copy link
Member

@negz negz commented Oct 30, 2023

Description of your changes

The composed.From function is intended to let function authors import and use MR structs from providers, e.g.:

cd, _ := composed.From(&v1beta1.Bucket{})

It's currently very broken in main, as demonstrated by this test:

$ go test -v -cover ./resource/composed/
=== RUN   Example
--- PASS: Example (0.00s)
=== RUN   ExampleFrom
--- FAIL: ExampleFrom (0.00s)
got:
metadata:
  creationTimestamp: null
  labels:
    coolness: high
spec:
  forProvider:
    region: us-east-2
  initProvider: {}
status:
  atProvider: {}
want:
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  labels:
    coolness: high
spec:
  forProvider:
    region: us-east-2
FAIL
coverage: 12.3% of statements
FAIL    github.com/crossplane/function-sdk-go/resource/composed 0.015s
FAIL

You can see the bucket:

  • Is missing its apiVersion and kind.
  • Has a null creation timestamp
  • Has empty initProvider and atProvider objects

Crossplane interprets this all as part of the server-side apply fully specified intent. Specifically it will interpret it as:

  • "I want to control the timestamp field, and I want it to be null"
  • "I want to control the initProvider field, and I want it to be an empty object"
  • "I want to control the atProvider field, and I want it to be an empty object".

It's a known issue that you can't just serialize a runtime.Object and have it be valid SSA fully-specified intent (see for example kubernetes-sigs/controller-runtime#347 (comment)). Nonetheless we're trying to do pretty much that.

I believe our MR structs are generally pretty good at making all optional fields pointers with omitempty tags. Unfortunately the exceptions are the top-level structs like atProvider, forProvider, etc. ObjectMeta is also known broken. So, here we use horrible hacks to make them marshal to JSON in a way that is SSA compatible.

I have:

  • Read and followed Crossplane's contribution process.
  • Run make reviewable to ensure this PR is ready for review.

How has this code been tested

Signed-off-by: Nic Cope <nicc@rk0n.org>
This is necessary for functions to be able to return JSON (as
structpb.Struct) that can be used as server side apply fully specified
intent. Without it we include empty structs that are non-nil, even if
they have the omitempty marker.

Signed-off-by: Nic Cope <nicc@rk0n.org>
I don't love the package-scoped state here but this seems like the
nicest API to offer callers.

Signed-off-by: Nic Cope <nicc@rk0n.org>
Comment on lines +85 to +94
// Unfortunately go-json-experiment also has different omitempty semantics
// for integers, in that it will include a zero-value integer with the
// omitempty tag. That will be the case for metadata.generation, so we
// delete it. It's not something a function should ever be setting.
if m, exists := obj["metadata"]; exists {
if mo, ok := m.(map[string]any); ok {
delete(mo, "generation")
}
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feels like we could add a cleanup step to Crossplane then probably

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean do something like this on the Crossplane side just in case? Yeah potentially. Maybe let's wait and see how much of an issue it will be in real life. I thought about addressing it there to start with, but it should only be an issue for Go functions and hopefully they'll all use this SDK...

Copy link
Collaborator

@phisco phisco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we have a better way unfortunately.

@negz negz merged commit 7a3f487 into crossplane:main Oct 30, 2023
8 checks passed
@negz negz deleted the typical branch October 30, 2023 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants