Skip to content
Merged
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: 8 additions & 5 deletions cmd/ff-proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ var (
flagPollInterval int
flagStreamEnabled bool
generateOfflineConfig bool
configDir string
)

const (
Expand All @@ -133,6 +134,7 @@ const (
flagPollIntervalEnv = "FLAG_POLL_INTERVAL"
flagStreamEnabledEnv = "FLAG_STREAM_ENABLED"
generateOfflineConfigEnv = "GENERATE_OFFLINE_CONFIG"
configDirEnv = "CONFIG_DIR"
pprofEnabledEnv = "PPROF"

bypassAuthFlag = "bypass-auth"
Expand All @@ -157,6 +159,7 @@ const (
pprofEnabledFlag = "pprof"
flagStreamEnabledFlag = "flag-stream-enabled"
generateOfflineConfigFlag = "generate-offline-config"
configDirFlag = "config-dir"
flagPollIntervalFlag = "flag-poll-interval"
)

Expand Down Expand Up @@ -185,6 +188,7 @@ func init() {
flag.IntVar(&flagPollInterval, flagPollIntervalFlag, 1, "how often in minutes the proxy should poll for flag updates (if stream not connected)")
flag.BoolVar(&flagStreamEnabled, flagStreamEnabledFlag, true, "should the proxy connect to Harness in streaming mode to get flag changes")
flag.BoolVar(&generateOfflineConfig, generateOfflineConfigFlag, false, "if true the proxy will produce offline config in the /config directory then terminate")
flag.StringVar(&configDir, configDirFlag, "/config", "specify a custom path to search for the offline config directory. Defaults to /config")
sdkClients = newSDKClientMap()

loadFlagsFromEnv(map[string]string{
Expand All @@ -210,6 +214,7 @@ func init() {
pprofEnabledEnv: pprofEnabledFlag,
flagStreamEnabledEnv: flagStreamEnabledFlag,
generateOfflineConfigEnv: generateOfflineConfigFlag,
configDirEnv: configDirFlag,
flagPollIntervalEnv: flagPollIntervalFlag,
})

Expand Down Expand Up @@ -297,7 +302,7 @@ func main() {
cancel()
}()

logger.Info("service config", "pprof", pprofEnabled, "debug", debug, "bypass-auth", bypassAuth, "offline", offline, "port", port, "admin-service", adminService, "account-identifier", accountIdentifier, "org-identifier", orgIdentifier, "sdk-base-url", sdkBaseURL, "sdk-events-url", sdkEventsURL, "redis-addr", redisAddress, "redis-db", redisDB, "api-keys", fmt.Sprintf("%v", apiKeys), "target-poll-duration", fmt.Sprintf("%ds", targetPollDuration), "heartbeat-interval", fmt.Sprintf("%ds", heartbeatInterval), "flag-stream-enabled", flagStreamEnabled, "flag-poll-interval", fmt.Sprintf("%dm", flagPollInterval))
logger.Info("service config", "pprof", pprofEnabled, "debug", debug, "bypass-auth", bypassAuth, "offline", offline, "port", port, "admin-service", adminService, "account-identifier", accountIdentifier, "org-identifier", orgIdentifier, "sdk-base-url", sdkBaseURL, "sdk-events-url", sdkEventsURL, "redis-addr", redisAddress, "redis-db", redisDB, "api-keys", fmt.Sprintf("%v", apiKeys), "target-poll-duration", fmt.Sprintf("%ds", targetPollDuration), "heartbeat-interval", fmt.Sprintf("%ds", heartbeatInterval), "flag-stream-enabled", flagStreamEnabled, "flag-poll-interval", fmt.Sprintf("%dm", flagPollInterval), "config-dir", configDir)

adminService, err := services.NewAdminService(logger, adminService, adminServiceToken)
if err != nil {
Expand Down Expand Up @@ -335,10 +340,8 @@ func main() {

// Load either local config from files or remote config from ff-server
if offline && !generateOfflineConfig {
// TODO - this works in the built image, see if we can have a better way to automatically work running locally too
// change to fs:= ffproxy.DefaultConfig if running locally
fs := os.DirFS("")
config, err := config.NewLocalConfig(fs, ffproxy.DefaultConfigDir)
fs := os.DirFS(configDir)
config, err := config.NewLocalConfig(fs)
if err != nil {
logger.Error("failed to load config", "err", err)
os.Exit(1)
Expand Down
20 changes: 10 additions & 10 deletions config/local_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,22 @@ type LocalConfig struct {

// NewLocalConfig creates a new FeatureFlagConfig that loads config from
// the passed FileSystem and directory.
func NewLocalConfig(fs fs.FS, dir string) (LocalConfig, error) {
func NewLocalConfig(fs fs.FS) (LocalConfig, error) {
o := LocalConfig{
config: make(map[string]config),
hasher: hash.NewSha256(),
}

if err := o.loadConfig(fs, dir); err != nil {
if err := o.loadConfig(fs); err != nil {
return LocalConfig{}, err
}
return o, nil
}

// loadConfig reads the directory of the filesystem and walks the file tree
// decoding any config files that it finds
func (f LocalConfig) loadConfig(fileSystem fs.FS, dir string) error {
if err := fs.WalkDir(fileSystem, dir, decodeConfigFiles(f.config)); err != nil {
func (f LocalConfig) loadConfig(fileSystem fs.FS) error {
if err := fs.WalkDir(fileSystem, ".", decodeConfigFiles(f.config, fileSystem)); err != nil {
return err
}
return nil
Expand All @@ -51,7 +51,7 @@ func (f LocalConfig) loadConfig(fileSystem fs.FS, dir string) error {
// getParentDirFromPath gets the name of the parent directory for a file in a path
func getParentDirFromPath(path string) (string, error) {
split := strings.SplitAfter(path, "/")
if len(split) <= 2 {
if len(split) < 2 {
return "", errors.New("path needs a length of at least 2 to have a parent")
}

Expand All @@ -61,7 +61,7 @@ func getParentDirFromPath(path string) (string, error) {

// decodeConfigFiles returns a WalkDirFunc that gets called on each file in the
// config directory.
func decodeConfigFiles(c map[string]config) fs.WalkDirFunc {
func decodeConfigFiles(c map[string]config, fileSystem fs.FS) fs.WalkDirFunc {
return func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
Expand Down Expand Up @@ -96,7 +96,7 @@ func decodeConfigFiles(c map[string]config) fs.WalkDirFunc {

if i.Name() == "feature_config.json" {
config := c[env]
if err := ffproxy.DecodeFile(path, &config.FeatureFlags); err != nil {
if err := ffproxy.DecodeFile(fileSystem, path, &config.FeatureFlags); err != nil {
return err
}
c[env] = config
Expand All @@ -105,7 +105,7 @@ func decodeConfigFiles(c map[string]config) fs.WalkDirFunc {

if i.Name() == "targets.json" {
config := c[env]
if err := ffproxy.DecodeFile(path, &config.Targets); err != nil {
if err := ffproxy.DecodeFile(fileSystem, path, &config.Targets); err != nil {
return err
}
c[env] = config
Expand All @@ -114,7 +114,7 @@ func decodeConfigFiles(c map[string]config) fs.WalkDirFunc {

if i.Name() == "segments.json" {
config := c[env]
if err := ffproxy.DecodeFile(path, &config.Segments); err != nil {
if err := ffproxy.DecodeFile(fileSystem, path, &config.Segments); err != nil {
return err
}
c[env] = config
Expand All @@ -123,7 +123,7 @@ func decodeConfigFiles(c map[string]config) fs.WalkDirFunc {

if i.Name() == "auth_config.json" {
config := c[env]
if err := ffproxy.DecodeFile(path, &config.Auth); err != nil {
if err := ffproxy.DecodeFile(fileSystem, path, &config.Auth); err != nil {
return err
}
c[env] = config
Expand Down
4 changes: 2 additions & 2 deletions config/local_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func TestLocalConfig(t *testing.T) {
domain.NewSegmentKey("1234"): []domain.Segment{flagsTeamSegment},
}

lc, err := NewLocalConfig(testConfig, testDir)
lc, err := NewLocalConfig(testConfig)
if err != nil {
t.Fatal(err)
}
Expand All @@ -237,7 +237,7 @@ func TestLocalConfig_Auth(t *testing.T) {
domain.AuthAPIKey(apikey3Hash): "1234",
}

lc, err := NewLocalConfig(testConfig, testDir)
lc, err := NewLocalConfig(testConfig)
if err != nil {
t.Fatal(err)
}
Expand Down
8 changes: 6 additions & 2 deletions config/remote_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,15 @@ func TestPollTargets(t *testing.T) {
close(ticker)
}

targetsCopy := map[string][]admingen.Target{}
for key, value := range targets{
targetsCopy[key] = value
}

adminClient := mockAdminClient{
projects: projects,
environments: environments,
targets: targets,
targets: targetsCopy,
Mutex: &sync.Mutex{},
}

Expand All @@ -444,7 +449,6 @@ func TestPollTargets(t *testing.T) {
}

if len(tc.targetsToAdd) > 0 {
t.Log("And I add Targets to the admin client")
key := string("FeatureFlagsDev-Dev")

adminClient.Lock()
Expand Down
16 changes: 0 additions & 16 deletions embed.go

This file was deleted.

12 changes: 6 additions & 6 deletions file_decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
"os"
"io/fs"
"path/filepath"

"gopkg.in/yaml.v2"
Expand All @@ -18,8 +18,8 @@ const (
)

// DecodeFile is a convienence function that creates a FileDecoder and calls Decode
func DecodeFile(path string, v interface{}) error {
dec, err := NewFileDecoder(path)
func DecodeFile(fileSystem fs.FS, path string, v interface{}) error {
dec, err := NewFileDecoder(fileSystem, path)
if err != nil {
return err
}
Expand All @@ -42,13 +42,13 @@ type FileDecoder struct {
// If the file extension is not supported it returns an error. NewFileDecoder does
// not close the opened file, for the file to be closed you have to call the Decode
// method.
func NewFileDecoder(file string) (*FileDecoder, error) {
f, err := os.Open(filepath.Clean(file))
func NewFileDecoder(fileSystem fs.FS, file string) (*FileDecoder, error) {
f, err := fileSystem.Open(file)
if err != nil {
return nil, err
}

ext := filepath.Ext(f.Name())
ext := filepath.Ext(file)
var dec decoder

switch ext {
Expand Down
4 changes: 2 additions & 2 deletions proxy-service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func getAllBenchmarkConfig() benchmarkConfig {
dir := fmt.Sprintf("../config/bench-test")
fileSystem := fileSystem{path: dir}

lc, err := config.NewLocalConfig(fileSystem, dir)
lc, err := config.NewLocalConfig(fileSystem)
if err != nil {
panic(err)
}
Expand All @@ -69,7 +69,7 @@ func getConfigByEnv(envID string, b *testing.B) benchmarkConfig {
dir := fmt.Sprintf("../config/bench-test/env-%s", envID)
fileSystem := fileSystem{path: dir}

lc, err := config.NewLocalConfig(fileSystem, dir)
lc, err := config.NewLocalConfig(fileSystem)
if err != nil {
b.Fatalf("failed to load config: %s", err)
}
Expand Down
17 changes: 2 additions & 15 deletions transport/http_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"net"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -70,18 +69,6 @@ func (m *mockMetricService) StoreMetrics(ctx context.Context, req domain.Metrics
return m.storeMetrics(ctx, req)
}

type fileSystem struct {
path string
}

func (f fileSystem) Open(name string) (fs.File, error) {
file, err := os.Open(name)
if err != nil {
return nil, err
}
return file, nil
}

const (
apiKey1 = "apikey1"
envID123 = "1234"
Expand Down Expand Up @@ -156,8 +143,8 @@ func setupWithCache(c cache.Cache) setupOpts {
// setupHTTPServer is a helper that loads test config for populating the repos
// and injects all the required dependencies into the proxy service and http server
func setupHTTPServer(t *testing.T, bypassAuth bool, opts ...setupOpts) *HTTPServer {
fileSystem := fileSystem{path: "../config/test"}
config, err := config.NewLocalConfig(fileSystem, "../config/test")
fileSystem := os.DirFS("../config/test")
config, err := config.NewLocalConfig(fileSystem)
if err != nil {
t.Fatal(err)
}
Expand Down