diff --git a/e2e/tests/replacepods/replacepods.go b/e2e/tests/replacepods/replacepods.go index bcef7a0537..2b5d92e954 100644 --- a/e2e/tests/replacepods/replacepods.go +++ b/e2e/tests/replacepods/replacepods.go @@ -14,6 +14,7 @@ import ( "github.com/loft-sh/devspace/pkg/devspace/kubectl/selector" "github.com/loft-sh/devspace/pkg/util/factory" "github.com/onsi/ginkgo" + autoscalingv1 "k8s.io/api/autoscaling/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" @@ -269,6 +270,37 @@ var _ = DevSpaceDescribe("replacepods", func() { framework.ExpectNoError(err) framework.ExpectEqual(pods.Items[0].Spec.Containers[0].Image, "alpine:3.14") + // now scale down the devspace deployment and upscale the replaced deployment + _, err = kubeClient.Client().KubeClient().AppsV1().Deployments(ns).UpdateScale(context.TODO(), "replace-deployment-devspace", &autoscalingv1.Scale{ + ObjectMeta: metav1.ObjectMeta{Name: "replace-deployment-devspace", Namespace: ns}, + Spec: autoscalingv1.ScaleSpec{Replicas: 0}, + }, metav1.UpdateOptions{}) + framework.ExpectNoError(err) + _, err = kubeClient.Client().KubeClient().AppsV1().Deployments(ns).UpdateScale(context.TODO(), "replace-deployment", &autoscalingv1.Scale{ + ObjectMeta: metav1.ObjectMeta{Name: "replace-deployment", Namespace: ns}, + Spec: autoscalingv1.ScaleSpec{Replicas: 1}, + }, metav1.UpdateOptions{}) + framework.ExpectNoError(err) + + // rerun the devspace command + devCmd = &cmd.RunPipelineCmd{ + GlobalFlags: &flags.GlobalFlags{ + NoWarn: true, + Namespace: ns, + }, + Pipeline: "dev", + } + err = devCmd.RunDefault(f) + framework.ExpectNoError(err) + + // make sure the deployments are correctly scaled + deployment, err := kubeClient.Client().KubeClient().AppsV1().Deployments(ns).Get(context.TODO(), "replace-deployment-devspace", metav1.GetOptions{}) + framework.ExpectNoError(err) + framework.ExpectEqual(*deployment.Spec.Replicas, int32(1)) + deployment, err = kubeClient.Client().KubeClient().AppsV1().Deployments(ns).Get(context.TODO(), "replace-deployment", metav1.GetOptions{}) + framework.ExpectNoError(err) + framework.ExpectEqual(*deployment.Spec.Replicas, int32(0)) + // now purge the deployment and make sure the replica set is deleted as well purgeCmd := &cmd.RunPipelineCmd{ GlobalFlags: &flags.GlobalFlags{ diff --git a/e2e/tests/replacepods/testdata/deployment/devspace.yaml b/e2e/tests/replacepods/testdata/deployment/devspace.yaml index 5283c7cfe9..d6911d22e4 100644 --- a/e2e/tests/replacepods/testdata/deployment/devspace.yaml +++ b/e2e/tests/replacepods/testdata/deployment/devspace.yaml @@ -3,7 +3,7 @@ vars: - name: IMAGE value: john/devbackend deployments: - - name: very-long-name-very-long-name-very-long-name + - name: replace-deployment helm: componentChart: true values: diff --git a/pkg/devspace/services/podreplace/builder.go b/pkg/devspace/services/podreplace/builder.go index a338053cb6..650332fc73 100644 --- a/pkg/devspace/services/podreplace/builder.go +++ b/pkg/devspace/services/podreplace/builder.go @@ -64,18 +64,21 @@ func buildDeployment(ctx devspacecontext.Context, name string, target runtime.Ob case *appsv1.ReplicaSet: deployment.Annotations[TargetNameAnnotation] = t.Name deployment.Annotations[TargetKindAnnotation] = "ReplicaSet" + deployment.Spec.Selector = t.Spec.Selector podTemplate.Labels = t.Spec.Template.Labels podTemplate.Annotations = t.Spec.Template.Annotations podTemplate.Spec = *t.Spec.Template.Spec.DeepCopy() case *appsv1.Deployment: deployment.Annotations[TargetNameAnnotation] = t.Name deployment.Annotations[TargetKindAnnotation] = "Deployment" + deployment.Spec.Selector = t.Spec.Selector podTemplate.Labels = t.Spec.Template.Labels podTemplate.Annotations = t.Spec.Template.Annotations podTemplate.Spec = *t.Spec.Template.Spec.DeepCopy() case *appsv1.StatefulSet: deployment.Annotations[TargetNameAnnotation] = t.Name deployment.Annotations[TargetKindAnnotation] = "StatefulSet" + deployment.Spec.Selector = t.Spec.Selector podTemplate.Labels = t.Spec.Template.Labels podTemplate.Annotations = t.Spec.Template.Annotations podTemplate.Spec = *t.Spec.Template.Spec.DeepCopy() @@ -148,12 +151,15 @@ func buildDeployment(ctx devspacecontext.Context, name string, target runtime.Ob podTemplate.Annotations[selector.MatchedContainerAnnotation] = strings.Join(containers, ";") } - deployment.Spec = appsv1.DeploymentSpec{ - Selector: &metav1.LabelSelector{ - MatchLabels: podTemplate.ObjectMeta.Labels, - }, - Template: *podTemplate, + deployment.Spec.Template = *podTemplate + if deployment.Spec.Selector == nil { + deployment.Spec.Selector = &metav1.LabelSelector{} + } + if deployment.Spec.Selector.MatchLabels == nil { + deployment.Spec.Selector.MatchLabels = podTemplate.Labels + deployment.Spec.Selector.MatchExpressions = nil } + deployment.Spec.Selector.MatchLabels[selector.ReplacedLabel] = "true" // make sure labels etc are there if ctx.Log().GetLevel() == logrus.DebugLevel { diff --git a/pkg/devspace/services/podreplace/replace.go b/pkg/devspace/services/podreplace/replace.go index f802b37adc..654d315aa4 100644 --- a/pkg/devspace/services/podreplace/replace.go +++ b/pkg/devspace/services/podreplace/replace.go @@ -8,7 +8,6 @@ import ( "github.com/loft-sh/devspace/pkg/devspace/deploy" patch2 "github.com/loft-sh/devspace/pkg/util/patch" "github.com/loft-sh/devspace/pkg/util/stringutil" - apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/resource" "strconv" "time" @@ -70,7 +69,7 @@ func (p *replacer) ReplacePod(ctx devspacecontext.Context, devPod *latest.DevPod // check if there is a replaced pod in the target namespace ctx.Log().Debug("Try to find replaced deployment...") - // find the replaced replica set + // find the replaced deployment deployment, err := ctx.KubeClient().KubeClient().AppsV1().Deployments(devPodCache.Namespace).Get(ctx.Context(), devPodCache.Deployment, metav1.GetOptions{}) if err != nil { if !kerrors.IsNotFound(err) { @@ -154,17 +153,14 @@ func updateNeeded(ctx devspacecontext.Context, deployment *appsv1.Deployment, de ctx.Log().Warnf("Error scaling down target: %v", err) } - // don't update if pod spec & config hash are the same - if apiequality.Semantic.DeepEqual(newDeployment.Spec.Template, deployment.Spec.Template) && configHash == deployment.Annotations[DevPodConfigHashAnnotation] { - // make sure target is downscaled - ctx.Log().Debugf("No changes required in replaced deployment %s", deployment.Name) - return false, nil - } - - // update deployment´ + // update deployment originalDeployment := deployment.DeepCopy() + deployment.Spec.Replicas = ptr.Int32(1) + deployment.Spec.Selector = newDeployment.Spec.Selector deployment.Spec.Template = newDeployment.Spec.Template + deployment.Annotations = newDeployment.Annotations deployment.Annotations[DevPodConfigHashAnnotation] = configHash + deployment.Labels = newDeployment.Labels patch := patch2.MergeFrom(originalDeployment) patchBytes, err := patch.Data(deployment) if err != nil {