diff --git a/README.md b/README.md index 410a05e..081a9e3 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,12 @@ For more information, see the example in [recursive](example/recursive). | [`setResourceNameAnnotation`](example/inline) | Returns the special resource-name annotation with given name | | [`include`](example/functions/include) | Outputs template as a string | +## Additional variables + +| Name | Description | +| ---- | ----------------------------------------------------------------------------------------------- | +| `$` | As for Helm, the `$` variable will always point to the root context, thus the function request. | + ## Developing this function This function uses [Go][go], [Docker][docker], and the [Crossplane CLI][cli] to diff --git a/fn.go b/fn.go index 6c2ccce..5c98ddf 100644 --- a/fn.go +++ b/fn.go @@ -49,6 +49,8 @@ const ( annotationKeyReady = "gotemplating.fn.crossplane.io/ready" metaApiVersion = "meta.gotemplating.fn.crossplane.io/v1alpha1" + + itemDollar = "$" ) // RunFunction runs the Function. @@ -82,6 +84,7 @@ func (f *Function) RunFunction(_ context.Context, req *fnv1beta1.RunFunctionRequ response.Fatal(rsp, errors.Wrap(err, "cannot convert request to map")) return rsp, nil } + reqMap[itemDollar] = &reqMap f.log.Debug("constructed request map", "request", reqMap) diff --git a/fn_test.go b/fn_test.go index 96a16e5..cf4bb98 100644 --- a/fn_test.go +++ b/fn_test.go @@ -23,6 +23,7 @@ import ( var ( cd = `{"apiVersion":"example.org/v1","kind":"CD","metadata":{"annotations":{"gotemplating.fn.crossplane.io/composition-resource-name":"cool-cd"},"name":"cool-cd"}}` cdTmpl = `{"apiVersion":"example.org/v1","kind":"CD","metadata":{"annotations":{"gotemplating.fn.crossplane.io/composition-resource-name":"cool-cd"},"name":"cool-cd","labels":{"belongsTo":{{.observed.composite.resource.metadata.name|quote}}}}}` + cdTmplWithDollar = `{"apiVersion":"example.org/v1","kind":"CD","metadata":{"annotations":{"gotemplating.fn.crossplane.io/composition-resource-name":"cool-cd"},"name":"cool-cd","labels":{"belongsTo":{{ $.observed.composite.resource.metadata.name|quote}}}}}` cdWrongTmpl = `{"apiVersion":"example.org/v1","kind":"CD","metadata":{"name":"cool-cd","labels":{"belongsTo":{{.invalid-key}}}}}` cdMissingKind = `{"apiVersion":"example.org/v1"}` cdMissingResourceName = `{"apiVersion":"example.org/v1","kind":"CD","metadata":{"name":"cool-cd"}}` @@ -316,6 +317,44 @@ func TestRunFunction(t *testing.T) { }, }, }, + "ResponseIsReturnedWithTemplatingFromGlobalContext": { + reason: "The Function should be able to template by accessing the global context from the dollar variable.", + args: args{ + req: &fnv1beta1.RunFunctionRequest{ + Meta: &fnv1beta1.RequestMeta{Tag: "templates"}, + Input: resource.MustStructObject( + &v1beta1.GoTemplate{ + Source: v1beta1.InlineSource, + Inline: &v1beta1.TemplateSourceInline{Template: cdTmplWithDollar}, + }), + Observed: &fnv1beta1.State{ + Composite: &fnv1beta1.Resource{ + Resource: resource.MustStructJSON(xr), + }, + }, + Desired: &fnv1beta1.State{ + Composite: &fnv1beta1.Resource{ + Resource: resource.MustStructJSON(xr), + }, + }, + }, + }, + want: want{ + rsp: &fnv1beta1.RunFunctionResponse{ + Meta: &fnv1beta1.ResponseMeta{Tag: "templates", Ttl: durationpb.New(response.DefaultTTL)}, + Desired: &fnv1beta1.State{ + Composite: &fnv1beta1.Resource{ + Resource: resource.MustStructJSON(xr), + }, + Resources: map[string]*fnv1beta1.Resource{ + "cool-cd": { + Resource: resource.MustStructJSON(`{"apiVersion": "example.org/v1","kind":"CD","metadata":{"annotations":{},"name":"cool-cd","labels":{"belongsTo":"cool-xr"}}}`), + }, + }, + }, + }, + }, + }, "UpdateDesiredCompositeStatus": { reason: "The Function should update the desired composite resource status.", args: args{