diff --git a/docs/docs/namespaces.md b/docs/docs/namespaces.md index d4a436c4e..18a88f9c8 100644 --- a/docs/docs/namespaces.md +++ b/docs/docs/namespaces.md @@ -31,12 +31,13 @@ legit cases where it's handy to have them span multiple namespaces, for example: Some resources in Kubernetes are cluster-wide, meaning they don't belong to a single namespace at all. -To Tanka these appear as _Scenario 1 (see above)_, so it will set the default -namespace. In reality however, this is **not a problem**, because `kubectl` -discards this information silently. We made this design-choice, because it -simplifies our code a lot. +Tanka will make an attempt to not add namespaces to *known* cluster-wide types. +It does this with a short list of types in [the source code](https://github.com/grafana/tanka/blob/master/pkg/process/namespace.go). -In case this ever becomes a problem, you can **override this** behavior +Tanka cannot feasably maintain this list for all known custom resource types. In those cases, resources will have namespaces added to their manifests, +and kubectl should happily apply them as non-namespaced resources. + +If this presents a problem for your workflow, you can **override this** behavior per-resource, by setting the `tanka.dev/namespaced` annotation to `"false"` (must be of `string` type): diff --git a/pkg/process/namespace.go b/pkg/process/namespace.go index e29176a7d..d8c7fff12 100644 --- a/pkg/process/namespace.go +++ b/pkg/process/namespace.go @@ -10,6 +10,36 @@ const ( AnnotationNamespaced = MetadataPrefix + "/namespaced" ) +// This is a list of "cluster-wide" resources harvested from `kubectl api-resources --namespaced=false` +// This helps us to know which objects we should NOT apply namespaces to automatically. +// We can add to this list periodically if new types are added. +// This only applies to built-in kubernetes types. CRDs will need to be handled with annotations. +var clusterWideKinds = map[string]bool{ + "APIService": true, + "CertificateSigningRequest": true, + "ClusterRole": true, + "ClusterRoleBinding": true, + "ComponentStatus": true, + "CSIDriver": true, + "CSINode": true, + "CustomResourceDefinition": true, + "MutatingWebhookConfiguration": true, + "Namespace": true, + "Node": true, + "NodeMetrics": true, + "PersistentVolume": true, + "PodSecurityPolicy": true, + "PriorityClass": true, + "RuntimeClass": true, + "SelfSubjectAccessReview": true, + "SelfSubjectRulesReview": true, + "StorageClass": true, + "SubjectAccessReview": true, + "TokenReview": true, + "ValidatingWebhookConfiguration": true, + "VolumeAttachment": true, +} + // Namespace injects the default namespace of the environment into each // resources, that does not already define one. AnnotationNamespaced can be used // to disable this per resource @@ -20,7 +50,9 @@ func Namespace(list manifest.List, def string) manifest.List { for i, m := range list { namespaced := true - + if clusterWideKinds[m.Kind()] { + namespaced = false + } // check for annotation override if s, ok := m.Metadata().Annotations()[AnnotationNamespaced]; ok { namespaced = s == "true"