common/pkg/ns/ns.go (87 lines of code) (raw):

// Package ns provides a way to map entities from one namespace into another. // The key idea here is that we can reduce the operator's privilege by only // granting it privileges in a separate namespace which is not the same as where // the operator's custom resources are created. package ns import ( "hash/fnv" "strings" ) // NamespaceMapper maps an entity namespace/name to another namespace/name. type NamespaceMapper interface { // DestName returns the destination name of a given namespace/name. DestName(srcNS, srcName string) string // DestNamespace returns the destination namespace for a given namespace. DestNamespace(srcNS string) string } // NewSameMapper returns a NamespaceMapper that maps to the same namespace/name. func NewSameMapper() NamespaceMapper { return &same{} } // NewRedirectMapper returns a NamespaceMapper that redirects all entities to // one select target namespace. // To avoid collision of the same name from different namespaces, the mapper // prefixes the source-namespace and appends a hash. // Long names may get truncated. // Examples: // destNs: "target" // { ns: "a", "name: "b" } -> { ns: "target", name: "a-b-hash" } // { ns: "", name: "c" } -> { ns: "target", name: "c-hash" } func NewRedirectMapper(destNS string) NamespaceMapper { return &redirectToAnother{targetNS: destNS} } // NewNSPrefixMapper returns a NamespaceMapper that redirects all entities to // another namespace with the specified prefix. The entity name doesn't change. // The destination namespace may have a prefix-hash appended at the end for // long names (with truncation). // Examples, for namespace prefix "pre" // { ns: "a", "name: "b" } -> { ns: "pre-a", name: "b" } // { ns: "", name: "c" } -> { ns: "pre", name: "c" } // { ns: "long-name", name: "c" } -> { ns: "pre-long-clipped-hash", name: "c" } func NewNSPrefixMapper(nsPrefix string) NamespaceMapper { return &prefixer{prefix: nsPrefix} } // NewPrefixSwappingNSMapper returns a NamespaceMapper that replaces the old // prefix of a namespace with a new prefix. If the namespace does not have the // old prefix, a new prefix will be added in front of the namespace. The entity name // doesn't change. The destination namespace may have a prefix-hash appended at // the end for long names (with truncation) // Examples, for an old prefix "g-" and a new prefix "gs-ods-" // { ns: "g-a", "name: "b" } -> { ns: "gs-ods-a", name: "b" } // { ns: "g-g-a", name: "c" } -> { ns: "gs-ods-g-a", name: "c" } // { ns: "a, name: "c" } -> { ns: "gs-ods-a", name: "c" } func NewPrefixSwappingNSMapper(oldPrefix, newPrefix string) NamespaceMapper { return &prefixSwapper{oldPre: oldPrefix, newPre: newPrefix} } type same struct{} func (*same) DestName(srcNS, srcName string) string { return srcName } func (*same) DestNamespace(srcNS string) string { return srcNS } type prefixer struct { prefix string } func (r *prefixer) DestName(srcNS, srcName string) string { return srcName } func (r *prefixer) DestNamespace(srcNS string) string { return munge(r.prefix, srcNS, 63, false) } type redirectToAnother struct { targetNS string } func (r *redirectToAnother) DestName(srcNS, srcName string) string { return munge(srcNS, srcName, 63, true) } func (r *redirectToAnother) DestNamespace(srcNS string) string { return r.targetNS } type prefixSwapper struct { oldPre string newPre string } func (p *prefixSwapper) DestName(srcNS, srcName string) string { return srcName } func (p *prefixSwapper) DestNamespace(srcNS string) string { destNS := p.newPre + strings.TrimPrefix(srcNS, p.oldPre) return munge("", destNS, 63, false) } func munge(s1, s2 string, limit int, alwaysAddHash bool) string { j := s1 + "-" + s2 if len(s1) == 0 { j = s2 } if len(s2) == 0 { j = s1 } if alwaysAddHash || len(j) >= limit { if len(j) > limit-suffixLen { j = j[:limit-suffixLen] } j = j + hashSuffix(s1+"/"+s2) } return j } var charset = []rune("0123456789abcdefghijklmnopqrstuvwxyz") const suffixLen = 14 func hashSuffix(s string) string { e := fnv.New64() e.Write([]byte(s)) i := e.Sum64() c := []rune{'-'} l := uint64(len(charset)) for i > 0 { c = append(c, charset[i%l]) i /= l } return string(c) }