crossplane / function-sdk-go Goto Github PK
View Code? Open in Web Editor NEWThe Go SDK for composition functions
Home Page: https://crossplane.io
License: Apache License 2.0
The Go SDK for composition functions
Home Page: https://crossplane.io
License: Apache License 2.0
resource/composite/composite.go
uses GetNumber()
, which was deprecated and removed from Crossplane runtime In crossplane/crossplane-runtime@11e9468. This causes downstream functions to break if they update their go.mod.
I've updated the crossplane-runtime v1.16 deps for the function-kcl, but it builds failed in CI. See the PR crossplane-contrib/function-kcl#105 and the CI pipeline https://github.com/crossplane-contrib/function-kcl/actions/runs/9106600367/job/25034062200?pr=105
In the function-kcl repo https://github.com/crossplane-contrib/function-kcl and bump the crossplane-runtime to v1.16, then build the code.
Crossplane version: v1.16
CI environment: https://github.com/crossplane-contrib/function-kcl/blob/main/.github/workflows/ci.yaml
This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.
These updates are currently rate-limited. Click on a checkbox below to force their creation now.
These updates have all been created already. Click a checkbox below to force a retry/rebase of any.
k8s.io/api
, k8s.io/apimachinery
).github/workflows/backport.yml
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
zeebe-io/backport-action v2.3.0@addffea45a2f0b5682f1d5ba0506f45bc18bf174
ubuntu 22.04
.github/workflows/ci.yml
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
actions/setup-go v5@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
actions/cache v4@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
actions/cache v4@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
fkirc/skip-duplicate-actions v5.3.1@f75f66ce1886f00957d99748a42c724f4330bdcf
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
actions/setup-go v5@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
actions/cache v4@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
actions/cache v4@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
golangci/golangci-lint-action v3@3a919529898de77ec3da873e3063ca4b10e7f5cc
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
actions/setup-go v5@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
actions/cache v4@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
actions/cache v4@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
github/codeql-action v3@47b3d888fe66b639e431abf22ebca059152f1eea
github/codeql-action v3@47b3d888fe66b639e431abf22ebca059152f1eea
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
aquasecurity/trivy-action 0.16.1@d43c1f16c00cfd3978dde6c07f4bbcf9eb6993ca
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
actions/setup-go v5@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
actions/cache v4@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
actions/cache v4@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9
codecov/codecov-action v4@84508663e988701840491b86de86b666e8a86bed
ubuntu 22.04
ubuntu 22.04
ubuntu 22.04
ubuntu 22.04
ubuntu 22.04
ubuntu 22.04
.github/workflows/commands.yml
xt0rted/slash-command-action v2@bf51f8f5f4ea3d58abc7eca58f77104182b23e88
actions/github-script v7@60a0d83039c74a4aee543508d2ffcb1c3799cdea
xt0rted/slash-command-action v2@bf51f8f5f4ea3d58abc7eca58f77104182b23e88
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
zeebe-io/backport-action v2.3.0@addffea45a2f0b5682f1d5ba0506f45bc18bf174
ubuntu 22.04
ubuntu 22.04
.github/workflows/promote.yml
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
docker/login-action v3@343f7c4344506bcbf9b4de18042ae17996df046d
ubuntu 22.04
.github/workflows/tag.yml
actions/checkout v4@a5ac7e51b41094c92402da3b24376905380afc29
negz/create-tag v1@39bae1e0932567a58c20dea5a1a0d18358503320
ubuntu 22.04
go.mod
go 1.21
github.com/bufbuild/buf v1.30.0
github.com/crossplane/crossplane-runtime v1.16.0
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0@2e55bd4e08b0
github.com/go-logr/logr v1.4.1
github.com/go-logr/zapr v1.3.0
github.com/google/go-cmp v0.6.0
github.com/pkg/errors v0.9.1
github.com/upbound/provider-aws v0.47.1
go.uber.org/zap v1.27.0
google.golang.org/grpc v1.62.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
google.golang.org/protobuf v1.33.0
k8s.io/api v0.29.1
k8s.io/apimachinery v0.29.1
k8s.io/utils v0.0.0-20240102154912-e7106e64919e@e7106e64919e
sigs.k8s.io/yaml v1.4.0
.github/workflows/ci.yml
golang 1.21.6
.github/workflows/ci.yml
golangci/golangci-lint 1.55.2
Makefile
golangci/golangci-lint 1.55.2
It would be neat if we could automatically wrap Functions with some basic observability (e.g. metrics, tracing). Ideally we'll need a format that could be consistent across Functions built in other in-demand languages - e.g. Python.
There's a bunch of stuff in https://github.com/grpc-ecosystem - perhaps this SDK should start a gRPC server that uses one of the interceptors there to add at least basic Prometheus-style metrics?
Crossplane v1.16 added support for passing credentials to secrets - crossplane/crossplane#5543.
This SDK should update its protobufs (at least) and potentially add helper utils to get those credentials.
There is a struct Resource
containing field Resource
. The doc says it is the json representation of the given resource. Could we rename the field to reflect that?
I think gRPC will propagate any deadline set by the client to the context.Context
our RunFunction
method is passed. It's unclear to me whether we have to check manually whether the deadline was exceeded before we run the Function, or whether gRPC will take care of that for us.
https://grpc.io/docs/what-is-grpc/core-concepts/#deadlines
Determine whether we need to be concerned with cancelled deadlines, and if so perhaps add a RunFunction wrapper that handles them (i.e. by avoiding doing work we don't need to do).
https://pkg.go.dev/github.com/crossplane/[email protected]/pkg/errors
https://pkg.go.dev/github.com/crossplane/[email protected]/pkg/logging
You can think of crossplane-runtime as a "Go SDK" of sorts for parts of Crossplane - i.e. core and providers. We have our own error and logging libraries there. Should Functions use those libraries? Use copies/aliases from this SDK? Use the stdlib or something else?
We should figure out what we want to do here. I can see a case for sticking closer to the stdlib here given that now Go can do the core things our libraries were originally created to do for us (i.e. wrap errors, and emit structured logs).
Some thoughts:
When trying to set a composed desired resource like
func GenerateS3SesConfig(desired *resource.DesiredComposed, oxr *resource.Composite){
desired.Resource.Object = map[string]interface{}{
"apiVersion": "s3.aws.upbound.io/v1beta1",
"kind": "BucketServerSideEncryptionConfiguration",
"metadata": map[string]interface{}{
"name": oxr.Resource.GetClaimReference().Name,
"namespace": oxr.Resource.GetClaimReference().Namespace,
},
"spec": map[string]interface{}{
"deletionPolicy": "Retain",
"forProvider": map[string]interface{}{
"region": "us-east-2",
"bucketSelector": map[string]interface{}{
"matchControllerRef": true,
},
"rule": []map[string]interface{}{
{
"applyServerSideEncryptionByDefault": []map[string]interface{}{
{
"sseAlgorithm": "AES256",
},
},
},
},
},
"providerConfigRef": map[string]interface{}{
"name": "aws-provider",
},
},
}
}
The following error occurs when running crossplane beta render
crossplane: error: cannot render composite resource: pipeline step "run-example-function" returned a fatal result: cannot set desired composed resources in *v1beta1.RunFunctionResponse: cannot create new Struct from *composed.Unstructured: proto: invalid type: []map[string]interface {}
When changing the code to
desired.Resource.SetValue("spec.rule", []map[string]interface{}{
{
"applyServerSideEncryptionByDefault": []map[string]interface{}{
{
"sseAlgorithm": "AES256",
},
},
},
})
It works with no errors.
Crossplane version:
require (
github.com/alecthomas/kong v0.8.1
github.com/crossplane/crossplane-runtime v1.14.0-rc.1
github.com/crossplane/function-sdk-go v0.0.0-20231027134439-0745c2a72577
github.com/google/go-cmp v0.6.0
google.golang.org/protobuf v1.31.0
k8s.io/apimachinery v0.28.3
sigs.k8s.io/controller-tools v0.13.0
)
Setting an annotation or label via SetValue
using a label or annotation key with a "." is set incorrectly ie: prometheus.io.metrics
It will be set as
metadata:
labels:
prometheus:
io:
metrics: /metrics
It should result in
metadata:
labels:
prometheus.io.metrics: /metrics
This seems to be more of an issue with the SetValue
in fieldpath
Pave objects.
I understand that you can use SetAnnotations
or SetLabels
but this requires either merging the existing annotations together, which would often take longer than setting the fields individually, or requires knowing the full annotations or labels at the start. either way it would be nice to use SetValue this way as well. If it is not intended to be used this way, perhaps a comment suggesting alternatives would be good
using this composed_test
file
package composed
import (
"fmt"
"testing"
"sigs.k8s.io/yaml"
)
func TestSetValue(t *testing.T) {
cd := New()
cd.SetAPIVersion("example.org/v1")
cd.SetKind("CoolComposedResource")
cd.SetValue("metadata.labels.prometheus.io.metrics", "/metrics")
cd.SetValue("metadata.labels.\"prometheus.io.url\"", "https://exampleprom.io")
cd.SetValue("metadata.annotations.prometheus.io.metrics", "/metrics")
cd.SetValue("metadata.annotations.\"prometheus.io.url\"", "https://exampleprom.io")
y, _ := yaml.Marshal(cd)
fmt.Println(string(y))
}
you will see the following output
$ go test -v ./...
=== RUN TestSetValue
apiVersion: example.org/v1
kind: CoolComposedResource
metadata:
annotations:
prometheus:
io:
metrics: /metrics
url: https://exampleprom.io
labels:
prometheus:
io:
metrics: /metrics
url: https://exampleprom.io
--- PASS: TestSetValue (0.00s)
PASS
ok github.com/crossplane/function-sdk-go/resource/composed 0.226s
Crossplane version: Latest version
The Go SDK uses the composite.Unstructured
and composed.Unstructured
types from crossplane-runtime pretty heavily. These are light wrappers around the Kubernetes unstructured.Unstructured
type (which is itself pretty much a wrapper around map[string]any
). The result is an object with getters and setters for common Kubernetes object metadata (e.g. GetName()
) as well as common Crossplane metadata (e.g. GetClaimReference()
.
These getters and setters don't allow folks to access the arbitrary data their APIs can support - for example if you create an XR that has spec.widgets
you can't get/set that field without reaching into the underlying map[string]any
.
We have another wrapper, fieldpath.Paved
, in crossplane-runtime. This allows field path based getters and setters, like SetInt("spec.widgets", 8)
. I think it would be handy to automatically expose such methods.
We've been using the composition functions basically since day one. When starting with them, we had lengthy discussions how to handle the objects.
We decided to use and import the already available go structs that most providers have (provider-kubernetes, provider-helm, etc.). This makes working with the objects a lot more comfortable, as the compiler will help with type checking.
While there's a composed.From(rutnime.Object)
function there's no function for the other way round e.g. composed.To()
witch could marshal the composed resources directly into a typed object.
Additionally, it might make sense to change composed.From(rutnime.Object)
to composed.From(resource.Managed)
to avoid adding a plain k8s object to the desired state. We've had the issues before that someone added plain k8s objects without wrapping them in a objects.kubernetes.crossplane.io
first. Which can lead to very annoying to debug bugs.
Also, it might be worth adding functions to response
to directly add objects to the desired map.
For example:
response.AddDesiredComposedResource(resp RunFuntionResponse, o resource.Managed, name string)
Which would do the marshalling and add (or override) the given object in the desired composed objects map.
IMHO these changes would greatly improve the developer experience of the SDK. It also helps to avoid errors like here: #68
I've already implemented such typed functions in our framework: https://github.com/vshn/appcat/blob/change/xplane114/pkg/comp-functions/runtime/function_mgr.go#L282
And it's working great so far, it's already a lot better than the previous runtime.RawExtension
in the FunctionIO.
It already does, these are just suggestions to make it even better. :)
I am building a function based on this SDK
(0.2.0). The functions runs regularly into a panic. This happens somewhere between 1 minutes and 30 minutes after start. The 'other' behaviour of the function is ok.
This is the beginning of the panic.
{"level":"info","ts":1718046487.051641,"caller":"fn/fn.go:41","msg":"Running function","tag":""}
{"level":"info","ts":1718046487.051857,"caller":"fn/fn.go:41","msg":"Running function","tag":""}
fatal error: concurrent map read and map write
goroutine 2882 [running]:
k8s.io/apimachinery/pkg/runtime.(*Scheme).AddKnownTypeWithName(0xc000250000, {{0x0, 0x0}, {0x1a185cf, 0x2}, {0x16df98b, 0x6}}, {0x1cf7bf0, 0xc00044a640})
/go/pkg/mod/k8s.io/[email protected]/pkg/runtime/scheme.go:170 +0x1d2
k8s.io/apimachinery/pkg/runtime.(*Scheme).AddKnownTypes(0xc000250000, {{0x0?, 0x10?}, {0x1a185cf?, 0xc000617380?}}, {0xc000617580?, 0x5?, 0x1a222b8?})
/go/pkg/mod/k8s.io/[email protected]/pkg/runtime/scheme.go:148 +0x165
k8s.io/apimachinery/pkg/runtime.(*Scheme).AddUnversionedTypes(0xc000250000, {{0x0?, 0x16?}, {0x1a185cf?, 0xa?}}, {0xc000617580, 0x5, 0x5?})
/go/pkg/mod/k8s.io/[email protected]/pkg/runtime/scheme.go:124 +0x9c
k8s.io/apimachinery/pkg/apis/meta/v1.AddToGroupVersion(0xc000250000, {{0x1a33ca7?, 0xc000617620?}, {0x1a1f4ca?, 0x6?}})
/go/pkg/mod/k8s.io/[email protected]/pkg/apis/meta/v1/register.go:75 +0x345
github.com/coopnorge/provider-github/apis/repo/v1alpha1.init.0.(*Builder).Register.func1(0xc000250000)
/go/pkg/mod/sigs.k8s.io/[email protected]/pkg/scheme/scheme.go:72 +0x5a
k8s.io/apimachinery/pkg/runtime.(*SchemeBuilder).AddToScheme(...)
/go/pkg/mod/k8s.io/[email protected]/pkg/runtime/scheme_builder.go:29
sigs.k8s.io/controller-runtime/pkg/scheme.(*Builder).AddToScheme(0x1877e20?, 0xc000250000)
/go/pkg/mod/sigs.k8s.io/[email protected]/pkg/scheme/scheme.go:86 +0x56
main.(*Function).RunFunction(0xc0001fef50, {0x1824660?, 0xc0008139a0?}, 0xc0000d2600)
/fn/fn.go:79 +0x42f
github.com/crossplane/function-sdk-go/proto/v1beta1._FunctionRunnerService_RunFunction_Handler({0x1824660, 0xc0001fef50}, {0x1d0a6c0, 0xc00096f680}, 0xc0004e2300, 0x0)
/go/pkg/mod/github.com/crossplane/[email protected]/proto/v1beta1/run_function_grpc.pb.go:104 +0x1a6
google.golang.org/grpc.(*Server).processUnaryRPC(0xc0000ef800, {0x1d0a6c0, 0xc00096f5c0}, {0x1d12620, 0xc000686340}, 0xc00056f8c0, 0xc00043ca80, 0x2a3acc0, 0x0)
/go/pkg/mod/google.golang.org/[email protected]/server.go:1385 +0xdd1
google.golang.org/grpc.(*Server).handleStream(0xc0000ef800, {0x1d12620, 0xc000686340}, 0xc00056f8c0)
/go/pkg/mod/google.golang.org/[email protected]/server.go:1796 +0xfb8
google.golang.org/grpc.(*Server).serveStreams.func2.1()
/go/pkg/mod/google.golang.org/[email protected]/server.go:1029 +0x8b
created by google.golang.org/grpc.(*Server).serveStreams.func2 in goroutine 11
/go/pkg/mod/google.golang.org/[email protected]/server.go:1040 +0x125
goroutine 1 [IO wait, 9 minutes]:
internal/poll.runtime_pollWait(0x7f941cd43e70, 0x72)
/usr/local/go/src/runtime/netpoll.go:345 +0x85
internal/poll.(*pollDesc).wait(0x3?, 0x4271e5?, 0x0)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
/usr/local/go/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Accept(0xc0005f6180)
/usr/local/go/src/internal/poll/fd_unix.go:611 +0x2ac
net.(*netFD).accept(0xc0005f6180)
/usr/local/go/src/net/fd_unix.go:172 +0x29
net.(*TCPListener).accept(0xc000430280)
/usr/local/go/src/net/tcpsock_posix.go:159 +0x1e
net.(*TCPListener).Accept(0xc000430280)
/usr/local/go/src/net/tcpsock.go:327 +0x30
google.golang.org/grpc.(*Server).Serve(0xc0000ef800, {0x1d01778, 0xc000430280})
/go/pkg/mod/google.golang.org/[email protected]/server.go:885 +0x469
github.com/crossplane/function-sdk-go.Serve({0x1cf78a8, 0xc0001fef50}, {0xc000439420, 0x3, 0xc0004dfa40?})
/go/pkg/mod/github.com/crossplane/[email protected]/sdk.go:143 +0x225
main.(*CLI).Run(0xc0004c0540)
/fn/main.go:27 +0x196
reflect.Value.call({0x17f6940?, 0xc0004c0540?, 0x419f45?}, {0x1a192d6, 0x4}, {0xc00050fb90, 0x0, 0x16d53b9?})
/usr/local/go/src/reflect/value.go:596 +0xca6
reflect.Value.Call({0x17f6940?, 0xc0004c0540?, 0x182c5e0?}, {0xc00050fb90?, 0xc00050fbd0?, 0x411b9b?})
/usr/local/go/src/reflect/value.go:380 +0xb9
github.com/alecthomas/kong.callFunction({0x17f6940?, 0xc0004c0540?, 0xc00050fd88?}, 0xc0004df9b0)
/go/pkg/mod/github.com/alecthomas/[email protected]/callbacks.go:98 +0x45a
github.com/alecthomas/kong.(*Context).RunNode(0xc0004e2500, 0xc0002bf680, {0x0, 0x0, 0x3?})
/go/pkg/mod/github.com/alecthomas/[email protected]/context.go:774 +0x80e
github.com/alecthomas/kong.(*Context).Run(0xc0004e2500, {0x0, 0x0, 0x0})
/go/pkg/mod/github.com/alecthomas/[email protected]/context.go:793 +0xfd
main.main()
/fn/main.go:35 +0xce
a bit further in the panic
goroutine 2809 [runnable]:
k8s.io/apimachinery/pkg/apis/meta/v1.AddToGroupVersion(0xc000250000, {{0x1a33ca7?, 0xc00053d620?}, {0x1a1f4ca?, 0x6?}})
/go/pkg/mod/k8s.io/[email protected]/pkg/apis/meta/v1/register.go:79 +0x22d
github.com/coopnorge/provider-github/apis/repo/v1alpha1.init.4.(*Builder).Register.func1(0xc000250000)
/go/pkg/mod/sigs.k8s.io/[email protected]/pkg/scheme/scheme.go:72 +0x5a
k8s.io/apimachinery/pkg/runtime.(*SchemeBuilder).AddToScheme(...)
/go/pkg/mod/k8s.io/[email protected]/pkg/runtime/scheme_builder.go:29
sigs.k8s.io/controller-runtime/pkg/scheme.(*Builder).AddToScheme(0x1877e20?, 0xc000250000)
/go/pkg/mod/sigs.k8s.io/[email protected]/pkg/scheme/scheme.go:86 +0x56
main.(*Function).RunFunction(0xc0001fef50, {0x1824660?, 0xc00085e9a0?}, 0xc000144480)
/fn/fn.go:79 +0x42f
github.com/crossplane/function-sdk-go/proto/v1beta1._FunctionRunnerService_RunFunction_Handler({0x1824660, 0xc0001fef50}, {0x1d0a6c0, 0xc000764240}, 0xc00037a200, 0x0)
/go/pkg/mod/github.com/crossplane/[email protected]/proto/v1beta1/run_function_grpc.pb.go:104 +0x1a6
google.golang.org/grpc.(*Server).processUnaryRPC(0xc0000ef800, {0x1d0a6c0, 0xc000764180}, {0x1d12620, 0xc000686340}, 0xc0004057a0, 0xc00043ca80, 0x2a3acc0, 0x0)
/go/pkg/mod/google.golang.org/[email protected]/server.go:1385 +0xdd1
google.golang.org/grpc.(*Server).handleStream(0xc0000ef800, {0x1d12620, 0xc000686340}, 0xc0004057a0)
/go/pkg/mod/google.golang.org/[email protected]/server.go:1796 +0xfb8
google.golang.org/grpc.(*Server).serveStreams.func2.1()
/go/pkg/mod/google.golang.org/[email protected]/server.go:1029 +0x8b
created by google.golang.org/grpc.(*Server).serveStreams.func2 in goroutine 11
/go/pkg/mod/google.golang.org/[email protected]/server.go:1040 +0x125
On line 79
in fn.go
the AddToScheme
happens:
err = repo.AddToScheme(composed.Scheme)
if err != nil {
response.Fatal(rsp, errors.Wrapf(err, "Unable to add v1alpha1/repo to scheme %T", req))
return rsp, nil
}
And repo
is imported as
repo "github.com/coopnorge/provider-github/apis/repo/v1alpha1"
I am not sure where to go from here.
I dont know, help is welcome.
Crossplane version: 1.15.3
Env: GKE 1.27
I would like for the NewLogger()
function to have a couple more options to initialize logger for the functions as per need:
Update NewLogger()
function to take in options to handle encoding time and setting a different caller skip.
Proposed changes made: #145
Logger for functions defined here is setup to proxy logger defined in crossplane-runtime here, which only allows logging Info
and Debug
level logs as function logs. As RunFunction
is only expected to append errors as Results
to RunFunctionResponse
and that it never returns an error even when we add a fatal result to the RunFunctionResponse
,
outside of looking at XRs status we don't have a means to log those errors on the function.
The solution suggested would involve setting up a FnLogger
interface with Error()
added and an implementation to invoke logger's Error() method to log errors.
Please find the proposed changes here.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.