Files
Ryan El KochtaandgVisor bot 32276e423f dockerutil: fix docker version parsing
AFAICT previously the function was parsing the Docker *client* version, since
the output of `docker version` has the client version first, which gets picked
up by the `re.FindStringSubmatch()` since it finds the _first_ match:

```
$ docker version
Client: Docker Engine - Community
 Version:           29.6.2
 API version:       1.55
 Go version:        go1.26.5
 Git commit:        dfc4efb
 Built:             Thu Jul 16 16:12:34 2026
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          29.6.2
  API version:      1.55 (minimum version 1.40)
  Go version:       go1.26.5
  Git commit:       3d80467
  Built:            Thu Jul 16 16:12:34 2026
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v2.2.6
  GitCommit:        11ce9d5f3c68c941867e82890e93e815c1304f1b
 runc:
  Version:          1.3.6
  GitCommit:        v1.3.6-0-g491b69ba
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

```

PiperOrigin-RevId: 966681719
2026-08-18 10:47:59 -07:00
..
2025-05-14 14:39:07 -07:00
2025-05-14 14:39:07 -07:00
2022-12-08 14:37:43 -08:00

dockerutil

This package is for creating and controlling docker containers for testing runsc, gVisor's docker/kubernetes binary. A simple test may look like:

 func TestSuperCool(t *testing.T) {
   ctx := context.Background()
   c := dockerutil.MakeContainer(ctx, t)
   got, err := c.Run(ctx, dockerutil.RunOpts{
     Image: "basic/alpine"
   }, "echo", "super cool")
   if err != nil {
      t.Fatalf("err was not nil: %v", err)
   }
   want := "super cool"
   if !strings.Contains(got, want){
     t.Fatalf("want: %s, got: %s", want, got)
   }
 }

For further examples, see many of our end to end tests elsewhere in the repo, such as those in //test/e2e or benchmarks at //test/benchmarks.

dockerutil uses the "official" docker golang api, which is very powerful. dockerutil is a thin wrapper around this API, allowing desired new use cases to be easily implemented.

Profiling

dockerutil is capable of generating profiles. Currently, the only option is to use pprof profiles generated by runsc debug. The profiler will generate Block, CPU, Heap, Goroutine, and Mutex profiles. To generate profiles:

  • Install runsc with the --profile flag: make configure RUNTIME=myrunsc ARGS="--profile" Also add other flags with ARGS like --platform=kvm.
  • Restart docker: sudo service docker restart

To run and generate CPU profiles run:

make sudo TARGETS=//path/to:target \
  ARGS="--runtime=myrunsc -test.v -test.bench=. --pprof-cpu" OPTIONS="-c opt"

Profiles would be at: /tmp/profile/myrunsc/CONTAINERNAME/cpu.pprof

Container name in most tests and benchmarks in gVisor is usually the test name and some random characters like so: BenchmarkABSL-CleanCache-JF2J2ZYF3U7SL47QAA727CSJI3C4ZAW2

Profiling requires root as runsc debug inspects running containers in /var/run among other things.

Writing for Profiling

The below shows an example of using profiles with dockerutil.

func TestSuperCool(t *testing.T){
  ctx := context.Background()
  // profiled and using runtime from dockerutil.runtime flag
  profiled := MakeContainer()

  // not profiled and using runtime runc
  native := MakeNativeContainer()

  err := profiled.Spawn(ctx, RunOpts{
    Image: "some/image",
  }, "sleep", "100000")
  // profiling has begun here
  ...
  expensive setup that I don't want to profile.
  ...
  profiled.RestartProfiles()
  // profiled activity
}

In the above example, profiled would be profiled and native would not. The call to RestartProfiles() restarts the clock on profiling. This is useful if the main activity being tested is done with docker exec or container.Spawn() followed by one or more container.Exec() calls.