From 82874e2b0db951185eb715076e77383c0be8551c Mon Sep 17 00:00:00 2001
From: Arvind Thirumurugan <arvindthirumurugan@gmail.com>
Date: Tue, 7 Jan 2025 01:01:18 -0800
Subject: [PATCH 1/3] fix: CEL validation for CRPDB and flag to disable
 eviction controller by default (#1009)

---
 apis/placement/v1alpha1/common.go             |  6 ++
 .../v1alpha1/disruptionbudget_types.go        |  4 +-
 charts/hub-agent/templates/deployment.yaml    |  1 +
 charts/hub-agent/values.yaml                  |  1 +
 cmd/hubagent/options/options.go               |  3 +
 cmd/hubagent/workload/setup.go                | 29 +++++--
 ...terresourceplacementdisruptionbudgets.yaml | 14 ++--
 ....io_clusterresourceplacementevictions.yaml |  5 ++
 .../api_validation_integration_test.go        | 76 ++++++++++++++++++-
 9 files changed, 119 insertions(+), 20 deletions(-)

diff --git a/apis/placement/v1alpha1/common.go b/apis/placement/v1alpha1/common.go
index 91bb39652..8e594246d 100644
--- a/apis/placement/v1alpha1/common.go
+++ b/apis/placement/v1alpha1/common.go
@@ -17,4 +17,10 @@ const (
 
 	// OverrideClusterNameVariable is the reserved variable in the override value that will be replaced by the actual cluster name.
 	OverrideClusterNameVariable = "${MEMBER-CLUSTER-NAME}"
+
+	// ClusterResourcePlacementEvictionKind is the kind of the ClusterResourcePlacementEviction.
+	ClusterResourcePlacementEvictionKind = "ClusterResourcePlacementEviction"
+
+	// ClusterResourcePlacementDisruptionBudgetKind is the kind of the ClusterResourcePlacementDisruptionBudget.
+	ClusterResourcePlacementDisruptionBudgetKind = "ClusterResourcePlacementDisruptionBudget"
 )
diff --git a/apis/placement/v1alpha1/disruptionbudget_types.go b/apis/placement/v1alpha1/disruptionbudget_types.go
index 982180ed3..afcff00c7 100644
--- a/apis/placement/v1alpha1/disruptionbudget_types.go
+++ b/apis/placement/v1alpha1/disruptionbudget_types.go
@@ -59,7 +59,7 @@ type PlacementDisruptionBudgetSpec struct {
 	// of them can be set at a time.
 	//
 	// +kubebuilder:validation:XIntOrString
-	// +kubebuilder:validation:XValidation:rule="type(self) == string ? self.matches('^(100|[0-9]{1,2}%)$') : self >= 0",message="If supplied value is String should match regex '^(100|[0-9]{1,2}%)$' or If supplied value is Integer must be greater than or equal to 0"
+	// +kubebuilder:validation:XValidation:rule="type(self) == string ? self.matches('^(100|[0-9]{1,2})%$') : self >= 0",message="If supplied value is String should match regex '^(100|[0-9]{1,2})%$' or If supplied value is Integer must be greater than or equal to 0"
 	// +optional
 	MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"`
 
@@ -88,7 +88,7 @@ type PlacementDisruptionBudgetSpec struct {
 	// of them can be set at a time.
 	//
 	// +kubebuilder:validation:XIntOrString
-	// +kubebuilder:validation:XValidation:rule="type(self) == string ? self.matches('^(100|[0-9]{1,2}%)$') : self >= 0",message="If supplied value is String should match regex '^(100|[0-9]{1,2}%)$' or If supplied value is Integer must be greater than or equal to 0"
+	// +kubebuilder:validation:XValidation:rule="type(self) == string ? self.matches('^(100|[0-9]{1,2})%$') : self >= 0",message="If supplied value is String should match regex '^(100|[0-9]{1,2})%$' or If supplied value is Integer must be greater than or equal to 0"
 	// +optional
 	MinAvailable *intstr.IntOrString `json:"minAvailable,omitempty"`
 }
diff --git a/charts/hub-agent/templates/deployment.yaml b/charts/hub-agent/templates/deployment.yaml
index 7d25f1775..5fef42640 100644
--- a/charts/hub-agent/templates/deployment.yaml
+++ b/charts/hub-agent/templates/deployment.yaml
@@ -32,6 +32,7 @@ spec:
             - --enable-v1beta1-apis={{ .Values.enableV1Beta1APIs }}
             - --enable-cluster-inventory-apis={{ .Values.enableClusterInventoryAPI }}
             - --enable-staged-update-run-apis={{ .Values.enableStagedUpdateRunAPIs }}
+            - --enable-eviction-apis={{ .Values.enableEvictionAPIs}}
             - --max-concurrent-cluster-placement={{ .Values.MaxConcurrentClusterPlacement }}
             - --concurrent-resource-change-syncs={{ .Values.ConcurrentResourceChangeSyncs }}
             - --log_file_max_size={{ .Values.logFileMaxSize }}
diff --git a/charts/hub-agent/values.yaml b/charts/hub-agent/values.yaml
index 777870a3d..845fa1c93 100644
--- a/charts/hub-agent/values.yaml
+++ b/charts/hub-agent/values.yaml
@@ -37,6 +37,7 @@ enableV1Alpha1APIs: false
 enableV1Beta1APIs: true
 enableClusterInventoryAPI: true
 enableStagedUpdateRunAPIs: true
+enableEvictionAPIs: true
 
 hubAPIQPS: 250
 hubAPIBurst: 1000
diff --git a/cmd/hubagent/options/options.go b/cmd/hubagent/options/options.go
index ec680a58e..d54f8d0cf 100644
--- a/cmd/hubagent/options/options.go
+++ b/cmd/hubagent/options/options.go
@@ -85,6 +85,8 @@ type Options struct {
 	ForceDeleteWaitTime metav1.Duration
 	// EnableStagedUpdateRunAPIs enables the agents to watch the clusterStagedUpdateRun CRs.
 	EnableStagedUpdateRunAPIs bool
+	// EnableEvictionAPIs enables to agents to watch the eviction and placement disruption budget CRs.
+	EnableEvictionAPIs bool
 }
 
 // NewOptions builds an empty options.
@@ -144,6 +146,7 @@ func (o *Options) AddFlags(flags *flag.FlagSet) {
 	flags.BoolVar(&o.EnableClusterInventoryAPIs, "enable-cluster-inventory-apis", false, "If set, the agents will watch for the ClusterInventory APIs.")
 	flags.DurationVar(&o.ForceDeleteWaitTime.Duration, "force-delete-wait-time", 15*time.Minute, "The duration the hub agent waits before force deleting a member cluster.")
 	flags.BoolVar(&o.EnableStagedUpdateRunAPIs, "enable-staged-update-run-apis", false, "If set, the agents will watch for the ClusterStagedUpdateRun APIs.")
+	flags.BoolVar(&o.EnableEvictionAPIs, "enable-eviction-apis", false, "If set, the agents will watch for the Eviction and PlacementDisruptionBudget APIs.")
 
 	o.RateLimiterOpts.AddFlags(flags)
 }
diff --git a/cmd/hubagent/workload/setup.go b/cmd/hubagent/workload/setup.go
index 73c65696d..2d69043b8 100644
--- a/cmd/hubagent/workload/setup.go
+++ b/cmd/hubagent/workload/setup.go
@@ -95,6 +95,11 @@ var (
 	clusterInventoryGVKs = []schema.GroupVersionKind{
 		clusterinventory.GroupVersion.WithKind("ClusterProfile"),
 	}
+
+	evictionGVKs = []schema.GroupVersionKind{
+		placementv1alpha1.GroupVersion.WithKind(placementv1alpha1.ClusterResourcePlacementEvictionKind),
+		placementv1alpha1.GroupVersion.WithKind(placementv1alpha1.ClusterResourcePlacementDisruptionBudgetKind),
+	}
 )
 
 // SetupControllers set up the customized controllers we developed
@@ -215,19 +220,27 @@ func SetupControllers(ctx context.Context, wg *sync.WaitGroup, mgr ctrl.Manager,
 			return err
 		}
 
-		klog.Info("Setting up cluster resource placement eviction controller")
-		if err := (&clusterresourceplacementeviction.Reconciler{
-			Client: mgr.GetClient(),
-		}).SetupWithManager(mgr); err != nil {
-			klog.ErrorS(err, "Unable to set up cluster resource placement eviction controller")
-			return err
+		if opts.EnableEvictionAPIs {
+			for _, gvk := range evictionGVKs {
+				if err = utils.CheckCRDInstalled(discoverClient, gvk); err != nil {
+					klog.ErrorS(err, "Unable to find the required CRD", "GVK", gvk)
+					return err
+				}
+			}
+			klog.Info("Setting up cluster resource placement eviction controller")
+			if err := (&clusterresourceplacementeviction.Reconciler{
+				Client: mgr.GetClient(),
+			}).SetupWithManager(mgr); err != nil {
+				klog.ErrorS(err, "Unable to set up cluster resource placement eviction controller")
+				return err
+			}
 		}
 
 		// Set up a controller to do staged update run, rolling out resources to clusters in a stage by stage manner.
 		if opts.EnableStagedUpdateRunAPIs {
 			for _, gvk := range clusterStagedUpdateRunGVKs {
 				if err = utils.CheckCRDInstalled(discoverClient, gvk); err != nil {
-					klog.ErrorS(err, "unable to find the required CRD", "GVK", gvk)
+					klog.ErrorS(err, "Unable to find the required CRD", "GVK", gvk)
 					return err
 				}
 			}
@@ -236,7 +249,7 @@ func SetupControllers(ctx context.Context, wg *sync.WaitGroup, mgr ctrl.Manager,
 				Client:          mgr.GetClient(),
 				InformerManager: dynamicInformerManager,
 			}).SetupWithManager(mgr); err != nil {
-				klog.ErrorS(err, "unable to set up clusterStagedUpdateRun controller")
+				klog.ErrorS(err, "Unable to set up clusterStagedUpdateRun controller")
 				return err
 			}
 		}
diff --git a/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementdisruptionbudgets.yaml b/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementdisruptionbudgets.yaml
index c5a651e38..c2b612d77 100644
--- a/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementdisruptionbudgets.yaml
+++ b/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementdisruptionbudgets.yaml
@@ -70,8 +70,7 @@ spec:
                   If a percentage is specified, Fleet will calculate the corresponding absolute values
                   as follows:
                   * if the linked Placement object is of the PickFixed placement type,
-                    the percentage is against the number of clusters specified in the placement (i.e., the
-                    length of ClusterNames field in the placement policy);
+                    we don't perform any calculation because eviction is not allowed for PickFixed CRP.
                   * if the linked Placement object is of the PickAll placement type, MaxUnavailable cannot
                     be specified since we cannot derive the total number of clusters selected.
                   * if the linked Placement object is of the PickN placement type,
@@ -88,10 +87,10 @@ spec:
                   of them can be set at a time.
                 x-kubernetes-int-or-string: true
                 x-kubernetes-validations:
-                - message: If supplied value is String should match regex '^(100|[0-9]{1,2}%)$'
+                - message: If supplied value is String should match regex '^(100|[0-9]{1,2})%$'
                     or If supplied value is Integer must be greater than or equal
                     to 0
-                  rule: 'type(self) == string ? self.matches(''^(100|[0-9]{1,2}%)$'')
+                  rule: 'type(self) == string ? self.matches(''^(100|[0-9]{1,2})%$'')
                     : self >= 0'
               minAvailable:
                 anyOf:
@@ -110,8 +109,7 @@ spec:
                   If a percentage is specified, Fleet will calculate the corresponding absolute values
                   as follows:
                   * if the linked Placement object is of the PickFixed placement type,
-                    the percentage is against the number of clusters specified in the placement (i.e., the
-                    length of ClusterNames field in the placement policy);
+                    we don't perform any calculation because eviction is not allowed for PickFixed CRP.
                   * if the linked Placement object is of the PickAll placement type, MinAvailable can be
                     specified but only as an integer since we cannot derive the total number of clusters selected.
                   * if the linked Placement object is of the PickN placement type,
@@ -128,10 +126,10 @@ spec:
                   of them can be set at a time.
                 x-kubernetes-int-or-string: true
                 x-kubernetes-validations:
-                - message: If supplied value is String should match regex '^(100|[0-9]{1,2}%)$'
+                - message: If supplied value is String should match regex '^(100|[0-9]{1,2})%$'
                     or If supplied value is Integer must be greater than or equal
                     to 0
-                  rule: 'type(self) == string ? self.matches(''^(100|[0-9]{1,2}%)$'')
+                  rule: 'type(self) == string ? self.matches(''^(100|[0-9]{1,2})%$'')
                     : self >= 0'
             type: object
             x-kubernetes-validations:
diff --git a/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementevictions.yaml b/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementevictions.yaml
index 96044b4f4..e4d0a8e96 100644
--- a/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementevictions.yaml
+++ b/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementevictions.yaml
@@ -49,6 +49,11 @@ spec:
           Eviction object.
 
 
+          Note: Eviction of resources from a cluster propagated by a PickFixed CRP is not allowed.
+          If the user wants to remove resources from a cluster propagated by a PickFixed CRP simply
+          remove the cluster name from cluster names field from the CRP spec.
+
+
           Executed evictions might be kept around for a while for auditing purposes; the Fleet controllers might
           have a TTL set up for such objects and will garbage collect them automatically. For further
           information, see the Fleet documentation.
diff --git a/test/apis/placement/v1alpha1/api_validation_integration_test.go b/test/apis/placement/v1alpha1/api_validation_integration_test.go
index e437827d5..d8a79abcf 100644
--- a/test/apis/placement/v1alpha1/api_validation_integration_test.go
+++ b/test/apis/placement/v1alpha1/api_validation_integration_test.go
@@ -37,7 +37,31 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 			Expect(hubClient.Create(ctx, &crpdb)).Should(Succeed())
 		})
 
-		It("should allow creation of ClusterPlacementDisruptionBudget with valid maxUnavailable - string", func() {
+		It("should allow creation of ClusterPlacementDisruptionBudget with valid maxUnavailable less than 10% specified as one digit - string", func() {
+			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
+				},
+				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+					MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "2%"},
+				},
+			}
+			Expect(hubClient.Create(ctx, &crpdb)).Should(Succeed())
+		})
+
+		It("should allow creation of ClusterPlacementDisruptionBudget with valid maxUnavailable less than 10% specified as two digits - string", func() {
+			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
+				},
+				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+					MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "02%"},
+				},
+			}
+			Expect(hubClient.Create(ctx, &crpdb)).Should(Succeed())
+		})
+
+		It("should allow creation of ClusterPlacementDisruptionBudget with valid maxUnavailable greater than or equal to 10% and less than 100% - string", func() {
 			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
@@ -49,6 +73,18 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 			Expect(hubClient.Create(ctx, &crpdb)).Should(Succeed())
 		})
 
+		It("should allow creation of ClusterPlacementDisruptionBudget with valid maxUnavailable equal to 100% - string", func() {
+			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
+				},
+				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+					MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
+				},
+			}
+			Expect(hubClient.Create(ctx, &crpdb)).Should(Succeed())
+		})
+
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable - int", func() {
 			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
@@ -61,7 +97,31 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 			Expect(hubClient.Create(ctx, &crpdb)).Should(Succeed())
 		})
 
-		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable - string", func() {
+		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable less than 10% specified as one digit - string", func() {
+			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
+				},
+				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+					MinAvailable: &intstr.IntOrString{Type: intstr.String, StrVal: "5%"},
+				},
+			}
+			Expect(hubClient.Create(ctx, &crpdb)).Should(Succeed())
+		})
+
+		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable less than 10% specified as two digits - string", func() {
+			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
+				},
+				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+					MinAvailable: &intstr.IntOrString{Type: intstr.String, StrVal: "05%"},
+				},
+			}
+			Expect(hubClient.Create(ctx, &crpdb)).Should(Succeed())
+		})
+
+		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable greater than or equal to 10% and less than 100% - string", func() {
 			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
@@ -73,6 +133,18 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 			Expect(hubClient.Create(ctx, &crpdb)).Should(Succeed())
 		})
 
