From e8ae5bc698fdd9a7f0ff2b836e296ef52ee7a93c Mon Sep 17 00:00:00 2001 From: gentele Date: Thu, 6 Sep 2018 01:56:35 +0200 Subject: [PATCH 1/7] new config schema + configutils (merging for maps is still todo) --- Gopkg.lock | 11 +- Gopkg.toml | 8 + cmd/add.go | 83 +++--- cmd/down.go | 13 +- cmd/init.go | 321 +++++++++++------------- cmd/list.go | 67 ++--- cmd/remove.go | 37 ++- cmd/reset.go | 70 ++---- cmd/status.go | 51 ++-- cmd/status_sync.go | 3 +- cmd/up.go | 131 +++++----- pkg/devspace/clients/helm/client.go | 98 ++++---- pkg/devspace/clients/kubectl/client.go | 16 +- pkg/devspace/config/config.go | 190 -------------- pkg/devspace/config/configutil/get.go | 74 ++++++ pkg/devspace/config/configutil/load.go | 77 ++++++ pkg/devspace/config/configutil/make.go | 29 +++ pkg/devspace/config/configutil/merge.go | 47 ++++ pkg/devspace/config/configutil/save.go | 250 ++++++++++++++++++ pkg/devspace/config/configutil/set.go | 9 + pkg/devspace/config/v1/schema.go | 132 +++++----- pkg/devspace/kaniko/kaniko.go | 13 +- pkg/devspace/registry/registry.go | 210 +++++++++------- pkg/util/stdinutil/stdin.go | 10 +- 24 files changed, 1089 insertions(+), 861 deletions(-) delete mode 100644 pkg/devspace/config/config.go create mode 100644 pkg/devspace/config/configutil/get.go create mode 100644 pkg/devspace/config/configutil/load.go create mode 100644 pkg/devspace/config/configutil/make.go create mode 100644 pkg/devspace/config/configutil/merge.go create mode 100644 pkg/devspace/config/configutil/save.go create mode 100644 pkg/devspace/config/configutil/set.go diff --git a/Gopkg.lock b/Gopkg.lock index 06eb27d626..66cd529360 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -84,7 +84,7 @@ version = "v3.5.1" [[projects]] - digest = "1:9453458ba06a7b4b5a6cb522b479f449b4d9387bcf33f9a7e3fdb834569d7a40" + digest = "1:2aaf2cc045d0219bba79655e4df795b973168c310574669cb75786684f7287d3" name = "github.com/bmatcuk/doublestar" packages = ["."] pruneopts = "UT" @@ -118,7 +118,7 @@ revision = "245ca4659e09e9745f3cc1217bf56e946509220c" [[projects]] - digest = "1:2cd0a6f821ce12a89c8b13b34dea6ec6d8df8643bc4338fa620cd6d3a021c512" + digest = "1:05c885988ade841b36bd201962b40a2566c62dc64225a26e4e8b6578e56e0ce9" name = "github.com/docker/docker" packages = [ "api/types", @@ -563,7 +563,7 @@ [[projects]] branch = "master" - digest = "1:8b52c2c0f2f7cf73a08f56d2497434cb7f548015b9f10e23f0ce3344a3503dab" + digest = "1:a9a0721dd9558d3b307fcb9bf4f713968a22528ebab7d0c95252619deba67ee0" name = "github.com/otiai10/copy" packages = ["."] pruneopts = "UT" @@ -1350,7 +1350,7 @@ version = "kubernetes-1.10.0" [[projects]] - digest = "1:71a6663e87278aaabcbe47b8be5b04cc31299f77ad65598dc033144b55e7860e" + digest = "1:0e624fac3318e17179da766a11bb68a8644edf0efb64cbef526282491e2301ea" name = "k8s.io/helm" packages = [ "cmd/helm/installer", @@ -1395,7 +1395,7 @@ [[projects]] branch = "release-1.10" - digest = "1:d35c5142e54d08f2851c06df0c1d9964767a80ed26bfbcedfa3705d87aaeab5f" + digest = "1:e054b6209d730dc4476b69d9f0bc8461f26f161ac50192b01db3019915d95a93" name = "k8s.io/kubernetes" packages = [ "pkg/api/events", @@ -1591,6 +1591,7 @@ "github.com/daviddengcn/go-colortext", "github.com/docker/docker/pkg/term", "github.com/foomo/htpasswd", + "github.com/imdario/mergo", "github.com/juju/errors", "github.com/mitchellh/go-homedir", "github.com/otiai10/copy", diff --git a/Gopkg.toml b/Gopkg.toml index 732fabaab7..94a453e38b 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -105,3 +105,11 @@ [[constraint]] name = "github.com/badgerodon/penv" revision = "7a4c6d64fa119b52afe2eb3f0801886bd8785318" + +[[constraint]] + revision = "ee51b09a773c7e7e4e32168cbec4d47a014161b4" + name = "github.com/covexo/yamlq" + +[[constraint]] + name = "github.com/imdario/mergo" + version = "v0.3.6" diff --git a/cmd/add.go b/cmd/add.go index 6459e890c6..b8fbeda1fa 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/covexo/devspace/pkg/devspace/config" + "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/devspace/config/v1" "github.com/covexo/devspace/pkg/util/log" "github.com/spf13/cobra" @@ -116,10 +116,10 @@ func init() { // RunAddSync executes the add sync command logic func (cmd *AddCmd) RunAddSync(cobraCmd *cobra.Command, args []string) { - loadConfig(&cmd.workdir, &cmd.privateConfig, &cmd.dsConfig) + config := configutil.GetConfig(false) if cmd.syncFlags.Selector == "" { - cmd.syncFlags.Selector = "release=" + cmd.privateConfig.Release.Name + cmd.syncFlags.Selector = "release=" + *config.DevSpace.Release.Name } labelSelectorMap, err := parseSelectors(cmd.syncFlags.Selector) @@ -128,25 +128,26 @@ func (cmd *AddCmd) RunAddSync(cobraCmd *cobra.Command, args []string) { log.Fatalf("Error parsing selectors: %s", err.Error()) } - excludedPaths := make([]string, 0, 0) + excludedPaths := make([]*string, 0, 0) if cmd.syncFlags.ExcludedPaths != "" { - excludedPaths = strings.Split(cmd.syncFlags.ExcludedPaths, ",") + excludedPathStrings := strings.Split(cmd.syncFlags.ExcludedPaths, ",") - for k, v := range excludedPaths { - excludedPaths[k] = strings.TrimSpace(v) + for _, v := range excludedPathStrings { + excludedPath := strings.TrimSpace(v) + excludedPaths = append(excludedPaths, &excludedPath) } } - cmd.dsConfig.SyncPaths = append(cmd.dsConfig.SyncPaths, &v1.SyncPath{ - ResourceType: cmd.syncFlags.ResourceType, + config.DevSpace.Sync = append(config.DevSpace.Sync, &v1.SyncConfig{ + ResourceType: configutil.String(cmd.syncFlags.ResourceType), LabelSelector: labelSelectorMap, - ContainerPath: cmd.syncFlags.ContainerPath, - LocalSubPath: cmd.syncFlags.LocalPath, + ContainerPath: configutil.String(cmd.syncFlags.ContainerPath), + LocalSubPath: configutil.String(cmd.syncFlags.LocalPath), ExcludeRegex: excludedPaths, }) - err = config.SaveConfig(cmd.dsConfig) + err = configutil.SaveConfig() if err != nil { log.Fatalf("Couldn't save config file: %s", err.Error()) @@ -155,10 +156,10 @@ func (cmd *AddCmd) RunAddSync(cobraCmd *cobra.Command, args []string) { // RunAddPort executes the add port command logic func (cmd *AddCmd) RunAddPort(cobraCmd *cobra.Command, args []string) { - loadConfig(&cmd.workdir, &cmd.privateConfig, &cmd.dsConfig) + config := configutil.GetConfig(false) if cmd.portFlags.Selector == "" { - cmd.portFlags.Selector = "release=" + cmd.privateConfig.Release.Name + cmd.portFlags.Selector = "release=" + *config.DevSpace.Release.Name } labelSelectorMap, err := parseSelectors(cmd.portFlags.Selector) @@ -175,37 +176,39 @@ func (cmd *AddCmd) RunAddPort(cobraCmd *cobra.Command, args []string) { cmd.insertOrReplacePortMapping(labelSelectorMap, portMappings) - err = config.SaveConfig(cmd.dsConfig) + err = configutil.SaveConfig() if err != nil { log.Fatalf("Couldn't save config file: %s", err.Error()) } } -func (cmd *AddCmd) insertOrReplacePortMapping(labelSelectorMap map[string]string, portMappings []*v1.PortMapping) { +func (cmd *AddCmd) insertOrReplacePortMapping(labelSelectorMap map[string]*string, portMappings []*v1.PortMapping) { + config := configutil.GetConfig(false) + // Check if we should add to existing port mapping - for _, v := range cmd.dsConfig.PortForwarding { - if v.ResourceType == cmd.portFlags.ResourceType && isMapEqual(v.LabelSelector, labelSelectorMap) { + for _, v := range config.DevSpace.PortForwarding { + if *v.ResourceType == cmd.portFlags.ResourceType && isMapEqual(v.LabelSelector, labelSelectorMap) { v.PortMappings = append(v.PortMappings, portMappings...) return } } - cmd.dsConfig.PortForwarding = append(cmd.dsConfig.PortForwarding, &v1.PortForwarding{ - ResourceType: cmd.portFlags.ResourceType, + config.DevSpace.PortForwarding = append(config.DevSpace.PortForwarding, &v1.PortForwardingConfig{ + ResourceType: configutil.String(cmd.portFlags.ResourceType), LabelSelector: labelSelectorMap, PortMappings: portMappings, }) } -func isMapEqual(map1 map[string]string, map2 map[string]string) bool { +func isMapEqual(map1 map[string]*string, map2 map[string]*string) bool { if len(map1) != len(map2) { return false } for k, v := range map1 { - if map2[k] != v { + if *map2[k] != *v { return false } } @@ -214,8 +217,6 @@ func isMapEqual(map1 map[string]string, map2 map[string]string) bool { } func parsePortMappings(portMappingsString string) ([]*v1.PortMapping, error) { - var err error - portMappings := make([]*v1.PortMapping, 0, 1) portMappingsSplitted := strings.Split(portMappingsString, ",") @@ -227,27 +228,25 @@ func parsePortMappings(portMappingsString string) ([]*v1.PortMapping, error) { } portMappingStruct := &v1.PortMapping{} + firstPort, err := strconv.Atoi(portMapping[0]) - if len(portMapping) == 1 { - portMappingStruct.LocalPort, err = strconv.Atoi(portMapping[0]) + if err != nil { + return nil, err + } - if err != nil { - return nil, err - } + if len(portMapping) == 1 { + portMappingStruct.LocalPort = &firstPort portMappingStruct.RemotePort = portMappingStruct.LocalPort } else { - portMappingStruct.LocalPort, err = strconv.Atoi(portMapping[0]) - - if err != nil { - return nil, err - } + portMappingStruct.LocalPort = &firstPort - portMappingStruct.RemotePort, err = strconv.Atoi(portMapping[1]) + secondPort, err := strconv.Atoi(portMapping[1]) if err != nil { return nil, err } + portMappingStruct.RemotePort = &secondPort } portMappings = append(portMappings, portMappingStruct) @@ -256,23 +255,23 @@ func parsePortMappings(portMappingsString string) ([]*v1.PortMapping, error) { return portMappings, nil } -func parseSelectors(selector string) (map[string]string, error) { - selectorMap := make(map[string]string) +func parseSelectors(selectorString string) (map[string]*string, error) { + selectorMap := make(map[string]*string) - if selector == "" { + if selectorString == "" { return selectorMap, nil } - selectors := strings.Split(selector, ",") + selectors := strings.Split(selectorString, ",") for _, v := range selectors { keyValue := strings.Split(v, "=") if len(keyValue) != 2 { - return nil, fmt.Errorf("Wrong selector format: %s", selector) + return nil, fmt.Errorf("Wrong selector format: %s", selectorString) } - - selectorMap[strings.TrimSpace(keyValue[0])] = strings.TrimSpace(keyValue[1]) + selector := strings.TrimSpace(keyValue[1]) + selectorMap[strings.TrimSpace(keyValue[0])] = &selector } return selectorMap, nil diff --git a/cmd/down.go b/cmd/down.go index d9466f7f33..498efe689f 100644 --- a/cmd/down.go +++ b/cmd/down.go @@ -3,11 +3,9 @@ package cmd import ( helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm" "github.com/covexo/devspace/pkg/devspace/clients/kubectl" - "github.com/covexo/devspace/pkg/devspace/config" + "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/util/log" - "github.com/covexo/devspace/pkg/devspace/config/v1" - "github.com/spf13/cobra" ) @@ -46,14 +44,9 @@ your project, use: devspace reset func (cmd *DownCmd) Run(cobraCmd *cobra.Command, args []string) { log.StartFileLogging() - privateConfig := &v1.PrivateConfig{} - err := config.LoadConfig(privateConfig) - - if err != nil { - log.Fatalf("Unable to load release name: %s. Does the file .devspace/private.yaml exist?", err.Error()) - } + config := configutil.GetConfig(false) - releaseName := privateConfig.Release.Name + releaseName := *config.DevSpace.Release.Name kubectl, err := kubectl.NewClient() if err != nil { diff --git a/cmd/init.go b/cmd/init.go index 2a632f1b00..9d5225f987 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -3,15 +3,16 @@ package cmd import ( "io/ioutil" "os" - "path/filepath" "strconv" "strings" - "github.com/covexo/devspace/pkg/devspace/config" + "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/devspace/generator" "github.com/covexo/devspace/pkg/util/log" "github.com/covexo/devspace/pkg/util/randutil" "github.com/covexo/devspace/pkg/util/yamlutil" + "github.com/imdario/mergo" + homedir "github.com/mitchellh/go-homedir" "github.com/covexo/devspace/pkg/devspace/config/v1" "github.com/covexo/devspace/pkg/util/stdinutil" @@ -21,11 +22,9 @@ import ( // InitCmd is a struct that defines a command call for "init" type InitCmd struct { flags *InitCmdFlags - dsConfig *v1.DevSpaceConfig - privateConfig *v1.PrivateConfig - appConfig *v1.AppConfig workdir string chartGenerator *generator.ChartGenerator + config *v1.Config } // InitCmdFlags are the flags available for the init-command @@ -102,35 +101,29 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) { } cmd.workdir = workdir - cmd.dsConfig = &v1.DevSpaceConfig{ - Version: "v1", - } - cmd.privateConfig = &v1.PrivateConfig{ - Version: "v1", - Release: &v1.Release{ - Namespace: "default", + configExists, _ := configutil.ConfigExists() + + if configExists { + cmd.config = configutil.GetConfig(false) + } else { + cmd.config = configutil.GetConfigInstance() + } + mergo.Merge(cmd.config, &v1.Config{ + Version: configutil.String("v1"), + DevSpace: &v1.DevSpaceConfig{ + Release: &v1.Release{ + Name: configutil.String("devspace"), + Namespace: configutil.String("default"), + }, + }, + Image: &v1.ImageConfig{ + Name: configutil.String("devspace"), }, Cluster: &v1.Cluster{ - ApiServer: "https://192.168.99.100:8443", + ApiServer: configutil.String("https://192.168.99.100:8443"), User: &v1.User{}, }, - } - cmd.appConfig = &v1.AppConfig{ - Name: filepath.Base(cmd.workdir), - Container: &v1.AppContainer{ - Ports: []int{}, - }, - External: &v1.AppExternal{ - Domain: "mydomain.com", - Port: 80, - }, - } - dsConfigExists, _ := config.ConfigExists(cmd.dsConfig) - privateConfigExists, _ := config.ConfigExists(cmd.privateConfig) - - if dsConfigExists || privateConfigExists { - cmd.loadExistingConfig() - } + }) cmd.initChartGenerator() createChart := cmd.flags.overwrite @@ -145,7 +138,7 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) { DefaultValue: "no", ValidationRegexPattern: "^(yes)|(no)$", }) - createChart = (overwriteAnswer == "yes") + createChart = (*overwriteAnswer == "yes") } else { createChart = true } @@ -154,14 +147,12 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) { if createChart { cmd.determineAppConfig() - if cmd.privateConfig.Release == nil || len(cmd.privateConfig.Release.Name) == 0 { - cmd.privateConfig.Release.Name = cmd.appConfig.Name + if cmd.config.DevSpace.Release == nil || cmd.config.DevSpace.Release.Name == nil { + cmd.config.DevSpace.Release.Name = configutil.String("my-app") } - cmd.addPortForwarding() - cmd.addSyncPath() } - if cmd.flags.reconfigure || !dsConfigExists || !privateConfigExists { + if cmd.flags.reconfigure || !configExists { cmd.reconfigure() } @@ -171,11 +162,6 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) { } } -func (cmd *InitCmd) loadExistingConfig() { - config.LoadConfig(cmd.dsConfig) - config.LoadConfig(cmd.privateConfig) -} - func (cmd *InitCmd) initChartGenerator() { templateRepoPath := cmd.flags.templateRepoPath @@ -197,36 +183,29 @@ func (cmd *InitCmd) determineAppConfig() { _, chartDirNotFound := os.Stat(cmd.chartGenerator.Path + "/chart") if chartDirNotFound == nil { + /*TODO existingChartYaml := map[interface{}]interface{}{} existingChartValuesYaml := map[interface{}]interface{}{} yamlutil.ReadYamlFromFile(cmd.chartGenerator.Path+"/chart/Chart.yaml", existingChartYaml) yamlutil.ReadYamlFromFile(cmd.chartGenerator.Path+"/chart/values.yaml", existingChartValuesYaml) - cmd.appConfig.Name = existingChartYaml["name"].(string) + cmd.config.Release.Name = existingChartYaml["name"].(string) applicationValues, applicationValuesCorrect := existingChartValuesYaml["container"].(map[interface{}]interface{}) externalValues, externalValuesCorrect := existingChartValuesYaml["external"].(map[interface{}]interface{}) - if applicationValuesCorrect { - value, isCorrect := applicationValues["port"].(int) - - if isCorrect { - cmd.appConfig.Container.Ports = []int{value} - } - } - if externalValuesCorrect { value, isCorrect := externalValues["domain"].(string) if isCorrect { cmd.appConfig.External.Domain = value } - } + }*/ } - cmd.appConfig.Name = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + cmd.config.DevSpace.Release.Name = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "What is the name of your application?", - DefaultValue: cmd.appConfig.Name, + DefaultValue: *cmd.config.DevSpace.Release.Name, ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name, }) @@ -236,25 +215,20 @@ func (cmd *InitCmd) determineAppConfig() { // ValidationRegexPattern: "^[1-9][0-9]{0,4}?(\\s[1-9][0-9]{0,4})?$", // })) - portsToSliceStr := []string{} - - for _, port := range cmd.appConfig.Container.Ports { - portsToSliceStr = append(portsToSliceStr, strconv.Itoa(port)) - } - - portStrings := strings.Split(stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + ports := strings.Split(*stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Which port(s) does your application listen on? (separated by spaces)", - DefaultValue: strings.Join(portsToSliceStr, " "), + DefaultValue: "", ValidationRegexPattern: "^([1-9][0-9]{0,4})?(\\s[1-9][0-9]{0,4})*?$", }), " ") - for _, port := range portStrings { + for _, port := range ports { portInt, _ := strconv.Atoi(port) if portInt > 0 { - cmd.appConfig.Container.Ports = append(cmd.appConfig.Container.Ports, portInt) + cmd.addPortForwarding(portInt) } } + cmd.addSyncPath() /* TODO cmd.appConfig.External.Domain = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ @@ -264,132 +238,114 @@ func (cmd *InitCmd) determineAppConfig() { })*/ } -func (cmd *InitCmd) addPortForwarding() { - portForwardingMissing := true - +func (cmd *InitCmd) addPortForwarding(port int) { OUTER: - for _, portForwarding := range cmd.dsConfig.PortForwarding { + for _, portForwarding := range cmd.config.DevSpace.PortForwarding { for _, portMapping := range portForwarding.PortMappings { - for _, port := range cmd.appConfig.Container.Ports { - if portMapping.RemotePort == port { - portForwardingMissing = false - break OUTER - } - } - } - } - - if portForwardingMissing { - for _, port := range cmd.appConfig.Container.Ports { - cmd.dsConfig.PortForwarding = append(cmd.dsConfig.PortForwarding, &v1.PortForwarding{ - PortMappings: []*v1.PortMapping{ - { - LocalPort: port, - RemotePort: port, + if *portMapping.RemotePort == port { + cmd.config.DevSpace.PortForwarding = append(cmd.config.DevSpace.PortForwarding, &v1.PortForwardingConfig{ + PortMappings: []*v1.PortMapping{ + { + LocalPort: &port, + RemotePort: &port, + }, }, - }, - ResourceType: "pod", - LabelSelector: map[string]string{ - "release": cmd.privateConfig.Release.Name, - }, - }) + ResourceType: configutil.String("pod"), + LabelSelector: map[string]*string{ + "release": cmd.config.DevSpace.Release.Name, + }, + }) + break OUTER + } } } } func (cmd *InitCmd) addSyncPath() { - syncPathMissing := true - - for _, syncPath := range cmd.dsConfig.SyncPaths { - if syncPath.LocalSubPath == "./" || syncPath.ContainerPath == "/app" { - syncPathMissing = false + for _, syncPath := range cmd.config.DevSpace.Sync { + if *syncPath.LocalSubPath == "./" || *syncPath.ContainerPath == "/app" { + cmd.config.DevSpace.Sync = append(cmd.config.DevSpace.Sync, &v1.SyncConfig{ + ContainerPath: configutil.String("/app"), + LocalSubPath: configutil.String("./"), + ResourceType: configutil.String("pod"), + LabelSelector: map[string]*string{ + "release": cmd.config.DevSpace.Release.Name, + }, + }) break } } - - if syncPathMissing { - cmd.dsConfig.SyncPaths = append(cmd.dsConfig.SyncPaths, &v1.SyncPath{ - ContainerPath: "/app", - LocalSubPath: "./", - ResourceType: "pod", - LabelSelector: map[string]string{ - "release": cmd.privateConfig.Release.Name, - }, - }) - } } func (cmd *InitCmd) reconfigure() { - clusterConfig := cmd.privateConfig.Cluster + clusterConfig := cmd.config.Cluster + tillerConfig := cmd.config.Services.Tiller + tillerRelease := tillerConfig.Release - cmd.privateConfig.Release.Namespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + cmd.config.DevSpace.Release.Namespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Which Kubernetes namespace should your application run in?", - DefaultValue: cmd.privateConfig.Release.Namespace, + DefaultValue: *cmd.config.DevSpace.Release.Namespace, ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name, }) - if len(clusterConfig.TillerNamespace) == 0 { - clusterConfig.TillerNamespace = cmd.privateConfig.Release.Namespace + if tillerRelease.Namespace == nil { + tillerRelease.Namespace = cmd.config.DevSpace.Release.Namespace } - clusterConfig.TillerNamespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + tillerRelease.Namespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Which Kubernetes namespace should your tiller server run in?", - DefaultValue: clusterConfig.TillerNamespace, + DefaultValue: *tillerRelease.Namespace, ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name, }) - kubeClusterConfig := &v1.Cluster{ - User: &v1.User{}, + useKubeConfig := false + homeDir, homeErr := homedir.Dir() + + if homeErr != nil { + log.With(homeErr).Fatalf("Unable to determine home dir") } - skipClusterConfig := false + kubeConfigPath := homeDir + "/.kube/config" - config.LoadClusterConfig(kubeClusterConfig, false) + _, kubeConfigNotFound := os.Stat(kubeConfigPath) - if len(kubeClusterConfig.ApiServer) != 0 && len(kubeClusterConfig.CaCert) != 0 && len(kubeClusterConfig.User.ClientCert) != 0 && len(kubeClusterConfig.User.ClientKey) != 0 { + if kubeConfigNotFound == nil { skipAnswer := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Do you want to use your existing $HOME/.kube/config for Kubernetes access? (yes | no)", DefaultValue: "yes", ValidationRegexPattern: "^(yes)|(no)$", }) - skipClusterConfig = (skipAnswer == "yes") + useKubeConfig = (*skipAnswer == "yes") } + clusterConfig.UseKubeConfig = configutil.Bool(useKubeConfig) - if skipClusterConfig { - clusterConfig.UseKubeConfig = true - } else { + if !useKubeConfig { clusterConfig.ApiServer = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "What is your Kubernetes API Server URL? (e.g. https://127.0.0.1:8443)", - DefaultValue: clusterConfig.ApiServer, + DefaultValue: *clusterConfig.ApiServer, ValidationRegexPattern: "^https?://[a-z0-9-.]{0,99}:[0-9]{1,5}$", }) clusterConfig.CaCert = stdinutil.AskChangeQuestion(&stdinutil.GetFromStdinParams{ Question: "What is the CA Certificate of your API Server? (PEM)", - DefaultValue: clusterConfig.CaCert, + DefaultValue: *clusterConfig.CaCert, InputTerminationString: "-----END CERTIFICATE-----", }) clusterConfig.User.Username = stdinutil.AskChangeQuestion(&stdinutil.GetFromStdinParams{ Question: "What is your Kubernetes username?", - DefaultValue: clusterConfig.User.Username, + DefaultValue: *clusterConfig.User.Username, ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name, }) clusterConfig.User.ClientCert = stdinutil.AskChangeQuestion(&stdinutil.GetFromStdinParams{ Question: "What is your Kubernetes client certificate? (PEM)", - DefaultValue: clusterConfig.User.ClientCert, + DefaultValue: *clusterConfig.User.ClientCert, InputTerminationString: "-----END CERTIFICATE-----", }) clusterConfig.User.ClientKey = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "What is your Kubernetes client key? (RSA, PEM)", - DefaultValue: clusterConfig.User.ClientKey, + DefaultValue: *clusterConfig.User.ClientKey, InputTerminationString: "-----END RSA PRIVATE KEY-----", }) } cmd.reconfigureRegistry() - err := config.SaveConfig(cmd.dsConfig) - - if err != nil { - log.With(err).Fatalf("Config error: %s", err.Error()) - } - - err = config.SaveConfig(cmd.privateConfig) + err := configutil.SaveConfig() if err != nil { log.With(err).Fatalf("Config error: %s", err.Error()) @@ -397,7 +353,8 @@ func (cmd *InitCmd) reconfigure() { } func (cmd *InitCmd) reconfigureRegistry() { - registryConfig := cmd.privateConfig.Registry + overwriteConfig := configutil.GetOverwriteConfig() + registryConfig := overwriteConfig.Services.Registry enableAutomaticBuilds := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Do you want to enable automatic Docker image building?", @@ -405,56 +362,70 @@ func (cmd *InitCmd) reconfigureRegistry() { ValidationRegexPattern: "^(yes)|(no)$", }) - if enableAutomaticBuilds == "yes" { - if registryConfig == nil { - registryConfig = &v1.RegistryAccess{} - cmd.privateConfig.Registry = registryConfig - } - registryConfig.Release = &v1.Release{ - Name: "devspace-registry", - Namespace: cmd.privateConfig.Release.Namespace, - } - registryConfig.User = &v1.RegistryUser{} + if *enableAutomaticBuilds == "yes" { + internalRegistryKey := "internal registry" + defaultRegistryValue := internalRegistryKey - if cmd.privateConfig.Cluster.User != nil && len(cmd.privateConfig.Cluster.User.Username) != 0 { - registryConfig.User.Username = cmd.privateConfig.Cluster.User.Username + if registryConfig.External != nil { + defaultRegistryValue = *registryConfig.External } + registryUrl := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + Question: "Which registry do you want to push to? (URL or 'internal registry')", + DefaultValue: defaultRegistryValue, + ValidationRegexPattern: "^.*$", + }) - if len(registryConfig.User.Username) == 0 { - randomUserSuffix, err := randutil.GenerateRandomString(5) + if *registryUrl != internalRegistryKey { + registryConfig.External = registryUrl + registryConfig.Internal = nil + } else { + registryConfig.External = nil - if err != nil { - log.Fatalf("Error creating random username: %s", err.Error()) + if registryConfig.Internal.Release.Name == nil { + registryConfig.Internal.Release.Name = configutil.String("devspace-registry") } - registryConfig.User.Username = "user-" + randomUserSuffix - } - if len(registryConfig.User.Password) == 0 { - randomPassword, err := randutil.GenerateRandomString(12) + if registryConfig.Internal.Release.Namespace == nil { + registryConfig.Internal.Release.Namespace = cmd.config.DevSpace.Release.Namespace + } + registryUser := overwriteConfig.Services.Registry.User + + if registryUser.Username == nil { + randomUserSuffix, err := randutil.GenerateRandomString(5) - if err != nil { - log.Fatalf("Error creating random password: %s", err.Error()) + if err != nil { + log.Fatalf("Error creating random username: %s", err.Error()) + } + registryUser.Username = configutil.String("user-" + randomUserSuffix) } - registryConfig.User.Password = randomPassword - } + if registryUser.Password == nil { + randomPassword, err := randutil.GenerateRandomString(12) - if cmd.dsConfig.Registry == nil { - cmd.dsConfig.Registry = map[interface{}]interface{}{} - } - secrets, registryHasSecrets := cmd.dsConfig.Registry["secrets"] + if err != nil { + log.Fatalf("Error creating random password: %s", err.Error()) + } + registryUser.Password = &randomPassword + } + registryReleaseValues := registryConfig.Internal.Release.Values - if !registryHasSecrets { - secrets = map[interface{}]interface{}{} - cmd.dsConfig.Registry["secrets"] = secrets - } - secretMap, secretsIsMap := secrets.(map[interface{}]interface{}) + if registryReleaseValues == nil { + registryReleaseValues = map[interface{}]interface{}{} + } + secrets, registryHasSecrets := registryReleaseValues["secrets"] + + if !registryHasSecrets { + secrets = map[interface{}]interface{}{} + registryReleaseValues["secrets"] = secrets + } + secretMap, secretsIsMap := secrets.(map[interface{}]interface{}) - if secretsIsMap { - _, registryHasSecretHtpasswd := secretMap["htpasswd"] + if secretsIsMap { + _, registryHasSecretHtpasswd := secretMap["htpasswd"] - if !registryHasSecretHtpasswd { - secretMap["htpasswd"] = "" + if !registryHasSecretHtpasswd { + secretMap["htpasswd"] = "" + } } } } @@ -481,7 +452,7 @@ func (cmd *InitCmd) determineLanguage() { log.Fatalf("Unable to get supported languages: %s", err.Error()) } - cmd.chartGenerator.Language = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + cmd.chartGenerator.Language = *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "What is the major programming language of your project?\nSupported languages: " + strings.Join(supportedLanguages, ", "), DefaultValue: cmd.chartGenerator.Language, ValidationRegexPattern: "^(" + strings.Join(supportedLanguages, ")|(") + ")$", @@ -502,8 +473,8 @@ func (cmd *InitCmd) createChart() { yamlutil.ReadYamlFromFile(cmd.chartGenerator.Path+"/chart/Chart.yaml", &createdChartYaml) yamlutil.ReadYamlFromFile(cmd.chartGenerator.Path+"/chart/values.yaml", &createdChartValuesYaml) - createdChartYaml["name"] = cmd.appConfig.Name - + createdChartYaml["name"] = cmd.config.DevSpace.Release.Name + /*TODO containerValues, chartHasContainerValues := createdChartValuesYaml["container"].(map[interface{}]interface{}) if !chartHasContainerValues && containerValues != nil { @@ -518,7 +489,7 @@ func (cmd *InitCmd) createChart() { externalValues["domain"] = cmd.appConfig.External.Domain createdChartValuesYaml["external"] = externalValues } - + */ yamlutil.WriteYamlToFile(createdChartYaml, cmd.chartGenerator.Path+"/chart/Chart.yaml") yamlutil.WriteYamlToFile(createdChartValuesYaml, cmd.chartGenerator.Path+"/chart/values.yaml") } diff --git a/cmd/list.go b/cmd/list.go index 11bf2fa40e..18e979f5d0 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -1,22 +1,17 @@ package cmd import ( - "os" "strconv" - "github.com/covexo/devspace/pkg/devspace/config" - "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/util/log" "github.com/spf13/cobra" ) // ListCmd holds the information needed for the list command type ListCmd struct { - flags *ListCmdFlags - dsConfig *v1.DevSpaceConfig - privateConfig *v1.PrivateConfig - appConfig *v1.AppConfig - workdir string + flags *ListCmdFlags + workdir string } // ListCmdFlags holds the possible flags for the list command @@ -81,9 +76,9 @@ func init() { // RunListSync runs the list sync command logic func (cmd *ListCmd) RunListSync(cobraCmd *cobra.Command, args []string) { - loadConfig(&cmd.workdir, &cmd.privateConfig, &cmd.dsConfig) + config := configutil.GetConfig(false) - if len(cmd.dsConfig.SyncPaths) == 0 { + if len(config.DevSpace.Sync) == 0 { log.Write("No sync paths are configured. Run `devspace add sync` to add new sync path\n") return } @@ -96,10 +91,10 @@ func (cmd *ListCmd) RunListSync(cobraCmd *cobra.Command, args []string) { "Excluded Paths", } - syncPaths := make([][]string, 0, len(cmd.dsConfig.SyncPaths)) + syncPaths := make([][]string, 0, len(config.DevSpace.Sync)) // Transform values into string arrays - for _, value := range cmd.dsConfig.SyncPaths { + for _, value := range config.DevSpace.Sync { selector := "" for k, v := range value.LabelSelector { @@ -107,7 +102,7 @@ func (cmd *ListCmd) RunListSync(cobraCmd *cobra.Command, args []string) { selector += ", " } - selector += k + "=" + v + selector += k + "=" + *v } excludedPaths := "" @@ -117,14 +112,14 @@ func (cmd *ListCmd) RunListSync(cobraCmd *cobra.Command, args []string) { excludedPaths += ", " } - excludedPaths += v + excludedPaths += *v } syncPaths = append(syncPaths, []string{ - value.ResourceType, + *value.ResourceType, selector, - value.LocalSubPath, - value.ContainerPath, + *value.LocalSubPath, + *value.ContainerPath, excludedPaths, }) } @@ -134,9 +129,9 @@ func (cmd *ListCmd) RunListSync(cobraCmd *cobra.Command, args []string) { // RunListPort runs the list port command logic func (cmd *ListCmd) RunListPort(cobraCmd *cobra.Command, args []string) { - loadConfig(&cmd.workdir, &cmd.privateConfig, &cmd.dsConfig) + config := configutil.GetConfig(false) - if len(cmd.dsConfig.PortForwarding) == 0 { + if len(config.DevSpace.PortForwarding) == 0 { log.Write("No ports are forwarded. Run `devspace add port` to add a port that should be forwarded\n") return } @@ -147,10 +142,10 @@ func (cmd *ListCmd) RunListPort(cobraCmd *cobra.Command, args []string) { "Ports (Local:Remote)", } - portForwards := make([][]string, 0, len(cmd.dsConfig.PortForwarding)) + portForwards := make([][]string, 0, len(config.DevSpace.PortForwarding)) // Transform values into string arrays - for _, value := range cmd.dsConfig.PortForwarding { + for _, value := range config.DevSpace.PortForwarding { selector := "" for k, v := range value.LabelSelector { @@ -158,7 +153,7 @@ func (cmd *ListCmd) RunListPort(cobraCmd *cobra.Command, args []string) { selector += ", " } - selector += k + "=" + v + selector += k + "=" + *v } portMappings := "" @@ -168,11 +163,11 @@ func (cmd *ListCmd) RunListPort(cobraCmd *cobra.Command, args []string) { portMappings += ", " } - portMappings += strconv.Itoa(v.LocalPort) + ":" + strconv.Itoa(v.RemotePort) + portMappings += strconv.Itoa(*v.LocalPort) + ":" + strconv.Itoa(*v.RemotePort) } portForwards = append(portForwards, []string{ - value.ResourceType, + *value.ResourceType, selector, portMappings, }) @@ -180,27 +175,3 @@ func (cmd *ListCmd) RunListPort(cobraCmd *cobra.Command, args []string) { log.PrintTable(headerColumnNames, portForwards) } - -func loadConfig(workdir *string, privateConfig **v1.PrivateConfig, dsConfig **v1.DevSpaceConfig) { - w, err := os.Getwd() - - if err != nil { - log.Fatalf("Unable to determine current workdir: %s", err.Error()) - } - - workdir = &w - *privateConfig = &v1.PrivateConfig{} - *dsConfig = &v1.DevSpaceConfig{} - - err = config.LoadConfig(privateConfig) - - if err != nil { - log.Fatalf("Unable to load .devspace/private.yaml: %s. Did you run `devspace init`?", err.Error()) - } - - err = config.LoadConfig(dsConfig) - - if err != nil { - log.Fatalf("Unable to load .devspace/config.yaml: %s. Did you run `devspace init`?", err.Error()) - } -} diff --git a/cmd/remove.go b/cmd/remove.go index 3e6783510f..ad7a4cb7ab 100644 --- a/cmd/remove.go +++ b/cmd/remove.go @@ -4,7 +4,7 @@ import ( "strconv" "strings" - "github.com/covexo/devspace/pkg/devspace/config" + "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/devspace/config/v1" "github.com/covexo/devspace/pkg/util/log" "github.com/spf13/cobra" @@ -12,11 +12,9 @@ import ( // RemoveCmd holds the information needed for the remove command type RemoveCmd struct { - syncFlags *removeSyncCmdFlags - portFlags *removePortCmdFlags - dsConfig *v1.DevSpaceConfig - privateConfig *v1.PrivateConfig - workdir string + syncFlags *removeSyncCmdFlags + portFlags *removePortCmdFlags + workdir string } type removeSyncCmdFlags struct { @@ -108,8 +106,7 @@ func init() { // RunRemoveSync executes the remove sync command logic func (cmd *RemoveCmd) RunRemoveSync(cobraCmd *cobra.Command, args []string) { - loadConfig(&cmd.workdir, &cmd.privateConfig, &cmd.dsConfig) - + config := configutil.GetConfig(false) labelSelectorMap, err := parseSelectors(cmd.syncFlags.Selector) if err != nil { @@ -123,12 +120,12 @@ func (cmd *RemoveCmd) RunRemoveSync(cobraCmd *cobra.Command, args []string) { return } - newSyncPaths := make([]*v1.SyncPath, 0, len(cmd.dsConfig.SyncPaths)-1) + newSyncPaths := make([]*v1.SyncConfig, 0, len(config.DevSpace.Sync)-1) - for _, v := range cmd.dsConfig.SyncPaths { + for _, v := range config.DevSpace.Sync { if cmd.syncFlags.RemoveAll || - cmd.syncFlags.LocalPath == v.LocalSubPath || - cmd.syncFlags.ContainerPath == v.ContainerPath || + cmd.syncFlags.LocalPath == *v.LocalSubPath || + cmd.syncFlags.ContainerPath == *v.ContainerPath || isMapEqual(labelSelectorMap, v.LabelSelector) { continue } @@ -136,9 +133,9 @@ func (cmd *RemoveCmd) RunRemoveSync(cobraCmd *cobra.Command, args []string) { newSyncPaths = append(newSyncPaths, v) } - cmd.dsConfig.SyncPaths = newSyncPaths + config.DevSpace.Sync = newSyncPaths - err = config.SaveConfig(cmd.dsConfig) + err = configutil.SaveConfig() if err != nil { log.Fatalf("Couldn't save config file: %s", err.Error()) @@ -147,7 +144,7 @@ func (cmd *RemoveCmd) RunRemoveSync(cobraCmd *cobra.Command, args []string) { // RunRemovePort executes the remove port command logic func (cmd *RemoveCmd) RunRemovePort(cobraCmd *cobra.Command, args []string) { - loadConfig(&cmd.workdir, &cmd.privateConfig, &cmd.dsConfig) + config := configutil.GetConfig(false) labelSelectorMap, err := parseSelectors(cmd.portFlags.Selector) @@ -169,17 +166,17 @@ func (cmd *RemoveCmd) RunRemovePort(cobraCmd *cobra.Command, args []string) { } ports := strings.Split(argPorts, ",") - newPortForwards := make([]*v1.PortForwarding, 0, len(cmd.dsConfig.PortForwarding)-1) + newPortForwards := make([]*v1.PortForwardingConfig, 0, len(config.DevSpace.PortForwarding)-1) OUTER: - for _, v := range cmd.dsConfig.PortForwarding { + for _, v := range config.DevSpace.PortForwarding { if cmd.portFlags.RemoveAll || isMapEqual(labelSelectorMap, v.LabelSelector) { continue } for _, pm := range v.PortMappings { - if containsPort(strconv.Itoa(pm.LocalPort), ports) || containsPort(strconv.Itoa(pm.RemotePort), ports) { + if containsPort(strconv.Itoa(*pm.LocalPort), ports) || containsPort(strconv.Itoa(*pm.RemotePort), ports) { continue OUTER } } @@ -187,9 +184,9 @@ OUTER: newPortForwards = append(newPortForwards, v) } - cmd.dsConfig.PortForwarding = newPortForwards + config.DevSpace.PortForwarding = newPortForwards - err = config.SaveConfig(cmd.dsConfig) + err = configutil.SaveConfig() if err != nil { log.Fatalf("Couldn't save config file: %s", err.Error()) diff --git a/cmd/reset.go b/cmd/reset.go index f20ded6a9d..08025e7607 100644 --- a/cmd/reset.go +++ b/cmd/reset.go @@ -1,17 +1,15 @@ package cmd import ( - "fmt" "os" "path" helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm" "github.com/covexo/devspace/pkg/devspace/clients/kubectl" - "github.com/covexo/devspace/pkg/devspace/config" "github.com/covexo/devspace/pkg/util/log" "github.com/covexo/devspace/pkg/util/stdinutil" - "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/spf13/cobra" "k8s.io/client-go/kubernetes" @@ -19,12 +17,10 @@ import ( // ResetCmd holds the needed command information type ResetCmd struct { - flags *ResetCmdFlags - helm *helmClient.HelmClientWrapper - kubectl *kubernetes.Clientset - privateConfig *v1.PrivateConfig - dsConfig *v1.DevSpaceConfig - workdir string + flags *ResetCmdFlags + helm *helmClient.HelmClientWrapper + kubectl *kubernetes.Clientset + workdir string } // ResetCmdFlags holds the command flags @@ -70,14 +66,9 @@ command: devspace down // Run executes the reset command logic func (cmd *ResetCmd) Run(cobraCmd *cobra.Command, args []string) { + var err error cmd.determineResetExtent() - err := cmd.loadConfig() - - if err != nil { - log.Fatalf("Couldn't load config: %s", err.Error()) - } - if cmd.flags.deleteRelease { err = cmd.deleteRelease() @@ -163,25 +154,25 @@ func (cmd *ResetCmd) determineResetExtent() { cmd.flags.deleteDevspaceFolder = true cmd.flags.deleteRelease = true - cmd.flags.deleteDockerfile = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + cmd.flags.deleteDockerfile = *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Should the Dockerfile be removed? (y/n)", DefaultValue: "y", ValidationRegexPattern: "^(y|n)$", }) == "y" - cmd.flags.deleteChart = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + cmd.flags.deleteChart = *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Should the Chart (chart/*) be removed ? (y/n)", DefaultValue: "y", ValidationRegexPattern: "^(y|n)$", }) == "y" - cmd.flags.deleteRegistry = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + cmd.flags.deleteRegistry = *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Should the docker registry be removed ? (y/n)", DefaultValue: "y", ValidationRegexPattern: "^(y|n)$", }) == "y" - cmd.flags.deleteTiller = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + cmd.flags.deleteTiller = *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Should the tiller server be removed ? (y/n)", DefaultValue: "y", ValidationRegexPattern: "^(y|n)$", @@ -189,43 +180,18 @@ func (cmd *ResetCmd) determineResetExtent() { } func (cmd *ResetCmd) shouldContinue() bool { - return stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + return *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "An error occurred, should the reset command continue? (y/n)", DefaultValue: "y", ValidationRegexPattern: "^(y|n)$", }) == "y" } -func (cmd *ResetCmd) loadConfig() error { - workdir, err := os.Getwd() - - if err != nil { - return fmt.Errorf("Unable to determine current workdir: %s", err.Error()) - } - - cmd.workdir = workdir - cmd.privateConfig = &v1.PrivateConfig{} - cmd.dsConfig = &v1.DevSpaceConfig{} - - err = config.LoadConfig(cmd.privateConfig) - - if err != nil { - return fmt.Errorf("Unable to load .devspace/private.yaml: %s", err.Error()) - } - - err = config.LoadConfig(cmd.dsConfig) - - if err != nil { - return fmt.Errorf("Unable to load .devspace/config.yaml: %s", err.Error()) - } - - return nil -} - func (cmd *ResetCmd) deleteRelease() error { var err error + config := configutil.GetConfig(false) - releaseName := cmd.privateConfig.Release.Name + releaseName := *config.DevSpace.Release.Name if cmd.kubectl == nil || cmd.helm == nil { cmd.kubectl, err = kubectl.NewClient() @@ -234,7 +200,7 @@ func (cmd *ResetCmd) deleteRelease() error { return err } - isDeployed := helmClient.IsTillerDeployed(cmd.kubectl, cmd.privateConfig) + isDeployed := helmClient.IsTillerDeployed(cmd.kubectl, config.Services.Tiller) if isDeployed == false { return nil @@ -254,8 +220,9 @@ func (cmd *ResetCmd) deleteRelease() error { func (cmd *ResetCmd) deleteRegistry() error { var err error + config := configutil.GetConfig(false) - registryReleaseName := cmd.privateConfig.Registry.Release.Name + registryReleaseName := *config.Services.Registry.Internal.Release.Name if cmd.kubectl == nil || cmd.helm == nil { cmd.kubectl, err = kubectl.NewClient() @@ -264,7 +231,7 @@ func (cmd *ResetCmd) deleteRegistry() error { return err } - isDeployed := helmClient.IsTillerDeployed(cmd.kubectl, cmd.privateConfig) + isDeployed := helmClient.IsTillerDeployed(cmd.kubectl, config.Services.Tiller) if isDeployed == false { return nil @@ -284,6 +251,7 @@ func (cmd *ResetCmd) deleteRegistry() error { func (cmd *ResetCmd) deleteTiller() error { var err error + config := configutil.GetConfig(false) if cmd.kubectl == nil { cmd.kubectl, err = kubectl.NewClient() @@ -293,7 +261,7 @@ func (cmd *ResetCmd) deleteTiller() error { } } - return helmClient.DeleteTiller(cmd.kubectl, cmd.privateConfig) + return helmClient.DeleteTiller(cmd.kubectl, config.Services.Tiller) } func (cmd *ResetCmd) deleteDockerfile() error { diff --git a/cmd/status.go b/cmd/status.go index d3115be768..cbf3cf4683 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -6,7 +6,7 @@ import ( helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm" "github.com/covexo/devspace/pkg/devspace/clients/kubectl" - "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/util/log" "github.com/daviddengcn/go-colortext" "github.com/spf13/cobra" @@ -17,13 +17,10 @@ import ( // StatusCmd holds the information needed for the status command type StatusCmd struct { - flags *StatusCmdFlags - helm *helmClient.HelmClientWrapper - kubectl *kubernetes.Clientset - dsConfig *v1.DevSpaceConfig - privateConfig *v1.PrivateConfig - appConfig *v1.AppConfig - workdir string + flags *StatusCmdFlags + helm *helmClient.HelmClientWrapper + kubectl *kubernetes.Clientset + workdir string } // StatusCmdFlags holds the possible flags for the list command @@ -79,9 +76,6 @@ func (cmd *StatusCmd) RunStatus(cobraCmd *cobra.Command, args []string) { "NAMESPACE", "INFO", } - - loadConfig(&cmd.workdir, &cmd.privateConfig, &cmd.dsConfig) - cmd.kubectl, err = kubectl.NewClient() if err != nil { @@ -158,6 +152,8 @@ func (cmd *StatusCmd) RunStatus(cobraCmd *cobra.Command, args []string) { } func (cmd *StatusCmd) getRegistryStatus() ([]string, error) { + config := configutil.GetConfig(false) + registry := config.Services.Registry.Internal releases, err := cmd.helm.Client.ListReleases() if err != nil { @@ -169,12 +165,12 @@ func (cmd *StatusCmd) getRegistryStatus() ([]string, error) { } for _, release := range releases.Releases { - if release.GetName() == cmd.privateConfig.Registry.Release.Name { + if release.GetName() == *registry.Release.Name { if release.Info.Status.Code.String() != "DEPLOYED" { return nil, fmt.Errorf("Registry helm release has bad status: %s", release.Info.Status.Code.String()) } - registryPods, err := kubectl.GetPodsFromDeployment(cmd.kubectl, cmd.privateConfig.Registry.Release.Name+"-docker-registry", cmd.privateConfig.Registry.Release.Namespace) + registryPods, err := kubectl.GetPodsFromDeployment(cmd.kubectl, *registry.Release.Name+"-docker-registry", *registry.Release.Namespace) if err != nil { return nil, err @@ -201,11 +197,12 @@ func (cmd *StatusCmd) getRegistryStatus() ([]string, error) { } } - return nil, fmt.Errorf("Registry helm release %s not found", cmd.privateConfig.Registry.Release.Name) + return nil, fmt.Errorf("Registry helm release %s not found", registry.Release.Name) } func (cmd *StatusCmd) getTillerStatus() ([]string, error) { - tillerPod, err := kubectl.GetPodsFromDeployment(cmd.kubectl, helmClient.TillerDeploymentName, cmd.privateConfig.Cluster.TillerNamespace) + config := configutil.GetConfig(false) + tillerPod, err := kubectl.GetPodsFromDeployment(cmd.kubectl, helmClient.TillerDeploymentName, *config.Services.Tiller.Release.Namespace) if err != nil { return nil, err @@ -232,6 +229,7 @@ func (cmd *StatusCmd) getTillerStatus() ([]string, error) { } func (cmd *StatusCmd) getDevspaceStatus() ([]string, error) { + config := configutil.GetConfig(false) releases, err := cmd.helm.Client.ListReleases() if err != nil { @@ -243,13 +241,13 @@ func (cmd *StatusCmd) getDevspaceStatus() ([]string, error) { } for _, release := range releases.Releases { - if release.GetName() == cmd.privateConfig.Release.Name { + if release.GetName() == *config.DevSpace.Release.Name { if release.Info.Status.Code.String() != "DEPLOYED" { - return nil, fmt.Errorf("Devspace helm release %s has bad status: %s", cmd.privateConfig.Release.Name, release.Info.Status.Code.String()) + return nil, fmt.Errorf("Devspace helm release %s has bad status: %s", *config.DevSpace.Release.Name, release.Info.Status.Code.String()) } - pods, err := cmd.kubectl.Core().Pods(cmd.privateConfig.Release.Namespace).List(metav1.ListOptions{ - LabelSelector: "release=" + cmd.privateConfig.Release.Name, + pods, err := cmd.kubectl.Core().Pods(*config.DevSpace.Release.Namespace).List(metav1.ListOptions{ + LabelSelector: "release=" + *config.DevSpace.Release.Name, }) if err != nil { @@ -288,10 +286,11 @@ func (cmd *StatusCmd) getDevspaceStatus() ([]string, error) { } } - return nil, fmt.Errorf("Devspace helm release %s not found", cmd.privateConfig.Release.Name) + return nil, fmt.Errorf("Devspace helm release %s not found", config.DevSpace.Release.Name) } -func getRunningDevSpacePod(helm *helmClient.HelmClientWrapper, client *kubernetes.Clientset, privateConfig *v1.PrivateConfig) (*k8sv1.Pod, error) { +func getRunningDevSpacePod(helm *helmClient.HelmClientWrapper, client *kubernetes.Clientset) (*k8sv1.Pod, error) { + config := configutil.GetConfig(false) releases, err := helm.Client.ListReleases() if err != nil { @@ -303,13 +302,13 @@ func getRunningDevSpacePod(helm *helmClient.HelmClientWrapper, client *kubernete } for _, release := range releases.Releases { - if release.GetName() == privateConfig.Release.Name { + if release.GetName() == *config.DevSpace.Release.Name { if release.Info.Status.Code.String() != "DEPLOYED" { - return nil, fmt.Errorf("Devspace helm release %s has bad status: %s", privateConfig.Release.Name, release.Info.Status.Code.String()) + return nil, fmt.Errorf("Devspace helm release %s has bad status: %s", *config.DevSpace.Release.Name, release.Info.Status.Code.String()) } - pods, err := client.Core().Pods(privateConfig.Release.Namespace).List(metav1.ListOptions{ - LabelSelector: "release=" + privateConfig.Release.Name, + pods, err := client.Core().Pods(*config.DevSpace.Release.Namespace).List(metav1.ListOptions{ + LabelSelector: "release=" + *config.DevSpace.Release.Name, }) if err != nil { @@ -331,5 +330,5 @@ func getRunningDevSpacePod(helm *helmClient.HelmClientWrapper, client *kubernete } } - return nil, fmt.Errorf("Devspace helm release %s not found", privateConfig.Release.Name) + return nil, fmt.Errorf("Devspace helm release %s not found", config.DevSpace.Release.Name) } diff --git a/cmd/status_sync.go b/cmd/status_sync.go index 4b9b55be5b..8b948465f8 100644 --- a/cmd/status_sync.go +++ b/cmd/status_sync.go @@ -6,6 +6,5 @@ import ( // RunStatusSync executes the devspace status sync commad logic func (cmd *StatusCmd) RunStatusSync(cobraCmd *cobra.Command, args []string) { - loadConfig(&cmd.workdir, &cmd.privateConfig, &cmd.dsConfig) - + //TODO } diff --git a/cmd/up.go b/cmd/up.go index ee40c0074c..2279361ddb 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -8,19 +8,19 @@ import ( "strings" "time" + "github.com/covexo/devspace/pkg/util/randutil" + "github.com/covexo/devspace/pkg/util/log" "github.com/covexo/devspace/pkg/devspace/kaniko" "github.com/covexo/devspace/pkg/devspace/registry" synctool "github.com/covexo/devspace/pkg/devspace/sync" - "github.com/covexo/devspace/pkg/devspace/config" - helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm" "github.com/covexo/devspace/pkg/devspace/clients/kubectl" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/spf13/cobra" k8sv1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" @@ -29,16 +29,12 @@ import ( // UpCmd is a struct that defines a command call for "up" type UpCmd struct { - flags *UpCmdFlags - helm *helmClient.HelmClientWrapper - kubectl *kubernetes.Clientset - privateConfig *v1.PrivateConfig - dsConfig *v1.DevSpaceConfig - workdir string - pod *k8sv1.Pod - container *k8sv1.Container - latestImageHostname string - latestImageIP string + flags *UpCmdFlags + helm *helmClient.HelmClientWrapper + kubectl *kubernetes.Clientset + workdir string + pod *k8sv1.Pod + container *k8sv1.Container } // UpCmdFlags are the flags available for the up-command @@ -114,36 +110,35 @@ func (cmd *UpCmd) Run(cobraCmd *cobra.Command, args []string) { } cmd.workdir = workdir - cmd.privateConfig = &v1.PrivateConfig{} - cmd.dsConfig = &v1.DevSpaceConfig{} - privateConfigExists, _ := config.ConfigExists(cmd.privateConfig) - dsConfigExists, _ := config.ConfigExists(cmd.dsConfig) + configExists, _ := configutil.ConfigExists() - if !privateConfigExists || !dsConfigExists { + if !configExists { initCmd := &InitCmd{ flags: InitCmdFlagsDefault, } initCmd.Run(nil, []string{}) } - - err = config.LoadConfig(cmd.privateConfig) + cmd.kubectl, err = kubectl.NewClient() if err != nil { - log.Fatalf("Couldn't load private.yaml: %s", err.Error()) + log.Fatalf("Unable to create new kubectl client: %s", err.Error()) } + cmd.initHelm() - err = config.LoadConfig(cmd.dsConfig) - - if err != nil { - log.Fatalf("Couldn't load config.yaml: %s", err.Error()) - } + if cmd.flags.initRegistry { + if cmd.flags.initRegistry && cmd.flags.imageDestination == "" { + log.StartWait("Initializing docker registry") + err := registry.InitRegistry(cmd.kubectl, cmd.helm) + log.StopWait() - cmd.kubectl, err = kubectl.NewClient() + if err != nil { + log.Fatalf("Docker registry error: %s", err.Error()) + } - if err != nil { - log.Fatalf("Unable to create new kubectl client: %s", err.Error()) + log.Done("Docker registry started") + } } if cmd.flags.build { @@ -152,25 +147,19 @@ func (cmd *UpCmd) Run(cobraCmd *cobra.Command, args []string) { if shouldRebuild { cmd.buildImage() - // Save new image ip to config - cmd.privateConfig.Release.LatestImage = cmd.latestImageIP - err = config.SaveConfig(cmd.privateConfig) + err = configutil.SaveConfig() if err != nil { log.Fatalf("Config saving error: %s", err.Error()) } - } else { - cmd.latestImageIP = cmd.privateConfig.Release.LatestImage } } if cmd.flags.deploy { cmd.deployChart() } else { - cmd.initHelm() - // Check if we find a running release pod - pod, err := getRunningDevSpacePod(cmd.helm, cmd.kubectl, cmd.privateConfig) + pod, err := getRunningDevSpacePod(cmd.helm, cmd.kubectl) if err != nil { log.Fatalf("Couldn't find running devspace pod: %s", err.Error()) @@ -191,12 +180,13 @@ func (cmd *UpCmd) Run(cobraCmd *cobra.Command, args []string) { } func (cmd *UpCmd) shouldRebuild(buildFlagChanged bool) bool { + config := configutil.GetConfig(false) mustRebuild := true dockerfileInfo, statErr := os.Stat(cmd.workdir + "/Dockerfile") var dockerfileModTime time.Time if statErr != nil { - if len(cmd.privateConfig.Release.LatestImage) == 0 { + if config.Image.BuildTime == nil { log.Fatalf("Dockerfile missing: %s", statErr.Error()) } else { mustRebuild = false @@ -206,45 +196,35 @@ func (cmd *UpCmd) shouldRebuild(buildFlagChanged bool) bool { // When user has not used -b or --build flags if buildFlagChanged == false { - if len(cmd.privateConfig.Release.LatestBuild) != 0 { - latestBuildTime, _ := time.Parse(time.RFC3339Nano, cmd.privateConfig.Release.LatestBuild) + if config.Image.BuildTime != nil { + latestBuildTime, _ := time.Parse(time.RFC3339Nano, *config.Image.BuildTime) // only rebuild Docker image when Dockerfile has changed since latest build mustRebuild = (latestBuildTime.Equal(dockerfileModTime) == false) } } } - - cmd.privateConfig.Release.LatestBuild = dockerfileModTime.Format(time.RFC3339Nano) + config.Image.BuildTime = configutil.String(dockerfileModTime.Format(time.RFC3339Nano)) return mustRebuild } func (cmd *UpCmd) buildImage() { - cmd.initHelm() - - if cmd.flags.initRegistry && cmd.flags.imageDestination == "" { - log.StartWait("Initializing docker registry") - latestImageHostname, latestImageIP, err := registry.InitDockerRegistry(cmd.kubectl, cmd.helm, cmd.privateConfig, cmd.dsConfig) - log.StopWait() - - if err != nil { - log.Fatalf("Docker registry error: %s", err.Error()) - } + config := configutil.GetConfig(false) - cmd.latestImageHostname = latestImageHostname - cmd.latestImageIP = latestImageIP + imageTag, randErr := randutil.GenerateRandomString(7) - log.Done("Docker registry started") + if randErr != nil { + log.Fatalf("Image building failed: %s", randErr.Error()) } - - imageDestination := cmd.latestImageHostname + imageDestination := registry.GetImageUrl(false) + ":" + imageTag if cmd.flags.imageDestination != "" { imageDestination = cmd.flags.imageDestination } + err := kaniko.BuildDockerfile(cmd.kubectl, *config.DevSpace.Release.Namespace, imageDestination, registry.PullSecretName, *config.Services.Registry.Insecure) - err := kaniko.BuildDockerfile(cmd.kubectl, cmd.privateConfig.Release.Namespace, imageDestination, registry.PullSecretName) + config.Image.Tag = &imageTag if err != nil { log.Fatalf("Image building failed: %s", err.Error()) @@ -268,17 +248,18 @@ func (cmd *UpCmd) initHelm() { } func (cmd *UpCmd) deployChart() { - cmd.initHelm() + config := configutil.GetConfig(false) + log.StartWait("Deploying helm chart") - releaseName := cmd.privateConfig.Release.Name - releaseNamespace := cmd.privateConfig.Release.Namespace + releaseName := *config.DevSpace.Release.Name + releaseNamespace := *config.DevSpace.Release.Namespace chartPath := "chart/" values := map[interface{}]interface{}{} containerValues := map[interface{}]interface{}{} - containerValues["image"] = cmd.latestImageIP + containerValues["image"] = registry.GetImageUrl(true) if !cmd.flags.noSleep { containerValues["command"] = []string{"sleep"} @@ -287,7 +268,7 @@ func (cmd *UpCmd) deployChart() { values["container"] = containerValues - appRelease, err := cmd.helm.InstallChartByPath(releaseName, releaseNamespace, chartPath, &values) + appRelease, err := cmd.helm.InstallChartByPath(releaseName, releaseNamespace, chartPath, values) log.StopWait() @@ -359,8 +340,10 @@ func (cmd *UpCmd) deployChart() { } func (cmd *UpCmd) startSync() { - for _, syncPath := range cmd.dsConfig.SyncPaths { - absLocalPath, err := filepath.Abs(cmd.workdir + syncPath.LocalSubPath) + config := configutil.GetConfig(false) + + for _, syncPath := range config.DevSpace.Sync { + absLocalPath, err := filepath.Abs(cmd.workdir + *syncPath.LocalSubPath) if err != nil { log.Panicf("Unable to resolve localSubPath %s: %s", syncPath.LocalSubPath, err.Error()) @@ -369,10 +352,10 @@ func (cmd *UpCmd) startSync() { labels := make([]string, 0, len(syncPath.LabelSelector)) for key, value := range syncPath.LabelSelector { - labels = append(labels, key+"="+value) + labels = append(labels, key+"="+*value) } - podList, podListErr := cmd.kubectl.Core().Pods(cmd.privateConfig.Registry.Release.Namespace).List(metav1.ListOptions{ + podList, podListErr := cmd.kubectl.Core().Pods(*config.Services.Registry.Internal.Release.Namespace).List(metav1.ListOptions{ LabelSelector: strings.Join(labels, ", "), }) @@ -384,7 +367,7 @@ func (cmd *UpCmd) startSync() { Pod: &podList.Items[0], Container: &podList.Items[0].Spec.Containers[0], WatchPath: absLocalPath, - DestPath: syncPath.ContainerPath, + DestPath: *syncPath.ContainerPath, } err = syncConfig.Start() @@ -400,16 +383,18 @@ func (cmd *UpCmd) startSync() { } func (cmd *UpCmd) startPortForwarding() { - for _, portForwarding := range cmd.dsConfig.PortForwarding { - if portForwarding.ResourceType == "pod" { + config := configutil.GetConfig(false) + + for _, portForwarding := range config.DevSpace.PortForwarding { + if *portForwarding.ResourceType == "pod" { if len(portForwarding.LabelSelector) > 0 { labels := make([]string, 0, len(portForwarding.LabelSelector)) for key, value := range portForwarding.LabelSelector { - labels = append(labels, key+"="+value) + labels = append(labels, key+"="+*value) } - podList, podListErr := cmd.kubectl.Core().Pods(cmd.privateConfig.Registry.Release.Namespace).List(metav1.ListOptions{ + podList, podListErr := cmd.kubectl.Core().Pods(*config.Services.Registry.Internal.Release.Namespace).List(metav1.ListOptions{ LabelSelector: strings.Join(labels, ", "), }) @@ -420,7 +405,7 @@ func (cmd *UpCmd) startPortForwarding() { ports := make([]string, len(portForwarding.PortMappings)) for index, value := range portForwarding.PortMappings { - ports[index] = strconv.Itoa(value.LocalPort) + ":" + strconv.Itoa(value.RemotePort) + ports[index] = strconv.Itoa(*value.LocalPort) + ":" + strconv.Itoa(*value.RemotePort) } readyChan := make(chan struct{}) diff --git a/pkg/devspace/clients/helm/client.go b/pkg/devspace/clients/helm/client.go index 7c3c48958f..f67014853d 100644 --- a/pkg/devspace/clients/helm/client.go +++ b/pkg/devspace/clients/helm/client.go @@ -23,7 +23,7 @@ import ( "k8s.io/client-go/kubernetes" "github.com/covexo/devspace/pkg/devspace/clients/kubectl" - "github.com/covexo/devspace/pkg/devspace/config" + "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/devspace/config/v1" homedir "github.com/mitchellh/go-homedir" k8sv1 "k8s.io/api/core/v1" @@ -42,15 +42,17 @@ import ( // HelmClientWrapper holds the necessary information for helm type HelmClientWrapper struct { - Client *k8shelm.Client - Settings *helmenvironment.EnvSettings - kubectl *kubernetes.Clientset + Client *k8shelm.Client + Settings *helmenvironment.EnvSettings + TillerConfig *v1.TillerConfig + kubectl *kubernetes.Clientset } // TillerDeploymentName is the string identifier for the tiller deployment const TillerDeploymentName = "tiller-deploy" const tillerServiceAccountName = "devspace-tiller" const tillerRoleName = "devspace-tiller" +const tillerRoleManagerName = "tiller-config-manager" const stableRepoCachePath = "repository/cache/stable-index.yaml" const defaultRepositories = `apiVersion: v1 repositories: @@ -62,7 +64,6 @@ repositories: url: https://kubernetes-charts.storage.googleapis.com ` -var privateConfig = &v1.PrivateConfig{} var defaultPolicyRules = []k8sv1beta1.PolicyRule{ { APIGroups: []string{ @@ -77,15 +78,16 @@ var defaultPolicyRules = []k8sv1beta1.PolicyRule{ // NewClient creates a new helm client func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmClientWrapper, error) { - config.LoadConfig(privateConfig) + config := configutil.GetConfig(false) + tillerConfig := config.Services.Tiller kubeconfig, err := kubectl.GetClientConfig() if err != nil { return nil, err } - tillerErr := ensureTiller(kubectlClient, upgradeTiller) + tillerErr := ensureTiller(kubectlClient, tillerConfig, upgradeTiller) if tillerErr != nil { return nil, tillerErr @@ -101,7 +103,7 @@ func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmCl // Next we wait till we can establish a tunnel to the running pod for tunnelWaitTime > 0 { - tunnel, err = portforwarder.New(privateConfig.Cluster.TillerNamespace, kubectlClient, kubeconfig) + tunnel, err = portforwarder.New(*tillerConfig.Release.Namespace, kubectlClient, kubeconfig) if err == nil { break @@ -169,7 +171,8 @@ func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmCl Settings: &helmenvironment.EnvSettings{ Home: helmpath.Home(helmHomePath), }, - kubectl: kubectlClient, + TillerConfig: tillerConfig, + kubectl: kubectlClient, } _, stableRepoCacheNotFoundErr := os.Stat(stableRepoCachePathAbs) @@ -180,20 +183,21 @@ func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmCl return wrapper, nil } -func ensureTiller(kubectlClient *kubernetes.Clientset, upgrade bool) error { +func ensureTiller(kubectlClient *kubernetes.Clientset, tillerConfig *v1.TillerConfig, upgrade bool) error { + tillerNamespace := *tillerConfig.Release.Namespace tillerSA := &k8sv1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ Name: tillerServiceAccountName, - Namespace: privateConfig.Cluster.TillerNamespace, + Namespace: tillerNamespace, }, } tillerOptions := &helminstaller.Options{ - Namespace: privateConfig.Cluster.TillerNamespace, + Namespace: tillerNamespace, MaxHistory: 10, ImageSpec: "gcr.io/kubernetes-helm/tiller:v2.9.1", ServiceAccount: tillerSA.ObjectMeta.Name, } - _, tillerCheckErr := kubectlClient.ExtensionsV1beta1().Deployments(privateConfig.Cluster.TillerNamespace).Get(TillerDeploymentName, metav1.GetOptions{}) + _, tillerCheckErr := kubectlClient.ExtensionsV1beta1().Deployments(tillerNamespace).Get(TillerDeploymentName, metav1.GetOptions{}) // Tiller is not there if tillerCheckErr != nil { @@ -210,7 +214,7 @@ func ensureTiller(kubectlClient *kubernetes.Clientset, upgrade bool) error { } } - err = ensureRoleBinding(kubectlClient, "tiller-config-manager", privateConfig.Cluster.TillerNamespace, privateConfig.Cluster.TillerNamespace, []k8sv1beta1.PolicyRule{ + err = ensureRoleBinding(kubectlClient, tillerConfig, tillerRoleManagerName, tillerNamespace, []k8sv1beta1.PolicyRule{ { APIGroups: []string{ k8sv1beta1.APIGroupAll, @@ -230,12 +234,13 @@ func ensureTiller(kubectlClient *kubernetes.Clientset, upgrade bool) error { helminstaller.Install(kubectlClient, tillerOptions) - err = ensureRoleBinding(kubectlClient, tillerRoleName, privateConfig.Release.Namespace, privateConfig.Cluster.TillerNamespace, defaultPolicyRules) + for _, appNamespace := range tillerConfig.AppNamespaces { + err = ensureRoleBinding(kubectlClient, tillerConfig, tillerRoleName, *appNamespace, defaultPolicyRules) - if err != nil { - return err + if err != nil { + return err + } } - log.StopWait() log.Done("Tiller started") @@ -259,7 +264,7 @@ func ensureTiller(kubectlClient *kubernetes.Clientset, upgrade bool) error { log.StartWait("Waiting for tiller to start") for tillerWaitingTime > 0 { - tillerDeployment, err := kubectlClient.ExtensionsV1beta1().Deployments(privateConfig.Cluster.TillerNamespace).Get(TillerDeploymentName, metav1.GetOptions{}) + tillerDeployment, err := kubectlClient.ExtensionsV1beta1().Deployments(tillerNamespace).Get(TillerDeploymentName, metav1.GetOptions{}) if err != nil { continue @@ -279,8 +284,9 @@ func ensureTiller(kubectlClient *kubernetes.Clientset, upgrade bool) error { } // IsTillerDeployed determines if we could connect to a tiller server -func IsTillerDeployed(kubectlClient *kubernetes.Clientset, privateConfig *v1.PrivateConfig) bool { - deployment, err := kubectlClient.ExtensionsV1beta1().Deployments(privateConfig.Cluster.TillerNamespace).Get(TillerDeploymentName, metav1.GetOptions{}) +func IsTillerDeployed(kubectlClient *kubernetes.Clientset, tillerConfig *v1.TillerConfig) bool { + tillerNamespace := *tillerConfig.Release.Namespace + deployment, err := kubectlClient.ExtensionsV1beta1().Deployments(tillerNamespace).Get(TillerDeploymentName, metav1.GetOptions{}) if err != nil { return false @@ -294,11 +300,12 @@ func IsTillerDeployed(kubectlClient *kubernetes.Clientset, privateConfig *v1.Pri } // DeleteTiller clears the tiller server, the service account and role binding -func DeleteTiller(kubectlClient *kubernetes.Clientset, privateConfig *v1.PrivateConfig) error { +func DeleteTiller(kubectlClient *kubernetes.Clientset, tillerConfig *v1.TillerConfig) error { + tillerNamespace := *tillerConfig.Release.Namespace errs := make([]error, 0, 1) propagationPolicy := metav1.DeletePropagationForeground - err := kubectlClient.ExtensionsV1beta1().Deployments(privateConfig.Cluster.TillerNamespace).Delete(TillerDeploymentName, &metav1.DeleteOptions{ + err := kubectlClient.ExtensionsV1beta1().Deployments(tillerNamespace).Delete(TillerDeploymentName, &metav1.DeleteOptions{ PropagationPolicy: &propagationPolicy, }) @@ -306,30 +313,26 @@ func DeleteTiller(kubectlClient *kubernetes.Clientset, privateConfig *v1.Private errs = append(errs, err) } - err = kubectlClient.CoreV1().ServiceAccounts(privateConfig.Cluster.TillerNamespace).Delete(tillerServiceAccountName, &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}) + err = kubectlClient.CoreV1().ServiceAccounts(tillerNamespace).Delete(tillerServiceAccountName, &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}) if err != nil { errs = append(errs, err) } + roleNamespace := append(tillerConfig.AppNamespaces, &tillerNamespace) - deleteRoleNames := []string{ - "tiller-config-manager", - tillerRoleName, - } + for _, appNamespace := range roleNamespace { + roleName := tillerRoleName - deleteRoleNamespaces := []string{ - privateConfig.Cluster.TillerNamespace, - privateConfig.Release.Namespace, - } - - for key, value := range deleteRoleNames { - err = kubectlClient.RbacV1beta1().Roles(deleteRoleNamespaces[key]).Delete(value, &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}) + if *appNamespace == tillerNamespace { + roleName = tillerRoleManagerName + } + err = kubectlClient.RbacV1beta1().Roles(*appNamespace).Delete(roleName, &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}) if err != nil { errs = append(errs, err) } - err = kubectlClient.RbacV1beta1().RoleBindings(deleteRoleNamespaces[key]).Delete(value+"-binding", &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}) + err = kubectlClient.RbacV1beta1().RoleBindings(*appNamespace).Delete(roleName+"-binding", &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}) if err != nil { errs = append(errs, err) @@ -353,7 +356,8 @@ func DeleteTiller(kubectlClient *kubernetes.Clientset, privateConfig *v1.Private // return ensureRoleBinding(helmClientWrapper.kubectl, tillerRoleName, namespace, helmClientWrapper.Settings.TillerNamespace, defaultPolicyRules) // } -func ensureRoleBinding(kubectlClient *kubernetes.Clientset, name, namespace string, tillerNamespace string, rules []k8sv1beta1.PolicyRule) error { +func ensureRoleBinding(kubectlClient *kubernetes.Clientset, tillerConfig *v1.TillerConfig, name, namespace string, rules []k8sv1beta1.PolicyRule) error { + tillerNamespace := *tillerConfig.Release.Namespace role := &k8sv1beta1.Role{ ObjectMeta: metav1.ObjectMeta{ Name: name, @@ -438,7 +442,7 @@ func (helmClientWrapper *HelmClientWrapper) ReleaseExists(releaseName string) (b } // InstallChartByPath installs the given chartpath und the releasename in the releasenamespace -func (helmClientWrapper *HelmClientWrapper) InstallChartByPath(releaseName string, releaseNamespace string, chartPath string, values *map[interface{}]interface{}) (*hapi_release5.Release, error) { +func (helmClientWrapper *HelmClientWrapper) InstallChartByPath(releaseName string, releaseNamespace string, chartPath string, values map[interface{}]interface{}) (*hapi_release5.Release, error) { chart, err := helmchartutil.Load(chartPath) if err != nil { @@ -454,13 +458,13 @@ func (helmClientWrapper *HelmClientWrapper) InstallChartByPath(releaseName strin return nil, err } chartDownloader := &helmdownloader.Manager{ - /* Out: i.out, - ChartPath: i.chartPath, - HelmHome: settings.Home, - Keyring: defaultKeyring(), - SkipUpdate: false, - Getters: getter.All(settings), - */ + /* Out: i.out, + ChartPath: i.chartPath, + HelmHome: settings.Home, + Keyring: defaultKeyring(), + SkipUpdate: false, + Getters: getter.All(settings), + */ } err = chartDownloader.Update() @@ -483,7 +487,7 @@ func (helmClientWrapper *HelmClientWrapper) InstallChartByPath(releaseName strin overwriteValues := []byte("") if values != nil { - unmarshalledValues, err := yaml.Marshal(*values) + unmarshalledValues, err := yaml.Marshal(values) if err != nil { return nil, err @@ -529,7 +533,7 @@ func (helmClientWrapper *HelmClientWrapper) InstallChartByPath(releaseName strin } // InstallChartByName installs the given chart by name under the releasename in the releasenamespace -func (helmClientWrapper *HelmClientWrapper) InstallChartByName(releaseName string, releaseNamespace string, chartName string, chartVersion string, values *map[interface{}]interface{}) (*hapi_release5.Release, error) { +func (helmClientWrapper *HelmClientWrapper) InstallChartByName(releaseName string, releaseNamespace string, chartName string, chartVersion string, values map[interface{}]interface{}) (*hapi_release5.Release, error) { if len(chartVersion) == 0 { chartVersion = ">0.0.0-0" } diff --git a/pkg/devspace/clients/kubectl/client.go b/pkg/devspace/clients/kubectl/client.go index 301047adc1..bacc025f43 100644 --- a/pkg/devspace/clients/kubectl/client.go +++ b/pkg/devspace/clients/kubectl/client.go @@ -9,7 +9,7 @@ import ( "os" "sync" - "github.com/covexo/devspace/pkg/devspace/config" + "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/devspace/config/v1" "github.com/covexo/devspace/pkg/util/log" dockerterm "github.com/docker/docker/pkg/term" @@ -167,19 +167,19 @@ func GetPodsFromDeployment(kubectl *kubernetes.Clientset, deployment, namespace //GetClientConfig loads the configuration for kubernetes clients and parses it to *rest.Config func GetClientConfig() (*rest.Config, error) { - config.LoadConfig(privateConfig) + config := configutil.GetConfig(false) - if privateConfig.Cluster == nil { + if config.Cluster == nil { return nil, errors.New("Couldn't load cluster config, did you run devspace init") } return &rest.Config{ - Host: privateConfig.Cluster.ApiServer, - Username: privateConfig.Cluster.User.Username, + Host: *config.Cluster.ApiServer, + Username: *config.Cluster.User.Username, TLSClientConfig: rest.TLSClientConfig{ - CAData: []byte(privateConfig.Cluster.CaCert), - CertData: []byte(privateConfig.Cluster.User.ClientCert), - KeyData: []byte(privateConfig.Cluster.User.ClientKey), + CAData: []byte(*config.Cluster.CaCert), + CertData: []byte(*config.Cluster.User.ClientCert), + KeyData: []byte(*config.Cluster.User.ClientKey), }, }, nil } diff --git a/pkg/devspace/config/config.go b/pkg/devspace/config/config.go deleted file mode 100644 index d1f878553b..0000000000 --- a/pkg/devspace/config/config.go +++ /dev/null @@ -1,190 +0,0 @@ -package config - -import ( - "errors" - "io/ioutil" - "os" - "path/filepath" - "reflect" - "strings" - - "github.com/covexo/devspace/pkg/devspace/config/v1" - "github.com/covexo/devspace/pkg/util/fsutil" - "k8s.io/client-go/tools/clientcmd" - - yaml "gopkg.in/yaml.v2" -) - -//ConfigInterface defines the pattern of every config -type ConfigInterface interface{} - -const configGitignore = `logs/ -private.yaml -cache.yaml -` - -var workdir, _ = os.Getwd() -var configFilesLoaded = map[string][]byte{} -var configPaths = map[string]string{ - "PrivateConfig": workdir + "/.devspace/private.yaml", - "DevSpaceConfig": workdir + "/.devspace/config.yaml", -} - -//GetConfigPath returns the path to the config's yaml file -func GetConfigPath(config ConfigInterface) (string, error) { - configType, _ := getConfigType(config) - configPath, _ := configPaths[configType] - - return configPath, nil -} - -//ConfigExists chacks whether the yaml file for the config exists -func ConfigExists(config ConfigInterface) (bool, error) { - configPath, _ := GetConfigPath(config) - - _, configNotFound := os.Stat(configPath) - - return (configNotFound == nil), nil -} - -//LoadConfig loads a config from its yaml file -func LoadConfig(config ConfigInterface) error { - configType, _ := getConfigType(config) - loadedFile, isLoaded := configFilesLoaded[configType] - - if !isLoaded { - loadYamlFromFile(configType) - - loadedFile, isLoaded = configFilesLoaded[configType] - - if !isLoaded { - return errors.New("Unable to load " + configType) - } - } - unmarshalErr := yaml.Unmarshal(loadedFile, config) - - if configType == "PrivateConfig" { - privateConf, isPrivateConf := config.(*v1.PrivateConfig) - - if isPrivateConf && privateConf.Cluster.UseKubeConfig { - LoadClusterConfig(privateConf.Cluster, false) - } - } - return unmarshalErr -} - -//LoadClusterConfig loads the config for a kubernetes cluster -func LoadClusterConfig(config *v1.Cluster, overwriteExistingValues bool) { - kubeconfig, kubeconfigErr := clientcmd.BuildConfigFromFlags("", filepath.Join(fsutil.GetHomeDir(), ".kube", "config")) - - if kubeconfigErr == nil { - if len(config.ApiServer) == 0 { - if len(kubeconfig.Host) != 0 { - config.ApiServer = kubeconfig.Host - } - } - - if len(config.CaCert) == 0 { - if len(kubeconfig.TLSClientConfig.CAData) == 0 { - caData, caFileErr := fsutil.ReadFile(kubeconfig.TLSClientConfig.CAFile, 0) - - if caFileErr == nil { - config.CaCert = string(caData) - } - } else { - config.CaCert = string(kubeconfig.CAData) - } - } - - if config.User == nil { - config.User = &v1.User{} - } - - if len(config.User.Username) == 0 { - config.User.Username = kubeconfig.Username - } - - if len(config.User.ClientCert) == 0 { - if len(kubeconfig.TLSClientConfig.CertData) == 0 { - certData, certFileErr := fsutil.ReadFile(kubeconfig.TLSClientConfig.CertFile, 0) - - if certFileErr == nil { - config.User.ClientCert = string(certData) - } - } else { - config.User.ClientCert = string(kubeconfig.TLSClientConfig.CertData) - } - } - - if len(config.User.ClientKey) == 0 { - if len(kubeconfig.TLSClientConfig.KeyData) == 0 { - keyData, keyFileErr := fsutil.ReadFile(kubeconfig.TLSClientConfig.KeyFile, 0) - - if keyFileErr == nil { - config.User.ClientKey = string(keyData) - } - } else { - config.User.ClientKey = string(kubeconfig.TLSClientConfig.KeyData) - } - } - } -} - -//SaveConfig writes the data of a config to its yaml file -func SaveConfig(config ConfigInterface) error { - configType, _ := getConfigType(config) - var currentClusterConfig v1.Cluster - isPrivateConf := (configType == "PrivateConfig") - - if isPrivateConf { - privateConf, isPrivateConf := config.(*v1.PrivateConfig) - currentClusterConfig = *privateConf.Cluster - - if isPrivateConf && privateConf.Cluster.UseKubeConfig { - privateConf.Cluster.ApiServer = "" - privateConf.Cluster.CaCert = "" - privateConf.Cluster.User = nil - } - } - yamlString, yamlErr := yaml.Marshal(config) - - if isPrivateConf { - privateConf, _ := config.(*v1.PrivateConfig) - - privateConf.Cluster = ¤tClusterConfig - } - - if yamlErr != nil { - return yamlErr - } - configFilesLoaded[configType] = yamlString - - return saveYamlToFile(configType) -} - -func getConfigType(config ConfigInterface) (string, error) { - configGolangType := reflect.TypeOf(config).String() - configType := strings.Split(configGolangType, ".")[1] - - return configType, nil -} - -func loadYamlFromFile(configType string) error { - yamlFileContent, err := ioutil.ReadFile(configPaths[configType]) - - if err != nil { - return err - } - configFilesLoaded[configType] = yamlFileContent - - return nil -} - -func saveYamlToFile(configType string) error { - os.MkdirAll(filepath.Dir(configPaths[configType]), os.ModePerm) - - if configType == "PrivateConfig" { - fsutil.WriteToFile([]byte(configGitignore), filepath.Join(filepath.Dir(configPaths[configType]), ".gitignore")) - } - return ioutil.WriteFile(configPaths[configType], configFilesLoaded[configType], os.ModePerm) -} diff --git a/pkg/devspace/config/configutil/get.go b/pkg/devspace/config/configutil/get.go new file mode 100644 index 0000000000..27115bf055 --- /dev/null +++ b/pkg/devspace/config/configutil/get.go @@ -0,0 +1,74 @@ +package configutil + +import ( + "os" + "unsafe" + + "github.com/covexo/devspace/pkg/util/log" + + "github.com/covexo/devspace/pkg/devspace/config/v1" +) + +//ConfigInterface defines the pattern of every config +type ConfigInterface interface{} + +const configGitignore = `logs/ +overwrite.yaml +` + +const configPath = "/.devspace/config.yaml" +const overwriteConfigPath = "/.devspace/overwrite.yaml" + +var config = makeConfig() +var configRaw = makeConfig() +var overwriteConfig = makeConfig() +var overwriteConfigRaw = makeConfig() +var workdir string + +func init() { + workdir, _ = os.Getwd() +} + +//ConfigExists chacks whether the yaml file for the config exists +func ConfigExists() (bool, error) { + _, configNotFound := os.Stat(workdir + configPath) + + return (configNotFound == nil), nil +} + +func GetConfig(reload bool) *v1.Config { + isLoaded := (config.Version != nil) + + if !isLoaded || reload { + err := loadConfig(configRaw, configPath) + + if err != nil { + log.Fatal("Unable to load config.") + } + GetOverwriteConfig() + + merge(config, configRaw, unsafe.Pointer(&config), unsafe.Pointer(configRaw)) + merge(config, overwriteConfig, unsafe.Pointer(&config), unsafe.Pointer(overwriteConfig)) + } + + if config.Cluster.UseKubeConfig != nil && *config.Cluster.UseKubeConfig { + loadClusterConfig(config.Cluster, false) + } + return config +} + +func GetOverwriteConfig() *v1.Config { + isLoaded := (overwriteConfig.Version != nil) + + if !isLoaded { + //ignore error as overwrite.yaml is optional + loadConfig(overwriteConfigRaw, overwriteConfigPath) + + merge(overwriteConfig, overwriteConfigRaw, unsafe.Pointer(&overwriteConfig), unsafe.Pointer(overwriteConfigRaw)) + } + return overwriteConfig +} + +func GetConfigInstance() *v1.Config { + return config +} diff --git a/pkg/devspace/config/configutil/load.go b/pkg/devspace/config/configutil/load.go new file mode 100644 index 0000000000..63f759ca6f --- /dev/null +++ b/pkg/devspace/config/configutil/load.go @@ -0,0 +1,77 @@ +package configutil + +import ( + "io/ioutil" + "path/filepath" + + "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/util/fsutil" + yaml "gopkg.in/yaml.v2" + "k8s.io/client-go/tools/clientcmd" +) + +func loadConfig(config *v1.Config, path string) error { + yamlFileContent, err := ioutil.ReadFile(workdir + path) + + if err != nil { + return err + } + return yaml.Unmarshal(yamlFileContent, config) +} + +//LoadClusterConfig loads the config for a kubernetes cluster +func loadClusterConfig(config *v1.Cluster, overwriteExistingValues bool) { + kubeconfig, kubeconfigErr := clientcmd.BuildConfigFromFlags("", filepath.Join(fsutil.GetHomeDir(), ".kube", "config")) + + if kubeconfigErr == nil { + if config.ApiServer == nil { + if len(kubeconfig.Host) != 0 { + config.ApiServer = String(kubeconfig.Host) + } + } + + if config.CaCert == nil { + if len(kubeconfig.TLSClientConfig.CAData) == 0 { + caData, caFileErr := fsutil.ReadFile(kubeconfig.TLSClientConfig.CAFile, 0) + + if caFileErr == nil { + config.CaCert = String(string(caData)) + } + } else { + config.CaCert = String(string(kubeconfig.CAData)) + } + } + + if config.User == nil { + config.User = &v1.User{} + } + + if config.User.Username == nil { + config.User.Username = String(kubeconfig.Username) + } + + if config.User.ClientCert == nil { + if len(kubeconfig.TLSClientConfig.CertData) == 0 { + certData, certFileErr := fsutil.ReadFile(kubeconfig.TLSClientConfig.CertFile, 0) + + if certFileErr == nil { + config.User.ClientCert = String(string(certData)) + } + } else { + config.User.ClientCert = String(string(kubeconfig.TLSClientConfig.CertData)) + } + } + + if config.User.ClientKey == nil { + if len(kubeconfig.TLSClientConfig.KeyData) == 0 { + keyData, keyFileErr := fsutil.ReadFile(kubeconfig.TLSClientConfig.KeyFile, 0) + + if keyFileErr == nil { + config.User.ClientKey = String(string(keyData)) + } + } else { + config.User.ClientKey = String(string(kubeconfig.TLSClientConfig.KeyData)) + } + } + } +} diff --git a/pkg/devspace/config/configutil/make.go b/pkg/devspace/config/configutil/make.go new file mode 100644 index 0000000000..50483ae625 --- /dev/null +++ b/pkg/devspace/config/configutil/make.go @@ -0,0 +1,29 @@ +package configutil + +import "github.com/covexo/devspace/pkg/devspace/config/v1" + +func makeConfig() *v1.Config { + return &v1.Config{ + Cluster: &v1.Cluster{ + User: &v1.User{}, + }, + DevSpace: &v1.DevSpaceConfig{ + PortForwarding: []*v1.PortForwardingConfig{}, + Release: &v1.Release{}, + Sync: []*v1.SyncConfig{}, + }, + Image: &v1.ImageConfig{}, + Services: &v1.ServiceConfig{ + Registry: &v1.RegistryConfig{ + Internal: &v1.InternalRegistry{ + Release: &v1.Release{}, + }, + User: &v1.RegistryUser{}, + }, + Tiller: &v1.TillerConfig{ + AppNamespaces: []*string{}, + Release: &v1.Release{}, + }, + }, + } +} diff --git a/pkg/devspace/config/configutil/merge.go b/pkg/devspace/config/configutil/merge.go new file mode 100644 index 0000000000..740f110f8f --- /dev/null +++ b/pkg/devspace/config/configutil/merge.go @@ -0,0 +1,47 @@ +package configutil + +import ( + "reflect" + "unsafe" +) + +func merge(objectPointer interface{}, overwriteObjectPointer interface{}, objectPointerUnsafe unsafe.Pointer, overwriteObjectPointerUnsafe unsafe.Pointer) { + overwriteObjectRef := reflect.ValueOf(overwriteObjectPointer) + + if !overwriteObjectRef.IsNil() { + if overwriteObjectRef.Kind() == reflect.Ptr { + overwriteObjectRef = overwriteObjectRef.Elem() + } + overwriteObject := overwriteObjectRef.Interface() + overwriteObjectType := reflect.TypeOf(overwriteObject) + overwriteObjectKind := overwriteObjectType.Kind() + + switch overwriteObjectKind { + case reflect.Slice: + case reflect.Struct: + objectValues := reflect.ValueOf(objectPointer).Elem() + overwriteObjectValues := reflect.ValueOf(overwriteObjectPointer).Elem() + + for i := 0; i < overwriteObjectValues.NumField(); i++ { + //fieldName := objectValues.Type().Field(i).Name + overwriteValueRef := overwriteObjectValues.Field(i) + overwriteValuePointerRef := reflect.ValueOf(overwriteValueRef.Interface()) + + if !overwriteValuePointerRef.IsNil() { + overwriteValue := overwriteValueRef.Interface() + valuePointerRef := objectValues.Field(i) + + if valuePointerRef.IsNil() { + objectValues.Field(i).Set(reflect.ValueOf(overwriteValue)) + } else { + valuePointer := objectValues.Field(i).Interface() + + merge(valuePointer, overwriteValue, unsafe.Pointer(&valuePointer), unsafe.Pointer(&overwriteValue)) + } + } + } + default: + *(*unsafe.Pointer)(objectPointerUnsafe) = overwriteObjectPointerUnsafe + } + } +} diff --git a/pkg/devspace/config/configutil/save.go b/pkg/devspace/config/configutil/save.go new file mode 100644 index 0000000000..a2a6bfb38c --- /dev/null +++ b/pkg/devspace/config/configutil/save.go @@ -0,0 +1,250 @@ +package configutil + +import ( + "errors" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "strings" + "unsafe" + + "github.com/covexo/devspace/pkg/util/fsutil" + yaml "gopkg.in/yaml.v2" +) + +//SaveConfig writes the data of a config to its yaml file +func SaveConfig() error { + configExists, _ := ConfigExists() + + // just in case someone has set a pointer to one of the structs to nil, merge empty an empty config object into all configs + baseConfig := makeConfig() + merge(config, baseConfig, unsafe.Pointer(&config), unsafe.Pointer(baseConfig)) + merge(configRaw, baseConfig, unsafe.Pointer(&configRaw), unsafe.Pointer(baseConfig)) + merge(overwriteConfig, baseConfig, unsafe.Pointer(&overwriteConfig), unsafe.Pointer(baseConfig)) + merge(overwriteConfigRaw, baseConfig, unsafe.Pointer(&overwriteConfigRaw), unsafe.Pointer(baseConfig)) + + configMapRaw, overwriteMapRaw, configErr := getConfigAndOverwriteMaps(config, configRaw, overwriteConfig, overwriteConfigRaw) + + configMap, _ := configMapRaw.(map[interface{}]interface{}) + overwriteMap, _ := overwriteMapRaw.(map[interface{}]interface{}) + + if configErr != nil { + return configErr + } + + if config.Cluster.UseKubeConfig != nil && *config.Cluster.UseKubeConfig { + clusterConfig := map[string]bool{ + "useKubeConfig": true, + } + _, configHasCluster := configMap["cluster"] + + if configHasCluster { + configMap["cluster"] = clusterConfig + } + _, overwriteConfigHasCluster := overwriteMap["cluster"] + + if overwriteConfigHasCluster { + overwriteMap["cluster"] = clusterConfig + } + } + configYaml, yamlErr := yaml.Marshal(configMap) + + if yamlErr != nil { + return yamlErr + } + configDir := filepath.Dir(workdir + configPath) + + os.MkdirAll(configDir, os.ModePerm) + + if !configExists { + fsutil.WriteToFile([]byte(configGitignore), filepath.Join(configDir, ".gitignore")) + } + writeErr := ioutil.WriteFile(workdir+configPath, configYaml, os.ModePerm) + + if writeErr != nil { + return writeErr + } + + if overwriteMap != nil { + overwriteConfigYaml, yamlErr := yaml.Marshal(overwriteMap) + + if yamlErr != nil { + return yamlErr + } + return ioutil.WriteFile(workdir+overwriteConfigPath, overwriteConfigYaml, os.ModePerm) + } + return nil +} + +func getConfigAndOverwriteMaps(config interface{}, configRaw interface{}, overwriteConfig interface{}, overwriteConfigRaw interface{}) (interface{}, interface{}, error) { + object, isObjectNil := getPointerValue(config) + objectType := reflect.TypeOf(object) + objectKind := objectType.Kind() + overwriteObject, isOverwriteObjectNil := getPointerValue(overwriteConfig) + overwriteObjectKind := reflect.TypeOf(overwriteObject).Kind() + + if objectKind != overwriteObjectKind && !isObjectNil && !isOverwriteObjectNil { + return nil, nil, errors.New("config (type: " + objectKind.String() + ") and overwriteConfig (type: " + overwriteObjectKind.String() + ") must be instances of the same type.") + } + objectValueRef := reflect.ValueOf(object) + objectValue := objectValueRef.Interface() + overwriteValueRef := reflect.ValueOf(overwriteObject) + overwriteValue := overwriteValueRef.Interface() + objectRaw, isObjectRawNil := getPointerValue(configRaw) + objectRawValueRef := reflect.ValueOf(objectRaw) + objectRawValue := objectRawValueRef.Interface() + overwriteObjectRaw, _ := getPointerValue(overwriteConfigRaw) + overwriteRawValueRef := reflect.ValueOf(overwriteObjectRaw) + overwriteRawValue := overwriteRawValueRef.Interface() + + switch objectKind { + case reflect.Slice: + returnSlice := []interface{}{} + returnOverwriteSlice := []interface{}{} + + for i := 0; i < objectValueRef.Len(); i++ { + val := objectValueRef.Index(i) + //TODO: remove overwriteValues and write them into returnOverwriteSlice + returnSlice = append(returnSlice, val) + } + + if len(returnSlice) > 0 && len(returnOverwriteSlice) > 0 { + return returnSlice, returnOverwriteSlice, nil + } else if len(returnSlice) > 0 { + return returnSlice, nil, nil + } else if len(returnOverwriteSlice) > 0 { + return nil, returnOverwriteSlice, nil + } else { + return nil, nil, nil + } + case reflect.Map: + valueMap := objectValue.(map[interface{}]interface{}) + returnMap := map[interface{}]interface{}{} + returnOverwriteMap := map[interface{}]interface{}{} + + for key, val := range valueMap { + key = getYamlKey(key.(string)) + valType := reflect.TypeOf(val) + + cleanVal, cleanOverwriteVal, err := getConfigAndOverwriteMaps( + val, + getValueOrZero(objectRawValue, key, valType), + getValueOrZero(overwriteValue, key, valType), + getValueOrZero(overwriteRawValue, key, valType), + ) + + if err != nil { + return nil, nil, err + } + + if cleanVal != nil { + returnMap[key] = cleanVal + } + + if cleanOverwriteVal != nil { + returnOverwriteMap[key] = cleanOverwriteVal + } + } + + if len(returnMap) > 0 && len(returnOverwriteMap) > 0 { + return returnMap, returnOverwriteMap, nil + } else if len(returnMap) > 0 { + return returnMap, nil, nil + } else if len(returnOverwriteMap) > 0 { + return nil, returnOverwriteMap, nil + } else { + return nil, nil, nil + } + case reflect.Struct: + returnMap := map[interface{}]interface{}{} + returnOverwriteMap := map[interface{}]interface{}{} + + for i := 0; i < objectValueRef.NumField(); i++ { + fieldName := getYamlKey(objectValueRef.Type().Field(i).Name) + + fieldValue := objectValueRef.Field(i).Interface() + fieldRawValue := objectRawValueRef.Field(i).Interface() + overwriteFieldValue := overwriteValueRef.Field(i).Interface() + overwriteRawFieldValue := overwriteRawValueRef.Field(i).Interface() + + fieldValueClean, overwriteFieldValueClean, err := getConfigAndOverwriteMaps( + fieldValue, + fieldRawValue, + overwriteFieldValue, + overwriteRawFieldValue, + ) + + if err != nil { + return nil, nil, err + } + + if fieldValueClean != nil { + returnMap[fieldName] = fieldValueClean + } + + if overwriteFieldValueClean != nil { + returnOverwriteMap[fieldName] = overwriteFieldValueClean + } + } + + if len(returnMap) > 0 && len(returnOverwriteMap) > 0 { + return returnMap, returnOverwriteMap, nil + } else if len(returnMap) > 0 { + return returnMap, nil, nil + } else if len(returnOverwriteMap) > 0 { + return nil, returnOverwriteMap, nil + } else { + return nil, nil, nil + } + default: + saveOverwriteValue := !isOverwriteObjectNil + saveValue := ((!isObjectNil && !saveOverwriteValue) || !isObjectRawNil) + + //TODO: Determine overwritten values and set objectValue accordingly + + if saveValue && saveOverwriteValue { + return objectValue, overwriteValue, nil + } else if saveOverwriteValue { + return nil, overwriteValue, nil + } else if saveValue { + return objectValue, nil, nil + } else { + return nil, nil, nil + } + } +} + +func getValueOrZero(valueMap interface{}, key interface{}, refType reflect.Type) interface{} { + valueMapValidated := valueMap.(map[interface{}]interface{}) + value, valueExists := valueMapValidated[key] + + if !valueExists { + value = reflect.Zero(refType).Interface() + } + return value +} + +func getYamlKey(key string) string { + return strings.ToLower(key[0:1]) + key[1:] +} + +func getPointerValue(object interface{}) (interface{}, bool) { + if object != nil { + objectType := reflect.TypeOf(object) + objectKind := objectType.Kind() + + if objectKind == reflect.Ptr { + objectValueRef := reflect.ValueOf(object) + + if objectValueRef.IsNil() { + zeroValue := reflect.Zero(objectValueRef.Type()).Interface() + + return zeroValue, true + } else { + return objectValueRef.Elem().Interface(), false + } + } + } + return object, false +} diff --git a/pkg/devspace/config/configutil/set.go b/pkg/devspace/config/configutil/set.go new file mode 100644 index 0000000000..d116a2abb3 --- /dev/null +++ b/pkg/devspace/config/configutil/set.go @@ -0,0 +1,9 @@ +package configutil + +func String(val string) *string { + return &val +} + +func Bool(val bool) *bool { + return &val +} diff --git a/pkg/devspace/config/v1/schema.go b/pkg/devspace/config/v1/schema.go index e956a027aa..88e2bbfec3 100644 --- a/pkg/devspace/config/v1/schema.go +++ b/pkg/devspace/config/v1/schema.go @@ -4,94 +4,102 @@ package v1 const Version string = "v1" //DevSpaceConfig defines the config for a DevSpace +type Config struct { + Version *string `yaml:"version"` + DevSpace *DevSpaceConfig `yaml:"devSpace,omitempty"` + Image *ImageConfig `yaml:"image,omitempty"` + Cluster *Cluster `yaml:"cluster,omitempty"` + Services *ServiceConfig `yaml:"services,omitempty"` +} + +type ServiceConfig struct { + Tiller *TillerConfig `yaml:"tiller,omitempty"` + Registry *RegistryConfig `yaml:"registry,omitempty"` +} + +type ImageConfig struct { + Name *string `yaml:"name"` + Tag *string `yaml:"tag"` + BuildTime *string `yaml:"buildTime"` +} + +type TillerConfig struct { + Release *Release `yaml:"release"` + AppNamespaces []*string `yaml:"appNamespaces"` +} + type DevSpaceConfig struct { - Version string `yaml:"version"` - PortForwarding []*PortForwarding `yaml:"portForwarding"` - SyncPaths []*SyncPath `yaml:"syncPath"` - Registry map[interface{}]interface{} `yaml:"registry,omitempty"` + Release *Release `yaml:"release"` + PortForwarding []*PortForwardingConfig `yaml:"portForwarding"` + Sync []*SyncConfig `yaml:"sync"` +} + +type RegistryConfig struct { + External *string `yaml:"external,omitempty"` + Internal *InternalRegistry `yaml:"internal,omitempty"` + User *RegistryUser `yaml:"user,omitempty"` + Insecure *bool `yaml:"insecure,omitempty"` +} + +type InternalRegistry struct { + Release *Release `yaml:"release,omitempty"` + Host *string `yaml:"host,omitempty"` +} + +//RegistryUser is a user for the registry +type RegistryUser struct { + Username *string `yaml:"username"` + Password *string `yaml:"password"` } //PortForwarding defines the ports for a port forwarding to a DevSpace -type PortForwarding struct { - ResourceType string `yaml:"resourceType"` - LabelSelector map[string]string `yaml:"labelSelector"` - PortMappings []*PortMapping `yaml:"portMappings"` +type PortForwardingConfig struct { + ResourceType *string `yaml:"resourceType"` + LabelSelector map[string]*string `yaml:"labelSelector"` + PortMappings []*PortMapping `yaml:"portMappings"` } //PortMapping defines the ports for a PortMapping type PortMapping struct { - LocalPort int `yaml:"localPort"` - RemotePort int `yaml:"remotePort"` + LocalPort *int `yaml:"localPort"` + RemotePort *int `yaml:"remotePort"` } //SyncPath defines the paths for a SyncFolder -type SyncPath struct { - ResourceType string `yaml:"resourceType"` - LabelSelector map[string]string `yaml:"labelSelector"` - LocalSubPath string `yaml:"localSubPath"` - ContainerPath string `yaml:"containerPath"` - ExcludeRegex []string `yaml:"excludeRegex"` +type SyncConfig struct { + ResourceType *string `yaml:"resourceType"` + LabelSelector map[string]*string `yaml:"labelSelector"` + LocalSubPath *string `yaml:"localSubPath"` + ContainerPath *string `yaml:"containerPath"` + ExcludeRegex []*string `yaml:"excludeRegex"` } //PrivateConfig defines the private config of the users' computer type PrivateConfig struct { - Version string `yaml:"version"` + Version *string `yaml:"version"` Release *Release `yaml:"release"` - Registry *RegistryAccess `yaml:"registry"` - Cluster *Cluster `yaml:"cluster"` + Tiller *TillerConfig `yaml:"tiller,omitempty"` + Registry *RegistryConfig `yaml:"registry"` } //Release defines running version of a project type Release struct { - Name string `yaml:"name"` - Namespace string `yaml:"namespace"` - LatestBuild string `yaml:"latestBuild,omitempty"` - LatestImage string `yaml:"latestImage,omitempty"` -} - -//RegistryAccess sets the access from a user to a release -type RegistryAccess struct { - Release *Release `yaml:"release"` - User *RegistryUser `yaml:"user"` -} - -//RegistryUser is a user for the registry -type RegistryUser struct { - Username string `yaml:"username"` - Password string `yaml:"password"` + Name *string `yaml:"name"` + Namespace *string `yaml:"namespace"` + Values map[interface{}]interface{} `yaml:"internal,omitempty"` } //Cluster is a struct that contains data for a Kubernetes-Cluster type Cluster struct { - TillerNamespace string `yaml:"tillerNamespace"` - UseKubeConfig bool `yaml:"useKubeConfig,omitempty"` - ApiServer string `yaml:"apiServer,omitempty"` - CaCert string `yaml:"caCert,omitempty"` - User *User `yaml:"user,omitempty"` + UseKubeConfig *bool `yaml:"useKubeConfig,omitempty"` + ApiServer *string `yaml:"apiServer,omitempty"` + CaCert *string `yaml:"caCert,omitempty"` + User *User `yaml:"user,omitempty"` } //User is a user with its username and its client certificate type User struct { - Username string `yaml:"username,omitempty"` - ClientCert string `yaml:"clientCert,omitempty"` - ClientKey string `yaml:"clientKey,omitempty"` -} - -//AppConfig is the config for a single app -type AppConfig struct { - Name string - Container *AppContainer - External *AppExternal -} - -//AppContainer is the container in which an app is running -type AppContainer struct { - Image string - Ports []int -} - -//AppExternal defines the external acces to an app -type AppExternal struct { - Domain string - Port int + Username *string `yaml:"username,omitempty"` + ClientCert *string `yaml:"clientCert,omitempty"` + ClientKey *string `yaml:"clientKey,omitempty"` } diff --git a/pkg/devspace/kaniko/kaniko.go b/pkg/devspace/kaniko/kaniko.go index 5a6684579d..7ee7cedb93 100644 --- a/pkg/devspace/kaniko/kaniko.go +++ b/pkg/devspace/kaniko/kaniko.go @@ -19,7 +19,7 @@ import ( ) // BuildDockerfile builds a dockerfile in a kaniko build pod -func BuildDockerfile(client *kubernetes.Clientset, buildNamespace, imageDestination, pullSecretName string) error { +func BuildDockerfile(client *kubernetes.Clientset, buildNamespace, imageDestination, pullSecretName string, allowInsecureRegistry bool) error { //registrySecretName := cmd.privateConfig.Registry.Release.Name + "-docker-registry-secret" //registryHostname := cmd.privateConfig.Registry.Release.Name + "-docker-registry." + cmd.privateConfig.Registry.Release.Namespace + ".svc.cluster.local:5000" workdir, err := os.Getwd() @@ -143,16 +143,19 @@ func BuildDockerfile(client *kubernetes.Clientset, buildNamespace, imageDestinat containerBuildPath := "/src/" + filepath.Base(workdir) exitChannel := make(chan error) - - stdin, stdout, stderr, execErr := kubectl.Exec(client, buildPod, buildContainer.Name, []string{ + kanikoBuildCmd := []string{ "/kaniko/executor", "--dockerfile=" + containerBuildPath + "/Dockerfile", "--context=dir://" + containerBuildPath, "--destination=" + imageDestination, - "--insecure-skip-tls-verify", "--single-snapshot", - }, false, exitChannel) + } + + if allowInsecureRegistry { + kanikoBuildCmd = append(kanikoBuildCmd, "--insecure-skip-tls-verify") + } + stdin, stdout, stderr, execErr := kubectl.Exec(client, buildPod, buildContainer.Name, kanikoBuildCmd, false, exitChannel) stdin.Close() if execErr != nil { diff --git a/pkg/devspace/registry/registry.go b/pkg/devspace/registry/registry.go index 9da326159c..b260b54823 100644 --- a/pkg/devspace/registry/registry.go +++ b/pkg/devspace/registry/registry.go @@ -8,132 +8,114 @@ import ( "time" "github.com/covexo/devspace/pkg/util/log" - "github.com/covexo/devspace/pkg/util/randutil" "github.com/foomo/htpasswd" "k8s.io/client-go/kubernetes" "github.com/covexo/devspace/pkg/devspace/clients/helm" - "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/devspace/config/configutil" + "github.com/covexo/yamlq" + k8sv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // PullSecretName for the docker registry const PullSecretName = "devspace-pull-secret" +const registryPort = 5000 -// InitDockerRegistry deploys and starts a new docker registry if necessary -func InitDockerRegistry(kubectl *kubernetes.Clientset, helm *helm.HelmClientWrapper, privateConfig *v1.PrivateConfig, dsConfig *v1.DevSpaceConfig) (string, string, error) { - registryReleaseName := privateConfig.Registry.Release.Name - registryReleaseNamespace := privateConfig.Registry.Release.Namespace - registryConfig := dsConfig.Registry - registrySecrets, secretsExist := registryConfig["secrets"] - - if !secretsExist { - //TODO - } - _, secretIsMap := registrySecrets.(map[interface{}]interface{}) - - if !secretIsMap { - //TODO - } - _, err := helm.InstallChartByName(registryReleaseName, registryReleaseNamespace, "stable/docker-registry", "", ®istryConfig) +// InitRegistry deploys and starts a new docker registry if necessary +func InitRegistry(kubectl *kubernetes.Clientset, helm *helm.HelmClientWrapper) error { + config := configutil.GetConfig(false) + registryConfig := config.Services.Registry + registryUser := registryConfig.User + registryAuthEncoded := base64.StdEncoding.EncodeToString([]byte(*registryUser.Username + ":" + *registryUser.Password)) - if err != nil { - return "", "", fmt.Errorf("Unable to initialize docker registry: %s", err.Error()) - } + if registryConfig.External == nil { + registry := registryConfig.Internal + registryReleaseName := *registry.Release.Name + registryReleaseNamespace := *registry.Release.Namespace - htpasswdSecretName := registryReleaseName + "-docker-registry-secret" - htpasswdSecret, err := kubectl.Core().Secrets(registryReleaseNamespace).Get(htpasswdSecretName, metav1.GetOptions{}) + _, err := helm.InstallChartByName(registryReleaseName, registryReleaseNamespace, "stable/docker-registry", "", registry.Release.Values) - if err != nil { - return "", "", fmt.Errorf("Unable to retrieve secret for docker registry: %s", err.Error()) - } - - if htpasswdSecret == nil || htpasswdSecret.Data == nil { - htpasswdSecret = &k8sv1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: htpasswdSecretName, - }, - Data: map[string][]byte{}, + if err != nil { + return fmt.Errorf("Unable to initialize docker registry: %s", err.Error()) } - } - - oldHtpasswdData := htpasswdSecret.Data["htpasswd"] - newHtpasswdData := htpasswd.HashedPasswords{} - - if len(oldHtpasswdData) != 0 { - oldHtpasswdDataBytes := []byte(oldHtpasswdData) - newHtpasswdData, _ = htpasswd.ParseHtpasswd(oldHtpasswdDataBytes) - } - registryUser := privateConfig.Registry.User - err = newHtpasswdData.SetPassword(registryUser.Username, registryUser.Password, htpasswd.HashBCrypt) + htpasswdSecretName := registryReleaseName + "-docker-registry-secret" + htpasswdSecret, err := kubectl.Core().Secrets(registryReleaseNamespace).Get(htpasswdSecretName, metav1.GetOptions{}) - if err != nil { - return "", "", fmt.Errorf("Unable to set password in htpasswd: %s", err.Error()) - } - - newHtpasswdDataBytes := newHtpasswdData.Bytes() - - htpasswdSecret.Data["htpasswd"] = newHtpasswdDataBytes + if err != nil { + return fmt.Errorf("Unable to retrieve secret for docker registry: %s", err.Error()) + } - _, err = kubectl.Core().Secrets(registryReleaseNamespace).Get(htpasswdSecretName, metav1.GetOptions{}) + if htpasswdSecret == nil || htpasswdSecret.Data == nil { + htpasswdSecret = &k8sv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: htpasswdSecretName, + }, + Data: map[string][]byte{}, + } + } - if err != nil { - _, err = kubectl.Core().Secrets(registryReleaseNamespace).Create(htpasswdSecret) - } else { - _, err = kubectl.Core().Secrets(registryReleaseNamespace).Update(htpasswdSecret) - } + oldHtpasswdData := htpasswdSecret.Data["htpasswd"] + newHtpasswdData := htpasswd.HashedPasswords{} - if err != nil { - return "", "", fmt.Errorf("Unable to update htpasswd secret: %s", err.Error()) - } + if len(oldHtpasswdData) != 0 { + oldHtpasswdDataBytes := []byte(oldHtpasswdData) + newHtpasswdData, _ = htpasswd.ParseHtpasswd(oldHtpasswdDataBytes) + } + err = newHtpasswdData.SetPassword(*registryUser.Username, *registryUser.Password, htpasswd.HashBCrypt) - registryAuthEncoded := base64.StdEncoding.EncodeToString([]byte(privateConfig.Registry.User.Username + ":" + privateConfig.Registry.User.Password)) - registryServiceName := registryReleaseName + "-docker-registry" + if err != nil { + return fmt.Errorf("Unable to set password in htpasswd: %s", err.Error()) + } - var registryService *k8sv1.Service + newHtpasswdDataBytes := newHtpasswdData.Bytes() - maxServiceWaiting := 60 * time.Second - serviceWaitingInterval := 3 * time.Second + htpasswdSecret.Data["htpasswd"] = newHtpasswdDataBytes - for true { - registryService, err = kubectl.Core().Services(registryReleaseNamespace).Get(registryServiceName, metav1.GetOptions{}) + _, err = kubectl.Core().Secrets(registryReleaseNamespace).Get(htpasswdSecretName, metav1.GetOptions{}) if err != nil { - log.Panic(err) + _, err = kubectl.Core().Secrets(registryReleaseNamespace).Create(htpasswdSecret) + } else { + _, err = kubectl.Core().Secrets(registryReleaseNamespace).Update(htpasswdSecret) } - if len(registryService.Spec.ClusterIP) > 0 { - break + if err != nil { + return fmt.Errorf("Unable to update htpasswd secret: %s", err.Error()) } + registryServiceName := registryReleaseName + "-docker-registry" + maxServiceWaiting := 60 * time.Second + serviceWaitingInterval := 3 * time.Second - time.Sleep(serviceWaitingInterval) - maxServiceWaiting = maxServiceWaiting - serviceWaitingInterval + for true { + registryService, err := kubectl.Core().Services(registryReleaseNamespace).Get(registryServiceName, metav1.GetOptions{}) - if maxServiceWaiting <= 0 { - return "", "", errors.New("Timeout waiting for registry service to start") - } - } + if err != nil { + log.Panic(err) + } - registryPort := 5000 - registryIP := registryService.Spec.ClusterIP + ":" + strconv.Itoa(registryPort) - registryHostname := registryServiceName + "." + registryReleaseNamespace + ".svc.cluster.local:" + strconv.Itoa(registryPort) - latestImageTag, _ := randutil.GenerateRandomString(10) + if len(registryService.Spec.ClusterIP) > 0 { + registryConfig.Internal.Host = configutil.String(registryService.Spec.ClusterIP + ":" + strconv.Itoa(registryPort)) + break + } - latestImageHostname := registryHostname + "/" + privateConfig.Release.Name + ":" + latestImageTag - latestImageIP := registryIP + "/" + privateConfig.Release.Name + ":" + latestImageTag + time.Sleep(serviceWaitingInterval) + maxServiceWaiting = maxServiceWaiting - serviceWaitingInterval + + if maxServiceWaiting <= 0 { + return errors.New("Timeout waiting for registry service to start") + } + } + } + registryHostname := GetRegistryHostname() pullSecretDataValue := []byte(`{ "auths": { "` + registryHostname + `": { "auth": "` + registryAuthEncoded + `", "email": "noreply-devspace@covexo.com" - }, - - "` + registryIP + `": { - "auth": "` + registryAuthEncoded + `", - "email": "noreply-devspace@covexo.com" } } }`) @@ -149,18 +131,62 @@ func InitDockerRegistry(kubectl *kubernetes.Clientset, helm *helm.HelmClientWrap Data: pullSecretData, Type: k8sv1.SecretTypeDockerConfigJson, } + appRelease := config.DevSpace.Release + appNamespace := *appRelease.Namespace - _, err = kubectl.Core().Secrets(privateConfig.Release.Namespace).Get(PullSecretName, metav1.GetOptions{}) + _, err := kubectl.Core().Secrets(appNamespace).Get(PullSecretName, metav1.GetOptions{}) if err != nil { - _, err = kubectl.Core().Secrets(privateConfig.Release.Namespace).Create(registryPullSecret) + _, err = kubectl.Core().Secrets(appNamespace).Create(registryPullSecret) } else { - _, err = kubectl.Core().Secrets(privateConfig.Release.Namespace).Update(registryPullSecret) + _, err = kubectl.Core().Secrets(appNamespace).Update(registryPullSecret) } if err != nil { - return "", "", fmt.Errorf("Unable to update image pull secret: %s", err.Error()) + return fmt.Errorf("Unable to update image pull secret: %s", err.Error()) } - return latestImageHostname, latestImageIP, nil + return nil +} + +func GetImageUrl(includingLatestTag bool) string { + config := configutil.GetConfig(false) + image := *config.Image.Name + + image = GetRegistryHostname() + "/" + image + + if includingLatestTag { + image = image + ":" + *config.Image.Tag + } + return image +} + +func GetRegistryHostname() string { + config := configutil.GetConfig(false) + registryConfig := config.Services.Registry + + if registryConfig.External != nil { + return *registryConfig.External + } else { + registryHostname := "" + + registryValues := yamlq.NewQuery(registryConfig.Internal.Release.Values) + isIngressEnabled, _ := registryValues.Bool("ingress", "enabled") + + if isIngressEnabled { + firstIngressHostname, _ := registryValues.String("ingress", "hosts", "0") + + if len(firstIngressHostname) > 0 { + registryHostname = firstIngressHostname + } + } + + if len(registryHostname) == 0 { + registryConfig.Insecure = configutil.Bool(true) + registryHostname = *registryConfig.Internal.Host + } else { + registryConfig.Insecure = configutil.Bool(false) + } + return registryHostname + } } diff --git a/pkg/util/stdinutil/stdin.go b/pkg/util/stdinutil/stdin.go index 9b844c509b..0c9a8bbaba 100644 --- a/pkg/util/stdinutil/stdin.go +++ b/pkg/util/stdinutil/stdin.go @@ -28,7 +28,7 @@ var reader *bufio.Reader const changeQuestion = "Would you like to change it? (yes, no/ENTER))" //GetFromStdin asks the user a question and returns the answer -func GetFromStdin(params *GetFromStdinParams) string { +func GetFromStdin(params *GetFromStdinParams) *string { paramutil.SetDefaults(params, defaultParams) validationRegexp, _ := regexp.Compile(params.ValidationRegexPattern) @@ -79,11 +79,11 @@ func GetFromStdin(params *GetFromStdinParams) string { input = "" } } - return input + return &input } //AskChangeQuestion asks two questions. Do you want to change this value? If yes, what's the new value? -func AskChangeQuestion(params *GetFromStdinParams) string { +func AskChangeQuestion(params *GetFromStdinParams) *string { paramutil.SetDefaults(params, defaultParams) if reader == nil { @@ -102,8 +102,8 @@ func AskChangeQuestion(params *GetFromStdinParams) string { shouldChangeAnswer := GetFromStdin(&shouldValueChangeQuestion) - if shouldChangeAnswer == "no" { - return params.DefaultValue + if *shouldChangeAnswer == "no" { + return ¶ms.DefaultValue } newValueQuestion := GetFromStdinParams{ From 630488fbc13126abc05bef120fdf53e5b5171f18 Mon Sep 17 00:00:00 2001 From: gentele Date: Thu, 6 Sep 2018 12:58:40 +0200 Subject: [PATCH 2/7] merge --- Gopkg.toml | 4 ---- cmd/up.go | 6 +++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Gopkg.toml b/Gopkg.toml index 524b06cc07..b8733a264d 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -1,10 +1,6 @@ [[constraint]] name = "github.com/juju/errors" branch = "master" - -[[constraint]] - name = "github.com/docker/cli" - revision = "37eebe5cb6eff08bde94d17111caa8fc099d7a94" [[constraint]] name = "github.com/docker/docker" diff --git a/cmd/up.go b/cmd/up.go index b864b531cc..ac837aaa3a 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -355,7 +355,7 @@ func (cmd *UpCmd) startSync() { labels = append(labels, key+"="+*value) } - pod, err := kubectl.GetFirstRunningPod(cmd.kubectl, strings.Join(labels, ", "), config.DevSpace.Release.Namespace) + pod, err := kubectl.GetFirstRunningPod(cmd.kubectl, strings.Join(labels, ", "), *config.DevSpace.Release.Namespace) if err != nil { log.Panicf("Unable to list devspace pods: %s", err.Error()) @@ -392,7 +392,7 @@ func (cmd *UpCmd) startPortForwarding() { labels = append(labels, key+"="+*value) } - pod, err := kubectl.GetFirstRunningPod(cmd.kubectl, strings.Join(labels, ", "), config.Devspace.Release.Namespace) + pod, err := kubectl.GetFirstRunningPod(cmd.kubectl, strings.Join(labels, ", "), *config.DevSpace.Release.Namespace) if err != nil { log.Errorf("Unable to list devspace pods: %s", err.Error()) @@ -400,7 +400,7 @@ func (cmd *UpCmd) startPortForwarding() { ports := make([]string, len(portForwarding.PortMappings)) for index, value := range portForwarding.PortMappings { - ports[index] = strconv.Itoa(value.LocalPort) + ":" + strconv.Itoa(value.RemotePort) + ports[index] = strconv.Itoa(*value.LocalPort) + ":" + strconv.Itoa(*value.RemotePort) } readyChan := make(chan struct{}) From de94e4241de5cf3969d8720fdc9b75d94adfcea9 Mon Sep 17 00:00:00 2001 From: gentele Date: Thu, 6 Sep 2018 13:03:23 +0200 Subject: [PATCH 3/7] fix v1.Registry.Values --- pkg/devspace/config/configutil/merge.go | 1 + pkg/devspace/config/v1/schema.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/devspace/config/configutil/merge.go b/pkg/devspace/config/configutil/merge.go index 740f110f8f..0920765eb6 100644 --- a/pkg/devspace/config/configutil/merge.go +++ b/pkg/devspace/config/configutil/merge.go @@ -18,6 +18,7 @@ func merge(objectPointer interface{}, overwriteObjectPointer interface{}, object switch overwriteObjectKind { case reflect.Slice: + case reflect.Map: case reflect.Struct: objectValues := reflect.ValueOf(objectPointer).Elem() overwriteObjectValues := reflect.ValueOf(overwriteObjectPointer).Elem() diff --git a/pkg/devspace/config/v1/schema.go b/pkg/devspace/config/v1/schema.go index 88e2bbfec3..fa194b75d9 100644 --- a/pkg/devspace/config/v1/schema.go +++ b/pkg/devspace/config/v1/schema.go @@ -86,7 +86,7 @@ type PrivateConfig struct { type Release struct { Name *string `yaml:"name"` Namespace *string `yaml:"namespace"` - Values map[interface{}]interface{} `yaml:"internal,omitempty"` + Values map[interface{}]interface{} `yaml:"values,omitempty"` } //Cluster is a struct that contains data for a Kubernetes-Cluster From bcffd79051b5e375371424d9df1a1a73c434aa20 Mon Sep 17 00:00:00 2001 From: gentele Date: Thu, 6 Sep 2018 13:36:02 +0200 Subject: [PATCH 4/7] fix v1.TillerConfig.AppNamespaces --- cmd/init.go | 4 +--- cmd/up.go | 1 - pkg/devspace/clients/helm/client.go | 34 +++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 9d5225f987..05e75e2245 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -147,9 +147,7 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) { if createChart { cmd.determineAppConfig() - if cmd.config.DevSpace.Release == nil || cmd.config.DevSpace.Release.Name == nil { - cmd.config.DevSpace.Release.Name = configutil.String("my-app") - } + cmd.config.Image.Name = cmd.config.DevSpace.Release.Name } if cmd.flags.reconfigure || !configExists { diff --git a/cmd/up.go b/cmd/up.go index ac837aaa3a..995dee1733 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -88,7 +88,6 @@ Starts and connects your DevSpace: rootCmd.AddCommand(cobraCmd) cobraCmd.Flags().BoolVar(&cmd.flags.tiller, "tiller", cmd.flags.tiller, "Install/upgrade tiller") - cobraCmd.Flags().StringVarP(&cmd.flags.open, "open", "o", cmd.flags.open, "Install/upgrade tiller") cobraCmd.Flags().BoolVar(&cmd.flags.initRegistry, "init-registry", cmd.flags.initRegistry, "Install or upgrade Docker registry") cobraCmd.Flags().BoolVarP(&cmd.flags.build, "build", "b", cmd.flags.build, "Build image if Dockerfile has been modified") cobraCmd.Flags().StringVarP(&cmd.flags.shell, "shell", "s", "", "Shell command (default: bash, fallback: sh)") diff --git a/pkg/devspace/clients/helm/client.go b/pkg/devspace/clients/helm/client.go index f67014853d..b0b91b09c2 100644 --- a/pkg/devspace/clients/helm/client.go +++ b/pkg/devspace/clients/helm/client.go @@ -87,7 +87,7 @@ func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmCl return nil, err } - tillerErr := ensureTiller(kubectlClient, tillerConfig, upgradeTiller) + tillerErr := ensureTiller(kubectlClient, config, upgradeTiller) if tillerErr != nil { return nil, tillerErr @@ -183,7 +183,8 @@ func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmCl return wrapper, nil } -func ensureTiller(kubectlClient *kubernetes.Clientset, tillerConfig *v1.TillerConfig, upgrade bool) error { +func ensureTiller(kubectlClient *kubernetes.Clientset, config *v1.Config, upgrade bool) error { + tillerConfig := config.Services.Tiller tillerNamespace := *tillerConfig.Release.Namespace tillerSA := &k8sv1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ @@ -234,6 +235,15 @@ func ensureTiller(kubectlClient *kubernetes.Clientset, tillerConfig *v1.TillerCo helminstaller.Install(kubectlClient, tillerOptions) + appNamespaces := []*string{ + config.DevSpace.Release.Namespace, + } + + if config.Services.Registry.Internal.Release.Namespace != nil { + appNamespaces = append(appNamespaces, config.Services.Registry.Internal.Release.Namespace) + } + tillerConfig.AppNamespaces = appNamespaces + for _, appNamespace := range tillerConfig.AppNamespaces { err = ensureRoleBinding(kubectlClient, tillerConfig, tillerRoleName, *appNamespace, defaultPolicyRules) @@ -283,6 +293,26 @@ func ensureTiller(kubectlClient *kubernetes.Clientset, tillerConfig *v1.TillerCo return nil } +func addAppNamespaces(appNamespaces *[]*string, namespaces []*string) { + newAppNamespaces := *appNamespaces + + for _, ns := range namespaces { + isExisting := false + + for _, existingNS := range newAppNamespaces { + if ns == existingNS { + isExisting = true + break + } + } + + if !isExisting { + newAppNamespaces = append(newAppNamespaces, ns) + } + } + appNamespaces = &newAppNamespaces +} + // IsTillerDeployed determines if we could connect to a tiller server func IsTillerDeployed(kubectlClient *kubernetes.Clientset, tillerConfig *v1.TillerConfig) bool { tillerNamespace := *tillerConfig.Release.Namespace From c9c30fdced4accdf7ed625f7f8dfd4c7964a23a2 Mon Sep 17 00:00:00 2001 From: gentele Date: Thu, 6 Sep 2018 14:10:59 +0200 Subject: [PATCH 5/7] fix lint issues --- cmd/init.go | 6 ++--- cmd/status.go | 6 ++--- docs/docs/configuration/private.yaml.md | 2 +- pkg/devspace/clients/kubectl/client.go | 2 +- pkg/devspace/config/configutil/get.go | 5 +++- pkg/devspace/config/configutil/load.go | 4 +-- pkg/devspace/config/configutil/save.go | 15 ++++------- pkg/devspace/config/configutil/set.go | 2 ++ pkg/devspace/config/v1/schema.go | 34 ++++++++++++++---------- pkg/devspace/registry/registry.go | 35 +++++++++++++------------ 10 files changed, 59 insertions(+), 52 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 05e75e2245..ae152bc351 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -120,7 +120,7 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) { Name: configutil.String("devspace"), }, Cluster: &v1.Cluster{ - ApiServer: configutil.String("https://192.168.99.100:8443"), + APIServer: configutil.String("https://192.168.99.100:8443"), User: &v1.User{}, }, }) @@ -315,9 +315,9 @@ func (cmd *InitCmd) reconfigure() { clusterConfig.UseKubeConfig = configutil.Bool(useKubeConfig) if !useKubeConfig { - clusterConfig.ApiServer = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + clusterConfig.APIServer = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "What is your Kubernetes API Server URL? (e.g. https://127.0.0.1:8443)", - DefaultValue: *clusterConfig.ApiServer, + DefaultValue: *clusterConfig.APIServer, ValidationRegexPattern: "^https?://[a-z0-9-.]{0,99}:[0-9]{1,5}$", }) clusterConfig.CaCert = stdinutil.AskChangeQuestion(&stdinutil.GetFromStdinParams{ diff --git a/cmd/status.go b/cmd/status.go index cbf3cf4683..427cc5f218 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -197,7 +197,7 @@ func (cmd *StatusCmd) getRegistryStatus() ([]string, error) { } } - return nil, fmt.Errorf("Registry helm release %s not found", registry.Release.Name) + return nil, fmt.Errorf("Registry helm release %s not found", *registry.Release.Name) } func (cmd *StatusCmd) getTillerStatus() ([]string, error) { @@ -286,7 +286,7 @@ func (cmd *StatusCmd) getDevspaceStatus() ([]string, error) { } } - return nil, fmt.Errorf("Devspace helm release %s not found", config.DevSpace.Release.Name) + return nil, fmt.Errorf("Devspace helm release %s not found", *config.DevSpace.Release.Name) } func getRunningDevSpacePod(helm *helmClient.HelmClientWrapper, client *kubernetes.Clientset) (*k8sv1.Pod, error) { @@ -330,5 +330,5 @@ func getRunningDevSpacePod(helm *helmClient.HelmClientWrapper, client *kubernete } } - return nil, fmt.Errorf("Devspace helm release %s not found", config.DevSpace.Release.Name) + return nil, fmt.Errorf("Devspace helm release %s not found", *config.DevSpace.Release.Name) } diff --git a/docs/docs/configuration/private.yaml.md b/docs/docs/configuration/private.yaml.md index 117cfad0f4..16eaec500e 100644 --- a/docs/docs/configuration/private.yaml.md +++ b/docs/docs/configuration/private.yaml.md @@ -45,7 +45,7 @@ The `cluster` field specifies: - `useKubeConfig` (yes to use the credentials defined in $HOME/.kube/config) If `useKubeConfig: false` is used, the following fields need to be specified: -- `apiServer` (Kubernetes API-Server URL) +- `APIServer` (Kubernetes API-Server URL) - `caCert` (CaCert for the Kubernetes API-Server in PEM format) - `user` specifying the following: - `username` diff --git a/pkg/devspace/clients/kubectl/client.go b/pkg/devspace/clients/kubectl/client.go index 6cddaad41a..34c357cdf8 100644 --- a/pkg/devspace/clients/kubectl/client.go +++ b/pkg/devspace/clients/kubectl/client.go @@ -193,7 +193,7 @@ func GetClientConfig() (*rest.Config, error) { } return &rest.Config{ - Host: *config.Cluster.ApiServer, + Host: *config.Cluster.APIServer, Username: *config.Cluster.User.Username, TLSClientConfig: rest.TLSClientConfig{ CAData: []byte(*config.Cluster.CaCert), diff --git a/pkg/devspace/config/configutil/get.go b/pkg/devspace/config/configutil/get.go index 27115bf055..f3083cb5be 100644 --- a/pkg/devspace/config/configutil/get.go +++ b/pkg/devspace/config/configutil/get.go @@ -29,13 +29,14 @@ func init() { workdir, _ = os.Getwd() } -//ConfigExists chacks whether the yaml file for the config exists +//ConfigExists checks whether the yaml file for the config exists func ConfigExists() (bool, error) { _, configNotFound := os.Stat(workdir + configPath) return (configNotFound == nil), nil } +//GetConfig returns the config merged from .devspace/config.yaml and .devspace/overwrite.yaml func GetConfig(reload bool) *v1.Config { isLoaded := (config.Version != nil) @@ -57,6 +58,7 @@ func GetConfig(reload bool) *v1.Config { return config } +//GetOverwriteConfig returns the config retrieved from .devspace/overwrite.yaml func GetOverwriteConfig() *v1.Config { isLoaded := (overwriteConfig.Version != nil) @@ -69,6 +71,7 @@ func GetOverwriteConfig() *v1.Config { return overwriteConfig } +//GetConfigInstance returns the reference to the config (in most cases it is recommended to use GetConfig instaed) func GetConfigInstance() *v1.Config { return config } diff --git a/pkg/devspace/config/configutil/load.go b/pkg/devspace/config/configutil/load.go index 63f759ca6f..11ec89a664 100644 --- a/pkg/devspace/config/configutil/load.go +++ b/pkg/devspace/config/configutil/load.go @@ -24,9 +24,9 @@ func loadClusterConfig(config *v1.Cluster, overwriteExistingValues bool) { kubeconfig, kubeconfigErr := clientcmd.BuildConfigFromFlags("", filepath.Join(fsutil.GetHomeDir(), ".kube", "config")) if kubeconfigErr == nil { - if config.ApiServer == nil { + if config.APIServer == nil { if len(kubeconfig.Host) != 0 { - config.ApiServer = String(kubeconfig.Host) + config.APIServer = String(kubeconfig.Host) } } diff --git a/pkg/devspace/config/configutil/save.go b/pkg/devspace/config/configutil/save.go index a2a6bfb38c..ab022ade2c 100644 --- a/pkg/devspace/config/configutil/save.go +++ b/pkg/devspace/config/configutil/save.go @@ -115,9 +115,8 @@ func getConfigAndOverwriteMaps(config interface{}, configRaw interface{}, overwr return returnSlice, nil, nil } else if len(returnOverwriteSlice) > 0 { return nil, returnOverwriteSlice, nil - } else { - return nil, nil, nil } + return nil, nil, nil case reflect.Map: valueMap := objectValue.(map[interface{}]interface{}) returnMap := map[interface{}]interface{}{} @@ -153,9 +152,8 @@ func getConfigAndOverwriteMaps(config interface{}, configRaw interface{}, overwr return returnMap, nil, nil } else if len(returnOverwriteMap) > 0 { return nil, returnOverwriteMap, nil - } else { - return nil, nil, nil } + return nil, nil, nil case reflect.Struct: returnMap := map[interface{}]interface{}{} returnOverwriteMap := map[interface{}]interface{}{} @@ -194,9 +192,8 @@ func getConfigAndOverwriteMaps(config interface{}, configRaw interface{}, overwr return returnMap, nil, nil } else if len(returnOverwriteMap) > 0 { return nil, returnOverwriteMap, nil - } else { - return nil, nil, nil } + return nil, nil, nil default: saveOverwriteValue := !isOverwriteObjectNil saveValue := ((!isObjectNil && !saveOverwriteValue) || !isObjectRawNil) @@ -209,9 +206,8 @@ func getConfigAndOverwriteMaps(config interface{}, configRaw interface{}, overwr return nil, overwriteValue, nil } else if saveValue { return objectValue, nil, nil - } else { - return nil, nil, nil } + return nil, nil, nil } } @@ -241,9 +237,8 @@ func getPointerValue(object interface{}) (interface{}, bool) { zeroValue := reflect.Zero(objectValueRef.Type()).Interface() return zeroValue, true - } else { - return objectValueRef.Elem().Interface(), false } + return objectValueRef.Elem().Interface(), false } } return object, false diff --git a/pkg/devspace/config/configutil/set.go b/pkg/devspace/config/configutil/set.go index d116a2abb3..589ea45390 100644 --- a/pkg/devspace/config/configutil/set.go +++ b/pkg/devspace/config/configutil/set.go @@ -1,9 +1,11 @@ package configutil +//String returns a pointer to a string variable func String(val string) *string { return &val } +//Bool returns a pointer to a bool variable func Bool(val bool) *bool { return &val } diff --git a/pkg/devspace/config/v1/schema.go b/pkg/devspace/config/v1/schema.go index fa194b75d9..1bb34ea545 100644 --- a/pkg/devspace/config/v1/schema.go +++ b/pkg/devspace/config/v1/schema.go @@ -3,7 +3,7 @@ package v1 // Version is the current api version const Version string = "v1" -//DevSpaceConfig defines the config for a DevSpace +//Config defines the configuration type Config struct { Version *string `yaml:"version"` DevSpace *DevSpaceConfig `yaml:"devSpace,omitempty"` @@ -12,28 +12,33 @@ type Config struct { Services *ServiceConfig `yaml:"services,omitempty"` } -type ServiceConfig struct { - Tiller *TillerConfig `yaml:"tiller,omitempty"` - Registry *RegistryConfig `yaml:"registry,omitempty"` -} - +//ImageConfig defines the image specification type ImageConfig struct { Name *string `yaml:"name"` Tag *string `yaml:"tag"` BuildTime *string `yaml:"buildTime"` } -type TillerConfig struct { - Release *Release `yaml:"release"` - AppNamespaces []*string `yaml:"appNamespaces"` -} - +//DevSpaceConfig defines the devspace deployment type DevSpaceConfig struct { Release *Release `yaml:"release"` PortForwarding []*PortForwardingConfig `yaml:"portForwarding"` Sync []*SyncConfig `yaml:"sync"` } +//ServiceConfig defines additional services +type ServiceConfig struct { + Tiller *TillerConfig `yaml:"tiller,omitempty"` + Registry *RegistryConfig `yaml:"registry,omitempty"` +} + +//TillerConfig defines the tiller service +type TillerConfig struct { + Release *Release `yaml:"release"` + AppNamespaces []*string `yaml:"appNamespaces"` +} + +//RegistryConfig defines the registry service type RegistryConfig struct { External *string `yaml:"external,omitempty"` Internal *InternalRegistry `yaml:"internal,omitempty"` @@ -41,6 +46,7 @@ type RegistryConfig struct { Insecure *bool `yaml:"insecure,omitempty"` } +//InternalRegistry defines the deployment of an internal registry type InternalRegistry struct { Release *Release `yaml:"release,omitempty"` Host *string `yaml:"host,omitempty"` @@ -52,7 +58,7 @@ type RegistryUser struct { Password *string `yaml:"password"` } -//PortForwarding defines the ports for a port forwarding to a DevSpace +//PortForwardingConfig defines the ports for a port forwarding to a DevSpace type PortForwardingConfig struct { ResourceType *string `yaml:"resourceType"` LabelSelector map[string]*string `yaml:"labelSelector"` @@ -65,7 +71,7 @@ type PortMapping struct { RemotePort *int `yaml:"remotePort"` } -//SyncPath defines the paths for a SyncFolder +//SyncConfig defines the paths for a SyncFolder type SyncConfig struct { ResourceType *string `yaml:"resourceType"` LabelSelector map[string]*string `yaml:"labelSelector"` @@ -92,7 +98,7 @@ type Release struct { //Cluster is a struct that contains data for a Kubernetes-Cluster type Cluster struct { UseKubeConfig *bool `yaml:"useKubeConfig,omitempty"` - ApiServer *string `yaml:"apiServer,omitempty"` + APIServer *string `yaml:"apiServer,omitempty"` CaCert *string `yaml:"caCert,omitempty"` User *User `yaml:"user,omitempty"` } diff --git a/pkg/devspace/registry/registry.go b/pkg/devspace/registry/registry.go index b260b54823..bbeb1c033a 100644 --- a/pkg/devspace/registry/registry.go +++ b/pkg/devspace/registry/registry.go @@ -149,6 +149,7 @@ func InitRegistry(kubectl *kubernetes.Clientset, helm *helm.HelmClientWrapper) e return nil } +//GetImageUrl returns the image (optional with tag) func GetImageUrl(includingLatestTag bool) string { config := configutil.GetConfig(false) image := *config.Image.Name @@ -161,32 +162,32 @@ func GetImageUrl(includingLatestTag bool) string { return image } +//GetRegistryHostname returns the hostname of the registry including the port func GetRegistryHostname() string { config := configutil.GetConfig(false) registryConfig := config.Services.Registry if registryConfig.External != nil { return *registryConfig.External - } else { - registryHostname := "" - - registryValues := yamlq.NewQuery(registryConfig.Internal.Release.Values) - isIngressEnabled, _ := registryValues.Bool("ingress", "enabled") + } + registryHostname := "" - if isIngressEnabled { - firstIngressHostname, _ := registryValues.String("ingress", "hosts", "0") + registryValues := yamlq.NewQuery(registryConfig.Internal.Release.Values) + isIngressEnabled, _ := registryValues.Bool("ingress", "enabled") - if len(firstIngressHostname) > 0 { - registryHostname = firstIngressHostname - } - } + if isIngressEnabled { + firstIngressHostname, _ := registryValues.String("ingress", "hosts", "0") - if len(registryHostname) == 0 { - registryConfig.Insecure = configutil.Bool(true) - registryHostname = *registryConfig.Internal.Host - } else { - registryConfig.Insecure = configutil.Bool(false) + if len(firstIngressHostname) > 0 { + registryHostname = firstIngressHostname } - return registryHostname } + + if len(registryHostname) == 0 { + registryConfig.Insecure = configutil.Bool(true) + registryHostname = *registryConfig.Internal.Host + } else { + registryConfig.Insecure = configutil.Bool(false) + } + return registryHostname } From 742af6981a2c42836227dfbdb4a6211e2b9abe3f Mon Sep 17 00:00:00 2001 From: gentele Date: Thu, 6 Sep 2018 14:14:48 +0200 Subject: [PATCH 6/7] fix lint issues --- cmd/init.go | 6 +++--- cmd/up.go | 4 ++-- pkg/devspace/registry/registry.go | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index ae152bc351..9297ca655d 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -367,14 +367,14 @@ func (cmd *InitCmd) reconfigureRegistry() { if registryConfig.External != nil { defaultRegistryValue = *registryConfig.External } - registryUrl := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + registryURL := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Which registry do you want to push to? (URL or 'internal registry')", DefaultValue: defaultRegistryValue, ValidationRegexPattern: "^.*$", }) - if *registryUrl != internalRegistryKey { - registryConfig.External = registryUrl + if *registryURL != internalRegistryKey { + registryConfig.External = registryURL registryConfig.Internal = nil } else { registryConfig.External = nil diff --git a/cmd/up.go b/cmd/up.go index 995dee1733..6b95a6134c 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -216,7 +216,7 @@ func (cmd *UpCmd) buildImage() { if randErr != nil { log.Fatalf("Image building failed: %s", randErr.Error()) } - imageDestination := registry.GetImageUrl(false) + ":" + imageTag + imageDestination := registry.GetImageURL(false) + ":" + imageTag if cmd.flags.imageDestination != "" { imageDestination = cmd.flags.imageDestination @@ -258,7 +258,7 @@ func (cmd *UpCmd) deployChart() { values := map[interface{}]interface{}{} containerValues := map[interface{}]interface{}{} - containerValues["image"] = registry.GetImageUrl(true) + containerValues["image"] = registry.GetImageURL(true) if !cmd.flags.noSleep { containerValues["command"] = []string{"sleep"} diff --git a/pkg/devspace/registry/registry.go b/pkg/devspace/registry/registry.go index bbeb1c033a..e83c18c144 100644 --- a/pkg/devspace/registry/registry.go +++ b/pkg/devspace/registry/registry.go @@ -149,8 +149,8 @@ func InitRegistry(kubectl *kubernetes.Clientset, helm *helm.HelmClientWrapper) e return nil } -//GetImageUrl returns the image (optional with tag) -func GetImageUrl(includingLatestTag bool) string { +//GetImageURL returns the image (optional with tag) +func GetImageURL(includingLatestTag bool) string { config := configutil.GetConfig(false) image := *config.Image.Name From a0d611f5ed02f28f70392818e849d497030004d0 Mon Sep 17 00:00:00 2001 From: gentele Date: Thu, 6 Sep 2018 14:59:19 +0200 Subject: [PATCH 7/7] fix stdin tests --- pkg/util/stdinutil/stdin_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/util/stdinutil/stdin_test.go b/pkg/util/stdinutil/stdin_test.go index 31f9e9e705..efa86e0db5 100644 --- a/pkg/util/stdinutil/stdin_test.go +++ b/pkg/util/stdinutil/stdin_test.go @@ -21,7 +21,7 @@ func TestGetFromStdin_NoChangeQuestion_Default(t *testing.T) { } defer cleanUpMockedStdin() - answer := GetFromStdin(¶ms) + answer := *GetFromStdin(¶ms) if answer != params.DefaultValue { t.Error("Wrong Answer.\nExpected default answer: " + params.DefaultValue + "\nBut Got: " + answer) @@ -42,7 +42,7 @@ func TestGetFromStdin_NoChangeQuestion_NonDefault(t *testing.T) { } defer cleanUpMockedStdin() - answer := GetFromStdin(¶ms) + answer := *GetFromStdin(¶ms) if answer != "Yes" { t.Error("Wrong Answer.\nExpected: Yes\nBut Got: " + answer) @@ -64,7 +64,7 @@ func TestGetFromStdin_ChangeQuestion_DontChange(t *testing.T) { } defer cleanUpMockedStdin() - answer := AskChangeQuestion(¶ms) + answer := *AskChangeQuestion(¶ms) if answer != "World" { t.Error("Wrong Answer.\nExpected default: World\nBut Got: " + answer) @@ -86,7 +86,7 @@ func TestGetFromStdin_ChangeQuestion_DoChange(t *testing.T) { } defer cleanUpMockedStdin() - answer := AskChangeQuestion(¶ms) + answer := *AskChangeQuestion(¶ms) if answer != "Universe" { t.Error("Wrong Answer.\nExpected default: Universe\nBut Got: " + answer)