switch accounts to struct tag based env config

This commit is contained in:
Willy Kloucek
2021-12-16 16:51:18 +01:00
committed by Jörn Friedrich Dreyer
parent ebfe8f069c
commit d2befd9257
9 changed files with 99 additions and 244 deletions

View File

@@ -4,13 +4,13 @@ import (
"context"
"os"
"github.com/owncloud/ocis/ocis-pkg/shared"
"github.com/imdario/mergo"
"github.com/owncloud/ocis/accounts/pkg/config"
ociscfg "github.com/owncloud/ocis/ocis-pkg/config"
"github.com/owncloud/ocis/ocis-pkg/version"
"github.com/thejerf/suture/v4"
"github.com/urfave/cli/v2"
"github.com/wkloucek/envdecode"
)
// Execute is the entry point for the ocis-accounts command.
@@ -58,28 +58,35 @@ func Execute(cfg *config.Config) error {
// ParseConfig loads accounts configuration from known paths.
func ParseConfig(c *cli.Context, cfg *config.Config) error {
conf, err := ociscfg.BindSourcesToStructs("accounts", cfg)
_, err := ociscfg.BindSourcesToStructs("accounts", cfg)
if err != nil {
return err
}
// provide with defaults for shared logging, since we need a valid destination address for BindEnv.
if cfg.Log == nil && cfg.Commons != nil && cfg.Commons.Log != nil {
cfg.Log = &shared.Log{
Level: cfg.Commons.Log.Level,
Pretty: cfg.Commons.Log.Pretty,
Color: cfg.Commons.Log.Color,
File: cfg.Commons.Log.File,
}
} else if cfg.Log == nil && cfg.Commons == nil {
cfg.Log = &shared.Log{}
}
//if cfg.Log == nil && cfg.Commons != nil && cfg.Commons.Log != nil {
// cfg.Log = &shared.Log{
// Level: cfg.Commons.Log.Level,
// Pretty: cfg.Commons.Log.Pretty,
// Color: cfg.Commons.Log.Color,
// File: cfg.Commons.Log.File,
// }
//} else if cfg.Log == nil && cfg.Commons == nil {
// cfg.Log = &shared.Log{}
//}
// load all env variables relevant to the config in the current context.
conf.LoadOSEnv(config.GetEnv(cfg), false)
envCfg := config.Config{}
if err := envdecode.Decode(&envCfg); err != nil {
return err
}
bindings := config.StructMappings(cfg)
return ociscfg.BindEnv(conf, bindings)
// merge environment variable config on top of the current config
if err := mergo.Merge(cfg, envCfg, mergo.WithOverride); err != nil {
return err
}
return nil
}
// SutureService allows for the accounts command to be embedded and supervised by a suture supervisor tree.
@@ -89,7 +96,7 @@ type SutureService struct {
// NewSutureService creates a new accounts.SutureService
func NewSutureService(cfg *ociscfg.Config) suture.Service {
cfg.Accounts.Commons = cfg.Commons
//cfg.Accounts.Commons = cfg.Commons
return SutureService{
cfg: cfg.Accounts,
}

View File

@@ -16,6 +16,18 @@ import (
"github.com/urfave/cli/v2"
)
// TODO: don't redeclare from ocis-pkg/log/log.go
// LoggerFromConfig initializes a service-specific logger instance.
func LoggerFromConfig(name string, cfg config.Log) log.Logger {
return log.NewLogger(
log.Name(name),
log.Level(cfg.Level),
log.Pretty(cfg.Pretty),
log.Color(cfg.Color),
log.File(cfg.File),
)
}
// Server is the entry point for the server command.
func Server(cfg *config.Config) *cli.Command {
return &cli.Command{
@@ -36,7 +48,7 @@ func Server(cfg *config.Config) *cli.Command {
return nil
},
Action: func(c *cli.Context) error {
logger := log.LoggerFromConfig("accounts", *cfg.Log)
logger := LoggerFromConfig("accounts", cfg.Log)
err := tracing.Configure(cfg)
if err != nil {
return err

View File

@@ -5,34 +5,9 @@ import (
"context"
"path"
"github.com/owncloud/ocis/ocis-pkg/shared"
"github.com/owncloud/ocis/ocis-pkg/config/defaults"
)
// LDAP defines the available ldap configuration.
type LDAP struct {
Hostname string `ocisConfig:"hostname"`
Port int `ocisConfig:"port"`
BaseDN string `ocisConfig:"base_dn"`
UserFilter string `ocisConfig:"user_filter"`
GroupFilter string `ocisConfig:"group_filter"`
BindDN string `ocisConfig:"bind_dn"`
BindPassword string `ocisConfig:"bind_password"`
IDP string `ocisConfig:"idp"`
Schema LDAPSchema `ocisConfig:"schema"`
}
// LDAPSchema defines the available ldap schema configuration.
type LDAPSchema struct {
AccountID string `ocisConfig:"account_id"`
Identities string `ocisConfig:"identities"`
Username string `ocisConfig:"username"`
DisplayName string `ocisConfig:"display_name"`
Mail string `ocisConfig:"mail"`
Groups string `ocisConfig:"groups"`
}
// CORS defines the available cors configuration.
type CORS struct {
AllowedOrigins []string `ocisConfig:"allowed_origins"`
@@ -43,98 +18,111 @@ type CORS struct {
// HTTP defines the available http configuration.
type HTTP struct {
Addr string `ocisConfig:"addr"`
Namespace string `ocisConfig:"namespace"`
Root string `ocisConfig:"root"`
CacheTTL int `ocisConfig:"cache_ttl"`
Addr string `ocisConfig:"addr" env:"ACCOUNTS_HTTP_ADDR"`
Namespace string
Root string `ocisConfig:"root" env:"ACCOUNTS_HTTP_ROOT"`
CacheTTL int `ocisConfig:"cache_ttl" env:"ACCOUNTS_CACHE_TTL"`
CORS CORS `ocisConfig:"cors"`
}
// GRPC defines the available grpc configuration.
type GRPC struct {
Addr string `ocisConfig:"addr"`
Namespace string `ocisConfig:"namespace"`
Addr string `ocisConfig:"addr" env:"ACCOUNTS_GRPC_ADDR"`
Namespace string
}
// Service defines the available service configuration.
type Service struct {
Name string `ocisConfig:"name"`
Version string `ocisConfig:"version"`
Name string
Version string
}
// Asset defines the available asset configuration.
type Asset struct {
Path string `ocisConfig:"path"`
Path string `ocisConfig:"path" env:"ACCOUNTS_ASSET_PATH"`
}
// TokenManager is the config for using the reva token manager
type TokenManager struct {
JWTSecret string `ocisConfig:"jwt_secret"`
JWTSecret string `ocisConfig:"jwt_secret" env:"OCIS_JWT_SECRET;ACCOUNTS_JWT_SECRET"`
}
// Repo defines which storage implementation is to be used.
type Repo struct {
Backend string `ocisConfig:"backend"`
Backend string `ocisConfig:"backend" env:"ACCOUNTS_STORAGE_BACKEND"`
Disk Disk `ocisConfig:"disk"`
CS3 CS3 `ocisConfig:"cs3"`
}
// Disk is the local disk implementation of the storage.
type Disk struct {
Path string `ocisConfig:"path"`
Path string `ocisConfig:"path" env:"ACCOUNTS_STORAGE_DISK_PATH"`
}
// CS3 is the cs3 implementation of the storage.
type CS3 struct {
ProviderAddr string `ocisConfig:"provider_addr"`
JWTSecret string `ocisConfig:"jwt_secret"`
ProviderAddr string `ocisConfig:"provider_addr" env:"ACCOUNTS_STORAGE_CS3_PROVIDER_ADDR"`
JWTSecret string `ocisConfig:"jwt_secret" env:"ACCOUNTS_STORAGE_CS3_JWT_SECRET"`
}
// ServiceUser defines the user required for EOS.
type ServiceUser struct {
UUID string `ocisConfig:"uuid"`
Username string `ocisConfig:"username"`
UID int64 `ocisConfig:"uid"`
GID int64 `ocisConfig:"gid"`
UUID string `ocisConfig:"uuid" env:"ACCOUNTS_SERVICE_USER_UUID"`
Username string `ocisConfig:"username" env:"ACCOUNTS_SERVICE_USER_USERNAME"`
UID int64 `ocisConfig:"uid" env:"ACCOUNTS_SERVICE_USER_UID"`
GID int64 `ocisConfig:"gid" env:"ACCOUNTS_SERVICE_USER_GID"`
}
// Index defines config for indexes.
type Index struct {
UID Bound `ocisConfig:"uid"`
GID Bound `ocisConfig:"gid"`
UID UIDBound `ocisConfig:"uid"`
GID GIDBound `ocisConfig:"gid"`
}
// Bound defines a lower and upper bound.
type Bound struct {
Lower int64 `ocisConfig:"lower"`
Upper int64 `ocisConfig:"upper"`
// GIDBound defines a lower and upper bound.
type GIDBound struct {
Lower int64 `ocisConfig:"lower" env:"ACCOUNTS_GID_INDEX_LOWER_BOUND"`
Upper int64 `ocisConfig:"upper" env:"ACCOUNTS_GID_INDEX_UPPER_BOUND"`
}
// UIDBound defines a lower and upper bound.
type UIDBound struct {
Lower int64 `ocisConfig:"lower" env:"ACCOUNTS_UID_INDEX_LOWER_BOUND"`
Upper int64 `ocisConfig:"upper" env:"ACCOUNTS_UID_INDEX_UPPER_BOUND"`
}
// Tracing defines the available tracing configuration.
type Tracing struct {
Enabled bool `ocisConfig:"enabled"`
Type string `ocisConfig:"type"`
Endpoint string `ocisConfig:"endpoint"`
Collector string `ocisConfig:"collector"`
Service string `ocisConfig:"service"`
Enabled bool `ocisConfig:"enabled" env:"OCIS_TRACING_ENABLED;ACCOUNTS_TRACING_ENABLED"`
Type string `ocisConfig:"type" env:"OCIS_TRACING_TYPE;ACCOUNTS_TRACING_TYPE"`
Endpoint string `ocisConfig:"endpoint" env:"OCIS_TRACING_ENDPOINT;ACCOUNTS_TRACING_ENDPOINT"`
Collector string `ocisConfig:"collector" env:"OCIS_TRACING_COLLECTOR;ACCOUNTS_TRACING_COLLECTOR"`
Service string `ocisConfig:"service" env:"ACCOUNTS_TRACING_SERVICE"` //TODO: should this be an ID? or the same as Service.Name?
}
// Log defines the available log configuration.
type Log struct {
Level string `mapstructure:"level" env:"OCIS_LOG_LEVEL;ACCOUNTS_LOG_LEVEL"`
Pretty bool `mapstructure:"pretty" env:"OCIS_LOG_PRETTY;ACCOUNTS_LOG_PRETTY"`
Color bool `mapstructure:"color" env:"OCIS_LOG_COLOR;ACCOUNTS_LOG_COLOR"`
File string `mapstructure:"file" env:"OCIS_LOG_FILE;ACCOUNTS_LOG_FILE"`
}
// Config merges all Account config parameters.
type Config struct {
*shared.Commons
//*shared.Commons
LDAP LDAP `ocisConfig:"ldap"`
HTTP HTTP `ocisConfig:"http"`
GRPC GRPC `ocisConfig:"grpc"`
Service Service `ocisConfig:"service"`
Asset Asset `ocisConfig:"asset"`
Log *shared.Log `ocisConfig:"log"`
Log Log `ocisConfig:"log"`
TokenManager TokenManager `ocisConfig:"token_manager"`
Repo Repo `ocisConfig:"repo"`
Index Index `ocisConfig:"index"`
ServiceUser ServiceUser `ocisConfig:"service_user"`
HashDifficulty int `ocisConfig:"hash_difficulty"`
DemoUsersAndGroups bool `ocisConfig:"demo_users_and_groups"`
HashDifficulty int `ocisConfig:"hash_difficulty" env:"ACCOUNTS_HASH_DIFFICULTY"`
DemoUsersAndGroups bool `ocisConfig:"demo_users_and_groups" env:"ACCOUNTS_DEMO_USERS_AND_GROUPS"`
Tracing Tracing `ocisConfig:"tracing"`
Context context.Context
@@ -143,14 +131,12 @@ type Config struct {
// New returns a new config.
func New() *Config {
return &Config{
Log: &shared.Log{},
}
return &Config{}
}
func DefaultConfig() *Config {
return &Config{
LDAP: LDAP{},
HTTP: HTTP{
Addr: "127.0.0.1:9181",
Namespace: "com.owncloud.web",
@@ -187,11 +173,11 @@ func DefaultConfig() *Config {
},
},
Index: Index{
UID: Bound{
UID: UIDBound{
Lower: 0,
Upper: 1000,
},
GID: Bound{
GID: GIDBound{
Lower: 0,
Upper: 1000,
},
@@ -208,14 +194,3 @@ func DefaultConfig() *Config {
},
}
}
// 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(cfg *Config) []string {
var r = make([]string, len(structMappings(cfg)))
for i := range structMappings(cfg) {
r = append(r, structMappings(cfg)[i].EnvVars...)
}
return r
}

View File

@@ -1,140 +0,0 @@
package config
import "github.com/owncloud/ocis/ocis-pkg/shared"
// StructMappings binds a set of environment variables to a destination on cfg. Iterating over this set and editing the
// Destination value of a binding will alter the original value, as it is a pointer to its memory address. This lets
// us propagate changes easier.
func StructMappings(cfg *Config) []shared.EnvBinding {
return structMappings(cfg)
}
// structMappings binds a set of environment variables to a destination on cfg.
func structMappings(cfg *Config) []shared.EnvBinding {
return []shared.EnvBinding{
{
EnvVars: []string{"OCIS_LOG_FILE", "ACCOUNTS_LOG_FILE"},
Destination: &cfg.Log.File,
},
{
EnvVars: []string{"OCIS_LOG_LEVEL", "ACCOUNTS_LOG_LEVEL"},
Destination: &cfg.Log.Level,
},
{
EnvVars: []string{"OCIS_LOG_COLOR", "ACCOUNTS_LOG_COLOR"},
Destination: &cfg.Log.Color,
},
{
EnvVars: []string{"OCIS_LOG_PRETTY", "ACCOUNTS_LOG_PRETTY"},
Destination: &cfg.Log.Pretty,
},
{
EnvVars: []string{"OCIS_TRACING_ENABLED", "ACCOUNTS_TRACING_ENABLED"},
Destination: &cfg.Tracing.Enabled,
},
{
EnvVars: []string{"OCIS_TRACING_TYPE", "ACCOUNTS_TRACING_TYPE"},
Destination: &cfg.Tracing.Type,
},
{
EnvVars: []string{"OCIS_TRACING_ENDPOINT", "ACCOUNTS_TRACING_ENDPOINT"},
Destination: &cfg.Tracing.Endpoint,
},
{
EnvVars: []string{"OCIS_TRACING_COLLECTOR", "ACCOUNTS_TRACING_COLLECTOR"},
Destination: &cfg.Tracing.Collector,
},
{
EnvVars: []string{"ACCOUNTS_TRACING_SERVICE"},
Destination: &cfg.Tracing.Service,
},
{
EnvVars: []string{"ACCOUNTS_HTTP_NAMESPACE"},
Destination: &cfg.HTTP.Namespace,
},
{
EnvVars: []string{"ACCOUNTS_HTTP_ADDR"},
Destination: &cfg.HTTP.Addr,
},
{
EnvVars: []string{"ACCOUNTS_HTTP_ROOT"},
Destination: &cfg.HTTP.Root,
},
{
EnvVars: []string{"ACCOUNTS_CACHE_TTL"},
Destination: &cfg.HTTP.CacheTTL,
},
{
EnvVars: []string{"ACCOUNTS_GRPC_NAMESPACE"},
Destination: &cfg.GRPC.Namespace,
},
{
EnvVars: []string{"ACCOUNTS_GRPC_ADDR"},
Destination: &cfg.GRPC.Addr,
},
{
EnvVars: []string{"ACCOUNTS_HASH_DIFFICULTY"},
Destination: &cfg.HashDifficulty,
},
{
EnvVars: []string{"ACCOUNTS_DEMO_USERS_AND_GROUPS"},
Destination: &cfg.DemoUsersAndGroups,
},
{
EnvVars: []string{"ACCOUNTS_ASSET_PATH"},
Destination: &cfg.Asset.Path,
},
{
EnvVars: []string{"OCIS_JWT_SECRET", "ACCOUNTS_JWT_SECRET"},
Destination: &cfg.TokenManager.JWTSecret,
},
{
EnvVars: []string{"ACCOUNTS_STORAGE_BACKEND"},
Destination: &cfg.Repo.Backend,
},
{
EnvVars: []string{"ACCOUNTS_STORAGE_DISK_PATH"},
Destination: &cfg.Repo.Disk.Path,
},
{
EnvVars: []string{"ACCOUNTS_STORAGE_CS3_PROVIDER_ADDR"},
Destination: &cfg.Repo.CS3.ProviderAddr,
},
{
EnvVars: []string{"OCIS_JWT_SECRET", "ACCOUNTS_STORAGE_CS3_JWT_SECRET"},
Destination: &cfg.Repo.CS3.JWTSecret,
},
{
EnvVars: []string{"ACCOUNTS_SERVICE_USER_UUID"},
Destination: &cfg.ServiceUser.UUID,
},
{
EnvVars: []string{"ACCOUNTS_SERVICE_USER_USERNAME"},
Destination: &cfg.ServiceUser.Username,
},
{
EnvVars: []string{"ACCOUNTS_SERVICE_USER_UID"},
Destination: &cfg.ServiceUser.UID,
},
{
EnvVars: []string{"ACCOUNTS_SERVICE_USER_GID"},
Destination: &cfg.ServiceUser.GID,
},
{
EnvVars: []string{"ACCOUNTS_UID_INDEX_LOWER_BOUND"},
Destination: &cfg.Index.UID.Lower,
},
{
EnvVars: []string{"ACCOUNTS_GID_INDEX_LOWER_BOUND"},
Destination: &cfg.Index.GID.Lower,
},
{
EnvVars: []string{"ACCOUNTS_UID_INDEX_UPPER_BOUND"},
Destination: &cfg.Index.UID.Upper,
},
{
EnvVars: []string{"ACCOUNTS_GID_INDEX_UPPER_BOUND"},
Destination: &cfg.Index.GID.Upper,
},
}
}

View File

@@ -8,8 +8,6 @@ import (
"strings"
"time"
"github.com/owncloud/ocis/ocis-pkg/shared"
"github.com/pkg/errors"
"github.com/owncloud/ocis/ocis-pkg/service/grpc"
@@ -111,9 +109,9 @@ func (s Service) buildIndex() (*indexer.Indexer, error) {
func configFromSvc(cfg *config.Config) (*idxcfg.Config, error) {
c := idxcfg.New()
if cfg.Log == nil {
cfg.Log = &shared.Log{}
}
//if cfg.Log == nil {
// cfg.Log = &shared.Log{}
//}
defer func(cfg *config.Config) {
l := log.NewLogger(log.Color(cfg.Log.Color), log.Pretty(cfg.Log.Pretty), log.Level(cfg.Log.Level))

3
go.mod
View File

@@ -36,6 +36,7 @@ require (
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.2
github.com/iancoleman/strcase v0.2.0
github.com/imdario/mergo v0.3.12
github.com/justinas/alice v1.2.0
github.com/libregraph/lico v0.53.1
github.com/mennanov/fieldmask-utils v0.5.0
@@ -56,6 +57,7 @@ require (
github.com/stretchr/testify v1.7.0
github.com/thejerf/suture/v4 v4.0.1
github.com/urfave/cli/v2 v2.3.0
github.com/wkloucek/envdecode v0.0.0-20211216135343-360f0d3c2679
go-micro.dev/v4 v4.5.0
go.opencensus.io v0.23.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0
@@ -150,7 +152,6 @@ require (
github.com/hashicorp/go-plugin v1.4.3 // indirect
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect

2
go.sum
View File

@@ -1316,6 +1316,8 @@ github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgq
github.com/vultr/govultr/v2 v2.0.0/go.mod h1:2PsEeg+gs3p/Fo5Pw8F9mv+DUBEOlrNZ8GmCTGmhOhs=
github.com/wk8/go-ordered-map v0.2.0 h1:KlvGyHstD1kkGZkPtHCyCfRYS0cz84uk6rrW/Dnhdtk=
github.com/wk8/go-ordered-map v0.2.0/go.mod h1:9ZIbRunKbuvfPKyBP1SIKLcXNlv74YCOZ3t3VTS6gRk=
github.com/wkloucek/envdecode v0.0.0-20211216135343-360f0d3c2679 h1:aFJVdr5Lo6QrfgW4nlmguvATkSp+iOfIg6rcdTfu9eM=
github.com/wkloucek/envdecode v0.0.0-20211216135343-360f0d3c2679/go.mod h1:lEir1NV8XGJ16mCsne3GrW6MbiQyhf5WUk55kvu9rYs=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=

View File

@@ -68,7 +68,7 @@ func sanitizeExtensions(set []string, ext []string, f func(a, b string) bool) []
// is to solely modify `dst`, not dealing with the config structs; and do so in a thread safe manner.
func BindSourcesToStructs(extension string, dst interface{}) (*gofig.Config, error) {
sources := DefaultConfigSources(extension, supportedExtensions)
cnf := gofig.NewWithOptions(extension, gofig.ParseEnv)
cnf := gofig.NewWithOptions(extension)
cnf.WithOptions(func(options *gofig.Options) {
options.DecoderConfig.TagName = "ocisConfig"
})

View File

@@ -29,9 +29,9 @@ func AccountsCommand(cfg *config.Config) *cli.Command {
return err
}
if cfg.Commons != nil {
cfg.Accounts.Commons = cfg.Commons
}
//if cfg.Commons != nil {
// cfg.Accounts.Commons = cfg.Commons
//}
return nil
},