Skip to content

Commit e816a5f

Browse files
committed
fix: init command allows manually entering an image or skipping when images are not found
1 parent c8a7310 commit e816a5f

2 files changed

Lines changed: 190 additions & 28 deletions

File tree

cmd/init.go

Lines changed: 71 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -347,17 +347,59 @@ func (cmd *InitCmd) initDevspace(f factory.Factory, configLoader loader.ConfigLo
347347
return errors.Wrap(err, "error parsing images")
348348
}
349349

350-
if len(images) == 0 {
351-
return fmt.Errorf("no images found for the selected deployments")
350+
imageManual := "Manually enter the image I want to work on"
351+
imageSkip := "Skip (do not add dev configuration for any images)"
352+
imageAnswer := ""
353+
354+
if len(images) > 0 {
355+
imageAnswer, err = cmd.log.Question(&survey.QuestionOptions{
356+
Question: "Which image do you want to develop with DevSpace?",
357+
DefaultValue: images[0],
358+
Options: append(images, []string{imageManual, imageSkip}...),
359+
})
360+
if err != nil {
361+
return err
362+
}
363+
} else {
364+
imageAnswer, err = cmd.log.Question(&survey.QuestionOptions{
365+
Question: "Couldn’t find any images in your manifests/helm charts. Do you want to skip this step?",
366+
Options: []string{imageManual, imageSkip},
367+
})
368+
if err != nil {
369+
return err
370+
}
352371
}
353372

354-
image, err = cmd.log.Question(&survey.QuestionOptions{
355-
Question: "Which image do you want to develop with DevSpace?",
356-
DefaultValue: images[0],
357-
Options: images,
358-
})
359-
if err != nil {
360-
return err
373+
if imageAnswer == imageSkip {
374+
break
375+
} else if imageAnswer == imageManual {
376+
imageQuestion := "What is the main container image of this project?"
377+
378+
if selectedDeploymentOption == DeployOptionHelm {
379+
imageQuestion = "What is the main container image of this project which is deployed by this Helm chart? (e.g. ecr.io/project/image)"
380+
}
381+
382+
if selectedDeploymentOption == DeployOptionKubectl {
383+
imageQuestion = "What is the main container image of this project which is deployed by these manifests? (e.g. ecr.io/project/image)"
384+
}
385+
386+
if selectedDeploymentOption == DeployOptionKustomize {
387+
imageQuestion = "What is the main container image of this project which is deployed by this Kustomization? (e.g. ecr.io/project/image)"
388+
}
389+
390+
image, err = cmd.log.Question(&survey.QuestionOptions{
391+
Question: imageQuestion,
392+
ValidationMessage: "Please enter a valid container image from a Kubernetes pod (e.g. myregistry.tld/project/image)",
393+
ValidationFunc: func(name string) error {
394+
_, _, err := dockerfile.GetStrippedDockerImageName(strings.ToLower(name))
395+
return err
396+
},
397+
})
398+
if err != nil {
399+
return err
400+
}
401+
} else {
402+
image = imageAnswer
361403
}
362404
}
363405

@@ -371,27 +413,29 @@ func (cmd *InitCmd) initDevspace(f factory.Factory, configLoader loader.ConfigLo
371413
}
372414
}
373415

374-
image = config.Images[imageName].Image
375-
376416
// Determine app port
377417
portString := ""
378418

