Install remote Knative components in canonical namespaces - #2349

Merged
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces
Aug 27, 2026
Merged

Install remote Knative components in canonical namespaces#2349
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces

Conversation

@kahirokunn

@kahirokunnkahirokunn commented Aug 26, 2026

Copy link
Copy Markdown
Member

Fixes#2345

Proposed Changes

  • Install remote KnativeServing and KnativeEventing components in their canonical spoke namespaces regardless of the hub management CR namespace.

Design

The component kind determines the remote installation namespace:

Management CR on the hubInstallation namespace on the spoke
KnativeServingknative-serving
KnativeEventingknative-eventing

Local installations without spec.clusterProfileRef continue to use the management CR metadata.namespace.

Verification Steps

E2E verification with Kind

Verified manually with three Kind clusters (hub, cluster1, and cluster2), OCM-managed ClusterProfile objects, the managed-serviceaccount credential syncer, cp-creds, and Envoy Gateway.

1. Set up OCM and ClusterProfile access

OCM was initialized on hub with ClusterProfile support. cluster1 and cluster2 were joined, accepted, assigned to the sandbox-fleet ManagedClusterSet, and bound to the knative-operator namespace. Both managed clusters reported Joined=True and Available=True; both ClusterProfiles reported ControlPlaneHealthy=True and Joined=True.

The managed-serviceaccount credential syncer and the cluster-proxy user server were enabled:

helm upgrade --install managed-serviceaccount ocm/managed-serviceaccount \
--kube-context kind-hub \
-n open-cluster-management-managed-serviceaccount \
--create-namespace \
--version 0.10.0 \
--set featureGates.clusterProfileCredSyncer=true
helm upgrade --install cluster-proxy ocm/cluster-proxy \
--kube-context kind-hub \
-n open-cluster-management-cluster-proxy \
--create-namespace \
--version 0.11.0 \
--set featureGates.clusterProfile=true \
--set userServer.enabled=true \
--set enableServiceProxy=true

The knative-operator ManagedServiceAccount was created for each spoke and labeled for ClusterProfile credential synchronization. The corresponding spoke service accounts were bound to cluster-admin for this installation test. The resulting cluster1-knative-operator and cluster2-knative-operator credentials appeared in the hub's knative-operator namespace, and both ClusterProfiles advertised the open-cluster-management access provider.

2. Configure the operator to use cp-creds

The operator mounted quay.io/open-cluster-management/cp-creds:latest at /access-plugins/cp-creds and used this provider configuration:

{
"providers": [
{
"name": "open-cluster-management",
"execConfig": {
"apiVersion": "client.authentication.k8s.io/v1",
"command": "/access-plugins/cp-creds/cp-creds",
"args": ["--managed-serviceaccount=knative-operator"],
"provideClusterInfo": true,
"interactiveMode": "Never"
}
}
]
}

3. Build and deploy this PR head

PATH="$(go env GOPATH)/bin:$PATH" \
KO_DOCKER_REPO=kind.local \
KIND_CLUSTER_NAME=hub \
ko apply -f config/
kubectl --context kind-hub -n knative-operator \
rollout status deployment/knative-operator --timeout=180s

Both referenced ClusterProfiles resolved through the configured cp-creds provider.

4. Configure the Serving data plane on cluster1

Gateway API v1.4.1 experimental CRDs and Envoy Gateway v1.7.1 were installed on cluster1 with the GatewayNamespace deployment model. The following data-plane resources were configured:

TrafficGateway namespace/nameEnvoy Service
Externaleg-external/eg-externaleg-external/knative-external
Cluster-localeg-internal/eg-internaleg-internal/knative-internal

Both GatewayClasses reported Accepted=True; the internal Gateway reported Programmed=True. Because this local environment did not expose a LoadBalancer address, external traffic was verified through the external Envoy Service's NodePort on the Kind Docker network.

5. Deploy Serving and Eventing from non-canonical hub namespaces

The management CR namespaces intentionally differed from the remote installation namespaces:

apiVersion: operator.knative.dev/v1beta1kind: KnativeServingmetadata:
name: ocm-servingnamespace: management-serving-hubspec:
clusterProfileRef:
name: cluster1namespace: knative-operatoringress:
gateway-api:
enabled: trueistio:
enabled: false
---
apiVersion: operator.knative.dev/v1beta1kind: KnativeEventingmetadata:
name: ocm-eventingnamespace: management-eventing-hubspec:
clusterProfileRef:
name: cluster2namespace: knative-operator

Both hub CRs reached TargetClusterResolved=True and Ready=True. All seven Serving Deployments became Available only in cluster1/knative-serving; Knative Eventing reached Ready=True with its controller, webhook, and broker Deployments only in cluster2/knative-eventing. No Knative Deployments appeared in either hub management namespace or on the wrong spoke.

The remote anchors were created as knativeserving-ocm-serving-root-owner and knativeeventing-ocm-eventing-root-owner, and managed resources referenced the matching anchor.

6. Verify Serving traffic

kubectl --context kind-cluster1 apply -f - <<'EOF'apiVersion: serving.knative.dev/v1kind: Servicemetadata: name: helloworld-go namespace: defaultspec: template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET value: OCM canonical namespace verificationEOF
kubectl --context kind-cluster1 -n default \
wait --for=condition=Ready kservice/helloworld-go --timeout=300s
NODE_PORT=$(kubectl --context kind-cluster1 -n eg-external \ get service knative-external \ -o jsonpath='{.spec.ports[?(@.port==80)].nodePort}')
docker run --rm --network kind curlimages/curl:8.15.0 \
-H 'Host: helloworld-go.default.example.com' \
"http://cluster1-control-plane:${NODE_PORT}"

Observed response:

Hello OCM canonical namespace verification!

The external and cluster-local HTTPRoutes both reported Accepted=True and ResolvedRefs=True.

7. Verify cleanup and recreation

Both management CRs were deleted:

kubectl --context kind-hub -n management-serving-hub \
delete knativeserving ocm-serving
kubectl --context kind-hub -n management-eventing-hub \
delete knativeeventing ocm-eventing

Both CRs completed finalization. Their anchors and all anchor-owned Deployments were removed from cluster1/knative-serving and cluster2/knative-eventing. After recreating the same management CRs, both returned to TargetClusterResolved=True and Ready=True, and the Serving request returned the same response again.

Upgrade note

Remote installations created before this change used the management CR namespace on the spoke. If an existing remote installation is not already in its canonical namespace:

  1. Delete the management CR with the old operator and wait for finalization to remove the spoke resources.
  2. Upgrade the operator.
  3. Recreate the same management CR on the hub.

The recreated installation is placed in knative-serving or knative-eventing. No migration is required when the existing remote installation already uses its canonical namespace.

Release Note

action required: Remote KnativeServing and KnativeEventing installations now use the canonical knative-serving and knative-eventing namespaces instead of the management CR namespace. Delete non-canonical remote installations with the old operator before upgrading, then recreate their management CRs.

