mirror of
https://github.com/zrepl/zrepl.git
synced 2024-11-22 00:13:52 +01:00
wip floocode backup
This commit is contained in:
parent
b0d17803f0
commit
c2b04d10c5
9
Gopkg.lock
generated
9
Gopkg.lock
generated
@ -17,14 +17,6 @@
|
|||||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||||
version = "v1.1.0"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:ae162f9b5c46f6d5ff4bd53a3d78f72e2eb6676c11c5d33b8b106c36f87ddb31"
|
|
||||||
name = "github.com/dustin/go-humanize"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = ""
|
|
||||||
revision = "bb3d318650d48840a39aa21a027c6630e198e626"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:5d0a2385edf4ba44f3b7b76bc0436ceb8f62bf55aa5d540a9eb9ec6c58d86809"
|
digest = "1:5d0a2385edf4ba44f3b7b76bc0436ceb8f62bf55aa5d540a9eb9ec6c58d86809"
|
||||||
@ -247,7 +239,6 @@
|
|||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
input-imports = [
|
input-imports = [
|
||||||
"github.com/dustin/go-humanize",
|
|
||||||
"github.com/go-logfmt/logfmt",
|
"github.com/go-logfmt/logfmt",
|
||||||
"github.com/go-yaml/yaml",
|
"github.com/go-yaml/yaml",
|
||||||
"github.com/golang/protobuf/proto",
|
"github.com/golang/protobuf/proto",
|
||||||
|
@ -6,6 +6,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AnyFSVFilter struct{}
|
||||||
|
|
||||||
|
func (AnyFSVFilter) Filter(t zfs.VersionType, name string) (accept bool, err error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
type PrefixFilter struct {
|
type PrefixFilter struct {
|
||||||
prefix string
|
prefix string
|
||||||
fstype zfs.VersionType
|
fstype zfs.VersionType
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zrepl/zrepl/cmd/config"
|
||||||
"github.com/zrepl/zrepl/cmd/endpoint"
|
"github.com/zrepl/zrepl/cmd/endpoint"
|
||||||
"github.com/zrepl/zrepl/replication"
|
"github.com/zrepl/zrepl/replication"
|
||||||
"github.com/zrepl/zrepl/zfs"
|
"github.com/zrepl/zrepl/zfs"
|
||||||
@ -22,7 +23,7 @@ type LocalJob struct {
|
|||||||
Debug JobDebugSettings
|
Debug JobDebugSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseLocalJob(c JobParsingContext, name string, i map[string]interface{}) (j *LocalJob, err error) {
|
func parseLocalJob(c config.Global, in source.LocalJob) (j *LocalJob, err error) {
|
||||||
|
|
||||||
var asMap struct {
|
var asMap struct {
|
||||||
Mapping map[string]string
|
Mapping map[string]string
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/problame/go-streamrpc"
|
"github.com/problame/go-streamrpc"
|
||||||
|
"github.com/zrepl/zrepl/cmd/config"
|
||||||
"github.com/zrepl/zrepl/cmd/endpoint"
|
"github.com/zrepl/zrepl/cmd/endpoint"
|
||||||
"github.com/zrepl/zrepl/replication"
|
"github.com/zrepl/zrepl/replication"
|
||||||
)
|
)
|
||||||
@ -21,74 +22,44 @@ type PullJob struct {
|
|||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
Mapping *DatasetMapFilter
|
Mapping *DatasetMapFilter
|
||||||
// constructed from mapping during parsing
|
// constructed from mapping during parsing
|
||||||
pruneFilter *DatasetMapFilter
|
pruneFilter *DatasetMapFilter
|
||||||
SnapshotPrefix string
|
Prune PrunePolicy
|
||||||
Prune PrunePolicy
|
|
||||||
Debug JobDebugSettings
|
|
||||||
|
|
||||||
rep *replication.Replication
|
rep *replication.Replication
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePullJob(c JobParsingContext, name string, i map[string]interface{}) (j *PullJob, err error) {
|
func parsePullJob(c config.Global, in config.PullJob) (j *PullJob, err error) {
|
||||||
|
|
||||||
var asMap struct {
|
j = &PullJob{Name: in.Name}
|
||||||
Connect map[string]interface{}
|
|
||||||
Interval string
|
|
||||||
Mapping map[string]string
|
|
||||||
InitialReplPolicy string `mapstructure:"initial_repl_policy"`
|
|
||||||
Prune map[string]interface{}
|
|
||||||
SnapshotPrefix string `mapstructure:"snapshot_prefix"`
|
|
||||||
Debug map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = mapstructure.Decode(i, &asMap); err != nil {
|
j.Connect, err = parseConnect(in.Replication.Connect)
|
||||||
err = errors.Wrap(err, "mapstructure error")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
j = &PullJob{Name: name}
|
|
||||||
|
|
||||||
j.Connect, err = parseConnect(asMap.Connect)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Wrap(err, "cannot parse 'connect'")
|
err = errors.Wrap(err, "cannot parse 'connect'")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if j.Interval, err = parsePostitiveDuration(asMap.Interval); err != nil {
|
j.Interval = in.Replication.Interval
|
||||||
err = errors.Wrap(err, "cannot parse 'interval'")
|
|
||||||
|
j.Mapping = NewDatasetMapFilter(1, false)
|
||||||
|
if err := j.Mapping.Add("<", in.Replication.RootDataset); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
j.Mapping, err = parseDatasetMapFilter(asMap.Mapping, false)
|
j.pruneFilter = NewDatasetMapFilter(1, true)
|
||||||
if err != nil {
|
if err := j.pruneFilter.Add(in.Replication.RootDataset, MapFilterResultOk); err != nil {
|
||||||
err = errors.Wrap(err, "cannot parse 'mapping'")
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if j.pruneFilter, err = j.Mapping.InvertedFilter(); err != nil {
|
|
||||||
err = errors.Wrap(err, "cannot automatically invert 'mapping' for prune job")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if j.SnapshotPrefix, err = parseSnapshotPrefix(asMap.SnapshotPrefix); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if j.Prune, err = parsePrunePolicy(asMap.Prune, false); err != nil {
|
if j.Prune, err = parsePrunePolicy(asMap.Prune, false); err != nil {
|
||||||
err = errors.Wrap(err, "cannot parse prune policy")
|
err = errors.Wrap(err, "cannot parse prune policy")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mapstructure.Decode(asMap.Debug, &j.Debug); err != nil {
|
if in.Debug.Conn.ReadDump != "" || j.Debug.Conn.WriteDump != "" {
|
||||||
err = errors.Wrap(err, "cannot parse 'debug'")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if j.Debug.Conn.ReadDump != "" || j.Debug.Conn.WriteDump != "" {
|
|
||||||
logConnecter := logNetConnConnecter{
|
logConnecter := logNetConnConnecter{
|
||||||
Connecter: j.Connect,
|
Connecter: j.Connect,
|
||||||
ReadDump: j.Debug.Conn.ReadDump,
|
ReadDump: in.Debug.Conn.ReadDump,
|
||||||
WriteDump: j.Debug.Conn.WriteDump,
|
WriteDump: in.Debug.Conn.WriteDump,
|
||||||
}
|
}
|
||||||
j.Connect = logConnecter
|
j.Connect = logConnecter
|
||||||
}
|
}
|
||||||
@ -96,11 +67,7 @@ func parsePullJob(c JobParsingContext, name string, i map[string]interface{}) (j
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *PullJob) JobName() string {
|
func (j *PullJob) JobName() string { return j.Name }
|
||||||
return j.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *PullJob) JobType() JobType { return JobTypePull }
|
|
||||||
|
|
||||||
func (j *PullJob) JobStart(ctx context.Context) {
|
func (j *PullJob) JobStart(ctx context.Context) {
|
||||||
|
|
||||||
@ -159,10 +126,7 @@ func (j *PullJob) doRun(ctx context.Context) {
|
|||||||
|
|
||||||
sender := endpoint.NewRemote(client)
|
sender := endpoint.NewRemote(client)
|
||||||
|
|
||||||
receiver, err := endpoint.NewReceiver(
|
receiver, err := endpoint.NewReceiver(j.Mapping, AnyFSVFilter{})
|
||||||
j.Mapping,
|
|
||||||
NewPrefixFilter(j.SnapshotPrefix),
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("error creating receiver endpoint")
|
log.WithError(err).Error("error creating receiver endpoint")
|
||||||
return
|
return
|
||||||
@ -198,7 +162,6 @@ func (j *PullJob) Pruner(side PrunePolicySide, dryRun bool) (p Pruner, err error
|
|||||||
time.Now(),
|
time.Now(),
|
||||||
dryRun,
|
dryRun,
|
||||||
j.pruneFilter,
|
j.pruneFilter,
|
||||||
j.SnapshotPrefix,
|
|
||||||
j.Prune,
|
j.Prune,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/zrepl/zrepl/cmd/endpoint"
|
"github.com/zrepl/zrepl/cmd/endpoint"
|
||||||
"github.com/zrepl/zrepl/zfs"
|
"github.com/zrepl/zrepl/zfs"
|
||||||
@ -258,16 +257,14 @@ func (m DatasetMapFilter) parseDatasetFilterResult(result string) (pass bool, er
|
|||||||
return false, fmt.Errorf("'%s' is not a valid filter result", result)
|
return false, fmt.Errorf("'%s' is not a valid filter result", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDatasetMapFilter(mi interface{}, filterMode bool) (f *DatasetMapFilter, err error) {
|
func parseDatasetMapFilterFilesystems(in map[string]bool) (f *DatasetMapFilter, err error) {
|
||||||
|
|
||||||
var m map[string]string
|
f = NewDatasetMapFilter(len(in), true)
|
||||||
if err = mapstructure.Decode(mi, &m); err != nil {
|
for pathPattern, accept := range in {
|
||||||
err = fmt.Errorf("maps / filters must be specified as map[string]string: %s", err)
|
mapping := MapFilterResultOmit
|
||||||
return
|
if accept {
|
||||||
}
|
mapping = MapFilterResultOk
|
||||||
|
}
|
||||||
f = NewDatasetMapFilter(len(m), filterMode)
|
|
||||||
for pathPattern, mapping := range m {
|
|
||||||
if err = f.Add(pathPattern, mapping); err != nil {
|
if err = f.Add(pathPattern, mapping); err != nil {
|
||||||
err = fmt.Errorf("invalid mapping entry ['%s':'%s']: %s", pathPattern, mapping, err)
|
err = fmt.Errorf("invalid mapping entry ['%s':'%s']: %s", pathPattern, mapping, err)
|
||||||
return
|
return
|
||||||
|
@ -8,10 +8,9 @@ import (
|
|||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/problame/go-streamrpc"
|
"github.com/problame/go-streamrpc"
|
||||||
|
"github.com/zrepl/zrepl/cmd/config"
|
||||||
|
"github.com/zrepl/zrepl/cmd/pruning/retentiongrid"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var ConfigFileDefaultLocations []string = []string{
|
var ConfigFileDefaultLocations []string = []string{
|
||||||
@ -141,116 +140,72 @@ func parseConfig(i interface{}) (c *Config, err error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractStringField(i map[string]interface{}, key string, notempty bool) (field string, err error) {
|
|
||||||
vi, ok := i[key]
|
|
||||||
if !ok {
|
|
||||||
err = errors.Errorf("must have field '%s'", key)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
field, ok = vi.(string)
|
|
||||||
if !ok {
|
|
||||||
err = errors.Errorf("'%s' field must have type string", key)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if notempty && len(field) <= 0 {
|
|
||||||
err = errors.Errorf("'%s' field must not be empty", key)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type JobParsingContext struct {
|
type JobParsingContext struct {
|
||||||
ConfigParsingContext
|
ConfigParsingContext
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseJob(c JobParsingContext, i map[string]interface{}) (j Job, err error) {
|
func parseJob(c config.Global, in config.JobEnum) (j Job, err error) {
|
||||||
|
|
||||||
name, err := extractStringField(i, "name", true)
|
switch v := in.Ret.(type) {
|
||||||
if err != nil {
|
case config.PullJob:
|
||||||
return nil, err
|
return parsePullJob(c, v)
|
||||||
|
case config.SourceJob:
|
||||||
|
return parseSourceJob(c, v)
|
||||||
|
case config.LocalJob:
|
||||||
|
return parseLocalJob(c, v)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("implementation error: unknown job type %s", v))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range ReservedJobNames {
|
}
|
||||||
if name == r {
|
|
||||||
err = errors.Errorf("job name '%s' is reserved", name)
|
func parseConnect(in config.ConnectEnum) (c streamrpc.Connecter, err error) {
|
||||||
return nil, err
|
switch v := in.Ret.(type) {
|
||||||
|
case config.SSHStdinserverConnect:
|
||||||
|
return parseSSHStdinserverConnecter(v)
|
||||||
|
case config.TCPConnect:
|
||||||
|
return parseTCPConnecter(v)
|
||||||
|
case config.TLSConnect:
|
||||||
|
return parseTLSConnecter(v)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unknown connect type %v", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePruning(in []config.PruningEnum, willSeeBookmarks bool) (p Pruner, err error) {
|
||||||
|
|
||||||
|
policies := make([]PrunePolicy, len(in))
|
||||||
|
for i := range in {
|
||||||
|
if policies[i], err = parseKeepRule(in[i]); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "invalid keep rule #%d:", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jobtypeStr, err := extractStringField(i, "type", true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
jobtype, err := ParseUserJobType(jobtypeStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch jobtype {
|
|
||||||
case JobTypePull:
|
|
||||||
return parsePullJob(c, name, i)
|
|
||||||
case JobTypeSource:
|
|
||||||
return parseSourceJob(c, name, i)
|
|
||||||
case JobTypeLocal:
|
|
||||||
return parseLocalJob(c, name, i)
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("implementation error: unknown job type %s", jobtype))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseConnect(i map[string]interface{}) (c streamrpc.Connecter, err error) {
|
func parseKeepRule(in config.PruningEnum) (p PrunePolicy, err error) {
|
||||||
|
switch v := in.Ret.(type) {
|
||||||
t, err := extractStringField(i, "type", true)
|
case config.PruneGrid:
|
||||||
if err != nil {
|
return retentiongrid.ParseGridPrunePolicy(v, willSeeBookmarks)
|
||||||
return nil, err
|
//case config.PruneKeepLastN:
|
||||||
}
|
//case config.PruneKeepPrefix:
|
||||||
|
//case config.PruneKeepNotReplicated:
|
||||||
switch t {
|
|
||||||
case "ssh+stdinserver":
|
|
||||||
return parseSSHStdinserverConnecter(i)
|
|
||||||
case "tcp":
|
|
||||||
return parseTCPConnecter(i)
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("unknown connection type '%s'", t)
|
panic(fmt.Sprintf("unknown keep rule type %v", v))
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func parsePrunePolicy(v map[string]interface{}, willSeeBookmarks bool) (p PrunePolicy, err error) {
|
|
||||||
|
|
||||||
policyName, err := extractStringField(v, "policy", true)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch policyName {
|
|
||||||
case "grid":
|
|
||||||
return parseGridPrunePolicy(v, willSeeBookmarks)
|
|
||||||
case "noprune":
|
|
||||||
return NoPrunePolicy{}, nil
|
|
||||||
default:
|
|
||||||
err = errors.Errorf("unknown policy '%s'", policyName)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseAuthenticatedChannelListenerFactory(c JobParsingContext, v map[string]interface{}) (p ListenerFactory, err error) {
|
func parseAuthenticatedChannelListenerFactory(c config.Global, in config.ServeEnum) (p ListenerFactory, err error) {
|
||||||
|
|
||||||
t, err := extractStringField(v, "type", true)
|
switch v := in.Ret.(type) {
|
||||||
if err != nil {
|
case config.StdinserverServer:
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t {
|
|
||||||
case "stdinserver":
|
|
||||||
return parseStdinserverListenerFactory(c, v)
|
return parseStdinserverListenerFactory(c, v)
|
||||||
case "tcp":
|
case config.TCPServe:
|
||||||
return parseTCPListenerFactory(c, v)
|
return parseTCPListenerFactory(c, v)
|
||||||
|
case config.TLSServe:
|
||||||
|
return parseTLSListenerFactory(c, v)
|
||||||
default:
|
default:
|
||||||
err = errors.Errorf("unknown type '%s'", t)
|
panic(fmt.Sprintf("unknown listener type %v", v))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,25 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/problame/go-netssh"
|
"github.com/problame/go-netssh"
|
||||||
|
"github.com/zrepl/zrepl/cmd/config"
|
||||||
|
"github.com/zrepl/zrepl/cmd/helpers"
|
||||||
"net"
|
"net"
|
||||||
"path"
|
"path"
|
||||||
"github.com/zrepl/zrepl/cmd/helpers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type StdinserverListenerFactory struct {
|
type StdinserverListenerFactory struct {
|
||||||
ClientIdentity string `mapstructure:"client_identity"`
|
ClientIdentity string
|
||||||
sockpath string
|
sockpath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseStdinserverListenerFactory(c JobParsingContext, i map[string]interface{}) (f *StdinserverListenerFactory, err error) {
|
func parseStdinserverListenerFactory(c config.Global, in config.StdinserverServer) (f *StdinserverListenerFactory, err error) {
|
||||||
|
|
||||||
f = &StdinserverListenerFactory{}
|
f = &StdinserverListenerFactory{
|
||||||
|
ClientIdentity: in.ClientIdentity,
|
||||||
if err = mapstructure.Decode(i, f); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "mapstructure error")
|
|
||||||
}
|
|
||||||
if !(len(f.ClientIdentity) > 0) {
|
|
||||||
err = errors.Errorf("must specify 'client_identity'")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f.sockpath = path.Join(c.Global.Serve.Stdinserver.SockDir, f.ClientIdentity)
|
f.sockpath = path.Join(c.Serve.StdinServer.SockDir, f.ClientIdentity)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,88 +1,26 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/zrepl/zrepl/cmd/config"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/zrepl/zrepl/cmd/tlsconf"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TCPListenerFactory struct {
|
type TCPListenerFactory struct {
|
||||||
Address string
|
Address string
|
||||||
tls bool
|
|
||||||
clientCA *x509.CertPool
|
|
||||||
serverCert tls.Certificate
|
|
||||||
clientCommonName string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTCPListenerFactory(c JobParsingContext, i map[string]interface{}) (*TCPListenerFactory, error) {
|
func parseTCPListenerFactory(c config.Global, in config.TCPServe) (*TCPListenerFactory, error) {
|
||||||
|
|
||||||
var in struct {
|
lf := &TCPListenerFactory{
|
||||||
Address string
|
Address: in.Listen,
|
||||||
TLS map[string]interface{}
|
|
||||||
}
|
}
|
||||||
if err := mapstructure.Decode(i, &in); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "mapstructure error")
|
|
||||||
}
|
|
||||||
|
|
||||||
lf := &TCPListenerFactory{}
|
|
||||||
|
|
||||||
if in.Address == "" {
|
|
||||||
return nil, errors.New("must specify field 'address'")
|
|
||||||
}
|
|
||||||
lf.Address = in.Address
|
|
||||||
|
|
||||||
if in.TLS != nil {
|
|
||||||
err := func(i map[string]interface{}) (err error) {
|
|
||||||
var in struct {
|
|
||||||
CA string
|
|
||||||
Cert string
|
|
||||||
Key string
|
|
||||||
ClientCN string `mapstructure:"client_cn"`
|
|
||||||
}
|
|
||||||
if err := mapstructure.Decode(i, &in); err != nil {
|
|
||||||
return errors.Wrap(err, "mapstructure error")
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.CA == "" || in.Cert == "" || in.Key == "" || in.ClientCN == "" {
|
|
||||||
return errors.New("fields 'ca', 'cert', 'key' and 'client_cn' must be specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
lf.clientCommonName = in.ClientCN
|
|
||||||
|
|
||||||
lf.clientCA, err = tlsconf.ParseCAFile(in.CA)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "cannot parse ca file")
|
|
||||||
}
|
|
||||||
|
|
||||||
lf.serverCert, err = tls.LoadX509KeyPair(in.Cert, in.Key)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "cannot parse cer/key pair")
|
|
||||||
}
|
|
||||||
|
|
||||||
lf.tls = true // mark success
|
|
||||||
return nil
|
|
||||||
}(in.TLS)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "error parsing TLS config in field 'tls'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lf, nil
|
return lf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var TCPListenerHandshakeTimeout = 10 * time.Second // FIXME make configurable
|
var TCPListenerHandshakeTimeout = 10 * time.Second // FIXME make configurable
|
||||||
|
|
||||||
func (f *TCPListenerFactory) Listen() (net.Listener, error) {
|
func (f *TCPListenerFactory) Listen() (net.Listener, error) {
|
||||||
l, err := net.Listen("tcp", f.Address)
|
return net.Listen("tcp", f.Address)
|
||||||
if !f.tls || err != nil {
|
|
||||||
return l, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tl := tlsconf.NewClientAuthListener(l, f.clientCA, f.serverCert, f.clientCommonName, TCPListenerHandshakeTimeout)
|
|
||||||
return tl, nil
|
|
||||||
}
|
}
|
||||||
|
78
cmd/config_serve_tls.go
Normal file
78
cmd/config_serve_tls.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zrepl/zrepl/cmd/config"
|
||||||
|
"github.com/zrepl/zrepl/cmd/tlsconf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TCPListenerFactory struct {
|
||||||
|
Address string
|
||||||
|
tls bool
|
||||||
|
clientCA *x509.CertPool
|
||||||
|
serverCert tls.Certificate
|
||||||
|
clientCommonName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTCPListenerFactory(c config.Global, in config.TCPServe) (*TCPListenerFactory, error) {
|
||||||
|
|
||||||
|
lf := &TCPListenerFactory{
|
||||||
|
Address: in.Listen,
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.TLS != nil {
|
||||||
|
err := func(i map[string]interface{}) (err error) {
|
||||||
|
var in struct {
|
||||||
|
CA string
|
||||||
|
Cert string
|
||||||
|
Key string
|
||||||
|
ClientCN string `mapstructure:"client_cn"`
|
||||||
|
}
|
||||||
|
if err := mapstructure.Decode(i, &in); err != nil {
|
||||||
|
return errors.Wrap(err, "mapstructure error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.CA == "" || in.Cert == "" || in.Key == "" || in.ClientCN == "" {
|
||||||
|
return errors.New("fields 'ca', 'cert', 'key' and 'client_cn' must be specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
lf.clientCommonName = in.ClientCN
|
||||||
|
|
||||||
|
lf.clientCA, err = tlsconf.ParseCAFile(in.CA)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "cannot parse ca file")
|
||||||
|
}
|
||||||
|
|
||||||
|
lf.serverCert, err = tls.LoadX509KeyPair(in.Cert, in.Key)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "cannot parse cer/key pair")
|
||||||
|
}
|
||||||
|
|
||||||
|
lf.tls = true // mark success
|
||||||
|
return nil
|
||||||
|
}(in.TLS)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error parsing TLS config in field 'tls'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var TCPListenerHandshakeTimeout = 10 * time.Second // FIXME make configurable
|
||||||
|
|
||||||
|
func (f *TCPListenerFactory) Listen() (net.Listener, error) {
|
||||||
|
l, err := net.Listen("tcp", f.Address)
|
||||||
|
if !f.tls || err != nil {
|
||||||
|
return l, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tl := tlsconf.NewClientAuthListener(l, f.clientCA, f.serverCert, f.clientCommonName, TCPListenerHandshakeTimeout)
|
||||||
|
return tl, nil
|
||||||
|
}
|
@ -7,14 +7,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/zrepl/zrepl/cmd/daemon"
|
||||||
"github.com/zrepl/zrepl/logger"
|
"github.com/zrepl/zrepl/logger"
|
||||||
|
"github.com/zrepl/zrepl/version"
|
||||||
"io"
|
"io"
|
||||||
golog "log"
|
golog "log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"github.com/zrepl/zrepl/version"
|
|
||||||
"github.com/zrepl/zrepl/cmd/daemon"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var controlCmd = &cobra.Command{
|
var controlCmd = &cobra.Command{
|
||||||
|
@ -5,13 +5,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/zrepl/zrepl/cmd/config"
|
"github.com/zrepl/zrepl/cmd/config"
|
||||||
|
"github.com/zrepl/zrepl/cmd/daemon"
|
||||||
|
"github.com/zrepl/zrepl/cmd/daemon/job"
|
||||||
"github.com/zrepl/zrepl/logger"
|
"github.com/zrepl/zrepl/logger"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
"github.com/zrepl/zrepl/cmd/daemon"
|
|
||||||
"github.com/zrepl/zrepl/cmd/daemon/job"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// daemonCmd represents the daemon command
|
// daemonCmd represents the daemon command
|
||||||
@ -92,6 +92,7 @@ func doDaemon(cmd *cobra.Command, args []string) {
|
|||||||
|
|
||||||
daemonJobs := make([]job.Job, 0, len(conf.Jobs))
|
daemonJobs := make([]job.Job, 0, len(conf.Jobs))
|
||||||
for i := range conf.Jobs {
|
for i := range conf.Jobs {
|
||||||
|
parseJob()
|
||||||
daemonJobs = append(daemonJobs, daemonJobAdaptor{conf.Jobs[i]})
|
daemonJobs = append(daemonJobs, daemonJobAdaptor{conf.Jobs[i]})
|
||||||
}
|
}
|
||||||
daemon.Run(ctx, conf.Global.Control.Sockpath, conf.Global.logging.Outlets, daemonJobs)
|
daemon.Run(ctx, conf.Global.Control.Sockpath, conf.Global.logging.Outlets, daemonJobs)
|
||||||
|
@ -5,18 +5,18 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/zrepl/zrepl/cmd/daemon/job"
|
||||||
|
"github.com/zrepl/zrepl/cmd/helpers"
|
||||||
"github.com/zrepl/zrepl/logger"
|
"github.com/zrepl/zrepl/logger"
|
||||||
|
"github.com/zrepl/zrepl/version"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"github.com/zrepl/zrepl/cmd/daemon/job"
|
|
||||||
"github.com/zrepl/zrepl/version"
|
|
||||||
"github.com/zrepl/zrepl/cmd/helpers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type controlJob struct {
|
type controlJob struct {
|
||||||
sockaddr *net.UnixAddr
|
sockaddr *net.UnixAddr
|
||||||
jobs *jobs
|
jobs *jobs
|
||||||
}
|
}
|
||||||
|
|
||||||
func newControlJob(sockpath string, jobs *jobs) (j *controlJob, err error) {
|
func newControlJob(sockpath string, jobs *jobs) (j *controlJob, err error) {
|
||||||
|
@ -2,19 +2,18 @@ package daemon
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"sync"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/zrepl/zrepl/cmd/daemon/job"
|
"github.com/zrepl/zrepl/cmd/daemon/job"
|
||||||
"strings"
|
|
||||||
"github.com/zrepl/zrepl/logger"
|
"github.com/zrepl/zrepl/logger"
|
||||||
"github.com/zrepl/zrepl/version"
|
"github.com/zrepl/zrepl/version"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func Run(ctx context.Context, controlSockpath string, outlets *logger.Outlets, confJobs []job.Job) {
|
func Run(ctx context.Context, controlSockpath string, outlets *logger.Outlets, confJobs []job.Job) {
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
@ -59,10 +58,10 @@ func Run(ctx context.Context, controlSockpath string, outlets *logger.Outlets, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-jobs.wait():
|
case <-jobs.wait():
|
||||||
log.Info("all jobs finished")
|
log.Info("all jobs finished")
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
log.WithError(ctx.Err()).Info("context finished")
|
log.WithError(ctx.Err()).Info("context finished")
|
||||||
}
|
}
|
||||||
log.Info("daemon exiting")
|
log.Info("daemon exiting")
|
||||||
}
|
}
|
||||||
@ -71,15 +70,15 @@ type jobs struct {
|
|||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
// m protects all fields below it
|
// m protects all fields below it
|
||||||
m sync.RWMutex
|
m sync.RWMutex
|
||||||
wakeups map[string]job.WakeupChan // by JobName
|
wakeups map[string]job.WakeupChan // by JobName
|
||||||
jobs map[string]job.Job
|
jobs map[string]job.Job
|
||||||
}
|
}
|
||||||
|
|
||||||
func newJobs() *jobs {
|
func newJobs() *jobs {
|
||||||
return &jobs{
|
return &jobs{
|
||||||
wakeups: make(map[string]job.WakeupChan),
|
wakeups: make(map[string]job.WakeupChan),
|
||||||
jobs: make(map[string]job.Job),
|
jobs: make(map[string]job.Job),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +101,7 @@ func (s *jobs) status() map[string]interface{} {
|
|||||||
defer s.m.RUnlock()
|
defer s.m.RUnlock()
|
||||||
|
|
||||||
type res struct {
|
type res struct {
|
||||||
name string
|
name string
|
||||||
status interface{}
|
status interface{}
|
||||||
}
|
}
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
@ -125,7 +124,7 @@ func (s *jobs) status() map[string]interface{} {
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
jobNamePrometheus = "_prometheus"
|
jobNamePrometheus = "_prometheus"
|
||||||
jobNameControl = "_control"
|
jobNameControl = "_control"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsInternalJobName(s string) bool {
|
func IsInternalJobName(s string) bool {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package job
|
package job
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/zrepl/zrepl/logger"
|
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/zrepl/zrepl/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Logger = logger.Logger
|
type Logger = logger.Logger
|
||||||
|
@ -4,10 +4,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"github.com/zrepl/zrepl/cmd/daemon/job"
|
||||||
"github.com/zrepl/zrepl/zfs"
|
"github.com/zrepl/zrepl/zfs"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"github.com/zrepl/zrepl/cmd/daemon/job"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type prometheusJob struct {
|
type prometheusJob struct {
|
||||||
@ -48,7 +48,7 @@ func init() {
|
|||||||
prometheus.MustRegister(prom.taskLogEntries)
|
prometheus.MustRegister(prom.taskLogEntries)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *prometheusJob) Name() string { return jobNamePrometheus }
|
func (j *prometheusJob) Name() string { return jobNamePrometheus }
|
||||||
|
|
||||||
func (j *prometheusJob) Status() interface{} { return nil }
|
func (j *prometheusJob) Status() interface{} { return nil }
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
|
||||||
"os"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PreparePrivateSockpath(sockpath string) error {
|
func PreparePrivateSockpath(sockpath string) error {
|
||||||
|
13
cmd/prune.go
13
cmd/prune.go
@ -8,11 +8,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Pruner struct {
|
type Pruner struct {
|
||||||
Now time.Time
|
Now time.Time
|
||||||
DryRun bool
|
DryRun bool
|
||||||
DatasetFilter zfs.DatasetFilter
|
DatasetFilter zfs.DatasetFilter
|
||||||
SnapshotPrefix string
|
policies []PrunePolicy
|
||||||
PrunePolicy PrunePolicy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PruneResult struct {
|
type PruneResult struct {
|
||||||
@ -38,14 +37,14 @@ func (p *Pruner) filterFilesystems(ctx context.Context) (filesystems []*zfs.Data
|
|||||||
func (p *Pruner) filterVersions(ctx context.Context, fs *zfs.DatasetPath) (fsversions []zfs.FilesystemVersion, stop bool) {
|
func (p *Pruner) filterVersions(ctx context.Context, fs *zfs.DatasetPath) (fsversions []zfs.FilesystemVersion, stop bool) {
|
||||||
log := getLogger(ctx).WithField("fs", fs.ToString())
|
log := getLogger(ctx).WithField("fs", fs.ToString())
|
||||||
|
|
||||||
filter := NewPrefixFilter(p.SnapshotPrefix)
|
filter := AnyFSVFilter{}
|
||||||
fsversions, err := zfs.ZFSListFilesystemVersions(fs, filter)
|
fsversions, err := zfs.ZFSListFilesystemVersions(fs, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("error listing filesytem versions")
|
log.WithError(err).Error("error listing filesytem versions")
|
||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
if len(fsversions) == 0 {
|
if len(fsversions) == 0 {
|
||||||
log.WithField("prefix", p.SnapshotPrefix).Info("no filesystem versions matching prefix")
|
log.Info("no filesystem versions matching prefix")
|
||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
return fsversions, false
|
return fsversions, false
|
||||||
|
@ -19,4 +19,3 @@ func init() {
|
|||||||
func doVersion(cmd *cobra.Command, args []string) {
|
func doVersion(cmd *cobra.Command, args []string) {
|
||||||
fmt.Println(version.NewZreplVersionInformation().String())
|
fmt.Println(version.NewZreplVersionInformation().String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package version
|
package version
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
Loading…
Reference in New Issue
Block a user