mirror of
https://github.com/zrepl/zrepl.git
synced 2024-11-23 00:43:51 +01:00
2642c64303
Config: ``` - type: push ... conflict_resolution: initial_replication: most_recent | all | fali ``` The ``initial_replication`` option determines which snapshots zrepl replicates if the filesystem has not been replicated before. If ``most_recent`` (the default), the initial replication will only transfer the most recent snapshot, while ignoring previous snapshots. If all snapshots should be replicated, specify ``all``. Use ``fail`` to make replication of the filesystem fail in case there is no corresponding fileystem on the receiver. Code-Level Changes, apart from the obvious: - Rework IncrementalPath()'s return signature. Now returns an error for initial replications as well. - Rename & rework it's consumer, resolveConflict(). Co-authored-by: Graham Christensen <graham@grahamc.com> Fixes https://github.com/zrepl/zrepl/issues/550 Fixes https://github.com/zrepl/zrepl/issues/187 Closes https://github.com/zrepl/zrepl/pull/592
113 lines
3.6 KiB
Go
113 lines
3.6 KiB
Go
package logic
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/go-playground/validator"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/zrepl/zrepl/config"
|
|
"github.com/zrepl/zrepl/replication/logic/pdu"
|
|
)
|
|
|
|
//go:generate enumer -type=InitialReplicationAutoResolution -trimprefix=InitialReplicationAutoResolution
|
|
type InitialReplicationAutoResolution uint32
|
|
|
|
const (
|
|
InitialReplicationAutoResolutionMostRecent InitialReplicationAutoResolution = 1 << iota
|
|
InitialReplicationAutoResolutionAll
|
|
InitialReplicationAutoResolutionFail
|
|
)
|
|
|
|
var initialReplicationAutoResolutionConfigMap = map[InitialReplicationAutoResolution]string{
|
|
InitialReplicationAutoResolutionMostRecent: "most_recent",
|
|
InitialReplicationAutoResolutionAll: "all",
|
|
InitialReplicationAutoResolutionFail: "fail",
|
|
}
|
|
|
|
func InitialReplicationAutoResolutionFromConfig(in string) (InitialReplicationAutoResolution, error) {
|
|
for v, s := range initialReplicationAutoResolutionConfigMap {
|
|
if s == in {
|
|
return v, nil
|
|
}
|
|
}
|
|
l := make([]string, 0, len(initialReplicationAutoResolutionConfigMap))
|
|
for _, v := range InitialReplicationAutoResolutionValues() {
|
|
l = append(l, initialReplicationAutoResolutionConfigMap[v])
|
|
}
|
|
return 0, fmt.Errorf("invalid value %q, must be one of %s", in, strings.Join(l, ", "))
|
|
}
|
|
|
|
type ConflictResolution struct {
|
|
InitialReplication InitialReplicationAutoResolution
|
|
}
|
|
|
|
func (c *ConflictResolution) Validate() error {
|
|
if !c.InitialReplication.IsAInitialReplicationAutoResolution() {
|
|
return errors.Errorf("must be one of %s", InitialReplicationAutoResolutionValues())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ConflictResolutionFromConfig(in *config.ConflictResolution) (*ConflictResolution, error) {
|
|
|
|
initialReplication, err := InitialReplicationAutoResolutionFromConfig(in.InitialReplication)
|
|
if err != nil {
|
|
return nil, errors.Errorf("field `initial_replication` is invalid: %q is not one of %v", in.InitialReplication, InitialReplicationAutoResolutionValues())
|
|
}
|
|
|
|
return &ConflictResolution{
|
|
InitialReplication: initialReplication,
|
|
}, nil
|
|
}
|
|
|
|
type PlannerPolicy struct {
|
|
EncryptedSend tri // all sends must be encrypted (send -w, and encryption!=off)
|
|
ConflictResolution *ConflictResolution `validate:"ne=nil"`
|
|
ReplicationConfig *pdu.ReplicationConfig `validate:"ne=nil"`
|
|
SizeEstimationConcurrency int `validate:"gte=1"`
|
|
}
|
|
|
|
var validate = validator.New()
|
|
|
|
func (p PlannerPolicy) Validate() error {
|
|
if err := validate.Struct(p); err != nil {
|
|
return err
|
|
}
|
|
if err := p.ConflictResolution.Validate(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ReplicationConfigFromConfig(in *config.Replication) (*pdu.ReplicationConfig, error) {
|
|
initial, err := pduReplicationGuaranteeKindFromConfig(in.Protection.Initial)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "field 'initial'")
|
|
}
|
|
incremental, err := pduReplicationGuaranteeKindFromConfig(in.Protection.Incremental)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "field 'incremental'")
|
|
}
|
|
return &pdu.ReplicationConfig{
|
|
Protection: &pdu.ReplicationConfigProtection{
|
|
Initial: initial,
|
|
Incremental: incremental,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func pduReplicationGuaranteeKindFromConfig(in string) (k pdu.ReplicationGuaranteeKind, _ error) {
|
|
switch in {
|
|
case "guarantee_nothing":
|
|
return pdu.ReplicationGuaranteeKind_GuaranteeNothing, nil
|
|
case "guarantee_incremental":
|
|
return pdu.ReplicationGuaranteeKind_GuaranteeIncrementalReplication, nil
|
|
case "guarantee_resumability":
|
|
return pdu.ReplicationGuaranteeKind_GuaranteeResumability, nil
|
|
default:
|
|
return k, errors.Errorf("%q is not in guarantee_{nothing,incremental,resumability}", in)
|
|
}
|
|
}
|