379-
// Try to get ports from dockerfile
380-
ports, err := dockerfile.GetPorts(config.Images[imageName].Dockerfile)
381-
if err == nil {
382-
if len(ports) == 1 {
383-
portString = strconv.Itoa(ports[0])
384-
} else if len(ports) > 1 {
385-
portString, err = cmd.log.Question(&survey.QuestionOptions{
386-
Question: "Which port is your application listening on?",
387-
DefaultValue: strconv.Itoa(ports[0]),
388-
})
389-
if err != nil {
390-
return err
391-
}
419+
if len(config.Images) > 0 {
420+
image = config.Images[imageName].Image
392421

393-
if portString == "" {
422+
// Try to get ports from dockerfile
423+
ports, err := dockerfile.GetPorts(config.Images[imageName].Dockerfile)
424+
if err == nil {
425+
if len(ports) == 1 {
394426
portString = strconv.Itoa(ports[0])
427+
} else if len(ports) > 1 {
428+
portString, err = cmd.log.Question(&survey.QuestionOptions{
429+
Question: "Which port is your application listening on?",
430+
DefaultValue: strconv.Itoa(ports[0]),
431+
})
432+
if err != nil {
433+
return err
434+
}
435+
436+
if portString == "" {
437+
portString = strconv.Itoa(ports[0])
438+
}
395439
}
396440
}
397441
}
@@ -722,7 +766,7 @@ func (cmd *InitCmd) render(f factory.Factory, config *latest.Config) (string, er
722766
err := loader.Save(renderPath, config)
723767
defer os.Remove(renderPath)
724768
if err != nil {
725-
return "", err
769+
return "", errors.Wrap(err, "temp render.yaml")
726770
}
727771

728772
// Use the render command to render it.
@@ -741,8 +785,7 @@ func (cmd *InitCmd) render(f factory.Factory, config *latest.Config) (string, er
741785
}
742786
err = renderCmd.RunDefault(f)
743787
if err != nil {
744-
f.GetLog().Debugf("error rendering chart: %v", err)
745-
return "", nil
788+
return "", errors.Wrap(err, "devspace render")
746789
}
747790

748791
return writer.String(), nil

cmd/init_test.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package cmd
2+
3+
import (
4+
"testing"
5+
6+
"gotest.tools/assert"
7+
"gotest.tools/assert/cmp"
8+
)
9+
10+
type parseImagesTestCase struct {
11+
name string
12+
manifests string
13+
expected []string
14+
}
15+
16+
func TestParseImages(t *testing.T) {
17+
testCases := []parseImagesTestCase{
18+
{
19+
name: `Single`,
20+
manifests: `
21+
apiVersion: apps/v1
22+
kind: Deployment
23+
metadata:
24+
name: "new"
25+
labels:
26+
"app.kubernetes.io/name": "devspace-app"
27+
"app.kubernetes.io/component": "test"
28+
"app.kubernetes.io/managed-by": "Helm"
29+
spec:
30+
replicas: 1
31+
strategy:
32+
type: Recreate
33+
selector:
34+
matchLabels:
35+
"app.kubernetes.io/name": "devspace-app"
36+
"app.kubernetes.io/component": "test"
37+
"app.kubernetes.io/managed-by": "Helm"
38+
template:
39+
metadata:
40+
labels:
41+
"app.kubernetes.io/name": "devspace-app"
42+
"app.kubernetes.io/component": "test"
43+
"app.kubernetes.io/managed-by": "Helm"
44+
spec:
45+
containers:
46+
- image: "username/app"
47+
name: "container-0"
48+
`,
49+
expected: []string{
50+
"username/app",
51+
},
52+
},
53+
{
54+
name: `Multiple`,
55+
manifests: `
56+
---
57+
# Source: my-app/templates/service.yaml
58+
apiVersion: v1
59+
kind: Service
60+
metadata:
61+
name: php
62+
labels:
63+
release: "test-helm"
64+
spec:
65+
ports:
66+
- port: 80
67+
protocol: TCP
68+
selector:
69+
release: "test-helm"
70+
---
71+
# Source: my-app/templates/deployment.yaml
72+
apiVersion: apps/v1
73+
kind: Deployment
74+
metadata:
75+
name: test-helm
76+
labels:
77+
release: "test-helm"
78+
spec:
79+
replicas: 1
80+
selector:
81+
matchLabels:
82+
release: "test-helm"
83+
template:
84+
metadata:
85+
annotations:
86+
revision: "1"
87+
labels:
88+
release: "test-helm"
89+
spec:
90+
containers:
91+
- name: default
92+
image: "php"
93+
`,
94+
expected: []string{
95+
"php",
96+
},
97+
},
98+
}
99+
100+
for _, testCase := range testCases {
101+
manifests := testCase.manifests
102+
103+
actual, err := parseImages(manifests)
104+
assert.NilError(
105+
t,
106+
err,
107+
"Unexpected error in test case %s",
108+
testCase.name,
109+
)
110+
111+
expected := testCase.expected
112+
assert.Assert(
113+
t,
114+
cmp.DeepEqual(expected, actual),
115+
"Unexpected values in test case %s",
116+
testCase.name,
117+
)
118+
}
119+
}

0 commit comments

Comments
 (0)