App templating

Variables, filter and template “queries” can be used as values for any field in your deployment YAMLs. These variables allow you to reference values in your YAML document or any resource preexisting in your edge project . This functionality adds flexibility and runtime binding in your deployments files.

Variables and Filters

Variables are interpolated (replaced) when the requests are made between applications and nodes . The variable value is a snapshot of the referenced value when the request is made. Any subsequent modification of the underlying value will NOT be persisted.

Darcy’s filtering logic sits on top of the LiquidJS engine. Full documentation for LiquidJS can be found here .

Variables

Defining Variables

Variables are defined in YAML by wrapping the variable with double brackets.

  • Defining a variable: {{variable-name}}
env:
  - key: selfname
    value: "{{ self.name }}"

Assigning Values

Values can be assigned to a new text string.

  • Assigning a value: {% assign agent = "agent-name" | findAgent %}
env:
  - key: rulesengineHOST
    value: '{% raw %}
{%  assign curmsvc= self.microservices | where: "name", "msvc-1" | first %}
{% endraw %}{{ curmsvc | findAgent: agents | map: "host" }}' # get the host where a microservice is running via agent

Filters

Filtering is used to display conditionals or alter the display of the text. Filters can be set as conditionals (where a filter may have an “If” statement).

  • Using a filter: {{"agent-name" | findAgent}}
  • Example: Getting the host value of the agent named zebra-1: {% assign agent = "zebra-1" | findAgent %}{{ agent.host }}

A list of filters accepted can be found in the LiquidJS documentation here .

Constant filters and keywords

This section details filters and values that are pre-defined.

Filters

NameDescriptionUsageReturns
findAgentLookup an existing ioFog Agent, by name. If name is an empty string, all agents are returned.agent-name” | findAgentAn ioFog Agent, as defined by Controller API
findApplicationLookup an existing ioFog Application, by nameapp-name” | findApplicationAn ioFog Applicaiton, as defined by Controller REST API

Values

Self: self is a reserved keyword. It references the current request body.

env:
  - key: selfname
    value: "{{ self.name | upcase }}"

Application YAML Example

---
apiVersion: iofog.org/v2
kind: Application # What are we deploying
metadata:
  name: edai-smartbuilding-rules-engine # Application name

# Specifications of the application
spec:
  # List of microservices composing your application
  microservices:
    - name: rulesengine
      agent:
        name: agent-aismall01
      images:
        x86: nodered/node-red:latest
        arm: nodered/node-red:latest
        registry: remote
      config: { }
      container:
        rootHostAccess: false
        volumes: [ ]
        ports:
          - internal: 1881
            external: 1882
        env:
          - key: selfname
            value: '{{ self.name | upcase }}' # setting the application name to the microservice in uppercase
          - key: sharedToken
            value: 'sekrittoken' # setting secret token once
          - key: http_proxy
            value: 'http://myproxy.fr:8080/' # setting corporate proxy once
          - key: https_proxy
            value: '{{ self.microservices | where: "name", "rulesengine" | first | map: "env" | first | where: "key" , "http_proxy" | first | map: "value" | first }}' # get the https proxy from rulesengine ms and env http_proxy
    - name: ms2
      agent:
        name: agent-aismall01
      images:
        x86: XXX/img1
        arm: XXX/img2
        registry: remote
      config: { }
      container:
        rootHostAccess: false
        volumes: [ ]
        ports:
          - internal: 1883
            external: 1884
        env:
          - key: selfname
            value: '{{ self.name | upcase }}' # setting the application name to the microservice in uppercase
          - key: sharedToken
            value: '{{ self.microservices | where: "name", "rulesengine" | first | map: "env" | first | where: "key", "sharedToken" | first | map: "value" | first }}' # get the sharedToken from rulesengine ms and env sharedToken
          - key: http_proxy
            value: '{{ self.microservices | where: "name", "rulesengine" | first | map: "env" | first | where: "key" , "http_proxy" | first | map: "value" | first }}' # get the http proxy from rulesengine ms and env http_proxy
          - key: https_proxy
            value: '{{ self.microservices | where: "name", "rulesengine" | first | map: "env" | first | where: "key" , "http_proxy" | first | map: "value" | first }}' # get the https proxy from rulesengine ms and env http_proxy
          - key: rulesengineHOST
            value: '{% raw %}
{%  assign curmsvc= self.microservices | where: "name", "msvc-1" | first %}{{ curmsvc | findAgent: agents | map: "host" }}' # get the host where a microservice is running via agent
          - key: rulesenginePORT
            value: '{{ self.microservices | where: "name", "rulesengine" | first | map: "ports" | first | map: "external" | first }}'
          - key: redisHost # get host and port of a mciroservice
            value: '{% assign redisApp = "redis-app" | findApplication %}{% assign redismsvc = redisApp.microservices | where: "name", "redistest" | first %}
{% endraw %}{{ redismsvc | findAgent: agents | map: "host"}}:{{ redismsvc | map: "ports" | first | first |map: "external" | first }}'
          - key: edgeResLiveness # Get edge resource endpoint for a specific version
            value: '{{ "com.orange.smart-door" | findEdgeResource: "0.0.1" | map: "interface" | map: "endpoints" | first  | where: "name", "liveness" | first | map: "url" }}'
          - key: edgeResVersion # Get edge resource endpoint
            value: '{{ "com.orange.smart-door" | findEdgeResource            | map: "interface" | map: "endpoints" | first  | where: "name", "version" | first | map: "url" }}'

