From eeb2475bc39674537df63d2133322bf037566ecc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 17:47:40 +0000 Subject: [PATCH 01/34] .github/workflows/spelling/excludes.txt: do not check CHANGES.old (and its commit hash strings), AUTHORS and generated man pages Signed-off-by: Jim Klimov --- .github/workflows/spelling/excludes.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/spelling/excludes.txt b/.github/workflows/spelling/excludes.txt index 0c0ef435..08eec09c 100644 --- a/.github/workflows/spelling/excludes.txt +++ b/.github/workflows/spelling/excludes.txt @@ -47,7 +47,10 @@ SUMS$ \.xpm$ \.yml$ \.zip$ +\.1$ +^AUTHORS$ ^CHANGES$ +^CHANGES.old$ ^excludes\.txt$ ^whitelist\.txt$ ^\.github/workflows/spelling/ From afde374f39d57284b9b1557518290494f124bcbc Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 17:56:17 +0000 Subject: [PATCH 02/34] .github/workflows/spelling/excludes.txt: allow certain words from IT world generally and znapzend in particular Signed-off-by: Jim Klimov --- .github/workflows/spelling/whitelist.txt | 49 +++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/.github/workflows/spelling/whitelist.txt b/.github/workflows/spelling/whitelist.txt index f86d1799..faa600ec 100644 --- a/.github/workflows/spelling/whitelist.txt +++ b/.github/workflows/spelling/whitelist.txt @@ -20,6 +20,7 @@ ARGV asciiphil asdf astring +atime attr austingroupbugs autocommit @@ -39,6 +40,7 @@ badkey bak Balert bashisms +bashrc Bcreate Bdebug Bdelete @@ -53,17 +55,21 @@ Binfo blib Bnoaction Bnot +Bossert +bossert bpatsubst Bpidfile Bpost Bpre bserv Bsyslog +buildpackage Bwarning Bzfs Bznapzend Bznapzendzetup Bznapzendztatz +canmount CBuilder cfg cgi @@ -81,6 +87,8 @@ chof chown chowncmd chownprog +chroot +cmds cmp cmpprog cnf @@ -109,7 +117,9 @@ cpanmin CPANSNAPV cpio cpprog +crlf crt +CVS Cwd CWORD CXX @@ -121,7 +131,10 @@ daemonized DAGOLDEN datadir DBD +de'u +debhelper debian +dedup defn defun deps @@ -129,10 +142,11 @@ dest DESTDIR destfail desttemp -de'u dirname +dirs distclean distdir +DISTRIBUTIONNAME distro dmp dnf @@ -142,6 +156,7 @@ dockerfile DOCTYPE DOITPROG donotask +dpkg DRBASE dsa dsq @@ -154,6 +169,7 @@ dstdsname DSTFILE dstname dsttmp +DTDs DTIPS DTRT dummydataset @@ -167,6 +183,8 @@ endif ENTRYPOINT envval envvar +EOL +eol errline errmsg esac @@ -179,6 +197,7 @@ exitstatus exitval expr EXTRADIST +failsafes Fcntl fedorainfracloud fh @@ -217,11 +236,14 @@ Hassler Heitm heitmueller HMSz +homedir Honame hostname +howtogeek html http Hypnotoad +ico Icommand Icommon Icreate @@ -242,6 +264,7 @@ Ilimited illumos imandir img +incrementals ings inh inheritedattr @@ -274,8 +297,10 @@ Ivalue Iznapzendzetup JB JBERGER +jenkins jimklimov JSON +keygen killproc Klimov LASTCOMMON @@ -294,6 +319,7 @@ localattr localhost localtime loctext +logbias logfile loglevel logto @@ -303,6 +329,7 @@ lsb ltrim mailprog MAINPID +Makefiles makeinfo manpage manpath @@ -388,17 +415,20 @@ pkill plaintar plist plugin +png populat posix prebuilt precmd prefork PREREQ +primarycache printf printsrc propname propstring propval +Proxmox psgi pstcmd pwd @@ -406,14 +436,18 @@ qprefix qq qw qwe +rbash RCAPUTO README recurseparent recv recvu redhat +refquota +refreservation regex regexing +remotehost RESFINAL respawn resync @@ -428,6 +462,7 @@ rootfs rootfs'es rpool rr +rsync runlevel runonce rw @@ -435,12 +470,14 @@ sbin sbindir screation scriptversion +secondarycache SEENDST SEENSRC selftest sendmail setsid shellwords +shlibs shtml SIGHUP SIGINT @@ -450,7 +487,9 @@ smartmatch smf snapname snapsuffix +snaptime SNO +softprops solaris sourced sourcemissing @@ -477,11 +516,13 @@ stdout stefan stip strftime +stringify stripcmd stripprog strptime subdataset SUBDIRS +substvars subsys sudo sudoers @@ -492,6 +533,7 @@ svcinstall SVCINSTALLDIR svcname svg +svn symlink synczbe sys @@ -530,6 +572,7 @@ tsformat tsnapshot tzoffset Ubuntu +ubuntu uid umask umontreal @@ -545,7 +588,9 @@ Uplevel url usbbackup usedbysnapshot +useradd userland +usermod username userprop usr @@ -582,6 +627,7 @@ xml xno Xproperties xset +xtrue yacc YAML Ymd @@ -599,6 +645,7 @@ ZFSW ZFSZONEROOTS ZL znap +znapdest ZNAPVER znapzend ZNAPZENDOPTIONS From 111943f28c2dc69c19888baad432ff6a43ee97fd Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 11:09:05 +0000 Subject: [PATCH 03/34] znapzendzetup: doc typo fix READNE=>README Signed-off-by: Jim Klimov --- bin/znapzendzetup | 2 +- man/znapzendzetup.1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/znapzendzetup b/bin/znapzendzetup index fa319e66..cedcb885 100755 --- a/bin/znapzendzetup +++ b/bin/znapzendzetup @@ -545,7 +545,7 @@ and where 'command' and its unique options is one of the following: NOTE: If you specify [user@]host:dataset for remote replication over SSH, make use of ~/.ssh/config for any advanced options - (custom ports, keys to use, etc.) See znapzend READNE for ideas. + (custom ports, keys to use, etc.) See znapzend README for ideas. delete [--dst=key] diff --git a/man/znapzendzetup.1 b/man/znapzendzetup.1 index 8c8352f1..334ac853 100644 --- a/man/znapzendzetup.1 +++ b/man/znapzendzetup.1 @@ -168,7 +168,7 @@ and where 'command' and its unique options is one of the following: \& \& NOTE: If you specify [user@]host:dataset for remote replication \& over SSH, make use of ~/.ssh/config for any advanced options -\& (custom ports, keys to use, etc.) See znapzend READNE for ideas. +\& (custom ports, keys to use, etc.) See znapzend README for ideas. \& \& delete [\-\-dst=key] \& From cf17797b35939f3bf6828359a6838a81f4c6eeb6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 11:11:09 +0000 Subject: [PATCH 04/34] Fix some trailing whitespaces Signed-off-by: Jim Klimov --- lib/ZnapZend.pm | 2 +- lib/ZnapZend/Time.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ZnapZend.pm b/lib/ZnapZend.pm index 128d8fa2..61478920 100644 --- a/lib/ZnapZend.pm +++ b/lib/ZnapZend.pm @@ -113,7 +113,7 @@ has zLog => sub { } else { $log->with_roles('+Clearable'); - $SIG{USR1} = sub { $log->clear_handle }; + $SIG{USR1} = sub { $log->clear_handle }; } return $log; diff --git a/lib/ZnapZend/Time.pm b/lib/ZnapZend/Time.pm index 8f10d08a..1727c8bd 100644 --- a/lib/ZnapZend/Time.pm +++ b/lib/ZnapZend/Time.pm @@ -98,7 +98,7 @@ my $parseDuration = sub { my ($value, $unit) = $duration =~ /^(\d+)([a-z]+)$/; die "ERROR: cannot parse expression '$duration'\n" unless ($value && $unit); - die "ERROR: unknown unit '$unit'\n" + die "ERROR: unknown unit '$unit'\n" unless exists $self->unitFactors->{$unit}; return $self->$timeToTimestamp($value, $unit); }; From 6d827fcce71e135c5ec7b668f74d3a9cae980355 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:08:35 +0000 Subject: [PATCH 05/34] Dockerfile: ensure prerequisites for self-testing Signed-off-by: Jim Klimov --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 6c29c4a9..8b46da12 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,5 +40,8 @@ CMD [ "znapzend --logto=/dev/stdout" ] ##### Tests FROM builder as test +RUN \ + cpan Devel::Cover + RUN \ ./test.sh From f54e21a9fe5f77da09b9e4a42cb0d67574735d8a Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:10:38 +0000 Subject: [PATCH 06/34] Address Warning: deprecation: please rename 'whitelist' to 'expect' Signed-off-by: Jim Klimov --- .github/workflows/spelling/{whitelist.txt => expect.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/spelling/{whitelist.txt => expect.txt} (100%) diff --git a/.github/workflows/spelling/whitelist.txt b/.github/workflows/spelling/expect.txt similarity index 100% rename from .github/workflows/spelling/whitelist.txt rename to .github/workflows/spelling/expect.txt From 696cf8e5a925f921e388b3acd6ffc051505e1f3e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:11:24 +0000 Subject: [PATCH 07/34] Address Warning: .github/workflows//spelling/patterns.txt: line 1, columns 10-11, Warning - no newline at eof (no-newline-at-eof) --- .github/workflows/spelling/patterns.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spelling/patterns.txt b/.github/workflows/spelling/patterns.txt index 1b75f31e..909cfb67 100644 --- a/.github/workflows/spelling/patterns.txt +++ b/.github/workflows/spelling/patterns.txt @@ -1 +1 @@ -cpanfile.+ \ No newline at end of file +cpanfile.+ From f0ce0b7bb51862c649561594fd40fd5fa2d1d6bd Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:13:02 +0000 Subject: [PATCH 08/34] .github/workflows/spelling/expect.txt: "coprs" is a word in our context Signed-off-by: Jim Klimov --- .github/workflows/spelling/expect.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/spelling/expect.txt b/.github/workflows/spelling/expect.txt index faa600ec..541891b9 100644 --- a/.github/workflows/spelling/expect.txt +++ b/.github/workflows/spelling/expect.txt @@ -105,6 +105,8 @@ conftest conftools CONNEC Consor +copr +coprs copyleft coreutils COUNTFAIL From 8c53d9627aa3ea4e281b18f0838b4c398cc109ad Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:14:11 +0000 Subject: [PATCH 09/34] Typo fix repoen=>reopen Signed-off-by: Jim Klimov --- bin/znapzend | 2 +- man/znapzend.1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/znapzend b/bin/znapzend index e5ce37a7..7aa7ab81 100755 --- a/bin/znapzend +++ b/bin/znapzend @@ -258,7 +258,7 @@ send logs out to either syslog or a logfile. Default is to send logs to B when running daemonized. When running in debug mode, the logs will go to STDERR by default. -When logging to a file, send USR1 to repoen the file handle after log rotation. +When logging to a file, send USR1 to reopen the file handle after log rotation. Examples: diff --git a/man/znapzend.1 b/man/znapzend.1 index 6784a29d..cc18fee0 100644 --- a/man/znapzend.1 +++ b/man/znapzend.1 @@ -224,7 +224,7 @@ send logs out to either syslog or a logfile. Default is to send logs to \&\fBsyslog::daemon\fR when running daemonized. When running in debug mode, the logs will go to \s-1STDERR\s0 by default. .Sp -When logging to a file, send \s-1USR1\s0 to repoen the file handle after log rotation. +When logging to a file, send \s-1USR1\s0 to reopen the file handle after log rotation. .Sp Examples: .Sp From 08f15cd633748354b150ef6c650c1c815948f04e Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:15:14 +0000 Subject: [PATCH 10/34] .github/workflows/spelling/expect.txt: "HAARG" is a word in our context (dev name) Signed-off-by: Jim Klimov --- .github/workflows/spelling/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/spelling/expect.txt b/.github/workflows/spelling/expect.txt index 541891b9..49c0b556 100644 --- a/.github/workflows/spelling/expect.txt +++ b/.github/workflows/spelling/expect.txt @@ -233,6 +233,7 @@ Gregy grep gtar gz +HAARG hadfl Hassler Heitm From e5dec2b925cb9cf349c7ce82bca14bbbd54d2000 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:15:51 +0000 Subject: [PATCH 11/34] Typo fix flaged=>flagged Signed-off-by: Jim Klimov --- lib/ZnapZend/ZFS.pm | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/ZnapZend/ZFS.pm b/lib/ZnapZend/ZFS.pm index c2debab2..320d4a1c 100644 --- a/lib/ZnapZend/ZFS.pm +++ b/lib/ZnapZend/ZFS.pm @@ -387,16 +387,21 @@ sub destroySnapshots { for my $task (@toDestroy){ my ($remote, $dataSetPathAndSnap) = $splitHostDataSet->($task); my ($dataSet, $snapshot) = $splitDataSetSnapshot->($dataSetPathAndSnap); - #tag local snapshots as 'local' so we have a key to build the hash - $remote = $remote || 'local'; - exists $toDestroy{$remote} or $toDestroy{$remote} = {}; - exists $toDestroy{$remote}{$dataSet} or $toDestroy{$remote}{$dataSet} = []; - push @{$toDestroy{$remote}{$dataSet}}, scalar @{$toDestroy{$remote}{$dataSet}} ? $snapshot : "$dataSet\@$snapshot" ; + if (defined ($dataSet)) { + #tag local snapshots as 'local' so we have a key to build the hash + $remote = $remote || 'local'; + exists $toDestroy{$remote} or $toDestroy{$remote} = {}; + exists $toDestroy{$remote}{$dataSet} or $toDestroy{$remote}{$dataSet} = []; + push @{$toDestroy{$remote}{$dataSet}}, scalar @{$toDestroy{$remote}{$dataSet}} ? $snapshot : "$dataSet\@$snapshot" ; + } else { + print STDERR "[D] task='$task' => remote='$remote' dataSetPathAndSnap='$dataSetPathAndSnap' => dataSet='$dataSet' snapshot='$snapshot'\n"; + Mojo::Exception->throw("ERROR: combinedDestroy: failed to parse task='$task', got undefined dataSet and/or snapshot"); + } } for $remote (keys %toDestroy){ for $dataSet (keys %{$toDestroy{$remote}}){ - #check if remote is flaged as 'local'. + #check if remote is flagged as 'local'. my @ssh = $self->$buildRemote($remote ne 'local' ? $remote : undef, [@{$self->priv}, qw(zfs destroy), @recursive, join(',', @{$toDestroy{$remote}{$dataSet}})]); From 5156d5afb1a9c6624537675c3c2c6cd99b0cef03 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:18:29 +0000 Subject: [PATCH 12/34] .github/workflows/spelling/expect.txt: fix spelling for debian/znapzend.links.in Signed-off-by: Jim Klimov --- .github/workflows/spelling/excludes.txt | 1 + .github/workflows/spelling/expect.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/spelling/excludes.txt b/.github/workflows/spelling/excludes.txt index 08eec09c..1f3ea5ea 100644 --- a/.github/workflows/spelling/excludes.txt +++ b/.github/workflows/spelling/excludes.txt @@ -1,3 +1,4 @@ +^debian/znapzend\.links\.in (?:^|/)go\.mod$ (?:^|/)go\.sum$ (?:^|/)package-lock\.json$ diff --git a/.github/workflows/spelling/expect.txt b/.github/workflows/spelling/expect.txt index 49c0b556..81b885fc 100644 --- a/.github/workflows/spelling/expect.txt +++ b/.github/workflows/spelling/expect.txt @@ -131,6 +131,7 @@ daemonization daemonize daemonized DAGOLDEN +DATADIR datadir DBD de'u From c0eb719976ca52b4fcd91ac6183dd0b92ebb6d23 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:21:57 +0000 Subject: [PATCH 13/34] .github/workflows/spelling/excludes.txt: fix filename patterns Signed-off-by: Jim Klimov --- .github/workflows/spelling/excludes.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/spelling/excludes.txt b/.github/workflows/spelling/excludes.txt index 1f3ea5ea..13953dea 100644 --- a/.github/workflows/spelling/excludes.txt +++ b/.github/workflows/spelling/excludes.txt @@ -51,7 +51,8 @@ SUMS$ \.1$ ^AUTHORS$ ^CHANGES$ -^CHANGES.old$ +^CHANGES\.old$ ^excludes\.txt$ ^whitelist\.txt$ +^expect\.txt$ ^\.github/workflows/spelling/ From ff84c33a9440addaa3dcc1fca77f03cca87ae7b8 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:27:57 +0000 Subject: [PATCH 14/34] .github/workflows/spelling.yml: bump action version Signed-off-by: Jim Klimov --- .github/workflows/spelling.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index 1af9e9a4..aeb3114c 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 - - uses: check-spelling/check-spelling@v0.0.19 + - uses: check-spelling/check-spelling@v0.0.22 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} bucket: .github/workflows/ From e66d62f52dd4728903c0ebf676bc713149ebf456 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:31:14 +0000 Subject: [PATCH 15/34] .github/workflows/spelling/expect.txt: drop uppercase and plural variants Signed-off-by: Jim Klimov --- .github/workflows/spelling/expect.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/spelling/expect.txt b/.github/workflows/spelling/expect.txt index 81b885fc..835397ac 100644 --- a/.github/workflows/spelling/expect.txt +++ b/.github/workflows/spelling/expect.txt @@ -55,7 +55,6 @@ Binfo blib Bnoaction Bnot -Bossert bossert bpatsubst Bpidfile @@ -106,7 +105,6 @@ conftools CONNEC Consor copr -coprs copyleft coreutils COUNTFAIL @@ -131,7 +129,6 @@ daemonization daemonize daemonized DAGOLDEN -DATADIR datadir DBD de'u @@ -186,7 +183,6 @@ endif ENTRYPOINT envval envvar -EOL eol errline errmsg @@ -575,7 +571,6 @@ troff tsformat tsnapshot tzoffset -Ubuntu ubuntu uid umask From 541d20f93d53d82f8ae970330d8915c772091d58 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:33:30 +0000 Subject: [PATCH 16/34] Typo fix ZnapZends=>ZnapZend's Signed-off-by: Jim Klimov --- bin/znapzend | 2 +- doc/znapzend.pod | 2 +- man/znapzend.1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/znapzend b/bin/znapzend index 7aa7ab81..ab4334a9 100755 --- a/bin/znapzend +++ b/bin/znapzend @@ -612,7 +612,7 @@ Automatically create a dataset on a destination host if it's not there yet. =item B<--timeWarp>=x -Shift ZnapZends sense of time into the future by x seconds. +Shift ZnapZend's sense of time into the future by x seconds. The practical application if this function is to determine what will happen at some future point in time. This can be useful for testing but also when diff --git a/doc/znapzend.pod b/doc/znapzend.pod index bed3ef19..03afb4bd 100644 --- a/doc/znapzend.pod +++ b/doc/znapzend.pod @@ -444,7 +444,7 @@ Automatically create a dataset on a destination host if it's not there yet. =item B<--timeWarp>=x -Shift ZnapZends sense of time into the future by x seconds. +Shift ZnapZend's sense of time into the future by x seconds. The practical application if this function is to determine what will happen at some future point in time. This can be useful for testing but also when diff --git a/man/znapzend.1 b/man/znapzend.1 index cc18fee0..6cf064a5 100644 --- a/man/znapzend.1 +++ b/man/znapzend.1 @@ -571,7 +571,7 @@ sets the ssh connection timeout (in seconds) Automatically create a dataset on a destination host if it's not there yet. .IP "\fB\-\-timeWarp\fR=x" 4 .IX Item "--timeWarp=x" -Shift ZnapZends sense of time into the future by x seconds. +Shift ZnapZend's sense of time into the future by x seconds. .Sp The practical application if this function is to determine what will happen at some future point in time. This can be useful for testing but also when From 42085d79220e76cddf8b5bba8e39b65fcb8a3d36 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:35:12 +0000 Subject: [PATCH 17/34] .github/workflows/spelling/expect.txt: update with words from perl sources Signed-off-by: Jim Klimov --- .github/workflows/spelling/expect.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/spelling/expect.txt b/.github/workflows/spelling/expect.txt index 835397ac..dfd156b9 100644 --- a/.github/workflows/spelling/expect.txt +++ b/.github/workflows/spelling/expect.txt @@ -8,6 +8,7 @@ admins Affero aix alpinelinux +amd AMTAR anewconfig anotherchild @@ -174,6 +175,7 @@ DTIPS DTRT dummydataset ebuild +ecdsa eckels egrep elif @@ -213,6 +215,7 @@ foced foreach forkcall FQSN +FRONTEND fsf gerczei Getopt @@ -462,6 +465,7 @@ rootfs rootfs'es rpool rr +rsa rsync runlevel runonce @@ -550,6 +554,7 @@ tabledata tarball tardir TARNAME +tattr tempfile templated testsuite @@ -559,14 +564,18 @@ tfilesystem tgerczei thirdparty timeslot +tinherit TION tium +tlowmem TLS tmp tmpdir tobi todo TOTHINK +tpropnames +trecurse troff tsformat tsnapshot @@ -580,6 +589,7 @@ unconfigured UNCONSIDER undef undef'ed +UNICODE unicode uniq unsubscribe From f5a08241a298a124ed3d025dde9009fa62c0aa3d Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:37:36 +0000 Subject: [PATCH 18/34] .github/workflows/spelling/expect.txt: ignore Makefile.am Signed-off-by: Jim Klimov --- .github/workflows/spelling/excludes.txt | 1 + .github/workflows/spelling/expect.txt | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spelling/excludes.txt b/.github/workflows/spelling/excludes.txt index 13953dea..ee9250d4 100644 --- a/.github/workflows/spelling/excludes.txt +++ b/.github/workflows/spelling/excludes.txt @@ -49,6 +49,7 @@ SUMS$ \.yml$ \.zip$ \.1$ +Makefile\.am$ ^AUTHORS$ ^CHANGES$ ^CHANGES\.old$ diff --git a/.github/workflows/spelling/expect.txt b/.github/workflows/spelling/expect.txt index dfd156b9..4bc437d1 100644 --- a/.github/workflows/spelling/expect.txt +++ b/.github/workflows/spelling/expect.txt @@ -589,7 +589,6 @@ unconfigured UNCONSIDER undef undef'ed -UNICODE unicode uniq unsubscribe From 963b95a9e0706a558bc3f82608d4ff3ae6cf1ae7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:41:17 +0000 Subject: [PATCH 19/34] .github/workflows/spelling/excludes.txt: ignore init/org.znapzend.plist.in Signed-off-by: Jim Klimov --- .github/workflows/spelling/excludes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/spelling/excludes.txt b/.github/workflows/spelling/excludes.txt index ee9250d4..3aa334f7 100644 --- a/.github/workflows/spelling/excludes.txt +++ b/.github/workflows/spelling/excludes.txt @@ -1,4 +1,5 @@ ^debian/znapzend\.links\.in +^init/org\.znapzend\.plist\.in$ (?:^|/)go\.mod$ (?:^|/)go\.sum$ (?:^|/)package-lock\.json$ From 5e6cf89c71c875bb3a463a2134371df1c996aa04 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 18:44:58 +0000 Subject: [PATCH 20/34] .github/workflows/spelling/expect.txt: like coprs again (over copr) Signed-off-by: Jim Klimov --- .github/workflows/spelling/expect.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/spelling/expect.txt b/.github/workflows/spelling/expect.txt index 4bc437d1..f10ebbaa 100644 --- a/.github/workflows/spelling/expect.txt +++ b/.github/workflows/spelling/expect.txt @@ -105,7 +105,7 @@ conftest conftools CONNEC Consor -copr +coprs copyleft coreutils COUNTFAIL From 07890236c487e2e6959dca384487967b2a5e08e4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 14:36:30 +0000 Subject: [PATCH 21/34] contrib/test-splitHostDataSet.sh: add test cases for root dataset of a pool [#585] Signed-off-by: Jim Klimov --- contrib/test-splitHostDataSet.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contrib/test-splitHostDataSet.sh b/contrib/test-splitHostDataSet.sh index bbc2eb4c..7d44ef8d 100755 --- a/contrib/test-splitHostDataSet.sh +++ b/contrib/test-splitHostDataSet.sh @@ -31,6 +31,15 @@ for S in \ "user@remotehost:pond/data/set/openindiana-2022:03:15-backup-1" \ "user@remotehost:pond/data/set/openindiana-2022:03:15-backup-1@snap" \ "user@remotehost:pond/data/set/openindiana-2022:03:15-backup-1@snaptime-12:34:56" \ + "rpool" \ + "rpool@snap" \ + "rpool@snaptime-12:34:56" \ + "remotehost:rpool" \ + "remotehost:rpool@snap" \ + "remotehost:rpool@snaptime-12:34:56" \ + "user@remotehost:rpool" \ + "user@remotehost:rpool@snap" \ + "user@remotehost:rpool@snaptime-12:34:56" \ ; do perl -e 'print STDERR "[D] Split \"" . $ARGV[0] . "\" into:\n\t[\"" . join("\", \"", ($ARGV[0] =~ '"$RE"')) . "\"]\n";' "$S" done From ebd9945681e12947574e0504d8d7a05d3bd92097 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 14:38:33 +0000 Subject: [PATCH 22/34] lib/ZnapZend/ZFS.pm: destroySnapshots(): sanity-check for undef $dataSet [#585] Signed-off-by: Jim Klimov --- lib/ZnapZend/ZFS.pm | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/ZnapZend/ZFS.pm b/lib/ZnapZend/ZFS.pm index c2debab2..880277c9 100644 --- a/lib/ZnapZend/ZFS.pm +++ b/lib/ZnapZend/ZFS.pm @@ -368,11 +368,16 @@ sub destroySnapshots { for my $task (@toDestroy){ my ($remote, $dataSetPathAndSnap) = $splitHostDataSet->($task); my ($dataSet, $snapshot) = $splitDataSetSnapshot->($dataSetPathAndSnap); - my @ssh = $self->$buildRemote($remote, [@{$self->priv}, qw(zfs destroy), @recursive, "$dataSet\@$snapshot"]); + if (defined ($dataSet)) { + my @ssh = $self->$buildRemote($remote, [@{$self->priv}, qw(zfs destroy), @recursive, "$dataSet\@$snapshot"]); - print STDERR '# ' . (($self->noaction || $self->nodestroy) ? "WOULD # " : "") . join(' ', @ssh) . "\n" if $self->debug; - system(@ssh) and $destroyError .= "ERROR: cannot destroy snapshot $dataSet\@$snapshot\n" - if !($self->noaction || $self->nodestroy); + print STDERR '# ' . (($self->noaction || $self->nodestroy) ? "WOULD # " : "") . join(' ', @ssh) . "\n" if $self->debug; + system(@ssh) and $destroyError .= "ERROR: cannot destroy snapshot $dataSet\@$snapshot\n" + if !($self->noaction || $self->nodestroy); + } else { + print STDERR "[D] task='$task' => remote='$remote' dataSetPathAndSnap='$dataSetPathAndSnap' => dataSet='$dataSet' snapshot='$snapshot'\n"; + Mojo::Exception->throw("ERROR: oracleMode destroy: failed to parse task='$task', got undefined dataSet and/or snapshot"); + } } #remove trailing \n chomp $destroyError; @@ -387,11 +392,16 @@ sub destroySnapshots { for my $task (@toDestroy){ my ($remote, $dataSetPathAndSnap) = $splitHostDataSet->($task); my ($dataSet, $snapshot) = $splitDataSetSnapshot->($dataSetPathAndSnap); - #tag local snapshots as 'local' so we have a key to build the hash - $remote = $remote || 'local'; - exists $toDestroy{$remote} or $toDestroy{$remote} = {}; - exists $toDestroy{$remote}{$dataSet} or $toDestroy{$remote}{$dataSet} = []; - push @{$toDestroy{$remote}{$dataSet}}, scalar @{$toDestroy{$remote}{$dataSet}} ? $snapshot : "$dataSet\@$snapshot" ; + if (defined ($dataSet)) { + #tag local snapshots as 'local' so we have a key to build the hash + $remote = $remote || 'local'; + exists $toDestroy{$remote} or $toDestroy{$remote} = {}; + exists $toDestroy{$remote}{$dataSet} or $toDestroy{$remote}{$dataSet} = []; + push @{$toDestroy{$remote}{$dataSet}}, scalar @{$toDestroy{$remote}{$dataSet}} ? $snapshot : "$dataSet\@$snapshot" ; + } else { + print STDERR "[D] task='$task' => remote='$remote' dataSetPathAndSnap='$dataSetPathAndSnap' => dataSet='$dataSet' snapshot='$snapshot'\n"; + Mojo::Exception->throw("ERROR: combinedDestroy: failed to parse task='$task', got undefined dataSet and/or snapshot"); + } } for $remote (keys %toDestroy){ From f63d604b1192308db87fe5896a7760a18651a013 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 16:11:46 +0000 Subject: [PATCH 23/34] t/znapzend-lib-splitter.t: add a pure-perl test for splitHostDataSet() and splitDataSetSnapshot() [#585] Signed-off-by: Jim Klimov --- t/znapzend-lib-splitter.t | 139 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100755 t/znapzend-lib-splitter.t diff --git a/t/znapzend-lib-splitter.t b/t/znapzend-lib-splitter.t new file mode 100755 index 00000000..ddb60703 --- /dev/null +++ b/t/znapzend-lib-splitter.t @@ -0,0 +1,139 @@ +#!/usr/bin/env perl + +# Test library methods around [[user@]host:]dataset[@snap] splitting +# since there are many use-cases and combinations to take care of. +# We do so below by constructing "task" strings from components we +# know to be a dataset name and some defined (or not) remote spec +# and/or snapshot name, and deconstructing it back with the class +# method. +# +# Copyright (C) 2024 by Jim Klimov + +use strict; +use warnings; + +# Avoid issues if we monkey-patch included sources in a wrong way +use warnings FATAL => 'recursion'; + +use FindBin; +$ENV{PATH} = $FindBin::RealBin.':'.$ENV{PATH}; +my $buildDir; + +BEGIN { + $buildDir = shift @ARGV // $FindBin::RealBin."/../"; +} + +# PERL5LIB +use lib "$FindBin::RealBin/../lib"; +use lib "$buildDir/thirdparty/lib/perl5"; +#place bin path to lib so it is stored in @INC +use lib "$FindBin::RealBin/../bin"; + +unshift @INC, sub { + my (undef, $filename) = @_; + return () if $filename !~ /ZnapZend|ZFS|znapzend/; + if (my $found = (grep { -e $_ } map { "$_/$filename" } grep { !ref } @INC)[0] ) { + local $/ = undef; + open my $fh, '<', $found or die("Can't read module file $found\n"); + my $module_text = <$fh>; + close $fh; + + # define everything in a sub, so Devel::Cover will DTRT + # NB this introduces no extra linefeeds so D::C's line numbers + # in reports match the file on disk + $module_text =~ s/(.*?package\s+\S+)(.*)__END__/$1sub classWrapper {$2} classWrapper();/s; + + # unhide private methods to avoid "Variable will not stay shared" + # warnings that appear due to change of applicable scoping rules + # Note: not '\s*' in the start of string, to avoid matching and + # removing blank lines before the private sub definitions. + $module_text =~ s/^[ \t]*my\s+(\S+\s*=\s*sub.*)$/our $1/gm; + + # For this test, also strip dollars from tested private method + # names so we can actually call them from the test context. + if($filename =~ /ZFS/) { + $module_text =~ s/^1;$/### Quick drop-in\nsub splitDataSetSnapshot {return \$splitDataSetSnapshot->(\$_[1]);}\nsub splitHostDataSet {return \$splitHostDataSet->(\$_[1]);}\n\n1;\n/gm; + } + + if(defined($ENV{DEBUG_ZNAPZEND_SELFTEST_REWRITE})) { + open(my $fhp, '>', $found . '.selftest-rewritten') or warn "Could not open " . $found . '.selftest-rewritten'; + if ($fhp) { print $fhp $module_text ; close $fhp; } + } + + # filehandle on the scalar + open $fh, '<', \$module_text; + + # and put it into %INC too so that it looks like we loaded the code + # from the file directly + $INC{$filename} = $found; + + warn ("Imported '$found'"); + return $fh; + } + else { + return (); + } +}; + +sub stringify { + my $s = shift; + return $s if defined($s); + return ""; +} + +sub printTaskReport { + print STDERR "[D] task='" . stringify($_[0]) . + "' => remote='" . stringify($_[1]) . + "' dataSetPathAndSnap='" . stringify($_[2]) . + "' => dataSet='" . stringify($_[3]) . + "' snapshot='" . stringify($_[4]) . "'\n"; +} + +use Test::More; + +use_ok 'ZnapZend::ZFS'; + +my $zZFS = ZnapZend::ZFS->new(); + +is (ref $zZFS,'ZnapZend::ZFS', 'instantiation of ZFS'); + +for my $r (qw(undef hostname username@hostname)) { + for my $d (qw(poolrootfs rpool/dataset rpool/dataset:with-colon)) { + for my $s (qw(undef snapname snap-1 snap-2:3 snap-12:35:00)) { + #EXAMPLE# my $task = 'user@host:dataset@snapname'; + + my $task = ''; + if ($r ne "undef") { $task .= $r . ':'; } + $task .= $d; + if ($s ne "undef") { $task .= '@' . $s; } + + # Decode it back, see if we can + # Note the methods are externalized from the module for the test by patcher above + my ($remote, $dataSetPathAndSnap) = $zZFS->splitHostDataSet($task); + my ($dataSet, $snapshot) = $zZFS->splitDataSetSnapshot($dataSetPathAndSnap); + #print STDERR "[D] task='$task' => remote='$remote' dataSetPathAndSnap='$dataSetPathAndSnap' => dataSet='$dataSet' snapshot='$snapshot'\n"; + printTaskReport($task, $remote, $dataSetPathAndSnap, $dataSet, $snapshot); + + is (defined ($dataSet), 1, "dataSet should always be defined after parsing"); + is (($dataSet eq $d), 1, "dataSet has expected value after parsing"); + + if ($r ne "undef") { + is (defined ($remote), 1, "remote should be defined after parsing this test case"); + is (($remote eq $r), 1, "remote has expected value after parsing"); + } else { + isnt (defined ($remote), 1, "remote should not be defined after parsing this test case"); + } + + if ($s ne "undef") { + is (defined ($snapshot), 1, "snapshot should be defined after parsing this test case"); + is (($snapshot eq $s), 1, "snapshot has expected value after parsing"); + } else { + isnt (defined ($snapshot), 1, "snapshot should not be defined after parsing this test case"); + } + } + } +} + +done_testing; + +1; From 84dec8d0c292f42cdf193065c11e973204859e85 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 16:20:37 +0000 Subject: [PATCH 24/34] lib/ZnapZend/ZFS.pm: splitDataSetSnapshot(): refine to check if argument is actually a "dataset@snapname", return an undef snapname in the array if not [#585] Signed-off-by: Jim Klimov --- lib/ZnapZend/ZFS.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/ZnapZend/ZFS.pm b/lib/ZnapZend/ZFS.pm index 880277c9..fcfed262 100644 --- a/lib/ZnapZend/ZFS.pm +++ b/lib/ZnapZend/ZFS.pm @@ -54,7 +54,12 @@ my $splitHostDataSet = sub { }; my $splitDataSetSnapshot = sub { - return ($_[0] =~ /^([^\@]+)\@([^\@]+)$/); + my $count = ($_[0] =~ tr/@//); + if ($count > 0) { + return ($_[0] =~ /^([^\@]+)\@([^\@]+)$/); + } else { + return ($_[0], undef); + } }; my $shellQuote = sub { From 3c7d3007cb5841e85d2418cb184896c9859fe0c7 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 16:45:19 +0000 Subject: [PATCH 25/34] test.sh: call t/znapzend-lib-splitter.t (but ignore faults for now) [#585] Signed-off-by: Jim Klimov --- test.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test.sh b/test.sh index d1fe70ee..d2529457 100755 --- a/test.sh +++ b/test.sh @@ -8,9 +8,13 @@ perl -I./thirdparty/lib/perl5 \ perl -I./thirdparty/lib/perl5 \ -MDevel::Cover=+ignore,thirdparty ./t/znapzend-daemonize.t perl -I./thirdparty/lib/perl5 \ - -MDevel::Cover=+ignore,thirdparty ./t/znapzendzetup.t + -MDevel::Cover=+ignore,thirdparty ./t/znapzendzetup.t perl -I./thirdparty/lib/perl5 \ -MDevel::Cover=+ignore,thirdparty ./t/znapzendztatz.t perl -I./thirdparty/lib/perl5 \ -MDevel::Cover=+ignore,thirdparty ./t/autoscrub.t +# Currently prone to failure with certain edge cases, +# so ignoring the result (fixes are investigated): +perl -I./thirdparty/lib/perl5 \ + -MDevel::Cover=+ignore,thirdparty ./t/znapzend-lib-splitter.t || echo "FAILURE Currently ignored" From 83186d709b6cab660106faee3d11a12eabde8263 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 11:09:05 +0000 Subject: [PATCH 26/34] znapzendzetup: doc typo fix READNE=>README Signed-off-by: Jim Klimov --- bin/znapzendzetup | 2 +- man/znapzendzetup.1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/znapzendzetup b/bin/znapzendzetup index fa319e66..cedcb885 100755 --- a/bin/znapzendzetup +++ b/bin/znapzendzetup @@ -545,7 +545,7 @@ and where 'command' and its unique options is one of the following: NOTE: If you specify [user@]host:dataset for remote replication over SSH, make use of ~/.ssh/config for any advanced options - (custom ports, keys to use, etc.) See znapzend READNE for ideas. + (custom ports, keys to use, etc.) See znapzend README for ideas. delete [--dst=key] diff --git a/man/znapzendzetup.1 b/man/znapzendzetup.1 index 8c8352f1..334ac853 100644 --- a/man/znapzendzetup.1 +++ b/man/znapzendzetup.1 @@ -168,7 +168,7 @@ and where 'command' and its unique options is one of the following: \& \& NOTE: If you specify [user@]host:dataset for remote replication \& over SSH, make use of ~/.ssh/config for any advanced options -\& (custom ports, keys to use, etc.) See znapzend READNE for ideas. +\& (custom ports, keys to use, etc.) See znapzend README for ideas. \& \& delete [\-\-dst=key] \& From 0b65d9b906bcc86668c1d1c976d633b9fea71958 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 11:11:09 +0000 Subject: [PATCH 27/34] Fix some trailing whitespaces Signed-off-by: Jim Klimov --- lib/ZnapZend.pm | 2 +- lib/ZnapZend/Time.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ZnapZend.pm b/lib/ZnapZend.pm index 128d8fa2..61478920 100644 --- a/lib/ZnapZend.pm +++ b/lib/ZnapZend.pm @@ -113,7 +113,7 @@ has zLog => sub { } else { $log->with_roles('+Clearable'); - $SIG{USR1} = sub { $log->clear_handle }; + $SIG{USR1} = sub { $log->clear_handle }; } return $log; diff --git a/lib/ZnapZend/Time.pm b/lib/ZnapZend/Time.pm index 8f10d08a..1727c8bd 100644 --- a/lib/ZnapZend/Time.pm +++ b/lib/ZnapZend/Time.pm @@ -98,7 +98,7 @@ my $parseDuration = sub { my ($value, $unit) = $duration =~ /^(\d+)([a-z]+)$/; die "ERROR: cannot parse expression '$duration'\n" unless ($value && $unit); - die "ERROR: unknown unit '$unit'\n" + die "ERROR: unknown unit '$unit'\n" unless exists $self->unitFactors->{$unit}; return $self->$timeToTimestamp($value, $unit); }; From b911de288bca3f7c31673c20fe348cbc0ad6d5b4 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 20:22:57 +0000 Subject: [PATCH 28/34] ZFS.pm: extend splitHostDataSet with edge use-case handling [#585] Signed-off-by: Jim Klimov --- lib/ZnapZend/ZFS.pm | 59 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/lib/ZnapZend/ZFS.pm b/lib/ZnapZend/ZFS.pm index c81f5a81..beefeb58 100644 --- a/lib/ZnapZend/ZFS.pm +++ b/lib/ZnapZend/ZFS.pm @@ -37,11 +37,64 @@ has priv => sub { my $self = shift; [$self->rootExec ? split(/ /, $se ### private functions ### my $splitHostDataSet = sub { + # When troubleshooting, set to 1: + my $debugHere = 0; + #my $debugHere = 1; + # See also https://github.com/oetiker/znapzend/issues/585 # If there are further bugs in the regex, comment away the # next implementation line and fall through to verbosely - # debugging code below to try and iterate a fix: - return ($_[0] =~ /^(?:([^:\/]+):)?([^@\s]+|[^@\s]+\@[^@\s]+)$/); + # debugging code below to try and iterate a fix. + # In the "if" clause below we separate the regexes for the + # use-case where we have two "@" characters: + # user@host:dataset@snap + # from having zero or one "@" characters: + # host:dataset + # host:dataset@snap + # user@host:dataset + # and note that dataset may lack "/" chars (root dataset + # of a pool), and (non-root?) dataset and snapshot names + # may have ":" chars of their own, e.g. + # ...@znapzend-auto-2024-01-08T10:22:13Z + # pond/export/vm-1:2 + my $count = ($_[0] =~ tr/@//); + if ($count > 1) { + #return ($_[0] =~ /^(?:([^:\/]+):)?([^@\s]+|[^@\s]+\@[^@\s]+)$/); + print STDERR "[D] splitHostDataSet: use-case: Two or more \@\n" if $debugHere; + return ($_[0] =~ /^(?:([^:\/]+):)?([^@\s]+\@[^@\s]+)$/); + } else { + # Zero or one "@" + # Either "[host:]dataset[@snap]" or "[user@]host:dataset" + print STDERR "[D] splitHostDataSet: use-case: zero or one \@: " if $debugHere; + if ($_[0] =~ /^[^:@\/]+:/) { + # Got a colon before any "@" (if at all present): + # assume host:... (no "user@") + print STDERR "colon before any frog\n" if $debugHere; + return ($_[0] =~ /^(?:([^:@\/]+):)([^@\s]+|[^@\s]+\@[^@\s]+)$/); + } elsif ($_[0] =~ /^[^:@\/]+\//) { + # Slashes are not anticipated in user or host names, so: + # assume poolroot/... + print STDERR "slashes before colon\n" if $debugHere; + return (undef, $_[0]); + } elsif ($_[0] =~ /^[^:\/@]+@[^:@\/]+$/) { + # X@Y without colons or slashes: + # assume poolroot@snap + print STDERR "no colon, no slash, with frog\n" if $debugHere; + return (undef, $_[0]); + } elsif ($_[0] =~ /^[^:@\/]+@[^:@\/]+:.*\//) { + # X@Y:Z/W - snapshot names can not have slashes + # assume X@Y = user@host, Z/W = rootds/ds...[@snap] + print STDERR "X\@Y:Z/W without slashes in X and Y\n" if $debugHere; + return ($_[0] =~ /^([^:\/]+):(.+)$/); + } elsif ($_[0] =~ /^[^:@\/]+@[^:@\/]+:/) { + # X@Y:Z without slashes in X and Y - may be: + # either user@host:... + # or e.g. rootds@snap-12:34:56 + print STDERR "X\@Y:Z without slashes in X and Y\n" if $debugHere; + } + print STDERR "other\n" if $debugHere; + return ($_[0] =~ /^(?:([^:@\/]+):)?([^@\s]+|[^@\s]+\@[^@\s]+)$/); + } my @return; ###push @return, ($_[0] =~ /^(?:(.+)\s)?([^\s]+)$/); @@ -49,7 +102,7 @@ my $splitHostDataSet = sub { push @return, ($_[0] =~ /^(?:([^:\/]+):)?([^@\s]+|[^@\s]+\@[^@\s]+)$/); # Note: Claims `Use of uninitialized value $return[0]...` when there # is no remote host portion matched, so using a map to stringify: - print STDERR "[D] Split '" . $_[0] . "' into: [" . join(", ", map { defined ? "'$_'" : '' } @return) . "]\n";# if $self->debug; + print STDERR "[D] Split '" . $_[0] . "' into: [" . join(", ", map { defined ? "'$_'" : '' } @return) . "]\n" if $debugHere; return @return; }; From 09ef18dc66a80107e0bd966a3516122289ef9b88 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 20:24:14 +0000 Subject: [PATCH 29/34] t/znapzend-lib-splitter.t: declare that we prefer "poolrootfs@snap-2:3" over "username@hostname:poolrootfs" among indistinguishable patterns [#585] --- t/znapzend-lib-splitter.t | 42 ++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/t/znapzend-lib-splitter.t b/t/znapzend-lib-splitter.t index ddb60703..1cfb3d8c 100755 --- a/t/znapzend-lib-splitter.t +++ b/t/znapzend-lib-splitter.t @@ -97,6 +97,16 @@ my $zZFS = ZnapZend::ZFS->new(); is (ref $zZFS,'ZnapZend::ZFS', 'instantiation of ZFS'); +# NOTE: In absence of any hints we can not reliably discern below +# a task='poolrootfs@snap-2:3' +# vs. task='username@hostname:poolrootfs' +# which one is a local pool's root dataset with a funny but legal +# snapshot name, and which one is a remote user@host spec with a +# remote pool's root dataset. For practical purposes, we proclaim +# preference for the former: we are more likely to look at funny +# local snapshot names, than to back up to (or otherwise care about) +# remote pools' ROOT datasets. + for my $r (qw(undef hostname username@hostname)) { for my $d (qw(poolrootfs rpool/dataset rpool/dataset:with-colon)) { for my $s (qw(undef snapname snap-1 snap-2:3 snap-12:35:00)) { @@ -115,20 +125,28 @@ for my $r (qw(undef hostname username@hostname)) { printTaskReport($task, $remote, $dataSetPathAndSnap, $dataSet, $snapshot); is (defined ($dataSet), 1, "dataSet should always be defined after parsing"); - is (($dataSet eq $d), 1, "dataSet has expected value after parsing"); - - if ($r ne "undef") { - is (defined ($remote), 1, "remote should be defined after parsing this test case"); - is (($remote eq $r), 1, "remote has expected value after parsing"); - } else { - isnt (defined ($remote), 1, "remote should not be defined after parsing this test case"); - } - if ($s ne "undef") { - is (defined ($snapshot), 1, "snapshot should be defined after parsing this test case"); - is (($snapshot eq $s), 1, "snapshot has expected value after parsing"); + # See big comment above: + if ($task eq 'username@hostname:poolrootfs') { + isnt (defined ($remote), 1, "remote should BOGUSLY be not defined after parsing for this exceptional test case"); + is (($dataSet eq "username"), 1, "dataSet has expected BOGUS value after parsing for this exceptional test case"); + is (($snapshot eq "hostname:poolrootfs"), 1, "snapshot has expected BOGUS value after parsing for this exceptional test case"); } else { - isnt (defined ($snapshot), 1, "snapshot should not be defined after parsing this test case"); + is (($dataSet eq $d), 1, "dataSet has expected value after parsing"); + + if ($r ne "undef") { + is (defined ($remote), 1, "remote should be defined after parsing this test case"); + is (($remote eq $r), 1, "remote has expected value after parsing"); + } else { + isnt (defined ($remote), 1, "remote should not be defined after parsing this test case"); + } + + if ($s ne "undef") { + is (defined ($snapshot), 1, "snapshot should be defined after parsing this test case"); + is (($snapshot eq $s), 1, "snapshot has expected value after parsing"); + } else { + isnt (defined ($snapshot), 1, "snapshot should not be defined after parsing this test case"); + } } } } From 05d72c4c2db667814fe16855abdf204d3c760a27 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 20:57:10 +0000 Subject: [PATCH 30/34] t/znapzend-lib-splitter.t: extend with ZnapZend::Config->splitHostDataSet() implementation test [#585] Signed-off-by: Jim Klimov --- t/znapzend-lib-splitter.t | 57 +++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/t/znapzend-lib-splitter.t b/t/znapzend-lib-splitter.t index 1cfb3d8c..4c8ac4c5 100755 --- a/t/znapzend-lib-splitter.t +++ b/t/znapzend-lib-splitter.t @@ -31,7 +31,7 @@ use lib "$FindBin::RealBin/../bin"; unshift @INC, sub { my (undef, $filename) = @_; - return () if $filename !~ /ZnapZend|ZFS|znapzend/; + return () if $filename !~ /ZnapZend|ZnapZend.Config|ZFS|znapzend/; if (my $found = (grep { -e $_ } map { "$_/$filename" } grep { !ref } @INC)[0] ) { local $/ = undef; open my $fh, '<', $found or die("Can't read module file $found\n"); @@ -53,6 +53,8 @@ unshift @INC, sub { # names so we can actually call them from the test context. if($filename =~ /ZFS/) { $module_text =~ s/^1;$/### Quick drop-in\nsub splitDataSetSnapshot {return \$splitDataSetSnapshot->(\$_[1]);}\nsub splitHostDataSet {return \$splitHostDataSet->(\$_[1]);}\n\n1;\n/gm; + } elsif($filename =~ /Config/) { + $module_text =~ s/^1;$/### Quick drop-in\nsub splitHostDataSet {return \$splitHostDataSet->(\$_[1]);}\n\n1;\n/gm; } if(defined($ENV{DEBUG_ZNAPZEND_SELFTEST_REWRITE})) { @@ -81,8 +83,14 @@ sub stringify { return ""; } -sub printTaskReport { - print STDERR "[D] task='" . stringify($_[0]) . +sub printTaskReportCFG { + print STDERR "[D:zCFG] task='" . stringify($_[0]) . + "' => remote='" . stringify($_[1]) . + "' dataSet='" . stringify($_[2]) . "'\n"; +} + +sub printTaskReportZFS { + print STDERR "[D:zZFS] task='" . stringify($_[0]) . "' => remote='" . stringify($_[1]) . "' dataSetPathAndSnap='" . stringify($_[2]) . "' => dataSet='" . stringify($_[3]) . @@ -121,8 +129,7 @@ for my $r (qw(undef hostname username@hostname)) { # Note the methods are externalized from the module for the test by patcher above my ($remote, $dataSetPathAndSnap) = $zZFS->splitHostDataSet($task); my ($dataSet, $snapshot) = $zZFS->splitDataSetSnapshot($dataSetPathAndSnap); - #print STDERR "[D] task='$task' => remote='$remote' dataSetPathAndSnap='$dataSetPathAndSnap' => dataSet='$dataSet' snapshot='$snapshot'\n"; - printTaskReport($task, $remote, $dataSetPathAndSnap, $dataSet, $snapshot); + printTaskReportZFS($task, $remote, $dataSetPathAndSnap, $dataSet, $snapshot); is (defined ($dataSet), 1, "dataSet should always be defined after parsing"); @@ -152,6 +159,46 @@ for my $r (qw(undef hostname username@hostname)) { } } +# This module has its own definition of splitHostDataSet for +# znapzendzetup property parsing - without snapshot parts +use_ok 'ZnapZend::Config'; + +my $zCFG = ZnapZend::Config->new(); + +is (ref $zCFG,'ZnapZend::Config', 'instantiation of Config'); + +for my $r (qw(undef hostname username@hostname)) { + for my $d (qw(poolrootfs rpool/dataset rpool/dataset:with-colon)) { + #EXAMPLE# my $task = 'user@host:dataset'; + + my $task = ''; + if ($r ne "undef") { $task .= $r . ':'; } + $task .= $d; + + # Decode it back, see if we can + # Note the methods are externalized from the module for the test by patcher above + my ($remote, $dataSet) = $zCFG->splitHostDataSet($task); + printTaskReportCFG($task, $remote, $dataSet); + + is (defined ($dataSet), 1, "dataSet should always be defined after parsing"); + + # See big comment above: +# if ($task eq 'username@hostname:poolrootfs') { +# isnt (defined ($remote), 1, "remote should BOGUSLY be not defined after parsing for this exceptional test case"); +# is (($dataSet eq "username"), 1, "dataSet has expected BOGUS value after parsing for this exceptional test case"); +# } else { + is (($dataSet eq $d), 1, "dataSet has expected value after parsing"); + + if ($r ne "undef") { + is (defined ($remote), 1, "remote should be defined after parsing this test case"); + is (($remote eq $r), 1, "remote has expected value after parsing"); + } else { + isnt (defined ($remote), 1, "remote should not be defined after parsing this test case"); + } + } +# } +} + done_testing; 1; From d0947fbeca6e063fc1f93a802d66aed263ace767 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 20:57:33 +0000 Subject: [PATCH 31/34] ZnapZend::Config->splitHostDataSet() implementation: fix like in #585 Signed-off-by: Jim Klimov --- lib/ZnapZend/Config.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ZnapZend/Config.pm b/lib/ZnapZend/Config.pm index 70b8b0f9..2a9eed57 100644 --- a/lib/ZnapZend/Config.pm +++ b/lib/ZnapZend/Config.pm @@ -47,7 +47,9 @@ has backupSets => sub { [] }; ### private functions ### my $splitHostDataSet = sub { - return ($_[0] =~ /^(?:([^:\/]+):)?([^:]+|[^:@]+\@.+)$/); + #return ($_[0] =~ /^(?:([^:\/]+):)?([^:]+|[^:@]+\@.+)$/); + # See https://github.com/oetiker/znapzend/pull/585 + return ($_[0] =~ /^(?:([^:\/]+):)?([^@\s]+|[^@\s]+\@[^@\s]+)$/); }; ### private methods ### From 62c2a4b0396c313663a5dd7f98b24d54639094c6 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 20:58:17 +0000 Subject: [PATCH 32/34] test.sh: finalize z/znapzend-lib-splitter.t for ZnapZend::ZFS splitHostDataSet() and splitDataSetSnapshot() and ZnapZend::Config splitHostDataSet() implementations --- test.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test.sh b/test.sh index d2529457..23f32bf3 100755 --- a/test.sh +++ b/test.sh @@ -13,8 +13,5 @@ perl -I./thirdparty/lib/perl5 \ -MDevel::Cover=+ignore,thirdparty ./t/znapzendztatz.t perl -I./thirdparty/lib/perl5 \ -MDevel::Cover=+ignore,thirdparty ./t/autoscrub.t - -# Currently prone to failure with certain edge cases, -# so ignoring the result (fixes are investigated): perl -I./thirdparty/lib/perl5 \ - -MDevel::Cover=+ignore,thirdparty ./t/znapzend-lib-splitter.t || echo "FAILURE Currently ignored" + -MDevel::Cover=+ignore,thirdparty ./t/znapzend-lib-splitter.t From a179154201b976b0a44bdc7a08ab856c091e14f9 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Mon, 8 Jan 2024 21:11:16 +0000 Subject: [PATCH 33/34] Reword stuff to satisfy spell-checker Signed-off-by: Jim Klimov --- .github/workflows/spelling/expect.txt | 3 +++ lib/ZnapZend/ZFS.pm | 10 +++++++-- t/znapzend-lib-splitter.t | 29 ++++++++++----------------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.github/workflows/spelling/expect.txt b/.github/workflows/spelling/expect.txt index f10ebbaa..d5deb3a9 100644 --- a/.github/workflows/spelling/expect.txt +++ b/.github/workflows/spelling/expect.txt @@ -383,6 +383,7 @@ notest NOTMAKE nroff NSTIPS +nsub nvpool nytprof OBJC @@ -419,6 +420,7 @@ plaintar plist plugin png +poolrootfs populat posix prebuilt @@ -461,6 +463,7 @@ rmdir rmprog rmtmp ron +rootds rootfs rootfs'es rpool diff --git a/lib/ZnapZend/ZFS.pm b/lib/ZnapZend/ZFS.pm index beefeb58..f47aa9ce 100644 --- a/lib/ZnapZend/ZFS.pm +++ b/lib/ZnapZend/ZFS.pm @@ -45,8 +45,8 @@ my $splitHostDataSet = sub { # If there are further bugs in the regex, comment away the # next implementation line and fall through to verbosely # debugging code below to try and iterate a fix. - # In the "if" clause below we separate the regexes for the - # use-case where we have two "@" characters: + # In the "if" clause below we separate the regular expressions + # for the use-case where we have two "@" characters: # user@host:dataset@snap # from having zero or one "@" characters: # host:dataset @@ -57,6 +57,12 @@ my $splitHostDataSet = sub { # may have ":" chars of their own, e.g. # ...@znapzend-auto-2024-01-08T10:22:13Z # pond/export/vm-1:2 + # Also note that we can not discern "X@Y:Z" strings by pattern + # alone - are they a remote "user@host:rootds" or a local + # "rootds@funny:snapname"? For practical purposes, we proclaim + # preference for the former: we are more likely to look at funny + # local snapshot names, than to back up to (or otherwise care + # about) remote pools' ROOT datasets. my $count = ($_[0] =~ tr/@//); if ($count > 1) { #return ($_[0] =~ /^(?:([^:\/]+):)?([^@\s]+|[^@\s]+\@[^@\s]+)$/); diff --git a/t/znapzend-lib-splitter.t b/t/znapzend-lib-splitter.t index 4c8ac4c5..c8f9575f 100755 --- a/t/znapzend-lib-splitter.t +++ b/t/znapzend-lib-splitter.t @@ -4,7 +4,7 @@ # since there are many use-cases and combinations to take care of. # We do so below by constructing "task" strings from components we # know to be a dataset name and some defined (or not) remote spec -# and/or snapshot name, and deconstructing it back with the class +# and/or snapshot name, and de-constructing it back with the class # method. # # Copyright (C) 2024 by Jim Klimov @@ -135,9 +135,9 @@ for my $r (qw(undef hostname username@hostname)) { # See big comment above: if ($task eq 'username@hostname:poolrootfs') { - isnt (defined ($remote), 1, "remote should BOGUSLY be not defined after parsing for this exceptional test case"); - is (($dataSet eq "username"), 1, "dataSet has expected BOGUS value after parsing for this exceptional test case"); - is (($snapshot eq "hostname:poolrootfs"), 1, "snapshot has expected BOGUS value after parsing for this exceptional test case"); + isnt (defined ($remote), 1, "BOGUS exceptional test case: remote should be not defined after parsing for this exceptional test case"); + is (($dataSet eq "username"), 1, "BOGUS exceptional test case: dataSet has expected BOGUS value after parsing for this exceptional test case"); + is (($snapshot eq "hostname:poolrootfs"), 1, "BOGUS exceptional test case: snapshot has expected BOGUS value after parsing for this exceptional test case"); } else { is (($dataSet eq $d), 1, "dataSet has expected value after parsing"); @@ -181,22 +181,15 @@ for my $r (qw(undef hostname username@hostname)) { printTaskReportCFG($task, $remote, $dataSet); is (defined ($dataSet), 1, "dataSet should always be defined after parsing"); + is (($dataSet eq $d), 1, "dataSet has expected value after parsing"); - # See big comment above: -# if ($task eq 'username@hostname:poolrootfs') { -# isnt (defined ($remote), 1, "remote should BOGUSLY be not defined after parsing for this exceptional test case"); -# is (($dataSet eq "username"), 1, "dataSet has expected BOGUS value after parsing for this exceptional test case"); -# } else { - is (($dataSet eq $d), 1, "dataSet has expected value after parsing"); - - if ($r ne "undef") { - is (defined ($remote), 1, "remote should be defined after parsing this test case"); - is (($remote eq $r), 1, "remote has expected value after parsing"); - } else { - isnt (defined ($remote), 1, "remote should not be defined after parsing this test case"); - } + if ($r ne "undef") { + is (defined ($remote), 1, "remote should be defined after parsing this test case"); + is (($remote eq $r), 1, "remote has expected value after parsing"); + } else { + isnt (defined ($remote), 1, "remote should not be defined after parsing this test case"); } -# } + } } done_testing; From 80ea4c49a60cb190dc223676d16df3cab23ba73f Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 10 Jan 2024 17:32:46 +0000 Subject: [PATCH 34/34] CHANGES: update for summary of recent pull requests Signed-off-by: Jim Klimov --- CHANGES | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES b/CHANGES index 1bd99dcc..39dab665 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,11 @@ +znapzend (0.21.3) unstable; urgency=medium + + * Maintenance release: refine splitting of [[user@]host:]dataset[:with-colons][@snap[:with-colons]] strings to work for the realistic majority of use-cases; fix back support of pool root dataset in such spec + * Update self-tests with verification that [[user@]host:]dataset[:with-colons][@snap[:with-colons]] string decoding yields expected results + * Fixed CI recipes and contents for spell-checker + + -- Jim Klimov Tue, 9 Jan 2024 13:42:28 +0100 + znapzend (0.21.2) unstable; urgency=medium * Maintenance release: Automate .deb package builds