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
This commit is contained in:
@@ -21,6 +21,9 @@ var Cases = []Case{BatchDestroy,
|
||||
ReplicationIncrementalCleansUpStaleAbstractionsWithoutCacheOnSecondReplication,
|
||||
ReplicationIncrementalDestroysStepHoldsIffIncrementalStepHoldsAreDisabledButStepHoldsExist,
|
||||
ReplicationIncrementalIsPossibleIfCommonSnapshotIsDestroyed,
|
||||
ReplicationInitialAll,
|
||||
ReplicationInitialFail,
|
||||
ReplicationInitialMostRecent,
|
||||
ReplicationIsResumableFullSend__both_GuaranteeResumability,
|
||||
ReplicationIsResumableFullSend__initial_GuaranteeIncrementalReplication_incremental_GuaranteeIncrementalReplication,
|
||||
ReplicationIsResumableFullSend__initial_GuaranteeResumability_incremental_GuaranteeIncrementalReplication,
|
||||
|
||||
@@ -44,6 +44,7 @@ type replicationInvocation struct {
|
||||
guarantee *pdu.ReplicationConfigProtection
|
||||
senderConfigHook func(*endpoint.SenderConfig)
|
||||
receiverConfigHook func(*endpoint.ReceiverConfig)
|
||||
plannerPolicyHook func(*logic.PlannerPolicy)
|
||||
}
|
||||
|
||||
func (i replicationInvocation) Do(ctx *platformtest.Context) *report.Report {
|
||||
@@ -95,8 +96,14 @@ func (i replicationInvocation) Do(ctx *platformtest.Context) *report.Report {
|
||||
ReplicationConfig: &pdu.ReplicationConfig{
|
||||
Protection: i.guarantee,
|
||||
},
|
||||
ConflictResolution: &logic.ConflictResolution{
|
||||
InitialReplication: logic.InitialReplicationAutoResolutionMostRecent,
|
||||
},
|
||||
SizeEstimationConcurrency: 1,
|
||||
}
|
||||
if i.plannerPolicyHook != nil {
|
||||
i.plannerPolicyHook(&plannerPolicy)
|
||||
}
|
||||
|
||||
report, wait := replication.Do(
|
||||
ctx,
|
||||
@@ -1306,3 +1313,79 @@ func ReplicationPlaceholderEncryption__EncryptOnReceiverUseCase__WorksIfConfigur
|
||||
require.NoError(ctx, err)
|
||||
require.Equal(ctx, rfsRoot, props.Get("encryptionroot"))
|
||||
}
|
||||
|
||||
func replicationInitialImpl(ctx *platformtest.Context, iras logic.InitialReplicationAutoResolution, expectExactRfsSnaps []string) *report.Report {
|
||||
// reverse order for snap names to expose sorting assumptions
|
||||
platformtest.Run(ctx, platformtest.PanicErr, ctx.RootDataset, `
|
||||
CREATEROOT
|
||||
+ "sender"
|
||||
+ "sender@3"
|
||||
+ "sender@2"
|
||||
+ "sender@1"
|
||||
+ "receiver"
|
||||
R zfs create -p "${ROOTDS}/receiver/${ROOTDS}"
|
||||
`)
|
||||
|
||||
sjid := endpoint.MustMakeJobID("sender-job")
|
||||
rjid := endpoint.MustMakeJobID("receiver-job")
|
||||
|
||||
sfs := ctx.RootDataset + "/sender"
|
||||
rfsRoot := ctx.RootDataset + "/receiver"
|
||||
|
||||
rep := replicationInvocation{
|
||||
sjid: sjid,
|
||||
rjid: rjid,
|
||||
sfs: sfs,
|
||||
rfsRoot: rfsRoot,
|
||||
guarantee: pdu.ReplicationConfigProtectionWithKind(pdu.ReplicationGuaranteeKind_GuaranteeResumability),
|
||||
plannerPolicyHook: func(pp *logic.PlannerPolicy) {
|
||||
pp.ConflictResolution.InitialReplication = iras
|
||||
},
|
||||
}
|
||||
rfs := rep.ReceiveSideFilesystem()
|
||||
|
||||
// first replication
|
||||
report := rep.Do(ctx)
|
||||
ctx.Logf("\n%s", pretty.Sprint(report))
|
||||
|
||||
versions, err := zfs.ZFSListFilesystemVersions(ctx, mustDatasetPath(rfs), zfs.ListFilesystemVersionsOptions{Types: zfs.Snapshots})
|
||||
if _, ok := err.(*zfs.DatasetDoesNotExist); ok {
|
||||
versions = nil
|
||||
} else {
|
||||
require.NoError(ctx, err)
|
||||
}
|
||||
|
||||
bySnapName := make(map[string]int)
|
||||
for _, v := range versions {
|
||||
bySnapName[v.GetName()] += 1
|
||||
}
|
||||
for _, v := range expectExactRfsSnaps {
|
||||
bySnapName[v] -= 1
|
||||
}
|
||||
|
||||
for _, v := range bySnapName {
|
||||
if v != 0 {
|
||||
ctx.Logf("unexpected snaps:\n%#v", bySnapName)
|
||||
ctx.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
return report
|
||||
}
|
||||
|
||||
func ReplicationInitialAll(ctx *platformtest.Context) {
|
||||
replicationInitialImpl(ctx, logic.InitialReplicationAutoResolutionAll, []string{"3", "2", "1"})
|
||||
}
|
||||
|
||||
func ReplicationInitialMostRecent(ctx *platformtest.Context) {
|
||||
replicationInitialImpl(ctx, logic.InitialReplicationAutoResolutionMostRecent, []string{"1"})
|
||||
}
|
||||
|
||||
func ReplicationInitialFail(ctx *platformtest.Context) {
|
||||
report := replicationInitialImpl(ctx, logic.InitialReplicationAutoResolutionFail, []string{})
|
||||
require.Len(ctx, report.Attempts, 1)
|
||||
require.Nil(ctx, report.Attempts[0].PlanError)
|
||||
require.Len(ctx, report.Attempts[0].Filesystems, 1)
|
||||
require.NotNil(ctx, report.Attempts[0].Filesystems[0].PlanError)
|
||||
require.Contains(ctx, report.Attempts[0].Filesystems[0].PlanError.Err, "automatic conflict resolution for initial replication is disabled in config")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user