Skip to content

Commit d4dd93f

Browse files
committed
Merge branch 'master' of https://github.com/loft-sh/devspace
2 parents f4f421a + 4395bde commit d4dd93f

File tree

29 files changed

+891
-147
lines changed

29 files changed

+891
-147
lines changed

.vscode/launch.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
{
22
"version": "0.2.0",
33
"configurations": [
4+
{
5+
"name": "Launch e2e tests",
6+
"type": "go",
7+
"request": "launch",
8+
"mode": "test",
9+
"program": "${workspaceRoot}/e2e/e2e_suite_test.go",
10+
"args": [
11+
"-ginkgo.v"
12+
]
13+
},
414
{
515
"name": "nodejs > devspace up",
616
"type": "go",

CONTRIBUTING.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ This project is mainly written in Golang. To contribute code,
1919
4. Clone the project: `git clone https://github.com/[YOUR_USERNAME]/devspace && cd devspace`
2020
5. Run `go clean -modcache`
2121
6. Run `go mod vendor` to install the dependencies
22-
7. Make changes to the code (add new dependencies to the Gopkg.toml)
23-
8. Build the project, e.g. via `go build -o devspace.exe`
24-
9. Make changes
25-
10. Run tests: `go test ./...`
22+
7. Build the project, e.g. via `go build -o devspace.exe`
23+
8. Make changes
24+
9. Run unit tests: `./hack/coverage.bash`
25+
10. Run E2E tests: `cd e2e/ && go test -v -ginkgo.v`
2626
11. Format your code: `go fmt ./...`
27-
12. Commit changes
27+
12. Commit changes *([Please refer the commit message conventions](https://www.conventionalcommits.org/en/v1.0.0/))*
2828
13. Push commits
2929
14. Open pull request
3030

cmd/init.go

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd
22

33
import (
4+
"bytes"
45
"fmt"
56
"io/ioutil"
67
"net/http"
@@ -10,7 +11,9 @@ import (
1011
"strconv"
1112
"strings"
1213

13-
"github.com/loft-sh/devspace/pkg/devspace/imageselector"
14+
"github.com/loft-sh/devspace/cmd/flags"
15+
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
16+
yaml "gopkg.in/yaml.v3"
1417

1518
"github.com/loft-sh/devspace/pkg/devspace/compose"
1619
"github.com/loft-sh/devspace/pkg/devspace/hook"
@@ -205,14 +208,10 @@ func (cmd *InitCmd) Run(f factory.Factory) error {
205208
}
206209

207210
if selectedDeploymentOption == HelmChartOption {
208-
imageQuestion = "What is the main container image of this project which is deployed by this Helm chart? (e.g. ecr.io/project/image)"
211+
imageQuestion = "Which image do you want to develop with DevSpace?"
209212
err = configureManager.AddHelmDeployment(deploymentName)
210213
} else if selectedDeploymentOption == ManifestsOption || selectedDeploymentOption == KustomizeOption {
211-
if selectedDeploymentOption == ManifestsOption {
212-
imageQuestion = "What is the main container image of this project which is deployed by these manifests? (e.g. ecr.io/project/image)"
213-
} else {
214-
imageQuestion = "What is the main container image of this project which is deployed by this Kustomization? (e.g. ecr.io/project/image)"
215-
}
214+
imageQuestion = "Which image do you want to develop with DevSpace?"
216215
err = configureManager.AddKubectlDeployment(deploymentName, selectedDeploymentOption == KustomizeOption)
217216
}
218217

@@ -238,13 +237,24 @@ func (cmd *InitCmd) Run(f factory.Factory) error {
238237
for {
239238
image := ""
240239
if imageQuestion != "" {
240+
manifests, err := cmd.render(f, config)
241+
if err != nil {
242+
return errors.Wrap(err, "error rendering deployment")
243+
}
244+
245+
images, err := parseImages(manifests)
246+
if err != nil {
247+
return errors.Wrap(err, "error parsing images")
248+
}
249+
250+
if len(images) == 0 {
251+
return fmt.Errorf("no images found for the selected deployments")
252+
}
253+
241254
image, err = cmd.log.Question(&survey.QuestionOptions{
242-
Question: imageQuestion,
243-
ValidationMessage: "Please enter a valid container image from a Kubernetes pod (e.g. myregistry.tld/project/image)",
244-
ValidationFunc: func(name string) error {
245-
_, _, err := imageselector.GetStrippedDockerImageName(strings.ToLower(name))
246-
return err
247-
},
255+
Question: imageQuestion,
256+
DefaultValue: images[0],
257+
Options: images,
248258
})
249259
if err != nil {
250260
return err
@@ -727,3 +737,57 @@ func (cmd *InitCmd) addProfileConfig(config *latest.Config, imageName string) er
727737
}
728738
return nil
729739
}
740+
741+
func (cmd *InitCmd) render(f factory.Factory, config *latest.Config) (string, error) {
742+
// Save temporary file to render it
743+
renderPath := loader.ConfigPath("render.yaml")
744+
configLoader := loader.NewConfigLoader(renderPath)
745+
746+
err := configLoader.Save(config)
747+
defer os.Remove(renderPath)
748+
if err != nil {
749+
return "", err
750+
}
751+
752+
// Use the render command to render it.
753+
writer := &bytes.Buffer{}
754+
renderCmd := &RenderCmd{
755+
GlobalFlags: &flags.GlobalFlags{
756+
Silent: true,
757+
ConfigPath: renderPath,
758+
},
759+
SkipPush: true,
760+
SkipBuild: true,
761+
Writer: writer,
762+
}
763+
err = renderCmd.Run(f)
764+
if err != nil {
765+
return "", err
766+
}
767+
768+
return writer.String(), nil
769+
}
770+
771+
func parseImages(manifests string) ([]string, error) {
772+
images := []string{}
773+
774+
var doc yaml.Node
775+
dec := yaml.NewDecoder(bytes.NewReader([]byte(manifests)))
776+
for dec.Decode(&doc) == nil {
777+
path, err := yamlpath.NewPath("..image")
778+
if err != nil {
779+
return nil, err
780+
}
781+
782+
matches, err := path.Find(&doc)
783+
if err != nil {
784+
return nil, err
785+
}
786+
787+
for _, match := range matches {
788+
images = append(images, match.Value)
789+
}
790+
}
791+
792+
return images, nil
793+
}

cmd/init_test.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
package cmd
22

3+
import (
4+
"testing"
5+
6+
"gotest.tools/assert"
7+
"gotest.tools/assert/cmp"
8+
)
9+
310
/*import (
411
"io/ioutil"
512
"os"
@@ -303,3 +310,114 @@ func testInit(t *testing.T, testCase initTestCase) {
303310
})
304311
assert.NilError(t, err, "Error cleaning up in testCase %s", testCase.name)
305312
}*/
313+
314+
type parseImagesTestCase struct {
315+
name string
316+
manifests string
317+
expected []string
318+
}
319+
320+
func TestParseImages(t *testing.T) {
321+
testCases := []parseImagesTestCase{
322+
{
323+
name: `Single`,
324+
manifests: `
325+
apiVersion: apps/v1
326+
kind: Deployment
327+
metadata:
328+
name: "new"
329+
labels:
330+
"app.kubernetes.io/name": "devspace-app"
331+
"app.kubernetes.io/component": "test"
332+
"app.kubernetes.io/managed-by": "Helm"
333+
spec:
334+
replicas: 1
335+
strategy:
336+
type: Recreate
337+
selector:
338+
matchLabels:
339+
"app.kubernetes.io/name": "devspace-app"
340+
"app.kubernetes.io/component": "test"
341+
"app.kubernetes.io/managed-by": "Helm"
342+
template:
343+
metadata:
344+
labels:
345+
"app.kubernetes.io/name": "devspace-app"
346+
"app.kubernetes.io/component": "test"
347+
"app.kubernetes.io/managed-by": "Helm"
348+
spec:
349+
containers:
350+
- image: "username/app"
351+
name: "container-0"
352+
`,
353+
expected: []string{
354+
"username/app",
355+
},
356+
},
357+
{
358+
name: `Multiple`,
359+
manifests: `
360+
---
361+
# Source: my-app/templates/service.yaml
362+
apiVersion: v1
363+
kind: Service
364+
metadata:
365+
name: php
366+
labels:
367+
release: "test-helm"
368+
spec:
369+
ports:
370+
- port: 80
371+
protocol: TCP
372+
selector:
373+
release: "test-helm"
374+
---
375+
# Source: my-app/templates/deployment.yaml
376+
apiVersion: apps/v1
377+
kind: Deployment
378+
metadata:
379+
name: test-helm
380+
labels:
381+
release: "test-helm"
382+
spec:
383+
replicas: 1
384+
selector:
385+
matchLabels:
386+
release: "test-helm"
387+
template:
388+
metadata:
389+
annotations:
390+
revision: "1"
391+
labels:
392+
release: "test-helm"
393+
spec:
394+
containers:
395+
- name: default
396+
image: "php"
397+
`,
398+
expected: []string{
399+
"php",
400+
},
401+
},
402+
}
403+
404+
for _, testCase := range testCases {
405+
manifests := testCase.manifests
406+
407+
actual, err := parseImages(manifests)
408+
assert.NilError(
409+
t,
410+
err,
411+
"Unexpected error in test case %s",
412+
testCase.name,
413+
)
414+
415+
expected := testCase.expected
416+
assert.Assert(
417+
t,
418+
cmp.DeepEqual(expected, actual),
419+
"Unexpected values in test case %s",
420+
testCase.name,
421+
)
422+
}
423+
}

docs/pages/configuration/deployments/basics.mdx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ deployments:
107107
</TabItem>
108108
</Tabs>
109109
110-
:::info Sequential Deployment
111-
Unlike images which are build in parallel, deployments will be deployed sequentially following the order in which they are specified in the `devspace.yaml`.
110+
:::info Sequential and Concurrent Deployment
111+
Unlike images, which are built in parallel by default, deployments will be deployed sequentially following the order in which they are specified in the `devspace.yaml`. If a deployment has `concurrent: true` set, then it will run before any sequential deployments and in parallel with other deployments that also have concurrency enabled.
112112
:::
113113

114114
## Run Deployments
@@ -123,8 +123,11 @@ The following flags are available for all commands that trigger the deployment p
123123

124124

125125
## Deployment Process
126-
DevSpace loads the `deployments` configuration from `devspace.yaml` and builds one deployment after another in the order that they are specified in the `deployments` array. Additionally, DevSpace also deploys related projects speficied in `dependencies`.
126+
DevSpace loads the `deployments` configuration from `devspace.yaml` and by default deploys each deployment sequentially in the order that they are specified in the `deployments` array. Alternatively, some or all deployments can be configured to deploy in parallel by setting `concurrent: true`. Deployments with concurrency enabled will deploy before sequential deployments. Additionally, DevSpace also deploys related projects specified in `dependencies`.
127127

128+
:::warning Helm hooks and concurrency
129+
When using concurrency for Helm deployments that have Helm hooks, be cautious if those hooks depend on resources created by other deployments. You may want such a deployments to be run sequentially after concurrent deployments are completed. Otherwise, appropriate retry logic will be necessary for the affected Helm hook in order to avoid deployment failure.
130+
:::
128131

129132
### 1. Deploy Dependencies
130133

docs/pages/fragments/config-deployments.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ deployments: # struct[] | Array of deployments
33
- name: my-deployment # string | Name of the deployment
44
namespace: "" # string | Namespace to deploy to (Default: "" = namespace of the active namespace/Space)
55
disabled: false # bool | If true, the deployment will not be deployed, purged or rendered
6+
concurrent: false # bool | Deploy concurrently with other deployments that also have concurrency enabled (Default: false)
67
helm: ... # struct | Use Helm as deployment tool and set options for Helm
78
kubectl: ... # struct | Use "kubectl apply" as deployment tool and set options for kubectl
89
```

docs/versioned_docs/version-5.18/configuration/development/auto-reloading.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ dev:
3535
- custom-manifests
3636
```
3737
38-
Take a look at the [redeploy-instead-of-hot-reload exmaple](https://github.com/loft-sh/devspace/tree/master/examples/redeploy-instead-of-hot-reload) to see how to disable hot reloading at all and enable redeployment on every file change instead.
38+
Take a look at the [redeploy-instead-of-hot-reload example](https://github.com/loft-sh/devspace/tree/master/examples/redeploy-instead-of-hot-reload) to see how to disable hot reloading at all and enable redeployment on every file change instead.
3939
4040
4141
## Configuration

e2e/framework/util.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func CleanupTempDir(initialDir, tempDir string) {
9191
}
9292

9393
func CopyToTempDir(relativePath string) (string, error) {
94-
dir, err := ioutil.TempDir("", "")
94+
dir, err := ioutil.TempDir("", "temp-*")
9595
if err != nil {
9696
return "", err
9797
}

0 commit comments

Comments
 (0)