diff --git a/matcher/helpers.go b/matcher/helpers.go index bc1bffd..2d518e8 100644 --- a/matcher/helpers.go +++ b/matcher/helpers.go @@ -18,6 +18,10 @@ import ( . "github.com/onsi/gomega" //nolint:revive ) +// LogDir is where gathered logs are written. Relative paths resolve against the +// process working directory. The default preserves the historical behavior. +var LogDir = "logs" + type VM struct { machine types.Machine cancelFunc context.CancelFunc // We call it when we `Destroy` the VM @@ -167,12 +171,13 @@ func machineGatherLog(m types.Machine, logPath string) { } baseName := filepath.Base(logPath) - _ = os.Mkdir("logs", 0755) + f, destination, err := createGatheredLogFile(LogDir, logPath) + if err != nil { + fmt.Printf("Couldn't create local log file for %s: %s\n", logPath, err) + return + } - f, _ := os.Create(fmt.Sprintf("logs/%s", baseName)) - // Close the file after it has been copied - // Close client connection after the file has been copied - defer scpClient.Close() + // Close the file after it has been copied. defer f.Close() ctx, can := context.WithTimeout(context.Background(), 2*time.Minute) @@ -183,10 +188,27 @@ func machineGatherLog(m types.Machine, logPath string) { return } // Change perms so its world readable - _ = os.Chmod(fmt.Sprintf("logs/%s", baseName), 0666) + if err := os.Chmod(destination, 0o666); err != nil { + fmt.Printf("Couldn't change permissions on %s: %s\n", destination, err) + return + } fmt.Printf("File %s copied!\n", baseName) } +func createGatheredLogFile(logDir, logPath string) (*os.File, string, error) { + if err := os.MkdirAll(logDir, 0o755); err != nil { + return nil, "", fmt.Errorf("creating log directory %q: %w", logDir, err) + } + + destination := filepath.Join(logDir, filepath.Base(logPath)) + f, err := os.Create(destination) + if err != nil { + return nil, "", fmt.Errorf("creating gathered log %q: %w", destination, err) + } + + return f, destination, nil +} + func machineHasFile(m types.Machine, s string) { out, err := m.Command("if [ -f " + s + " ]; then echo ok; else echo wrong; fi") Expect(err).ToNot(HaveOccurred()) diff --git a/matcher/helpers_test.go b/matcher/helpers_test.go new file mode 100644 index 0000000..bf276e9 --- /dev/null +++ b/matcher/helpers_test.go @@ -0,0 +1,103 @@ +package matcher + +import ( + "os" + "path/filepath" + "testing" +) + +func TestLogDirDefault(t *testing.T) { + if LogDir != "logs" { + t.Fatalf("LogDir default = %q, want %q", LogDir, "logs") + } +} + +func TestCreateGatheredLogFileDefaultDir(t *testing.T) { + oldWorkingDir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + if err := os.Chdir(t.TempDir()); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = os.Chdir(oldWorkingDir) }) + + f, destination, err := createGatheredLogFile(LogDir, "/var/log/messages") + if err != nil { + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } + + want := filepath.Join("logs", "messages") + if destination != want { + t.Fatalf("destination = %q, want %q", destination, want) + } + if _, err := os.Stat(want); err != nil { + t.Fatalf("stat gathered log: %v", err) + } +} + +func TestCreateGatheredLogFileAbsoluteNestedDir(t *testing.T) { + logDir := filepath.Join(t.TempDir(), "run", "gathered-logs") + f, destination, err := createGatheredLogFile(logDir, "/var/log/kern.log") + if err != nil { + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } + + want := filepath.Join(logDir, "kern.log") + if destination != want { + t.Fatalf("destination = %q, want %q", destination, want) + } + if _, err := os.Stat(want); err != nil { + t.Fatalf("stat gathered log: %v", err) + } +} + +func TestCreateGatheredLogFilePreservesBaseName(t *testing.T) { + logDir := t.TempDir() + f, destination, err := createGatheredLogFile(logDir, "/nested/remote/audit.json") + if err != nil { + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } + + if got, want := filepath.Base(destination), "audit.json"; got != want { + t.Fatalf("destination base = %q, want %q", got, want) + } +} + +func TestCreateGatheredLogFileInvalidDir(t *testing.T) { + parent := t.TempDir() + notDirectory := filepath.Join(parent, "not-a-directory") + if err := os.WriteFile(notDirectory, []byte("file"), 0o600); err != nil { + t.Fatal(err) + } + + f, destination, err := createGatheredLogFile(filepath.Join(notDirectory, "logs"), "/var/log/messages") + if err == nil { + t.Fatal("expected an error") + } + if f != nil { + t.Fatal("expected no file on error") + } + if destination != "" { + t.Fatalf("destination = %q, want empty", destination) + } +} + +func TestCreateGatheredLogFileInvalidDestination(t *testing.T) { + f, destination, err := createGatheredLogFile(t.TempDir(), ".") + if err == nil { + t.Fatal("expected an error") + } + if f != nil || destination != "" { + t.Fatalf("result = %#v, %q; want nil, empty", f, destination) + } +}