From 3496590a15aa0f0ca1242514e1cfeb5060667721 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Thu, 8 Nov 2018 15:24:13 +0100 Subject: [PATCH 01/14] Allow deploy without up before --- cmd/deploy.go | 12 ++++++++---- pkg/devspace/cloud/update.go | 10 ++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cmd/deploy.go b/cmd/deploy.go index 4571859290..83f3237db8 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -26,6 +26,7 @@ type DeployCmdFlags struct { DockerTarget string CloudTarget string SwitchContext bool + SkipBuild bool } func init() { @@ -58,6 +59,7 @@ devspace deploy --cloud-target=production cobraCmd.Flags().StringVar(&cmd.flags.DockerTarget, "docker-target", "", "The docker target to use for building") cobraCmd.Flags().StringVar(&cmd.flags.CloudTarget, "cloud-target", "", "When using a cloud provider, the target to use") cobraCmd.Flags().BoolVar(&cmd.flags.SwitchContext, "switch-context", false, "Switches the kube context to the deploy context") + cobraCmd.Flags().BoolVar(&cmd.flags.SkipBuild, "skip-build", false, "Skips the image build & push step") rootCmd.AddCommand(cobraCmd) } @@ -100,10 +102,12 @@ func (cmd *DeployCmd) Run(cobraCmd *cobra.Command, args []string) { log.Fatalf("Error loading generated.yaml: %v", err) } - // Force image build - _, err = image.BuildAll(client, generatedConfig, true, log.GetInstance()) - if err != nil { - log.Fatal(err) + if cmd.flags.SkipBuild == false { + // Force image build + _, err = image.BuildAll(client, generatedConfig, true, log.GetInstance()) + if err != nil { + log.Fatal(err) + } } // Force deployment of all defined deployments diff --git a/pkg/devspace/cloud/update.go b/pkg/devspace/cloud/update.go index efde60846d..41abee34c9 100644 --- a/pkg/devspace/cloud/update.go +++ b/pkg/devspace/cloud/update.go @@ -52,9 +52,6 @@ func Update(providerConfig ProviderConfig, options *UpdateOptions, log log.Logge if dsConfig.Cluster.Namespace != nil { devSpaceID = *dsConfig.Cluster.Namespace } - if devSpaceID == "" && target != "" { - return fmt.Errorf("Cannot deploy to target %s without a devspace. You need to run `devspace up` beforehand", target) - } domain, namespace, cluster, authInfo, err := CheckAuth(provider, devSpaceID, target, log) if err != nil { @@ -68,7 +65,7 @@ func Update(providerConfig ProviderConfig, options *UpdateOptions, log log.Logge DevSpaceURL = domain - err = updateDevSpaceConfig(target, namespace, cluster, authInfo, options) + err = updateDevSpaceConfig(devSpaceID, target, namespace, cluster, authInfo, options) if err != nil { return err } @@ -76,7 +73,7 @@ func Update(providerConfig ProviderConfig, options *UpdateOptions, log log.Logge return nil } -func updateDevSpaceConfig(target, namespace string, cluster *api.Cluster, authInfo *api.AuthInfo, options *UpdateOptions) error { +func updateDevSpaceConfig(devSpaceID, target, namespace string, cluster *api.Cluster, authInfo *api.AuthInfo, options *UpdateOptions) error { dsConfig := configutil.GetConfig() overwriteConfig := configutil.GetOverwriteConfig() saveConfig := false @@ -143,7 +140,8 @@ func updateDevSpaceConfig(target, namespace string, cluster *api.Cluster, authIn } } - if saveConfig && target == "" { + // Either save when config has changed && devspace up or devspace deploy (with no up before) + if saveConfig && (target == "" || (target != "" && devSpaceID == "")) { err := configutil.SaveConfig() if err != nil { return err From 2a64081cf45c7bae727fd3a8eafc9e804284a6cd Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Thu, 8 Nov 2018 15:59:43 +0100 Subject: [PATCH 02/14] Implementation of devspace get --- cmd/deploy.go | 9 +++- cmd/get.go | 66 +++++++++++++++++++++++++++ pkg/devspace/builder/docker/config.go | 4 +- 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 cmd/get.go diff --git a/cmd/deploy.go b/cmd/deploy.go index 83f3237db8..83dc945bed 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -49,7 +49,7 @@ devspace deploy --kube-context=deploy-context devspace deploy --config=.devspace/deploy.yaml devspace deploy --cloud-target=production #######################################################`, - Args: cobra.NoArgs, + Args: cobra.RangeArgs(0, 2), Run: cmd.Run, } @@ -66,6 +66,13 @@ devspace deploy --cloud-target=production // Run executes the down command logic func (cmd *DeployCmd) Run(cobraCmd *cobra.Command, args []string) { + if len(args) > 0 { + getCmd := &GetCmd{ + flags: &GetCmdFlags{}, + } + getCmd.Run(nil, args) + } + cloud.UseDeployTarget = true log.StartFileLogging() diff --git a/cmd/get.go b/cmd/get.go new file mode 100644 index 0000000000..3ed2123056 --- /dev/null +++ b/cmd/get.go @@ -0,0 +1,66 @@ +package cmd + +import ( + "os" + + "github.com/covexo/devspace/pkg/util/log" + "github.com/spf13/cobra" + git "gopkg.in/src-d/go-git.v4" +) + +// GetCmd is a struct that defines a command call for "get" +type GetCmd struct { + flags *GetCmdFlags +} + +// GetCmdFlags are the flags available for the get-command +type GetCmdFlags struct { +} + +func init() { + cmd := &EnterCmd{ + flags: &EnterCmdFlags{}, + } + + cobraCmd := &cobra.Command{ + Use: "get", + Short: "Get a devspace project", + Long: ` +####################################################### +################### devspace get ###################### +####################################################### +Clone a devspace project. + +Example: + +devspace get https://github.com/covexo/devspace-quickstart-nodejs +#######################################################`, + Args: cobra.RangeArgs(1, 2), + Run: cmd.Run, + } + + rootCmd.AddCommand(cobraCmd) +} + +// Run executes the command logic +func (cmd *GetCmd) Run(cobraCmd *cobra.Command, args []string) { + directoryName := "devspace" + if len(args) == 2 { + directoryName = args[1] + } + + _, err := git.PlainClone(directoryName, false, &git.CloneOptions{ + URL: args[0], + Progress: os.Stdout, + }) + if err != nil { + log.Fatal(err) + } + + err = os.Chdir(directoryName) + if err != nil { + log.Fatal(err) + } + + log.Donef("Successfully checked out %s into %s", args[0], directoryName) +} diff --git a/pkg/devspace/builder/docker/config.go b/pkg/devspace/builder/docker/config.go index 6b3ce7aba9..cebc856523 100644 --- a/pkg/devspace/builder/docker/config.go +++ b/pkg/devspace/builder/docker/config.go @@ -15,13 +15,11 @@ const dockerFileFolder = ".docker" var configDir = os.Getenv("DOCKER_CONFIG") -func init() { +func loadDockerConfig() (*configfile.ConfigFile, error) { if configDir == "" { configDir = filepath.Join(homedir.Get(), dockerFileFolder) } -} -func loadDockerConfig() (*configfile.ConfigFile, error) { return config.Load(configDir) } From 37b1f5e063d2d9aaa3d90de670f772b65f9812c4 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Thu, 8 Nov 2018 16:41:10 +0100 Subject: [PATCH 03/14] Skip config save when using cloud --- cmd/init.go | 1 + pkg/devspace/cloud/update.go | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/init.go b/cmd/init.go index 898139b35d..6dde277ff2 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -296,6 +296,7 @@ func (cmd *InitCmd) loginToCloudProvider(providerConfig cloud.ProviderConfig, cl err := cloud.Update(providerConfig, &cloud.UpdateOptions{ UseKubeContext: addToContext, SwitchKubeContext: true, + SkipSaveConfig: true, }, log.GetInstance()) if err != nil { log.Fatalf("Couldn't authenticate to %s: %v", cloudProviderSelected, err) diff --git a/pkg/devspace/cloud/update.go b/pkg/devspace/cloud/update.go index 41abee34c9..fceadce594 100644 --- a/pkg/devspace/cloud/update.go +++ b/pkg/devspace/cloud/update.go @@ -23,6 +23,7 @@ var UseDeployTarget = false type UpdateOptions struct { UseKubeContext bool SwitchKubeContext bool + SkipSaveConfig bool } // Update updates the cloud provider information if necessary @@ -141,7 +142,7 @@ func updateDevSpaceConfig(devSpaceID, target, namespace string, cluster *api.Clu } // Either save when config has changed && devspace up or devspace deploy (with no up before) - if saveConfig && (target == "" || (target != "" && devSpaceID == "")) { + if options.SkipSaveConfig == false && saveConfig && (target == "" || (target != "" && devSpaceID == "")) { err := configutil.SaveConfig() if err != nil { return err From 4061ead8df69c30b76f147c04e1ef479448a8dbe Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Thu, 8 Nov 2018 16:41:47 +0100 Subject: [PATCH 04/14] Refactor --- cmd/init.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/init.go b/cmd/init.go index 6dde277ff2..0deb946b5d 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -280,6 +280,7 @@ func (cmd *InitCmd) useCloudProvider() bool { return false } + func (cmd *InitCmd) loginToCloudProvider(providerConfig cloud.ProviderConfig, cloudProviderSelected string) { config := configutil.GetConfig() addToContext := cmd.flags.skipQuestions || cmd.flags.addDevSpaceCloudToLocalKubernetes From 5b7140093b047d42b76058133e7a41a85378ea61 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Thu, 8 Nov 2018 16:55:25 +0100 Subject: [PATCH 05/14] Remove devspace get --- cmd/deploy.go | 30 ++++++++++++++++++++--- cmd/get.go | 66 --------------------------------------------------- 2 files changed, 27 insertions(+), 69 deletions(-) delete mode 100644 cmd/get.go diff --git a/cmd/deploy.go b/cmd/deploy.go index 83dc945bed..d188c775d3 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -1,6 +1,10 @@ package cmd import ( + "os" + + "gopkg.in/src-d/go-git.v4/plumbing" + "github.com/covexo/devspace/pkg/devspace/cloud" "github.com/covexo/devspace/pkg/devspace/config/configutil" "github.com/covexo/devspace/pkg/devspace/config/generated" @@ -11,6 +15,7 @@ import ( "github.com/covexo/devspace/pkg/devspace/registry" "github.com/covexo/devspace/pkg/util/log" "github.com/spf13/cobra" + git "gopkg.in/src-d/go-git.v4" ) // DeployCmd holds the required data for the down cmd @@ -27,6 +32,7 @@ type DeployCmdFlags struct { CloudTarget string SwitchContext bool SkipBuild bool + GitBranch string } func init() { @@ -48,6 +54,7 @@ devspace deploy --namespace=deploy --docker-target=production devspace deploy --kube-context=deploy-context devspace deploy --config=.devspace/deploy.yaml devspace deploy --cloud-target=production +devspace deploy https://github.com/covexo/devspace --branch test #######################################################`, Args: cobra.RangeArgs(0, 2), Run: cmd.Run, @@ -60,6 +67,7 @@ devspace deploy --cloud-target=production cobraCmd.Flags().StringVar(&cmd.flags.CloudTarget, "cloud-target", "", "When using a cloud provider, the target to use") cobraCmd.Flags().BoolVar(&cmd.flags.SwitchContext, "switch-context", false, "Switches the kube context to the deploy context") cobraCmd.Flags().BoolVar(&cmd.flags.SkipBuild, "skip-build", false, "Skips the image build & push step") + cobraCmd.Flags().StringVar(&cmd.flags.GitBranch, "--branch", "master", "The git branch to checkout") rootCmd.AddCommand(cobraCmd) } @@ -67,10 +75,26 @@ devspace deploy --cloud-target=production // Run executes the down command logic func (cmd *DeployCmd) Run(cobraCmd *cobra.Command, args []string) { if len(args) > 0 { - getCmd := &GetCmd{ - flags: &GetCmdFlags{}, + directoryName := "devspace" + if len(args) == 2 { + directoryName = args[1] + } + + _, err := git.PlainClone(directoryName, false, &git.CloneOptions{ + URL: args[0], + Progress: os.Stdout, + ReferenceName: plumbing.ReferenceName(cmd.flags.GitBranch), + }) + if err != nil { + log.Fatal(err) } - getCmd.Run(nil, args) + + err = os.Chdir(directoryName) + if err != nil { + log.Fatal(err) + } + + log.Donef("Successfully checked out %s into %s", args[0], directoryName) } cloud.UseDeployTarget = true diff --git a/cmd/get.go b/cmd/get.go deleted file mode 100644 index 3ed2123056..0000000000 --- a/cmd/get.go +++ /dev/null @@ -1,66 +0,0 @@ -package cmd - -import ( - "os" - - "github.com/covexo/devspace/pkg/util/log" - "github.com/spf13/cobra" - git "gopkg.in/src-d/go-git.v4" -) - -// GetCmd is a struct that defines a command call for "get" -type GetCmd struct { - flags *GetCmdFlags -} - -// GetCmdFlags are the flags available for the get-command -type GetCmdFlags struct { -} - -func init() { - cmd := &EnterCmd{ - flags: &EnterCmdFlags{}, - } - - cobraCmd := &cobra.Command{ - Use: "get", - Short: "Get a devspace project", - Long: ` -####################################################### -################### devspace get ###################### -####################################################### -Clone a devspace project. - -Example: - -devspace get https://github.com/covexo/devspace-quickstart-nodejs -#######################################################`, - Args: cobra.RangeArgs(1, 2), - Run: cmd.Run, - } - - rootCmd.AddCommand(cobraCmd) -} - -// Run executes the command logic -func (cmd *GetCmd) Run(cobraCmd *cobra.Command, args []string) { - directoryName := "devspace" - if len(args) == 2 { - directoryName = args[1] - } - - _, err := git.PlainClone(directoryName, false, &git.CloneOptions{ - URL: args[0], - Progress: os.Stdout, - }) - if err != nil { - log.Fatal(err) - } - - err = os.Chdir(directoryName) - if err != nil { - log.Fatal(err) - } - - log.Donef("Successfully checked out %s into %s", args[0], directoryName) -} From a9565aa757cd1b15eff91521eaeab88b36aa20fb Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Thu, 8 Nov 2018 16:55:50 +0100 Subject: [PATCH 06/14] Fix branch flag --- cmd/deploy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/deploy.go b/cmd/deploy.go index d188c775d3..e6462f7aec 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -67,7 +67,7 @@ devspace deploy https://github.com/covexo/devspace --branch test cobraCmd.Flags().StringVar(&cmd.flags.CloudTarget, "cloud-target", "", "When using a cloud provider, the target to use") cobraCmd.Flags().BoolVar(&cmd.flags.SwitchContext, "switch-context", false, "Switches the kube context to the deploy context") cobraCmd.Flags().BoolVar(&cmd.flags.SkipBuild, "skip-build", false, "Skips the image build & push step") - cobraCmd.Flags().StringVar(&cmd.flags.GitBranch, "--branch", "master", "The git branch to checkout") + cobraCmd.Flags().StringVar(&cmd.flags.GitBranch, "branch", "master", "The git branch to checkout") rootCmd.AddCommand(cobraCmd) } From c22c74256f23473c184c107cb229ba388c057661 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Thu, 8 Nov 2018 18:16:40 +0100 Subject: [PATCH 07/14] Docker login method that saves docker credentials in credentials store --- pkg/devspace/builder/docker/auth.go | 7 +- pkg/devspace/builder/docker/docker.go | 103 ++++++++++++++++---------- 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/pkg/devspace/builder/docker/auth.go b/pkg/devspace/builder/docker/auth.go index d7c1be3b38..225052cbfc 100644 --- a/pkg/devspace/builder/docker/auth.go +++ b/pkg/devspace/builder/docker/auth.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "encoding/json" - "fmt" "github.com/covexo/devspace/pkg/util/log" "github.com/docker/docker/api/types" @@ -12,7 +11,7 @@ import ( "github.com/docker/docker/registry" ) -func getOfficialServer(ctx context.Context, client client.CommonAPIClient) string { +func getOfficialServer(ctx context.Context, client client.CommonAPIClient, log log.Logger) string { // The daemon `/info` endpoint informs us of the default registry being // used. This is essential in cross-platforms environment, where for // example a Linux client might be interacting with a Windows daemon, hence @@ -20,9 +19,9 @@ func getOfficialServer(ctx context.Context, client client.CommonAPIClient) strin serverAddress := registry.IndexServer if info, err := client.Info(ctx); err != nil { // Only report the warning if we're in debug mode to prevent nagging during engine initialization workflows - fmt.Fprintf(log.GetInstance(), "Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s\n", err, serverAddress) + log.Warnf("Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s", err, serverAddress) } else if info.IndexServerAddress == "" { - fmt.Fprintf(log.GetInstance(), "Warning: Empty registry endpoint from daemon. Using system default: %s\n", serverAddress) + log.Warnf("Warning: Empty registry endpoint from daemon. Using system default: %s", serverAddress) } else { serverAddress = info.IndexServerAddress } diff --git a/pkg/devspace/builder/docker/docker.go b/pkg/devspace/builder/docker/docker.go index 75d852eff2..54b10987f4 100644 --- a/pkg/devspace/builder/docker/docker.go +++ b/pkg/devspace/builder/docker/docker.go @@ -1,10 +1,12 @@ package docker import ( + "fmt" "strings" "context" + "github.com/covexo/devspace/pkg/util/log" "github.com/docker/distribution/reference" "github.com/docker/docker/pkg/term" "github.com/docker/docker/registry" @@ -149,47 +151,11 @@ func (b *Builder) BuildImage(contextPath, dockerfilePath string, options *types. // Authenticate authenticates the client with a remote registry func (b *Builder) Authenticate(user, password string, checkCredentialsStore bool) (*types.AuthConfig, error) { - ctx := context.Background() - authServer := getOfficialServer(ctx, b.client) - serverAddress := b.RegistryURL - - if serverAddress == "" { - serverAddress = authServer - } else { - ref, err := reference.ParseNormalizedNamed(b.imageURL) - if err != nil { - return nil, err - } - - repoInfo, err := registry.ParseRepositoryInfo(ref) - if err != nil { - return nil, err - } - - if repoInfo.Index.Official { - serverAddress = authServer - } - } - - authConfig, err := getDefaultAuthConfig(b.client, checkCredentialsStore, serverAddress, serverAddress == authServer) - - if err != nil || authConfig.Username == "" || authConfig.Password == "" { - authConfig.Username = strings.TrimSpace(user) - authConfig.Password = strings.TrimSpace(password) - } - - response, err := b.client.RegistryLogin(ctx, *authConfig) + authConfig, err := b.Login(user, password, checkCredentialsStore, false) if err != nil { return nil, err } - if response.IdentityToken != "" { - authConfig.Password = "" - authConfig.IdentityToken = response.IdentityToken - } - - b.authConfig = authConfig - // Cache authConfig for GetAuthConfig authConfigs[b.RegistryURL] = authConfig @@ -232,7 +198,6 @@ func GetAuthConfig(registryURL string) (*types.AuthConfig, error) { } authConfig, authConfigExists := authConfigs[registryURL] - if !authConfigExists { dockerBuilder, err := NewBuilder(registryURL, "", "", false) if err != nil { @@ -244,5 +209,67 @@ func GetAuthConfig(registryURL string) (*types.AuthConfig, error) { return nil, err } } + return authConfig, nil } + +// Login logs the user into docker +func (b *Builder) Login(user, password string, checkCredentialsStore, saveAuthConfig bool) (*types.AuthConfig, error) { + ctx := context.Background() + authServer := getOfficialServer(ctx, b.client, log.GetInstance()) + serverAddress := b.RegistryURL + + if serverAddress == "" { + serverAddress = authServer + } else { + ref, err := reference.ParseNormalizedNamed(b.imageURL) + if err != nil { + return nil, err + } + + repoInfo, err := registry.ParseRepositoryInfo(ref) + if err != nil { + return nil, err + } + + if repoInfo.Index.Official { + serverAddress = authServer + } + } + + authConfig, err := getDefaultAuthConfig(b.client, checkCredentialsStore, serverAddress, serverAddress == authServer) + if err != nil || authConfig.Username == "" || authConfig.Password == "" { + authConfig.Username = strings.TrimSpace(user) + authConfig.Password = strings.TrimSpace(password) + } + + response, err := b.client.RegistryLogin(ctx, *authConfig) + if err != nil { + return nil, err + } + + if response.IdentityToken != "" { + authConfig.Password = "" + authConfig.IdentityToken = response.IdentityToken + } + + if saveAuthConfig { + configfile, err := loadDockerConfig() + if err != nil { + return nil, err + } + + err = configfile.GetCredentialsStore(serverAddress).Store(*authConfig) + if err != nil { + return nil, fmt.Errorf("Error saving auth info in credentials store: %v", err) + } + + err = configfile.Save() + if err != nil { + return nil, fmt.Errorf("Error saving docker config: %v", err) + } + } + + b.authConfig = authConfig + return b.authConfig, nil +} From a08571df096fb959752d71c65e2a0804a72815cc Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Thu, 8 Nov 2018 18:31:53 +0100 Subject: [PATCH 08/14] Ask for dockerhub credentials if docker is not installed --- pkg/devspace/builder/docker/auth.go | 4 ++-- pkg/devspace/configure/registry.go | 29 ++++++++++++++++++++++++++++- pkg/util/stdinutil/stdin.go | 18 +++++++++++++++++- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/pkg/devspace/builder/docker/auth.go b/pkg/devspace/builder/docker/auth.go index 225052cbfc..d22c6b528a 100644 --- a/pkg/devspace/builder/docker/auth.go +++ b/pkg/devspace/builder/docker/auth.go @@ -19,9 +19,9 @@ func getOfficialServer(ctx context.Context, client client.CommonAPIClient, log l serverAddress := registry.IndexServer if info, err := client.Info(ctx); err != nil { // Only report the warning if we're in debug mode to prevent nagging during engine initialization workflows - log.Warnf("Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s", err, serverAddress) + // log.Warnf("Warning: failed to get default registry endpoint from daemon (%v). Using system default: %s", err, serverAddress) } else if info.IndexServerAddress == "" { - log.Warnf("Warning: Empty registry endpoint from daemon. Using system default: %s", serverAddress) + // log.Warnf("Warning: Empty registry endpoint from daemon. Using system default: %s", serverAddress) } else { serverAddress = info.IndexServerAddress } diff --git a/pkg/devspace/configure/registry.go b/pkg/devspace/configure/registry.go index ab6d71c7f8..b23bc274cb 100644 --- a/pkg/devspace/configure/registry.go +++ b/pkg/devspace/configure/registry.go @@ -43,7 +43,34 @@ func Image(dockerUsername string, skipQuestions bool, registryURL, defaultImageN dockerUsername = dockerAuthConfig.Username } } else if dockerUsername == "" { - return fmt.Errorf("Make sure you login to docker hub with: docker login") + log.Warn("No docker credentials were found in the credentials store") + log.Warn("Please make sure you have a https://hub.docker.com account") + + for { + dockerUsername = *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + Question: "What is your docker hub username?", + DefaultValue: "", + ValidationRegexPattern: "^.*$", + }) + + dockerPassword := *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + Question: "What is your docker hub password?", + DefaultValue: "", + ValidationRegexPattern: "^.*$", + }) + + builder, err := docker.NewBuilder("", "", "", false) + if err != nil { + return err + } + + _, err = builder.Login(dockerUsername, dockerPassword, false, true) + if err != nil { + log.Warn(err) + } + + break + } } googleRegistryRegex := regexp.MustCompile("^(.+\\.)?gcr.io$") diff --git a/pkg/util/stdinutil/stdin.go b/pkg/util/stdinutil/stdin.go index 5dabeabdaa..65cb52e9d1 100644 --- a/pkg/util/stdinutil/stdin.go +++ b/pkg/util/stdinutil/stdin.go @@ -19,6 +19,7 @@ type GetFromStdinParams struct { DefaultValue string ValidationRegexPattern string InputTerminationString string + IsPassword bool } var defaultParams = &GetFromStdinParams{ @@ -56,7 +57,22 @@ func GetFromStdin(params *GetFromStdinParams) *string { for { fmt.Print("> ") - nextLine, _ := reader.ReadString('\n') + + nextLine := "" + if params.IsPassword { + for { + nextChar, _ := reader.ReadByte() + if nextChar == '\n' { + break + } + + fmt.Print("\r> ") + nextLine += string(nextChar) + } + } else { + nextLine, _ = reader.ReadString('\n') + } + nextLine = strings.Trim(nextLine, "\r\n ") if strings.Compare(params.InputTerminationString, "\n") == 0 { From 9cf8c53e069644a00641f29fd9f1c7cda9c98e23 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Thu, 8 Nov 2018 18:32:37 +0100 Subject: [PATCH 09/14] Mark docker password as password field --- pkg/devspace/configure/registry.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/devspace/configure/registry.go b/pkg/devspace/configure/registry.go index b23bc274cb..217005dccf 100644 --- a/pkg/devspace/configure/registry.go +++ b/pkg/devspace/configure/registry.go @@ -57,6 +57,7 @@ func Image(dockerUsername string, skipQuestions bool, registryURL, defaultImageN Question: "What is your docker hub password?", DefaultValue: "", ValidationRegexPattern: "^.*$", + IsPassword: true, }) builder, err := docker.NewBuilder("", "", "", false) From 4a3bc7c2c5fbfa0977a46bb7e295506673e9f2eb Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Thu, 8 Nov 2018 18:35:20 +0100 Subject: [PATCH 10/14] Continue asking for correct credentials after login fail --- pkg/devspace/configure/registry.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/devspace/configure/registry.go b/pkg/devspace/configure/registry.go index 217005dccf..b48826d6fe 100644 --- a/pkg/devspace/configure/registry.go +++ b/pkg/devspace/configure/registry.go @@ -68,6 +68,7 @@ func Image(dockerUsername string, skipQuestions bool, registryURL, defaultImageN _, err = builder.Login(dockerUsername, dockerPassword, false, true) if err != nil { log.Warn(err) + continue } break From 0d665a4510e41ded3ce7c38971edb5fd43cc2e16 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Fri, 9 Nov 2018 16:14:35 +0100 Subject: [PATCH 11/14] Fix issue with password question --- pkg/util/stdinutil/stdin.go | 45 +++++++++++++------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/pkg/util/stdinutil/stdin.go b/pkg/util/stdinutil/stdin.go index 65cb52e9d1..3e73101543 100644 --- a/pkg/util/stdinutil/stdin.go +++ b/pkg/util/stdinutil/stdin.go @@ -11,6 +11,8 @@ import ( "github.com/daviddengcn/go-colortext" "github.com/covexo/devspace/pkg/util/paramutil" + "github.com/docker/cli/cli/command" + "github.com/docker/docker/pkg/term" ) //GetFromStdinParams defines a question and its answerpatterns @@ -27,8 +29,6 @@ var defaultParams = &GetFromStdinParams{ InputTerminationString: "\n", } -var reader *bufio.Reader - const changeQuestion = "Would you like to change it? (yes, no/ENTER))" //GetFromStdin asks the user a question and returns the answer @@ -36,14 +36,6 @@ func GetFromStdin(params *GetFromStdinParams) *string { paramutil.SetDefaults(params, defaultParams) validationRegexp, _ := regexp.Compile(params.ValidationRegexPattern) - - if reader == nil { - reader = bufio.NewReader(os.Stdin) - - defer func() { - reader = nil - }() - } input := "" for { @@ -56,25 +48,26 @@ func GetFromStdin(params *GetFromStdinParams) *string { fmt.Print("\n") for { + inStreamFD := command.NewInStream(os.Stdin).FD() + oldState, err := term.SaveState(inStreamFD) + if err != nil { + log.Fatal(err) + } + fmt.Print("> ") - nextLine := "" if params.IsPassword { - for { - nextChar, _ := reader.ReadByte() - if nextChar == '\n' { - break - } - - fmt.Print("\r> ") - nextLine += string(nextChar) - } - } else { - nextLine, _ = reader.ReadString('\n') + term.DisableEcho(inStreamFD, oldState) } + reader := bufio.NewReader(os.Stdin) + nextLine, _ := reader.ReadString('\n') nextLine = strings.Trim(nextLine, "\r\n ") + if params.IsPassword { + term.RestoreTerminal(inStreamFD, oldState) + } + if strings.Compare(params.InputTerminationString, "\n") == 0 { // Assign the input value to input var input = nextLine @@ -107,14 +100,6 @@ func GetFromStdin(params *GetFromStdinParams) *string { func AskChangeQuestion(params *GetFromStdinParams) *string { paramutil.SetDefaults(params, defaultParams) - if reader == nil { - reader = bufio.NewReader(os.Stdin) - - defer func() { - reader = nil - }() - } - shouldValueChangeQuestion := GetFromStdinParams{ Question: params.Question + "\nThis is the current value:\n#################\n" + strings.TrimRight(params.DefaultValue, "\r\n") + "\n#################\n" + changeQuestion, DefaultValue: "no", From 6689212720d10d4df0b3444ad56bb825d54c4353 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Fri, 9 Nov 2018 17:25:10 +0100 Subject: [PATCH 12/14] Limit find command download bandwidth (#377) --- pkg/devspace/sync/downstream.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/devspace/sync/downstream.go b/pkg/devspace/sync/downstream.go index 652052b3c7..c54caf3e2f 100644 --- a/pkg/devspace/sync/downstream.go +++ b/pkg/devspace/sync/downstream.go @@ -165,8 +165,13 @@ func (d *downstream) collectChanges(removeFiles map[string]*fileInformation) ([] overlap := "" done := false + var downloadReader io.Reader = d.stdoutPipe + if d.config.DownstreamLimit > 0 { + downloadReader = ratelimit.Reader(d.stdoutPipe, ratelimit.NewBucketWithRate(float64(d.config.DownstreamLimit), d.config.DownstreamLimit)) + } + for done == false { - n, err := d.stdoutPipe.Read(buf[:cap(buf)]) + n, err := downloadReader.Read(buf[:cap(buf)]) buf = buf[:n] if n == 0 { From 84fdb027fb9a7e497bae50b103bad8882c4fa122 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Fri, 9 Nov 2018 17:51:48 +0100 Subject: [PATCH 13/14] Fix stdin test --- pkg/util/stdinutil/stdin.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/pkg/util/stdinutil/stdin.go b/pkg/util/stdinutil/stdin.go index 3e73101543..dee8159b90 100644 --- a/pkg/util/stdinutil/stdin.go +++ b/pkg/util/stdinutil/stdin.go @@ -48,26 +48,27 @@ func GetFromStdin(params *GetFromStdinParams) *string { fmt.Print("\n") for { - inStreamFD := command.NewInStream(os.Stdin).FD() - oldState, err := term.SaveState(inStreamFD) - if err != nil { - log.Fatal(err) - } - fmt.Print("> ") - if params.IsPassword { - term.DisableEcho(inStreamFD, oldState) - } - reader := bufio.NewReader(os.Stdin) - nextLine, _ := reader.ReadString('\n') - nextLine = strings.Trim(nextLine, "\r\n ") + nextLine := "" if params.IsPassword { + inStreamFD := command.NewInStream(os.Stdin).FD() + oldState, err := term.SaveState(inStreamFD) + if err != nil { + log.Fatal(err) + } + + term.DisableEcho(inStreamFD, oldState) + nextLine, _ = reader.ReadString('\n') term.RestoreTerminal(inStreamFD, oldState) + } else { + nextLine, _ = reader.ReadString('\n') } + nextLine = strings.Trim(nextLine, "\r\n ") + if strings.Compare(params.InputTerminationString, "\n") == 0 { // Assign the input value to input var input = nextLine From 6071e98efbf147f1b795228d81282cdc3fa186a6 Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Fri, 9 Nov 2018 18:49:54 +0100 Subject: [PATCH 14/14] Remove stdin test --- main.go | 16 ++-- pkg/util/stdinutil/stdin.go | 27 ------- pkg/util/stdinutil/stdin_test.go | 125 ------------------------------- 3 files changed, 11 insertions(+), 157 deletions(-) delete mode 100644 pkg/util/stdinutil/stdin_test.go diff --git a/main.go b/main.go index a4baf6cdbf..0067b066a6 100644 --- a/main.go +++ b/main.go @@ -1,18 +1,24 @@ package main import ( - "os" + "fmt" - "github.com/covexo/devspace/cmd" - "github.com/covexo/devspace/pkg/devspace/upgrade" + "github.com/covexo/devspace/pkg/util/stdinutil" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" ) var version string func main() { - upgrade.SetVersion(version) + /*upgrade.SetVersion(version) cmd.Execute() - os.Exit(0) + os.Exit(0)*/ + useDevSpaceCloud := *stdinutil.GetFromStdin(&stdinutil.GetFromStdinParams{ + Question: "Do you want to use the DevSpace Cloud? (free ready-to-use Kubernetes) (yes | no)", + DefaultValue: "yes", + ValidationRegexPattern: "^(yes)|(no)$", + }) + + fmt.Println(useDevSpaceCloud) } diff --git a/pkg/util/stdinutil/stdin.go b/pkg/util/stdinutil/stdin.go index dee8159b90..ee6cecebfa 100644 --- a/pkg/util/stdinutil/stdin.go +++ b/pkg/util/stdinutil/stdin.go @@ -96,30 +96,3 @@ func GetFromStdin(params *GetFromStdinParams) *string { return &input } - -//AskChangeQuestion asks two questions. Do you want to change this value? If yes, what's the new value? -func AskChangeQuestion(params *GetFromStdinParams) *string { - paramutil.SetDefaults(params, defaultParams) - - shouldValueChangeQuestion := GetFromStdinParams{ - Question: params.Question + "\nThis is the current value:\n#################\n" + strings.TrimRight(params.DefaultValue, "\r\n") + "\n#################\n" + changeQuestion, - DefaultValue: "no", - ValidationRegexPattern: "yes|no", - } - - shouldChangeAnswer := GetFromStdin(&shouldValueChangeQuestion) - - if *shouldChangeAnswer == "no" { - return ¶ms.DefaultValue - } - - newValueQuestion := GetFromStdinParams{ - Question: "Please enter the new value:", - DefaultValue: params.DefaultValue, - ValidationRegexPattern: params.ValidationRegexPattern, - InputTerminationString: params.InputTerminationString, - } - - newValue := GetFromStdin(&newValueQuestion) - return newValue -} diff --git a/pkg/util/stdinutil/stdin_test.go b/pkg/util/stdinutil/stdin_test.go deleted file mode 100644 index efa86e0db5..0000000000 --- a/pkg/util/stdinutil/stdin_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package stdinutil - -import ( - "io/ioutil" - "os" - "testing" - - "github.com/juju/errors" -) - -func TestGetFromStdin_NoChangeQuestion_Default(t *testing.T) { - params := GetFromStdinParams{ - Question: "Is this a test?", - DefaultValue: "Yes", - ValidationRegexPattern: "No|Yes", - } - - err := mockStdin("invalid\ninvalid\n\n") - if err != nil { - t.Error(errors.ErrorStack(err)) - } - defer cleanUpMockedStdin() - - answer := *GetFromStdin(¶ms) - - if answer != params.DefaultValue { - t.Error("Wrong Answer.\nExpected default answer: " + params.DefaultValue + "\nBut Got: " + answer) - } -} - -func TestGetFromStdin_NoChangeQuestion_NonDefault(t *testing.T) { - - params := GetFromStdinParams{ - Question: "Is this a test?", - DefaultValue: "No", - ValidationRegexPattern: "No|Yes", - } - - err := mockStdin("invalid\nYes\n") - if err != nil { - t.Error(errors.ErrorStack(err)) - } - defer cleanUpMockedStdin() - - answer := *GetFromStdin(¶ms) - - if answer != "Yes" { - t.Error("Wrong Answer.\nExpected: Yes\nBut Got: " + answer) - } -} - -func TestGetFromStdin_ChangeQuestion_DontChange(t *testing.T) { - - params := GetFromStdinParams{ - Question: "Hello?", - DefaultValue: "World", - ValidationRegexPattern: "World|Universe", - InputTerminationString: " ", - } - - err := mockStdin("invalid\nno\n") - if err != nil { - t.Error(errors.ErrorStack(err)) - } - defer cleanUpMockedStdin() - - answer := *AskChangeQuestion(¶ms) - - if answer != "World" { - t.Error("Wrong Answer.\nExpected default: World\nBut Got: " + answer) - } -} - -func TestGetFromStdin_ChangeQuestion_DoChange(t *testing.T) { - - params := GetFromStdinParams{ - Question: "Hello?", - DefaultValue: "World", - ValidationRegexPattern: "World|Universe", - InputTerminationString: "!", - } - - err := mockStdin("invalid\nyes\ninvalid!\nUniverse!\n") - if err != nil { - t.Error(errors.ErrorStack(err)) - } - defer cleanUpMockedStdin() - - answer := *AskChangeQuestion(¶ms) - - if answer != "Universe" { - t.Error("Wrong Answer.\nExpected default: Universe\nBut Got: " + answer) - } -} - -var tmpfile *os.File -var oldStdin *os.File - -func mockStdin(inputString string) error { - //Code from https://stackoverflow.com/a/46365584 (modified) - input := []byte(inputString) - var err error - tmpfile, err = ioutil.TempFile("", "testGetFromStdin") - if err != nil { - return errors.Trace(err) - } - - if _, err := tmpfile.Write(input); err != nil { - return errors.Trace(err) - } - - if _, err := tmpfile.Seek(0, 0); err != nil { - return errors.Trace(err) - } - - oldStdin = os.Stdin - os.Stdin = tmpfile - - return nil -} - -func cleanUpMockedStdin() { - os.Remove(tmpfile.Name()) - os.Stdin = oldStdin -}