+		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable equal to 100% - string", func() {
+			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+				ObjectMeta: metav1.ObjectMeta{
+					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
+				},
+				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+					MinAvailable: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
+				},
+			}
+			Expect(hubClient.Create(ctx, &crpdb)).Should(Succeed())
+		})
+
 		AfterEach(func() {
 			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{

From 8a8f6d03a3373573a367c811e37b78f19ae07977 Mon Sep 17 00:00:00 2001
From: Wantong <jwtty0919@gmail.com>
Date: Tue, 7 Jan 2025 19:31:04 -0800
Subject: [PATCH 2/3] fix stagedUpdateRun examples to use v1beta1 (#1012)

---
 examples/stagedupdaterun/approvalRequest.yaml        | 10 +++++-----
 examples/stagedupdaterun/clusterStagedUpdateRun.yaml |  4 ++--
 examples/stagedupdaterun/example-crp.yaml            |  2 +-
 examples/stagedupdaterun/updateStrategy.yaml         |  4 ++--
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/examples/stagedupdaterun/approvalRequest.yaml b/examples/stagedupdaterun/approvalRequest.yaml
index ea25b959b..5bc22e184 100644
--- a/examples/stagedupdaterun/approvalRequest.yaml
+++ b/examples/stagedupdaterun/approvalRequest.yaml
@@ -1,15 +1,15 @@
-apiVersion: placement.kubernetes-fleet.io/v1alpha1
+apiVersion: placement.kubernetes-fleet.io/v1beta1
 kind: ClusterApprovalRequest
 metadata:
   name: example-approvalrequest
   labels:
-    TargetUpdateRun: example-clusterstagedupdaterun
-    TargetStage: stage1
-    IsLatestUpdateRunApproval: "true"
+    kubernetes-fleet.io/targetupdaterun: example-run
+    kubernetes-fleet.io/targetUpdatingStage: canary
+    kubernetes-fleet.io/isLatestUpdateRunApproval: "true"
 spec:
   parentStageRollout: example-run
   targetStage: canary
 status:
   conditions:
     - type: Approved
-      status: "True"
\ No newline at end of file
+      status: "True"
diff --git a/examples/stagedupdaterun/clusterStagedUpdateRun.yaml b/examples/stagedupdaterun/clusterStagedUpdateRun.yaml
index 3e96a1280..a3ad1c0a1 100644
--- a/examples/stagedupdaterun/clusterStagedUpdateRun.yaml
+++ b/examples/stagedupdaterun/clusterStagedUpdateRun.yaml
@@ -1,4 +1,4 @@
-apiVersion: placement.kubernetes-fleet.io/v1alpha1
+apiVersion: placement.kubernetes-fleet.io/v1beta1
 kind: ClusterStagedUpdateRun
 metadata:
   name: example-run
@@ -39,4 +39,4 @@ status:
     - type: Progressing
       status: "True"
     - type: Succeeded
-      status: "False"
\ No newline at end of file
+      status: "False"
diff --git a/examples/stagedupdaterun/example-crp.yaml b/examples/stagedupdaterun/example-crp.yaml
index aff076417..c38dd6e33 100644
--- a/examples/stagedupdaterun/example-crp.yaml
+++ b/examples/stagedupdaterun/example-crp.yaml
@@ -14,4 +14,4 @@ spec:
       - key: gpu-workload
         operator: Exists
   strategy:
-    type: External
\ No newline at end of file
+    type: External
diff --git a/examples/stagedupdaterun/updateStrategy.yaml b/examples/stagedupdaterun/updateStrategy.yaml
index 452ec5190..9c34c96d1 100644
--- a/examples/stagedupdaterun/updateStrategy.yaml
+++ b/examples/stagedupdaterun/updateStrategy.yaml
@@ -1,4 +1,4 @@
-apiVersion: placement.kubernetes-fleet.io/v1alpha1
+apiVersion: placement.kubernetes-fleet.io/v1beta1
 kind: ClusterStagedUpdateStrategy
 metadata:
   name: example-strategy
@@ -26,4 +26,4 @@ spec:
       afterStageTasks:
         - type: Approval
         - type: TimedWait
-          waitTime: 1h
\ No newline at end of file
+          waitTime: 1h

From 3479ddb5a2dbb35a03bf904cff85c9e40b7cc8c6 Mon Sep 17 00:00:00 2001
From: Arvind Thirumurugan <arvindthirumurugan@gmail.com>
Date: Wed, 8 Jan 2025 01:05:17 -0800
Subject: [PATCH 3/3] interface: add v1beta1 API for eviction & PDB (#1013)

---
 apis/placement/v1alpha1/common.go             |   6 -
 .../v1alpha1/disruptionbudget_types.go        |   1 -
 apis/placement/v1alpha1/eviction_types.go     |   1 -
 apis/placement/v1beta1/commons.go             |   4 +
 .../v1beta1/disruptionbudget_types.go         | 111 +++++++++
 apis/placement/v1beta1/eviction_types.go      | 142 ++++++++++++
 .../v1beta1/zz_generated.deepcopy.go          | 181 ++++++++++++++-
 cmd/hubagent/workload/setup.go                |   4 +-
 ...terresourceplacementdisruptionbudgets.yaml | 121 ++++++++++
 ....io_clusterresourceplacementevictions.yaml | 172 ++++++++++++++
 .../controller.go                             |  37 ++-
 .../controller_intergration_test.go           |  27 ++-
 .../controller_test.go                        | 215 +++++++++---------
 .../suite_test.go                             |   3 -
 .../api_validation_integration_test.go        |  82 +++----
 .../{v1alpha1 => v1beta1}/suite_test.go       |   6 +-
 test/e2e/actuals_test.go                      |   3 +-
 test/e2e/placement_eviction_test.go           |   9 +-
 test/e2e/utils_test.go                        |   2 +-
 test/utils/eviction/eviction_status.go        |  14 +-
 20 files changed, 927 insertions(+), 214 deletions(-)
 create mode 100644 apis/placement/v1beta1/disruptionbudget_types.go
 create mode 100644 apis/placement/v1beta1/eviction_types.go
 rename test/apis/placement/{v1alpha1 => v1beta1}/api_validation_integration_test.go (80%)
 rename test/apis/placement/{v1alpha1 => v1beta1}/suite_test.go (93%)

diff --git a/apis/placement/v1alpha1/common.go b/apis/placement/v1alpha1/common.go
index 8e594246d..91bb39652 100644
--- a/apis/placement/v1alpha1/common.go
+++ b/apis/placement/v1alpha1/common.go
@@ -17,10 +17,4 @@ const (
 
 	// OverrideClusterNameVariable is the reserved variable in the override value that will be replaced by the actual cluster name.
 	OverrideClusterNameVariable = "${MEMBER-CLUSTER-NAME}"
-
-	// ClusterResourcePlacementEvictionKind is the kind of the ClusterResourcePlacementEviction.
-	ClusterResourcePlacementEvictionKind = "ClusterResourcePlacementEviction"
-
-	// ClusterResourcePlacementDisruptionBudgetKind is the kind of the ClusterResourcePlacementDisruptionBudget.
-	ClusterResourcePlacementDisruptionBudgetKind = "ClusterResourcePlacementDisruptionBudget"
 )
diff --git a/apis/placement/v1alpha1/disruptionbudget_types.go b/apis/placement/v1alpha1/disruptionbudget_types.go
index afcff00c7..6ae9e51ef 100644
--- a/apis/placement/v1alpha1/disruptionbudget_types.go
+++ b/apis/placement/v1alpha1/disruptionbudget_types.go
@@ -12,7 +12,6 @@ import (
 
 // +kubebuilder:object:root=true
 // +kubebuilder:resource:scope=Cluster,categories={fleet,fleet-placement},shortName=crpdb
-// +kubebuilder:storageversion
 
 // ClusterResourcePlacementDisruptionBudget is the policy applied to a ClusterResourcePlacement
 // object that specifies its disruption budget, i.e., how many placements (clusters) can be
diff --git a/apis/placement/v1alpha1/eviction_types.go b/apis/placement/v1alpha1/eviction_types.go
index d72327606..5d5822e9e 100644
--- a/apis/placement/v1alpha1/eviction_types.go
+++ b/apis/placement/v1alpha1/eviction_types.go
@@ -13,7 +13,6 @@ import (
 // +kubebuilder:object:root=true
 // +kubebuilder:resource:scope=Cluster,categories={fleet,fleet-placement},shortName=crpe
 // +kubebuilder:subresource:status
-// +kubebuilder:storageversion
 
 // ClusterResourcePlacementEviction is an eviction attempt on a specific placement from
 // a ClusterResourcePlacement object; one may use this API to force the removal of specific
diff --git a/apis/placement/v1beta1/commons.go b/apis/placement/v1beta1/commons.go
index dcbbdee73..b3fc5b594 100644
--- a/apis/placement/v1beta1/commons.go
+++ b/apis/placement/v1beta1/commons.go
@@ -26,6 +26,10 @@ const (
 	ClusterStagedUpdateStrategyKind = "ClusterStagedUpdateStrategy"
 	// ClusterApprovalRequestKind is the kind of the ClusterApprovalRequest.
 	ClusterApprovalRequestKind = "ClusterApprovalRequest"
+	// ClusterResourcePlacementEvictionKind is the kind of the ClusterResourcePlacementEviction.
+	ClusterResourcePlacementEvictionKind = "ClusterResourcePlacementEviction"
+	// ClusterResourcePlacementDisruptionBudgetKind is the kind of the ClusterResourcePlacementDisruptionBudget.
+	ClusterResourcePlacementDisruptionBudgetKind = "ClusterResourcePlacementDisruptionBudget"
 )
 
 const (
diff --git a/apis/placement/v1beta1/disruptionbudget_types.go b/apis/placement/v1beta1/disruptionbudget_types.go
new file mode 100644
index 000000000..441e033ac
--- /dev/null
+++ b/apis/placement/v1beta1/disruptionbudget_types.go
@@ -0,0 +1,111 @@
+/*
+Copyright (c) Microsoft Corporation.
+Licensed under the MIT license.
+*/
+
+package v1beta1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/intstr"
+)
+
+// +kubebuilder:object:root=true
+// +kubebuilder:resource:scope=Cluster,categories={fleet,fleet-placement},shortName=crpdb
+// +kubebuilder:storageversion
+
+// ClusterResourcePlacementDisruptionBudget is the policy applied to a ClusterResourcePlacement
+// object that specifies its disruption budget, i.e., how many placements (clusters) can be
+// down at the same time due to voluntary disruptions (e.g., evictions). Involuntary
+// disruptions are not subject to this budget, but will still count against it.
+//
+// To apply a ClusterResourcePlacementDisruptionBudget to a ClusterResourcePlacement, use the
+// same name for the ClusterResourcePlacementDisruptionBudget object as the ClusterResourcePlacement
+// object. This guarantees a 1:1 link between the two objects.
+type ClusterResourcePlacementDisruptionBudget struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	// Spec is the desired state of the ClusterResourcePlacementDisruptionBudget.
+	// +kubebuilder:validation:XValidation:rule="!(has(self.maxUnavailable) && has(self.minAvailable))",message="Both MaxUnavailable and MinAvailable cannot be specified"
+	// +required
+	Spec PlacementDisruptionBudgetSpec `json:"spec"`
+}
+
+// PlacementDisruptionBudgetSpec is the desired state of the PlacementDisruptionBudget.
+type PlacementDisruptionBudgetSpec struct {
+	// MaxUnavailable is the maximum number of placements (clusters) that can be down at the
+	// same time due to voluntary disruptions. For example, a setting of 1 would imply that
+	// a voluntary disruption (e.g., an eviction) can only happen if all placements (clusters)
+	// from the linked Placement object are applied and available.
+	//
+	// This can be either an absolute value (e.g., 1) or a percentage (e.g., 10%).
+	//
+	// If a percentage is specified, Fleet will calculate the corresponding absolute values
+	// as follows:
+	// * if the linked Placement object is of the PickFixed placement type,
+	//   we don't perform any calculation because eviction is not allowed for PickFixed CRP.
+	// * if the linked Placement object is of the PickAll placement type, MaxUnavailable cannot
+	//   be specified since we cannot derive the total number of clusters selected.
+	// * if the linked Placement object is of the PickN placement type,
+	//   the percentage is against the number of clusters specified in the placement (i.e., the
+	//   value of the NumberOfClusters fields in the placement policy).
+	// The end result will be rounded up to the nearest integer if applicable.
+	//
+	// One may use a value of 0 for this field; in this case, no voluntary disruption would be
+	// allowed.
+	//
+	// This field is mutually exclusive with the MinAvailable field in the spec; exactly one
+	// of them can be set at a time.
+	//
+	// +kubebuilder:validation:XIntOrString
+	// +kubebuilder:validation:XValidation:rule="type(self) == string ? self.matches('^(100|[0-9]{1,2})%$') : self >= 0",message="If supplied value is String should match regex '^(100|[0-9]{1,2})%$' or If supplied value is Integer must be greater than or equal to 0"
+	// +optional
+	MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty"`
+
+	// MinAvailable is the minimum number of placements (clusters) that must be available at any
+	// time despite voluntary disruptions. For example, a setting of 10 would imply that
+	// a voluntary disruption (e.g., an eviction) can only happen if there are at least 11
+	// placements (clusters) from the linked Placement object are applied and available.
+	//
+	// This can be either an absolute value (e.g., 1) or a percentage (e.g., 10%).
+	//
+	// If a percentage is specified, Fleet will calculate the corresponding absolute values
+	// as follows:
+	// * if the linked Placement object is of the PickFixed placement type,
+	//   we don't perform any calculation because eviction is not allowed for PickFixed CRP.
+	// * if the linked Placement object is of the PickAll placement type, MinAvailable can be
+	//   specified but only as an integer since we cannot derive the total number of clusters selected.
+	// * if the linked Placement object is of the PickN placement type,
+	//   the percentage is against the number of clusters specified in the placement (i.e., the
+	//   value of the NumberOfClusters fields in the placement policy).
+	// The end result will be rounded up to the nearest integer if applicable.
+	//
+	// One may use a value of 0 for this field; in this case, voluntary disruption would be
+	// allowed at any time.
+	//
+	// This field is mutually exclusive with the MaxUnavailable field in the spec; exactly one
+	// of them can be set at a time.
+	//
+	// +kubebuilder:validation:XIntOrString
+	// +kubebuilder:validation:XValidation:rule="type(self) == string ? self.matches('^(100|[0-9]{1,2})%$') : self >= 0",message="If supplied value is String should match regex '^(100|[0-9]{1,2})%$' or If supplied value is Integer must be greater than or equal to 0"
+	// +optional
+	MinAvailable *intstr.IntOrString `json:"minAvailable,omitempty"`
+}
+
+// ClusterResourcePlacementDisruptionBudgetList contains a list of ClusterResourcePlacementDisruptionBudget objects.
+// +kubebuilder:resource:scope=Cluster
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type ClusterResourcePlacementDisruptionBudgetList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+
+	// Items is the list of PlacementDisruptionBudget objects.
+	Items []ClusterResourcePlacementDisruptionBudget `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(
+		&ClusterResourcePlacementDisruptionBudget{},
+		&ClusterResourcePlacementDisruptionBudgetList{})
+}
diff --git a/apis/placement/v1beta1/eviction_types.go b/apis/placement/v1beta1/eviction_types.go
new file mode 100644
index 000000000..5c8abfbce
--- /dev/null
+++ b/apis/placement/v1beta1/eviction_types.go
@@ -0,0 +1,142 @@
+/*
+Copyright (c) Microsoft Corporation.
+Licensed under the MIT license.
+*/
+
+package v1beta1
+
+import (
+	"k8s.io/apimachinery/pkg/api/meta"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// +kubebuilder:object:root=true
+// +kubebuilder:resource:scope=Cluster,categories={fleet,fleet-placement},shortName=crpe
+// +kubebuilder:subresource:status
+// +kubebuilder:storageversion
+
+// ClusterResourcePlacementEviction is an eviction attempt on a specific placement from
+// a ClusterResourcePlacement object; one may use this API to force the removal of specific
+// resources from a cluster.
+//
+// An eviction is a voluntary disruption; its execution is subject to the disruption budget
+// linked with the target ClusterResourcePlacement object (if present).
+//
+// Beware that an eviction alone does not guarantee that a placement will not re-appear; i.e.,
+// after an eviction, the Fleet scheduler might still pick the previous target cluster for
+// placement. To prevent this, considering adding proper taints to the target cluster before running
+// an eviction that will exclude it from future placements; this is especially true in scenarios
+// where one would like to perform a cluster replacement.
+//
+// For safety reasons, Fleet will only execute an eviction once; the spec in this object is immutable,
+// and once executed, the object will be ignored after. To trigger another eviction attempt on the
+// same placement from the same ClusterResourcePlacement object, one must re-create (delete and
+// create) the same Eviction object. Note also that an Eviction object will be
+// ignored once it is deemed invalid (e.g., such an object might be targeting a CRP object or
+// a placement that does not exist yet), even if it does become valid later
+// (e.g., the CRP object or the placement appears later). To fix the situation, re-create the
+// Eviction object.
+//
+// Note: Eviction of resources from a cluster propagated by a PickFixed CRP is not allowed.
+// If the user wants to remove resources from a cluster propagated by a PickFixed CRP simply
+// remove the cluster name from cluster names field from the CRP spec.
+//
+// Executed evictions might be kept around for a while for auditing purposes; the Fleet controllers might
+// have a TTL set up for such objects and will garbage collect them automatically. For further
+// information, see the Fleet documentation.
+type ClusterResourcePlacementEviction struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	// Spec is the desired state of the ClusterResourcePlacementEviction.
+	//
+	// Note that all fields in the spec are immutable.
+	// +required
+	Spec PlacementEvictionSpec `json:"spec"`
+
+	// Status is the observed state of the ClusterResourcePlacementEviction.
+	// +optional
+	Status PlacementEvictionStatus `json:"status,omitempty"`
+}
+
+// PlacementEvictionSpec is the desired state of the parent PlacementEviction.
+type PlacementEvictionSpec struct {
+	// PlacementName is the name of the Placement object which
+	// the Eviction object targets.
+	// +kubebuilder:validation:Required
+	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="The PlacementName field is immutable"
+	// +kubebuilder:validation:MaxLength=255
+	PlacementName string `json:"placementName"`
+
+	// ClusterName is the name of the cluster that the Eviction object targets.
+	// +kubebuilder:validation:Required
+	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="The ClusterName field is immutable"
+	// +kubebuilder:validation:MaxLength=255
+	ClusterName string `json:"clusterName"`
+}
+
+// PlacementEvictionStatus is the observed state of the parent PlacementEviction.
+type PlacementEvictionStatus struct {
+	// Conditions is the list of currently observed conditions for the
+	// PlacementEviction object.
+	//
+	// Available condition types include:
+	// * Valid: whether the Eviction object is valid, i.e., it targets at a valid placement.
+	// * Executed: whether the Eviction object has been executed.
+	// +optional
+	Conditions []metav1.Condition `json:"conditions,omitempty"`
+}
+
+// PlacementEvictionConditionType identifies a specific condition of the
+// PlacementEviction.
+type PlacementEvictionConditionType string
+
+const (
+	// PlacementEvictionConditionTypeValid indicates whether the Eviction object is valid.
+	//
+	// The following values are possible:
+	// * True: the Eviction object is valid.
+	// * False: the Eviction object is invalid; it might be targeting a CRP object or a placement
+	//   that does not exist yet.
+	//   Note that this is a terminal state; once an Eviction object is deemed invalid, it will
+	//   not be evaluated again, even if the target appears later.
+	PlacementEvictionConditionTypeValid PlacementEvictionConditionType = "Valid"
+
+	// PlacementEvictionConditionTypeExecuted indicates whether the Eviction object has been executed.
+	//
+	// The following values are possible:
+	// * True: the Eviction object has been executed.
+	//   Note that this is a terminal state; once an Eviction object is executed, it will not be
+	//   executed again.
+	// * False: the Eviction object has not been executed yet.
+	PlacementEvictionConditionTypeExecuted PlacementEvictionConditionType = "Executed"
+)
+
+// ClusterResourcePlacementEvictionList contains a list of ClusterResourcePlacementEviction objects.
+// +kubebuilder:resource:scope=Cluster
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+type ClusterResourcePlacementEvictionList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+
+	// Items is the list of ClusterResourcePlacementEviction objects.
+	Items []ClusterResourcePlacementEviction `json:"items"`
+}
+
+// SetConditions set the given conditions on the ClusterResourcePlacementEviction.
+func (e *ClusterResourcePlacementEviction) SetConditions(conditions ...metav1.Condition) {
+	for _, c := range conditions {
+		meta.SetStatusCondition(&e.Status.Conditions, c)
+	}
+}
+
+// GetCondition returns the condition of the given ClusterResourcePlacementEviction.
+func (e *ClusterResourcePlacementEviction) GetCondition(conditionType string) *metav1.Condition {
+	return meta.FindStatusCondition(e.Status.Conditions, conditionType)
+}
+
+func init() {
+	SchemeBuilder.Register(
+		&ClusterResourcePlacementEviction{},
+		&ClusterResourcePlacementEvictionList{})
+}
diff --git a/apis/placement/v1beta1/zz_generated.deepcopy.go b/apis/placement/v1beta1/zz_generated.deepcopy.go
index 392ab3571..4ab8c4a7d 100644
--- a/apis/placement/v1beta1/zz_generated.deepcopy.go
+++ b/apis/placement/v1beta1/zz_generated.deepcopy.go
@@ -10,7 +10,7 @@ Licensed under the MIT license.
 package v1beta1
 
 import (
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/intstr"
 )
@@ -432,6 +432,123 @@ func (in *ClusterResourcePlacement) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterResourcePlacementDisruptionBudget) DeepCopyInto(out *ClusterResourcePlacementDisruptionBudget) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourcePlacementDisruptionBudget.
+func (in *ClusterResourcePlacementDisruptionBudget) DeepCopy() *ClusterResourcePlacementDisruptionBudget {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterResourcePlacementDisruptionBudget)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterResourcePlacementDisruptionBudget) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterResourcePlacementDisruptionBudgetList) DeepCopyInto(out *ClusterResourcePlacementDisruptionBudgetList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]ClusterResourcePlacementDisruptionBudget, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourcePlacementDisruptionBudgetList.
+func (in *ClusterResourcePlacementDisruptionBudgetList) DeepCopy() *ClusterResourcePlacementDisruptionBudgetList {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterResourcePlacementDisruptionBudgetList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterResourcePlacementDisruptionBudgetList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterResourcePlacementEviction) DeepCopyInto(out *ClusterResourcePlacementEviction) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	out.Spec = in.Spec
+	in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourcePlacementEviction.
+func (in *ClusterResourcePlacementEviction) DeepCopy() *ClusterResourcePlacementEviction {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterResourcePlacementEviction)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterResourcePlacementEviction) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterResourcePlacementEvictionList) DeepCopyInto(out *ClusterResourcePlacementEvictionList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]ClusterResourcePlacementEviction, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterResourcePlacementEvictionList.
+func (in *ClusterResourcePlacementEvictionList) DeepCopy() *ClusterResourcePlacementEvictionList {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterResourcePlacementEvictionList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterResourcePlacementEvictionList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ClusterResourcePlacementList) DeepCopyInto(out *ClusterResourcePlacementList) {
 	*out = *in
@@ -1108,6 +1225,68 @@ func (in *PatchDetail) DeepCopy() *PatchDetail {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PlacementDisruptionBudgetSpec) DeepCopyInto(out *PlacementDisruptionBudgetSpec) {
+	*out = *in
+	if in.MaxUnavailable != nil {
+		in, out := &in.MaxUnavailable, &out.MaxUnavailable
+		*out = new(intstr.IntOrString)
+		**out = **in
+	}
+	if in.MinAvailable != nil {
+		in, out := &in.MinAvailable, &out.MinAvailable
+		*out = new(intstr.IntOrString)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementDisruptionBudgetSpec.
+func (in *PlacementDisruptionBudgetSpec) DeepCopy() *PlacementDisruptionBudgetSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(PlacementDisruptionBudgetSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PlacementEvictionSpec) DeepCopyInto(out *PlacementEvictionSpec) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementEvictionSpec.
+func (in *PlacementEvictionSpec) DeepCopy() *PlacementEvictionSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(PlacementEvictionSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PlacementEvictionStatus) DeepCopyInto(out *PlacementEvictionStatus) {
+	*out = *in
+	if in.Conditions != nil {
+		in, out := &in.Conditions, &out.Conditions
+		*out = make([]v1.Condition, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PlacementEvictionStatus.
+func (in *PlacementEvictionStatus) DeepCopy() *PlacementEvictionStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(PlacementEvictionStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *PlacementPolicy) DeepCopyInto(out *PlacementPolicy) {
 	*out = *in
diff --git a/cmd/hubagent/workload/setup.go b/cmd/hubagent/workload/setup.go
index 2d69043b8..2d2f8b422 100644
--- a/cmd/hubagent/workload/setup.go
+++ b/cmd/hubagent/workload/setup.go
@@ -97,8 +97,8 @@ var (
 	}
 
 	evictionGVKs = []schema.GroupVersionKind{
-		placementv1alpha1.GroupVersion.WithKind(placementv1alpha1.ClusterResourcePlacementEvictionKind),
-		placementv1alpha1.GroupVersion.WithKind(placementv1alpha1.ClusterResourcePlacementDisruptionBudgetKind),
+		placementv1beta1.GroupVersion.WithKind(placementv1beta1.ClusterResourcePlacementEvictionKind),
+		placementv1beta1.GroupVersion.WithKind(placementv1beta1.ClusterResourcePlacementDisruptionBudgetKind),
 	}
 )
 
diff --git a/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementdisruptionbudgets.yaml b/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementdisruptionbudgets.yaml
index c2b612d77..bc998eaf1 100644
--- a/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementdisruptionbudgets.yaml
+++ b/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementdisruptionbudgets.yaml
@@ -122,6 +122,127 @@ spec:
                   allowed at any time.
 
 
+                  This field is mutually exclusive with the MaxUnavailable field in the spec; exactly one
+                  of them can be set at a time.
+                x-kubernetes-int-or-string: true
+                x-kubernetes-validations:
+                - message: If supplied value is String should match regex '^(100|[0-9]{1,2})%$'
+                    or If supplied value is Integer must be greater than or equal
+                    to 0
+                  rule: 'type(self) == string ? self.matches(''^(100|[0-9]{1,2})%$'')
+                    : self >= 0'
+            type: object
+            x-kubernetes-validations:
+            - message: Both MaxUnavailable and MinAvailable cannot be specified
+              rule: '!(has(self.maxUnavailable) && has(self.minAvailable))'
+        required:
+        - spec
+        type: object
+    served: true
+    storage: false
+  - name: v1beta1
+    schema:
+      openAPIV3Schema:
+        description: |-
+          ClusterResourcePlacementDisruptionBudget is the policy applied to a ClusterResourcePlacement
+          object that specifies its disruption budget, i.e., how many placements (clusters) can be
+          down at the same time due to voluntary disruptions (e.g., evictions). Involuntary
+          disruptions are not subject to this budget, but will still count against it.
+
+
+          To apply a ClusterResourcePlacementDisruptionBudget to a ClusterResourcePlacement, use the
+          same name for the ClusterResourcePlacementDisruptionBudget object as the ClusterResourcePlacement
+          object. This guarantees a 1:1 link between the two objects.
+        properties:
+          apiVersion:
+            description: |-
+              APIVersion defines the versioned schema of this representation of an object.
+              Servers should convert recognized schemas to the latest internal value, and
+              may reject unrecognized values.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+            type: string
+          kind:
+            description: |-
+              Kind is a string value representing the REST resource this object represents.
+              Servers may infer this from the endpoint the client submits requests to.
+              Cannot be updated.
+              In CamelCase.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: Spec is the desired state of the ClusterResourcePlacementDisruptionBudget.
+            properties:
+              maxUnavailable:
+                anyOf:
+                - type: integer
+                - type: string
+                description: |-
+                  MaxUnavailable is the maximum number of placements (clusters) that can be down at the
+                  same time due to voluntary disruptions. For example, a setting of 1 would imply that
+                  a voluntary disruption (e.g., an eviction) can only happen if all placements (clusters)
+                  from the linked Placement object are applied and available.
+
+
+                  This can be either an absolute value (e.g., 1) or a percentage (e.g., 10%).
+
+
+                  If a percentage is specified, Fleet will calculate the corresponding absolute values
+                  as follows:
+                  * if the linked Placement object is of the PickFixed placement type,
+                    we don't perform any calculation because eviction is not allowed for PickFixed CRP.
+                  * if the linked Placement object is of the PickAll placement type, MaxUnavailable cannot
+                    be specified since we cannot derive the total number of clusters selected.
+                  * if the linked Placement object is of the PickN placement type,
+                    the percentage is against the number of clusters specified in the placement (i.e., the
+                    value of the NumberOfClusters fields in the placement policy).
+                  The end result will be rounded up to the nearest integer if applicable.
+
+
+                  One may use a value of 0 for this field; in this case, no voluntary disruption would be
+                  allowed.
+
+
+                  This field is mutually exclusive with the MinAvailable field in the spec; exactly one
+                  of them can be set at a time.
+                x-kubernetes-int-or-string: true
+                x-kubernetes-validations:
+                - message: If supplied value is String should match regex '^(100|[0-9]{1,2})%$'
+                    or If supplied value is Integer must be greater than or equal
+                    to 0
+                  rule: 'type(self) == string ? self.matches(''^(100|[0-9]{1,2})%$'')
+                    : self >= 0'
+              minAvailable:
+                anyOf:
+                - type: integer
+                - type: string
+                description: |-
+                  MinAvailable is the minimum number of placements (clusters) that must be available at any
+                  time despite voluntary disruptions. For example, a setting of 10 would imply that
+                  a voluntary disruption (e.g., an eviction) can only happen if there are at least 11
+                  placements (clusters) from the linked Placement object are applied and available.
+
+
+                  This can be either an absolute value (e.g., 1) or a percentage (e.g., 10%).
+
+
+                  If a percentage is specified, Fleet will calculate the corresponding absolute values
+                  as follows:
+                  * if the linked Placement object is of the PickFixed placement type,
+                    we don't perform any calculation because eviction is not allowed for PickFixed CRP.
+                  * if the linked Placement object is of the PickAll placement type, MinAvailable can be
+                    specified but only as an integer since we cannot derive the total number of clusters selected.
+                  * if the linked Placement object is of the PickN placement type,
+                    the percentage is against the number of clusters specified in the placement (i.e., the
+                    value of the NumberOfClusters fields in the placement policy).
+                  The end result will be rounded up to the nearest integer if applicable.
+
+
+                  One may use a value of 0 for this field; in this case, voluntary disruption would be
+                  allowed at any time.
+
+
                   This field is mutually exclusive with the MaxUnavailable field in the spec; exactly one
                   of them can be set at a time.
                 x-kubernetes-int-or-string: true
diff --git a/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementevictions.yaml b/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementevictions.yaml
index e4d0a8e96..acec70803 100644
--- a/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementevictions.yaml
+++ b/config/crd/bases/placement.kubernetes-fleet.io_clusterresourceplacementevictions.yaml
@@ -112,6 +112,178 @@ spec:
                   PlacementEviction object.
 
 
+                  Available condition types include:
+                  * Valid: whether the Eviction object is valid, i.e., it targets at a valid placement.
+                  * Executed: whether the Eviction object has been executed.
+                items:
+                  description: "Condition contains details for one aspect of the current
+                    state of this API Resource.\n---\nThis struct is intended for
+                    direct use as an array at the field path .status.conditions.  For
+                    example,\n\n\n\ttype FooStatus struct{\n\t    // Represents the
+                    observations of a foo's current state.\n\t    // Known .status.conditions.type
+                    are: \"Available\", \"Progressing\", and \"Degraded\"\n\t    //
+                    +patchMergeKey=type\n\t    // +patchStrategy=merge\n\t    // +listType=map\n\t
+                    \   // +listMapKey=type\n\t    Conditions []metav1.Condition `json:\"conditions,omitempty\"
+                    patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
+                    \   // other fields\n\t}"
+                  properties:
+                    lastTransitionTime:
+                      description: |-
+                        lastTransitionTime is the last time the condition transitioned from one status to another.
+                        This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.
+                      format: date-time
+                      type: string
+                    message:
+                      description: |-
+                        message is a human readable message indicating details about the transition.
+                        This may be an empty string.
+                      maxLength: 32768
+                      type: string
+                    observedGeneration:
+                      description: |-
+                        observedGeneration represents the .metadata.generation that the condition was set based upon.
+                        For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
+                        with respect to the current state of the instance.
+                      format: int64
+                      minimum: 0
+                      type: integer
+                    reason:
+                      description: |-
+                        reason contains a programmatic identifier indicating the reason for the condition's last transition.
+                        Producers of specific condition types may define expected values and meanings for this field,
+                        and whether the values are considered a guaranteed API.
+                        The value should be a CamelCase string.
+                        This field may not be empty.
+                      maxLength: 1024
+                      minLength: 1
+                      pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+                      type: string
+                    status:
+                      description: status of the condition, one of True, False, Unknown.
+                      enum:
+                      - "True"
+                      - "False"
+                      - Unknown
+                      type: string
+                    type:
+                      description: |-
+                        type of condition in CamelCase or in foo.example.com/CamelCase.
+                        ---
+                        Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
+                        useful (see .node.status.conditions), the ability to deconflict is important.
+                        The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+                      maxLength: 316
+                      pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+                      type: string
+                  required:
+                  - lastTransitionTime
+                  - message
+                  - reason
+                  - status
+                  - type
+                  type: object
+                type: array
+            type: object
+        required:
+        - spec
+        type: object
+    served: true
+    storage: false
+    subresources:
+      status: {}
+  - name: v1beta1
+    schema:
+      openAPIV3Schema:
+        description: |-
+          ClusterResourcePlacementEviction is an eviction attempt on a specific placement from
+          a ClusterResourcePlacement object; one may use this API to force the removal of specific
+          resources from a cluster.
+
+
+          An eviction is a voluntary disruption; its execution is subject to the disruption budget
+          linked with the target ClusterResourcePlacement object (if present).
+
+
+          Beware that an eviction alone does not guarantee that a placement will not re-appear; i.e.,
+          after an eviction, the Fleet scheduler might still pick the previous target cluster for
+          placement. To prevent this, considering adding proper taints to the target cluster before running
+          an eviction that will exclude it from future placements; this is especially true in scenarios
+          where one would like to perform a cluster replacement.
+
+
+          For safety reasons, Fleet will only execute an eviction once; the spec in this object is immutable,
+          and once executed, the object will be ignored after. To trigger another eviction attempt on the
+          same placement from the same ClusterResourcePlacement object, one must re-create (delete and
+          create) the same Eviction object. Note also that an Eviction object will be
+          ignored once it is deemed invalid (e.g., such an object might be targeting a CRP object or
+          a placement that does not exist yet), even if it does become valid later
+          (e.g., the CRP object or the placement appears later). To fix the situation, re-create the
+          Eviction object.
+
+
+          Note: Eviction of resources from a cluster propagated by a PickFixed CRP is not allowed.
+          If the user wants to remove resources from a cluster propagated by a PickFixed CRP simply
+          remove the cluster name from cluster names field from the CRP spec.
+
+
+          Executed evictions might be kept around for a while for auditing purposes; the Fleet controllers might
+          have a TTL set up for such objects and will garbage collect them automatically. For further
+          information, see the Fleet documentation.
+        properties:
+          apiVersion:
+            description: |-
+              APIVersion defines the versioned schema of this representation of an object.
+              Servers should convert recognized schemas to the latest internal value, and
+              may reject unrecognized values.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+            type: string
+          kind:
+            description: |-
+              Kind is a string value representing the REST resource this object represents.
+              Servers may infer this from the endpoint the client submits requests to.
+              Cannot be updated.
+              In CamelCase.
+              More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: |-
+              Spec is the desired state of the ClusterResourcePlacementEviction.
+
+
+              Note that all fields in the spec are immutable.
+            properties:
+              clusterName:
+                description: ClusterName is the name of the cluster that the Eviction
+                  object targets.
+                maxLength: 255
+                type: string
+                x-kubernetes-validations:
+                - message: The ClusterName field is immutable
+                  rule: self == oldSelf
+              placementName:
+                description: |-
+                  PlacementName is the name of the Placement object which
+                  the Eviction object targets.
+                maxLength: 255
+                type: string
+                x-kubernetes-validations:
+                - message: The PlacementName field is immutable
+                  rule: self == oldSelf
+            required:
+            - clusterName
+            - placementName
+            type: object
+          status:
+            description: Status is the observed state of the ClusterResourcePlacementEviction.
+            properties:
+              conditions:
+                description: |-
+                  Conditions is the list of currently observed conditions for the
+                  PlacementEviction object.
+
+
                   Available condition types include:
                   * Valid: whether the Eviction object is valid, i.e., it targets at a valid placement.
                   * Executed: whether the Eviction object has been executed.
diff --git a/pkg/controllers/clusterresourceplacementeviction/controller.go b/pkg/controllers/clusterresourceplacementeviction/controller.go
index beac3bebb..69d39133e 100644
--- a/pkg/controllers/clusterresourceplacementeviction/controller.go
+++ b/pkg/controllers/clusterresourceplacementeviction/controller.go
@@ -20,7 +20,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	ctrl "sigs.k8s.io/controller-runtime/pkg/controller"
 
-	placementv1alpha1 "go.goms.io/fleet/apis/placement/v1alpha1"
 	placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
 	bindingutils "go.goms.io/fleet/pkg/utils/binding"
 	"go.goms.io/fleet/pkg/utils/condition"
@@ -43,7 +42,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req runtime.Request) (runtim
 		klog.V(2).InfoS("ClusterResourcePlacementEviction reconciliation ends", "clusterResourcePlacementEviction", evictionName, "latency", latency)
 	}()
 
-	var eviction placementv1alpha1.ClusterResourcePlacementEviction
+	var eviction placementv1beta1.ClusterResourcePlacementEviction
 	if err := r.Client.Get(ctx, req.NamespacedName, &eviction); err != nil {
 		klog.ErrorS(err, "Failed to get cluster resource placement eviction", "clusterResourcePlacementEviction", evictionName)
 		return runtime.Result{}, client.IgnoreNotFound(err)
@@ -71,7 +70,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req runtime.Request) (runtim
 }
 
 // validateEviction performs validation for eviction object's spec and returns a wrapped validation result.
-func (r *Reconciler) validateEviction(ctx context.Context, eviction *placementv1alpha1.ClusterResourcePlacementEviction) (*evictionValidationResult, error) {
+func (r *Reconciler) validateEviction(ctx context.Context, eviction *placementv1beta1.ClusterResourcePlacementEviction) (*evictionValidationResult, error) {
 	validationResult := &evictionValidationResult{isValid: false}
 	var crp placementv1beta1.ClusterResourcePlacement
 	if err := r.Client.Get(ctx, types.NamespacedName{Name: eviction.Spec.PlacementName}, &crp); err != nil {
@@ -128,7 +127,7 @@ func (r *Reconciler) validateEviction(ctx context.Context, eviction *placementv1
 }
 
 // updateEvictionStatus updates eviction status.
-func (r *Reconciler) updateEvictionStatus(ctx context.Context, eviction *placementv1alpha1.ClusterResourcePlacementEviction) error {
+func (r *Reconciler) updateEvictionStatus(ctx context.Context, eviction *placementv1beta1.ClusterResourcePlacementEviction) error {
 	evictionRef := klog.KObj(eviction)
 	if err := r.Client.Status().Update(ctx, eviction); err != nil {
 		klog.ErrorS(err, "Failed to update eviction status", "clusterResourcePlacementEviction", evictionRef)
@@ -155,7 +154,7 @@ func (r *Reconciler) deleteClusterResourceBinding(ctx context.Context, binding *
 }
 
 // executeEviction tries to remove resources from target cluster placed by placement targeted by eviction.
-func (r *Reconciler) executeEviction(ctx context.Context, validationResult *evictionValidationResult, eviction *placementv1alpha1.ClusterResourcePlacementEviction) error {
+func (r *Reconciler) executeEviction(ctx context.Context, validationResult *evictionValidationResult, eviction *placementv1beta1.ClusterResourcePlacementEviction) error {
 	// Unwrap validation result for processing.
 	crp, evictionTargetBinding, bindingList := validationResult.crp, validationResult.crb, validationResult.bindings
 
@@ -185,7 +184,7 @@ func (r *Reconciler) executeEviction(ctx context.Context, validationResult *evic
 		return nil
 	}
 
-	var db placementv1alpha1.ClusterResourcePlacementDisruptionBudget
+	var db placementv1beta1.ClusterResourcePlacementDisruptionBudget
 	if err := r.Client.Get(ctx, types.NamespacedName{Name: crp.Name}, &db); err != nil {
 		if k8serrors.IsNotFound(err) {
 			if err = r.deleteClusterResourceBinding(ctx, evictionTargetBinding); err != nil {
@@ -219,14 +218,14 @@ func (r *Reconciler) executeEviction(ctx context.Context, validationResult *evic
 }
 
 // isEvictionInTerminalState checks to see if eviction is in a terminal state.
-func isEvictionInTerminalState(eviction *placementv1alpha1.ClusterResourcePlacementEviction) bool {
-	validCondition := eviction.GetCondition(string(placementv1alpha1.PlacementEvictionConditionTypeValid))
+func isEvictionInTerminalState(eviction *placementv1beta1.ClusterResourcePlacementEviction) bool {
+	validCondition := eviction.GetCondition(string(placementv1beta1.PlacementEvictionConditionTypeValid))
 	if condition.IsConditionStatusFalse(validCondition, eviction.GetGeneration()) {
 		klog.V(2).InfoS("Invalid eviction, no need to reconcile", "clusterResourcePlacementEviction", eviction.Name)
 		return true
 	}
 
-	executedCondition := eviction.GetCondition(string(placementv1alpha1.PlacementEvictionConditionTypeExecuted))
+	executedCondition := eviction.GetCondition(string(placementv1beta1.PlacementEvictionConditionTypeExecuted))
 	if executedCondition != nil {
 		klog.V(2).InfoS("Eviction has executed condition specified, no need to reconcile", "clusterResourcePlacementEviction", eviction.Name)
 		return true
@@ -250,7 +249,7 @@ func isPlacementPresent(binding *placementv1beta1.ClusterResourceBinding) bool {
 }
 
 // isEvictionAllowed calculates if eviction allowed based on available bindings and spec specified in placement disruption budget.
-func isEvictionAllowed(bindings []placementv1beta1.ClusterResourceBinding, crp placementv1beta1.ClusterResourcePlacement, db placementv1alpha1.ClusterResourcePlacementDisruptionBudget) (bool, int) {
+func isEvictionAllowed(bindings []placementv1beta1.ClusterResourceBinding, crp placementv1beta1.ClusterResourcePlacement, db placementv1beta1.ClusterResourcePlacementDisruptionBudget) (bool, int) {
 	availableBindings := 0
 	for i := range bindings {
 		availableCondition := bindings[i].GetCondition(string(placementv1beta1.ResourceBindingAvailable))
@@ -290,9 +289,9 @@ func isEvictionAllowed(bindings []placementv1beta1.ClusterResourceBinding, crp p
 }
 
 // markEvictionValid sets the valid condition as true in eviction status.
-func markEvictionValid(eviction *placementv1alpha1.ClusterResourcePlacementEviction) {
+func markEvictionValid(eviction *placementv1beta1.ClusterResourcePlacementEviction) {
 	cond := metav1.Condition{
-		Type:               string(placementv1alpha1.PlacementEvictionConditionTypeValid),
+		Type:               string(placementv1beta1.PlacementEvictionConditionTypeValid),
 		Status:             metav1.ConditionTrue,
 		ObservedGeneration: eviction.Generation,
 		Reason:             condition.ClusterResourcePlacementEvictionValidReason,
@@ -304,9 +303,9 @@ func markEvictionValid(eviction *placementv1alpha1.ClusterResourcePlacementEvict
 }
 
 // markEvictionInvalid sets the valid condition as false in eviction status.
-func markEvictionInvalid(eviction *placementv1alpha1.ClusterResourcePlacementEviction, message string) {
+func markEvictionInvalid(eviction *placementv1beta1.ClusterResourcePlacementEviction, message string) {
 	cond := metav1.Condition{
-		Type:               string(placementv1alpha1.PlacementEvictionConditionTypeValid),
+		Type:               string(placementv1beta1.PlacementEvictionConditionTypeValid),
 		Status:             metav1.ConditionFalse,
 		ObservedGeneration: eviction.Generation,
 		Reason:             condition.ClusterResourcePlacementEvictionInvalidReason,
@@ -317,9 +316,9 @@ func markEvictionInvalid(eviction *placementv1alpha1.ClusterResourcePlacementEvi
 }
 
 // markEvictionExecuted sets the executed condition as true in eviction status.
-func markEvictionExecuted(eviction *placementv1alpha1.ClusterResourcePlacementEviction, message string) {
+func markEvictionExecuted(eviction *placementv1beta1.ClusterResourcePlacementEviction, message string) {
 	cond := metav1.Condition{
-		Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+		Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 		Status:             metav1.ConditionTrue,
 		ObservedGeneration: eviction.Generation,
 		Reason:             condition.ClusterResourcePlacementEvictionExecutedReason,
@@ -330,9 +329,9 @@ func markEvictionExecuted(eviction *placementv1alpha1.ClusterResourcePlacementEv
 }
 
 // markEvictionNotExecuted sets the executed condition as false in eviction status.
-func markEvictionNotExecuted(eviction *placementv1alpha1.ClusterResourcePlacementEviction, message string) {
+func markEvictionNotExecuted(eviction *placementv1beta1.ClusterResourcePlacementEviction, message string) {
 	cond := metav1.Condition{
-		Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+		Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 		Status:             metav1.ConditionFalse,
 		ObservedGeneration: eviction.Generation,
 		Reason:             condition.ClusterResourcePlacementEvictionNotExecutedReason,
@@ -346,7 +345,7 @@ func markEvictionNotExecuted(eviction *placementv1alpha1.ClusterResourcePlacemen
 func (r *Reconciler) SetupWithManager(mgr runtime.Manager) error {
 	return runtime.NewControllerManagedBy(mgr).
 		WithOptions(ctrl.Options{MaxConcurrentReconciles: 1}). // max concurrent reconciles is currently set to 1 for concurrency control.
-		For(&placementv1alpha1.ClusterResourcePlacementEviction{}).
+		For(&placementv1beta1.ClusterResourcePlacementEviction{}).
 		Complete(r)
 }
 
diff --git a/pkg/controllers/clusterresourceplacementeviction/controller_intergration_test.go b/pkg/controllers/clusterresourceplacementeviction/controller_intergration_test.go
index a91d5b877..3ba44867e 100644
--- a/pkg/controllers/clusterresourceplacementeviction/controller_intergration_test.go
+++ b/pkg/controllers/clusterresourceplacementeviction/controller_intergration_test.go
@@ -19,7 +19,6 @@ import (
 	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
-	placementv1alpha1 "go.goms.io/fleet/apis/placement/v1alpha1"
 	placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
 	"go.goms.io/fleet/pkg/utils/condition"
 	testutilseviction "go.goms.io/fleet/test/utils/eviction"
@@ -85,11 +84,11 @@ var _ = Describe("Test ClusterResourcePlacementEviction Controller", func() {
 		})
 
 		By("Create ClusterResourcePlacementDisruptionBudget", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: crpName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -178,11 +177,11 @@ var _ = Describe("Test ClusterResourcePlacementEviction Controller", func() {
 		})
 
 		By("Create ClusterResourcePlacementDisruptionBudget", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: crpName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -256,11 +255,11 @@ var _ = Describe("Test ClusterResourcePlacementEviction Controller", func() {
 		})
 
 		By("Create ClusterResourcePlacementDisruptionBudget", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: crpName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -372,11 +371,11 @@ var _ = Describe("Test ClusterResourcePlacementEviction Controller", func() {
 		})
 
 		By("Create ClusterResourcePlacementDisruptionBudget", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: crpName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -445,13 +444,13 @@ func buildTestPickNCRP(crpName string, clusterCount int32) placementv1beta1.Clus
 	}
 }
 
-func buildTestEviction(evictionName, placementName, clusterName string) *placementv1alpha1.ClusterResourcePlacementEviction {
-	return &placementv1alpha1.ClusterResourcePlacementEviction{
+func buildTestEviction(evictionName, placementName, clusterName string) *placementv1beta1.ClusterResourcePlacementEviction {
+	return &placementv1beta1.ClusterResourcePlacementEviction{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:       evictionName,
 			Generation: 1,
 		},
-		Spec: placementv1alpha1.PlacementEvictionSpec{
+		Spec: placementv1beta1.PlacementEvictionSpec{
 			PlacementName: placementName,
 			ClusterName:   clusterName,
 		},
@@ -460,7 +459,7 @@ func buildTestEviction(evictionName, placementName, clusterName string) *placeme
 
 func ensureEvictionRemoved(name string) {
 	// Delete eviction.
-	eviction := placementv1alpha1.ClusterResourcePlacementEviction{
+	eviction := placementv1beta1.ClusterResourcePlacementEviction{
 		ObjectMeta: metav1.ObjectMeta{
 			Name: name,
 		},
@@ -494,7 +493,7 @@ func ensureCRPRemoved(name string) {
 
 func ensureCRPDBRemoved(name string) {
 	// Delete CRPDB.
-	crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+	crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 		ObjectMeta: metav1.ObjectMeta{
 			Name: name,
 		},
diff --git a/pkg/controllers/clusterresourceplacementeviction/controller_test.go b/pkg/controllers/clusterresourceplacementeviction/controller_test.go
index 7539e597e..60149851e 100644
--- a/pkg/controllers/clusterresourceplacementeviction/controller_test.go
+++ b/pkg/controllers/clusterresourceplacementeviction/controller_test.go
@@ -20,7 +20,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
-	placementv1alpha1 "go.goms.io/fleet/apis/placement/v1alpha1"
 	placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
 	"go.goms.io/fleet/pkg/utils/condition"
 	"go.goms.io/fleet/pkg/utils/defaulter"
@@ -86,7 +85,7 @@ func TestValidateEviction(t *testing.T) {
 	}
 	tests := []struct {
 		name                         string
-		eviction                     *placementv1alpha1.ClusterResourcePlacementEviction
+		eviction                     *placementv1beta1.ClusterResourcePlacementEviction
 		crp                          *placementv1beta1.ClusterResourcePlacement
 		bindings                     []placementv1beta1.ClusterResourceBinding
 		wantValidationResult         *evictionValidationResult
@@ -100,7 +99,7 @@ func TestValidateEviction(t *testing.T) {
 				isValid: false,
 			},
 			wantEvictionInvalidCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeValid),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeValid),
 				Status:             metav1.ConditionFalse,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionInvalidReason,
@@ -127,7 +126,7 @@ func TestValidateEviction(t *testing.T) {
 				isValid: false,
 			},
 			wantEvictionInvalidCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeValid),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeValid),
 				Status:             metav1.ConditionFalse,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionInvalidReason,
@@ -152,7 +151,7 @@ func TestValidateEviction(t *testing.T) {
 				isValid: false,
 			},
 			wantEvictionInvalidCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeValid),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeValid),
 				Status:             metav1.ConditionFalse,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionInvalidReason,
@@ -173,7 +172,7 @@ func TestValidateEviction(t *testing.T) {
 				bindings: []placementv1beta1.ClusterResourceBinding{testBinding1, testBinding2},
 			},
 			wantEvictionInvalidCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeValid),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeValid),
 				Status:             metav1.ConditionFalse,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionInvalidReason,
@@ -191,7 +190,7 @@ func TestValidateEviction(t *testing.T) {
 				bindings: []placementv1beta1.ClusterResourceBinding{},
 			},
 			wantEvictionInvalidCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeValid),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeValid),
 				Status:             metav1.ConditionFalse,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionInvalidReason,
@@ -247,7 +246,7 @@ func TestValidateEviction(t *testing.T) {
 			if diff := cmp.Diff(tc.wantValidationResult, gotValidationResult, validationResultCmpOptions...); diff != "" {
 				t.Errorf("validateEviction() validation result mismatch (-want, +got):\n%s", diff)
 			}
-			gotInvalidCondition := tc.eviction.GetCondition(string(placementv1alpha1.PlacementEvictionConditionTypeValid))
+			gotInvalidCondition := tc.eviction.GetCondition(string(placementv1beta1.PlacementEvictionConditionTypeValid))
 			if diff := cmp.Diff(tc.wantEvictionInvalidCondition, gotInvalidCondition, cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime")); diff != "" {
 				t.Errorf("validateEviction() eviction invalid condition mismatch (-want, +got):\n%s", diff)
 			}
@@ -369,8 +368,8 @@ func TestExecuteEviction(t *testing.T) {
 	tests := []struct {
 		name                          string
 		validationResult              *evictionValidationResult
-		eviction                      *placementv1alpha1.ClusterResourcePlacementEviction
-		pdb                           *placementv1alpha1.ClusterResourcePlacementDisruptionBudget
+		eviction                      *placementv1beta1.ClusterResourcePlacementEviction
+		pdb                           *placementv1beta1.ClusterResourcePlacementDisruptionBudget
 		wantEvictionExecutedCondition *metav1.Condition
 		wantErr                       error
 	}{
@@ -388,7 +387,7 @@ func TestExecuteEviction(t *testing.T) {
 			},
 			eviction: buildTestEviction(testEvictionName, testCRPName, testClusterName),
 			wantEvictionExecutedCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 				Status:             metav1.ConditionFalse,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionNotExecutedReason,
@@ -410,7 +409,7 @@ func TestExecuteEviction(t *testing.T) {
 			},
 			eviction: buildTestEviction(testEvictionName, testCRPName, testClusterName),
 			wantEvictionExecutedCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 				Status:             metav1.ConditionFalse,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionNotExecutedReason,
@@ -433,7 +432,7 @@ func TestExecuteEviction(t *testing.T) {
 			},
 			eviction: buildTestEviction(testEvictionName, testCRPName, testClusterName),
 			wantEvictionExecutedCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 				Status:             metav1.ConditionFalse,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionNotExecutedReason,
@@ -458,7 +457,7 @@ func TestExecuteEviction(t *testing.T) {
 			},
 			eviction: buildTestEviction(testEvictionName, testCRPName, testClusterName),
 			wantEvictionExecutedCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 				Status:             metav1.ConditionTrue,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionExecutedReason,
@@ -491,7 +490,7 @@ func TestExecuteEviction(t *testing.T) {
 			},
 			eviction: buildTestEviction(testEvictionName, testCRPName, testClusterName),
 			wantEvictionExecutedCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 				Status:             metav1.ConditionTrue,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionExecutedReason,
@@ -530,7 +529,7 @@ func TestExecuteEviction(t *testing.T) {
 			},
 			eviction: buildTestEviction(testEvictionName, testCRPName, testClusterName),
 			wantEvictionExecutedCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 				Status:             metav1.ConditionTrue,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionExecutedReason,
@@ -550,7 +549,7 @@ func TestExecuteEviction(t *testing.T) {
 			},
 			eviction: buildTestEviction(testEvictionName, testCRPName, testClusterName),
 			wantEvictionExecutedCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 				Status:             metav1.ConditionTrue,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionExecutedReason,
@@ -565,11 +564,11 @@ func TestExecuteEviction(t *testing.T) {
 				crp: ptr.To(buildTestPickAllCRP(testCRPName)),
 			},
 			eviction: buildTestEviction(testEvictionName, testCRPName, testClusterName),
-			pdb: &placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			pdb: &placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testCRPName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -577,7 +576,7 @@ func TestExecuteEviction(t *testing.T) {
 				},
 			},
 			wantEvictionExecutedCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 				Status:             metav1.ConditionFalse,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionNotExecutedReason,
@@ -592,11 +591,11 @@ func TestExecuteEviction(t *testing.T) {
 				crp: ptr.To(buildTestPickAllCRP(testCRPName)),
 			},
 			eviction: buildTestEviction(testEvictionName, testCRPName, testClusterName),
-			pdb: &placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			pdb: &placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testCRPName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.String,
 						StrVal: "10%",
@@ -604,7 +603,7 @@ func TestExecuteEviction(t *testing.T) {
 				},
 			},
 			wantEvictionExecutedCondition: &metav1.Condition{
-				Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+				Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 				Status:             metav1.ConditionFalse,
 				ObservedGeneration: 1,
 				Reason:             condition.ClusterResourcePlacementEvictionNotExecutedReason,
@@ -628,7 +627,7 @@ func TestExecuteEviction(t *testing.T) {
 				Client: fakeClient,
 			}
 			gotErr := r.executeEviction(ctx, tc.validationResult, tc.eviction)
-			gotExecutedCondition := tc.eviction.GetCondition(string(placementv1alpha1.PlacementEvictionConditionTypeExecuted))
+			gotExecutedCondition := tc.eviction.GetCondition(string(placementv1beta1.PlacementEvictionConditionTypeExecuted))
 			if diff := cmp.Diff(tc.wantEvictionExecutedCondition, gotExecutedCondition, cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime")); diff != "" {
 				t.Errorf("executeEviction() eviction executed condition mismatch (-want, +got):\n%s", diff)
 			}
@@ -713,7 +712,7 @@ func TestIsEvictionAllowed(t *testing.T) {
 		name                  string
 		crp                   placementv1beta1.ClusterResourcePlacement
 		bindings              []placementv1beta1.ClusterResourceBinding
-		disruptionBudget      placementv1alpha1.ClusterResourcePlacementDisruptionBudget
+		disruptionBudget      placementv1beta1.ClusterResourcePlacementDisruptionBudget
 		wantAllowed           bool
 		wantAvailableBindings int
 	}{
@@ -721,11 +720,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as Integer zero, one available binding - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 0,
@@ -739,11 +738,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as Integer zero, one unavailable bindings - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 0,
@@ -757,11 +756,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as Integer one, one unavailable binding - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -775,11 +774,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as Integer one, one available binding, upscaling - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -793,11 +792,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as Integer one, one available, one unavailable binding - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, boundUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -811,11 +810,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as Integer one, two available binding - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -829,11 +828,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as Integer one, available bindings greater than target, downscaling - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, anotherBoundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -847,11 +846,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as Integer greater than one - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 4),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundAvailableBinding, anotherBoundAvailableBinding, boundUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 2,
@@ -865,11 +864,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as Integer greater than one - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 3),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 2,
@@ -883,11 +882,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as Integer large number greater than target number - allows eviction",
 			crp:      buildTestPickNCRP(testCRPName, 4),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundAvailableBinding, boundUnavailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 10,
@@ -901,11 +900,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as percentage zero - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.String,
 						StrVal: "0%",
@@ -919,11 +918,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as percentage greater than zero, rounds up to 1 - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.String,
 						StrVal: "10%",
@@ -937,11 +936,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as percentage greater than zero, rounds up to 1 - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{
 						Type:   intstr.String,
 						StrVal: "10%",
@@ -955,11 +954,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as percentage greater than zero, rounds up to greater than 1 - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 4),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundAvailableBinding, boundUnavailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{ // equates to 2.
 						Type:   intstr.String,
 						StrVal: "40%",
@@ -973,11 +972,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as percentage greater than zero, rounds up to greater than 1 - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 3),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{ // equates to 2.
 						Type:   intstr.String,
 						StrVal: "50%",
@@ -991,11 +990,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as percentage hundred, target number greater than bindings - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 10),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundAvailableBinding, boundUnavailableBinding, anotherBoundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{ // equates to 10.
 						Type:   intstr.String,
 						StrVal: "100%",
@@ -1009,11 +1008,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as percentage hundred, target number equal to bindings - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{ // equates to 2.
 						Type:   intstr.String,
 						StrVal: "100%",
@@ -1027,11 +1026,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MaxUnavailable specified as percentage hundred, target number equal to bindings - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 4),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundAvailableBinding, boundUnavailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{ // equates to 4.
 						Type:   intstr.String,
 						StrVal: "100%",
@@ -1045,11 +1044,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer zero, unavailable binding - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 0,
@@ -1063,11 +1062,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer zero, available binding - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 0,
@@ -1081,11 +1080,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer one, unavailable binding - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -1099,11 +1098,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer one, available binding, upscaling - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -1117,11 +1116,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer one, one available, one unavailable binding - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, boundUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -1135,11 +1134,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer one, two available bindings - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -1153,11 +1152,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer one, available bindings greater than target number, downscaling - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, anotherBoundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -1171,11 +1170,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer greater than one - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 2,
@@ -1189,11 +1188,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer greater than one - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 4),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundAvailableBinding, anotherBoundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 2,
@@ -1207,11 +1206,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer greater than one, available bindings greater than target number, downscaling - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, anotherBoundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 3,
@@ -1225,11 +1224,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer large number greater than target number - blocks eviction",
 			crp:      buildTestPickNCRP(testCRPName, 5),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundAvailableBinding, anotherBoundAvailableBinding, boundUnavailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 10,
@@ -1243,11 +1242,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as percentage zero, all bindings are unavailable - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.String,
 						StrVal: "0%",
@@ -1261,11 +1260,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as percentage zero, all bindings are available - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 3),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, anotherBoundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.String,
 						StrVal: "0%",
@@ -1279,11 +1278,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as percentage rounds upto one - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 1),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.String,
 						StrVal: "10%",
@@ -1297,11 +1296,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as percentage rounds upto one - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.String,
 						StrVal: "10%",
@@ -1315,11 +1314,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as percentage greater than zero, rounds up to greater than 1 - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 3),
 			bindings: []placementv1beta1.ClusterResourceBinding{scheduledUnavailableBinding, boundAvailableBinding, anotherBoundAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{ // equates to 2.
 						Type:   intstr.String,
 						StrVal: "40%",
@@ -1333,11 +1332,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as percentage greater than zero, rounds up to greater than 1 - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 3),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, anotherBoundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{ // equates to 2.
 						Type:   intstr.String,
 						StrVal: "40%",
@@ -1351,11 +1350,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as percentage hundred, bindings less than target number - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 10),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, anotherBoundAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{ // equates to 10.
 						Type:   intstr.String,
 						StrVal: "100%",
@@ -1369,11 +1368,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as percentage hundred, bindings equal to target number  - block eviction",
 			crp:      buildTestPickNCRP(testCRPName, 3),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, anotherBoundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{ // equates to 3.
 						Type:   intstr.String,
 						StrVal: "100%",
@@ -1387,11 +1386,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as percentage hundred, bindings greater than target number - allow eviction",
 			crp:      buildTestPickNCRP(testCRPName, 2),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, anotherBoundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{ // equates to 2.
 						Type:   intstr.String,
 						StrVal: "100%",
@@ -1405,11 +1404,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer zero, available binding, PickAll CRP - allow eviction",
 			crp:      buildTestPickAllCRP(testCRPName),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 0,
@@ -1423,11 +1422,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer one, available binding, PickAll CRP - block eviction",
 			crp:      buildTestPickAllCRP(testCRPName),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 1,
@@ -1441,11 +1440,11 @@ func TestIsEvictionAllowed(t *testing.T) {
 			name:     "MinAvailable specified as Integer greater than one, available binding, PickAll CRP - allow eviction",
 			crp:      buildTestPickAllCRP(testCRPName),
 			bindings: []placementv1beta1.ClusterResourceBinding{boundAvailableBinding, anotherBoundAvailableBinding, unScheduledAvailableBinding},
-			disruptionBudget: placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			disruptionBudget: placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: testDisruptionBudgetName,
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{
 						Type:   intstr.Int,
 						IntVal: 2,
@@ -1487,7 +1486,7 @@ func serviceScheme(t *testing.T) *runtime.Scheme {
 	if err := placementv1beta1.AddToScheme(scheme); err != nil {
 		t.Fatalf("Failed to add placement v1beta1 scheme: %v", err)
 	}
-	if err := placementv1alpha1.AddToScheme(scheme); err != nil {
+	if err := placementv1beta1.AddToScheme(scheme); err != nil {
 		t.Fatalf("Failed to add v1alpha1 scheme: %v", err)
 	}
 	return scheme
diff --git a/pkg/controllers/clusterresourceplacementeviction/suite_test.go b/pkg/controllers/clusterresourceplacementeviction/suite_test.go
index 5ed603eae..695d0a711 100644
--- a/pkg/controllers/clusterresourceplacementeviction/suite_test.go
+++ b/pkg/controllers/clusterresourceplacementeviction/suite_test.go
@@ -24,7 +24,6 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/manager"
 	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
 
-	placementv1alpha1 "go.goms.io/fleet/apis/placement/v1alpha1"
 	placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
 )
 
@@ -59,8 +58,6 @@ var _ = BeforeSuite(func() {
 	Expect(err).Should(Succeed())
 	Expect(cfg).NotTo(BeNil())
 
-	err = placementv1alpha1.AddToScheme(scheme.Scheme)
-	Expect(err).NotTo(HaveOccurred())
 	err = placementv1beta1.AddToScheme(scheme.Scheme)
 	Expect(err).NotTo(HaveOccurred())
 
diff --git a/test/apis/placement/v1alpha1/api_validation_integration_test.go b/test/apis/placement/v1beta1/api_validation_integration_test.go
similarity index 80%
rename from test/apis/placement/v1alpha1/api_validation_integration_test.go
rename to test/apis/placement/v1beta1/api_validation_integration_test.go
index d8a79abcf..372c5a7f6 100644
--- a/test/apis/placement/v1alpha1/api_validation_integration_test.go
+++ b/test/apis/placement/v1beta1/api_validation_integration_test.go
@@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation.
 Licensed under the MIT license.
 */
 
-package v1alpha1
+package v1beta1
 
 import (
 	"errors"
@@ -16,7 +16,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
 
-	placementv1alpha1 "go.goms.io/fleet/apis/placement/v1alpha1"
+	placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
 )
 
 const (
@@ -26,11 +26,11 @@ const (
 var _ = Describe("Test placement v1alpha1 API validation", func() {
 	Context("Test ClusterPlacementDisruptionBudget API validation - valid cases", func() {
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid maxUnavailable - int", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: 2},
 				},
 			}
@@ -38,11 +38,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid maxUnavailable less than 10% specified as one digit - string", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "2%"},
 				},
 			}
@@ -50,11 +50,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid maxUnavailable less than 10% specified as two digits - string", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "02%"},
 				},
 			}
@@ -62,11 +62,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid maxUnavailable greater than or equal to 10% and less than 100% - string", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "10%"},
 				},
 			}
@@ -74,11 +74,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid maxUnavailable equal to 100% - string", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
 				},
 			}
@@ -86,11 +86,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable - int", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{Type: intstr.Int, IntVal: 2},
 				},
 			}
@@ -98,11 +98,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable less than 10% specified as one digit - string", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{Type: intstr.String, StrVal: "5%"},
 				},
 			}
@@ -110,11 +110,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable less than 10% specified as two digits - string", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{Type: intstr.String, StrVal: "05%"},
 				},
 			}
@@ -122,11 +122,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable greater than or equal to 10% and less than 100% - string", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{Type: intstr.String, StrVal: "10%"},
 				},
 			}
@@ -134,11 +134,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should allow creation of ClusterPlacementDisruptionBudget with valid minAvailable equal to 100% - string", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{Type: intstr.String, StrVal: "100%"},
 				},
 			}
@@ -146,7 +146,7 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		AfterEach(func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
@@ -157,11 +157,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 
 	Context("Test ClusterPlacementDisruptionBudget API validation - invalid cases", func() {
 		It("should deny creation of ClusterPlacementDisruptionBudget when both maxUnavailable and minAvailable are specified", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: 1},
 					MinAvailable:   &intstr.IntOrString{Type: intstr.String, StrVal: "10%"},
 				},
@@ -173,11 +173,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should deny creation of ClusterPlacementDisruptionBudget with invalid maxUnavailable - negative int", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: -1},
 				},
 			}
@@ -188,11 +188,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should deny creation of ClusterPlacementDisruptionBudget with invalid maxUnavailable - negative percentage", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "-1%"},
 				},
 			}
@@ -203,11 +203,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should deny creation of ClusterPlacementDisruptionBudget with invalid maxUnavailable - greater than 100", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "101%"},
 				},
 			}
@@ -218,11 +218,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should deny creation of ClusterPlacementDisruptionBudget with invalid maxUnavailable - no percentage specified", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MaxUnavailable: &intstr.IntOrString{Type: intstr.String, StrVal: "-1"},
 				},
 			}
@@ -233,11 +233,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should deny creation of ClusterPlacementDisruptionBudget with invalid minAvailable - negative int", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{Type: intstr.Int, IntVal: -1},
 				},
 			}
@@ -248,11 +248,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should deny creation of ClusterPlacementDisruptionBudget with invalid minAvailable - negative percentage", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{Type: intstr.String, StrVal: "-1%"},
 				},
 			}
@@ -263,11 +263,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should deny creation of ClusterPlacementDisruptionBudget with invalid minAvailable - greater than 100", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{Type: intstr.String, StrVal: "101%"},
 				},
 			}
@@ -278,11 +278,11 @@ var _ = Describe("Test placement v1alpha1 API validation", func() {
 		})
 
 		It("should deny creation of ClusterPlacementDisruptionBudget with invalid minAvailable - no percentage specified", func() {
-			crpdb := placementv1alpha1.ClusterResourcePlacementDisruptionBudget{
+			crpdb := placementv1beta1.ClusterResourcePlacementDisruptionBudget{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: fmt.Sprintf(crpdbNameTemplate, GinkgoParallelProcess()),
 				},
-				Spec: placementv1alpha1.PlacementDisruptionBudgetSpec{
+				Spec: placementv1beta1.PlacementDisruptionBudgetSpec{
 					MinAvailable: &intstr.IntOrString{Type: intstr.String, StrVal: "-1"},
 				},
 			}
diff --git a/test/apis/placement/v1alpha1/suite_test.go b/test/apis/placement/v1beta1/suite_test.go
similarity index 93%
rename from test/apis/placement/v1alpha1/suite_test.go
rename to test/apis/placement/v1beta1/suite_test.go
index 6b81b00d8..22a0159ac 100644
--- a/test/apis/placement/v1alpha1/suite_test.go
+++ b/test/apis/placement/v1beta1/suite_test.go
@@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation.
 Licensed under the MIT license.
 */
 
-package v1alpha1
+package v1beta1
 
 import (
 	"context"
@@ -21,7 +21,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/envtest"
 	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
 
-	placementv1alpha1 "go.goms.io/fleet/apis/placement/v1alpha1"
+	placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
 )
 
 var (
@@ -57,7 +57,7 @@ var _ = BeforeSuite(func() {
 	Expect(err).NotTo(HaveOccurred())
 	Expect(hubCfg).NotTo(BeNil())
 
-	Expect(placementv1alpha1.AddToScheme(scheme.Scheme)).Should(Succeed())
+	Expect(placementv1beta1.AddToScheme(scheme.Scheme)).Should(Succeed())
 
 	klog.InitFlags(flag.CommandLine)
 	flag.Parse()
diff --git a/test/e2e/actuals_test.go b/test/e2e/actuals_test.go
index fd7d257a1..cf5ea216e 100644
--- a/test/e2e/actuals_test.go
+++ b/test/e2e/actuals_test.go
@@ -16,7 +16,6 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
-	placementv1alpha1 "go.goms.io/fleet/apis/placement/v1alpha1"
 	placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
 	"go.goms.io/fleet/pkg/controllers/clusterresourceplacement"
 	"go.goms.io/fleet/pkg/controllers/work"
@@ -914,7 +913,7 @@ func crpRemovedActual(crpName string) func() error {
 
 func crpEvictionRemovedActual(crpEvictionName string) func() error {
 	return func() error {
-		if err := hubClient.Get(ctx, types.NamespacedName{Name: crpEvictionName}, &placementv1alpha1.ClusterResourcePlacementEviction{}); !errors.IsNotFound(err) {
+		if err := hubClient.Get(ctx, types.NamespacedName{Name: crpEvictionName}, &placementv1beta1.ClusterResourcePlacementEviction{}); !errors.IsNotFound(err) {
 			return fmt.Errorf("CRP eviction still exists or an unexpected error occurred: %w", err)
 		}
 
diff --git a/test/e2e/placement_eviction_test.go b/test/e2e/placement_eviction_test.go
index 916a5e939..313a98cbd 100644
--- a/test/e2e/placement_eviction_test.go
+++ b/test/e2e/placement_eviction_test.go
@@ -12,7 +12,6 @@ import (
 	. "github.com/onsi/gomega"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
-	placementv1alpha1 "go.goms.io/fleet/apis/placement/v1alpha1"
 	placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
 	"go.goms.io/fleet/pkg/utils/condition"
 	"go.goms.io/fleet/test/e2e/framework"
@@ -62,11 +61,11 @@ var _ = Describe("ClusterResourcePlacement eviction of bound binding, taint clus
 	})
 
 	It("create cluster resource placement eviction targeting member cluster 1", func() {
-		crpe := &placementv1alpha1.ClusterResourcePlacementEviction{
+		crpe := &placementv1beta1.ClusterResourcePlacementEviction{
 			ObjectMeta: metav1.ObjectMeta{
 				Name: crpEvictionName,
 			},
-			Spec: placementv1alpha1.PlacementEvictionSpec{
+			Spec: placementv1beta1.PlacementEvictionSpec{
 				PlacementName: crpName,
 				ClusterName:   taintClusterNames[0],
 			},
@@ -140,11 +139,11 @@ var _ = Describe("ClusterResourcePlacement eviction of bound binding, no taint s
 	It("should place resources on the all available member clusters", checkIfPlacedWorkResourcesOnAllMemberClusters)
 
 	It("create cluster resource placement eviction targeting member cluster 1", func() {
-		crpe := &placementv1alpha1.ClusterResourcePlacementEviction{
+		crpe := &placementv1beta1.ClusterResourcePlacementEviction{
 			ObjectMeta: metav1.ObjectMeta{
 				Name: crpEvictionName,
 			},
-			Spec: placementv1alpha1.PlacementEvictionSpec{
+			Spec: placementv1beta1.PlacementEvictionSpec{
 				PlacementName: crpName,
 				ClusterName:   memberCluster1EastProdName,
 			},
diff --git a/test/e2e/utils_test.go b/test/e2e/utils_test.go
index 9b898ace3..e552457f9 100644
--- a/test/e2e/utils_test.go
+++ b/test/e2e/utils_test.go
@@ -918,7 +918,7 @@ func ensureCRPAndRelatedResourcesDeletion(crpName string, memberClusters []*fram
 }
 
 func ensureCRPEvictionDeletion(crpEvictionName string) {
-	crpe := &placementv1alpha1.ClusterResourcePlacementEviction{
+	crpe := &placementv1beta1.ClusterResourcePlacementEviction{
 		ObjectMeta: metav1.ObjectMeta{
 			Name: crpEvictionName,
 		},
diff --git a/test/utils/eviction/eviction_status.go b/test/utils/eviction/eviction_status.go
index 06cc106ab..2ba0abe30 100644
--- a/test/utils/eviction/eviction_status.go
+++ b/test/utils/eviction/eviction_status.go
@@ -15,7 +15,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
-	placementv1alpha1 "go.goms.io/fleet/apis/placement/v1alpha1"
+	placementv1beta1 "go.goms.io/fleet/apis/placement/v1beta1"
 	"go.goms.io/fleet/pkg/utils/condition"
 )
 
@@ -32,7 +32,7 @@ var (
 
 func StatusUpdatedActual(ctx context.Context, client client.Client, evictionName string, isValidEviction *IsValidEviction, isExecutedEviction *IsExecutedEviction) func() error {
 	return func() error {
-		var eviction placementv1alpha1.ClusterResourcePlacementEviction
+		var eviction placementv1beta1.ClusterResourcePlacementEviction
 		if err := client.Get(ctx, types.NamespacedName{Name: evictionName}, &eviction); err != nil {
 			return err
 		}
@@ -40,7 +40,7 @@ func StatusUpdatedActual(ctx context.Context, client client.Client, evictionName
 		if isValidEviction != nil {
 			if isValidEviction.IsValid {
 				validCondition := metav1.Condition{
-					Type:               string(placementv1alpha1.PlacementEvictionConditionTypeValid),
+					Type:               string(placementv1beta1.PlacementEvictionConditionTypeValid),
 					Status:             metav1.ConditionTrue,
 					ObservedGeneration: eviction.GetGeneration(),
 					Reason:             condition.ClusterResourcePlacementEvictionValidReason,
@@ -49,7 +49,7 @@ func StatusUpdatedActual(ctx context.Context, client client.Client, evictionName
 				conditions = append(conditions, validCondition)
 			} else {
 				invalidCondition := metav1.Condition{
-					Type:               string(placementv1alpha1.PlacementEvictionConditionTypeValid),
+					Type:               string(placementv1beta1.PlacementEvictionConditionTypeValid),
 					Status:             metav1.ConditionFalse,
 					ObservedGeneration: eviction.GetGeneration(),
 					Reason:             condition.ClusterResourcePlacementEvictionInvalidReason,
@@ -61,7 +61,7 @@ func StatusUpdatedActual(ctx context.Context, client client.Client, evictionName
 		if isExecutedEviction != nil {
 			if isExecutedEviction.IsExecuted {
 				executedCondition := metav1.Condition{
-					Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+					Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 					Status:             metav1.ConditionTrue,
 					ObservedGeneration: eviction.GetGeneration(),
 					Reason:             condition.ClusterResourcePlacementEvictionExecutedReason,
@@ -70,7 +70,7 @@ func StatusUpdatedActual(ctx context.Context, client client.Client, evictionName
 				conditions = append(conditions, executedCondition)
 			} else {
 				notExecutedCondition := metav1.Condition{
-					Type:               string(placementv1alpha1.PlacementEvictionConditionTypeExecuted),
+					Type:               string(placementv1beta1.PlacementEvictionConditionTypeExecuted),
 					Status:             metav1.ConditionFalse,
 					ObservedGeneration: eviction.GetGeneration(),
 					Reason:             condition.ClusterResourcePlacementEvictionNotExecutedReason,
@@ -79,7 +79,7 @@ func StatusUpdatedActual(ctx context.Context, client client.Client, evictionName
 				conditions = append(conditions, notExecutedCondition)
 			}
 		}
-		wantStatus := placementv1alpha1.PlacementEvictionStatus{
+		wantStatus := placementv1beta1.PlacementEvictionStatus{
 			Conditions: conditions,
 		}
 		if diff := cmp.Diff(eviction.Status, wantStatus, evictionStatusCmpOptions...); diff != "" {