With controller API the same configuration looks like:

{
  "name": "edai-smartbuilding-rules-engine",
  "isSystem": false,
  "description": "Description",
  "isActivated": true,
  "microservices": [
    {
      "name": "rulesengine",
      "config": "string",
      "images": [
        {
          "containerImage": "nodered/node-red:latest",
          "fogTypeId": 1
        },
        {
          "containerImage": "nodered/node-red:latest",
          "fogTypeId": 2
        }
      ],
      "registryId": 1,
      "application": "edai-smartbuilding-rules-engine",
      "iofogUuid": "node-id",
      "rootHostAccess": true,
      "logSize": 0,
      "volumeMappings": [],
      "ports": [
        {
          "internal": 1881,
          "external": 1882
        }
      ],
      "routes": [],
      "env": [
        {
          "key": "selfname",
          "value": "{{ self.name | upcase }}"
        },
        {
          "key": "sharedToken",
          "value": "sekrittoken"
        },
        {
          "key": "http_proxy",
          "value": "http://myproxy:8080/"
        },
        {
          "key": "https_proxy",
          "value": "{{ self.microservices | where: \"name\", \"rulesengine\" | first | map: \"env\" | first | where: \"key\" , \"http_proxy\" | first | map: \"value\" | first }}"
        }
      ],
      "cmd": []
    },
    {
      "name": "ms2",
      "config": "string",
      "images": [
        {
          "containerImage": " XXX/img1",
          "fogTypeId": 1
        },
        {
          "containerImage": " XXX/img2",
          "fogTypeId": 2
        }
      ],
      "registryId": 1,
      "application": "edai-smartbuilding-rules-engine",
      "iofogUuid": "node-id",
      "rootHostAccess": true,
      "logSize": 0,
      "volumeMappings": [],
      "ports": [
        {
          "internal": 1883,
          "external": 1884
        }
      ],
      "routes": [],
      "env": [
        {
          "key": "selfname",
          "value": "{{ self.name | upcase }}"
        },
        {
          "key": "sharedToken",
          "value": "{{ self.microservices | where: \"name\", \"rulesengine\" | first | map: \"env\" | first | where: \"key\", \"sharedToken\" | first | map: \"value\" | first }}"
        },
        {
          "key": "http_proxy",
          "value": "{{ self.microservices | where: \"name\", \"rulesengine\" | first | map: \"env\" | first | where: \"key\" , \"http_proxy\" | first | map: \"value\" | first }}"
        },
        {
          "key": "https_proxy",
          "value": "{{ self.microservices | where: \"name\", \"rulesengine\" | first | map: \"env\" | first | where: \"key\" , \"http_proxy\" | first | map: \"value\" | first }}"
        },
        {
          "key": "rulesengineHOST",
          "value": "{% raw %}
        {
          %
        assign
        curmsvc=
        self.microservices
        |
        where
        :
        \
        "name\", \"msvc-1\" | first %}{{ curmsvc | findAgent: agents | map: \"host\" }}"
        },
        {
          "key": "rulesenginePORT",
          "value": "{{ self.microservices | where: \"name\", \"rulesengine\" | first | map: \"ports\" | first | map: \"external\" | first }}"
        },
        {
          "key": "redisHost",
          "value": "{% assign redisApp = \"redis-app\" | findApplication %}{% assign redismsvc = redisApp.microservices | where: \"name\", \"redistest\" | first %}
        {
          %
        endraw
        %
      }
        {
        {
          redismsvc
          |
          findAgent: agents
          |
          map:
          \
          "host\"}}:{{ redismsvc | map: \"ports\" | first | first |map: \"external\" | first }}"
        },
        {
          "key": "edgeResLiveness",
          "value": "{{ \"com.orange.smart-door\" | findEdgeResource: \"0.0.1\" | map: \"interface\" | map: \"endpoints\" | first  | where: \"name\", \"liveness\" | first | map: \"url\" }}"
        },
        {
          "key": "edgeResVersion",
          "value": "{{ \"com.orange.smart-door\" | findEdgeResource            | map: \"interface\" | map: \"endpoints\" | first  | where: \"name\", \"version\" | first | map: \"url\" }}"
        }
      ],
      "cmd": []
    }
  ]
}

Troubleshooting and Caveats

  • The algorithmic operator of liquidjs or variable assignment have the scope on the processing string.
---
....
- key: testaffect
value: "{% raw %}
{% assign ms =self.microservices | where: \"name\", \"rulesengine\" | first %}
{% endraw %}{{
ms.env | where: \"key\" , \"http_proxy\" | first }}"
....
  • Make sure to define the template parametric expressions as string, otherwise the YAML parser will interpret them as Object, and you will encounter multiple type of errors

Incorrect:

---
name: {{my-variable}} # This will error, as name are expected to be strings, and the yaml parser will interpret this as an object

Correct:

---
name: '{{my-variable}}' # This will behave as expected