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 }