-// Copyright (C) 2021 Simon Ruderich
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Copyright (C) 2021-2024 Simon Ruderich
package main_test
"testing"
"time"
+ ft "ruderich.org/simon/safcm/remote/sync/filetest"
"ruderich.org/simon/safcm/testutil"
)
if err != nil {
t.Fatal(err)
}
- defer os.Chdir(cwd)
+ defer os.Chdir(cwd) //nolint:errcheck
var suffix string
// Needs different options in sshd_config
if err != nil {
t.Fatal(err)
}
- defer sshCmd.Process.Kill()
+ defer sshCmd.Process.Kill() //nolint:errcheck
// Wait until SSH server is ready (up to 30 seconds)
for i := 0; i < 30; i++ {
t.Fatal(err)
}
+ ft.CreateDirectoryExists("no-changes.example.org", 0755)
+ ft.CreateDirectoryExists("no-changes.example.org/files", 0755)
+ ft.CreateDirectoryExists("no-changes.example.org/files/etc", 0755)
+ ft.CreateDirectoryExists("no-changes.example.org/files/tmp", 0755)
+
+ noChangePermissions := `
+/: 0755 root root
+/etc: 0755 root root
+/tmp: 1777 root root
+`
+ if runtime.GOOS == "openbsd" || runtime.GOOS == "freebsd" {
+ noChangePermissions = `
+/: 0755 root wheel
+/etc: 0755 root wheel
+/tmp: 1777 root wheel
+`
+ }
+ ft.CreateFile("no-changes.example.org/permissions.yaml",
+ noChangePermissions, 0644)
+
+ skipUnlessCiRun := len(os.Getenv("SAFCM_CI_RUN")) == 0
+
tests := []struct {
name string
+ skip bool
remove bool
args []string
exp string
{
"no settings",
+ false,
true,
[]string{"no-settings.example.org"},
`<LOG>[info] [no-settings.example.org] remote helper upload in progress
{
"no settings (no helper upload)",
false,
+ false,
[]string{"no-settings.example.org"},
`<LOG>[info] [no-settings.example.org] no changes
`,
},
{
"no settings (error)",
+ false,
true,
[]string{"-log", "error", "no-settings.example.org"},
``,
},
{
"no settings (verbose)",
+ false,
true,
[]string{"-log", "verbose", "no-settings.example.org"},
`<LOG>[info] [no-settings.example.org] remote helper upload in progress
},
{
"no settings (debug2)",
+ false,
true,
[]string{"-log", "debug2", "no-settings.example.org"},
`<LOG>[info] [no-settings.example.org] remote helper upload in progress
`,
nil,
},
+
+ // NOTE: We use -n on regular runs to prevent changing
+ // anything important on the host when running as root!
+
+ {
+ "no changes (dry-run)",
+ false,
+ true,
+ []string{"-n", "no-changes.example.org"},
+ `<LOG>[info] [no-changes.example.org] remote helper upload in progress
+<LOG>[info] [no-changes.example.org] no changes
+`,
+ nil,
+ },
+ {
+ "no changes (dry-run, debug2)",
+ false,
+ true,
+ []string{"-n", "-log", "debug2", "no-changes.example.org"},
+ `<LOG>[info] [no-changes.example.org] remote helper upload in progress
+<LOG>[verbose] [no-changes.example.org] host groups: all <DET> <DET> no-changes.example.org
+<LOG>[verbose] [no-changes.example.org] host group priorities (descending): no-changes.example.org
+<LOG>[debug] [no-changes.example.org] files: "/" (no-changes.example.org): unchanged
+<LOG>[debug] [no-changes.example.org] files: "/etc" (no-changes.example.org): unchanged
+<LOG>[debug] [no-changes.example.org] files: "/tmp" (no-changes.example.org): unchanged
+<LOG>[info] [no-changes.example.org] no changes
+`,
+ nil,
+ },
+ {
+ "no changes",
+ skipUnlessCiRun,
+ true,
+ []string{"no-changes.example.org"},
+ `<LOG>[info] [no-changes.example.org] remote helper upload in progress
+<LOG>[info] [no-changes.example.org] no changes
+`,
+ nil,
+ },
+ {
+ "no changes (debug2)",
+ skipUnlessCiRun,
+ true,
+ []string{"-log", "debug2", "no-changes.example.org"},
+ `<LOG>[info] [no-changes.example.org] remote helper upload in progress
+<LOG>[verbose] [no-changes.example.org] host groups: all <DET> <DET> no-changes.example.org
+<LOG>[verbose] [no-changes.example.org] host group priorities (descending): no-changes.example.org
+<LOG>[debug] [no-changes.example.org] files: "/" (no-changes.example.org): unchanged
+<LOG>[debug] [no-changes.example.org] files: "/etc" (no-changes.example.org): unchanged
+<LOG>[debug] [no-changes.example.org] files: "/tmp" (no-changes.example.org): unchanged
+<LOG>[info] [no-changes.example.org] no changes
+`,
+ nil,
+ },
+
+ {
+ "no effect commands (dry-run)",
+ false,
+ true,
+ []string{"-n", "no-effect-commands.example.org"},
+ `<LOG>[info] [no-effect-commands.example.org] remote helper upload in progress
+<LOG>[info] [no-effect-commands.example.org]
+will execute 2 command(s): (dry-run)
+"echo this is a command"
+"true"
+`,
+ nil,
+ },
+ {
+ "no effect commands (dry-run)",
+ false,
+ true,
+ []string{"-n", "-log", "debug2", "no-effect-commands.example.org"},
+ `<LOG>[info] [no-effect-commands.example.org] remote helper upload in progress
+<LOG>[verbose] [no-effect-commands.example.org] host groups: all <DET> <DET> no-effect-commands.example.org
+<LOG>[verbose] [no-effect-commands.example.org] host group priorities (descending): no-effect-commands.example.org
+<LOG>[info] [no-effect-commands.example.org]
+will execute 2 command(s): (dry-run)
+"echo this is a command"
+"true"
+`,
+ nil,
+ },
+ {
+ "no effect commands",
+ false,
+ true,
+ []string{"no-effect-commands.example.org"},
+ `<LOG>[info] [no-effect-commands.example.org] remote helper upload in progress
+<LOG>[info] [no-effect-commands.example.org]
+executed 2 command(s):
+"echo this is a command":
+ > this is a command
+"true"
+`,
+ nil,
+ },
+ {
+ "no effect commands (debug2)",
+ false,
+ true,
+ []string{"-log", "debug2", "no-effect-commands.example.org"},
+ `<LOG>[info] [no-effect-commands.example.org] remote helper upload in progress
+<LOG>[verbose] [no-effect-commands.example.org] host groups: all <DET> <DET> no-effect-commands.example.org
+<LOG>[verbose] [no-effect-commands.example.org] host group priorities (descending): no-effect-commands.example.org
+<LOG>[verbose] [no-effect-commands.example.org] commands: running "/bin/sh" "-c" "echo this is a command" (no-effect-commands.example.org)
+<LOG>[debug2] [no-effect-commands.example.org] commands: command output:
+this is a command
+<LOG>[verbose] [no-effect-commands.example.org] commands: running "/bin/sh" "-c" "true" (no-effect-commands.example.org)
+<LOG>[info] [no-effect-commands.example.org]
+executed 2 command(s):
+"echo this is a command":
+ > this is a command
+"true"
+`,
+ nil,
+ },
+
+ {
+ "no effect commands failing (dry-run)",
+ false,
+ true,
+ []string{"-n", "no-effect-commands-failing.example.org"},
+ `<LOG>[info] [no-effect-commands-failing.example.org] remote helper upload in progress
+<LOG>[info] [no-effect-commands-failing.example.org]
+will execute 2 command(s): (dry-run)
+"echo this is a command"
+"echo failing; false"
+`,
+ nil,
+ },
+ {
+ "no effect commands failing (dry-run)",
+ false,
+ true,
+ []string{"-n", "-log", "debug2", "no-effect-commands-failing.example.org"},
+ `<LOG>[info] [no-effect-commands-failing.example.org] remote helper upload in progress
+<LOG>[verbose] [no-effect-commands-failing.example.org] host groups: all <DET> <DET> no-effect-commands-failing.example.org
+<LOG>[verbose] [no-effect-commands-failing.example.org] host group priorities (descending): no-effect-commands-failing.example.org
+<LOG>[info] [no-effect-commands-failing.example.org]
+will execute 2 command(s): (dry-run)
+"echo this is a command"
+"echo failing; false"
+`,
+ nil,
+ },
+ {
+ "no effect commands failing",
+ false,
+ true,
+ []string{"no-effect-commands-failing.example.org"},
+ `<LOG>[info] [no-effect-commands-failing.example.org] remote helper upload in progress
+<LOG>[info] [no-effect-commands-failing.example.org]
+executed 2 command(s):
+"echo this is a command":
+ > this is a command
+"echo failing; false", failed: "exit status 1":
+ > failing
+<LOG>[error] [no-effect-commands-failing.example.org] commands: "echo failing; false" failed: exit status 1
+`,
+ fmt.Errorf("exit status 1"),
+ },
+ {
+ "no effect commands failing (debug2)",
+ false,
+ true,
+ []string{"-log", "debug2", "no-effect-commands-failing.example.org"},
+ `<LOG>[info] [no-effect-commands-failing.example.org] remote helper upload in progress
+<LOG>[verbose] [no-effect-commands-failing.example.org] host groups: all <DET> <DET> no-effect-commands-failing.example.org
+<LOG>[verbose] [no-effect-commands-failing.example.org] host group priorities (descending): no-effect-commands-failing.example.org
+<LOG>[verbose] [no-effect-commands-failing.example.org] commands: running "/bin/sh" "-c" "echo this is a command" (no-effect-commands-failing.example.org)
+<LOG>[debug2] [no-effect-commands-failing.example.org] commands: command output:
+this is a command
+<LOG>[verbose] [no-effect-commands-failing.example.org] commands: running "/bin/sh" "-c" "echo failing; false" (no-effect-commands-failing.example.org)
+<LOG>[debug2] [no-effect-commands-failing.example.org] commands: command output:
+failing
+<LOG>[info] [no-effect-commands-failing.example.org]
+executed 2 command(s):
+"echo this is a command":
+ > this is a command
+"echo failing; false", failed: "exit status 1":
+ > failing
+<LOG>[error] [no-effect-commands-failing.example.org] commands: "echo failing; false" failed: exit status 1
+`,
+ fmt.Errorf("exit status 1"),
+ },
}
remotePath := fmt.Sprintf("/tmp/safcm-remote-%d", os.Getuid())
logRegexp := regexp.MustCompile(`^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} `)
detectedRegexp := regexp.MustCompile(`detected_\S+`)
+ t.Run("error before connection is established", func(t *testing.T) {
+ // Fake $PATH so safcm cannot find the `ssh` binary.
+ path := os.Getenv("PATH")
+ os.Setenv("PATH", "")
+ defer os.Setenv("PATH", path)
+
+ cmd := exec.Command("../../../../../safcm",
+ "sync", "-n", "no-settings.example.org")
+ _, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Errorf("err = nil")
+ }
+ })
+
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if tc.remove {