Repository: argoproj/argo-rollouts (go)

Issue:
Canary strategy is not defaulting maxUnavailable to 25%
## Summary 

The canary strategy is supposed to behave like a Deployment rollingUpdate strategy by defaulting both maxUnavailable and maxSurge to 25%.

While maxSurge does seem to be defaulting to 25%, maxUnavailable appears to be defaulting to 0.

## Diagnostics

What version of Argo Rollouts are you running?

v0.10.2 but I tested with v0.8.2 and v0.6.3 those versions also exhibit the same behavior.


---
<!-- Issue Author: Don't delete this message to encourage other users to support your issue! -->
**Message from the maintainers**:

Impacted by this bug? Give it a 👍. We prioritize the issues with the most 👍.

Pull request description:
fix: Set Canary Strategy default maxUnavailable to 25%
Closes #938 

Signed-off-by: khhirani <kareena.hirani@gmail.com>

Patch:
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index e26dbab1d..49d173bf0 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -2,7 +2,7 @@ Checklist:
 
 * [ ] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo-rollouts/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this is a chore.
 * [ ] The title of the PR is (a) [conventional](https://www.conventionalcommits.org/en/v1.0.0/), (b) states what changed, and (c) suffixes the related issues number. E.g. `"fix(controller): Updates such and such. Fixes #1234"`.  
-* [ ] I've signed the CLA.
+* [ ] I've signed my commits with [DCO](https://github.com/argoproj/argoproj)
 * [ ] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
 * [ ] My builds are green. Try syncing with master if they are not. 
 * [ ] My organization is added to [USERS.md](https://github.com/argoproj/argo-rollouts/blob/master/USERS.md).
\ No newline at end of file
diff --git a/README.md b/README.md
index 7383a4184..05fa47cc5 100644
--- a/README.md
+++ b/README.md
@@ -44,20 +44,8 @@ For these reasons, in large scale high-volume production environments, a rolling
 To learn more about Argo Rollouts go to the [complete documentation](https://argoproj.github.io/argo-rollouts/).
 
 ## Who uses Argo Rollouts?
-Organizations below are **officially** using Argo Rollouts. Please send a PR with your organization name if you are using Argo Rollouts.
-
-1. [ADP](https://www.adp.com)
-1. [Intuit](https://www.intuit.com/)
-1. [Nozzle](https://nozzle.io)
-1. [PayPay](https://paypay.ne.jp/)
-1. [Twilio SendGrid](https://sendgrid.com)
-1. [Ubie](https://ubie.life/)
-1. [VISITS Technologies](https://visits.world/en)
-1. [Spotify](https://www.spotify.com/)
-1. [New Relic](https://newrelic.com/)
-1. [Quipper](https://www.quipper.com/)
-1. [Devtron Labs](https://github.com/devtron-labs/devtron)
 
+[Official Argo Rollouts User List](https://github.com/argoproj/argo-rollouts/blob/master/USERS.md)
 
 ## Community Blogs and Presentations
 * [Argo Rollouts - Canary Deployments Made Easy In Kubernetes](https://youtu.be/84Ky0aPbHvY) @ [The DevOps Toolkit Series](https://www.youtube.com/c/TheDevOpsToolkitSeries)
diff --git a/USERS.md b/USERS.md
new file mode 100644
index 000000000..0e4abcf9b
--- /dev/null
+++ b/USERS.md
@@ -0,0 +1,15 @@
+## Who uses Argo Rollouts?
+Organizations below are **officially** using Argo Rollouts. Please send a PR with your organization name if you are using Argo Rollouts.
+
+1. [ADP](https://www.adp.com)
+1. [Intuit](https://www.intuit.com/)
+1. [Nozzle](https://nozzle.io)
+1. [PayPay](https://paypay.ne.jp/)
+1. [Twilio SendGrid](https://sendgrid.com)
+1. [Ubie](https://ubie.life/)
+1. [VISITS Technologies](https://visits.world/en)
+1. [Spotify](https://www.spotify.com/)
+1. [New Relic](https://newrelic.com/)
+1. [Quipper](https://www.quipper.com/)
+1. [Devtron Labs](https://github.com/devtron-labs/devtron)
+1. [Quizlet](https://quizlet.com)
diff --git a/docs/features/canary.md b/docs/features/canary.md
index 84de50d47..3e9dd83b1 100644
--- a/docs/features/canary.md
+++ b/docs/features/canary.md
@@ -163,7 +163,7 @@ Defaults to "25%".
 ### maxUnavailable
 The maximum number of pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxSurge is 0.
 
-Defaults to 0
+Defaults to 25%
 
 ### trafficRouting
 The [traffic management](traffic-management/index.md) rules to apply to control the flow of traffic between the active and canary versions. If not set, the default weighted pod replica based routing will be used.
diff --git a/docs/features/ephemeral-metadata.md b/docs/features/ephemeral-metadata.md
index e95974f5e..6e3ec93d5 100644
--- a/docs/features/ephemeral-metadata.md
+++ b/docs/features/ephemeral-metadata.md
@@ -1,16 +1,21 @@
 # Ephemeral Metadata
 
 !!! important
-    Available since v0.10.0
 
-One use case is for a Rollout to label or annotate the canary/stable pods with user-defined labels/annotations,
-for *only* the duration which they are the canary or stable set, and for the labels to be updated/removed as soon
-as the ReplicaSet switches roles (e.g. from canary to stable). The use case which this enables, is to allow
-prometheus, wavefront, datadog queries and dashboards to be built, which can rely on a consistent
-labels, rather than the `rollouts-pod-template-hash` which is unpredictable and changing from revision
-to revision.
+    Available for canary rollouts since v0.10.0
 
-A Rollout using the canary strategy has the ability to attach ephemeral metadata to the stable or 
+!!! important
+
+    Available for blue-green rollouts since v1.0
+
+One use case is for a Rollout to label or annotate the desired/stable pods with user-defined
+labels/annotations, for _only_ the duration which they are the desired or stable set, and for the
+labels to be updated/removed as soon as the ReplicaSet switches roles (e.g. from desired to stable).
+The use case which this enables, is to allow prometheus, wavefront, datadog queries and dashboards
+to be built, which can rely on a consistent labels, rather than the `rollouts-pod-template-hash`
+which is unpredictable and changing from revision to revision.
+
+A Rollout using the canary strategy has the ability to attach ephemeral metadata to the stable or
 canary Pods using the `stableMetadata` and `canaryMetadata` fields respectively.
 
 ```yaml
@@ -25,14 +30,29 @@ spec:
           role: canary
 ```
 
-During an update, the Rollout will create the canary ReplicaSet while also merging the metadata
-defined in `canaryMetadata` to the canary ReplicaSet's `spec.template.metadata`. This results in all
-Pods of the ReplicaSet being created with the canary metadata. When the rollout becomes fully
-promoted, the canary ReplicaSet becomes the stable, and is updated to use the labels and annotations
-under `stableMetadata`. The Pods of the ReplicaSet will then be updated *in place* to use the stable
-metadata (without recreating the pods).
+A Rollout using the blue-green strategy has the ability to attach ephemeral metadata to the active
+or preview Pods using the `activeMetadata` and `previewMetadata` fields respectively.
+
+```yaml
+spec:
+  strategy:
+    blueGreen:
+      activeMetadata:
+        labels:
+          role: active
+      previewMetadata:
+        labels:
+          role: preview
+```
+
+During an update, the Rollout will create the desired ReplicaSet while also merging the metadata
+defined in `canaryMetadata`/`previewMetadata` to the desired ReplicaSet's `spec.template.metadata`.
+This results in all Pods of the ReplicaSet being created with the desired metadata. When the rollout
+becomes fully promoted, the desired ReplicaSet becomes the stable, and is updated to use the labels
+and annotations under `stableMetadata`/`activeMetadata`. The Pods of the ReplicaSet will then be
+updated _in place_ to use the stable metadata (without recreating the pods).
 
 !!! important
-    In order for tooling to take advantage of this feature, they would need to recognize the change
-    in labels and/or annotations that happen *after* the Pod has already started. Not all tools may
-    detect this.
+In order for tooling to take advantage of this feature, they would need to recognize the change in
+labels and/or annotations that happen _after_ the Pod has already started. Not all tools may detect
+this.
diff --git a/manifests/crds/rollout-crd.yaml b/manifests/crds/rollout-crd.yaml
index 02f8c2c96..83fe6956d 100644
--- a/manifests/crds/rollout-crd.yaml
+++ b/manifests/crds/rollout-crd.yaml
@@ -92,6 +92,17 @@ spec:
               properties:
                 blueGreen:
                   properties:
+                    activeMetadata:
+                      properties:
+                        annotations:
+                          additionalProperties:
+                            type: string
+                          type: object
+                        labels:
+                          additionalProperties:
+                            type: string
+                          type: object
+                      type: object
                     activeService:
                       type: string
                     antiAffinity:
@@ -195,6 +206,17 @@ spec:
                             type: object
                           type: array
                       type: object
+                    previewMetadata:
+                      properties:
+                        annotations:
+                          additionalProperties:
+                            type: string
+                          type: object
+                        labels:
+                          additionalProperties:
+                            type: string
+                          type: object
+                      type: object
                     previewReplicaCount:
                       format: int32
                       type: integer
diff --git a/manifests/install.yaml b/manifests/install.yaml
index c36ca8353..56b160884 100644
--- a/manifests/install.yaml
+++ b/manifests/install.yaml
@@ -11626,6 +11626,17 @@ spec:
               properties:
                 blueGreen:
                   properties:
+                    activeMetadata:
+                      properties:
+                        annotations:
+                          additionalProperties:
+                            type: string
+                          type: object
+                        labels:
+                          additionalProperties:
+                            type: string
+                          type: object
+                      type: object
                     activeService:
                       type: string
                     antiAffinity:
@@ -11729,6 +11740,17 @@ spec:
                             type: object
                           type: array
                       type: object
+                    previewMetadata:
+                      properties:
+                        annotations:
+                          additionalProperties:
+                            type: string
+                          type: object
+                        labels:
+                          additionalProperties:
+                            type: string
+                          type: object
+                      type: object
                     previewReplicaCount:
                       format: int32
                       type: integer
diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml
index 2b10d7786..84139106f 100644
--- a/manifests/namespace-install.yaml
+++ b/manifests/namespace-install.yaml
@@ -11626,6 +11626,17 @@ spec:
               properties:
                 blueGreen:
                   properties:
+                    activeMetadata:
+                      properties:
+                        annotations:
+                          additionalProperties:
+                            type: string
+                          type: object
+                        labels:
+                          additionalProperties:
+                            type: string
+                          type: object
+                      type: object
                     activeService:
                       type: string
                     antiAffinity:
@@ -11729,6 +11740,17 @@ spec:
                             type: object
                           type: array
                       type: object
+                    previewMetadata:
+                      properties:
+                        annotations:
+                          additionalProperties:
+                            type: string
+                          type: object
+                        labels:
+                          additionalProperties:
+                            type: string
+                          type: object
+                      type: object
                     previewReplicaCount:
                       format: int32
                       type: integer
diff --git a/pkg/apis/rollouts/v1alpha1/types.go b/pkg/apis/rollouts/v1alpha1/types.go
index 6a8096c2a..ea86e7cf3 100644
--- a/pkg/apis/rollouts/v1alpha1/types.go
+++ b/pkg/apis/rollouts/v1alpha1/types.go
@@ -139,6 +139,12 @@ type BlueGreenStrategy struct {
 	AntiAffinity *AntiAffinity `json:"antiAffinity,omitempty"`
 	// PostPromotionAnalysis configuration to run analysis after a selector switch
 	PostPromotionAnalysis *RolloutAnalysis `json:"postPromotionAnalysis,omitempty"`
+	// PreviewMetadata specify labels and annotations which will be attached to the preview pods for
+	// the duration which they act as a preview pod, and will be removed after
+	PreviewMetadata *PodTemplateMetadata `json:"previewMetadata,omitempty"`
+	// ActiveMetadata specify labels and annotations which will be attached to the active pods for
+	// the duration which they act as a active pod, and will be removed after
+	ActiveMetadata *PodTemplateMetadata `json:"activeMetadata,omitempty"`
 }
 
 // AntiAffinity defines which inter-pod scheduling rule to use for anti-affinity injection
@@ -176,7 +182,7 @@ type CanaryStrategy struct {
 	// Value can be an absolute number (ex: 5) or a percentage of total pods at the start of update (ex: 10%).
 	// Absolute number is calculated from percentage by rounding down.
 	// This can not be 0 if MaxSurge is 0.
-	// By default, a fixed value of 1 is used.
+	// By default, a fixed value of 25% is used.
 	// Example: when this is set to 30%, the old RC can be scaled down by 30%
 	// immediately when the rolling update starts. Once new pods are ready, old RC
 	// can be scaled down further, followed by scaling up the new RC, ensuring
@@ -190,7 +196,7 @@ type CanaryStrategy struct {
 	// Value can be an absolute number (ex: 5) or a percentage of total pods at
 	// the start of the update (ex: 10%). This can not be 0 if MaxUnavailable is 0.
 	// Absolute number is calculated from percentage by rounding up.
-	// By default, a value of 1 is used.
+	// By default, a value of 25% is used.
 	// Example: when this is set to 30%, the new RC can be scaled up by 30%
 	// immediately when the rolling update starts. Once old pods have been killed,
 	// new RC can be scaled up further, ensuring that total number of pods running
diff --git a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go
index bb5e7fa3b..333aa29bf 100644
--- a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go
@@ -427,6 +427,16 @@ func (in *BlueGreenStrategy) DeepCopyInto(out *BlueGreenStrategy) {
 		*out = new(RolloutAnalysis)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.PreviewMetadata != nil {
+		in, out := &in.PreviewMetadata, &out.PreviewMetadata
+		*out = new(PodTemplateMetadata)
+		(*in).DeepCopyInto(*out)
+	}
+	if in.ActiveMetadata != nil {
+		in, out := &in.ActiveMetadata, &out.ActiveMetadata
+		*out = new(PodTemplateMetadata)
+		(*in).DeepCopyInto(*out)
+	}
 	return
 }
 
diff --git a/rollout/bluegreen.go b/rollout/bluegreen.go
index 161afb13c..7cdb04cbd 100644
--- a/rollout/bluegreen.go
+++ b/rollout/bluegreen.go
@@ -60,6 +60,12 @@ func (c *rolloutContext) rolloutBlueGreen() error {
 	if err != nil {
 		return err
 	}
+
+	err = c.reconcileEphemeralMetadata()
+	if err != nil {
+		return err
+	}
+
 	return c.syncRolloutStatusBlueGreen(previewSvc, activeSvc)
 }
 
diff --git a/rollout/canary.go b/rollout/canary.go
index 43b2e294f..861a3b8e5 100644
--- a/rollout/canary.go
+++ b/rollout/canary.go
@@ -1,7 +1,6 @@
 package rollout
 
 import (
-	"context"
 	"fmt"
 	"sort"
 
@@ -367,75 +366,3 @@ func (c *rolloutContext) reconcileCanaryReplicaSets() (bool, error) {
 	}
 	return false, nil
 }
-
-// reconcileEphemeralMetadata syncs canary/stable ephemeral metadata to ReplicaSets and pods
-func (c *rolloutContext) reconcileEphemeralMetadata() error {
-	ctx := context.TODO()
-	if c.rollout.Spec.Strategy.Canary == nil {
-		return nil
-	}
-	fullyRolledOut := c.rollout.Status.StableRS == "" || c.rollout.Status.StableRS == replicasetutil.GetPodTemplateHash(c.newRS)
-
-	if fullyRolledOut {
-		// We are in a steady-state (fully rolled out). newRS is the stableRS. there is no longer a canary
-		err := c.syncEphemeralMetadata(ctx, c.newRS, c.rollout.Spec.Strategy.Canary.StableMetadata)
-		if err != nil {
-			return err
-		}
-	} else {
-		// we are in a upgrading state. newRS is a canary
-		err := c.syncEphemeralMetadata(ctx, c.newRS, c.rollout.Spec.Strategy.Canary.CanaryMetadata)
-		if err != nil {
-			return err
-		}
-		// sync stable metadata to the stable rs
-		err = c.syncEphemeralMetadata(ctx, c.stableRS, c.rollout.Spec.Strategy.Canary.StableMetadata)
-		if err != nil {
-			return err
-		}
-	}
-
-	// Iterate all other ReplicaSets and verify we don't have injected metadata for them
-	for _, rs := range c.otherRSs {
-		err := c.syncEphemeralMetadata(ctx, rs, nil)
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func (c *rolloutContext) syncEphemeralMetadata(ctx context.Context, rs *appsv1.ReplicaSet, podMetadata *v1alpha1.PodTemplateMetadata) error {
-	if rs == nil {
-		return nil
-	}
-	modifiedRS, modified := replicasetutil.SyncReplicaSetEphemeralPodMetadata(rs, podMetadata)
-	if !modified {
-		return nil
-	}
-	// 1. Sync ephemeral metadata to pods
-	pods, err := replicasetutil.GetPodsOwnedByReplicaSet(ctx, c.kubeclientset, rs)
-	if err != nil {
-		return err
-	}
-	existingPodMetadata := replicasetutil.ParseExistingPodMetadata(rs)
-	for _, pod := range pods {
-		newPodObjectMeta, podModified := replicasetutil.SyncEphemeralPodMetadata(&pod.ObjectMeta, existingPodMetadata, podMetadata)
-		if podModified {
-			pod.ObjectMeta = *newPodObjectMeta
-			_, err = c.kubeclientset.CoreV1().Pods(pod.Namespace).Update(ctx, pod, metav1.UpdateOptions{})
-			if err != nil {
-				return err
-			}
-			c.log.Infof("synced ephemeral metadata %v to Pod %s", podMetadata, pod.Name)
-		}
-	}
-
-	// 2. Update ReplicaSet so that any new pods it creates will have the metadata
-	_, err = c.kubeclientset.AppsV1().ReplicaSets(modifiedRS.Namespace).Update(ctx, modifiedRS, metav1.UpdateOptions{})
-	if err != nil {
-		return err
-	}
-	c.log.Infof("synced ephemeral metadata %v to ReplicaSet %s", podMetadata, rs.Name)
-	return nil
-}
diff --git a/rollout/ephemeralmetadata.go b/rollout/ephemeralmetadata.go
new file mode 100644
index 000000000..92fc5d267
--- /dev/null
+++ b/rollout/ephemeralmetadata.go
@@ -0,0 +1,90 @@
+package rollout
+
+import (
+	"context"
+
+	appsv1 "k8s.io/api/apps/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	"github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
+	replicasetutil "github.com/argoproj/argo-rollouts/utils/replicaset"
+)
+
+// reconcileEphemeralMetadata syncs canary/stable ephemeral metadata to ReplicaSets and pods
+func (c *rolloutContext) reconcileEphemeralMetadata() error {
+	ctx := context.TODO()
+	var newMetadata, stableMetadata *v1alpha1.PodTemplateMetadata
+	if c.rollout.Spec.Strategy.Canary != nil {
+		newMetadata = c.rollout.Spec.Strategy.Canary.CanaryMetadata
+		stableMetadata = c.rollout.Spec.Strategy.Canary.StableMetadata
+	} else if c.rollout.Spec.Strategy.BlueGreen != nil {
+		newMetadata = c.rollout.Spec.Strategy.BlueGreen.PreviewMetadata
+		stableMetadata = c.rollout.Spec.Strategy.BlueGreen.ActiveMetadata
+	} else {
+		return nil
+	}
+	fullyRolledOut := c.rollout.Status.StableRS == "" || c.rollout.Status.StableRS == replicasetutil.GetPodTemplateHash(c.newRS)
+
+	if fullyRolledOut {
+		// We are in a steady-state (fully rolled out). newRS is the stableRS. there is no longer a canary
+		err := c.syncEphemeralMetadata(ctx, c.newRS, stableMetadata)
+		if err != nil {
+			return err
+		}
+	} else {
+		// we are in a upgrading state. newRS is a canary
+		err := c.syncEphemeralMetadata(ctx, c.newRS, newMetadata)
+		if err != nil {
+			return err
+		}
+		// sync stable metadata to the stable rs
+		err = c.syncEphemeralMetadata(ctx, c.stableRS, stableMetadata)
+		if err != nil {
+			return err
+		}
+	}
+
+	// Iterate all other ReplicaSets and verify we don't have injected metadata for them
+	for _, rs := range c.otherRSs {
+		err := c.syncEphemeralMetadata(ctx, rs, nil)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (c *rolloutContext) syncEphemeralMetadata(ctx context.Context, rs *appsv1.ReplicaSet, podMetadata *v1alpha1.PodTemplateMetadata) error {
+	if rs == nil {
+		return nil
+	}
+	modifiedRS, modified := replicasetutil.SyncReplicaSetEphemeralPodMetadata(rs, podMetadata)
+	if !modified {
+		return nil
+	}
+	// 1. Sync ephemeral metadata to pods
+	pods, err := replicasetutil.GetPodsOwnedByReplicaSet(ctx, c.kubeclientset, rs)
+	if err != nil {
+		return err
+	}
+	existingPodMetadata := replicasetutil.ParseExistingPodMetadata(rs)
+	for _, pod := range pods {
+		newPodObjectMeta, podModified := replicasetutil.SyncEphemeralPodMetadata(&pod.ObjectMeta, existingPodMetadata, podMetadata)
+		if podModified {
+			pod.ObjectMeta = *newPodObjectMeta
+			_, err = c.kubeclientset.CoreV1().Pods(pod.Namespace).Update(ctx, pod, metav1.UpdateOptions{})
+			if err != nil {
+				return err
+			}
+			c.log.Infof("synced ephemeral metadata %v to Pod %s", podMetadata, pod.Name)
+		}
+	}
+
+	// 2. Update ReplicaSet so that any new pods it creates will have the metadata
+	_, err = c.kubeclientset.AppsV1().ReplicaSets(modifiedRS.Namespace).Update(ctx, modifiedRS, metav1.UpdateOptions{})
+	if err != nil {
+		return err
+	}
+	c.log.Infof("synced ephemeral metadata %v to ReplicaSet %s", podMetadata, rs.Name)
+	return nil
+}
diff --git a/rollout/sync.go b/rollout/sync.go
index 64f2d17dd..3aec9c467 100644
--- a/rollout/sync.go
+++ b/rollout/sync.go
@@ -157,16 +157,26 @@ func (c *rolloutContext) createDesiredReplicaSet() (*appsv1.ReplicaSet, error) {
 	// Set new replica set's annotation
 	annotations.SetNewReplicaSetAnnotations(c.rollout, newRS, newRevision, false)
 
-	if c.rollout.Spec.Strategy.Canary != nil {
+	if c.rollout.Spec.Strategy.Canary != nil || c.rollout.Spec.Strategy.BlueGreen != nil {
+		var ephemeralMetadata *v1alpha1.PodTemplateMetadata
 		if c.stableRS != nil && c.stableRS != c.newRS {
 			// If this is a canary rollout, with ephemeral *canary* metadata, and there is a stable RS,
 			// then inject the canary metadata so that all the RS's new pods get the canary labels/annotation
-			newRS, _ = replicasetutil.SyncReplicaSetEphemeralPodMetadata(newRS, c.rollout.Spec.Strategy.Canary.CanaryMetadata)
+			if c.rollout.Spec.Strategy.Canary != nil {
+				ephemeralMetadata = c.rollout.Spec.Strategy.Canary.CanaryMetadata
+			} else {
+				ephemeralMetadata = c.rollout.Spec.Strategy.BlueGreen.PreviewMetadata
+			}
 		} else {
 			// Otherwise, if stableRS is nil, we are in a brand-new rollout and then this replicaset
 			// will eventually become the stableRS, so we should inject the stable labels/annotation
-			newRS, _ = replicasetutil.SyncReplicaSetEphemeralPodMetadata(newRS, c.rollout.Spec.Strategy.Canary.StableMetadata)
+			if c.rollout.Spec.Strategy.Canary != nil {
+				ephemeralMetadata = c.rollout.Spec.Strategy.Canary.StableMetadata
+			} else {
+				ephemeralMetadata = c.rollout.Spec.Strategy.BlueGreen.ActiveMetadata
+			}
 		}
+		newRS, _ = replicasetutil.SyncReplicaSetEphemeralPodMetadata(newRS, ephemeralMetadata)
 	}
 
 	// Create the new ReplicaSet. If it already exists, then we need to check for possible
diff --git a/utils/defaults/defaults.go b/utils/defaults/defaults.go
index 68ba9274f..83417eafe 100644
--- a/utils/defaults/defaults.go
+++ b/utils/defaults/defaults.go
@@ -14,7 +14,7 @@ const (
 	// DefaultMaxSurge default number for the max number of additional pods that can be brought up during a rollout
 	DefaultMaxSurge = "25"
 	// DefaultMaxUnavailable default number for the max number of unavailable pods during a rollout
-	DefaultMaxUnavailable = 0
+	DefaultMaxUnavailable = "25"
 	// DefaultProgressDeadlineSeconds default number of seconds for the rollout to be making progress
 	DefaultProgressDeadlineSeconds = int32(600)
 	// DefaultScaleDownDelaySeconds default seconds before scaling down old replicaset after switching services
@@ -57,7 +57,7 @@ func GetMaxUnavailableOrDefault(rollout *v1alpha1.Rollout) *intstr.IntOrString {
 	if rollout.Spec.Strategy.Canary != nil && rollout.Spec.Strategy.Canary.MaxUnavailable != nil {
 		return rollout.Spec.Strategy.Canary.MaxUnavailable
 	}
-	defaultValue := intstr.FromInt(DefaultMaxUnavailable)
+	defaultValue := intstr.FromString(DefaultMaxUnavailable)
 	return &defaultValue
 }
 
diff --git a/utils/replicaset/canary.go b/utils/replicaset/canary.go
index 35b5109b2..fb107eb15 100644
--- a/utils/replicaset/canary.go
+++ b/utils/replicaset/canary.go
@@ -486,10 +486,10 @@ func SyncEphemeralPodMetadata(metadata *metav1.ObjectMeta, existingPodMetadata,
 func SyncReplicaSetEphemeralPodMetadata(rs *appsv1.ReplicaSet, podMetadata *v1alpha1.PodTemplateMetadata) (*appsv1.ReplicaSet, bool) {
 	existingPodMetadata := ParseExistingPodMetadata(rs)
 	newObjectMeta, modified := SyncEphemeralPodMetadata(&rs.Spec.Template.ObjectMeta, existingPodMetadata, podMetadata)
+	rs = rs.DeepCopy()
 	if !modified {
 		return rs, false
 	}
-	rs = rs.DeepCopy()
 	rs.Spec.Template.ObjectMeta = *newObjectMeta
 	if podMetadata != nil {
 		// remember what we injected by annotating it


Test patch:
diff --git a/rollout/canary_test.go b/rollout/canary_test.go
index 28e34e171..4a8fa1d47 100644
--- a/rollout/canary_test.go
+++ b/rollout/canary_test.go
@@ -11,7 +11,6 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
-	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	k8sfake "k8s.io/client-go/kubernetes/fake"
 	"k8s.io/kubernetes/pkg/controller"
@@ -1378,109 +1377,3 @@ func TestHandleCanaryAbort(t *testing.T) {
 		assert.Equal(t, calculatePatch(r1, fmt.Sprintf(expectedPatch, newConditions)), patch)
 	})
 }
-
-// TestSyncEphemeralMetadataInitialRevision verifies when we create a revision 1 ReplicaSet
-// (with no previous revisions), that the ReplicaSet will get the stable metadata.
-func TestSyncEphemeralMetadataInitialRevision(t *testing.T) {
-	f := newFixture(t)
-	defer f.Close()
-
-	r1 := newCanaryRollout("foo", 1, nil, nil, pointer.Int32Ptr(1), intstr.FromInt(1), intstr.FromInt(1))
-	r1.Spec.Strategy.Canary.CanaryMetadata = &v1alpha1.PodTemplateMetadata{
-		Labels: map[string]string{
-			"role": "canary",
-		},
-	}
-	r1.Spec.Strategy.Canary.StableMetadata = &v1alpha1.PodTemplateMetadata{
-		Labels: map[string]string{
-			"role": "stable",
-		},
-	}
-	rs1 := newReplicaSetWithStatus(r1, 3, 3)
-	f.rolloutLister = append(f.rolloutLister, r1)
-	f.objects = append(f.objects, r1)
-
-	f.expectUpdateRolloutStatusAction(r1)
-	idx := f.expectCreateReplicaSetAction(rs1)
-	_ = f.expectPatchRolloutAction(r1)
-	f.run(getKey(r1, t))
-	createdRS1 := f.getCreatedReplicaSet(idx)
-	expectedLabels := map[string]string{
-		"foo":                        "bar",
-		"role":                       "stable",
-		"rollouts-pod-template-hash": r1.Status.CurrentPodHash,
-	}
-	assert.Equal(t, expectedLabels, createdRS1.Spec.Template.Labels)
-}
-
-// TestSyncEphemeralMetadataSecondRevision verifies when we deploy a canary ReplicaSet, the canary
-// contains the canary ephemeral metadata.  Also verifies we patch existing pods of the ReplicaSet
-// with the metadata
-func TestSyncEphemeralMetadataSecondRevision(t *testing.T) {
-	f := newFixture(t)
-	defer f.Close()
-
-	r1 := newCanaryRollout("foo", 1, nil, nil, pointer.Int32Ptr(1), intstr.FromInt(1), intstr.FromInt(1))
-	r1.Annotations[annotations.RevisionAnnotation] = "1"
-	r1.Spec.Strategy.Canary.CanaryMetadata = &v1alpha1.PodTemplateMetadata{
-		Labels: map[string]string{
-			"role": "canary",
-		},
-	}
-	r1.Spec.Strategy.Canary.StableMetadata = &v1alpha1.PodTemplateMetadata{
-		Labels: map[string]string{
-			"role": "stable",
-		},
-	}
-	rs1 := newReplicaSetWithStatus(r1, 3, 3)
-	r2 := bumpVersion(r1)
-	r2.Status.StableRS = r1.Status.CurrentPodHash
-	rs2 := newReplicaSetWithStatus(r2, 3, 3)
-	rsGVK := schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "ReplicaSet"}
-	pod := corev1.Pod{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      "foo-abc123",
-			Namespace: r1.Namespace,
-			Labels: map[string]string{
-				"foo":                        "bar",
-				"rollouts-pod-template-hash": r1.Status.CurrentPodHash,
-			},
-			OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(rs1, rsGVK)},
-		},
-	}
-
-	f.rolloutLister = append(f.rolloutLister, r2)
-	f.objects = append(f.objects, r2)
-	f.kubeobjects = append(f.kubeobjects, rs1, &pod)
-	f.replicaSetLister = append(f.replicaSetLister, rs1)
-
-	f.expectUpdateRolloutStatusAction(r2)         // Update Rollout conditions
-	rs2idx := f.expectCreateReplicaSetAction(rs2) // Create revision 2 ReplicaSet
-	f.expectListPodAction(r1.Namespace)           // list pods to patch ephemeral data on revision 1 ReplicaSets pods
-	podIdx := f.expectUpdatePodAction(&pod)       // Update pod with ephemeral data
-	rs1idx := f.expectUpdateReplicaSetAction(rs1) // update stable replicaset with stable metadata
-	f.expectUpdateReplicaSetAction(rs1)           // scale revision 1 ReplicaSet down
-	f.expectPatchRolloutAction(r2)                // Patch Rollout status
-
-	f.run(getKey(r2, t))
-	// revision 2 replicaset should been updated to use canary metadata
-	createdRS2 := f.getCreatedReplicaSet(rs2idx)
-	expectedCanaryLabels := map[string]string{
-		"foo":                        "bar",
-		"role":                       "canary",
-		"rollouts-pod-template-hash": r2.Status.CurrentPodHash,
-	}
-	assert.Equal(t, expectedCanaryLabels, createdRS2.Spec.Template.Labels)
-
-	// revision 1 replicaset should been updated to use stable metadata
-	updatedRS1 := f.getCreatedReplicaSet(rs1idx)
-	expectedStableLabels := map[string]string{
-		"foo":                        "bar",
-		"role":                       "stable",
-		"rollouts-pod-template-hash": r1.Status.CurrentPodHash,
-	}
-	assert.Equal(t, expectedStableLabels, updatedRS1.Spec.Template.Labels)
-	// also it's pods
-	updatedPod := f.getUpdatedPod(podIdx)
-	assert.Equal(t, expectedStableLabels, updatedPod.Labels)
-}
diff --git a/rollout/ephemeralmetadata_test.go b/rollout/ephemeralmetadata_test.go
new file mode 100644
index 000000000..7e9d4b850
--- /dev/null
+++ b/rollout/ephemeralmetadata_test.go
@@ -0,0 +1,239 @@
+package rollout
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/util/intstr"
+	"k8s.io/utils/pointer"
+
+	"github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1"
+	"github.com/argoproj/argo-rollouts/utils/annotations"
+)
+
+// TestSyncCanaryEphemeralMetadataInitialRevision verifies when we create a revision 1 ReplicaSet
+// (with no previous revisions), that the ReplicaSet will get the stable metadata.
+func TestSyncCanaryEphemeralMetadataInitialRevision(t *testing.T) {
+	f := newFixture(t)
+	defer f.Close()
+
+	r1 := newCanaryRollout("foo", 1, nil, nil, pointer.Int32Ptr(1), intstr.FromInt(1), intstr.FromInt(1))
+	r1.Spec.Strategy.Canary.CanaryMetadata = &v1alpha1.PodTemplateMetadata{
+		Labels: map[string]string{
+			"role": "canary",
+		},
+	}
+	r1.Spec.Strategy.Canary.StableMetadata = &v1alpha1.PodTemplateMetadata{
+		Labels: map[string]string{
+			"role": "stable",
+		},
+	}
+	rs1 := newReplicaSetWithStatus(r1, 3, 3)
+	f.rolloutLister = append(f.rolloutLister, r1)
+	f.objects = append(f.objects, r1)
+
+	f.expectUpdateRolloutStatusAction(r1)
+	idx := f.expectCreateReplicaSetAction(rs1)
+	_ = f.expectPatchRolloutAction(r1)
+	f.run(getKey(r1, t))
+	createdRS1 := f.getCreatedReplicaSet(idx)
+	expectedLabels := map[string]string{
+		"foo":                        "bar",
+		"role":                       "stable",
+		"rollouts-pod-template-hash": r1.Status.CurrentPodHash,
+	}
+	assert.Equal(t, expectedLabels, createdRS1.Spec.Template.Labels)
+}
+
+// TestSyncBlueGreenEphemeralMetadataInitialRevision verifies when we create a revision 1 ReplicaSet
+// (with no previous revisions), that the ReplicaSet will get the active metadata.
+func TestSyncBlueGreenEphemeralMetadataInitialRevision(t *testing.T) {
+	f := newFixture(t)
+	defer f.Close()
+
+	r1 := newBlueGreenRollout("foo", 1, nil, "active", "preview")
+	r1.Spec.Strategy.BlueGreen.PreviewMetadata = &v1alpha1.PodTemplateMetadata{
+		Labels: map[string]string{
+			"role": "preview",
+		},
+	}
+	r1.Spec.Strategy.BlueGreen.ActiveMetadata = &v1alpha1.PodTemplateMetadata{
+		Labels: map[string]string{
+			"role": "active",
+		},
+	}
+	rs1 := newReplicaSetWithStatus(r1, 1, 1)
+	f.rolloutLister = append(f.rolloutLister, r1)
+	f.objects = append(f.objects, r1)
+	previewSvc := newService("preview", 80, nil, r1)
+	activeSvc := newService("active", 80, nil, r1)
+	f.kubeobjects = append(f.kubeobjects, previewSvc, activeSvc)
+	f.serviceLister = append(f.serviceLister, activeSvc, previewSvc)
+
+	f.expectUpdateRolloutStatusAction(r1)
+	idx := f.expectCreateReplicaSetAction(rs1)
+	_ = f.expectPatchRolloutAction(r1)
+	f.expectPatchServiceAction(previewSvc, rs1.Labels[v1alpha1.DefaultRolloutUniqueLabelKey])
+	f.run(getKey(r1, t))
+	createdRS1 := f.getCreatedReplicaSet(idx)
+	expectedLabels := map[string]string{
+		"foo":                        "bar",
+		"role":                       "active",
+		"rollouts-pod-template-hash": r1.Status.CurrentPodHash,
+	}
+	assert.Equal(t, expectedLabels, createdRS1.Spec.Template.Labels)
+}
+
+// TestSyncCanaryEphemeralMetadataSecondRevision verifies when we deploy a canary ReplicaSet, the canary
+// contains the canary ephemeral metadata.  Also verifies we patch existing pods of the ReplicaSet
+// with the metadata
+func TestSyncCanaryEphemeralMetadataSecondRevision(t *testing.T) {
+	f := newFixture(t)
+	defer f.Close()
+
+	r1 := newCanaryRollout("foo", 1, nil, nil, pointer.Int32Ptr(1), intstr.FromInt(1), intstr.FromInt(1))
+	r1.Annotations[annotations.RevisionAnnotation] = "1"
+	r1.Spec.Strategy.Canary.CanaryMetadata = &v1alpha1.PodTemplateMetadata{
+		Labels: map[string]string{
+			"role": "canary",
+		},
+	}
+	r1.Spec.Strategy.Canary.StableMetadata = &v1alpha1.PodTemplateMetadata{
+		Labels: map[string]string{
+			"role": "stable",
+		},
+	}
+	rs1 := newReplicaSetWithStatus(r1, 3, 3)
+	r2 := bumpVersion(r1)
+	r2.Status.StableRS = r1.Status.CurrentPodHash
+	rs2 := newReplicaSetWithStatus(r2, 3, 3)
+	rsGVK := schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "ReplicaSet"}
+	pod := corev1.Pod{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "foo-abc123",
+			Namespace: r1.Namespace,
+			Labels: map[string]string{
+				"foo":                        "bar",
+				"rollouts-pod-template-hash": r1.Status.CurrentPodHash,
+			},
+			OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(rs1, rsGVK)},
+		},
+	}
+
+	f.rolloutLister = append(f.rolloutLister, r2)
+	f.objects = append(f.objects, r2)
+	f.kubeobjects = append(f.kubeobjects, rs1, &pod)
+	f.replicaSetLister = append(f.replicaSetLister, rs1)
+
+	f.expectUpdateRolloutStatusAction(r2)         // Update Rollout conditions
+	rs2idx := f.expectCreateReplicaSetAction(rs2) // Create revision 2 ReplicaSet
+	f.expectListPodAction(r1.Namespace)           // list pods to patch ephemeral data on revision 1 ReplicaSets pods
+	podIdx := f.expectUpdatePodAction(&pod)       // Update pod with ephemeral data
+	rs1idx := f.expectUpdateReplicaSetAction(rs1) // update stable replicaset with stable metadata
+	f.expectUpdateReplicaSetAction(rs1)           // scale revision 1 ReplicaSet down
+	f.expectPatchRolloutAction(r2)                // Patch Rollout status
+
+	f.run(getKey(r2, t))
+	// revision 2 replicaset should been updated to use canary metadata
+	createdRS2 := f.getCreatedReplicaSet(rs2idx)
+	expectedCanaryLabels := map[string]string{
+		"foo":                        "bar",
+		"role":                       "canary",
+		"rollouts-pod-template-hash": r2.Status.CurrentPodHash,
+	}
+	assert.Equal(t, expectedCanaryLabels, createdRS2.Spec.Template.Labels)
+
+	// revision 1 replicaset should been updated to use stable metadata
+	updatedRS1 := f.getCreatedReplicaSet(rs1idx)
+	expectedStableLabels := map[string]string{
+		"foo":                        "bar",
+		"role":                       "stable",
+		"rollouts-pod-template-hash": r1.Status.CurrentPodHash,
+	}
+	assert.Equal(t, expectedStableLabels, updatedRS1.Spec.Template.Labels)
+	// also it's pods
+	updatedPod := f.getUpdatedPod(podIdx)
+	assert.Equal(t, expectedStableLabels, updatedPod.Labels)
+}
+
+// TestSyncBlueGreenEphemeralMetadataSecondRevision verifies when we deploy a canary ReplicaSet, the canary
+// contains the canary ephemeral metadata.  Also verifies we patch existing pods of the ReplicaSet
+// with the metadata
+func TestSyncBlueGreenEphemeralMetadataSecondRevision(t *testing.T) {
+	f := newFixture(t)
+	defer f.Close()
+
+	r1 := newBlueGreenRollout("foo", 1, nil, "active", "preview")
+	r1.Spec.Strategy.BlueGreen.AutoPromotionEnabled = pointer.BoolPtr(false)
+	r1.Annotations[annotations.RevisionAnnotation] = "1"
+	r1.Spec.Strategy.BlueGreen.PreviewMetadata = &v1alpha1.PodTemplateMetadata{
+		Labels: map[string]string{
+			"role": "preview",
+		},
+	}
+	r1.Spec.Strategy.BlueGreen.ActiveMetadata = &v1alpha1.PodTemplateMetadata{
+		Labels: map[string]string{
+			"role": "active",
+		},
+	}
+	rs1 := newReplicaSetWithStatus(r1, 3, 3)
+	r2 := bumpVersion(r1)
+	r2.Status.StableRS = r1.Status.CurrentPodHash
+	rs2 := newReplicaSetWithStatus(r2, 3, 3)
+	rs2PodHash := rs2.Labels[v1alpha1.DefaultRolloutUniqueLabelKey]
+	rsGVK := schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "ReplicaSet"}
+	pod := corev1.Pod{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "foo-abc123",
+			Namespace: r1.Namespace,
+			Labels: map[string]string{
+				"foo":                        "bar",
+				"rollouts-pod-template-hash": r1.Status.CurrentPodHash,
+			},
+			OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(rs1, rsGVK)},
+		},
+	}
+
+	previewSvc := newService("preview", 80, nil, r1)
+	activeSvc := newService("active", 80, nil, r1)
+
+	f.rolloutLister = append(f.rolloutLister, r2)
+	f.objects = append(f.objects, r2)
+	f.kubeobjects = append(f.kubeobjects, rs1, &pod, previewSvc, activeSvc)
+	f.replicaSetLister = append(f.replicaSetLister, rs1)
+	f.serviceLister = append(f.serviceLister, activeSvc, previewSvc)
+
+	f.expectUpdateRolloutStatusAction(r2)              // Update Rollout conditions
+	rs2idx := f.expectCreateReplicaSetAction(rs2)      // Create revision 2 ReplicaSet
+	f.expectPatchServiceAction(previewSvc, rs2PodHash) // Update preview service to point at revision 2 replicaset
+	f.expectListPodAction(r1.Namespace)                // list pods to patch ephemeral data on revision 1 ReplicaSets pods`
+	podIdx := f.expectUpdatePodAction(&pod)            // Update pod with ephemeral data
+	rs1idx := f.expectUpdateReplicaSetAction(rs1)      // update stable replicaset with stable metadata
+	f.expectPatchReplicaSetAction(rs1)                 // scale revision 1 ReplicaSet down
+	f.expectPatchRolloutAction(r2)                     // Patch Rollout status
+
+	f.run(getKey(r2, t))
+	// revision 2 replicaset should been updated to use canary metadata
+	createdRS2 := f.getCreatedReplicaSet(rs2idx)
+	expectedCanaryLabels := map[string]string{
+		"foo":                        "bar",
+		"role":                       "preview",
+		"rollouts-pod-template-hash": r2.Status.CurrentPodHash,
+	}
+	assert.Equal(t, expectedCanaryLabels, createdRS2.Spec.Template.Labels)
+
+	// revision 1 replicaset should been updated to use stable metadata
+	updatedRS1 := f.getCreatedReplicaSet(rs1idx)
+	expectedStableLabels := map[string]string{
+		"foo":                        "bar",
+		"role":                       "active",
+		"rollouts-pod-template-hash": r1.Status.CurrentPodHash,
+	}
+	assert.Equal(t, expectedStableLabels, updatedRS1.Spec.Template.Labels)
+	// also it's pods
+	updatedPod := f.getUpdatedPod(podIdx)
+	assert.Equal(t, expectedStableLabels, updatedPod.Labels)
+}
diff --git a/test/e2e/bluegreen_test.go b/test/e2e/bluegreen_test.go
new file mode 100644
index 000000000..bf4ed2374
--- /dev/null
+++ b/test/e2e/bluegreen_test.go
@@ -0,0 +1,160 @@
+// +build e2e
+
+package e2e
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/suite"
+	corev1 "k8s.io/api/core/v1"
+
+	"github.com/argoproj/argo-rollouts/test/fixtures"
+)
+
+type BlueGreenSuite struct {
+	fixtures.E2ESuite
+}
+
+func TestBlueGreenSuite(t *testing.T) {
+	suite.Run(t, new(BlueGreenSuite))
+}
+
+func (s *BlueGreenSuite) SetupSuite() {
+	s.E2ESuite.SetupSuite()
+}
+
+// TestEphemeralMetadata tests the ephemeral metadata feature
+func (s *BlueGreenSuite) TestEphemeralMetadata() {
+	podsHaveActiveMetadata := func(pods *corev1.PodList) bool {
+		for _, pod := range pods.Items {
+			if pod.Labels["role"] != "active" {
+				return false
+			}
+		}
+		return true
+	}
+	podsHavePreviewMetadata := func(pods *corev1.PodList) bool {
+		for _, pod := range pods.Items {
+			if pod.Labels["role"] != "preview" {
+				return false
+			}
+		}
+		return true
+	}
+	podsHavePreviewMetadata2 := func(pods *corev1.PodList) bool {
+		for _, pod := range pods.Items {
+			if _, ok := pod.Labels["role"]; ok {
+				return false
+			}
+			if pod.Labels["role2"] != "preview2" {
+				return false
+			}
+		}
+		return true
+	}
+	podsHaveActiveMetadata2 := func(pods *corev1.PodList) bool {
+		for _, pod := range pods.Items {
+			if _, ok := pod.Labels["role"]; ok {
+				return false
+			}
+			if pod.Labels["role2"] != "active2" {
+				return false
+			}
+		}
+		return true
+	}
+
+	s.Given().
+		RolloutObjects(`
+apiVersion: v1
+kind: Service
+metadata:
+  name: ephemeral-metadata-bg
+spec:
+  ports:
+  - port: 80
+    targetPort: http
+    protocol: TCP
+    name: http
+  selector:
+    app: ephemeral-metadata
+---
+apiVersion: argoproj.io/v1alpha1
+kind: Rollout
+metadata:
+  name: ephemeral-metadata-bg
+spec:
+  replicas: 2
+  strategy:
+    blueGreen:
+      previewMetadata:
+        labels:
+          role: preview
+      activeMetadata:
+        labels:
+          role: active
+      activeService: ephemeral-metadata-bg
+      autoPromotionEnabled: false
+  selector:
+    matchLabels:
+      app: ephemeral-metadata-bg
+  template:
+    metadata:
+      labels:
+        app: ephemeral-metadata-bg
+    spec:
+      containers:
+      - name: ephemeral-metadata
+        image: nginx:1.19-alpine
+        resources:
+          requests:
+            memory: 16Mi
+            cpu: 1m
+`).
+		When().
+		ApplyManifests().
+		WaitForRolloutReplicas(2).
+		Then().
+		ExpectPods("all pods have active metadata", podsHaveActiveMetadata).
+		When().
+		WaitForRolloutStatus("Healthy").
+		UpdateSpec().
+		WaitForRolloutStatus("Paused").
+		Then().
+		ExpectRevisionPods("revision 2 has preview metadata", "2", podsHavePreviewMetadata).
+		ExpectRevisionPods("revision 1 has active metadata", "1", podsHaveActiveMetadata).
+		When().
+		// update preview metadata to different value, verify old data is gone, and replaced with new
+		PatchSpec(`
+spec:
+  strategy:
+    blueGreen:
+      previewMetadata:
+        labels:
+          role: null
+          role2: preview2`).
+		Sleep(time.Second).
+		Then().
+		ExpectRevisionPods("revision 2 has preview metadata2", "2", podsHavePreviewMetadata2).
+		When().
+		PromoteRollout().
+		WaitForRolloutStatus("Healthy").
+		Sleep(time.Second).
+		Then().
+		// after fully promoted, revision 2 should switch to active metadata
+		ExpectRevisionPods("revision 2 has active metadata", "2", podsHaveActiveMetadata).
+		When().
+		// if we change active metadata, pods and replicasets should see the change
+		PatchSpec(`
+spec:
+  strategy:
+    blueGreen:
+      activeMetadata:
+        labels:
+          role: null
+          role2: active2`).
+		Sleep(2*time.Second).
+		Then().
+		ExpectRevisionPods("revision 2 has active metadata2", "2", podsHaveActiveMetadata2)
+}
diff --git a/test/e2e/canary_test.go b/test/e2e/canary_test.go
index 9f668d185..7f3da3ae3 100644
--- a/test/e2e/canary_test.go
+++ b/test/e2e/canary_test.go
@@ -3,6 +3,7 @@
 package e2e
 
 import (
+	"log"
 	"testing"
 	"time"
 
@@ -236,6 +237,7 @@ spec:
 func (s *CanarySuite) TestEphemeralMetadata() {
 	podsHaveStableMetadata := func(pods *corev1.PodList) bool {
 		for _, pod := range pods.Items {
+			log.Printf("+%v", pod.Labels)
 			if pod.Labels["role"] != "stable" {
 				return false
 			}
@@ -278,7 +280,7 @@ func (s *CanarySuite) TestEphemeralMetadata() {
 apiVersion: argoproj.io/v1alpha1
 kind: Rollout
 metadata:
-  name: ephemeral-metadata
+  name: ephemeral-metadata-canary
 spec:
   replicas: 2
   strategy:
@@ -294,11 +296,11 @@ spec:
       - pause: {}
   selector:
     matchLabels:
-      app: ephemeral-metadata
+      app: ephemeral-metadata-canary
   template:
     metadata:
       labels:
-        app: ephemeral-metadata
+        app: ephemeral-metadata-canary
     spec:
       containers:
       - name: ephemeral-metadata
diff --git a/utils/defaults/defaults_test.go b/utils/defaults/defaults_test.go
index c28a04540..bd5399083 100644
--- a/utils/defaults/defaults_test.go
+++ b/utils/defaults/defaults_test.go
@@ -71,7 +71,7 @@ func TestGetMaxUnavailableOrDefault(t *testing.T) {
 	assert.Equal(t, maxUnavailable, *GetMaxUnavailableOrDefault(rolloutBlueGreenNonDefaultValue))
 
 	rolloutDefaultValue := &v1alpha1.Rollout{}
-	assert.Equal(t, intstr.FromInt(DefaultMaxUnavailable), *GetMaxUnavailableOrDefault(rolloutDefaultValue))
+	assert.Equal(t, intstr.FromString(DefaultMaxUnavailable), *GetMaxUnavailableOrDefault(rolloutDefaultValue))
 }
 
 func TestGetCanaryIngressAnnotationPrefixOrDefault(t *testing.T) {