Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit 9a15e4b

Browse files
committed
Import yatee in internal code base
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 94e3761 commit 9a15e4b

9 files changed

Lines changed: 1009 additions & 1 deletion

File tree

internal/renderer/render.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import (
1212
"github.com/cbroglie/mustache"
1313
"github.com/docker/app/internal"
1414
"github.com/docker/app/internal/packager"
15+
"github.com/docker/app/internal/yatee"
1516
"github.com/docker/cli/cli/compose/loader"
1617
composetypes "github.com/docker/cli/cli/compose/types"
17-
"github.com/docker/yatee/yatee"
1818
"github.com/pkg/errors"
1919
"gopkg.in/yaml.v2"
2020
)

internal/yatee/README.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# What is yatee?
2+
3+
Yatee is a basic YAML templating engine whose input is a valid YAML file.
4+
5+
# What does it support?
6+
7+
If, for, variable expansion, arithmetic expressions.
8+
9+
# Show me some examples!
10+
11+
version: "3.4"
12+
services:
13+
"@for i in 0..2":
14+
replica$i:
15+
image: superduperserver:latest
16+
command: /run $i
17+
port:
18+
- $(5000 + ($i*2))
19+
- $(5001 + ($i * 2))
20+
"@if ${myapp.debug}":
21+
debugger:
22+
image: debug
23+
"@if ! ${myapp.debug}":
24+
monitor:
25+
image: monitor
26+
"@for i in $myapp.services":
27+
"$i":
28+
image: $i:latest
29+
30+
When processed with the following settings file:
31+
32+
myapp:
33+
debug: false
34+
services:
35+
- nginx
36+
- redis
37+
38+
Will produce the following output:
39+
40+
services:
41+
monitor:
42+
image: monitor
43+
nginx:
44+
image: nginx:latest
45+
redis:
46+
image: redis:latest
47+
replica0:
48+
command: /run 0
49+
image: superduperserver:latest
50+
port:
51+
- "5000"
52+
- "5001"
53+
replica1:
54+
command: /run 1
55+
image: superduperserver:latest
56+
port:
57+
- "5002"
58+
- "5003"
59+
60+
# How do I invoke it?
61+
62+
./yatee TEMPLATE_FILE SETTINGS_FILES...
63+
64+
# How do I use it as a library?
65+
66+
The yatee go package exports the following two functions:
67+
68+
// LoadSettings loads a set of settings file and produce a property dictionary
69+
func LoadSettings(files []string) (map[string]interface{}, error)
70+
// Process resolves input templated yaml using values given in settings
71+
func Process(inputString string, settings map[string]interface{}) (map[interface{}] interface{}, error)
72+
73+
# Tell me more about the templating
74+
75+
## All features at a glance
76+
77+
- `$foo.bar and ${foo.bar}` are replaced by the value of `foo.bar` in the settings structure. Nesting is allowed.
78+
- `${foo?IF_TRUE:IF_FALSE}` is replaced by IF_TRUE if `foo` in settings is true (not empty, 0 or `false`).
79+
- `$(expr)` is evaluated as an arithmetic expression. Integers, parenthesis, and the operators
80+
'+-*/%' are supported. Note that there is no operator precedence, evaluation is from left to right.
81+
- `$$` is replaced by a single literal `$` without any variable expansion.
82+
- A YAML key of `@for VAR in begin..end` or `@for VAR in VALUE LIST` will inject the value in the
83+
parent node for each value of VAR.
84+
- A YAML key of `@if VALUE` will inject its content in the parent node only if VAULE
85+
is not false (0, empty or 'false'). A prefix '!' is supported to negate the value. A `@else` dict can be specified
86+
under the `@if` node, and will be injected if the condition is false.
87+
- A YAML key of `@switch VALUE` will inject it's sub-key's value matching VALUE to the parent node, or
88+
inject the value under the `default` key if present and no match is found.
89+
- A YAML value of `@if (EXPR) VALUE` in a list will be replaced by `VALUE` if `EXPR` is true,
90+
suppressed otherwise
91+
92+
## Variable expansion examples
93+
94+
All examples below use the following settings:
95+
96+
app:
97+
debug: true
98+
release: false
99+
foo: bar
100+
bar: baz
101+
count: 2
102+
103+
Input | Output
104+
----- | ------
105+
${app.debug} | true
106+
${foo}${bar} | barbaz
107+
${$foo} | baz
108+
$(1+2*3) | 9
109+
$(1+(2*3)) | 7
110+
$($count + 40) | 42
111+
${app.debug?foo:bar} | foo
112+
${app.release?foo:bar} | bar
113+
$$foo | $$foo
114+
$$$foo | $$bar
115+
116+
## Control flow examples
117+
118+
Using the same settings as above.
119+
120+
### If
121+
122+
"@if !$app.release":
123+
shown: nope
124+
"@else":
125+
shown: yes
126+
somelist:
127+
- a
128+
- @if ($app.debug) b
129+
- @if ($app.release) c
130+
- d
131+
132+
produces:
133+
134+
shown: yes
135+
somelist:
136+
- a
137+
- b
138+
- d
139+
140+
### For
141+
142+
"@for v in 0..$(count)":
143+
key$v: val$($v + 1)
144+
"@for v in a b c":
145+
key$v: val$v
146+
147+
produces:
148+
149+
key0: val1
150+
key1: val2
151+
keya: vala
152+
keyb: valb
153+
keyc: valc
154+
155+
### Switch
156+
157+
"@switch $foo":
158+
baz:
159+
isbaz: 1
160+
bar:
161+
isbar: 1
162+
default:
163+
isother: 1
164+
"@switch $bar":
165+
foo:
166+
isfoo: 2
167+
default:
168+
isother:2
169+
170+
produces
171+
172+
isbar: 1
173+
isother: 2

internal/yatee/gopher/main.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package main
2+
3+
import (
4+
"github.com/docker/app/internal/yatee/yatee"
5+
"github.com/gopherjs/gopherjs/js"
6+
)
7+
8+
func processJS(input, settings string) (string, string) {
9+
res, err := yatee.ProcessStrings(input, settings)
10+
var errStr string
11+
if err != nil {
12+
errStr = err.Error()
13+
}
14+
return res, errStr
15+
}
16+
17+
func main() {
18+
js.Global.Set("yatee", map[string]interface{}{
19+
"ProcessJS": processJS,
20+
})
21+
}

internal/yatee/gopher/yatee.html

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<html>
2+
<head>
3+
<script language="javascript" src="gopher.js"></script>
4+
<script language="javascript">
5+
function compute() {
6+
t = document.getElementById('itemplate').value
7+
s = document.getElementById('isettings').value
8+
r = yatee.ProcessJS(t, s)
9+
res = r[0]
10+
if (!res) {
11+
res = "<font color='red'>" + r[1] + "</font>"
12+
}
13+
document.getElementById('iresult').innerHTML = res
14+
}
15+
16+
</script>
17+
</head>
18+
<body>
19+
Test <a href="http://github.com/docker/yatee">yatee</a> in your browser.
20+
<br/>
21+
Input template <br/>
22+
<textarea id="itemplate" rows=20 cols=80>
23+
version: "3.4"
24+
services:
25+
"@for i in 0..2":
26+
replica$i:
27+
image: superduperserver:latest
28+
command: /run $i
29+
port:
30+
- $(5000 + ($i*2))
31+
- $(5001 + ($i * 2))
32+
"@if ${myapp.debug}":
33+
debugger:
34+
image: debug
35+
"@if ! ${myapp.debug}":
36+
monitor:
37+
image: monitor
38+
"@for i in $myapp.services":
39+
"$i":
40+
image: $i:latest
41+
</textarea>
42+
<textarea id="isettings" rows=20 cols=80>
43+
myapp:
44+
debug: false
45+
services:
46+
- nginx
47+
- redis
48+
</textarea>
49+
<br/>
50+
<input type=submit onclick="javascript:compute()"></input>
51+
<br/>
52+
Result:
53+
<pre><div id="iresult"></div></pre>
54+
</body>
55+
</html>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
services:
2+
"@for i in 0..2":
3+
replica$i:
4+
image: superduperserver:latest
5+
command: /run $i
6+
port:
7+
- $(5000 + ($i*2))
8+
- $(5001 + ($i * 2))
9+
"@switch ${myapp.mode}":
10+
debug:
11+
debugger2:
12+
image: debug
13+
release:
14+
monitor2:
15+
image: monitor
16+
"@if ${myapp.debug}":
17+
debugger:
18+
image: debug
19+
"@if ! ${myapp.debug}":
20+
monitor:
21+
image: monitor
22+
"@for i in $myapp.services":
23+
"$i":
24+
image: $i:latest
25+
test:
26+
- v1
27+
- v2
28+
- "@if (true) vtrue"
29+
- "@if (false) vfalse"
30+
- "@if (!false) vtrue2"

internal/yatee/samples/main.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"os"
7+
8+
"github.com/docker/app/internal/yatee"
9+
yaml "gopkg.in/yaml.v2"
10+
)
11+
12+
func main() {
13+
if len(os.Args) == 1 || os.Args[1] == "-h" || os.Args[1] == "--help" {
14+
fmt.Printf("usage: %s TEMPLATEFILE SETTINGSFILES...\n", os.Args[0])
15+
os.Exit(1)
16+
}
17+
input, err := ioutil.ReadFile(os.Args[1])
18+
if err != nil {
19+
fmt.Printf("%v\n", err)
20+
os.Exit(1)
21+
}
22+
settings, err := yatee.LoadSettings(os.Args[2:])
23+
if err != nil {
24+
fmt.Printf("%v\n", err)
25+
os.Exit(1)
26+
}
27+
output, err := yatee.Process(string(input), settings)
28+
if err != nil {
29+
fmt.Printf("processing error: %v\n", err)
30+
os.Exit(1)
31+
}
32+
raw, err := yaml.Marshal(output)
33+
if err != nil {
34+
fmt.Printf("marshalling error: %v\n", err)
35+
os.Exit(1)
36+
}
37+
fmt.Println(string(raw))
38+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
myapp:
2+
debug: false
3+
services:
4+
- nginx
5+
- redis
6+
mode: debug

0 commit comments

Comments
 (0)