From 8980d95ceb3138f0c4830e1f0c39366d015236a6 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sat, 1 Nov 2025 10:19:32 +0100 Subject: [PATCH] tests: end-to-end: add test with changes in /tmp The test is careful not to affect real data on the system. --- .gitignore | 1 + Makefile | 1 + cmd/safcm/main_sync_test.go | 113 ++++++++++++++++++ .../tmp/kjn8W3OR4LFwo4Iw7YLY5/dir/.gitignore | 0 .../tmp/kjn8W3OR4LFwo4Iw7YLY5/executable | 3 + .../files/tmp/kjn8W3OR4LFwo4Iw7YLY5/file | 5 + .../files/tmp/kjn8W3OR4LFwo4Iw7YLY5/symlink | 1 + .../files/tmp/kjn8W3OR4LFwo4Iw7YLY5/template | 3 + .../changes.example.org/templates.yaml | 1 + cmd/safcm/testdata/ssh/project/groups.yaml | 2 + cmd/safcm/testdata/ssh/project/hosts.yaml | 5 +- cmd/safcm/testdata/ssh/ssh/ssh_config | 2 +- 12 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/dir/.gitignore create mode 100755 cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/executable create mode 100644 cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/file create mode 120000 cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/symlink create mode 100644 cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/template create mode 100644 cmd/safcm/testdata/ssh/project/changes.example.org/templates.yaml diff --git a/.gitignore b/.gitignore index 8e87b2f..6a45e08 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /cmd/safcm/remote/ +/cmd/safcm/testdata/ssh/project/changes.example.org/permissions.yaml /cmd/safcm/testdata/ssh/project/no-changes.example.org/ /cmd/safcm/testdata/ssh/ssh/authorized_keys /cmd/safcm/testdata/ssh/ssh/id_ed25519 diff --git a/Makefile b/Makefile index 759e703..321de80 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ clean: rm -f cmd/safcm/testdata/ssh/ssh/known_hosts rm -f cmd/safcm/testdata/ssh/sshd/ssh_host_key rm -f cmd/safcm/testdata/ssh/sshd/ssh_host_key.pub + rm -f cmd/safcm/testdata/ssh/project/changes.example.org/permissions.yaml rm -rf cmd/safcm/testdata/ssh/project/no-changes.example.org/ .PHONY: all test clean safcm diff --git a/cmd/safcm/main_sync_test.go b/cmd/safcm/main_sync_test.go index dbab316..c02b881 100644 --- a/cmd/safcm/main_sync_test.go +++ b/cmd/safcm/main_sync_test.go @@ -5,6 +5,7 @@ package main_test import ( "fmt" + "io/fs" "net" "os" "os/exec" @@ -24,6 +25,21 @@ func TestSyncSshEndToEnd(t *testing.T) { t.Fatal(err) } + var userStr, groupStr string + { + u, uid, g, gid := ft.CurrentUserAndGroup() + userStr = fmt.Sprintf("%s(%d)", u, uid) + groupStr = fmt.Sprintf("%s(%d)", g, gid) + } + + changesPath := "/tmp/kjn8W3OR4LFwo4Iw7YLY5" + _, err = os.Stat(changesPath) + if err == nil { + // Very unlikely, but just in case + t.Fatalf("%q already exists, aborting to prevent data loss", + changesPath) + } + var suffix string // Needs different options in sshd_config if runtime.GOOS == "openbsd" { @@ -77,6 +93,23 @@ func TestSyncSshEndToEnd(t *testing.T) { ft.CreateFile("no-changes.example.org/permissions.yaml", noChangePermissions, 0644) + changesPermissions := ` +/: 0755 root root +/tmp: 1777 root root +/tmp/kjn8W3OR4LFwo4Iw7YLY5/file: 0604 +/tmp/kjn8W3OR4LFwo4Iw7YLY5/template: 0400 +` + if runtime.GOOS == "openbsd" || runtime.GOOS == "freebsd" { + changesPermissions = ` +/: 0755 root wheel +/tmp: 1777 root wheel +/tmp/kjn8W3OR4LFwo4Iw7YLY5/file: 0604 +/tmp/kjn8W3OR4LFwo4Iw7YLY5/template: 0400 +` + } + ft.CreateFile("changes.example.org/permissions.yaml", + changesPermissions, 0644) + ciRun := len(os.Getenv("SAFCM_CI_RUN")) != 0 isRoot := os.Getuid() == 0 skipUnlessHarmless := !(ciRun || !isRoot) @@ -192,6 +225,33 @@ func TestSyncSshEndToEnd(t *testing.T) { nil, }, + { + "changes", + skipUnlessHarmless, + false, + []string{"changes.example.org"}, + `[info] [changes.example.org] +changed 7 file(s): +"/tmp/kjn8W3OR4LFwo4Iw7YLY5": created, dir, USER GROUP, 0755 +"/tmp/kjn8W3OR4LFwo4Iw7YLY5/dir": created, dir, USER GROUP, 0755 +"/tmp/kjn8W3OR4LFwo4Iw7YLY5/dir/.gitignore": created, file, USER GROUP, 0644 +"/tmp/kjn8W3OR4LFwo4Iw7YLY5/executable": created, file, USER GROUP, 0755 +"/tmp/kjn8W3OR4LFwo4Iw7YLY5/file": created, file, USER GROUP, 0604 +"/tmp/kjn8W3OR4LFwo4Iw7YLY5/symlink": created, symlink, USER GROUP, 0777 +"/tmp/kjn8W3OR4LFwo4Iw7YLY5/template": created, file, USER GROUP, 0400 +`, + nil, + }, + { + "changes (up-to-date)", + skipUnlessHarmless, + false, + []string{"changes.example.org"}, + `[info] [changes.example.org] no changes +`, + nil, + }, + { "no effect commands (dry-run)", false, @@ -358,16 +418,69 @@ executed 2 command(s): tmp = append(tmp, x) } res := strings.Join(tmp, "\n") + // Adapt output to current user/group + tc.exp = strings.ReplaceAll(tc.exp, "USER", userStr) + tc.exp = strings.ReplaceAll(tc.exp, "GROUP", groupStr) testutil.AssertEqual(t, "res", res, tc.exp) testutil.AssertErrorEqual(t, "err", err, tc.expErr) }) } + if !skipUnlessHarmless { + t.Run("check result of changes.example.org", func(t *testing.T) { + exp := []ft.File{ + { + Path: ".", + Mode: fs.ModeDir | 0755, + }, + { + Path: "dir", + Mode: fs.ModeDir | 0755, + }, + { + Path: "dir/.gitignore", + Mode: 0644, + Data: []byte{}, + }, + { + Path: "executable", + Mode: 0755, + Data: []byte("#!/bin/sh\n\necho 'Hello World!'\n"), + }, + { + Path: "file", + Mode: 0604, + Data: []byte("just\na\nsmall\ntext\nfile\n"), + }, + { + Path: "symlink", + Mode: fs.ModeSymlink | 0777, + Data: []byte("file"), + }, + { + Path: "template", + Mode: 0400, + Data: []byte("\n>> changes\n\n"), + }, + } + + files, err := ft.WalkDir(changesPath) + if err != nil { + t.Fatal(err) + } + testutil.AssertEqual(t, "files", files, exp) + }) + } + err = os.RemoveAll(remotePath) if err != nil { t.Fatal(err) } + err = os.RemoveAll(changesPath) + if err != nil { + t.Fatal(err) + } } // vi: set noet ts=4 sw=4 sts=4: diff --git a/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/dir/.gitignore b/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/dir/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/executable b/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/executable new file mode 100755 index 0000000..97cca98 --- /dev/null +++ b/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/executable @@ -0,0 +1,3 @@ +#!/bin/sh + +echo 'Hello World!' diff --git a/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/file b/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/file new file mode 100644 index 0000000..1e25e25 --- /dev/null +++ b/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/file @@ -0,0 +1,5 @@ +just +a +small +text +file diff --git a/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/symlink b/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/symlink new file mode 120000 index 0000000..1a010b1 --- /dev/null +++ b/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/symlink @@ -0,0 +1 @@ +file \ No newline at end of file diff --git a/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/template b/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/template new file mode 100644 index 0000000..e3be637 --- /dev/null +++ b/cmd/safcm/testdata/ssh/project/changes.example.org/files/tmp/kjn8W3OR4LFwo4Iw7YLY5/template @@ -0,0 +1,3 @@ +{{if .InGroup "changes"}} +>> changes +{{end}} diff --git a/cmd/safcm/testdata/ssh/project/changes.example.org/templates.yaml b/cmd/safcm/testdata/ssh/project/changes.example.org/templates.yaml new file mode 100644 index 0000000..b8b8097 --- /dev/null +++ b/cmd/safcm/testdata/ssh/project/changes.example.org/templates.yaml @@ -0,0 +1 @@ +- /tmp/kjn8W3OR4LFwo4Iw7YLY5/template diff --git a/cmd/safcm/testdata/ssh/project/groups.yaml b/cmd/safcm/testdata/ssh/project/groups.yaml index e69de29..ee6e00c 100644 --- a/cmd/safcm/testdata/ssh/project/groups.yaml +++ b/cmd/safcm/testdata/ssh/project/groups.yaml @@ -0,0 +1,2 @@ +changes: + - changes.example.org diff --git a/cmd/safcm/testdata/ssh/project/hosts.yaml b/cmd/safcm/testdata/ssh/project/hosts.yaml index 34a858f..e0af9d7 100644 --- a/cmd/safcm/testdata/ssh/project/hosts.yaml +++ b/cmd/safcm/testdata/ssh/project/hosts.yaml @@ -1,4 +1,5 @@ -- name: no-settings.example.org +- name: changes.example.org - name: no-changes.example.org -- name: no-effect-commands.example.org - name: no-effect-commands-failing.example.org +- name: no-effect-commands.example.org +- name: no-settings.example.org diff --git a/cmd/safcm/testdata/ssh/ssh/ssh_config b/cmd/safcm/testdata/ssh/ssh/ssh_config index a3394e3..6784ad4 100644 --- a/cmd/safcm/testdata/ssh/ssh/ssh_config +++ b/cmd/safcm/testdata/ssh/ssh/ssh_config @@ -2,6 +2,6 @@ IdentityFile ../ssh/id_ed25519 IdentitiesOnly yes UserKnownHostsFile ../ssh/known_hosts -Host no-settings.example.org no-changes.example.org no-effect-commands.example.org no-effect-commands-failing.example.org +Host changes.example.org no-changes.example.org no-effect-commands-failing.example.org no-effect-commands.example.org no-settings.example.org Hostname 127.0.0.1 Port 29327 -- 2.51.2