Skip to content
This repository has been archived by the owner on Feb 3, 2022. It is now read-only.

Templates

Ryan Kuba edited this page Jun 29, 2018 · 1 revision

Taisun Taisun

http://taisun.io

A Taisun stack template is a single YAML file that will render as a web form for a normal user to launch a Docker Compose command with their variables swapped into the compose file.

A Docker compose file can be a single container or a series of containers that make up a complex application deployment, more here:

https://docs.docker.com/compose/compose-file/

File Format

The file is broken into 3 main sections:

  • name/description- Here you can define the listed name of the application and a Markdown description of the application.
  • form- This is used to define the inputs you want to capture from the user.
  • compose- This is the actual compose file that will be executed on the users machine.

Name/Description

Example:

name: Hello World
description: |
  An h1 header
  ============
  
  Paragraphs are separated by a blank line.
  
  2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
  look like:
  
    * this one
    * that one
    * the other one

Here we can see we are using the Pipe symbol in YAML to define a multi line string that is our markdown description. When the Template is loaded in the Taisun application it will be rendered and presented to the user above the input form. IE for this example:

Form

The form section of the template file accepts the following input types:

  • input- Basic text box used for single string input.
  • select- Dropdown with predefined options for the user.
  • textarea- A large text box used to capture arrays of values from the user with line breaks as the delimiter.
  • checkbox- Simple on off switch for the user, presents true/false for templating.
  • hidden- If you need no user input, this will present nothing in the form area.

We recommend including an input type labeled as "name" so the stackname in the web interface is shown as this versus a randomly generated guid.

Form input will be grabbed and passed to the compose template based on the "label" string that it is set to.

Input

Example:

  - type: input
    label: name
    FormName: Name
    placeholder: Unique Name to identify

Every one of these variables is required and will produce the following output:

If you have a requirement to pre-fill in the form you can also pass a value:

  - type: input
    label: name
    FormName: Name
    placeholder: Unique Name to identify
    value: myname

Producing:

Select

Example:

  - type: select
    label: myvar
    FormName: My Variable
    options:
      - option1
      - option2

Here the user will be able to choose two hard coded values:

TextArea

Example:

  - type: textarea
    label: ports
    FormName: Application Ports
    placeholder: To enter multiple use line breaks (enter) Format <hostport>:<containerport>

As stated earlier the textarea form type is useful when you need to capture an unknown amount of variables. The input of this form will always be presented to the compose rendering as an Array, so even if only one value is passed by the user you will still need to "for loop" it in order to add the value to your compose file.

The example above will yield:

CheckBox

Example:

  - type: checkbox
    label: test
    FormName: Test?

Yielding:

The checkbox form type will return either:

  • true
  • false

This can be used in basic if statements for Compose file rendering logic.

Form Validation

The simplelest way to implement form validation is to simply pass the required: true in the form section of your template. IE:

  - type: input
    label: name
    FormName: Name
    placeholder: Unique Name to identify
    required: true

If the user fails to fill out this field they will be prompted to do so.

If you wish to do more advanced form checking you can do so in the form of regexp sequences. These validations work in a pair of values:

  • validation- the regexp string
  • errormessage- the text that will be displayed to the user if it does not pass your regexp.

Any regexp string will be valid here so you can customize it to your needs, here are some common examples:

