diff --git a/.github/workflows/integration-tests-win.yaml b/.github/workflows/integration-tests-win.yaml new file mode 100644 index 0000000000000..69869be553d66 --- /dev/null +++ b/.github/workflows/integration-tests-win.yaml @@ -0,0 +1,68 @@ +name: Integration Tests (Win) +run-name: Integration Tests (Win) - ${{ github.run_id }} - @${{ github.actor }} + +on: + pull_request: + merge_group: + +jobs: + changes: + name: Check for relevant changes + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + changed: ${{ steps.changes.outputs.changed }} + steps: + - name: Checkout + if: ${{ github.event_name == 'merge_group' }} + uses: actions/checkout@v4 + - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + base: ${{ github.event.pull_request.base.ref || github.event.merge_group.base_ref }} + ref: ${{ github.event.pull_request.head.ref || github.event.merge_group.head_ref }} + filters: | + changed: + - '.github/workflows/integration-tests-win.yaml' + - 'lib/autoupdate/tools/**.go' + - 'lib/teleterm/autoupdate/**.go' + - 'lib/utils/packaging/**.go' + - 'integration/autoupdate/tools/**.go' + - 'go.mod' + - 'go.sum' + - 'build.assets/Makefile' + - 'build.assets/Dockerfile*' + - 'Makefile' + + test: + name: Integration Tests (Win) + needs: changes + if: ${{ !startsWith(github.head_ref, 'dependabot/') && needs.changes.outputs.changed == 'true' }} + runs-on: windows-2022-16core + + permissions: + contents: read + packages: read + + steps: + - name: Checkout Teleport + uses: actions/checkout@v4 + + - name: Get Go version + id: go-version + shell: bash + run: echo "go-version=$(make -s print-go-version | tr -d '\n')" >> $GITHUB_OUTPUT + + - name: Setup Go + uses: actions/setup-go@v5 + with: + cache: false + go-version: ${{ steps.go-version.outputs.go-version }} + + - name: Run tests + shell: bash + run: | + export OS="windows" + go test -timeout 30m ./integration/autoupdate/tools -v + diff --git a/integration/autoupdate/tools/helper_unix_test.go b/integration/autoupdate/tools/helper_unix_test.go index 61ba0766b90d4..7828f39546a2c 100644 --- a/integration/autoupdate/tools/helper_unix_test.go +++ b/integration/autoupdate/tools/helper_unix_test.go @@ -22,11 +22,18 @@ package tools_test import ( "errors" + "os/exec" "syscall" "github.com/gravitational/trace" ) +// newCommand creates command depends on platform. +func newCommand(path string, args ...string) *exec.Cmd { + cmd := exec.Command(path, args...) + return cmd +} + // sendInterrupt sends a SIGINT to the process. func sendInterrupt(pid int) error { err := syscall.Kill(pid, syscall.SIGINT) diff --git a/integration/autoupdate/tools/helper_windows_test.go b/integration/autoupdate/tools/helper_windows_test.go index b2ede9ade8c19..fe8269a964924 100644 --- a/integration/autoupdate/tools/helper_windows_test.go +++ b/integration/autoupdate/tools/helper_windows_test.go @@ -21,20 +21,32 @@ package tools_test import ( + "os/exec" "syscall" "github.com/gravitational/trace" - "golang.org/x/sys/windows" ) -var ( - kernel = windows.NewLazyDLL("kernel32.dll") - ctrlEvent = kernel.NewProc("GenerateConsoleCtrlEvent") -) +// newCommand creates command depends on platform. +func newCommand(path string, args ...string) *exec.Cmd { + cmd := exec.Command(path, args...) + cmd.SysProcAttr = &syscall.SysProcAttr{ + CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP, + } + return cmd +} // sendInterrupt sends a Ctrl-Break event to the process. func sendInterrupt(pid int) error { - r, _, err := ctrlEvent.Call(uintptr(syscall.CTRL_BREAK_EVENT), uintptr(pid)) + d, err := syscall.LoadDLL("kernel32.dll") + if err != nil { + return trace.Wrap(err) + } + p, err := d.FindProc("GenerateConsoleCtrlEvent") + if err != nil { + return trace.Wrap(err) + } + r, _, err := p.Call(syscall.CTRL_BREAK_EVENT, uintptr(pid)) if r == 0 { return trace.Wrap(err) } diff --git a/integration/autoupdate/tools/updater_test.go b/integration/autoupdate/tools/updater_test.go index b3d82846c4fc8..42ed74a5a4819 100644 --- a/integration/autoupdate/tools/updater_test.go +++ b/integration/autoupdate/tools/updater_test.go @@ -22,6 +22,7 @@ import ( "bytes" "context" "fmt" + "io" "os" "os/exec" "path/filepath" @@ -211,14 +212,18 @@ func TestUpdateInterruptSignal(t *testing.T) { t.Setenv(types.HomeEnvVar, t.TempDir()) var output bytes.Buffer - cmd := exec.Command(tshPath, "version") - cmd.Stdout = &output - cmd.Stderr = &output + multiOut := io.MultiWriter(&output, os.Stdout) + cmd := newCommand(tshPath, "version") + cmd.Stdout = multiOut + cmd.Stderr = multiOut cmd.Env = append( os.Environ(), fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]), ) err := cmd.Start() + if err != nil { + t.Log(output.String()) + } require.NoError(t, err, "failed to start updater") pid := cmd.Process.Pid @@ -241,7 +246,10 @@ func TestUpdateInterruptSignal(t *testing.T) { require.Fail(t, "failed to wait till the download is started") case <-lock: time.Sleep(100 * time.Millisecond) - require.NoError(t, sendInterrupt(pid)) + t.Logf("sending signal to updater, pid: %d, test pid: %d", pid, os.Getpid()) + err := sendInterrupt(pid) + require.NoError(t, err, "failed to send signal to updater") + time.Sleep(100 * time.Millisecond) lock <- struct{}{} } @@ -254,6 +262,10 @@ func TestUpdateInterruptSignal(t *testing.T) { require.NoError(t, err) } assert.Contains(t, output.String(), "Update progress:") + + matches := pattern.FindStringSubmatch(output.String()) + require.Len(t, matches, 2) + require.Equal(t, testVersions[0], matches[1]) } // TestUpdateForOSSBuild verifies the update logic for AGPL editions of Teleport requires @@ -273,6 +285,9 @@ func TestUpdateForOSSBuild(t *testing.T) { fmt.Sprintf("%s=%s", teleportToolsVersion, testVersions[1]), ) out, err := cmd.Output() + if err != nil { + t.Log(string(out)) + } require.NoError(t, err) matchVersion(t, string(out), testVersions[0])