Skip to content

Commit

Permalink
feat: default to ~/Library/Application\ Support/flipt for db sqlite f…
Browse files Browse the repository at this point in the history
…ile on mac (#2039)
  • Loading branch information
markphelps authored Aug 23, 2023
1 parent a92355c commit 06a899b
Show file tree
Hide file tree
Showing 18 changed files with 107 additions and 59 deletions.
42 changes: 27 additions & 15 deletions cmd/flipt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ func run(ctx context.Context, logger *zap.Logger, cfg *config.Config) error {

g, ctx := errgroup.WithContext(ctx)

if err := initLocalState(cfg); err != nil {
if err := initMetaStateDir(cfg); err != nil {
logger.Debug("disabling telemetry, state directory not accessible", zap.String("path", cfg.Meta.StateDirectory), zap.Error(err))
cfg.Meta.TelemetryEnabled = false
} else {
Expand Down Expand Up @@ -369,33 +369,45 @@ func run(ctx context.Context, logger *zap.Logger, cfg *config.Config) error {
return g.Wait()
}

// check if state directory already exists, create it if not
func initLocalState(cfg *config.Config) error {
if cfg.Meta.StateDirectory == "" {
configDir, err := os.UserConfigDir()
if err != nil {
return fmt.Errorf("getting user config dir: %w", err)
}
cfg.Meta.StateDirectory = filepath.Join(configDir, "flipt")
func defaultUserStateDir() (string, error) {
configDir, err := os.UserConfigDir()
if err != nil {
return "", fmt.Errorf("getting user state dir: %w", err)
}

fp, err := os.Stat(cfg.Meta.StateDirectory)
return filepath.Join(configDir, "flipt"), nil
}

func ensureDir(path string) error {
fp, err := os.Stat(path)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
// state directory doesnt exist, so try to create it
return os.MkdirAll(cfg.Meta.StateDirectory, 0700)
// directory doesnt exist, so try to create it
return os.MkdirAll(path, 0700)
}
return fmt.Errorf("checking state directory: %w", err)
return fmt.Errorf("checking directory: %w", err)
}

if fp != nil && !fp.IsDir() {
return fmt.Errorf("state directory is not a directory")
return fmt.Errorf("not a directory")
}

// assume state directory exists and is a directory
return nil
}

// check if state directory already exists, create it if not
func initMetaStateDir(cfg *config.Config) error {
if cfg.Meta.StateDirectory == "" {
var err error
cfg.Meta.StateDirectory, err = defaultUserStateDir()
if err != nil {
return err
}
}

return ensureDir(cfg.Meta.StateDirectory)
}

// clientConn constructs and configures a client connection to the underlying gRPC server.
func clientConn(ctx context.Context, cfg *config.Config) (*grpc.ClientConn, error) {
opts := []grpc.DialOption{grpc.WithBlock()}
Expand Down
4 changes: 0 additions & 4 deletions config/local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ log:
cors:
enabled: true
allowed_origins: ["*"]

# cache:
# enabled: false
# backend: memory
Expand All @@ -30,6 +29,3 @@ cors:
# https_port: 443
# http_port: 8080
# grpc_port: 9000

db:
url: file:flipt.db
7 changes: 6 additions & 1 deletion internal/config/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"github.com/spf13/viper"
)

// cheers up the unparam linter
var _ defaulter = (*AuditConfig)(nil)

// AuditConfig contains fields, which enable and configure
// Flipt's various audit sink mechanisms.
type AuditConfig struct {
Expand All @@ -19,7 +22,7 @@ func (c *AuditConfig) Enabled() bool {
return c.Sinks.LogFile.Enabled
}

func (c *AuditConfig) setDefaults(v *viper.Viper) {
func (c *AuditConfig) setDefaults(v *viper.Viper) error {
v.SetDefault("audit", map[string]any{
"sinks": map[string]any{
"log": map[string]any{
Expand All @@ -32,6 +35,8 @@ func (c *AuditConfig) setDefaults(v *viper.Viper) {
"flush_period": "2m",
},
})

return nil
}

func (c *AuditConfig) validate() error {
Expand Down
4 changes: 3 additions & 1 deletion internal/config/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (c AuthenticationConfig) ShouldRunCleanup() (shouldCleanup bool) {
return
}

func (c *AuthenticationConfig) setDefaults(v *viper.Viper) {
func (c *AuthenticationConfig) setDefaults(v *viper.Viper) error {
methods := map[string]any{}

// set default for each methods
Expand Down Expand Up @@ -112,6 +112,8 @@ func (c *AuthenticationConfig) setDefaults(v *viper.Viper) {
},
"methods": methods,
})

return nil
}

func (c *AuthenticationConfig) SessionEnabled() bool {
Expand Down
4 changes: 3 additions & 1 deletion internal/config/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type CacheConfig struct {
Redis RedisCacheConfig `json:"redis,omitempty" mapstructure:"redis"`
}

func (c *CacheConfig) setDefaults(v *viper.Viper) {
func (c *CacheConfig) setDefaults(v *viper.Viper) error {
v.SetDefault("cache", map[string]any{
"enabled": false,
"backend": CacheMemory,
Expand All @@ -48,6 +48,8 @@ func (c *CacheConfig) setDefaults(v *viper.Viper) {
// ensure ttl default is set
v.SetDefault("cache.memory.expiration", 1*time.Minute)
}

return nil
}

func (c *CacheConfig) deprecations(v *viper.Viper) []deprecated {
Expand Down
16 changes: 13 additions & 3 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"os"
"path/filepath"
"reflect"
"strings"
"time"
Expand Down Expand Up @@ -140,7 +141,9 @@ func Load(path string) (*Result, error) {

// run any defaulters
for _, defaulter := range defaulters {
defaulter.setDefaults(v)
if err := defaulter.setDefaults(v); err != nil {
return nil, err
}
}

if err := v.Unmarshal(cfg, viper.DecodeHook(
Expand All @@ -162,7 +165,7 @@ func Load(path string) (*Result, error) {
}

type defaulter interface {
setDefaults(v *viper.Viper)
setDefaults(v *viper.Viper) error
}

type validator interface {
Expand Down Expand Up @@ -411,6 +414,13 @@ func stringToSliceHookFunc() mapstructure.DecodeHookFunc {

// DefaultConfig is the base config used when no configuration is explicit provided.
func DefaultConfig() *Config {
dbRoot, err := defaultDatabaseRoot()
if err != nil {
panic(err)
}

dbPath := filepath.Join(dbRoot, "flipt", "flipt.db")

return &Config{
Log: LogConfig{
Level: "INFO",
Expand Down Expand Up @@ -476,7 +486,7 @@ func DefaultConfig() *Config {
},

Database: DatabaseConfig{
URL: "file:/var/opt/flipt/flipt.db",
URL: "file:" + dbPath,
MaxIdleConn: 2,
PreparedStatementsEnabled: true,
},
Expand Down
12 changes: 0 additions & 12 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,18 +248,6 @@ func TestLoad(t *testing.T) {
"\"cache.memory.enabled\" is deprecated. Please use 'cache.enabled' and 'cache.backend' instead.",
},
},
{
name: "deprecated database migrations path",
path: "./testdata/deprecated/database_migrations_path.yml",
expected: DefaultConfig,
warnings: []string{"\"db.migrations.path\" is deprecated. Migrations are now embedded within Flipt and are no longer required on disk."},
},
{
name: "deprecated database migrations path legacy",
path: "./testdata/deprecated/database_migrations_path_legacy.yml",
expected: DefaultConfig,
warnings: []string{"\"db.migrations.path\" is deprecated. Migrations are now embedded within Flipt and are no longer required on disk."},
},
{
name: "deprecated ui disabled",
path: "./testdata/deprecated/ui_disabled.yml",
Expand Down
4 changes: 3 additions & 1 deletion internal/config/cors.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ type CorsConfig struct {
AllowedOrigins []string `json:"allowedOrigins,omitempty" mapstructure:"allowed_origins"`
}

func (c *CorsConfig) setDefaults(v *viper.Viper) {
func (c *CorsConfig) setDefaults(v *viper.Viper) error {
v.SetDefault("cors", map[string]any{
"enabled": false,
"allowed_origins": "*",
})

return nil
}
23 changes: 11 additions & 12 deletions internal/config/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package config

import (
"encoding/json"
"fmt"
"path/filepath"
"time"

"github.com/spf13/viper"
Expand Down Expand Up @@ -40,7 +42,7 @@ type DatabaseConfig struct {
PreparedStatementsEnabled bool `json:"preparedStatementsEnabled,omitempty" mapstructure:"prepared_statements_enabled"`
}

func (c *DatabaseConfig) setDefaults(v *viper.Viper) {
func (c *DatabaseConfig) setDefaults(v *viper.Viper) error {
v.SetDefault("db", map[string]any{
"max_idle_conn": 2,
})
Expand All @@ -53,20 +55,17 @@ func (c *DatabaseConfig) setDefaults(v *viper.Viper) {
}

if setDefaultURL {
v.SetDefault("db.url", "file:/var/opt/flipt/flipt.db")
}

v.SetDefault("db.prepared_statements_enabled", true)
}

func (c *DatabaseConfig) deprecations(v *viper.Viper) []deprecated {
var deprecations []deprecated
dbRoot, err := defaultDatabaseRoot()
if err != nil {
return fmt.Errorf("getting default database directory: %w", err)
}

if v.IsSet("db.migrations.path") || v.IsSet("db.migrations_path") {
deprecations = append(deprecations, "db.migrations.path")
path := filepath.Join(dbRoot, "flipt", "flipt.db")
v.SetDefault("db.url", "file:"+path)
}

return deprecations
v.SetDefault("db.prepared_statements_enabled", true)
return nil
}

func (c *DatabaseConfig) validate() (err error) {
Expand Down
12 changes: 12 additions & 0 deletions internal/config/database_default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build !linux
// +build !linux

package config

import (
"os"
)

func defaultDatabaseRoot() (string, error) {
return os.UserConfigDir()
}
8 changes: 8 additions & 0 deletions internal/config/database_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//go:build linux
// +build linux

package config

func defaultDatabaseRoot() (string, error) {
return "/var/opt", nil
}
3 changes: 0 additions & 3 deletions internal/config/deprecations.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ var (
"tracing.jaeger.enabled": deprecatedMsgTracingJaegerEnabled,
"cache.memory.enabled": deprecatedMsgCacheMemoryEnabled,
"cache.memory.expiration": deprecatedMsgCacheMemoryExpiration,
"database.migrations": deprecatedMsgDatabaseMigrations,
"db.migrations.path": deprecatedMsgDatabaseMigrations,
"experimental.filesystem_storage": deprecatedMsgExperimentalFilesystemStorage,
}
)
Expand All @@ -26,7 +24,6 @@ const (
deprecatedMsgTracingJaegerEnabled = `Please use 'tracing.enabled' and 'tracing.exporter' instead.`
deprecatedMsgCacheMemoryEnabled = `Please use 'cache.enabled' and 'cache.backend' instead.`
deprecatedMsgCacheMemoryExpiration = `Please use 'cache.ttl' instead.`
deprecatedMsgDatabaseMigrations = `Migrations are now embedded within Flipt and are no longer required on disk.`
deprecatedMsgExperimentalFilesystemStorage = `The experimental filesystem storage backend has graduated to a stable feature. Please use 'storage' instead.`
)

Expand Down
4 changes: 3 additions & 1 deletion internal/config/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type LogKeys struct {
Message string `json:"message" mapstructure:"message"`
}

func (c *LogConfig) setDefaults(v *viper.Viper) {
func (c *LogConfig) setDefaults(v *viper.Viper) error {
v.SetDefault("log", map[string]any{
"level": "INFO",
"encoding": "console",
Expand All @@ -36,6 +36,8 @@ func (c *LogConfig) setDefaults(v *viper.Viper) {
"message": "M",
},
})

return nil
}

var (
Expand Down
4 changes: 3 additions & 1 deletion internal/config/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ type MetaConfig struct {
StateDirectory string `json:"stateDirectory" mapstructure:"state_directory"`
}

func (c *MetaConfig) setDefaults(v *viper.Viper) {
func (c *MetaConfig) setDefaults(v *viper.Viper) error {
v.SetDefault("meta", map[string]any{
"check_for_updates": true,
"telemetry_enabled": true,
})

return nil
}
4 changes: 3 additions & 1 deletion internal/config/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ type ServerConfig struct {
CertKey string `json:"certKey,omitempty" mapstructure:"cert_key"`
}

func (c *ServerConfig) setDefaults(v *viper.Viper) {
func (c *ServerConfig) setDefaults(v *viper.Viper) error {
v.SetDefault("server", map[string]any{
"host": "0.0.0.0",
"protocol": HTTP,
"http_port": 8080,
"https_port": 443,
"grpc_port": 9000,
})

return nil
}

func (c *ServerConfig) validate() (err error) {
Expand Down
7 changes: 6 additions & 1 deletion internal/config/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"github.com/spf13/viper"
)

// cheers up the unparam linter
var _ defaulter = (*StorageConfig)(nil)

type StorageType string

const (
Expand All @@ -32,7 +35,7 @@ type StorageConfig struct {
ReadOnly *bool `json:"readOnly,omitempty" mapstructure:"readOnly,omitempty"`
}

func (c *StorageConfig) setDefaults(v *viper.Viper) {
func (c *StorageConfig) setDefaults(v *viper.Viper) error {
switch v.GetString("storage.type") {
case string(LocalStorageType):
v.SetDefault("storage.local.path", ".")
Expand All @@ -50,6 +53,8 @@ func (c *StorageConfig) setDefaults(v *viper.Viper) {
default:
v.SetDefault("storage.type", "database")
}

return nil
}

func (c *StorageConfig) validate() error {
Expand Down
Loading

0 comments on commit 06a899b

Please sign in to comment.