Skip to content

Commit

Permalink
#11 Added directive expander
Browse files Browse the repository at this point in the history
  • Loading branch information
roma-glushko committed Dec 24, 2023
1 parent ae80099 commit ec30aac
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 18 deletions.
8 changes: 8 additions & 0 deletions config.sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
telemetry:
logging:
level: INFO # DEBUG, INFO, WARNING, ERROR, FATAL
encoding: json # console, json

#api:
# http:
# ...
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/cloudwego/hertz v0.7.3
github.com/go-playground/validator/v10 v10.16.0
github.com/hertz-contrib/logger/zap v1.1.0
github.com/mitchellh/mapstructure v1.5.0
github.com/spf13/cobra v1.8.0
go.uber.org/goleak v1.3.0
go.uber.org/multierr v1.11.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg=
github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import "glide/pkg/api/http"

// Config defines configuration for all API types we support (e.g. HTTP, gRPC)
type Config struct {
Http http.ServerConfig `json:"http" yaml:"http"`
Http http.ServerConfig `mapstructure:"http"`

Check warning on line 7 in pkg/api/config.go

View workflow job for this annotation

GitHub Actions / Static Checks

var-naming: struct field Http should be HTTP (revive)
}
2 changes: 1 addition & 1 deletion pkg/cmd/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func NewCLI() *cobra.Command {
Use: "glide",
Short: "🐦Glide is an open source lightweight high-performant model gateway",
Long: "TODO",
Version: pkg.GetVersion(),
Version: pkg.FullVersion,
RunE: func(cmd *cobra.Command, args []string) error {
configProvider, err := config.NewProvider().Load(cfgFile)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import (

// Config is a general top-level Glide configuration
type Config struct {
Telemetry *telemetry.Config `json:"telemetry" yaml:"telemetry"`
API *api.Config `json:"api" yaml:"api"`
Telemetry *telemetry.Config `mapstructure:"telemetry"`
API *api.Config `mapstructure:"api"`
}
71 changes: 71 additions & 0 deletions pkg/config/expander.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package config

const directivePattern = `[A-Za-z][A-Za-z0-9+.-]+`

type RawConfig = map[string]interface{}

// Expander finds special directives like ${env:ENV_VAR} in the config file and fill them with actual values
type Expander struct{}

func (e *Expander) Expand(rawConfig RawConfig) (RawConfig, error) {
for configKey, configValue := range rawConfig {
expandedValue, err := e.expandConfigValue(configValue)

if err != nil {
return nil, err
}

rawConfig[configKey] = expandedValue
}

return rawConfig, nil
}

func (e *Expander) expandConfigValue(value any) (any, error) {
switch v := value.(type) {
case string:
return e.expandString(v)
case []any:
return e.expandSlice(v)
case map[string]any:
return e.expandMap(v)
default:
// could be int or float
return value, nil
}
}

func (e *Expander) expandString(value string) (string, error) {
// TODO: implement
return value, nil
}

func (e *Expander) expandSlice(value []any) ([]any, error) {
slice := make([]any, 0, len(value))

for _, vint := range value {
val, err := e.expandConfigValue(vint)
if err != nil {
return nil, err
}

slice = append(slice, val)
}

return slice, nil
}

func (e Expander) expandMap(value map[string]any) (map[string]any, error) {
newMap := map[string]any{}

for mapKey, mapValue := range value {
val, err := e.expandConfigValue(mapValue)
if err != nil {
return nil, err
}

newMap[mapKey] = val
}

return newMap, nil
}
36 changes: 36 additions & 0 deletions pkg/config/expander_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package config

import (
"testing"
)

func TestExpander_EnvVarExpanded(t *testing.T) {
var testCases = []struct {
name string // test case name (also file name containing config yaml)
}{
{name: "no-env.yaml"},
{name: "partial-env.yaml"},
{name: "all-env.yaml"},
}

const valueExtra = "some string"
const valueExtraMapValue = "some map value"
const valueExtraListMapValue = "some list map value"
const valueExtraListElement = "some list value"

t.Setenv("EXTRA", valueExtra)
t.Setenv("EXTRA_MAP_VALUE_1", valueExtraMapValue+"_1")
t.Setenv("EXTRA_MAP_VALUE_2", valueExtraMapValue+"_2")
t.Setenv("EXTRA_LIST_MAP_VALUE_1", valueExtraListMapValue+"_1")
t.Setenv("EXTRA_LIST_MAP_VALUE_2", valueExtraListMapValue+"_2")
t.Setenv("EXTRA_LIST_VALUE_1", valueExtraListElement+"_1")
t.Setenv("EXTRA_LIST_VALUE_2", valueExtraListElement+"_2")

for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
expander := Expander{}

modifiedConfig, err := expander.Expand()

Check failure on line 33 in pkg/config/expander_test.go

View workflow job for this annotation

GitHub Actions / Vulnerability Check

not enough arguments in call to expander.Expand

Check failure on line 33 in pkg/config/expander_test.go

View workflow job for this annotation

GitHub Actions / Vulnerability Check

modifiedConfig declared and not used

Check failure on line 33 in pkg/config/expander_test.go

View workflow job for this annotation

GitHub Actions / Vulnerability Check

err declared and not used

Check failure on line 33 in pkg/config/expander_test.go

View workflow job for this annotation

GitHub Actions / Tests

modifiedConfig declared and not used

Check failure on line 33 in pkg/config/expander_test.go

View workflow job for this annotation

GitHub Actions / Tests

err declared and not used

Check failure on line 33 in pkg/config/expander_test.go

View workflow job for this annotation

GitHub Actions / Tests

not enough arguments in call to expander.Expand
})
}
}
32 changes: 27 additions & 5 deletions pkg/config/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,55 @@ import (
"os"
"path/filepath"

"github.com/mitchellh/mapstructure"

"gopkg.in/yaml.v3"
)

// Provider reads, collects, validates and process config files
type Provider struct {
Config *Config
expander *Expander
Config *Config
}

// NewProvider creates a instance of Config Provider
func NewProvider() *Provider {
return &Provider{
Config: nil,
expander: &Expander{},
Config: nil,
}
}

func (p *Provider) Load(configPath string) (*Provider, error) {
rawConfig, err := os.ReadFile(filepath.Clean(configPath))
rawContent, err := os.ReadFile(filepath.Clean(configPath))
if err != nil {
return p, fmt.Errorf("unable to read the file %v: %w", configPath, err)
}

var cfg *Config
var rawConfig RawConfig

if err := yaml.Unmarshal(rawConfig, &cfg); err != nil {
if err := yaml.Unmarshal(rawContent, &rawConfig); err != nil {
return p, fmt.Errorf("unable to serialize the file %v: %w", configPath, err)
}

// process raw config
rawConfig, err = p.expander.Expand(rawConfig)

if err != nil {
return p, fmt.Errorf("unable to expand config directives %v: %w", configPath, err)
}

// validate the config structure
var cfg *Config

err = mapstructure.Decode(rawConfig, cfg)

if err != nil {
return p, err
}

// TODO: validate config values

p.Config = cfg

return p, nil
Expand Down
12 changes: 6 additions & 6 deletions pkg/telemetry/logging.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@ import (

type LogConfig struct {
// Level is the minimum enabled logging level.
Level zap.AtomicLevel `yaml:"level"`
Level zap.AtomicLevel `mapstructure:"level"`

// Encoding sets the logger's encoding. Valid values are "json", "console"
Encoding string `yaml:"encoding"`
Encoding string `mapstructure:"encoding"`

// DisableCaller stops annotating logs with the calling function's file name and line number.
// By default, all logs are annotated.
DisableCaller bool `yaml:"disable_caller"`
DisableCaller bool `mapstructure:"disable_caller"`

// DisableStacktrace completely disables automatic stacktrace capturing. By
// default, stacktraces are captured for WarnLevel and above logs in
// development and ErrorLevel and above in production.
DisableStacktrace bool `yaml:"disable_stacktrace"`
DisableStacktrace bool `mapstructure:"disable_stacktrace"`

// OutputPaths is a list of URLs or file paths to write logging output to.
OutputPaths []string `yaml:"output_paths"`
OutputPaths []string `mapstructure:"output_paths"`

// InitialFields is a collection of fields to add to the root logger.
InitialFields map[string]interface{} `yaml:"initial_fields"`
InitialFields map[string]interface{} `mapstructure:"initial_fields"`
}

func NewLogConfig() *LogConfig {
Expand Down
2 changes: 1 addition & 1 deletion pkg/telemetry/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package telemetry
import "go.uber.org/zap"

type Config struct {
LogConfig *LogConfig `json:"logging" yaml:"logging"`
LogConfig *LogConfig `mapstructure:"logging"`
// TODO: add OTEL config
}

Expand Down
6 changes: 4 additions & 2 deletions pkg/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ var version = "devel"
// and will be populated by the Makefile
var commitSha = "unknown"

func GetVersion() string {
return fmt.Sprintf("%s (commit: %s, %s)", version, commitSha, runtime.Version())
var FullVersion string

func init() {
FullVersion = fmt.Sprintf("%s (commit: %s, %s)", version, commitSha, runtime.Version())
}

0 comments on commit ec30aac

Please sign in to comment.