@knative-prowknative-prowBot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 26, 2026
@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.95%. Comparing base (7f45332) to head (e31a55d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #2349 +/- ##
==========================================
+ Coverage 65.84% 65.95% +0.11% 
==========================================
Files 56 56 Lines 2673 2682 +9 ==========================================
+ Hits 1760 1769 +9 
Misses 777 777 Partials 136 136 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kahirokunn

Copy link
Copy Markdown
MemberAuthor

I plan to implement a separate validation rule to prevent the creation of more than one KnativeServing or KnativeEventing instance per cluster.

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from a1af5ee to fe54616CompareAugust 26, 2026 11:50
@kahirokunn

Copy link
Copy Markdown
MemberAuthor

@dsimansk Hi ✋ PTAL. Thank you 🙏

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from fe54616 to 7ce0e6fCompareAugust 27, 2026 02:23
Comment threadpkg/reconciler/common/multicluster.go
Comment threaddocs/multicluster.md
@knative-prow-robotknative-prow-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from 7ce0e6f to e31a55dCompareAugust 27, 2026 12:41
@knative-prow-robotknative-prow-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
@dsimansk

Copy link
Copy Markdown
Contributor

/approve
/lgtm

@knative-prowknative-prowBot added the lgtm Indicates that a PR is ready to be merged. label Aug 27, 2026
@knative-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dsimansk, kahirokunn

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prowknative-prowBot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 27, 2026
@knative-prow
knative-prowBot merged commit bd7bb89 into knative:mainAug 27, 2026
26 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by an approver from all required OWNERS files.lgtmIndicates that a PR is ready to be merged.size/LDenotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install remote Knative components in canonical namespaces

3 participants

@kahirokunn@dsimansk@knative-prow-robot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Install remote Knative components in canonical namespaces - #2349

Merged
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces
Aug 27, 2026
Merged

Install remote Knative components in canonical namespaces#2349
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces

Conversation

@kahirokunn

@kahirokunnkahirokunn commented Aug 26, 2026

Copy link
Copy Markdown
Member

Fixes#2345

Proposed Changes

  • Install remote KnativeServing and KnativeEventing components in their canonical spoke namespaces regardless of the hub management CR namespace.

Design

The component kind determines the remote installation namespace:

Management CR on the hubInstallation namespace on the spoke
KnativeServingknative-serving
KnativeEventingknative-eventing

Local installations without spec.clusterProfileRef continue to use the management CR metadata.namespace.

Verification Steps

E2E verification with Kind

Verified manually with three Kind clusters (hub, cluster1, and cluster2), OCM-managed ClusterProfile objects, the managed-serviceaccount credential syncer, cp-creds, and Envoy Gateway.

1. Set up OCM and ClusterProfile access

OCM was initialized on hub with ClusterProfile support. cluster1 and cluster2 were joined, accepted, assigned to the sandbox-fleet ManagedClusterSet, and bound to the knative-operator namespace. Both managed clusters reported Joined=True and Available=True; both ClusterProfiles reported ControlPlaneHealthy=True and Joined=True.

The managed-serviceaccount credential syncer and the cluster-proxy user server were enabled:

helm upgrade --install managed-serviceaccount ocm/managed-serviceaccount \
--kube-context kind-hub \
-n open-cluster-management-managed-serviceaccount \
--create-namespace \
--version 0.10.0 \
--set featureGates.clusterProfileCredSyncer=true
helm upgrade --install cluster-proxy ocm/cluster-proxy \
--kube-context kind-hub \
-n open-cluster-management-cluster-proxy \
--create-namespace \
--version 0.11.0 \
--set featureGates.clusterProfile=true \
--set userServer.enabled=true \
--set enableServiceProxy=true

The knative-operator ManagedServiceAccount was created for each spoke and labeled for ClusterProfile credential synchronization. The corresponding spoke service accounts were bound to cluster-admin for this installation test. The resulting cluster1-knative-operator and cluster2-knative-operator credentials appeared in the hub's knative-operator namespace, and both ClusterProfiles advertised the open-cluster-management access provider.

2. Configure the operator to use cp-creds

The operator mounted quay.io/open-cluster-management/cp-creds:latest at /access-plugins/cp-creds and used this provider configuration:

{
"providers": [
{
"name": "open-cluster-management",
"execConfig": {
"apiVersion": "client.authentication.k8s.io/v1",
"command": "/access-plugins/cp-creds/cp-creds",
"args": ["--managed-serviceaccount=knative-operator"],
"provideClusterInfo": true,
"interactiveMode": "Never"
}
}
]
}

3. Build and deploy this PR head

PATH="$(go env GOPATH)/bin:$PATH" \
KO_DOCKER_REPO=kind.local \
KIND_CLUSTER_NAME=hub \
ko apply -f config/
kubectl --context kind-hub -n knative-operator \
rollout status deployment/knative-operator --timeout=180s

Both referenced ClusterProfiles resolved through the configured cp-creds provider.

4. Configure the Serving data plane on cluster1

Gateway API v1.4.1 experimental CRDs and Envoy Gateway v1.7.1 were installed on cluster1 with the GatewayNamespace deployment model. The following data-plane resources were configured:

TrafficGateway namespace/nameEnvoy Service
Externaleg-external/eg-externaleg-external/knative-external
Cluster-localeg-internal/eg-internaleg-internal/knative-internal

Both GatewayClasses reported Accepted=True; the internal Gateway reported Programmed=True. Because this local environment did not expose a LoadBalancer address, external traffic was verified through the external Envoy Service's NodePort on the Kind Docker network.

5. Deploy Serving and Eventing from non-canonical hub namespaces

The management CR namespaces intentionally differed from the remote installation namespaces:

apiVersion: operator.knative.dev/v1beta1kind: KnativeServingmetadata:
name: ocm-servingnamespace: management-serving-hubspec:
clusterProfileRef:
name: cluster1namespace: knative-operatoringress:
gateway-api:
enabled: trueistio:
enabled: false
---
apiVersion: operator.knative.dev/v1beta1kind: KnativeEventingmetadata:
name: ocm-eventingnamespace: management-eventing-hubspec:
clusterProfileRef:
name: cluster2namespace: knative-operator

Both hub CRs reached TargetClusterResolved=True and Ready=True. All seven Serving Deployments became Available only in cluster1/knative-serving; Knative Eventing reached Ready=True with its controller, webhook, and broker Deployments only in cluster2/knative-eventing. No Knative Deployments appeared in either hub management namespace or on the wrong spoke.

The remote anchors were created as knativeserving-ocm-serving-root-owner and knativeeventing-ocm-eventing-root-owner, and managed resources referenced the matching anchor.

6. Verify Serving traffic

kubectl --context kind-cluster1 apply -f - <<'EOF'apiVersion: serving.knative.dev/v1kind: Servicemetadata: name: helloworld-go namespace: defaultspec: template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET value: OCM canonical namespace verificationEOF
kubectl --context kind-cluster1 -n default \
wait --for=condition=Ready kservice/helloworld-go --timeout=300s
NODE_PORT=$(kubectl --context kind-cluster1 -n eg-external \ get service knative-external \ -o jsonpath='{.spec.ports[?(@.port==80)].nodePort}')
docker run --rm --network kind curlimages/curl:8.15.0 \
-H 'Host: helloworld-go.default.example.com' \
"http://cluster1-control-plane:${NODE_PORT}"

Observed response:

Hello OCM canonical namespace verification!

The external and cluster-local HTTPRoutes both reported Accepted=True and ResolvedRefs=True.

7. Verify cleanup and recreation

Both management CRs were deleted:

kubectl --context kind-hub -n management-serving-hub \
delete knativeserving ocm-serving
kubectl --context kind-hub -n management-eventing-hub \
delete knativeeventing ocm-eventing

Both CRs completed finalization. Their anchors and all anchor-owned Deployments were removed from cluster1/knative-serving and cluster2/knative-eventing. After recreating the same management CRs, both returned to TargetClusterResolved=True and Ready=True, and the Serving request returned the same response again.

Upgrade note

Remote installations created before this change used the management CR namespace on the spoke. If an existing remote installation is not already in its canonical namespace:

  1. Delete the management CR with the old operator and wait for finalization to remove the spoke resources.
  2. Upgrade the operator.
  3. Recreate the same management CR on the hub.

The recreated installation is placed in knative-serving or knative-eventing. No migration is required when the existing remote installation already uses its canonical namespace.

Release Note

action required: Remote KnativeServing and KnativeEventing installations now use the canonical knative-serving and knative-eventing namespaces instead of the management CR namespace. Delete non-canonical remote installations with the old operator before upgrading, then recreate their management CRs.

@knative-prowknative-prowBot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 26, 2026
@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.95%. Comparing base (7f45332) to head (e31a55d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #2349 +/- ##
==========================================
+ Coverage 65.84% 65.95% +0.11% 
==========================================
Files 56 56 Lines 2673 2682 +9 ==========================================
+ Hits 1760 1769 +9 
Misses 777 777 Partials 136 136 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kahirokunn

Copy link
Copy Markdown
MemberAuthor

I plan to implement a separate validation rule to prevent the creation of more than one KnativeServing or KnativeEventing instance per cluster.

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from a1af5ee to fe54616CompareAugust 26, 2026 11:50
@kahirokunn

Copy link
Copy Markdown
MemberAuthor

@dsimansk Hi ✋ PTAL. Thank you 🙏

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from fe54616 to 7ce0e6fCompareAugust 27, 2026 02:23
Comment threadpkg/reconciler/common/multicluster.go
Comment threaddocs/multicluster.md
@knative-prow-robotknative-prow-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from 7ce0e6f to e31a55dCompareAugust 27, 2026 12:41
@knative-prow-robotknative-prow-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
@dsimansk

Copy link
Copy Markdown
Contributor

/approve
/lgtm

@knative-prowknative-prowBot added the lgtm Indicates that a PR is ready to be merged. label Aug 27, 2026
@knative-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dsimansk, kahirokunn

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prowknative-prowBot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 27, 2026
@knative-prow
knative-prowBot merged commit bd7bb89 into knative:mainAug 27, 2026
26 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by an approver from all required OWNERS files.lgtmIndicates that a PR is ready to be merged.size/LDenotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install remote Knative components in canonical namespaces

3 participants

@kahirokunn@dsimansk@knative-prow-robot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Install remote Knative components in canonical namespaces - #2349

Merged
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces
Aug 27, 2026
Merged

Install remote Knative components in canonical namespaces#2349
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces

Conversation

@kahirokunn

@kahirokunnkahirokunn commented Aug 26, 2026

Copy link
Copy Markdown
Member

Fixes#2345

Proposed Changes

  • Install remote KnativeServing and KnativeEventing components in their canonical spoke namespaces regardless of the hub management CR namespace.

Design

The component kind determines the remote installation namespace:

Management CR on the hubInstallation namespace on the spoke
KnativeServingknative-serving
KnativeEventingknative-eventing

Local installations without spec.clusterProfileRef continue to use the management CR metadata.namespace.

Verification Steps

E2E verification with Kind

Verified manually with three Kind clusters (hub, cluster1, and cluster2), OCM-managed ClusterProfile objects, the managed-serviceaccount credential syncer, cp-creds, and Envoy Gateway.

1. Set up OCM and ClusterProfile access

OCM was initialized on hub with ClusterProfile support. cluster1 and cluster2 were joined, accepted, assigned to the sandbox-fleet ManagedClusterSet, and bound to the knative-operator namespace. Both managed clusters reported Joined=True and Available=True; both ClusterProfiles reported ControlPlaneHealthy=True and Joined=True.

The managed-serviceaccount credential syncer and the cluster-proxy user server were enabled:

helm upgrade --install managed-serviceaccount ocm/managed-serviceaccount \
--kube-context kind-hub \
-n open-cluster-management-managed-serviceaccount \
--create-namespace \
--version 0.10.0 \
--set featureGates.clusterProfileCredSyncer=true
helm upgrade --install cluster-proxy ocm/cluster-proxy \
--kube-context kind-hub \
-n open-cluster-management-cluster-proxy \
--create-namespace \
--version 0.11.0 \
--set featureGates.clusterProfile=true \
--set userServer.enabled=true \
--set enableServiceProxy=true

The knative-operator ManagedServiceAccount was created for each spoke and labeled for ClusterProfile credential synchronization. The corresponding spoke service accounts were bound to cluster-admin for this installation test. The resulting cluster1-knative-operator and cluster2-knative-operator credentials appeared in the hub's knative-operator namespace, and both ClusterProfiles advertised the open-cluster-management access provider.

2. Configure the operator to use cp-creds

The operator mounted quay.io/open-cluster-management/cp-creds:latest at /access-plugins/cp-creds and used this provider configuration:

{
"providers": [
{
"name": "open-cluster-management",
"execConfig": {
"apiVersion": "client.authentication.k8s.io/v1",
"command": "/access-plugins/cp-creds/cp-creds",
"args": ["--managed-serviceaccount=knative-operator"],
"provideClusterInfo": true,
"interactiveMode": "Never"
}
}
]
}

3. Build and deploy this PR head

PATH="$(go env GOPATH)/bin:$PATH" \
KO_DOCKER_REPO=kind.local \
KIND_CLUSTER_NAME=hub \
ko apply -f config/
kubectl --context kind-hub -n knative-operator \
rollout status deployment/knative-operator --timeout=180s

Both referenced ClusterProfiles resolved through the configured cp-creds provider.

4. Configure the Serving data plane on cluster1

Gateway API v1.4.1 experimental CRDs and Envoy Gateway v1.7.1 were installed on cluster1 with the GatewayNamespace deployment model. The following data-plane resources were configured:

TrafficGateway namespace/nameEnvoy Service
Externaleg-external/eg-externaleg-external/knative-external
Cluster-localeg-internal/eg-internaleg-internal/knative-internal

Both GatewayClasses reported Accepted=True; the internal Gateway reported Programmed=True. Because this local environment did not expose a LoadBalancer address, external traffic was verified through the external Envoy Service's NodePort on the Kind Docker network.

5. Deploy Serving and Eventing from non-canonical hub namespaces

The management CR namespaces intentionally differed from the remote installation namespaces:

apiVersion: operator.knative.dev/v1beta1kind: KnativeServingmetadata:
name: ocm-servingnamespace: management-serving-hubspec:
clusterProfileRef:
name: cluster1namespace: knative-operatoringress:
gateway-api:
enabled: trueistio:
enabled: false
---
apiVersion: operator.knative.dev/v1beta1kind: KnativeEventingmetadata:
name: ocm-eventingnamespace: management-eventing-hubspec:
clusterProfileRef:
name: cluster2namespace: knative-operator

Both hub CRs reached TargetClusterResolved=True and Ready=True. All seven Serving Deployments became Available only in cluster1/knative-serving; Knative Eventing reached Ready=True with its controller, webhook, and broker Deployments only in cluster2/knative-eventing. No Knative Deployments appeared in either hub management namespace or on the wrong spoke.

The remote anchors were created as knativeserving-ocm-serving-root-owner and knativeeventing-ocm-eventing-root-owner, and managed resources referenced the matching anchor.

6. Verify Serving traffic

kubectl --context kind-cluster1 apply -f - <<'EOF'apiVersion: serving.knative.dev/v1kind: Servicemetadata: name: helloworld-go namespace: defaultspec: template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET value: OCM canonical namespace verificationEOF
kubectl --context kind-cluster1 -n default \
wait --for=condition=Ready kservice/helloworld-go --timeout=300s
NODE_PORT=$(kubectl --context kind-cluster1 -n eg-external \ get service knative-external \ -o jsonpath='{.spec.ports[?(@.port==80)].nodePort}')
docker run --rm --network kind curlimages/curl:8.15.0 \
-H 'Host: helloworld-go.default.example.com' \
"http://cluster1-control-plane:${NODE_PORT}"

Observed response:

Hello OCM canonical namespace verification!

The external and cluster-local HTTPRoutes both reported Accepted=True and ResolvedRefs=True.

7. Verify cleanup and recreation

Both management CRs were deleted:

kubectl --context kind-hub -n management-serving-hub \
delete knativeserving ocm-serving
kubectl --context kind-hub -n management-eventing-hub \
delete knativeeventing ocm-eventing

Both CRs completed finalization. Their anchors and all anchor-owned Deployments were removed from cluster1/knative-serving and cluster2/knative-eventing. After recreating the same management CRs, both returned to TargetClusterResolved=True and Ready=True, and the Serving request returned the same response again.

Upgrade note

Remote installations created before this change used the management CR namespace on the spoke. If an existing remote installation is not already in its canonical namespace:

  1. Delete the management CR with the old operator and wait for finalization to remove the spoke resources.
  2. Upgrade the operator.
  3. Recreate the same management CR on the hub.

The recreated installation is placed in knative-serving or knative-eventing. No migration is required when the existing remote installation already uses its canonical namespace.

Release Note

action required: Remote KnativeServing and KnativeEventing installations now use the canonical knative-serving and knative-eventing namespaces instead of the management CR namespace. Delete non-canonical remote installations with the old operator before upgrading, then recreate their management CRs.

@knative-prowknative-prowBot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 26, 2026
@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.95%. Comparing base (7f45332) to head (e31a55d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #2349 +/- ##
==========================================
+ Coverage 65.84% 65.95% +0.11% 
==========================================
Files 56 56 Lines 2673 2682 +9 ==========================================
+ Hits 1760 1769 +9 
Misses 777 777 Partials 136 136 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kahirokunn

Copy link
Copy Markdown
MemberAuthor

I plan to implement a separate validation rule to prevent the creation of more than one KnativeServing or KnativeEventing instance per cluster.

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from a1af5ee to fe54616CompareAugust 26, 2026 11:50
@kahirokunn

Copy link
Copy Markdown
MemberAuthor

@dsimansk Hi ✋ PTAL. Thank you 🙏

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from fe54616 to 7ce0e6fCompareAugust 27, 2026 02:23
Comment threadpkg/reconciler/common/multicluster.go
Comment threaddocs/multicluster.md
@knative-prow-robotknative-prow-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from 7ce0e6f to e31a55dCompareAugust 27, 2026 12:41
@knative-prow-robotknative-prow-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
@dsimansk

Copy link
Copy Markdown
Contributor

/approve
/lgtm

@knative-prowknative-prowBot added the lgtm Indicates that a PR is ready to be merged. label Aug 27, 2026
@knative-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dsimansk, kahirokunn

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prowknative-prowBot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 27, 2026
@knative-prow
knative-prowBot merged commit bd7bb89 into knative:mainAug 27, 2026
26 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by an approver from all required OWNERS files.lgtmIndicates that a PR is ready to be merged.size/LDenotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install remote Knative components in canonical namespaces

3 participants

@kahirokunn@dsimansk@knative-prow-robot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Install remote Knative components in canonical namespaces - #2349

Merged
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces
Aug 27, 2026
Merged

Install remote Knative components in canonical namespaces#2349
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces

Conversation

@kahirokunn

@kahirokunnkahirokunn commented Aug 26, 2026

Copy link
Copy Markdown
Member

Fixes#2345

Proposed Changes

  • Install remote KnativeServing and KnativeEventing components in their canonical spoke namespaces regardless of the hub management CR namespace.

Design

The component kind determines the remote installation namespace:

Management CR on the hubInstallation namespace on the spoke
KnativeServingknative-serving
KnativeEventingknative-eventing

Local installations without spec.clusterProfileRef continue to use the management CR metadata.namespace.

Verification Steps

E2E verification with Kind

Verified manually with three Kind clusters (hub, cluster1, and cluster2), OCM-managed ClusterProfile objects, the managed-serviceaccount credential syncer, cp-creds, and Envoy Gateway.

1. Set up OCM and ClusterProfile access

OCM was initialized on hub with ClusterProfile support. cluster1 and cluster2 were joined, accepted, assigned to the sandbox-fleet ManagedClusterSet, and bound to the knative-operator namespace. Both managed clusters reported Joined=True and Available=True; both ClusterProfiles reported ControlPlaneHealthy=True and Joined=True.

The managed-serviceaccount credential syncer and the cluster-proxy user server were enabled:

helm upgrade --install managed-serviceaccount ocm/managed-serviceaccount \
--kube-context kind-hub \
-n open-cluster-management-managed-serviceaccount \
--create-namespace \
--version 0.10.0 \
--set featureGates.clusterProfileCredSyncer=true
helm upgrade --install cluster-proxy ocm/cluster-proxy \
--kube-context kind-hub \
-n open-cluster-management-cluster-proxy \
--create-namespace \
--version 0.11.0 \
--set featureGates.clusterProfile=true \
--set userServer.enabled=true \
--set enableServiceProxy=true

The knative-operator ManagedServiceAccount was created for each spoke and labeled for ClusterProfile credential synchronization. The corresponding spoke service accounts were bound to cluster-admin for this installation test. The resulting cluster1-knative-operator and cluster2-knative-operator credentials appeared in the hub's knative-operator namespace, and both ClusterProfiles advertised the open-cluster-management access provider.

2. Configure the operator to use cp-creds

The operator mounted quay.io/open-cluster-management/cp-creds:latest at /access-plugins/cp-creds and used this provider configuration:

{
"providers": [
{
"name": "open-cluster-management",
"execConfig": {
"apiVersion": "client.authentication.k8s.io/v1",
"command": "/access-plugins/cp-creds/cp-creds",
"args": ["--managed-serviceaccount=knative-operator"],
"provideClusterInfo": true,
"interactiveMode": "Never"
}
}
]
}

3. Build and deploy this PR head

PATH="$(go env GOPATH)/bin:$PATH" \
KO_DOCKER_REPO=kind.local \
KIND_CLUSTER_NAME=hub \
ko apply -f config/
kubectl --context kind-hub -n knative-operator \
rollout status deployment/knative-operator --timeout=180s

Both referenced ClusterProfiles resolved through the configured cp-creds provider.

4. Configure the Serving data plane on cluster1

Gateway API v1.4.1 experimental CRDs and Envoy Gateway v1.7.1 were installed on cluster1 with the GatewayNamespace deployment model. The following data-plane resources were configured:

TrafficGateway namespace/nameEnvoy Service
Externaleg-external/eg-externaleg-external/knative-external
Cluster-localeg-internal/eg-internaleg-internal/knative-internal

Both GatewayClasses reported Accepted=True; the internal Gateway reported Programmed=True. Because this local environment did not expose a LoadBalancer address, external traffic was verified through the external Envoy Service's NodePort on the Kind Docker network.

5. Deploy Serving and Eventing from non-canonical hub namespaces

The management CR namespaces intentionally differed from the remote installation namespaces:

apiVersion: operator.knative.dev/v1beta1kind: KnativeServingmetadata:
name: ocm-servingnamespace: management-serving-hubspec:
clusterProfileRef:
name: cluster1namespace: knative-operatoringress:
gateway-api:
enabled: trueistio:
enabled: false
---
apiVersion: operator.knative.dev/v1beta1kind: KnativeEventingmetadata:
name: ocm-eventingnamespace: management-eventing-hubspec:
clusterProfileRef:
name: cluster2namespace: knative-operator

Both hub CRs reached TargetClusterResolved=True and Ready=True. All seven Serving Deployments became Available only in cluster1/knative-serving; Knative Eventing reached Ready=True with its controller, webhook, and broker Deployments only in cluster2/knative-eventing. No Knative Deployments appeared in either hub management namespace or on the wrong spoke.

The remote anchors were created as knativeserving-ocm-serving-root-owner and knativeeventing-ocm-eventing-root-owner, and managed resources referenced the matching anchor.

6. Verify Serving traffic

kubectl --context kind-cluster1 apply -f - <<'EOF'apiVersion: serving.knative.dev/v1kind: Servicemetadata: name: helloworld-go namespace: defaultspec: template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET value: OCM canonical namespace verificationEOF
kubectl --context kind-cluster1 -n default \
wait --for=condition=Ready kservice/helloworld-go --timeout=300s
NODE_PORT=$(kubectl --context kind-cluster1 -n eg-external \ get service knative-external \ -o jsonpath='{.spec.ports[?(@.port==80)].nodePort}')
docker run --rm --network kind curlimages/curl:8.15.0 \
-H 'Host: helloworld-go.default.example.com' \
"http://cluster1-control-plane:${NODE_PORT}"

Observed response:

Hello OCM canonical namespace verification!

The external and cluster-local HTTPRoutes both reported Accepted=True and ResolvedRefs=True.

7. Verify cleanup and recreation

Both management CRs were deleted:

kubectl --context kind-hub -n management-serving-hub \
delete knativeserving ocm-serving
kubectl --context kind-hub -n management-eventing-hub \
delete knativeeventing ocm-eventing

Both CRs completed finalization. Their anchors and all anchor-owned Deployments were removed from cluster1/knative-serving and cluster2/knative-eventing. After recreating the same management CRs, both returned to TargetClusterResolved=True and Ready=True, and the Serving request returned the same response again.

Upgrade note

Remote installations created before this change used the management CR namespace on the spoke. If an existing remote installation is not already in its canonical namespace:

  1. Delete the management CR with the old operator and wait for finalization to remove the spoke resources.
  2. Upgrade the operator.
  3. Recreate the same management CR on the hub.

The recreated installation is placed in knative-serving or knative-eventing. No migration is required when the existing remote installation already uses its canonical namespace.

Release Note

action required: Remote KnativeServing and KnativeEventing installations now use the canonical knative-serving and knative-eventing namespaces instead of the management CR namespace. Delete non-canonical remote installations with the old operator before upgrading, then recreate their management CRs.

@knative-prowknative-prowBot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 26, 2026
@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.95%. Comparing base (7f45332) to head (e31a55d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #2349 +/- ##
==========================================
+ Coverage 65.84% 65.95% +0.11% 
==========================================
Files 56 56 Lines 2673 2682 +9 ==========================================
+ Hits 1760 1769 +9 
Misses 777 777 Partials 136 136 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kahirokunn

Copy link
Copy Markdown
MemberAuthor

I plan to implement a separate validation rule to prevent the creation of more than one KnativeServing or KnativeEventing instance per cluster.

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from a1af5ee to fe54616CompareAugust 26, 2026 11:50
@kahirokunn

Copy link
Copy Markdown
MemberAuthor

@dsimansk Hi ✋ PTAL. Thank you 🙏

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from fe54616 to 7ce0e6fCompareAugust 27, 2026 02:23
Comment threadpkg/reconciler/common/multicluster.go
Comment threaddocs/multicluster.md
@knative-prow-robotknative-prow-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from 7ce0e6f to e31a55dCompareAugust 27, 2026 12:41
@knative-prow-robotknative-prow-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
@dsimansk

Copy link
Copy Markdown
Contributor

/approve
/lgtm

@knative-prowknative-prowBot added the lgtm Indicates that a PR is ready to be merged. label Aug 27, 2026
@knative-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dsimansk, kahirokunn

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prowknative-prowBot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 27, 2026
@knative-prow
knative-prowBot merged commit bd7bb89 into knative:mainAug 27, 2026
26 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by an approver from all required OWNERS files.lgtmIndicates that a PR is ready to be merged.size/LDenotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install remote Knative components in canonical namespaces

3 participants

@kahirokunn@dsimansk@knative-prow-robot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Install remote Knative components in canonical namespaces - #2349

Merged
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces
Aug 27, 2026
Merged

Install remote Knative components in canonical namespaces#2349
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces

Conversation

@kahirokunn

@kahirokunnkahirokunn commented Aug 26, 2026

Copy link
Copy Markdown
Member

Fixes#2345

Proposed Changes

  • Install remote KnativeServing and KnativeEventing components in their canonical spoke namespaces regardless of the hub management CR namespace.

Design

The component kind determines the remote installation namespace:

Management CR on the hubInstallation namespace on the spoke
KnativeServingknative-serving
KnativeEventingknative-eventing

Local installations without spec.clusterProfileRef continue to use the management CR metadata.namespace.

Verification Steps

E2E verification with Kind

Verified manually with three Kind clusters (hub, cluster1, and cluster2), OCM-managed ClusterProfile objects, the managed-serviceaccount credential syncer, cp-creds, and Envoy Gateway.

1. Set up OCM and ClusterProfile access

OCM was initialized on hub with ClusterProfile support. cluster1 and cluster2 were joined, accepted, assigned to the sandbox-fleet ManagedClusterSet, and bound to the knative-operator namespace. Both managed clusters reported Joined=True and Available=True; both ClusterProfiles reported ControlPlaneHealthy=True and Joined=True.

The managed-serviceaccount credential syncer and the cluster-proxy user server were enabled:

helm upgrade --install managed-serviceaccount ocm/managed-serviceaccount \
--kube-context kind-hub \
-n open-cluster-management-managed-serviceaccount \
--create-namespace \
--version 0.10.0 \
--set featureGates.clusterProfileCredSyncer=true
helm upgrade --install cluster-proxy ocm/cluster-proxy \
--kube-context kind-hub \
-n open-cluster-management-cluster-proxy \
--create-namespace \
--version 0.11.0 \
--set featureGates.clusterProfile=true \
--set userServer.enabled=true \
--set enableServiceProxy=true

The knative-operator ManagedServiceAccount was created for each spoke and labeled for ClusterProfile credential synchronization. The corresponding spoke service accounts were bound to cluster-admin for this installation test. The resulting cluster1-knative-operator and cluster2-knative-operator credentials appeared in the hub's knative-operator namespace, and both ClusterProfiles advertised the open-cluster-management access provider.

2. Configure the operator to use cp-creds

The operator mounted quay.io/open-cluster-management/cp-creds:latest at /access-plugins/cp-creds and used this provider configuration:

{
"providers": [
{
"name": "open-cluster-management",
"execConfig": {
"apiVersion": "client.authentication.k8s.io/v1",
"command": "/access-plugins/cp-creds/cp-creds",
"args": ["--managed-serviceaccount=knative-operator"],
"provideClusterInfo": true,
"interactiveMode": "Never"
}
}
]
}

3. Build and deploy this PR head

PATH="$(go env GOPATH)/bin:$PATH" \
KO_DOCKER_REPO=kind.local \
KIND_CLUSTER_NAME=hub \
ko apply -f config/
kubectl --context kind-hub -n knative-operator \
rollout status deployment/knative-operator --timeout=180s

Both referenced ClusterProfiles resolved through the configured cp-creds provider.

4. Configure the Serving data plane on cluster1

Gateway API v1.4.1 experimental CRDs and Envoy Gateway v1.7.1 were installed on cluster1 with the GatewayNamespace deployment model. The following data-plane resources were configured:

TrafficGateway namespace/nameEnvoy Service
Externaleg-external/eg-externaleg-external/knative-external
Cluster-localeg-internal/eg-internaleg-internal/knative-internal

Both GatewayClasses reported Accepted=True; the internal Gateway reported Programmed=True. Because this local environment did not expose a LoadBalancer address, external traffic was verified through the external Envoy Service's NodePort on the Kind Docker network.

5. Deploy Serving and Eventing from non-canonical hub namespaces

The management CR namespaces intentionally differed from the remote installation namespaces:

apiVersion: operator.knative.dev/v1beta1kind: KnativeServingmetadata:
name: ocm-servingnamespace: management-serving-hubspec:
clusterProfileRef:
name: cluster1namespace: knative-operatoringress:
gateway-api:
enabled: trueistio:
enabled: false
---
apiVersion: operator.knative.dev/v1beta1kind: KnativeEventingmetadata:
name: ocm-eventingnamespace: management-eventing-hubspec:
clusterProfileRef:
name: cluster2namespace: knative-operator

Both hub CRs reached TargetClusterResolved=True and Ready=True. All seven Serving Deployments became Available only in cluster1/knative-serving; Knative Eventing reached Ready=True with its controller, webhook, and broker Deployments only in cluster2/knative-eventing. No Knative Deployments appeared in either hub management namespace or on the wrong spoke.

The remote anchors were created as knativeserving-ocm-serving-root-owner and knativeeventing-ocm-eventing-root-owner, and managed resources referenced the matching anchor.

6. Verify Serving traffic

kubectl --context kind-cluster1 apply -f - <<'EOF'apiVersion: serving.knative.dev/v1kind: Servicemetadata: name: helloworld-go namespace: defaultspec: template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET value: OCM canonical namespace verificationEOF
kubectl --context kind-cluster1 -n default \
wait --for=condition=Ready kservice/helloworld-go --timeout=300s
NODE_PORT=$(kubectl --context kind-cluster1 -n eg-external \ get service knative-external \ -o jsonpath='{.spec.ports[?(@.port==80)].nodePort}')
docker run --rm --network kind curlimages/curl:8.15.0 \
-H 'Host: helloworld-go.default.example.com' \
"http://cluster1-control-plane:${NODE_PORT}"

Observed response:

Hello OCM canonical namespace verification!

The external and cluster-local HTTPRoutes both reported Accepted=True and ResolvedRefs=True.

7. Verify cleanup and recreation

Both management CRs were deleted:

kubectl --context kind-hub -n management-serving-hub \
delete knativeserving ocm-serving
kubectl --context kind-hub -n management-eventing-hub \
delete knativeeventing ocm-eventing

Both CRs completed finalization. Their anchors and all anchor-owned Deployments were removed from cluster1/knative-serving and cluster2/knative-eventing. After recreating the same management CRs, both returned to TargetClusterResolved=True and Ready=True, and the Serving request returned the same response again.

Upgrade note

Remote installations created before this change used the management CR namespace on the spoke. If an existing remote installation is not already in its canonical namespace:

  1. Delete the management CR with the old operator and wait for finalization to remove the spoke resources.
  2. Upgrade the operator.
  3. Recreate the same management CR on the hub.

The recreated installation is placed in knative-serving or knative-eventing. No migration is required when the existing remote installation already uses its canonical namespace.

Release Note

action required: Remote KnativeServing and KnativeEventing installations now use the canonical knative-serving and knative-eventing namespaces instead of the management CR namespace. Delete non-canonical remote installations with the old operator before upgrading, then recreate their management CRs.

@knative-prowknative-prowBot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 26, 2026
@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.95%. Comparing base (7f45332) to head (e31a55d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #2349 +/- ##
==========================================
+ Coverage 65.84% 65.95% +0.11% 
==========================================
Files 56 56 Lines 2673 2682 +9 ==========================================
+ Hits 1760 1769 +9 
Misses 777 777 Partials 136 136 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kahirokunn

Copy link
Copy Markdown
MemberAuthor

I plan to implement a separate validation rule to prevent the creation of more than one KnativeServing or KnativeEventing instance per cluster.

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from a1af5ee to fe54616CompareAugust 26, 2026 11:50
@kahirokunn

Copy link
Copy Markdown
MemberAuthor

@dsimansk Hi ✋ PTAL. Thank you 🙏

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from fe54616 to 7ce0e6fCompareAugust 27, 2026 02:23
Comment threadpkg/reconciler/common/multicluster.go
Comment threaddocs/multicluster.md
@knative-prow-robotknative-prow-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from 7ce0e6f to e31a55dCompareAugust 27, 2026 12:41
@knative-prow-robotknative-prow-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
@dsimansk

Copy link
Copy Markdown
Contributor

/approve
/lgtm

@knative-prowknative-prowBot added the lgtm Indicates that a PR is ready to be merged. label Aug 27, 2026
@knative-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dsimansk, kahirokunn

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prowknative-prowBot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 27, 2026
@knative-prow
knative-prowBot merged commit bd7bb89 into knative:mainAug 27, 2026
26 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by an approver from all required OWNERS files.lgtmIndicates that a PR is ready to be merged.size/LDenotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install remote Knative components in canonical namespaces

3 participants

@kahirokunn@dsimansk@knative-prow-robot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Install remote Knative components in canonical namespaces - #2349

Merged
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces
Aug 27, 2026
Merged

Install remote Knative components in canonical namespaces#2349
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces

Conversation

@kahirokunn

@kahirokunnkahirokunn commented Aug 26, 2026

Copy link
Copy Markdown
Member

Fixes#2345

Proposed Changes

  • Install remote KnativeServing and KnativeEventing components in their canonical spoke namespaces regardless of the hub management CR namespace.

Design

The component kind determines the remote installation namespace:

Management CR on the hubInstallation namespace on the spoke
KnativeServingknative-serving
KnativeEventingknative-eventing

Local installations without spec.clusterProfileRef continue to use the management CR metadata.namespace.

Verification Steps

E2E verification with Kind

Verified manually with three Kind clusters (hub, cluster1, and cluster2), OCM-managed ClusterProfile objects, the managed-serviceaccount credential syncer, cp-creds, and Envoy Gateway.

1. Set up OCM and ClusterProfile access

OCM was initialized on hub with ClusterProfile support. cluster1 and cluster2 were joined, accepted, assigned to the sandbox-fleet ManagedClusterSet, and bound to the knative-operator namespace. Both managed clusters reported Joined=True and Available=True; both ClusterProfiles reported ControlPlaneHealthy=True and Joined=True.

The managed-serviceaccount credential syncer and the cluster-proxy user server were enabled:

helm upgrade --install managed-serviceaccount ocm/managed-serviceaccount \
--kube-context kind-hub \
-n open-cluster-management-managed-serviceaccount \
--create-namespace \
--version 0.10.0 \
--set featureGates.clusterProfileCredSyncer=true
helm upgrade --install cluster-proxy ocm/cluster-proxy \
--kube-context kind-hub \
-n open-cluster-management-cluster-proxy \
--create-namespace \
--version 0.11.0 \
--set featureGates.clusterProfile=true \
--set userServer.enabled=true \
--set enableServiceProxy=true

The knative-operator ManagedServiceAccount was created for each spoke and labeled for ClusterProfile credential synchronization. The corresponding spoke service accounts were bound to cluster-admin for this installation test. The resulting cluster1-knative-operator and cluster2-knative-operator credentials appeared in the hub's knative-operator namespace, and both ClusterProfiles advertised the open-cluster-management access provider.

2. Configure the operator to use cp-creds

The operator mounted quay.io/open-cluster-management/cp-creds:latest at /access-plugins/cp-creds and used this provider configuration:

{
"providers": [
{
"name": "open-cluster-management",
"execConfig": {
"apiVersion": "client.authentication.k8s.io/v1",
"command": "/access-plugins/cp-creds/cp-creds",
"args": ["--managed-serviceaccount=knative-operator"],
"provideClusterInfo": true,
"interactiveMode": "Never"
}
}
]
}

3. Build and deploy this PR head

PATH="$(go env GOPATH)/bin:$PATH" \
KO_DOCKER_REPO=kind.local \
KIND_CLUSTER_NAME=hub \
ko apply -f config/
kubectl --context kind-hub -n knative-operator \
rollout status deployment/knative-operator --timeout=180s

Both referenced ClusterProfiles resolved through the configured cp-creds provider.

4. Configure the Serving data plane on cluster1

Gateway API v1.4.1 experimental CRDs and Envoy Gateway v1.7.1 were installed on cluster1 with the GatewayNamespace deployment model. The following data-plane resources were configured:

TrafficGateway namespace/nameEnvoy Service
Externaleg-external/eg-externaleg-external/knative-external
Cluster-localeg-internal/eg-internaleg-internal/knative-internal

Both GatewayClasses reported Accepted=True; the internal Gateway reported Programmed=True. Because this local environment did not expose a LoadBalancer address, external traffic was verified through the external Envoy Service's NodePort on the Kind Docker network.

5. Deploy Serving and Eventing from non-canonical hub namespaces

The management CR namespaces intentionally differed from the remote installation namespaces:

apiVersion: operator.knative.dev/v1beta1kind: KnativeServingmetadata:
name: ocm-servingnamespace: management-serving-hubspec:
clusterProfileRef:
name: cluster1namespace: knative-operatoringress:
gateway-api:
enabled: trueistio:
enabled: false
---
apiVersion: operator.knative.dev/v1beta1kind: KnativeEventingmetadata:
name: ocm-eventingnamespace: management-eventing-hubspec:
clusterProfileRef:
name: cluster2namespace: knative-operator

Both hub CRs reached TargetClusterResolved=True and Ready=True. All seven Serving Deployments became Available only in cluster1/knative-serving; Knative Eventing reached Ready=True with its controller, webhook, and broker Deployments only in cluster2/knative-eventing. No Knative Deployments appeared in either hub management namespace or on the wrong spoke.

The remote anchors were created as knativeserving-ocm-serving-root-owner and knativeeventing-ocm-eventing-root-owner, and managed resources referenced the matching anchor.

6. Verify Serving traffic

kubectl --context kind-cluster1 apply -f - <<'EOF'apiVersion: serving.knative.dev/v1kind: Servicemetadata: name: helloworld-go namespace: defaultspec: template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET value: OCM canonical namespace verificationEOF
kubectl --context kind-cluster1 -n default \
wait --for=condition=Ready kservice/helloworld-go --timeout=300s
NODE_PORT=$(kubectl --context kind-cluster1 -n eg-external \ get service knative-external \ -o jsonpath='{.spec.ports[?(@.port==80)].nodePort}')
docker run --rm --network kind curlimages/curl:8.15.0 \
-H 'Host: helloworld-go.default.example.com' \
"http://cluster1-control-plane:${NODE_PORT}"

Observed response:

Hello OCM canonical namespace verification!

The external and cluster-local HTTPRoutes both reported Accepted=True and ResolvedRefs=True.

7. Verify cleanup and recreation

Both management CRs were deleted:

kubectl --context kind-hub -n management-serving-hub \
delete knativeserving ocm-serving
kubectl --context kind-hub -n management-eventing-hub \
delete knativeeventing ocm-eventing

Both CRs completed finalization. Their anchors and all anchor-owned Deployments were removed from cluster1/knative-serving and cluster2/knative-eventing. After recreating the same management CRs, both returned to TargetClusterResolved=True and Ready=True, and the Serving request returned the same response again.

Upgrade note

Remote installations created before this change used the management CR namespace on the spoke. If an existing remote installation is not already in its canonical namespace:

  1. Delete the management CR with the old operator and wait for finalization to remove the spoke resources.
  2. Upgrade the operator.
  3. Recreate the same management CR on the hub.

The recreated installation is placed in knative-serving or knative-eventing. No migration is required when the existing remote installation already uses its canonical namespace.

Release Note

action required: Remote KnativeServing and KnativeEventing installations now use the canonical knative-serving and knative-eventing namespaces instead of the management CR namespace. Delete non-canonical remote installations with the old operator before upgrading, then recreate their management CRs.

@knative-prowknative-prowBot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 26, 2026
@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.95%. Comparing base (7f45332) to head (e31a55d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #2349 +/- ##
==========================================
+ Coverage 65.84% 65.95% +0.11% 
==========================================
Files 56 56 Lines 2673 2682 +9 ==========================================
+ Hits 1760 1769 +9 
Misses 777 777 Partials 136 136 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kahirokunn

Copy link
Copy Markdown
MemberAuthor

I plan to implement a separate validation rule to prevent the creation of more than one KnativeServing or KnativeEventing instance per cluster.

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from a1af5ee to fe54616CompareAugust 26, 2026 11:50
@kahirokunn

Copy link
Copy Markdown
MemberAuthor

@dsimansk Hi ✋ PTAL. Thank you 🙏

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from fe54616 to 7ce0e6fCompareAugust 27, 2026 02:23
Comment threadpkg/reconciler/common/multicluster.go
Comment threaddocs/multicluster.md
@knative-prow-robotknative-prow-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from 7ce0e6f to e31a55dCompareAugust 27, 2026 12:41
@knative-prow-robotknative-prow-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
@dsimansk

Copy link
Copy Markdown
Contributor

/approve
/lgtm

@knative-prowknative-prowBot added the lgtm Indicates that a PR is ready to be merged. label Aug 27, 2026
@knative-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dsimansk, kahirokunn

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prowknative-prowBot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 27, 2026
@knative-prow
knative-prowBot merged commit bd7bb89 into knative:mainAug 27, 2026
26 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by an approver from all required OWNERS files.lgtmIndicates that a PR is ready to be merged.size/LDenotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install remote Knative components in canonical namespaces

3 participants

@kahirokunn@dsimansk@knative-prow-robot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Install remote Knative components in canonical namespaces - #2349

Merged
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces
Aug 27, 2026
Merged

Install remote Knative components in canonical namespaces#2349
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces

Conversation

@kahirokunn

@kahirokunnkahirokunn commented Aug 26, 2026

Copy link
Copy Markdown
Member

Fixes#2345

Proposed Changes

  • Install remote KnativeServing and KnativeEventing components in their canonical spoke namespaces regardless of the hub management CR namespace.

Design

The component kind determines the remote installation namespace:

Management CR on the hubInstallation namespace on the spoke
KnativeServingknative-serving
KnativeEventingknative-eventing

Local installations without spec.clusterProfileRef continue to use the management CR metadata.namespace.

Verification Steps

E2E verification with Kind

Verified manually with three Kind clusters (hub, cluster1, and cluster2), OCM-managed ClusterProfile objects, the managed-serviceaccount credential syncer, cp-creds, and Envoy Gateway.

1. Set up OCM and ClusterProfile access

OCM was initialized on hub with ClusterProfile support. cluster1 and cluster2 were joined, accepted, assigned to the sandbox-fleet ManagedClusterSet, and bound to the knative-operator namespace. Both managed clusters reported Joined=True and Available=True; both ClusterProfiles reported ControlPlaneHealthy=True and Joined=True.

The managed-serviceaccount credential syncer and the cluster-proxy user server were enabled:

helm upgrade --install managed-serviceaccount ocm/managed-serviceaccount \
--kube-context kind-hub \
-n open-cluster-management-managed-serviceaccount \
--create-namespace \
--version 0.10.0 \
--set featureGates.clusterProfileCredSyncer=true
helm upgrade --install cluster-proxy ocm/cluster-proxy \
--kube-context kind-hub \
-n open-cluster-management-cluster-proxy \
--create-namespace \
--version 0.11.0 \
--set featureGates.clusterProfile=true \
--set userServer.enabled=true \
--set enableServiceProxy=true

The knative-operator ManagedServiceAccount was created for each spoke and labeled for ClusterProfile credential synchronization. The corresponding spoke service accounts were bound to cluster-admin for this installation test. The resulting cluster1-knative-operator and cluster2-knative-operator credentials appeared in the hub's knative-operator namespace, and both ClusterProfiles advertised the open-cluster-management access provider.

2. Configure the operator to use cp-creds

The operator mounted quay.io/open-cluster-management/cp-creds:latest at /access-plugins/cp-creds and used this provider configuration:

{
"providers": [
{
"name": "open-cluster-management",
"execConfig": {
"apiVersion": "client.authentication.k8s.io/v1",
"command": "/access-plugins/cp-creds/cp-creds",
"args": ["--managed-serviceaccount=knative-operator"],
"provideClusterInfo": true,
"interactiveMode": "Never"
}
}
]
}

3. Build and deploy this PR head

PATH="$(go env GOPATH)/bin:$PATH" \
KO_DOCKER_REPO=kind.local \
KIND_CLUSTER_NAME=hub \
ko apply -f config/
kubectl --context kind-hub -n knative-operator \
rollout status deployment/knative-operator --timeout=180s

Both referenced ClusterProfiles resolved through the configured cp-creds provider.

4. Configure the Serving data plane on cluster1

Gateway API v1.4.1 experimental CRDs and Envoy Gateway v1.7.1 were installed on cluster1 with the GatewayNamespace deployment model. The following data-plane resources were configured:

TrafficGateway namespace/nameEnvoy Service
Externaleg-external/eg-externaleg-external/knative-external
Cluster-localeg-internal/eg-internaleg-internal/knative-internal

Both GatewayClasses reported Accepted=True; the internal Gateway reported Programmed=True. Because this local environment did not expose a LoadBalancer address, external traffic was verified through the external Envoy Service's NodePort on the Kind Docker network.

5. Deploy Serving and Eventing from non-canonical hub namespaces

The management CR namespaces intentionally differed from the remote installation namespaces:

apiVersion: operator.knative.dev/v1beta1kind: KnativeServingmetadata:
name: ocm-servingnamespace: management-serving-hubspec:
clusterProfileRef:
name: cluster1namespace: knative-operatoringress:
gateway-api:
enabled: trueistio:
enabled: false
---
apiVersion: operator.knative.dev/v1beta1kind: KnativeEventingmetadata:
name: ocm-eventingnamespace: management-eventing-hubspec:
clusterProfileRef:
name: cluster2namespace: knative-operator

Both hub CRs reached TargetClusterResolved=True and Ready=True. All seven Serving Deployments became Available only in cluster1/knative-serving; Knative Eventing reached Ready=True with its controller, webhook, and broker Deployments only in cluster2/knative-eventing. No Knative Deployments appeared in either hub management namespace or on the wrong spoke.

The remote anchors were created as knativeserving-ocm-serving-root-owner and knativeeventing-ocm-eventing-root-owner, and managed resources referenced the matching anchor.

6. Verify Serving traffic

kubectl --context kind-cluster1 apply -f - <<'EOF'apiVersion: serving.knative.dev/v1kind: Servicemetadata: name: helloworld-go namespace: defaultspec: template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET value: OCM canonical namespace verificationEOF
kubectl --context kind-cluster1 -n default \
wait --for=condition=Ready kservice/helloworld-go --timeout=300s
NODE_PORT=$(kubectl --context kind-cluster1 -n eg-external \ get service knative-external \ -o jsonpath='{.spec.ports[?(@.port==80)].nodePort}')
docker run --rm --network kind curlimages/curl:8.15.0 \
-H 'Host: helloworld-go.default.example.com' \
"http://cluster1-control-plane:${NODE_PORT}"

Observed response:

Hello OCM canonical namespace verification!

The external and cluster-local HTTPRoutes both reported Accepted=True and ResolvedRefs=True.

7. Verify cleanup and recreation

Both management CRs were deleted:

kubectl --context kind-hub -n management-serving-hub \
delete knativeserving ocm-serving
kubectl --context kind-hub -n management-eventing-hub \
delete knativeeventing ocm-eventing

Both CRs completed finalization. Their anchors and all anchor-owned Deployments were removed from cluster1/knative-serving and cluster2/knative-eventing. After recreating the same management CRs, both returned to TargetClusterResolved=True and Ready=True, and the Serving request returned the same response again.

Upgrade note

Remote installations created before this change used the management CR namespace on the spoke. If an existing remote installation is not already in its canonical namespace:

  1. Delete the management CR with the old operator and wait for finalization to remove the spoke resources.
  2. Upgrade the operator.
  3. Recreate the same management CR on the hub.

The recreated installation is placed in knative-serving or knative-eventing. No migration is required when the existing remote installation already uses its canonical namespace.

Release Note

action required: Remote KnativeServing and KnativeEventing installations now use the canonical knative-serving and knative-eventing namespaces instead of the management CR namespace. Delete non-canonical remote installations with the old operator before upgrading, then recreate their management CRs.

@knative-prowknative-prowBot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 26, 2026
@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.95%. Comparing base (7f45332) to head (e31a55d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #2349 +/- ##
==========================================
+ Coverage 65.84% 65.95% +0.11% 
==========================================
Files 56 56 Lines 2673 2682 +9 ==========================================
+ Hits 1760 1769 +9 
Misses 777 777 Partials 136 136 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kahirokunn

Copy link
Copy Markdown
MemberAuthor

I plan to implement a separate validation rule to prevent the creation of more than one KnativeServing or KnativeEventing instance per cluster.

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from a1af5ee to fe54616CompareAugust 26, 2026 11:50
@kahirokunn

Copy link
Copy Markdown
MemberAuthor

@dsimansk Hi ✋ PTAL. Thank you 🙏

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from fe54616 to 7ce0e6fCompareAugust 27, 2026 02:23
Comment threadpkg/reconciler/common/multicluster.go
Comment threaddocs/multicluster.md
@knative-prow-robotknative-prow-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from 7ce0e6f to e31a55dCompareAugust 27, 2026 12:41
@knative-prow-robotknative-prow-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
@dsimansk

Copy link
Copy Markdown
Contributor

/approve
/lgtm

@knative-prowknative-prowBot added the lgtm Indicates that a PR is ready to be merged. label Aug 27, 2026
@knative-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dsimansk, kahirokunn

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prowknative-prowBot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 27, 2026
@knative-prow
knative-prowBot merged commit bd7bb89 into knative:mainAug 27, 2026
26 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by an approver from all required OWNERS files.lgtmIndicates that a PR is ready to be merged.size/LDenotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install remote Knative components in canonical namespaces

3 participants

@kahirokunn@dsimansk@knative-prow-robot
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Install remote Knative components in canonical namespaces - #2349

Merged
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces
Aug 27, 2026
Merged

Install remote Knative components in canonical namespaces#2349
knative-prow[bot] merged 2 commits into
knative:mainfrom
kahirokunn:canonical-remote-namespaces

Conversation

@kahirokunn

@kahirokunnkahirokunn commented Aug 26, 2026

Copy link
Copy Markdown
Member

Fixes#2345

Proposed Changes

  • Install remote KnativeServing and KnativeEventing components in their canonical spoke namespaces regardless of the hub management CR namespace.

Design

The component kind determines the remote installation namespace:

Management CR on the hubInstallation namespace on the spoke
KnativeServingknative-serving
KnativeEventingknative-eventing

Local installations without spec.clusterProfileRef continue to use the management CR metadata.namespace.

Verification Steps

E2E verification with Kind

Verified manually with three Kind clusters (hub, cluster1, and cluster2), OCM-managed ClusterProfile objects, the managed-serviceaccount credential syncer, cp-creds, and Envoy Gateway.

1. Set up OCM and ClusterProfile access

OCM was initialized on hub with ClusterProfile support. cluster1 and cluster2 were joined, accepted, assigned to the sandbox-fleet ManagedClusterSet, and bound to the knative-operator namespace. Both managed clusters reported Joined=True and Available=True; both ClusterProfiles reported ControlPlaneHealthy=True and Joined=True.

The managed-serviceaccount credential syncer and the cluster-proxy user server were enabled:

helm upgrade --install managed-serviceaccount ocm/managed-serviceaccount \
--kube-context kind-hub \
-n open-cluster-management-managed-serviceaccount \
--create-namespace \
--version 0.10.0 \
--set featureGates.clusterProfileCredSyncer=true
helm upgrade --install cluster-proxy ocm/cluster-proxy \
--kube-context kind-hub \
-n open-cluster-management-cluster-proxy \
--create-namespace \
--version 0.11.0 \
--set featureGates.clusterProfile=true \
--set userServer.enabled=true \
--set enableServiceProxy=true

The knative-operator ManagedServiceAccount was created for each spoke and labeled for ClusterProfile credential synchronization. The corresponding spoke service accounts were bound to cluster-admin for this installation test. The resulting cluster1-knative-operator and cluster2-knative-operator credentials appeared in the hub's knative-operator namespace, and both ClusterProfiles advertised the open-cluster-management access provider.

2. Configure the operator to use cp-creds

The operator mounted quay.io/open-cluster-management/cp-creds:latest at /access-plugins/cp-creds and used this provider configuration:

{
"providers": [
{
"name": "open-cluster-management",
"execConfig": {
"apiVersion": "client.authentication.k8s.io/v1",
"command": "/access-plugins/cp-creds/cp-creds",
"args": ["--managed-serviceaccount=knative-operator"],
"provideClusterInfo": true,
"interactiveMode": "Never"
}
}
]
}

3. Build and deploy this PR head

PATH="$(go env GOPATH)/bin:$PATH" \
KO_DOCKER_REPO=kind.local \
KIND_CLUSTER_NAME=hub \
ko apply -f config/
kubectl --context kind-hub -n knative-operator \
rollout status deployment/knative-operator --timeout=180s

Both referenced ClusterProfiles resolved through the configured cp-creds provider.

4. Configure the Serving data plane on cluster1

Gateway API v1.4.1 experimental CRDs and Envoy Gateway v1.7.1 were installed on cluster1 with the GatewayNamespace deployment model. The following data-plane resources were configured:

TrafficGateway namespace/nameEnvoy Service
Externaleg-external/eg-externaleg-external/knative-external
Cluster-localeg-internal/eg-internaleg-internal/knative-internal

Both GatewayClasses reported Accepted=True; the internal Gateway reported Programmed=True. Because this local environment did not expose a LoadBalancer address, external traffic was verified through the external Envoy Service's NodePort on the Kind Docker network.

5. Deploy Serving and Eventing from non-canonical hub namespaces

The management CR namespaces intentionally differed from the remote installation namespaces:

apiVersion: operator.knative.dev/v1beta1kind: KnativeServingmetadata:
name: ocm-servingnamespace: management-serving-hubspec:
clusterProfileRef:
name: cluster1namespace: knative-operatoringress:
gateway-api:
enabled: trueistio:
enabled: false
---
apiVersion: operator.knative.dev/v1beta1kind: KnativeEventingmetadata:
name: ocm-eventingnamespace: management-eventing-hubspec:
clusterProfileRef:
name: cluster2namespace: knative-operator

Both hub CRs reached TargetClusterResolved=True and Ready=True. All seven Serving Deployments became Available only in cluster1/knative-serving; Knative Eventing reached Ready=True with its controller, webhook, and broker Deployments only in cluster2/knative-eventing. No Knative Deployments appeared in either hub management namespace or on the wrong spoke.

The remote anchors were created as knativeserving-ocm-serving-root-owner and knativeeventing-ocm-eventing-root-owner, and managed resources referenced the matching anchor.

6. Verify Serving traffic

kubectl --context kind-cluster1 apply -f - <<'EOF'apiVersion: serving.knative.dev/v1kind: Servicemetadata: name: helloworld-go namespace: defaultspec: template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET value: OCM canonical namespace verificationEOF
kubectl --context kind-cluster1 -n default \
wait --for=condition=Ready kservice/helloworld-go --timeout=300s
NODE_PORT=$(kubectl --context kind-cluster1 -n eg-external \ get service knative-external \ -o jsonpath='{.spec.ports[?(@.port==80)].nodePort}')
docker run --rm --network kind curlimages/curl:8.15.0 \
-H 'Host: helloworld-go.default.example.com' \
"http://cluster1-control-plane:${NODE_PORT}"

Observed response:

Hello OCM canonical namespace verification!

The external and cluster-local HTTPRoutes both reported Accepted=True and ResolvedRefs=True.

7. Verify cleanup and recreation

Both management CRs were deleted:

kubectl --context kind-hub -n management-serving-hub \
delete knativeserving ocm-serving
kubectl --context kind-hub -n management-eventing-hub \
delete knativeeventing ocm-eventing

Both CRs completed finalization. Their anchors and all anchor-owned Deployments were removed from cluster1/knative-serving and cluster2/knative-eventing. After recreating the same management CRs, both returned to TargetClusterResolved=True and Ready=True, and the Serving request returned the same response again.

Upgrade note

Remote installations created before this change used the management CR namespace on the spoke. If an existing remote installation is not already in its canonical namespace:

  1. Delete the management CR with the old operator and wait for finalization to remove the spoke resources.
  2. Upgrade the operator.
  3. Recreate the same management CR on the hub.

The recreated installation is placed in knative-serving or knative-eventing. No migration is required when the existing remote installation already uses its canonical namespace.

Release Note

action required: Remote KnativeServing and KnativeEventing installations now use the canonical knative-serving and knative-eventing namespaces instead of the management CR namespace. Delete non-canonical remote installations with the old operator before upgrading, then recreate their management CRs.

@knative-prowknative-prowBot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Aug 26, 2026
@codecov

codecovBot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.95%. Comparing base (7f45332) to head (e31a55d).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #2349 +/- ##
==========================================
+ Coverage 65.84% 65.95% +0.11% 
==========================================
Files 56 56 Lines 2673 2682 +9 ==========================================
+ Hits 1760 1769 +9 
Misses 777 777 Partials 136 136 

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kahirokunn

Copy link
Copy Markdown
MemberAuthor

I plan to implement a separate validation rule to prevent the creation of more than one KnativeServing or KnativeEventing instance per cluster.

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from a1af5ee to fe54616CompareAugust 26, 2026 11:50
@kahirokunn

Copy link
Copy Markdown
MemberAuthor

@dsimansk Hi ✋ PTAL. Thank you 🙏

@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from fe54616 to 7ce0e6fCompareAugust 27, 2026 02:23
Comment threadpkg/reconciler/common/multicluster.go
Comment threaddocs/multicluster.md
@knative-prow-robotknative-prow-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
Signed-off-by: kahirokunn <okinakahiro@gmail.com>
@kahirokunn
kahirokunnforce-pushed the canonical-remote-namespaces branch from 7ce0e6f to e31a55dCompareAugust 27, 2026 12:41
@knative-prow-robotknative-prow-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Aug 27, 2026
@dsimansk

Copy link
Copy Markdown
Contributor

/approve
/lgtm

@knative-prowknative-prowBot added the lgtm Indicates that a PR is ready to be merged. label Aug 27, 2026
@knative-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: dsimansk, kahirokunn

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prowknative-prowBot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Aug 27, 2026
@knative-prow
knative-prowBot merged commit bd7bb89 into knative:mainAug 27, 2026
26 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by an approver from all required OWNERS files.lgtmIndicates that a PR is ready to be merged.size/LDenotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install remote Knative components in canonical namespaces

3 participants

@kahirokunn@dsimansk@knative-prow-robot