zrepl/replication/logic/replication_logic_policy.go
Christian Schwarz 2642c64303 make initial replication policy configurable (most_recent, all, fail)
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
2022-06-26 14:36:59 +02:00

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)
}
}