Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

run osq runtime tests on windows #1665

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
156 changes: 156 additions & 0 deletions pkg/osquery/runtime/runtime_posix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
//go:build !windows
// +build !windows

package runtime

import (
"fmt"
"log/slog"
"os/exec"
"path/filepath"
"syscall"
"testing"
"time"

typesMocks "github.com/kolide/launcher/ee/agent/types/mocks"
"github.com/kolide/launcher/pkg/log/multislogger"
"github.com/kolide/launcher/pkg/threadsafebuffer"
"github.com/osquery/osquery-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

func requirePgidMatch(t *testing.T, pid int) {
pgid, err := syscall.Getpgid(pid)
require.NoError(t, err)
require.Equal(t, pgid, pid)
}

// hasPermissionsToRunTest always return true for non-windows platforms since
// elveated permissions are not required to run the tests
func hasPermissionsToRunTest() bool {
return true
}

// TestOsquerySlowStart tests that launcher can handle a slow-starting osqueryd process.
// This this is only enabled on non-Windows platforms because we have not yet figured
// out how to suspend and resume a process on Windows via golang.
func TestOsquerySlowStart(t *testing.T) {
t.Parallel()
rootDirectory, rmRootDirectory, err := osqueryTempDir()
require.NoError(t, err)
defer rmRootDirectory()

var logBytes threadsafebuffer.ThreadSafeBuffer

k := typesMocks.NewKnapsack(t)
k.On("OsqueryHealthcheckStartupDelay").Return(0 * time.Second).Maybe()
k.On("WatchdogEnabled").Return(false)
k.On("RegisterChangeObserver", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
slogger := multislogger.New(slog.NewJSONHandler(&logBytes, &slog.HandlerOptions{Level: slog.LevelDebug}))
k.On("Slogger").Return(slogger.Logger)
k.On("PinnedOsquerydVersion").Return("")

runner := New(
k,
WithKnapsack(k),
WithRootDirectory(rootDirectory),
WithOsquerydBinary(testOsqueryBinaryDirectory),
WithSlogger(slogger.Logger),
WithStartFunc(func(cmd *exec.Cmd) error {
err := cmd.Start()
if err != nil {
return fmt.Errorf("unexpected error starting command: %w", err)
}
// suspend the process right away
cmd.Process.Signal(syscall.SIGTSTP)
go func() {
// wait a while before resuming the process
time.Sleep(3 * time.Second)
cmd.Process.Signal(syscall.SIGCONT)
}()
return nil
}),
)
go runner.Run()
waitHealthy(t, runner)

// ensure that we actually had to wait on the socket
require.Contains(t, logBytes.String(), "osquery extension socket not created yet")
require.NoError(t, runner.Shutdown())
}

// TestExtensionSocketPath tests that the launcher can start osqueryd with a custom extension socket path.
// This is only run on non-windows platforms because the extension socket path is semi random on windows.
func TestExtensionSocketPath(t *testing.T) {
t.Parallel()

rootDirectory, rmRootDirectory, err := osqueryTempDir()
require.NoError(t, err)
defer rmRootDirectory()

k := typesMocks.NewKnapsack(t)
k.On("OsqueryHealthcheckStartupDelay").Return(0 * time.Second).Maybe()
k.On("WatchdogEnabled").Return(false)
k.On("RegisterChangeObserver", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything)
k.On("Slogger").Return(multislogger.NewNopLogger())
k.On("PinnedOsquerydVersion").Return("")

extensionSocketPath := filepath.Join(rootDirectory, "sock")
runner := New(
k,
WithKnapsack(k),
WithRootDirectory(rootDirectory),
WithExtensionSocketPath(extensionSocketPath),
WithOsquerydBinary(testOsqueryBinaryDirectory),
)
go runner.Run()

waitHealthy(t, runner)

// wait for the launcher-provided extension to register
time.Sleep(2 * time.Second)

client, err := osquery.NewClient(extensionSocketPath, 5*time.Second, osquery.DefaultWaitTime(1*time.Second), osquery.MaxWaitTime(1*time.Minute))
require.NoError(t, err)
defer client.Close()

resp, err := client.Query("select * from launcher_gc_info")
require.NoError(t, err)
assert.Equal(t, int32(0), resp.Status.Code)
assert.Equal(t, "OK", resp.Status.Message)

require.NoError(t, runner.Shutdown())
}

// TestRestart tests that the launcher can restart the osqueryd process.
// This test causes time outs on windows, so it is only run on non-windows platforms.
// Should investigate why this is the case.
Comment on lines +128 to +129
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this timeout is related to our prod issue, or unrelated. I think it's reasonable to refactor (as this PR does) and pick that up next PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was thinking the same, merge this then try to dig into this test

func TestRestart(t *testing.T) {
t.Parallel()
runner, teardown := setupOsqueryInstanceForTests(t)
defer teardown()

previousStats := runner.instance.stats

require.NoError(t, runner.Restart())
waitHealthy(t, runner)

require.NotEmpty(t, runner.instance.stats.StartTime, "start time should be set on latest instance stats after restart")
require.NotEmpty(t, runner.instance.stats.ConnectTime, "connect time should be set on latest instance stats after restart")

require.NotEmpty(t, previousStats.ExitTime, "exit time should be set on last instance stats when restarted")
require.NotEmpty(t, previousStats.Error, "stats instance should have an error on restart")

previousStats = runner.instance.stats

require.NoError(t, runner.Restart())
waitHealthy(t, runner)

require.NotEmpty(t, runner.instance.stats.StartTime, "start time should be added to latest instance stats after restart")
require.NotEmpty(t, runner.instance.stats.ConnectTime, "connect time should be added to latest instance stats after restart")

require.NotEmpty(t, previousStats.ExitTime, "exit time should be set on instance stats when restarted")
require.NotEmpty(t, previousStats.Error, "stats instance should have an error on restart")
}