Skip to content

Transformation of Mustache parse results into parameter schema does not condense required properties from independent sections #23

@sectoreleven

Description

@sectoreleven

We are trying to use the FAST CLI as part of an internal toolchain for producing AS3 declarations absent the FAST GUI (we do not allow users direct access to our BIG-IPs). As we have begun writing FAST templates, I have found that if a template attempts to loop over a collection multiple times independently, any elements referenced in both sections get incorrectly duplicated in the relevant 'required' section of the parameter schema. This causes the template validation/load/render process to fail.

Sample failing template (not a full/valid AS3 declaration, but enough to get structure across):

title: basic-https
description: Basic HTTPS
parameters:
    appName: myApp
    poolMembers:
        memberRatio: 1
definitions:
    poolMonitors:
        type: array
        items: 
            type: object
            properties:
                monitorName:
                    type: string
                monitorType:
                    type: string
                monitorSend:
                    type: string
                monitorReceive:
                    type: integer
                monitorInterval:
                    type: integer
                monitorTimeout:
                    type: integer
template: |
    {
        "class": "AS3",
        "persist": false,
        "declaration": {
            "class": "ADC",
            "schemaVersion": "3.21.0",
            "constants": {
                "class": "Constants",
                "timestamp": "{{buildTimeStamp}}",
                "version": "{{buildNumber}}"
            },
            "id": "{{appName}}",
            "remark": "templateVersion:##templateVersion##",
            "{{appName}}": {
                "class": "Tenant",
                "app": {
                    "class": "Application",
                    "template": "generic",
                    "{{appName}}-vs": {
                        "class": "Service_HTTP",
                        "virtualAddresses": [
                            {{virtualServerIP}}
                        ],
                        "pool": "{{appName}}-pool"
                    },
                    {{#poolMonitors}}
                    "{{monitorName}}-monitor" : {
                        "class": "Monitor",
                        "monitorType": "{{monitorType}}",
                        "send": "{{monitorSend}}",
                        "receive": "{{monitorReceive}}",
                        "interval": {{monitorInterval}},
                        "timeout": {{monitorTimeout}}
                    },
                    {{/poolMonitors}}
                    "{{appName}}-pool": {
                        "class": "Pool",
                        "monitors": [
                            {{#poolMonitors}}
                            {
                                "use": "{{monitorName}}-monitor",
                            }
                            {{/poolMonitors}}
                        ],
                        "members": [
                            {{#poolMembers}}
                            {
                                "servicePort": {{memberPort}},
                                "serverAddresses": [
                                    "{{memberIpAddress}}"
                                ]
                            },
                            {{/poolMembers}}
                        ],
                    }
                }
            }
        }
    }

Sample Data:

{
	"poolMonitors": [
		{
			"monitorName": "monitor1",
			"monitorType": "https",
			"monitorSend": "GET /",
			"monitorReceive": "200.OK",
			"monitorInterval": 30,
			"monitorTimeout": 91
		},
		{
			"monitorName": "monitor2",
			"monitorType": "https",
			"monitorSend": "GET /heartbeat",
			"monitorReceive": "App.is.alive",
			"monitorInterval": 30,
			"monitorTimeout": 91
		},
	],
	"buildTimeStamp": "2021-11-17 0800H",
	"buildNumber":  "21.1.0.2525",
	"appName": "mySampleApp",
	"virtualServerIP": "10.0.0.10",
	"poolMembers": [
		{
			"memberIpAddress": "10.0.1.11",
			"memberPort": "443"
		},
		{
			"memberIpAddress": "10.0.1.12",
			"memberPort": "443"
		},
		{
			"memberIpAddress": "10.0.1.15",
			"memberPort": "443"
		}
	]
}

Results:

❯ fast validate .\basic-https.yml
failed to load template
Error: Failed to compile parameter validator
schema:
{
  "type": "object",
  "properties": {
    "poolMonitors": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "monitorName": {
            "type": "string"
          },
          "monitorType": {
            "type": "string"
          },
          "monitorSend": {
            "type": "string"
          },
          "monitorReceive": {
            "type": "integer"
          },
          "monitorInterval": {
            "type": "integer"
          },
          "monitorTimeout": {
            "type": "integer"
          }
        },
        "required": [
          "monitorName",
          "monitorType",
          "monitorSend",
          "monitorReceive",
          "monitorInterval",
          "monitorTimeout",
          "monitorName"
        ]
      },
      "skip_xform": true
    },
    "buildTimeStamp": {
      "type": "string"
    },
    "buildNumber": {
      "type": "string"
    },
    "appName": {
      "type": "string"
    },
    "virtualServerIP": {
      "type": "string"
    },
    "poolMembers": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "memberPort": {
            "type": "string"
          },
          "memberIpAddress": {
            "type": "string"
          }
        },
        "required": [
          "memberPort",
          "memberIpAddress"
        ]
      },
      "skip_xform": true
    }
  },
  "required": [
    "buildTimeStamp",
    "buildNumber",
    "appName",
    "virtualServerIP",
    "poolMonitors",
    "poolMembers"
  ],
  "dependencies": {
    "monitorName": [
      "poolMonitors"
    ],
    "monitorType": [
      "poolMonitors"
    ],
    "monitorSend": [
      "poolMonitors"
    ],
    "monitorReceive": [
      "poolMonitors"
    ],
    "monitorInterval": [
      "poolMonitors"
    ],
    "monitorTimeout": [
      "poolMonitors"
    ],
    "memberPort": [
      "poolMembers"
    ],
    "memberIpAddress": [
      "poolMembers"
    ]
  },
  "title": "basic-https",
  "description": "Basic HTTPS",
  "definitions": {}
}
compile error:
schema is invalid: data.properties['poolMonitors'].items.required should NOT have duplicate items (items ## 6 and 0 are identical), data.properties['poolMonitors'].items should be array, data.properties['poolMonitors'].items should match some schema in anyOf
    at C:\Users\myuser\AppData\Roaming\npm\node_modules\@f5devcentral\f5-fast-core\lib\template.js:682:40

The expectation here is that the items.required array would include 'monitorName' only once in this case.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions