Project: Project Calico (Runtime / Cloud Native Network)
File: templates.md

Templates define a single application configuration template. Templates are stored under the `/etc/confd/templates` directory by default. Templates are written in Go's . creates a key-value map of string -> interface{} ``` {{$endpoint := map "name" "elasticsearch" "privateport" 9200 "publicport" 443}} name: {{index $endpoint "name"}} private-port: {{index $endpoint "private_port"}} public-port: {{index $endpoint "public_port"}} ``` specifically useful if you a sub-template and you want to pass multiple values to it. Alias for the function. ``` {{with get "/key"}} key: {{base .Key}} value: {{.Value}} {{end}} ``` Checks if the key exists. Return false if key is not found. ``` {{if exists "/key"}} value: {{getv "/key"}} {{end}} ``` Returns the KVPair where key matches its argument. Returns an error if key is not found. ``` {{with get "/key"}} key: {{.Key}} value: {{.Value}} {{end}} ``` Returns all KVPair, []KVPair, where key matches its argument. Returns an error if key is not found. ``` {{range gets "/*"}} key: {{.Key}} value: {{.Value}} {{end}} ``` Returns the value as a string where key matches its argument or an optional default value. Returns an error if key is not found and no default value given. ``` value: {{getv "/key"}} ``` ``` value: {{getv "/key" "default_value"}} ``` Returns all values, []string, where key matches its argument. Returns an error if key is not found. ``` {{range getvs "/*"}} value: {{.}} {{end}} ``` Wrapper for . Retrieves the value of the environment variable named by the key. It returns the value, which will be empty if the variable is not present. Optionally, you can give a default value that will be returned if the key is not present. ``` export HOSTNAME=`hostname` ``` ``` hostname: {{getenv "HOSTNAME"}} ``` ``` ipaddr: {{getenv "HOST_IP" "127.0.0.1"}} ``` Alias for ``` ``` Outputs: ``` ``` ``` ``` Outputs: ``` ``` See the time package for more usage: http://golang.org/pkg/time/ Wrapper for . Splits the input string on the separating string and returns a slice of substrings. ``` {{ $url := split (getv "/deis/service") ":" }} host: {{index $url 0}} port: {{index $url 1}} ``` Alias for Returns uppercased string. ``` key: {{toUpper "value"}} ``` Alias for . Returns lowercased string. ``` key: {{toLower "Value"}} ``` Returns an map[string]interface{} of the json value. Wrapper for . The wrapper also sorts the SRV records alphabetically by combining all the fields of the net.SRV struct to reduce unnecessary config reloads. ``` {{range lookupSRV "mail" "tcp" "example.com"}} target: {{.Target}} port: {{.Port}} priority: {{.Priority}} weight: {{.Weight}} {{end}} ``` Returns a base64 encoded string of the value. ``` key: {{base64Encode "Value"}} ``` Returns the string representing the decoded base64 value. ``` key: {{base64Decode "VmFsdWU="}} ``` ``` etcdctl set /services/zookeeper/host1 '{"Id":"host1", "IP":"192.168.10.11"}' etcdctl set /services/zookeeper/host2 '{"Id":"host2", "IP":"192.168.10.12"}' ``` ``` [template] src = "services.conf.tmpl" dest = "/tmp/services.conf" keys = [ "/services/zookeeper/" ] ``` ``` {{range gets "/services/zookeeper/*"}} {{$data := json .Value}} id: {{$data.Id}} ip: {{$data.IP}} {{end}} ``` Once you have parsed the JSON, it is possible to traverse it with normal Go template functions such as

A more advanced structure, like this: ``` { "animals": [ {"type": "dog", "name": "Fido"}, {"type": "cat", "name": "Misse"} ] } ``` It can be traversed like this: ``` {{$data := json (getv "/test/data/")}} type: {{ (index $data.animals 1).type }} name: {{ (index $data.animals 1).name }} {{range $data.animals}} {{.name}} {{end}} ``` Returns a []interface{} from a json array such as `["a", "b", "c"]`. ``` {{range jsonArray (getv "/services/data/")}} val: {{.}} {{end}} ``` Returns all subkeys, []string, where path matches its argument. Returns an empty list if path is not found. ``` {{range ls "/deis/services"}} value: {{.}} {{end}} ``` Returns all subkeys, []string, where path matches its argument. It only returns subkeys that also have subkeys. Returns an empty list if path is not found. ``` {{range lsdir "/deis/services"}} value: {{.}} {{end}} ``` Returns the parent directory of a given key. ``` {{with dir "/services/data/url"}} dir: {{.}} {{end}} ``` Alias for the function. ``` {{$services := getvs "/services/elasticsearch/*"}} services: {{join $services ","}} ``` Alias for the function. ``` {{$backend := getv "/services/backend/nginx"}} backend = {{replace $backend "-" "_" -1}} ``` Wrapper for function. The wrapper also sorts (alphabetically) the IP addresses. This is crucial since in dynamic environments DNS servers typically shuffle the addresses linked to domain name. And that would cause unnecessary config reloads. ``` {{range lookupIP "some.host.local"}} server {{.}}; {{end}} ``` ```Bash etcdctl set /nginx/domain 'example.com' etcdctl set /nginx/root '/var/www/example_dotcom' etcdctl set /nginx/worker_processes '2' etcdctl set /app/upstream/app1 "10.0.1.100:80" etcdctl set /app/upstream/app2 "10.0.1.101:80" ``` `/etc/confd/templates/nginx.conf.tmpl` ```Text workerprocesses {{getv "/nginx/workerprocesses"}}; upstream app { {{range getvs "/app/upstream/*"}} server {{.}}; {{end}} } server { listen 80; server_name www.{{getv "/nginx/domain"}}; access_log /var/log/nginx/{{getv "/nginx/domain"}}.access.log; error_log /var/log/nginx/{{getv "/nginx/domain"}}.log; location / { root {{getv "/nginx/root"}}; index index.html index.htm; proxy_pass http://app; proxy_redirect off; proxysetheader Host $host; proxysetheader X-Real-IP $remote_addr; proxysetheader X-Forwarded-For $proxyaddxforwardedfor; } } ``` Output: `/etc/nginx/nginx.conf` ```Text worker_processes 2; upstream app { server 10.0.1.100:80; server 10.0.1.101:80; } server { listen 80; server_name www.example.com; access_log /var/log/nginx/example.com.access.log; error_log /var/log/nginx/example.com.error.log; location / { root /var/www/example_dotcom; index index.html index.htm; proxy_pass http://app; proxy_redirect off; proxysetheader Host $host; proxysetheader X-Real-IP $remote_addr; proxysetheader X-Forwarded-For $proxyaddxforwardedfor; } } ``` This examples show how to use a combination of the templates functions to do nested iteration. ``` etcdctl mkdir /services/web/cust1/ etcdctl mkdir /services/web/cust2/ etcdctl set /services/web/cust1/2 '{"IP": "10.0.0.2"}' etcdctl set /services/web/cust2/2 '{"IP": "10.0.0.4"}' etcdctl set /services/web/cust2/1 '{"IP": "10.0.0.3"}' etcdctl set /services/web/cust1/1 '{"IP": "10.0.0.1"}' ``` ``` [template] src = "services.conf.tmpl" dest = "/tmp/services.conf" keys = [ "/services/web" ] ``` ``` {{range $dir := lsdir "/services/web"}} upstream {{base $dir}} { {{$custdir := printf "/services/web/%s/*" $dir}}{{range gets $custdir}} server {{$data := json .Value}}{{$data.IP}}:80; {{end}} } server { server_name {{base $dir}}.example.com; location / { proxy_pass {{base $dir}}; } } {{end}} ``` Output:`/tmp/services.conf` ```Text upstream cust1 { server 10.0.0.1:80; server 10.0.0.2:80; } server { server_name cust1.example.com; location / { proxy_pass cust1; } } upstream cust2 { server 10.0.0.3:80; server 10.0.0.4:80; } server { server_name cust2.example.com; location / { proxy_pass cust2; } } ``` Go's package is very powerful. For more details on it's capabilities see its