Files
fission-src/pkg/utils/namespace.go
T

211 lines
5.6 KiB
Go

package utils
import (
"os"
"sort"
"strings"
"sync"
"go.uber.org/zap"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/fission/fission/pkg/utils/loggerfactory"
)
const (
ENV_FUNCTION_NAMESPACE string = "FISSION_FUNCTION_NAMESPACE"
ENV_BUILDER_NAMESPACE string = "FISSION_BUILDER_NAMESPACE"
ENV_DEFAULT_NAMESPACE string = "FISSION_DEFAULT_NAMESPACE"
ENV_ADDITIONAL_NAMESPACE string = "FISSION_RESOURCE_NAMESPACES"
)
type (
NamespaceResolver struct {
mu sync.RWMutex // protects FissionResourceNS
FunctionNamespace string
BuilderNamespace string
DefaultNamespace string
FissionResourceNS map[string]string
Logger *zap.Logger
}
options struct {
functionNS bool
builderNS bool
defaultNs bool
}
option func(options *options) *options
)
var nsResolver *NamespaceResolver
func init() {
nsResolver = &NamespaceResolver{
FunctionNamespace: os.Getenv(ENV_FUNCTION_NAMESPACE),
BuilderNamespace: os.Getenv(ENV_BUILDER_NAMESPACE),
DefaultNamespace: os.Getenv(ENV_DEFAULT_NAMESPACE),
FissionResourceNS: GetNamespaces(),
Logger: loggerfactory.GetLogger(),
}
nsResolver.Logger.Debug("namespaces", zap.String("function_namespace", nsResolver.FunctionNamespace),
zap.String("builder_namespace", nsResolver.BuilderNamespace),
zap.String("default_namespace", nsResolver.DefaultNamespace),
zap.Any("fission_resource_namespace", listNamespaces(nsResolver.FissionResourceNS)))
}
// listNamespaces => convert namespaces from map to slice
func listNamespaces(namespaces map[string]string) []string {
ns := make([]string, 0)
for _, namespace := range namespaces {
ns = append(ns, namespace)
}
return ns
}
func WithBuilderNs() option {
return func(options *options) *options {
options.builderNS = true
return options
}
}
func WithFunctionNs() option {
return func(options *options) *options {
options.functionNS = true
return options
}
}
func WithDefaultNs() option {
return func(options *options) *options {
options.defaultNs = true
return options
}
}
// AddNamespace dynamically adds a namespace to FissionResourceNS without restarting the process.
// Returns true if the namespace was newly added, false if it was already present.
// Thread-safe — multiple goroutines may call this concurrently.
// The label fission.io/managed=true on the Namespace object is the trigger for this call.
func (nsr *NamespaceResolver) AddNamespace(ns string) bool {
nsr.mu.Lock()
defer nsr.mu.Unlock()
if _, exists := nsr.FissionResourceNS[ns]; exists {
return false
}
nsr.FissionResourceNS[ns] = ns
if nsr.Logger != nil {
nsr.Logger.Info("dynamically added namespace to resolver", zap.String("namespace", ns))
}
return true
}
// Snapshot returns a stable copy of the currently registered resource namespaces.
// The returned slice is detached from the internal mutable map and safe to iterate.
func (nsr *NamespaceResolver) Snapshot() []string {
nsr.mu.RLock()
defer nsr.mu.RUnlock()
namespaces := listNamespaces(nsr.FissionResourceNS)
sort.Strings(namespaces)
return namespaces
}
func (nsr *NamespaceResolver) FissionNSWithOptions(option ...option) map[string]string {
var options options
for _, opt := range option {
options = *opt(&options)
}
nsr.mu.RLock()
fissionResourceNS := make(map[string]string, len(nsr.FissionResourceNS))
for k, v := range nsr.FissionResourceNS {
fissionResourceNS[k] = v
}
nsr.mu.RUnlock()
if options.functionNS && nsr.FunctionNamespace != "" {
fissionResourceNS[nsr.FunctionNamespace] = nsr.FunctionNamespace
}
if options.builderNS && nsr.BuilderNamespace != "" {
fissionResourceNS[nsr.BuilderNamespace] = nsr.BuilderNamespace
}
if options.defaultNs && nsr.DefaultNamespace != "" {
fissionResourceNS[nsr.DefaultNamespace] = nsr.DefaultNamespace
}
if nsr.Logger != nil {
nsr.Logger.Debug("fission resource namespaces", zap.Any("namespaces", listNamespaces(fissionResourceNS)))
}
return fissionResourceNS
}
// SnapshotWithOptions returns a stable slice copy of Fission namespaces after applying
// optional builder/function/default namespace expansion.
func (nsr *NamespaceResolver) SnapshotWithOptions(option ...option) []string {
namespaces := listNamespaces(nsr.FissionNSWithOptions(option...))
sort.Strings(namespaces)
return namespaces
}
func GetNamespaces() map[string]string {
namespaces := make(map[string]string)
envValue := os.Getenv(ENV_DEFAULT_NAMESPACE)
if len(envValue) > 0 {
namespaces[envValue] = envValue
}
envValue = os.Getenv(ENV_ADDITIONAL_NAMESPACE)
if len(envValue) > 0 {
lstNamespaces := strings.Split(envValue, ",")
for _, namespace := range lstNamespaces {
// check to handle string with additional comma at the end of string. eg- ns1,ns2,
if namespace != "" {
namespaces[namespace] = namespace
}
}
}
if len(namespaces) == 0 {
namespaces[metav1.NamespaceDefault] = metav1.NamespaceDefault
}
return namespaces
}
func (nsr *NamespaceResolver) GetBuilderNS(namespace string) string {
if nsr.BuilderNamespace == "" {
return namespace
}
if namespace != metav1.NamespaceDefault {
return namespace
}
return nsr.BuilderNamespace
}
func (nsr *NamespaceResolver) GetFunctionNS(namespace string) string {
if nsr.FunctionNamespace == "" {
return namespace
}
if namespace != metav1.NamespaceDefault {
return namespace
}
return nsr.FunctionNamespace
}
func (nsr *NamespaceResolver) ResolveNamespace(namespace string) string {
if nsr.FunctionNamespace == "" || nsr.BuilderNamespace == "" {
return nsr.DefaultNamespace
}
return namespace
}
// GetFissionNamespaces => return all fission core component namespaces
func DefaultNSResolver() *NamespaceResolver {
return nsResolver
}