Repository: deislabs/ratify
Pull request #1079 (2023-09-12 15:34:03)

feat: add type to policy CRD
# Description

## What this PR does / why we need it:
1. Create a new v1beta1 version and add spec.type to the v1beta1 schema.
2. Restrict the `metadata.name` to be `ratify-policy` only.
3. Restrict the `spec.type` to be `rego-policy` or `config-policy` only.
4. Update documentation.

## Which issue(s) this PR fixes *(optional, using `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when the PR gets merged)*:
Fixes https://github.com/deislabs/ratify/issues/1067

## Type of change

Please delete options that are not relevant.

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [x] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Helm Chart Change (any edit/addition/update that is necessary for changes merged to the `main` branch)
- [ ] This change requires a documentation update

# How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Please also list any relevant details for your test configuration

- [ ] Test A
- [ ] Test B

# Checklist:

- [ ] Does the affected code have corresponding tests?
- [ ] Are the changes documented, not just with inline documentation, but also with conceptual documentation such as an overview of a new feature, or task-based documentation like a tutorial? Consider if this change should be announced on your project blog.
- [ ] Does this introduce breaking changes that would require an announcement or bumping the major version?
- [ ] Do all new files have appropriate license header?

# Post Merge Requirements
- [ ] MAINTAINERS: manually trigger the "Publish Package" workflow after merging any PR that indicates `Helm Chart Change`


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/deislabs/ratify/pull/1079?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) Report
Patch coverage: **`55.55%`** and project coverage change: **`+1.36%`** :tada:
> Comparison is base [(`1cae9e1`)](https://app.codecov.io/gh/deislabs/ratify/commit/1cae9e1ec69a16f9445c464f1eec63038f9c7fb8?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) 58.36% compared to head [(`8b2a90d`)](https://app.codecov.io/gh/deislabs/ratify/pull/1079?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) 59.73%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1079      +/-   ##
==========================================
+ Coverage   58.36%   59.73%   +1.36%     
==========================================
  Files          93       75      -18     
  Lines        5546     4500    -1046     
==========================================
- Hits         3237     2688     -549     
+ Misses       1994     1566     -428     
+ Partials      315      246      -69     
```


| [Files Changed](https://app.codecov.io/gh/deislabs/ratify/pull/1079?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) | Coverage Δ | |
|---|---|---|
| [pkg/controllers/policy\_controller.go](https://app.codecov.io/gh/deislabs/ratify/pull/1079?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs#diff-cGtnL2NvbnRyb2xsZXJzL3BvbGljeV9jb250cm9sbGVyLmdv) | `59.09% <55.55%> (+9.09%)` | :arrow_up: |

... and [38 files with indirect coverage changes](https://app.codecov.io/gh/deislabs/ratify/pull/1079/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/deislabs/ratify/pull/1079?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs).

susanshi: i would usually think with PRs introducing new properties, there should be a version update. However customer probably doesn't have any custom policy. Any Policy CR from RC8, would be automatically updated with a CR with the type property. Is that correct? Also interested to hear feedback from @akashsinghal 
binbin-li: > i would usually think with PRs introducing new properties, there should be a version update. However customer probably doesn't have any custom policy. Any Policy CR from RC8, would be automatically updated with a CR with the type property. Is that correct? Also interested to hear feedback from @akashsinghal

good call. I'm open to any advice on version update, I'll wait for more feedback on it as it's not an urgent PR.
akashsinghal: > > i would usually think with PRs introducing new properties, there should be a version update. However customer probably doesn't have any custom policy. Any Policy CR from RC8, would be automatically updated with a CR with the type property. Is that correct? Also interested to hear feedback from @akashsinghal
> 
> good call. I'm open to any advice on version update, I'll wait for more feedback on it as it's not an urgent PR.

I think the change is on not purely additiveand is a breaking change if I'm understanding correctly? Ther user will need to update the CR to provide the type of policy in the `type` field of the spec and also add `ratify-policy` in the metadata.name field. In that case this would be a breaking change to the v1alpha1 version introduced in rc7. 

I think the most thorough way would be to bump the CR version and add the conversion logic between the versions. Now, if we think that disruption will be minimal due to the fact that the original CR was originally introduced in an RC version, then might be ok to not bump.
binbin-li: > LGTM. One question: what is the upgrade process like again for the new CRD version? If a user has v1alpha1 already installed, will the next pre-install hook in the new helm chart automatically upgrade. Or does user need to manually delete the old CRD def for policy before the new install?

CRD would be auto-installed, but users need to apply new CRs in v1beta1 to make it work.

Patch:
diff --git a/Makefile b/Makefile
index 5e9b944..ac8b702 100644
--- a/Makefile
+++ b/Makefile
@@ -118,7 +118,7 @@ delete-demo-constraints:
 
 .PHONY: deploy-rego-policy
 deploy-rego-policy:
-	kubectl apply -f ./config/samples/policy/config_v1alpha1_policy_rego.yaml
+	kubectl apply -f ./config/samples/policy/config_v1beta1_policy_rego.yaml
 
 .PHONY: deploy-gatekeeper
 deploy-gatekeeper:
diff --git a/api/unversioned/policy_types.go b/api/unversioned/policy_types.go
index 528f9e0..345d4af 100644
--- a/api/unversioned/policy_types.go
+++ b/api/unversioned/policy_types.go
@@ -25,6 +25,8 @@ import (
 type PolicySpec struct {
 	// Important: Run "make" to regenerate code after modifying this file
 
+	// Type of the policy
+	Type string `json:"type,omitempty"`
 	// Parameters for this policy
 	Parameters runtime.RawExtension `json:"parameters,omitempty"`
 }
@@ -33,6 +35,15 @@ type PolicySpec struct {
 type PolicyStatus struct {
 	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
 	// Important: Run "make" to regenerate code after modifying this file
+
+	// Is successful while applying the policy.
+	IsSuccess bool `json:"issuccess"`
+	// Error message if policy is not successfully applied.
+	// +optional
+	Error string `json:"error,omitempty"`
+	// Truncated error message if the message is too long
+	// +optional
+	BriefError string `json:"brieferror,omitempty"`
 }
 
 // Policy is the Schema for the policies API
diff --git a/api/v1alpha1/policy_conversion.go b/api/v1alpha1/policy_conversion.go
new file mode 100644
index 0000000..b67653e
--- /dev/null
+++ b/api/v1alpha1/policy_conversion.go
@@ -0,0 +1,66 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+	unversioned "github.com/deislabs/ratify/api/unversioned"
+	"github.com/deislabs/ratify/internal/constants"
+	conversion "k8s.io/apimachinery/pkg/conversion"
+)
+
+// Convert unversioned PolicySpec to PolicySpec of v1alpha1.
+//
+//nolint:revive // ignore linter for autogenerated code
+func Convert_unversioned_PolicySpec_To_v1alpha1_PolicySpec(in *unversioned.PolicySpec, out *PolicySpec, _ conversion.Scope) error {
+	out.Parameters = in.Parameters
+	return nil
+}
+
+// Convert unversioned PolicyStatus to PolicyStatus of v1alpha1.
+//
+//nolint:revive // ignore linter for autogenerated code
+func Convert_unversioned_PolicyStatus_To_v1alpha1_PolicyStatus(in *unversioned.PolicyStatus, out *PolicyStatus, _ conversion.Scope) error {
+	return nil
+}
+
+// Convert unversioned Policy to Policy of v1alpha1.
+//
+//nolint:revive // ignore linter for autogenerated code
+func Convert_unversioned_Policy_To_v1alpha1_Policy(in *unversioned.Policy, out *Policy, s conversion.Scope) error {
+	out.ObjectMeta = in.ObjectMeta
+	// metadata.name in v1alpha1 is same as spec.type in unversioned.
+	out.ObjectMeta.Name = in.Spec.Type
+	if err := Convert_unversioned_PolicySpec_To_v1alpha1_PolicySpec(&in.Spec, &out.Spec, s); err != nil {
+		return err
+	}
+	return Convert_unversioned_PolicyStatus_To_v1alpha1_PolicyStatus(&in.Status, &out.Status, s)
+}
+
+// Convert Policy of v1alpha1 to unversioned Policy.
+//
+//nolint:revive // ignore linter for autogenerated code
+func Convert_v1alpha1_Policy_To_unversioned_Policy(in *Policy, out *unversioned.Policy, s conversion.Scope) error {
+	out.ObjectMeta = in.ObjectMeta
+	// metadata.name MUST be `ratify-policy` in unversioned.
+	out.ObjectMeta.Name = constants.RatifyPolicy
+	if err := Convert_v1alpha1_PolicySpec_To_unversioned_PolicySpec(&in.Spec, &out.Spec, s); err != nil {
+		return err
+	}
+	// spec.type in unversioned is same as metadata.name in v1alpha1.
+	out.Spec.Type = in.ObjectMeta.Name
+	return Convert_v1alpha1_PolicyStatus_To_unversioned_PolicyStatus(&in.Status, &out.Status, s)
+}
diff --git a/api/v1alpha1/policy_types.go b/api/v1alpha1/policy_types.go
index 63e4f53..a1abeee 100644
--- a/api/v1alpha1/policy_types.go
+++ b/api/v1alpha1/policy_types.go
@@ -41,6 +41,7 @@ type PolicyStatus struct {
 
 // +kubebuilder:object:root=true
 // +kubebuilder:resource:scope="Cluster"
+// +kubebuilder:deprecatedversion:warning="v1alpha1 of the Policy API has been deprecated. Please migrate to v1beta1."
 // Policy is the Schema for the policies API
 type Policy struct {
 	metav1.TypeMeta   `json:",inline"`
@@ -51,7 +52,6 @@ type Policy struct {
 }
 
 // +kubebuilder:object:root=true
-// +kubebuilder:storageversion
 // PolicyList contains a list of Policy
 type PolicyList struct {
 	metav1.TypeMeta `json:",inline"`
diff --git a/api/v1alpha1/zz_generated.conversion.go b/api/v1alpha1/zz_generated.conversion.go
index 18e3e83..2e6fd04 100644
--- a/api/v1alpha1/zz_generated.conversion.go
+++ b/api/v1alpha1/zz_generated.conversion.go
@@ -85,11 +85,6 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
-	if err := s.AddGeneratedConversionFunc((*unversioned.Policy)(nil), (*Policy)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_unversioned_Policy_To_v1alpha1_Policy(a.(*unversioned.Policy), b.(*Policy), scope)
-	}); err != nil {
-		return err
-	}
 	if err := s.AddGeneratedConversionFunc((*PolicyList)(nil), (*unversioned.PolicyList)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1alpha1_PolicyList_To_unversioned_PolicyList(a.(*PolicyList), b.(*unversioned.PolicyList), scope)
 	}); err != nil {
@@ -105,21 +100,11 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
-	if err := s.AddGeneratedConversionFunc((*unversioned.PolicySpec)(nil), (*PolicySpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_unversioned_PolicySpec_To_v1alpha1_PolicySpec(a.(*unversioned.PolicySpec), b.(*PolicySpec), scope)
-	}); err != nil {
-		return err
-	}
 	if err := s.AddGeneratedConversionFunc((*PolicyStatus)(nil), (*unversioned.PolicyStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1alpha1_PolicyStatus_To_unversioned_PolicyStatus(a.(*PolicyStatus), b.(*unversioned.PolicyStatus), scope)
 	}); err != nil {
 		return err
 	}
-	if err := s.AddGeneratedConversionFunc((*unversioned.PolicyStatus)(nil), (*PolicyStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
-		return Convert_unversioned_PolicyStatus_To_v1alpha1_PolicyStatus(a.(*unversioned.PolicyStatus), b.(*PolicyStatus), scope)
-	}); err != nil {
-		return err
-	}
 	if err := s.AddGeneratedConversionFunc((*Store)(nil), (*unversioned.Store)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1alpha1_Store_To_unversioned_Store(a.(*Store), b.(*unversioned.Store), scope)
 	}); err != nil {
@@ -205,6 +190,21 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddConversionFunc((*unversioned.PolicySpec)(nil), (*PolicySpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_PolicySpec_To_v1alpha1_PolicySpec(a.(*unversioned.PolicySpec), b.(*PolicySpec), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddConversionFunc((*unversioned.PolicyStatus)(nil), (*PolicyStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_PolicyStatus_To_v1alpha1_PolicyStatus(a.(*unversioned.PolicyStatus), b.(*PolicyStatus), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddConversionFunc((*unversioned.Policy)(nil), (*Policy)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_Policy_To_v1alpha1_Policy(a.(*unversioned.Policy), b.(*Policy), scope)
+	}); err != nil {
+		return err
+	}
 	return nil
 }
 
@@ -355,11 +355,6 @@ func autoConvert_v1alpha1_Policy_To_unversioned_Policy(in *Policy, out *unversio
 	return nil
 }
 
-// Convert_v1alpha1_Policy_To_unversioned_Policy is an autogenerated conversion function.
-func Convert_v1alpha1_Policy_To_unversioned_Policy(in *Policy, out *unversioned.Policy, s conversion.Scope) error {
-	return autoConvert_v1alpha1_Policy_To_unversioned_Policy(in, out, s)
-}
-
 func autoConvert_unversioned_Policy_To_v1alpha1_Policy(in *unversioned.Policy, out *Policy, s conversion.Scope) error {
 	out.ObjectMeta = in.ObjectMeta
 	if err := Convert_unversioned_PolicySpec_To_v1alpha1_PolicySpec(&in.Spec, &out.Spec, s); err != nil {
@@ -371,14 +366,19 @@ func autoConvert_unversioned_Policy_To_v1alpha1_Policy(in *unversioned.Policy, o
 	return nil
 }
 
-// Convert_unversioned_Policy_To_v1alpha1_Policy is an autogenerated conversion function.
-func Convert_unversioned_Policy_To_v1alpha1_Policy(in *unversioned.Policy, out *Policy, s conversion.Scope) error {
-	return autoConvert_unversioned_Policy_To_v1alpha1_Policy(in, out, s)
-}
-
 func autoConvert_v1alpha1_PolicyList_To_unversioned_PolicyList(in *PolicyList, out *unversioned.PolicyList, s conversion.Scope) error {
 	out.ListMeta = in.ListMeta
-	out.Items = *(*[]unversioned.Policy)(unsafe.Pointer(&in.Items))
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]unversioned.Policy, len(*in))
+		for i := range *in {
+			if err := Convert_v1alpha1_Policy_To_unversioned_Policy(&(*in)[i], &(*out)[i], s); err != nil {
+				return err
+			}
+		}
+	} else {
+		out.Items = nil
+	}
 	return nil
 }
 
@@ -389,7 +389,17 @@ func Convert_v1alpha1_PolicyList_To_unversioned_PolicyList(in *PolicyList, out *
 
 func autoConvert_unversioned_PolicyList_To_v1alpha1_PolicyList(in *unversioned.PolicyList, out *PolicyList, s conversion.Scope) error {
 	out.ListMeta = in.ListMeta
-	out.Items = *(*[]Policy)(unsafe.Pointer(&in.Items))
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]Policy, len(*in))
+		for i := range *in {
+			if err := Convert_unversioned_Policy_To_v1alpha1_Policy(&(*in)[i], &(*out)[i], s); err != nil {
+				return err
+			}
+		}
+	} else {
+		out.Items = nil
+	}
 	return nil
 }
 
@@ -409,15 +419,11 @@ func Convert_v1alpha1_PolicySpec_To_unversioned_PolicySpec(in *PolicySpec, out *
 }
 
 func autoConvert_unversioned_PolicySpec_To_v1alpha1_PolicySpec(in *unversioned.PolicySpec, out *PolicySpec, s conversion.Scope) error {
+	// WARNING: in.Type requires manual conversion: does not exist in peer-type
 	out.Parameters = in.Parameters
 	return nil
 }
 
-// Convert_unversioned_PolicySpec_To_v1alpha1_PolicySpec is an autogenerated conversion function.
-func Convert_unversioned_PolicySpec_To_v1alpha1_PolicySpec(in *unversioned.PolicySpec, out *PolicySpec, s conversion.Scope) error {
-	return autoConvert_unversioned_PolicySpec_To_v1alpha1_PolicySpec(in, out, s)
-}
-
 func autoConvert_v1alpha1_PolicyStatus_To_unversioned_PolicyStatus(in *PolicyStatus, out *unversioned.PolicyStatus, s conversion.Scope) error {
 	return nil
 }
@@ -428,14 +434,12 @@ func Convert_v1alpha1_PolicyStatus_To_unversioned_PolicyStatus(in *PolicyStatus,
 }
 
 func autoConvert_unversioned_PolicyStatus_To_v1alpha1_PolicyStatus(in *unversioned.PolicyStatus, out *PolicyStatus, s conversion.Scope) error {
+	// WARNING: in.IsSuccess requires manual conversion: does not exist in peer-type
+	// WARNING: in.Error requires manual conversion: does not exist in peer-type
+	// WARNING: in.BriefError requires manual conversion: does not exist in peer-type
 	return nil
 }
 
-// Convert_unversioned_PolicyStatus_To_v1alpha1_PolicyStatus is an autogenerated conversion function.
-func Convert_unversioned_PolicyStatus_To_v1alpha1_PolicyStatus(in *unversioned.PolicyStatus, out *PolicyStatus, s conversion.Scope) error {
-	return autoConvert_unversioned_PolicyStatus_To_v1alpha1_PolicyStatus(in, out, s)
-}
-
 func autoConvert_v1alpha1_Store_To_unversioned_Store(in *Store, out *unversioned.Store, s conversion.Scope) error {
 	out.ObjectMeta = in.ObjectMeta
 	if err := Convert_v1alpha1_StoreSpec_To_unversioned_StoreSpec(&in.Spec, &out.Spec, s); err != nil {
diff --git a/api/v1beta1/policy_types.go b/api/v1beta1/policy_types.go
new file mode 100644
index 0000000..c64d431
--- /dev/null
+++ b/api/v1beta1/policy_types.go
@@ -0,0 +1,78 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+// EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
+// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.
+
+// PolicySpec defines the desired state of Policy
+type PolicySpec struct {
+	// Important: Run "make" to regenerate code after modifying this file
+
+	// Type of the policy
+	Type string `json:"type,omitempty"`
+	// +kubebuilder:pruning:PreserveUnknownFields
+	// Parameters for this policy
+	Parameters runtime.RawExtension `json:"parameters,omitempty"`
+}
+
+// PolicyStatus defines the observed state of Policy
+type PolicyStatus struct {
+	// Important: Run "make" to regenerate code after modifying this file
+
+	// Is successful while applying the policy.
+	IsSuccess bool `json:"issuccess"`
+	// Error message if policy is not successfully applied.
+	// +optional
+	Error string `json:"error,omitempty"`
+	// Truncated error message if the message is too long
+	// +optional
+	BriefError string `json:"brieferror,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:resource:scope="Cluster"
+// +kubebuilder:subresource:status
+// +kubebuilder:storageversion
+// +kubebuilder:printcolumn:name="IsSuccess",type=boolean,JSONPath=`.status.issuccess`
+// +kubebuilder:printcolumn:name="Error",type=string,JSONPath=`.status.brieferror`
+// Policy is the Schema for the policies API
+type Policy struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   PolicySpec   `json:"spec,omitempty"`
+	Status PolicyStatus `json:"status,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// PolicyList contains a list of Policy
+type PolicyList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []Policy `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&Policy{}, &PolicyList{})
+}
diff --git a/api/v1beta1/zz_generated.conversion.go b/api/v1beta1/zz_generated.conversion.go
index 246f180..fbaa094 100644
--- a/api/v1beta1/zz_generated.conversion.go
+++ b/api/v1beta1/zz_generated.conversion.go
@@ -86,6 +86,46 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddGeneratedConversionFunc((*Policy)(nil), (*unversioned.Policy)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_Policy_To_unversioned_Policy(a.(*Policy), b.(*unversioned.Policy), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*unversioned.Policy)(nil), (*Policy)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_Policy_To_v1beta1_Policy(a.(*unversioned.Policy), b.(*Policy), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*PolicyList)(nil), (*unversioned.PolicyList)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_PolicyList_To_unversioned_PolicyList(a.(*PolicyList), b.(*unversioned.PolicyList), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*unversioned.PolicyList)(nil), (*PolicyList)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_PolicyList_To_v1beta1_PolicyList(a.(*unversioned.PolicyList), b.(*PolicyList), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*PolicySpec)(nil), (*unversioned.PolicySpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_PolicySpec_To_unversioned_PolicySpec(a.(*PolicySpec), b.(*unversioned.PolicySpec), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*unversioned.PolicySpec)(nil), (*PolicySpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_PolicySpec_To_v1beta1_PolicySpec(a.(*unversioned.PolicySpec), b.(*PolicySpec), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*PolicyStatus)(nil), (*unversioned.PolicyStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_PolicyStatus_To_unversioned_PolicyStatus(a.(*PolicyStatus), b.(*unversioned.PolicyStatus), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*unversioned.PolicyStatus)(nil), (*PolicyStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_PolicyStatus_To_v1beta1_PolicyStatus(a.(*unversioned.PolicyStatus), b.(*PolicyStatus), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddGeneratedConversionFunc((*Store)(nil), (*unversioned.Store)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta1_Store_To_unversioned_Store(a.(*Store), b.(*unversioned.Store), scope)
 	}); err != nil {
@@ -295,6 +335,106 @@ func Convert_unversioned_PluginSource_To_v1beta1_PluginSource(in *unversioned.Pl
 	return autoConvert_unversioned_PluginSource_To_v1beta1_PluginSource(in, out, s)
 }
 
+func autoConvert_v1beta1_Policy_To_unversioned_Policy(in *Policy, out *unversioned.Policy, s conversion.Scope) error {
+	out.ObjectMeta = in.ObjectMeta
+	if err := Convert_v1beta1_PolicySpec_To_unversioned_PolicySpec(&in.Spec, &out.Spec, s); err != nil {
+		return err
+	}
+	if err := Convert_v1beta1_PolicyStatus_To_unversioned_PolicyStatus(&in.Status, &out.Status, s); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Convert_v1beta1_Policy_To_unversioned_Policy is an autogenerated conversion function.
+func Convert_v1beta1_Policy_To_unversioned_Policy(in *Policy, out *unversioned.Policy, s conversion.Scope) error {
+	return autoConvert_v1beta1_Policy_To_unversioned_Policy(in, out, s)
+}
+
+func autoConvert_unversioned_Policy_To_v1beta1_Policy(in *unversioned.Policy, out *Policy, s conversion.Scope) error {
+	out.ObjectMeta = in.ObjectMeta
+	if err := Convert_unversioned_PolicySpec_To_v1beta1_PolicySpec(&in.Spec, &out.Spec, s); err != nil {
+		return err
+	}
+	if err := Convert_unversioned_PolicyStatus_To_v1beta1_PolicyStatus(&in.Status, &out.Status, s); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Convert_unversioned_Policy_To_v1beta1_Policy is an autogenerated conversion function.
+func Convert_unversioned_Policy_To_v1beta1_Policy(in *unversioned.Policy, out *Policy, s conversion.Scope) error {
+	return autoConvert_unversioned_Policy_To_v1beta1_Policy(in, out, s)
+}
+
+func autoConvert_v1beta1_PolicyList_To_unversioned_PolicyList(in *PolicyList, out *unversioned.PolicyList, s conversion.Scope) error {
+	out.ListMeta = in.ListMeta
+	out.Items = *(*[]unversioned.Policy)(unsafe.Pointer(&in.Items))
+	return nil
+}
+
+// Convert_v1beta1_PolicyList_To_unversioned_PolicyList is an autogenerated conversion function.
+func Convert_v1beta1_PolicyList_To_unversioned_PolicyList(in *PolicyList, out *unversioned.PolicyList, s conversion.Scope) error {
+	return autoConvert_v1beta1_PolicyList_To_unversioned_PolicyList(in, out, s)
+}
+
+func autoConvert_unversioned_PolicyList_To_v1beta1_PolicyList(in *unversioned.PolicyList, out *PolicyList, s conversion.Scope) error {
+	out.ListMeta = in.ListMeta
+	out.Items = *(*[]Policy)(unsafe.Pointer(&in.Items))
+	return nil
+}
+
+// Convert_unversioned_PolicyList_To_v1beta1_PolicyList is an autogenerated conversion function.
+func Convert_unversioned_PolicyList_To_v1beta1_PolicyList(in *unversioned.PolicyList, out *PolicyList, s conversion.Scope) error {
+	return autoConvert_unversioned_PolicyList_To_v1beta1_PolicyList(in, out, s)
+}
+
+func autoConvert_v1beta1_PolicySpec_To_unversioned_PolicySpec(in *PolicySpec, out *unversioned.PolicySpec, s conversion.Scope) error {
+	out.Type = in.Type
+	out.Parameters = in.Parameters
+	return nil
+}
+
+// Convert_v1beta1_PolicySpec_To_unversioned_PolicySpec is an autogenerated conversion function.
+func Convert_v1beta1_PolicySpec_To_unversioned_PolicySpec(in *PolicySpec, out *unversioned.PolicySpec, s conversion.Scope) error {
+	return autoConvert_v1beta1_PolicySpec_To_unversioned_PolicySpec(in, out, s)
+}
+
+func autoConvert_unversioned_PolicySpec_To_v1beta1_PolicySpec(in *unversioned.PolicySpec, out *PolicySpec, s conversion.Scope) error {
+	out.Type = in.Type
+	out.Parameters = in.Parameters
+	return nil
+}
+
+// Convert_unversioned_PolicySpec_To_v1beta1_PolicySpec is an autogenerated conversion function.
+func Convert_unversioned_PolicySpec_To_v1beta1_PolicySpec(in *unversioned.PolicySpec, out *PolicySpec, s conversion.Scope) error {
+	return autoConvert_unversioned_PolicySpec_To_v1beta1_PolicySpec(in, out, s)
+}
+
+func autoConvert_v1beta1_PolicyStatus_To_unversioned_PolicyStatus(in *PolicyStatus, out *unversioned.PolicyStatus, s conversion.Scope) error {
+	out.IsSuccess = in.IsSuccess
+	out.Error = in.Error
+	out.BriefError = in.BriefError
+	return nil
+}
+
+// Convert_v1beta1_PolicyStatus_To_unversioned_PolicyStatus is an autogenerated conversion function.
+func Convert_v1beta1_PolicyStatus_To_unversioned_PolicyStatus(in *PolicyStatus, out *unversioned.PolicyStatus, s conversion.Scope) error {
+	return autoConvert_v1beta1_PolicyStatus_To_unversioned_PolicyStatus(in, out, s)
+}
+
+func autoConvert_unversioned_PolicyStatus_To_v1beta1_PolicyStatus(in *unversioned.PolicyStatus, out *PolicyStatus, s conversion.Scope) error {
+	out.IsSuccess = in.IsSuccess
+	out.Error = in.Error
+	out.BriefError = in.BriefError
+	return nil
+}
+
+// Convert_unversioned_PolicyStatus_To_v1beta1_PolicyStatus is an autogenerated conversion function.
+func Convert_unversioned_PolicyStatus_To_v1beta1_PolicyStatus(in *unversioned.PolicyStatus, out *PolicyStatus, s conversion.Scope) error {
+	return autoConvert_unversioned_PolicyStatus_To_v1beta1_PolicyStatus(in, out, s)
+}
+
 func autoConvert_v1beta1_Store_To_unversioned_Store(in *Store, out *unversioned.Store, s conversion.Scope) error {
 	out.ObjectMeta = in.ObjectMeta
 	if err := Convert_v1beta1_StoreSpec_To_unversioned_StoreSpec(&in.Spec, &out.Spec, s); err != nil {
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index b55ceca..cb22292 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -136,6 +136,96 @@ func (in *PluginSource) DeepCopy() *PluginSource {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Policy) DeepCopyInto(out *Policy) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	out.Status = in.Status
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Policy.
+func (in *Policy) DeepCopy() *Policy {
+	if in == nil {
+		return nil
+	}
+	out := new(Policy)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Policy) 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 *PolicyList) DeepCopyInto(out *PolicyList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]Policy, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyList.
+func (in *PolicyList) DeepCopy() *PolicyList {
+	if in == nil {
+		return nil
+	}
+	out := new(PolicyList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *PolicyList) 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 *PolicySpec) DeepCopyInto(out *PolicySpec) {
+	*out = *in
+	in.Parameters.DeepCopyInto(&out.Parameters)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicySpec.
+func (in *PolicySpec) DeepCopy() *PolicySpec {
+	if in == nil {
+		return nil
+	}
+	out := new(PolicySpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PolicyStatus) DeepCopyInto(out *PolicyStatus) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyStatus.
+func (in *PolicyStatus) DeepCopy() *PolicyStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(PolicyStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Store) DeepCopyInto(out *Store) {
 	*out = *in
diff --git a/charts/ratify/crds/policy-customresourcedefinition.yaml b/charts/ratify/crds/policy-customresourcedefinition.yaml
index 75ed67e..40f7139 100644
--- a/charts/ratify/crds/policy-customresourcedefinition.yaml
+++ b/charts/ratify/crds/policy-customresourcedefinition.yaml
@@ -1,3 +1,4 @@
+---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
@@ -14,7 +15,11 @@ spec:
     singular: policy
   scope: Cluster
   versions:
-    - name: v1alpha1
+    - deprecated: true
+      deprecationWarning:
+        v1alpha1 of the Policy API has been deprecated. Please migrate
+        to v1beta1.
+      name: v1alpha1
       schema:
         openAPIV3Schema:
           description: Policy is the Schema for the policies API
@@ -46,4 +51,61 @@ spec:
               type: object
           type: object
       served: true
+      storage: false
+    - additionalPrinterColumns:
+        - jsonPath: .status.issuccess
+          name: IsSuccess
+          type: boolean
+        - jsonPath: .status.brieferror
+          name: Error
+          type: string
+      name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description: Policy is the Schema for the policies API
+          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: PolicySpec defines the desired state of Policy
+              properties:
+                parameters:
+                  description: Parameters for this policy
+                  type: object
+                  x-kubernetes-preserve-unknown-fields: true
+                type:
+                  description: Type of the policy
+                  type: string
+              type: object
+            status:
+              description: PolicyStatus defines the observed state of Policy
+              properties:
+                brieferror:
+                  description: Truncated error message if the message is too long
+                  type: string
+                error:
+                  description: Error message if policy is not successfully applied.
+                  type: string
+                issuccess:
+                  description: Is successful while applying the policy.
+                  type: boolean
+              required:
+                - issuccess
+              type: object
+          type: object
+      served: true
       storage: true
+      subresources:
+        status: {}
diff --git a/config/crd/bases/config.ratify.deislabs.io_policies.yaml b/config/crd/bases/config.ratify.deislabs.io_policies.yaml
index d01200d..63f9997 100644
--- a/config/crd/bases/config.ratify.deislabs.io_policies.yaml
+++ b/config/crd/bases/config.ratify.deislabs.io_policies.yaml
@@ -15,7 +15,10 @@ spec:
     singular: policy
   scope: Cluster
   versions:
-  - name: v1alpha1
+  - deprecated: true
+    deprecationWarning: v1alpha1 of the Policy API has been deprecated. Please migrate
+      to v1beta1.
+    name: v1alpha1
     schema:
       openAPIV3Schema:
         description: Policy is the Schema for the policies API
@@ -45,4 +48,59 @@ spec:
             type: object
         type: object
     served: true
+    storage: false
+  - additionalPrinterColumns:
+    - jsonPath: .status.issuccess
+      name: IsSuccess
+      type: boolean
+    - jsonPath: .status.brieferror
+      name: Error
+      type: string
+    name: v1beta1
+    schema:
+      openAPIV3Schema:
+        description: Policy is the Schema for the policies API
+        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: PolicySpec defines the desired state of Policy
+            properties:
+              parameters:
+                description: Parameters for this policy
+                type: object
+                x-kubernetes-preserve-unknown-fields: true
+              type:
+                description: Type of the policy
+                type: string
+            type: object
+          status:
+            description: PolicyStatus defines the observed state of Policy
+            properties:
+              brieferror:
+                description: Truncated error message if the message is too long
+                type: string
+              error:
+                description: Error message if policy is not successfully applied.
+                type: string
+              issuccess:
+                description: Is successful while applying the policy.
+                type: boolean
+            required:
+            - issuccess
+            type: object
+        type: object
+    served: true
     storage: true
+    subresources:
+      status: {}
diff --git a/config/samples/policy/config_v1beta1_policy_json.yaml b/config/samples/policy/config_v1beta1_policy_json.yaml
new file mode 100644
index 0000000..1fbf1ad
--- /dev/null
+++ b/config/samples/policy/config_v1beta1_policy_json.yaml
@@ -0,0 +1,9 @@
+apiVersion: config.ratify.deislabs.io/v1beta1
+kind: Policy
+metadata:
+  name: "ratify-policy"
+spec:
+  type: "config-policy"
+  parameters:
+    artifactVerificationPolicies:
+      default: "all"
diff --git a/config/samples/policy/config_v1beta1_policy_rego.yaml b/config/samples/policy/config_v1beta1_policy_rego.yaml
new file mode 100644
index 0000000..89ac6a0
--- /dev/null
+++ b/config/samples/policy/config_v1beta1_policy_rego.yaml
@@ -0,0 +1,31 @@
+apiVersion: config.ratify.deislabs.io/v1beta1
+kind: Policy
+metadata:
+  name: "ratify-policy"
+spec:
+  type: "rego-policy"
+  parameters:
+    passthroughEnabled: false
+    policy: |
+      package ratify.policy
+
+      default valid := false
+
+      # all artifacts MUST be valid
+      valid {
+        not failed_verify(input)
+      }
+
+      # all reports MUST pass the verification
+      failed_verify(reports) {
+        [path, value] := walk(reports)
+        value == false
+        path[count(path) - 1] == "isSuccess"
+      }
+
+      # each artifact MUST have at least one report
+      failed_verify(reports) {
+        [path, value] := walk(reports)
+        path[count(path) - 1] == "verifierReports"
+        count(value) == 0
+      }
diff --git a/docs/reference/crds/policies.md b/docs/reference/crds/policies.md
index 40e42ae..f9fcb63 100644
--- a/docs/reference/crds/policies.md
+++ b/docs/reference/crds/policies.md
@@ -5,13 +5,14 @@ View more CRD samples [here](../../../config/samples/policy). The `metadata.name
 apiVersion: config.ratify.deislabs.io/v1alpha1
 kind: Policy
 metadata:
-  name: "configpolicy"
+  name: "ratify-policy"
 spec:
+  type: required. Type of the policy.
   parameters: required. Parameters specific to this policy
 ```
-There would be exactly one CR existing in the cluster. That means if users apply a new CR, the old one would be replaced.
+Ratify would only accept a policy setting `metadata.name` as `ratify-policy` since there is at most one active policy for Ratify to use. That means if users apply a new CR with different `metadata.name`, Ratify will not update the existing policy.
 
-Note: `metadata.name` MUST be `configpolicy` or `regopolicy` per the usage.
+Note: `spec.name` MUST be `configpolicy` or `regopolicy` per the usage.
 
 ## configpolicy
 Sample spec:
@@ -19,8 +20,9 @@ Sample spec:
 apiVersion: config.ratify.deislabs.io/v1alpha1
 kind: Policy
 metadata:
-  name: "configpolicy"
+  name: "ratify-policy"
 spec:
+  type: "configpolicy"
   parameters:
     artifactVerificationPolicies:
       "application/vnd.cncf.notary.signature": "any"
@@ -38,8 +40,9 @@ Sample spec:
 apiVersion: config.ratify.deislabs.io/v1alpha1
 kind: Policy
 metadata:
-  name: "regopolicy"
+  name: "ratify-policy"
 spec:
+  type: "regopolicy"
   parameters:
     passthroughEnabled: false
     policy: |
diff --git a/docs/reference/providers.md b/docs/reference/providers.md
index b3ca3bb..72aa88f 100644
--- a/docs/reference/providers.md
+++ b/docs/reference/providers.md
@@ -42,7 +42,7 @@ Ratify embeds OPA engine inside the executor to provide a built-in policy provid
 1. Set the helm chart value of `policy.useRego` to `true` while deploying Ratify.
 2. Apply a Policy Custom Resource with Rego Policy if the service is up. e.g.
 ```bash
-kubectl apply -f ./config/samples/policy/config_v1alpha1_policy_rego.yaml
+kubectl apply -f ./config/samples/policy/config_v1beta1_policy_rego.yaml
 ```
 
 And if Ratify is used as command line tool, users MUST provide a config with Rego Policy. Check `test/bats/tests/config/config_rego_policy_notation_leaf_cert.json` as an example.
diff --git a/errors/errors.go b/errors/errors.go
index f27e9b0..cdc63a0 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -20,6 +20,7 @@ const (
 	OrasLink           = "https://oras.land/"
 	AuthProviderLink   = "https://github.com/deislabs/ratify/blob/main/docs/reference/oras-auth-provider.md"
 	PolicyProviderLink = "https://github.com/deislabs/ratify/blob/main/docs/reference/providers.md#policy-providers"
+	PolicyCRDLink      = "https://ratify.dev/docs/1.0.0-rc.6/reference/crds/policies"
 )
 
 var (
diff --git a/internal/constants/constants.go b/internal/constants/constants.go
new file mode 100644
index 0000000..104646f
--- /dev/null
+++ b/internal/constants/constants.go
@@ -0,0 +1,19 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package constants
+
+const RatifyPolicy = "ratify-policy"
diff --git a/pkg/controllers/policy_controller.go b/pkg/controllers/policy_controller.go
index 85290bb..109071c 100644
--- a/pkg/controllers/policy_controller.go
+++ b/pkg/controllers/policy_controller.go
@@ -20,7 +20,8 @@ import (
 	"encoding/json"
 	"fmt"
 
-	configv1alpha1 "github.com/deislabs/ratify/api/v1alpha1"
+	configv1beta1 "github.com/deislabs/ratify/api/v1beta1"
+	"github.com/deislabs/ratify/internal/constants"
 	"github.com/deislabs/ratify/pkg/policyprovider"
 	"github.com/deislabs/ratify/pkg/policyprovider/config"
 	pf "github.com/deislabs/ratify/pkg/policyprovider/factory"
@@ -60,7 +61,7 @@ var ActivePolicy policy
 func (r *PolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
 	policyLogger := logrus.WithContext(ctx)
 
-	var policy configv1alpha1.Policy
+	var policy configv1beta1.Policy
 	var resource = req.Name
 	policyLogger.Infof("Reconciling Policy %s", resource)
 
@@ -74,56 +75,42 @@ func (r *PolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
 		return ctrl.Result{}, client.IgnoreNotFound(err)
 	}
 
-	if err := policyAddOrReplace(policy.Spec, resource); err != nil {
-		policyLogger.Error("unable to create policy from policy crd: ", err)
-		return ctrl.Result{}, err
+	if resource != constants.RatifyPolicy {
+		errStr := fmt.Sprintf("metadata.name must be ratify-policy, got %s", resource)
+		policyLogger.Error(errStr)
+		writePolicyStatus(ctx, r, &policy, policyLogger, false, errStr)
+		return ctrl.Result{}, nil
 	}
 
-	// List all policies in the same namespace.
-	policyList := &configv1alpha1.PolicyList{}
-	if err := r.List(ctx, policyList, client.InNamespace(req.Namespace)); err != nil {
-		policyLogger.Error("failed to list Policies: ", err)
+	if err := policyAddOrReplace(policy.Spec); err != nil {
+		policyLogger.Error("unable to create policy from policy crd: ", err)
+		writePolicyStatus(ctx, r, &policy, policyLogger, false, err.Error())
 		return ctrl.Result{}, err
 	}
 
-	// Delete all policies except the current one.
-	for _, item := range policyList.Items {
-		item := item
-		if item.Name != resource {
-			policyLogger.Infof("Deleting policy %s", item.Name)
-			err := r.Delete(ctx, &item)
-			if err != nil {
-				policyLogger.Error("failed to delete Policy: ", err)
-				return ctrl.Result{}, err
-			}
-			policyLogger.Info("Deleted policy", "name", item.Name)
-		}
-	}
-
-	// returning empty result and no error to indicate we’ve successfully reconciled this object
 	return ctrl.Result{}, nil
 }
 
 // SetupWithManager sets up the controller with the Manager.
 func (r *PolicyReconciler) SetupWithManager(mgr ctrl.Manager) error {
 	return ctrl.NewControllerManagedBy(mgr).
-		For(&configv1alpha1.Policy{}).
+		For(&configv1beta1.Policy{}).
 		Complete(r)
 }
 
-func policyAddOrReplace(spec configv1alpha1.PolicySpec, policyName string) error {
-	policyEnforcer, err := specToPolicyEnforcer(spec, policyName)
+func policyAddOrReplace(spec configv1beta1.PolicySpec) error {
+	policyEnforcer, err := specToPolicyEnforcer(spec)
 	if err != nil {
 		return fmt.Errorf("failed to create policy enforcer: %w", err)
 	}
 
-	ActivePolicy.Name = policyName
+	ActivePolicy.Name = spec.Type
 	ActivePolicy.Enforcer = policyEnforcer
 	return nil
 }
 
-func specToPolicyEnforcer(spec configv1alpha1.PolicySpec, policyName string) (policyprovider.PolicyProvider, error) {
-	policyConfig, err := rawToPolicyConfig(spec.Parameters.Raw, policyName)
+func specToPolicyEnforcer(spec configv1beta1.PolicySpec) (policyprovider.PolicyProvider, error) {
+	policyConfig, err := rawToPolicyConfig(spec.Parameters.Raw, spec.Type)
 	if err != nil {
 		return nil, fmt.Errorf("failed to parse policy config: %w", err)
 	}
@@ -164,3 +151,29 @@ func (p *policy) deletePolicy(resource string) {
 func (p *policy) IsEmpty() bool {
 	return p.Name == "" && p.Enforcer == nil
 }
+
+func writePolicyStatus(ctx context.Context, r client.StatusClient, policy *configv1beta1.Policy, logger *logrus.Entry, isSuccess bool, errString string) {
+	if isSuccess {
+		updatePolicySuccessStatus(policy)
+	} else {
+		updatePolicyErrorStatus(policy, errString)
+	}
+	if statusErr := r.Status().Update(ctx, policy); statusErr != nil {
+		logger.Error(statusErr, ", unbale to update policy error status")
+	}
+}
+
+func updatePolicySuccessStatus(policy *configv1beta1.Policy) {
+	policy.Status.IsSuccess = true
+	policy.Status.Error = ""
+}
+
+func updatePolicyErrorStatus(policy *configv1beta1.Policy, errString string) {
+	briefErr := errString
+	if len(errString) > maxBriefErrLength {
+		briefErr = fmt.Sprintf("%s...", errString[:maxBriefErrLength])
+	}
+	policy.Status.IsSuccess = false
+	policy.Status.Error = errString
+	policy.Status.BriefError = briefErr
+}
diff --git a/pkg/policyprovider/factory/factory.go b/pkg/policyprovider/factory/factory.go
index d43b18b..dbf9ed5 100644
--- a/pkg/policyprovider/factory/factory.go
+++ b/pkg/policyprovider/factory/factory.go
@@ -57,11 +57,11 @@ func CreatePolicyProviderFromConfig(policyConfig config.PoliciesConfig) (policyp
 		return nil, re.ErrorCodeConfigInvalid.WithComponentType(re.PolicyProvider).WithDetail(fmt.Sprintf("failed to find policy provider name in the policy config with key: %s", types.Name))
 	}
 
-	providerNameStr := strings.ToLower(fmt.Sprintf("%s", policyProviderName))
+	providerNameStr := strings.Replace(strings.ToLower(fmt.Sprintf("%s", policyProviderName)), "-", "", -1)
 
 	policyFactory, ok := builtInPolicyProviders[providerNameStr]
 	if !ok {
-		return nil, re.ErrorCodePolicyProviderNotFound.NewError(re.PolicyProvider, providerNameStr, re.EmptyLink, nil, "failed to find registered policy provider", re.HideStackTrace)
+		return nil, re.ErrorCodePolicyProviderNotFound.NewError(re.PolicyProvider, providerNameStr, re.PolicyCRDLink, nil, fmt.Sprintf("policy type: %s is not registered policy provider", providerNameStr), re.HideStackTrace)
 	}
 
 	policyProvider, err := policyFactory.Create(policyConfig.PolicyPlugin)


Test patch:
diff --git a/api/v1alpha1/policy_conversion_test.go b/api/v1alpha1/policy_conversion_test.go
new file mode 100644
index 0000000..862e16c
--- /dev/null
+++ b/api/v1alpha1/policy_conversion_test.go
@@ -0,0 +1,83 @@
+/*
+Copyright The Ratify Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+	"reflect"
+	"testing"
+
+	unversioned "github.com/deislabs/ratify/api/unversioned"
+	"github.com/deislabs/ratify/internal/constants"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	runtime "k8s.io/apimachinery/pkg/runtime"
+)
+
+var params = runtime.RawExtension{}
+
+const testPolicyType = "testPolicyType"
+
+func TestConvert_unversioned_PolicySpec_To_v1alpha1_PolicySpec(t *testing.T) {
+	in := &unversioned.PolicySpec{
+		Parameters: params,
+	}
+	out := &PolicySpec{}
+	if err := Convert_unversioned_PolicySpec_To_v1alpha1_PolicySpec(in, out, nil); err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if !reflect.DeepEqual(out.Parameters, in.Parameters) {
+		t.Fatalf("expect parameters to be equal, but got different values")
+	}
+}
+
+func TestConvert_unversioned_PolicyStatus_To_v1alpha1_PolicyStatus(t *testing.T) {
+	if err := Convert_unversioned_PolicyStatus_To_v1alpha1_PolicyStatus(nil, nil, nil); err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+}
+
+func TestConvert_unversioned_Policy_To_v1alpha1_Policy(t *testing.T) {
+	in := &unversioned.Policy{
+		Spec: unversioned.PolicySpec{
+			Type: testPolicyType,
+		},
+	}
+	out := &Policy{}
+	if err := Convert_unversioned_Policy_To_v1alpha1_Policy(in, out, nil); err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if out.ObjectMeta.Name != in.Spec.Type {
+		t.Fatalf("expect metadata.name to be %s, but got %s", in.Spec.Type, out.ObjectMeta.Name)
+	}
+}
+
+func TestConvert_v1alpha1_Policy_To_unversioned_Policy(t *testing.T) {
+	in := &Policy{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: testPolicyType,
+		},
+	}
+	out := &unversioned.Policy{}
+	if err := Convert_v1alpha1_Policy_To_unversioned_Policy(in, out, nil); err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if out.ObjectMeta.Name != constants.RatifyPolicy {
+		t.Fatalf("expect metadata.name to be %s, but got %s", constants.RatifyPolicy, out.ObjectMeta.Name)
+	}
+	if out.Spec.Type != in.ObjectMeta.Name {
+		t.Fatalf("expect spec.type to be %s, but got %s", in.ObjectMeta.Name, out.Spec.Type)
+	}
+}
diff --git a/pkg/controllers/policy_controller_test.go b/pkg/controllers/policy_controller_test.go
index 45b305f..d3b0417 100644
--- a/pkg/controllers/policy_controller_test.go
+++ b/pkg/controllers/policy_controller_test.go
@@ -16,13 +16,17 @@ limitations under the License.
 package controllers
 
 import (
+	"context"
+	"errors"
 	"reflect"
 	"testing"
 
-	configv1alpha1 "github.com/deislabs/ratify/api/v1alpha1"
+	configv1beta1 "github.com/deislabs/ratify/api/v1beta1"
 	"github.com/deislabs/ratify/pkg/policyprovider/config"
 	_ "github.com/deislabs/ratify/pkg/policyprovider/configpolicy"
+	"github.com/sirupsen/logrus"
 	"k8s.io/apimachinery/pkg/runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 const (
@@ -30,6 +34,35 @@ const (
 	policyName2 = "policy2"
 )
 
+type mockResourceWriter struct {
+	updateFailed bool
+}
+
+func (w mockResourceWriter) Create(_ context.Context, _ client.Object, _ client.Object, _ ...client.SubResourceCreateOption) error {
+	return nil
+}
+
+func (w mockResourceWriter) Update(_ context.Context, _ client.Object, _ ...client.SubResourceUpdateOption) error {
+	if w.updateFailed {
+		return errors.New("update failed")
+	}
+	return nil
+}
+
+func (w mockResourceWriter) Patch(_ context.Context, _ client.Object, _ client.Patch, _ ...client.SubResourcePatchOption) error {
+	return nil
+}
+
+type mockStatusClient struct {
+	updateFailed bool
+}
+
+func (c mockStatusClient) Status() client.SubResourceWriter {
+	writer := mockResourceWriter{}
+	writer.updateFailed = c.updateFailed
+	return writer
+}
+
 func TestDeletePolicy(t *testing.T) {
 	testCases := []struct {
 		name             string
@@ -140,35 +173,37 @@ func TestSpecToPolicyEnforcer(t *testing.T) {
 	testCases := []struct {
 		name           string
 		policyName     string
-		spec           configv1alpha1.PolicySpec
+		spec           configv1beta1.PolicySpec
 		expectErr      bool
 		expectProvider bool
 	}{
 		{
-			name:           "invalid spec",
-			policyName:     policyName1,
-			spec:           configv1alpha1.PolicySpec{},
+			name:       "invalid spec",
+			policyName: policyName1,
+			spec: configv1beta1.PolicySpec{
+				Type: policyName1,
+			},
 			expectErr:      true,
 			expectProvider: false,
 		},
 		{
-			name:       "non-supported policy",
-			policyName: policyName1,
-			spec: configv1alpha1.PolicySpec{
+			name: "non-supported policy",
+			spec: configv1beta1.PolicySpec{
 				Parameters: runtime.RawExtension{
 					Raw: []byte("{\"name\": \"policy1\"}"),
 				},
+				Type: policyName1,
 			},
 			expectErr:      true,
 			expectProvider: false,
 		},
 		{
-			name:       "valid spec",
-			policyName: "configpolicy",
-			spec: configv1alpha1.PolicySpec{
+			name: "valid spec",
+			spec: configv1beta1.PolicySpec{
 				Parameters: runtime.RawExtension{
 					Raw: []byte("{\"name\": \"configpolicy\"}"),
 				},
+				Type: "configpolicy",
 			},
 			expectErr:      false,
 			expectProvider: true,
@@ -177,7 +212,7 @@ func TestSpecToPolicyEnforcer(t *testing.T) {
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			provider, err := specToPolicyEnforcer(tc.spec, tc.policyName)
+			provider, err := specToPolicyEnforcer(tc.spec)
 
 			if tc.expectErr != (err != nil) {
 				t.Fatalf("Expected error to be %t, got %t", tc.expectErr, err != nil)
@@ -192,31 +227,32 @@ func TestSpecToPolicyEnforcer(t *testing.T) {
 func TestPolicyAddOrReplace(t *testing.T) {
 	testCases := []struct {
 		name       string
-		spec       configv1alpha1.PolicySpec
+		spec       configv1beta1.PolicySpec
 		policyName string
 		expectErr  bool
 	}{
 		{
-			name:       "invalid spec",
-			spec:       configv1alpha1.PolicySpec{},
-			policyName: policyName1,
-			expectErr:  true,
+			name: "invalid spec",
+			spec: configv1beta1.PolicySpec{
+				Type: policyName1,
+			},
+			expectErr: true,
 		},
 		{
 			name: "valid spec",
-			spec: configv1alpha1.PolicySpec{
+			spec: configv1beta1.PolicySpec{
 				Parameters: runtime.RawExtension{
 					Raw: []byte("{\"name\": \"configpolicy\"}"),
 				},
+				Type: "configpolicy",
 			},
-			policyName: "configpolicy",
-			expectErr:  false,
+			expectErr: false,
 		},
 	}
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			err := policyAddOrReplace(tc.spec, tc.policyName)
+			err := policyAddOrReplace(tc.spec)
 
 			if tc.expectErr != (err != nil) {
 				t.Fatalf("Expected error to be %t, got %t", tc.expectErr, err != nil)
@@ -224,3 +260,42 @@ func TestPolicyAddOrReplace(t *testing.T) {
 		})
 	}
 }
+
+func TestWritePolicyStatus(t *testing.T) {
+	logger := logrus.WithContext(context.Background())
+	testCases := []struct {
+		name       string
+		isSuccess  bool
+		policy     *configv1beta1.Policy
+		errString  string
+		reconciler client.StatusClient
+	}{
+		{
+			name:       "success status",
+			isSuccess:  true,
+			policy:     &configv1beta1.Policy{},
+			reconciler: &mockStatusClient{},
+		},
+		{
+			name:       "error status",
+			isSuccess:  false,
+			policy:     &configv1beta1.Policy{},
+			errString:  "a long error string that exceeds the max length of 30 characters",
+			reconciler: &mockStatusClient{},
+		},
+		{
+			name:      "status update failed",
+			isSuccess: true,
+			policy:    &configv1beta1.Policy{},
+			reconciler: &mockStatusClient{
+				updateFailed: true,
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			writePolicyStatus(context.Background(), tc.reconciler, tc.policy, logger, tc.isSuccess, tc.errString)
+		})
+	}
+}
diff --git a/pkg/policyprovider/factory/factory_test.go b/pkg/policyprovider/factory/factory_test.go
index a017104..157c697 100644
--- a/pkg/policyprovider/factory/factory_test.go
+++ b/pkg/policyprovider/factory/factory_test.go
@@ -32,7 +32,7 @@ func (f *TestPolicyProviderFactory) Create(_ config.PolicyPluginConfig) (policyp
 // Checks the correct registered policy provider is invoked based on config
 func TestCreatePolicyProvidersFromConfig_BuiltInPolicyProviders_ReturnsExpected(t *testing.T) {
 	builtInPolicyProviders = map[string]PolicyFactory{
-		"test-policyprovider": &TestPolicyProviderFactory{},
+		"testpolicyprovider": &TestPolicyProviderFactory{},
 	}
 
 	configPolicyConfig := map[string]interface{}{