From d304e83aa74bbe6259f495a98f7fbbc1fa671f1a Mon Sep 17 00:00:00 2001 From: twobiers <22715034+twobiers@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:10:39 +0200 Subject: [PATCH] implement helper function for environment variables Signed-off-by: twobiers <22715034+twobiers@users.noreply.github.com> --- README.md | 1 + .../functions/getCompositionEnvVar/README.md | 2 + .../getCompositionEnvVar/composition.yaml | 31 +++++++ .../getCompositionEnvVar/environment.yaml | 6 ++ .../getCompositionEnvVar/functions.yaml | 6 ++ .../functions/getCompositionEnvVar/xr.yaml | 10 +++ function_maps.go | 12 +++ function_maps_test.go | 82 +++++++++++++++++++ 8 files changed, 150 insertions(+) create mode 100644 example/functions/getCompositionEnvVar/README.md create mode 100644 example/functions/getCompositionEnvVar/composition.yaml create mode 100644 example/functions/getCompositionEnvVar/environment.yaml create mode 100644 example/functions/getCompositionEnvVar/functions.yaml create mode 100644 example/functions/getCompositionEnvVar/xr.yaml diff --git a/README.md b/README.md index 7f1ee9a..2ffc095 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ example: | [`getResourceCondition`](example/functions/getResourceCondition) | Helper function to retreive conditions of resources | | [`getComposedResouce`](example/functions/getComposedResource) | Helper function to retrieve observed composed resources | | [`getCompositeResource`](example/functions/getCompositeResource) | Helper function to retreive the observed composite resource | +| [`getCompositionEnvVar`](example/functions/getCompositionEnvVar) | Helper function to retreive an environment variable from the request context | | [`setResourceNameAnnotation`](example/inline) | Returns the special resource-name annotation with given name | | [`include`](example/functions/include) | Outputs template as a string | diff --git a/example/functions/getCompositionEnvVar/README.md b/example/functions/getCompositionEnvVar/README.md new file mode 100644 index 0000000..1d7bc2b --- /dev/null +++ b/example/functions/getCompositionEnvVar/README.md @@ -0,0 +1,2 @@ +# getCompositionEnvVar +The getCompositionEnvVar function is a helper function used to retrieve [environment variables](https://docs.crossplane.io/latest/concepts/environment-configs/) from the request context. Upon successful retrieval, the function returns a string containing the variable. If no variable with the key exists, an empty string is returned. diff --git a/example/functions/getCompositionEnvVar/composition.yaml b/example/functions/getCompositionEnvVar/composition.yaml new file mode 100644 index 0000000..4891dde --- /dev/null +++ b/example/functions/getCompositionEnvVar/composition.yaml @@ -0,0 +1,31 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: example-function-get-composition-env-var +spec: + compositeTypeRef: + apiVersion: example.crossplane.io/v1beta1 + kind: XR + mode: Pipeline + pipeline: + - step: render-templates + functionRef: + name: function-go-templating + input: + apiVersion: gotemplating.fn.crossplane.io/v1beta1 + kind: GoTemplate + source: Inline + inline: + template: | + --- + apiVersion: dbforpostgresql.azure.upbound.io/v1beta1 + kind: FlexibleServer + metadata: + annotations: + {{ setResourceNameAnnotation "flexserver" }} + gotemplating.fn.crossplane.io/ready: "False" + spec: + forProvider: + + # Retrieve AdminLogin from request context + adminLogin: {{ getCompositionEnvVar . "adminLogin" }} \ No newline at end of file diff --git a/example/functions/getCompositionEnvVar/environment.yaml b/example/functions/getCompositionEnvVar/environment.yaml new file mode 100644 index 0000000..6fdeeee --- /dev/null +++ b/example/functions/getCompositionEnvVar/environment.yaml @@ -0,0 +1,6 @@ +apiVersion: apiextensions.crossplane.io/v1alpha1 +kind: EnvironmentConfig +metadata: + name: example-environment +data: + adminLogin: admin \ No newline at end of file diff --git a/example/functions/getCompositionEnvVar/functions.yaml b/example/functions/getCompositionEnvVar/functions.yaml new file mode 100644 index 0000000..6ce853e --- /dev/null +++ b/example/functions/getCompositionEnvVar/functions.yaml @@ -0,0 +1,6 @@ +apiVersion: pkg.crossplane.io/v1beta1 +kind: Function +metadata: + name: function-go-templating +spec: + package: xpkg.upbound.io/crossplane-contrib/function-go-templating:v0.5.0 diff --git a/example/functions/getCompositionEnvVar/xr.yaml b/example/functions/getCompositionEnvVar/xr.yaml new file mode 100644 index 0000000..f5e62b5 --- /dev/null +++ b/example/functions/getCompositionEnvVar/xr.yaml @@ -0,0 +1,10 @@ +apiVersion: example.crossplane.io/v1beta1 +kind: XR +metadata: + name: example +spec: + environment: + environmentConfigs: + - type: Reference + ref: + name: example-environment diff --git a/function_maps.go b/function_maps.go index 4dff6ea..ec82513 100644 --- a/function_maps.go +++ b/function_maps.go @@ -26,6 +26,7 @@ var funcMaps = []template.FuncMap{ "setResourceNameAnnotation": setResourceNameAnnotation, "getComposedResource": getComposedResource, "getCompositeResource": getCompositeResource, + "getCompositionEnvVar": getCompositionEnvVar, }, } @@ -130,3 +131,14 @@ func getCompositeResource(req map[string]any) map[string]any { return cr } + +func getCompositionEnvVar(req map[string]any, name string) (string, error) { + path := fmt.Sprintf("context[\"apiextensions.crossplane.io/environment\"][\"%s\"]", name) + + env, err := fieldpath.Pave(req).GetString(path); + if err != nil { + return "", err + } + + return env, nil +} \ No newline at end of file diff --git a/function_maps_test.go b/function_maps_test.go index 8767755..2af6fb1 100644 --- a/function_maps_test.go +++ b/function_maps_test.go @@ -459,3 +459,85 @@ func Test_getCompositeResource(t *testing.T) { }) } } + +func Test_getCompositionEnvVar(t *testing.T) { + type args struct { + req map[string]any + name string + } + + type want struct { + rsp string + err error + } + + cases := map[string]struct { + reason string + args args + want want + }{ + "RetrieveCompositionEnvVar": { + reason: "Should successfully retrieve a composition env var", + args: args{ + req: map[string]any{ + "context": map[string]any{ + "apiextensions.crossplane.io/environment": map[string]any{ + "test": "abc", + }, + }, + }, + name: "test", + }, + want: want{ + rsp: "abc", + err: nil, + }, + }, + "RetrieveCompositionEnvVarCaseInsensitive": { + reason: "Should successfully retrieve a composition env var case insensitive", + args: args{ + req: map[string]any{ + "context": map[string]any{ + "apiextensions.crossplane.io/environment": map[string]any{ + "TEST": "abc", + }, + }, + }, + name: "TEST", + }, + want: want{ + rsp: "abc", + err: nil, + }, + }, + "NotExistingEnvVar": { + reason: "Should return empty string when env var does not exist", + args: args{ + req: map[string]any{ + "context": map[string]any{ + "apiextensions.crossplane.io/environment": map[string]any{}, + }, + }, + name: "test", + }, + want: want{ + rsp: "", + err: cmpopts.AnyError, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + rsp, err := getCompositionEnvVar(tc.args.req, tc.args.name) + + if diff := cmp.Diff(tc.want.rsp, rsp, protocmp.Transform()); diff != "" { + t.Errorf("%s\ngetCompositionEnvVar(...): -want rsp, +got rsp:\n%s", tc.reason, diff) + } + + if diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != "" { + t.Errorf("%s\ntgetCompositionEnvVar(...): -want err, +got err:\n%s", tc.reason, diff) + } + }) + } +} \ No newline at end of file