A port binding entry for a docker container
  - type: textarea
    label: ports
    FormName: Ports
    placeholder: To enter multiple use line breaks (enter) Format hostport:containerport
    validation: ^([1-9]|[1-8][0-9]|9[0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|[1-8][0-9]{3}|9[0-8][0-9]{2}|99[0-8][0-9]|999[0-9]|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])+(:([1-9]|[1-8][0-9]|9[0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|[1-8][0-9]{3}|9[0-8][0-9]{2}|99[0-8][0-9]|999[0-9]|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))$
    errormessage: Values must be in the format hostport:containerport 1-65535
A volume binding for a docker container
  - type: textarea
    label: volumes
    FormName: Volumes
    placeholder: To enter multiple use line breaks (enter) Format /hostfolder:/containerfolder
    validation: ^([\/A-Za-z0-9._~()'!*:@,;+?-]+:[\/A-Za-z0-9._~()'!*:@,;+?-]+)*$
    errormessage: Values must be in the format /hostfolder:/containerfolder
Environment variable validation
  - type: textarea
    label: envars
    FormName: Environment Variables
    placeholder: To enter multiple use line breaks (enter) Format MYENVVALUE=SOMEVALUE
    validation: ^([\/A-Za-z0-9._~()'!*:@,;+?-]+=[\/A-Za-z0-9._~()'!*:@,;+?-]+)*$
    errormessage: Values must be in the format MYENVVALUE=SOMEVALUE
Single port validation for input
  - type: input
    format: text
    label: myappport
    FormName: My App Port
    placeholder: This port is the default port for the application
    validation: ^([1-9]|[1-8][0-9]|9[0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|[1-8][0-9]{3}|9[0-8][0-9]{2}|99[0-8][0-9]|999[0-9]|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$
    errormessage: Values must be in a valid port range 1-65535
IP address validation

This is useful if your container needs to communicate with the host for some reason or proxy a connection to it as the host IP is not available to the container.

  - type: input
    format: text
    label: serverip
    FormName: Server IP
    placeholder: The local IP of your server
    validation: ^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
    errormessage: Value must be a valid IP IE 192.168.1.100

Compose

The compose section of the Stack Template is where all of the user input will be parsed into a usable Docker Compose file. The engine used for templating is NunJucks . Any logic baked into the NunJucks engine can be used inside of this section.

Lets take a look at a minimum Compose file example:

compose: |
  version: "3"
  services:
    {{ name }}:
      image: hello-world:latest
      network_mode: bridge
      labels:
        - "stackname={{ stackname }}"
        - "stacktype={{ stacktype }}"
        - "stackurl={{ stackurl }}"
        - "appport=8888"

The following variables are required as meta data on all containers:

  • stackname
  • stacktype
  • stackurl

The input for these variables is automatically generated by Taisun no need to put them in your form section.

Also on exactly one container in your stack you need to define

  • appport

This will be the clickable link for the user to launch your application.

If multiple web applications links are needed you can pass an array here IE:

        - appport=[{"Rocket.Chat":"{{ rocketport }}"},{"Mongo-Express":"{{ mongoport }}"}]

This format will present the user with a drop down menu when launching the application to select which web based app to open:

If they are left out the compose file Taisun will be unable to render the access link or information on the Stacks page of the application.

Putting it all together

Lets assemble everything we have entered into a single file:

name: Hello World
description: |
  An h1 header
  ============
  
  Paragraphs are separated by a blank line.
  
  2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
  look like:
  
    * this one
    * that one
    * the other one
form:
  - type: input
    label: name
    FormName: Name
    placeholder: Unique Name to identify
  - type: select
    label: myvar
    FormName: My Variable
    options:
      - option1
      - option2
  - type: textarea
    label: ports
    FormName: Application Ports
    placeholder: To enter multiple use line breaks (enter) Format <hostport>:<containerport>
  - type: checkbox
    label: test
    FormName: Test?
compose: |
  version: "3"
  services:
    {{ name }}:
      image: hello-world:latest
      network_mode: bridge
      labels:
        - "stackname={{ stackname }}"
        - "stacktype={{ stacktype }}"
        - "stackurl={{ stackurl }}"
        - "appport=8888"

Presented to the user this will look like:

Now in this example we are not using any of our variables so lets look at a more complex example building on these inputs:

name: Hello World
description: |
  An h1 header
  ============
  
  Paragraphs are separated by a blank line.
  
  2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
  look like:
  
    * this one
    * that one
    * the other one
form:
  - type: input
    label: name
    FormName: Name
    placeholder: Unique Name to identify
  - type: select
    label: myvar
    FormName: My Variable
    options:
      - option1
      - option2
  - type: textarea
    label: ports
    FormName: Application Ports
    placeholder: To enter multiple use line breaks (enter) Format <hostport>:<containerport>
  - type: checkbox
    label: test
    FormName: Test?
compose: |
  version: "3"
  services:
    {{ name }}:
      image: hello-world:{% if test == 'true' %}testing{% else %}latest{% endif %}
      network_mode: bridge
      ports:
        - "8888:8888"
        {% if ports %}{% for port in ports %}- "{{ port }}"
        {% endfor %}{% endif %}
      labels:
        - "stackname={{ stackname }}"
        - "stacktype={{ stacktype }}"
        - "stackurl={{ stackurl }}"
        - "appport=8888"
      environment:
        - "MYVAR={{ myvar }}"

Best practice is to wrap potentially non existent variables (if the user does not fill them out) in an if statement, we used that for the ports variable here.

So above we are using a different image tag based on the user checking the "Test?" box. If the user entered port mapping we will loop through the entries and add them to the container.

NunJucks is very versatile, to read more about the options you can use:

https://mozilla.github.io/nunjucks/templating.html

Lets take a look at a more complex real world example :

name: Rocket.Chat
description: |
  Rocket.Chat [_/rocket.chat](https://hub.docker.com/_/rocket.chat/)
  ============

  [Rocket.Chat](https://rocket.chat/) is free, unlimited and open source. Replace email & Slack with the ultimate team chat software solution.

  ### Parameters

  * Rocket.Chat Port - Port Rocket.Chat will listen on the host standard is 3000
  * Mongo-Express Port - Port Mongo-Express will listen on the host standard is 8081
  * Mongo Directory - Host folder for the MongoDB data tied into Rocket Chat
  * Rocket Directory - Optional folder to store RocketChat Uploads, the application will use GridFS by default, set this folder if you want to use local folders

form:
  - type: input
    label: name
    FormName: Stack Name
    placeholder: Single word to identify this application IE RocketChat
    validation: ^[\w\d-\_]+$
    errormessage: Name must only container letters, numbers, and - or _
    required: true
  - type: input
    label: rocketport
    FormName: RocketChat Port
    placeholder: Port RocketChat will listen on the host standard is 3000
    validation: ^([1-9]|[1-8][0-9]|9[0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|[1-8][0-9]{3}|9[0-8][0-9]{2}|99[0-8][0-9]|999[0-9]|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$
    errormessage: Values must be in a valid port range 1-65535
    required: true
  - type: input
    label: mongoport
    FormName: Mongo-Express Port
    placeholder: Port Mongo-Express will listen on the host standard is 8081
    validation: ^([1-9]|[1-8][0-9]|9[0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|[1-8][0-9]{3}|9[0-8][0-9]{2}|99[0-8][0-9]|999[0-9]|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$
    errormessage: Values must be in a valid port range 1-65535
    required: true
  - type: input
    label: mongodir
    FormName: Mongo Directory
    placeholder: The folder to store MongoDB data IE /home/user/mongodb
    required: true
  - type: input
    label: rocketdir
    FormName: Rocket Directory
    placeholder: Optional folder to store RocketChat Uploads Default is in Mongo GridFS

compose: |
  version: "3"
  services:
    mongo:
      image: mongo:latest
      container_name: {{ name }}_mongo
      restart: always
      labels:
        - "stackname={{ stackname }}"
        - "stacktype={{ stacktype }}"
        - "stackurl={{ stackurl }}"
      volumes:
        - {{ mongodir }}:/data/db
        - {{ mongodir }}/configdb:/data/configdb
    rocketchat:
      image: rocket.chat:latest
      container_name: {{ name }}_rocketchat
      restart: always
      depends_on:
        - mongo
      labels:
        - "stackname={{ stackname }}"
        - "stacktype={{ stacktype }}"
        - "stackurl={{ stackurl }}"
        - appport=[{"Rocket.Chat":"{{ rocketport }}"},{"Mongo-Express":"{{ mongoport }}"}]
      {% if rocketdir %}volumes:
        - {{ rocketdir }}:/app/uploads{% endif %}
      environment:
        - MONGO_URL=mongodb://mongo:27017/rocketchat
      ports:
        - "{{ rocketport }}:3000"
    mongoexpress:
      restart: always
      depends_on:
        - mongo
      labels:
        - "stackname={{ stackname }}"
        - "stacktype={{ stacktype }}"
        - "stackurl={{ stackurl }}"
      image: mongo-express:latest
      environment:
        - ME_CONFIG_MONGODB_SERVER=mongo
      ports:
        - {{ mongoport }}:8081

In this stack we will be spinning up a total of 3 containers and linking them all to eachother.

Testing Templates

In order to vet a Template or to use one not shared at https://stacks.taisun.io/ you can manually upload and edit YAML in the Taisun web interface.

After clicking on the Upload YAML button under the Stacks page:

You will be presented with a YAML syntax highlighting editor:

From here you can review and modify the template.

When you are satisfied clicking "Upload" will bring you to the same launch page a user will be presented. This is not just an example of the input, you can use this form to proceed to launch your stack.

Stacks.taisun.io

If your stack can be used by other users on the platform there is a central repository of stack files located at:

https://stacks.taisun.io/

Uploading to the central site requires that you have a github account https://github.com/join .

After you login a button will be available to upload your stack:

Simply fill out the form with the information you want to include for your stack including entering the yaml template in the built in editor:

If you ever want to remove your stack click on the delete button next to it and confirm:

All stacks on this page will be available to install from the User UI of Taisun