-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
1,108 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
VANITY_MAINTENANCE=false | ||
VANITY_LOG_LEVEL=info | ||
VANITY_CONSOLE_LOG=true | ||
VANITY_BIND_ADDR=127.0.0.1:8000 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,54 @@ | ||
package config | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/rotationalio/confire" | ||
"github.com/rotationalio/vanity/logger" | ||
"github.com/rs/zerolog" | ||
) | ||
|
||
// All environment variables will have this prefix unless otherwise defined in struct | ||
// tags. For example, the conf.LogLevel environment variable will be VANITY_LOG_LEVEL | ||
// because of this prefix and the split_words struct tag in the conf below. | ||
const Prefix = "vanity" | ||
|
||
type Config struct{} | ||
// Configures the vanityd server from the environment. | ||
type Config struct { | ||
Maintenance bool `default:"false" desc:"if true, the server will start in maintenance mode"` | ||
LogLevel logger.LevelDecoder `split_words:"true" default:"info" desc:"specify the verbosity of logging (trace, debug, info, warn, error, fatal panic)"` | ||
ConsoleLog bool `split_words:"true" default:"false" desc:"if true logs colorized human readable output instead of json"` | ||
BindAddr string `split_words:"true" default:":3264" desc:"the ip address and port to bind the server on"` | ||
ReadTimeout time.Duration `split_words:"true" default:"20s" desc:"amount of time allowed to read request headers before server decides the request is too slow"` | ||
WriteTimeout time.Duration `split_words:"true" default:"20s" desc:"maximum amount of time before timing out a write to a response"` | ||
IdleTimeout time.Duration `split_words:"true" default:"10m" desc:"maximum amount of time to wait for the next request while keep alives are enabled"` | ||
processed bool | ||
} | ||
|
||
func New() (conf Config, err error) { | ||
if err = confire.Process(Prefix, &conf); err != nil { | ||
return Config{}, err | ||
} | ||
|
||
if err = conf.Validate(); err != nil { | ||
return Config{}, err | ||
} | ||
|
||
conf.processed = true | ||
return conf, nil | ||
} | ||
|
||
// Returns true if the config has not been correctly processed from the environment. | ||
func (c Config) IsZero() bool { | ||
return !c.processed | ||
} | ||
|
||
// Custom validations are added here, particularly validations that require one or more | ||
// fields to be processed before the validation occurs. | ||
func (c Config) Validate() (err error) { | ||
return err | ||
} | ||
|
||
func New() (Config, error) { | ||
return Config{}, nil | ||
func (c Config) GetLogLevel() zerolog.Level { | ||
return zerolog.Level(c.LogLevel) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package config_test | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/rotationalio/vanity/config" | ||
"github.com/rs/zerolog" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
var testEnv = map[string]string{ | ||
"VANITY_MAINTENANCE": "true", | ||
"VANITY_LOG_LEVEL": "debug", | ||
"VANITY_CONSOLE_LOG": "true", | ||
"VANITY_BIND_ADDR": "127.0.0.1:443", | ||
} | ||
|
||
func TestConfig(t *testing.T) { | ||
// Set required environment variables and cleanup after the test is complete. | ||
t.Cleanup(cleanupEnv()) | ||
setEnv() | ||
|
||
conf, err := config.New() | ||
require.NoError(t, err, "could not process configuration from the environment") | ||
require.False(t, conf.IsZero(), "processed config should not be zero valued") | ||
|
||
// Ensure configuration is correctly set from the environment | ||
require.True(t, conf.Maintenance) | ||
require.Equal(t, zerolog.DebugLevel, conf.GetLogLevel()) | ||
require.True(t, conf.ConsoleLog) | ||
require.Equal(t, testEnv["VANITY_BIND_ADDR"], conf.BindAddr) | ||
} | ||
|
||
// Returns the current environment for the specified keys, or if no keys are specified | ||
// then it returns the current environment for all keys in the testEnv variable. | ||
func curEnv(keys ...string) map[string]string { | ||
env := make(map[string]string) | ||
if len(keys) > 0 { | ||
for _, key := range keys { | ||
if val, ok := os.LookupEnv(key); ok { | ||
env[key] = val | ||
} | ||
} | ||
} else { | ||
for key := range testEnv { | ||
env[key] = os.Getenv(key) | ||
} | ||
} | ||
|
||
return env | ||
} | ||
|
||
// Sets the environment variables from the testEnv variable. If no keys are specified, | ||
// then this function sets all environment variables from the testEnv. | ||
func setEnv(keys ...string) { | ||
if len(keys) > 0 { | ||
for _, key := range keys { | ||
if val, ok := testEnv[key]; ok { | ||
os.Setenv(key, val) | ||
} | ||
} | ||
} else { | ||
for key, val := range testEnv { | ||
os.Setenv(key, val) | ||
} | ||
} | ||
} | ||
|
||
// Cleanup helper function that can be run when the tests are complete to reset the | ||
// environment back to its previous state before the test was run. | ||
func cleanupEnv(keys ...string) func() { | ||
prevEnv := curEnv(keys...) | ||
return func() { | ||
for key, val := range prevEnv { | ||
if val != "" { | ||
os.Setenv(key, val) | ||
} else { | ||
os.Unsetenv(key) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,54 @@ | ||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= | ||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= | ||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= | ||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= | ||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= | ||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= | ||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= | ||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= | ||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | ||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= | ||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | ||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | ||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= | ||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= | ||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= | ||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= | ||
github.com/rotationalio/confire v1.1.0 h1:h10RDxiO/XH6UStfxY+oMJOVxt3Elqociilb7fIfANs= | ||
github.com/rotationalio/confire v1.1.0/go.mod h1:ug7pBDiZZl/4JjXJ2Effmj+L+0T2DBbG+Us1qQcRex0= | ||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= | ||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= | ||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= | ||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= | ||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= | ||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= | ||
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= | ||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= | ||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= | ||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= | ||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package logger | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/rs/zerolog" | ||
) | ||
|
||
// LogLevelDecoder deserializes the log level from a config string. | ||
type LevelDecoder zerolog.Level | ||
|
||
// Names of log levels for use in encoding/decoding from strings. | ||
const ( | ||
llPanic = "panic" | ||
llFatal = "fatal" | ||
llError = "error" | ||
llWarn = "warn" | ||
llInfo = "info" | ||
llDebug = "debug" | ||
llTrace = "trace" | ||
) | ||
|
||
// Decode implements confire Decoder interface. | ||
func (ll *LevelDecoder) Decode(value string) error { | ||
value = strings.TrimSpace(strings.ToLower(value)) | ||
switch value { | ||
case llPanic: | ||
*ll = LevelDecoder(zerolog.PanicLevel) | ||
case llFatal: | ||
*ll = LevelDecoder(zerolog.FatalLevel) | ||
case llError: | ||
*ll = LevelDecoder(zerolog.ErrorLevel) | ||
case llWarn: | ||
*ll = LevelDecoder(zerolog.WarnLevel) | ||
case llInfo: | ||
*ll = LevelDecoder(zerolog.InfoLevel) | ||
case llDebug: | ||
*ll = LevelDecoder(zerolog.DebugLevel) | ||
case llTrace: | ||
*ll = LevelDecoder(zerolog.TraceLevel) | ||
default: | ||
return fmt.Errorf("unknown log level %q", value) | ||
} | ||
return nil | ||
} | ||
|
||
// Encode converts the loglevel into a string for use in YAML and JSON | ||
func (ll *LevelDecoder) Encode() (string, error) { | ||
switch zerolog.Level(*ll) { | ||
case zerolog.PanicLevel: | ||
return llPanic, nil | ||
case zerolog.FatalLevel: | ||
return llFatal, nil | ||
case zerolog.ErrorLevel: | ||
return llError, nil | ||
case zerolog.WarnLevel: | ||
return llWarn, nil | ||
case zerolog.InfoLevel: | ||
return llInfo, nil | ||
case zerolog.DebugLevel: | ||
return llDebug, nil | ||
case zerolog.TraceLevel: | ||
return llTrace, nil | ||
default: | ||
return "", fmt.Errorf("unknown log level %d", ll) | ||
} | ||
} | ||
|
||
func (ll LevelDecoder) String() string { | ||
ls, _ := ll.Encode() | ||
return ls | ||
} | ||
|
||
// UnmarshalJSON implements json.Unmarshaler | ||
func (ll *LevelDecoder) UnmarshalJSON(data []byte) error { | ||
var ls string | ||
if err := json.Unmarshal(data, &ls); err != nil { | ||
return err | ||
} | ||
return ll.Decode(ls) | ||
} | ||
|
||
// MarshalJSON implements json.Marshaler | ||
func (ll LevelDecoder) MarshalJSON() ([]byte, error) { | ||
ls, err := ll.Encode() | ||
if err != nil { | ||
return nil, err | ||
} | ||
return json.Marshal(ls) | ||
} |
Oops, something went wrong.