Skip to content

Commit 83f8e53

Browse files
authored
Merge pull request #486 from devspace-cloud/cloud-refactor
Implement devspace add component
2 parents 8631993 + ffc3e64 commit 83f8e53

File tree

8 files changed

+514
-7
lines changed

8 files changed

+514
-7
lines changed

cmd/add/add.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ func NewAddCmd() *cobra.Command {
2424
addCmd.AddCommand(newPackageCmd())
2525
addCmd.AddCommand(newImageCmd())
2626
addCmd.AddCommand(newDeploymentCmd())
27+
addCmd.AddCommand(newComponentCmd())
2728

2829
return addCmd
2930
}

cmd/add/component.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package add
2+
3+
import (
4+
"github.com/devspace-cloud/devspace/pkg/devspace/chart"
5+
"github.com/devspace-cloud/devspace/pkg/devspace/config/configutil"
6+
"github.com/devspace-cloud/devspace/pkg/devspace/config/versions/latest"
7+
"github.com/devspace-cloud/devspace/pkg/util/log"
8+
"github.com/devspace-cloud/devspace/pkg/util/stdinutil"
9+
"github.com/mgutz/ansi"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
type componentCmd struct {
14+
Deployment string
15+
}
16+
17+
func newComponentCmd() *cobra.Command {
18+
cmd := &componentCmd{}
19+
20+
componentCmd := &cobra.Command{
21+
Use: "component [name]",
22+
Short: "Add a component to the chart",
23+
Long: `
24+
#######################################################
25+
############## devspace add component #################
26+
#######################################################
27+
Adds a component to the chart.
28+
Run 'devspace list available-components' to see all available components
29+
30+
Examples:
31+
devspace add component mysql
32+
#######################################################
33+
`,
34+
Args: cobra.ExactArgs(1),
35+
Run: cmd.RunAddComponent,
36+
}
37+
38+
componentCmd.Flags().StringVarP(&cmd.Deployment, "deployment", "d", "", "The deployment name to use")
39+
40+
return componentCmd
41+
}
42+
43+
// RunAddPackage executes the add package command logic
44+
func (cmd *componentCmd) RunAddComponent(cobraCmd *cobra.Command, args []string) {
45+
// Set config root
46+
configExists, err := configutil.SetDevSpaceRoot()
47+
if err != nil {
48+
log.Fatal(err)
49+
}
50+
if !configExists {
51+
log.Fatal("Couldn't find a DevSpace configuration. Please run `devspace init`")
52+
}
53+
54+
// Get config
55+
config := configutil.GetConfig()
56+
if config.Deployments == nil || len(*config.Deployments) == 0 {
57+
log.Fatal("No deployment specified")
58+
}
59+
60+
// get all helm deployments
61+
var helmDeployments []*latest.DeploymentConfig
62+
for _, deploy := range *config.Deployments {
63+
if deploy.Helm != nil {
64+
helmDeployments = append(helmDeployments, deploy)
65+
}
66+
}
67+
if len(helmDeployments) == 0 {
68+
log.Fatal("There is no helm deployment specified in configuration")
69+
}
70+
71+
helmDeployment := helmDeployments[0]
72+
if cmd.Deployment != "" {
73+
found := false
74+
for _, deploy := range helmDeployments {
75+
if *deploy.Name == cmd.Deployment {
76+
helmDeployment = deploy
77+
found = true
78+
break
79+
}
80+
}
81+
82+
if found == false {
83+
log.Fatalf("Couldn't find a deployment with name %s", cmd.Deployment)
84+
}
85+
} else if len(helmDeployments) > 1 {
86+
deployments := []string{}
87+
for _, deploy := range helmDeployments {
88+
deployments = append(deployments, *deploy.Name)
89+
}
90+
91+
deploymentName := *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{
92+
Question: "Select a deployment",
93+
Options: deployments,
94+
})
95+
96+
for _, deploy := range helmDeployments {
97+
if *deploy.Name == deploymentName {
98+
helmDeployment = deploy
99+
break
100+
}
101+
}
102+
}
103+
104+
// Add the component to the chart
105+
err = chart.AddComponent(*helmDeployment.Helm.ChartPath, args[0])
106+
if err != nil {
107+
log.Fatalf("Error adding component %s: %v", args[0], err)
108+
}
109+
110+
log.Donef("Successfully added the component %s\nRun:\n- `%s` to update the application", args[0], ansi.Color("devspace deploy", "white+b"))
111+
}

