mirror of
https://github.com/zrepl/zrepl.git
synced 2025-06-13 21:36:45 +02:00
WIP adopt updated yaml-config with 'fromdefaults' struct tag
This commit is contained in:
parent
b95e983d0d
commit
d55a271ac7
@ -62,7 +62,7 @@ func (t *tui) addIndent(indent int) {
|
|||||||
t.moveLine(0, 0)
|
t.moveLine(0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunStatus(config config.Config, args []string) error {
|
func RunStatus(config *config.Config, args []string) error {
|
||||||
httpc, err := controlHttpClient(config.Global.Control.SockPath)
|
httpc, err := controlHttpClient(config.Global.Control.SockPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/zrepl/zrepl/daemon"
|
"github.com/zrepl/zrepl/daemon"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RunWakeup(config config.Config, args []string) error {
|
func RunWakeup(config *config.Config, args []string) error {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return errors.Errorf("Expected 1 argument: job")
|
return errors.Errorf("Expected 1 argument: job")
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,12 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Jobs []JobEnum `yaml:"jobs"`
|
Jobs []JobEnum `yaml:"jobs"`
|
||||||
Global Global `yaml:"global"`
|
Global *Global `yaml:"global,optional,fromdefaults"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type JobEnum struct {
|
type JobEnum struct {
|
||||||
@ -102,11 +103,51 @@ type PruningLocal struct {
|
|||||||
Keep []PruningEnum `yaml:"keep"`
|
Keep []PruningEnum `yaml:"keep"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LoggingOutletEnumList []LoggingOutletEnum
|
||||||
|
|
||||||
|
func (l *LoggingOutletEnumList) SetDefault() {
|
||||||
|
def := `
|
||||||
|
type: "stdout"
|
||||||
|
time: true
|
||||||
|
level: "warn"
|
||||||
|
format: "human"
|
||||||
|
`
|
||||||
|
s := StdoutLoggingOutlet{}
|
||||||
|
err := yaml.UnmarshalStrict([]byte(def), &s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*l = []LoggingOutletEnum{LoggingOutletEnum{Ret: s}}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ yaml.Defaulter = &LoggingOutletEnumList{}
|
||||||
|
|
||||||
type Global struct {
|
type Global struct {
|
||||||
Logging []LoggingOutletEnum `yaml:"logging"`
|
Logging *LoggingOutletEnumList `yaml:"logging,optional,fromdefaults"`
|
||||||
Monitoring []MonitoringEnum `yaml:"monitoring,optional"`
|
Monitoring []MonitoringEnum `yaml:"monitoring,optional"`
|
||||||
Control GlobalControl `yaml:"control"`
|
Control *GlobalControl `yaml:"control,optional,fromdefaults"`
|
||||||
Serve GlobalServe `yaml:"serve"`
|
Serve *GlobalServe `yaml:"serve,optional,fromdefaults"`
|
||||||
|
RPC *RPCConfig `yaml:"rpc,optional,fromdefaults"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Default(i interface{}) {
|
||||||
|
v := reflect.ValueOf(i)
|
||||||
|
if v.Kind() != reflect.Ptr {
|
||||||
|
panic(v)
|
||||||
|
}
|
||||||
|
y := `{}`
|
||||||
|
err := yaml.Unmarshal([]byte(y), v.Interface())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RPCConfig struct {
|
||||||
|
Timeout time.Duration `yaml:"timeout,optional,positive,default=10s"`
|
||||||
|
TxChunkSize uint `yaml:"tx_chunk_size,optional,default=32768"`
|
||||||
|
RxStructuredMaxLen uint `yaml:"rx_structured_max,optional,default=16777216"`
|
||||||
|
RxStreamChunkMaxLen uint `yaml:"rx_stream_chunk_max,optional,default=16777216"`
|
||||||
|
RxHeaderMaxLen uint `yaml:"rx_header_max,optional,default=40960"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConnectEnum struct {
|
type ConnectEnum struct {
|
||||||
@ -115,12 +156,14 @@ type ConnectEnum struct {
|
|||||||
|
|
||||||
type TCPConnect struct {
|
type TCPConnect struct {
|
||||||
Type string `yaml:"type"`
|
Type string `yaml:"type"`
|
||||||
|
RPC *RPCConfig `yaml:"rpc,optional"`
|
||||||
Address string `yaml:"address"`
|
Address string `yaml:"address"`
|
||||||
DialTimeout time.Duration `yaml:"dial_timeout,positive,default=10s"`
|
DialTimeout time.Duration `yaml:"dial_timeout,positive,default=10s"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TLSConnect struct {
|
type TLSConnect struct {
|
||||||
Type string `yaml:"type"`
|
Type string `yaml:"type"`
|
||||||
|
RPC *RPCConfig `yaml:"rpc,optional"`
|
||||||
Address string `yaml:"address"`
|
Address string `yaml:"address"`
|
||||||
Ca string `yaml:"ca"`
|
Ca string `yaml:"ca"`
|
||||||
Cert string `yaml:"cert"`
|
Cert string `yaml:"cert"`
|
||||||
@ -131,6 +174,7 @@ type TLSConnect struct {
|
|||||||
|
|
||||||
type SSHStdinserverConnect struct {
|
type SSHStdinserverConnect struct {
|
||||||
Type string `yaml:"type"`
|
Type string `yaml:"type"`
|
||||||
|
RPC *RPCConfig `yaml:"rpc,optional"`
|
||||||
Host string `yaml:"host"`
|
Host string `yaml:"host"`
|
||||||
User string `yaml:"user"`
|
User string `yaml:"user"`
|
||||||
Port uint16 `yaml:"port"`
|
Port uint16 `yaml:"port"`
|
||||||
@ -233,7 +277,7 @@ type GlobalControl struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type GlobalServe struct {
|
type GlobalServe struct {
|
||||||
StdinServer GlobalStdinServer `yaml:"stdinserver"`
|
StdinServer *GlobalStdinServer `yaml:"stdinserver,optional,fromdefaults"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GlobalStdinServer struct {
|
type GlobalStdinServer struct {
|
||||||
@ -241,10 +285,11 @@ type GlobalStdinServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type JobDebugSettings struct {
|
type JobDebugSettings struct {
|
||||||
Conn struct {
|
Conn *struct {
|
||||||
ReadDump string `yaml:"read_dump"`
|
ReadDump string `yaml:"read_dump"`
|
||||||
WriteDump string `yaml:"write_dump"`
|
WriteDump string `yaml:"write_dump"`
|
||||||
} `yaml:"conn"`
|
} `yaml:"conn,optional"`
|
||||||
|
RPCLog bool `yaml:"rpc_log,optional,default=false"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func enumUnmarshal(u func(interface{}, bool) error, types map[string]interface{}) (interface{}, error) {
|
func enumUnmarshal(u func(interface{}, bool) error, types map[string]interface{}) (interface{}, error) {
|
||||||
@ -328,7 +373,7 @@ var ConfigFileDefaultLocations = []string{
|
|||||||
"/usr/local/etc/zrepl/zrepl.yml",
|
"/usr/local/etc/zrepl/zrepl.yml",
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseConfig(path string) (i Config, err error) {
|
func ParseConfig(path string) (i *Config, err error) {
|
||||||
|
|
||||||
if path == "" {
|
if path == "" {
|
||||||
// Try default locations
|
// Try default locations
|
||||||
@ -352,11 +397,18 @@ func ParseConfig(path string) (i Config, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = yaml.UnmarshalStrict(bytes, &i); err != nil {
|
return ParseConfigBytes(bytes)
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return
|
func ParseConfigBytes(bytes []byte) (*Config, error) {
|
||||||
|
var c *Config
|
||||||
|
if err := yaml.UnmarshalStrict(bytes, &c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if c == nil {
|
||||||
|
return nil, fmt.Errorf("config is empty or only consists of comments")
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var durationStringRegex *regexp.Regexp = regexp.MustCompile(`^\s*(\d+)\s*(s|m|h|d|w)\s*$`)
|
var durationStringRegex *regexp.Regexp = regexp.MustCompile(`^\s*(\d+)\s*(s|m|h|d|w)\s*$`)
|
||||||
|
41
config/config_minimal_test.go
Normal file
41
config/config_minimal_test.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfigEmptyFails(t *testing.T) {
|
||||||
|
conf, err := testConfig(t, "\n")
|
||||||
|
assert.Nil(t, conf)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJobsOnlyWorks(t *testing.T) {
|
||||||
|
testValidConfig(t, `
|
||||||
|
jobs:
|
||||||
|
- name: push
|
||||||
|
type: push
|
||||||
|
# snapshot the filesystems matched by the left-hand-side of the mapping
|
||||||
|
# every 10m with zrepl_ as prefix
|
||||||
|
replication:
|
||||||
|
connect:
|
||||||
|
type: tcp
|
||||||
|
address: localhost:2342
|
||||||
|
filesystems: {
|
||||||
|
"pool1/var/db<": true,
|
||||||
|
"pool1/usr/home<": true,
|
||||||
|
"pool1/usr/home/paranoid": false, #don't backup paranoid user
|
||||||
|
"pool1/poudriere/ports<": false #don't backup the ports trees
|
||||||
|
}
|
||||||
|
snapshotting:
|
||||||
|
snapshot_prefix: zrepl_
|
||||||
|
interval: 10m
|
||||||
|
pruning:
|
||||||
|
keep_sender:
|
||||||
|
- type: not_replicated
|
||||||
|
keep_receiver:
|
||||||
|
- type: last_n
|
||||||
|
count: 1
|
||||||
|
`)
|
||||||
|
}
|
61
config/config_rpc_test.go
Normal file
61
config/config_rpc_test.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRPC (t *testing.T) {
|
||||||
|
conf := testValidConfig(t, `
|
||||||
|
jobs:
|
||||||
|
- name: pull_servers
|
||||||
|
type: pull
|
||||||
|
replication:
|
||||||
|
connect:
|
||||||
|
type: tcp
|
||||||
|
address: "server1.foo.bar:8888"
|
||||||
|
rpc:
|
||||||
|
timeout: 20s # different form default, should merge
|
||||||
|
root_dataset: "pool2/backup_servers"
|
||||||
|
interval: 10m
|
||||||
|
pruning:
|
||||||
|
keep_sender:
|
||||||
|
- type: not_replicated
|
||||||
|
keep_receiver:
|
||||||
|
- type: last_n
|
||||||
|
count: 100
|
||||||
|
|
||||||
|
- name: pull_servers2
|
||||||
|
type: pull
|
||||||
|
replication:
|
||||||
|
connect:
|
||||||
|
type: tcp
|
||||||
|
address: "server1.foo.bar:8888"
|
||||||
|
rpc:
|
||||||
|
tx_chunk_size: 0xabcd # different from default, should merge
|
||||||
|
root_dataset: "pool2/backup_servers"
|
||||||
|
interval: 10m
|
||||||
|
pruning:
|
||||||
|
keep_sender:
|
||||||
|
- type: not_replicated
|
||||||
|
keep_receiver:
|
||||||
|
- type: last_n
|
||||||
|
count: 100
|
||||||
|
`)
|
||||||
|
|
||||||
|
assert.Equal(t, 20*time.Second, conf.Jobs[0].Ret.(*PullJob).Replication.Connect.Ret.(*TCPConnect).RPC.Timeout)
|
||||||
|
assert.Equal(t, uint(0xabcd), conf.Jobs[1].Ret.(*PullJob).Replication.Connect.Ret.(*TCPConnect).RPC.TxChunkSize)
|
||||||
|
defConf := RPCConfig{}
|
||||||
|
Default(&defConf)
|
||||||
|
assert.Equal(t, defConf.Timeout, conf.Global.RPC.Timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGlobal_DefaultRPCConfig(t *testing.T) {
|
||||||
|
assert.NotPanics(t, func() {
|
||||||
|
var c RPCConfig
|
||||||
|
Default(&c)
|
||||||
|
assert.NotNil(t, c)
|
||||||
|
assert.Equal(t, c.TxChunkSize, uint(1)<<15)
|
||||||
|
})
|
||||||
|
}
|
@ -4,6 +4,9 @@ import (
|
|||||||
"github.com/kr/pretty"
|
"github.com/kr/pretty"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/zrepl/yaml-config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSampleConfigsAreParsedWithoutErrors(t *testing.T) {
|
func TestSampleConfigsAreParsedWithoutErrors(t *testing.T) {
|
||||||
@ -27,3 +30,26 @@ func TestSampleConfigsAreParsedWithoutErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoggingOutletEnumList_SetDefaults(t *testing.T) {
|
||||||
|
e := &LoggingOutletEnumList{}
|
||||||
|
var i yaml.Defaulter = e
|
||||||
|
require.NotPanics(t, func() {
|
||||||
|
i.SetDefault()
|
||||||
|
assert.Equal(t, "warn", (*e)[0].Ret.(StdoutLoggingOutlet).Level)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func testValidConfig(t *testing.T, input string) (*Config) {
|
||||||
|
t.Helper()
|
||||||
|
conf, err := testConfig(t, input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, conf)
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConfig(t *testing.T, input string) (*Config, error) {
|
||||||
|
t.Helper()
|
||||||
|
return ParseConfigBytes([]byte(input))
|
||||||
|
}
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/zrepl/zrepl/config"
|
"github.com/zrepl/zrepl/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FromConfig(g config.Global, in config.ConnectEnum) (streamrpc.Connecter, error) {
|
func FromConfig(g *config.Global, in config.ConnectEnum) (streamrpc.Connecter, error) {
|
||||||
switch v := in.Ret.(type) {
|
switch v := in.Ret.(type) {
|
||||||
case *config.SSHStdinserverConnect:
|
case *config.SSHStdinserverConnect:
|
||||||
return SSHStdinserverConnecterFromConfig(v)
|
return SSHStdinserverConnecterFromConfig(v)
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run(conf config.Config) error {
|
func Run(conf *config.Config) error {
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ func Run(conf config.Config) error {
|
|||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
outlets, err := logging.OutletsFromConfig(conf.Global.Logging)
|
outlets, err := logging.OutletsFromConfig(*conf.Global.Logging)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "cannot build logging from config")
|
return errors.Wrap(err, "cannot build logging from config")
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func JobsFromConfig(c config.Config) ([]Job, error) {
|
func JobsFromConfig(c *config.Config) ([]Job, error) {
|
||||||
js := make([]Job, len(c.Jobs))
|
js := make([]Job, len(c.Jobs))
|
||||||
for i := range c.Jobs {
|
for i := range c.Jobs {
|
||||||
j, err := buildJob(c.Global, c.Jobs[i])
|
j, err := buildJob(c.Global, c.Jobs[i])
|
||||||
@ -18,7 +18,7 @@ func JobsFromConfig(c config.Config) ([]Job, error) {
|
|||||||
return js, nil
|
return js, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildJob(c config.Global, in config.JobEnum) (j Job, err error) {
|
func buildJob(c *config.Global, in config.JobEnum) (j Job, err error) {
|
||||||
switch v := in.Ret.(type) {
|
switch v := in.Ret.(type) {
|
||||||
case *config.SinkJob:
|
case *config.SinkJob:
|
||||||
j, err = SinkFromConfig(c, v)
|
j, err = SinkFromConfig(c, v)
|
||||||
|
@ -25,7 +25,7 @@ type Push struct {
|
|||||||
replication *replication.Replication
|
replication *replication.Replication
|
||||||
}
|
}
|
||||||
|
|
||||||
func PushFromConfig(g config.Global, in *config.PushJob) (j *Push, err error) {
|
func PushFromConfig(g *config.Global, in *config.PushJob) (j *Push, err error) {
|
||||||
|
|
||||||
j = &Push{}
|
j = &Push{}
|
||||||
j.name = in.Name
|
j.name = in.Name
|
||||||
|
@ -19,7 +19,7 @@ type Sink struct {
|
|||||||
fsmapInv endpoint.FSFilter
|
fsmapInv endpoint.FSFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
func SinkFromConfig(g config.Global, in *config.SinkJob) (s *Sink, err error) {
|
func SinkFromConfig(g *config.Global, in *config.SinkJob) (s *Sink, err error) {
|
||||||
|
|
||||||
// FIXME multi client support
|
// FIXME multi client support
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func OutletsFromConfig(in []config.LoggingOutletEnum) (*logger.Outlets, error) {
|
func OutletsFromConfig(in config.LoggingOutletEnumList) (*logger.Outlets, error) {
|
||||||
|
|
||||||
outlets := logger.NewOutlets()
|
outlets := logger.NewOutlets()
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ type ListenerFactory interface {
|
|||||||
Listen() (net.Listener, error)
|
Listen() (net.Listener, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromConfig(g config.Global, in config.ServeEnum) (ListenerFactory, error) {
|
func FromConfig(g *config.Global, in config.ServeEnum) (ListenerFactory, error) {
|
||||||
|
|
||||||
switch v := in.Ret.(type) {
|
switch v := in.Ret.(type) {
|
||||||
case *config.TCPServe:
|
case *config.TCPServe:
|
||||||
|
@ -15,7 +15,7 @@ type StdinserverListenerFactory struct {
|
|||||||
sockpath string
|
sockpath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func StdinserverListenerFactoryFromConfig(g config.Global, in *config.StdinserverServer) (f *StdinserverListenerFactory, err error) {
|
func StdinserverListenerFactoryFromConfig(g *config.Global, in *config.StdinserverServer) (f *StdinserverListenerFactory, err error) {
|
||||||
|
|
||||||
f = &StdinserverListenerFactory{
|
f = &StdinserverListenerFactory{
|
||||||
ClientIdentity: in.ClientIdentity,
|
ClientIdentity: in.ClientIdentity,
|
||||||
|
@ -9,7 +9,7 @@ type TCPListenerFactory struct {
|
|||||||
Address string
|
Address string
|
||||||
}
|
}
|
||||||
|
|
||||||
func TCPListenerFactoryFromConfig(c config.Global, in *config.TCPServe) (*TCPListenerFactory, error) {
|
func TCPListenerFactoryFromConfig(c *config.Global, in *config.TCPServe) (*TCPListenerFactory, error) {
|
||||||
lf := &TCPListenerFactory{
|
lf := &TCPListenerFactory{
|
||||||
Address: in.Listen,
|
Address: in.Listen,
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ type TLSListenerFactory struct {
|
|||||||
handshakeTimeout time.Duration
|
handshakeTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func TLSListenerFactoryFromConfig(c config.Global, in *config.TLSServe) (lf *TLSListenerFactory, err error) {
|
func TLSListenerFactoryFromConfig(c *config.Global, in *config.TLSServe) (lf *TLSListenerFactory, err error) {
|
||||||
lf = &TLSListenerFactory{
|
lf = &TLSListenerFactory{
|
||||||
address: in.Listen,
|
address: in.Listen,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user