tstest/integration: run Windows integration tests against the service by default (#20565)

Updates #20464

Signed-off-by: Yaruk Asghar <yaruk@tailscale.com>
This commit is contained in:
yaruk-byte authored and GitHub committed 2026-08-14 22:49:30 -07:00
1 parent 8052bb2c53
commit cfe32b8be6
5 files changed
+75 -31

No files matched your search

+4
View File
@@ -9,6 +9,7 @@
"errors"
"fmt"
"io"
"runtime"
"testing"
"time"
@@ -40,6 +41,9 @@ func TestTaildropIntegration_Fresh(t *testing.T) {
//
// This exercises an ipnext hook ordering issue we hit earlier.
func testTaildropIntegration(t *testing.T, freshProfiles bool) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
tstest.Parallel(t)
controlOpt := integration.ConfigureControl(func(s *testcontrol.Server) {
s.AllNodesSameUser = true // required for Taildrop
+7
View File
@@ -5,6 +5,7 @@
import (
"errors"
"runtime"
"testing"
"time"
@@ -14,6 +15,9 @@
// TestPeerCapMap tests that the node capability map (CapMap) is included in peer information.
func TestPeerCapMap(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
tstest.Parallel(t)
env := NewTestEnv(t)
@@ -96,6 +100,9 @@ func TestPeerCapMap(t *testing.T) {
// TestSetNodeCapMap tests that SetNodeCapMap updates are propagated to peers.
func TestSetNodeCapMap(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
tstest.Parallel(t)
env := NewTestEnv(t)
+14 -29
View File
@@ -9,6 +9,7 @@
import (
"bytes"
"cmp"
"context"
"crypto/tls"
"encoding/json"
@@ -26,6 +27,7 @@
"path/filepath"
"regexp"
"runtime"
"slices"
"strconv"
"strings"
"sync"
@@ -60,8 +62,7 @@
verboseTailscaled = flag.Bool("verbose-tailscaled", false, "verbose tailscaled logging")
verboseTailscale = flag.Bool("verbose-tailscale", false, "verbose tailscale CLI logging")
// runWindowsServiceTests enables the Windows service-mode integration tests.
// On by default in CI; tests opt in via NewTestEnv(t, canRunAsServiceOnWindows()).
// runWindowsServiceTests enables the Windows service-mode integration tests, on by default in CI.
runWindowsServiceTests = flag.Bool("run-windows-service-tests", cibuild.On(), "run Windows service-mode integration tests")
)
@@ -541,37 +542,10 @@ func (f ConfigureControl) ModifyTestEnv(te *TestEnv) {
f(te.Control)
}
// canRunAsServiceOnWindowsOpt is the TestEnvOpt returned by canRunAsServiceOnWindows.
type canRunAsServiceOnWindowsOpt struct{}
func (canRunAsServiceOnWindowsOpt) ModifyTestEnv(te *TestEnv) {
// Only run as a service on Windows; on other platforms the test runs
// the normal userspace daemon with a faked Windows GOOS, as it always has.
if runtime.GOOS == "windows" {
te.windowsService = true
}
}
// canRunAsServiceOnWindows enables the test to run on Windows.
// TODO(#20464): remove this and explicitly skip tests that need more work
// before they can run on Windows, instead of requiring tests to opt in with this option.
func canRunAsServiceOnWindows() TestEnvOpt { return canRunAsServiceOnWindowsOpt{} }
// NewTestEnv starts a bunch of services and returns a new test environment.
// NewTestEnv arranges for the environment's resources to be cleaned up on exit.
func NewTestEnv(t testing.TB, opts ...TestEnvOpt) *TestEnv {
// Integration tests skip on Windows unless a test opts in via canRunAsServiceOnWindows.
// Pre-scan the opts before starting any servers so a skip leaks nothing.
canRunAsService := false
for _, o := range opts {
if _, ok := o.(canRunAsServiceOnWindowsOpt); ok {
canRunAsService = true
}
}
if runtime.GOOS == "windows" {
if !canRunAsService {
t.Skip("integration tests skip on Windows unless the test calls canRunAsServiceOnWindows")
}
if !*runWindowsServiceTests {
t.Skip("Windows service tests disabled (--run-windows-service-tests=false)")
}
@@ -587,6 +561,7 @@ func NewTestEnv(t testing.TB, opts ...TestEnvOpt) *TestEnv {
binaries := GetBinaries(t)
e := &TestEnv{
t: t,
windowsService: runtime.GOOS == "windows",
cli: binaries.Tailscale.Path,
daemon: binaries.Tailscaled.Path,
LogCatcher: logc,
@@ -1130,6 +1105,16 @@ func (n *TestNode) TailscaleForOutput(arg ...string) *exec.Cmd {
// Tailscale returns a command that runs the tailscale CLI with the provided arguments.
// It does not start the process.
func (n *TestNode) Tailscale(arg ...string) *exec.Cmd {
isUp := len(arg) > 0 && arg[0] == "up"
if isUp && cmp.Or(n.upFlagGOOS, runtime.GOOS) == "windows" {
isBareUp := len(arg) == 1
// --unattended keeps the current profile after the CLI exits; without it
// Windows switches to an empty background profile and the node drops to NoState.
// TODO(yaruk): also run tests without --unattended; see #20751.
if !isBareUp && !slices.Contains(arg, "--unattended") {
arg = append(arg, "--unattended")
}
}
cmd := exec.Command(n.env.cli)
cmd.Args = append(cmd.Args, "--socket="+n.sockFile)
cmd.Args = append(cmd.Args, arg...)
+46 -2
View File
@@ -207,6 +207,9 @@ func TestExpectedFeaturesLinked(t *testing.T) {
}
func TestCollectPanic(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("has a Windows panic-capture race; see #20443")
}
tstest.Parallel(t)
env := NewTestEnv(t)
n := NewTestNode(t, env)
@@ -846,6 +849,9 @@ func(control *testcontrol.Server) {
}
func TestConfigFileAuthKey(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("--config is unsupported by the Windows service; see #20871")
}
t.Parallel()
const authKey = "opensesame"
env := NewTestEnv(t, ConfigureControl(func(control *testcontrol.Server) {
@@ -871,6 +877,9 @@ func TestConfigFileAuthKey(t *testing.T) {
}
func TestTwoNodes(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
tstest.Parallel(t)
env := NewTestEnv(t)
@@ -956,6 +965,9 @@ func TestTwoNodes(t *testing.T) {
// tests two nodes where the first gets a incremental MapResponse (with only
// PeersRemoved set) saying that the second node disappeared.
func TestIncrementalMapUpdatePeersRemoved(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
tstest.Parallel(t)
env := NewTestEnv(t)
@@ -1043,6 +1055,9 @@ func TestIncrementalMapUpdatePeersRemoved(t *testing.T) {
// This covers VIP additions at runtime, where the VIP route is not reachable
// before the map mutation but is reachable over TSMP afterward.
func TestIncrementalMapUpdatePeerAllowedIPsReachability(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
tstest.Parallel(t)
env := NewTestEnv(t)
@@ -1268,6 +1283,9 @@ func TestC2NPingRequest(t *testing.T) {
// Issue 2434: when "down" (WantRunning false), tailscaled shouldn't
// be connected to control.
func TestNoControlConnWhenDown(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("restarting the daemon with preserved state needs harness support; see #20750")
}
tstest.Parallel(t)
env := NewTestEnv(t)
n1 := NewTestNode(t, env)
@@ -1317,7 +1335,7 @@ func TestNoControlConnWhenDown(t *testing.T) {
// without the GUI to kick off a Start.
func TestOneNodeUpWindowsStyle(t *testing.T) {
tstest.Parallel(t)
env := NewTestEnv(t, canRunAsServiceOnWindows())
env := NewTestEnv(t)
n1 := NewTestNode(t, env)
n1.upFlagGOOS = "windows"
@@ -1335,6 +1353,9 @@ func TestOneNodeUpWindowsStyle(t *testing.T) {
// jailed node cannot initiate connections to the other node however the other
// node can initiate connections to the jailed node.
func TestClientSideJailing(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
flakytest.Mark(t, "https://github.com/tailscale/tailscale/issues/17419")
tstest.Parallel(t)
env := NewTestEnv(t)
@@ -1447,6 +1468,9 @@ func TestClientSideJailing(t *testing.T) {
// TestNATPing creates two nodes, n1 and n2, sets up masquerades for both and
// tries to do bi-directional pings between them.
func TestNATPing(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
flakytest.Mark(t, "https://github.com/tailscale/tailscale/issues/12169")
tstest.Parallel(t)
for _, v6 := range []bool{false, true} {
@@ -1575,6 +1599,9 @@ func TestNATPing(t *testing.T) {
}
func TestLogoutRemovesAllPeers(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
tstest.Parallel(t)
env := NewTestEnv(t)
// Spin up some nodes.
@@ -1634,6 +1661,9 @@ func TestAutoUpdateDefaults_cap(t *testing.T) { testAutoUpdateDefaults(t, true)
// useCap is whether to use NodeAttrDefaultAutoUpdate (as opposed to the old
// DeprecatedDefaultAutoUpdate top-level MapResponse field).
func testAutoUpdateDefaults(t *testing.T, useCap bool) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
t.Cleanup(feature.HookCanAutoUpdate.SetForTest(func() bool { return true }))
env := NewTestEnv(t)
@@ -2118,6 +2148,9 @@ func TestNetstackUDPLoopback(t *testing.T) {
}
func TestEncryptStateMigration(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("--encrypt-state is unsupported by the Windows service; see #20872")
}
if !hostinfo.New().TPM.Present() {
t.Skip("TPM not available")
}
@@ -2178,6 +2211,9 @@ func TestEncryptStateMigration(t *testing.T) {
// relay between all 3 nodes, and "tailscale debug peer-relay-sessions" returns
// expected values.
func TestPeerRelayPing(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
flakytest.Mark(t, "https://github.com/tailscale/tailscale/issues/17251")
tstest.Parallel(t)
@@ -2318,6 +2354,9 @@ func TestPeerRelayPing(t *testing.T) {
}
func TestC2NDebugNetmap(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
tstest.Parallel(t)
env := NewTestEnv(t, ConfigureControl(func(s *testcontrol.Server) {
s.CollectServices = opt.False
@@ -2456,7 +2495,9 @@ func TestC2NDebugNetmap(t *testing.T) {
}
func TestTailnetLock(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
// If you run `tailscale lock log` on a node where Tailnet Lock isn't
// enabled, you get an error explaining that.
t.Run("log-when-not-enabled", func(t *testing.T) {
@@ -2598,6 +2639,9 @@ func TestTailnetLock(t *testing.T) {
}
func TestNodeWithBadStateFile(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("service harness can't seed a corrupt state file before start; see #20750")
}
tstest.Parallel(t)
env := NewTestEnv(t)
n1 := NewTestNode(t, env)
+4
View File
@@ -8,6 +8,7 @@
"errors"
"fmt"
"net"
"runtime"
"testing"
"time"
@@ -21,6 +22,9 @@
// netstack forwards the connection to localhost, and the listener
// calls WhoIs on n2's LocalAPI to identify the remote peer as n1.
func TestUserspaceWhoIsProxyMap(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("multiple nodes need the userspace-peer harness; see #20711")
}
tstest.Parallel(t)
env := NewTestEnv(t)