2022-02-11 08:49:52 +00:00
|
|
|
# Test logrotate service works and is enabled by default
|
|
|
|
|
2022-02-28 21:54:12 +00:00
|
|
|
let
|
|
|
|
importTest = { ... }: {
|
|
|
|
services.logrotate.settings.import = {
|
|
|
|
olddir = false;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
in
|
|
|
|
|
2022-02-27 10:06:44 +00:00
|
|
|
import ./make-test-python.nix ({ pkgs, ... }: rec {
|
2022-02-11 08:49:52 +00:00
|
|
|
name = "logrotate";
|
|
|
|
meta = with pkgs.lib.maintainers; {
|
|
|
|
maintainers = [ martinetd ];
|
|
|
|
};
|
|
|
|
|
2022-02-27 10:06:44 +00:00
|
|
|
nodes = {
|
|
|
|
defaultMachine = { ... }: { };
|
2022-03-04 22:50:12 +00:00
|
|
|
failingMachine = { ... }: {
|
|
|
|
services.logrotate.configFile = pkgs.writeText "logrotate.conf" ''
|
|
|
|
# self-written config file
|
|
|
|
su notarealuser notagroupeither
|
|
|
|
'';
|
|
|
|
};
|
2022-02-27 10:06:44 +00:00
|
|
|
machine = { config, ... }: {
|
2022-02-28 21:54:12 +00:00
|
|
|
imports = [ importTest ];
|
|
|
|
|
|
|
|
services.logrotate.settings = {
|
|
|
|
# remove default frequency header and add another
|
|
|
|
header = {
|
|
|
|
frequency = null;
|
|
|
|
delaycompress = true;
|
|
|
|
};
|
|
|
|
# extra global setting... affecting nothing
|
|
|
|
last_line = {
|
|
|
|
global = true;
|
|
|
|
priority = 2000;
|
|
|
|
shred = true;
|
|
|
|
};
|
2023-05-20 02:10:21 +00:00
|
|
|
# using mail somewhere should add --mail to logrotate invocation
|
2022-02-27 10:06:44 +00:00
|
|
|
sendmail = {
|
2022-02-28 21:54:12 +00:00
|
|
|
mail = "user@domain.tld";
|
|
|
|
};
|
|
|
|
# postrotate should be suffixed by 'endscript'
|
|
|
|
postrotate = {
|
|
|
|
postrotate = "touch /dev/null";
|
|
|
|
};
|
logrotate: add configuration check at build time
Now the service no longer starts immediately,
check if the config we generated makes sense as soon as possible.
The check isn't perfect because logrotate --debug wants to check
users required, there are two problems:
- /etc/passwd and /etc/group are sandboxed and we don't have
visibility of system users
- the check phase runs as nixbld which cannot su to other users
and logrotate fails on this
Until these two problems can be addressed, users-related checks
are filtered out, it's still much better than no check.
The check can be disabled with services.logrotate.checkConfig
if required
(bird also has a preCheck param, to prepare the environment
before check, but we can add it if it becomes necessary)
Since this makes for very verbose builds, we only show errors:
There is no way to control log level, but logrotate hardcodes
'error:' at common log level, so we can use grep, taking care
to keep error codes
Some manual tests:
───────┬──────────────────────────────────────────
│ File: valid-config.conf
───────┼──────────────────────────────────────────
1 │ missingok
───────┴──────────────────────────────────────────
logrotate --debug ok
grep ok
───────┬──────────────────────────────────────────
│ File: postrotate-no-end.conf
───────┼──────────────────────────────────────────
1 │ missingok
2 │ /file {
3 │ postrotate
4 │ test
5 │ }
───────┴──────────────────────────────────────────
error: postrotate-no-end.conf:prerotate, postrotate or preremove without endscript
───────┬──────────────────────────────────────────
│ File: missing-file.conf
───────┼──────────────────────────────────────────
1 │ "test" { daily }
───────┴──────────────────────────────────────────
error: stat of test failed: No such file or directory
───────┬──────────────────────────────────────────
│ File: unknown-option.conf
───────┼──────────────────────────────────────────
1 │ some syntax error
───────┴──────────────────────────────────────────
logrotate --debug ok
error: unknown-option.conf:1 unknown option 'some' -- ignoring line
───────┬──────────────────────────────────────────
│ File: unknown-user.conf
───────┼──────────────────────────────────────────
1 │ su notauser notagroup
───────┴──────────────────────────────────────────
error: unknown-user.conf:1 unknown user 'notauser'
In particular note that logrotate would not error on unknown option
(it just ignores the line) but this change makes the check fail.
2022-03-02 13:12:45 +00:00
|
|
|
# check checkConfig works as expected: there is nothing to check here
|
|
|
|
# except that the file build passes
|
|
|
|
checkConf = {
|
|
|
|
su = "root utmp";
|
|
|
|
createolddir = "0750 root utmp";
|
|
|
|
create = "root utmp";
|
|
|
|
"create " = "0750 root utmp";
|
|
|
|
};
|
2022-02-28 21:54:12 +00:00
|
|
|
# multiple paths should be aggregated
|
|
|
|
multipath = {
|
|
|
|
files = [ "file1" "file2" ];
|
|
|
|
};
|
|
|
|
# overriding imported path should keep existing attributes
|
|
|
|
# (e.g. olddir is still set)
|
|
|
|
import = {
|
|
|
|
notifempty = true;
|
|
|
|
};
|
|
|
|
};
|
2022-02-27 10:06:44 +00:00
|
|
|
};
|
2022-02-11 08:49:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
testScript =
|
|
|
|
''
|
|
|
|
with subtest("whether logrotate works"):
|
2022-02-27 10:06:44 +00:00
|
|
|
# we must rotate once first to create logrotate stamp
|
|
|
|
defaultMachine.succeed("systemctl start logrotate.service")
|
2022-02-26 07:04:44 +00:00
|
|
|
# we need to wait for console text once here to
|
|
|
|
# clear console buffer up to this point for next wait
|
2022-02-27 10:06:44 +00:00
|
|
|
defaultMachine.wait_for_console_text('logrotate.service: Deactivated successfully')
|
2022-02-11 08:49:52 +00:00
|
|
|
|
2022-02-27 10:06:44 +00:00
|
|
|
defaultMachine.succeed(
|
2022-02-11 08:49:52 +00:00
|
|
|
# wtmp is present in default config.
|
|
|
|
"rm -f /var/log/wtmp*",
|
2022-02-26 22:04:18 +00:00
|
|
|
# we need to give it at least 1MB
|
|
|
|
"dd if=/dev/zero of=/var/log/wtmp bs=2M count=1",
|
2022-02-11 08:49:52 +00:00
|
|
|
|
2022-02-26 07:04:44 +00:00
|
|
|
# move into the future and check rotation.
|
|
|
|
"date -s 'now + 1 month + 1 day'")
|
2022-02-27 10:06:44 +00:00
|
|
|
defaultMachine.wait_for_console_text('logrotate.service: Deactivated successfully')
|
|
|
|
defaultMachine.succeed(
|
2022-02-11 08:49:52 +00:00
|
|
|
# check rotate worked
|
|
|
|
"[ -e /var/log/wtmp.1 ]",
|
|
|
|
)
|
2022-02-27 10:06:44 +00:00
|
|
|
with subtest("default config does not have mail"):
|
|
|
|
defaultMachine.fail("systemctl cat logrotate.service | grep -- --mail")
|
|
|
|
with subtest("using mails adds mail option"):
|
|
|
|
machine.succeed("systemctl cat logrotate.service | grep -- --mail")
|
2022-02-28 21:54:12 +00:00
|
|
|
with subtest("check generated config matches expectation"):
|
|
|
|
machine.succeed(
|
|
|
|
# copy conf to /tmp/logrotate.conf for easy grep
|
|
|
|
"conf=$(systemctl cat logrotate | grep -oE '/nix/store[^ ]*logrotate.conf'); cp $conf /tmp/logrotate.conf",
|
|
|
|
"! grep weekly /tmp/logrotate.conf",
|
|
|
|
"grep -E '^delaycompress' /tmp/logrotate.conf",
|
|
|
|
"tail -n 1 /tmp/logrotate.conf | grep shred",
|
|
|
|
"sed -ne '/\"sendmail\" {/,/}/p' /tmp/logrotate.conf | grep 'mail user@domain.tld'",
|
|
|
|
"sed -ne '/\"postrotate\" {/,/}/p' /tmp/logrotate.conf | grep endscript",
|
|
|
|
"grep '\"file1\"\n\"file2\" {' /tmp/logrotate.conf",
|
|
|
|
"sed -ne '/\"import\" {/,/}/p' /tmp/logrotate.conf | grep noolddir",
|
|
|
|
)
|
2022-03-04 22:50:12 +00:00
|
|
|
# also check configFile option
|
|
|
|
failingMachine.succeed(
|
|
|
|
"conf=$(systemctl cat logrotate | grep -oE '/nix/store[^ ]*logrotate.conf'); cp $conf /tmp/logrotate.conf",
|
|
|
|
"grep 'self-written config' /tmp/logrotate.conf",
|
|
|
|
)
|
|
|
|
with subtest("Check logrotate-checkconf service"):
|
|
|
|
machine.wait_for_unit("logrotate-checkconf.service")
|
|
|
|
# wait_for_unit also asserts for success, so wait for
|
|
|
|
# parent target instead and check manually.
|
|
|
|
failingMachine.wait_for_unit("multi-user.target")
|
|
|
|
info = failingMachine.get_unit_info("logrotate-checkconf.service")
|
|
|
|
if info["ActiveState"] != "failed":
|
|
|
|
raise Exception('logrotate-checkconf.service was not failed')
|
|
|
|
|
2022-02-11 08:49:52 +00:00
|
|
|
'';
|
|
|
|
})
|