Skip to content

Add "+kubebuilder:validation:MaxItems" marker for validating embedded structs in CRD schemas #10

@ulucinar

Description

@ulucinar

What problem are you facing?

While working on #4, we have realized that Terraform represents embedded blocks always with Sets/Lists of length of most 1 (if optional, length is 0 or 1, if required, it will be always of length 1). An example is the policy_settings block in azurerm_web_application_firewall_policy. In fact, this is an optional configuration block nested in azurerm_web_application_firewall_policy block. We would normally want to represents it with a CRD schema as follows:

type WebApplicationFirewallPolicyParameters struct {
	// +kubebuilder:validation:Optional
	PolicySettings *PolicySettingsParameters `json:"policySettings,omitempty" tf:"policy_settings,omitempty"`
        ...

However, it looks like (probably due to HCL semantics), Terraform provider instead represents this as a list of length at most 1:

			"policy_settings": {
				Type:     pluginsdk.TypeList,
				Optional: true,
				MaxItems: 1,

Upjet CRD generation pipeline also does not allow directly embedding a pluginsdk.Resource directly under another pluginsdk.Resource as expected.

Thus, Terrajet currently generates the following instead of the above fragment:

type WebApplicationFirewallPolicyParameters struct {
	// +kubebuilder:validation:Optional
	PolicySettings []PolicySettingsParameters `json:"policySettings,omitempty" tf:"policy_settings,omitempty"`
        ...

Since this is a generated API that reflects the underlying Terraform resource API, I believe this is okay. But we have some room for improvement for such nested blocks (structs) by asserting a max length of 1 with kubebuilder markers.

How could Terrajet help solve your problem?

We can use the Terraform resource schema to observe the pluginsdk.Schema.MaxItems to place the appropriate kubebuilder marker on the generated field. This will provide a better UX around the generated Crossplane provider API. The resulting generated code could be something like the following for the above field:

type WebApplicationFirewallPolicyParameters struct {
	// +kubebuilder:validation:Optional
        // +kubebuilder:validation:MaxItems=1
	PolicySettings []PolicySettingsParameters `json:"policySettings,omitempty" tf:"policy_settings,omitempty"`
        ...

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions