Repository: clusternet/clusternet
Pull request #211 (2021-12-16 04:36:51)

configurable clusternet reserved namespace
Signed-off-by: Di Xu <stephenhsu90@gmail.com>

<!--  Thanks for sending a pull request!

-->

#### What type of PR is this?
kind/enhancement
kind/feature

#### What this PR does / why we need it:
This PR introduces a new flag `--reserved-namespace` to make reserved namespace `clusternet-reserved` configurable, which is the default namespace to create Manifest in.

With this PR, #209, #205, you could deploy `clusternet-hub` in any namespaces and using customized namespace to create Manifest objects.

#### Which issue(s) this PR fixes:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #

#### Special notes for your reviewer:


codecov-commenter: # [Codecov](https://codecov.io/gh/clusternet/clusternet/pull/211?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet) Report
> Merging [#211](https://codecov.io/gh/clusternet/clusternet/pull/211?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet) (f4fe246) into [main](https://codecov.io/gh/clusternet/clusternet/commit/c5943f2aa033354f002f46a998ed22e80782dbc3?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet) (c5943f2) will **decrease** coverage by `0.01%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/clusternet/clusternet/pull/211/graphs/tree.svg?width=650&height=150&src=pr&token=4O6PTR0RJN&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet)](https://codecov.io/gh/clusternet/clusternet/pull/211?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet)

```diff
@@           Coverage Diff            @@
##            main    #211      +/-   ##
========================================
- Coverage   8.52%   8.51%   -0.02%     
========================================
  Files         35      35              
  Lines       4105    4110       +5     
========================================
  Hits         350     350              
- Misses      3728    3733       +5     
  Partials      27      27              
```


| [Impacted Files](https://codecov.io/gh/clusternet/clusternet/pull/211?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet) | Coverage Δ | |
|---|---|---|
| [...usternet/clusternet/pkg/hub/apiserver/apiserver.go](https://codecov.io/gh/clusternet/clusternet/pull/211/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet#diff-c3JjL2dpdGh1Yi5jb20vY2x1c3Rlcm5ldC9jbHVzdGVybmV0L3BrZy9odWIvYXBpc2VydmVyL2FwaXNlcnZlci5nbw==) | `20.77% <0.00%> (-0.28%)` | :arrow_down: |
| [...t/clusternet/pkg/hub/apiserver/shadow/apiserver.go](https://codecov.io/gh/clusternet/clusternet/pull/211/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet#diff-c3JjL2dpdGh1Yi5jb20vY2x1c3Rlcm5ldC9jbHVzdGVybmV0L3BrZy9odWIvYXBpc2VydmVyL3NoYWRvdy9hcGlzZXJ2ZXIuZ28=) | `11.28% <0.00%> (-0.06%)` | :arrow_down: |
| [...usternet/clusternet/pkg/hub/localizer/localizer.go](https://codecov.io/gh/clusternet/clusternet/pull/211/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet#diff-c3JjL2dpdGh1Yi5jb20vY2x1c3Rlcm5ldC9jbHVzdGVybmV0L3BrZy9odWIvbG9jYWxpemVyL2xvY2FsaXplci5nbw==) | `0.00% <0.00%> (ø)` | |
| [...pkg/hub/apiserver/shadow/customresource\_handler.go](https://codecov.io/gh/clusternet/clusternet/pull/211/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet#diff-c3JjL2dpdGh1Yi5jb20vY2x1c3Rlcm5ldC9jbHVzdGVybmV0L3BrZy9odWIvYXBpc2VydmVyL3NoYWRvdy9jdXN0b21yZXNvdXJjZV9oYW5kbGVyLmdv) | `0.00% <0.00%> (ø)` | |
| [...et/clusternet/pkg/registry/shadow/template/rest.go](https://codecov.io/gh/clusternet/clusternet/pull/211/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet#diff-c3JjL2dpdGh1Yi5jb20vY2x1c3Rlcm5ldC9jbHVzdGVybmV0L3BrZy9yZWdpc3RyeS9zaGFkb3cvdGVtcGxhdGUvcmVzdC5nbw==) | `2.75% <0.00%> (+<0.01%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/clusternet/clusternet/pull/211?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/clusternet/clusternet/pull/211?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet). Last update [c5943f2...f4fe246](https://codecov.io/gh/clusternet/clusternet/pull/211?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=clusternet).


Patch:
diff --git a/pkg/apis/apps/v1alpha1/manifest.go b/pkg/apis/apps/v1alpha1/manifest.go
index 39d3496..77412ab 100644
--- a/pkg/apis/apps/v1alpha1/manifest.go
+++ b/pkg/apis/apps/v1alpha1/manifest.go
@@ -21,11 +21,6 @@ import (
 	"k8s.io/apimachinery/pkg/runtime"
 )
 
-const (
-	// objects will be store into Manifest with ReservedNamespace
-	ReservedNamespace = "clusternet-reserved"
-)
-
 // +genclient
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 // +kubebuilder:resource:scope="Namespaced",categories=clusternet
diff --git a/pkg/controllers/apps/globalization/globalization.go b/pkg/controllers/apps/globalization/globalization.go
index a47b717..72e3366 100644
--- a/pkg/controllers/apps/globalization/globalization.go
+++ b/pkg/controllers/apps/globalization/globalization.go
@@ -73,27 +73,31 @@ type Controller struct {
 	recorder record.EventRecorder
 
 	syncHandlerFunc SyncHandlerFunc
+
+	// namespace where Manifests are created
+	reservedNamespace string
 }
 
 func NewController(clusternetClient clusternetclientset.Interface,
 	globInformer appinformers.GlobalizationInformer,
 	chartInformer appinformers.HelmChartInformer, manifestInformer appinformers.ManifestInformer,
-	recorder record.EventRecorder, syncHandlerFunc SyncHandlerFunc) (*Controller, error) {
+	recorder record.EventRecorder, syncHandlerFunc SyncHandlerFunc, reservedNamespace string) (*Controller, error) {
 	if syncHandlerFunc == nil {
 		return nil, fmt.Errorf("syncHandlerFunc must be set")
 	}
 
 	c := &Controller{
-		clusternetClient: clusternetClient,
-		workqueue:        workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "globalization"),
-		globLister:       globInformer.Lister(),
-		globSynced:       globInformer.Informer().HasSynced,
-		chartLister:      chartInformer.Lister(),
-		chartSynced:      chartInformer.Informer().HasSynced,
-		manifestLister:   manifestInformer.Lister(),
-		manifestSynced:   manifestInformer.Informer().HasSynced,
-		recorder:         recorder,
-		syncHandlerFunc:  syncHandlerFunc,
+		clusternetClient:  clusternetClient,
+		workqueue:         workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "globalization"),
+		globLister:        globInformer.Lister(),
+		globSynced:        globInformer.Informer().HasSynced,
+		chartLister:       chartInformer.Lister(),
+		chartSynced:       chartInformer.Informer().HasSynced,
+		manifestLister:    manifestInformer.Lister(),
+		manifestSynced:    manifestInformer.Informer().HasSynced,
+		recorder:          recorder,
+		syncHandlerFunc:   syncHandlerFunc,
+		reservedNamespace: reservedNamespace,
 	}
 
 	// Manage the addition/update of Globalization
@@ -332,7 +336,7 @@ func (c *Controller) getLabelsForPatching(glob *appsapi.Globalization) (map[stri
 			labelsToPatch[string(chart.UID)] = &chartKind.Kind
 		}
 	default:
-		manifests, err := utils.ListManifestsBySelector(c.manifestLister, glob.Spec.Feed)
+		manifests, err := utils.ListManifestsBySelector(c.reservedNamespace, c.manifestLister, glob.Spec.Feed)
 		if err != nil {
 			return nil, err
 		}
diff --git a/pkg/controllers/apps/localization/localization.go b/pkg/controllers/apps/localization/localization.go
index 2c358fa..505a43f 100644
--- a/pkg/controllers/apps/localization/localization.go
+++ b/pkg/controllers/apps/localization/localization.go
@@ -73,27 +73,31 @@ type Controller struct {
 	recorder record.EventRecorder
 
 	syncHandlerFunc SyncHandlerFunc
+
+	// namespace where Manifests are created
+	reservedNamespace string
 }
 
 func NewController(clusternetClient clusternetclientset.Interface,
 	locInformer appinformers.LocalizationInformer,
 	chartInformer appinformers.HelmChartInformer, manifestInformer appinformers.ManifestInformer,
-	recorder record.EventRecorder, syncHandlerFunc SyncHandlerFunc) (*Controller, error) {
+	recorder record.EventRecorder, syncHandlerFunc SyncHandlerFunc, reservedNamespace string) (*Controller, error) {
 	if syncHandlerFunc == nil {
 		return nil, fmt.Errorf("syncHandlerFunc must be set")
 	}
 
 	c := &Controller{
-		clusternetClient: clusternetClient,
-		workqueue:        workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "localization"),
-		locLister:        locInformer.Lister(),
-		locSynced:        locInformer.Informer().HasSynced,
-		chartLister:      chartInformer.Lister(),
-		chartSynced:      chartInformer.Informer().HasSynced,
-		manifestLister:   manifestInformer.Lister(),
-		manifestSynced:   manifestInformer.Informer().HasSynced,
-		recorder:         recorder,
-		syncHandlerFunc:  syncHandlerFunc,
+		clusternetClient:  clusternetClient,
+		workqueue:         workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "localization"),
+		locLister:         locInformer.Lister(),
+		locSynced:         locInformer.Informer().HasSynced,
+		chartLister:       chartInformer.Lister(),
+		chartSynced:       chartInformer.Informer().HasSynced,
+		manifestLister:    manifestInformer.Lister(),
+		manifestSynced:    manifestInformer.Informer().HasSynced,
+		recorder:          recorder,
+		syncHandlerFunc:   syncHandlerFunc,
+		reservedNamespace: reservedNamespace,
 	}
 
 	// Manage the addition/update of Localization
@@ -332,7 +336,7 @@ func (c *Controller) getLabelsForPatching(loc *appsapi.Localization) (map[string
 			labelsToPatch[string(chart.UID)] = &chartKind.Kind
 		}
 	default:
-		manifests, err := utils.ListManifestsBySelector(c.manifestLister, loc.Spec.Feed)
+		manifests, err := utils.ListManifestsBySelector(c.reservedNamespace, c.manifestLister, loc.Spec.Feed)
 		if err != nil {
 			return nil, err
 		}
diff --git a/pkg/controllers/apps/manifest/manifest.go b/pkg/controllers/apps/manifest/manifest.go
index ac991c6..52e7e31 100644
--- a/pkg/controllers/apps/manifest/manifest.go
+++ b/pkg/controllers/apps/manifest/manifest.go
@@ -66,11 +66,15 @@ type Controller struct {
 
 	recorder        record.EventRecorder
 	syncHandlerFunc SyncHandlerFunc
+
+	// namespace where Manifests are created
+	reservedNamespace string
 }
 
 func NewController(clusternetClient clusternetclientset.Interface,
 	manifestInformer appinformers.ManifestInformer, baseInformer appinformers.BaseInformer,
-	feedInUseProtection bool, recorder record.EventRecorder, syncHandlerFunc SyncHandlerFunc) (*Controller, error) {
+	feedInUseProtection bool, recorder record.EventRecorder, syncHandlerFunc SyncHandlerFunc,
+	reservedNamespace string) (*Controller, error) {
 	if syncHandlerFunc == nil {
 		return nil, fmt.Errorf("syncHandlerFunc must be set")
 	}
@@ -85,6 +89,7 @@ func NewController(clusternetClient clusternetclientset.Interface,
 		feedInUseProtection: feedInUseProtection,
 		recorder:            recorder,
 		syncHandlerFunc:     syncHandlerFunc,
+		reservedNamespace:   reservedNamespace,
 	}
 
 	// Manage the addition/update of Manifest
@@ -193,7 +198,7 @@ func (c *Controller) updateBase(old, cur interface{}) {
 			continue
 		}
 
-		manifests, err := utils.ListManifestsBySelector(c.manifestLister, feed)
+		manifests, err := utils.ListManifestsBySelector(c.reservedNamespace, c.manifestLister, feed)
 		if err != nil {
 			klog.ErrorDepth(6, err)
 			continue
diff --git a/pkg/hub/apiserver/apiserver.go b/pkg/hub/apiserver/apiserver.go
index 577800e..8c5e503 100644
--- a/pkg/hub/apiserver/apiserver.go
+++ b/pkg/hub/apiserver/apiserver.go
@@ -117,7 +117,8 @@ func (cfg *Config) Complete() CompletedConfig {
 func (c completedConfig) New(tunnelLogging, socketConnection bool, extraHeaderPrefixes []string,
 	kubeclient *kubernetes.Clientset, clusternetclient *clusternet.Clientset,
 	clusternetInformerFactory informers.SharedInformerFactory,
-	clientBuilder clientbuilder.ControllerClientBuilder) (*HubAPIServer, error) {
+	clientBuilder clientbuilder.ControllerClientBuilder,
+	reservedNamespace string) (*HubAPIServer, error) {
 	genericServer, err := c.GenericConfig.New("clusternet-hub", genericapiserver.NewEmptyDelegate())
 	if err != nil {
 		return nil, err
@@ -162,7 +163,8 @@ func (c completedConfig) New(tunnelLogging, socketConnection bool, extraHeaderPr
 				kubeclient.RESTClient(),
 				clusternetclient,
 				clusternetInformerFactory.Apps().V1alpha1().Manifests().Lister(),
-				crdInformerFactory)
+				crdInformerFactory,
+				reservedNamespace)
 			crdInformerFactory.Start(context.StopCh)
 			return ss.InstallShadowAPIGroups(context.StopCh, kubeclient.DiscoveryClient)
 		}
diff --git a/pkg/hub/apiserver/shadow/apiserver.go b/pkg/hub/apiserver/shadow/apiserver.go
index e8833d3..c128aa9 100644
--- a/pkg/hub/apiserver/shadow/apiserver.go
+++ b/pkg/hub/apiserver/shadow/apiserver.go
@@ -110,6 +110,9 @@ type ShadowAPIServer struct {
 	crdLister      apiextensionsv1lister.CustomResourceDefinitionLister
 	crdSynced      cache.InformerSynced
 	crdHandler     *crdHandler
+
+	// namespace where Manifests are created
+	reservedNamespace string
 }
 
 func NewShadowAPIServer(apiserver *genericapiserver.GenericAPIServer,
@@ -117,7 +120,8 @@ func NewShadowAPIServer(apiserver *genericapiserver.GenericAPIServer,
 	admissionControl admission.Interface,
 	kubeRESTClient restclient.Interface, clusternetclient *clusternet.Clientset,
 	manifestLister applisters.ManifestLister,
-	crdInformerFactory crdinformers.SharedInformerFactory) *ShadowAPIServer {
+	crdInformerFactory crdinformers.SharedInformerFactory,
+	reservedNamespace string) *ShadowAPIServer {
 	return &ShadowAPIServer{
 		GenericAPIServer:    apiserver,
 		maxRequestBodyBytes: maxRequestBodyBytes,
@@ -131,7 +135,8 @@ func NewShadowAPIServer(apiserver *genericapiserver.GenericAPIServer,
 		crdHandler: NewCRDHandler(
 			kubeRESTClient, clusternetclient, manifestLister,
 			crdInformerFactory.Apiextensions().V1().CustomResourceDefinitions(),
-			minRequestTimeout, maxRequestBodyBytes, admissionControl, apiserver.Authorizer, apiserver.Serializer),
+			minRequestTimeout, maxRequestBodyBytes, admissionControl, apiserver.Authorizer, apiserver.Serializer, reservedNamespace),
+		reservedNamespace: reservedNamespace,
 	}
 }
 
@@ -188,7 +193,7 @@ func (ss *ShadowAPIServer) InstallShadowAPIGroups(stopCh <-chan struct{}, cl dis
 			Scheme.AddKnownTypeWithName(schema.GroupVersion{Group: apiGroupResource.Group.Name, Version: apiresource.Version}.WithKind(apiresource.Kind),
 				&unstructured.Unstructured{},
 			)
-			resourceRest := template.NewREST(ss.kubeRESTClient, ss.clusternetclient, ParameterCodec, ss.manifestLister)
+			resourceRest := template.NewREST(ss.kubeRESTClient, ss.clusternetclient, ParameterCodec, ss.manifestLister, ss.reservedNamespace)
 			resourceRest.SetNamespaceScoped(apiresource.Namespaced)
 			resourceRest.SetName(apiresource.Name)
 			resourceRest.SetShortNames(apiresource.ShortNames)
diff --git a/pkg/hub/apiserver/shadow/customresource_handler.go b/pkg/hub/apiserver/shadow/customresource_handler.go
index e5e509c..5539756 100644
--- a/pkg/hub/apiserver/shadow/customresource_handler.go
+++ b/pkg/hub/apiserver/shadow/customresource_handler.go
@@ -83,12 +83,16 @@ type crdHandler struct {
 	requestScopes           map[string]*handlers.RequestScope
 	versionDiscoveryHandler *versionDiscoveryHandler
 	nonCRDAPIResources      []metav1.APIResource
+
+	// namespace where Manifests are created
+	reservedNamespace string
 }
 
 func NewCRDHandler(kubeRESTClient restclient.Interface, clusternetClient *clusternet.Clientset,
 	manifestLister applisters.ManifestLister, crdInformer apiextensionsinformers.CustomResourceDefinitionInformer,
 	minRequestTimeout int, maxRequestBodyBytes int64,
-	admissionControl admission.Interface, authorizer authorizer.Authorizer, serializer runtime.NegotiatedSerializer) *crdHandler {
+	admissionControl admission.Interface, authorizer authorizer.Authorizer, serializer runtime.NegotiatedSerializer,
+	reservedNamespace string) *crdHandler {
 	r := &crdHandler{
 		rootPrefix:          path.Join(genericapiserver.APIGroupPrefix, shadowapi.SchemeGroupVersion.String()),
 		kubeRESTClient:      kubeRESTClient,
@@ -102,6 +106,7 @@ func NewCRDHandler(kubeRESTClient restclient.Interface, clusternetClient *cluste
 		serializer:          serializer,
 		storages:            map[string]*template.REST{},
 		requestScopes:       map[string]*handlers.RequestScope{},
+		reservedNamespace:   reservedNamespace,
 	}
 	return r
 }
@@ -290,7 +295,7 @@ func (r *crdHandler) addStorage(crd *apiextensionsv1.CustomResourceDefinition) e
 		selfLinkPrefix = "/" + path.Join("apis", shadowapi.GroupName, shadowapi.SchemeGroupVersion.Version, "namespaces") + "/"
 	}
 
-	restStorage := template.NewREST(r.kubeRESTClient, r.clusternetClient, ParameterCodec, r.manifestLister)
+	restStorage := template.NewREST(r.kubeRESTClient, r.clusternetClient, ParameterCodec, r.manifestLister, r.reservedNamespace)
 	restStorage.SetNamespaceScoped(crd.Spec.Scope == apiextensionsv1.NamespaceScoped)
 	restStorage.SetName(resource)
 	restStorage.SetShortNames(crd.Spec.Names.ShortNames)
diff --git a/pkg/hub/deployer/deployer.go b/pkg/hub/deployer/deployer.go
index 8b86a64..bf62591 100644
--- a/pkg/hub/deployer/deployer.go
+++ b/pkg/hub/deployer/deployer.go
@@ -104,31 +104,35 @@ type Deployer struct {
 
 	// apiserver url of parent cluster
 	apiserverURL string
+
+	// namespace where Manifests are created
+	reservedNamespace string
 }
 
-func NewDeployer(apiserverURL, systemNamespace string,
+func NewDeployer(apiserverURL, systemNamespace, reservedNamespace string,
 	kubeclient *kubernetes.Clientset, clusternetclient *clusternetclientset.Clientset,
 	clusternetInformerFactory clusternetinformers.SharedInformerFactory, kubeInformerFactory kubeinformers.SharedInformerFactory,
 	recorder record.EventRecorder, anonymousAuthSupported bool) (*Deployer, error) {
 	feedInUseProtection := utilfeature.DefaultFeatureGate.Enabled(features.FeedInUseProtection)
 
 	deployer := &Deployer{
-		apiserverURL:     apiserverURL,
-		chartLister:      clusternetInformerFactory.Apps().V1alpha1().HelmCharts().Lister(),
-		chartSynced:      clusternetInformerFactory.Apps().V1alpha1().HelmCharts().Informer().HasSynced,
-		descLister:       clusternetInformerFactory.Apps().V1alpha1().Descriptions().Lister(),
-		descSynced:       clusternetInformerFactory.Apps().V1alpha1().Descriptions().Informer().HasSynced,
-		baseLister:       clusternetInformerFactory.Apps().V1alpha1().Bases().Lister(),
-		baseSynced:       clusternetInformerFactory.Apps().V1alpha1().Bases().Informer().HasSynced,
-		mfstLister:       clusternetInformerFactory.Apps().V1alpha1().Manifests().Lister(),
-		mfstSynced:       clusternetInformerFactory.Apps().V1alpha1().Manifests().Informer().HasSynced,
-		subLister:        clusternetInformerFactory.Apps().V1alpha1().Subscriptions().Lister(),
-		subSynced:        clusternetInformerFactory.Apps().V1alpha1().Subscriptions().Informer().HasSynced,
-		clusterLister:    clusternetInformerFactory.Clusters().V1beta1().ManagedClusters().Lister(),
-		clusterSynced:    clusternetInformerFactory.Clusters().V1beta1().ManagedClusters().Informer().HasSynced,
-		clusternetClient: clusternetclient,
-		kubeClient:       kubeclient,
-		recorder:         recorder,
+		apiserverURL:      apiserverURL,
+		reservedNamespace: reservedNamespace,
+		chartLister:       clusternetInformerFactory.Apps().V1alpha1().HelmCharts().Lister(),
+		chartSynced:       clusternetInformerFactory.Apps().V1alpha1().HelmCharts().Informer().HasSynced,
+		descLister:        clusternetInformerFactory.Apps().V1alpha1().Descriptions().Lister(),
+		descSynced:        clusternetInformerFactory.Apps().V1alpha1().Descriptions().Informer().HasSynced,
+		baseLister:        clusternetInformerFactory.Apps().V1alpha1().Bases().Lister(),
+		baseSynced:        clusternetInformerFactory.Apps().V1alpha1().Bases().Informer().HasSynced,
+		mfstLister:        clusternetInformerFactory.Apps().V1alpha1().Manifests().Lister(),
+		mfstSynced:        clusternetInformerFactory.Apps().V1alpha1().Manifests().Informer().HasSynced,
+		subLister:         clusternetInformerFactory.Apps().V1alpha1().Subscriptions().Lister(),
+		subSynced:         clusternetInformerFactory.Apps().V1alpha1().Subscriptions().Informer().HasSynced,
+		clusterLister:     clusternetInformerFactory.Clusters().V1beta1().ManagedClusters().Lister(),
+		clusterSynced:     clusternetInformerFactory.Clusters().V1beta1().ManagedClusters().Informer().HasSynced,
+		clusternetClient:  clusternetclient,
+		kubeClient:        kubeclient,
+		recorder:          recorder,
 	}
 
 	helmChartController, err := helmchart.NewController(clusternetclient,
@@ -173,7 +177,8 @@ func NewDeployer(apiserverURL, systemNamespace string,
 		clusternetInformerFactory.Apps().V1alpha1().Bases(),
 		feedInUseProtection,
 		deployer.recorder,
-		deployer.handleManifest)
+		deployer.handleManifest,
+		reservedNamespace)
 	if err != nil {
 		return nil, err
 	}
@@ -190,7 +195,7 @@ func NewDeployer(apiserverURL, systemNamespace string,
 	deployer.baseController = baseController
 
 	l, err := localizer.NewLocalizer(clusternetclient, clusternetInformerFactory,
-		deployer.handleHelmChart, deployer.handleManifest, deployer.recorder)
+		deployer.handleHelmChart, deployer.handleManifest, deployer.recorder, reservedNamespace)
 	if err != nil {
 		return nil, err
 	}
@@ -528,7 +533,7 @@ func (deployer *Deployer) populateDescriptions(base *appsapi.Base) error {
 				})
 			}
 		default:
-			manifests, err = utils.ListManifestsBySelector(deployer.mfstLister, feed)
+			manifests, err = utils.ListManifestsBySelector(deployer.reservedNamespace, deployer.mfstLister, feed)
 			if err != nil {
 				break
 			}
@@ -879,7 +884,7 @@ func (deployer *Deployer) addLabelsToReferredFeeds(b *appsapi.Base) error {
 				allErrs = append(allErrs, err)
 			}
 		default:
-			manifests, err := utils.ListManifestsBySelector(deployer.mfstLister, feed)
+			manifests, err := utils.ListManifestsBySelector(deployer.reservedNamespace, deployer.mfstLister, feed)
 			if err == nil {
 				allManifests = append(allManifests, manifests...)
 			} else {
diff --git a/pkg/hub/hub.go b/pkg/hub/hub.go
index 29281a0..c41b196 100644
--- a/pkg/hub/hub.go
+++ b/pkg/hub/hub.go
@@ -112,7 +112,7 @@ func NewHub(opts *options.HubServerOptions) (*Hub, error) {
 
 	var d *deployer.Deployer
 	if deployerEnabled {
-		d, err = deployer.NewDeployer(config.Host, opts.LeaderElection.ResourceNamespace,
+		d, err = deployer.NewDeployer(config.Host, opts.LeaderElection.ResourceNamespace, opts.ReservedNamespace,
 			kubeClient, clusternetClient, clusternetInformerFactory, kubeInformerFactory,
 			recorder, opts.AnonymousAuthSupported)
 		if err != nil {
@@ -154,7 +154,8 @@ func (hub *Hub) Run(ctx context.Context) error {
 		hub.kubeClient,
 		hub.clusternetClient,
 		hub.clusternetInformerFactory,
-		hub.clientBuilder)
+		hub.clientBuilder,
+		hub.options.ReservedNamespace)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/hub/localizer/localizer.go b/pkg/hub/localizer/localizer.go
index 6c86099..3ad2116 100644
--- a/pkg/hub/localizer/localizer.go
+++ b/pkg/hub/localizer/localizer.go
@@ -68,26 +68,30 @@ type Localizer struct {
 	manifestCallback func(*appsapi.Manifest) error
 
 	recorder record.EventRecorder
+
+	// namespace where Manifests are created
+	reservedNamespace string
 }
 
 func NewLocalizer(clusternetClient *clusternetclientset.Clientset,
 	clusternetInformerFactory clusternetinformers.SharedInformerFactory,
 	chartCallback func(*appsapi.HelmChart) error, manifestCallback func(*appsapi.Manifest) error,
-	recorder record.EventRecorder) (*Localizer, error) {
+	recorder record.EventRecorder, reservedNamespace string) (*Localizer, error) {
 
 	localizer := &Localizer{
-		clusternetClient: clusternetClient,
-		locLister:        clusternetInformerFactory.Apps().V1alpha1().Localizations().Lister(),
-		locSynced:        clusternetInformerFactory.Apps().V1alpha1().Localizations().Informer().HasSynced,
-		globLister:       clusternetInformerFactory.Apps().V1alpha1().Globalizations().Lister(),
-		globSynced:       clusternetInformerFactory.Apps().V1alpha1().Globalizations().Informer().HasSynced,
-		chartLister:      clusternetInformerFactory.Apps().V1alpha1().HelmCharts().Lister(),
-		chartSynced:      clusternetInformerFactory.Apps().V1alpha1().HelmCharts().Informer().HasSynced,
-		manifestLister:   clusternetInformerFactory.Apps().V1alpha1().Manifests().Lister(),
-		manifestSynced:   clusternetInformerFactory.Apps().V1alpha1().Manifests().Informer().HasSynced,
-		chartCallback:    chartCallback,
-		manifestCallback: manifestCallback,
-		recorder:         recorder,
+		clusternetClient:  clusternetClient,
+		locLister:         clusternetInformerFactory.Apps().V1alpha1().Localizations().Lister(),
+		locSynced:         clusternetInformerFactory.Apps().V1alpha1().Localizations().Informer().HasSynced,
+		globLister:        clusternetInformerFactory.Apps().V1alpha1().Globalizations().Lister(),
+		globSynced:        clusternetInformerFactory.Apps().V1alpha1().Globalizations().Informer().HasSynced,
+		chartLister:       clusternetInformerFactory.Apps().V1alpha1().HelmCharts().Lister(),
+		chartSynced:       clusternetInformerFactory.Apps().V1alpha1().HelmCharts().Informer().HasSynced,
+		manifestLister:    clusternetInformerFactory.Apps().V1alpha1().Manifests().Lister(),
+		manifestSynced:    clusternetInformerFactory.Apps().V1alpha1().Manifests().Informer().HasSynced,
+		chartCallback:     chartCallback,
+		manifestCallback:  manifestCallback,
+		recorder:          recorder,
+		reservedNamespace: reservedNamespace,
 	}
 
 	locController, err := localization.NewController(clusternetClient,
@@ -95,7 +99,8 @@ func NewLocalizer(clusternetClient *clusternetclientset.Clientset,
 		clusternetInformerFactory.Apps().V1alpha1().HelmCharts(),
 		clusternetInformerFactory.Apps().V1alpha1().Manifests(),
 		recorder,
-		localizer.handleLocalization)
+		localizer.handleLocalization,
+		reservedNamespace)
 	if err != nil {
 		return nil, err
 	}
@@ -106,7 +111,8 @@ func NewLocalizer(clusternetClient *clusternetclientset.Clientset,
 		clusternetInformerFactory.Apps().V1alpha1().HelmCharts(),
 		clusternetInformerFactory.Apps().V1alpha1().Manifests(),
 		recorder,
-		localizer.handleGlobalization)
+		localizer.handleGlobalization,
+		reservedNamespace)
 	if err != nil {
 		return nil, err
 	}
@@ -155,7 +161,7 @@ func (l *Localizer) handleLocalization(loc *appsapi.Localization) error {
 			}
 			break
 		}
-		manifests, err := utils.ListManifestsBySelector(l.manifestLister, loc.Spec.Feed)
+		manifests, err := utils.ListManifestsBySelector(l.reservedNamespace, l.manifestLister, loc.Spec.Feed)
 		if err != nil {
 			return err
 		}
@@ -211,7 +217,7 @@ func (l *Localizer) handleGlobalization(glob *appsapi.Globalization) error {
 			}
 			break
 		}
-		manifests, err := utils.ListManifestsBySelector(l.manifestLister, glob.Spec.Feed)
+		manifests, err := utils.ListManifestsBySelector(l.reservedNamespace, l.manifestLister, glob.Spec.Feed)
 		if err != nil {
 			return err
 		}
@@ -315,7 +321,7 @@ func (l *Localizer) getOverrides(namespace string, feed appsapi.Feed) ([]appsapi
 		}
 		uid = chart.UID
 	default:
-		manifests, err := utils.ListManifestsBySelector(l.manifestLister, feed)
+		manifests, err := utils.ListManifestsBySelector(l.reservedNamespace, l.manifestLister, feed)
 		if err != nil {
 			return nil, err
 		}
diff --git a/pkg/hub/options/options.go b/pkg/hub/options/options.go
index 053e7c0..130aeb9 100644
--- a/pkg/hub/options/options.go
+++ b/pkg/hub/options/options.go
@@ -64,6 +64,10 @@ type HubServerOptions struct {
 	// If not, serviceaccount "clusternet-hub-proxy" will be used instead.
 	AnonymousAuthSupported bool
 
+	// default namespace to create Manifest in
+	// default to be "clusternet-reserved"
+	ReservedNamespace string
+
 	RecommendedOptions *genericoptions.RecommendedOptions
 
 	LoopbackSharedInformerFactory informers.SharedInformerFactory
@@ -83,6 +87,7 @@ func NewHubServerOptions() (*HubServerOptions, error) {
 	return &HubServerOptions{
 		RecommendedOptions:     genericoptions.NewRecommendedOptions("fake", nil),
 		AnonymousAuthSupported: true,
+		ReservedNamespace:      known.ClusternetReservedNamespace,
 		ControllerOptions:      controllerOpts,
 	}, nil
 }
@@ -155,6 +160,7 @@ func (o *HubServerOptions) AddFlags(fs *pflag.FlagSet) {
 
 	fs.BoolVar(&o.TunnelLogging, "enable-tunnel-logging", o.TunnelLogging, "Enable tunnel logging")
 	fs.BoolVar(&o.AnonymousAuthSupported, "anonymous-auth-supported", o.AnonymousAuthSupported, "Whether the anonymous access is allowed by the 'core' kubernetes server")
+	fs.StringVar(&o.ReservedNamespace, "reserved-namespace", o.ReservedNamespace, "The default namespace to create Manifest in")
 }
 
 func (o *HubServerOptions) addRecommendedOptionsFlags(fs *pflag.FlagSet) {
diff --git a/pkg/known/constant.go b/pkg/known/constant.go
index ad1c7e2..538e005 100644
--- a/pkg/known/constant.go
+++ b/pkg/known/constant.go
@@ -36,6 +36,9 @@ const (
 	// This could be re-configured with flag "--leader-elect-resource-namespace"
 	ClusternetSystemNamespace = "clusternet-system"
 
+	// ClusternetReservedNamespace is the default namespace to store Manifest into
+	ClusternetReservedNamespace = "clusternet-reserved"
+
 	// ClusternetAppSA is the service account where we store credentials to deploy resources
 	ClusternetAppSA = "clusternet-app-deployer"
 
diff --git a/pkg/registry/shadow/template/rest.go b/pkg/registry/shadow/template/rest.go
index 31c746e..d0212d4 100644
--- a/pkg/registry/shadow/template/rest.go
+++ b/pkg/registry/shadow/template/rest.go
@@ -83,6 +83,9 @@ type REST struct {
 	// DeleteCollection call. Delete requests for the items in a collection
 	// are issued in parallel.
 	deleteCollectionWorkers int
+
+	// namespace where Manifests are created
+	reservedNamespace string
 }
 
 // Create inserts a new item into Manifest according to the unique key from the object.
@@ -97,7 +100,7 @@ func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation
 	manifest := &appsapi.Manifest{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      r.getNormalizedManifestName(result.GetNamespace(), result.GetName()),
-			Namespace: appsapi.ReservedNamespace,
+			Namespace: r.reservedNamespace,
 			Labels:    result.GetLabels(), // reuse labels from original object, which is useful for label selector
 		},
 		Template: runtime.RawExtension{
@@ -128,9 +131,9 @@ func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions)
 	var manifest *appsapi.Manifest
 	var err error
 	if len(options.ResourceVersion) == 0 {
-		manifest, err = r.manifestLister.Manifests(appsapi.ReservedNamespace).Get(r.getNormalizedManifestName(request.NamespaceValue(ctx), name))
+		manifest, err = r.manifestLister.Manifests(r.reservedNamespace).Get(r.getNormalizedManifestName(request.NamespaceValue(ctx), name))
 	} else {
-		manifest, err = r.clusternetClient.AppsV1alpha1().Manifests(appsapi.ReservedNamespace).
+		manifest, err = r.clusternetClient.AppsV1alpha1().Manifests(r.reservedNamespace).
 			Get(ctx, r.getNormalizedManifestName(request.NamespaceValue(ctx), name), *options)
 	}
 	if err != nil {
@@ -160,7 +163,7 @@ func (r *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObje
 		return nil, false, err
 	}
 
-	manifest, err := r.manifestLister.Manifests(appsapi.ReservedNamespace).Get(r.getNormalizedManifestName(request.NamespaceValue(ctx), name))
+	manifest, err := r.manifestLister.Manifests(r.reservedNamespace).Get(r.getNormalizedManifestName(request.NamespaceValue(ctx), name))
 	if err != nil {
 		if errors.IsNotFound(err) {
 			return nil, false, errors.NewNotFound(schema.GroupResource{Group: r.group, Resource: r.name}, name)
@@ -204,7 +207,7 @@ func (r *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObje
 	manifestCopy.Template.Reset()
 	manifestCopy.Template.Object = result
 	// save the updates
-	manifestCopy, err = r.clusternetClient.AppsV1alpha1().Manifests(appsapi.ReservedNamespace).Update(ctx, manifestCopy, *options)
+	manifestCopy, err = r.clusternetClient.AppsV1alpha1().Manifests(r.reservedNamespace).Update(ctx, manifestCopy, *options)
 	if err != nil {
 		return nil, false, err
 	}
@@ -216,7 +219,7 @@ func (r *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObje
 // Delete removes the item from storage.
 // options can be mutated by rest.BeforeDelete due to a graceful deletion strategy.
 func (r *REST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
-	err := r.clusternetClient.AppsV1alpha1().Manifests(appsapi.ReservedNamespace).
+	err := r.clusternetClient.AppsV1alpha1().Manifests(r.reservedNamespace).
 		Delete(ctx, r.getNormalizedManifestName(request.NamespaceValue(ctx), name), *options)
 	if err != nil {
 		if errors.IsNotFound(err) {
@@ -321,7 +324,7 @@ func (r *REST) Watch(ctx context.Context, options *internalversion.ListOptions)
 	}
 
 	klog.V(5).Infof("%v", label)
-	watcher, err := r.clusternetClient.AppsV1alpha1().Manifests(appsapi.ReservedNamespace).Watch(ctx, metav1.ListOptions{
+	watcher, err := r.clusternetClient.AppsV1alpha1().Manifests(r.reservedNamespace).Watch(ctx, metav1.ListOptions{
 		LabelSelector:        label.String(),
 		FieldSelector:        "", // explicitly set FieldSelector to an empty string
 		Watch:                options.Watch,
@@ -362,7 +365,7 @@ func (r *REST) List(ctx context.Context, options *internalversion.ListOptions) (
 		return nil, err
 	}
 
-	manifests, err := r.clusternetClient.AppsV1alpha1().Manifests(appsapi.ReservedNamespace).List(ctx, metav1.ListOptions{
+	manifests, err := r.clusternetClient.AppsV1alpha1().Manifests(r.reservedNamespace).List(ctx, metav1.ListOptions{
 		LabelSelector:        label.String(),
 		FieldSelector:        "", // explicitly set FieldSelector to an empty string
 		Watch:                options.Watch,
@@ -483,7 +486,7 @@ func (r *REST) normalizeRequest(req *clientgorest.Request, namespace string) *cl
 func (r *REST) getNormalizedManifestName(namespace, name string) string {
 	legacyManifestName := r.generateLegacyNameForManifest(namespace, name)
 	// backward compatible
-	_, err := r.manifestLister.Manifests(appsapi.ReservedNamespace).Get(legacyManifestName)
+	_, err := r.manifestLister.Manifests(r.reservedNamespace).Get(legacyManifestName)
 	if err == nil {
 		return legacyManifestName
 	}
@@ -543,10 +546,10 @@ func (r *REST) dryRunCreate(ctx context.Context, obj runtime.Object, _ rest.Vali
 	u.SetLabels(labels)
 
 	if r.kind != "Namespace" && r.namespaced {
-		u.SetNamespace(appsapi.ReservedNamespace)
+		u.SetNamespace(r.reservedNamespace)
 	}
-	// use reserved namespace "clusternet-reserved" to avoid error "namespaces not found"
-	dryRunNamespace := appsapi.ReservedNamespace
+	// use reserved namespace (default to be "clusternet-reserved") to avoid error "namespaces not found"
+	dryRunNamespace := r.reservedNamespace
 	if r.kind == "Namespace" {
 		dryRunNamespace = ""
 	}
@@ -654,15 +657,14 @@ func (r *REST) getListKind() string {
 
 // NewREST returns a RESTStorage object that will work against API services.
 func NewREST(dryRunClient clientgorest.Interface, clusternetclient *clusternet.Clientset, parameterCodec runtime.ParameterCodec,
-	manifestLister applisters.ManifestLister) *REST {
+	manifestLister applisters.ManifestLister, reservedNamespace string) *REST {
 	return &REST{
-		dryRunClient:     dryRunClient,
-		clusternetClient: clusternetclient,
-		manifestLister:   manifestLister,
-		parameterCodec:   parameterCodec,
-		// currently we only set a default value for deleteCollectionWorkers
-		// TODO: make it configurable?
-		deleteCollectionWorkers: DefaultDeleteCollectionWorkers,
+		dryRunClient:            dryRunClient,
+		clusternetClient:        clusternetclient,
+		manifestLister:          manifestLister,
+		parameterCodec:          parameterCodec,
+		deleteCollectionWorkers: DefaultDeleteCollectionWorkers, // currently we only set a default value for deleteCollectionWorkers
+		reservedNamespace:       reservedNamespace,
 	}
 }
 
diff --git a/pkg/utils/feed.go b/pkg/utils/feed.go
index 5ecb5e0..5463bfe 100644
--- a/pkg/utils/feed.go
+++ b/pkg/utils/feed.go
@@ -63,7 +63,7 @@ func GetLabelsSelectorFromFeed(feed appsapi.Feed) (labels.Selector, error) {
 	return selector, nil
 }
 
-func ListManifestsBySelector(manifestLister applisters.ManifestLister, feed appsapi.Feed) ([]*appsapi.Manifest, error) {
+func ListManifestsBySelector(reservedNamespace string, manifestLister applisters.ManifestLister, feed appsapi.Feed) ([]*appsapi.Manifest, error) {
 	if manifestLister == nil {
 		return nil, errors.New("manifestLister is nil when listing charts by selector")
 	}
@@ -72,7 +72,7 @@ func ListManifestsBySelector(manifestLister applisters.ManifestLister, feed apps
 	if err != nil {
 		return nil, err
 	}
-	return manifestLister.Manifests(appsapi.ReservedNamespace).List(selector)
+	return manifestLister.Manifests(reservedNamespace).List(selector)
 }
 
 func FormatFeed(feed appsapi.Feed) string {


Test patch:
diff --git a/pkg/registry/shadow/template/transformer_test.go b/pkg/registry/shadow/template/transformer_test.go
index 493ae5f..d15c30b 100644
--- a/pkg/registry/shadow/template/transformer_test.go
+++ b/pkg/registry/shadow/template/transformer_test.go
@@ -49,7 +49,7 @@ func TestTransformManifest(t *testing.T) {
 						known.ConfigNameLabel:      "boo",
 						known.ConfigNamespaceLabel: "ns1",
 					},
-					Namespace:       appsapi.ReservedNamespace,
+					Namespace:       known.ClusternetReservedNamespace,
 					Name:            "bars-boo",
 					ResourceVersion: "1860247",
 					UID:             "13ff776c-1e91-4a84-b77d-6c35f3a52fed",