tstest/integration/vms,.github/workflows: bump Ubuntu and NixOS for VM tests + cleanup

This PR cleans up a bunch of things in ./tstest/integration/vms:

- Bumps version of Ubuntu that's actually run from CI 20.04 -> 24.04
- Removes Ubuntu 18.04 test
- Bumps NixOS 21.05 -> 25.05

Updates#cleanup

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
This commit is contained in:
Irbe Krumina 2025-05-27 11:00:52 +01:00
parent cd49faa123
commit c7e33aa4dd
9 changed files with 15 additions and 205 deletions

View File

@ -207,7 +207,7 @@ jobs:
- name: checkout - name: checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Run VM tests - name: Run VM tests
run: ./tool/go test ./tstest/integration/vms -v -no-s3 -run-vm-tests -run=TestRunUbuntu2004 run: ./tool/go test ./tstest/integration/vms -v -no-s3 -run-vm-tests -run=TestRunUbuntu2404
env: env:
HOME: "/var/lib/ghrunner/home" HOME: "/var/lib/ghrunner/home"
TMPDIR: "/tmp" TMPDIR: "/tmp"

View File

@ -1,7 +1,6 @@
# End-to-End VM-based Integration Testing # End-to-End VM-based Integration Testing
This test spins up a bunch of common linux distributions and then tries to get These tests spin up a Tailscale client in a Linux VM and try to connect it to
them to connect to a
[`testcontrol`](https://pkg.go.dev/tailscale.com/tstest/integration/testcontrol) [`testcontrol`](https://pkg.go.dev/tailscale.com/tstest/integration/testcontrol)
server. server.
@ -55,26 +54,6 @@ If you pass the `-no-s3` flag to `go test`, the S3 step will be skipped in favor
of downloading the images directly from upstream sources, which may cause the of downloading the images directly from upstream sources, which may cause the
test to fail in odd places. test to fail in odd places.
### Distribution Picking
This test runs on a large number of distributions. By default it tries to run
everything, which may or may not be ideal for you. If you only want to test a
subset of distributions, you can use the `--distro-regex` flag to match a subset
of distributions using a [regular expression](https://golang.org/pkg/regexp/)
such as like this:
```console
$ go test -run-vm-tests -distro-regex centos
```
This would run all tests on all versions of CentOS.
```console
$ go test -run-vm-tests -distro-regex '(debian|ubuntu)'
```
This would run all tests on all versions of Debian and Ubuntu.
### Ram Limiting ### Ram Limiting
This test uses a lot of memory. In order to avoid making machines run out of This test uses a lot of memory. In order to avoid making machines run out of

View File

@ -12,24 +12,16 @@
// /var/log/cloud-init-output.log for what you messed up. // /var/log/cloud-init-output.log for what you messed up.
[ [
{ {
"Name": "ubuntu-18-04", "Name": "ubuntu-24-04",
"URL": "https://cloud-images.ubuntu.com/releases/bionic/release-20210817/ubuntu-18.04-server-cloudimg-amd64.img", "URL": "https://cloud-images.ubuntu.com/noble/20250523/noble-server-cloudimg-amd64.img",
"SHA256Sum": "1ee1039f0b91c8367351413b5b5f56026aaf302fd5f66f17f8215132d6e946d2", "SHA256Sum": "0e865619967706765cdc8179fb9929202417ab3a0719d77d8c8942d38aa9611b",
"MemoryMegs": 512, "MemoryMegs": 512,
"PackageManager": "apt", "PackageManager": "apt",
"InitSystem": "systemd" "InitSystem": "systemd"
}, },
{ {
"Name": "ubuntu-20-04", "Name": "nixos-25-05",
"URL": "https://cloud-images.ubuntu.com/releases/focal/release-20210819/ubuntu-20.04-server-cloudimg-amd64.img", "URL": "channel:nixos-25.05",
"SHA256Sum": "99e25e6e344e3a50a081235e825937238a3d51b099969e107ef66f0d3a1f955e",
"MemoryMegs": 512,
"PackageManager": "apt",
"InitSystem": "systemd"
},
{
"Name": "nixos-21-11",
"URL": "channel:nixos-21.11",
"SHA256Sum": "lolfakesha", "SHA256Sum": "lolfakesha",
"MemoryMegs": 512, "MemoryMegs": 512,
"PackageManager": "nix", "PackageManager": "nix",

View File

@ -97,7 +97,7 @@ let
# Wrap tailscaled with the ip and iptables commands. # Wrap tailscaled with the ip and iptables commands.
wrapProgram $out/bin/tailscaled --prefix PATH : ${ wrapProgram $out/bin/tailscaled --prefix PATH : ${
lib.makeBinPath [ iproute iptables ] lib.makeBinPath [ iproute2 iptables ]
} }
# Install systemd unit. # Install systemd unit.
@ -127,6 +127,9 @@ in {
# yolo, this vm can sudo freely. # yolo, this vm can sudo freely.
security.sudo.wheelNeedsPassword = false; security.sudo.wheelNeedsPassword = false;
# nix considers squid insecure, but this is fine for a test.
nixpkgs.config.permittedInsecurePackages = [ "squid-7.0.1" ];
# Enable cloud-init so we can set VM hostnames and the like the same as other # Enable cloud-init so we can set VM hostnames and the like the same as other
# distros. This will also take care of SSH keys. It's pretty handy. # distros. This will also take care of SSH keys. It's pretty handy.
services.cloud-init = { services.cloud-init = {

View File

@ -1,85 +0,0 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !windows && !plan9
package vms
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"github.com/google/uuid"
)
/*
The images that we use for OpenSUSE Leap 15.1 have an issue that makes the
nocloud backend[1] for cloud-init just not work. As a distro-specific
workaround, we're gonna pretend to be OpenStack.
TODO(Xe): delete once we no longer need to support OpenSUSE Leap 15.1.
[1]: https://cloudinit.readthedocs.io/en/latest/topics/datasources/nocloud.html
*/
type openSUSELeap151MetaData struct {
Zone string `json:"availability_zone"` // nova
Hostname string `json:"hostname"` // opensuse-leap-15-1
LaunchIndex string `json:"launch_index"` // 0
Meta openSUSELeap151MetaDataMeta `json:"meta"` // some openstack metadata we don't need to care about
Name string `json:"name"` // opensuse-leap-15-1
UUID string `json:"uuid"` // e9c664cd-b116-433b-aa61-7ff420163dcd
}
type openSUSELeap151MetaDataMeta struct {
Role string `json:"role"` // server
DSMode string `json:"dsmode"` // local
Essential string `json:"essential"` // essential
}
func hackOpenSUSE151UserData(t *testing.T, d Distro, dir string) bool {
if d.Name != "opensuse-leap-15-1" {
return false
}
t.Log("doing OpenSUSE Leap 15.1 hack")
osDir := filepath.Join(dir, "openstack", "latest")
err := os.MkdirAll(osDir, 0755)
if err != nil {
t.Fatalf("can't make metadata home: %v", err)
}
metadata, err := json.Marshal(openSUSELeap151MetaData{
Zone: "nova",
Hostname: d.Name,
LaunchIndex: "0",
Meta: openSUSELeap151MetaDataMeta{
Role: "server",
DSMode: "local",
Essential: "false",
},
Name: d.Name,
UUID: uuid.New().String(),
})
if err != nil {
t.Fatalf("can't encode metadata: %v", err)
}
err = os.WriteFile(filepath.Join(osDir, "meta_data.json"), metadata, 0666)
if err != nil {
t.Fatalf("can't write to meta_data.json: %v", err)
}
data, err := os.ReadFile(filepath.Join(dir, "user-data"))
if err != nil {
t.Fatalf("can't read user_data: %v", err)
}
err = os.WriteFile(filepath.Join(osDir, "user_data"), data, 0666)
if err != nil {
t.Fatalf("can't create output user_data: %v", err)
}
return true
}

View File

@ -1,29 +0,0 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package vms
import "regexp"
type regexValue struct {
r *regexp.Regexp
}
func (r *regexValue) String() string {
if r.r == nil {
return ""
}
return r.r.String()
}
func (r *regexValue) Set(val string) error {
if rex, err := regexp.Compile(val); err != nil {
return err
} else {
r.r = rex
return nil
}
}
func (r regexValue) Unwrap() *regexp.Regexp { return r.r }

View File

@ -1,21 +0,0 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package vms
import (
"flag"
"testing"
)
func TestRegexFlag(t *testing.T) {
var v regexValue
fs := flag.NewFlagSet(t.Name(), flag.PanicOnError)
fs.Var(&v, "regex", "regex to parse")
const want = `.*`
fs.Parse([]string{"-regex", want})
if v.Unwrap().String() != want {
t.Fatalf("got wrong regex: %q, wanted: %q", v.Unwrap().String(), want)
}
}

View File

@ -14,17 +14,13 @@ import (
expect "github.com/tailscale/goexpect" expect "github.com/tailscale/goexpect"
) )
func TestRunUbuntu1804(t *testing.T) { func TestRunUbuntu2404(t *testing.T) {
testOneDistribution(t, 0, Distros[0]) testOneDistribution(t, 0, Distros[0])
} }
func TestRunUbuntu2004(t *testing.T) { func TestRunNixos2505(t *testing.T) {
testOneDistribution(t, 1, Distros[1])
}
func TestRunNixos2111(t *testing.T) {
t.Parallel() t.Parallel()
testOneDistribution(t, 2, Distros[2]) testOneDistribution(t, 1, Distros[1])
} }
// TestMITMProxy is a smoke test for derphttp through a MITM proxy. // TestMITMProxy is a smoke test for derphttp through a MITM proxy.
@ -39,13 +35,7 @@ func TestRunNixos2111(t *testing.T) {
func TestMITMProxy(t *testing.T) { func TestMITMProxy(t *testing.T) {
t.Parallel() t.Parallel()
setupTests(t) setupTests(t)
distro := Distros[2] // nixos-21.11 distro := Distros[1] // nixos-25.05
if distroRex.Unwrap().MatchString(distro.Name) {
t.Logf("%s matches %s", distro.Name, distroRex.Unwrap())
} else {
t.Skip("regex not matched")
}
ctx, done := context.WithCancel(context.Background()) ctx, done := context.WithCancel(context.Background())
t.Cleanup(done) t.Cleanup(done)

View File

@ -15,7 +15,6 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -43,11 +42,6 @@ var (
useVNC = flag.Bool("use-vnc", false, "if set, display guest vms over VNC") useVNC = flag.Bool("use-vnc", false, "if set, display guest vms over VNC")
verboseLogcatcher = flag.Bool("verbose-logcatcher", true, "if set, print logcatcher to t.Logf") verboseLogcatcher = flag.Bool("verbose-logcatcher", true, "if set, print logcatcher to t.Logf")
verboseQemu = flag.Bool("verbose-qemu", true, "if set, print qemu console to t.Logf") verboseQemu = flag.Bool("verbose-qemu", true, "if set, print qemu console to t.Logf")
distroRex = func() *regexValue {
result := &regexValue{r: regexp.MustCompile(`.*`)}
flag.Var(result, "distro-regex", "The regex that matches what distros should be run")
return result
}()
) )
func TestDownloadImages(t *testing.T) { func TestDownloadImages(t *testing.T) {
@ -59,9 +53,6 @@ func TestDownloadImages(t *testing.T) {
distro := d distro := d
t.Run(distro.Name, func(t *testing.T) { t.Run(distro.Name, func(t *testing.T) {
t.Parallel() t.Parallel()
if !distroRex.Unwrap().MatchString(distro.Name) {
t.Skipf("distro name %q doesn't match regex: %s", distro.Name, distroRex)
}
if strings.HasPrefix(distro.Name, "nixos") { if strings.HasPrefix(distro.Name, "nixos") {
t.Skip("NixOS is built on the fly, no need to download it") t.Skip("NixOS is built on the fly, no need to download it")
} }
@ -175,10 +166,6 @@ func mkSeed(t *testing.T, d Distro, sshKey, hostURL, tdir string, port int) {
filepath.Join(dir, "user-data"), filepath.Join(dir, "user-data"),
} }
if hackOpenSUSE151UserData(t, d, dir) {
args = append(args, filepath.Join(dir, "openstack"))
}
run(t, tdir, "genisoimage", args...) run(t, tdir, "genisoimage", args...)
} }
@ -247,12 +234,6 @@ var ramsem struct {
func testOneDistribution(t *testing.T, n int, distro Distro) { func testOneDistribution(t *testing.T, n int, distro Distro) {
setupTests(t) setupTests(t)
if distroRex.Unwrap().MatchString(distro.Name) {
t.Logf("%s matches %s", distro.Name, distroRex.Unwrap())
} else {
t.Skip("regex not matched")
}
ctx, done := context.WithCancel(context.Background()) ctx, done := context.WithCancel(context.Background())
t.Cleanup(done) t.Cleanup(done)