From 47cb5c5b4bba12627007155937ca23bfc468c0ba Mon Sep 17 00:00:00 2001 From: Alper Rifat Ulucinar Date: Tue, 13 Feb 2024 21:12:04 +0300 Subject: [PATCH] Add config.NewExternalNameFrom that makes the existing ExternalName configurations reusable via compositions. - Format the doc comments for the config.TemplatedStringAsIdentifier function. Signed-off-by: Alper Rifat Ulucinar --- pkg/config/externalname.go | 119 +++++++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 11 deletions(-) diff --git a/pkg/config/externalname.go b/pkg/config/externalname.go index 7b87884a..cbd34b8e 100644 --- a/pkg/config/externalname.go +++ b/pkg/config/externalname.go @@ -70,34 +70,38 @@ func ParameterAsIdentifier(param string) ExternalName { // TemplatedStringAsIdentifier accepts a template as the shape of the Terraform // ID and lets you provide a field path for the argument you're using as external // name. The available variables you can use in the template are as follows: -// parameters: A tree of parameters that you'd normally see in a Terraform HCL // -// file. You can use TF registry documentation of given resource to -// see what's available. +// parameters: A tree of parameters that you'd normally see in a Terraform HCL +// file. You can use TF registry documentation of given resource to +// see what's available. // // setup.configuration: The Terraform configuration object of the provider. You can -// -// take a look at the TF registry provider configuration object -// to see what's available. Not to be confused with ProviderConfig -// custom resource of the Crossplane provider. +// take a look at the TF registry provider configuration object +// to see what's available. Not to be confused with ProviderConfig +// custom resource of the Crossplane provider. // // setup.client_metadata: The Terraform client metadata available for the provider, -// -// such as the AWS account ID for the AWS provider. +// such as the AWS account ID for the AWS provider. // // external_name: The value of external name annotation of the custom resource. -// -// It is required to use this as part of the template. +// It is required to use this as part of the template. // // The following template functions are available: +// // ToLower: Converts the contents of the pipeline to lower-case +// // ToUpper: Converts the contents of the pipeline to upper-case +// // Please note that it's currently *not* possible to use // the template functions on the .external_name template variable. // Example usages: +// // TemplatedStringAsIdentifier("index_name", "/subscriptions/{{ .setup.configuration.subscription }}/{{ .external_name }}") +// // TemplatedStringAsIdentifier("index_name", "/resource/{{ .external_name }}/static") +// // TemplatedStringAsIdentifier("index_name", "{{ .parameters.cluster_id }}:{{ .parameters.node_id }}:{{ .external_name }}") +// // TemplatedStringAsIdentifier("", "arn:aws:network-firewall:{{ .setup.configuration.region }}:{{ .setup.client_metadata.account_id }}:{{ .parameters.type | ToLower }}-rulegroup/{{ .external_name }}") func TemplatedStringAsIdentifier(nameFieldPath, tmpl string) ExternalName { t, err := template.New("getid").Funcs(template.FuncMap{ @@ -205,3 +209,96 @@ func GetExternalNameFromTemplated(tmpl, val string) (string, error) { //nolint:g } return "", errors.Errorf("unhandled case with template %s and value %s", tmpl, val) } + +// ExternalNameFrom is an ExternalName configuration which uses a parent +// configuration as its base and modifies any of the GetIDFn, +// GetExternalNameFn or SetIdentifierArgumentsFn. This enables us to reuse +// the existing ExternalName configurations with modifications in their +// behaviors via compositions. +type ExternalNameFrom struct { + ExternalName + getIDFn func(GetIDFn, context.Context, string, map[string]any, map[string]any) (string, error) + getExternalNameFn func(GetExternalNameFn, map[string]any) (string, error) + setIdentifierArgumentFn func(SetIdentifierArgumentsFn, map[string]any, string) +} + +// ExternalNameFromOption is an option that modifies the behavior of an +// ExternalNameFrom external-name configuration. +type ExternalNameFromOption func(from *ExternalNameFrom) + +// WithGetIDFn sets the GetIDFn for the ExternalNameFrom configuration. +// The function parameter fn receives the parent ExternalName's GetIDFn, and +// implementations may invoke the parent's GetIDFn via this +// parameter. For the description of the rest of the parameters and return +// values, please see the documentation of GetIDFn. +func WithGetIDFn(fn func(fn GetIDFn, ctx context.Context, externalName string, parameters map[string]any, terraformProviderConfig map[string]any) (string, error)) ExternalNameFromOption { + return func(ec *ExternalNameFrom) { + ec.getIDFn = fn + } +} + +// WithGetExternalNameFn sets the GetExternalNameFn for the ExternalNameFrom +// configuration. The function parameter fn receives the parent ExternalName's +// GetExternalNameFn, and implementations may invoke the parent's +// GetExternalNameFn via this parameter. For the description of the rest +// of the parameters and return values, please see the documentation of +// GetExternalNameFn. +func WithGetExternalNameFn(fn func(fn GetExternalNameFn, tfstate map[string]any) (string, error)) ExternalNameFromOption { + return func(ec *ExternalNameFrom) { + ec.getExternalNameFn = fn + } +} + +// WithSetIdentifierArgumentsFn sets the SetIdentifierArgumentsFn for the +// ExternalNameFrom configuration. The function parameter fn receives the +// parent ExternalName's SetIdentifierArgumentsFn, and implementations may +// invoke the parent's SetIdentifierArgumentsFn via this +// parameter. For the description of the rest of the parameters and return +// values, please see the documentation of SetIdentifierArgumentsFn. +func WithSetIdentifierArgumentsFn(fn func(fn SetIdentifierArgumentsFn, base map[string]any, externalName string)) ExternalNameFromOption { + return func(ec *ExternalNameFrom) { + ec.setIdentifierArgumentFn = fn + } +} + +// NewExternalNameFrom initializes a new ExternalNameFrom with the given parent +// and with the given options. An example configuration that uses a +// TemplatedStringAsIdentifier as its parent (base) and sets a default value +// for the external-name if the external-name is yet not populated is as +// follows: +// +// config.NewExternalNameFrom(config.TemplatedStringAsIdentifier("", "{{ .parameters.type }}/{{ .setup.client_metadata.account_id }}/{{ .external_name }}"), +// +// config.WithGetIDFn(func(fn config.GetIDFn, ctx context.Context, externalName string, parameters map[string]any, terraformProviderConfig map[string]any) (string, error) { +// if externalName == "" { +// externalName = "some random string" +// } +// return fn(ctx, externalName, parameters, terraformProviderConfig) +// })) +func NewExternalNameFrom(parent ExternalName, opts ...ExternalNameFromOption) ExternalName { + ec := &ExternalNameFrom{} + for _, o := range opts { + o(ec) + } + + ec.ExternalName.GetIDFn = func(ctx context.Context, externalName string, parameters map[string]any, terraformProviderConfig map[string]any) (string, error) { + if ec.getIDFn == nil { + return parent.GetIDFn(ctx, externalName, parameters, terraformProviderConfig) + } + return ec.getIDFn(parent.GetIDFn, ctx, externalName, parameters, terraformProviderConfig) + } + ec.ExternalName.GetExternalNameFn = func(tfstate map[string]any) (string, error) { + if ec.getExternalNameFn == nil { + return parent.GetExternalNameFn(tfstate) + } + return ec.getExternalNameFn(parent.GetExternalNameFn, tfstate) + } + ec.ExternalName.SetIdentifierArgumentFn = func(base map[string]any, externalName string) { + if ec.setIdentifierArgumentFn == nil { + parent.SetIdentifierArgumentFn(base, externalName) + return + } + ec.setIdentifierArgumentFn(parent.SetIdentifierArgumentFn, base, externalName) + } + return ec.ExternalName +}