gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/packetimpact/dut/runsc/main.go (about)

     1  // Copyright 2021 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  //go:build linux
    16  // +build linux
    17  
    18  // The runsc binary is used to bring up a gVisor DUT.
    19  package main
    20  
    21  import (
    22  	"context"
    23  	"flag"
    24  	"fmt"
    25  	"log"
    26  	"os"
    27  	"os/exec"
    28  	"strconv"
    29  
    30  	specs "github.com/opencontainers/runtime-spec/specs-go"
    31  	"golang.org/x/sys/unix"
    32  	"gvisor.dev/gvisor/pkg/test/testutil"
    33  	"gvisor.dev/gvisor/test/packetimpact/dut"
    34  	"gvisor.dev/gvisor/test/packetimpact/dut/linux"
    35  	"gvisor.dev/gvisor/test/packetimpact/internal/testing"
    36  	"gvisor.dev/gvisor/test/packetimpact/testbench"
    37  )
    38  
    39  type runsc struct {
    40  	dut.Ifaces
    41  	containerID      string
    42  	runscPath        string
    43  	runscLogsPath    string
    44  	bundleDir        string
    45  	rootDir          string
    46  	cleanupRootDir   func()
    47  	cleanupBundleDir func()
    48  }
    49  
    50  var _ dut.DUT = (*runsc)(nil)
    51  
    52  func main() {
    53  	ifaces, err := dut.Init(flag.CommandLine)
    54  	if err != nil {
    55  		log.Fatal(err)
    56  	}
    57  	// Find the path to the binaries.
    58  	posixServerPath, err := testutil.FindFile(linux.PosixServerPath)
    59  	if err != nil {
    60  		log.Fatalf("failed to find posix_server binary: %s", err)
    61  	}
    62  	runscPath, err := testutil.FindFile("runsc/runsc")
    63  	if err != nil {
    64  		log.Fatalf("failed to find runsc binary: %s", err)
    65  	}
    66  
    67  	// Create the OCI spec for the container with posix_server as the entrypoint.
    68  	spec := testutil.NewSpecWithArgs(posixServerPath, "--ip", "0.0.0.0", "--port", strconv.FormatUint(dut.PosixServerPort, 10))
    69  	pwd, err := os.Getwd()
    70  	if err != nil {
    71  		log.Fatalf("failed to get the current working directory: %s", err)
    72  	}
    73  	spec.Process.Cwd = pwd
    74  	if spec.Linux == nil {
    75  		spec.Linux = &specs.Linux{}
    76  	}
    77  
    78  	// Use the DUT namespace which is the current namespace's.
    79  	spec.Linux.Namespaces = append(spec.Linux.Namespaces, specs.LinuxNamespace{
    80  		Type: "network",
    81  		Path: fmt.Sprintf("/proc/%d/ns/net", os.Getpid()),
    82  	})
    83  
    84  	// Prepare logs.
    85  	runscLogPath, err := testing.UndeclaredOutput("runsc.%%TIMESTAMP%%.%%COMMAND%%.log")
    86  	if err != nil {
    87  		log.Fatalf("failed to create runsc log file: %s", err)
    88  	}
    89  
    90  	// Build the command to start runsc container.
    91  	bundleDir, cleanupBundleDir, err := testutil.SetupBundleDir(spec)
    92  	if err != nil {
    93  		log.Fatalf("failed to create bundle dir: %s", err)
    94  	}
    95  	rootDir, cleanupRootDir, err := testutil.SetupRootDir()
    96  	if err != nil {
    97  		cleanupBundleDir()
    98  		log.Fatalf("SetupRootDir failed: %v", err)
    99  	}
   100  	if err := dut.Run(&runsc{
   101  		Ifaces:           ifaces,
   102  		containerID:      testutil.RandomContainerID(),
   103  		runscPath:        runscPath,
   104  		runscLogsPath:    runscLogPath,
   105  		bundleDir:        bundleDir,
   106  		rootDir:          rootDir,
   107  		cleanupRootDir:   cleanupRootDir,
   108  		cleanupBundleDir: cleanupBundleDir,
   109  	}); err != nil {
   110  		log.Fatal(err)
   111  	}
   112  }
   113  
   114  // Bootstrap implements dut.DUT.
   115  func (r *runsc) Bootstrap(ctx context.Context) (testbench.DUTInfo, func() error, error) {
   116  	// runsc will flush the addresses so we collect the info before we start it.
   117  	info, err := linux.DUTInfo(r.Ifaces)
   118  	if err != nil {
   119  		return testbench.DUTInfo{}, nil, fmt.Errorf("failed to collect information about the DUT: %w", err)
   120  	}
   121  
   122  	// Start posix_server inside a runsc container.
   123  	cmd := exec.CommandContext(
   124  		ctx,
   125  		r.runscPath,
   126  		"-root", r.rootDir,
   127  		"-network=sandbox",
   128  		"-debug",
   129  		"-debug-log", r.runscLogsPath,
   130  		"-log-format=text",
   131  		"-TESTONLY-unsafe-nonroot=true",
   132  		"-net-raw=true",
   133  		fmt.Sprintf("-panic-signal=%d", unix.SIGTERM),
   134  		"-watchdog-action=panic",
   135  		"run",
   136  		"-bundle", r.bundleDir,
   137  		r.containerID,
   138  	)
   139  	errPipe, err := cmd.StderrPipe()
   140  	if err != nil {
   141  		return testbench.DUTInfo{}, nil, fmt.Errorf("failed to create stderr pipe to the posix server process: %w", err)
   142  	}
   143  	if err := cmd.Start(); err != nil {
   144  		return testbench.DUTInfo{}, nil, fmt.Errorf("failed to start the posix server process: %w", err)
   145  	}
   146  	if err := dut.WaitForServer(errPipe); err != nil {
   147  		return testbench.DUTInfo{}, nil, fmt.Errorf("failed to wait for the server to listen: %w", err)
   148  	}
   149  
   150  	// runsc will keep using the assigned ip and mac addresses, but the device
   151  	// id could have changed, we need to figure it out.
   152  	remoteDevID, err := r.remoteDevID()
   153  	if err != nil {
   154  		return testbench.DUTInfo{}, nil, fmt.Errorf("failed to get test dev id: %w", err)
   155  	}
   156  	info.Net.RemoteDevID = remoteDevID
   157  	return info, cmd.Wait, nil
   158  }
   159  
   160  // Cleanup implements dut.DUT.
   161  func (r *runsc) Cleanup() {
   162  	r.cleanupRootDir()
   163  	r.cleanupBundleDir()
   164  }
   165  
   166  // remoteDevID gets the id of the test interface inside the runsc container.
   167  func (r *runsc) remoteDevID() (uint32, error) {
   168  	runscDevIDPath, err := testutil.FindFile("test/packetimpact/dut/runsc/devid")
   169  	if err != nil {
   170  		return 0, fmt.Errorf("failed to find binary runsc_devid: %w", err)
   171  	}
   172  	cmd := exec.Command(
   173  		r.runscPath,
   174  		"-root",
   175  		r.rootDir,
   176  		"-TESTONLY-unsafe-nonroot=true",
   177  		"exec",
   178  		r.containerID,
   179  		runscDevIDPath,
   180  		r.Ifaces.Test,
   181  	)
   182  	bytes, err := cmd.CombinedOutput()
   183  	output := string(bytes)
   184  	if err != nil {
   185  		return 0, fmt.Errorf("failed to get the remote device id: %w, output: %s", err, output)
   186  	}
   187  	id, err := strconv.ParseUint(output, 10, 32)
   188  	if err != nil {
   189  		return 0, fmt.Errorf("%s is not a number: %w", output, err)
   190  	}
   191  	return uint32(id), nil
   192  }