cmd/list/available_components.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package list
2+
3+
import (
4+
"github.com/devspace-cloud/devspace/pkg/devspace/chart"
5+
"github.com/devspace-cloud/devspace/pkg/util/log"
6+
"github.com/spf13/cobra"
7+
)
8+
9+
type availableComponentsCmd struct{}
10+
11+
func newAvailableComponentsCmd() *cobra.Command {
12+
cmd := &availableComponentsCmd{}
13+
14+
availableComponentsCmd := &cobra.Command{
15+
Use: "available-components",
16+
Short: "Lists all available components",
17+
Long: `
18+
#######################################################
19+
######### devspace list available-components ##########
20+
#######################################################
21+
Lists all the available components that can be used
22+
in devspace
23+
#######################################################
24+
`,
25+
Args: cobra.NoArgs,
26+
Run: cmd.RunListAvailableComponents,
27+
}
28+
29+
return availableComponentsCmd
30+
}
31+
32+
// RunListPackage runs the list available components logic
33+
func (cmd *availableComponentsCmd) RunListAvailableComponents(cobraCmd *cobra.Command, args []string) {
34+
headerColumnNames := []string{
35+
"Name",
36+
"Description",
37+
}
38+
values := [][]string{}
39+
40+
components, err := chart.ListAvailableComponents()
41+
if err != nil {
42+
log.Fatalf("Error listing components: %v", err)
43+
}
44+
45+
for _, component := range components {
46+
values = append(values, []string{
47+
component.Name,
48+
component.Description,
49+
})
50+
}
51+
52+
log.PrintTable(headerColumnNames, values)
53+
}

cmd/list/list.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func NewListCmd() *cobra.Command {
2525
listCmd.AddCommand(newConfigsCmd())
2626
listCmd.AddCommand(newVarsCmd())
2727
listCmd.AddCommand(newProvidersCmd())
28+
listCmd.AddCommand(newAvailableComponentsCmd())
2829

2930
return listCmd
3031
}

pkg/devspace/chart/component.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package chart
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
11+
"github.com/devspace-cloud/devspace/pkg/devspace/generator"
12+
yaml "gopkg.in/yaml.v2"
13+
)
14+
15+
// ListAvailableComponents lists all available devspace components
16+
func ListAvailableComponents() ([]*generator.ComponentSchema, error) {
17+
// Create component generator
18+
componentGenerator, err := generator.NewComponentGenerator("")
19+
if err != nil {
20+
return nil, fmt.Errorf("Error initializing component generator: %v", err)
21+
}
22+
23+
return componentGenerator.ListComponents()
24+
}
25+
26+
// AddComponent adds a component with the given name to the chart
27+
func AddComponent(chartPath string, name string) error {
28+
// Check if devspace chart
29+
_, err := os.Stat(filepath.Join(chartPath, "devspace-version.yaml"))
30+
if os.IsNotExist(err) {
31+
return errors.New("Chart is not a devspace chart. `devspace add component` only works with the devspace chart")
32+
}
33+
34+
// Create component generator
35+
componentGenerator, err := generator.NewComponentGenerator(chartPath)
36+
if err != nil {
37+
return fmt.Errorf("Error initializing component generator: %v", err)
38+
}
39+
40+
// Get values.yaml
41+
valuesYaml := filepath.Join(chartPath, "values.yaml")
42+
content, err := ioutil.ReadFile(valuesYaml)
43+
if err != nil {
44+
return err
45+
}
46+
47+
// Split into content lines, we don't parse the values.yaml here
48+
// because all comments would be lost on write so we do a little
49+
// workaround by inserting the components directly after the
50+
// components and volumes identifier
51+
contentLines := strings.Split(string(content), "\n")
52+
53+
// Get component template
54+
template, err := componentGenerator.GetComponentTemplate(name)
55+
if err != nil {
56+
return fmt.Errorf("Error retrieving template: %v", err)
57+
}
58+
59+
// Fill components
60+
if len(template.Components) > 0 {
61+
out, err := yaml.Marshal(template.Components)
62+
if err != nil {
63+
return err
64+
}
65+
66+
componentsString := "components:\n" + strings.TrimSpace(string(out))
67+
contentLines = insertAfterLine(componentsString, "components:", contentLines)
68+
}
69+
70+
// Fill volumes
71+
if len(template.Volumes) > 0 {
72+
out, err := yaml.Marshal(template.Volumes)
73+
if err != nil {
74+
return err
75+
}
76+
77+
volumesString := "volumes:\n" + strings.TrimSpace(string(out))
78+
contentLines = insertAfterLine(volumesString, "volumes:", contentLines)
79+
}
80+
81+
err = ioutil.WriteFile(valuesYaml, []byte(strings.Join(contentLines, "\n")), 0666)
82+
if err != nil {
83+
return err
84+
}
85+
86+
return nil
87+
}
88+
89+
func insertAfterLine(insertString, prefix string, contentLines []string) []string {
90+
index := -1
91+
for idx, line := range contentLines {
92+
if strings.HasPrefix(line, prefix) {
93+
index = idx
94+
break
95+
}
96+
}
97+
98+
// Check if found
99+
if index > -1 {
100+
// Set components: [] to components: etc.
101+
contentLines[index] = insertString
102+
} else {
103+
contentLines = append([]string{insertString}, contentLines...)
104+
}
105+
106+
return contentLines
107+
}

