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

Commit 7a8946d

Browse files
author
Matthieu Nottale
committed
Helm: Fix multiple issues.
- Correctly prefix `template_` to the name of all fields. - Fix kind and apiVersion in outputed Stack. - Unquote gotemplates so that integer values are accepted. Signed-off-by: Matthieu Nottale <matthieu.nottale@docker.com>
1 parent e17c17f commit 7a8946d

6 files changed

Lines changed: 143 additions & 114 deletions

File tree

e2e/testdata/helm-expected.chart/templates/stack.yaml

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
apiversion: v1beta2
2-
kind: stacks.compose.docker.com
1+
apiVersion: compose.docker.com/v1beta2
2+
kind: Stack
33
metadata:
44
annotations: {}
55
clustername: ""
@@ -19,34 +19,34 @@ metadata:
1919
uid: ""
2020
spec:
2121
services:
22-
- image: '{{.Values.watcher.image}}'
22+
- image: {{.Values.watcher.image}}
2323
name: app-watcher
2424
- deploy:
2525
resources:
2626
limits:
27-
memory: '{{.Values.memory}}'
27+
memory: {{.Values.memory}}
2828
health_check:
2929
interval: 2m0s
3030
test:
3131
- /ping
3232
- debug
33-
timeout: '{{.Values.timeout}}'
33+
timeout: {{.Values.timeout}}
3434
image: busybox:latest
3535
name: debug
3636
ports:
3737
- mode: ingress
3838
protocol: tcp
39-
target: '{{.Values.aport}}'
39+
target: {{.Values.aport}}
4040
- mode: ingress
4141
protocol: tcp
42-
published: '{{.Values.dport}}'
43-
target: '{{.Values.sport}}'
44-
privileged: '{{.Values.privileged}}'
45-
read_only: '{{.Values.read_only}}'
46-
stdin_open: '{{.Values.stdin_open}}'
47-
tty: '{{.Values.tty}}'
42+
published: {{.Values.dport}}
43+
target: {{.Values.sport}}
44+
privileged: {{.Values.privileged}}
45+
read_only: {{.Values.read_only}}
46+
stdin_open: {{.Values.stdin_open}}
47+
tty: {{.Values.tty}}
4848
- deploy:
49-
replicas: '{{.Values.myapp.nginx_replicas}}'
49+
replicas: {{.Values.myapp.nginx_replicas}}
5050
image: nginx:{{.Values.myapp.nginx_version}}
5151
name: front
5252
- command:
Lines changed: 19 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,47 @@
1-
objectmeta:
2-
annotations: {}
3-
clustername: ""
4-
creationtimestamp: "0001-01-01T00:00:00Z"
5-
deletiongraceperiodseconds: null
6-
deletiontimestamp: null
7-
finalizers: []
8-
generatename: ""
9-
generation: 0
10-
initializers: null
11-
labels: {}
1+
apiVersion: compose.docker.com/v1beta1
2+
kind: Stack
3+
metadata:
4+
creationTimestamp: null
125
name: helm
13-
namespace: ""
14-
ownerreferences: []
15-
resourceversion: ""
16-
selflink: ""
17-
uid: ""
186
spec:
19-
composefile: |
20-
version: "3.7"
7+
composeFile: |
218
services:
229
app-watcher:
2310
image: {{.Values.watcher.image}}
2411
debug:
2512
deploy:
2613
resources:
2714
limits:
28-
memory:
29-
valuetemplate: {{.Values.memory}}
15+
memory: {{.Values.memory}}
3016
healthcheck:
17+
interval: 2m0s
3118
test:
3219
- /ping
3320
- debug
34-
timeout:
35-
valuetemplate: {{.Values.timeout}}
36-
interval:
37-
value: 2m0s
21+
timeout: {{.Values.timeout}}
3822
image: busybox:latest
3923
ports:
4024
- mode: ingress
41-
target:
42-
valuetemplate: {{.Values.aport}}
4325
protocol: tcp
26+
target: {{.Values.aport}}
4427
- mode: ingress
45-
target:
46-
valuetemplate: {{.Values.sport}}
47-
published:
48-
valuetemplate: {{.Values.dport}}
4928
protocol: tcp
50-
template_privileged:
51-
valuetemplate: {{.Values.privileged}}
52-
read_only:
53-
valuetemplate: {{.Values.read_only}}
54-
stdin_open:
55-
valuetemplate: {{.Values.stdin_open}}
56-
tty:
57-
valuetemplate: {{.Values.tty}}
29+
published: {{.Values.dport}}
30+
target: {{.Values.sport}}
31+
privileged: {{.Values.privileged}}
32+
read_only: {{.Values.read_only}}
33+
stdin_open: {{.Values.stdin_open}}
34+
tty: {{.Values.tty}}
5835
front:
5936
deploy:
60-
replicas:
61-
valuetemplate: {{.Values.myapp.nginx_replicas}}
37+
replicas: {{.Values.myapp.nginx_replicas}}
6238
image: nginx:{{.Values.myapp.nginx_version}}
6339
monitor:
6440
command:
6541
- monitor
6642
- --source
67-
- {{.Values.app.name}}-{{.Values.app.version}}
43+
- '{{.Values.app.name}}-{{.Values.app.version}}'
6844
- $dollar
6945
image: busybox:latest
70-
status:
71-
message: ""
72-
phase: ""
73-
typemeta:
74-
apiversion: v1beta1
75-
kind: stacks.compose.docker.com
46+
version: "3.7"
47+
status: {}

e2e/testdata/helm-expected.chart/templates/stackv1beta2.yaml

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
apiversion: v1beta2
2-
kind: stacks.compose.docker.com
1+
apiVersion: compose.docker.com/v1beta2
2+
kind: Stack
33
metadata:
44
annotations: {}
55
clustername: ""
@@ -19,34 +19,34 @@ metadata:
1919
uid: ""
2020
spec:
2121
services:
22-
- image: '{{.Values.watcher.image}}'
22+
- image: {{.Values.watcher.image}}
2323
name: app-watcher
2424
- deploy:
2525
resources:
2626
limits:
27-
memory: '{{.Values.memory}}'
27+
memory: {{.Values.memory}}
2828
health_check:
2929
interval: 2m0s
3030
test:
3131
- /ping
3232
- debug
33-
timeout: '{{.Values.timeout}}'
33+
timeout: {{.Values.timeout}}
3434
image: busybox:latest
3535
name: debug
3636
ports:
3737
- mode: ingress
3838
protocol: tcp
39-
target: '{{.Values.aport}}'
39+
target: {{.Values.aport}}
4040
- mode: ingress
4141
protocol: tcp
42-
published: '{{.Values.dport}}'
43-
target: '{{.Values.sport}}'
44-
privileged: '{{.Values.privileged}}'
45-
read_only: '{{.Values.read_only}}'
46-
stdin_open: '{{.Values.stdin_open}}'
47-
tty: '{{.Values.tty}}'
42+
published: {{.Values.dport}}
43+
target: {{.Values.sport}}
44+
privileged: {{.Values.privileged}}
45+
read_only: {{.Values.read_only}}
46+
stdin_open: {{.Values.stdin_open}}
47+
tty: {{.Values.tty}}
4848
- deploy:
49-
replicas: '{{.Values.myapp.nginx_replicas}}'
49+
replicas: {{.Values.myapp.nginx_replicas}}
5050
image: nginx:{{.Values.myapp.nginx_version}}
5151
name: front
5252
- command:

internal/helm/helm.go

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package helm
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"io/ioutil"
67
"os"
@@ -109,6 +110,28 @@ func makeValues(app *types.App, targetDir string, env map[string]string, variabl
109110
return ioutil.WriteFile(filepath.Join(targetDir, "values.yaml"), valuesRaw, 0644)
110111
}
111112

113+
// convertTemplatesAny resolves templated fields in input, and returns marshaled YAML
114+
func convertTemplatesAny(input interface{}) ([]byte, error) {
115+
stackData, err := yaml.Marshal(input)
116+
if err != nil {
117+
return nil, errors.Wrap(err, "failed to marshal stack data")
118+
}
119+
preStack := make(map[interface{}]interface{})
120+
err = yaml.Unmarshal(stackData, preStack)
121+
if err != nil {
122+
return nil, errors.Wrap(err, "failed to unmarshal stack data")
123+
}
124+
err = convertTemplates(preStack)
125+
if err != nil {
126+
return nil, errors.Wrap(err, "failed to convert stack templates")
127+
}
128+
stackData, err = yaml.Marshal(preStack)
129+
if err != nil {
130+
return nil, errors.Wrap(err, "failed to marshal final stack")
131+
}
132+
return stackData, nil
133+
}
134+
112135
// makeStack converts data into a helm template for a stack
113136
func makeStack(appname string, targetDir string, data []byte, stackVersion string) error {
114137
parsed, err := loader.ParseYAML(data)
@@ -122,47 +145,53 @@ func makeStack(appname string, targetDir string, data []byte, stackVersion strin
122145
if err := os.MkdirAll(filepath.Join(targetDir, "templates"), 0755); err != nil {
123146
return err
124147
}
125-
var stack interface{}
148+
var stackData []byte
126149
switch stackVersion {
127150
case V1Beta2:
128151
stackSpec := templateconversion.FromComposeConfig(rendered)
129-
stack = templatev1beta2.Stack{
130-
TypeMeta: typeMeta(stackVersion),
152+
stack := templatev1beta2.Stack{
153+
TypeMeta: templatev1beta2.TypeMeta{
154+
Kind: "Stack",
155+
APIVersion: "compose.docker.com/" + stackVersion,
156+
},
131157
ObjectMeta: objectMeta(appname),
132158
Spec: stackSpec,
133159
}
160+
stackData, err = convertTemplatesAny(stack)
161+
if err != nil {
162+
return err
163+
}
134164
case V1Beta1:
135-
composeFile, err := yaml.Marshal(rendered)
165+
composeFile, err := convertTemplatesAny(rendered)
136166
if err != nil {
137167
return err
138168
}
139-
stack = v1beta1.Stack{
169+
stack := v1beta1.Stack{
140170
TypeMeta: typeMeta(stackVersion),
141171
ObjectMeta: objectMeta(appname),
142172
Spec: v1beta1.StackSpec{
143173
ComposeFile: string(composeFile),
144174
},
145175
}
176+
// Kube doesn't have the right annotations for YAML, so serialize into json
177+
// and convert
178+
j, err := json.Marshal(stack)
179+
if err != nil {
180+
return errors.Wrap(err, "failed to marshal stack to json")
181+
}
182+
intermediate := make(map[string]interface{})
183+
err = json.Unmarshal(j, &intermediate)
184+
if err != nil {
185+
return errors.Wrap(err, "failed to unmarshal stack from json")
186+
}
187+
stackData, err = yaml.Marshal(intermediate)
188+
if err != nil {
189+
return errors.Wrap(err, "failed to marshal final stack")
190+
}
146191
default:
147192
return fmt.Errorf("invalid stack version %q", stackVersion)
148193
}
149-
stackData, err := yaml.Marshal(stack)
150-
if err != nil {
151-
return errors.Wrap(err, "failed to marshal stack data")
152-
}
153-
preStack := make(map[interface{}]interface{})
154-
err = yaml.Unmarshal(stackData, preStack)
155-
if err != nil {
156-
return errors.Wrap(err, "failed to unmarshal stack data")
157-
}
158-
err = convertTemplates(preStack)
159-
if err != nil {
160-
return errors.Wrap(err, "failed to convert stack templates")
161-
}
162-
stackData, err = yaml.Marshal(preStack)
163-
if err != nil {
164-
return errors.Wrap(err, "failed to marshal final stack")
165-
}
194+
stackData = []byte(unquote(string(stackData)))
166195
return ioutil.WriteFile(filepath.Join(targetDir, "templates", "stack.yaml"), stackData, 0644)
167196
}
168197

@@ -233,8 +262,8 @@ func makeChart(meta *metadata.AppMetadata, targetDir string) error {
233262

234263
func typeMeta(stackVersion string) metav1.TypeMeta {
235264
return metav1.TypeMeta{
236-
Kind: "stacks.compose.docker.com",
237-
APIVersion: stackVersion,
265+
Kind: "Stack",
266+
APIVersion: "compose.docker.com/" + stackVersion,
238267
}
239268
}
240269

@@ -310,6 +339,12 @@ func filterVariables(s map[string]interface{}, variables []string, prefix string
310339
}
311340
}
312341

342+
// unquote unquotes gotemplates in template
343+
func unquote(template string) string {
344+
re := regexp.MustCompile(`'(\{\{[^'}]*\}\})'`)
345+
return re.ReplaceAllString(template, "$1")
346+
}
347+
313348
// toGoTemplate converts $foo and ${foo} into {{.foo}}
314349
func toGoTemplate(template string) (string, error) {
315350
re := regexp.MustCompile(`(^|[^$])\${?([a-zA-Z0-9_.]+)}?`)

0 commit comments

Comments
 (0)