The unit level runs in bubblewrap, where /var/log is not there; the base's /var/log/wtmp block has no missingok, so logrotate -d failed its stat and every mesh-catalog check that reached this test failed.
88 lines
3.2 KiB
Go
88 lines
3.2 KiB
Go
package main
|
|
|
|
// The module's shape (novox/hq to-be 42 Phase 1, research 027): the package, its base configuration
|
|
// whole, and its timer — and the base configuration proven by logrotate's own dry run where logrotate
|
|
// is installed, because a base configuration that does not parse stops every rotation on the machine.
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestItDeclaresThePackageTheBaseAndTheTimer(t *testing.T) {
|
|
m := manifest(t)
|
|
if p := m.resource(t, "package"); p["package"] != "logrotate" {
|
|
t.Fatalf("%v", p)
|
|
}
|
|
c := m.resource(t, "config")
|
|
if c["path"] != BaseConf || c["into"] != nil || !strings.HasPrefix(templateText(t, c), "# The mesh's (module logrotate") {
|
|
t.Fatalf("%v", c)
|
|
}
|
|
if !strings.Contains(templateText(t, c), "\ninclude "+RulesDir+"\n") {
|
|
t.Fatal("the base must include the packages' rules, or nothing of theirs rotates")
|
|
}
|
|
if strings.Contains(templateText(t, c), "olddir") {
|
|
t.Fatal("olddir flattens logs of different directories into one, where two of one name collide")
|
|
}
|
|
timer := m.resource(t, "timer")
|
|
if timer["unit"] != "logrotate.timer" || timer["state"] != "running" || timer["boot"] != "enabled" {
|
|
t.Fatalf("%v", timer)
|
|
}
|
|
}
|
|
|
|
func TestTheBaseParses(t *testing.T) {
|
|
logrotate, err := exec.LookPath("logrotate")
|
|
if err != nil {
|
|
t.Skip("logrotate is not installed here; the base configuration is not dry-run")
|
|
}
|
|
dir := t.TempDir()
|
|
conf := filepath.Join(dir, "logrotate.conf")
|
|
if err := os.WriteFile(conf, []byte(dryRunBase(t, dir)), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out, err := exec.Command(logrotate, "-d", "-s", filepath.Join(dir, "state"), conf).CombinedOutput()
|
|
if err != nil || strings.Contains(string(out), "error:") {
|
|
t.Fatalf("logrotate -d: %v\n%s", err, out)
|
|
}
|
|
}
|
|
|
|
// The dry run proves the base parses, not what the machine's logs are: the unit level runs shut off in
|
|
// bubblewrap (mesh-sdk buildstep.Isolated), where /var/log is not there, and a base block without
|
|
// missingok then fails its stat (issue 1891).
|
|
func TestTheDryRunReadsNothingOfTheMachine(t *testing.T) {
|
|
dir := t.TempDir()
|
|
content := dryRunBase(t, dir)
|
|
for _, machine := range []string{"include " + RulesDir, "/var/log/"} {
|
|
if strings.Contains(content, machine) {
|
|
t.Fatalf("the dry run's base still names %s of the machine:\n%s", machine, content)
|
|
}
|
|
}
|
|
for _, log := range []string{"wtmp", "btmp"} {
|
|
if _, err := os.Stat(filepath.Join(dir, "log", log)); err != nil {
|
|
t.Fatalf("the base's own log %s is not in the dry run's directory: %v", log, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// dryRunBase is the base configuration pointed into dir: its include at dir/d, and its own logs at dir/log,
|
|
// both made there.
|
|
func dryRunBase(t *testing.T, dir string) string {
|
|
t.Helper()
|
|
content := strings.ReplaceAll(templateText(t, manifest(t).resource(t, "config")), "include "+RulesDir, "include "+filepath.Join(dir, "d"))
|
|
content = strings.ReplaceAll(content, "/var/log/", filepath.Join(dir, "log")+"/")
|
|
for _, d := range []string{"d", "log"} {
|
|
if err := os.Mkdir(filepath.Join(dir, d), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
for _, log := range []string{"wtmp", "btmp"} {
|
|
if err := os.WriteFile(filepath.Join(dir, "log", log), nil, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
return content
|
|
}
|