hashicorp / terraform-plugin-docs

Generate and validate Terraform plugin/provider documentation.
Mozilla Public License 2.0
224 stars 69 forks source link

Error when resource schema element has a default value with double curly braces #247

Open gramsa49 opened 1 year ago

gramsa49 commented 1 year ago

Take the following code snippet from a Terraform provider:

func resourceWizAutomationRuleServiceNowCreateTicket() *schema.Resource {
        return &schema.Resource{
                Description: "Automation Rules define associations between actions and findings.",
                Schema: map[string]*schema.Schema{
                        "servicenow_summary": {
                                Type:        schema.TypeString,
                                Required:    true,
                                Default:     "Wiz Issue: {{issue.control.name}}",
                                Description: "Ticket summary",
                        },

An error is returned:

Error executing command: unable to generate website: unable to render resource template "resources/automation_rule_servicenow_create_ticket.md.tmpl": 
unable to parse template "---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: \"wiz_automation_rule_servicenow_create_ticket Resource - terraform-provider-wiz\"
subcategory: \"\"
description: |-
  Automation Rules define associations between actions and findings.
---

# wiz_automation_rule_servicenow_create_ticket (Resource)

Automation Rules define associations between actions and findings.

## Example Usage

{{tffile \"examples/resources/wiz_automation_rule_servicenow_create_ticket/resource.tf\"}}

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `description` (String) Description of the automation rule
- `filters` (String) Value should be wrapped in jsonencode() to avoid diff detection. This is required even though the API states it is not required.  Validate is performed by the UI.
- `integration_id` (String) Wiz identifier for the Integration to leverage for this action. Must be resource type integration_aws_sns.
- `name` (String) Name of the automation rule
- `servicenow_description` (String) Ticket description
- `servicenow_summary` (String) Ticket summary
    - Defaults to `Wiz Issue: {{issue.control.name}}`.
- `servicenow_table_name` (String) Table name to which new tickets will be added to, e.g: 'incident'.
    - Defaults to `incident`.
- `trigger_source` (String) Trigger source.
    - Allowed values: 
        - ISSUES
        - CLOUD_EVENTS
        - CONTROL
        - CONFIGURATION_FINDING
- `trigger_type` (List of String) Trigger type.
    - Allowed values: 
        - CREATED
        - UPDATED
        - RESOLVED
        - REOPENED

### Optional

- `enabled` (Boolean) Enabled?
    - Defaults to `true`.
- `project_id` (String) Wiz internal ID for a project.
- `servicenow_attach_evidence_csv` (Boolean) Upload issue evidence CSV as attachment?
    - Defaults to `false`.
- `servicenow_custom_fields` (String) Custom configuration fields as specified in Service Now. Make sure you add the fields that are configured as required in Service Now Project, otherwise ticket creation will fail. Must be valid JSON.

### Read-Only

- `action_id` (String) Wiz internal ID for the action.
- `created_at` (String) The date/time at which the automation rule was created.
- `id` (String) Wiz internal identifier.

": template: resourceTemplate:28: function "issue" not defined

There looks to be an issue when a default value for a resource element contains double curly braces, which is what the tfplugindocs template format uses.

It's worth noting that the provider is configured to render the defaults in the docs:

func init() {
        // Set descriptions to support markdown syntax, this will be used in document generation and the language server.
        schema.DescriptionKind = schema.StringMarkdown

        // Customize the content of descriptions when output. For example you can add defaults on
        // to the exported descriptions if present.
        schema.SchemaDescriptionBuilder = func(s *schema.Schema) string {
                desc := s.Description
                if s.Default != nil {
                        desc += fmt.Sprintf("\n    - Defaults to `%v`.", s.Default)
                }
                if s.ConflictsWith != nil {
                        desc += fmt.Sprintf("\n    - Conflicts with `%v`.", s.ConflictsWith)
                }
                if s.AtLeastOneOf != nil {
                        desc += fmt.Sprintf("\n    - Requires least one of: `%v`.", s.AtLeastOneOf)
                }
                if s.ExactlyOneOf != nil {
                        desc += fmt.Sprintf("\n    - Required exactly one of: `%v`.", s.ExactlyOneOf)
                }

                return strings.TrimSpace(desc)
        }
}
austinvalle commented 1 year ago

Hi @gramsa49 👋🏻 , thanks for submitting the issue and sorry you're running into trouble here.

tfplugindocs makes use of Go template strings which, as you noted, utilize the double curly braces. For your use case you should be able to use a raw string constant with backticks (`) and another pair of double curly braces, like:

{{`{{issue.control.name}}`}}

Full example w/ your use-case:

        return &schema.Resource{
                Description: "Automation Rules define associations between actions and findings.",
                Schema: map[string]*schema.Schema{
                        "servicenow_summary": {
                                Type:        schema.TypeString,
                                Required:    true,
                                Default:     "Wiz Issue:  {{`{{issue.control.name}}`}}",
                                Description: "Ticket summary",
                        },

Let me know if that doesn't solve the issue!

gramsa49 commented 1 year ago

That has the downside that it alters the default value for the schema in the provider. I was able to work around the issue in the call to tfplugindocs as follows:

func init() {
        // Set descriptions to support markdown syntax, this will be used in document generation and the language server.
        schema.DescriptionKind = schema.StringMarkdown

        // Customize the content of descriptions when output. For example you can add defaults on
        // to the exported descriptions if present.
        schema.SchemaDescriptionBuilder = func(s *schema.Schema) string {
                desc := s.Description

                switch v := s.Default.(type) {
                case string:
                        _ = v
                        if s.Default != nil {
                                desc += fmt.Sprintf("\n    - Defaults to `{{`%s`}}`.", s.Default.(string))
                        }
                default:
                        if s.Default != nil {
                                desc += fmt.Sprintf("\n    - Defaults to `%v`.", s.Default)
                        }
                }
                if s.ConflictsWith != nil {
                        desc += fmt.Sprintf("\n    - Conflicts with `%v`.", s.ConflictsWith)
                }
                if s.AtLeastOneOf != nil {
                        desc += fmt.Sprintf("\n    - Requires least one of: `%v`.", s.AtLeastOneOf)
                }
                if s.ExactlyOneOf != nil {
                        desc += fmt.Sprintf("\n    - Required exactly one of: `%v`.", s.ExactlyOneOf)
                }

                return strings.TrimSpace(desc)
        }
}

This takes a similar approach to the one proposed, but changes the handling by terraform-plugin-docs versus changing the default value in the Terraform providr.

austinvalle commented 1 year ago

Ah! I overlooked your example was with Default and not Description, apologies.

Re-opening the issue as we'd need to tweak tfplugindocs escape handling to remedy this.

CarstenLeue commented 1 year ago

running into the same problem for https://github.com/ibm-hyper-protect/terraform-provider-hpcr

Error executing command: unable to generate website: unable to render data source template "data-sources\\encryption_certs.md.tmpl": unable to execute template: template: resourceTemplate:24:114: executing "resourceTemplate" at <.Major>: can't evaluate field Major in type struct { Type string; Name string; Description string; HasExample bool; ExampleFile string; HasImport bool; ImportFile string; ProviderName string; ProviderShortName string; SchemaMarkdown string; RenderedProviderName string }