Repository: deislabs/ratify
Pull request #1422 (2024-04-29 03:07:42)

feat: add NamespacedKMP and switch KMP scope to cluster [multi-tenancy PR 9]
# Description

## What this PR does / why we need it:
This is the 9th PR for multi-tenancy support.

1. Created new CRD: `NamespacedKeyManagementProvider` to replace the previous `KeyManagementProvider` for namespaced KMP resource.
2. Update the scope from Namespaced to Clustered for the original `KeyManagementProvider` CRD.
3. The above 2 changes are intended to ensure uniformity in the naming and scope across all CRDs.
4. For the cluster controller, it will fetch the `Name` instead of `NamespacedName`, which means we'll get `ProviderName` instead of `/ProviderName` as an example.
5. Updated unit tests.

## 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 #

## 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)
- [ ] 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/1422?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) Report
Attention: Patch coverage is `64.53488%` with `61 lines` in your changes are missing coverage. Please review.
> :exclamation: No coverage uploaded for pull request base (`dev@9f9f551`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs#section-missing-base-commit).

| [Files](https://app.codecov.io/gh/deislabs/ratify/pull/1422?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) | Patch % | Lines |
|---|---|---|
| [...espaceresource/keymanagementprovider\_controller.go](https://app.codecov.io/gh/deislabs/ratify/pull/1422?src=pr&el=tree&filepath=pkg%2Fcontrollers%2Fnamespaceresource%2Fkeymanagementprovider_controller.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs#diff-cGtnL2NvbnRyb2xsZXJzL25hbWVzcGFjZXJlc291cmNlL2tleW1hbmFnZW1lbnRwcm92aWRlcl9jb250cm9sbGVyLmdv) | 35.21% | [46 Missing :warning: ](https://app.codecov.io/gh/deislabs/ratify/pull/1422?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) |
| [.../controllers/namespaceresource/store\_controller.go](https://app.codecov.io/gh/deislabs/ratify/pull/1422?src=pr&el=tree&filepath=pkg%2Fcontrollers%2Fnamespaceresource%2Fstore_controller.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs#diff-cGtnL2NvbnRyb2xsZXJzL25hbWVzcGFjZXJlc291cmNlL3N0b3JlX2NvbnRyb2xsZXIuZ28=) | 66.66% | [11 Missing and 2 partials :warning: ](https://app.codecov.io/gh/deislabs/ratify/pull/1422?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) |
| [...lusterresource/keymanagementprovider\_controller.go](https://app.codecov.io/gh/deislabs/ratify/pull/1422?src=pr&el=tree&filepath=pkg%2Fcontrollers%2Fclusterresource%2Fkeymanagementprovider_controller.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs#diff-cGtnL2NvbnRyb2xsZXJzL2NsdXN0ZXJyZXNvdXJjZS9rZXltYW5hZ2VtZW50cHJvdmlkZXJfY29udHJvbGxlci5nbw==) | 50.00% | [2 Missing :warning: ](https://app.codecov.io/gh/deislabs/ratify/pull/1422?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deislabs) |

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


```diff
@@          Coverage Diff           @@
##             dev    #1422   +/-   ##
======================================
  Coverage       ?   66.51%           
======================================
  Files          ?      100           
  Lines          ?     5376           
  Branches       ?        0           
======================================
  Hits           ?     3576           
  Misses         ?     1485           
  Partials       ?      315           
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/deislabs/ratify/pull/1422?dropdown=coverage&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).


Patch:
diff --git a/PROJECT b/PROJECT
index 418f446..fca492f 100644
--- a/PROJECT
+++ b/PROJECT
@@ -96,4 +96,12 @@ resources:
   kind: NamespacedStore
   path: github.com/deislabs/ratify/api/v1beta1
   version: v1beta1
+- api:
+    crdVersion: v1
+    namespaced: true
+  domain: ratify.deislabs.io
+  group: config
+  kind: NamespacedKeyManagementProvider
+  path: github.com/deislabs/ratify/api/v1beta1
+  version: v1beta1
 version: "3"
diff --git a/api/unversioned/namespacedkeymanagementprovider_types.go b/api/unversioned/namespacedkeymanagementprovider_types.go
new file mode 100644
index 0000000..70dcf55
--- /dev/null
+++ b/api/unversioned/namespacedkeymanagementprovider_types.go
@@ -0,0 +1,76 @@
+/*
+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.
+*/
+
+// +kubebuilder:skip
+package unversioned
+
+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.
+
+// NamespacedKeyManagementProviderSpec defines the desired state of NamespacedKeyManagementProvider
+type NamespacedKeyManagementProviderSpec struct {
+	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
+	// Important: Run "make" to regenerate code after modifying this file
+
+	// Name of the key management provider
+	Type string `json:"type,omitempty"`
+
+	// +kubebuilder:pruning:PreserveUnknownFields
+	// Parameters of the key management provider
+	Parameters runtime.RawExtension `json:"parameters,omitempty"`
+}
+
+// NamespacedKeyManagementProviderStatus defines the observed state of NamespacedKeyManagementProvider
+type NamespacedKeyManagementProviderStatus struct {
+	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
+	// Important: Run "make" to regenerate code after modifying this file
+
+	// Is successful in loading certificate/key files
+	IsSuccess bool `json:"issuccess"`
+	// Error message if operation was unsuccessful
+	// +optional
+	Error string `json:"error,omitempty"`
+	// Truncated error message if the message is too long
+	// +optional
+	BriefError string `json:"brieferror,omitempty"`
+	// The time stamp of last successful certificate/key fetch operation. If operation failed, last fetched time shows the time of error
+	// +optional
+	LastFetchedTime *metav1.Time `json:"lastfetchedtime,omitempty"`
+	// provider specific properties of the each individual certificate/key
+	// +optional
+	Properties runtime.RawExtension `json:"properties,omitempty"`
+}
+
+// NamespacedKeyManagementProvider is the Schema for the namespacedkeymanagementproviders API
+type NamespacedKeyManagementProvider struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   NamespacedKeyManagementProviderSpec   `json:"spec,omitempty"`
+	Status NamespacedKeyManagementProviderStatus `json:"status,omitempty"`
+}
+
+// NamespacedKeyManagementProviderList contains a list of NamespacedKeyManagementProvider
+type NamespacedKeyManagementProviderList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []NamespacedKeyManagementProvider `json:"items"`
+}
diff --git a/api/unversioned/zz_generated.deepcopy.go b/api/unversioned/zz_generated.deepcopy.go
index ccf9bbe..63d22ac 100644
--- a/api/unversioned/zz_generated.deepcopy.go
+++ b/api/unversioned/zz_generated.deepcopy.go
@@ -181,6 +181,85 @@ func (in *KeyManagementProviderStatus) DeepCopy() *KeyManagementProviderStatus {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NamespacedKeyManagementProvider) DeepCopyInto(out *NamespacedKeyManagementProvider) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedKeyManagementProvider.
+func (in *NamespacedKeyManagementProvider) DeepCopy() *NamespacedKeyManagementProvider {
+	if in == nil {
+		return nil
+	}
+	out := new(NamespacedKeyManagementProvider)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NamespacedKeyManagementProviderList) DeepCopyInto(out *NamespacedKeyManagementProviderList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]NamespacedKeyManagementProvider, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedKeyManagementProviderList.
+func (in *NamespacedKeyManagementProviderList) DeepCopy() *NamespacedKeyManagementProviderList {
+	if in == nil {
+		return nil
+	}
+	out := new(NamespacedKeyManagementProviderList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NamespacedKeyManagementProviderSpec) DeepCopyInto(out *NamespacedKeyManagementProviderSpec) {
+	*out = *in
+	in.Parameters.DeepCopyInto(&out.Parameters)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedKeyManagementProviderSpec.
+func (in *NamespacedKeyManagementProviderSpec) DeepCopy() *NamespacedKeyManagementProviderSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(NamespacedKeyManagementProviderSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NamespacedKeyManagementProviderStatus) DeepCopyInto(out *NamespacedKeyManagementProviderStatus) {
+	*out = *in
+	if in.LastFetchedTime != nil {
+		in, out := &in.LastFetchedTime, &out.LastFetchedTime
+		*out = (*in).DeepCopy()
+	}
+	in.Properties.DeepCopyInto(&out.Properties)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedKeyManagementProviderStatus.
+func (in *NamespacedKeyManagementProviderStatus) DeepCopy() *NamespacedKeyManagementProviderStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(NamespacedKeyManagementProviderStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *NamespacedPolicy) DeepCopyInto(out *NamespacedPolicy) {
 	*out = *in
diff --git a/api/v1beta1/keymanagementproviders_types.go b/api/v1beta1/keymanagementproviders_types.go
index d8f10d5..d957f38 100644
--- a/api/v1beta1/keymanagementproviders_types.go
+++ b/api/v1beta1/keymanagementproviders_types.go
@@ -58,6 +58,7 @@ type KeyManagementProviderStatus struct {
 }
 
 // +kubebuilder:object:root=true
+// +kubebuilder:resource:scope="Cluster"
 // +kubebuilder:subresource:status
 // +kubebuilder:storageversion
 // +kubebuilder:printcolumn:name="IsSuccess",type=boolean,JSONPath=`.status.issuccess`
diff --git a/api/v1beta1/namespacedkeymanagementprovider_types.go b/api/v1beta1/namespacedkeymanagementprovider_types.go
new file mode 100644
index 0000000..f4d3e58
--- /dev/null
+++ b/api/v1beta1/namespacedkeymanagementprovider_types.go
@@ -0,0 +1,89 @@
+/*
+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.
+
+// NamespacedKeyManagementProviderSpec defines the desired state of NamespacedKeyManagementProvider
+type NamespacedKeyManagementProviderSpec struct {
+	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
+	// Important: Run "make" to regenerate code after modifying this file
+
+	// Name of the key management provider
+	Type string `json:"type,omitempty"`
+
+	// +kubebuilder:pruning:PreserveUnknownFields
+	// Parameters of the key management provider
+	Parameters runtime.RawExtension `json:"parameters,omitempty"`
+}
+
+// NamespacedKeyManagementProviderStatus defines the observed state of NamespacedKeyManagementProvider
+type NamespacedKeyManagementProviderStatus struct {
+	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
+	// Important: Run "make" to regenerate code after modifying this file
+
+	// Is successful in loading certificate/key files
+	IsSuccess bool `json:"issuccess"`
+	// Error message if operation was unsuccessful
+	// +optional
+	Error string `json:"error,omitempty"`
+	// Truncated error message if the message is too long
+	// +optional
+	BriefError string `json:"brieferror,omitempty"`
+	// The time stamp of last successful certificate/key fetch operation. If operation failed, last fetched time shows the time of error
+	// +optional
+	LastFetchedTime *metav1.Time `json:"lastfetchedtime,omitempty"`
+	// +kubebuilder:pruning:PreserveUnknownFields
+	// provider specific properties of the each individual certificate/key
+	// +optional
+	Properties runtime.RawExtension `json:"properties,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:resource:scope="Namespaced"
+// +kubebuilder:subresource:status
+// +kubebuilder:storageversion
+// +kubebuilder:printcolumn:name="IsSuccess",type=boolean,JSONPath=`.status.issuccess`
+// +kubebuilder:printcolumn:name="Error",type=string,JSONPath=`.status.brieferror`
+// +kubebuilder:printcolumn:name="LastFetchedTime",type=date,JSONPath=`.status.lastfetchedtime`
+// NamespacedKeyManagementProvider is the Schema for the namespacedkeymanagementproviders API
+type NamespacedKeyManagementProvider struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   NamespacedKeyManagementProviderSpec   `json:"spec,omitempty"`
+	Status NamespacedKeyManagementProviderStatus `json:"status,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// NamespacedKeyManagementProviderList contains a list of NamespacedKeyManagementProvider
+type NamespacedKeyManagementProviderList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []NamespacedKeyManagementProvider `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&NamespacedKeyManagementProvider{}, &NamespacedKeyManagementProviderList{})
+}
diff --git a/api/v1beta1/zz_generated.conversion.go b/api/v1beta1/zz_generated.conversion.go
index b5d5b9c..9a16b46 100644
--- a/api/v1beta1/zz_generated.conversion.go
+++ b/api/v1beta1/zz_generated.conversion.go
@@ -116,6 +116,46 @@ func RegisterConversions(s *runtime.Scheme) error {
 	}); err != nil {
 		return err
 	}
+	if err := s.AddGeneratedConversionFunc((*NamespacedKeyManagementProvider)(nil), (*unversioned.NamespacedKeyManagementProvider)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_NamespacedKeyManagementProvider_To_unversioned_NamespacedKeyManagementProvider(a.(*NamespacedKeyManagementProvider), b.(*unversioned.NamespacedKeyManagementProvider), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*unversioned.NamespacedKeyManagementProvider)(nil), (*NamespacedKeyManagementProvider)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_NamespacedKeyManagementProvider_To_v1beta1_NamespacedKeyManagementProvider(a.(*unversioned.NamespacedKeyManagementProvider), b.(*NamespacedKeyManagementProvider), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*NamespacedKeyManagementProviderList)(nil), (*unversioned.NamespacedKeyManagementProviderList)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_NamespacedKeyManagementProviderList_To_unversioned_NamespacedKeyManagementProviderList(a.(*NamespacedKeyManagementProviderList), b.(*unversioned.NamespacedKeyManagementProviderList), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*unversioned.NamespacedKeyManagementProviderList)(nil), (*NamespacedKeyManagementProviderList)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_NamespacedKeyManagementProviderList_To_v1beta1_NamespacedKeyManagementProviderList(a.(*unversioned.NamespacedKeyManagementProviderList), b.(*NamespacedKeyManagementProviderList), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*NamespacedKeyManagementProviderSpec)(nil), (*unversioned.NamespacedKeyManagementProviderSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_NamespacedKeyManagementProviderSpec_To_unversioned_NamespacedKeyManagementProviderSpec(a.(*NamespacedKeyManagementProviderSpec), b.(*unversioned.NamespacedKeyManagementProviderSpec), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*unversioned.NamespacedKeyManagementProviderSpec)(nil), (*NamespacedKeyManagementProviderSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_NamespacedKeyManagementProviderSpec_To_v1beta1_NamespacedKeyManagementProviderSpec(a.(*unversioned.NamespacedKeyManagementProviderSpec), b.(*NamespacedKeyManagementProviderSpec), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*NamespacedKeyManagementProviderStatus)(nil), (*unversioned.NamespacedKeyManagementProviderStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_v1beta1_NamespacedKeyManagementProviderStatus_To_unversioned_NamespacedKeyManagementProviderStatus(a.(*NamespacedKeyManagementProviderStatus), b.(*unversioned.NamespacedKeyManagementProviderStatus), scope)
+	}); err != nil {
+		return err
+	}
+	if err := s.AddGeneratedConversionFunc((*unversioned.NamespacedKeyManagementProviderStatus)(nil), (*NamespacedKeyManagementProviderStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
+		return Convert_unversioned_NamespacedKeyManagementProviderStatus_To_v1beta1_NamespacedKeyManagementProviderStatus(a.(*unversioned.NamespacedKeyManagementProviderStatus), b.(*NamespacedKeyManagementProviderStatus), scope)
+	}); err != nil {
+		return err
+	}
 	if err := s.AddGeneratedConversionFunc((*NamespacedPolicy)(nil), (*unversioned.NamespacedPolicy)(nil), func(a, b interface{}, scope conversion.Scope) error {
 		return Convert_v1beta1_NamespacedPolicy_To_unversioned_NamespacedPolicy(a.(*NamespacedPolicy), b.(*unversioned.NamespacedPolicy), scope)
 	}); err != nil {
@@ -537,6 +577,110 @@ func Convert_unversioned_KeyManagementProviderStatus_To_v1beta1_KeyManagementPro
 	return autoConvert_unversioned_KeyManagementProviderStatus_To_v1beta1_KeyManagementProviderStatus(in, out, s)
 }
 
+func autoConvert_v1beta1_NamespacedKeyManagementProvider_To_unversioned_NamespacedKeyManagementProvider(in *NamespacedKeyManagementProvider, out *unversioned.NamespacedKeyManagementProvider, s conversion.Scope) error {
+	out.ObjectMeta = in.ObjectMeta
+	if err := Convert_v1beta1_NamespacedKeyManagementProviderSpec_To_unversioned_NamespacedKeyManagementProviderSpec(&in.Spec, &out.Spec, s); err != nil {
+		return err
+	}
+	if err := Convert_v1beta1_NamespacedKeyManagementProviderStatus_To_unversioned_NamespacedKeyManagementProviderStatus(&in.Status, &out.Status, s); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Convert_v1beta1_NamespacedKeyManagementProvider_To_unversioned_NamespacedKeyManagementProvider is an autogenerated conversion function.
+func Convert_v1beta1_NamespacedKeyManagementProvider_To_unversioned_NamespacedKeyManagementProvider(in *NamespacedKeyManagementProvider, out *unversioned.NamespacedKeyManagementProvider, s conversion.Scope) error {
+	return autoConvert_v1beta1_NamespacedKeyManagementProvider_To_unversioned_NamespacedKeyManagementProvider(in, out, s)
+}
+
+func autoConvert_unversioned_NamespacedKeyManagementProvider_To_v1beta1_NamespacedKeyManagementProvider(in *unversioned.NamespacedKeyManagementProvider, out *NamespacedKeyManagementProvider, s conversion.Scope) error {
+	out.ObjectMeta = in.ObjectMeta
+	if err := Convert_unversioned_NamespacedKeyManagementProviderSpec_To_v1beta1_NamespacedKeyManagementProviderSpec(&in.Spec, &out.Spec, s); err != nil {
+		return err
+	}
+	if err := Convert_unversioned_NamespacedKeyManagementProviderStatus_To_v1beta1_NamespacedKeyManagementProviderStatus(&in.Status, &out.Status, s); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Convert_unversioned_NamespacedKeyManagementProvider_To_v1beta1_NamespacedKeyManagementProvider is an autogenerated conversion function.
+func Convert_unversioned_NamespacedKeyManagementProvider_To_v1beta1_NamespacedKeyManagementProvider(in *unversioned.NamespacedKeyManagementProvider, out *NamespacedKeyManagementProvider, s conversion.Scope) error {
+	return autoConvert_unversioned_NamespacedKeyManagementProvider_To_v1beta1_NamespacedKeyManagementProvider(in, out, s)
+}
+
+func autoConvert_v1beta1_NamespacedKeyManagementProviderList_To_unversioned_NamespacedKeyManagementProviderList(in *NamespacedKeyManagementProviderList, out *unversioned.NamespacedKeyManagementProviderList, s conversion.Scope) error {
+	out.ListMeta = in.ListMeta
+	out.Items = *(*[]unversioned.NamespacedKeyManagementProvider)(unsafe.Pointer(&in.Items))
+	return nil
+}
+
+// Convert_v1beta1_NamespacedKeyManagementProviderList_To_unversioned_NamespacedKeyManagementProviderList is an autogenerated conversion function.
+func Convert_v1beta1_NamespacedKeyManagementProviderList_To_unversioned_NamespacedKeyManagementProviderList(in *NamespacedKeyManagementProviderList, out *unversioned.NamespacedKeyManagementProviderList, s conversion.Scope) error {
+	return autoConvert_v1beta1_NamespacedKeyManagementProviderList_To_unversioned_NamespacedKeyManagementProviderList(in, out, s)
+}
+
+func autoConvert_unversioned_NamespacedKeyManagementProviderList_To_v1beta1_NamespacedKeyManagementProviderList(in *unversioned.NamespacedKeyManagementProviderList, out *NamespacedKeyManagementProviderList, s conversion.Scope) error {
+	out.ListMeta = in.ListMeta
+	out.Items = *(*[]NamespacedKeyManagementProvider)(unsafe.Pointer(&in.Items))
+	return nil
+}
+
+// Convert_unversioned_NamespacedKeyManagementProviderList_To_v1beta1_NamespacedKeyManagementProviderList is an autogenerated conversion function.
+func Convert_unversioned_NamespacedKeyManagementProviderList_To_v1beta1_NamespacedKeyManagementProviderList(in *unversioned.NamespacedKeyManagementProviderList, out *NamespacedKeyManagementProviderList, s conversion.Scope) error {
+	return autoConvert_unversioned_NamespacedKeyManagementProviderList_To_v1beta1_NamespacedKeyManagementProviderList(in, out, s)
+}
+
+func autoConvert_v1beta1_NamespacedKeyManagementProviderSpec_To_unversioned_NamespacedKeyManagementProviderSpec(in *NamespacedKeyManagementProviderSpec, out *unversioned.NamespacedKeyManagementProviderSpec, s conversion.Scope) error {
+	out.Type = in.Type
+	out.Parameters = in.Parameters
+	return nil
+}
+
+// Convert_v1beta1_NamespacedKeyManagementProviderSpec_To_unversioned_NamespacedKeyManagementProviderSpec is an autogenerated conversion function.
+func Convert_v1beta1_NamespacedKeyManagementProviderSpec_To_unversioned_NamespacedKeyManagementProviderSpec(in *NamespacedKeyManagementProviderSpec, out *unversioned.NamespacedKeyManagementProviderSpec, s conversion.Scope) error {
+	return autoConvert_v1beta1_NamespacedKeyManagementProviderSpec_To_unversioned_NamespacedKeyManagementProviderSpec(in, out, s)
+}
+
+func autoConvert_unversioned_NamespacedKeyManagementProviderSpec_To_v1beta1_NamespacedKeyManagementProviderSpec(in *unversioned.NamespacedKeyManagementProviderSpec, out *NamespacedKeyManagementProviderSpec, s conversion.Scope) error {
+	out.Type = in.Type
+	out.Parameters = in.Parameters
+	return nil
+}
+
+// Convert_unversioned_NamespacedKeyManagementProviderSpec_To_v1beta1_NamespacedKeyManagementProviderSpec is an autogenerated conversion function.
+func Convert_unversioned_NamespacedKeyManagementProviderSpec_To_v1beta1_NamespacedKeyManagementProviderSpec(in *unversioned.NamespacedKeyManagementProviderSpec, out *NamespacedKeyManagementProviderSpec, s conversion.Scope) error {
+	return autoConvert_unversioned_NamespacedKeyManagementProviderSpec_To_v1beta1_NamespacedKeyManagementProviderSpec(in, out, s)
+}
+
+func autoConvert_v1beta1_NamespacedKeyManagementProviderStatus_To_unversioned_NamespacedKeyManagementProviderStatus(in *NamespacedKeyManagementProviderStatus, out *unversioned.NamespacedKeyManagementProviderStatus, s conversion.Scope) error {
+	out.IsSuccess = in.IsSuccess
+	out.Error = in.Error
+	out.BriefError = in.BriefError
+	out.LastFetchedTime = (*v1.Time)(unsafe.Pointer(in.LastFetchedTime))
+	out.Properties = in.Properties
+	return nil
+}
+
+// Convert_v1beta1_NamespacedKeyManagementProviderStatus_To_unversioned_NamespacedKeyManagementProviderStatus is an autogenerated conversion function.
+func Convert_v1beta1_NamespacedKeyManagementProviderStatus_To_unversioned_NamespacedKeyManagementProviderStatus(in *NamespacedKeyManagementProviderStatus, out *unversioned.NamespacedKeyManagementProviderStatus, s conversion.Scope) error {
+	return autoConvert_v1beta1_NamespacedKeyManagementProviderStatus_To_unversioned_NamespacedKeyManagementProviderStatus(in, out, s)
+}
+
+func autoConvert_unversioned_NamespacedKeyManagementProviderStatus_To_v1beta1_NamespacedKeyManagementProviderStatus(in *unversioned.NamespacedKeyManagementProviderStatus, out *NamespacedKeyManagementProviderStatus, s conversion.Scope) error {
+	out.IsSuccess = in.IsSuccess
+	out.Error = in.Error
+	out.BriefError = in.BriefError
+	out.LastFetchedTime = (*v1.Time)(unsafe.Pointer(in.LastFetchedTime))
+	out.Properties = in.Properties
+	return nil
+}
+
+// Convert_unversioned_NamespacedKeyManagementProviderStatus_To_v1beta1_NamespacedKeyManagementProviderStatus is an autogenerated conversion function.
+func Convert_unversioned_NamespacedKeyManagementProviderStatus_To_v1beta1_NamespacedKeyManagementProviderStatus(in *unversioned.NamespacedKeyManagementProviderStatus, out *NamespacedKeyManagementProviderStatus, s conversion.Scope) error {
+	return autoConvert_unversioned_NamespacedKeyManagementProviderStatus_To_v1beta1_NamespacedKeyManagementProviderStatus(in, out, s)
+}
+
 func autoConvert_v1beta1_NamespacedPolicy_To_unversioned_NamespacedPolicy(in *NamespacedPolicy, out *unversioned.NamespacedPolicy, s conversion.Scope) error {
 	out.ObjectMeta = in.ObjectMeta
 	if err := Convert_v1beta1_NamespacedPolicySpec_To_unversioned_NamespacedPolicySpec(&in.Spec, &out.Spec, s); err != nil {
diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go
index 49342bd..05aac89 100644
--- a/api/v1beta1/zz_generated.deepcopy.go
+++ b/api/v1beta1/zz_generated.deepcopy.go
@@ -215,6 +215,101 @@ func (in *KeyManagementProviderStatus) DeepCopy() *KeyManagementProviderStatus {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NamespacedKeyManagementProvider) DeepCopyInto(out *NamespacedKeyManagementProvider) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedKeyManagementProvider.
+func (in *NamespacedKeyManagementProvider) DeepCopy() *NamespacedKeyManagementProvider {
+	if in == nil {
+		return nil
+	}
+	out := new(NamespacedKeyManagementProvider)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *NamespacedKeyManagementProvider) 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 *NamespacedKeyManagementProviderList) DeepCopyInto(out *NamespacedKeyManagementProviderList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]NamespacedKeyManagementProvider, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedKeyManagementProviderList.
+func (in *NamespacedKeyManagementProviderList) DeepCopy() *NamespacedKeyManagementProviderList {
+	if in == nil {
+		return nil
+	}
+	out := new(NamespacedKeyManagementProviderList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *NamespacedKeyManagementProviderList) 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 *NamespacedKeyManagementProviderSpec) DeepCopyInto(out *NamespacedKeyManagementProviderSpec) {
+	*out = *in
+	in.Parameters.DeepCopyInto(&out.Parameters)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedKeyManagementProviderSpec.
+func (in *NamespacedKeyManagementProviderSpec) DeepCopy() *NamespacedKeyManagementProviderSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(NamespacedKeyManagementProviderSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NamespacedKeyManagementProviderStatus) DeepCopyInto(out *NamespacedKeyManagementProviderStatus) {
+	*out = *in
+	if in.LastFetchedTime != nil {
+		in, out := &in.LastFetchedTime, &out.LastFetchedTime
+		*out = (*in).DeepCopy()
+	}
+	in.Properties.DeepCopyInto(&out.Properties)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespacedKeyManagementProviderStatus.
+func (in *NamespacedKeyManagementProviderStatus) DeepCopy() *NamespacedKeyManagementProviderStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(NamespacedKeyManagementProviderStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *NamespacedPolicy) DeepCopyInto(out *NamespacedPolicy) {
 	*out = *in
diff --git a/charts/ratify/crds/keymanagementprovider-customresourcedefinition.yaml b/charts/ratify/crds/keymanagementprovider-customresourcedefinition.yaml
index 29ddb90..d8110ba 100644
--- a/charts/ratify/crds/keymanagementprovider-customresourcedefinition.yaml
+++ b/charts/ratify/crds/keymanagementprovider-customresourcedefinition.yaml
@@ -13,7 +13,7 @@ spec:
     listKind: KeyManagementProviderList
     plural: keymanagementproviders
     singular: keymanagementprovider
-  scope: Namespaced
+  scope: Cluster
   versions:
   - additionalPrinterColumns:
     - jsonPath: .status.issuccess
diff --git a/charts/ratify/crds/namespacedkeymanagementprovider-customresourcedefinition.yaml b/charts/ratify/crds/namespacedkeymanagementprovider-customresourcedefinition.yaml
new file mode 100644
index 0000000..f7b9539
--- /dev/null
+++ b/charts/ratify/crds/namespacedkeymanagementprovider-customresourcedefinition.yaml
@@ -0,0 +1,88 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.9.2
+  creationTimestamp: null
+  name: namespacedkeymanagementproviders.config.ratify.deislabs.io
+spec:
+  group: config.ratify.deislabs.io
+  names:
+    kind: NamespacedKeyManagementProvider
+    listKind: NamespacedKeyManagementProviderList
+    plural: namespacedkeymanagementproviders
+    singular: namespacedkeymanagementprovider
+  scope: Namespaced
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .status.issuccess
+      name: IsSuccess
+      type: boolean
+    - jsonPath: .status.brieferror
+      name: Error
+      type: string
+    - jsonPath: .status.lastfetchedtime
+      name: LastFetchedTime
+      type: date
+    name: v1beta1
+    schema:
+      openAPIV3Schema:
+        description: NamespacedKeyManagementProvider is the Schema for the namespacedkeymanagementproviders
+          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: NamespacedKeyManagementProviderSpec defines the desired state
+              of NamespacedKeyManagementProvider
+            properties:
+              parameters:
+                description: Parameters of the key management provider
+                type: object
+                x-kubernetes-preserve-unknown-fields: true
+              type:
+                description: Name of the key management provider
+                type: string
+            type: object
+          status:
+            description: NamespacedKeyManagementProviderStatus defines the observed
+              state of NamespacedKeyManagementProvider
+            properties:
+              brieferror:
+                description: Truncated error message if the message is too long
+                type: string
+              error:
+                description: Error message if operation was unsuccessful
+                type: string
+              issuccess:
+                description: Is successful in loading certificate/key files
+                type: boolean
+              lastfetchedtime:
+                description: The time stamp of last successful certificate/key fetch
+                  operation. If operation failed, last fetched time shows the time
+                  of error
+                format: date-time
+                type: string
+              properties:
+                description: provider specific properties of the each individual certificate/key
+                type: object
+                x-kubernetes-preserve-unknown-fields: true
+            required:
+            - issuccess
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
diff --git a/charts/ratify/templates/akv-key-management-provider.yaml b/charts/ratify/templates/akv-key-management-provider.yaml
index 8314967..3700b6a 100644
--- a/charts/ratify/templates/akv-key-management-provider.yaml
+++ b/charts/ratify/templates/akv-key-management-provider.yaml
@@ -1,6 +1,6 @@
 {{- if or .Values.azurekeyvault.enabled .Values.akvCertConfig.enabled }}
 apiVersion: config.ratify.deislabs.io/v1beta1
-kind: KeyManagementProvider
+kind: NamespacedKeyManagementProvider
 metadata:
   name: kmprovider-akv
   annotations:
diff --git a/charts/ratify/templates/inline-key-management-provider.yaml b/charts/ratify/templates/inline-key-management-provider.yaml
index 665ea0b..407bacd 100644
--- a/charts/ratify/templates/inline-key-management-provider.yaml
+++ b/charts/ratify/templates/inline-key-management-provider.yaml
@@ -2,7 +2,7 @@
 ---
 {{- if .Values.notationCert }} 
 apiVersion: config.ratify.deislabs.io/v1beta1
-kind: KeyManagementProvider
+kind: NamespacedKeyManagementProvider
 metadata:
   name: {{$fullname}}-notation-inline-cert
   annotations:
@@ -17,7 +17,7 @@ spec:
 ---
 {{-  range $i, $cert := .Values.notationCerts }}
 apiVersion: config.ratify.deislabs.io/v1beta1
-kind: KeyManagementProvider
+kind: NamespacedKeyManagementProvider
 metadata:
   name: {{$fullname}}-notation-inline-cert-{{$i}}
   annotations:
@@ -32,7 +32,7 @@ spec:
 ---
 {{-  range $i, $key := .Values.cosignKeys }}
 apiVersion: config.ratify.deislabs.io/v1beta1
-kind: KeyManagementProvider
+kind: NamespacedKeyManagementProvider
 metadata:
   name: {{$fullname}}-cosign-inline-key-{{$i}}
   annotations:
diff --git a/charts/ratify/templates/ratify-manager-role-clusterrole.yaml b/charts/ratify/templates/ratify-manager-role-clusterrole.yaml
index 14bf034..2653f15 100644
--- a/charts/ratify/templates/ratify-manager-role-clusterrole.yaml
+++ b/charts/ratify/templates/ratify-manager-role-clusterrole.yaml
@@ -135,6 +135,32 @@ rules:
   - get
   - patch
   - update
+- apiGroups:
+  - config.ratify.deislabs.io
+  resources:
+  - namespacedkeymanagementproviders
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - config.ratify.deislabs.io
+  resources:
+  - namespacedkeymanagementproviders/finalizers
+  verbs:
+  - update
+- apiGroups:
+  - config.ratify.deislabs.io
+  resources:
+  - namespacedkeymanagementproviders/status
+  verbs:
+  - get
+  - patch
+  - update
 - apiGroups:
   - config.ratify.deislabs.io
   resources:
diff --git a/config/crd/bases/config.ratify.deislabs.io_keymanagementproviders.yaml b/config/crd/bases/config.ratify.deislabs.io_keymanagementproviders.yaml
index 29ddb90..d8110ba 100644
--- a/config/crd/bases/config.ratify.deislabs.io_keymanagementproviders.yaml
+++ b/config/crd/bases/config.ratify.deislabs.io_keymanagementproviders.yaml
@@ -13,7 +13,7 @@ spec:
     listKind: KeyManagementProviderList
     plural: keymanagementproviders
     singular: keymanagementprovider
-  scope: Namespaced
+  scope: Cluster
   versions:
   - additionalPrinterColumns:
     - jsonPath: .status.issuccess
diff --git a/config/crd/bases/config.ratify.deislabs.io_namespacedkeymanagementproviders.yaml b/config/crd/bases/config.ratify.deislabs.io_namespacedkeymanagementproviders.yaml
new file mode 100644
index 0000000..f7b9539
--- /dev/null
+++ b/config/crd/bases/config.ratify.deislabs.io_namespacedkeymanagementproviders.yaml
@@ -0,0 +1,88 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.9.2
+  creationTimestamp: null
+  name: namespacedkeymanagementproviders.config.ratify.deislabs.io
+spec:
+  group: config.ratify.deislabs.io
+  names:
+    kind: NamespacedKeyManagementProvider
+    listKind: NamespacedKeyManagementProviderList
+    plural: namespacedkeymanagementproviders
+    singular: namespacedkeymanagementprovider
+  scope: Namespaced
+  versions:
+  - additionalPrinterColumns:
+    - jsonPath: .status.issuccess
+      name: IsSuccess
+      type: boolean
+    - jsonPath: .status.brieferror
+      name: Error
+      type: string
+    - jsonPath: .status.lastfetchedtime
+      name: LastFetchedTime
+      type: date
+    name: v1beta1
+    schema:
+      openAPIV3Schema:
+        description: NamespacedKeyManagementProvider is the Schema for the namespacedkeymanagementproviders
+          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: NamespacedKeyManagementProviderSpec defines the desired state
+              of NamespacedKeyManagementProvider
+            properties:
+              parameters:
+                description: Parameters of the key management provider
+                type: object
+                x-kubernetes-preserve-unknown-fields: true
+              type:
+                description: Name of the key management provider
+                type: string
+            type: object
+          status:
+            description: NamespacedKeyManagementProviderStatus defines the observed
+              state of NamespacedKeyManagementProvider
+            properties:
+              brieferror:
+                description: Truncated error message if the message is too long
+                type: string
+              error:
+                description: Error message if operation was unsuccessful
+                type: string
+              issuccess:
+                description: Is successful in loading certificate/key files
+                type: boolean
+              lastfetchedtime:
+                description: The time stamp of last successful certificate/key fetch
+                  operation. If operation failed, last fetched time shows the time
+                  of error
+                format: date-time
+                type: string
+              properties:
+                description: provider specific properties of the each individual certificate/key
+                type: object
+                x-kubernetes-preserve-unknown-fields: true
+            required:
+            - issuccess
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml
index 4e4cf12..112b1c6 100644
--- a/config/crd/kustomization.yaml
+++ b/config/crd/kustomization.yaml
@@ -9,6 +9,7 @@ resources:
   - bases/config.ratify.deislabs.io_keymanagementproviders.yaml
   - bases/config.ratify.deislabs.io_namespacedpolicies.yaml
   - bases/config.ratify.deislabs.io_namespacedstores.yaml
+  - bases/config.ratify.deislabs.io_namespacedkeymanagementproviders.yaml
 #+kubebuilder:scaffold:crdkustomizeresource
 
 patchesStrategicMerge:
@@ -21,6 +22,7 @@ patchesStrategicMerge:
   #- patches/webhook_in_keymanagementproviders.yaml
   #- patches/webhook_in_namespacedpolicies.yaml
   #- patches/webhook_in_namespacedstores.yaml
+  #- patches/webhook_in_namespacedkeymanagementproviders.yaml
   #+kubebuilder:scaffold:crdkustomizewebhookpatch
 
   # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
@@ -32,6 +34,7 @@ patchesStrategicMerge:
   #- patches/cainjection_in_keymanagementproviders.yaml
   #- patches/cainjection_in_namespacedpolicies.yaml
   #- patches/cainjection_in_namespacedstores.yaml
+  #- patches/cainjection_in_namespacedkeymanagementproviders.yaml
 #+kubebuilder:scaffold:crdkustomizecainjectionpatch
 
 # the following config is for teaching kustomize how to do kustomization for CRDs.
diff --git a/config/crd/patches/cainjection_in_namespacedkeymanagementproviders.yaml b/config/crd/patches/cainjection_in_namespacedkeymanagementproviders.yaml
new file mode 100644
index 0000000..d998423
--- /dev/null
+++ b/config/crd/patches/cainjection_in_namespacedkeymanagementproviders.yaml
@@ -0,0 +1,7 @@
+# The following patch adds a directive for certmanager to inject CA into the CRD
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+  name: namespacedkeymanagementproviders.config.ratify.deislabs.io
diff --git a/config/crd/patches/webhook_in_namespacedkeymanagementproviders.yaml b/config/crd/patches/webhook_in_namespacedkeymanagementproviders.yaml
new file mode 100644
index 0000000..2d1f077
--- /dev/null
+++ b/config/crd/patches/webhook_in_namespacedkeymanagementproviders.yaml
@@ -0,0 +1,16 @@
+# The following patch enables a conversion webhook for the CRD
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: namespacedkeymanagementproviders.config.ratify.deislabs.io
+spec:
+  conversion:
+    strategy: Webhook
+    webhook:
+      clientConfig:
+        service:
+          namespace: system
+          name: webhook-service
+          path: /convert
+      conversionReviewVersions:
+      - v1
diff --git a/config/rbac/namespacedkeymanagementprovider_editor_role.yaml b/config/rbac/namespacedkeymanagementprovider_editor_role.yaml
new file mode 100644
index 0000000..671f375
--- /dev/null
+++ b/config/rbac/namespacedkeymanagementprovider_editor_role.yaml
@@ -0,0 +1,31 @@
+# permissions for end users to edit namespacedkeymanagementproviders.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: clusterrole
+    app.kubernetes.io/instance: namespacedkeymanagementprovider-editor-role
+    app.kubernetes.io/component: rbac
+    app.kubernetes.io/created-by: ratify
+    app.kubernetes.io/part-of: ratify
+    app.kubernetes.io/managed-by: kustomize
+  name: namespacedkeymanagementprovider-editor-role
+rules:
+- apiGroups:
+  - config.ratify.deislabs.io
+  resources:
+  - namespacedkeymanagementproviders
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - config.ratify.deislabs.io
+  resources:
+  - namespacedkeymanagementproviders/status
+  verbs:
+  - get
diff --git a/config/rbac/namespacedkeymanagementprovider_viewer_role.yaml b/config/rbac/namespacedkeymanagementprovider_viewer_role.yaml
new file mode 100644
index 0000000..f9d6a84
--- /dev/null
+++ b/config/rbac/namespacedkeymanagementprovider_viewer_role.yaml
@@ -0,0 +1,27 @@
+# permissions for end users to view namespacedkeymanagementproviders.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  labels:
+    app.kubernetes.io/name: clusterrole
+    app.kubernetes.io/instance: namespacedkeymanagementprovider-viewer-role
+    app.kubernetes.io/component: rbac
+    app.kubernetes.io/created-by: ratify
+    app.kubernetes.io/part-of: ratify
+    app.kubernetes.io/managed-by: kustomize
+  name: namespacedkeymanagementprovider-viewer-role
+rules:
+- apiGroups:
+  - config.ratify.deislabs.io
+  resources:
+  - namespacedkeymanagementproviders
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - config.ratify.deislabs.io
+  resources:
+  - namespacedkeymanagementproviders/status
+  verbs:
+  - get
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index 97cdeaa..d7521fb 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -186,4 +186,30 @@ rules:
   verbs:
   - get
   - patch
+  - update
+- apiGroups:
+  - config.ratify.deislabs.io
+  resources:
+  - namespacedkeymanagementproviders
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - config.ratify.deislabs.io
+  resources:
+  - namespacedkeymanagementproviders/finalizers
+  verbs:
+  - update
+- apiGroups:
+  - config.ratify.deislabs.io
+  resources:
+  - namespacedkeymanagementproviders/status
+  verbs:
+  - get
+  - patch
   - update
\ No newline at end of file
diff --git a/config/samples/config_v1beta1_keymanagementprovider_akv.yaml b/config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_akv.yaml
similarity index 100%
rename from config/samples/config_v1beta1_keymanagementprovider_akv.yaml
rename to config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_akv.yaml
diff --git a/config/samples/config_v1beta1_keymanagementprovider_inline.yaml b/config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_inline.yaml
similarity index 100%
rename from config/samples/config_v1beta1_keymanagementprovider_inline.yaml
rename to config/samples/clustered/kmp/config_v1beta1_keymanagementprovider_inline.yaml
diff --git a/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_akv.yaml b/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_akv.yaml
new file mode 100644
index 0000000..e8971e1
--- /dev/null
+++ b/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_akv.yaml
@@ -0,0 +1,13 @@
+apiVersion: config.ratify.deislabs.io/v1beta1
+kind: NamespacedKeyManagementProvider
+metadata:
+  name: keymanagementprovider-inline
+spec:
+  type: azurekeyvault
+  parameters:
+    vaultURI: https://yourkeyvault.vault.azure.net/
+    certificates:
+      - name: yourCertName
+        version: yourCertVersion # Optional, fetch latest version if empty 
+    tenantID:
+    clientID: 
diff --git a/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_inline.yaml b/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_inline.yaml
new file mode 100644
index 0000000..1fac2d5
--- /dev/null
+++ b/config/samples/namespaced/kmp/config_v1beta1_keymanagementprovider_inline.yaml
@@ -0,0 +1,29 @@
+apiVersion: config.ratify.deislabs.io/v1beta1
+kind: NamespacedKeyManagementProvider
+metadata:
+  name: keymanagementprovider-inline
+spec:
+  type: inline
+  parameters:
+    contentType: certificate
+    value: |
+      -----BEGIN CERTIFICATE-----
+      MIIDWDCCAkCgAwIBAgIBUTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJVUzEL
+      MAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEb
+      MBkGA1UEAxMSd2FiYml0LW5ldHdvcmtzLmlvMCAXDTIyMTIwMjA4MDg0NFoYDzIx
+      MjIxMjAzMDgwODQ0WjBaMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNV
+      BAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEbMBkGA1UEAxMSd2FiYml0LW5l
+      dHdvcmtzLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnoskJWB0
+      ZsYcfbTvCYQMLqWaB/yN3Jf7Ryxvndrij83fWEQPBQJi8Mk8SpNqm2x9uP3gsQDc
+      L/73a0p6/D+hza2jQQVhebe/oB0LJtUoD5LXlJ83UQdZETLMYAzeBNcBR4kMecrY
+      CnE6yjHeiEWdAH+U7Mt39zJh+9lGIcbk0aUE5UOp8o3t5RWFDcl9hQ7QOXROwmpO
+      thLUIiY/bcPpsg/2nH1nzFjqiBef3sgopFCTgtJ7qF8B83Xy/+hJ5vD29xsbSwuB
+      3iLE7qLxu2NxdIa4oL0Y2QKMh/getjI0xnvwAmPkFiFbzC7LFdDfd6+gA5GpUXxL
+      u6UmwucAgiljGQIDAQABoycwJTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI
+      KwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAFvRW/mGjnnMNFKJc/e3o/+yiJor
+      dcrq/1UzyD7eNmOaASXz8rrrFT/6/TBXExPuB2OIf9OgRJFfPGLxmzCwVgaWQbK0
+      VfTN4MQzRrSwPmNYsBAAwLxXbarYlMbm4DEmdJGyVikq08T2dZI51GC/YXEwzlnv
+      ldN0dBflb/FKkY5rAp0JgpHLGKeStxFvB62noBjWfrm7ShCf9gkn1CjmgvP/sYK0
+      pJgA1FHPd6EeB6yRBpLV4EJgQYUJoOpbHz+us62jKj5fAXsX052LPmk9ArmP0uJ1
+      CJLNdj+aShCs4paSWOObDmIyXHwCx3MxCvYsFk/Wsnwura6jGC+cNsjzSx4=
+      -----END CERTIFICATE-----
diff --git a/pkg/controllers/keymanagementprovider_controller.go b/pkg/controllers/clusterresource/keymanagementprovider_controller.go
similarity index 78%
rename from pkg/controllers/keymanagementprovider_controller.go
rename to pkg/controllers/clusterresource/keymanagementprovider_controller.go
index 13826e0..f8888ed 100644
--- a/pkg/controllers/keymanagementprovider_controller.go
+++ b/pkg/controllers/clusterresource/keymanagementprovider_controller.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package controllers
+package clusterresource
 
 import (
 	"context"
@@ -22,6 +22,7 @@ import (
 	"fmt"
 	"maps"
 
+	"github.com/deislabs/ratify/internal/constants"
 	_ "github.com/deislabs/ratify/pkg/keymanagementprovider/azurekeyvault" // register azure key vault key management provider
 	_ "github.com/deislabs/ratify/pkg/keymanagementprovider/inline"        // register inline key management provider
 	apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -32,11 +33,8 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/predicate"
 
 	configv1beta1 "github.com/deislabs/ratify/api/v1beta1"
-	c "github.com/deislabs/ratify/config"
+	cutils "github.com/deislabs/ratify/pkg/controllers/utils"
 	kmp "github.com/deislabs/ratify/pkg/keymanagementprovider"
-	"github.com/deislabs/ratify/pkg/keymanagementprovider/config"
-	"github.com/deislabs/ratify/pkg/keymanagementprovider/factory"
-	"github.com/deislabs/ratify/pkg/keymanagementprovider/types"
 	"github.com/sirupsen/logrus"
 )
 
@@ -52,10 +50,10 @@ type KeyManagementProviderReconciler struct {
 func (r *KeyManagementProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
 	logger := logrus.WithContext(ctx)
 
-	var resource = req.NamespacedName.String()
+	var resource = req.Name
 	var keyManagementProvider configv1beta1.KeyManagementProvider
 
-	logger.Infof("reconciling key management provider '%v'", resource)
+	logger.Infof("reconciling cluster key management provider '%v'", resource)
 
 	if err := r.Get(ctx, req.NamespacedName, &keyManagementProvider); err != nil {
 		if apierrors.IsNotFound(err) {
@@ -85,7 +83,7 @@ func (r *KeyManagementProviderReconciler) Reconcile(ctx context.Context, req ctr
 		logger.Warn("Certificate Store already exists. Key management provider and certificate store should not be configured together. Please migrate to key management provider and delete certificate store.")
 	}
 
-	provider, err := specToKeyManagementProvider(keyManagementProvider.Spec)
+	provider, err := cutils.SpecToKeyManagementProvider(keyManagementProvider.Spec.Parameters.Raw, keyManagementProvider.Spec.Type)
 	if err != nil {
 		writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
 		return ctrl.Result{}, err
@@ -130,38 +128,6 @@ func (r *KeyManagementProviderReconciler) SetupWithManager(mgr ctrl.Manager) err
 		Complete(r)
 }
 
-// specToKeyManagementProvider creates KeyManagementProviderProvider from  KeyManagementProviderSpec config
-func specToKeyManagementProvider(spec configv1beta1.KeyManagementProviderSpec) (kmp.KeyManagementProvider, error) {
-	kmProviderConfig, err := rawToKeyManagementProviderConfig(spec.Parameters.Raw, spec.Type)
-	if err != nil {
-		return nil, fmt.Errorf("failed to parse key management provider config: %w", err)
-	}
-
-	// TODO: add Version and Address to KeyManagementProviderSpec
-	keyManagementProviderProvider, err := factory.CreateKeyManagementProviderFromConfig(kmProviderConfig, "0.1.0", c.GetDefaultPluginPath())
-	if err != nil {
-		return nil, fmt.Errorf("failed to create key management provider provider: %w", err)
-	}
-
-	return keyManagementProviderProvider, nil
-}
-
-// rawToKeyManagementProviderConfig converts raw json to KeyManagementProviderConfig
-func rawToKeyManagementProviderConfig(raw []byte, keyManagamentSystemName string) (config.KeyManagementProviderConfig, error) {
-	pluginConfig := config.KeyManagementProviderConfig{}
-
-	if string(raw) == "" {
-		return config.KeyManagementProviderConfig{}, fmt.Errorf("no key management provider parameters provided")
-	}
-	if err := json.Unmarshal(raw, &pluginConfig); err != nil {
-		return config.KeyManagementProviderConfig{}, fmt.Errorf("unable to decode key management provider parameters.Raw: %s, err: %w", raw, err)
-	}
-
-	pluginConfig[types.Type] = keyManagamentSystemName
-
-	return pluginConfig, nil
-}
-
 // writeKMProviderStatus updates the status of the key management provider resource
 func writeKMProviderStatus(ctx context.Context, r client.StatusClient, keyManagementProvider *configv1beta1.KeyManagementProvider, logger *logrus.Entry, isSuccess bool, errorString string, operationTime metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
 	if isSuccess {
@@ -178,8 +144,8 @@ func writeKMProviderStatus(ctx context.Context, r client.StatusClient, keyManage
 func updateKMProviderErrorStatus(keyManagementProvider *configv1beta1.KeyManagementProvider, errorString string, operationTime *metav1.Time) {
 	// truncate brief error string to maxBriefErrLength
 	briefErr := errorString
-	if len(errorString) > maxBriefErrLength {
-		briefErr = fmt.Sprintf("%s...", errorString[:maxBriefErrLength])
+	if len(errorString) > constants.MaxBriefErrLength {
+		briefErr = fmt.Sprintf("%s...", errorString[:constants.MaxBriefErrLength])
 	}
 	keyManagementProvider.Status.IsSuccess = false
 	keyManagementProvider.Status.Error = errorString
diff --git a/pkg/controllers/namespaceresource/keymanagementprovider_controller.go b/pkg/controllers/namespaceresource/keymanagementprovider_controller.go
new file mode 100644
index 0000000..b898d38
--- /dev/null
+++ b/pkg/controllers/namespaceresource/keymanagementprovider_controller.go
@@ -0,0 +1,172 @@
+/*
+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 namespaceresource
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"maps"
+
+	"github.com/deislabs/ratify/internal/constants"
+	_ "github.com/deislabs/ratify/pkg/keymanagementprovider/azurekeyvault" // register azure key vault key management provider
+	_ "github.com/deislabs/ratify/pkg/keymanagementprovider/inline"        // register inline key management provider
+	apierrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/predicate"
+
+	configv1beta1 "github.com/deislabs/ratify/api/v1beta1"
+	cutils "github.com/deislabs/ratify/pkg/controllers/utils"
+	kmp "github.com/deislabs/ratify/pkg/keymanagementprovider"
+	"github.com/sirupsen/logrus"
+)
+
+// KeyManagementProviderReconciler reconciles a KeyManagementProvider object
+type KeyManagementProviderReconciler struct {
+	client.Client
+	Scheme *runtime.Scheme
+}
+
+// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=namespacedkeymanagementproviders,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=namespacedkeymanagementproviders/status,verbs=get;update;patch
+// +kubebuilder:rbac:groups=config.ratify.deislabs.io,resources=namespacedkeymanagementproviders/finalizers,verbs=update
+func (r *KeyManagementProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	logger := logrus.WithContext(ctx)
+
+	var resource = req.NamespacedName.String()
+	var keyManagementProvider configv1beta1.NamespacedKeyManagementProvider
+
+	logger.Infof("reconciling namespaced key management provider '%v'", resource)
+
+	if err := r.Get(ctx, req.NamespacedName, &keyManagementProvider); err != nil {
+		if apierrors.IsNotFound(err) {
+			logger.Infof("deletion detected, removing key management provider %v", resource)
+			kmp.DeleteCertificatesFromMap(resource)
+			kmp.DeleteKeysFromMap(resource)
+		} else {
+			logger.Error(err, "unable to fetch key management provider")
+		}
+
+		return ctrl.Result{}, client.IgnoreNotFound(err)
+	}
+
+	lastFetchedTime := metav1.Now()
+	isFetchSuccessful := false
+
+	// get certificate store list to check if certificate store is configured
+	// TODO: remove check in v2.0.0+
+	var certificateStoreList configv1beta1.CertificateStoreList
+	if err := r.List(ctx, &certificateStoreList); err != nil {
+		logger.Error(err, "unable to list certificate stores")
+		return ctrl.Result{}, err
+	}
+	// if certificate store is configured, return error. Only one of certificate store and key management provider can be configured
+	if len(certificateStoreList.Items) > 0 {
+		// Note: for backwards compatibility in upgrade scenarios, Ratify will only log a warning statement.
+		logger.Warn("Certificate Store already exists. Key management provider and certificate store should not be configured together. Please migrate to key management provider and delete certificate store.")
+	}
+
+	provider, err := cutils.SpecToKeyManagementProvider(keyManagementProvider.Spec.Parameters.Raw, keyManagementProvider.Spec.Type)
+	if err != nil {
+		writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
+		return ctrl.Result{}, err
+	}
+
+	// fetch certificates and store in map
+	certificates, certAttributes, err := provider.GetCertificates(ctx)
+	if err != nil {
+		writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
+		return ctrl.Result{}, fmt.Errorf("Error fetching certificates in KMProvider %v with %v provider, error: %w", resource, keyManagementProvider.Spec.Type, err)
+	}
+
+	// fetch keys and store in map
+	keys, keyAttributes, err := provider.GetKeys(ctx)
+	if err != nil {
+		writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, err.Error(), lastFetchedTime, nil)
+		return ctrl.Result{}, fmt.Errorf("Error fetching keys in KMProvider %v with %v provider, error: %w", resource, keyManagementProvider.Spec.Type, err)
+	}
+	kmp.SetCertificatesInMap(resource, certificates)
+	kmp.SetKeysInMap(resource, keyManagementProvider.Spec.Type, keys)
+	// merge certificates and keys status into one
+	maps.Copy(keyAttributes, certAttributes)
+	isFetchSuccessful = true
+	emptyErrorString := ""
+	writeKMProviderStatus(ctx, r, &keyManagementProvider, logger, isFetchSuccessful, emptyErrorString, lastFetchedTime, keyAttributes)
+
+	logger.Infof("%v certificate(s) & %v key(s) fetched for key management provider %v", len(certificates), len(keys), resource)
+
+	// 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 *KeyManagementProviderReconciler) SetupWithManager(mgr ctrl.Manager) error {
+	pred := predicate.GenerationChangedPredicate{}
+
+	// status updates will trigger a reconcile event
+	// if there are no changes to spec of CRD, this event should be filtered out by using the predicate
+	// see more discussions at https://github.com/kubernetes-sigs/kubebuilder/issues/618
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&configv1beta1.NamespacedKeyManagementProvider{}).WithEventFilter(pred).
+		Complete(r)
+}
+
+// writeKMProviderStatus updates the status of the key management provider resource
+func writeKMProviderStatus(ctx context.Context, r client.StatusClient, keyManagementProvider *configv1beta1.NamespacedKeyManagementProvider, logger *logrus.Entry, isSuccess bool, errorString string, operationTime metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
+	if isSuccess {
+		updateKMProviderSuccessStatus(keyManagementProvider, &operationTime, kmProviderStatus)
+	} else {
+		updateKMProviderErrorStatus(keyManagementProvider, errorString, &operationTime)
+	}
+	if statusErr := r.Status().Update(ctx, keyManagementProvider); statusErr != nil {
+		logger.Error(statusErr, ",unable to update key management provider error status")
+	}
+}
+
+// updateKMProviderErrorStatus updates the key management provider status with error, brief error and last fetched time
+func updateKMProviderErrorStatus(keyManagementProvider *configv1beta1.NamespacedKeyManagementProvider, errorString string, operationTime *metav1.Time) {
+	// truncate brief error string to maxBriefErrLength
+	briefErr := errorString
+	if len(errorString) > constants.MaxBriefErrLength {
+		briefErr = fmt.Sprintf("%s...", errorString[:constants.MaxBriefErrLength])
+	}
+	keyManagementProvider.Status.IsSuccess = false
+	keyManagementProvider.Status.Error = errorString
+	keyManagementProvider.Status.BriefError = briefErr
+	keyManagementProvider.Status.LastFetchedTime = operationTime
+}
+
+// updateKMProviderSuccessStatus updates the key management provider status if status argument is non nil
+// Success status includes last fetched time and other provider-specific properties
+func updateKMProviderSuccessStatus(keyManagementProvider *configv1beta1.NamespacedKeyManagementProvider, lastOperationTime *metav1.Time, kmProviderStatus kmp.KeyManagementProviderStatus) {
+	keyManagementProvider.Status.IsSuccess = true
+	keyManagementProvider.Status.Error = ""
+	keyManagementProvider.Status.BriefError = ""
+	keyManagementProvider.Status.LastFetchedTime = lastOperationTime
+
+	if kmProviderStatus != nil {
+		jsonString, _ := json.Marshal(kmProviderStatus)
+
+		raw := runtime.RawExtension{
+			Raw: jsonString,
+		}
+		keyManagementProvider.Status.Properties = raw
+	}
+}
diff --git a/pkg/controllers/utils/kmp.go b/pkg/controllers/utils/kmp.go
new file mode 100644
index 0000000..d0b0668
--- /dev/null
+++ b/pkg/controllers/utils/kmp.go
@@ -0,0 +1,57 @@
+/*
+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 utils
+
+import (
+	"encoding/json"
+	"fmt"
+
+	c "github.com/deislabs/ratify/config"
+	kmp "github.com/deislabs/ratify/pkg/keymanagementprovider"
+	"github.com/deislabs/ratify/pkg/keymanagementprovider/config"
+	"github.com/deislabs/ratify/pkg/keymanagementprovider/factory"
+	"github.com/deislabs/ratify/pkg/keymanagementprovider/types"
+)
+
+// SpecToKeyManagementProvider creates KeyManagementProvider from  KeyManagementProviderSpec config
+func SpecToKeyManagementProvider(raw []byte, keyManagamentSystemName string) (kmp.KeyManagementProvider, error) {
+	kmProviderConfig, err := rawToKeyManagementProviderConfig(raw, keyManagamentSystemName)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse key management provider config: %w", err)
+	}
+
+	// TODO: add Version and Address to KeyManagementProviderSpec
+	keyManagementProviderProvider, err := factory.CreateKeyManagementProviderFromConfig(kmProviderConfig, "0.1.0", c.GetDefaultPluginPath())
+	if err != nil {
+		return nil, fmt.Errorf("failed to create key management provider provider: %w", err)
+	}
+
+	return keyManagementProviderProvider, nil
+}
+
+// rawToKeyManagementProviderConfig converts raw json to KeyManagementProviderConfig
+func rawToKeyManagementProviderConfig(raw []byte, keyManagamentSystemName string) (config.KeyManagementProviderConfig, error) {
+	pluginConfig := config.KeyManagementProviderConfig{}
+
+	if string(raw) == "" {
+		return config.KeyManagementProviderConfig{}, fmt.Errorf("no key management provider parameters provided")
+	}
+	if err := json.Unmarshal(raw, &pluginConfig); err != nil {
+		return config.KeyManagementProviderConfig{}, fmt.Errorf("unable to decode key management provider parameters.Raw: %s, err: %w", raw, err)
+	}
+
+	pluginConfig[types.Type] = keyManagamentSystemName
+
+	return pluginConfig, nil
+}
diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go
index 5e372b0..e993d11 100644
--- a/pkg/manager/manager.go
+++ b/pkg/manager/manager.go
@@ -235,11 +235,18 @@ func StartManager(certRotatorReady chan struct{}, probeAddr string) {
 		setupLog.Error(err, "unable to create controller", "controller", "Policy")
 		os.Exit(1)
 	}
-	if err = (&controllers.KeyManagementProviderReconciler{
+	if err = (&clusterresource.KeyManagementProviderReconciler{
 		Client: mgr.GetClient(),
 		Scheme: mgr.GetScheme(),
 	}).SetupWithManager(mgr); err != nil {
-		setupLog.Error(err, "unable to create controller", "controller", "Key Management Provider")
+		setupLog.Error(err, "unable to create controller", "controller", "Cluster Key Management Provider")
+		os.Exit(1)
+	}
+	if err = (&namespaceresource.KeyManagementProviderReconciler{
+		Client: mgr.GetClient(),
+		Scheme: mgr.GetScheme(),
+	}).SetupWithManager(mgr); err != nil {
+		setupLog.Error(err, "unable to create controller", "controller", "Namespaced Key Management Provider")
 		os.Exit(1)
 	}
 	//+kubebuilder:scaffold:builder


Test patch:
diff --git a/pkg/controllers/keymanagementprovider_controller_test.go b/pkg/controllers/clusterresource/keymanagementprovider_controller_test.go
similarity index 59%
rename from pkg/controllers/keymanagementprovider_controller_test.go
rename to pkg/controllers/clusterresource/keymanagementprovider_controller_test.go
index 8a0c610..21ad63d 100644
--- a/pkg/controllers/keymanagementprovider_controller_test.go
+++ b/pkg/controllers/clusterresource/keymanagementprovider_controller_test.go
@@ -13,20 +13,19 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package controllers
+package clusterresource
 
 import (
 	"context"
 	"fmt"
-	"reflect"
 	"testing"
 
 	configv1beta1 "github.com/deislabs/ratify/api/v1beta1"
 	"github.com/deislabs/ratify/pkg/keymanagementprovider"
-	"github.com/deislabs/ratify/pkg/keymanagementprovider/config"
 	"github.com/sirupsen/logrus"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
+	test "github.com/deislabs/ratify/pkg/utils"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 )
@@ -129,94 +128,6 @@ func TestKMProviderUpdateSuccessStatus_emptyProperties(t *testing.T) {
 	}
 }
 
-// TestRawToKeyManagementProviderConfig tests the rawToKeyManagementProviderConfig method
-func TestRawToKeyManagementProviderConfig(t *testing.T) {
-	testCases := []struct {
-		name         string
-		raw          []byte
-		expectErr    bool
-		expectConfig config.KeyManagementProviderConfig
-	}{
-		{
-			name:         "empty Raw",
-			raw:          []byte{},
-			expectErr:    true,
-			expectConfig: config.KeyManagementProviderConfig{},
-		},
-		{
-			name:         "unmarshal failure",
-			raw:          []byte("invalid"),
-			expectErr:    true,
-			expectConfig: config.KeyManagementProviderConfig{},
-		},
-		{
-			name:      "valid Raw",
-			raw:       []byte("{\"type\": \"inline\"}"),
-			expectErr: false,
-			expectConfig: config.KeyManagementProviderConfig{
-				"type": "inline",
-			},
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			config, err := rawToKeyManagementProviderConfig(tc.raw, "inline")
-
-			if tc.expectErr != (err != nil) {
-				t.Fatalf("Expected error to be %t, got %t", tc.expectErr, err != nil)
-			}
-			if !reflect.DeepEqual(config, tc.expectConfig) {
-				t.Fatalf("Expected config to be %v, got %v", tc.expectConfig, config)
-			}
-		})
-	}
-}
-
-// TestSpecToKeyManagementProviderProvider tests the specToKeyManagementProviderProvider method
-func TestSpecToKeyManagementProviderProvider(t *testing.T) {
-	testCases := []struct {
-		name      string
-		spec      configv1beta1.KeyManagementProviderSpec
-		expectErr bool
-	}{
-		{
-			name:      "empty spec",
-			spec:      configv1beta1.KeyManagementProviderSpec{},
-			expectErr: true,
-		},
-		{
-			name: "missing inline provider required fields",
-			spec: configv1beta1.KeyManagementProviderSpec{
-				Type: "inline",
-				Parameters: runtime.RawExtension{
-					Raw: []byte("{\"type\": \"inline\"}"),
-				},
-			},
-			expectErr: true,
-		},
-		{
-			name: "valid spec",
-			spec: configv1beta1.KeyManagementProviderSpec{
-				Type: "inline",
-				Parameters: runtime.RawExtension{
-					Raw: []byte(`{"type": "inline", "contentType": "certificate", "value": "-----BEGIN CERTIFICATE-----\nMIID2jCCAsKgAwIBAgIQXy2VqtlhSkiZKAGhsnkjbDANBgkqhkiG9w0BAQsFADBvMRswGQYDVQQD\nExJyYXRpZnkuZXhhbXBsZS5jb20xDzANBgNVBAsTBk15IE9yZzETMBEGA1UEChMKTXkgQ29tcGFu\neTEQMA4GA1UEBxMHUmVkbW9uZDELMAkGA1UECBMCV0ExCzAJBgNVBAYTAlVTMB4XDTIzMDIwMTIy\nNDUwMFoXDTI0MDIwMTIyNTUwMFowbzEbMBkGA1UEAxMScmF0aWZ5LmV4YW1wbGUuY29tMQ8wDQYD\nVQQLEwZNeSBPcmcxEzARBgNVBAoTCk15IENvbXBhbnkxEDAOBgNVBAcTB1JlZG1vbmQxCzAJBgNV\nBAgTAldBMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL10bM81\npPAyuraORABsOGS8M76Bi7Guwa3JlM1g2D8CuzSfSTaaT6apy9GsccxUvXd5cmiP1ffna5z+EFmc\nizFQh2aq9kWKWXDvKFXzpQuhyqD1HeVlRlF+V0AfZPvGt3VwUUjNycoUU44ctCWmcUQP/KShZev3\n6SOsJ9q7KLjxxQLsUc4mg55eZUThu8mGB8jugtjsnLUYvIWfHhyjVpGrGVrdkDMoMn+u33scOmrt\nsBljvq9WVo4T/VrTDuiOYlAJFMUae2Ptvo0go8XTN3OjLblKeiK4C+jMn9Dk33oGIT9pmX0vrDJV\nX56w/2SejC1AxCPchHaMuhlwMpftBGkCAwEAAaNyMHAwDgYDVR0PAQH/BAQDAgeAMAkGA1UdEwQC\nMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU0eaKkZj+MS9jCp9Dg1zdv3v/aKww\nHQYDVR0OBBYEFNHmipGY/jEvYwqfQ4Nc3b97/2isMA0GCSqGSIb3DQEBCwUAA4IBAQBNDcmSBizF\nmpJlD8EgNcUCy5tz7W3+AAhEbA3vsHP4D/UyV3UgcESx+L+Nye5uDYtTVm3lQejs3erN2BjW+ds+\nXFnpU/pVimd0aYv6mJfOieRILBF4XFomjhrJOLI55oVwLN/AgX6kuC3CJY2NMyJKlTao9oZgpHhs\nLlxB/r0n9JnUoN0Gq93oc1+OLFjPI7gNuPXYOP1N46oKgEmAEmNkP1etFrEjFRgsdIFHksrmlOlD\nIed9RcQ087VLjmuymLgqMTFX34Q3j7XgN2ENwBSnkHotE9CcuGRW+NuiOeJalL8DBmFXXWwHTKLQ\nPp5g6m1yZXylLJaFLKz7tdMmO355\n-----END CERTIFICATE-----\n"}`),
-				},
-			},
-			expectErr: false,
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			_, err := specToKeyManagementProvider(tc.spec)
-			if tc.expectErr != (err != nil) {
-				t.Fatalf("Expected error to be %t, got %t", tc.expectErr, err != nil)
-			}
-		})
-	}
-}
-
 func TestWriteKMProviderStatus(t *testing.T) {
 	logger := logrus.WithContext(context.Background())
 	lastFetchedTime := metav1.Now()
@@ -232,21 +143,21 @@ func TestWriteKMProviderStatus(t *testing.T) {
 			isSuccess:  true,
 			errString:  "",
 			kmProvider: &configv1beta1.KeyManagementProvider{},
-			reconciler: &mockStatusClient{},
+			reconciler: &test.MockStatusClient{},
 		},
 		{
 			name:       "error status",
 			isSuccess:  false,
 			kmProvider: &configv1beta1.KeyManagementProvider{},
 			errString:  "a long error string that exceeds the max length of 30 characters",
-			reconciler: &mockStatusClient{},
+			reconciler: &test.MockStatusClient{},
 		},
 		{
 			name:       "status update failed",
 			isSuccess:  true,
 			kmProvider: &configv1beta1.KeyManagementProvider{},
-			reconciler: &mockStatusClient{
-				updateFailed: true,
+			reconciler: &test.MockStatusClient{
+				UpdateFailed: true,
 			},
 		},
 	}
diff --git a/pkg/controllers/namespaceresource/keymanagementprovider_controller_test.go b/pkg/controllers/namespaceresource/keymanagementprovider_controller_test.go
new file mode 100644
index 0000000..bc6baba
--- /dev/null
+++ b/pkg/controllers/namespaceresource/keymanagementprovider_controller_test.go
@@ -0,0 +1,178 @@
+/*
+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 namespaceresource
+
+import (
+	"context"
+	"fmt"
+	"testing"
+
+	configv1beta1 "github.com/deislabs/ratify/api/v1beta1"
+	"github.com/deislabs/ratify/pkg/keymanagementprovider"
+	"github.com/sirupsen/logrus"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	test "github.com/deislabs/ratify/pkg/utils"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+// TestUpdateErrorStatus tests the updateErrorStatus method
+func TestKMProviderUpdateErrorStatus(t *testing.T) {
+	var parametersString = "{\"certs\":{\"name\":\"certName\"}}"
+	var kmProviderStatus = []byte(parametersString)
+
+	status := configv1beta1.NamespacedKeyManagementProviderStatus{
+		IsSuccess: true,
+		Properties: runtime.RawExtension{
+			Raw: kmProviderStatus,
+		},
+	}
+	keyManagementProvider := configv1beta1.NamespacedKeyManagementProvider{
+		Status: status,
+	}
+	expectedErr := "it's a long error from unit test"
+	lastFetchedTime := metav1.Now()
+	updateKMProviderErrorStatus(&keyManagementProvider, expectedErr, &lastFetchedTime)
+
+	if keyManagementProvider.Status.IsSuccess != false {
+		t.Fatalf("Unexpected error, expected isSuccess to be false , actual %+v", keyManagementProvider.Status.IsSuccess)
+	}
+
+	if keyManagementProvider.Status.Error != expectedErr {
+		t.Fatalf("Unexpected error string, expected %+v, got %+v", expectedErr, keyManagementProvider.Status.Error)
+	}
+	expectedBriedErr := fmt.Sprintf("%s...", expectedErr[:30])
+	if keyManagementProvider.Status.BriefError != expectedBriedErr {
+		t.Fatalf("Unexpected error string, expected %+v, got %+v", expectedBriedErr, keyManagementProvider.Status.Error)
+	}
+
+	//make sure properties of last cached cert was not overridden
+	if len(keyManagementProvider.Status.Properties.Raw) == 0 {
+		t.Fatalf("Unexpected properties,  expected %+v, got %+v", parametersString, string(keyManagementProvider.Status.Properties.Raw))
+	}
+}
+
+// TestKMProviderUpdateSuccessStatus tests the updateSuccessStatus method
+func TestKMProviderUpdateSuccessStatus(t *testing.T) {
+	kmProviderStatus := keymanagementprovider.KeyManagementProviderStatus{}
+	properties := map[string]string{}
+	properties["Name"] = "wabbit"
+	properties["Version"] = "ABC"
+
+	kmProviderStatus["Certificates"] = properties
+
+	lastFetchedTime := metav1.Now()
+
+	status := configv1beta1.NamespacedKeyManagementProviderStatus{
+		IsSuccess: false,
+		Error:     "error from last operation",
+	}
+	keyManagementProvider := configv1beta1.NamespacedKeyManagementProvider{
+		Status: status,
+	}
+
+	updateKMProviderSuccessStatus(&keyManagementProvider, &lastFetchedTime, kmProviderStatus)
+
+	if keyManagementProvider.Status.IsSuccess != true {
+		t.Fatalf("Expected isSuccess to be true , actual %+v", keyManagementProvider.Status.IsSuccess)
+	}
+
+	if keyManagementProvider.Status.Error != "" {
+		t.Fatalf("Unexpected error string, actual %+v", keyManagementProvider.Status.Error)
+	}
+
+	//make sure properties of last cached cert was updated
+	if len(keyManagementProvider.Status.Properties.Raw) == 0 {
+		t.Fatalf("Properties should not be empty")
+	}
+}
+
+// TestKMProviderUpdateSuccessStatus tests the updateSuccessStatus method with empty properties
+func TestKMProviderUpdateSuccessStatus_emptyProperties(t *testing.T) {
+	lastFetchedTime := metav1.Now()
+	status := configv1beta1.NamespacedKeyManagementProviderStatus{
+		IsSuccess: false,
+		Error:     "error from last operation",
+	}
+	keyManagementProvider := configv1beta1.NamespacedKeyManagementProvider{
+		Status: status,
+	}
+
+	updateKMProviderSuccessStatus(&keyManagementProvider, &lastFetchedTime, nil)
+
+	if keyManagementProvider.Status.IsSuccess != true {
+		t.Fatalf("Expected isSuccess to be true , actual %+v", keyManagementProvider.Status.IsSuccess)
+	}
+
+	if keyManagementProvider.Status.Error != "" {
+		t.Fatalf("Unexpected error string, actual %+v", keyManagementProvider.Status.Error)
+	}
+
+	//make sure properties of last cached cert was updated
+	if len(keyManagementProvider.Status.Properties.Raw) != 0 {
+		t.Fatalf("Properties should be empty")
+	}
+}
+
+func TestWriteKMProviderStatus(t *testing.T) {
+	logger := logrus.WithContext(context.Background())
+	lastFetchedTime := metav1.Now()
+	testCases := []struct {
+		name       string
+		isSuccess  bool
+		kmProvider *configv1beta1.NamespacedKeyManagementProvider
+		errString  string
+		reconciler client.StatusClient
+	}{
+		{
+			name:       "success status",
+			isSuccess:  true,
+			errString:  "",
+			kmProvider: &configv1beta1.NamespacedKeyManagementProvider{},
+			reconciler: &test.MockStatusClient{},
+		},
+		{
+			name:       "error status",
+			isSuccess:  false,
+			kmProvider: &configv1beta1.NamespacedKeyManagementProvider{},
+			errString:  "a long error string that exceeds the max length of 30 characters",
+			reconciler: &test.MockStatusClient{},
+		},
+		{
+			name:       "status update failed",
+			isSuccess:  true,
+			kmProvider: &configv1beta1.NamespacedKeyManagementProvider{},
+			reconciler: &test.MockStatusClient{
+				UpdateFailed: true,
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			writeKMProviderStatus(context.Background(), tc.reconciler, tc.kmProvider, logger, tc.isSuccess, tc.errString, lastFetchedTime, nil)
+
+			if tc.kmProvider.Status.IsSuccess != tc.isSuccess {
+				t.Fatalf("Expected isSuccess to be %+v , actual %+v", tc.isSuccess, tc.kmProvider.Status.IsSuccess)
+			}
+
+			if tc.kmProvider.Status.Error != tc.errString {
+				t.Fatalf("Expected Error to be %+v , actual %+v", tc.errString, tc.kmProvider.Status.Error)
+			}
+		})
+	}
+}
diff --git a/pkg/controllers/utils/kmp_test.go b/pkg/controllers/utils/kmp_test.go
new file mode 100644
index 0000000..eff43b9
--- /dev/null
+++ b/pkg/controllers/utils/kmp_test.go
@@ -0,0 +1,101 @@
+/*
+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 utils
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/deislabs/ratify/pkg/keymanagementprovider/config"
+	_ "github.com/deislabs/ratify/pkg/keymanagementprovider/inline"
+)
+
+func TestSpecToKeyManagementProviderProvider(t *testing.T) {
+	testCases := []struct {
+		name      string
+		raw       []byte
+		kmpType   string
+		expectErr bool
+	}{
+		{
+			name:      "empty spec",
+			expectErr: true,
+		},
+		{
+			name:      "missing inline provider required fields",
+			raw:       []byte("{\"type\": \"inline\"}"),
+			kmpType:   "inline",
+			expectErr: true,
+		},
+		{
+			name:      "valid spec",
+			raw:       []byte(`{"type": "inline", "contentType": "certificate", "value": "-----BEGIN CERTIFICATE-----\nMIID2jCCAsKgAwIBAgIQXy2VqtlhSkiZKAGhsnkjbDANBgkqhkiG9w0BAQsFADBvMRswGQYDVQQD\nExJyYXRpZnkuZXhhbXBsZS5jb20xDzANBgNVBAsTBk15IE9yZzETMBEGA1UEChMKTXkgQ29tcGFu\neTEQMA4GA1UEBxMHUmVkbW9uZDELMAkGA1UECBMCV0ExCzAJBgNVBAYTAlVTMB4XDTIzMDIwMTIy\nNDUwMFoXDTI0MDIwMTIyNTUwMFowbzEbMBkGA1UEAxMScmF0aWZ5LmV4YW1wbGUuY29tMQ8wDQYD\nVQQLEwZNeSBPcmcxEzARBgNVBAoTCk15IENvbXBhbnkxEDAOBgNVBAcTB1JlZG1vbmQxCzAJBgNV\nBAgTAldBMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL10bM81\npPAyuraORABsOGS8M76Bi7Guwa3JlM1g2D8CuzSfSTaaT6apy9GsccxUvXd5cmiP1ffna5z+EFmc\nizFQh2aq9kWKWXDvKFXzpQuhyqD1HeVlRlF+V0AfZPvGt3VwUUjNycoUU44ctCWmcUQP/KShZev3\n6SOsJ9q7KLjxxQLsUc4mg55eZUThu8mGB8jugtjsnLUYvIWfHhyjVpGrGVrdkDMoMn+u33scOmrt\nsBljvq9WVo4T/VrTDuiOYlAJFMUae2Ptvo0go8XTN3OjLblKeiK4C+jMn9Dk33oGIT9pmX0vrDJV\nX56w/2SejC1AxCPchHaMuhlwMpftBGkCAwEAAaNyMHAwDgYDVR0PAQH/BAQDAgeAMAkGA1UdEwQC\nMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwHwYDVR0jBBgwFoAU0eaKkZj+MS9jCp9Dg1zdv3v/aKww\nHQYDVR0OBBYEFNHmipGY/jEvYwqfQ4Nc3b97/2isMA0GCSqGSIb3DQEBCwUAA4IBAQBNDcmSBizF\nmpJlD8EgNcUCy5tz7W3+AAhEbA3vsHP4D/UyV3UgcESx+L+Nye5uDYtTVm3lQejs3erN2BjW+ds+\nXFnpU/pVimd0aYv6mJfOieRILBF4XFomjhrJOLI55oVwLN/AgX6kuC3CJY2NMyJKlTao9oZgpHhs\nLlxB/r0n9JnUoN0Gq93oc1+OLFjPI7gNuPXYOP1N46oKgEmAEmNkP1etFrEjFRgsdIFHksrmlOlD\nIed9RcQ087VLjmuymLgqMTFX34Q3j7XgN2ENwBSnkHotE9CcuGRW+NuiOeJalL8DBmFXXWwHTKLQ\nPp5g6m1yZXylLJaFLKz7tdMmO355\n-----END CERTIFICATE-----\n"}`),
+			kmpType:   "inline",
+			expectErr: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			_, err := SpecToKeyManagementProvider(tc.raw, tc.kmpType)
+			if tc.expectErr != (err != nil) {
+				t.Fatalf("Expected error to be %t, got %t", tc.expectErr, err != nil)
+			}
+		})
+	}
+}
+
+// TestRawToKeyManagementProviderConfig tests the rawToKeyManagementProviderConfig method
+func TestRawToKeyManagementProviderConfig(t *testing.T) {
+	testCases := []struct {
+		name         string
+		raw          []byte
+		expectErr    bool
+		expectConfig config.KeyManagementProviderConfig
+	}{
+		{
+			name:         "empty Raw",
+			raw:          []byte{},
+			expectErr:    true,
+			expectConfig: config.KeyManagementProviderConfig{},
+		},
+		{
+			name:         "unmarshal failure",
+			raw:          []byte("invalid"),
+			expectErr:    true,
+			expectConfig: config.KeyManagementProviderConfig{},
+		},
+		{
+			name:      "valid Raw",
+			raw:       []byte("{\"type\": \"inline\"}"),
+			expectErr: false,
+			expectConfig: config.KeyManagementProviderConfig{
+				"type": "inline",
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			config, err := rawToKeyManagementProviderConfig(tc.raw, "inline")
+
+			if tc.expectErr != (err != nil) {
+				t.Fatalf("Expected error to be %t, got %t", tc.expectErr, err != nil)
+			}
+			if !reflect.DeepEqual(config, tc.expectConfig) {
+				t.Fatalf("Expected config to be %v, got %v", tc.expectConfig, config)
+			}
+		})
+	}
+}
diff --git a/test/bats/azure-test.bats b/test/bats/azure-test.bats
index 89692e2..a8adba2 100644
--- a/test/bats/azure-test.bats
+++ b/test/bats/azure-test.bats
@@ -45,7 +45,7 @@ SLEEP_TIME=1
 
 @test "validate image signed by leaf cert" {
     teardown() {
-        wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete keymanagementproviders.config.ratify.deislabs.io/keymanagementprovider-inline --namespace default --ignore-not-found=true'
+        wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete namespacedkeymanagementproviders.config.ratify.deislabs.io/keymanagementprovider-inline --namespace default --ignore-not-found=true'
         wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete pod demo-leaf --namespace default --force --ignore-not-found=true'
         wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete pod demo-leaf2 --namespace default --force --ignore-not-found=true'
 
diff --git a/test/bats/base-test.bats b/test/bats/base-test.bats
index c3ade58..58ddae2 100644
--- a/test/bats/base-test.bats
+++ b/test/bats/base-test.bats
@@ -35,7 +35,7 @@ RATIFY_NAMESPACE=gatekeeper-system
     assert_success
     sleep 5
     # validate key management provider status property shows success
-    run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml | grep 'issuccess: true'"
+    run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml | grep 'issuccess: true'"
     assert_success
     run kubectl run demo --namespace default --image=registry:5000/notation:signed
     assert_success
@@ -87,7 +87,7 @@ RATIFY_NAMESPACE=gatekeeper-system
     sleep 5
 
     # validate key management provider status property shows success
-    run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml | grep 'issuccess: true'"
+    run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml | grep 'issuccess: true'"
     assert_success
     run kubectl run demo --namespace default --image=registry:5000/notation:signed
     assert_success
@@ -103,8 +103,8 @@ RATIFY_NAMESPACE=gatekeeper-system
         wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete pod demo1 --namespace default --force --ignore-not-found=true'
         
         # restore cert store in ratify namespace
-        run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -o yaml -n default > kmprovider.yaml"
-        run kubectl delete keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n default
+        run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -o yaml -n default > kmprovider.yaml"
+        run kubectl delete namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n default
         sed 's/default/gatekeeper-system/' kmprovider.yaml > kmproviderNewNS.yaml
         run kubectl apply -f kmproviderNewNS.yaml        
         assert_success
@@ -121,12 +121,12 @@ RATIFY_NAMESPACE=gatekeeper-system
     sleep 5
     
     # apply the key management provider to default namespace
-    run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -o yaml -n ${RATIFY_NAMESPACE} > kmprovider.yaml"    
+    run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -o yaml -n ${RATIFY_NAMESPACE} > kmprovider.yaml"    
     assert_success
     sed 's/gatekeeper-system/default/' kmprovider.yaml > kmproviderNewNS.yaml    
     run kubectl apply -f kmproviderNewNS.yaml
     assert_success
-    run kubectl delete keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE}
+    run kubectl delete namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE}
     assert_success
     
     # configure the notation verifier to use inline certificate store with specific namespace
@@ -330,7 +330,7 @@ RATIFY_NAMESPACE=gatekeeper-system
     }
 
     # save the existing key management provider inline resource to restore later
-    run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml > kmprovider_staging.yaml"
+    run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml > kmprovider_staging.yaml"
     assert_success
     # configure the default template/constraint
     run kubectl apply -f ./library/default/template.yaml
@@ -343,7 +343,7 @@ RATIFY_NAMESPACE=gatekeeper-system
     assert_failure
 
     # delete the existing key management provider inline resource since certificate store and key management provider cannot be used together
-    run kubectl delete keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE}
+    run kubectl delete namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE}
     assert_success
     # add the alternate certificate as an inline certificate store
     cat ~/.config/notation/truststore/x509/ca/alternate-cert/alternate-cert.crt | sed 's/^/      /g' >>./test/bats/tests/config/config_v1beta1_certstore_inline.yaml
@@ -363,7 +363,7 @@ RATIFY_NAMESPACE=gatekeeper-system
 
 @test "validate inline key management provider" {
     teardown() {
-        wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete keymanagementproviders.config.ratify.deislabs.io/keymanagementprovider-inline --namespace ${RATIFY_NAMESPACE} --ignore-not-found=true'
+        wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete namespacedkeymanagementproviders.config.ratify.deislabs.io/keymanagementprovider-inline --namespace ${RATIFY_NAMESPACE} --ignore-not-found=true'
         wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete pod demo-alternate --namespace default --force --ignore-not-found=true'
 
         # restore the original notation verifier for other tests
@@ -414,7 +414,7 @@ RATIFY_NAMESPACE=gatekeeper-system
     assert_success
 
     # validate key management provider status property shows success
-    run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml | grep 'issuccess: true'"
+    run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n ${RATIFY_NAMESPACE} -o yaml | grep 'issuccess: true'"
     assert_success
     run kubectl run demo --namespace default --image=registry:5000/notation:signed
     assert_success
@@ -459,7 +459,7 @@ RATIFY_NAMESPACE=gatekeeper-system
 
 @test "validate image signed by leaf cert" {
     teardown() {
-        wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete keymanagementproviders.config.ratify.deislabs.io/keymanagementprovider-inline --namespace ${RATIFY_NAMESPACE} --ignore-not-found=true'
+        wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete namespacedkeymanagementproviders.config.ratify.deislabs.io/keymanagementprovider-inline --namespace ${RATIFY_NAMESPACE} --ignore-not-found=true'
         wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete pod demo-leaf --namespace default --force --ignore-not-found=true'
         wait_for_process ${WAIT_TIME} ${SLEEP_TIME} 'kubectl delete pod demo-leaf2 --namespace default --force --ignore-not-found=true'
 
diff --git a/test/bats/high-availability.bats b/test/bats/high-availability.bats
index e0a11bd..c45e9f0 100644
--- a/test/bats/high-availability.bats
+++ b/test/bats/high-availability.bats
@@ -32,7 +32,7 @@ SLEEP_TIME=1
     assert_success
     sleep 5
     # validate key management provider status property shows success
-    run bash -c "kubectl get keymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n gatekeeper-system -o yaml | grep 'issuccess: true'"
+    run bash -c "kubectl get namespacedkeymanagementproviders.config.ratify.deislabs.io/ratify-notation-inline-cert-0 -n gatekeeper-system -o yaml | grep 'issuccess: true'"
     assert_success
     run kubectl run demo --namespace default --image=registry:5000/notation:signed
     assert_success
diff --git a/test/bats/tests/config/config_v1beta1_keymanagementprovider_inline.yaml b/test/bats/tests/config/config_v1beta1_keymanagementprovider_inline.yaml
index b0984cb..bb5bc47 100644
--- a/test/bats/tests/config/config_v1beta1_keymanagementprovider_inline.yaml
+++ b/test/bats/tests/config/config_v1beta1_keymanagementprovider_inline.yaml
@@ -1,5 +1,5 @@
 apiVersion: config.ratify.deislabs.io/v1beta1
-kind: KeyManagementProvider
+kind: NamespacedKeyManagementProvider
 metadata:
   name: keymanagementprovider-inline
 spec: