Skip to content

Commit

Permalink
docs: add templating documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
jahvon committed Oct 11, 2024
1 parent 33bd90b commit 1369a55
Show file tree
Hide file tree
Showing 5 changed files with 322 additions and 81 deletions.
252 changes: 250 additions & 2 deletions docs/guide/templating.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,250 @@
The templating guide is coming soon. In the meantime, you can check out the [templating reference](../types/template.md) for more
information on the YAML template config file and the [flow template](../cli/flow_template.md) command for managing templates.
Templates are a powerful feature in flow that allow you to define reusable flowfile and workspace structures.
This guide will walk you through the process of creating and using templates in flow. It will use a simple example
of create a set of executables for managing a Kubernetes deployment.

## Registering templates

Templates are registered with flow using the [flow template register](../cli/flow_template_register.md) command. This command accepts a
the name to be given to the template and the path to the template file.

```shell
# Register the k8s-deployment template
flow template register k8s-deployment --file /path/to/k8s-deployment.flow.tmpl
```

## Generating scaffolding from a template

Templates are rendered using the [flow template generate](../cli/flow_template_generate.md) command. This command accepts a
the name to be given to the flowfile (if applicable) when rendering its template and several flags to control the rendering process.

```shell
# Run the kes-deployment template generation in the mealie directory of the homelab workspace.
# The rendered flowfile will be given the name mealie.flow
flow template generate mealie --output mealie --template k8s-deployment --workspace homelab
```

Alternatively, you can reference a flowfile template directly from a file using the `--file` flag.

```shell
flow template generate mealie --output mealie --file /path/to/k8s-deployment.flow.tmpl --workspace homelab
```

## Viewing templates

Individual templates can be viewed using the [flow template view](../cli/flow_template_view.md) command. This command
accepts either the registered name of the template or the path to the template file.

```shell
# View the k8s-deployment template by its registered name
flow template view --template k8s-deployment
# View the k8s-deployment template by its file path
flow template view --file /path/to/k8s-deployment.flow.tmpl
```

You can also list all registered templates using the [flow template list](../cli/flow_template_list.md) command.

```shell
flow template list
```

## Creating a template file

