diff --git a/packages/api/internal/cfg/model.go b/packages/api/internal/cfg/model.go index d92de26c55..1ea714c62c 100644 --- a/packages/api/internal/cfg/model.go +++ b/packages/api/internal/cfg/model.go @@ -66,8 +66,6 @@ type Config struct { DefaultKernelVersion string `env:"DEFAULT_KERNEL_VERSION"` - DefaultPersistentVolumeType string `env:"DEFAULT_PERSISTENT_VOLUME_TYPE"` - // SandboxStorageBackend selects the sandbox storage implementation. // "redis" uses Redis directly; "populate_redis" uses in-memory with Redis shadow writes. SandboxStorageBackend string `env:"SANDBOX_STORAGE_BACKEND" envDefault:"memory"` diff --git a/packages/api/internal/handlers/volume_create.go b/packages/api/internal/handlers/volume_create.go index f13bbb97ff..fa871937b8 100644 --- a/packages/api/internal/handlers/volume_create.go +++ b/packages/api/internal/handlers/volume_create.go @@ -65,7 +65,7 @@ func (a *APIStore) PostVolumes(c *gin.Context) { ctx = featureflags.AddToContext(ctx, featureflags.VolumeContext(body.Name)) - volumeType := a.getVolumeType(ctx) + volumeType := a.featureFlags.StringFlag(ctx, featureflags.DefaultPersistentVolumeType) if volumeType == "" { a.sendAPIStoreError(c, http.StatusInternalServerError, "No persistent volume type is configured") telemetry.ReportCriticalError(ctx, "default persistent volume type is not configured", nil) @@ -165,15 +165,6 @@ func (a *APIStore) PostVolumes(c *gin.Context) { c.JSON(http.StatusCreated, result) } -func (a *APIStore) getVolumeType(ctx context.Context) string { - volumeType := a.featureFlags.StringFlag(ctx, featureflags.DefaultPersistentVolumeType) - if volumeType == "" { - volumeType = a.config.DefaultPersistentVolumeType - } - - return volumeType -} - var validVolumeNameRegex = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`) func isValidVolumeName(name string) bool { diff --git a/packages/shared/pkg/featureflags/client.go b/packages/shared/pkg/featureflags/client.go index 242d6d920f..d15956ae31 100644 --- a/packages/shared/pkg/featureflags/client.go +++ b/packages/shared/pkg/featureflags/client.go @@ -3,6 +3,7 @@ package featureflags import ( "context" "os" + "reflect" "time" "github.com/launchdarkly/go-sdk-common/v3/ldcontext" @@ -108,6 +109,7 @@ func (c *Client) StringFlag(ctx context.Context, flag StringFlag, contexts ...ld type typedFlag[T any] interface { Key() string Fallback() T + FallbackOnZero() bool } func getFlag[T any]( @@ -128,9 +130,18 @@ func getFlag[T any]( logger.L().Warn(ctx, "error evaluating flag", zap.Error(err), zap.String("flag", flag.Key())) } + if flag.FallbackOnZero() && isZeroValue(value) { + return flag.Fallback() + } + return value } +// isZeroValue returns true if the value is the zero value for its type. +func isZeroValue[T any](v T) bool { + return reflect.ValueOf(&v).Elem().IsZero() +} + func (c *Client) Close(ctx context.Context) error { if c.ld == nil { return nil diff --git a/packages/shared/pkg/featureflags/flags.go b/packages/shared/pkg/featureflags/flags.go index 84416f6201..b755e43da3 100644 --- a/packages/shared/pkg/featureflags/flags.go +++ b/packages/shared/pkg/featureflags/flags.go @@ -3,6 +3,7 @@ package featureflags import ( "context" "fmt" + "os" "strings" "github.com/launchdarkly/go-sdk-common/v3/ldcontext" @@ -34,8 +35,9 @@ const ( // All flags must be defined here: https://app.launchdarkly.com/projects/default/flags/ type JSONFlag struct { - name string - fallback ldvalue.Value + name string + fallback ldvalue.Value + fallbackOnZero bool } func (f JSONFlag) Key() string { @@ -50,6 +52,10 @@ func (f JSONFlag) Fallback() ldvalue.Value { return f.fallback } +func (f JSONFlag) FallbackOnZero() bool { + return f.fallbackOnZero +} + func newJSONFlag(name string, fallback ldvalue.Value) JSONFlag { flag := JSONFlag{name: name, fallback: fallback} builder := launchDarklyOfflineStore.Flag(flag.name).ValueForAll(fallback) @@ -88,6 +94,10 @@ func (f BoolFlag) Fallback() bool { return f.fallback } +func (f BoolFlag) FallbackOnZero() bool { + return false +} + func newBoolFlag(name string, fallback bool) BoolFlag { flag := BoolFlag{name: name, fallback: fallback} builder := launchDarklyOfflineStore.Flag(flag.name).VariationForAll(fallback) @@ -122,8 +132,9 @@ var ( ) type IntFlag struct { - name string - fallback int + name string + fallback int + fallbackOnZero bool } func (f IntFlag) Key() string { @@ -138,6 +149,10 @@ func (f IntFlag) Fallback() int { return f.fallback } +func (f IntFlag) FallbackOnZero() bool { + return f.fallbackOnZero +} + func newIntFlag(name string, fallback int) IntFlag { flag := IntFlag{name: name, fallback: fallback} builder := launchDarklyOfflineStore.Flag(flag.name).ValueForAll(ldvalue.Int(fallback)) @@ -207,8 +222,9 @@ var ( ) type StringFlag struct { - name string - fallback string + name string + fallback string + fallbackOnEmptyString bool } func (f StringFlag) Key() string { @@ -223,6 +239,10 @@ func (f StringFlag) Fallback() string { return f.fallback } +func (f StringFlag) FallbackOnZero() bool { + return f.fallbackOnEmptyString +} + func newStringFlag(name string, fallback string) StringFlag { flag := StringFlag{name: name, fallback: fallback} builder := launchDarklyOfflineStore.Flag(flag.name).ValueForAll(ldvalue.String(fallback)) @@ -231,6 +251,13 @@ func newStringFlag(name string, fallback string) StringFlag { return flag } +func newStringFlagFallbackOnEmptyString(name string, fallback string) StringFlag { + flag := newStringFlag(name, fallback) + flag.fallbackOnEmptyString = true + + return flag +} + // This is currently not configurable via feature flags. const ( DefaultKernelVersion = "vmlinux-6.1.158" @@ -251,9 +278,9 @@ var FirecrackerVersionMap = map[string]string{ // BuildIoEngine Sync is used by default as there seems to be a bad interaction between Async and a lot of io operations. var ( - BuildFirecrackerVersion = newStringFlag("build-firecracker-version", env.GetEnv("DEFAULT_FIRECRACKER_VERSION", DefaultFirecrackerVersion)) + BuildFirecrackerVersion = newStringFlagFallbackOnEmptyString("build-firecracker-version", env.GetEnv("DEFAULT_FIRECRACKER_VERSION", DefaultFirecrackerVersion)) BuildIoEngine = newStringFlag("build-io-engine", "Sync") - DefaultPersistentVolumeType = newStringFlag("default-persistent-volume-type", "") + DefaultPersistentVolumeType = newStringFlagFallbackOnEmptyString("default-persistent-volume-type", os.Getenv("DEFAULT_PERSISTENT_VOLUME_TYPE")) BuildNodeInfo = newJSONFlag("preferred-build-node", ldvalue.Null()) FirecrackerVersions = newJSONFlag("firecracker-versions", ldvalue.FromJSONMarshal(FirecrackerVersionMap)) )