Dockerless PacketImpact

- More hermetic test network setup
- Easier to use/setup without docker

Updates #6018, #6835.

PiperOrigin-RevId: 410039248
This commit is contained in:
Zeling Feng
2021-11-15 11:44:51 -08:00
committed by gVisor bot
parent 2857afc5e4
commit b318556f83
21 changed files with 1368 additions and 31 deletions
+9 -2
View File
@@ -564,6 +564,13 @@ go_repository(
version = "v0.27.0",
)
go_repository(
name = "com_github_google_gopacket",
importpath = "github.com/google/gopacket",
sum = "h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=",
version = "v1.1.19",
)
rules_proto_dependencies()
rules_proto_toolchains()
@@ -1439,8 +1446,8 @@ go_repository(
go_repository(
name = "com_github_vishvananda_netns",
importpath = "github.com/vishvananda/netns",
sum = "h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=",
version = "v0.0.0-20210104183010-2eb08e3e575f",
sum = "h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=",
version = "v0.0.0-20211101163701-50045581ed74",
)
go_repository(
+2 -1
View File
@@ -36,6 +36,7 @@ require (
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/go-github/v35 v35.1.0 // indirect
github.com/google/gofuzz v1.0.0 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20211008130755-947d60d73cc0 // indirect
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8 // indirect
github.com/google/uuid v1.1.2 // indirect
@@ -59,7 +60,7 @@ require (
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 // indirect
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
+4 -2
View File
@@ -304,6 +304,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -533,8 +535,8 @@ github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 h1:7SWt9pGCMaw+N1ZhRsaLKaYNviFhxambdoaoYlDqz1w=
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+1 -1
View File
@@ -7,7 +7,7 @@ RUN apt-get update && apt-get install -y curl gnupg2 git \
openjdk-11-jdk-headless zip unzip \
apt-transport-https ca-certificates gnupg-agent \
software-properties-common \
pkg-config libffi-dev patch diffutils libssl-dev
pkg-config libffi-dev patch diffutils libssl-dev iptables
# Install Docker client for the website build.
RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
+12 -1
View File
@@ -1,4 +1,4 @@
load("//tools:defs.bzl", "cc_binary", "grpcpp")
load("//tools:defs.bzl", "cc_binary", "go_library", "grpcpp")
package(
default_visibility = ["//test/packetimpact:__subpackages__"],
@@ -28,3 +28,14 @@ cc_binary(
"@com_google_absl//absl/strings:str_format",
],
)
go_library(
name = "dut",
testonly = True,
srcs = ["dut.go"],
deps = [
"//test/packetimpact/testbench",
"@org_golang_x_sync//errgroup:go_default_library",
"@org_golang_x_sys//unix:go_default_library",
],
)
+146
View File
@@ -0,0 +1,146 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build linux
// +build linux
// Package dut provides common definitions and utilities to be shared by DUTs.
package dut
import (
"bufio"
"context"
"encoding/json"
"flag"
"fmt"
"io"
"os"
"os/signal"
"strings"
"golang.org/x/sync/errgroup"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/test/packetimpact/testbench"
)
const (
// completeFd is used for notifying the parent for the completion of setup.
completeFd = 3
// PosixServerPort is the port the posix server should listen on.
PosixServerPort = 54321
)
// Ifaces describe the names of the interfaces on DUT.
type Ifaces struct {
// Ctrl is the name of the control interface.
Ctrl string
// Test is the name of the test interface.
Test string
}
// Init puts the current process into the target network namespace, the user of
// this library should call this function in the beginning.
func Init() (Ifaces, error) {
// The DUT might create child processes, we don't want this fd to leak into
// those processes as it keeps the pipe open and the testbench will hang
// waiting for an EOF on the pipe.
unix.CloseOnExec(completeFd)
var ifaces Ifaces
// Parse command line flags. It is effectively the same as using top-level
// functions in flag package, but more explicit that we exit if the parsing
// failed.
fs := flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
fs.StringVar(&ifaces.Ctrl, "ctrl_iface", "", "the name of the control interface")
fs.StringVar(&ifaces.Test, "test_iface", "", "the name of the test interface")
if err := fs.Parse(os.Args[1:]); err != nil {
return Ifaces{}, err
}
return ifaces, nil
}
// DUT is an interface for different platforms of DUTs.
type DUT interface {
// Bootstrap starts a DUT and returns the collected DUTInfo and a function
// for the caller to call to wait for the completion of the DUT.
Bootstrap(ctx context.Context) (testbench.DUTInfo, func() error, error)
// Cleanup stops the DUT and cleans up the resources being used.
Cleanup()
}
// Run is the provided function that calls dut's Bootstrap and Cleanup
// methods and returns the DUT information to the parent through the pipe.
func Run(dut DUT) error {
defer dut.Cleanup()
// Register for cleanup signals.
stopSigs := make(chan os.Signal, 1)
signal.Notify(stopSigs, unix.SIGTERM, unix.SIGINT)
defer signal.Stop(stopSigs)
// Start bootstrapping the DUT.
g, ctx := errgroup.WithContext(context.Background())
ctx, cancel := context.WithCancel(ctx)
defer cancel()
g.Go(func() error {
info, waitFn, err := dut.Bootstrap(ctx)
if err != nil {
return err
}
bytes, err := json.Marshal(info)
if err != nil {
return fmt.Errorf("failed to marshal DUT info into json: %w", err)
}
// Send the DUT information to the parent through the pipe.
completeFile := os.NewFile(completeFd, "complete")
for len(bytes) > 0 {
n, err := completeFile.Write(bytes)
if err != nil && err != io.ErrShortWrite {
return fmt.Errorf("write(%s) = %d, %w", completeFile.Name(), n, err)
}
bytes = bytes[n:]
}
if err := completeFile.Close(); err != nil {
return fmt.Errorf("close(%s) = %w", completeFile.Name(), err)
}
return waitFn()
})
select {
case <-ctx.Done():
// The only reason for our context to be cancelled is the propagation of
// the cancellation from the errgroup g, which means DUT returned an error
// so we report it with g.Wait().
if ctx.Err() == context.Canceled {
return fmt.Errorf("failed to bootstrap DUT: %w", g.Wait())
}
panic(fmt.Sprintf("unknown reason for the context to be cancelled: %s, g.Wait() = %s", ctx.Err(), g.Wait()))
// An signal occurred, we should exit.
case <-stopSigs:
return nil
}
}
// WaitForServer waits for a pattern to occur in posix_server's logs.
func WaitForServer(output io.Reader) error {
// TODO(gvisor.dev/issue/6835): waiting for the server via log output is
// fragile, a better way could be passing a file descriptor, which is not
// possible with docker, do that after the docker runner is removed.
scanner := bufio.NewScanner(output)
for scanner.Scan() {
if text := scanner.Text(); strings.HasPrefix(text, "Server listening on") {
return nil
}
}
return scanner.Err()
}
+17
View File
@@ -0,0 +1,17 @@
load("//tools:defs.bzl", "go_library")
package(
default_visibility = ["//test/packetimpact/dut:__subpackages__"],
licenses = ["notice"],
)
go_library(
name = "linux",
testonly = True,
srcs = ["linux.go"],
deps = [
"//test/packetimpact/dut",
"//test/packetimpact/netdevs/netlink",
"//test/packetimpact/testbench",
],
)
+95
View File
@@ -0,0 +1,95 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build linux
// +build linux
// Package linux provides utilities specific to bringing up linux DUTs.
package linux
import (
"os/exec"
"strings"
"gvisor.dev/gvisor/test/packetimpact/dut"
netdevs "gvisor.dev/gvisor/test/packetimpact/netdevs/netlink"
"gvisor.dev/gvisor/test/packetimpact/testbench"
)
const (
// PosixServerPath is the path to the posix_server.
PosixServerPath = "test/packetimpact/dut/posix_server"
)
func uname() (*testbench.DUTUname, error) {
machine, err := exec.Command("uname", "-m").Output()
if err != nil {
return nil, err
}
kernelRelease, err := exec.Command("uname", "-r").Output()
if err != nil {
return nil, err
}
kernelVersion, err := exec.Command("uname", "-v").Output()
if err != nil {
return nil, err
}
kernelName, err := exec.Command("uname", "-s").Output()
if err != nil {
return nil, err
}
operatingSystem, err := exec.Command("uname", "-o").Output()
if err != nil {
return nil, err
}
return &testbench.DUTUname{
Machine: strings.TrimRight(string(machine), "\n"),
KernelName: strings.TrimRight(string(kernelName), "\n"),
KernelRelease: strings.TrimRight(string(kernelRelease), "\n"),
KernelVersion: strings.TrimRight(string(kernelVersion), "\n"),
OperatingSystem: strings.TrimRight(string(operatingSystem), "\n"),
}, nil
}
// DUTInfo gatthers information about the linux DUT.
func DUTInfo(ifaces dut.Ifaces) (testbench.DUTInfo, error) {
_, ctrlIPv4, _, err := netdevs.IfaceInfo(ifaces.Ctrl)
if err != nil {
return testbench.DUTInfo{}, err
}
testLink, testIPv4, testIPv6, err := netdevs.IfaceInfo(ifaces.Test)
if err != nil {
return testbench.DUTInfo{}, err
}
dutUname, err := uname()
if err != nil {
return testbench.DUTInfo{}, err
}
prefix, _ := testIPv4.Mask.Size()
return testbench.DUTInfo{
Net: &testbench.DUTTestNet{
RemoteIPv6: testIPv6.IP,
RemoteIPv4: testIPv4.IP.To4(),
IPv4PrefixLength: prefix,
RemoteDevID: uint32(testLink.Attrs().Index),
RemoteDevName: ifaces.Test,
POSIXServerIP: ctrlIPv4.IP.To4(),
POSIXServerPort: dut.PosixServerPort,
RemoteMAC: testLink.Attrs().HardwareAddr,
},
Uname: dutUname,
}, nil
}
+22
View File
@@ -0,0 +1,22 @@
load("//tools:defs.bzl", "go_binary")
package(
default_visibility = ["//test/packetimpact:__subpackages__"],
licenses = ["notice"],
)
go_binary(
name = "native",
testonly = True,
srcs = ["main.go"],
data = [
"//test/packetimpact/dut:posix_server",
],
deps = [
"//pkg/test/testutil",
"//test/packetimpact/dut",
"//test/packetimpact/dut/linux",
"//test/packetimpact/testbench",
"@org_golang_x_sys//unix:go_default_library",
],
)
+95
View File
@@ -0,0 +1,95 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build linux && go1.10
// +build linux,go1.10
// The native binary is used to bring up a native linux DUT.
package main
import (
"context"
"fmt"
"log"
"os"
"os/exec"
"strconv"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/test/testutil"
"gvisor.dev/gvisor/test/packetimpact/dut"
"gvisor.dev/gvisor/test/packetimpact/dut/linux"
"gvisor.dev/gvisor/test/packetimpact/testbench"
)
var _ dut.DUT = (*native)(nil)
type native struct {
dut.Ifaces
}
func main() {
ifaces, err := dut.Init()
if err != nil {
log.Fatal(err)
}
if err := dut.Run(&native{
Ifaces: ifaces,
}); err != nil {
log.Fatal(err)
}
}
// Bootstrap implements dut.DUT.
func (n *native) Bootstrap(ctx context.Context) (testbench.DUTInfo, func() error, error) {
// Enable ICMP sockets.
if err := os.WriteFile("/proc/sys/net/ipv4/ping_group_range", []byte("0 0"), 0); err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to enable icmp sockets: %w", err)
}
// Find the posix_server binary.
path, err := testutil.FindFile(linux.PosixServerPath)
if err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to find the posix_server binary: %w", err)
}
// Start the process.
cmd := exec.CommandContext(ctx, path, "--ip", "0.0.0.0", "--port", strconv.FormatUint(dut.PosixServerPort, 10))
cmd.SysProcAttr = &unix.SysProcAttr{
Pdeathsig: unix.SIGKILL,
}
errPipe, err := cmd.StderrPipe()
if err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to create stderr pipe to the posix server process: %w", err)
}
if err := cmd.Start(); err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to start the posix server process: %w", err)
}
if err := dut.WaitForServer(errPipe); err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to wait for the server to listen: %w", err)
}
// Collect DUT information.
info, err := linux.DUTInfo(n.Ifaces)
if err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to collect information about the DUT: %w", err)
}
return info, cmd.Wait, nil
}
// Bootstrap implements dut.DUT
func (*native) Cleanup() {
// For a native DUT case, we only need to cleanup the posix_server process which we set up to
// deliver a SIGKILL signal whenever we exit, so there is nothing to do here.
}
+33
View File
@@ -0,0 +1,33 @@
load("//tools:defs.bzl", "go_binary")
package(
default_visibility = ["//test/packetimpact:__subpackages__"],
licenses = ["notice"],
)
go_binary(
name = "runsc",
testonly = True,
srcs = ["main.go"],
data = [
":devid",
"//runsc",
"//test/packetimpact/dut:posix_server",
],
deps = [
"//pkg/test/testutil",
"//test/packetimpact/dut",
"//test/packetimpact/dut/linux",
"//test/packetimpact/internal/testing",
"//test/packetimpact/testbench",
"@com_github_opencontainers_runtime_spec//specs-go:go_default_library",
"@org_golang_x_sys//unix:go_default_library",
],
)
go_binary(
name = "devid",
testonly = True,
srcs = ["devid.go"],
deps = ["@com_github_vishvananda_netlink//:go_default_library"],
)
+35
View File
@@ -0,0 +1,35 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build linux
// +build linux
// The devid binary is used to get the device ID in the runsc container.
package main
import (
"fmt"
"log"
"os"
"github.com/vishvananda/netlink"
)
func main() {
link, err := netlink.LinkByName(os.Args[1])
if err != nil {
log.Fatalf("could not find the link: %s", err)
}
fmt.Printf("%d", link.Attrs().Index)
}
+191
View File
@@ -0,0 +1,191 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build linux
// +build linux
// The runsc binary is used to bring up a gVisor DUT.
package main
import (
"context"
"fmt"
"log"
"os"
"os/exec"
"strconv"
specs "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/test/testutil"
"gvisor.dev/gvisor/test/packetimpact/dut"
"gvisor.dev/gvisor/test/packetimpact/dut/linux"
"gvisor.dev/gvisor/test/packetimpact/internal/testing"
"gvisor.dev/gvisor/test/packetimpact/testbench"
)
type runsc struct {
dut.Ifaces
containerID string
runscPath string
runscLogsPath string
bundleDir string
rootDir string
cleanupRootDir func()
cleanupBundleDir func()
}
var _ dut.DUT = (*runsc)(nil)
func main() {
ifaces, err := dut.Init()
if err != nil {
log.Fatal(err)
}
// Find the path to the binaries.
posixServerPath, err := testutil.FindFile(linux.PosixServerPath)
if err != nil {
log.Fatalf("failed to find posix_server binary: %s", err)
}
runscPath, err := testutil.FindFile("runsc/runsc")
if err != nil {
log.Fatalf("failed to find runsc binary: %s", err)
}
// Create the OCI spec for the container with posix_server as the entrypoint.
spec := testutil.NewSpecWithArgs(posixServerPath, "--ip", "0.0.0.0", "--port", strconv.FormatUint(dut.PosixServerPort, 10))
pwd, err := os.Getwd()
if err != nil {
log.Fatalf("failed to get the current working directory: %s", err)
}
spec.Process.Cwd = pwd
if spec.Linux == nil {
spec.Linux = &specs.Linux{}
}
// Use the DUT namespace which is the current namespace's.
spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.LinuxNamespace{
Type: "network",
Path: fmt.Sprintf("/proc/%d/ns/net", os.Getpid()),
})
// Prepare logs.
runscLogPath, err := testing.UndeclaredOutput("runsc.%%TIMESTAMP%%.%%COMMAND%%.log")
if err != nil {
log.Fatalf("failed to create runsc log file: %s", err)
}
// Build the command to start runsc container.
bundleDir, cleanupBundleDir, err := testutil.SetupBundleDir(spec)
if err != nil {
log.Fatalf("failed to create bundle dir: %s", err)
}
rootDir, cleanupRootDir, err := testutil.SetupRootDir()
if err != nil {
cleanupBundleDir()
log.Fatalf("SetupRootDir failed: %v", err)
}
if err := dut.Run(&runsc{
Ifaces: ifaces,
containerID: testutil.RandomContainerID(),
runscPath: runscPath,
runscLogsPath: runscLogPath,
bundleDir: bundleDir,
rootDir: rootDir,
cleanupRootDir: cleanupRootDir,
cleanupBundleDir: cleanupBundleDir,
}); err != nil {
log.Fatal(err)
}
}
// Bootstrap implements dut.DUT.
func (r *runsc) Bootstrap(ctx context.Context) (testbench.DUTInfo, func() error, error) {
// runsc will flush the addresses so we collect the info before we start it.
info, err := linux.DUTInfo(r.Ifaces)
if err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to collect information about the DUT: %w", err)
}
// Start posix_server inside a runsc container.
cmd := exec.CommandContext(
ctx,
r.runscPath,
"-root", r.rootDir,
"-network=sandbox",
"-debug",
"-debug-log", r.runscLogsPath,
"-log-format=text",
"-TESTONLY-unsafe-nonroot=true",
"-net-raw=true",
fmt.Sprintf("-panic-signal=%d", unix.SIGTERM),
"-watchdog-action=panic",
"run",
"-bundle", r.bundleDir,
r.containerID,
)
errPipe, err := cmd.StderrPipe()
if err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to create stderr pipe to the posix server process: %w", err)
}
if err := cmd.Start(); err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to start the posix server process: %w", err)
}
if err := dut.WaitForServer(errPipe); err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to wait for the server to listen: %w", err)
}
// runsc will keep using the assigned ip and mac addresses, but the device
// id could have changed, we need to figure it out.
remoteDevID, err := r.remoteDevID()
if err != nil {
return testbench.DUTInfo{}, nil, fmt.Errorf("failed to get test dev id: %w", err)
}
info.Net.RemoteDevID = remoteDevID
return info, cmd.Wait, nil
}
// Cleanup implements dut.DUT.
func (r *runsc) Cleanup() {
r.cleanupRootDir()
r.cleanupBundleDir()
}
// remoteDevID gets the id of the test interface inside the runsc container.
func (r *runsc) remoteDevID() (uint32, error) {
runscDevIDPath, err := testutil.FindFile("test/packetimpact/dut/runsc/devid")
if err != nil {
return 0, fmt.Errorf("failed to find binary runsc_devid: %w", err)
}
cmd := exec.Command(
r.runscPath,
"-root",
r.rootDir,
"-TESTONLY-unsafe-nonroot=true",
"exec",
r.containerID,
runscDevIDPath,
r.Ifaces.Test,
)
bytes, err := cmd.CombinedOutput()
output := string(bytes)
if err != nil {
return 0, fmt.Errorf("failed to get the remote device id: %w, output: %s", err, output)
}
id, err := strconv.ParseUint(output, 10, 32)
if err != nil {
return 0, fmt.Errorf("%s is not a number: %w", output, err)
}
return uint32(id), nil
}
+12
View File
@@ -0,0 +1,12 @@
load("//tools:defs.bzl", "go_library")
package(
default_visibility = ["//test/packetimpact:__subpackages__"],
licenses = ["notice"],
)
go_library(
name = "testing",
testonly = True,
srcs = ["testing.go"],
)
@@ -0,0 +1,31 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package testing provides common testing functionalities.
package testing
import (
"fmt"
"os"
"path/filepath"
)
// UndeclaredOutput creates a path under the undeclared outputs directory.
func UndeclaredOutput(name string) (string, error) {
const testUndeclaredOutputsDir = "TEST_UNDECLARED_OUTPUTS_DIR"
if dir, ok := os.LookupEnv(testUndeclaredOutputsDir); ok {
return filepath.Join(dir, name), nil
}
return "", fmt.Errorf("no %s env var", testUndeclaredOutputsDir)
}
+18
View File
@@ -0,0 +1,18 @@
load("//tools:defs.bzl", "go_library")
package(
licenses = ["notice"],
)
go_library(
name = "netlink",
testonly = True,
srcs = ["netlink.go"],
visibility = [
"//test/packetimpact:__subpackages__",
],
deps = [
"@com_github_vishvananda_netlink//:go_default_library",
"@org_golang_x_sys//unix:go_default_library",
],
)
@@ -0,0 +1,51 @@
// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build linux
// +build linux
// Package netlink has routines to get interfaces information through netlink.
package netlink
import (
"fmt"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
)
// IfaceInfo returns the device with its IPv4 and IPv6 addresses. An error is
// returned if the device is not present or there are no or more than 1 ip addr
// per address family.
func IfaceInfo(name string) (netlink.Link, netlink.Addr, netlink.Addr, error) {
link, err := netlink.LinkByName(name)
if err != nil {
return nil, netlink.Addr{}, netlink.Addr{}, fmt.Errorf("failed to get interface %s: %w", name, err)
}
ipv4Addrs, err := netlink.AddrList(link, unix.AF_INET)
if err != nil {
return nil, netlink.Addr{}, netlink.Addr{}, fmt.Errorf("failed to get ipv4 addrs: %w", err)
}
if len(ipv4Addrs) != 1 {
return nil, netlink.Addr{}, netlink.Addr{}, fmt.Errorf("expected 1 ipv4 addresses, got %d", len(ipv4Addrs))
}
ipv6Addrs, err := netlink.AddrList(link, unix.AF_INET6)
if err != nil {
return nil, netlink.Addr{}, netlink.Addr{}, fmt.Errorf("failed to get ipv6 addrs: %w", err)
}
if len(ipv6Addrs) != 1 {
return nil, netlink.Addr{}, netlink.Addr{}, fmt.Errorf("expected 1 ipv6 addresses, got %d", len(ipv6Addrs))
}
return link, ipv4Addrs[0], ipv6Addrs[0], nil
}
+18 -1
View File
@@ -1,4 +1,4 @@
load("//tools:defs.bzl", "bzl_library", "go_library", "go_test")
load("//tools:defs.bzl", "bzl_library", "go_binary", "go_library", "go_test")
package(
default_visibility = ["//test/packetimpact:__subpackages__"],
@@ -36,3 +36,20 @@ go_library(
"@com_github_docker_docker//api/types/mount:go_default_library",
],
)
go_binary(
name = "main",
testonly = True,
srcs = ["main.go"],
deps = [
"//test/packetimpact/internal/testing",
"//test/packetimpact/netdevs/netlink",
"//test/packetimpact/testbench",
"@com_github_google_gopacket//:go_default_library",
"@com_github_google_gopacket//layers:go_default_library",
"@com_github_google_gopacket//pcapgo:go_default_library",
"@com_github_vishvananda_netlink//:go_default_library",
"@org_golang_x_sync//errgroup:go_default_library",
"@org_golang_x_sys//unix:go_default_library",
],
)
+68 -23
View File
@@ -3,8 +3,12 @@
load("//tools:defs.bzl", "go_test")
def _packetimpact_test_impl(ctx):
test_runner = ctx.executable._test_runner
test_runner = ctx.executable.test_runner
bench = ctx.actions.declare_file("%s-bench" % ctx.label.name)
dut_binary_flag = [] if ctx.attr.dut_binary == None else [
"--dut_binary",
ctx.file.dut_binary.short_path,
]
bench_content = "\n".join([
"#!/bin/bash",
# This test will run part in a distinct user namespace. This can cause
@@ -14,7 +18,7 @@ def _packetimpact_test_impl(ctx):
"find . -type f -or -type d -exec chmod a+rx {} \\;",
"%s %s --testbench_binary %s --num_duts %d $@\n" % (
test_runner.short_path,
" ".join(ctx.attr.flags),
" ".join(ctx.attr.flags + dut_binary_flag),
ctx.files.testbench_binary[0].short_path,
ctx.attr.num_duts,
),
@@ -22,9 +26,11 @@ def _packetimpact_test_impl(ctx):
ctx.actions.write(bench, bench_content, is_executable = True)
transitive_files = []
if hasattr(ctx.attr._test_runner, "data_runfiles"):
transitive_files.append(ctx.attr._test_runner.data_runfiles.files)
files = [test_runner] + ctx.files.testbench_binary + ctx.files._posix_server
if hasattr(ctx.attr.test_runner, "data_runfiles"):
transitive_files.append(ctx.attr.test_runner.data_runfiles.files)
if hasattr(ctx.attr.dut_binary, "data_runfiles"):
transitive_files.append(ctx.attr.dut_binary.data_runfiles.files)
files = [test_runner] + ctx.files.testbench_binary + ctx.files._posix_server + ctx.files.dut_binary
runfiles = ctx.runfiles(
files = files,
transitive_files = depset(transitive = transitive_files),
@@ -35,7 +41,7 @@ def _packetimpact_test_impl(ctx):
_packetimpact_test = rule(
attrs = {
"_test_runner": attr.label(
"test_runner": attr.label(
executable = True,
cfg = "target",
default = ":packetimpact_test",
@@ -56,6 +62,11 @@ _packetimpact_test = rule(
mandatory = False,
default = 1,
),
"dut_binary": attr.label(
executable = True,
cfg = "target",
allow_single_file = True,
),
},
test = True,
implementation = _packetimpact_test_impl,
@@ -71,6 +82,7 @@ def packetimpact_native_test(
name,
testbench_binary,
expect_failure = False,
legacy_runner = False,
**kwargs):
"""Add a native packetimpact test.
@@ -78,21 +90,34 @@ def packetimpact_native_test(
name: name of the test
testbench_binary: the testbench binary
expect_failure: the test must fail
legacy_runner: use the legacy docker runner
**kwargs: all the other args, forwarded to _packetimpact_test
"""
expect_failure_flag = ["--expect_failure"] if expect_failure else []
_packetimpact_test(
name = name + "_native_test",
testbench_binary = testbench_binary,
flags = ["--native"] + expect_failure_flag,
tags = PACKETIMPACT_TAGS,
**kwargs
)
if legacy_runner:
_packetimpact_test(
name = name + "_native_test",
testbench_binary = testbench_binary,
flags = ["--native"] + expect_failure_flag,
tags = PACKETIMPACT_TAGS,
**kwargs
)
else:
_packetimpact_test(
test_runner = "//test/packetimpact/runner:main",
name = name + "_native_test",
testbench_binary = testbench_binary,
flags = expect_failure_flag + ["--variant", "native"],
dut_binary = "//test/packetimpact/dut/native",
tags = PACKETIMPACT_TAGS,
**kwargs
)
def packetimpact_netstack_test(
name,
testbench_binary,
expect_failure = False,
legacy_runner = False,
**kwargs):
"""Add a packetimpact test on netstack.
@@ -100,22 +125,34 @@ def packetimpact_netstack_test(
name: name of the test
testbench_binary: the testbench binary
expect_failure: the test must fail
legacy_runner: use the legacy docker runner
**kwargs: all the other args, forwarded to _packetimpact_test
"""
expect_failure_flag = []
if expect_failure:
expect_failure_flag = ["--expect_failure"]
_packetimpact_test(
name = name + "_netstack_test",
testbench_binary = testbench_binary,
# Note that a distinct runtime must be provided in the form
# --test_arg=--runtime=other when invoking bazel.
flags = expect_failure_flag,
tags = PACKETIMPACT_TAGS,
**kwargs
)
if legacy_runner:
_packetimpact_test(
name = name + "_netstack_test",
testbench_binary = testbench_binary,
# Note that a distinct runtime must be provided in the form
# --test_arg=--runtime=other when invoking bazel.
flags = expect_failure_flag,
tags = PACKETIMPACT_TAGS,
**kwargs
)
else:
_packetimpact_test(
test_runner = "//test/packetimpact/runner:main",
name = name + "_netstack_test",
testbench_binary = testbench_binary,
flags = expect_failure_flag + ["--variant", "gvisor"],
dut_binary = "//test/packetimpact/dut/runsc",
tags = PACKETIMPACT_TAGS,
**kwargs
)
def packetimpact_go_test(name, expect_native_failure = False, expect_netstack_failure = False, num_duts = 1, **kwargs):
def packetimpact_go_test(name, expect_native_failure = False, expect_netstack_failure = False, num_duts = 1, legacy_runner = False, **kwargs):
"""Add packetimpact tests written in go.
Args:
@@ -123,6 +160,7 @@ def packetimpact_go_test(name, expect_native_failure = False, expect_netstack_fa
expect_native_failure: the test must fail natively
expect_netstack_failure: the test must fail for Netstack
num_duts: how many DUTs are needed for the test
legacy_runner: use the legacy docker runner
**kwargs: all the other args, forwarded to packetimpact_native_test and packetimpact_netstack_test
"""
testbench_binary = name + "_test"
@@ -131,6 +169,7 @@ def packetimpact_go_test(name, expect_native_failure = False, expect_netstack_fa
expect_failure = expect_native_failure,
num_duts = num_duts,
testbench_binary = testbench_binary,
legacy_runner = legacy_runner,
**kwargs
)
packetimpact_netstack_test(
@@ -138,6 +177,7 @@ def packetimpact_go_test(name, expect_native_failure = False, expect_netstack_fa
expect_failure = expect_netstack_failure,
num_duts = num_duts,
testbench_binary = testbench_binary,
legacy_runner = legacy_runner,
**kwargs
)
@@ -169,6 +209,7 @@ PacketimpactTestInfo = provider(
"timeout",
"expect_netstack_failure",
"num_duts",
"legacy_runner",
],
)
@@ -293,6 +334,10 @@ ALL_TESTS = [
PacketimpactTestInfo(
name = "generic_dgram_socket_send_recv",
timeout = "long",
# This test has assumed the presense of the default interface and the
# default route installed by docker, using the docker until the test
# is migrated.
legacy_runner = True,
),
]
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More