Skip to content

Data Rendering

DRP uses Go's text/template package to render task scripts and configuration files before they are delivered to the machine runner. Rendering happens in two phases: first, DRP resolves the template on the server side using machine state and parameters; then, the rendered script is handed to the runner (agent on the target machine or a server-side context) for execution. Understanding the rendering context and available functions is essential for writing tasks that adapt to per-machine configuration.

Two-Phase Execution

Phase 1 — Server-side rendering: DRP fetches the task's template Contents, evaluates all {{ }} expressions against the current machine state, and produces a plain script or configuration file. No code is executed during this phase — only template expressions are evaluated.

Phase 2 — Runner-side execution: The rendered script is sent to the runner agent on the machine (or in the configured context). The agent writes the script to disk, sets it executable, and runs it. Output and exit codes are captured back to DRP as job log entries.

This separation means that any DRP API calls, parameter lookups, or conditional logic written in template syntax happens before the script reaches the machine. The machine itself only sees the final rendered output.

Template Context

Every template is evaluated with a root context object (.) that exposes the following top-level fields:

Field Type Description
.Machine Machine object The full machine model for the target machine
.Env BootEnv object The current boot environment
.Task Task object The current task being rendered
.Stage Stage object The current stage
.Job Job object The current job
.Param "name" any Resolved parameter value (machine → profiles → global)
.ParamExists "name" bool True if the parameter is set anywhere in the resolution chain

Accessing a machine field directly:

Text Only
{{ .Machine.Name }}
{{ .Machine.Address }}
{{ .Machine.UUID }}
{{ .Machine.Workflow }}

Accessing a resolved parameter (recommended over .Machine.Params for proper profile resolution):

Text Only
{{ .Param "ntp/servers" }}
{{ .Param "dns/search-domain" }}

Common Template Functions

DRP extends the standard Go template function set with several helpers:

Text Only
{{ .Param "my-param" }}               # Get a parameter value
{{ .ParamExists "my-param" }}         # Check if parameter exists
{{ .Machine.Params.SomeKey }}         # Access raw machine params (no profile resolution)

DRP templates include the full Sprig function library. Use the dig function to safely navigate nested maps with a default value rather than risking a nil dereference:

Text Only
{{/* Safely retrieve a nested key with a default */}}
{{ dig "network" "gateway" "192.168.1.1" (.Param "site/network-config") }}

Standard Go template functions are also available: range, if, with, index, len, printf, and, or, not, and all text/template built-ins.

Example: Rendering Machine-Specific Configuration

The following task template renders an NTP configuration file using parameters resolved for the target machine:

Bash
#!/bin/bash
# Rendered by DRP for machine: {{ .Machine.Name }}
# This script configures NTP on the target machine.

cat > /etc/chrony.conf <<EOF
{{ range (.Param "ntp/servers") -}}
server {{ . }} iburst
{{ end -}}
driftfile /var/lib/chrony/drift
EOF

systemctl enable chronyd
systemctl restart chronyd

The range loop iterates over the ntp/servers parameter (a list of strings). The - after {{ and before }} trims surrounding whitespace from the rendered output.

Conditional Rendering Based on Parameters

Use if and with to conditionally include configuration:

Bash
#!/bin/bash
# Configure DNS search domain if set

{{ if .ParamExists "dns/search-domain" -}}
echo "search {{ .Param "dns/search-domain" }}" >> /etc/resolv.conf
{{ end -}}

{{ range (.Param "dns/servers") -}}
echo "nameserver {{ . }}" >> /etc/resolv.conf
{{ end -}}

Troubleshooting Rendering

When a template fails to render, the error is captured in the job log with a line number referencing the template source. Common issues:

  • Missing required parameter: The template references .Param "name" but the parameter is not set on the machine or any attached profile. Check drpcli machines show <uuid> | jq .Params and verify the parameter is present.
  • Type mismatch: A template expects a list (range) but the parameter is a string. Check the parameter schema with drpcli params show <name>.
  • Nil dereference: Accessing a sub-field on a nil value (e.g., .Machine.Params.SomeKey when SomeKey is not set) causes a render failure. Use {{ if .ParamExists "key" }} guards.

To test rendering without running the task, create a job and inspect the rendered actions:

Bash
# Create a job for the machine's current task
JOB=$(drpcli jobs create <machine-uuid> | jq -r .UUID)

# See the rendered scripts that would execute
drpcli jobs actions $JOB