| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
populate's tests are in pkgs/populate/tests, independent of krops.
They run the program, with the sources as JSON from populate's Nix
library, and target strings for the remote targets, leaving the user
and whether the target is local to populate, so they cover its command
line too. Phases expecting populate to refuse its arguments, e.g. a
source name or a target path that can't be used, expect the program's
refusal, with refused, the message, since nothing checks them during
evaluation there.
builder.nix is what builds the scenarios, which krops' tests use as
well, with a function running the phases, and whether that checks the
arguments during evaluation. vm.nix takes a module for both machines,
for what else the scenarios run, and fixtures.nix and smoke.nix are
what both use of the fixtures and the smoke test.
krops' tests are deploy, writeDeploy's arguments to nixos-rebuild, with
the stand-in for nixos-rebuild in the module, and populate, populate's
scenarios that expect refusals, which pkgs.populate makes evaluation
fail with, and those of populate's smoke test, all through
pkgs.populate with target attribute sets as mkTarget returns them.
-A only selects from all of populate's scenarios and krops' own.
|
| |
|
|
|
|
|
|
| |
A phase's unexpected lists what populate is expected not to print,
whether it succeeds or not. notdir expects populate not to go on
locking after it fails to find the URL of a source drawn from, which
would lock the current directory, the login directory on a remote
target, copying it into the store.
|
| |
|
|
|
|
|
|
|
|
|
| |
The shell implementation runs under set -e, so it exits with the status
of the command that failed, e.g. 255 if ssh can't reach the target,
which wrappers retrying on connection failures rely on, or 128 if git
fetch fails. The program does the same, instead of exiting with 1, and
with 128 plus the signal for a command a signal ended.
A phase's expect.status is the exit status it expects populate to fail
with; git-status checks git fetch's.
|
| |
|
|
|
|
|
|
|
|
| |
The test driver runs commands in a shell whose input is the console it
talks through, /dev/hvc0, which is a terminal. ssh -t, which
writeDeploy's allocateTTY passes for remote targets, allocates a terminal
then, and reads the console, so the deploy-*-allocateTTY scenarios of
remote and sudo targets hang. Populate and writeDeploy read nothing, so
their input is /dev/null, and ssh allocates no terminal, as these
scenarios expect.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Several checks would pass with the feature they cover broken, or cover
it only by chance:
- Phases can state what a succeeding populate prints. That covers the
warnings of insecureLinks, the lines pruning prints, the one about a
replaced symlink, and what rsync logs about each item.
- The insecureLinks scenarios take their expectations from the version of
rsync, so with an rsync older than 3.5.0 they pass without the check
ever running. New tests, rsync-3-4-1 and rsync-3-5-0, run them with
these versions, which tests/rsync.nix builds from their release
tarballs, with ACLs and extended attributes, which backups use.
- Pruning has a leftover upload to remove, and a source that stops
having inputs to root leaves no empty directory of roots.
- A full clone has every commit from the start, so the fetch for a commit
it lacks never runs. A git scenario deploys a commit that appears on
the server after cloning.
- The jq test also runs the Nix function that finds a flake's
subdirectory on the target, over both shapes of nix flake metadata's
output.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The shell doesn't run the EXIT trap when a signal ends it, so an
interrupted deploy leaves the ssh master, for an hour, and populate's
temporary directory. Populate traps HUP, INT, and TERM and exits, which
runs the EXIT trap.
The shell runs a trap once the command it waits for ends. A signal to
the process group, like the one of an interrupt key, of a closed terminal,
or of timeout, ends that command as well. A signal to populate alone
takes effect after the step that is running.
Phases of scenarios can end populate after some seconds. An edge case
does so while a pipe source's command sleeps, and checks that neither the
ssh master nor the temporary directory remain.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
rsync reads .rsync-filter files in the directories above a source as
well, and their rules apply to the transfer. The check for files that
rsync 3.5.0 and later can't read only starts if the source itself
contains one, so a subdirectory of a repository with a filter file at
its top gets synced without it, silently. The check also starts for
filter files above the source.
The check's dry run is the transfer it stands for, as far as filtering
goes:
- It gets the source's exclude and filters, so it doesn't enter
directories the transfer skips.
- It doesn't run for transfers that apply no .rsync-filter files, i.e.
uploads of flakes and patches.
- A filter file the user may not read anyway doesn't count: rsync fails
on its own when it gets to transfer that file, and --insecure-links
changes nothing about it.
- rsync prints bytes it takes for unprintable as \#ooo, which depends on
the locale. File names get decoded before checking that they exist, so
files below, e.g., directories with umlauts in their names don't pass
for missing in the C locale.
- If rsync doesn't tell which filter files it reads at all, populate
warns that it can't tell either.
The README mentions that --insecure-links also affects the way to the
target path on local targets, and that only rsync 3.5.0 ignores filter
files below directories that may be entered but not read.
Phases of scenarios can set environment variables for populate. New
file scenarios sync a subdirectory whose filter file is above it, and a
source with a filter file below a directory with an umlaut in its name,
in the C locale.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Populate replaces what's at the target paths of sources, removes what
sources don't deliver anymore, and prunes. Nothing of that may reach
beyond the target directory, whatever names sources have and whatever the
target directory contains:
- Source names that aren't file names in the target directory don't
evaluate: the empty name, ., .., .populate, and names with slashes or
control characters. E.g. a git source named "" removes the target
directory, and a file source named .. syncs into its parent.
- Target paths that aren't absolute, that are the root directory, or that
contain .. don't evaluate.
- A symlink at the target path of a source gets replaced, not followed,
and populate says so. rsync-based and flake sources do that already.
git sources replace the link as well, and also a work tree whose .git is
a symlink or a file naming a repository elsewhere, instead of fetching,
resetting, and cleaning there. derivation sources replace a link that
doesn't point into the store, instead of failing.
- Populate refuses to use the sentinel, or a state directory therein, if
it is a symlink, instead of changing and removing what it points to.
- What populate removes recursively gets removed as a link if it is one:
chmod -R follows a link named on the command line.
- Flake and flake-input sources that get patched, get their lock file
rewritten, or are materialized replace a symlink at their target path
like every source does, and say so.
- Nix rewrites lock files in place, which changes other hard links to
them as well, e.g. in a backup or outside the target directory, and
what a lock file that is a symlink points to. Populate replaces a lock
file by a copy of its own before rewriting it.
The target directory itself may be a symlink. Nix refuses path: URLs with
symlinks on the way, so the flakes step resolves the paths it hands to
Nix.
Backups work on a sentinel that is still a file, which becomes a
directory, like for everything else that keeps state.
The tests create canaries outside the target directories: a file and a
directory beside them, and a directory elsewhere with a read-only
directory and a Git work tree with uncommitted changes. The check of
every phase of every scenario fails if a canary changed, or if a store
path that the target directory links to doesn't match its hash anymore.
A new test, safety, points links at the canaries: at the target path of
every source type, inside what sources replace, in place of populate's
state, and among what it removes and prunes. It also covers the names
and target paths that don't evaluate, a target directory that is a
symlink, and a lock file with another hard link.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A new test, edge-cases, covers paths that work but have no scenario yet:
a flake given by an indirect reference, which the target fetches by its
locked reference; a remote flake in a subdirectory; a flake in a
subdirectory drawing inputs from sources; a dirty Git work tree; a flake
whose lock file needs updating; drawing inputs from a source that isn't
a directory; nested names of missing inputs; several sources sharing one
flake; and an input locked to a path that the target has.
The deploying machine gets a registry entry for the indirect reference.
Resolving it fails if the global registry can't be downloaded, which it
can't in the VM, and disabling the global registry with flake-registry
needs flakes, so the deploying machine enables them, like any deploying
machine using indirect references does. The target doesn't, so checks
for flake support on targets still mean something.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Several weaknesses let checks pass although the feature under test is
broken:
- Nix restores fetched inputs from its fetcher cache, so evaluating a
delivered flake offline after collecting garbage succeeds even if
populate rooted none of its inputs. nix_eval uses an empty cache
directory.
- Scenarios share target directories and stores, so store paths and roots
of earlier scenarios stand in for missing ones, and uploads of flakes
never run in a passing scenario. Scenarios of the flake, flake-input,
and features tests are isolated now: they start without target
directories and with a collected store on both machines.
- Phases expecting evaluation to fail pass on any evaluation failure,
e.g. a misspelt option. Each such phase has a control now: sources
differing only in what makes evaluation fail, which must evaluate.
Further, populate runs with a time limit, so a hang fails its scenario
instead of the whole test, phases can run steps on the deploying machine
before and after, and evaluating the scenarios fails if the remote
target counts as local for whoever evaluates them.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
For targets using sudo, writeDeploy runs nixos-rebuild on the target via
sudo, so --use-remote-sudo has no effect there. It only matters when
nixos-rebuild runs on a separate build target and activates the system
on the target, so writeDeploy passes it only then.
A new VM test, deploy, runs writeDeploy with a stand-in for
nixos-rebuild, which records the user it runs as, NIX_PATH, and its
arguments, for deploying flakes and other sources, with and without a
separate build target, to every kind of target.
|
|
|
tests/default.nix provides one VM test per source type, and one for
features spanning sources. Each test runs scenarios from
tests/scenarios.nix, which cover every combination of a source type's
options, deployed to the deploying machine itself, to a remote target as
root, and to a remote target using sudo. A scenario prepares the target
directory, deploys, checks the result, and deploys and checks again.
Scenarios expecting populate to fail check its error message, or check
at evaluation time if populate is expected not to evaluate.
The deploying machine creates fixtures using tests/fixtures.sh and serves
them via HTTP and git daemon, so flakes can be fetched on the target
without further network access. Checks evaluate delivered flakes without
network access to the fixtures after collecting garbage, so they verify
that the delivered store paths are complete and rooted.
|