From 3a91a097a00cdb7bb60192465dda5e3f9dcedd40 Mon Sep 17 00:00:00 2001 From: Satyanarayana Kolluri Date: Thu, 20 Nov 2025 14:58:13 +0530 Subject: [PATCH 1/2] Initial implementation of env pkg --- cmd/syncer/main.go | 16 +++++-- cmd/vsphere-csi/main.go | 20 ++++++--- pkg/common/env/env.go | 95 +++++++++++++++++++++++++++++++++++++++ pkg/csi/service/driver.go | 25 +++++++++-- pkg/csi/service/server.go | 9 +++- 5 files changed, 151 insertions(+), 14 deletions(-) create mode 100644 pkg/common/env/env.go diff --git a/cmd/syncer/main.go b/cmd/syncer/main.go index cf802b653b..dda2f11ba3 100644 --- a/cmd/syncer/main.go +++ b/cmd/syncer/main.go @@ -38,6 +38,7 @@ import ( "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/cns-lib/node" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/config" + "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/env" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/prometheus" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/utils" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/common" @@ -96,7 +97,15 @@ func main() { fmt.Printf("%s\n", syncer.Version) return } - logType := logger.LogLevel(os.Getenv(logger.EnvLoggerLevel)) + + // Load startup environment variables + startupEnv, err := env.LoadStartupEnv(context.Background()) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to load startup environment: %v\n", err) + os.Exit(1) + } + + logType := logger.LogLevel(startupEnv.LoggerLevel) logger.SetLoggerLevel(logType) ctx, log := logger.GetNewContextWithLogger() log.Infof("Version : %s", syncer.Version) @@ -473,8 +482,9 @@ func sanitizeName(name string) string { // the env var POD_NAMESPACE, then the file /var/run/secrets/kubernetes.io/serviceaccount/namespace. // if neither returns a valid namespace, the "default" namespace is returned func inClusterNamespace() string { - if ns := os.Getenv("POD_NAMESPACE"); ns != "" { - return ns + // Try to get from centralized env first + if startupEnv, err := env.GetStartupEnv(); err == nil && startupEnv.PodNamespace != "" { + return startupEnv.PodNamespace } if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil { diff --git a/cmd/vsphere-csi/main.go b/cmd/vsphere-csi/main.go index f0b1a2507b..28b66b8658 100644 --- a/cmd/vsphere-csi/main.go +++ b/cmd/vsphere-csi/main.go @@ -27,11 +27,11 @@ import ( "syscall" csiconfig "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/config" + "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/env" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/utils" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/common/commonco" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger" - csitypes "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/types" ) var ( @@ -53,7 +53,15 @@ func main() { fmt.Printf("%s\n", service.Version) return } - logType := logger.LogLevel(os.Getenv(logger.EnvLoggerLevel)) + + // Load startup environment variables + startupEnv, err := env.LoadStartupEnv(context.Background()) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to load startup environment: %v\n", err) + os.Exit(1) + } + + logType := logger.LogLevel(startupEnv.LoggerLevel) logger.SetLoggerLevel(logType) ctx, log := logger.GetNewContextWithLogger() log.Infof("Version : %s", service.Version) @@ -73,13 +81,11 @@ func main() { if err != nil { log.Errorf("failed retrieving the cluster flavor. Error: %v", err) } - serviceMode := os.Getenv(csitypes.EnvVarMode) commonco.SetInitParams(ctx, clusterFlavor, &service.COInitParams, *supervisorFSSName, *supervisorFSSNamespace, - *internalFSSName, *internalFSSNamespace, serviceMode, "") + *internalFSSName, *internalFSSNamespace, startupEnv.CSIMode, "") // If no endpoint is set then exit the program. - CSIEndpoint := os.Getenv(csitypes.EnvVarEndpoint) - if CSIEndpoint == "" { + if startupEnv.CSIEndpoint == "" { log.Error("CSI endpoint cannot be empty. Please set the env variable.") os.Exit(1) } @@ -106,7 +112,7 @@ func main() { }() vSphereCSIDriver := service.NewDriver() - vSphereCSIDriver.Run(ctx, CSIEndpoint) + vSphereCSIDriver.Run(ctx, startupEnv.CSIEndpoint) } diff --git a/pkg/common/env/env.go b/pkg/common/env/env.go new file mode 100644 index 0000000000..0ad2c425db --- /dev/null +++ b/pkg/common/env/env.go @@ -0,0 +1,95 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package env + +import ( + "context" + "fmt" + "os" + + "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger" +) + +// StartupEnv holds all environment variables read at service startup. +type StartupEnv struct { + // LoggerLevel specifies the logging level (PRODUCTION or DEVELOPMENT) + LoggerLevel string + + // CSIEndpoint specifies the CSI endpoint for CSI driver + CSIEndpoint string + + // CSIMode specifies the service mode (controller or node) + CSIMode string + + // ClusterFlavor specifies the cluster flavor (VANILLA, WORKLOAD, or GUEST) + ClusterFlavor string + + // PodNamespace specifies the namespace where the pod is running + PodNamespace string +} + +var ( + // globalStartupEnv holds the loaded startup environment variables + globalStartupEnv *StartupEnv +) + +// LoadStartupEnv reads and validates all startup environment variables. +// This should be called once at service initialization. +func LoadStartupEnv(ctx context.Context) (*StartupEnv, error) { + log := logger.GetLogger(ctx) + + env := &StartupEnv{ + LoggerLevel: os.Getenv("LOGGER_LEVEL"), + CSIEndpoint: os.Getenv("CSI_ENDPOINT"), + CSIMode: os.Getenv("X_CSI_MODE"), + ClusterFlavor: os.Getenv("CLUSTER_FLAVOR"), + PodNamespace: os.Getenv("POD_NAMESPACE"), + } + + // Validate required environment variables for CSI driver + // Note: CSI_ENDPOINT is required for vsphere-csi but not for syncer + // The validation is handled by the caller based on service type + + // Store globally for access by other components + globalStartupEnv = env + + // Log loaded configuration (without sensitive values) + log.Infof("Loaded startup environment: LoggerLevel=%q, CSIEndpoint=%q, CSIMode=%q, ClusterFlavor=%q, PodNamespace=%q", + env.LoggerLevel, maskValue(env.CSIEndpoint), env.CSIMode, env.ClusterFlavor, env.PodNamespace) + + return env, nil +} + +// GetStartupEnv returns the globally loaded startup environment. +// Returns an error if LoadStartupEnv has not been called yet. +func GetStartupEnv() (*StartupEnv, error) { + if globalStartupEnv == nil { + return nil, fmt.Errorf("startup environment not loaded, call LoadStartupEnv first") + } + return globalStartupEnv, nil +} + +// maskValue masks a value for logging, showing only the first few characters. +func maskValue(value string) string { + if value == "" { + return "" + } + if len(value) <= 10 { + return "***" + } + return value[:10] + "***" +} diff --git a/pkg/csi/service/driver.go b/pkg/csi/service/driver.go index a2b7e8620d..1a2603f90c 100644 --- a/pkg/csi/service/driver.go +++ b/pkg/csi/service/driver.go @@ -27,6 +27,7 @@ import ( "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/cns-lib/node" cnsconfig "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/config" + "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/env" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/common" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/common/commonco" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger" @@ -74,7 +75,13 @@ type vsphereCSIDriver struct { // If k8s node died unexpectedly in an earlier run, the unix socket is left // behind. This method will clean up the sock file during initialization. func init() { - sockPath := os.Getenv(csitypes.EnvVarEndpoint) + // Try to get from centralized env, fallback to direct os.Getenv for backward compatibility + var sockPath string + if startupEnv, err := env.GetStartupEnv(); err == nil { + sockPath = startupEnv.CSIEndpoint + } else { + sockPath = os.Getenv(csitypes.EnvVarEndpoint) + } sockPath = strings.TrimPrefix(sockPath, UnixSocketPrefix) if len(sockPath) > 1 { // Minimal valid path length. os.Remove(sockPath) @@ -90,7 +97,14 @@ func NewDriver() Driver { func (driver *vsphereCSIDriver) GetController() csi.ControllerServer { // Check which controller type to use. - clusterFlavor = cnstypes.CnsClusterFlavor(os.Getenv(cnsconfig.EnvClusterFlavor)) + // Try to get from centralized env, fallback to direct os.Getenv for backward compatibility + var clusterFlavorStr string + if startupEnv, err := env.GetStartupEnv(); err == nil { + clusterFlavorStr = startupEnv.ClusterFlavor + } else { + clusterFlavorStr = os.Getenv(cnsconfig.EnvClusterFlavor) + } + clusterFlavor = cnstypes.CnsClusterFlavor(clusterFlavorStr) switch clusterFlavor { case cnstypes.CnsClusterFlavorWorkload: driver.cnscs = wcp.New() @@ -130,7 +144,12 @@ func (driver *vsphereCSIDriver) BeforeServe(ctx context.Context) error { } // Get the SP's operating mode. - driver.mode = os.Getenv(csitypes.EnvVarMode) + // Try to get from centralized env, fallback to direct os.Getenv for backward compatibility + if startupEnv, err := env.GetStartupEnv(); err == nil { + driver.mode = startupEnv.CSIMode + } else { + driver.mode = os.Getenv(csitypes.EnvVarMode) + } // Create OsUtils for node driver driver.osUtils, err = osutils.NewOsUtils(ctx) if err != nil { diff --git a/pkg/csi/service/server.go b/pkg/csi/service/server.go index f8f2284b19..0969283381 100644 --- a/pkg/csi/service/server.go +++ b/pkg/csi/service/server.go @@ -24,6 +24,7 @@ import ( "github.com/container-storage-interface/spec/lib/go/csi" "google.golang.org/grpc" + "sigs.k8s.io/vsphere-csi-driver/v3/pkg/common/env" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger" csitypes "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/types" @@ -131,7 +132,13 @@ func (s *nonBlockingGRPCServer) serve(endpoint string, ids csi.IdentityServer, log.Info("identity service registered") // Determine which of the controller/node services to register. - mode := os.Getenv(csitypes.EnvVarMode) + // Try to get from centralized env, fallback to direct os.Getenv for backward compatibility + var mode string + if startupEnv, err := env.GetStartupEnv(); err == nil { + mode = startupEnv.CSIMode + } else { + mode = os.Getenv(csitypes.EnvVarMode) + } if strings.EqualFold(mode, "controller") { if cs == nil { return logger.LogNewError(log, "controller service required when running in controller mode") From db5a0b4fd3b1b7e987099ef07a6ab8e1851c0e0d Mon Sep 17 00:00:00 2001 From: Satyanarayana Kolluri Date: Thu, 20 Nov 2025 15:09:28 +0530 Subject: [PATCH 2/2] Refactored the env pkg --- cmd/syncer/main.go | 9 +++------ cmd/vsphere-csi/main.go | 6 +----- pkg/common/env/env.go | 20 ++++++++------------ pkg/csi/service/driver.go | 9 ++++++--- pkg/csi/service/server.go | 3 ++- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/cmd/syncer/main.go b/cmd/syncer/main.go index dda2f11ba3..eaea62e288 100644 --- a/cmd/syncer/main.go +++ b/cmd/syncer/main.go @@ -99,11 +99,7 @@ func main() { } // Load startup environment variables - startupEnv, err := env.LoadStartupEnv(context.Background()) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to load startup environment: %v\n", err) - os.Exit(1) - } + startupEnv := env.Load(context.Background()) logType := logger.LogLevel(startupEnv.LoggerLevel) logger.SetLoggerLevel(logType) @@ -483,7 +479,8 @@ func sanitizeName(name string) string { // if neither returns a valid namespace, the "default" namespace is returned func inClusterNamespace() string { // Try to get from centralized env first - if startupEnv, err := env.GetStartupEnv(); err == nil && startupEnv.PodNamespace != "" { + startupEnv := env.GetStartupEnv() + if startupEnv.PodNamespace != "" { return startupEnv.PodNamespace } diff --git a/cmd/vsphere-csi/main.go b/cmd/vsphere-csi/main.go index 28b66b8658..c88fd6af2f 100644 --- a/cmd/vsphere-csi/main.go +++ b/cmd/vsphere-csi/main.go @@ -55,11 +55,7 @@ func main() { } // Load startup environment variables - startupEnv, err := env.LoadStartupEnv(context.Background()) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to load startup environment: %v\n", err) - os.Exit(1) - } + startupEnv := env.Load(context.Background()) logType := logger.LogLevel(startupEnv.LoggerLevel) logger.SetLoggerLevel(logType) diff --git a/pkg/common/env/env.go b/pkg/common/env/env.go index 0ad2c425db..7a02159c4d 100644 --- a/pkg/common/env/env.go +++ b/pkg/common/env/env.go @@ -18,7 +18,6 @@ package env import ( "context" - "fmt" "os" "sigs.k8s.io/vsphere-csi-driver/v3/pkg/csi/service/logger" @@ -44,15 +43,15 @@ type StartupEnv struct { var ( // globalStartupEnv holds the loaded startup environment variables - globalStartupEnv *StartupEnv + globalStartupEnv StartupEnv ) -// LoadStartupEnv reads and validates all startup environment variables. +// Load reads and validates all startup environment variables. // This should be called once at service initialization. -func LoadStartupEnv(ctx context.Context) (*StartupEnv, error) { +func Load(ctx context.Context) StartupEnv { log := logger.GetLogger(ctx) - env := &StartupEnv{ + env := StartupEnv{ LoggerLevel: os.Getenv("LOGGER_LEVEL"), CSIEndpoint: os.Getenv("CSI_ENDPOINT"), CSIMode: os.Getenv("X_CSI_MODE"), @@ -71,16 +70,13 @@ func LoadStartupEnv(ctx context.Context) (*StartupEnv, error) { log.Infof("Loaded startup environment: LoggerLevel=%q, CSIEndpoint=%q, CSIMode=%q, ClusterFlavor=%q, PodNamespace=%q", env.LoggerLevel, maskValue(env.CSIEndpoint), env.CSIMode, env.ClusterFlavor, env.PodNamespace) - return env, nil + return env } // GetStartupEnv returns the globally loaded startup environment. -// Returns an error if LoadStartupEnv has not been called yet. -func GetStartupEnv() (*StartupEnv, error) { - if globalStartupEnv == nil { - return nil, fmt.Errorf("startup environment not loaded, call LoadStartupEnv first") - } - return globalStartupEnv, nil +// Returns an error if Load has not been called yet. +func GetStartupEnv() StartupEnv { + return globalStartupEnv } // maskValue masks a value for logging, showing only the first few characters. diff --git a/pkg/csi/service/driver.go b/pkg/csi/service/driver.go index 1a2603f90c..c0ff0ed019 100644 --- a/pkg/csi/service/driver.go +++ b/pkg/csi/service/driver.go @@ -77,7 +77,8 @@ type vsphereCSIDriver struct { func init() { // Try to get from centralized env, fallback to direct os.Getenv for backward compatibility var sockPath string - if startupEnv, err := env.GetStartupEnv(); err == nil { + startupEnv := env.GetStartupEnv() + if startupEnv.CSIEndpoint != "" { sockPath = startupEnv.CSIEndpoint } else { sockPath = os.Getenv(csitypes.EnvVarEndpoint) @@ -99,7 +100,8 @@ func (driver *vsphereCSIDriver) GetController() csi.ControllerServer { // Check which controller type to use. // Try to get from centralized env, fallback to direct os.Getenv for backward compatibility var clusterFlavorStr string - if startupEnv, err := env.GetStartupEnv(); err == nil { + startupEnv := env.GetStartupEnv() + if startupEnv.ClusterFlavor != "" { clusterFlavorStr = startupEnv.ClusterFlavor } else { clusterFlavorStr = os.Getenv(cnsconfig.EnvClusterFlavor) @@ -145,7 +147,8 @@ func (driver *vsphereCSIDriver) BeforeServe(ctx context.Context) error { // Get the SP's operating mode. // Try to get from centralized env, fallback to direct os.Getenv for backward compatibility - if startupEnv, err := env.GetStartupEnv(); err == nil { + startupEnv := env.GetStartupEnv() + if startupEnv.CSIMode != "" { driver.mode = startupEnv.CSIMode } else { driver.mode = os.Getenv(csitypes.EnvVarMode) diff --git a/pkg/csi/service/server.go b/pkg/csi/service/server.go index 0969283381..50a9e9e970 100644 --- a/pkg/csi/service/server.go +++ b/pkg/csi/service/server.go @@ -134,7 +134,8 @@ func (s *nonBlockingGRPCServer) serve(endpoint string, ids csi.IdentityServer, // Determine which of the controller/node services to register. // Try to get from centralized env, fallback to direct os.Getenv for backward compatibility var mode string - if startupEnv, err := env.GetStartupEnv(); err == nil { + startupEnv := env.GetStartupEnv() + if startupEnv.CSIMode != "" { mode = startupEnv.CSIMode } else { mode = os.Getenv(csitypes.EnvVarMode)