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
104 changes: 104 additions & 0 deletions database/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package database

import (
"github.com/creasty/defaults"
"github.com/icinga/icinga-go-library/config"
"github.com/stretchr/testify/require"
"testing"
)

func TestConfig(t *testing.T) {
var defaultOptions Options
require.NoError(t, defaults.Set(&defaultOptions), "setting default options")

subtests := []struct {
name string
opts config.EnvOptions
expected Config
error bool
}{
{
name: "empty-missing-fields",
opts: config.EnvOptions{},
error: true,
},
{
name: "unknown-db-type",
opts: config.EnvOptions{Environment: map[string]string{"TYPE": "☃"}},
error: true,
},
{
name: "minimal-config",
opts: config.EnvOptions{Environment: map[string]string{
"HOST": "db.example.com",
"USER": "user",
"DATABASE": "db",
}},
expected: Config{
Type: "mysql",
Host: "db.example.com",
Database: "db",
User: "user",
Options: defaultOptions,
},
},
{
name: "tls",
opts: config.EnvOptions{Environment: map[string]string{
"HOST": "db.example.com",
"USER": "user",
"DATABASE": "db",
"TLS": "true",
"CERT": "/var/empty/db.crt",
"CA": "/var/empty/ca.crt",
}},
expected: Config{
Type: "mysql",
Host: "db.example.com",
Database: "db",
User: "user",
TlsOptions: config.TLS{
Enable: true,
Cert: "/var/empty/db.crt",
Ca: "/var/empty/ca.crt",
},
Options: defaultOptions,
},
},
{
name: "options",
opts: config.EnvOptions{Environment: map[string]string{
"HOST": "db.example.com",
"USER": "user",
"DATABASE": "db",
"OPTIONS_MAX_CONNECTIONS": "1",
"OPTIONS_MAX_ROWS_PER_TRANSACTION": "65535",
}},
expected: Config{
Type: "mysql",
Host: "db.example.com",
Database: "db",
User: "user",
Options: Options{
MaxConnections: 1,
MaxConnectionsPerTable: 8,
MaxPlaceholdersPerStatement: 8192,
MaxRowsPerTransaction: 65535,
WsrepSyncWait: 7,
},
},
},
}

for _, test := range subtests {
t.Run(test.name, func(t *testing.T) {
var out Config
if err := config.FromEnv(&out, test.opts); test.error {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, test.expected, out)
}
})
}
}
27 changes: 27 additions & 0 deletions logging/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,39 @@ import (
"github.com/pkg/errors"
"go.uber.org/zap/zapcore"
"os"
"strings"
"time"
)

// Options define child loggers with their desired log level.
type Options map[string]zapcore.Level

// UnmarshalText implements encoding.TextUnmarshaler to allow Options to be parsed by env.
//
// This custom TextUnmarshaler is necessary as - for the moment - env does not support map[T]encoding.TextUnmarshaler.
// After <https://github.com/caarlos0/env/pull/323> got merged and a new env release was drafted, this method can be
// removed.
func (o *Options) UnmarshalText(text []byte) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out the lib already supports []encoding.TextUnmarshaler, so why not map[T]encoding.TextUnmarshaler?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for trying to solve this issue upstream. I have updated the PR to reflect this potentially upcoming change in the method's signature, https://github.com/Icinga/icinga-go-library/compare/7d7fd247cd9a69fddf46f237a6ae54ba475cb7e5..2ba2d8b19d9a9460f7f58cc2cd8d31561c03edf3.

However, I am against waiting for this PR to get merged and a new env release to get drafted. If this will be supported in the future, the custom encoding.TextUnmarshaler can just be removed on our end.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written over at your env PR, it works fine. Thanks again.

However, I would still say to merge it in the current state, eventually removing the encoding.TextUnmarshaler implementation later on. We have no "influence" on the upstream, resulting in blocking this PR, resulting in blocking your PR, resulting in having less tests (your PR would be nice to have in #60) and also resulting in blocking the icinga-go-library release.

optionsMap := make(map[string]zapcore.Level)

for _, entry := range strings.Split(string(text), ",") {
key, valueStr, found := strings.Cut(entry, ":")
if !found {
return fmt.Errorf("entry %q cannot be unmarshalled as an Option entry", entry)
}

valueLvl, err := zapcore.ParseLevel(valueStr)
if err != nil {
return fmt.Errorf("entry %q cannot be unmarshalled as level, %w", entry, err)
}

optionsMap[key] = valueLvl
}

*o = optionsMap
return nil
}

