Skip to content

Commit

Permalink
config: support loading config from yaml file
Browse files Browse the repository at this point in the history
  • Loading branch information
andydunstall committed May 1, 2024
1 parent c657ea6 commit 5ebac35
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 30 deletions.
20 changes: 10 additions & 10 deletions agent/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import (

type ServerConfig struct {
// URL is the server URL.
URL string `json:"url"`
HeartbeatIntervalSeconds int `json:"heartbeat_interval_seconds"`
HeartbeatTimeoutSeconds int `json:"heartbeat_timeout_seconds"`
URL string `json:"url" yaml:"url"`
HeartbeatIntervalSeconds int `json:"heartbeat_interval_seconds" yaml:"heartbeat_interval_seconds"`
HeartbeatTimeoutSeconds int `json:"heartbeat_timeout_seconds" yaml:"heartbeat_timeout_seconds"`
}

func (c *ServerConfig) Validate() error {
Expand All @@ -35,12 +35,12 @@ func (c *ServerConfig) Validate() error {
// ForwarderConfig contains the configuration for how to forward requests
// from Pico.
type ForwarderConfig struct {
TimeoutSeconds int `json:"timeout_seconds"`
TimeoutSeconds int `json:"timeout_seconds" yaml:"timeout_seconds"`
}

type AdminConfig struct {
// BindAddr is the address to bind to listen for incoming HTTP connections.
BindAddr string `json:"bind_addr"`
BindAddr string `json:"bind_addr" yaml:"bind_addr"`
}

func (c *AdminConfig) Validate() error {
Expand All @@ -55,11 +55,11 @@ type Config struct {
//
// Each endpoint has format '<endpoint ID>/<forward addr>', such
// as 'd3934d4f/localhost:3000'.
Endpoints []string `json:"endpoints"`
Server ServerConfig `json:"server"`
Forwarder ForwarderConfig `json:"forwarder"`
Admin AdminConfig `json:"admin"`
Log log.Config `json:"log"`
Endpoints []string `json:"endpoints" yaml:"endpoints"`
Server ServerConfig `json:"server" yaml:"server"`
Forwarder ForwarderConfig `json:"forwarder" yaml:"forwarder"`
Admin AdminConfig `json:"admin" yaml:"admin"`
Log log.Config `json:"log" yaml:"log"`
}

func (c *Config) Validate() error {
Expand Down
17 changes: 17 additions & 0 deletions cli/agent/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/andydunstall/pico/agent"
"github.com/andydunstall/pico/agent/config"
picoconfig "github.com/andydunstall/pico/pkg/config"
"github.com/andydunstall/pico/pkg/log"
adminserver "github.com/andydunstall/pico/server/server/admin"
rungroup "github.com/oklog/run"
Expand Down Expand Up @@ -50,10 +51,26 @@ Examples:

var conf config.Config

var configPath string
cmd.Flags().StringVar(
&configPath,
"config.path",
"",
`
YAML config file path.`,
)

// Register flags and set default values.
conf.RegisterFlags(cmd.Flags())

cmd.Run = func(cmd *cobra.Command, args []string) {
if configPath != "" {
if err := picoconfig.Load(configPath, &conf); err != nil {
fmt.Printf("load config: %s\n", err.Error())
os.Exit(1)
}
}

if err := conf.Validate(); err != nil {
fmt.Printf("invalid config: %s\n", err.Error())
os.Exit(1)
Expand Down
17 changes: 17 additions & 0 deletions cli/server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"syscall"
"time"

picoconfig "github.com/andydunstall/pico/pkg/config"
"github.com/andydunstall/pico/pkg/log"
"github.com/andydunstall/pico/server/config"
"github.com/andydunstall/pico/server/gossip"
Expand Down Expand Up @@ -58,10 +59,26 @@ Examples:

var conf config.Config

var configPath string
cmd.Flags().StringVar(
&configPath,
"config.path",
"",
`
YAML config file path.`,
)

// Register flags and set default values.
conf.RegisterFlags(cmd.Flags())

cmd.Run = func(cmd *cobra.Command, args []string) {
if configPath != "" {
if err := picoconfig.Load(configPath, &conf); err != nil {
fmt.Printf("load config: %s\n", err.Error())
os.Exit(1)
}
}

if err := conf.Validate(); err != nil {
fmt.Printf("invalid config: %s\n", err.Error())
os.Exit(1)
Expand Down
25 changes: 25 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package config

import (
"bytes"
"fmt"
"os"

"gopkg.in/yaml.v3"
)

func Load(path string, conf interface{}) error {
buf, err := os.ReadFile(path)
if err != nil {
return fmt.Errorf("read file: %s: %w", path, err)
}

dec := yaml.NewDecoder(bytes.NewReader(buf))
dec.KnownFields(true)

if err := dec.Decode(conf); err != nil {
return fmt.Errorf("parse config: %s: %w", path, err)
}

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

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
)

type fakeConfig struct {
Foo string `yaml:"foo"`
Bar string `yaml:"bar"`
Sub fakeSubConfig `yaml:"sub"`
}

type fakeSubConfig struct {
Car int `yaml:"car"`
}

func TestLoad(t *testing.T) {
t.Run("ok", func(t *testing.T) {
f, err := os.CreateTemp("", "pico")
assert.NoError(t, err)

_, err = f.WriteString(`foo: val1
bar: val2
sub:
car: 5`)
assert.NoError(t, err)

var conf fakeConfig
assert.NoError(t, Load(f.Name(), &conf))

assert.Equal(t, "val1", conf.Foo)
assert.Equal(t, "val2", conf.Bar)
assert.Equal(t, 5, conf.Sub.Car)
})

t.Run("invalid yaml", func(t *testing.T) {
f, err := os.CreateTemp("", "pico")
assert.NoError(t, err)

_, err = f.WriteString(`invalid yaml...`)
assert.NoError(t, err)

var conf fakeConfig
assert.Error(t, Load(f.Name(), &conf))
})

t.Run("not found", func(t *testing.T) {
var conf fakeConfig
assert.Error(t, Load("notfound", &conf))
})
}
4 changes: 2 additions & 2 deletions pkg/log/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (
type Config struct {
// Level is the minimum record level to log. Either 'debug', 'info', 'warn'
// or 'error'.
Level string `json:"level"`
Level string `json:"level" yaml:"level"`

// Subsystems enables debug logging on log records whose 'subsystem'
// matches one of the given values (overrides `Level`).
Subsystems []string `json:"subsystems"`
Subsystems []string `json:"subsystems" yaml:"subsystems"`
}

func (c *Config) Validate() error {
Expand Down
36 changes: 18 additions & 18 deletions server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import (

type ProxyConfig struct {
// BindAddr is the address to bind to listen for incoming HTTP connections.
BindAddr string `json:"bind_addr"`
BindAddr string `json:"bind_addr" yaml:"bind_addr"`

// AdvertiseAddr is the address to advertise to other nodes.
AdvertiseAddr string `json:"advertise_addr"`
AdvertiseAddr string `json:"advertise_addr" yaml:"advertise_addr"`

// GatewayTimeout is the timeout in seconds of forwarding requests to an
// upstream listener.
GatewayTimeout int `json:"gateway_timeout"`
GatewayTimeout int `json:"gateway_timeout" yaml:"gateway_timeout"`
}

func (c *ProxyConfig) Validate() error {
Expand All @@ -31,7 +31,7 @@ func (c *ProxyConfig) Validate() error {

type UpstreamConfig struct {
// BindAddr is the address to bind to listen for incoming HTTP connections.
BindAddr string `json:"bind_addr"`
BindAddr string `json:"bind_addr" yaml:"bind_addr"`
}

func (c *UpstreamConfig) Validate() error {
Expand All @@ -43,10 +43,10 @@ func (c *UpstreamConfig) Validate() error {

type AdminConfig struct {
// BindAddr is the address to bind to listen for incoming HTTP connections.
BindAddr string `json:"bind_addr"`
BindAddr string `json:"bind_addr" yaml:"bind_addr"`

// AdvertiseAddr is the address to advertise to other nodes.
AdvertiseAddr string `json:"advertise_addr"`
AdvertiseAddr string `json:"advertise_addr" yaml:"advertise_addr"`
}

func (c *AdminConfig) Validate() error {
Expand All @@ -58,10 +58,10 @@ func (c *AdminConfig) Validate() error {

type GossipConfig struct {
// BindAddr is the address to bind to listen for gossip traffic.
BindAddr string `json:"bind_addr"`
BindAddr string `json:"bind_addr" yaml:"bind_addr"`

// AdvertiseAddr is the address to advertise to other nodes.
AdvertiseAddr string `json:"advertise_addr"`
AdvertiseAddr string `json:"advertise_addr" yaml:"advertise_addr"`
}

func (c *GossipConfig) Validate() error {
Expand All @@ -73,16 +73,16 @@ func (c *GossipConfig) Validate() error {

type ClusterConfig struct {
// NodeID is a unique identifier for this node in the cluster.
NodeID string `json:"node_id"`
NodeID string `json:"node_id" yaml:"node_id"`

// Join contians a list of addresses of members in the cluster to join.
Join []string `json:"join"`
Join []string `json:"join" yaml:"join"`
}

type ServerConfig struct {
// GracefulShutdownTimeout is the timeout to allow for graceful shutdown
// of the server in seconds.
GracefulShutdownTimeout int `json:"graceful_shutdown_timeout"`
GracefulShutdownTimeout int `json:"graceful_shutdown_timeout" yaml:"graceful_shutdown_timeout"`
}

func (c *ServerConfig) Validate() error {
Expand All @@ -93,13 +93,13 @@ func (c *ServerConfig) Validate() error {
}

type Config struct {
Proxy ProxyConfig `json:"proxy"`
Upstream UpstreamConfig `json:"upstream"`
Admin AdminConfig `json:"admin"`
Gossip GossipConfig `json:"gossip"`
Cluster ClusterConfig `json:"cluster"`
Server ServerConfig `json:"server"`
Log log.Config `json:"log"`
Proxy ProxyConfig `json:"proxy" yaml:"proxy"`
Upstream UpstreamConfig `json:"upstream" yaml:"upstream"`
Admin AdminConfig `json:"admin" yaml:"admin"`
Gossip GossipConfig `json:"gossip" yaml:"gossip"`
Cluster ClusterConfig `json:"cluster" yaml:"cluster"`
Server ServerConfig `json:"server" yaml:"server"`
Log log.Config `json:"log" yaml:"log"`
}

func (c *Config) Validate() error {
Expand Down

0 comments on commit 5ebac35

Please sign in to comment.