diff --git a/example/custom-delims/composition.yaml b/example/custom-delims/composition.yaml new file mode 100644 index 0000000..c60dd55 --- /dev/null +++ b/example/custom-delims/composition.yaml @@ -0,0 +1,69 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + name: example-inline +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 + delims: + left: '[[' + right: ']]' + source: Inline + inline: + template: | + [[- range $i := until ( .observed.composite.resource.spec.count | int ) ]] + --- + apiVersion: iam.aws.upbound.io/v1beta1 + kind: User + metadata: + annotations: + gotemplating.fn.crossplane.io/composition-resource-name: test-user-[[ $i ]] + labels: + testing.upbound.io/example-name: test-user-[[ $i ]] + [[ if eq $.observed.resources nil ]] + dummy: [[ randomChoice "foo" "bar" "baz" ]] + [[ else ]] + dummy: [[ ( index $.observed.resources ( print "test-user-" $i ) ).resource.metadata.labels.dummy ]] + [[ end ]] + spec: + forProvider: {} + --- + apiVersion: iam.aws.upbound.io/v1beta1 + kind: AccessKey + metadata: + annotations: + gotemplating.fn.crossplane.io/composition-resource-name: sample-access-key-[[ $i ]] + spec: + forProvider: + userSelector: + matchLabels: + testing.upbound.io/example-name: test-user-[[ $i ]] + writeConnectionSecretToRef: + name: sample-access-key-secret-[[ $i ]] + namespace: crossplane-system + [[- end ]] + --- + apiVersion: meta.gotemplating.fn.crossplane.io/v1alpha1 + kind: CompositeConnectionDetails + [[ if eq $.observed.resources nil ]] + data: {} + [[ else ]] + data: + username: [[ ( index $.observed.resources "sample-access-key-0" ).connectionDetails.username ]] + password: [[ ( index $.observed.resources "sample-access-key-0" ).connectionDetails.password ]] + url: [[ "http://www.example.com" | b64enc ]] + [[ end ]] + --- + apiVersion: example.crossplane.io/v1beta1 + kind: XR + status: + dummy: cool-status diff --git a/example/custom-delims/functions.yaml b/example/custom-delims/functions.yaml new file mode 100644 index 0000000..4247bb8 --- /dev/null +++ b/example/custom-delims/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.0.0-20231101231317-cdb49945da4e \ No newline at end of file diff --git a/example/custom-delims/xr.yaml b/example/custom-delims/xr.yaml new file mode 100644 index 0000000..27cae0a --- /dev/null +++ b/example/custom-delims/xr.yaml @@ -0,0 +1,6 @@ +apiVersion: example.crossplane.io/v1beta1 +kind: XR +metadata: + name: example +spec: + count: 2 diff --git a/fn.go b/fn.go index e5a8afb..31d2f33 100644 --- a/fn.go +++ b/fn.go @@ -57,7 +57,7 @@ func (f *Function) RunFunction(_ context.Context, req *fnv1beta1.RunFunctionRequ return rsp, nil } - tmpl, err := GetNewTemplateWithFunctionMaps().Parse(tg.GetTemplates()) + tmpl, err := GetNewTemplateWithFunctionMaps(in.Delims).Parse(tg.GetTemplates()) if err != nil { response.Fatal(rsp, errors.Wrap(err, "invalid function input: cannot parse the provided templates")) return rsp, nil diff --git a/function_maps.go b/function_maps.go index 06401c9..2533880 100644 --- a/function_maps.go +++ b/function_maps.go @@ -8,7 +8,7 @@ import ( "gopkg.in/yaml.v3" sprig "github.com/Masterminds/sprig/v3" - + "github.com/crossplane-contrib/function-go-templating/input/v1beta1" xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" "github.com/crossplane/crossplane-runtime/pkg/fieldpath" ) @@ -48,9 +48,15 @@ var funcMaps = []template.FuncMap{ }, } -func GetNewTemplateWithFunctionMaps() *template.Template { +func GetNewTemplateWithFunctionMaps(delims *v1beta1.Delims) *template.Template { tpl := template.New("manifests") + if delims != nil { + if delims.Left != nil && delims.Right != nil { + tpl = tpl.Delims(*delims.Left, *delims.Right) + } + } + for _, f := range funcMaps { tpl.Funcs(f) } diff --git a/input/v1beta1/input.go b/input/v1beta1/input.go index 853d26d..77f88fa 100644 --- a/input/v1beta1/input.go +++ b/input/v1beta1/input.go @@ -18,7 +18,9 @@ import ( type GoTemplate struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - + // Template delimiters + // +optional + Delims *Delims `json:"delims,omitempty"` // Source specifies the different types of input sources that can be used with this function Source TemplateSource `json:"source"` // Inline is the inline form input of the templates @@ -44,3 +46,14 @@ type TemplateSourceInline struct { type TemplateSourceFileSystem struct { DirPath string `json:"dirPath,omitempty"` } + +type Delims struct { + // Template start characters + // +kubebuilder:default:="{{" + // +optional + Left *string `json:"left,omitempty"` + // Template end characters + // +kubebuilder:default:="}}" + // +optional + Right *string `json:"right,omitempty"` +} diff --git a/input/v1beta1/zz_generated.deepcopy.go b/input/v1beta1/zz_generated.deepcopy.go index 40947fc..443d5cc 100644 --- a/input/v1beta1/zz_generated.deepcopy.go +++ b/input/v1beta1/zz_generated.deepcopy.go @@ -8,11 +8,41 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Delims) DeepCopyInto(out *Delims) { + *out = *in + if in.Left != nil { + in, out := &in.Left, &out.Left + *out = new(string) + **out = **in + } + if in.Right != nil { + in, out := &in.Right, &out.Right + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Delims. +func (in *Delims) DeepCopy() *Delims { + if in == nil { + return nil + } + out := new(Delims) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GoTemplate) DeepCopyInto(out *GoTemplate) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + if in.Delims != nil { + in, out := &in.Delims, &out.Delims + *out = new(Delims) + (*in).DeepCopyInto(*out) + } if in.Inline != nil { in, out := &in.Inline, &out.Inline *out = new(TemplateSourceInline) diff --git a/package/input/gotemplating.fn.crossplane.io_gotemplates.yaml b/package/input/gotemplating.fn.crossplane.io_gotemplates.yaml index 7e449c8..28abe44 100644 --- a/package/input/gotemplating.fn.crossplane.io_gotemplates.yaml +++ b/package/input/gotemplating.fn.crossplane.io_gotemplates.yaml @@ -26,6 +26,18 @@ spec: of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string + delims: + description: Template delimiters + properties: + left: + default: '{{' + description: Template start characters + type: string + right: + default: '}}' + description: Template end characters + type: string + type: object fileSystem: description: FileSystem is the folder path where the templates are located properties: