Files
coder/cli/vpndaemon_windows_linux_shared_test.go
Michael Suchacz adc7775405 feat(vpn): add Linux support for vpn-daemon and OS networking stack (#22051)
This change adds Linux support for Desktop VPN by aligning Linux
behavior with the existing Windows daemon implementation and adding a
Linux networking stack implementation.

### What changed
- Consolidated the daemon command implementation into a shared file:
  - `cli/vpndaemon_windows_linux.go` (`//go:build windows || linux`)
- Consolidated daemon tests into a shared file:
- `cli/vpndaemon_windows_linux_test.go` (`//go:build windows || linux`)
- Removed Linux-only duplicate daemon files:
  - `cli/vpndaemon_linux.go`
  - `cli/vpndaemon_linux_test.go`
- Removed unsupported-platform stubs per current supported OS targets:
  - `cli/vpndaemon_other.go`
  - `vpn/tun.go`
- Kept Linux networking stack implementation in:
  - `vpn/tun_linux.go`

### Notes
- Linux now uses the same `rpc-read-handle` / `rpc-write-handle` flags
and env vars as Windows.
- The daemon logs to stderr (via CLI logger sinks), and does not forward
logs over the RPC pipe.
2026-02-12 12:14:56 +01:00

106 lines
2.7 KiB
Go

//go:build windows || linux
package cli_test
import (
"fmt"
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/testutil"
)
func TestVPNDaemonRun(t *testing.T) {
t.Parallel()
t.Run("InvalidFlags", func(t *testing.T) {
t.Parallel()
cases := []struct {
Name string
Args []string
ErrorContains string
}{
{
Name: "NoReadHandle",
Args: []string{"--rpc-write-handle", "10"},
ErrorContains: "rpc-read-handle",
},
{
Name: "NoWriteHandle",
Args: []string{"--rpc-read-handle", "10"},
ErrorContains: "rpc-write-handle",
},
{
Name: "NegativeReadHandle",
Args: []string{"--rpc-read-handle", "-1", "--rpc-write-handle", "10"},
ErrorContains: "rpc-read-handle",
},
{
Name: "NegativeWriteHandle",
Args: []string{"--rpc-read-handle", "10", "--rpc-write-handle", "-1"},
ErrorContains: "rpc-write-handle",
},
{
Name: "SameHandles",
Args: []string{"--rpc-read-handle", "10", "--rpc-write-handle", "10"},
ErrorContains: "rpc-read-handle",
},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
inv, _ := clitest.New(t, append([]string{"vpn-daemon", "run"}, c.Args...)...)
err := inv.WithContext(ctx).Run()
require.ErrorContains(t, err, c.ErrorContains)
})
}
})
t.Run("StartsTunnel", func(t *testing.T) {
t.Parallel()
r1, w1, err := os.Pipe()
require.NoError(t, err)
defer w1.Close()
r2, w2, err := os.Pipe()
require.NoError(t, err)
defer r2.Close()
// The daemon closes the handles passed via NewBidirectionalPipe. Since our
// CLI tests run in-process, pass duplicated handles so we can close the
// originals without risking a double-close on FD reuse.
rpcReadHandle := dupHandle(t, r1)
rpcWriteHandle := dupHandle(t, w2)
require.NoError(t, r1.Close())
require.NoError(t, w2.Close())
ctx := testutil.Context(t, testutil.WaitLong)
inv, _ := clitest.New(t,
"vpn-daemon",
"run",
"--rpc-read-handle",
fmt.Sprint(rpcReadHandle),
"--rpc-write-handle",
fmt.Sprint(rpcWriteHandle),
)
waiter := clitest.StartWithWaiter(t, inv.WithContext(ctx))
// Send an invalid header, including a newline delimiter, so the handshake
// fails without requiring context cancellation.
_, err = w1.Write([]byte("garbage\n"))
require.NoError(t, err)
err = waiter.Wait()
require.ErrorContains(t, err, "handshake failed")
})
// TODO: once the VPN tunnel functionality is implemented, add tests that
// actually try to instantiate a tunnel to a workspace
}