Skip to content

Declarative resource definitions #399

Description

@runleveldev

Create an endpoint at /api/v1/import which accepts template and context. The template should be in the shape:

inputs:
  name:
    display: Name
    type: Number|string|<any api resource, ie. Container or Site>|<any validator.js>
resources:
  name:
    type: <any api resource>
    values:
      key1: value
      key2: {{inputs.name}}
  name2:
    type: <another api resource>
    values:
      ...

As a practical example, an adiministrator could bootstrap a cluster using the following template:

inputs:
  siteName:
    display: "Site Name"
    type: string
  internalDomain:
    display: "Internal Domain"
    type: domainname  # verify valid domain name
  externalDomain:
    display: "External Domain"
    type: domainename
  dhcpRangeStart:
    display: "DHCP Start"
    type: ipAddress
  dhcpRangeEnd:
    display: "DHCP End"
    type: ipAddress
  subnetMask:
    display: "Subnet Mask"
    type: ipAddress
  gateway:
    display: "Gateway"
    type: ipAddress
  dnsForwarders:
    display: "DNS Forwarders"
    type: string  # takes multple IPs so can't use ipAddress
  externalIp:
    display: "External IP"
    type: ipAddress
  apiUrl:
    display: "API Url"
    type: url
  apiPassword:
    display: "API Password"
    type: string
resources:
  site:
    type: Site
    values:
      name: {{inputs.name}}
      internalDomain: {{inputs.internalDomain}}
      dhcpRange: "{{inputs.dhcpRangeStart}},{{inputs.dhcpRangeEnd}}"
      subnetMask: {{inputs.subnetMask}}
      gateway: {{inputs.gateway}}
      dnsForwarders: {{inputs.dnsForwarders}}
      externalIP: {{inputs.externalIP}}
  external-domain:
    type: ExternalDomain
    values:
      name: {{inputs.externalDomain}}
  proxmox:
    type: ProxmoxImport  # this isn't a DB type, but it is an API function
    values:
      # this demonstrates getting values from another step
      # which would require late-binding support.
      siteId: {{site.id}}
      apiUrl: {{inputs.apiUrl}}
      username: root@pam
      password: {{inputs.apiPassword}}
  manager-container:
    type: Container
    values:
      # This assumes the manager is the first container, which is fine for
      # demonstration. This id value should update the container rather than throwing a
      # conflict due to conflicting ID values
      id: {{proxmox.importedContainers[0].id}}
      services:
        - type: http
          internalPort: 3000
          externalHostname: {{proxmox.importedContainers[0].hostname}}  
          externalDomainId: {{external-domain.id}}

The API route would take a JSON object of the shape: {"template":"...yaml...","context":{"key":"value"}} and the context object is what will be used to render the inputs for the YAML template.

Additional requirements:

  1. The /api/v1/import route should not do any processing with the resources past rendering the template. Queue a background job like with creating containers and return it in the API response for polling.
  2. Neither the background job nor the /import route should do any processing with the resources directly. The background job should resolve the template into the proper API calls via the OpenAPI specification and call those endpoints to do any processing after rendering the templates.
  3. The shape of the YAML given above should be illustrative only, not hard requirement. The {{ sigils are modeled off of handlebarsjs, but that is not a strict requirement. As long as the inputs and resources top-level keys are there, you have freedom on the exact syntax and implementation.
  4. With the current shape, it may be difficult to infer POST to create a new resource or PUT to update an existing resource. It is a requirement that the template application use create-or-update semantics. The template should specify resources in enough detail that either POST (to create) or PUT (to update) the resource could succeed. If it isn't the job should error. If an existing resource is found to need an update on something that can't be changed (such as a container template) I would want to delete then re-create that resource, but that might not always be possible, so throwing an error in that case is also acceptable. Perhaps a force: true or similar could be added alongside type and values in the resource spec to force re-creation.
  5. The API requests the background job makes should run as the user that ran the import. This will give access control "for free"

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions