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

Commit 3ddfd96

Browse files
authored
Merge pull request #247 from shin-/243-v1beta1-helm
Allow creating helm chart for v1beta1 spec of Compose-Kubernetes
2 parents 48ec729 + 7b1457a commit 3ddfd96

5 files changed

Lines changed: 181 additions & 27 deletions

File tree

cmd/docker-app/helm.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package main
22

33
import (
4+
"fmt"
5+
46
"github.com/docker/app/internal"
57
"github.com/docker/app/internal/packager"
68
"github.com/docker/app/internal/renderer"
@@ -13,6 +15,7 @@ var (
1315
helmSettingsFile []string
1416
helmEnv []string
1517
helmRender bool
18+
stackVersion string
1619
)
1720

1821
func helmCmd() *cobra.Command {
@@ -31,7 +34,10 @@ func helmCmd() *cobra.Command {
3134
if err != nil {
3235
return err
3336
}
34-
return renderer.Helm(appname, helmComposeFiles, helmSettingsFile, d, helmRender)
37+
if stackVersion != renderer.V1Beta1 && stackVersion != renderer.V1Beta2 {
38+
return fmt.Errorf("invalid stack version %q (accepted values: %s, %s)", stackVersion, renderer.V1Beta1, renderer.V1Beta2)
39+
}
40+
return renderer.Helm(appname, helmComposeFiles, helmSettingsFile, d, helmRender, stackVersion)
3541
},
3642
}
3743
if internal.Experimental == "on" {
@@ -43,5 +49,6 @@ be rendered instead of exported as a template.`
4349
}
4450
cmd.Flags().StringArrayVarP(&helmSettingsFile, "settings-files", "f", []string{}, "Override settings files")
4551
cmd.Flags().StringArrayVarP(&helmEnv, "set", "s", []string{}, "Override settings values")
52+
cmd.Flags().StringVarP(&stackVersion, "stack-version", "", renderer.V1Beta2, "Version of the stack specification for the produced helm chart")
4653
return cmd
4754
}

e2e/binary_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,22 @@ func TestHelmBinary(t *testing.T) {
286286
golden.Assert(t, string(stack), "helm-expected.chart/templates/stack.yaml")
287287
}
288288

289+
func TestHelmV1Beta1Binary(t *testing.T) {
290+
dockerApp, _ := getBinary(t)
291+
assertCommand(t, dockerApp, "helm", "helm", "-s", "myapp.nginx_version=2", "--stack-version", "v1beta1")
292+
chart, _ := ioutil.ReadFile("helm.chart/Chart.yaml")
293+
values, _ := ioutil.ReadFile("helm.chart/values.yaml")
294+
stack, _ := ioutil.ReadFile("helm.chart/templates/stack.yaml")
295+
golden.Assert(t, string(chart), "helm-expected.chart/Chart.yaml")
296+
golden.Assert(t, string(values), "helm-expected.chart/values.yaml")
297+
golden.Assert(t, string(stack), "helm-expected.chart/templates/stack-v1beta1.yaml")
298+
}
299+
300+
func TestHelmInvalidStackVersionBinary(t *testing.T) {
301+
dockerApp, _ := getBinary(t)
302+
assertCommandFailureOutput(t, "invalid-stack-version.golden", dockerApp, "helm", "helm", "--stack-version", "foobar")
303+
}
304+
289305
func TestSplitMergeBinary(t *testing.T) {
290306
dockerApp, hasExperimental := getBinary(t)
291307
if !hasExperimental {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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: {}
12+
name: helm
13+
namespace: default
14+
ownerreferences: []
15+
resourceversion: ""
16+
selflink: ""
17+
uid: ""
18+
spec:
19+
composefile: |
20+
version: "3.4"
21+
services:
22+
app-watcher:
23+
image: {{.Values.watcher.image}}
24+
debug:
25+
deploy:
26+
resources:
27+
limits:
28+
memory:
29+
valuetemplate: {{.Values.memory}}
30+
healthcheck:
31+
test:
32+
- /ping
33+
- debug
34+
timeout:
35+
valuetemplate: {{.Values.timeout}}
36+
interval:
37+
value: 2m0s
38+
image: busybox:latest
39+
ports:
40+
- mode: ingress
41+
target:
42+
valuetemplate: {{.Values.aport}}
43+
protocol: tcp
44+
- mode: ingress
45+
target:
46+
valuetemplate: {{.Values.sport}}
47+
published:
48+
valuetemplate: {{.Values.dport}}
49+
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}}
58+
front:
59+
deploy:
60+
replicas:
61+
valuetemplate: {{.Values.myapp.nginx_replicas}}
62+
image: nginx:{{.Values.myapp.nginx_version}}
63+
monitor:
64+
command:
65+
- monitor
66+
- --source
67+
- {{.Values.app.name}}-{{.Values.app.version}}
68+
- $dollar
69+
image: busybox:latest
70+
status:
71+
message: ""
72+
phase: ""
73+
typemeta:
74+
apiversion: v1beta1
75+
kind: stacks.compose.docker.com
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Error: invalid stack version "foobar" (accepted values: v1beta1, v1beta2)

internal/renderer/helm.go

Lines changed: 81 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/docker/app/internal/types"
1717
conversion "github.com/docker/cli/cli/command/stack/kubernetes"
1818
"github.com/docker/cli/cli/compose/loader"
19+
"github.com/docker/cli/kubernetes/compose/v1beta1"
1920
"github.com/docker/cli/kubernetes/compose/v1beta2"
2021
"github.com/pkg/errors"
2122
yaml "gopkg.in/yaml.v2"
@@ -38,6 +39,13 @@ post-process the serialized yaml to replace all 'template_'-prefixed keys
3839
with the appropriate content (value or template)
3940
*/
4041

42+
const (
43+
// V1Beta1 is the string identifier for the v1beta1 version of the stack spec
44+
V1Beta1 = "v1beta1"
45+
// V1Beta2 is the string identifier for the v1beta2 version of the stack spec
46+
V1Beta2 = "v1beta2"
47+
)
48+
4149
type helmMaintainer struct {
4250
Name string
4351
}
@@ -207,21 +215,44 @@ func makeChart(appname, targetDir string) error {
207215
return ioutil.WriteFile(filepath.Join(targetDir, "Chart.yaml"), hmetadata, 0644)
208216
}
209217

210-
func helmRender(appname string, targetDir string, composeFiles []string, settingsFile []string, env map[string]string) error {
218+
func helmRender(appname string, targetDir string, composeFiles []string, settingsFile []string, env map[string]string, stackVersion string) error {
211219
rendered, err := Render(appname, composeFiles, settingsFile, env)
212220
if err != nil {
213221
return err
214222
}
215-
stackSpec := conversion.FromComposeConfig(rendered)
216-
stack := v1beta2.Stack{
217-
TypeMeta: metav1.TypeMeta{
218-
Kind: "stacks.compose.docker.com",
219-
APIVersion: "v1beta2",
220-
},
221-
ObjectMeta: metav1.ObjectMeta{
222-
Name: internal.AppNameFromDir(appname),
223-
},
224-
Spec: stackSpec,
223+
var stack interface{}
224+
switch stackVersion {
225+
case V1Beta2:
226+
stackSpec := conversion.FromComposeConfig(rendered)
227+
stack = v1beta2.Stack{
228+
TypeMeta: metav1.TypeMeta{
229+
Kind: "stacks.compose.docker.com",
230+
APIVersion: V1Beta2,
231+
},
232+
ObjectMeta: metav1.ObjectMeta{
233+
Name: internal.AppNameFromDir(appname),
234+
},
235+
Spec: stackSpec,
236+
}
237+
case V1Beta1:
238+
composeFile, err := yaml.Marshal(rendered)
239+
if err != nil {
240+
return err
241+
}
242+
stack = v1beta1.Stack{
243+
TypeMeta: metav1.TypeMeta{
244+
Kind: "stacks.compose.docker.com",
245+
APIVersion: V1Beta1,
246+
},
247+
ObjectMeta: metav1.ObjectMeta{
248+
Name: internal.AppNameFromDir(appname),
249+
},
250+
Spec: v1beta1.StackSpec{
251+
ComposeFile: string(composeFile),
252+
},
253+
}
254+
default:
255+
return fmt.Errorf("invalid stack version %q", stackVersion)
225256
}
226257
stackData, err := yaml.Marshal(stack)
227258
if err != nil {
@@ -231,7 +262,7 @@ func helmRender(appname string, targetDir string, composeFiles []string, setting
231262
}
232263

233264
//makeStack converts data into a helm template for a stack
234-
func makeStack(appname string, targetDir string, data []byte) error {
265+
func makeStack(appname string, targetDir string, data []byte, stackVersion string) error {
235266
parsed, err := loader.ParseYAML(data)
236267
if err != nil {
237268
return errors.Wrap(err, "failed to parse template compose")
@@ -241,17 +272,41 @@ func makeStack(appname string, targetDir string, data []byte) error {
241272
return errors.Wrap(err, "failed to load template compose")
242273
}
243274
os.Mkdir(filepath.Join(targetDir, "templates"), 0755)
244-
stackSpec := templateconversion.FromComposeConfig(rendered)
245-
stack := templatev1beta2.Stack{
246-
TypeMeta: metav1.TypeMeta{
247-
Kind: "stacks.compose.docker.com",
248-
APIVersion: "v1beta2",
249-
},
250-
ObjectMeta: metav1.ObjectMeta{
251-
Name: internal.AppNameFromDir(appname),
252-
Namespace: "default", // FIXME
253-
},
254-
Spec: stackSpec,
275+
var stack interface{}
276+
switch stackVersion {
277+
case V1Beta2:
278+
stackSpec := templateconversion.FromComposeConfig(rendered)
279+
stack = templatev1beta2.Stack{
280+
TypeMeta: metav1.TypeMeta{
281+
Kind: "stacks.compose.docker.com",
282+
APIVersion: V1Beta2,
283+
},
284+
ObjectMeta: metav1.ObjectMeta{
285+
Name: internal.AppNameFromDir(appname),
286+
Namespace: "default", // FIXME
287+
},
288+
Spec: stackSpec,
289+
}
290+
case V1Beta1:
291+
composeFile, err := yaml.Marshal(rendered)
292+
if err != nil {
293+
return err
294+
}
295+
stack = v1beta1.Stack{
296+
TypeMeta: metav1.TypeMeta{
297+
Kind: "stacks.compose.docker.com",
298+
APIVersion: V1Beta1,
299+
},
300+
ObjectMeta: metav1.ObjectMeta{
301+
Name: internal.AppNameFromDir(appname),
302+
Namespace: "default", // FIXME
303+
},
304+
Spec: v1beta1.StackSpec{
305+
ComposeFile: string(composeFile),
306+
},
307+
}
308+
default:
309+
return fmt.Errorf("invalid stack version %q", stackVersion)
255310
}
256311
stackData, err := yaml.Marshal(stack)
257312
if err != nil {
@@ -274,7 +329,7 @@ func makeStack(appname string, targetDir string, data []byte) error {
274329
}
275330

276331
// Helm renders an app as an Helm Chart
277-
func Helm(appname string, composeFiles []string, settingsFile []string, env map[string]string, render bool) error {
332+
func Helm(appname string, composeFiles []string, settingsFile []string, env map[string]string, render bool, stackVersion string) error {
278333
targetDir := internal.AppNameFromDir(appname) + ".chart"
279334
if err := os.Mkdir(targetDir, 0755); err != nil && !os.IsExist(err) {
280335
return errors.Wrap(err, "failed to create Chart directory")
@@ -284,7 +339,7 @@ func Helm(appname string, composeFiles []string, settingsFile []string, env map[
284339
return err
285340
}
286341
if render {
287-
return helmRender(appname, targetDir, composeFiles, settingsFile, env)
342+
return helmRender(appname, targetDir, composeFiles, settingsFile, env, stackVersion)
288343
}
289344
data, err := ioutil.ReadFile(filepath.Join(appname, internal.ComposeFileName))
290345
if err != nil {
@@ -294,7 +349,7 @@ func Helm(appname string, composeFiles []string, settingsFile []string, env map[
294349
if err != nil {
295350
return errors.Wrap(err, "failed to parse docker-compose.yml, maybe because it is a template")
296351
}
297-
err = makeStack(appname, targetDir, data)
352+
err = makeStack(appname, targetDir, data, stackVersion)
298353
if err != nil {
299354
return err
300355
}

0 commit comments

Comments
 (0)