pkg/devspace/config/configs/schema.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ type VarsWrapper struct {
2424

2525
// Variable describes the var definition
2626
type Variable struct {
27-
Name *string `yaml:"name"`
28-
Default *string `yaml:"default,omitempty"`
29-
Question *string `yaml:"question,omitempty"`
30-
RegexPattern *string `yaml:"regexPattern,omitempty"`
27+
Name *string `yaml:"name"`
28+
Options *[]string `yaml:"options,omitempty"`
29+
Default *string `yaml:"default,omitempty"`
30+
Question *string `yaml:"question,omitempty"`
31+
RegexPattern *string `yaml:"regexPattern,omitempty"`
3132
}

pkg/devspace/config/configutil/load.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,10 @@ func AskQuestion(variable *configs.Variable) interface{} {
9292
if variable.Default != nil {
9393
params.DefaultValue = *variable.Default
9494
}
95-
if variable.RegexPattern != nil {
95+
96+
if variable.Options != nil {
97+
params.Options = *variable.Options
98+
} else if variable.RegexPattern != nil {
9699
params.ValidationRegexPattern = *variable.RegexPattern
97100
}
98101
}
@@ -171,15 +174,16 @@ func LoadConfigs(configs *configs.Configs, path string) error {
171174
return yaml.UnmarshalStrict(yamlFileContent, configs)
172175
}
173176

174-
func resolveVars(yamlFileContent []byte) ([]byte, error) {
177+
// CustomResolveVars resolves variables with a custom replace function
178+
func CustomResolveVars(yamlFileContent []byte, matchFn func(string, string) bool, replaceFn func(string) interface{}) ([]byte, error) {
175179
rawConfig := make(map[interface{}]interface{})
176180

177181
err := yaml.Unmarshal(yamlFileContent, &rawConfig)
178182
if err != nil {
179183
return nil, err
180184
}
181185

182-
walk.Walk(rawConfig, varMatchFn, varReplaceFn)
186+
walk.Walk(rawConfig, matchFn, replaceFn)
183187

184188
out, err := yaml.Marshal(rawConfig)
185189
if err != nil {
@@ -188,3 +192,7 @@ func resolveVars(yamlFileContent []byte) ([]byte, error) {
188192

189193
return out, nil
190194
}
195+
196+
func resolveVars(yamlFileContent []byte) ([]byte, error) {
197+
return CustomResolveVars(yamlFileContent, varMatchFn, varReplaceFn)
198+
}

0 commit comments

Comments
 (0)