瀏覽代碼

POR-1503: allow fallback to the database for feature flags (#3575)

jose-fully-ported 2 年之前
父節點
當前提交
116f97f061

+ 2 - 0
api/server/shared/config/env/envconfs.go

@@ -55,6 +55,8 @@ type ServerConf struct {
 	GoogleClientSecret     string `env:"GOOGLE_CLIENT_SECRET"`
 	GoogleRestrictedDomain string `env:"GOOGLE_RESTRICTED_DOMAIN"`
 
+	// FeatureFlagClient controls which client to use (database or launch_darkly)
+	FeatureFlagClient  string `env:"FEATURE_FLAG_CLIENT,default=launch_darkly"`
 	LaunchDarklySDKKey string `env:"LAUNCHDARKLY_SDK_KEY"`
 
 	SendgridAPIKey                     string `env:"SENDGRID_API_KEY"`

+ 1 - 1
api/server/shared/config/loader/loader.go

@@ -244,7 +244,7 @@ func (e *EnvConfigLoader) LoadConfig() (res *config.Config, err error) {
 		sc.GithubAppSecret = append(sc.GithubAppSecret, secret...)
 	}
 
-	launchDarklyClient, err := features.GetClient(envConf.ServerConf.LaunchDarklySDKKey)
+	launchDarklyClient, err := features.GetClient(envConf.ServerConf.FeatureFlagClient, envConf.ServerConf.LaunchDarklySDKKey)
 	if err != nil {
 		return nil, fmt.Errorf("could not create launch darkly client: %s", err)
 	}

+ 1 - 1
cmd/migrate/main.go

@@ -30,7 +30,7 @@ func main() {
 		return
 	}
 
-	launchDarklyClient, err := features.GetClient(envConf.ServerConf.LaunchDarklySDKKey)
+	launchDarklyClient, err := features.GetClient(envConf.ServerConf.FeatureFlagClient, envConf.ServerConf.LaunchDarklySDKKey)
 	if err != nil {
 		logger.Fatal().Err(err).Msg("could not load launch darkly client")
 		return

+ 42 - 6
internal/features/launch_darkly.go

@@ -11,7 +11,8 @@ import (
 
 // Client is a struct wrapper around the launchdarkly client
 type Client struct {
-	Client LDClient
+	Client      LDClient
+	useDatabase bool
 }
 
 // LDClient is an interface that allows us to mock
@@ -33,20 +34,55 @@ func (c Client) BoolVariation(field string, context ldcontext.Context, defaultVa
 	return c.Client.BoolVariation(field, context, defaultValue)
 }
 
+// UseDatabase returns whether we should force using the database for feature flags
+//
+// The initial implementation of feature flags stored flags in
+// table-specific columns, making it so we need to use a nasty hack to
+// fetch the correct value for on-prem deployments. A proper refactor would
+// be to introduce a migration that moved these flags to a feature flags
+// table that we could implement a proper WrappedClient for, but a shortcut
+// is taken here in order to fix this sooner and give us time for a proper
+// refactor.
+func (c Client) UseDatabase() bool {
+	return c.useDatabase
+}
+
 // GetClient retrieves a Client for interacting with LaunchDarkly
-func GetClient(launchDarklySDKKey string) (*Client, error) {
+func GetClient(featureFlagClient string, launchDarklySDKKey string) (*Client, error) {
+	validClients := map[string]bool{
+		"database":      true,
+		"launch_darkly": true,
+	}
+
+	if !validClients[featureFlagClient] {
+		return &Client{}, fmt.Errorf("failed to create new feature flag client: invalid feature flag client specified")
+	}
+
+	if featureFlagClient == "database" {
+		return &Client{
+			useDatabase: true,
+		}, nil
+	}
+
+	if launchDarklySDKKey == "" {
+		return &Client{}, fmt.Errorf("failed to create new feature flag client: missing launch_darkly sdk key")
+	}
+
 	ldClient, err := ld.MakeClient(launchDarklySDKKey, 5*time.Second)
 	if err != nil {
-		return &Client{}, fmt.Errorf("failed to create new launchdarkly client: %w", err)
+		return &Client{}, fmt.Errorf("failed to create new feature flag client: %w", err)
 	}
 
 	if ldClient == nil {
-		return &Client{}, errors.New("failed to create new launchdarkly client: invalid config")
+		return &Client{}, errors.New("failed to create new feature flag client: invalid config")
 	}
 
 	if !ldClient.Initialized() {
-		return &Client{}, errors.New("failed to create new launchdarkly client: sdk failed to initialize")
+		return &Client{}, errors.New("failed to create new feature flag client: sdk failed to initialize")
 	}
 
-	return &Client{ldClient}, nil
+	return &Client{
+		Client:      ldClient,
+		useDatabase: false,
+	}, nil
 }

+ 32 - 0
internal/models/project.go

@@ -168,6 +168,38 @@ type Project struct {
 // GetFeatureFlag calls launchdarkly for the specified flag
 // and returns the configured value
 func (p *Project) GetFeatureFlag(flagName FeatureFlagLabel, launchDarklyClient *features.Client) bool {
+	if launchDarklyClient.UseDatabase() {
+		// case switch things
+		switch flagName {
+		case "api_tokens_enabled":
+			return p.APITokensEnabled
+		case "azure_enabled":
+			return p.AzureEnabled
+		case "capi_provisioner_enabled":
+			return p.CapiProvisionerEnabled
+		case "enable_reprovision":
+			return p.EnableReprovision
+		case "full_add_ons":
+			return p.FullAddOns
+		case "helm_values_enabled":
+			return p.HelmValuesEnabled
+		case "managed_infra_enabled":
+			return p.ManagedInfraEnabled
+		case "multi_cluster":
+			return p.MultiCluster
+		case "preview_envs_enabled":
+			return p.PreviewEnvsEnabled
+		case "rds_databases_enabled":
+			return p.RDSDatabasesEnabled
+		case "simplified_view_enabled":
+			return p.SimplifiedViewEnabled
+		case "stacks_enabled":
+			return p.StacksEnabled
+		case "validate_apply_v2":
+			return p.ValidateApplyV2
+		}
+	}
+
 	projectID := p.ID
 	projectName := p.Name
 	ldContext := getProjectContext(projectID, projectName)

+ 4 - 1
provisioner/server/config/config.go

@@ -126,6 +126,9 @@ type ProvisionerConf struct {
 	// Client key for segment to report provisioning events
 	SegmentClientKey string `env:"SEGMENT_CLIENT_KEY"`
 
+	// FeatureFlagClient controls which client to use (database or launch_darkly)
+	FeatureFlagClient string `env:"FEATURE_FLAG_CLIENT,default=launch_darkly"`
+
 	// Launch Darkly SDK key
 	LaunchDarklySDKKey string `env:"LAUNCHDARKLY_SDK_KEY"`
 }
@@ -172,7 +175,7 @@ func GetConfig(envConf *EnvConf) (*Config, error) {
 
 	res.Repo = gorm.NewRepository(db, &key, InstanceCredentialBackend)
 
-	launchDarklyClient, err := features.GetClient(envConf.LaunchDarklySDKKey)
+	launchDarklyClient, err := features.GetClient(envConf.FeatureFlagClient, envConf.LaunchDarklySDKKey)
 	if err != nil {
 		return nil, fmt.Errorf("could not create launch darkly client: %s", err)
 	}

+ 1 - 0
zarf/helm/.serverenv

@@ -34,6 +34,7 @@ GITHUB_APP_SECRET_PATH=<path_to_secret>
 # LAUNCHDARKLY_SDK_KEY is used to interact with the launchdarkly api
 # Retrieve your SDK key from https://app.launchdarkly.com/settings/projects/dev/environments
 
+FEATURE_FLAG_CLIENT=launch_darkly
 LAUNCHDARKLY_SDK_KEY=<launchdarkly_sdk_key>
 
 # Optional parameters