2020-05-27 00:39:19 +00:00
|
|
|
// Copyright 2020 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.
|
|
|
|
|
|
|
|
// The runner starts docker containers and networking for a packetimpact test.
|
|
|
|
package packetimpact_test
|
|
|
|
|
|
|
|
import (
|
2020-07-08 20:24:58 +00:00
|
|
|
"context"
|
2020-05-27 00:39:19 +00:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
2020-05-28 21:42:01 +00:00
|
|
|
"io/ioutil"
|
2020-05-27 00:39:19 +00:00
|
|
|
"log"
|
|
|
|
"math/rand"
|
|
|
|
"net"
|
2020-05-28 21:42:01 +00:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
2020-05-27 00:39:19 +00:00
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
"github.com/docker/docker/api/types/mount"
|
2020-05-27 00:39:19 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/test/dockerutil"
|
|
|
|
"gvisor.dev/gvisor/test/packetimpact/netdevs"
|
|
|
|
)
|
|
|
|
|
|
|
|
// stringList implements flag.Value.
|
|
|
|
type stringList []string
|
|
|
|
|
|
|
|
// String implements flag.Value.String.
|
|
|
|
func (l *stringList) String() string {
|
|
|
|
return strings.Join(*l, ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set implements flag.Value.Set.
|
|
|
|
func (l *stringList) Set(value string) error {
|
|
|
|
*l = append(*l, value)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
dutPlatform = flag.String("dut_platform", "", "either \"linux\" or \"netstack\"")
|
|
|
|
testbenchBinary = flag.String("testbench_binary", "", "path to the testbench binary")
|
|
|
|
tshark = flag.Bool("tshark", false, "use more verbose tshark in logs instead of tcpdump")
|
|
|
|
extraTestArgs = stringList{}
|
|
|
|
expectFailure = flag.Bool("expect_failure", false, "expect that the test will fail when run")
|
|
|
|
|
|
|
|
dutAddr = net.IPv4(0, 0, 0, 10)
|
|
|
|
testbenchAddr = net.IPv4(0, 0, 0, 20)
|
|
|
|
)
|
|
|
|
|
|
|
|
const ctrlPort = "40000"
|
|
|
|
|
|
|
|
// logger implements testutil.Logger.
|
|
|
|
//
|
|
|
|
// Labels logs based on their source and formats multi-line logs.
|
|
|
|
type logger string
|
|
|
|
|
|
|
|
// Name implements testutil.Logger.Name.
|
|
|
|
func (l logger) Name() string {
|
|
|
|
return string(l)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Logf implements testutil.Logger.Logf.
|
|
|
|
func (l logger) Logf(format string, args ...interface{}) {
|
|
|
|
lines := strings.Split(fmt.Sprintf(format, args...), "\n")
|
|
|
|
log.Printf("%s: %s", l, lines[0])
|
|
|
|
for _, line := range lines[1:] {
|
|
|
|
log.Printf("%*s %s", len(l), "", line)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestOne(t *testing.T) {
|
|
|
|
flag.Var(&extraTestArgs, "extra_test_arg", "extra arguments to pass to the testbench")
|
|
|
|
flag.Parse()
|
|
|
|
if *dutPlatform != "linux" && *dutPlatform != "netstack" {
|
|
|
|
t.Fatal("--dut_platform should be either linux or netstack")
|
|
|
|
}
|
|
|
|
if *testbenchBinary == "" {
|
|
|
|
t.Fatal("--testbench_binary is missing")
|
|
|
|
}
|
|
|
|
if *dutPlatform == "netstack" {
|
|
|
|
if _, err := dockerutil.RuntimePath(); err != nil {
|
|
|
|
t.Fatal("--runtime is missing or invalid with --dut_platform=netstack:", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dockerutil.EnsureSupportedDockerVersion()
|
2020-07-08 20:24:58 +00:00
|
|
|
ctx := context.Background()
|
2020-05-27 00:39:19 +00:00
|
|
|
|
|
|
|
// Create the networks needed for the test. One control network is needed for
|
|
|
|
// the gRPC control packets and one test network on which to transmit the test
|
|
|
|
// packets.
|
2020-07-08 20:24:58 +00:00
|
|
|
ctrlNet := dockerutil.NewNetwork(ctx, logger("ctrlNet"))
|
|
|
|
testNet := dockerutil.NewNetwork(ctx, logger("testNet"))
|
|
|
|
for _, dn := range []*dockerutil.Network{ctrlNet, testNet} {
|
2020-05-27 00:39:19 +00:00
|
|
|
for {
|
2020-07-08 20:24:58 +00:00
|
|
|
if err := createDockerNetwork(ctx, dn); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
t.Log("creating docker network:", err)
|
|
|
|
const wait = 100 * time.Millisecond
|
|
|
|
t.Logf("sleeping %s and will try creating docker network again", wait)
|
|
|
|
// This can fail if another docker network claimed the same IP so we'll
|
|
|
|
// just try again.
|
|
|
|
time.Sleep(wait)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
2020-07-08 20:24:58 +00:00
|
|
|
defer func(dn *dockerutil.Network) {
|
|
|
|
if err := dn.Cleanup(ctx); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
t.Errorf("unable to cleanup container %s: %s", dn.Name, err)
|
|
|
|
}
|
|
|
|
}(dn)
|
2020-07-08 20:24:58 +00:00
|
|
|
// Sanity check.
|
|
|
|
inspect, err := dn.Inspect(ctx)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to inspect network %s: %v", dn.Name, err)
|
|
|
|
} else if inspect.Name != dn.Name {
|
|
|
|
t.Fatalf("name mismatch for network want: %s got: %s", dn.Name, inspect.Name)
|
|
|
|
}
|
|
|
|
|
2020-05-27 00:39:19 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 21:42:01 +00:00
|
|
|
tmpDir, err := ioutil.TempDir("", "container-output")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("creating temp dir:", err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
|
|
|
|
const testOutputDir = "/tmp/testoutput"
|
|
|
|
|
2020-05-27 00:39:19 +00:00
|
|
|
// Create the Docker container for the DUT.
|
2020-07-08 20:24:58 +00:00
|
|
|
dut := dockerutil.MakeContainer(ctx, logger("dut"))
|
2020-05-27 00:39:19 +00:00
|
|
|
if *dutPlatform == "linux" {
|
|
|
|
dut.Runtime = ""
|
|
|
|
}
|
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
runOpts := dockerutil.RunOpts{
|
|
|
|
Image: "packetimpact",
|
|
|
|
CapAdd: []string{"NET_ADMIN"},
|
|
|
|
Mounts: []mount.Mount{mount.Mount{
|
|
|
|
Type: mount.TypeBind,
|
|
|
|
Source: tmpDir,
|
|
|
|
Target: testOutputDir,
|
|
|
|
ReadOnly: false,
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
2020-05-27 00:39:19 +00:00
|
|
|
const containerPosixServerBinary = "/packetimpact/posix_server"
|
2020-06-18 01:41:55 +00:00
|
|
|
dut.CopyFiles(&runOpts, "/packetimpact", "/test/packetimpact/dut/posix_server")
|
2020-05-27 00:39:19 +00:00
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
conf, hostconf, _ := dut.ConfigsFrom(runOpts, containerPosixServerBinary, "--ip=0.0.0.0", "--port="+ctrlPort)
|
|
|
|
hostconf.AutoRemove = true
|
|
|
|
hostconf.Sysctls = map[string]string{"net.ipv6.conf.all.disable_ipv6": "0"}
|
|
|
|
|
|
|
|
if err := dut.CreateFrom(ctx, conf, hostconf, nil); err != nil {
|
|
|
|
t.Fatalf("unable to create container %s: %v", dut.Name, err)
|
2020-05-27 00:39:19 +00:00
|
|
|
}
|
2020-07-08 20:24:58 +00:00
|
|
|
|
|
|
|
defer dut.CleanUp(ctx)
|
2020-05-27 00:39:19 +00:00
|
|
|
|
|
|
|
// Add ctrlNet as eth1 and testNet as eth2.
|
|
|
|
const testNetDev = "eth2"
|
2020-07-08 20:24:58 +00:00
|
|
|
if err := addNetworks(ctx, dut, dutAddr, []*dockerutil.Network{ctrlNet, testNet}); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
if err := dut.Start(ctx); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
t.Fatalf("unable to start container %s: %s", dut.Name, err)
|
|
|
|
}
|
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
if _, err := dut.WaitForOutput(ctx, "Server listening.*\n", 60*time.Second); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
t.Fatalf("%s on container %s never listened: %s", containerPosixServerBinary, dut.Name, err)
|
|
|
|
}
|
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
dutTestDevice, dutDeviceInfo, err := deviceByIP(ctx, dut, addressInSubnet(dutAddr, *testNet.Subnet))
|
2020-05-27 00:39:19 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
remoteMAC := dutDeviceInfo.MAC
|
|
|
|
remoteIPv6 := dutDeviceInfo.IPv6Addr
|
|
|
|
// Netstack as DUT doesn't assign IPv6 addresses automatically so do it if
|
|
|
|
// needed.
|
|
|
|
if remoteIPv6 == nil {
|
2020-07-08 20:24:58 +00:00
|
|
|
if _, err := dut.Exec(ctx, dockerutil.ExecOpts{}, "ip", "addr", "add", netdevs.MACToIP(remoteMAC).String(), "scope", "link", "dev", dutTestDevice); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
t.Fatalf("unable to ip addr add on container %s: %s", dut.Name, err)
|
|
|
|
}
|
|
|
|
// Now try again, to make sure that it worked.
|
2020-07-08 20:24:58 +00:00
|
|
|
_, dutDeviceInfo, err = deviceByIP(ctx, dut, addressInSubnet(dutAddr, *testNet.Subnet))
|
2020-05-27 00:39:19 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
remoteIPv6 = dutDeviceInfo.IPv6Addr
|
|
|
|
if remoteIPv6 == nil {
|
|
|
|
t.Fatal("unable to set IPv6 address on container", dut.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the Docker container for the testbench.
|
2020-07-08 20:24:58 +00:00
|
|
|
testbench := dockerutil.MakeContainer(ctx, logger("testbench"))
|
2020-05-27 00:39:19 +00:00
|
|
|
testbench.Runtime = "" // The testbench always runs on Linux.
|
|
|
|
|
|
|
|
tbb := path.Base(*testbenchBinary)
|
|
|
|
containerTestbenchBinary := "/packetimpact/" + tbb
|
2020-06-18 01:41:55 +00:00
|
|
|
runOpts = dockerutil.RunOpts{
|
2020-07-08 20:24:58 +00:00
|
|
|
Image: "packetimpact",
|
|
|
|
CapAdd: []string{"NET_ADMIN"},
|
|
|
|
Mounts: []mount.Mount{mount.Mount{
|
|
|
|
Type: mount.TypeBind,
|
|
|
|
Source: tmpDir,
|
|
|
|
Target: testOutputDir,
|
|
|
|
ReadOnly: false,
|
|
|
|
}},
|
2020-06-18 01:41:55 +00:00
|
|
|
}
|
|
|
|
testbench.CopyFiles(&runOpts, "/packetimpact", "/test/packetimpact/tests/"+tbb)
|
2020-05-27 00:39:19 +00:00
|
|
|
|
|
|
|
// Run tcpdump in the test bench unbuffered, without DNS resolution, just on
|
|
|
|
// the interface with the test packets.
|
|
|
|
snifferArgs := []string{
|
2020-05-28 21:42:01 +00:00
|
|
|
"tcpdump",
|
|
|
|
"-S", "-vvv", "-U", "-n",
|
|
|
|
"-i", testNetDev,
|
|
|
|
"-w", testOutputDir + "/dump.pcap",
|
2020-05-27 00:39:19 +00:00
|
|
|
}
|
|
|
|
snifferRegex := "tcpdump: listening.*\n"
|
|
|
|
if *tshark {
|
|
|
|
// Run tshark in the test bench unbuffered, without DNS resolution, just on
|
|
|
|
// the interface with the test packets.
|
|
|
|
snifferArgs = []string{
|
|
|
|
"tshark", "-V", "-l", "-n", "-i", testNetDev,
|
|
|
|
"-o", "tcp.check_checksum:TRUE",
|
|
|
|
"-o", "udp.check_checksum:TRUE",
|
|
|
|
}
|
|
|
|
snifferRegex = "Capturing on.*\n"
|
|
|
|
}
|
|
|
|
|
2020-05-28 21:42:01 +00:00
|
|
|
defer func() {
|
|
|
|
if err := exec.Command("/bin/cp", "-r", tmpDir, os.Getenv("TEST_UNDECLARED_OUTPUTS_DIR")).Run(); err != nil {
|
|
|
|
t.Error("unable to copy container output files:", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
conf, hostconf, _ = testbench.ConfigsFrom(runOpts, snifferArgs...)
|
|
|
|
hostconf.AutoRemove = true
|
|
|
|
hostconf.Sysctls = map[string]string{"net.ipv6.conf.all.disable_ipv6": "0"}
|
|
|
|
|
|
|
|
if err := testbench.CreateFrom(ctx, conf, hostconf, nil); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
t.Fatalf("unable to create container %s: %s", testbench.Name, err)
|
|
|
|
}
|
2020-07-08 20:24:58 +00:00
|
|
|
defer testbench.CleanUp(ctx)
|
2020-05-27 00:39:19 +00:00
|
|
|
|
|
|
|
// Add ctrlNet as eth1 and testNet as eth2.
|
2020-07-08 20:24:58 +00:00
|
|
|
if err := addNetworks(ctx, testbench, testbenchAddr, []*dockerutil.Network{ctrlNet, testNet}); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
if err := testbench.Start(ctx); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
t.Fatalf("unable to start container %s: %s", testbench.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Kill so that it will flush output.
|
2020-07-01 06:55:16 +00:00
|
|
|
defer func() {
|
|
|
|
time.Sleep(1 * time.Second)
|
2020-07-08 20:24:58 +00:00
|
|
|
testbench.Exec(ctx, dockerutil.ExecOpts{}, "killall", snifferArgs[0])
|
2020-07-01 06:55:16 +00:00
|
|
|
}()
|
2020-05-27 00:39:19 +00:00
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
if _, err := testbench.WaitForOutput(ctx, snifferRegex, 60*time.Second); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
t.Fatalf("sniffer on %s never listened: %s", dut.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Because the Linux kernel receives the SYN-ACK but didn't send the SYN it
|
2020-07-10 05:37:11 +00:00
|
|
|
// will issue an RST. To prevent this IPtables can be used to filter out all
|
2020-05-27 00:39:19 +00:00
|
|
|
// incoming packets. The raw socket that packetimpact tests use will still see
|
|
|
|
// everything.
|
2020-07-10 05:37:11 +00:00
|
|
|
for _, bin := range []string{"iptables", "ip6tables"} {
|
|
|
|
if logs, err := testbench.Exec(ctx, dockerutil.ExecOpts{}, bin, "-A", "INPUT", "-i", testNetDev, "-p", "tcp", "-j", "DROP"); err != nil {
|
|
|
|
t.Fatalf("unable to Exec %s on container %s: %s, logs from testbench:\n%s", bin, testbench.Name, err, logs)
|
|
|
|
}
|
2020-05-27 00:39:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME(b/156449515): Some piece of the system has a race. The old
|
|
|
|
// bash script version had a sleep, so we have one too. The race should
|
|
|
|
// be fixed and this sleep removed.
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
|
|
|
// Start a packetimpact test on the test bench. The packetimpact test sends
|
|
|
|
// and receives packets and also sends POSIX socket commands to the
|
|
|
|
// posix_server to be executed on the DUT.
|
|
|
|
testArgs := []string{containerTestbenchBinary}
|
|
|
|
testArgs = append(testArgs, extraTestArgs...)
|
|
|
|
testArgs = append(testArgs,
|
|
|
|
"--posix_server_ip", addressInSubnet(dutAddr, *ctrlNet.Subnet).String(),
|
|
|
|
"--posix_server_port", ctrlPort,
|
|
|
|
"--remote_ipv4", addressInSubnet(dutAddr, *testNet.Subnet).String(),
|
|
|
|
"--local_ipv4", addressInSubnet(testbenchAddr, *testNet.Subnet).String(),
|
|
|
|
"--remote_ipv6", remoteIPv6.String(),
|
|
|
|
"--remote_mac", remoteMAC.String(),
|
2020-07-14 18:48:26 +00:00
|
|
|
"--remote_interface_id", fmt.Sprintf("%d", dutDeviceInfo.ID),
|
2020-05-27 00:39:19 +00:00
|
|
|
"--device", testNetDev,
|
2020-06-12 01:02:58 +00:00
|
|
|
"--dut_type", *dutPlatform,
|
2020-05-27 00:39:19 +00:00
|
|
|
)
|
2020-07-17 18:17:20 +00:00
|
|
|
testbenchLogs, err := testbench.Exec(ctx, dockerutil.ExecOpts{}, testArgs...)
|
|
|
|
if (err != nil) != *expectFailure {
|
|
|
|
var dutLogs string
|
|
|
|
if logs, err := dut.Logs(ctx); err != nil {
|
|
|
|
dutLogs = fmt.Sprintf("failed to fetch DUT logs: %s", err)
|
|
|
|
} else {
|
|
|
|
dutLogs = logs
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Errorf(`test error: %v, expect failure: %t
|
|
|
|
|
|
|
|
====== Begin of DUT Logs ======
|
|
|
|
|
|
|
|
%s
|
|
|
|
|
|
|
|
====== End of DUT Logs ======
|
|
|
|
|
|
|
|
====== Begin of Testbench Logs ======
|
|
|
|
|
|
|
|
%s
|
|
|
|
|
|
|
|
====== End of Testbench Logs ======`,
|
|
|
|
err, *expectFailure, dutLogs, testbenchLogs)
|
2020-05-27 00:39:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
func addNetworks(ctx context.Context, d *dockerutil.Container, addr net.IP, networks []*dockerutil.Network) error {
|
2020-05-27 00:39:19 +00:00
|
|
|
for _, dn := range networks {
|
|
|
|
ip := addressInSubnet(addr, *dn.Subnet)
|
|
|
|
// Connect to the network with the specified IP address.
|
2020-07-08 20:24:58 +00:00
|
|
|
if err := dn.Connect(ctx, d, ip.String(), ""); err != nil {
|
2020-05-27 00:39:19 +00:00
|
|
|
return fmt.Errorf("unable to connect container %s to network %s: %w", d.Name, dn.Name, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// addressInSubnet combines the subnet provided with the address and returns a
|
|
|
|
// new address. The return address bits come from the subnet where the mask is 1
|
|
|
|
// and from the ip address where the mask is 0.
|
|
|
|
func addressInSubnet(addr net.IP, subnet net.IPNet) net.IP {
|
|
|
|
var octets []byte
|
|
|
|
for i := 0; i < 4; i++ {
|
|
|
|
octets = append(octets, (subnet.IP.To4()[i]&subnet.Mask[i])+(addr.To4()[i]&(^subnet.Mask[i])))
|
|
|
|
}
|
|
|
|
return net.IP(octets)
|
|
|
|
}
|
|
|
|
|
2020-07-08 20:24:58 +00:00
|
|
|
// createDockerNetwork makes a randomly-named network that will start with the
|
2020-05-27 00:39:19 +00:00
|
|
|
// namePrefix. The network will be a random /24 subnet.
|
2020-07-08 20:24:58 +00:00
|
|
|
func createDockerNetwork(ctx context.Context, n *dockerutil.Network) error {
|
2020-05-27 00:39:19 +00:00
|
|
|
randSource := rand.NewSource(time.Now().UnixNano())
|
|
|
|
r1 := rand.New(randSource)
|
|
|
|
// Class C, 192.0.0.0 to 223.255.255.255, transitionally has mask 24.
|
|
|
|
ip := net.IPv4(byte(r1.Intn(224-192)+192), byte(r1.Intn(256)), byte(r1.Intn(256)), 0)
|
|
|
|
n.Subnet = &net.IPNet{
|
|
|
|
IP: ip,
|
|
|
|
Mask: ip.DefaultMask(),
|
|
|
|
}
|
2020-07-08 20:24:58 +00:00
|
|
|
return n.Create(ctx)
|
2020-05-27 00:39:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// deviceByIP finds a deviceInfo and device name from an IP address.
|
2020-07-08 20:24:58 +00:00
|
|
|
func deviceByIP(ctx context.Context, d *dockerutil.Container, ip net.IP) (string, netdevs.DeviceInfo, error) {
|
|
|
|
out, err := d.Exec(ctx, dockerutil.ExecOpts{}, "ip", "addr", "show")
|
2020-05-27 00:39:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", netdevs.DeviceInfo{}, fmt.Errorf("listing devices on %s container: %w", d.Name, err)
|
|
|
|
}
|
|
|
|
devs, err := netdevs.ParseDevices(out)
|
|
|
|
if err != nil {
|
|
|
|
return "", netdevs.DeviceInfo{}, fmt.Errorf("parsing devices from %s container: %w", d.Name, err)
|
|
|
|
}
|
|
|
|
testDevice, deviceInfo, err := netdevs.FindDeviceByIP(ip, devs)
|
|
|
|
if err != nil {
|
|
|
|
return "", netdevs.DeviceInfo{}, fmt.Errorf("can't find deviceInfo for container %s: %w", d.Name, err)
|
|
|
|
}
|
|
|
|
return testDevice, deviceInfo, nil
|
|
|
|
}
|