diff --git a/glauth/pkg/command/server.go b/glauth/pkg/command/server.go index 7716e8f9c..d3e903503 100644 --- a/glauth/pkg/command/server.go +++ b/glauth/pkg/command/server.go @@ -32,14 +32,6 @@ func Server(cfg *config.Config) *cli.Command { return err } - // TODO(refs) there is no cli flags anymore... - //if len(ctx.StringSlice("backend-server")) > 0 { - // cfg.Backend.Servers = ctx.StringSlice("backend-server") - //} - //if len(ctx.StringSlice("fallback-server")) > 0 { - // cfg.Fallback.Servers = ctx.StringSlice("fallback-server") - //} - return nil }, Action: func(c *cli.Context) error { diff --git a/ocis-pkg/config/config.go b/ocis-pkg/config/config.go index 224c90327..f445a9991 100644 --- a/ocis-pkg/config/config.go +++ b/ocis-pkg/config/config.go @@ -125,7 +125,7 @@ func New() *Config { Web: web.New(), Storage: storage.New(), Store: store.New(), - Thumbnails: thumbnails.New(), + Thumbnails: thumbnails.DefaultConfig(), WebDAV: webdav.New(), } } diff --git a/ocis/pkg/command/thumbnails.go b/ocis/pkg/command/thumbnails.go index 083442f8a..b81a0eedb 100644 --- a/ocis/pkg/command/thumbnails.go +++ b/ocis/pkg/command/thumbnails.go @@ -8,7 +8,6 @@ import ( "github.com/owncloud/ocis/ocis-pkg/version" "github.com/owncloud/ocis/ocis/pkg/register" "github.com/owncloud/ocis/thumbnails/pkg/command" - "github.com/owncloud/ocis/thumbnails/pkg/flagset" "github.com/urfave/cli/v2" svcconfig "github.com/owncloud/ocis/thumbnails/pkg/config" @@ -20,7 +19,6 @@ func ThumbnailsCommand(cfg *config.Config) *cli.Command { Name: "thumbnails", Usage: "Start thumbnails server", Category: "Extensions", - Flags: flagset.ServerWithConfig(cfg.Thumbnails), Subcommands: []*cli.Command{ command.PrintVersion(cfg.Thumbnails), }, diff --git a/thumbnails/cmd/thumbnails/main.go b/thumbnails/cmd/thumbnails/main.go index 7d89ce29f..2098a6341 100644 --- a/thumbnails/cmd/thumbnails/main.go +++ b/thumbnails/cmd/thumbnails/main.go @@ -8,7 +8,7 @@ import ( ) func main() { - if err := command.Execute(config.New()); err != nil { + if err := command.Execute(config.DefaultConfig()); err != nil { os.Exit(1) } } diff --git a/thumbnails/pkg/command/root.go b/thumbnails/pkg/command/root.go index c6b0d43a1..4ebc5e413 100644 --- a/thumbnails/pkg/command/root.go +++ b/thumbnails/pkg/command/root.go @@ -3,15 +3,11 @@ package command import ( "context" "os" - "strings" - - "github.com/owncloud/ocis/ocis-pkg/sync" ociscfg "github.com/owncloud/ocis/ocis-pkg/config" "github.com/owncloud/ocis/ocis-pkg/log" "github.com/owncloud/ocis/ocis-pkg/version" "github.com/owncloud/ocis/thumbnails/pkg/config" - "github.com/spf13/viper" "github.com/thejerf/suture/v4" "github.com/urfave/cli/v2" ) @@ -68,45 +64,18 @@ func NewLogger(cfg *config.Config) log.Logger { } // ParseConfig loads configuration from Viper known paths. +// ParseConfig loads glauth configuration from known paths. func ParseConfig(c *cli.Context, cfg *config.Config) error { - sync.ParsingViperConfig.Lock() - defer sync.ParsingViperConfig.Unlock() - logger := NewLogger(cfg) - - viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - viper.SetEnvPrefix("THUMBNAILS") - viper.AutomaticEnv() - - if c.IsSet("config-file") { - viper.SetConfigFile(c.String("config-file")) - } else { - viper.SetConfigName("thumbnails") - - viper.AddConfigPath("/etc/ocis") - viper.AddConfigPath("$HOME/.ocis") - viper.AddConfigPath("./config") + conf, err := ociscfg.BindSourcesToStructs("thumbnails", cfg) + if err != nil { + return err } - if err := viper.ReadInConfig(); err != nil { - switch err.(type) { - case viper.ConfigFileNotFoundError: - logger.Debug(). - Msg("no config found on preconfigured location") - case viper.UnsupportedConfigError: - logger.Fatal(). - Err(err). - Msg("unsupported config type") - default: - logger.Fatal(). - Err(err). - Msg("failed to read config") - } - } + // load all env variables relevant to the config in the current context. + conf.LoadOSEnv(config.GetEnv(), false) - if err := viper.Unmarshal(&cfg); err != nil { - logger.Fatal(). - Err(err). - Msg("failed to parse config") + if err = cfg.UnmapEnv(conf); err != nil { + return err } return nil diff --git a/thumbnails/pkg/command/server.go b/thumbnails/pkg/command/server.go index 856b7a5a8..4a96c12e5 100644 --- a/thumbnails/pkg/command/server.go +++ b/thumbnails/pkg/command/server.go @@ -7,7 +7,6 @@ import ( "github.com/oklog/run" "github.com/owncloud/ocis/ocis-pkg/sync" "github.com/owncloud/ocis/thumbnails/pkg/config" - "github.com/owncloud/ocis/thumbnails/pkg/flagset" "github.com/owncloud/ocis/thumbnails/pkg/metrics" "github.com/owncloud/ocis/thumbnails/pkg/server/debug" "github.com/owncloud/ocis/thumbnails/pkg/server/grpc" @@ -20,19 +19,12 @@ func Server(cfg *config.Config) *cli.Command { return &cli.Command{ Name: "server", Usage: "Start integrated server", - Flags: flagset.ServerWithConfig(cfg), Before: func(ctx *cli.Context) error { + if err := ParseConfig(ctx, cfg); err != nil { + return err + } + logger := NewLogger(cfg) - - // StringSliceFlag doesn't support Destination - // UPDATE Destination on string flags supported. Wait for https://github.com/urfave/cli/pull/1078 to get to micro/cli - if len(ctx.StringSlice("thumbnail-resolution")) > 0 { - cfg.Thumbnail.Resolutions = ctx.StringSlice("thumbnail-resolution") - } - - if !cfg.Supervised { - return ParseConfig(ctx, cfg) - } logger.Debug().Str("service", "thumbnails").Msg("ignoring config file parsing when running supervised") return nil }, diff --git a/thumbnails/pkg/config/config.go b/thumbnails/pkg/config/config.go index 7f8a97a81..570695698 100644 --- a/thumbnails/pkg/config/config.go +++ b/thumbnails/pkg/config/config.go @@ -1,48 +1,57 @@ package config -import "context" +import ( + "context" + "fmt" + "path" + "reflect" + + gofig "github.com/gookit/config/v2" + + "github.com/owncloud/ocis/ocis-pkg/config/defaults" +) // Log defines the available logging configuration. type Log struct { - Level string - Pretty bool - Color bool - File string + Level string `mapstructure:"level"` + Pretty bool `mapstructure:"pretty"` + Color bool `mapstructure:"color"` + File string `mapstructure:"file"` } // Debug defines the available debug configuration. type Debug struct { - Addr string - Token string - Pprof bool - Zpages bool + Addr string `mapstructure:"addr"` + Token string `mapstructure:"token"` + Pprof bool `mapstructure:"pprof"` + Zpages bool `mapstructure:"zpages"` } // Server defines the available server configuration. type Server struct { - Name string - Namespace string - Address string - Version string + Name string `mapstructure:"name"` + Namespace string `mapstructure:"namespace"` + Address string `mapstructure:"address"` + Version string `mapstructure:"version"` } // Tracing defines the available tracing configuration. type Tracing struct { - Enabled bool - Type string - Endpoint string - Collector string - Service string + Enabled bool `mapstructure:"enabled"` + Type string `mapstructure:"type"` + Endpoint string `mapstructure:"endpoint"` + Collector string `mapstructure:"collector"` + Service string `mapstructure:"service"` } // Config combines all available configuration parts. type Config struct { - File string - Log Log - Debug Debug - Server Server - Tracing Tracing - Thumbnail Thumbnail + File string `mapstructure:"file"` + Log Log `mapstructure:"log"` + Debug Debug `mapstructure:"debug"` + Server Server `mapstructure:"server"` + Tracing Tracing `mapstructure:"tracing"` + Thumbnail Thumbnail `mapstructure:"thumbnail"` Context context.Context Supervised bool @@ -50,24 +59,103 @@ type Config struct { // FileSystemStorage defines the available filesystem storage configuration. type FileSystemStorage struct { - RootDirectory string + RootDirectory string `mapstructure:"root_directory"` } // FileSystemSource defines the available filesystem source configuration. type FileSystemSource struct { - BasePath string + BasePath string `mapstructure:"base_path"` } // Thumbnail defines the available thumbnail related configuration. type Thumbnail struct { - Resolutions []string - FileSystemStorage FileSystemStorage - WebdavAllowInsecure bool - RevaGateway string - WebdavNamespace string + Resolutions []string `mapstructure:"resolutions"` + FileSystemStorage FileSystemStorage `mapstructure:"filesystem_storage"` + WebdavAllowInsecure bool `mapstructure:"webdav_allow_insecure"` + RevaGateway string `mapstructure:"reva_gateway"` + WebdavNamespace string `mapstructure:"webdav_namespace"` } // New initializes a new configuration with or without defaults. func New() *Config { return &Config{} } + +func DefaultConfig() *Config { + return &Config{ + Log: Log{}, + Debug: Debug{ + Addr: "127.0.0.1:9189", + Token: "", + Pprof: false, + Zpages: false, + }, + Server: Server{ + Name: "thumbnails", + Namespace: "com.owncloud.api", + Address: "127.0.0.1:9185", + }, + Tracing: Tracing{ + Enabled: false, + Type: "jaeger", + Endpoint: "", + Collector: "", + Service: "thumbnails", + }, + Thumbnail: Thumbnail{ + Resolutions: []string{"16x16", "32x32", "64x64", "128x128", "1920x1080", "3840x2160", "7680x4320"}, + FileSystemStorage: FileSystemStorage{ + RootDirectory: path.Join(defaults.BaseDataPath(), "thumbnails"), + }, + WebdavAllowInsecure: true, + RevaGateway: "127.0.0.1:9142", + WebdavNamespace: "/home", + }, + } +} + +// GetEnv fetches a list of known env variables for this extension. It is to be used by gookit, as it provides a list +// with all the environment variables an extension supports. +func GetEnv() []string { + var r = make([]string, len(structMappings(&Config{}))) + for i := range structMappings(&Config{}) { + r = append(r, structMappings(&Config{})[i].EnvVars...) + } + + return r +} + +// UnmapEnv loads values from the gooconf.Config argument and sets them in the expected destination. +func (c *Config) UnmapEnv(gooconf *gofig.Config) error { + vals := structMappings(c) + for i := range vals { + for j := range vals[i].EnvVars { + // we need to guard against v != "" because this is the condition that checks that the value is set from the environment. + // the `ok` guard is not enough, apparently. + if v, ok := gooconf.GetValue(vals[i].EnvVars[j]); ok && v != "" { + + // get the destination type from destination + switch reflect.ValueOf(vals[i].Destination).Type().String() { + case "*bool": + r := gooconf.Bool(vals[i].EnvVars[j]) + *vals[i].Destination.(*bool) = r + case "*string": + r := gooconf.String(vals[i].EnvVars[j]) + *vals[i].Destination.(*string) = r + case "*int": + r := gooconf.Int(vals[i].EnvVars[j]) + *vals[i].Destination.(*int) = r + case "*float64": + // defaults to float64 + r := gooconf.Float(vals[i].EnvVars[j]) + *vals[i].Destination.(*float64) = r + default: + // it is unlikely we will ever get here. Let this serve more as a runtime check for when debugging. + return fmt.Errorf("invalid type for env var: `%v`", vals[i].EnvVars[j]) + } + } + } + } + + return nil +} diff --git a/thumbnails/pkg/config/env.go b/thumbnails/pkg/config/env.go new file mode 100644 index 000000000..7c012b691 --- /dev/null +++ b/thumbnails/pkg/config/env.go @@ -0,0 +1,96 @@ +package config + +type mapping struct { + EnvVars []string // name of the EnvVars var. + Destination interface{} // memory address of the original config value to modify. +} + +// structMappings binds a set of environment variables to a destination on cfg. +func structMappings(cfg *Config) []mapping { + return []mapping{ + { + EnvVars: []string{"THUMBNAILS_LOG_FILE", "OCIS_LOG_FILE"}, + Destination: &cfg.Log.File, + }, + { + EnvVars: []string{"THUMBNAILS_LOG_LEVEL", "OCIS_LOG_LEVEL"}, + Destination: &cfg.Log.Level, + }, + { + EnvVars: []string{"THUMBNAILS_LOG_PRETTY", "OCIS_LOG_PRETTY"}, + Destination: &cfg.Log.Pretty, + }, + { + EnvVars: []string{"THUMBNAILS_LOG_COLOR", "OCIS_LOG_COLOR"}, + Destination: &cfg.Log.Color, + }, + { + EnvVars: []string{"THUMBNAILS_CONFIG_FILE"}, + Destination: &cfg.File, + }, + { + EnvVars: []string{"THUMBNAILS_TRACING_ENABLED", "OCIS_TRACING_ENABLED"}, + Destination: &cfg.Tracing.Enabled, + }, + { + EnvVars: []string{"THUMBNAILS_TRACING_TYPE", "OCIS_TRACING_TYPE"}, + Destination: &cfg.Tracing.Type, + }, + { + EnvVars: []string{"THUMBNAILS_TRACING_ENDPOINT", "OCIS_TRACING_ENDPOINT"}, + Destination: &cfg.Tracing.Endpoint, + }, + { + EnvVars: []string{"THUMBNAILS_TRACING_COLLECTOR", "OCIS_TRACING_COLLECTOR"}, + Destination: &cfg.Tracing.Collector, + }, + { + EnvVars: []string{"THUMBNAILS_TRACING_SERVICE"}, + Destination: &cfg.Tracing.Service, + }, + { + EnvVars: []string{"THUMBNAILS_DEBUG_ADDR"}, + Destination: &cfg.Debug.Addr, + }, + { + EnvVars: []string{"THUMBNAILS_DEBUG_TOKEN"}, + Destination: &cfg.Debug.Token, + }, + { + EnvVars: []string{"THUMBNAILS_DEBUG_PPROF"}, + Destination: &cfg.Debug.Pprof, + }, + { + EnvVars: []string{"THUMBNAILS_DEBUG_ZPAGES"}, + Destination: &cfg.Debug.Zpages, + }, + { + EnvVars: []string{"THUMBNAILS_GRPC_NAME"}, + Destination: &cfg.Server.Name, + }, + { + EnvVars: []string{"THUMBNAILS_GRPC_ADDR"}, + Destination: &cfg.Server.Address, + }, + { + EnvVars: []string{"THUMBNAILS_GRPC_NAMESPACE"}, + Destination: &cfg.Server.Namespace, + }, + { + EnvVars: []string{"THUMBNAILS_FILESYSTEMSTORAGE_ROOT"}, + Destination: &cfg.Thumbnail.FileSystemStorage.RootDirectory, + }, + { + EnvVars: []string{"REVA_GATEWAY"}, + Destination: &cfg.Thumbnail.RevaGateway, + }, + { + EnvVars: []string{"THUMBNAILS_WEBDAVSOURCE_INSECURE"}, + Destination: &cfg.Thumbnail.WebdavAllowInsecure, + }, + { + EnvVars: []string{"STORAGE_WEBDAV_NAMESPACE"}, + Destination: &cfg.Thumbnail.WebdavNamespace, + }, + } +} diff --git a/thumbnails/pkg/flagset/flagset.go b/thumbnails/pkg/flagset/flagset.go index 6a877400e..0640c730b 100644 --- a/thumbnails/pkg/flagset/flagset.go +++ b/thumbnails/pkg/flagset/flagset.go @@ -1,9 +1,6 @@ package flagset import ( - "path" - - "github.com/owncloud/ocis/ocis-pkg/config/defaults" "github.com/owncloud/ocis/ocis-pkg/flags" "github.com/owncloud/ocis/thumbnails/pkg/config" @@ -23,162 +20,6 @@ func HealthWithConfig(cfg *config.Config) []cli.Flag { } } -// ServerWithConfig applies cfg to the root flagset -func ServerWithConfig(cfg *config.Config) []cli.Flag { - return []cli.Flag{ - &cli.StringFlag{ - Name: "log-file", - Usage: "Enable log to file", - EnvVars: []string{"THUMBNAILS_LOG_FILE", "OCIS_LOG_FILE"}, - Destination: &cfg.Log.File, - }, - &cli.StringFlag{ - Name: "log-level", - Usage: "Set logging level", - EnvVars: []string{"THUMBNAILS_LOG_LEVEL", "OCIS_LOG_LEVEL"}, - Destination: &cfg.Log.Level, - }, - &cli.BoolFlag{ - Name: "log-pretty", - Usage: "Enable pretty logging", - EnvVars: []string{"THUMBNAILS_LOG_PRETTY", "OCIS_LOG_PRETTY"}, - Destination: &cfg.Log.Pretty, - }, - &cli.BoolFlag{ - Name: "log-color", - Usage: "Enable colored logging", - EnvVars: []string{"THUMBNAILS_LOG_COLOR", "OCIS_LOG_COLOR"}, - Destination: &cfg.Log.Color, - }, - &cli.StringFlag{ - Name: "config-file", - Value: "", - Usage: "Path to config file", - EnvVars: []string{"THUMBNAILS_CONFIG_FILE"}, - Destination: &cfg.File, - }, - &cli.BoolFlag{ - Name: "tracing-enabled", - Usage: "Enable sending traces", - EnvVars: []string{"THUMBNAILS_TRACING_ENABLED", "OCIS_TRACING_ENABLED"}, - Destination: &cfg.Tracing.Enabled, - }, - &cli.StringFlag{ - Name: "tracing-type", - Value: flags.OverrideDefaultString(cfg.Tracing.Type, "jaeger"), - Usage: "Tracing backend type", - EnvVars: []string{"THUMBNAILS_TRACING_TYPE", "OCIS_TRACING_TYPE"}, - Destination: &cfg.Tracing.Type, - }, - &cli.StringFlag{ - Name: "tracing-endpoint", - Value: flags.OverrideDefaultString(cfg.Tracing.Endpoint, ""), - Usage: "Endpoint for the agent", - EnvVars: []string{"THUMBNAILS_TRACING_ENDPOINT", "OCIS_TRACING_ENDPOINT"}, - Destination: &cfg.Tracing.Endpoint, - }, - &cli.StringFlag{ - Name: "tracing-collector", - Value: flags.OverrideDefaultString(cfg.Tracing.Collector, ""), - Usage: "Endpoint for the collector", - EnvVars: []string{"THUMBNAILS_TRACING_COLLECTOR", "OCIS_TRACING_COLLECTOR"}, - Destination: &cfg.Tracing.Collector, - }, - &cli.StringFlag{ - Name: "tracing-service", - Value: flags.OverrideDefaultString(cfg.Tracing.Service, "thumbnails"), - Usage: "Service name for tracing", - EnvVars: []string{"THUMBNAILS_TRACING_SERVICE"}, - Destination: &cfg.Tracing.Service, - }, - &cli.StringFlag{ - Name: "debug-addr", - Value: flags.OverrideDefaultString(cfg.Debug.Addr, "127.0.0.1:9189"), - Usage: "Address to bind debug server", - EnvVars: []string{"THUMBNAILS_DEBUG_ADDR"}, - Destination: &cfg.Debug.Addr, - }, - &cli.StringFlag{ - Name: "debug-token", - Value: flags.OverrideDefaultString(cfg.Debug.Token, ""), - Usage: "Token to grant metrics access", - EnvVars: []string{"THUMBNAILS_DEBUG_TOKEN"}, - Destination: &cfg.Debug.Token, - }, - &cli.BoolFlag{ - Name: "debug-pprof", - Usage: "Enable pprof debugging", - EnvVars: []string{"THUMBNAILS_DEBUG_PPROF"}, - Destination: &cfg.Debug.Pprof, - }, - &cli.BoolFlag{ - Name: "debug-zpages", - Usage: "Enable zpages debugging", - EnvVars: []string{"THUMBNAILS_DEBUG_ZPAGES"}, - Destination: &cfg.Debug.Zpages, - }, - &cli.StringFlag{ - Name: "grpc-name", - Value: flags.OverrideDefaultString(cfg.Server.Name, "thumbnails"), - Usage: "Name of the service", - EnvVars: []string{"THUMBNAILS_GRPC_NAME"}, - Destination: &cfg.Server.Name, - }, - &cli.StringFlag{ - Name: "grpc-addr", - Value: flags.OverrideDefaultString(cfg.Server.Address, "127.0.0.1:9185"), - Usage: "Address to bind grpc server", - EnvVars: []string{"THUMBNAILS_GRPC_ADDR"}, - Destination: &cfg.Server.Address, - }, - &cli.StringFlag{ - Name: "grpc-namespace", - Value: flags.OverrideDefaultString(cfg.Server.Namespace, "com.owncloud.api"), - Usage: "Set the base namespace for the grpc namespace", - EnvVars: []string{"THUMBNAILS_GRPC_NAMESPACE"}, - Destination: &cfg.Server.Namespace, - }, - &cli.StringFlag{ - Name: "filesystemstorage-root", - Value: flags.OverrideDefaultString(cfg.Thumbnail.FileSystemStorage.RootDirectory, path.Join(defaults.BaseDataPath(), "thumbnails")), - Usage: "Root path of the filesystem storage directory", - EnvVars: []string{"THUMBNAILS_FILESYSTEMSTORAGE_ROOT"}, - Destination: &cfg.Thumbnail.FileSystemStorage.RootDirectory, - }, - &cli.StringFlag{ - Name: "reva-gateway-addr", - Value: flags.OverrideDefaultString(cfg.Thumbnail.RevaGateway, "127.0.0.1:9142"), - Usage: "Address of REVA gateway endpoint", - EnvVars: []string{"REVA_GATEWAY"}, - Destination: &cfg.Thumbnail.RevaGateway, - }, - &cli.BoolFlag{ - Name: "webdavsource-insecure", - Value: flags.OverrideDefaultBool(cfg.Thumbnail.WebdavAllowInsecure, true), - Usage: "Whether to skip certificate checks", - EnvVars: []string{"THUMBNAILS_WEBDAVSOURCE_INSECURE"}, - Destination: &cfg.Thumbnail.WebdavAllowInsecure, - }, - &cli.StringSliceFlag{ - Name: "thumbnail-resolution", - Value: cli.NewStringSlice("16x16", "32x32", "64x64", "128x128", "1920x1080", "3840x2160", "7680x4320"), - Usage: "--thumbnail-resolution 16x16 [--thumbnail-resolution 32x32]", - EnvVars: []string{"THUMBNAILS_RESOLUTIONS"}, - }, - &cli.StringFlag{ - Name: "webdav-namespace", - Value: flags.OverrideDefaultString(cfg.Thumbnail.WebdavNamespace, "/home"), - Usage: "Namespace prefix for the webdav endpoint", - EnvVars: []string{"STORAGE_WEBDAV_NAMESPACE"}, - Destination: &cfg.Thumbnail.WebdavNamespace, - }, - &cli.StringFlag{ - Name: "extensions", - Usage: "Run specific extensions during supervised mode", - }, - } -} - // ListThumbnailsWithConfig applies the config to the flagset for listing thumbnails services. func ListThumbnailsWithConfig(cfg *config.Config) []cli.Flag { return []cli.Flag{