Skip to content

Commit

Permalink
POR-1503: allow fallback to the database for feature flags (#3575)
Browse files Browse the repository at this point in the history
  • Loading branch information
jose-fully-ported authored Sep 14, 2023
1 parent 206bf43 commit 116f97f
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 9 deletions.
2 changes: 2 additions & 0 deletions api/server/shared/config/env/envconfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
2 changes: 1 addition & 1 deletion api/server/shared/config/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/migrate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 42 additions & 6 deletions internal/features/launch_darkly.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 changes: 32 additions & 0 deletions internal/models/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion provisioner/server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
Expand Down Expand Up @@ -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)
}
Expand Down
1 change: 1 addition & 0 deletions zarf/helm/.serverenv
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 116f97f

Please sign in to comment.