diff --git a/cmd/add.go b/cmd/add.go index cf578ac509..443b786f8d 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -14,10 +14,10 @@ import ( "github.com/covexo/devspace/pkg/util/tar" "github.com/covexo/devspace/pkg/util/yamlutil" - helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/devspace/config/v1" + helmClient "github.com/covexo/devspace/pkg/devspace/deploy/helm" + "github.com/covexo/devspace/pkg/devspace/kubectl" "github.com/covexo/devspace/pkg/util/log" "github.com/russross/blackfriday" "github.com/skratchdot/open-golang/open" @@ -309,7 +309,7 @@ func (cmd *AddCmd) showReadme(chartVersion *repo.ChartVersion) { // RunAddSync executes the add sync command logic func (cmd *AddCmd) RunAddSync(cobraCmd *cobra.Command, args []string) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() if cmd.syncFlags.Selector == "" { cmd.syncFlags.Selector = "release=" + *config.DevSpace.Release.Name @@ -345,7 +345,7 @@ func (cmd *AddCmd) RunAddSync(cobraCmd *cobra.Command, args []string) { } syncConfig := append(*config.DevSpace.Sync, &v1.SyncConfig{ - ResourceType: configutil.String(cmd.syncFlags.ResourceType), + ResourceType: nil, LabelSelector: &labelSelectorMap, ContainerPath: configutil.String(cmd.syncFlags.ContainerPath), LocalSubPath: configutil.String(cmd.syncFlags.LocalPath), @@ -362,7 +362,7 @@ 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) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() if cmd.portFlags.Selector == "" { cmd.portFlags.Selector = "release=" + *config.DevSpace.Release.Name @@ -390,7 +390,7 @@ func (cmd *AddCmd) RunAddPort(cobraCmd *cobra.Command, args []string) { } func (cmd *AddCmd) insertOrReplacePortMapping(labelSelectorMap map[string]*string, portMappings []*v1.PortMapping) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() // Check if we should add to existing port mapping for _, v := range *config.DevSpace.PortForwarding { @@ -411,7 +411,7 @@ func (cmd *AddCmd) insertOrReplacePortMapping(labelSelectorMap map[string]*strin } } portMap := append(*config.DevSpace.PortForwarding, &v1.PortForwardingConfig{ - ResourceType: configutil.String(cmd.portFlags.ResourceType), + ResourceType: nil, LabelSelector: &labelSelectorMap, PortMappings: &portMappings, }) diff --git a/cmd/down.go b/cmd/down.go index 498efe689f..b7e3aeb8f1 100644 --- a/cmd/down.go +++ b/cmd/down.go @@ -1,9 +1,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/configutil" + helmClient "github.com/covexo/devspace/pkg/devspace/deploy/helm" + "github.com/covexo/devspace/pkg/devspace/kubectl" "github.com/covexo/devspace/pkg/util/log" "github.com/spf13/cobra" @@ -43,8 +43,7 @@ your project, use: devspace reset // Run executes the down command logic func (cmd *DownCmd) Run(cobraCmd *cobra.Command, args []string) { log.StartFileLogging() - - config := configutil.GetConfig(false) + config := configutil.GetConfig() releaseName := *config.DevSpace.Release.Name kubectl, err := kubectl.NewClient() diff --git a/cmd/enter.go b/cmd/enter.go index e376d56822..2077166061 100644 --- a/cmd/enter.go +++ b/cmd/enter.go @@ -1,9 +1,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/configutil" + helmClient "github.com/covexo/devspace/pkg/devspace/deploy/helm" + "github.com/covexo/devspace/pkg/devspace/kubectl" "github.com/covexo/devspace/pkg/util/log" "github.com/spf13/cobra" k8sv1 "k8s.io/api/core/v1" @@ -14,7 +14,7 @@ import ( // EnterCmd is a struct that defines a command call for "enter" type EnterCmd struct { flags *EnterCmdFlags - helm *helmClient.HelmClientWrapper + helm *helmClient.ClientWrapper kubectl *kubernetes.Clientset pod *k8sv1.Pod } @@ -80,7 +80,7 @@ func (cmd *EnterCmd) Run(cobraCmd *cobra.Command, args []string) { func enterTerminal(client *kubernetes.Clientset, pod *k8sv1.Pod, containerNameOverride string, args []string) { var command []string - config := configutil.GetConfig(false) + config := configutil.GetConfig() if len(args) == 0 && (config.DevSpace.Terminal.Command == nil || len(*config.DevSpace.Terminal.Command) == 0) { command = []string{ diff --git a/cmd/init.go b/cmd/init.go index a67ec19dc8..4e76f403d4 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -3,25 +3,21 @@ package cmd import ( "io/ioutil" "os" - "os/exec" "path/filepath" - "regexp" - "strconv" "strings" "github.com/covexo/devspace/pkg/util/kubeconfig" "k8s.io/client-go/tools/clientcmd" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" "github.com/covexo/devspace/pkg/devspace/cloud" + "github.com/covexo/devspace/pkg/devspace/configure" "github.com/covexo/devspace/pkg/devspace/builder/docker" "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/devspace/config/v1" "github.com/covexo/devspace/pkg/util/stdinutil" @@ -30,13 +26,10 @@ import ( // InitCmd is a struct that defines a command call for "init" type InitCmd struct { - flags *InitCmdFlags - workdir string - chartGenerator *generator.ChartGenerator - config *v1.Config - overwriteConfig *v1.Config - defaultImage *v1.ImageConfig - defaultRegistry *v1.RegistryConfig + flags *InitCmdFlags + workdir string + chartGenerator *generator.ChartGenerator + defaultImage *v1.ImageConfig } // InitCmdFlags are the flags available for the init-command @@ -107,21 +100,26 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) { log.StartFileLogging() workdir, err := os.Getwd() - if err != nil { log.Fatalf("Unable to determine current workdir: %s", err.Error()) } cmd.workdir = workdir - configExists, _ := configutil.ConfigExists() - if configExists { - cmd.config = configutil.GetConfig(false) + var config *v1.Config + + configExists, _ := configutil.ConfigExists() + if configExists && cmd.flags.reconfigure == false { + config = configutil.GetConfig() } else { - cmd.config = configutil.GetConfigInstance() + // Delete config & overwrite config + os.Remove(filepath.Join(workdir, configutil.ConfigPath)) + os.Remove(filepath.Join(workdir, configutil.OverwriteConfigPath)) + + config = configutil.InitConfig() } - configutil.Merge(cmd.config, &v1.Config{ + configutil.Merge(config, &v1.Config{ Version: configutil.String("v1"), DevSpace: &v1.DevSpaceConfig{ Release: &v1.Release{ @@ -132,14 +130,6 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) { Images: &map[string]*v1.ImageConfig{ "default": &v1.ImageConfig{ Name: configutil.String("devspace"), - Build: &v1.BuildConfig{ - Engine: &v1.BuildEngine{ - Docker: &v1.DockerBuildEngine{ - Enabled: configutil.Bool(true), - }, - }, - }, - Registry: configutil.String("default"), }, }, Registries: &map[string]*v1.RegistryConfig{ @@ -151,13 +141,9 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) { }, }, }) - cmd.overwriteConfig = configutil.GetOverwriteConfig(false) - imageMap := *cmd.config.Images - cmd.defaultImage, _ = imageMap["default"] - - registryMap := *cmd.config.Registries - cmd.defaultRegistry, _ = registryMap["default"] + imageMap := *config.Images + cmd.defaultImage = imageMap["default"] cmd.initChartGenerator() @@ -186,20 +172,12 @@ func (cmd *InitCmd) Run(cobraCmd *cobra.Command, args []string) { if cmd.flags.reconfigure || !configExists { cmd.configureKubernetes() - cmd.configureDevSpace() - - cmd.defaultImage.Name = cmd.config.DevSpace.Release.Name - - cmd.configureTiller() cmd.configureRegistry() err := configutil.SaveConfig() - if err != nil { log.With(err).Fatalf("Config error: %s", err.Error()) } - - _ = configutil.GetConfig(true) } } @@ -221,92 +199,19 @@ func (cmd *InitCmd) initChartGenerator() { } func (cmd *InitCmd) configureDevSpace() { - _, chartDirNotFound := os.Stat(cmd.workdir + "/chart") - if chartDirNotFound == nil { - /*TODO - existingChartYaml := map[interface{}]interface{}{} - existingChartValuesYaml := map[interface{}]interface{}{} + config := configutil.GetConfig() - yamlutil.ReadYamlFromFile(cmd.workdir+"/chart/Chart.yaml", existingChartYaml) - yamlutil.ReadYamlFromFile(cmd.workdir+"/chart/values.yaml", existingChartValuesYaml) - - cmd.config.Release.Name = existingChartYaml["name"].(string) - - applicationValues, applicationValuesCorrect := existingChartValuesYaml["container"].(map[interface{}]interface{}) - externalValues, externalValuesCorrect := existingChartValuesYaml["external"].(map[interface{}]interface{}) - - if externalValuesCorrect { - value, isCorrect := externalValues["domain"].(string) - - if isCorrect { - cmd.appConfig.External.Domain = value - } - }*/ - } - - cmd.config.DevSpace.Release.Name = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "What is the name of your application?", - DefaultValue: *cmd.config.DevSpace.Release.Name, + config.DevSpace.Release.Namespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + Question: "Which Kubernetes namespace should your application run in?", + DefaultValue: *config.DevSpace.Release.Namespace, ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name, }) - - ports := strings.Split(*stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "Which port(s) does your application listen on? (separated by spaces)", - DefaultValue: "", - ValidationRegexPattern: "^([1-9][0-9]{0,4})?(\\s[1-9][0-9]{0,4})*?$", - }), " ") - - for _, port := range ports { - portInt, _ := strconv.Atoi(port) - - if portInt > 0 { - cmd.addPortForwarding(portInt) - } - } - cmd.addDefaultSyncConfig() - - if cmd.config.DevSpace.Release.Namespace == nil || len(*cmd.config.DevSpace.Release.Namespace) == 0 { - cmd.config.DevSpace.Release.Namespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "Which Kubernetes namespace should your application run in?", - DefaultValue: *cmd.config.DevSpace.Release.Namespace, - ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name, - }) - } - - /* TODO - cmd.appConfig.External.Domain = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "Which domain do you want to run your application on?", - DefaultValue: cmd.appConfig.External.Domain, - ValidationRegexPattern: "^([a-z0-9]([a-z0-9-]{0,120}[a-z0-9])?\\.)+[a-z0-9]{2,}$", - })*/ -} - -func (cmd *InitCmd) addPortForwarding(port int) { - for _, portForwarding := range *cmd.config.DevSpace.PortForwarding { - for _, portMapping := range *portForwarding.PortMappings { - if *portMapping.RemotePort == port { - return - } - } - } - - portForwarding := append(*cmd.config.DevSpace.PortForwarding, &v1.PortForwardingConfig{ - PortMappings: &[]*v1.PortMapping{ - { - LocalPort: &port, - RemotePort: &port, - }, - }, - ResourceType: configutil.String("pod"), - LabelSelector: &map[string]*string{ - "release": cmd.config.DevSpace.Release.Name, - }, - }) - cmd.config.DevSpace.PortForwarding = &portForwarding } func (cmd *InitCmd) addDefaultSyncConfig() { - for _, syncPath := range *cmd.config.DevSpace.Sync { + config := configutil.GetConfig() + + for _, syncPath := range *config.DevSpace.Sync { if *syncPath.LocalSubPath == "./" || *syncPath.ContainerPath == "/app" { return } @@ -325,34 +230,21 @@ func (cmd *InitCmd) addDefaultSyncConfig() { } } - syncConfig := append(*cmd.config.DevSpace.Sync, &v1.SyncConfig{ + syncConfig := append(*config.DevSpace.Sync, &v1.SyncConfig{ ContainerPath: configutil.String("/app"), LocalSubPath: configutil.String("./"), - ResourceType: configutil.String("pod"), + ResourceType: nil, LabelSelector: &map[string]*string{ - "release": cmd.config.DevSpace.Release.Name, + "release": config.DevSpace.Release.Name, }, UploadExcludePaths: &uploadExcludePaths, }) - cmd.config.DevSpace.Sync = &syncConfig -} - -func (cmd *InitCmd) configureTiller() { - tillerConfig := cmd.config.Services.Tiller - tillerRelease := tillerConfig.Release - if tillerRelease.Namespace == nil || len(*tillerRelease.Namespace) == 0 { - tillerRelease.Namespace = cmd.config.DevSpace.Release.Namespace - - tillerRelease.Namespace = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "Which Kubernetes namespace should your tiller server run in?", - DefaultValue: *tillerRelease.Namespace, - ValidationRegexPattern: v1.Kubernetes.RegexPatterns.Name, - }) - } + config.DevSpace.Sync = &syncConfig } func (cmd *InitCmd) useCloudProvider() bool { + config := configutil.GetConfig() providerConfig, err := cloud.ParseCloudConfig() if err != nil { log.Fatalf("Error loading cloud config: %v", err) @@ -388,11 +280,10 @@ func (cmd *InitCmd) useCloudProvider() bool { ValidationRegexPattern: "^(yes)|(no)$", }) == "yes" - cmd.config.Cluster.CloudProvider = &cloudProviderSelected - cmd.config.Cluster.UseKubeConfig = &addToContext + config.Cluster.CloudProvider = &cloudProviderSelected log.StartWait("Logging into cloud provider " + providerConfig[cloudProviderSelected].Host + cloud.LoginEndpoint + "...") - err := cloud.Update(providerConfig, cmd.config, true) + err := cloud.Update(providerConfig, config, addToContext, true) log.StopWait() if err != nil { log.Fatalf("Couldn't authenticate to devspace cloud: %v", err) @@ -414,11 +305,10 @@ func (cmd *InitCmd) useCloudProvider() bool { ValidationRegexPattern: "^(yes)|(no)$", }) == "yes" - cmd.config.Cluster.CloudProvider = configutil.String(cloud.DevSpaceCloudProviderName) - cmd.config.Cluster.UseKubeConfig = &addToContext + config.Cluster.CloudProvider = configutil.String(cloud.DevSpaceCloudProviderName) log.StartWait("Logging into cloud provider " + providerConfig[cloud.DevSpaceCloudProviderName].Host + cloud.LoginEndpoint + "...") - err := cloud.Update(providerConfig, cmd.config, true) + err := cloud.Update(providerConfig, config, addToContext, true) log.StopWait() if err != nil { log.Fatalf("Couldn't authenticate to devspace cloud: %v", err) @@ -432,102 +322,111 @@ func (cmd *InitCmd) useCloudProvider() bool { } func (cmd *InitCmd) configureKubernetes() { - clusterConfig := cmd.config.Cluster + config := configutil.GetConfig() + clusterConfig := config.Cluster useKubeConfig := false // Check if devspace cloud should be used - if cmd.useCloudProvider() { - return - } + if cmd.useCloudProvider() == false { + _, err := os.Stat(clientcmd.RecommendedHomeFile) + if err == 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)$", + }) - _, err := os.Stat(clientcmd.RecommendedHomeFile) - if err == 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)$", - }) + useKubeConfig = (*skipAnswer == "yes") + } - useKubeConfig = (*skipAnswer == "yes") - } + if !useKubeConfig { + if clusterConfig.APIServer == nil { + clusterConfig.APIServer = configutil.String("https://192.168.99.100:8443") + } - clusterConfig.UseKubeConfig = configutil.Bool(useKubeConfig) - if !useKubeConfig { - if clusterConfig.APIServer == nil { - clusterConfig.APIServer = configutil.String("https://192.168.99.100:8443") - } - 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, - ValidationRegexPattern: "^https?://[a-z0-9-.]{0,99}:[0-9]{1,5}$", - }) + 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, + ValidationRegexPattern: "^https?://[a-z0-9-.]{0,99}:[0-9]{1,5}$", + }) - if clusterConfig.CaCert == nil { - clusterConfig.CaCert = configutil.String("") - } - clusterConfig.CaCert = stdinutil.AskChangeQuestion(&stdinutil.GetFromStdinParams{ - Question: "What is the CA Certificate of your API Server? (PEM)", - DefaultValue: *clusterConfig.CaCert, - InputTerminationString: "-----END CERTIFICATE-----", - }) + if clusterConfig.CaCert == nil { + clusterConfig.CaCert = configutil.String("") + } + clusterConfig.CaCert = stdinutil.AskChangeQuestion(&stdinutil.GetFromStdinParams{ + Question: "What is the CA Certificate of your API Server? (PEM)", + DefaultValue: *clusterConfig.CaCert, + InputTerminationString: "-----END CERTIFICATE-----", + }) + + if clusterConfig.User == nil { + clusterConfig.User = &v1.ClusterUser{ + ClientCert: configutil.String(""), + ClientKey: configutil.String(""), + } + } else { + if clusterConfig.User.ClientCert == nil { + clusterConfig.User.ClientCert = configutil.String("") + } - if clusterConfig.User == nil { - clusterConfig.User = &v1.ClusterUser{ - ClientCert: configutil.String(""), - ClientKey: configutil.String(""), + if clusterConfig.User.ClientKey == nil { + clusterConfig.User.ClientKey = configutil.String("") + } } + + clusterConfig.User.ClientCert = stdinutil.AskChangeQuestion(&stdinutil.GetFromStdinParams{ + Question: "What is your Kubernetes client certificate? (PEM)", + 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, + InputTerminationString: "-----END RSA PRIVATE KEY-----", + }) } else { - if clusterConfig.User.ClientCert == nil { - clusterConfig.User.ClientCert = configutil.String("") + currentContext, err := kubeconfig.GetCurrentContext() + if err != nil { + log.Fatalf("Couldn't determine current kubernetes context: %v", err) } - if clusterConfig.User.ClientKey == nil { - clusterConfig.User.ClientKey = configutil.String("") - } - } - clusterConfig.User.ClientCert = stdinutil.AskChangeQuestion(&stdinutil.GetFromStdinParams{ - Question: "What is your Kubernetes client certificate? (PEM)", - 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, - InputTerminationString: "-----END RSA PRIVATE KEY-----", - }) - } else { - currentContext, err := kubeconfig.GetCurrentContext() - if err != nil { - log.Fatalf("Couldn't determine current kubernetes context: %v", err) + clusterConfig.KubeContext = ¤tContext } - clusterConfig.KubeContext = ¤tContext + cmd.configureDevSpace() } + + cmd.addDefaultSyncConfig() } func (cmd *InitCmd) configureRegistry() { dockerUsername := "" createInternalRegistryDefaultAnswer := "yes" - var imageBuilder *docker.Builder - var dockerBuilderErr error - - imageBuilder, dockerBuilderErr = docker.NewBuilder("", "", "", false) - - if dockerBuilderErr == nil { + imageBuilder, err := docker.NewBuilder("", "", "", false) + if err != nil { log.StartWait("Checking Docker credentials") - dockerAuthConfig, dockerAuthErr := imageBuilder.Authenticate("", "", true) + dockerAuthConfig, err := imageBuilder.Authenticate("", "", true) log.StopWait() - if dockerAuthErr == nil { + if err == nil { dockerUsername = dockerAuthConfig.Username - if dockerUsername != "" { createInternalRegistryDefaultAnswer = "no" } } + } else { + // Set default build engine to kaniko, if no docker is installed + cmd.defaultImage.Build = &v1.BuildConfig{ + Engine: &v1.BuildEngine{ + Kaniko: &v1.KanikoBuildEngine{ + Enabled: configutil.Bool(true), + }, + }, + } } - internalRegistryConfig := cmd.config.Services.InternalRegistry + createInternalRegistry := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ Question: "Should we create a private registry within your Kubernetes cluster for you? (yes | no)", DefaultValue: createInternalRegistryDefaultAnswer, @@ -535,210 +434,15 @@ func (cmd *InitCmd) configureRegistry() { }) if *createInternalRegistry == "no" { - registryURL := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "Which registry do you want to push to? ('hub.docker.com' or URL)", - DefaultValue: "hub.docker.com", - ValidationRegexPattern: "^.*$", - }) - - cmd.defaultRegistry.URL = registryURL - internalRegistryConfig = nil - loginWarningServer := "" - - if dockerUsername == "" { - if *registryURL != "hub.docker.com" { - loginWarningServer = " " + *registryURL - imageBuilder, dockerBuilderErr = docker.NewBuilder(*registryURL, "", "", false) - } - - if dockerBuilderErr == nil { - log.StartWait("Checking Docker credentials") - dockerAuthConfig, dockerAuthErr := imageBuilder.Authenticate("", "", true) - log.StopWait() - - if dockerAuthErr == nil { - dockerUsername = dockerAuthConfig.Username - } - } - } - googleRegistryRegex := regexp.MustCompile("^(.+\\.)?gcr.io$") - isGoogleRegistry := googleRegistryRegex.Match([]byte(*registryURL)) - isDockerHub := *registryURL == "hub.docker.com" - - if dockerUsername == "" { - if cmd.defaultImage.Build.Engine.Docker != nil { - log.Fatal("Make sure you login to the registry with: docker login" + loginWarningServer) - } else { - registryMapOverwrite := *cmd.overwriteConfig.Registries - defaultRegistryOverwrite, defaultRegistryOverwriteDefined := registryMapOverwrite["default"] - - if !defaultRegistryOverwriteDefined { - defaultRegistryOverwrite = &v1.RegistryConfig{} - registryMapOverwrite["default"] = defaultRegistryOverwrite - } - - if defaultRegistryOverwrite.Auth == nil { - defaultRegistryOverwrite.Auth = &v1.RegistryAuth{} - } - - if defaultRegistryOverwrite.Auth.Username == nil { - defaultRegistryOverwrite.Auth.Username = configutil.String("") - } - - defaultRegistryOverwrite.Auth.Username = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "Which username do you want to use to push images to " + *registryURL + "?", - DefaultValue: *defaultRegistryOverwrite.Auth.Username, - ValidationRegexPattern: "^[a-zA-Z0-9]{4,30}$", - }) - dockerUsername = *defaultRegistryOverwrite.Auth.Username - - if defaultRegistryOverwrite.Auth.Password == nil { - defaultRegistryOverwrite.Auth.Password = configutil.String("") - } - - defaultRegistryOverwrite.Auth.Username = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "Which password do you want to use to push images to " + *registryURL + "?", - DefaultValue: *defaultRegistryOverwrite.Auth.Password, - ValidationRegexPattern: "^.*$", - }) - } - } - defaultImageName := *cmd.defaultImage.Name - defaultImageNameParts := strings.Split(defaultImageName, "/") - - if isDockerHub { - if len(defaultImageNameParts) < 2 { - defaultImageName = dockerUsername + "/" + strings.TrimPrefix(defaultImageName, dockerUsername) - } - - cmd.defaultImage.Name = stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "Which image name do you want to use on Docker Hub?", - DefaultValue: defaultImageName, - ValidationRegexPattern: "^[a-zA-Z0-9/]{4,30}$", - }) - } - - if isGoogleRegistry { - if len(defaultImageNameParts) < 2 { - project, err := exec.Command("gcloud", "config", "get-value", "project").Output() - gcloudProject := "" - - if err == nil { - gcloudProject = strings.TrimSpace(string(project)) - } - - gcloudProjectName := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "What Google Cloud Project should be used?", - DefaultValue: gcloudProject, - ValidationRegexPattern: "^.*$", - }) - - cmd.defaultImage.Name = configutil.String(*gcloudProjectName + "/" + strings.TrimPrefix(defaultImageName, *gcloudProjectName)) - } + err := configure.ImageName(dockerUsername) + if err != nil { + log.Fatal(err) } } else { - imageMap := *cmd.config.Images - defaultImageConf, defaultImageExists := imageMap["default"] - - if defaultImageExists { - defaultImageConf.Registry = configutil.String("internal") - } - - if internalRegistryConfig == nil { - internalRegistryConfig = &v1.InternalRegistry{ - Release: &v1.Release{}, - } - cmd.config.Services.InternalRegistry = internalRegistryConfig - } - - if internalRegistryConfig.Release.Name == nil { - internalRegistryConfig.Release.Name = configutil.String("devspace-registry") - } - - if internalRegistryConfig.Release.Namespace == nil { - internalRegistryConfig.Release.Namespace = cmd.config.DevSpace.Release.Namespace - } - overwriteRegistryMap := *cmd.overwriteConfig.Registries - - overwriteRegistryConfig, overwriteRegistryConfigFound := overwriteRegistryMap["internal"] - - if !overwriteRegistryConfigFound { - overwriteRegistryConfig = &v1.RegistryConfig{ - Auth: &v1.RegistryAuth{}, - } - overwriteRegistryMap["internal"] = overwriteRegistryConfig - } - registryAuth := overwriteRegistryConfig.Auth - - if registryAuth.Username == nil { - randomUserSuffix, err := randutil.GenerateRandomString(5) - - if err != nil { - log.Fatalf("Error creating random username: %s", err.Error()) - } - registryAuth.Username = configutil.String("user-" + randomUserSuffix) - } - - if registryAuth.Password == nil { - randomPassword, err := randutil.GenerateRandomString(12) - - if err != nil { - log.Fatalf("Error creating random password: %s", err.Error()) - } - registryAuth.Password = &randomPassword - } - var registryReleaseValues map[interface{}]interface{} - - if internalRegistryConfig.Release.Values != nil { - registryReleaseValues = *internalRegistryConfig.Release.Values - } else { - registryReleaseValues = map[interface{}]interface{}{} - - registryDomain := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ - Question: "Which domain should your container registry be using? (optional, requires an ingress controller)", - ValidationRegexPattern: "^(([a-z0-9]([a-z0-9-]{0,120}[a-z0-9])?\\.)+[a-z0-9]{2,})?$", - }) - - if *registryDomain != "" { - registryReleaseValues = map[interface{}]interface{}{ - "Ingress": map[string]interface{}{ - "Enabled": true, - "Hosts": []string{ - *registryDomain, - }, - "Annotations": map[string]string{ - "Kubernetes.io/tls-acme": "true", - }, - "Tls": []map[string]interface{}{ - map[string]interface{}{ - "SecretName": "tls-devspace-registry", - "Hosts": []string{ - *registryDomain, - }, - }, - }, - }, - } - } else if kubectl.IsMinikube() == false { - log.Warn("Your Kubernetes cluster will not be able to pull images from a registry without a registry domain!\n") - } - } - 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 !registryHasSecretHtpasswd { - secretMap["htpasswd"] = "" - } + err := configure.InternalRegistry() + if err != nil { + log.Fatal(err) } - internalRegistryConfig.Release.Values = ®istryReleaseValues } } @@ -779,33 +483,7 @@ func (cmd *InitCmd) determineLanguage() { func (cmd *InitCmd) createChart() { err := cmd.chartGenerator.CreateChart() - if err != nil { log.Fatalf("Error while creating Helm chart and Dockerfile: %s", err.Error()) } - - /*TODO - createdChartYaml := map[interface{}]interface{}{} - createdChartValuesYaml := map[interface{}]interface{}{} - - yamlutil.ReadYamlFromFile(cmd.chartGenerator.Path+"/chart/Chart.yaml", &createdChartYaml) - yamlutil.ReadYamlFromFile(cmd.chartGenerator.Path+"/chart/values.yaml", &createdChartValuesYaml) - - containerValues, chartHasContainerValues := createdChartValuesYaml["container"].(map[interface{}]interface{}) - - if !chartHasContainerValues && containerValues != nil { - containerValues["port"] = cmd.appConfig.Container.Ports - - createdChartValuesYaml["container"] = containerValues - } - - externalValues, chartHasExternalValues := createdChartValuesYaml["external"].(map[interface{}]interface{}) - - if !chartHasExternalValues && externalValues != nil { - 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/install.go b/cmd/install.go index d77c7970e2..56c8893345 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -7,7 +7,7 @@ import ( "github.com/covexo/devspace/pkg/util/envutil" "github.com/covexo/devspace/pkg/util/log" - helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm" + helmClient "github.com/covexo/devspace/pkg/devspace/deploy/helm" "github.com/covexo/devspace/pkg/devspace/config/v1" @@ -18,7 +18,7 @@ import ( // InstallCmd is a struct that defines a command call for "install" type InstallCmd struct { flags *InstallCmdFlags - helm *helmClient.HelmClientWrapper + helm *helmClient.ClientWrapper kubectl *kubernetes.Clientset dsConfig *v1.DevSpaceConfig workdir string diff --git a/cmd/list.go b/cmd/list.go index 8c108ec364..dac43bd7df 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -136,7 +136,7 @@ func (cmd *ListCmd) RunListPackage(cobraCmd *cobra.Command, args []string) { // RunListSync runs the list sync command logic func (cmd *ListCmd) RunListSync(cobraCmd *cobra.Command, args []string) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() if len(*config.DevSpace.Sync) == 0 { log.Write("No sync paths are configured. Run `devspace add sync` to add new sync path\n") @@ -176,8 +176,14 @@ func (cmd *ListCmd) RunListSync(cobraCmd *cobra.Command, args []string) { excludedPaths += v } } + + resourceType := "pod" + if value.ResourceType != nil { + resourceType = *value.ResourceType + } + syncPaths = append(syncPaths, []string{ - *value.ResourceType, + resourceType, selector, *value.LocalSubPath, *value.ContainerPath, @@ -190,7 +196,7 @@ 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) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() 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") @@ -208,7 +214,6 @@ func (cmd *ListCmd) RunListPort(cobraCmd *cobra.Command, args []string) { // Transform values into string arrays for _, value := range *config.DevSpace.PortForwarding { selector := "" - for k, v := range *value.LabelSelector { if len(selector) > 0 { selector += ", " @@ -218,7 +223,6 @@ func (cmd *ListCmd) RunListPort(cobraCmd *cobra.Command, args []string) { } portMappings := "" - for _, v := range *value.PortMappings { if len(portMappings) > 0 { portMappings += ", " @@ -227,8 +231,13 @@ func (cmd *ListCmd) RunListPort(cobraCmd *cobra.Command, args []string) { portMappings += strconv.Itoa(*v.LocalPort) + ":" + strconv.Itoa(*v.RemotePort) } + resourceType := "pod" + if value.ResourceType != nil { + resourceType = *value.ResourceType + } + portForwards = append(portForwards, []string{ - *value.ResourceType, + resourceType, selector, portMappings, }) diff --git a/cmd/remove.go b/cmd/remove.go index 6f59043163..222f09059f 100644 --- a/cmd/remove.go +++ b/cmd/remove.go @@ -6,10 +6,10 @@ import ( "strconv" "strings" - helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/devspace/config/v1" + helmClient "github.com/covexo/devspace/pkg/devspace/deploy/helm" + "github.com/covexo/devspace/pkg/devspace/kubectl" "github.com/covexo/devspace/pkg/util/log" "github.com/covexo/devspace/pkg/util/yamlutil" "github.com/spf13/cobra" @@ -223,7 +223,7 @@ func (cmd *RemoveCmd) rebuildDependencies(newYamlContents map[interface{}]interf // RunRemoveSync executes the remove sync command logic func (cmd *RemoveCmd) RunRemoveSync(cobraCmd *cobra.Command, args []string) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() labelSelectorMap, err := parseSelectors(cmd.syncFlags.Selector) if err != nil { @@ -261,7 +261,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) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() labelSelectorMap, err := parseSelectors(cmd.portFlags.Selector) diff --git a/cmd/reset.go b/cmd/reset.go index 3ef60762b3..45322bfc9f 100644 --- a/cmd/reset.go +++ b/cmd/reset.go @@ -4,8 +4,8 @@ import ( "os" "path" - helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" + helmClient "github.com/covexo/devspace/pkg/devspace/deploy/helm" + "github.com/covexo/devspace/pkg/devspace/kubectl" "github.com/covexo/devspace/pkg/util/log" "github.com/covexo/devspace/pkg/util/stdinutil" @@ -19,7 +19,7 @@ import ( // ResetCmd holds the needed command information type ResetCmd struct { flags *ResetCmdFlags - helm *helmClient.HelmClientWrapper + helm *helmClient.ClientWrapper kubectl *kubernetes.Clientset workdir string } @@ -193,7 +193,7 @@ func (cmd *ResetCmd) Run(cobraCmd *cobra.Command, args []string) { } func (cmd *ResetCmd) determineResetExtent() { - config := configutil.GetConfig(false) + config := configutil.GetConfig() cmd.flags.deleteRelease = true @@ -256,12 +256,12 @@ func (cmd *ResetCmd) shouldContinue() bool { func (cmd *ResetCmd) deleteRelease() error { var err error - config := configutil.GetConfig(false) + config := configutil.GetConfig() releaseName := *config.DevSpace.Release.Name if cmd.kubectl == nil || cmd.helm == nil { - isDeployed := helmClient.IsTillerDeployed(cmd.kubectl, config.Services.Tiller) + isDeployed := helmClient.IsTillerDeployed(cmd.kubectl) if isDeployed == false { return nil @@ -281,12 +281,12 @@ func (cmd *ResetCmd) deleteRelease() error { func (cmd *ResetCmd) deleteRegistry() error { var err error - config := configutil.GetConfig(false) + config := configutil.GetConfig() registryReleaseName := *config.Services.InternalRegistry.Release.Name if cmd.kubectl == nil || cmd.helm == nil { - isDeployed := helmClient.IsTillerDeployed(cmd.kubectl, config.Services.Tiller) + isDeployed := helmClient.IsTillerDeployed(cmd.kubectl) if isDeployed == false { return nil diff --git a/cmd/status.go b/cmd/status.go index 0f4112b0e0..a3df03520d 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -4,9 +4,9 @@ import ( "errors" "fmt" - helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" "github.com/covexo/devspace/pkg/devspace/config/configutil" + helmClient "github.com/covexo/devspace/pkg/devspace/deploy/helm" + "github.com/covexo/devspace/pkg/devspace/kubectl" "github.com/covexo/devspace/pkg/util/log" "github.com/daviddengcn/go-colortext" "github.com/spf13/cobra" @@ -18,7 +18,7 @@ import ( // StatusCmd holds the information needed for the status command type StatusCmd struct { flags *StatusCmdFlags - helm *helmClient.HelmClientWrapper + helm *helmClient.ClientWrapper kubectl *kubernetes.Clientset workdir string } @@ -150,7 +150,7 @@ func (cmd *StatusCmd) RunStatus(cobraCmd *cobra.Command, args []string) { } func (cmd *StatusCmd) getRegistryStatus() ([]string, error) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() registry := config.Services.InternalRegistry if registry == nil { return nil, nil @@ -203,8 +203,7 @@ func (cmd *StatusCmd) getRegistryStatus() ([]string, error) { } func (cmd *StatusCmd) getTillerStatus() ([]string, error) { - config := configutil.GetConfig(false) - tillerPod, err := kubectl.GetPodsFromDeployment(cmd.kubectl, helmClient.TillerDeploymentName, *config.Services.Tiller.Release.Namespace) + tillerPod, err := kubectl.GetPodsFromDeployment(cmd.kubectl, helmClient.TillerDeploymentName, helmClient.GetTillerNamespace()) if err != nil { return nil, err @@ -231,7 +230,7 @@ func (cmd *StatusCmd) getTillerStatus() ([]string, error) { } func (cmd *StatusCmd) getDevspaceStatus() ([]string, error) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() releases, err := cmd.helm.Client.ListReleases() if err != nil { @@ -291,8 +290,8 @@ func (cmd *StatusCmd) getDevspaceStatus() ([]string, error) { 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) { - config := configutil.GetConfig(false) +func getRunningDevSpacePod(helm *helmClient.ClientWrapper, client *kubernetes.Clientset) (*k8sv1.Pod, error) { + config := configutil.GetConfig() releases, err := helm.Client.ListReleases() if err != nil { diff --git a/cmd/status_sync.go b/cmd/status_sync.go index 9f3b32141c..1f278bd1ec 100644 --- a/cmd/status_sync.go +++ b/cmd/status_sync.go @@ -94,6 +94,10 @@ func (cmd *StatusCmd) RunStatusSync(cobraCmd *cobra.Command, args []string) { } parsedTime, _ := time.Parse(time.RFC3339, status.LastActivityTime) + if parsedTime.Unix() == 0 { + parsedTime = time.Now() + } + latestActivity += " (" + intToTimeString(int(time.Now().Unix()-parsedTime.Unix())) + " ago)" syncStatus := status.Status @@ -101,6 +105,16 @@ func (cmd *StatusCmd) RunStatusSync(cobraCmd *cobra.Command, args []string) { syncStatus = "Active" } + if len(status.Pod) > 15 { + status.Pod = status.Pod[:15] + "..." + } + if len(status.Local) > 20 { + status.Local = "..." + status.Local[len(status.Local)-20:len(status.Local)] + } + if len(status.Container) > 20 { + status.Container = "..." + status.Container[len(status.Container)-20:len(status.Container)] + } + values = append(values, []string{ syncStatus, status.Pod, @@ -118,39 +132,39 @@ func intToTimeString(timeDifference int) string { days := math.Floor(float64(timeDifference) / (60.0 * 60.0 * 24.0)) if days > 0 { if days == 1 { - return "1 day" + return "1d" } - return strconv.Itoa(int(days)) + " days" + return strconv.Itoa(int(days)) + "d" } hours := math.Floor(float64(timeDifference) / (60.0 * 60.0)) if hours > 0 { if hours == 1 { - return "1 hour" + return "1h" } - return strconv.Itoa(int(hours)) + " hours" + return strconv.Itoa(int(hours)) + "h" } minutes := math.Floor(float64(timeDifference) / 60.0) if minutes > 0 { if minutes == 1 { - return "1 minute" + return "1m" } - return strconv.Itoa(int(minutes)) + " minutes" + return strconv.Itoa(int(minutes)) + "m" } if timeDifference > 0 { if timeDifference == 1 { - return "1 seconds" + return "1s" } - return strconv.Itoa(timeDifference) + " seconds" + return strconv.Itoa(timeDifference) + "s" } - return "0 seconds" + return "0s" } func isSyncJSONMapInvalid(jsonMap map[string]string) bool { diff --git a/cmd/up.go b/cmd/up.go index 215497eafc..9ac2e94f58 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -1,7 +1,6 @@ package cmd import ( - "fmt" "os" "os/exec" "path/filepath" @@ -14,24 +13,16 @@ import ( "github.com/covexo/devspace/pkg/util/yamlutil" - "github.com/covexo/devspace/pkg/devspace/builder" - - "github.com/docker/docker/api/types" - - "github.com/covexo/devspace/pkg/devspace/builder/docker" - - "github.com/covexo/devspace/pkg/devspace/config/v1" - - "github.com/covexo/devspace/pkg/util/randutil" + "github.com/covexo/devspace/pkg/devspace/config/generated" + "github.com/covexo/devspace/pkg/devspace/image" "github.com/covexo/devspace/pkg/util/log" - "github.com/covexo/devspace/pkg/devspace/builder/kaniko" "github.com/covexo/devspace/pkg/devspace/registry" synctool "github.com/covexo/devspace/pkg/devspace/sync" - helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" + helmClient "github.com/covexo/devspace/pkg/devspace/deploy/helm" + "github.com/covexo/devspace/pkg/devspace/kubectl" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/covexo/devspace/pkg/devspace/config/configutil" @@ -44,7 +35,7 @@ import ( // UpCmd is a struct that defines a command call for "up" type UpCmd struct { flags *UpCmdFlags - helm *helmClient.HelmClientWrapper + helm *helmClient.ClientWrapper kubectl *kubernetes.Clientset workdir string pod *k8sv1.Pod @@ -125,7 +116,6 @@ func (cmd *UpCmd) Run(cobraCmd *cobra.Command, args []string) { } cmd.workdir = workdir - configExists, _ := configutil.ConfigExists() if !configExists { initCmd := &InitCmd{ @@ -156,8 +146,33 @@ func (cmd *UpCmd) Run(cobraCmd *cobra.Command, args []string) { cmd.initRegistries() } + cmd.buildAndDeploy() + + if cmd.flags.portforwarding { + cmd.startPortForwarding() + } + + if cmd.flags.sync { + syncConfigs := cmd.startSync() + defer func() { + for _, v := range syncConfigs { + v.Stop() + } + }() + } + + enterTerminal(cmd.kubectl, cmd.pod, cmd.flags.container, args) +} + +func (cmd *UpCmd) buildAndDeploy() { + // Load config + generatedConfig, err := generated.LoadConfig() + if err != nil { + log.Fatalf("Error loading generated.yaml: %v", err) + } + // Build image if necessary - mustRedeploy := cmd.buildImages() + mustRedeploy := cmd.buildImages(generatedConfig) // Check if the chart directory has changed hash, err := hash.Directory("chart") @@ -165,46 +180,31 @@ func (cmd *UpCmd) Run(cobraCmd *cobra.Command, args []string) { log.Fatalf("Error hashing chart directory: %v", err) } - // Load config - config := configutil.GetConfig(false) - // Check if we find a running release pod pod, err := getRunningDevSpacePod(cmd.helm, cmd.kubectl) - if err != nil || mustRedeploy || cmd.flags.deploy || config.DevSpace.ChartHash == nil || *config.DevSpace.ChartHash != hash { - cmd.deployChart() + if err != nil || mustRedeploy || cmd.flags.deploy || generatedConfig.HelmChartHash != hash { + cmd.deployChart(generatedConfig) - config.DevSpace.ChartHash = &hash + generatedConfig.HelmChartHash = hash - err = configutil.SaveConfig() + // Save Config + err = generated.SaveConfig(generatedConfig) if err != nil { log.Fatalf("Error saving config: %v", err) } } else { cmd.pod = pod } - - if cmd.flags.portforwarding { - cmd.startPortForwarding() - } - - if cmd.flags.sync { - syncConfigs := cmd.startSync() - defer func() { - for _, v := range syncConfigs { - v.Stop() - } - }() - } - - enterTerminal(cmd.kubectl, cmd.pod, cmd.flags.container, args) } func (cmd *UpCmd) ensureNamespace() error { - config := configutil.GetConfig(false) + config := configutil.GetConfig() releaseNamespace := *config.DevSpace.Release.Namespace _, err := cmd.kubectl.CoreV1().Namespaces().Get(releaseNamespace, metav1.GetOptions{}) if err != nil { + log.Infof("Create namespace %s", releaseNamespace) + // Create release namespace _, err = cmd.kubectl.CoreV1().Namespaces().Create(&k8sv1.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -276,7 +276,7 @@ func (cmd *UpCmd) ensureClusterRoleBinding() error { return err } } else { - cfg := configutil.GetConfig(false) + cfg := configutil.GetConfig() if cfg.Cluster.CloudProvider == nil || *cfg.Cluster.CloudProvider == "" { log.Warn("Unable to check permissions: If you run into errors, please create the ClusterRoleBinding '" + clusterRoleBindingName + "' as described here: https://devspace.covexo.com/docs/advanced/rbac.html") @@ -288,10 +288,10 @@ func (cmd *UpCmd) ensureClusterRoleBinding() error { } func (cmd *UpCmd) initRegistries() { - config := configutil.GetConfig(false) + config := configutil.GetConfig() registryMap := *config.Registries - if config.Services.InternalRegistry != nil { + if config.Services != nil && config.Services.InternalRegistry != nil && config.Registries != nil { registryConf, regConfExists := registryMap["internal"] if !regConfExists { log.Fatal("Registry config not found for internal registry") @@ -300,7 +300,6 @@ func (cmd *UpCmd) initRegistries() { log.StartWait("Initializing internal registry") err := registry.InitInternalRegistry(cmd.kubectl, cmd.helm, config.Services.InternalRegistry, registryConf) log.StopWait() - if err != nil { log.Fatalf("Internal registry error: %v", err) } @@ -313,207 +312,49 @@ func (cmd *UpCmd) initRegistries() { log.Done("Internal registry started") } - for registryName, registryConf := range registryMap { - if registryConf.Auth != nil && registryConf.Auth.Password != nil { - username := "" - password := *registryConf.Auth.Password - email := "noreply@devspace-cloud.com" - registryURL := "" - - if registryConf.Auth.Username != nil { - username = *registryConf.Auth.Username - } - if registryConf.URL != nil { - registryURL = *registryConf.URL - } - - log.StartWait("Creating image pull secret for registry: " + registryName) - err := registry.CreatePullSecret(cmd.kubectl, *config.DevSpace.Release.Namespace, registryURL, username, password, email) - log.StopWait() - - if err != nil { - log.Fatalf("Failed to create pull secret for registry: %v", err) - } - } - } -} - -func (cmd *UpCmd) shouldRebuild(imageConf *v1.ImageConfig, dockerfilePath string) bool { - var dockerfileModTime time.Time - - mustRebuild := true - dockerfileInfo, err := os.Stat(dockerfilePath) + if registryMap != nil { + for registryName, registryConf := range registryMap { + if registryConf.Auth != nil && registryConf.Auth.Password != nil { + username := "" + password := *registryConf.Auth.Password + email := "noreply@devspace-cloud.com" + registryURL := "" - if err != nil { - if imageConf.Build.LatestTimestamp == nil { - log.Fatalf("Dockerfile missing: %v", err) - } else { - mustRebuild = false - } - } else { - dockerfileModTime = dockerfileInfo.ModTime() + if registryConf.Auth.Username != nil { + username = *registryConf.Auth.Username + } + if registryConf.URL != nil { + registryURL = *registryConf.URL + } - // When user has not used -b or --build flags - if cmd.flags.build == false { - if imageConf.Build.LatestTimestamp != nil { - latestBuildTime, _ := time.Parse(time.RFC3339Nano, *imageConf.Build.LatestTimestamp) + log.StartWait("Creating image pull secret for registry: " + registryName) + err := registry.CreatePullSecret(cmd.kubectl, *config.DevSpace.Release.Namespace, registryURL, username, password, email) + log.StopWait() - // only rebuild Docker image when Dockerfile has changed since latest build - mustRebuild = (latestBuildTime.Equal(dockerfileModTime) == false) + if err != nil { + log.Fatalf("Failed to create pull secret for registry: %v", err) + } } } } - - imageConf.Build.LatestTimestamp = configutil.String(dockerfileModTime.Format(time.RFC3339Nano)) - return mustRebuild } // returns true when one of the images had to be rebuild -func (cmd *UpCmd) buildImages() bool { +func (cmd *UpCmd) buildImages(generatedConfig *generated.Config) bool { re := false - config := configutil.GetConfig(false) + config := configutil.GetConfig() for imageName, imageConf := range *config.Images { - dockerfilePath := "./Dockerfile" - contextPath := "./" - - if imageConf.Build.DockerfilePath != nil { - dockerfilePath = *imageConf.Build.DockerfilePath - } - - if imageConf.Build.ContextPath != nil { - contextPath = *imageConf.Build.ContextPath - } - - dockerfilePath, err := filepath.Abs(dockerfilePath) - if err != nil { - log.Fatalf("Couldn't determine absolute path for %s", *imageConf.Build.DockerfilePath) - } - - contextPath, err = filepath.Abs(contextPath) + shouldRebuild, err := image.Build(cmd.kubectl, generatedConfig, imageName, imageConf, cmd.flags.build) if err != nil { - log.Fatalf("Couldn't determine absolute path for %s", *imageConf.Build.ContextPath) + log.Fatal(err) } - if cmd.shouldRebuild(imageConf, dockerfilePath) { + if shouldRebuild { re = true - imageTag, randErr := randutil.GenerateRandomString(7) - - if randErr != nil { - log.Fatalf("Image building failed: %s", randErr.Error()) - } - registryConf, err := registry.GetRegistryConfig(imageConf) - if err != nil { - log.Fatal(err) - } - - var imageBuilder builder.Interface - - buildInfo := "Building image '%s' with engine '%s'" - engineName := "" - registryURL := "" - - if registryConf.URL != nil { - registryURL = *registryConf.URL - } - if registryURL == "hub.docker.com" { - registryURL = "" - } - - if imageConf.Build.Engine.Kaniko != nil { - engineName = "kaniko" - buildNamespace := *config.DevSpace.Release.Namespace - allowInsecurePush := false - - if imageConf.Build.Engine.Kaniko.Namespace != nil { - buildNamespace = *imageConf.Build.Engine.Kaniko.Namespace - } - - if registryConf.Insecure != nil { - allowInsecurePush = *registryConf.Insecure - } - imageBuilder, err = kaniko.NewBuilder(registryURL, *imageConf.Name, imageTag, buildNamespace, cmd.kubectl, allowInsecurePush) - if err != nil { - log.Fatalf("Error creating kaniko builder: %v", err) - } - } else { - engineName = "docker" - preferMinikube := true - - if imageConf.Build.Engine.Docker.PreferMinikube != nil { - preferMinikube = *imageConf.Build.Engine.Docker.PreferMinikube - } - - imageBuilder, err = docker.NewBuilder(registryURL, *imageConf.Name, imageTag, preferMinikube) - if err != nil { - log.Fatalf("Error creating docker client: %v", err) - } - } - - log.Infof(buildInfo, imageName, engineName) - - if registryConf.URL != nil { - registryURL = *registryConf.URL - } - - username := "" - password := "" - if registryConf.Auth != nil { - if registryConf.Auth.Username != nil { - username = *registryConf.Auth.Username - } - - if registryConf.Auth.Password != nil { - password = *registryConf.Auth.Password - } - } - - log.StartWait("Authenticating (" + registryURL + ")") - _, err = imageBuilder.Authenticate(username, password, len(username) == 0) - log.StopWait() - - if err != nil { - log.Fatalf("Error during image registry authentication: %v", err) - } - - log.Done("Authentication successful (" + registryURL + ")") - - buildOptions := &types.ImageBuildOptions{} - if imageConf.Build.Options != nil { - if imageConf.Build.Options.BuildArgs != nil { - buildOptions.BuildArgs = *imageConf.Build.Options.BuildArgs - } - if imageConf.Build.Options.Target != nil { - buildOptions.Target = *imageConf.Build.Options.Target - } - if imageConf.Build.Options.Network != nil { - buildOptions.NetworkMode = *imageConf.Build.Options.Network - } - } - - err = imageBuilder.BuildImage(contextPath, dockerfilePath, buildOptions) - if err != nil { - log.Fatalf("Error during image build: %v", err) - } - - err = imageBuilder.PushImage() - if err != nil { - log.Fatalf("Error during image push: %v", err) - } - - log.Info("Image pushed to registry (" + registryURL + ")") - imageConf.Tag = &imageTag - - err = configutil.SaveConfig() - if err != nil { - log.Fatalf("Config saving error: %s", err.Error()) - } - - log.Done("Done building and pushing image '" + imageName + "'") - } else { - log.Infof("Skip building image '%s'", imageName) } } + return re } @@ -532,10 +373,11 @@ func (cmd *UpCmd) initHelm() { } } -func (cmd *UpCmd) deployChart() { - config := configutil.GetConfig(false) +func (cmd *UpCmd) deployChart(generatedConfig *generated.Config) { + config := configutil.GetConfig() log.StartWait("Deploying helm chart") + defer log.StopWait() releaseName := *config.DevSpace.Release.Name releaseNamespace := *config.DevSpace.Release.Namespace @@ -553,7 +395,7 @@ func (cmd *UpCmd) deployChart() { for imageName, imageConf := range *config.Images { container := map[string]interface{}{} - container["image"] = registry.GetImageURL(imageConf, true) + container["image"] = registry.GetImageURL(generatedConfig, imageConf, true) if cmd.flags.noSleep { container["command"] = []string{} @@ -581,9 +423,6 @@ func (cmd *UpCmd) deployChart() { overwriteValues["pullSecrets"] = pullSecrets appRelease, err := cmd.helm.InstallChartByPath(releaseName, releaseNamespace, chartPath, &overwriteValues) - - log.StopWait() - if err != nil { log.Fatalf("Unable to deploy helm chart: %s", err.Error()) } @@ -591,75 +430,15 @@ func (cmd *UpCmd) deployChart() { releaseRevision := int(appRelease.Version) log.Donef("Deployed helm chart (Release revision: %d)", releaseRevision) log.StartWait("Waiting for release pod to become ready") - defer log.StopWait() - - for true { - podList, err := cmd.kubectl.Core().Pods(releaseNamespace).List(metav1.ListOptions{ - LabelSelector: "release=" + releaseName, - }) - - if err != nil { - log.Panicf("Unable to list devspace pods: %s", err.Error()) - } - - if len(podList.Items) > 0 { - highestRevision := 0 - var selectedPod *k8sv1.Pod - - for i, pod := range podList.Items { - if kubectl.GetPodStatus(&pod) == "Terminating" { - continue - } - - podRevision, podHasRevision := pod.Annotations["revision"] - hasHigherRevision := (i == 0) - - if !hasHigherRevision && podHasRevision { - podRevisionInt, _ := strconv.Atoi(podRevision) - - if podRevisionInt > highestRevision { - hasHigherRevision = true - } - } - - if hasHigherRevision { - selectedPod = &pod - highestRevision, _ = strconv.Atoi(podRevision) - } - } - - if selectedPod != nil { - _, hasRevision := selectedPod.Annotations["revision"] - if !hasRevision || highestRevision == releaseRevision { - if !hasRevision { - log.Warn("Found pod without revision. Use annotation 'revision' for your pods to avoid this warning.") - } - - cmd.pod = selectedPod - err = waitForPodReady(cmd.kubectl, cmd.pod, 2*60*time.Second, 5*time.Second) - - if err != nil { - log.Fatalf("Error during waiting for pod: %s", err.Error()) - } - - break - } else { - log.Info("Waiting for release upgrade to complete.") - } - } - } else { - log.Info("Waiting for release to be deployed.") - } - - time.Sleep(2 * time.Second) + cmd.pod, err = helmClient.WaitForReleasePodToGetReady(cmd.kubectl, releaseName, releaseNamespace, releaseRevision) + if err != nil { + log.Fatal(err) } - - log.StopWait() } func (cmd *UpCmd) startSync() []*synctool.SyncConfig { - config := configutil.GetConfig(false) + config := configutil.GetConfig() syncConfigs := make([]*synctool.SyncConfig, 0, len(*config.DevSpace.Sync)) for _, syncPath := range *config.DevSpace.Sync { @@ -744,10 +523,10 @@ func (cmd *UpCmd) startSync() []*synctool.SyncConfig { } func (cmd *UpCmd) startPortForwarding() { - config := configutil.GetConfig(false) + config := configutil.GetConfig() for _, portForwarding := range *config.DevSpace.PortForwarding { - if *portForwarding.ResourceType == "pod" { + if portForwarding.ResourceType == nil || *portForwarding.ResourceType == "pod" { if len(*portForwarding.LabelSelector) > 0 { labels := make([]string, 0, len(*portForwarding.LabelSelector)) @@ -789,22 +568,3 @@ func (cmd *UpCmd) startPortForwarding() { } } } - -func waitForPodReady(kubectl *kubernetes.Clientset, pod *k8sv1.Pod, maxWaitTime time.Duration, checkInterval time.Duration) error { - for maxWaitTime > 0 { - pod, err := kubectl.Core().Pods(pod.Namespace).Get(pod.Name, metav1.GetOptions{}) - - if err != nil { - return err - } - - if len(pod.Status.ContainerStatuses) > 0 && pod.Status.ContainerStatuses[0].Ready { - return nil - } - - time.Sleep(checkInterval) - maxWaitTime = maxWaitTime - checkInterval - } - - return fmt.Errorf("Max wait time expired") -} diff --git a/pkg/devspace/builder/docker/client.go b/pkg/devspace/builder/docker/client.go index 2c55e78ff3..617cb58a4b 100644 --- a/pkg/devspace/builder/docker/client.go +++ b/pkg/devspace/builder/docker/client.go @@ -9,7 +9,7 @@ import ( "path/filepath" "strings" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" + "github.com/covexo/devspace/pkg/devspace/kubectl" "github.com/docker/docker/api" "github.com/docker/docker/client" diff --git a/pkg/devspace/builder/kaniko/kaniko.go b/pkg/devspace/builder/kaniko/kaniko.go index 8f74b98fb9..43c02d689c 100644 --- a/pkg/devspace/builder/kaniko/kaniko.go +++ b/pkg/devspace/builder/kaniko/kaniko.go @@ -8,7 +8,7 @@ import ( "github.com/covexo/devspace/pkg/devspace/builder/docker" "github.com/covexo/devspace/pkg/devspace/registry" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" + "github.com/covexo/devspace/pkg/devspace/kubectl" synctool "github.com/covexo/devspace/pkg/devspace/sync" "github.com/covexo/devspace/pkg/util/ignoreutil" "github.com/covexo/devspace/pkg/util/log" diff --git a/pkg/devspace/cloud/login.go b/pkg/devspace/cloud/login.go index 56192fb5c3..9e00e30b00 100644 --- a/pkg/devspace/cloud/login.go +++ b/pkg/devspace/cloud/login.go @@ -112,7 +112,7 @@ func Login(provider *Provider) (string, *api.Cluster, *api.AuthInfo, error) { } // Update updates the cloud provider information if necessary -func Update(providerConfig ProviderConfig, dsConfig *v1.Config, switchKubeContext bool) error { +func Update(providerConfig ProviderConfig, dsConfig *v1.Config, useKubeContext, switchKubeContext bool) error { cloudProvider := *dsConfig.Cluster.CloudProvider // Don't update anything if we don't use a cloud provider @@ -131,9 +131,8 @@ func Update(providerConfig ProviderConfig, dsConfig *v1.Config, switchKubeContex } dsConfig.DevSpace.Release.Namespace = &namespace - dsConfig.Services.Tiller.Release.Namespace = &namespace - if *dsConfig.Cluster.UseKubeConfig { + if useKubeContext { kubeContext := DevSpaceKubeContextName + "-" + namespace err = UpdateKubeConfig(kubeContext, namespace, cluster, authInfo, switchKubeContext) diff --git a/pkg/devspace/config/configutil/get.go b/pkg/devspace/config/configutil/get.go index 210c0f4619..a3a0b92e7b 100644 --- a/pkg/devspace/config/configutil/get.go +++ b/pkg/devspace/config/configutil/get.go @@ -2,6 +2,7 @@ package configutil import ( "os" + "sync" "unsafe" "github.com/covexo/devspace/pkg/util/log" @@ -16,73 +17,79 @@ const configGitignore = `logs/ overwrite.yaml ` -const configPath = "/.devspace/config.yaml" -const overwriteConfigPath = "/.devspace/overwrite.yaml" +// ConfigPath is the path for the main config +const ConfigPath = "/.devspace/config.yaml" -var config = makeConfig() -var configRaw = makeConfig() -var overwriteConfig = makeConfig() -var overwriteConfigRaw = makeConfig() -var configLoaded = false -var overwriteConfigLoaded = false -var workdir string +// OverwriteConfigPath specifies where the override.yaml lies +const OverwriteConfigPath = "/.devspace/overwrite.yaml" -func init() { - workdir, _ = os.Getwd() -} +// Global config vars +var config *v1.Config +var configRaw *v1.Config +var overwriteConfig *v1.Config +var overwriteConfigRaw *v1.Config + +// Thread-safety helper +var getConfigOnce sync.Once -//ConfigExists checks 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) + workdir, _ := os.Getwd() - if configNotFound != nil { + _, err := os.Stat(workdir + ConfigPath) + if os.IsNotExist(err) { return false, nil + } else if err != nil { + return false, err } - config := GetConfig(false) - return (config.Version != nil), nil + return true, nil } -//GetConfig returns the config merged from .devspace/config.yaml and .devspace/overwrite.yaml -func GetConfig(reload bool) *v1.Config { - if !configLoaded || reload { - if reload { - config = makeConfig() - configRaw = makeConfig() - } - configLoaded = true +// InitConfig initializes the config objects +func InitConfig() *v1.Config { + getConfigOnce.Do(func() { + config = makeConfig() + configRaw = makeConfig() + overwriteConfig = makeConfig() + overwriteConfigRaw = makeConfig() + }) + + return config +} - err := loadConfig(configRaw, configPath) +// GetConfig returns the config merged from .devspace/config.yaml and .devspace/overwrite.yaml +func GetConfig() *v1.Config { + getConfigOnce.Do(func() { + config = makeConfig() + configRaw = makeConfig() + overwriteConfig = makeConfig() + overwriteConfigRaw = makeConfig() + err := loadConfig(configRaw, ConfigPath) if err != nil { - log.Fatal("Unable to load config.") + log.Errorf("Loading config: %v", err) + log.Fatal("Please run `devspace init -r` to repair your config") } - GetOverwriteConfig(false) + + //ignore error as overwrite.yaml is optional + loadConfig(overwriteConfigRaw, OverwriteConfigPath) merge(config, configRaw, unsafe.Pointer(&config), unsafe.Pointer(configRaw)) + merge(overwriteConfig, overwriteConfigRaw, unsafe.Pointer(&overwriteConfig), unsafe.Pointer(overwriteConfigRaw)) merge(config, overwriteConfig, unsafe.Pointer(&config), unsafe.Pointer(overwriteConfig)) - } - return config -} -//GetOverwriteConfig returns the config retrieved from .devspace/overwrite.yaml -func GetOverwriteConfig(reload bool) *v1.Config { - if !overwriteConfigLoaded || reload { - if reload { - overwriteConfig = makeConfig() - overwriteConfigRaw = makeConfig() + if config.DevSpace.Release != nil && config.DevSpace.Release.Namespace == nil { + config.DevSpace.Release.Namespace = String("default") } - overwriteConfigLoaded = true - - //ignore error as overwrite.yaml is optional - loadConfig(overwriteConfigRaw, overwriteConfigPath) + }) - merge(overwriteConfig, overwriteConfigRaw, unsafe.Pointer(&overwriteConfig), unsafe.Pointer(overwriteConfigRaw)) - } - return overwriteConfig + return config } -//GetConfigInstance returns the reference to the config (in most cases it is recommended to use GetConfig instaed) -func GetConfigInstance() *v1.Config { - return config +// GetOverwriteConfig returns the config retrieved from .devspace/overwrite.yaml +func GetOverwriteConfig() *v1.Config { + GetConfig() + + return overwriteConfig } diff --git a/pkg/devspace/config/configutil/load.go b/pkg/devspace/config/configutil/load.go index 25f514c00a..3be31cb5a0 100644 --- a/pkg/devspace/config/configutil/load.go +++ b/pkg/devspace/config/configutil/load.go @@ -2,16 +2,19 @@ package configutil import ( "io/ioutil" + "os" + "path/filepath" "github.com/covexo/devspace/pkg/devspace/config/v1" yaml "gopkg.in/yaml.v2" ) func loadConfig(config *v1.Config, path string) error { - yamlFileContent, err := ioutil.ReadFile(workdir + path) - + workdir, _ := os.Getwd() + yamlFileContent, err := ioutil.ReadFile(filepath.Join(workdir, path)) if err != nil { return err } + return yaml.Unmarshal(yamlFileContent, config) } diff --git a/pkg/devspace/config/configutil/make.go b/pkg/devspace/config/configutil/make.go index 51cc28fef0..01f049e820 100644 --- a/pkg/devspace/config/configutil/make.go +++ b/pkg/devspace/config/configutil/make.go @@ -17,11 +17,6 @@ func makeConfig() *v1.Config { }, Images: &map[string]*v1.ImageConfig{}, Registries: &map[string]*v1.RegistryConfig{}, - Services: &v1.ServiceConfig{ - Tiller: &v1.TillerConfig{ - AppNamespaces: &[]*string{}, - Release: &v1.Release{}, - }, - }, + Services: &v1.ServiceConfig{}, } } diff --git a/pkg/devspace/config/configutil/save.go b/pkg/devspace/config/configutil/save.go index 70777e19a6..bc35f21668 100644 --- a/pkg/devspace/config/configutil/save.go +++ b/pkg/devspace/config/configutil/save.go @@ -7,7 +7,6 @@ import ( "path/filepath" "reflect" "strings" - "unsafe" "github.com/covexo/devspace/pkg/util/fsutil" yaml "gopkg.in/yaml.v2" @@ -15,58 +14,48 @@ import ( //SaveConfig writes the data of a config to its yaml file func SaveConfig() error { - configExists, _ := ConfigExists() - baseConfig := makeConfig() - - // just in case someone has set a pointer to one of the structs to nil, merge empty an empty config object into all configs - 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) + workdir, _ := os.Getwd() + configMapRaw, overwriteMapRaw, err := getConfigAndOverwriteMaps(config, configRaw, overwriteConfig, overwriteConfigRaw) + if err != nil { + return err + } configMap, _ := configMapRaw.(map[interface{}]interface{}) overwriteMap, _ := overwriteMapRaw.(map[interface{}]interface{}) - if configErr != nil { - return configErr + configYaml, err := yaml.Marshal(configMap) + if err != nil { + return err } - configYaml, yamlErr := yaml.Marshal(configMap) - - if yamlErr != nil { - return yamlErr - } - configDir := filepath.Dir(workdir + configPath) - + configDir := filepath.Dir(workdir + ConfigPath) os.MkdirAll(configDir, os.ModePerm) - if !configExists { + // Check if .gitignore exists + _, err = os.Stat(filepath.Join(configDir, ".gitignore")) + if os.IsNotExist(err) { fsutil.WriteToFile([]byte(configGitignore), filepath.Join(configDir, ".gitignore")) } - writeErr := ioutil.WriteFile(workdir+configPath, configYaml, os.ModePerm) + 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 + overwriteConfigYaml, err := yaml.Marshal(overwriteMap) + if err != nil { + return err } - return ioutil.WriteFile(workdir+overwriteConfigPath, overwriteConfigYaml, os.ModePerm) + + return ioutil.WriteFile(workdir+OverwriteConfigPath, overwriteConfigYaml, os.ModePerm) } - configLoaded = true - overwriteConfigLoaded = true return nil } +// TODO: Think about removing configRaw & overwriteConfigRaw func getConfigAndOverwriteMaps(config interface{}, configRaw interface{}, overwriteConfig interface{}, overwriteConfigRaw interface{}) (interface{}, interface{}, error) { - object, isObjectNil := getPointerValue(config) objectType := reflect.TypeOf(object) diff --git a/pkg/devspace/config/generated/config.go b/pkg/devspace/config/generated/config.go new file mode 100644 index 0000000000..9a4ad75aed --- /dev/null +++ b/pkg/devspace/config/generated/config.go @@ -0,0 +1,59 @@ +package generated + +import ( + "io/ioutil" + "os" + "path/filepath" + + yaml "gopkg.in/yaml.v2" +) + +// Config specifies the runtime config struct +type Config struct { + HelmChartHash string `yaml:"chartHash"` + DockerLatestTimestamps map[string]int64 `yaml:"dockerLatestTimestamps"` + ImageTags map[string]string `yaml:"imageTags"` +} + +// ConfigPath is the relative generated config path +var ConfigPath = "/.devspace/generated.yaml" + +// LoadConfig loads the config from the filesystem +func LoadConfig() (*Config, error) { + workdir, _ := os.Getwd() + + data, err := ioutil.ReadFile(filepath.Join(workdir, ConfigPath)) + if err != nil { + return &Config{ + DockerLatestTimestamps: make(map[string]int64), + ImageTags: make(map[string]string), + }, nil + } + + config := &Config{} + err = yaml.Unmarshal(data, config) + if err != nil { + return nil, err + } + + return config, nil +} + +// SaveConfig saves the config to the filesystem +func SaveConfig(config *Config) error { + workdir, _ := os.Getwd() + + data, err := yaml.Marshal(config) + if err != nil { + return err + } + + configPath := filepath.Join(workdir, ConfigPath) + + err = os.MkdirAll(filepath.Dir(configPath), 0755) + if err != nil { + return err + } + + return ioutil.WriteFile(configPath, data, 0666) +} diff --git a/pkg/devspace/config/v1/cluster.go b/pkg/devspace/config/v1/cluster.go index 9a492c2a69..1292530301 100644 --- a/pkg/devspace/config/v1/cluster.go +++ b/pkg/devspace/config/v1/cluster.go @@ -2,7 +2,6 @@ package v1 //Cluster is a struct that contains data for a Kubernetes-Cluster type Cluster struct { - UseKubeConfig *bool `yaml:"useKubeConfig,omitempty"` CloudProvider *string `yaml:"cloudProvider,omitempty"` KubeContext *string `yaml:"kubeContext,omitempty"` APIServer *string `yaml:"apiServer,omitempty"` diff --git a/pkg/devspace/config/v1/devspace.go b/pkg/devspace/config/v1/devspace.go index af96f57dd0..089a288649 100644 --- a/pkg/devspace/config/v1/devspace.go +++ b/pkg/devspace/config/v1/devspace.go @@ -4,7 +4,6 @@ package v1 type DevSpaceConfig struct { Terminal *Terminal `yaml:"terminal"` Release *Release `yaml:"release"` - ChartHash *string `yaml:"chartHash"` PortForwarding *[]*PortForwardingConfig `yaml:"portForwarding"` Sync *[]*SyncConfig `yaml:"sync"` } @@ -12,7 +11,7 @@ type DevSpaceConfig struct { //PortForwardingConfig defines the ports for a port forwarding to a DevSpace type PortForwardingConfig struct { Namespace *string `yaml:"namespace"` - ResourceType *string `yaml:"resourceType"` + ResourceType *string `yaml:"resourceType,omitempty"` LabelSelector *map[string]*string `yaml:"labelSelector"` PortMappings *[]*PortMapping `yaml:"portMappings"` } @@ -26,11 +25,11 @@ type PortMapping struct { //SyncConfig defines the paths for a SyncFolder type SyncConfig struct { Namespace *string `yaml:"namespace"` - ResourceType *string `yaml:"resourceType"` + ResourceType *string `yaml:"resourceType,omitempty"` LabelSelector *map[string]*string `yaml:"labelSelector"` LocalSubPath *string `yaml:"localSubPath"` ContainerPath *string `yaml:"containerPath"` - ContainerName *string `yaml:"containerName"` + ContainerName *string `yaml:"containerName,omitempty"` ExcludePaths *[]string `yaml:"excludePaths"` DownloadExcludePaths *[]string `yaml:"downloadExcludePaths"` UploadExcludePaths *[]string `yaml:"uploadExcludePaths"` diff --git a/pkg/devspace/config/v1/image.go b/pkg/devspace/config/v1/image.go index 69fd66ae63..7a417a4e0b 100644 --- a/pkg/devspace/config/v1/image.go +++ b/pkg/devspace/config/v1/image.go @@ -10,11 +10,10 @@ type ImageConfig struct { //BuildConfig defines the build process for an image type BuildConfig struct { - ContextPath *string `yaml:"contextPath"` - DockerfilePath *string `yaml:"dockerfilePath"` - Engine *BuildEngine `yaml:"engine"` - LatestTimestamp *string `yaml:"latestTimestamp"` - Options *BuildOptions `yaml:"options"` + ContextPath *string `yaml:"contextPath"` + DockerfilePath *string `yaml:"dockerfilePath"` + Engine *BuildEngine `yaml:"engine"` + Options *BuildOptions `yaml:"options"` } //BuildEngine defines which build engine to use diff --git a/pkg/devspace/config/v1/services.go b/pkg/devspace/config/v1/services.go index 9a8de0553a..0964a43e48 100644 --- a/pkg/devspace/config/v1/services.go +++ b/pkg/devspace/config/v1/services.go @@ -8,8 +8,8 @@ type ServiceConfig struct { //TillerConfig defines the tiller service type TillerConfig struct { - Release *Release `yaml:"release"` - AppNamespaces *[]*string `yaml:"appNamespaces"` + Release *Release `yaml:"release,omitempty"` + AppNamespaces *[]*string `yaml:"appNamespaces,omitempty"` } //InternalRegistry defines the deployment of an internal registry diff --git a/pkg/devspace/config/v1/terminal.go b/pkg/devspace/config/v1/terminal.go index 1347864221..db6ee0aa83 100644 --- a/pkg/devspace/config/v1/terminal.go +++ b/pkg/devspace/config/v1/terminal.go @@ -2,6 +2,8 @@ package v1 // Terminal describes the terminal options type Terminal struct { - ContainerName *string `yaml:"containerName"` - Command *[]*string `yaml:"command"` + ResourceType *string `yaml:"resourceType"` + LabelSelector *map[string]*string `yaml:"labelSelector"` + ContainerName *string `yaml:"containerName"` + Command *[]*string `yaml:"command"` } diff --git a/pkg/devspace/configure/registry.go b/pkg/devspace/configure/registry.go new file mode 100644 index 0000000000..c791d11106 --- /dev/null +++ b/pkg/devspace/configure/registry.go @@ -0,0 +1,194 @@ +package configure + +import ( + "fmt" + "os/exec" + "regexp" + "strings" + + "github.com/covexo/devspace/pkg/devspace/builder/docker" + "github.com/covexo/devspace/pkg/devspace/config/configutil" + "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/devspace/kubectl" + "github.com/covexo/devspace/pkg/util/log" + "github.com/covexo/devspace/pkg/util/randutil" + "github.com/covexo/devspace/pkg/util/stdinutil" +) + +// ImageName configures the image name +func ImageName(dockerUsername string) error { + config := configutil.GetConfig() + registryURL := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + Question: "Which registry do you want to push to? ('hub.docker.com' or URL)", + DefaultValue: "hub.docker.com", + ValidationRegexPattern: "^.*$", + }) + + config.Services.InternalRegistry = nil + + if *registryURL != "hub.docker.com" { + imageBuilder, err := docker.NewBuilder(*registryURL, "", "", false) + if err == nil { + log.StartWait("Checking Docker credentials") + dockerAuthConfig, err := imageBuilder.Authenticate("", "", true) + log.StopWait() + + if err != nil { + return fmt.Errorf("Couldn't find credentials in credentials store. Make sure you login to the registry with: docker login %s", *registryURL) + } + + dockerUsername = dockerAuthConfig.Username + } + } else if dockerUsername == "" { + return fmt.Errorf("Make sure you login to docker hub with: docker login") + } + + googleRegistryRegex := regexp.MustCompile("^(.+\\.)?gcr.io$") + isGoogleRegistry := googleRegistryRegex.Match([]byte(*registryURL)) + isDockerHub := *registryURL == "hub.docker.com" + defaultImageName := "" + + if isDockerHub { + defaultImageName = *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + Question: "Which image name do you want to use on Docker Hub?", + DefaultValue: dockerUsername + "/devspace", + ValidationRegexPattern: "^[a-zA-Z0-9/]{4,30}$", + }) + } else if isGoogleRegistry { + project, err := exec.Command("gcloud", "config", "get-value", "project").Output() + gcloudProject := "myGCloudProject" + + if err == nil { + gcloudProject = strings.TrimSpace(string(project)) + } + + defaultImageName = *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + Question: "Which image name do you want to push to?", + DefaultValue: *registryURL + "/" + gcloudProject + "/devspace", + ValidationRegexPattern: "^.*$", + }) + } else { + defaultImageName = *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + Question: "Which image name do you want to push to?", + DefaultValue: *registryURL + "/" + dockerUsername + "/devspace", + ValidationRegexPattern: "^[a-zA-Z0-9\\./]{4,30}$", + }) + } + + imageMap := *config.Images + imageMap["default"].Name = &defaultImageName + + return nil +} + +// InternalRegistry configures the internal registry +func InternalRegistry() error { + config := configutil.GetConfig() + overwriteConfig := configutil.GetOverwriteConfig() + internalRegistryConfig := config.Services.InternalRegistry + + imageMap := *config.Images + defaultImageConf, defaultImageExists := imageMap["default"] + + if defaultImageExists { + defaultImageConf.Registry = configutil.String("internal") + } + + if internalRegistryConfig == nil { + internalRegistryConfig = &v1.InternalRegistry{ + Release: &v1.Release{}, + } + config.Services.InternalRegistry = internalRegistryConfig + } + + if internalRegistryConfig.Release.Name == nil { + internalRegistryConfig.Release.Name = configutil.String("devspace-registry") + } + if internalRegistryConfig.Release.Namespace == nil { + internalRegistryConfig.Release.Namespace = config.DevSpace.Release.Namespace + } + + overwriteRegistryMap := *overwriteConfig.Registries + overwriteRegistryConfig, overwriteRegistryConfigFound := overwriteRegistryMap["internal"] + + if !overwriteRegistryConfigFound { + overwriteRegistryConfig = &v1.RegistryConfig{ + Auth: &v1.RegistryAuth{}, + } + overwriteRegistryMap["internal"] = overwriteRegistryConfig + } + + registryAuth := overwriteRegistryConfig.Auth + if registryAuth.Username == nil { + randomUserSuffix, err := randutil.GenerateRandomString(5) + if err != nil { + return fmt.Errorf("Error creating random username: %s", err.Error()) + } + + registryAuth.Username = configutil.String("user-" + randomUserSuffix) + } + + if registryAuth.Password == nil { + randomPassword, err := randutil.GenerateRandomString(12) + if err != nil { + return fmt.Errorf("Error creating random password: %s", err.Error()) + } + + registryAuth.Password = &randomPassword + } + + var registryReleaseValues map[interface{}]interface{} + if internalRegistryConfig.Release.Values != nil { + registryReleaseValues = *internalRegistryConfig.Release.Values + } else { + registryReleaseValues = map[interface{}]interface{}{} + + registryDomain := stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + Question: "Which domain should your container registry be using? (optional, requires an ingress controller)", + ValidationRegexPattern: "^(([a-z0-9]([a-z0-9-]{0,120}[a-z0-9])?\\.)+[a-z0-9]{2,})?$", + }) + + if *registryDomain != "" { + registryReleaseValues = map[interface{}]interface{}{ + "Ingress": map[string]interface{}{ + "Enabled": true, + "Hosts": []string{ + *registryDomain, + }, + "Annotations": map[string]string{ + "Kubernetes.io/tls-acme": "true", + }, + "Tls": []map[string]interface{}{ + map[string]interface{}{ + "SecretName": "tls-devspace-registry", + "Hosts": []string{ + *registryDomain, + }, + }, + }, + }, + } + } else if kubectl.IsMinikube() == false { + log.Warn("Your Kubernetes cluster will not be able to pull images from a registry without a registry domain!\n") + } + } + + 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 !registryHasSecretHtpasswd { + secretMap["htpasswd"] = "" + } + } + + internalRegistryConfig.Release.Values = ®istryReleaseValues + config.Registries = &overwriteRegistryMap + + return nil +} diff --git a/pkg/devspace/clients/helm/client.go b/pkg/devspace/deploy/helm/client.go similarity index 83% rename from pkg/devspace/clients/helm/client.go rename to pkg/devspace/deploy/helm/client.go index 11f626df92..4ea840ff25 100644 --- a/pkg/devspace/clients/helm/client.go +++ b/pkg/devspace/deploy/helm/client.go @@ -17,9 +17,9 @@ import ( "k8s.io/client-go/kubernetes" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/devspace/kubectl" homedir "github.com/mitchellh/go-homedir" k8shelm "k8s.io/helm/pkg/helm" helmenvironment "k8s.io/helm/pkg/helm/environment" @@ -29,8 +29,8 @@ import ( helmstoragedriver "k8s.io/helm/pkg/storage/driver" ) -// HelmClientWrapper holds the necessary information for helm -type HelmClientWrapper struct { +// ClientWrapper holds the necessary information for helm +type ClientWrapper struct { Client *k8shelm.Client Settings *helmenvironment.EnvSettings TillerConfig *v1.TillerConfig @@ -38,10 +38,10 @@ type HelmClientWrapper struct { } // NewClient creates a new helm client -func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmClientWrapper, error) { - config := configutil.GetConfig(false) +func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*ClientWrapper, error) { + config := configutil.GetConfig() + tillerNamespace := GetTillerNamespace() - tillerConfig := config.Services.Tiller kubeconfig, err := kubectl.GetClientConfig() if err != nil { return nil, err @@ -57,12 +57,12 @@ func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmCl tunnelWaitTime := 2 * 60 * time.Second tunnelCheckInterval := 5 * time.Second - log.StartWait("Waiting for tiller to become ready") + log.StartWait("Waiting for " + tillerNamespace + "/tiller-deploy to become ready") defer log.StopWait() // Next we wait till we can establish a tunnel to the running pod for tunnelWaitTime > 0 { - tunnel, err = portforwarder.New(*tillerConfig.Release.Namespace, kubectlClient, kubeconfig) + tunnel, err = portforwarder.New(tillerNamespace, kubectlClient, kubeconfig) if err == nil { break } @@ -88,7 +88,6 @@ func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmCl for helmWaitTime > 0 { _, tillerError = client.ListReleases(k8shelm.ReleaseListLimit(1)) - if tillerError == nil || helmWaitTime < 0 { break } @@ -118,7 +117,6 @@ func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmCl os.MkdirAll(filepath.Dir(stableRepoCachePathAbs), os.ModePerm) _, repoFileNotFound := os.Stat(repoFile) - if repoFileNotFound != nil { err = fsutil.WriteToFile([]byte(defaultRepositories), repoFile) if err != nil { @@ -126,7 +124,16 @@ func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmCl } } - wrapper := &HelmClientWrapper{ + tillerConfig := config.Services.Tiller + if tillerConfig == nil { + tillerConfig = &v1.TillerConfig{ + Release: &v1.Release{ + Namespace: &tillerNamespace, + }, + } + } + + wrapper := &ClientWrapper{ Client: client, Settings: &helmenvironment.EnvSettings{ Home: helmpath.Home(helmHomePath), @@ -146,7 +153,7 @@ func NewClient(kubectlClient *kubernetes.Clientset, upgradeTiller bool) (*HelmCl return wrapper, nil } -func (helmClientWrapper *HelmClientWrapper) updateRepos() error { +func (helmClientWrapper *ClientWrapper) updateRepos() error { allRepos, err := repo.LoadRepositoriesFile(helmClientWrapper.Settings.Home.RepositoryFile()) if err != nil { return err @@ -186,7 +193,7 @@ func (helmClientWrapper *HelmClientWrapper) updateRepos() error { } // ReleaseExists checks if the given release name exists -func (helmClientWrapper *HelmClientWrapper) ReleaseExists(releaseName string) (bool, error) { +func (helmClientWrapper *ClientWrapper) ReleaseExists(releaseName string) (bool, error) { _, err := helmClientWrapper.Client.ReleaseHistory(releaseName, k8shelm.WithMaxHistory(1)) if err != nil { if strings.Contains(err.Error(), helmstoragedriver.ErrReleaseNotFound(releaseName).Error()) { @@ -200,6 +207,6 @@ func (helmClientWrapper *HelmClientWrapper) ReleaseExists(releaseName string) (b } // DeleteRelease deletes a helm release and optionally purges it -func (helmClientWrapper *HelmClientWrapper) DeleteRelease(releaseName string, purge bool) (*rls.UninstallReleaseResponse, error) { +func (helmClientWrapper *ClientWrapper) DeleteRelease(releaseName string, purge bool) (*rls.UninstallReleaseResponse, error) { return helmClientWrapper.Client.DeleteRelease(releaseName, k8shelm.DeletePurge(purge)) } diff --git a/pkg/devspace/deploy/helm/deploy.go b/pkg/devspace/deploy/helm/deploy.go new file mode 100644 index 0000000000..a914a41a75 --- /dev/null +++ b/pkg/devspace/deploy/helm/deploy.go @@ -0,0 +1,98 @@ +package helm + +import ( + "fmt" + "strconv" + "time" + + k8sv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + + "github.com/covexo/devspace/pkg/devspace/kubectl" + "github.com/covexo/devspace/pkg/util/log" +) + +// WaitForReleasePodToGetReady waits for the release pod to get ready +func WaitForReleasePodToGetReady(client *kubernetes.Clientset, releaseName, releaseNamespace string, releaseRevision int) (*k8sv1.Pod, error) { + for true { + time.Sleep(4 * time.Second) + + podList, err := client.Core().Pods(releaseNamespace).List(metav1.ListOptions{ + LabelSelector: "release=" + releaseName, + }) + + if err != nil { + log.Panicf("Unable to list devspace pods: %s", err.Error()) + } + + if len(podList.Items) > 0 { + highestRevision := 0 + var selectedPod *k8sv1.Pod + + for i, pod := range podList.Items { + if kubectl.GetPodStatus(&pod) == "Terminating" { + continue + } + + podRevision, podHasRevision := pod.Annotations["revision"] + hasHigherRevision := (i == 0) + + if !hasHigherRevision && podHasRevision { + podRevisionInt, _ := strconv.Atoi(podRevision) + + if podRevisionInt > highestRevision { + hasHigherRevision = true + } + } + + if hasHigherRevision { + selectedPod = &pod + highestRevision, _ = strconv.Atoi(podRevision) + } + } + + if selectedPod != nil { + _, hasRevision := selectedPod.Annotations["revision"] + + if !hasRevision || highestRevision == releaseRevision { + if !hasRevision { + log.Warn("Found pod without revision. Use annotation 'revision' for your pods to avoid this warning.") + } + + err = waitForPodReady(client, selectedPod, 2*60*time.Second, 5*time.Second) + if err != nil { + return nil, fmt.Errorf("Error during waiting for pod: %s", err.Error()) + } + + return selectedPod, nil + } + + log.Info("Waiting for release upgrade to complete.") + } + } else { + log.Info("Waiting for release to be deployed.") + } + } + + return nil, nil +} + +func waitForPodReady(kubectl *kubernetes.Clientset, pod *k8sv1.Pod, maxWaitTime time.Duration, checkInterval time.Duration) error { + for maxWaitTime > 0 { + pod, err := kubectl.Core().Pods(pod.Namespace).Get(pod.Name, metav1.GetOptions{}) + + if err != nil { + return err + } + + if len(pod.Status.ContainerStatuses) > 0 && pod.Status.ContainerStatuses[0].Ready { + return nil + } + + time.Sleep(checkInterval) + maxWaitTime = maxWaitTime - checkInterval + } + + return fmt.Errorf("Max wait time expired") +} diff --git a/pkg/devspace/clients/helm/install.go b/pkg/devspace/deploy/helm/install.go similarity index 89% rename from pkg/devspace/clients/helm/install.go rename to pkg/devspace/deploy/helm/install.go index b79fed62a6..d910cb8821 100644 --- a/pkg/devspace/clients/helm/install.go +++ b/pkg/devspace/deploy/helm/install.go @@ -40,7 +40,7 @@ func checkDependencies(ch *chart.Chart, reqs *helmchartutil.Requirements) error } // 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 *ClientWrapper) InstallChartByPath(releaseName string, releaseNamespace string, chartPath string, values *map[interface{}]interface{}) (*hapi_release5.Release, error) { chart, err := helmchartutil.Load(chartPath) if err != nil { return nil, err @@ -124,7 +124,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 *ClientWrapper) 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/helm/rbac.go b/pkg/devspace/deploy/helm/rbac.go similarity index 85% rename from pkg/devspace/clients/helm/rbac.go rename to pkg/devspace/deploy/helm/rbac.go index f17f26b611..10f94cdcb8 100644 --- a/pkg/devspace/clients/helm/rbac.go +++ b/pkg/devspace/deploy/helm/rbac.go @@ -4,6 +4,7 @@ import ( "regexp" "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/util/log" k8sv1 "k8s.io/api/core/v1" k8sv1beta1 "k8s.io/api/rbac/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -22,8 +23,7 @@ const TillerRoleManagerName = "tiller-config-manager" var alreadyExistsRegexp = regexp.MustCompile(".* already exists$") func createTillerRBAC(kubectlClient *kubernetes.Clientset, dsConfig *v1.Config) error { - tillerConfig := dsConfig.Services.Tiller - tillerNamespace := *dsConfig.Services.Tiller.Release.Namespace + tillerNamespace := GetTillerNamespace() // Create service account err := createTillerServiceAccount(kubectlClient, tillerNamespace) @@ -45,14 +45,32 @@ func createTillerRBAC(kubectlClient *kubernetes.Clientset, dsConfig *v1.Config) } // Check if there is an internal registry - if dsConfig.Services.InternalRegistry != nil && dsConfig.Services.InternalRegistry.Release.Namespace != nil { + if dsConfig.Services.InternalRegistry != nil && dsConfig.Services.InternalRegistry.Release != nil && dsConfig.Services.InternalRegistry.Release.Namespace != nil { // Tiller needs access to the internal registry namespace appNamespaces = append(appNamespaces, dsConfig.Services.InternalRegistry.Release.Namespace) } + if dsConfig.Services.Tiller != nil && dsConfig.Services.Tiller.AppNamespaces != nil { + appNamespaces = append(appNamespaces, *dsConfig.Services.Tiller.AppNamespaces...) + } + // Persist the app namespaces to the config - tillerConfig.AppNamespaces = &appNamespaces - for _, appNamespace := range *tillerConfig.AppNamespaces { + for _, appNamespace := range appNamespaces { + // Create namespaces if they are not there already + _, err := kubectlClient.CoreV1().Namespaces().Get(*appNamespace, metav1.GetOptions{}) + if err != nil { + log.Infof("Create namespace %s", *appNamespace) + + _, err = kubectlClient.CoreV1().Namespaces().Create(&k8sv1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: *appNamespace, + }, + }) + if err != nil { + return err + } + } + err = addDeployAccessToTiller(kubectlClient, tillerNamespace, *appNamespace) if err != nil { return err diff --git a/pkg/devspace/clients/helm/search.go b/pkg/devspace/deploy/helm/search.go similarity index 89% rename from pkg/devspace/clients/helm/search.go rename to pkg/devspace/deploy/helm/search.go index 814988a544..b5896e7270 100644 --- a/pkg/devspace/clients/helm/search.go +++ b/pkg/devspace/deploy/helm/search.go @@ -24,7 +24,7 @@ func (s stringArraySorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s stringArraySorter) Less(a, b int) bool { return s[a][0] < s[b][0] } // PrintAllAvailableCharts prints all available charts -func (helmClientWrapper *HelmClientWrapper) PrintAllAvailableCharts() { +func (helmClientWrapper *ClientWrapper) PrintAllAvailableCharts() { var values stringArraySorter var header = []string{ "NAME", @@ -74,7 +74,7 @@ func (helmClientWrapper *HelmClientWrapper) PrintAllAvailableCharts() { } // SearchChart searches the chart name in all repositories -func (helmClientWrapper *HelmClientWrapper) SearchChart(chartName, chartVersion, appVersion string) (*repo.Entry, *repo.ChartVersion, error) { +func (helmClientWrapper *ClientWrapper) SearchChart(chartName, chartVersion, appVersion string) (*repo.Entry, *repo.ChartVersion, error) { allRepos, err := repo.LoadRepositoriesFile(helmClientWrapper.Settings.Home.RepositoryFile()) if err != nil { return nil, nil, err @@ -127,7 +127,7 @@ func (helmClientWrapper *HelmClientWrapper) SearchChart(chartName, chartVersion, } // BuildDependencies builds the dependencies -func (helmClientWrapper *HelmClientWrapper) BuildDependencies(chartPath string) error { +func (helmClientWrapper *ClientWrapper) BuildDependencies(chartPath string) error { man := &helmdownloader.Manager{ Out: ioutil.Discard, ChartPath: chartPath, @@ -139,7 +139,7 @@ func (helmClientWrapper *HelmClientWrapper) BuildDependencies(chartPath string) } // UpdateDependencies updates the dependencies -func (helmClientWrapper *HelmClientWrapper) UpdateDependencies(chartPath string) error { +func (helmClientWrapper *ClientWrapper) UpdateDependencies(chartPath string) error { man := &helmdownloader.Manager{ Out: ioutil.Discard, ChartPath: chartPath, diff --git a/pkg/devspace/clients/helm/tiller.go b/pkg/devspace/deploy/helm/tiller.go similarity index 81% rename from pkg/devspace/clients/helm/tiller.go rename to pkg/devspace/deploy/helm/tiller.go index 00791dd908..98aff2c2b3 100644 --- a/pkg/devspace/clients/helm/tiller.go +++ b/pkg/devspace/deploy/helm/tiller.go @@ -28,7 +28,7 @@ repositories: ` func ensureTiller(kubectlClient *kubernetes.Clientset, config *v1.Config, upgrade bool) error { - tillerNamespace := *config.Services.Tiller.Release.Namespace + tillerNamespace := GetTillerNamespace() tillerOptions := &helminstaller.Options{ Namespace: tillerNamespace, MaxHistory: 10, @@ -38,6 +38,8 @@ func ensureTiller(kubectlClient *kubernetes.Clientset, config *v1.Config, upgrad _, err := kubectlClient.CoreV1().Namespaces().Get(tillerNamespace, metav1.GetOptions{}) if err != nil { + log.Infof("Create namespace %s", tillerNamespace) + // Create tiller namespace _, err = kubectlClient.CoreV1().Namespaces().Create(&k8sv1.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -67,7 +69,7 @@ func ensureTiller(kubectlClient *kubernetes.Clientset, config *v1.Config, upgrad } } - return waitUntilTillerIsStarted(kubectlClient, tillerNamespace) + return waitUntilTillerIsStarted(kubectlClient) } func createTiller(kubectlClient *kubernetes.Clientset, dsConfig *v1.Config, tillerOptions *helminstaller.Options) error { @@ -75,7 +77,7 @@ func createTiller(kubectlClient *kubernetes.Clientset, dsConfig *v1.Config, till defer log.StopWait() // If the service account is already there we do not create it or any roles/rolebindings - _, err := kubectlClient.CoreV1().ServiceAccounts(*dsConfig.Services.Tiller.Release.Namespace).Get(TillerServiceAccountName, metav1.GetOptions{}) + _, err := kubectlClient.CoreV1().ServiceAccounts(GetTillerNamespace()).Get(TillerServiceAccountName, metav1.GetOptions{}) if err != nil { err = createTillerRBAC(kubectlClient, dsConfig) if err != nil { @@ -87,7 +89,7 @@ func createTiller(kubectlClient *kubernetes.Clientset, dsConfig *v1.Config, till return helminstaller.Install(kubectlClient, tillerOptions) } -func waitUntilTillerIsStarted(kubectlClient *kubernetes.Clientset, tillerNamespace string) error { +func waitUntilTillerIsStarted(kubectlClient *kubernetes.Clientset) error { tillerWaitingTime := 2 * 60 * time.Second tillerCheckInterval := 5 * time.Second @@ -95,7 +97,7 @@ func waitUntilTillerIsStarted(kubectlClient *kubernetes.Clientset, tillerNamespa defer log.StopWait() for tillerWaitingTime > 0 { - tillerDeployment, err := kubectlClient.ExtensionsV1beta1().Deployments(tillerNamespace).Get(TillerDeploymentName, metav1.GetOptions{}) + tillerDeployment, err := kubectlClient.ExtensionsV1beta1().Deployments(GetTillerNamespace()).Get(TillerDeploymentName, metav1.GetOptions{}) if err != nil { continue } @@ -142,11 +144,24 @@ func addAppNamespaces(appNamespaces *[]*string, namespaces []*string) { appNamespaces = &newAppNamespaces } +// GetTillerNamespace retrieves the tillernamespace +func GetTillerNamespace() string { + config := configutil.GetConfig() + if config.DevSpace.Release == nil || config.DevSpace.Release.Namespace == nil { + log.Panic("Cannot get tiller namespace when helm is not configured") + } + + if config.Services == nil || config.Services.Tiller == nil || config.Services.Tiller.Release == nil || config.Services.Tiller.Release.Namespace == nil { + return *config.DevSpace.Release.Namespace + } + + return *config.Services.Tiller.Release.Namespace +} + // IsTillerDeployed determines if we could connect to a tiller server -func IsTillerDeployed(kubectlClient *kubernetes.Clientset, tillerConfig *v1.TillerConfig) bool { - tillerNamespace := *tillerConfig.Release.Namespace +func IsTillerDeployed(kubectlClient *kubernetes.Clientset) bool { + tillerNamespace := GetTillerNamespace() deployment, err := kubectlClient.ExtensionsV1beta1().Deployments(tillerNamespace).Get(TillerDeploymentName, metav1.GetOptions{}) - if err != nil { return false } @@ -160,10 +175,9 @@ func IsTillerDeployed(kubectlClient *kubernetes.Clientset, tillerConfig *v1.Till // DeleteTiller clears the tiller server, the service account and role binding func DeleteTiller(kubectlClient *kubernetes.Clientset) error { - config := configutil.GetConfig(false) + config := configutil.GetConfig() - tillerConfig := config.Services.Tiller - tillerNamespace := *tillerConfig.Release.Namespace + tillerNamespace := GetTillerNamespace() errs := make([]error, 0, 1) propagationPolicy := metav1.DeletePropagationForeground @@ -186,8 +200,16 @@ func DeleteTiller(kubectlClient *kubernetes.Clientset) error { errs = append(errs, err) } - roleNamespace := append(*tillerConfig.AppNamespaces, &tillerNamespace) - for _, appNamespace := range roleNamespace { + appNamespaces := []*string{ + config.DevSpace.Release.Namespace, + &tillerNamespace, + } + + if config.Services.InternalRegistry != nil && config.Services.InternalRegistry.Release != nil && config.Services.InternalRegistry.Release.Namespace != nil { + appNamespaces = append(appNamespaces, config.Services.InternalRegistry.Release.Namespace) + } + + for _, appNamespace := range appNamespaces { err = kubectlClient.RbacV1beta1().Roles(*appNamespace).Delete(TillerRoleName, &metav1.DeleteOptions{PropagationPolicy: &propagationPolicy}) if err != nil && strings.HasSuffix(err.Error(), "not found") == false { errs = append(errs, err) diff --git a/pkg/devspace/deploy/kubectl/kubectl.go b/pkg/devspace/deploy/kubectl/kubectl.go new file mode 100644 index 0000000000..2d3abb936d --- /dev/null +++ b/pkg/devspace/deploy/kubectl/kubectl.go @@ -0,0 +1 @@ +package kubectl diff --git a/pkg/devspace/image/build.go b/pkg/devspace/image/build.go new file mode 100644 index 0000000000..3a06b90f93 --- /dev/null +++ b/pkg/devspace/image/build.go @@ -0,0 +1,222 @@ +package image + +import ( + "fmt" + "os" + "path/filepath" + + "k8s.io/client-go/kubernetes" + + "github.com/covexo/devspace/pkg/devspace/builder" + "github.com/covexo/devspace/pkg/devspace/builder/docker" + "github.com/covexo/devspace/pkg/devspace/builder/kaniko" + "github.com/covexo/devspace/pkg/devspace/config/configutil" + "github.com/covexo/devspace/pkg/devspace/config/generated" + "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/devspace/registry" + "github.com/covexo/devspace/pkg/util/log" + "github.com/covexo/devspace/pkg/util/randutil" + "github.com/docker/distribution/reference" + "github.com/docker/docker/api/types" + dockerregistry "github.com/docker/docker/registry" +) + +// Build builds an image with the specified engine +func Build(client *kubernetes.Clientset, generatedConfig *generated.Config, imageName string, imageConf *v1.ImageConfig, forceRebuild bool) (bool, error) { + rebuild := false + config := configutil.GetConfig() + dockerfilePath := "./Dockerfile" + contextPath := "./" + + if imageConf.Build != nil { + if imageConf.Build.DockerfilePath != nil { + dockerfilePath = *imageConf.Build.DockerfilePath + } + + if imageConf.Build.ContextPath != nil { + contextPath = *imageConf.Build.ContextPath + } + } + + dockerfilePath, err := filepath.Abs(dockerfilePath) + if err != nil { + return false, fmt.Errorf("Couldn't determine absolute path for %s", *imageConf.Build.DockerfilePath) + } + + contextPath, err = filepath.Abs(contextPath) + if err != nil { + return false, fmt.Errorf("Couldn't determine absolute path for %s", *imageConf.Build.ContextPath) + } + + if shouldRebuild(generatedConfig, imageConf, dockerfilePath, forceRebuild) { + rebuild = true + imageTag, randErr := randutil.GenerateRandomString(7) + if randErr != nil { + return false, fmt.Errorf("Image building failed: %s", randErr.Error()) + } + + var registryConf *v1.RegistryConfig + var imageBuilder builder.Interface + + engineName := "" + registryURL := "" + imageName := *imageConf.Name + + if imageConf.Registry != nil { + registryConf, err = registry.GetRegistryConfig(imageConf) + if err != nil { + return false, err + } + + if registryConf.URL != nil { + registryURL = *registryConf.URL + } + if registryURL == "hub.docker.com" { + registryURL = "" + } + } else { + registryURL, err = GetRegistryFromImageName(*imageConf.Name) + if err != nil { + return false, err + } + + if len(registryURL) > 0 { + // Crop registry Url from imageName + imageName = imageName[len(registryURL)+1:] + } + + registryConf = &v1.RegistryConfig{ + URL: ®istryURL, + Insecure: configutil.Bool(false), + } + } + + if imageConf.Build != nil && imageConf.Build.Engine != nil && imageConf.Build.Engine.Kaniko != nil { + engineName = "kaniko" + buildNamespace := *config.DevSpace.Release.Namespace + allowInsecurePush := false + + if imageConf.Build.Engine.Kaniko.Namespace != nil { + buildNamespace = *imageConf.Build.Engine.Kaniko.Namespace + } + + if registryConf.Insecure != nil { + allowInsecurePush = *registryConf.Insecure + } + + imageBuilder, err = kaniko.NewBuilder(registryURL, imageName, imageTag, buildNamespace, client, allowInsecurePush) + if err != nil { + log.Fatalf("Error creating kaniko builder: %v", err) + } + } else { + engineName = "docker" + preferMinikube := true + + if imageConf.Build != nil && imageConf.Build.Engine != nil && imageConf.Build.Engine.Docker != nil && imageConf.Build.Engine.Docker.PreferMinikube != nil { + preferMinikube = *imageConf.Build.Engine.Docker.PreferMinikube + } + + imageBuilder, err = docker.NewBuilder(registryURL, imageName, imageTag, preferMinikube) + if err != nil { + log.Fatalf("Error creating docker client: %v", err) + } + } + + log.Infof("Building image '%s' with engine '%s'", imageName, engineName) + + username := "" + password := "" + if registryConf.Auth != nil { + if registryConf.Auth.Username != nil { + username = *registryConf.Auth.Username + } + + if registryConf.Auth.Password != nil { + password = *registryConf.Auth.Password + } + } + + log.StartWait("Authenticating (" + registryURL + ")") + _, err = imageBuilder.Authenticate(username, password, len(username) == 0) + log.StopWait() + + if err != nil { + log.Fatalf("Error during image registry authentication: %v", err) + } + + log.Done("Authentication successful (" + registryURL + ")") + + buildOptions := &types.ImageBuildOptions{} + + if imageConf.Build != nil && imageConf.Build.Options != nil { + if imageConf.Build.Options.BuildArgs != nil { + buildOptions.BuildArgs = *imageConf.Build.Options.BuildArgs + } + if imageConf.Build.Options.Target != nil { + buildOptions.Target = *imageConf.Build.Options.Target + } + if imageConf.Build.Options.Network != nil { + buildOptions.NetworkMode = *imageConf.Build.Options.Network + } + } + + err = imageBuilder.BuildImage(contextPath, dockerfilePath, buildOptions) + if err != nil { + return false, fmt.Errorf("Error during image build: %v", err) + } + + err = imageBuilder.PushImage() + if err != nil { + return false, fmt.Errorf("Error during image push: %v", err) + } + + log.Info("Image pushed to registry (" + registryURL + ")") + + // Update config + generatedConfig.ImageTags[imageName] = imageTag + + log.Done("Done building and pushing image '" + imageName + "'") + } else { + log.Infof("Skip building image '%s'", imageName) + } + + return rebuild, nil +} + +func shouldRebuild(runtimeConfig *generated.Config, imageConf *v1.ImageConfig, dockerfilePath string, forceRebuild bool) bool { + mustRebuild := true + dockerfileInfo, err := os.Stat(dockerfilePath) + + if err != nil { + log.Warnf("Dockerfile %s missing: %v", dockerfilePath, err) + mustRebuild = false + } else { + // When user has not used -b or --build flags + if forceRebuild == false { + // only rebuild Docker image when Dockerfile has changed since latest build + mustRebuild = dockerfileInfo.ModTime().Unix() != runtimeConfig.DockerLatestTimestamps[dockerfilePath] + } + } + + runtimeConfig.DockerLatestTimestamps[dockerfilePath] = dockerfileInfo.ModTime().Unix() + return mustRebuild +} + +// GetRegistryFromImageName retrieves the registry name from an imageName +func GetRegistryFromImageName(imageName string) (string, error) { + ref, err := reference.ParseNormalizedNamed(imageName) + if err != nil { + return "", err + } + + repoInfo, err := dockerregistry.ParseRepositoryInfo(ref) + if err != nil { + return "", err + } + + if repoInfo.Index.Official { + return "", nil + } + + return repoInfo.Index.Name, nil +} diff --git a/pkg/devspace/clients/kubectl/client.go b/pkg/devspace/kubectl/client.go similarity index 97% rename from pkg/devspace/clients/kubectl/client.go rename to pkg/devspace/kubectl/client.go index a7215ec1e2..7d240d6fa6 100644 --- a/pkg/devspace/clients/kubectl/client.go +++ b/pkg/devspace/kubectl/client.go @@ -47,7 +47,7 @@ func NewClient() (*kubernetes.Clientset, error) { //GetClientConfig loads the configuration for kubernetes clients and parses it to *rest.Config func GetClientConfig() (*rest.Config, error) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() if config.Cluster == nil { return nil, errors.New("Couldn't load cluster config, did you run devspace init") } @@ -59,7 +59,7 @@ func GetClientConfig() (*rest.Config, error) { return nil, fmt.Errorf("Couldn't load cloud provider config: %v", err) } - err = cloud.Update(providerConfig, config, false) + err = cloud.Update(providerConfig, config, config.Cluster.APIServer == nil, false) if err != nil { log.Warnf("Couldn't update cloud provider %s information: %v", *config.Cluster.CloudProvider, err) } @@ -70,7 +70,7 @@ func GetClientConfig() (*rest.Config, error) { } } - if (config.Cluster.UseKubeConfig != nil && *config.Cluster.UseKubeConfig) || config.Cluster.APIServer == nil { + if config.Cluster.APIServer == nil { // If we should use a certain kube context use that if config.Cluster.KubeContext != nil && len(*config.Cluster.KubeContext) > 0 { kubeConfig, err := kubeconfig.ReadKubeConfig(clientcmd.RecommendedHomeFile) @@ -124,8 +124,8 @@ func GetClientConfig() (*rest.Config, error) { func IsMinikube() bool { if isMinikubeVar == nil { isMinikube := false - config := configutil.GetConfig(false) - if config.Cluster.UseKubeConfig != nil && *config.Cluster.UseKubeConfig == true { + config := configutil.GetConfig() + if config.Cluster.APIServer == nil { if config.Cluster.KubeContext == nil { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) diff --git a/pkg/devspace/registry/create.go b/pkg/devspace/registry/create.go index 6817021070..6edeafe55c 100644 --- a/pkg/devspace/registry/create.go +++ b/pkg/devspace/registry/create.go @@ -8,16 +8,16 @@ import ( "k8s.io/client-go/kubernetes" - "github.com/covexo/devspace/pkg/devspace/clients/helm" "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/devspace/config/v1" + "github.com/covexo/devspace/pkg/devspace/deploy/helm" "github.com/covexo/yamlq" "github.com/foomo/htpasswd" k8sv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func createRegistry(kubectl *kubernetes.Clientset, helm *helm.HelmClientWrapper, internalRegistry *v1.InternalRegistry, registryConfig *v1.RegistryConfig) error { +func createRegistry(kubectl *kubernetes.Clientset, helm *helm.ClientWrapper, internalRegistry *v1.InternalRegistry, registryConfig *v1.RegistryConfig) error { registryReleaseName := *internalRegistry.Release.Name registryReleaseNamespace := *internalRegistry.Release.Namespace registryReleaseValues := internalRegistry.Release.Values @@ -88,6 +88,7 @@ func createOrUpdateRegistrySecret(kubectl *kubernetes.Clientset, internalRegistr registryReleaseNamespace := *internalRegistry.Release.Namespace registryAuth := registryConfig.Auth + htpasswdSecretName := registryReleaseName + "-docker-registry-secret" htpasswdSecret, err := kubectl.Core().Secrets(registryReleaseNamespace).Get(htpasswdSecretName, metav1.GetOptions{}) if err != nil { diff --git a/pkg/devspace/registry/registry.go b/pkg/devspace/registry/registry.go index b5f24f7c7f..b053ab9acb 100644 --- a/pkg/devspace/registry/registry.go +++ b/pkg/devspace/registry/registry.go @@ -8,13 +8,14 @@ import ( "fmt" "time" + "github.com/covexo/devspace/pkg/devspace/config/generated" "github.com/covexo/devspace/pkg/devspace/config/v1" "github.com/covexo/devspace/pkg/util/log" "k8s.io/client-go/kubernetes" - "github.com/covexo/devspace/pkg/devspace/clients/helm" "github.com/covexo/devspace/pkg/devspace/config/configutil" + "github.com/covexo/devspace/pkg/devspace/deploy/helm" k8sv1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -78,7 +79,7 @@ func GetRegistryAuthSecretName(registryURL string) string { } // InitInternalRegistry deploys and starts a new docker registry if necessary -func InitInternalRegistry(kubectl *kubernetes.Clientset, helm *helm.HelmClientWrapper, internalRegistry *v1.InternalRegistry, registryConfig *v1.RegistryConfig) error { +func InitInternalRegistry(kubectl *kubernetes.Clientset, helm *helm.ClientWrapper, internalRegistry *v1.InternalRegistry, registryConfig *v1.RegistryConfig) error { registryReleaseName := *internalRegistry.Release.Name registryReleaseDeploymentName := registryReleaseName + "-docker-registry" registryReleaseNamespace := *internalRegistry.Release.Namespace @@ -128,34 +129,41 @@ func waitForRegistry(registryNamespace, registryReleaseDeploymentName string, cl } // GetImageURL returns the image (optional with tag) -func GetImageURL(imageConfig *v1.ImageConfig, includingLatestTag bool) string { - registryConfig, registryConfErr := GetRegistryConfig(imageConfig) - - if registryConfErr != nil { - log.Fatal(registryConfErr) - } +func GetImageURL(generatedConfig *generated.Config, imageConfig *v1.ImageConfig, includingLatestTag bool) string { image := *imageConfig.Name - registryURL := *registryConfig.URL - if registryURL != "" && registryURL != "hub.docker.com" { - image = registryURL + "/" + image + if imageConfig.Registry != nil { + registryConfig, registryConfErr := GetRegistryConfig(imageConfig) + if registryConfErr != nil { + log.Fatal(registryConfErr) + } + + registryURL := *registryConfig.URL + if registryURL != "" && registryURL != "hub.docker.com" { + image = registryURL + "/" + image + } } if includingLatestTag { - image = image + ":" + *imageConfig.Tag + if imageConfig.Tag != nil { + image = image + ":" + *imageConfig.Tag + } else { + image = image + ":" + generatedConfig.ImageTags[*imageConfig.Name] + } } + return image } // GetRegistryConfig returns the registry config for an image or an error if the registry is not defined func GetRegistryConfig(imageConfig *v1.ImageConfig) (*v1.RegistryConfig, error) { - config := configutil.GetConfig(false) + config := configutil.GetConfig() registryName := *imageConfig.Registry registryMap := *config.Registries registryConfig, registryFound := registryMap[registryName] - if !registryFound { return nil, errors.New("Unable to find registry: " + registryName) } + return registryConfig, nil } diff --git a/pkg/devspace/sync/downstream.go b/pkg/devspace/sync/downstream.go index 5ab703cfa1..cc64f3aae7 100644 --- a/pkg/devspace/sync/downstream.go +++ b/pkg/devspace/sync/downstream.go @@ -15,7 +15,7 @@ import ( "github.com/juju/errors" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" + "github.com/covexo/devspace/pkg/devspace/kubectl" ) type downstream struct { diff --git a/pkg/devspace/sync/upstream.go b/pkg/devspace/sync/upstream.go index a775aab10e..d6d04b6765 100644 --- a/pkg/devspace/sync/upstream.go +++ b/pkg/devspace/sync/upstream.go @@ -11,7 +11,7 @@ import ( "github.com/juju/errors" - "github.com/covexo/devspace/pkg/devspace/clients/kubectl" + "github.com/covexo/devspace/pkg/devspace/kubectl" "github.com/rjeczalik/notify" )