diff --git a/cmd/cleanup/images.go b/cmd/cleanup/images.go index d946209f52..c1e8d37137 100644 --- a/cmd/cleanup/images.go +++ b/cmd/cleanup/images.go @@ -41,6 +41,7 @@ Deletes all locally created docker images from docker // RunCleanupImages executes the cleanup images command logic func (cmd *imagesCmd) RunCleanupImages(f factory.Factory, cobraCmd *cobra.Command, args []string) error { // Set config root + ctx := context.Background() log := f.GetLog() configLoader, err := f.NewConfigLoader(cmd.ConfigPath) if err != nil { @@ -65,13 +66,13 @@ func (cmd *imagesCmd) RunCleanupImages(f factory.Factory, cobraCmd *cobra.Comman } // Create docker client - client, err := docker.NewClientWithMinikube(kubeContext, true, log) + client, err := docker.NewClientWithMinikube(ctx, kubeContext, true, log) if err != nil { return err } // Load config - configInterface, err := configLoader.Load(context.Background(), nil, cmd.ToConfigOptions(), log) + configInterface, err := configLoader.Load(ctx, nil, cmd.ToConfigOptions(), log) if err != nil { return err } @@ -82,7 +83,7 @@ func (cmd *imagesCmd) RunCleanupImages(f factory.Factory, cobraCmd *cobra.Comman return nil } - _, err = client.Ping(context.Background()) + _, err = client.Ping(ctx) if err != nil { return errors.Errorf("Docker seems to be not running: %v", err) } @@ -91,7 +92,7 @@ func (cmd *imagesCmd) RunCleanupImages(f factory.Factory, cobraCmd *cobra.Comman for _, imageConfig := range config.Images { log.Info("Deleting local image " + imageConfig.Image + "...") - response, err := client.DeleteImageByName(imageConfig.Image, log) + response, err := client.DeleteImageByName(ctx, imageConfig.Image, log) if err != nil { return err } @@ -109,7 +110,7 @@ func (cmd *imagesCmd) RunCleanupImages(f factory.Factory, cobraCmd *cobra.Comman // Cleanup dangling images aswell for { - response, err := client.DeleteImageByFilter(filters.NewArgs(filters.Arg("dangling", "true")), log) + response, err := client.DeleteImageByFilter(ctx, filters.NewArgs(filters.Arg("dangling", "true")), log) if err != nil { return err } diff --git a/cmd/init.go b/cmd/init.go index b8c7072a81..ae201b4e30 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -375,7 +375,7 @@ func (cmd *InitCmd) Run(f factory.Factory) error { } } - err = configureManager.AddImage(imageName, image, projectNamespace+"/"+projectName, cmd.Dockerfile, languageHandler) + err = configureManager.AddImage(imageName, image, projectNamespace+"/"+projectName, cmd.Dockerfile) if err != nil { if err.Error() != "" { cmd.log.Errorf("Error: %s", err.Error()) diff --git a/e2e/tests/build/build.go b/e2e/tests/build/build.go index 603c3d62ed..c1f1873342 100644 --- a/e2e/tests/build/build.go +++ b/e2e/tests/build/build.go @@ -57,7 +57,7 @@ var _ = DevSpaceDescribe("build", func() { framework.ExpectNoError(err) // create devspace docker client to access docker APIs - devspaceDockerClient, err := docker.NewClient(log) + devspaceDockerClient, err := docker.NewClient(context.TODO(), log) framework.ExpectNoError(err) dockerClient := devspaceDockerClient.DockerAPIClient() @@ -93,7 +93,7 @@ var _ = DevSpaceDescribe("build", func() { framework.ExpectNoError(err) // create devspace docker client to access docker APIs - devspaceDockerClient, err := docker.NewClient(log) + devspaceDockerClient, err := docker.NewClient(context.TODO(), log) framework.ExpectNoError(err) dockerClient := devspaceDockerClient.DockerAPIClient() @@ -143,7 +143,7 @@ var _ = DevSpaceDescribe("build", func() { framework.ExpectNoError(err) // create devspace docker client to access docker APIs - devspaceDockerClient, err := docker.NewClient(log) + devspaceDockerClient, err := docker.NewClient(context.TODO(), log) framework.ExpectNoError(err) dockerClient := devspaceDockerClient.DockerAPIClient() @@ -177,7 +177,7 @@ var _ = DevSpaceDescribe("build", func() { framework.ExpectNoError(err) // create devspace docker client to access docker APIs - devspaceDockerClient, err := docker.NewClient(log) + devspaceDockerClient, err := docker.NewClient(context.TODO(), log) framework.ExpectNoError(err) dockerClient := devspaceDockerClient.DockerAPIClient() @@ -241,7 +241,7 @@ var _ = DevSpaceDescribe("build", func() { framework.ExpectNoError(err) // create devspace docker client to access docker APIs - devspaceDockerClient, err := docker.NewClient(log) + devspaceDockerClient, err := docker.NewClient(context.TODO(), log) framework.ExpectNoError(err) dockerClient := devspaceDockerClient.DockerAPIClient() @@ -305,7 +305,7 @@ var _ = DevSpaceDescribe("build", func() { framework.ExpectNoError(err) // create devspace docker client to access docker APIs - devspaceDockerClient, err := docker.NewClient(log) + devspaceDockerClient, err := docker.NewClient(context.TODO(), log) framework.ExpectNoError(err) dockerClient := devspaceDockerClient.DockerAPIClient() diff --git a/pkg/devspace/build/builder/buildkit/buildkit.go b/pkg/devspace/build/builder/buildkit/buildkit.go index 697f94810b..23367701f9 100644 --- a/pkg/devspace/build/builder/buildkit/buildkit.go +++ b/pkg/devspace/build/builder/buildkit/buildkit.go @@ -67,7 +67,7 @@ func (b *Builder) ShouldRebuild(ctx *devspacecontext.Context, forceRebuild bool) // Check if image is present in local repository if !rebuild && err == nil && b.helper.ImageConf.BuildKit.InCluster == nil { if b.skipPushOnLocalKubernetes && ctx.KubeClient != nil && kubectl.IsLocalKubernetes(ctx.KubeClient.CurrentContext()) { - dockerClient, err := dockerpkg.NewClientWithMinikube(ctx.KubeClient.CurrentContext(), b.helper.ImageConf.BuildKit.PreferMinikube == nil || *b.helper.ImageConf.BuildKit.PreferMinikube, ctx.Log) + dockerClient, err := dockerpkg.NewClientWithMinikube(ctx.Context, ctx.KubeClient.CurrentContext(), b.helper.ImageConf.BuildKit.PreferMinikube == nil || *b.helper.ImageConf.BuildKit.PreferMinikube, ctx.Log) if err != nil { return false, err } @@ -201,7 +201,7 @@ func buildWithCLI(ctx context.Context, dir string, context io.Reader, writer io. err error ) if useMinikubeDocker { - minikubeEnv, err = dockerpkg.GetMinikubeEnvironment() + minikubeEnv, err = dockerpkg.GetMinikubeEnvironment(ctx) if err != nil { return fmt.Errorf("error retrieving minikube environment with 'minikube docker-env --shell none'. Try setting the option preferMinikube to false: %v", err) } diff --git a/pkg/devspace/build/builder/docker/docker.go b/pkg/devspace/build/builder/docker/docker.go index 3d9bccd4dc..d61ee03923 100644 --- a/pkg/devspace/build/builder/docker/docker.go +++ b/pkg/devspace/build/builder/docker/docker.go @@ -112,7 +112,7 @@ func (b *Builder) BuildImage(ctx *devspacecontext.Context, contextPath, dockerfi // Authenticate if !b.skipPush && !b.helper.ImageConf.SkipPush { ctx.Log.Info("Authenticating (" + displayRegistryURL + ")...") - _, err = b.Authenticate() + _, err = b.Authenticate(ctx.Context) if err != nil { return errors.Errorf("Error during image registry authentication: %v", err) } @@ -173,7 +173,7 @@ func (b *Builder) BuildImage(ctx *devspacecontext.Context, contextPath, dockerfi // Check if we skip push if !b.skipPush && !b.helper.ImageConf.SkipPush { for _, tag := range buildOptions.Tags { - err = b.pushImage(writer, tag) + err = b.pushImage(ctx.Context, writer, tag) if err != nil { return errors.Errorf("error during image push: %v", err) } @@ -188,13 +188,13 @@ func (b *Builder) BuildImage(ctx *devspacecontext.Context, contextPath, dockerfi } // Authenticate authenticates the client with a remote registry -func (b *Builder) Authenticate() (*types.AuthConfig, error) { +func (b *Builder) Authenticate(ctx context.Context) (*types.AuthConfig, error) { registryURL, err := pullsecrets.GetRegistryFromImageName(b.helper.ImageName + ":" + b.helper.ImageTags[0]) if err != nil { return nil, err } - b.authConfig, err = b.client.Login(registryURL, "", "", true, false, false) + b.authConfig, err = b.client.Login(ctx, registryURL, "", "", true, false, false) if err != nil { return nil, err } @@ -203,7 +203,7 @@ func (b *Builder) Authenticate() (*types.AuthConfig, error) { } // pushImage pushes an image to the specified registry -func (b *Builder) pushImage(writer io.Writer, imageName string) error { +func (b *Builder) pushImage(ctx context.Context, writer io.Writer, imageName string) error { ref, err := reference.ParseNormalizedNamed(imageName) if err != nil { return err @@ -214,7 +214,7 @@ func (b *Builder) pushImage(writer io.Writer, imageName string) error { return err } - out, err := b.client.ImagePush(context.Background(), reference.FamiliarString(ref), types.ImagePushOptions{ + out, err := b.client.ImagePush(ctx, reference.FamiliarString(ref), types.ImagePushOptions{ RegistryAuth: encodedAuth, }) if err != nil { diff --git a/pkg/devspace/build/builder/helper/helper.go b/pkg/devspace/build/builder/helper/helper.go index 9c17b811fd..d29d5eb4b1 100644 --- a/pkg/devspace/build/builder/helper/helper.go +++ b/pkg/devspace/build/builder/helper/helper.go @@ -1,7 +1,6 @@ package helper import ( - "context" "github.com/docker/cli/cli/command/image/build" "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/archive" @@ -206,7 +205,7 @@ func (b *BuildHelper) IsImageAvailableLocally(ctx *devspacecontext.Context, dock imageCache, _ := ctx.Config.LocalCache().GetImageCache(b.ImageConfigName) imageName := imageCache.ImageName + ":" + imageCache.Tag dockerAPIClient := dockerClient.DockerAPIClient() - imageList, err := dockerAPIClient.ImageList(context.Background(), types.ImageListOptions{}) + imageList, err := dockerAPIClient.ImageList(ctx.Context, types.ImageListOptions{}) if err != nil { return false, err } diff --git a/pkg/devspace/build/create_builder.go b/pkg/devspace/build/create_builder.go index a58810aa1e..8241f162f3 100644 --- a/pkg/devspace/build/create_builder.go +++ b/pkg/devspace/build/create_builder.go @@ -1,7 +1,6 @@ package build import ( - "context" "github.com/loft-sh/devspace/pkg/devspace/build/builder" "github.com/loft-sh/devspace/pkg/devspace/build/builder/buildkit" "github.com/loft-sh/devspace/pkg/devspace/build/builder/custom" @@ -59,13 +58,13 @@ func (c *controller) createBuilder(ctx *devspacecontext.Context, imageConfigName kubeContext = ctx.KubeClient.CurrentContext() } - dockerClient, err := dockerclient.NewClientWithMinikube(kubeContext, preferMinikube, ctx.Log) + dockerClient, err := dockerclient.NewClientWithMinikube(ctx.Context, kubeContext, preferMinikube, ctx.Log) if err != nil { return nil, errors.Errorf("Error creating docker client: %v", err) } // Check if docker daemon is running - _, err = dockerClient.Ping(context.Background()) + _, err = dockerClient.Ping(ctx.Context) if err != nil { if imageConf.Docker != nil && imageConf.Docker.DisableFallback != nil && *imageConf.Docker.DisableFallback { return nil, errors.Errorf("Couldn't reach docker daemon: %v. Is the docker daemon running?", err) @@ -89,7 +88,7 @@ func (c *controller) createBuilder(ctx *devspacecontext.Context, imageConfigName return nil, err } - dockerClient, err := dockerclient.NewClient(ctx.Log) + dockerClient, err := dockerclient.NewClient(ctx.Context, ctx.Log) if err == nil { if imageConf.Kaniko != nil && imageConf.Kaniko.Namespace != "" && ctx.KubeClient.Namespace() != imageConf.Kaniko.Namespace { err = pullsecrets.NewClient().EnsurePullSecret(ctx, dockerClient, imageConf.Kaniko.Namespace, registryURL) diff --git a/pkg/devspace/config/loader/loader.go b/pkg/devspace/config/loader/loader.go index b19e147318..fdfd69d80e 100644 --- a/pkg/devspace/config/loader/loader.go +++ b/pkg/devspace/config/loader/loader.go @@ -172,7 +172,7 @@ func (l *configLoader) LoadWithParser(ctx context.Context, localCache localcache return nil, err } - err = l.ensureRequires(parsedConfig, log) + err = l.ensureRequires(ctx, parsedConfig, log) if err != nil { return nil, errors.Wrap(err, "require versions") } @@ -191,7 +191,7 @@ func (l *configLoader) LoadWithParser(ctx context.Context, localCache localcache return c, nil } -func (l *configLoader) ensureRequires(config *latest.Config, log log.Logger) error { +func (l *configLoader) ensureRequires(ctx context.Context, config *latest.Config, log log.Logger) error { if config == nil { return nil } @@ -259,7 +259,7 @@ func (l *configLoader) ensureRequires(config *latest.Config, log log.Logger) err return errors.Wrapf(err, "parsing require.commands[%d].version", index) } - out, err := command.Output(context.TODO(), filepath.Dir(l.absConfigPath), c.Name, versionArgs...) + out, err := command.Output(ctx, filepath.Dir(l.absConfigPath), c.Name, versionArgs...) if err != nil { return fmt.Errorf("cannot run command '%s' (%v), however it is required by the config. Please make sure you have correctly installed '%s' with version %s", c.Name, err, c.Name, c.Version) } diff --git a/pkg/devspace/config/versions/latest/schema.go b/pkg/devspace/config/versions/latest/schema.go index 3683c04b65..f675fbbfc0 100644 --- a/pkg/devspace/config/versions/latest/schema.go +++ b/pkg/devspace/config/versions/latest/schema.go @@ -1328,7 +1328,7 @@ type CommandConfig struct { // the command was interrupted which will set the env variable COMMAND_INTERRUPT // to true as well as when the command errored which will set the error string to // COMMAND_ERROR. - After string `yaml:"after" json:"after"` + After string `yaml:"after,omitempty" json:"after,omitempty"` // DisableReplace signals DevSpace to not replace the default command. E.g. // dev does not replace devspace dev. diff --git a/pkg/devspace/config/versions/versions.go b/pkg/devspace/config/versions/versions.go index f224a7489a..b6751ce6d0 100644 --- a/pkg/devspace/config/versions/versions.go +++ b/pkg/devspace/config/versions/versions.go @@ -60,7 +60,7 @@ func ParseProfile(ctx context.Context, basePath string, data map[string]interfac activatedProfiles := []string{} if !disableProfileActivation { var err error - activatedProfiles, err = getActivatedProfiles(data, resolver, log) + activatedProfiles, err = getActivatedProfiles(ctx, data, resolver, log) if err != nil { return nil, err } @@ -305,7 +305,7 @@ func getProfiles(ctx context.Context, basePath string, data map[string]interface return errors.Errorf("Couldn't find profile '%s'", profile) } -func getActivatedProfiles(data map[string]interface{}, resolver variable.Resolver, log log.Logger) ([]string, error) { +func getActivatedProfiles(ctx context.Context, data map[string]interface{}, resolver variable.Resolver, log log.Logger) ([]string, error) { activatedProfiles := []string{} // Check if there are profiles @@ -331,7 +331,7 @@ func getActivatedProfiles(data map[string]interface{}, resolver variable.Resolve return activatedProfiles, errors.Wrap(err, "error activating profile with env") } - activatedByVars, err := matchVars(activation.Vars, resolver) + activatedByVars, err := matchVars(ctx, activation.Vars, resolver) if err != nil { return activatedProfiles, errors.Wrap(err, "error activating profile with vars") } @@ -360,9 +360,9 @@ func matchEnvironment(env map[string]string) (bool, error) { return true, nil } -func matchVars(activationVars map[string]string, resolver variable.Resolver) (bool, error) { +func matchVars(ctx context.Context, activationVars map[string]string, resolver variable.Resolver) (bool, error) { for k, v := range activationVars { - value, err := resolveVariableValue(k, resolver) + value, err := resolveVariableValue(ctx, k, resolver) if err != nil { return false, err } @@ -393,8 +393,8 @@ func sanitizeMatchExpression(expression string) string { return exp } -func resolveVariableValue(name string, resolver variable.Resolver) (string, error) { - val, err := resolver.FillVariables(context.TODO(), "${"+name+"}") +func resolveVariableValue(ctx context.Context, name string, resolver variable.Resolver) (string, error) { + val, err := resolver.FillVariables(ctx, "${"+name+"}") if err != nil { return "", err } diff --git a/pkg/devspace/configure/image.go b/pkg/devspace/configure/image.go index 0968836471..bb01216b8c 100644 --- a/pkg/devspace/configure/image.go +++ b/pkg/devspace/configure/image.go @@ -24,7 +24,7 @@ import ( const dockerHubHostname = "hub.docker.com" // AddImage adds an image to the provided config -func (m *manager) AddImage(imageName, image, projectNamespace, dockerfile string, languageHandler *generator.LanguageHandler) error { +func (m *manager) AddImage(imageName, image, projectNamespace, dockerfile string) error { var ( useDockerHub = "Use " + dockerHubHostname useGithubRegistry = "Use GitHub image registry" @@ -113,14 +113,14 @@ func (m *manager) AddImage(imageName, image, projectNamespace, dockerfile string } // Get docker client - dockerClient, err := m.factory.NewDockerClientWithMinikube(kubeContext, true, m.log) + dockerClient, err := m.factory.NewDockerClientWithMinikube(context.TODO(), kubeContext, true, m.log) if err != nil { return errors.Errorf("Cannot create docker client: %v", err) } // Check if user is logged into docker hub isLoggedIntoDockerHub := false - authConfig, err := dockerClient.GetAuthConfig(dockerHubHostname, true) + authConfig, err := dockerClient.GetAuthConfig(context.TODO(), dockerHubHostname, true) if err == nil && authConfig.Username != "" { useDockerHub = useDockerHub + fmt.Sprintf(registryUsernameHint, authConfig.Username) isLoggedIntoDockerHub = true @@ -128,7 +128,7 @@ func (m *manager) AddImage(imageName, image, projectNamespace, dockerfile string // Check if user is logged into GitHub isLoggedIntoGitHub := false - authConfig, err = dockerClient.GetAuthConfig(generator.GithubContainerRegistry, true) + authConfig, err = dockerClient.GetAuthConfig(context.TODO(), generator.GithubContainerRegistry, true) if err == nil && authConfig.Username != "" { useGithubRegistry = useGithubRegistry + fmt.Sprintf(registryUsernameHint, authConfig.Username) isLoggedIntoGitHub = true @@ -237,7 +237,7 @@ func (m *manager) addPullSecretConfig(dockerClient docker.Client, image string) for { m.log.Info("Checking registry authentication for " + registryHostnamePrintable + "...") - authConfig, err := dockerClient.Login(registryHostname, registryUsername, registryPassword, true, retry, retry) + authConfig, err := dockerClient.Login(context.TODO(), registryHostname, registryUsername, registryPassword, true, retry, retry) if err == nil && (authConfig.Username != "" || authConfig.Password != "") { registryUsername = authConfig.Username diff --git a/pkg/devspace/configure/manager.go b/pkg/devspace/configure/manager.go index 98dddfb9bb..046fb8b98d 100644 --- a/pkg/devspace/configure/manager.go +++ b/pkg/devspace/configure/manager.go @@ -1,10 +1,10 @@ package configure import ( + "context" "github.com/loft-sh/devspace/pkg/devspace/config/localcache" "github.com/loft-sh/devspace/pkg/devspace/config/versions/latest" "github.com/loft-sh/devspace/pkg/devspace/docker" - "github.com/loft-sh/devspace/pkg/devspace/generator" "github.com/loft-sh/devspace/pkg/util/kubeconfig" "github.com/loft-sh/devspace/pkg/util/log" ) @@ -14,12 +14,12 @@ type Manager interface { AddKubectlDeployment(deploymentName string, isKustomization bool) error AddHelmDeployment(deploymentName string) error AddComponentDeployment(deploymentName, image string, servicePort int) error - AddImage(imageName, image, projectNamespace, dockerfile string, languageHandler *generator.LanguageHandler) error + AddImage(imageName, image, projectNamespace, dockerfile string) error } // Factory defines the factory methods needed by the configure manager to create new configuration type Factory interface { - NewDockerClientWithMinikube(currentKubeContext string, preferMinikube bool, log log.Logger) (docker.Client, error) + NewDockerClientWithMinikube(ctx context.Context, currentKubeContext string, preferMinikube bool, log log.Logger) (docker.Client, error) NewKubeConfigLoader() kubeconfig.Loader } diff --git a/pkg/devspace/devpod/devpod.go b/pkg/devspace/devpod/devpod.go index b93e5ef79e..f138fcb9e5 100644 --- a/pkg/devspace/devpod/devpod.go +++ b/pkg/devspace/devpod/devpod.go @@ -3,7 +3,11 @@ package devpod import ( "context" "fmt" + "github.com/mgutz/ansi" "io" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" "net/http" "os" syncpkg "sync" @@ -64,13 +68,14 @@ func newDevPod() *devPod { func (d *devPod) Start(ctx *devspacecontext.Context, devPodConfig *latest.DevPod, options Options) error { d.m.Lock() - defer d.m.Unlock() - if d.cancel != nil { + d.m.Unlock() return errors.Errorf("dev pod is already running, please stop it before starting") } + d.cancelCtx, d.cancel = context.WithCancel(ctx.Context) ctx = ctx.WithContext(d.cancelCtx) + d.m.Unlock() // log devpod to console if debug if ctx.Log.GetLevel() == logrus.DebugLevel { @@ -83,8 +88,7 @@ func (d *devPod) Start(ctx *devspacecontext.Context, devPodConfig *latest.DevPod // start the dev pod err := d.startWithRetry(ctx, devPodConfig, options) if err != nil { - d.cancel() - <-d.done + d.Stop() return err } @@ -104,57 +108,73 @@ func (d *devPod) Done() <-chan struct{} { func (d *devPod) Stop() { d.m.Lock() - defer d.m.Lock() - if d.cancel != nil { d.cancel() - <-d.done } + d.m.Unlock() + <-d.done } func (d *devPod) startWithRetry(ctx *devspacecontext.Context, devPodConfig *latest.DevPod, options Options) error { t := &tomb.Tomb{} go func(ctx *devspacecontext.Context) { + // wait for parent context cancel + // or that the DevPod is done select { case <-ctx.Context.Done(): + case <-t.Dead(): + } + + if ctx.IsDone() { <-t.Dead() + ctx.Log.Debugf("Stopped dev %s", devPodConfig.Name) close(d.done) return - case <-t.Dead(): - if ctx.IsDone() { - close(d.done) - return - } - - // try restarting the dev pod if it has stopped because of - // a lost connection - if _, ok := t.Err().(DevPodLostConnection); ok { - for { - err := d.startWithRetry(ctx, devPodConfig, options) - if err != nil { - if ctx.IsDone() { - return - } + } - ctx.Log.Infof("Restart dev %s because of: %v", devPodConfig.Name, err) - select { - case <-ctx.Context.Done(): - return - case <-time.After(time.Second * 10): - continue - } + // check if pod was terminated + d.m.Lock() + selectedPod := d.selectedPod + d.selectedPod = nil + d.m.Unlock() + + // check if we need to restart + if selectedPod != nil { + shouldRestart := false + err := wait.PollImmediateUntil(time.Second, func() (bool, error) { + pod, err := ctx.KubeClient.KubeClient().CoreV1().Pods(selectedPod.Pod.Namespace).Get(ctx.Context, selectedPod.Pod.Name, metav1.GetOptions{}) + if err != nil { + if kerrors.IsNotFound(err) { + ctx.Log.Debugf("Restart dev %s because pod isn't found anymore", devPodConfig.Name) + shouldRestart = true + return true, nil } - return + // this case means there might be problems with internet + ctx.Log.Debugf("error trying to retrieve pod: %v", err) + return false, nil + } else if pod.DeletionTimestamp != nil { + ctx.Log.Debugf("Restart dev %s because pod is terminating", devPodConfig.Name) + shouldRestart = true + return true, nil } - } else { - d.m.Lock() - d.err = t.Err() - d.m.Unlock() - close(d.done) + + return true, nil + }, ctx.Context.Done()) + if err != nil { + ctx.Log.Errorf("error restarting dev: %v", err) + } else if shouldRestart { + d.restart(ctx, devPodConfig, options) + return } } + + ctx.Log.Debugf("Stopped dev %s", devPodConfig.Name) + d.m.Lock() + d.err = t.Err() + d.m.Unlock() + close(d.done) }(ctx) // Create a new tomb and run it @@ -170,6 +190,27 @@ func (d *devPod) startWithRetry(ctx *devspacecontext.Context, devPodConfig *late return nil } +func (d *devPod) restart(ctx *devspacecontext.Context, devPodConfig *latest.DevPod, options Options) { + for { + err := d.startWithRetry(ctx, devPodConfig, options) + if err != nil { + if ctx.IsDone() { + return + } + + ctx.Log.Infof("Restart dev %s because of: %v", devPodConfig.Name, err) + select { + case <-ctx.Context.Done(): + return + case <-time.After(time.Second * 10): + continue + } + } + + return + } +} + func (d *devPod) start(ctx *devspacecontext.Context, devPodConfig *latest.DevPod, opts Options, parent *tomb.Tomb) error { // check first if we need to replace the pod if !opts.DisablePodReplace && needPodReplace(devPodConfig) { @@ -204,14 +245,20 @@ func (d *devPod) start(ctx *devspacecontext.Context, devPodConfig *latest.DevPod WithWaitingStrategy(targetselector.NewUntilNewestRunningWaitingStrategy(time.Millisecond * 500)). WithSkipInitContainers(true) var err error - d.selectedPod, err = targetselector.NewTargetSelector(options).SelectSingleContainer(ctx.Context, ctx.KubeClient, ctx.Log) + selectedPod, err := targetselector.NewTargetSelector(options).SelectSingleContainer(ctx.Context, ctx.KubeClient, ctx.Log) if err != nil { return errors.Wrap(err, "waiting for pod to become ready") } - ctx.Log.Debugf("Selected pod:container %s:%s", d.selectedPod.Pod.Name, d.selectedPod.Container.Name) + ctx.Log.Infof("Selected %s (%s)", ansi.Color(fmt.Sprintf("%s:%s", selectedPod.Pod.Name, selectedPod.Container.Name), "yellow+b"), ansi.Color("pod:container", "white+b")) + + // set selected pod + d.m.Lock() + d.selectedPod = selectedPod + d.m.Unlock() // Run dev.open configs if !opts.DisableOpen { + ctx := ctx.WithLogger(ctx.Log.WithPrefixColor("open ", "yellow+b")) for _, openConfig := range devPodConfig.Open { if openConfig.URL != "" { url := openConfig.URL @@ -223,11 +270,9 @@ func (d *devPod) start(ctx *devspacecontext.Context, devPodConfig *latest.DevPod case <-ctx.Context.Done(): return nil case <-time.After(time.Second): - resp, _ := http.Get(url) - if resp != nil && resp.StatusCode != http.StatusBadGateway && resp.StatusCode != http.StatusServiceUnavailable { - time.Sleep(time.Second * 1) - _ = open.Start(url) - ctx.Log.Donef("Successfully opened %s", url) + err := tryOpen(ctx.Context, url, ctx.Log) + if err == nil { + return nil } } } @@ -239,7 +284,7 @@ func (d *devPod) start(ctx *devspacecontext.Context, devPodConfig *latest.DevPod } // start sync and port forwarding - err = d.startServices(ctx, devPodConfig, newTargetSelector(d.selectedPod.Pod.Name, d.selectedPod.Pod.Namespace, d.selectedPod.Container.Name, parent), opts, parent) + err = d.startServices(ctx, devPodConfig, newTargetSelector(selectedPod.Pod.Name, selectedPod.Pod.Namespace, selectedPod.Container.Name, parent), opts, parent) if err != nil { return err } @@ -247,26 +292,55 @@ func (d *devPod) start(ctx *devspacecontext.Context, devPodConfig *latest.DevPod // start logs terminalDevContainer := d.getTerminalDevContainer(devPodConfig) if terminalDevContainer != nil { - return d.startTerminal(ctx, terminalDevContainer, opts, parent) + return d.startTerminal(ctx, terminalDevContainer, opts, selectedPod, parent) } // start attach if defined attachDevContainer := d.getAttachDevContainer(devPodConfig) if attachDevContainer != nil { - return d.startAttach(ctx, attachDevContainer, opts, parent) + return d.startAttach(ctx, attachDevContainer, opts, selectedPod, parent) + } + + return d.startLogs(ctx, devPodConfig, selectedPod, parent) +} + +func tryOpen(ctx context.Context, url string, log logpkg.Logger) error { + timeoutCtx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(timeoutCtx, "GET", url, nil) + if err != nil { + return err + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + if resp != nil && resp.StatusCode != http.StatusBadGateway && resp.StatusCode != http.StatusServiceUnavailable { + select { + case <-ctx.Done(): + return nil + case <-time.After(time.Second): + } + _ = open.Start(url) + log.Donef("Successfully opened %s", url) + return nil } - return d.startLogs(ctx, devPodConfig, parent) + return fmt.Errorf("not reachable") } -func (d *devPod) startLogs(ctx *devspacecontext.Context, devPodConfig *latest.DevPod, parent *tomb.Tomb) error { +func (d *devPod) startLogs(ctx *devspacecontext.Context, devPodConfig *latest.DevPod, selectedPod *selector.SelectedPodContainer, parent *tomb.Tomb) error { + ctx = ctx.WithLogger(ctx.Log.WithPrefixColor("logs ", "yellow+b")) loader.EachDevContainer(devPodConfig, func(devContainer *latest.DevContainer) bool { if devContainer.Logs == nil || (devContainer.Logs.Enabled != nil && !*devContainer.Logs.Enabled) { return true } parent.Go(func() error { - return logs.StartLogs(ctx, devContainer, newTargetSelector(d.selectedPod.Pod.Name, d.selectedPod.Pod.Namespace, d.selectedPod.Container.Name, parent)) + return logs.StartLogs(ctx, devContainer, newTargetSelector(selectedPod.Pod.Name, selectedPod.Pod.Namespace, selectedPod.Container.Name, parent)) }) return true @@ -303,7 +377,7 @@ func (d *devPod) getTerminalDevContainer(devPodConfig *latest.DevPod) *latest.De return devContainer } -func (d *devPod) startAttach(ctx *devspacecontext.Context, devContainer *latest.DevContainer, opts Options, parent *tomb.Tomb) error { +func (d *devPod) startAttach(ctx *devspacecontext.Context, devContainer *latest.DevContainer, opts Options, selectedPod *selector.SelectedPodContainer, parent *tomb.Tomb) error { parent.Go(func() error { id, err := logpkg.AcquireGlobalSilence() if err != nil { @@ -312,10 +386,11 @@ func (d *devPod) startAttach(ctx *devspacecontext.Context, devContainer *latest. defer logpkg.ReleaseGlobalSilence(id) // make sure the global log is silent + ctx = ctx.WithLogger(ctx.Log.WithPrefixColor("attach ", "yellow+b")) err = attach.StartAttach( ctx, devContainer, - newTargetSelector(d.selectedPod.Pod.Name, d.selectedPod.Pod.Namespace, d.selectedPod.Container.Name, parent), + newTargetSelector(selectedPod.Pod.Name, selectedPod.Pod.Namespace, selectedPod.Container.Name, parent), DefaultTerminalStdout, DefaultTerminalStderr, DefaultTerminalStdin, @@ -325,6 +400,11 @@ func (d *devPod) startAttach(ctx *devspacecontext.Context, devContainer *latest. return errors.Wrap(err, "error in attach") } + // if context is done we just return + if ctx.IsDone() { + return nil + } + // kill ourselves here if !opts.ContinueOnTerminalExit && opts.KillApplication != nil { go opts.KillApplication() @@ -337,7 +417,7 @@ func (d *devPod) startAttach(ctx *devspacecontext.Context, devContainer *latest. return nil } -func (d *devPod) startTerminal(ctx *devspacecontext.Context, devContainer *latest.DevContainer, opts Options, parent *tomb.Tomb) error { +func (d *devPod) startTerminal(ctx *devspacecontext.Context, devContainer *latest.DevContainer, opts Options, selectedPod *selector.SelectedPodContainer, parent *tomb.Tomb) error { parent.Go(func() error { id, err := logpkg.AcquireGlobalSilence() if err != nil { @@ -346,10 +426,11 @@ func (d *devPod) startTerminal(ctx *devspacecontext.Context, devContainer *lates defer logpkg.ReleaseGlobalSilence(id) // make sure the global log is silent + ctx = ctx.WithLogger(ctx.Log.WithPrefixColor("term ", "yellow+b")) err = terminal.StartTerminal( ctx, devContainer, - newTargetSelector(d.selectedPod.Pod.Name, d.selectedPod.Pod.Namespace, d.selectedPod.Container.Name, parent), + newTargetSelector(selectedPod.Pod.Name, selectedPod.Pod.Namespace, selectedPod.Container.Name, parent), DefaultTerminalStdout, DefaultTerminalStderr, DefaultTerminalStdin, @@ -359,6 +440,11 @@ func (d *devPod) startTerminal(ctx *devspacecontext.Context, devContainer *lates return errors.Wrap(err, "error in terminal forwarding") } + // if context is done we just return + if ctx.IsDone() { + return nil + } + // kill ourselves here if !opts.ContinueOnTerminalExit && opts.KillApplication != nil { go opts.KillApplication() @@ -383,10 +469,9 @@ func (d *devPod) startServices(ctx *devspacecontext.Context, devPod *latest.DevP return nil } + // add prefix + ctx := ctx.WithLogger(ctx.Log.WithPrefixColor("sync ", "yellow+b")) err := sync.StartSync(ctx, devPod, selector, parent) - if err != nil { - fmt.Println(err) - } return err }) @@ -396,16 +481,21 @@ func (d *devPod) startServices(ctx *devspacecontext.Context, devPod *latest.DevP return nil } + ctx := ctx.WithLogger(ctx.Log.WithPrefixColor("ports ", "yellow+b")) return portforwarding.StartPortForwarding(ctx, devPod, selector, parent) }) // Start SSH sshDone := parent.NotifyGo(func() error { + // add ssh prefix + ctx := ctx.WithLogger(ctx.Log.WithPrefixColor("ssh ", "yellow+b")) return ssh.StartSSH(ctx, devPod, selector, parent) }) // Start Reverse Commands reverseCommandsDone := parent.NotifyGo(func() error { + // add proxy prefix + ctx := ctx.WithLogger(ctx.Log.WithPrefixColor("proxy ", "yellow+b")) return proxycommands.StartProxyCommands(ctx, devPod, selector, parent) }) diff --git a/pkg/devspace/devpod/targetselector.go b/pkg/devspace/devpod/targetselector.go index c3f4a1114f..0b909a7209 100644 --- a/pkg/devspace/devpod/targetselector.go +++ b/pkg/devspace/devpod/targetselector.go @@ -124,7 +124,7 @@ func (u *untilNewestRunning) SelectPod(ctx context.Context, client kubectl.Clien u.podInfoPrinter.PrintPodWarning(pods[0], log) return false, nil, nil } else if kubectl.GetPodStatus(pods[0]) != "Running" { - u.podInfoPrinter.PrintPodInfo(client, pods[0], log) + u.podInfoPrinter.PrintPodInfo(ctx, client, pods[0], log) return false, nil, nil } @@ -147,7 +147,7 @@ func (u *untilNewestRunning) SelectContainer(ctx context.Context, client kubectl u.podInfoPrinter.PrintPodWarning(containers[0].Pod, log) return false, nil, nil } else if !targetselector.IsContainerRunning(containers[0]) { - u.podInfoPrinter.PrintPodInfo(client, containers[0].Pod, log) + u.podInfoPrinter.PrintPodInfo(ctx, client, containers[0].Pod, log) return false, nil, nil } diff --git a/pkg/devspace/docker/auth.go b/pkg/devspace/docker/auth.go index 5967bdb261..165a6f085c 100644 --- a/pkg/devspace/docker/auth.go +++ b/pkg/devspace/docker/auth.go @@ -12,8 +12,8 @@ import ( ) // GetRegistryEndpoint retrieves the correct registry url -func (c *client) GetRegistryEndpoint(registryURL string) (bool, string, error) { - authServer := c.getOfficialServer(context.Background()) +func (c *client) GetRegistryEndpoint(ctx context.Context, registryURL string) (bool, string, error) { + authServer := c.getOfficialServer(ctx) if registryURL == "" || registryURL == "hub.docker.com" { registryURL = authServer } @@ -22,8 +22,8 @@ func (c *client) GetRegistryEndpoint(registryURL string) (bool, string, error) { } // GetAuthConfig returns the AuthConfig for a Docker registry from the Docker credential helper -func (c *client) GetAuthConfig(registryURL string, checkCredentialsStore bool) (*types.AuthConfig, error) { - isDefaultRegistry, serverAddress, err := c.GetRegistryEndpoint(registryURL) +func (c *client) GetAuthConfig(ctx context.Context, registryURL string, checkCredentialsStore bool) (*types.AuthConfig, error) { + isDefaultRegistry, serverAddress, err := c.GetRegistryEndpoint(ctx, registryURL) if err != nil { return nil, err } @@ -32,9 +32,8 @@ func (c *client) GetAuthConfig(registryURL string, checkCredentialsStore bool) ( } // Login logs the user into docker -func (c *client) Login(registryURL, user, password string, checkCredentialsStore, saveAuthConfig, relogin bool) (*types.AuthConfig, error) { - ctx := context.Background() - isDefaultRegistry, serverAddress, err := c.GetRegistryEndpoint(registryURL) +func (c *client) Login(ctx context.Context, registryURL, user, password string, checkCredentialsStore, saveAuthConfig, relogin bool) (*types.AuthConfig, error) { + isDefaultRegistry, serverAddress, err := c.GetRegistryEndpoint(ctx, registryURL) if err != nil { return nil, err } diff --git a/pkg/devspace/docker/client.go b/pkg/devspace/docker/client.go index 0f064be679..9a5183b1d8 100644 --- a/pkg/devspace/docker/client.go +++ b/pkg/devspace/docker/client.go @@ -30,13 +30,13 @@ type Client interface { ImagePush(ctx context.Context, ref string, options dockertypes.ImagePushOptions) (io.ReadCloser, error) - Login(registryURL, user, password string, checkCredentialsStore, saveAuthConfig, relogin bool) (*dockertypes.AuthConfig, error) - GetAuthConfig(registryURL string, checkCredentialsStore bool) (*dockertypes.AuthConfig, error) + Login(ctx context.Context, registryURL, user, password string, checkCredentialsStore, saveAuthConfig, relogin bool) (*dockertypes.AuthConfig, error) + GetAuthConfig(ctx context.Context, registryURL string, checkCredentialsStore bool) (*dockertypes.AuthConfig, error) ParseProxyConfig(buildArgs map[string]*string) map[string]*string - DeleteImageByName(imageName string, log log.Logger) ([]dockertypes.ImageDeleteResponseItem, error) - DeleteImageByFilter(filter filters.Args, log log.Logger) ([]dockertypes.ImageDeleteResponseItem, error) + DeleteImageByName(ctx context.Context, imageName string, log log.Logger) ([]dockertypes.ImageDeleteResponseItem, error) + DeleteImageByFilter(ctx context.Context, filter filters.Args, log log.Logger) ([]dockertypes.ImageDeleteResponseItem, error) DockerAPIClient() dockerclient.CommonAPIClient } @@ -48,17 +48,17 @@ type client struct { } // NewClient retrieves a new docker client -func NewClient(log log.Logger) (Client, error) { - return NewClientWithMinikube("", false, log) +func NewClient(ctx context.Context, log log.Logger) (Client, error) { + return NewClientWithMinikube(ctx, "", false, log) } // NewClientWithMinikube creates a new docker client with optionally from the minikube vm -func NewClientWithMinikube(currentKubeContext string, preferMinikube bool, log log.Logger) (Client, error) { +func NewClientWithMinikube(ctx context.Context, currentKubeContext string, preferMinikube bool, log log.Logger) (Client, error) { var cli Client var err error if preferMinikube { - cli, err = newDockerClientFromMinikube(currentKubeContext) + cli, err = newDockerClientFromMinikube(ctx, currentKubeContext) if err != nil && err != errNotMinikube { log.Warnf("Error creating minikube docker client: %v", err) } @@ -76,7 +76,7 @@ func NewClientWithMinikube(currentKubeContext string, preferMinikube bool, log l } } - cli.NegotiateAPIVersion(context.Background()) + cli.NegotiateAPIVersion(ctx) return cli, nil } @@ -104,12 +104,12 @@ func newDockerClientFromEnvironment() (Client, error) { }, nil } -func newDockerClientFromMinikube(currentKubeContext string) (Client, error) { +func newDockerClientFromMinikube(ctx context.Context, currentKubeContext string) (Client, error) { if currentKubeContext != "minikube" { return nil, errNotMinikube } - env, err := GetMinikubeEnvironment() + env, err := GetMinikubeEnvironment(ctx) if err != nil { return nil, errors.Errorf("can't retrieve minikube docker environment due to error: %v", err) } @@ -151,8 +151,8 @@ func newDockerClientFromMinikube(currentKubeContext string) (Client, error) { }, nil } -func GetMinikubeEnvironment() (map[string]string, error) { - out, err := command.Output(context.TODO(), "", "minikube", "docker-env", "--shell", "none") +func GetMinikubeEnvironment(ctx context.Context) (map[string]string, error) { + out, err := command.Output(ctx, "", "minikube", "docker-env", "--shell", "none") if err != nil { if ee, ok := err.(*exec.ExitError); ok { diff --git a/pkg/devspace/docker/images.go b/pkg/devspace/docker/images.go index b9e35f9deb..53a366eb24 100644 --- a/pkg/devspace/docker/images.go +++ b/pkg/devspace/docker/images.go @@ -11,13 +11,13 @@ import ( ) // DeleteImageByName deletes an image by name -func (c *client) DeleteImageByName(imageName string, log log.Logger) ([]types.ImageDeleteResponseItem, error) { - return c.DeleteImageByFilter(filters.NewArgs(filters.Arg("reference", strings.TrimSpace(imageName))), log) +func (c *client) DeleteImageByName(ctx context.Context, imageName string, log log.Logger) ([]types.ImageDeleteResponseItem, error) { + return c.DeleteImageByFilter(ctx, filters.NewArgs(filters.Arg("reference", strings.TrimSpace(imageName))), log) } // DeleteImageByFilter deletes an image by filter -func (c *client) DeleteImageByFilter(filter filters.Args, log log.Logger) ([]types.ImageDeleteResponseItem, error) { - summary, err := c.ImageList(context.Background(), types.ImageListOptions{ +func (c *client) DeleteImageByFilter(ctx context.Context, filter filters.Args, log log.Logger) ([]types.ImageDeleteResponseItem, error) { + summary, err := c.ImageList(ctx, types.ImageListOptions{ Filters: filter, }) if err != nil { @@ -26,7 +26,7 @@ func (c *client) DeleteImageByFilter(filter filters.Args, log log.Logger) ([]typ responseItems := make([]types.ImageDeleteResponseItem, 0, 128) for _, image := range summary { - deleteResponse, err := c.ImageRemove(context.Background(), image.ID, types.ImageRemoveOptions{ + deleteResponse, err := c.ImageRemove(ctx, image.ID, types.ImageRemoveOptions{ PruneChildren: true, Force: true, }) diff --git a/pkg/devspace/hook/logs.go b/pkg/devspace/hook/logs.go index 9378644bd4..f240fb95c5 100644 --- a/pkg/devspace/hook/logs.go +++ b/pkg/devspace/hook/logs.go @@ -1,7 +1,6 @@ package hook import ( - "context" devspacecontext "github.com/loft-sh/devspace/pkg/devspace/context" "io" @@ -22,7 +21,7 @@ type remoteLogsHook struct { func (r *remoteLogsHook) ExecuteRemotely(ctx *devspacecontext.Context, hook *latest.HookConfig, podContainer *selector.SelectedPodContainer) error { ctx.Log.Infof("Execute hook '%s' in container '%s/%s/%s'", ansi.Color(hookName(hook), "white+b"), podContainer.Pod.Namespace, podContainer.Pod.Name, podContainer.Container.Name) - reader, err := ctx.KubeClient.Logs(context.TODO(), podContainer.Pod.Namespace, podContainer.Pod.Name, podContainer.Container.Name, false, hook.Logs.TailLines, true) + reader, err := ctx.KubeClient.Logs(ctx.Context, podContainer.Pod.Namespace, podContainer.Pod.Name, podContainer.Container.Name, false, hook.Logs.TailLines, true) if err != nil { return err } diff --git a/pkg/devspace/hook/wait.go b/pkg/devspace/hook/wait.go index 3c63ce7e4e..8b1b3da73d 100644 --- a/pkg/devspace/hook/wait.go +++ b/pkg/devspace/hook/wait.go @@ -51,7 +51,7 @@ func (r *waitHook) Execute(ctx *devspacecontext.Context, hook *latest.HookConfig } } - err = r.execute(hook, ctx.KubeClient, imageSelectors, ctx.Log) + err = r.execute(ctx.Context, hook, ctx.KubeClient, imageSelectors, ctx.Log) if err != nil { return err } @@ -60,7 +60,7 @@ func (r *waitHook) Execute(ctx *devspacecontext.Context, hook *latest.HookConfig return nil } -func (r *waitHook) execute(hook *latest.HookConfig, client kubectl.Client, imageSelector []imageselector.ImageSelector, log logpkg.Logger) error { +func (r *waitHook) execute(ctx context.Context, hook *latest.HookConfig, client kubectl.Client, imageSelector []imageselector.ImageSelector, log logpkg.Logger) error { labelSelector := "" if len(hook.Container.LabelSelector) > 0 { labelSelector = labels.Set(hook.Container.LabelSelector).String() @@ -73,7 +73,7 @@ func (r *waitHook) execute(hook *latest.HookConfig, client kubectl.Client, image // wait until the defined condition will be true, this will wait initially 2 seconds err := wait.Poll(time.Second*2, time.Duration(timeout)*time.Second, func() (done bool, err error) { - podContainers, err := selector.NewFilter(client).SelectContainers(context.TODO(), selector.Selector{ + podContainers, err := selector.NewFilter(client).SelectContainers(ctx, selector.Selector{ ImageSelector: targetselector.ToStringImageSelector(imageSelector), LabelSelector: labelSelector, Pod: hook.Container.Pod, diff --git a/pkg/devspace/pipeline/engine/pipelinehandler/commands/ensure_pull_secrets.go b/pkg/devspace/pipeline/engine/pipelinehandler/commands/ensure_pull_secrets.go index b728e2752f..5e238dcda3 100644 --- a/pkg/devspace/pipeline/engine/pipelinehandler/commands/ensure_pull_secrets.go +++ b/pkg/devspace/pipeline/engine/pipelinehandler/commands/ensure_pull_secrets.go @@ -55,7 +55,7 @@ func EnsurePullSecrets(ctx *devspacecontext.Context, args []string) error { return fmt.Errorf("either specify 'ensure_pull_secrets --all' or 'ensure_pull_secrets pullSecret1 pullSecret2'") } - dockerClient, err := docker.NewClient(ctx.Log) + dockerClient, err := docker.NewClient(ctx.Context, ctx.Log) if err != nil { ctx.Log.Debugf("Error creating docker client: %v", err) dockerClient = nil diff --git a/pkg/devspace/pullsecrets/init.go b/pkg/devspace/pullsecrets/init.go index 4865485106..b0eae522b6 100644 --- a/pkg/devspace/pullsecrets/init.go +++ b/pkg/devspace/pullsecrets/init.go @@ -162,7 +162,7 @@ func (r *client) createPullSecret(ctx *devspacecontext.Context, dockerClient doc username := pullSecret.Username password := pullSecret.Password if username == "" && password == "" && dockerClient != nil { - authConfig, _ := dockerClient.GetAuthConfig(pullSecret.Registry, true) + authConfig, _ := dockerClient.GetAuthConfig(ctx.Context, pullSecret.Registry, true) if authConfig != nil { username = authConfig.Username password = authConfig.Password diff --git a/pkg/devspace/server/logs.go b/pkg/devspace/server/logs.go index 260b14a8f0..8fbad9ad27 100644 --- a/pkg/devspace/server/logs.go +++ b/pkg/devspace/server/logs.go @@ -145,7 +145,7 @@ func (h *handler) logs(w http.ResponseWriter, r *http.Request) { defer ws.Close() // Open logs connection - reader, err := client.Logs(context.Background(), namespace[0], name[0], container[0], false, ptr.Int64(100), true) + reader, err := client.Logs(context.TODO(), namespace[0], name[0], container[0], false, ptr.Int64(100), true) if err != nil { h.ctx.Log.Errorf("Error in %s: %v", r.URL.String(), err) websocketError(ws, err) diff --git a/pkg/devspace/server/port_forward.go b/pkg/devspace/server/port_forward.go index d096724415..4444ba666e 100644 --- a/pkg/devspace/server/port_forward.go +++ b/pkg/devspace/server/port_forward.go @@ -92,7 +92,6 @@ func (h *handler) forward(w http.ResponseWriter, r *http.Request) { ports := []string{strconv.Itoa(checkPort) + ":" + targetPort[0]} pf, err := kubectl.NewPortForwarder(client, pod, ports, []string{"127.0.0.1"}, stopChan, readyChan, nil) - if err != nil { h.ctx.Log.Errorf("Error in %s: %v", r.URL.String(), err) http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/pkg/devspace/services/attach/attach.go b/pkg/devspace/services/attach/attach.go index e85dd19ff6..9119bc7ae5 100644 --- a/pkg/devspace/services/attach/attach.go +++ b/pkg/devspace/services/attach/attach.go @@ -73,12 +73,17 @@ func StartAttach( return } - ctx.Log.WriteString(logrus.InfoLevel, "\n") - ctx.Log.Infof("Restarting attach because: %s", err) - time.Sleep(time.Second * 3) + ctx.Log.Infof("Restarting because: %s", err) + select { + case <-ctx.Context.Done(): + return + case <-time.After(time.Second * 3): + } err = StartAttach(ctx, devContainer, selector, stdout, stderr, stdin, parent) return } + + ctx.Log.Debugf("Stopped attach") }() before := log.GetBaseInstance().GetLevel() diff --git a/pkg/devspace/services/inject/inject.go b/pkg/devspace/services/inject/inject.go index 09485ccfa4..ac6a119a8c 100644 --- a/pkg/devspace/services/inject/inject.go +++ b/pkg/devspace/services/inject/inject.go @@ -80,6 +80,7 @@ func InjectDevSpaceHelper(ctx context.Context, client kubectl.Client, pod *v1.Po localHelperName := "devspacehelper" + arch stdout, _, err := client.ExecBuffered(ctx, pod, container, []string{DevSpaceHelperContainerPath, "version"}, nil) if err != nil || version != string(stdout) { + log.Info("Inject devspacehelper...") homedir, err := homedir.Dir() if err != nil { return err @@ -98,8 +99,6 @@ func InjectDevSpaceHelper(ctx context.Context, client kubectl.Client, pod *v1.Po log.Debugf("Couldn't download devspacehelper in container, error: %s", err) } - log.Info("Trying to inject devspacehelper from local machine") - // check if we can find it in the assets helperBytes, err := assets.Asset("release/" + localHelperName) if err == nil { diff --git a/pkg/devspace/services/logs/logs.go b/pkg/devspace/services/logs/logs.go index 169dc09c4a..efdada8e7b 100644 --- a/pkg/devspace/services/logs/logs.go +++ b/pkg/devspace/services/logs/logs.go @@ -8,7 +8,6 @@ import ( "github.com/loft-sh/devspace/pkg/util/scanner" "github.com/mgutz/ansi" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "io" "time" ) @@ -40,16 +39,20 @@ func StartLogs( defer func() { if err != nil { if ctx.IsDone() { - err = nil return } - ctx.Log.WriteString(logrus.InfoLevel, "\n") - ctx.Log.Infof("Restarting logs because: %s", err) - time.Sleep(time.Second * 3) + ctx.Log.Infof("Restarting because: %s", err) + select { + case <-ctx.Context.Done(): + return + case <-time.After(time.Second * 3): + } err = StartLogs(ctx, devContainer, selector) return } + + ctx.Log.Debugf("Stopped logs") }() containerObj, err := selector.WithContainer(devContainer.Container).SelectSingleContainer(ctx.Context, ctx.KubeClient, ctx.Log) diff --git a/pkg/devspace/services/portforwarding/portforwarding.go b/pkg/devspace/services/portforwarding/portforwarding.go index 9425bd4bd4..6d1509482d 100644 --- a/pkg/devspace/services/portforwarding/portforwarding.go +++ b/pkg/devspace/services/portforwarding/portforwarding.go @@ -7,6 +7,7 @@ import ( "github.com/loft-sh/devspace/pkg/devspace/kubectl" "github.com/loft-sh/devspace/pkg/devspace/kubectl/portforward" "github.com/loft-sh/devspace/pkg/util/tomb" + "github.com/mgutz/ansi" "strings" "time" @@ -15,7 +16,6 @@ import ( "github.com/loft-sh/devspace/pkg/devspace/hook" "github.com/loft-sh/devspace/pkg/devspace/services/sync" "github.com/loft-sh/devspace/pkg/devspace/services/targetselector" - logpkg "github.com/loft-sh/devspace/pkg/util/log" "github.com/pkg/errors" ) @@ -114,6 +114,7 @@ func StartForwarding(ctx *devspacecontext.Context, name string, portMappings []* } ports := make([]string, len(portMappings)) + portsFormatted := make([]string, len(portMappings)) addresses := make([]string, len(portMappings)) for index, value := range portMappings { if value.Port == "" { @@ -135,6 +136,7 @@ func StartForwarding(ctx *devspacecontext.Context, name string, portMappings []* } ports[index] = fmt.Sprintf("%d:%d", int(localPort), int(remotePort)) + portsFormatted[index] = ansi.Color(fmt.Sprintf("%d -> %d", int(localPort), int(remotePort)), "white+b") if value.BindAddress == "" { addresses[index] = "localhost" } else { @@ -161,7 +163,7 @@ func StartForwarding(ctx *devspacecontext.Context, name string, portMappings []* case <-ctx.Context.Done(): return nil case <-readyChan: - ctx.Log.Donef("Port forwarding started on %s", strings.Join(ports, ", ")) + ctx.Log.Donef("Port forwarding started on: %s", strings.Join(portsFormatted, ", ")) case err := <-errorChan: if ctx.IsDone() { return nil @@ -173,41 +175,44 @@ func StartForwarding(ctx *devspacecontext.Context, name string, portMappings []* } parent.Go(func() error { - fileLog := logpkg.GetDevPodFileLogger(name) select { case <-ctx.Context.Done(): pf.Close() - stopPortForwarding(ctx, name, portMappings, fileLog, parent) + stopPortForwarding(ctx, name, portMappings, parent) case err := <-errorChan: if ctx.IsDone() { pf.Close() - stopPortForwarding(ctx, name, portMappings, fileLog, parent) + stopPortForwarding(ctx, name, portMappings, parent) return nil } if err != nil { - fileLog.Errorf("Portforwarding restarting, because: %v", err) - sync.PrintPodError(ctx.Context, ctx.KubeClient, pod, fileLog) + ctx.Log.Errorf("Restarting because: %v", err) + shouldExit := sync.PrintPodError(ctx.Context, ctx.KubeClient, pod, ctx.Log) pf.Close() - hook.LogExecuteHooks(ctx.WithLogger(fileLog), map[string]interface{}{ + hook.LogExecuteHooks(ctx, map[string]interface{}{ "port_forwarding_config": portMappings, "error": err, }, hook.EventsForSingle("restart:portForwarding", name).With("portForwarding.restart")...) + if shouldExit { + stopPortForwarding(ctx, name, portMappings, parent) + return nil + } for { - err = StartForwarding(ctx.WithLogger(fileLog), name, portMappings, selector, parent) + err = StartForwarding(ctx, name, portMappings, selector, parent) if err != nil { - hook.LogExecuteHooks(ctx.WithLogger(fileLog), map[string]interface{}{ + hook.LogExecuteHooks(ctx, map[string]interface{}{ "port_forwarding_config": portMappings, "error": err, }, hook.EventsForSingle("restart:portForwarding", name).With("portForwarding.restart")...) - fileLog.Errorf("Error restarting port-forwarding: %v", err) - fileLog.Errorf("Will try again in 15 seconds") + ctx.Log.Errorf("Error restarting port-forwarding: %v", err) + ctx.Log.Errorf("Will try again in 15 seconds") select { case <-time.After(time.Second * 15): continue case <-ctx.Context.Done(): - stopPortForwarding(ctx, name, portMappings, fileLog, parent) + stopPortForwarding(ctx, name, portMappings, parent) return nil } } @@ -222,10 +227,12 @@ func StartForwarding(ctx *devspacecontext.Context, name string, portMappings []* return nil } -func stopPortForwarding(ctx *devspacecontext.Context, name string, portMappings []*latest.PortMapping, fileLog logpkg.Logger, parent *tomb.Tomb) { - hook.LogExecuteHooks(ctx.WithLogger(fileLog), map[string]interface{}{ +func stopPortForwarding(ctx *devspacecontext.Context, name string, portMappings []*latest.PortMapping, parent *tomb.Tomb) { + hook.LogExecuteHooks(ctx, map[string]interface{}{ "port_forwarding_config": portMappings, }, hook.EventsForSingle("stop:portForwarding", name).With("portForwarding.stop")...) parent.Kill(nil) - fileLog.Done("Stopped port forwarding") + for _, m := range portMappings { + ctx.Log.Debugf("Stopped port forwarding %v", m.Port) + } } diff --git a/pkg/devspace/services/portforwarding/reverse_portforwarding.go b/pkg/devspace/services/portforwarding/reverse_portforwarding.go index 21642e0d3a..b52bc9f6c9 100644 --- a/pkg/devspace/services/portforwarding/reverse_portforwarding.go +++ b/pkg/devspace/services/portforwarding/reverse_portforwarding.go @@ -3,7 +3,6 @@ package portforwarding import ( devspacecontext "github.com/loft-sh/devspace/pkg/devspace/context" "github.com/loft-sh/devspace/pkg/devspace/services/sync" - logpkg "github.com/loft-sh/devspace/pkg/util/log" "github.com/loft-sh/devspace/pkg/util/tomb" "io" "time" @@ -22,14 +21,12 @@ func StartReversePortForwarding(ctx *devspacecontext.Context, name, arch string, return nil } - fileLog := logpkg.GetDevPodFileLogger(name) container, err := selector.SelectSingleContainer(ctx.Context, ctx.KubeClient, ctx.Log) if err != nil { return errors.Wrap(err, "error selecting container") } // make sure the DevSpace helper binary is injected - ctx.Log.Info("Reverse-Port-Forwarding: Inject devspacehelper...") err = inject.InjectDevSpaceHelper(ctx.Context, ctx.KubeClient, container.Pod, container.Container.Name, arch, ctx.Log) if err != nil { return err @@ -41,7 +38,7 @@ func StartReversePortForwarding(ctx *devspacecontext.Context, name, arch string, stdinReader, stdinWriter := io.Pipe() stdoutReader, stdoutWriter := io.Pipe() go func() { - err := sync.StartStream(ctx.Context, ctx.KubeClient, container.Pod, container.Container.Name, []string{inject.DevSpaceHelperContainerPath, "tunnel"}, stdinReader, stdoutWriter, false, fileLog) + err := sync.StartStream(ctx.Context, ctx.KubeClient, container.Pod, container.Container.Name, []string{inject.DevSpaceHelperContainerPath, "tunnel"}, stdinReader, stdoutWriter, false, ctx.Log) if err != nil { errorChan <- errors.Errorf("connection lost to pod %s/%s: %v", container.Pod.Namespace, container.Pod.Name, err) } @@ -60,41 +57,45 @@ func StartReversePortForwarding(ctx *devspacecontext.Context, name, arch string, close(closeChan) _ = stdinWriter.Close() _ = stdoutWriter.Close() - doneReverseForwarding(ctx, name, portForwarding, fileLog, parent) + doneReverseForwarding(ctx, name, portForwarding, parent) case err := <-errorChan: if ctx.IsDone() { close(closeChan) _ = stdinWriter.Close() _ = stdoutWriter.Close() - doneReverseForwarding(ctx, name, portForwarding, fileLog, parent) + doneReverseForwarding(ctx, name, portForwarding, parent) return nil } if err != nil { - fileLog.Errorf("Reverse portforwarding restarting, because: %v", err) - sync.PrintPodError(ctx.Context, ctx.KubeClient, container.Pod, fileLog) + ctx.Log.Errorf("Restarting because: %v", err) + shouldExit := sync.PrintPodError(ctx.Context, ctx.KubeClient, container.Pod, ctx.Log) close(closeChan) _ = stdinWriter.Close() _ = stdoutWriter.Close() - hook.LogExecuteHooks(ctx.WithLogger(fileLog), map[string]interface{}{ + hook.LogExecuteHooks(ctx, map[string]interface{}{ "reverse_port_forwarding_config": portForwarding, "error": err, }, hook.EventsForSingle("restart:reversePortForwarding", name).With("reversePortForwarding.restart")...) + if shouldExit { + doneReverseForwarding(ctx, name, portForwarding, parent) + return nil + } for { - err = StartReversePortForwarding(ctx.WithLogger(fileLog), name, arch, portForwarding, selector, parent) + err = StartReversePortForwarding(ctx, name, arch, portForwarding, selector, parent) if err != nil { - hook.LogExecuteHooks(ctx.WithLogger(fileLog), map[string]interface{}{ + hook.LogExecuteHooks(ctx, map[string]interface{}{ "reverse_port_forwarding_config": portForwarding, "error": err, }, hook.EventsForSingle("restart:reversePortForwarding", name).With("reversePortForwarding.restart")...) - fileLog.Errorf("Error restarting reverse port-forwarding: %v", err) - fileLog.Errorf("Will try again in 15 seconds") + ctx.Log.Errorf("Error restarting reverse port-forwarding: %v", err) + ctx.Log.Errorf("Will try again in 15 seconds") select { case <-time.After(time.Second * 15): continue case <-ctx.Context.Done(): - doneReverseForwarding(ctx, name, portForwarding, fileLog, parent) + doneReverseForwarding(ctx, name, portForwarding, parent) return nil } } @@ -109,10 +110,12 @@ func StartReversePortForwarding(ctx *devspacecontext.Context, name, arch string, return nil } -func doneReverseForwarding(ctx *devspacecontext.Context, name string, portForwarding []*latest.PortMapping, fileLog logpkg.Logger, parent *tomb.Tomb) { - hook.LogExecuteHooks(ctx.WithLogger(fileLog), map[string]interface{}{ +func doneReverseForwarding(ctx *devspacecontext.Context, name string, portForwarding []*latest.PortMapping, parent *tomb.Tomb) { + hook.LogExecuteHooks(ctx, map[string]interface{}{ "reverse_port_forwarding_config": portForwarding, }, hook.EventsForSingle("stop:reversePortForwarding", name).With("reversePortForwarding.stop")...) - fileLog.Done("Stopped reverse port forwarding %s", name) parent.Kill(nil) + for _, m := range portForwarding { + ctx.Log.Debugf("Stopped reverse port forwarding %v", m.Port) + } } diff --git a/pkg/devspace/services/ssh/ssh.go b/pkg/devspace/services/ssh/ssh.go index bf2839450e..66926f4fef 100644 --- a/pkg/devspace/services/ssh/ssh.go +++ b/pkg/devspace/services/ssh/ssh.go @@ -3,6 +3,7 @@ package ssh import ( "bytes" "fmt" + "github.com/mgutz/ansi" "io" "strconv" "strings" @@ -122,7 +123,6 @@ func startSSHWithRestart(ctx *devspacecontext.Context, arch, addr, sshHost strin } // make sure the DevSpace helper binary is injected - ctx.Log.Info("SSH: Inject devspacehelper...") err = inject.InjectDevSpaceHelper(ctx.Context, ctx.KubeClient, container.Pod, container.Container.Name, arch, ctx.Log) if err != nil { return err @@ -183,6 +183,6 @@ func startSSHWithRestart(ctx *devspacecontext.Context, arch, addr, sshHost strin return nil }) - ctx.Log.Donef("SSH started on host %s. Use 'ssh %s' to connect", sshHost, sshHost) + ctx.Log.Donef("Use '%s' to connect via SSH", ansi.Color(fmt.Sprintf("ssh %s", sshHost), "white+b")) return nil } diff --git a/pkg/devspace/services/sync/controller.go b/pkg/devspace/services/sync/controller.go index 2205dc7cdf..9e80afc56c 100644 --- a/pkg/devspace/services/sync/controller.go +++ b/pkg/devspace/services/sync/controller.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "github.com/loft-sh/devspace/pkg/util/tomb" + "github.com/mgutz/ansi" "io" "os" "path/filepath" @@ -190,8 +191,12 @@ func (c *controller) startWithWait(ctx *devspacecontext.Context, options *Option "ERROR": err, }, hook.EventsForSingle("restart:sync", options.Name).With("sync.restart")...) - options.SyncLog.Info("Restarting sync...") - PrintPodError(ctx.Context, ctx.KubeClient, pod.Pod, options.SyncLog) + ctx.Log.Errorf("Restarting because: %v", err) + shouldExit := PrintPodError(ctx.Context, ctx.KubeClient, pod.Pod, ctx.Log) + if shouldExit { + syncStop(ctx, client, options, parent) + return nil + } for { err := c.startWithWait(ctx.WithLogger(options.SyncLog), options, parent) if err != nil { @@ -233,24 +238,30 @@ func syncDone(ctx *devspacecontext.Context, options *Options, parent *tomb.Tomb) hook.LogExecuteHooks(ctx.WithLogger(options.SyncLog), map[string]interface{}{ "sync_config": options.SyncConfig, }, hook.EventsForSingle("stop:sync", options.Name).With("sync.stop")...) + ctx.Log.Debugf("Stopped sync %s", options.SyncConfig.Path) } -func PrintPodError(ctx context.Context, kubeClient kubectl.Client, pod *v1.Pod, log logpkg.Logger) { +func PrintPodError(ctx context.Context, kubeClient kubectl.Client, pod *v1.Pod, log logpkg.Logger) bool { // check if pod still exists newPod, err := kubeClient.KubeClient().CoreV1().Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{}) if err != nil { if kerrors.IsNotFound(err) { log.Errorf("Restarted because old pod %s/%s seems to be erased", pod.Namespace, pod.Name) - return + return true } - return + return false + } + if newPod.DeletionTimestamp != nil { + return true } podStatus := kubectl.GetPodStatus(newPod) if podStatus != "Running" { log.Errorf("Restarted because old pod %s/%s has status %s", pod.Namespace, pod.Name, podStatus) } + + return false } func (c *controller) startSync(ctx *devspacecontext.Context, options *Options, onInitUploadDone chan struct{}, onInitDownloadDone chan struct{}, onDone chan struct{}, onError chan error) (*sync.Sync, *selector.SelectedPodContainer, error) { @@ -263,7 +274,7 @@ func (c *controller) startSync(ctx *devspacecontext.Context, options *Options, o return nil, nil, errors.Wrap(err, "error selecting container") } - ctx.Log.Info("Starting sync...") + ctx.Log.Debug("Starting sync...") syncClient, err := c.initClient(ctx, container.Pod, options.Arch, container.Container.Name, syncConfig, options.Starter, options.Verbose, options.SyncLog) if err != nil { return nil, nil, errors.Wrap(err, "start sync") @@ -274,12 +285,11 @@ func (c *controller) startSync(ctx *devspacecontext.Context, options *Options, o return nil, nil, errors.Errorf("Sync error: %v", err) } - syncPath := "." - if syncConfig.Path != "" { - syncPath = syncConfig.Path + localPath, remotePath, err := ParseSyncPath(syncConfig.Path) + if err == nil { + ctx.Log.Donef("Sync started on: %s", ansi.Color(fmt.Sprintf("%s <-> %s", localPath, remotePath), "white+b")) } - ctx.Log.Donef("Sync started on %s", syncPath) return syncClient, container, nil } @@ -547,7 +557,7 @@ func StartStream(ctx context.Context, client kubectl.Client, pod *v1.Pod, contai defer stderrReader.Close() s := scanner.NewScanner(stderrReader) for s.Scan() { - log.Info("Helper - " + s.Text()) + log.Debug("Helper - " + s.Text()) } if s.Err() != nil && s.Err() != context.Canceled { log.Warnf("Helper - Error streaming logs: %v", s.Err()) diff --git a/pkg/devspace/services/targetselector/until_newest_running.go b/pkg/devspace/services/targetselector/until_newest_running.go index f79d60de83..b627273aec 100644 --- a/pkg/devspace/services/targetselector/until_newest_running.go +++ b/pkg/devspace/services/targetselector/until_newest_running.go @@ -52,7 +52,7 @@ func (u *untilNewestRunning) SelectPod(ctx context.Context, client kubectl.Clien if now.Before(u.initialDelay) { return false, nil, nil } else if len(pods) == 0 { - u.podInfoPrinter.PrintNotFoundWarning(client, namespace, log) + u.podInfoPrinter.PrintNotFoundWarning(ctx, client, namespace, log) return false, nil, nil } @@ -63,7 +63,7 @@ func (u *untilNewestRunning) SelectPod(ctx context.Context, client kubectl.Clien u.podInfoPrinter.PrintPodWarning(pods[0], log) return false, nil, nil } else if kubectl.GetPodStatus(pods[0]) != "Running" { - u.podInfoPrinter.PrintPodInfo(client, pods[0], log) + u.podInfoPrinter.PrintPodInfo(ctx, client, pods[0], log) return false, nil, nil } @@ -75,7 +75,7 @@ func (u *untilNewestRunning) SelectContainer(ctx context.Context, client kubectl if now.Before(u.initialDelay) { return false, nil, nil } else if len(containers) == 0 { - u.podInfoPrinter.PrintNotFoundWarning(client, namespace, log) + u.podInfoPrinter.PrintNotFoundWarning(ctx, client, namespace, log) return false, nil, nil } @@ -86,7 +86,7 @@ func (u *untilNewestRunning) SelectContainer(ctx context.Context, client kubectl u.podInfoPrinter.PrintPodWarning(containers[0].Pod, log) return false, nil, nil } else if !IsContainerRunning(containers[0]) { - u.podInfoPrinter.PrintPodInfo(client, containers[0].Pod, log) + u.podInfoPrinter.PrintPodInfo(ctx, client, containers[0].Pod, log) return false, nil, nil } @@ -101,7 +101,7 @@ type PodInfoPrinter struct { printedInitContainers []string } -func (u *PodInfoPrinter) PrintPodInfo(client kubectl.Client, pod *v1.Pod, log log.Logger) { +func (u *PodInfoPrinter) PrintPodInfo(ctx context.Context, client kubectl.Client, pod *v1.Pod, log log.Logger) { u.lastMutex.Lock() defer u.lastMutex.Unlock() @@ -112,7 +112,7 @@ func (u *PodInfoPrinter) PrintPodInfo(client kubectl.Client, pod *v1.Pod, log lo if !stringutil.Contains(u.printedInitContainers, initContainer.Name) && initContainer.State.Running != nil { // show logs of this currently running init container log.Debugf("Printing init container logs of pod %s", pod.Name) - reader, err := client.Logs(context.TODO(), pod.Namespace, pod.Name, initContainer.Name, false, nil, true) + reader, err := client.Logs(ctx, pod.Namespace, pod.Name, initContainer.Name, false, nil, true) if err != nil { log.Warnf("Error reading init container logs: %v", err) } else { @@ -129,7 +129,7 @@ func (u *PodInfoPrinter) PrintPodInfo(client kubectl.Client, pod *v1.Pod, log lo } status := kubectl.GetPodStatus(pod) - u.shownEvents = displayWarnings(relevantObjectsFromPod(pod), pod.Namespace, client, u.shownEvents, log) + u.shownEvents = displayWarnings(ctx, relevantObjectsFromPod(pod), pod.Namespace, client, u.shownEvents, log) if status != "Running" { log.Warnf("DevSpace is waiting, because Pod %s has status: %s", pod.Name, status) } @@ -137,12 +137,12 @@ func (u *PodInfoPrinter) PrintPodInfo(client kubectl.Client, pod *v1.Pod, log lo } } -func (u *PodInfoPrinter) PrintNotFoundWarning(client kubectl.Client, namespace string, log log.Logger) { +func (u *PodInfoPrinter) PrintNotFoundWarning(ctx context.Context, client kubectl.Client, namespace string, log log.Logger) { u.lastMutex.Lock() defer u.lastMutex.Unlock() if time.Since(u.LastWarning) > time.Second*10 { - u.shownEvents = displayWarnings([]relevantObject{ + u.shownEvents = displayWarnings(ctx, []relevantObject{ { Kind: "StatefulSet", }, @@ -178,8 +178,8 @@ type relevantObject struct { UID string } -func displayWarnings(relevantObjects []relevantObject, namespace string, client kubectl.Client, filter []string, log log.Logger) []string { - events, err := client.KubeClient().CoreV1().Events(namespace).List(context.TODO(), metav1.ListOptions{}) +func displayWarnings(ctx context.Context, relevantObjects []relevantObject, namespace string, client kubectl.Client, filter []string, log log.Logger) []string { + events, err := client.KubeClient().CoreV1().Events(namespace).List(ctx, metav1.ListOptions{}) if err != nil { log.Debugf("Error retrieving pod events: %v", err) return nil diff --git a/pkg/devspace/services/targetselector/until_not_terminating.go b/pkg/devspace/services/targetselector/until_not_terminating.go index 7c1b3e41bf..2499f3f4a3 100644 --- a/pkg/devspace/services/targetselector/until_not_terminating.go +++ b/pkg/devspace/services/targetselector/until_not_terminating.go @@ -46,7 +46,7 @@ func (u *untilNotTerminating) SelectPod(ctx context.Context, client kubectl.Clie return false, nil, nil } else if len(pods) == 0 { if now.After(u.initialDelay.Add(time.Second * 6)) { - u.podInfoPrinter.PrintNotFoundWarning(client, namespace, log) + u.podInfoPrinter.PrintNotFoundWarning(ctx, client, namespace, log) } return false, nil, nil @@ -68,7 +68,7 @@ func (u *untilNotTerminating) SelectContainer(ctx context.Context, client kubect return false, nil, nil } else if len(containers) == 0 { if now.After(u.initialDelay.Add(time.Second * 6)) { - u.podInfoPrinter.PrintNotFoundWarning(client, namespace, log) + u.podInfoPrinter.PrintNotFoundWarning(ctx, client, namespace, log) } return false, nil, nil diff --git a/pkg/devspace/services/targetselector/until_not_waiting.go b/pkg/devspace/services/targetselector/until_not_waiting.go index 49437612f3..3d5869b9b2 100644 --- a/pkg/devspace/services/targetselector/until_not_waiting.go +++ b/pkg/devspace/services/targetselector/until_not_waiting.go @@ -47,7 +47,7 @@ func (u *untilNotWaiting) SelectPod(ctx context.Context, client kubectl.Client, return false, nil, nil } else if len(pods) == 0 { if now.After(u.initialDelay.Add(time.Second * 6)) { - u.podInfoPrinter.PrintNotFoundWarning(client, namespace, log) + u.podInfoPrinter.PrintNotFoundWarning(ctx, client, namespace, log) } return false, nil, nil @@ -69,7 +69,7 @@ func (u *untilNotWaiting) SelectContainer(ctx context.Context, client kubectl.Cl return false, nil, nil } else if len(containers) == 0 { if now.After(u.initialDelay.Add(time.Second * 6)) { - u.podInfoPrinter.PrintNotFoundWarning(client, namespace, log) + u.podInfoPrinter.PrintNotFoundWarning(ctx, client, namespace, log) } return false, nil, nil diff --git a/pkg/devspace/services/terminal/terminal.go b/pkg/devspace/services/terminal/terminal.go index 34b8d14281..b8ed137880 100644 --- a/pkg/devspace/services/terminal/terminal.go +++ b/pkg/devspace/services/terminal/terminal.go @@ -69,14 +69,14 @@ func StartTerminalFromCMD( // 130 - Script terminated by Control-C if restart && IsUnexpectedExitCode(exitError.Code) { ctx.Log.WriteString(logrus.InfoLevel, "\n") - ctx.Log.Infof("Restarting terminal because: %s", err) + ctx.Log.Infof("Restarting because: %s", err) return StartTerminalFromCMD(ctx, selector, command, wait, restart, stdout, stderr, stdin) } return exitError.Code, nil } else if restart { ctx.Log.WriteString(logrus.InfoLevel, "\n") - ctx.Log.Infof("Restarting terminal because: %s", err) + ctx.Log.Infof("Restarting because: %s", err) return StartTerminalFromCMD(ctx, selector, command, wait, restart, stdout, stderr, stdin) } @@ -104,12 +104,17 @@ func StartTerminal( return } - ctx.Log.WriteString(logrus.InfoLevel, "\n") - ctx.Log.Infof("Restarting terminal because: %s", err) - time.Sleep(time.Second * 3) + ctx.Log.Infof("Restarting because: %s", err) + select { + case <-ctx.Context.Done(): + return + case <-time.After(time.Second * 3): + } err = StartTerminal(ctx, devContainer, selector, stdout, stderr, stdin, parent) return } + + ctx.Log.Debugf("Stopped terminal") }() command := getCommand(devContainer) @@ -118,7 +123,7 @@ func StartTerminal( return err } - ctx.Log.Infof("Opening shell to pod:container %s:%s", ansi.Color(container.Pod.Name, "white+b"), ansi.Color(container.Container.Name, "white+b")) + ctx.Log.Infof("Opening shell to %s:%s (pod:container)", ansi.Color(container.Container.Name, "white+b"), ansi.Color(container.Pod.Name, "white+b")) errChan := make(chan error) parent.Go(func() error { interruptpkg.Global.Stop() diff --git a/pkg/devspace/sync/sync.go b/pkg/devspace/sync/sync.go index 95867ebbda..311c28655c 100644 --- a/pkg/devspace/sync/sync.go +++ b/pkg/devspace/sync/sync.go @@ -430,7 +430,7 @@ func (s *Sync) Stop(fatalError error) { } } - s.log.Infof("Sync stopped") + s.log.Debugf("Sync stopped") if s.onDone != nil { close(s.onDone) } diff --git a/pkg/devspace/tunnel/client.go b/pkg/devspace/tunnel/client.go index 20d1051321..1e44897746 100644 --- a/pkg/devspace/tunnel/client.go +++ b/pkg/devspace/tunnel/client.go @@ -3,6 +3,7 @@ package tunnel import ( "fmt" "github.com/loft-sh/devspace/pkg/devspace/kubectl/portforward" + "github.com/mgutz/ansi" "io" "net" "strings" @@ -283,7 +284,7 @@ func StartReverseForward(ctx context.Context, reader io.ReadCloser, writer io.Wr }() // wait until close - log.Donef("Reverse port forwarding started at %d->%d (%s/%s)", remotePort, localPort, namespace, name) + log.Donef("Port forwarding started on: %s", ansi.Color(fmt.Sprintf("%d <- %d", localPort, remotePort), "white+b")) <-closeStream }(c, int32(localPort), int32(remotePort)) closeStreams[i] = c diff --git a/pkg/util/factory/factory.go b/pkg/util/factory/factory.go index f54938c60a..0a95acd058 100644 --- a/pkg/util/factory/factory.go +++ b/pkg/util/factory/factory.go @@ -1,6 +1,7 @@ package factory import ( + "context" "github.com/loft-sh/devspace/pkg/devspace/analyze" "github.com/loft-sh/devspace/pkg/devspace/build" "github.com/loft-sh/devspace/pkg/devspace/config/loader" @@ -39,8 +40,8 @@ type Factory interface { NewDependencyManager(ctx *devspacecontext.Context, configOptions *loader.ConfigOptions) dependency.Manager // NewDockerClient creates a new docker API client - NewDockerClient(log log.Logger) (docker.Client, error) - NewDockerClientWithMinikube(currentKubeContext string, preferMinikube bool, log log.Logger) (docker.Client, error) + NewDockerClient(ctx context.Context, log log.Logger) (docker.Client, error) + NewDockerClientWithMinikube(ctx context.Context, currentKubeContext string, preferMinikube bool, log log.Logger) (docker.Client, error) // NewBuildController & NewDeployController NewBuildController() build.Controller @@ -113,13 +114,13 @@ func (f *DefaultFactoryImpl) NewConfigureManager(config *latest.Config, generate } // NewDockerClient implements interface -func (f *DefaultFactoryImpl) NewDockerClient(log log.Logger) (docker.Client, error) { - return docker.NewClient(log) +func (f *DefaultFactoryImpl) NewDockerClient(ctx context.Context, log log.Logger) (docker.Client, error) { + return docker.NewClient(ctx, log) } // NewDockerClientWithMinikube implements interface -func (f *DefaultFactoryImpl) NewDockerClientWithMinikube(currentKubeContext string, preferMinikube bool, log log.Logger) (docker.Client, error) { - return docker.NewClientWithMinikube(currentKubeContext, preferMinikube, log) +func (f *DefaultFactoryImpl) NewDockerClientWithMinikube(ctx context.Context, currentKubeContext string, preferMinikube bool, log log.Logger) (docker.Client, error) { + return docker.NewClientWithMinikube(ctx, currentKubeContext, preferMinikube, log) } // NewKubeDefaultClient implements interface diff --git a/pkg/util/log/discard_logger.go b/pkg/util/log/discard_logger.go index 44b71ca87c..068e3184fb 100644 --- a/pkg/util/log/discard_logger.go +++ b/pkg/util/log/discard_logger.go @@ -124,6 +124,10 @@ func (d *DiscardLogger) WithPrefix(prefix string) Logger { return d } +func (d *DiscardLogger) WithPrefixColor(prefix, color string) Logger { + return d +} + func (d *DiscardLogger) ErrorStreamOnly() Logger { return d } diff --git a/pkg/util/log/file_logger.go b/pkg/util/log/file_logger.go index a315095aec..7b6eefb560 100644 --- a/pkg/util/log/file_logger.go +++ b/pkg/util/log/file_logger.go @@ -369,6 +369,16 @@ func (f *fileLogger) WithPrefix(prefix string) Logger { return &n } +func (f *fileLogger) WithPrefixColor(prefix, color string) Logger { + f.m.Lock() + defer f.m.Unlock() + + n := *f + n.m = &sync.Mutex{} + n.prefixes = append(n.prefixes, prefix) + return &n +} + func (f *fileLogger) ErrorStreamOnly() Logger { return f } diff --git a/pkg/util/log/logger.go b/pkg/util/log/logger.go index be84e0a273..6a04b6eae5 100644 --- a/pkg/util/log/logger.go +++ b/pkg/util/log/logger.go @@ -53,6 +53,7 @@ type Logger interface { WithLevel(level logrus.Level) Logger ErrorStreamOnly() Logger WithPrefix(prefix string) Logger + WithPrefixColor(prefix, color string) Logger WithSink(sink Logger) Logger AddSink(sink Logger) } diff --git a/pkg/util/log/stream_logger.go b/pkg/util/log/stream_logger.go index 599c9de168..1feb54a985 100644 --- a/pkg/util/log/stream_logger.go +++ b/pkg/util/log/stream_logger.go @@ -25,10 +25,22 @@ import ( var Colors = []string{ "blue", + "blue+h", + "blue+b", "green", + "green+h", + "green+b", "yellow", + "yellow+h", + "yellow+b", "magenta", + "magenta+h", + "magenta+b", "cyan", + "cyan+h", + "cyan+b", + "white", + "white+h", "white+b", } @@ -197,6 +209,21 @@ func (s *StreamLogger) WithPrefix(prefix string) Logger { return &n } +func (s *StreamLogger) WithPrefixColor(prefix, color string) Logger { + s.m.Lock() + defer s.m.Unlock() + + n := *s + n.m = &sync.Mutex{} + n.prefixes = []Prefix{} + n.prefixes = append(n.prefixes, s.prefixes...) + n.prefixes = append(n.prefixes, Prefix{ + Prefix: prefix, + Color: color, + }) + return &n +} + func (s *StreamLogger) AddSink(log Logger) { s.m.Lock() defer s.m.Unlock() diff --git a/pkg/util/log/testing/fake.go b/pkg/util/log/testing/fake.go index 2b002159e6..45318cc472 100644 --- a/pkg/util/log/testing/fake.go +++ b/pkg/util/log/testing/fake.go @@ -139,6 +139,10 @@ func (d *FakeLogger) WithPrefix(prefix string) log.Logger { return d } +func (d *FakeLogger) WithPrefixColor(prefix, color string) log.Logger { + return d +} + func (d *FakeLogger) ErrorStreamOnly() log.Logger { return d } diff --git a/pkg/util/tomb/context.go b/pkg/util/tomb/context.go index a07ecbd051..a4a171fead 100644 --- a/pkg/util/tomb/context.go +++ b/pkg/util/tomb/context.go @@ -38,10 +38,7 @@ func (t *Tomb) Context(parent context.Context) context.Context { defer t.m.Unlock() if parent == nil { - if t.parent == nil { - t.parent = context.Background() - } - parent = t.parent.(context.Context) + panic("parent context is empty") } if child, ok := t.child[parent]; ok {