Compare commits
36 Commits
fix/pipeli
...
config
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba749a12ad | ||
|
|
b59744a4e0 | ||
|
|
fbfc2d3626 | ||
|
|
fb1157515d | ||
|
|
8e7ede0642 | ||
|
|
4a068eb68c | ||
|
|
0aba107436 | ||
|
|
e99d3427ec | ||
|
|
44cbe53705 | ||
|
|
1ccc0b3c48 | ||
|
|
e695f89c85 | ||
|
|
f080bcd3ee | ||
|
|
a5635b10e1 | ||
|
|
1ab9018641 | ||
|
|
c3153012a6 | ||
|
|
8ba479d3bb | ||
|
|
4d398b1bb1 | ||
|
|
8874da0cf6 | ||
|
|
756c9d7364 | ||
|
|
f48a919945 | ||
|
|
f0b58cd5ae | ||
|
|
3095db106b | ||
|
|
0f06ea1a0c | ||
|
|
188d8a4302 | ||
|
|
db95840260 | ||
|
|
c0bf5f5b0a | ||
|
|
35ecd38cef | ||
|
|
6bd1e1387c | ||
|
|
6680622762 | ||
|
|
f3f315726d | ||
|
|
513629e02d | ||
|
|
b180999a71 | ||
|
|
040c0d708b | ||
|
|
64c62896f8 | ||
|
|
a1160b990d | ||
|
|
79d99f21f8 |
5
go.mod
5
go.mod
@@ -20,6 +20,7 @@ require (
|
||||
github.com/go-kit/log v0.2.1
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-redis/redismock/v8 v8.11.5
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
@@ -29,6 +30,7 @@ require (
|
||||
github.com/jmoiron/sqlx v1.3.4
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/knadh/koanf v1.5.0
|
||||
github.com/knadh/koanf/v2 v2.1.1
|
||||
github.com/mailru/easyjson v0.7.7
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/oklog/oklog v0.3.2
|
||||
@@ -101,6 +103,7 @@ require (
|
||||
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-faster/city v1.0.1 // indirect
|
||||
github.com/go-faster/errors v0.7.1 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
||||
@@ -108,7 +111,6 @@ require (
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
@@ -129,7 +131,6 @@ require (
|
||||
github.com/jpillora/backoff v1.0.0 // indirect
|
||||
github.com/jtolds/gls v4.20.0+incompatible // indirect
|
||||
github.com/klauspost/compress v1.17.10 // indirect
|
||||
github.com/knadh/koanf/v2 v2.1.1 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/leodido/go-syslog/v4 v4.2.0 // indirect
|
||||
github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b // indirect
|
||||
|
||||
90
pkg/config/conf.go
Normal file
90
pkg/config/conf.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/knadh/koanf/providers/confmap"
|
||||
"github.com/knadh/koanf/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
KoanfDelimiter string = "::"
|
||||
)
|
||||
|
||||
// Conf is a wrapper around the koanf library.
|
||||
type Conf struct {
|
||||
*koanf.Koanf
|
||||
}
|
||||
|
||||
// NewConf creates a new Conf instance.
|
||||
func NewConf() *Conf {
|
||||
return &Conf{koanf.New(KoanfDelimiter)}
|
||||
}
|
||||
|
||||
// NewConfFromMap creates a new Conf instance from a map.
|
||||
func NewConfFromMap(m map[string]any) (*Conf, error) {
|
||||
conf := NewConf()
|
||||
if err := conf.Koanf.Load(confmap.Provider(m, KoanfDelimiter), nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
// MustNewConfFromMap creates a new Conf instance from a map.
|
||||
// It panics if the conf cannot be created.
|
||||
func MustNewConfFromMap(m map[string]any) *Conf {
|
||||
conf, err := NewConfFromMap(m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return conf
|
||||
}
|
||||
|
||||
// Merge merges the current configuration with the input configuration.
|
||||
func (conf *Conf) Merge(input *Conf) error {
|
||||
return conf.Koanf.Merge(input.Koanf)
|
||||
}
|
||||
|
||||
// Merge merges the current configuration with the input configuration.
|
||||
func (conf *Conf) MergeAt(input *Conf, path string) error {
|
||||
return conf.Koanf.MergeAt(input.Koanf, path)
|
||||
}
|
||||
|
||||
// Unmarshal unmarshals the configuration at the given path into the input.
|
||||
// It uses a WeaklyTypedInput to allow for more flexible unmarshalling.
|
||||
func (conf *Conf) Unmarshal(path string, input any) error {
|
||||
dc := &mapstructure.DecoderConfig{
|
||||
TagName: "mapstructure",
|
||||
WeaklyTypedInput: true,
|
||||
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
||||
mapstructure.StringToSliceHookFunc(","),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
mapstructure.TextUnmarshallerHookFunc(),
|
||||
),
|
||||
Result: input,
|
||||
}
|
||||
|
||||
return conf.Koanf.UnmarshalWithConf(path, input, koanf.UnmarshalConf{Tag: "mapstructure", DecoderConfig: dc})
|
||||
}
|
||||
|
||||
// Set sets the configuration at the given key.
|
||||
// It decodes the input into a map as per mapstructure.Decode and then merges it into the configuration.
|
||||
func (conf *Conf) Set(key string, input any) error {
|
||||
m := map[string]any{}
|
||||
err := mapstructure.Decode(input, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newConf := NewConf()
|
||||
if err := newConf.Koanf.Load(confmap.Provider(m, KoanfDelimiter), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := conf.Koanf.MergeAt(newConf.Koanf, key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
38
pkg/config/conf_test.go
Normal file
38
pkg/config/conf_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConfMerge(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
conf *Conf
|
||||
input *Conf
|
||||
expected *Conf
|
||||
pass bool
|
||||
}{
|
||||
{name: "Empty", conf: NewConf(), input: NewConf(), expected: NewConf(), pass: true},
|
||||
{name: "Merge", conf: MustNewConfFromMap(map[string]any{"a": "b"}), input: MustNewConfFromMap(map[string]any{"c": "d"}), expected: MustNewConfFromMap(map[string]any{"a": "b", "c": "d"}), pass: true},
|
||||
{name: "NestedMerge", conf: MustNewConfFromMap(map[string]any{"a": map[string]any{"b": "v1", "c": "v2"}}), input: MustNewConfFromMap(map[string]any{"a": map[string]any{"d": "v1", "e": "v2"}}), expected: MustNewConfFromMap(map[string]any{"a": map[string]any{"b": "v1", "c": "v2", "d": "v1", "e": "v2"}}), pass: true},
|
||||
{name: "Override", conf: MustNewConfFromMap(map[string]any{"a": "b"}), input: MustNewConfFromMap(map[string]any{"a": "c"}), expected: MustNewConfFromMap(map[string]any{"a": "c"}), pass: true},
|
||||
}
|
||||
|
||||
t.Parallel()
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.conf.Merge(tc.input)
|
||||
if !tc.pass {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.expected.Raw(), tc.conf.Raw())
|
||||
assert.Equal(t, tc.expected.Raw(), tc.conf.Raw())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -3,32 +3,34 @@ package config
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.signoz.io/signoz/pkg/cache"
|
||||
signozconfmap "go.signoz.io/signoz/pkg/confmap"
|
||||
"go.signoz.io/signoz/pkg/instrumentation"
|
||||
"go.signoz.io/signoz/pkg/web"
|
||||
"go.signoz.io/signoz/pkg/factory"
|
||||
)
|
||||
|
||||
// This map contains the default values of all config structs
|
||||
var (
|
||||
defaults = map[string]signozconfmap.Config{
|
||||
"web": &web.Config{},
|
||||
"cache": &cache.Config{},
|
||||
}
|
||||
)
|
||||
|
||||
// Config defines the entire configuration of signoz.
|
||||
type Config struct {
|
||||
Instrumentation instrumentation.Config `mapstructure:"instrumentation"`
|
||||
Web web.Config `mapstructure:"web"`
|
||||
Cache cache.Config `mapstructure:"cache"`
|
||||
}
|
||||
|
||||
func New(ctx context.Context, settings ProviderSettings) (*Config, error) {
|
||||
provider, err := NewProvider(settings)
|
||||
func New(ctx context.Context, resolverConfig ResolverConfig, configFactories []factory.ConfigFactory) (*Conf, error) {
|
||||
// Get the config from the resolver
|
||||
resolver, err := NewResolver(resolverConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return provider.Get(ctx)
|
||||
resolvedConf, err := resolver.Do(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf := NewConf()
|
||||
// Set the default configs
|
||||
for _, factory := range configFactories {
|
||||
c := factory.New()
|
||||
if err := conf.Set(factory.Name().String(), c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = conf.Merge(resolvedConf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/collector/confmap"
|
||||
"go.signoz.io/signoz/pkg/cache"
|
||||
"go.signoz.io/signoz/pkg/confmap/provider/signozenvprovider"
|
||||
"go.signoz.io/signoz/pkg/web"
|
||||
)
|
||||
|
||||
func TestNewWithSignozEnvProvider(t *testing.T) {
|
||||
|
||||
t.Setenv("SIGNOZ__WEB__PREFIX", "/web")
|
||||
t.Setenv("SIGNOZ__WEB__DIRECTORY", "/build")
|
||||
t.Setenv("SIGNOZ__CACHE__PROVIDER", "redis")
|
||||
t.Setenv("SIGNOZ__CACHE__REDIS__HOST", "127.0.0.1")
|
||||
|
||||
config, err := New(context.Background(), ProviderSettings{
|
||||
ResolverSettings: confmap.ResolverSettings{
|
||||
URIs: []string{"signozenv:"},
|
||||
ProviderFactories: []confmap.ProviderFactory{
|
||||
signozenvprovider.NewFactory(),
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
expected := &Config{
|
||||
Web: web.Config{
|
||||
Prefix: "/web",
|
||||
Directory: "/build",
|
||||
},
|
||||
Cache: cache.Config{
|
||||
Provider: "redis",
|
||||
Memory: cache.Memory{
|
||||
TTL: time.Duration(-1),
|
||||
CleanupInterval: 1 * time.Minute,
|
||||
},
|
||||
Redis: cache.Redis{
|
||||
Host: "127.0.0.1",
|
||||
Port: 6379,
|
||||
Password: "",
|
||||
DB: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.Equal(t, expected, config)
|
||||
}
|
||||
71
pkg/config/envprovider/provider.go
Normal file
71
pkg/config/envprovider/provider.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package envprovider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
koanfenv "github.com/knadh/koanf/providers/env"
|
||||
"go.signoz.io/signoz/pkg/config"
|
||||
)
|
||||
|
||||
const (
|
||||
prefix string = "SIGNOZ_"
|
||||
scheme string = "env"
|
||||
)
|
||||
|
||||
type provider struct{}
|
||||
|
||||
func NewFactory() config.ProviderFactory {
|
||||
return config.NewProviderFactory(New)
|
||||
}
|
||||
|
||||
func New(config config.ProviderConfig) config.Provider {
|
||||
return &provider{}
|
||||
}
|
||||
|
||||
func (provider *provider) Scheme() string {
|
||||
return scheme
|
||||
}
|
||||
|
||||
func (provider *provider) Get(ctx context.Context, uri config.Uri) (*config.Conf, error) {
|
||||
conf := config.NewConf()
|
||||
err := conf.Load(
|
||||
koanfenv.Provider(
|
||||
prefix,
|
||||
// Do not set this to `_`. The correct delimiter is being set by the custom callback provided below.
|
||||
// Since this had to be passed, using `config.KoanfDelimiter` eliminates any possible side effect.
|
||||
config.KoanfDelimiter,
|
||||
func(s string) string {
|
||||
s = strings.ToLower(strings.TrimPrefix(s, prefix))
|
||||
return provider.cb(s, config.KoanfDelimiter)
|
||||
},
|
||||
),
|
||||
nil,
|
||||
)
|
||||
|
||||
return conf, err
|
||||
}
|
||||
|
||||
func (provider *provider) cb(s string, delim string) string {
|
||||
delims := []rune(delim)
|
||||
runes := []rune(s)
|
||||
result := make([]rune, 0, len(runes))
|
||||
|
||||
for i := 0; i < len(runes); i++ {
|
||||
// Check for double underscore pattern
|
||||
if i < len(runes)-1 && runes[i] == '_' && runes[i+1] == '_' {
|
||||
result = append(result, '_')
|
||||
i++ // Skip next underscore
|
||||
continue
|
||||
}
|
||||
|
||||
if runes[i] == '_' {
|
||||
result = append(result, delims...)
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, runes[i])
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
78
pkg/config/envprovider/provider_test.go
Normal file
78
pkg/config/envprovider/provider_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package envprovider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.signoz.io/signoz/pkg/config"
|
||||
)
|
||||
|
||||
func TestGetWithStrings(t *testing.T) {
|
||||
t.Setenv("SIGNOZ_K1_K2", "string")
|
||||
t.Setenv("SIGNOZ_K3__K4", "string")
|
||||
t.Setenv("SIGNOZ_K5__K6_K7__K8", "string")
|
||||
t.Setenv("SIGNOZ_K9___K10", "string")
|
||||
t.Setenv("SIGNOZ_K11____K12", "string")
|
||||
expected := map[string]any{
|
||||
"k1::k2": "string",
|
||||
"k3_k4": "string",
|
||||
"k5_k6::k7_k8": "string",
|
||||
"k9_::k10": "string",
|
||||
"k11__k12": "string",
|
||||
}
|
||||
|
||||
provider := New(config.ProviderConfig{})
|
||||
actual, err := provider.Get(context.Background(), config.MustNewUri("env:"))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected, actual.All())
|
||||
}
|
||||
|
||||
func TestGetWithGoTypes(t *testing.T) {
|
||||
t.Setenv("SIGNOZ_BOOL", "true")
|
||||
t.Setenv("SIGNOZ_STRING", "string")
|
||||
t.Setenv("SIGNOZ_INT", "1")
|
||||
t.Setenv("SIGNOZ_SLICE", "[1,2]")
|
||||
expected := map[string]any{
|
||||
"bool": "true",
|
||||
"int": "1",
|
||||
"slice": "[1,2]",
|
||||
"string": "string",
|
||||
}
|
||||
|
||||
provider := New(config.ProviderConfig{})
|
||||
actual, err := provider.Get(context.Background(), config.MustNewUri("env:"))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected, actual.All())
|
||||
}
|
||||
|
||||
func TestGetWithGoTypesWithUnmarshal(t *testing.T) {
|
||||
t.Setenv("SIGNOZ_BOOL", "true")
|
||||
t.Setenv("SIGNOZ_STRING", "string")
|
||||
t.Setenv("SIGNOZ_INT", "1")
|
||||
|
||||
type test struct {
|
||||
Bool bool `mapstructure:"bool"`
|
||||
String string `mapstructure:"string"`
|
||||
Int int `mapstructure:"int"`
|
||||
}
|
||||
|
||||
expected := test{
|
||||
Bool: true,
|
||||
String: "string",
|
||||
Int: 1,
|
||||
}
|
||||
|
||||
provider := New(config.ProviderConfig{})
|
||||
conf, err := provider.Get(context.Background(), config.MustNewUri("env:"))
|
||||
require.NoError(t, err)
|
||||
|
||||
actual := test{}
|
||||
err = conf.Unmarshal("", &actual)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
34
pkg/config/fileprovider/provider.go
Normal file
34
pkg/config/fileprovider/provider.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package fileprovider
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
koanfyaml "github.com/knadh/koanf/parsers/yaml"
|
||||
koanffile "github.com/knadh/koanf/providers/file"
|
||||
"go.signoz.io/signoz/pkg/config"
|
||||
)
|
||||
|
||||
const (
|
||||
scheme string = "file"
|
||||
)
|
||||
|
||||
type provider struct{}
|
||||
|
||||
func NewFactory() config.ProviderFactory {
|
||||
return config.NewProviderFactory(New)
|
||||
}
|
||||
|
||||
func New(config config.ProviderConfig) config.Provider {
|
||||
return &provider{}
|
||||
}
|
||||
|
||||
func (provider *provider) Scheme() string {
|
||||
return scheme
|
||||
}
|
||||
|
||||
func (provider *provider) Get(ctx context.Context, uri config.Uri) (*config.Conf, error) {
|
||||
conf := config.NewConf()
|
||||
err := conf.Load(koanffile.Provider(uri.Value()), koanfyaml.Parser())
|
||||
|
||||
return conf, err
|
||||
}
|
||||
68
pkg/config/fileprovider/provider_test.go
Normal file
68
pkg/config/fileprovider/provider_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package fileprovider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.signoz.io/signoz/pkg/config"
|
||||
)
|
||||
|
||||
func TestGetWithStrings(t *testing.T) {
|
||||
expected := map[string]any{
|
||||
"k1::k2": "string",
|
||||
"k3_k4": "string",
|
||||
"k5_k6::k7_k8": "string",
|
||||
"k9_::k10": "string",
|
||||
"k11__k12": "string",
|
||||
}
|
||||
|
||||
provider := New(config.ProviderConfig{})
|
||||
actual, err := provider.Get(context.Background(), config.MustNewUri("file:"+filepath.Join("testdata", "strings.yaml")))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected, actual.All())
|
||||
}
|
||||
|
||||
func TestGetWithGoTypes(t *testing.T) {
|
||||
expected := map[string]any{
|
||||
"bool": true,
|
||||
"int": 1,
|
||||
"slice": []any{1, 2},
|
||||
"string": "string",
|
||||
}
|
||||
|
||||
provider := New(config.ProviderConfig{})
|
||||
actual, err := provider.Get(context.Background(), config.MustNewUri("file:"+filepath.Join("testdata", "gotypes.yaml")))
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected, actual.All())
|
||||
}
|
||||
|
||||
func TestGetWithGoTypesWithUnmarshal(t *testing.T) {
|
||||
type test struct {
|
||||
Bool bool `mapstructure:"bool"`
|
||||
String string `mapstructure:"string"`
|
||||
Int int `mapstructure:"int"`
|
||||
Slice []any `mapstructure:"slice"`
|
||||
}
|
||||
|
||||
expected := test{
|
||||
Bool: true,
|
||||
String: "string",
|
||||
Int: 1,
|
||||
Slice: []any{1, 2},
|
||||
}
|
||||
|
||||
provider := New(config.ProviderConfig{})
|
||||
conf, err := provider.Get(context.Background(), config.MustNewUri("file:"+filepath.Join("testdata", "gotypes.yaml")))
|
||||
require.NoError(t, err)
|
||||
|
||||
actual := test{}
|
||||
err = conf.Unmarshal("", &actual)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
6
pkg/config/fileprovider/testdata/gotypes.yaml
vendored
Normal file
6
pkg/config/fileprovider/testdata/gotypes.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
bool: true
|
||||
string: string
|
||||
int: 1
|
||||
slice:
|
||||
- 1
|
||||
- 2
|
||||
8
pkg/config/fileprovider/testdata/strings.yaml
vendored
Normal file
8
pkg/config/fileprovider/testdata/strings.yaml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
k1:
|
||||
k2: string
|
||||
k3_k4: string
|
||||
k5_k6:
|
||||
k7_k8: string
|
||||
k9_:
|
||||
k10: string
|
||||
k11__k12: string
|
||||
@@ -2,51 +2,38 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.opentelemetry.io/collector/confmap"
|
||||
)
|
||||
|
||||
// Provides the configuration for signoz.
|
||||
// NewProviderFunc is a function that creates a new provider.
|
||||
type NewProviderFunc = func(ProviderConfig) Provider
|
||||
|
||||
// ProviderFactory is a factory that creates a new provider.
|
||||
type ProviderFactory interface {
|
||||
New(ProviderConfig) Provider
|
||||
}
|
||||
|
||||
// NewProviderFactory creates a new provider factory.
|
||||
func NewProviderFactory(f NewProviderFunc) ProviderFactory {
|
||||
return &providerFactory{f: f}
|
||||
}
|
||||
|
||||
// providerFactory is a factory that implements the ProviderFactory interface.
|
||||
type providerFactory struct {
|
||||
f NewProviderFunc
|
||||
}
|
||||
|
||||
// New creates a new provider.
|
||||
func (factory *providerFactory) New(config ProviderConfig) Provider {
|
||||
return factory.f(config)
|
||||
}
|
||||
|
||||
// ProviderConfig is the configuration for a provider.
|
||||
type ProviderConfig struct{}
|
||||
|
||||
// Provider is an interface that represents a configuration provider.
|
||||
type Provider interface {
|
||||
// Get returns the configuration, or error otherwise.
|
||||
Get(ctx context.Context) (*Config, error)
|
||||
}
|
||||
|
||||
type provider struct {
|
||||
resolver *confmap.Resolver
|
||||
}
|
||||
|
||||
// ProviderSettings are the settings to configure the behavior of the Provider.
|
||||
type ProviderSettings struct {
|
||||
// ResolverSettings are the settings to configure the behavior of the confmap.Resolver.
|
||||
ResolverSettings confmap.ResolverSettings
|
||||
}
|
||||
|
||||
// NewProvider returns a new Provider that provides the entire configuration.
|
||||
// See https://github.com/open-telemetry/opentelemetry-collector/blob/main/otelcol/configprovider.go for
|
||||
// more details
|
||||
func NewProvider(settings ProviderSettings) (Provider, error) {
|
||||
resolver, err := confmap.NewResolver(settings.ResolverSettings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &provider{
|
||||
resolver: resolver,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (provider *provider) Get(ctx context.Context) (*Config, error) {
|
||||
conf, err := provider.resolver.Resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot resolve configuration: %w", err)
|
||||
}
|
||||
|
||||
config, err := unmarshal(conf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot unmarshal configuration: %w", err)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
// Get returns the configuration for the given URI.
|
||||
Get(context.Context, Uri) (*Conf, error)
|
||||
// Scheme returns the scheme of the provider.
|
||||
Scheme() string
|
||||
}
|
||||
|
||||
87
pkg/config/resolver.go
Normal file
87
pkg/config/resolver.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ResolverConfig struct {
|
||||
// Each string or `uri` must follow "<scheme>:<value>" format. This format is compatible with the URI definition
|
||||
// defined at https://datatracker.ietf.org/doc/html/rfc3986".
|
||||
// It is required to have at least one uri.
|
||||
Uris []string
|
||||
|
||||
// ProviderFactories is a slice of Provider factories.
|
||||
// It is required to have at least one factory.
|
||||
ProviderFactories []ProviderFactory
|
||||
}
|
||||
|
||||
type Resolver struct {
|
||||
uris []Uri
|
||||
providers map[string]Provider
|
||||
}
|
||||
|
||||
func NewResolver(config ResolverConfig) (*Resolver, error) {
|
||||
if len(config.Uris) == 0 {
|
||||
return nil, errors.New("cannot build resolver, no uris have been provided")
|
||||
}
|
||||
|
||||
if len(config.ProviderFactories) == 0 {
|
||||
return nil, errors.New("cannot build resolver, no providers have been provided")
|
||||
}
|
||||
|
||||
uris := make([]Uri, len(config.Uris))
|
||||
for i, inputUri := range config.Uris {
|
||||
uri, err := NewUri(inputUri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uris[i] = uri
|
||||
}
|
||||
|
||||
providers := make(map[string]Provider, len(config.ProviderFactories))
|
||||
for _, factory := range config.ProviderFactories {
|
||||
provider := factory.New(ProviderConfig{})
|
||||
|
||||
scheme := provider.Scheme()
|
||||
// Check that the scheme is unique.
|
||||
if _, ok := providers[scheme]; ok {
|
||||
return nil, fmt.Errorf("cannot build resolver, duplicate scheme %q found", scheme)
|
||||
}
|
||||
|
||||
providers[provider.Scheme()] = provider
|
||||
}
|
||||
|
||||
return &Resolver{
|
||||
uris: uris,
|
||||
providers: providers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (resolver *Resolver) Do(ctx context.Context) (*Conf, error) {
|
||||
conf := NewConf()
|
||||
|
||||
for _, uri := range resolver.uris {
|
||||
currentConf, err := resolver.get(ctx, uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = conf.Merge(currentConf); err != nil {
|
||||
return nil, fmt.Errorf("cannot merge config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func (resolver *Resolver) get(ctx context.Context, uri Uri) (*Conf, error) {
|
||||
provider, ok := resolver.providers[uri.scheme]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot find provider with schema %q", uri.scheme)
|
||||
}
|
||||
|
||||
return provider.Get(ctx, uri)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.opentelemetry.io/collector/confmap"
|
||||
)
|
||||
|
||||
// unmarshal converts a confmap.Conf into a Config struct.
|
||||
// It splits the input confmap into a map of key-value pairs, fetches the corresponding
|
||||
// signozconfmap.Config interface by name, merges it with the default config, validates it,
|
||||
// and then creates a new confmap from the parsed map to unmarshal into the Config struct.
|
||||
func unmarshal(conf *confmap.Conf) (*Config, error) {
|
||||
raw := make(map[string]any)
|
||||
if err := conf.Unmarshal(&raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parsed := make(map[string]any)
|
||||
|
||||
// To help the defaults kick in, we need iterate over the default map instead of the raw values
|
||||
for k, v := range defaults {
|
||||
sub, err := conf.Sub(k)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read config for %q: %w", k, err)
|
||||
}
|
||||
|
||||
d := v.NewWithDefaults()
|
||||
if err := sub.Unmarshal(&d); err != nil {
|
||||
return nil, fmt.Errorf("cannot merge config for %q: %w", k, err)
|
||||
}
|
||||
|
||||
err = d.Validate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to validate config for for %q: %w", k, err)
|
||||
}
|
||||
|
||||
parsed[k] = d
|
||||
}
|
||||
|
||||
parsedConf := confmap.NewFromStringMap(parsed)
|
||||
config := new(Config)
|
||||
err := parsedConf.Unmarshal(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot unmarshal config: %w", err)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
46
pkg/config/uri.go
Normal file
46
pkg/config/uri.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var (
|
||||
// uriRegex is a regex that matches the URI format. It complies with the URI definition defined at https://datatracker.ietf.org/doc/html/rfc3986.
|
||||
// The format is "<scheme>:<value>".
|
||||
uriRegex = regexp.MustCompile(`(?s:^(?P<Scheme>[A-Za-z][A-Za-z0-9+.-]+):(?P<Value>.*)$)`)
|
||||
)
|
||||
|
||||
type Uri struct {
|
||||
scheme string
|
||||
value string
|
||||
}
|
||||
|
||||
func NewUri(input string) (Uri, error) {
|
||||
submatches := uriRegex.FindStringSubmatch(input)
|
||||
|
||||
if len(submatches) != 3 {
|
||||
return Uri{}, fmt.Errorf("invalid uri: %q", input)
|
||||
}
|
||||
return Uri{
|
||||
scheme: submatches[1],
|
||||
value: submatches[2],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func MustNewUri(input string) Uri {
|
||||
uri, err := NewUri(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return uri
|
||||
}
|
||||
|
||||
func (uri Uri) Scheme() string {
|
||||
return uri.scheme
|
||||
}
|
||||
|
||||
func (uri Uri) Value() string {
|
||||
return uri.value
|
||||
}
|
||||
35
pkg/config/uri_test.go
Normal file
35
pkg/config/uri_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewUri(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected Uri
|
||||
pass bool
|
||||
}{
|
||||
{input: "file:/path/1", expected: Uri{scheme: "file", value: "/path/1"}, pass: true},
|
||||
{input: "file:", expected: Uri{scheme: "file", value: ""}, pass: true},
|
||||
{input: "env:", expected: Uri{scheme: "env", value: ""}, pass: true},
|
||||
{input: "scheme", expected: Uri{}, pass: false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
uri, err := NewUri(tc.input)
|
||||
if !tc.pass {
|
||||
assert.Error(t, err)
|
||||
continue
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NotPanics(t, func() { MustNewUri(tc.input) })
|
||||
assert.Equal(t, tc.expected, uri)
|
||||
assert.Equal(t, tc.expected.Scheme(), uri.scheme)
|
||||
assert.Equal(t, tc.expected.Value(), uri.value)
|
||||
}
|
||||
}
|
||||
41
pkg/signoz/config.go
Normal file
41
pkg/signoz/config.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package signoz
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.signoz.io/signoz/pkg/cache"
|
||||
"go.signoz.io/signoz/pkg/config"
|
||||
"go.signoz.io/signoz/pkg/factory"
|
||||
"go.signoz.io/signoz/pkg/instrumentation"
|
||||
"go.signoz.io/signoz/pkg/sqlstore"
|
||||
"go.signoz.io/signoz/pkg/web"
|
||||
)
|
||||
|
||||
// Config defines the entire configuration of signoz.
|
||||
type Config struct {
|
||||
Instrumentation instrumentation.Config `mapstructure:"instrumentation"`
|
||||
Web web.Config `mapstructure:"web"`
|
||||
Cache cache.Config `mapstructure:"cache"`
|
||||
SQLStore sqlstore.Config `mapstructure:"sqlstore"`
|
||||
}
|
||||
|
||||
func NewConfig(ctx context.Context, resolverConfig config.ResolverConfig) (Config, error) {
|
||||
configFactories := []factory.ConfigFactory{
|
||||
instrumentation.NewConfigFactory(),
|
||||
web.NewConfigFactory(),
|
||||
sqlstore.NewConfigFactory(),
|
||||
cache.NewConfigFactory(),
|
||||
}
|
||||
|
||||
conf, err := config.New(ctx, resolverConfig, configFactories)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
var config Config
|
||||
if err := conf.Unmarshal("", &config); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
@@ -15,7 +15,7 @@ type SigNoz struct {
|
||||
Web web.Web
|
||||
}
|
||||
|
||||
func New(config *config.Config, skipWebFrontend bool) (*SigNoz, error) {
|
||||
func New(config config.Config, skipWebFrontend bool) (*SigNoz, error) {
|
||||
var cache cache.Cache
|
||||
|
||||
// init for the cache
|
||||
|
||||
Reference in New Issue
Block a user