Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions cmd/syncer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -96,7 +97,11 @@ func main() {
fmt.Printf("%s\n", syncer.Version)
return
}
logType := logger.LogLevel(os.Getenv(logger.EnvLoggerLevel))

// Load startup environment variables
startupEnv := env.Load(context.Background())

logType := logger.LogLevel(startupEnv.LoggerLevel)
logger.SetLoggerLevel(logType)
ctx, log := logger.GetNewContextWithLogger()
log.Infof("Version : %s", syncer.Version)
Expand Down Expand Up @@ -473,8 +478,10 @@ 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
startupEnv := env.GetStartupEnv()
if startupEnv.PodNamespace != "" {
return startupEnv.PodNamespace
}

if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
Expand Down
16 changes: 9 additions & 7 deletions cmd/vsphere-csi/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -53,7 +53,11 @@ func main() {
fmt.Printf("%s\n", service.Version)
return
}
logType := logger.LogLevel(os.Getenv(logger.EnvLoggerLevel))

// Load startup environment variables
startupEnv := env.Load(context.Background())

logType := logger.LogLevel(startupEnv.LoggerLevel)
logger.SetLoggerLevel(logType)
ctx, log := logger.GetNewContextWithLogger()
log.Infof("Version : %s", service.Version)
Expand All @@ -73,13 +77,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)
}
Expand All @@ -106,7 +108,7 @@ func main() {
}()

vSphereCSIDriver := service.NewDriver()
vSphereCSIDriver.Run(ctx, CSIEndpoint)
vSphereCSIDriver.Run(ctx, startupEnv.CSIEndpoint)

}

Expand Down
91 changes: 91 additions & 0 deletions pkg/common/env/env.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
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"
"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
)

// Load reads and validates all startup environment variables.
// This should be called once at service initialization.
func Load(ctx context.Context) StartupEnv {
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
}

// GetStartupEnv returns the globally loaded startup environment.
// 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.
func maskValue(value string) string {
if value == "" {
return ""
}
if len(value) <= 10 {
return "***"
}
return value[:10] + "***"
}
28 changes: 25 additions & 3 deletions pkg/csi/service/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -74,7 +75,14 @@ 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
startupEnv := env.GetStartupEnv()
if startupEnv.CSIEndpoint != "" {
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)
Expand All @@ -90,7 +98,15 @@ 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
startupEnv := env.GetStartupEnv()
if startupEnv.ClusterFlavor != "" {
clusterFlavorStr = startupEnv.ClusterFlavor
} else {
clusterFlavorStr = os.Getenv(cnsconfig.EnvClusterFlavor)
}
clusterFlavor = cnstypes.CnsClusterFlavor(clusterFlavorStr)
switch clusterFlavor {
case cnstypes.CnsClusterFlavorWorkload:
driver.cnscs = wcp.New()
Expand Down Expand Up @@ -130,7 +146,13 @@ 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
startupEnv := env.GetStartupEnv()
if startupEnv.CSIMode != "" {
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 {
Expand Down
10 changes: 9 additions & 1 deletion pkg/csi/service/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -131,7 +132,14 @@ 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
startupEnv := env.GetStartupEnv()
if startupEnv.CSIMode != "" {
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")
Expand Down