diff --git a/deploy/crd/kcp.io/syncagent.kcp.io_publishedresources.yaml b/deploy/crd/kcp.io/syncagent.kcp.io_publishedresources.yaml index 63c39acc..738586ca 100644 --- a/deploy/crd/kcp.io/syncagent.kcp.io_publishedresources.yaml +++ b/deploy/crd/kcp.io/syncagent.kcp.io_publishedresources.yaml @@ -266,6 +266,10 @@ spec: This guarantees unique names as long as the cluster name is used for the local namespace (the default unless configured otherwise). + For cluster-scoped objects, which have no namespace, the default value is: + + "{{ .ClusterName | sha3short }}-{{ .Object.metadata.name | sha3short }}" + This value is a Go template, see the documentation for the available variables and functions. Alternatively (but deprecated), this value can be a simple string using one of the following diff --git a/docs/content/publish-resources/index.md b/docs/content/publish-resources/index.md index cb730ac9..55e38a3a 100644 --- a/docs/content/publish-resources/index.md +++ b/docs/content/publish-resources/index.md @@ -157,6 +157,10 @@ This configuration ensures that no collisions will happen: Each workspace in kcp will create a namespace on the local cluster, with a combination of namespace and name hashes used for the actual resource names. +Cluster-scoped objects have no namespace, so for them the default name is +{% raw %}`{{ .ClusterName | sha3short }}-{{ .Object.metadata.name | sha3short }}`{% endraw %} instead. This keeps +names unique even when the local copy is cluster-scoped as well. + You can override the name or namespaces rules, or both. It is your responsibility to ensure no naming conflicts can happen on the service cluster, as the agent cannot determine this automatically. diff --git a/internal/sync/templating/naming.go b/internal/sync/templating/naming.go index 32058b3e..3a1b09fc 100644 --- a/internal/sync/templating/naming.go +++ b/internal/sync/templating/naming.go @@ -53,6 +53,11 @@ var defaultNamingScheme = syncagentv1alpha1.ResourceNaming{ Name: "{{ .Object.metadata.namespace | sha3short }}-{{ .Object.metadata.name | sha3short }}", } +// defaultClusterScopedName is the default name for cluster-scoped remote objects. They have +// no namespace to hash, and their local copy can be cluster-scoped too (so the cluster name +// namespace is dropped), so the cluster name is part of the name instead. +const defaultClusterScopedName = "{{ .ClusterName | sha3short }}-{{ .Object.metadata.name | sha3short }}" + func GenerateLocalObjectName(pr *syncagentv1alpha1.PublishedResource, object *unstructured.Unstructured, clusterName logicalcluster.Name, workspacePath logicalcluster.Path) (types.NamespacedName, error) { naming := pr.Spec.Naming if naming == nil { @@ -75,6 +80,9 @@ func GenerateLocalObjectName(pr *syncagentv1alpha1.PublishedResource, object *un pattern = naming.Name if pattern == "" { pattern = defaultNamingScheme.Name + if object.GetNamespace() == "" { + pattern = defaultClusterScopedName + } } rendered, err = generateLocalObjectIdentifier(pattern, object, clusterName, workspacePath) if err != nil { diff --git a/internal/sync/templating/naming_test.go b/internal/sync/templating/naming_test.go index 471ae634..6cc1ec9e 100644 --- a/internal/sync/templating/naming_test.go +++ b/internal/sync/templating/naming_test.go @@ -51,6 +51,20 @@ func TestGenerateLocalObjectName(t *testing.T) { namingConfig: nil, expected: types.NamespacedName{Namespace: "testcluster", Name: "2928c2aa0e510f017f07-73d08a1ba188f1340dae"}, }, + { + name: "follow default naming rules for cluster-scoped objects", + clusterName: "testcluster", + remoteObject: createNewObject("objname", ""), + namingConfig: nil, + expected: types.NamespacedName{Namespace: "testcluster", Name: "4f96fcff7885055b00ad-73d08a1ba188f1340dae"}, + }, + { + name: "custom namespace pattern for cluster-scoped objects keeps the default name", + clusterName: "testcluster", + remoteObject: createNewObject("objname", ""), + namingConfig: &syncagentv1alpha1.ResourceNaming{Namespace: "foobar"}, + expected: types.NamespacedName{Namespace: "foobar", Name: "4f96fcff7885055b00ad-73d08a1ba188f1340dae"}, + }, { name: "custom static namespace pattern", clusterName: "testcluster", diff --git a/sdk/apis/syncagent/v1alpha1/published_resource.go b/sdk/apis/syncagent/v1alpha1/published_resource.go index 0e6fdde9..5dc4e5be 100644 --- a/sdk/apis/syncagent/v1alpha1/published_resource.go +++ b/sdk/apis/syncagent/v1alpha1/published_resource.go @@ -115,6 +115,10 @@ type ResourceNaming struct { // This guarantees unique names as long as the cluster name is used for the local namespace // (the default unless configured otherwise). // + // For cluster-scoped objects, which have no namespace, the default value is: + // + // "{{ .ClusterName | sha3short }}-{{ .Object.metadata.name | sha3short }}" + // // This value is a Go template, see the documentation for the available variables and functions. // // Alternatively (but deprecated), this value can be a simple string using one of the following