From 5cea0e21f438cb7ff6c37d721fe00379c44036ad Mon Sep 17 00:00:00 2001 From: Fabian Kramm Date: Fri, 9 Sep 2022 16:27:47 +0200 Subject: [PATCH] refactor: improve sleep mode exit --- cmd/root.go | 8 ++++++ cmd/run_pipeline.go | 20 ++++++++++++++ pkg/devspace/devpod/devpod.go | 9 ++++--- pkg/devspace/devpod/manager.go | 3 --- pkg/devspace/kill/kill.go | 22 +++++++++++++++ pkg/devspace/kubectl/client.go | 20 +++++++++----- .../commands/get_config_value.go | 27 ++++++++++++++----- .../pipelinehandler/commands/start_dev.go | 15 ----------- pkg/util/extract/unzip.go | 3 +-- pkg/util/idle/idle.go | 4 ++- 10 files changed, 94 insertions(+), 37 deletions(-) create mode 100644 pkg/devspace/kill/kill.go diff --git a/cmd/root.go b/cmd/root.go index d6e1a48d56..fcc7aac440 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "github.com/loft-sh/devspace/pkg/devspace/kill" "io/ioutil" "os" "strings" @@ -209,6 +210,13 @@ func BuildRoot(f factory.Factory, excludePlugins bool) *cobra.Command { }) persistentFlags := rootCmd.PersistentFlags() globalFlags = flags.SetGlobalFlags(persistentFlags) + kill.SetStopFunction(func(message string) { + if message == "" { + os.Exit(1) + } else { + f.GetLog().Fatal(message) + } + }) rootCmd.SetUsageTemplate(`Usage:{{if .Runnable}} {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} diff --git a/cmd/run_pipeline.go b/cmd/run_pipeline.go index f6b070748d..c6c8c09b09 100644 --- a/cmd/run_pipeline.go +++ b/cmd/run_pipeline.go @@ -15,6 +15,7 @@ import ( "github.com/loft-sh/devspace/pkg/devspace/dev" "github.com/loft-sh/devspace/pkg/devspace/devpod" "github.com/loft-sh/devspace/pkg/devspace/hook" + "github.com/loft-sh/devspace/pkg/devspace/kill" "github.com/loft-sh/devspace/pkg/devspace/kubectl" "github.com/loft-sh/devspace/pkg/devspace/pipeline" "github.com/loft-sh/devspace/pkg/devspace/pipeline/types" @@ -24,6 +25,7 @@ import ( "github.com/loft-sh/devspace/pkg/util/interrupt" "github.com/loft-sh/devspace/pkg/util/log" "github.com/loft-sh/devspace/pkg/util/message" + "github.com/mgutz/ansi" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -459,6 +461,16 @@ func runPipeline(ctx devspacecontext.Context, args []string, options *CommandOpt // get deploy pipeline pipe := pipeline.NewPipeline(ctx.Config().Config().Name, devPodManager, dependencyRegistry, configPipeline, options.Options) + kill.SetStopFunction(func(message string) { + if message != "" { + ctx.Log().WriteString(logrus.FatalLevel, "\n"+ansi.Color("fatal", "red+b")+" "+message+"\n") + } + + err = pipe.Close() + if err != nil { + ctx.Log().Debugf("Error closing pipeline: %v", err) + } + }) // start ui & open serv, err := dev.UI(ctx, options.UIPort, options.ShowUI, pipe) @@ -478,6 +490,10 @@ func runPipeline(ctx devspacecontext.Context, args []string, options *CommandOpt // start pipeline err = pipe.Run(ctx.WithLogger(log.NewStreamLoggerWithFormat(stdoutWriter, stderrWriter, ctx.Log().GetLevel(), log.TimeFormat)), args) if err != nil { + if err == context.Canceled { + return nil + } + return err } ctx.Log().Debugf("Wait for dev to finish") @@ -485,6 +501,10 @@ func runPipeline(ctx devspacecontext.Context, args []string, options *CommandOpt // wait for dev err = pipe.WaitDev() if err != nil { + if err == context.Canceled { + return nil + } + return err } diff --git a/pkg/devspace/devpod/devpod.go b/pkg/devspace/devpod/devpod.go index 324959ef89..ee4d4045a8 100644 --- a/pkg/devspace/devpod/devpod.go +++ b/pkg/devspace/devpod/devpod.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "fmt" + "github.com/loft-sh/devspace/pkg/devspace/kill" "io" "net/http" "os" @@ -420,8 +421,8 @@ func (d *devPod) startAttach(ctx devspacecontext.Context, devContainer *latest.D } // kill ourselves here - if !opts.ContinueOnTerminalExit && opts.KillApplication != nil { - go opts.KillApplication() + if !opts.ContinueOnTerminalExit { + kill.StopDevSpace("") } else { parent.Kill(nil) } @@ -460,8 +461,8 @@ func (d *devPod) startTerminal(ctx devspacecontext.Context, devContainer *latest } // kill ourselves here - if !opts.ContinueOnTerminalExit && opts.KillApplication != nil { - go opts.KillApplication() + if !opts.ContinueOnTerminalExit { + kill.StopDevSpace("") } else { parent.Kill(nil) } diff --git a/pkg/devspace/devpod/manager.go b/pkg/devspace/devpod/manager.go index b29de90e91..b519bc0719 100644 --- a/pkg/devspace/devpod/manager.go +++ b/pkg/devspace/devpod/manager.go @@ -22,9 +22,6 @@ type Options struct { DisablePortForwarding bool `long:"disable-port-forwarding" description:"If enabled will not start any port forwarding configuration"` DisablePodReplace bool `long:"disable-pod-replace" description:"If enabled will not replace any pods"` DisableOpen bool `long:"disable-open" description:"If enabled will not replace any pods"` - - // KillApplication kills the whole pipeline including all dev pods - KillApplication func() } type Manager interface { diff --git a/pkg/devspace/kill/kill.go b/pkg/devspace/kill/kill.go new file mode 100644 index 0000000000..f2c1aa633d --- /dev/null +++ b/pkg/devspace/kill/kill.go @@ -0,0 +1,22 @@ +package kill + +import "sync" + +// stopDevSpace can be used to stop DevSpace globally +var stopDevSpace func(message string) +var stopDevSpaceMutex sync.Mutex + +func StopDevSpace(message string) { + stopDevSpaceMutex.Lock() + defer stopDevSpaceMutex.Unlock() + + // don't block here + go stopDevSpace(message) +} + +func SetStopFunction(stopFn func(message string)) { + stopDevSpaceMutex.Lock() + defer stopDevSpaceMutex.Unlock() + + stopDevSpace = stopFn +} diff --git a/pkg/devspace/kubectl/client.go b/pkg/devspace/kubectl/client.go index a681ac0f0e..6cdffd9b11 100644 --- a/pkg/devspace/kubectl/client.go +++ b/pkg/devspace/kubectl/client.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/loft-sh/devspace/pkg/devspace/config/localcache" + "github.com/loft-sh/devspace/pkg/devspace/kill" "github.com/loft-sh/devspace/pkg/devspace/kubectl/util" "github.com/loft-sh/devspace/pkg/devspace/upgrade" "github.com/loft-sh/devspace/pkg/util/idle" @@ -118,7 +119,7 @@ func NewClientFromContext(context, namespace string, switchContext bool, kubeLoa requestType: "Regular", callback: func(response *http.Response) { if response.Header.Get("X-DevSpace-Response-Type") == "Blocked" { - log.GetInstance().Fatalf("Targeted Kubernetes environment has begun sleeping. Please restart DevSpace to wake up the environment") + kill.StopDevSpace("Targeted Kubernetes environment has begun sleeping. Please restart DevSpace to wake up the environment") } }, } @@ -329,7 +330,7 @@ func wakeUpAndPing(ctx context.Context, client Client, log log.Logger) error { requestType: "Ping", callback: func(response *http.Response) { if response.Header.Get("X-DevSpace-Response-Type") == "Blocked" { - log.Fatalf("Targeted Kubernetes environment has begun sleeping. Please restart DevSpace to wake up the environment") + kill.StopDevSpace("Targeted Kubernetes environment has begun sleeping. Please restart DevSpace to wake up the environment") } }, } @@ -356,7 +357,7 @@ func wakeUpAndPing(ctx context.Context, client Client, log log.Logger) error { if err != nil { log.Debugf("Error pinging Kubernetes environment: %v", err) } - }, time.Minute*3) + }, time.Minute) }() return nil @@ -416,9 +417,16 @@ func wakeUp(ctx context.Context, client Client, log log.Logger) error { log.Infof("DevSpace is waking up the Kubernetes environment, please wait a moment...") // wake up the environment - _, err = kubeClient.CoreV1().Pods(client.Namespace()).List(ctx, metav1.ListOptions{LabelSelector: "devspace=wakeup"}) - if err != nil { - return errors.Wrap(err, "error waking up the environment") + waitErr := wait.PollImmediate(time.Second, time.Second*30, func() (done bool, err error) { + _, err = kubeClient.CoreV1().Pods(client.Namespace()).List(ctx, metav1.ListOptions{LabelSelector: "devspace=wakeup"}) + if err != nil { + return false, nil + } + + return true, nil + }) + if waitErr != nil { + return errors.Wrap(err, "wake up environment") } return nil diff --git a/pkg/devspace/pipeline/engine/pipelinehandler/commands/get_config_value.go b/pkg/devspace/pipeline/engine/pipelinehandler/commands/get_config_value.go index 52c56e8905..5ff156c7db 100644 --- a/pkg/devspace/pipeline/engine/pipelinehandler/commands/get_config_value.go +++ b/pkg/devspace/pipeline/engine/pipelinehandler/commands/get_config_value.go @@ -2,7 +2,9 @@ package commands import ( "fmt" + "github.com/jessevdk/go-flags" "github.com/loft-sh/devspace/pkg/util/yamlutil" + "github.com/pkg/errors" "strings" devspacecontext "github.com/loft-sh/devspace/pkg/devspace/context" @@ -11,23 +13,28 @@ import ( "mvdan.cc/sh/v3/interp" ) +type GetConfigValueOptions struct{} + func GetConfigValue(ctx devspacecontext.Context, args []string) error { ctx = ctx.WithLogger(ctx.Log().ErrorStreamOnly()) ctx.Log().Debugf("get_config_value %s", strings.Join(args, " ")) - if len(args) != 1 { + options := &GetConfigValueOptions{} + args, err := flags.ParseArgs(options, args) + if err != nil { + return errors.Wrap(err, "parse args") + } else if len(args) != 1 { return fmt.Errorf("usage: get_config_value deployments.my-deployment.helm.chart.name") } hc := interp.HandlerCtx(ctx.Context()) - rawConfig := ctx.Config().Raw() - + config := ctx.Config().RawBeforeConversion() nodePath, err := yamlpath.NewPath(args[0]) if err != nil { ctx.Log().Debugf("%v", err) return nil } - out, err := yaml.Marshal(rawConfig) + out, err := yaml.Marshal(config) if err != nil { ctx.Log().Debugf("%v", err) return nil @@ -45,11 +52,19 @@ func GetConfigValue(ctx devspacecontext.Context, args []string) error { ctx.Log().Debugf("%v", err) return nil } - if len(nodes) < 1 { return nil } - _, _ = hc.Stdout.Write([]byte(nodes[0].Value)) + if nodes[0].Kind == yaml.ScalarNode { + _, _ = hc.Stdout.Write([]byte(nodes[0].Value)) + return nil + } + + out, err = yaml.Marshal(nodes[0]) + if err != nil { + return err + } + _, _ = hc.Stdout.Write(out) return nil } diff --git a/pkg/devspace/pipeline/engine/pipelinehandler/commands/start_dev.go b/pkg/devspace/pipeline/engine/pipelinehandler/commands/start_dev.go index cef0a7093f..9f6ba73bbf 100644 --- a/pkg/devspace/pipeline/engine/pipelinehandler/commands/start_dev.go +++ b/pkg/devspace/pipeline/engine/pipelinehandler/commands/start_dev.go @@ -6,7 +6,6 @@ import ( devspacecontext "github.com/loft-sh/devspace/pkg/devspace/context" "github.com/loft-sh/devspace/pkg/devspace/devpod" "github.com/loft-sh/devspace/pkg/devspace/pipeline/types" - logpkg "github.com/loft-sh/devspace/pkg/util/log" "github.com/loft-sh/devspace/pkg/util/stringutil" "github.com/pkg/errors" "strings" @@ -72,19 +71,5 @@ func StartDev(ctx devspacecontext.Context, pipeline types.Pipeline, args []strin } else { return fmt.Errorf("either specify 'start_dev --all' or 'dev devConfig1 devConfig2'") } - options.Options.KillApplication = func() { - killApplication(pipeline) - } return pipeline.DevPodManager().StartMultiple(ctx, args, options.Options) } - -func killApplication(pipeline types.Pipeline) { - for pipeline.Parent() != nil { - pipeline = pipeline.Parent() - } - - err := pipeline.Close() - if err != nil { - logpkg.GetInstance().Errorf("error closing pipeline: %v", err) - } -} diff --git a/pkg/util/extract/unzip.go b/pkg/util/extract/unzip.go index 11ca050b52..ede6555ade 100644 --- a/pkg/util/extract/unzip.go +++ b/pkg/util/extract/unzip.go @@ -6,7 +6,6 @@ import ( "compress/gzip" "fmt" "io" - "log" "os" "path/filepath" ) @@ -34,7 +33,7 @@ func (e *extractor) UntarGz(src, dest string) error { uncompressedStream, err := gzip.NewReader(gzipStream) if err != nil { - log.Fatal("ExtractTarGz: NewReader failed") + return fmt.Errorf("ExtractTarGz: NewReader failed") } tarReader := tar.NewReader(uncompressedStream) diff --git a/pkg/util/idle/idle.go b/pkg/util/idle/idle.go index dc757f4de0..8a1327c650 100644 --- a/pkg/util/idle/idle.go +++ b/pkg/util/idle/idle.go @@ -1,6 +1,8 @@ package idle import ( + "fmt" + "github.com/loft-sh/devspace/pkg/devspace/kill" "time" "github.com/loft-sh/devspace/pkg/util/log" @@ -48,7 +50,7 @@ func (m *monitor) Start(timeout time.Duration, log log.Logger) { return } else if duration > timeout { // we exit here - log.Fatalf("Automatically exit DevSpace, because the user is inactive for %s. To disable automatic exiting, run with --inactivity-timeout=0", duration.String()) + kill.StopDevSpace(fmt.Sprintf("Automatically exit DevSpace, because the user is inactive for %s. To disable automatic exiting, run with --inactivity-timeout=0", duration.String())) } }, time.Second*10) }()