Templates are defined as YAML files that contain a string template of a [flowfile](executable.md#flowfile), artifacts to be copied,
executables to be run during the templating process, and form fields that can be used throughout the template's fields.

Check out the [template configurations](../types/template.md) for more details on the structure the template file.

### Form inputs

The form section defines the fields that will be prompted to the user when the template is rendered. Each field has a key,
prompt, and optional default value, type, and validation. It's the first step in the template rendering process and is what
provides the data for the [Go text templating](https://pkg.go.dev/text/template) that is used throughout the template file.

For instance, the following form section will prompt the user for the namespace, image, replicas, and
whether the app should be deployed immediately.

```yaml
form:
- key: "Namespace"
prompt: "What namespace should the deployment be created in?"
default: "apps"
- key: "Deploy"
prompt: "Should the app be deployed immediately?"
type: "confirm" # This will prompt the user with a yes/no question
- key: "Image"
prompt: "What image should be used for the deployment?"
required: true # The template will not render if this field is not provided
- key: "Replicas"
prompt: "How many replicas should be created?"
default: "1"
validate: "^[0-9]+$" # This will validate that the input is a number
- key: "Type"
prompt: "Should the deployment be a Helm chart or a Kubernetes manifest?"
default: "Helm"
validate: "^(Helm|K8s)$" # This will validate that the input is either "Helm" or "K8s"
```
### Artifacts
The artifacts section defines the files that will be copied to the output directory when the template is rendered.
Each artifact has a source, destination, and optional template flag that will render the file as a Go text template.
In the following example, the template will copy the `helm-deploy.sh`, `deploy.sh`, `values.yaml.tmpl`, and `resources.yaml.tmpl` files.
The `values.yaml.tmpl` and `resources.yaml.tmpl` files will be rendered as Go text templates (using the data provided through the form).

The `if` field can be used to conditionally copy the file based on the value of a form field. Below the `helm-deploy.sh` and `values.yaml.tmpl`
files will only be copied if the `Type` field is set to `Helm`. The `deploy.sh` and `resources.yaml.tmpl` files will only be copied if the
`Type` field is set to `K8s`.

```yaml
artifacts:
- srcName: "helm-deploy.sh"
srcDir: "scripts" # By default, the file will be copied from the template directory. This field can be used to specify a different directory.
if: "{{ eq .Type 'Helm' }}" # This will only copy the file if the Helm field is true
- srcName: "deploy.sh"
srcDir: "scripts"
if: "{{ eq .Type 'K8s' }}" # This will only copy the file if the K8s field is true
- srcName: "values.yaml.tmpl"
asTemplate: true
dstName: "values.yaml"
if: "{{ eq .Type 'Helm' }}" # This will only copy the file if the Helm field is true
- srcName: "resources.yaml.tmpl"
asTemplate: true
dstName: "resources.yaml"
if: "{{ eq .Type 'K8s' }}" # This will only copy the file if the K8s field is true
```

### flowfile template string

The template section defines the string template of the flowfile that will be rendered. The template can be as simple or
complex as needed and can include Go text templating to reference the form fields provided by the user.

In the following example, the template will create a set flowfile with executables for deploying, restarting, and opening the app.

```yaml
template: |
tags: [k8s]
executables:
- verb: deploy
name: "{{ .FlowFileName }}"
exec:
file: "{{ if eq .Type 'Helm' }}helm-deploy.sh{{ else }}deploy.sh{{ end }}"
params:
- envKey: "NAMESPACE"
text: "{{ .Namespace }}"
- envKey: "APP_NAME"
text: "{{ .FlowFileName }}"
- verb: restart
name: "{{ .FlowFileName }}"
exec:
cmd: "kubectl rollout restart deployment/{{ .FlowFileName }} -n {{ .Namespace }}"
- verb: open
name: "{{ .FlowFileName }}"
launch:
uri: "https://{{ .FlowFileName }}.my.haus"
```

### Pre- and post- run executables

The preRun and postRun sections define the executables that will be run before and after the template is rendered.
These executables can be used to extend the templating process by running additional commands.

In the following example, the template will run a validation executable before copying artifacts and rendering the flowfile.
Before exiting, it will also run a simple command and either open the flowfile in vscode or deploy the app based on the user's input.

```yaml
preRun:
- ref: "validate k8s/validation:context" # You can reference other executables that you have on your system
args: ["homelab"]
if: "{{ .Deploy }}"
postRun:
- cmd: "echo 'Rendered {{ if .Helm }}Helm values{{ else }}k8s manifest{{end}}'; ls -al"
- ref: "edit vscode"
args: ["{{ .FlowFilePath }}"]
if: "{{ not .Deploy }}"
- ref: "deploy {{ .FlowFileName }}"
if: "{{ .Deploy }}"
```

**Note**: preRun executables are run from the template directory, while postRun executables are run from the output directory.

### Full template example

Bringing it all together, the following is a full example of the k8s deployment template. It's not required to have all the sections
in a template, but it's a good starting point for creating your own templates.

```yaml
form:
- key: "Namespace"
prompt: "What namespace should the deployment be created in?"
default: "apps"
- key: "Deploy"
prompt: "Should the app be deployed immediately?"
type: "confirm"
- key: "Image"
prompt: "What image should be used for the deployment?"
- key: "Replicas"
prompt: "How many replicas should be created?"
default: "1"
validate: "^[0-9]+$"
- key: "Type"
prompt: "Should the deployment be a Helm chart or a Kubernetes manifest?"
default: "Helm"
validate: "^(Helm|K8s)$"
artifacts:
- srcName: "helm-deploy.sh"
srcDir: "scripts"
if: "{{ eq .Type 'Helm' }}"
- srcName: "deploy.sh"
srcDir: "scripts"
if: "{{ eq .Type 'K8s' }}"
- srcName: "values.yaml.tmpl"
asTemplate: true
dstName: "values.yaml"
if: "{{ eq .Type 'Helm' }}"
- srcName: "resources.yaml.tmpl"
asTemplate: true
dstName: "resources.yaml"
if: "{{ eq .Type 'K8s' }}"
preRun:
- ref: "validate k8s/validation:context"
args: ["homelab"]
if: "{{ .Deploy }}"
postRun:
- cmd: "echo 'Rendered {{ if .Helm }}Helm values{{ else }}k8s manifest{{end}}'; ls -al"
- ref: "edit vscode"
args: ["{{ .FlowFilePath }}"]
if: "{{ not .Deploy }}"
- ref: "deploy {{ .FlowFileName }}"
if: "{{ .Deploy }}"
template: |
tags: [k8s]
executables:
- verb: deploy
name: "{{ .FlowFileName }}"
exec:
file: "{{ if eq .Type 'Helm' }}helm-deploy.sh{{ else }}deploy.sh{{ end }}"
params:
- envKey: "NAMESPACE"
text: "{{ .Namespace }}"
- envKey: "APP_NAME"
text: "{{ .FlowFileName }}"
- verb: restart
name: "{{ .FlowFileName }}"
exec:
cmd: "kubectl rollout restart deployment/{{ .FlowFileName }} -n {{ .Namespace }}"
- verb: open
name: "{{ .FlowFileName }}"
launch:
uri: "https://{{ .FlowFileName }}.my.haus"
```

### Template helpers

Templates can use the [Sprig functions](https://masterminds.github.io/sprig/) to manipulate data during the rendering process.
Additionally, the following keys are available to the template:

- `FlowFileName`: The name of the flowfile being rendered. This is the argument passed into the `generate` command.
- `FlowFilePath`: The output path to the flowfile being rendered.
- `FlowWorkspace`: The name of the workspace that the template is rendering into.
- `FlowWorkspacePath`: The path to the workspace root directory.
82 changes: 45 additions & 37 deletions docs/schemas/template_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,42 +45,10 @@
}
}
},
"ExecutableArgumentList": {},
"ExecutableDirectory": {
"default": ""
},
"ExecutableExecExecutableType": {
"description": "Standard executable type. Runs a command/file in a subprocess.",
"type": "object",
"properties": {
"args": {
"$ref": "#/definitions/ExecutableArgumentList"
},
"cmd": {
"description": "The command to execute.\nOnly one of `cmd` or `file` must be set.\n",
"type": "string",
"default": ""
},
"dir": {
"$ref": "#/definitions/ExecutableDirectory",
"default": ""
},
"file": {
"description": "The file to execute.\nOnly one of `cmd` or `file` must be set.\n",
"type": "string",
"default": ""
},
"logMode": {
"description": "The log mode to use when running the executable.\nThis can either be `hidden`, `json`, `logfmt` or `text`\n",
"type": "string",
"default": "logfmt"
},
"params": {
"$ref": "#/definitions/ExecutableParameterList"
}
}
"ExecutableRef": {
"description": "A reference to an executable.\nThe format is `\u003cverb\u003e \u003cworkspace\u003e/\u003cnamespace\u003e:\u003cexecutable name\u003e`.\nFor example, `exec ws/ns:my-workflow`.\n\nThe workspace and namespace are optional.\nIf the workspace is not specified, the current workspace will be used.\nIf the namespace is not specified, the current namespace will be used.\n",
"type": "string"
},
"ExecutableParameterList": {},
"Field": {
"description": "A field to be displayed to the user when generating a flow file from a template.",
"type": "object",
Expand Down Expand Up @@ -117,12 +85,52 @@
"type": "boolean",
"default": false
},
"type": {
"description": "The type of input field to display.",
"type": "string",
"default": "text",
"enum": [
"text",
"masked",
"multiline",
"confirm"
]
},
"validate": {
"description": "A regular expression to validate the input value against.",
"type": "string",
"default": ""
}
}
},
"TemplateRefConfig": {
"description": "Configuration for a template executable.",
"type": "object",
"properties": {
"args": {
"description": "Arguments to pass to the executable.",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"cmd": {
"description": "The command to execute.\nOne of `cmd` or `ref` must be set.\n",
"type": "string",
"default": ""
},
"if": {
"description": "A condition to determine if the executable should be run. The condition is evaluated using Go templating \nfrom the form data. If the condition is not met, the executable run will be skipped.\n[Sprig functions](https://masterminds.github.io/sprig/) are available for use in the condition.\n\nFor example, to run a command only if the `name` field is set:\n```\n{{ if .name }}true{{ end }}\n```\n",
"type": "string",
"default": ""
},
"ref": {
"$ref": "#/definitions/ExecutableRef",
"description": "A reference to another executable to run in serial.\nOne of `cmd` or `ref` must be set.\n",
"default": ""
}
}
}
},
"properties": {
Expand All @@ -145,14 +153,14 @@
"description": "A list of exec executables to run after generating the flow file.",
"type": "array",
"items": {
"$ref": "#/definitions/ExecutableExecExecutableType"
"$ref": "#/definitions/TemplateRefConfig"
}
},
"preRun": {
"description": "A list of exec executables to run before generating the flow file.",
"type": "array",
"items": {
"$ref": "#/definitions/ExecutableExecExecutableType"
"$ref": "#/definitions/TemplateRefConfig"
}
},
"template": {
Expand Down
Loading

0 comments on commit 1369a55

Please sign in to comment.