// Config defines Logger configuration.
type Config struct {
// zapcore.Level at 0 is for info level.
Expand Down
75 changes: 75 additions & 0 deletions logging/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package logging

import (
"github.com/icinga/icinga-go-library/config"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zapcore"
"testing"
"time"
)

func TestConfig(t *testing.T) {
subtests := []struct {
name string
opts config.EnvOptions
expected Config
error bool
}{
{
name: "empty",
opts: config.EnvOptions{},
expected: Config{
Output: "console",
Interval: 20 * time.Second,
},
},
{
name: "invalid-output",
opts: config.EnvOptions{Environment: map[string]string{"OUTPUT": "☃"}},
error: true,
},
{
name: "customized",
opts: config.EnvOptions{Environment: map[string]string{
"LEVEL": zapcore.DebugLevel.String(),
"OUTPUT": JOURNAL,
"INTERVAL": "3m14s",
}},
expected: Config{
Level: zapcore.DebugLevel,
Output: JOURNAL,
Interval: 3*time.Minute + 14*time.Second,
},
},
{
name: "options",
opts: config.EnvOptions{Environment: map[string]string{"OPTIONS": "foo:debug,bar:info,buz:panic"}},
expected: Config{
Output: "console",
Interval: 20 * time.Second,
Options: map[string]zapcore.Level{
"foo": zapcore.DebugLevel,
"bar": zapcore.InfoLevel,
"buz": zapcore.PanicLevel,
},
},
},
{
name: "options-invalid-levels",
opts: config.EnvOptions{Environment: map[string]string{"OPTIONS": "foo:foo,bar:0"}},
error: true,
},
}

for _, test := range subtests {
t.Run(test.name, func(t *testing.T) {
var out Config
if err := config.FromEnv(&out, test.opts); test.error {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, test.expected, out)
}
})
}
}
100 changes: 100 additions & 0 deletions redis/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package redis

import (
"github.com/creasty/defaults"
"github.com/icinga/icinga-go-library/config"
"github.com/stretchr/testify/require"
"testing"
"time"
)

func TestConfig(t *testing.T) {
var defaultOptions Options
require.NoError(t, defaults.Set(&defaultOptions), "setting default options")

subtests := []struct {
name string
opts config.EnvOptions
expected Config
error bool
}{
{
name: "empty-missing-host",
opts: config.EnvOptions{},
error: true,
},
{
name: "minimal-config",
opts: config.EnvOptions{Environment: map[string]string{"HOST": "kv.example.com"}},
expected: Config{
Host: "kv.example.com",
Options: defaultOptions,
},
},
{
name: "customized-config",
opts: config.EnvOptions{Environment: map[string]string{
"HOST": "kv.example.com",
"USERNAME": "user",
"PASSWORD": "insecure",
"DATABASE": "23",
}},
expected: Config{
Host: "kv.example.com",
Username: "user",
Password: "insecure",
Database: 23,
Options: defaultOptions,
},
},
{
name: "tls",
opts: config.EnvOptions{Environment: map[string]string{
"HOST": "kv.example.com",
"TLS": "true",
"CERT": "/var/empty/db.crt",
"CA": "/var/empty/ca.crt",
}},
expected: Config{
Host: "kv.example.com",
TlsOptions: config.TLS{
Enable: true,
Cert: "/var/empty/db.crt",
Ca: "/var/empty/ca.crt",
},
Options: defaultOptions,
},
},
{
name: "options",
opts: config.EnvOptions{Environment: map[string]string{
"HOST": "kv.example.com",
"OPTIONS_BLOCK_TIMEOUT": "1m",
"OPTIONS_MAX_HMGET_CONNECTIONS": "1000",
}},
expected: Config{
Host: "kv.example.com",
Options: Options{
BlockTimeout: time.Minute,
HMGetCount: 4096,
HScanCount: 4096,
MaxHMGetConnections: 1000,
Timeout: 30 * time.Second,
XReadCount: 4096,
},
},
},
}

for _, test := range subtests {
t.Run(test.name, func(t *testing.T) {
var out Config
if err := config.FromEnv(&out, test.opts); test.error {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, test.expected, out)
}
})
}
}