github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/test/dockerutil/dockerutil.go (about) 1 // Copyright 2018 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 // Package dockerutil is a collection of utility functions. 16 package dockerutil 17 18 import ( 19 "encoding/json" 20 "flag" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "log" 25 "os/exec" 26 "regexp" 27 "strconv" 28 "time" 29 30 "github.com/SagerNet/gvisor/pkg/test/testutil" 31 ) 32 33 var ( 34 // runtime is the runtime to use for tests. This will be applied to all 35 // containers. Note that the default here ("runsc") corresponds to the 36 // default used by the installations. This is important, because the 37 // default installer for vm_tests (in tools/installers:head, invoked 38 // via tools/vm:defs.bzl) will install with this name. So without 39 // changing anything, tests should have a runsc runtime available to 40 // them. Otherwise installers should update the existing runtime 41 // instead of installing a new one. 42 runtime = flag.String("runtime", "runsc", "specify which runtime to use") 43 44 // config is the default Docker daemon configuration path. 45 config = flag.String("config_path", "/etc/docker/daemon.json", "configuration file for reading paths") 46 47 // The following flags are for the "pprof" profiler tool. 48 49 // pprofBaseDir allows the user to change the directory to which profiles are 50 // written. By default, profiles will appear under: 51 // /tmp/profile/RUNTIME/CONTAINER_NAME/*.pprof. 52 pprofBaseDir = flag.String("pprof-dir", "/tmp/profile", "base directory in: BASEDIR/RUNTIME/CONTINER_NAME/FILENAME (e.g. /tmp/profile/runtime/mycontainer/cpu.pprof)") 53 pprofDuration = flag.Duration("pprof-duration", time.Hour, "profiling duration (automatically stopped at container exit)") 54 55 // The below flags enable each type of profile. Multiple profiles can be 56 // enabled for each run. The profile will be collected from the start. 57 pprofBlock = flag.Bool("pprof-block", false, "enables block profiling with runsc debug") 58 pprofCPU = flag.Bool("pprof-cpu", false, "enables CPU profiling with runsc debug") 59 pprofHeap = flag.Bool("pprof-heap", false, "enables heap profiling with runsc debug") 60 pprofMutex = flag.Bool("pprof-mutex", false, "enables mutex profiling with runsc debug") 61 ) 62 63 // EnsureSupportedDockerVersion checks if correct docker is installed. 64 // 65 // This logs directly to stderr, as it is typically called from a Main wrapper. 66 func EnsureSupportedDockerVersion() { 67 cmd := exec.Command("docker", "version") 68 out, err := cmd.CombinedOutput() 69 if err != nil { 70 log.Fatalf("error running %q: %v", "docker version", err) 71 } 72 re := regexp.MustCompile(`Version:\s+(\d+)\.(\d+)\.\d.*`) 73 matches := re.FindStringSubmatch(string(out)) 74 if len(matches) != 3 { 75 log.Fatalf("Invalid docker output: %s", out) 76 } 77 major, _ := strconv.Atoi(matches[1]) 78 minor, _ := strconv.Atoi(matches[2]) 79 if major < 17 || (major == 17 && minor < 9) { 80 log.Fatalf("Docker version 17.09.0 or greater is required, found: %02d.%02d", major, minor) 81 } 82 } 83 84 // RuntimePath returns the binary path for the current runtime. 85 func RuntimePath() (string, error) { 86 rs, err := runtimeMap() 87 if err != nil { 88 return "", err 89 } 90 91 p, ok := rs["path"].(string) 92 if !ok { 93 // The runtime does not declare a path. 94 return "", fmt.Errorf("runtime does not declare a path: %v", rs) 95 } 96 return p, nil 97 } 98 99 // UsingVFS2 returns true if the 'runtime' has the vfs2 flag set. 100 // TODO(github.com/SagerNet/issue/1624): Remove. 101 func UsingVFS2() (bool, error) { 102 rMap, err := runtimeMap() 103 if err != nil { 104 return false, err 105 } 106 107 list, ok := rMap["runtimeArgs"].([]interface{}) 108 if !ok { 109 return false, fmt.Errorf("unexpected format: %v", rMap) 110 } 111 112 for _, element := range list { 113 if element == "--vfs2" { 114 return true, nil 115 } 116 } 117 return false, nil 118 } 119 120 func runtimeMap() (map[string]interface{}, error) { 121 // Read the configuration data; the file must exist. 122 configBytes, err := ioutil.ReadFile(*config) 123 if err != nil { 124 return nil, err 125 } 126 127 // Unmarshal the configuration. 128 c := make(map[string]interface{}) 129 if err := json.Unmarshal(configBytes, &c); err != nil { 130 return nil, err 131 } 132 133 // Decode the expected configuration. 134 r, ok := c["runtimes"] 135 if !ok { 136 return nil, fmt.Errorf("no runtimes declared: %v", c) 137 } 138 rs, ok := r.(map[string]interface{}) 139 if !ok { 140 // The runtimes are not a map. 141 return nil, fmt.Errorf("unexpected format: %v", rs) 142 } 143 r, ok = rs[*runtime] 144 if !ok { 145 // The expected runtime is not declared. 146 return nil, fmt.Errorf("runtime %q not found: %v", *runtime, rs) 147 } 148 rs, ok = r.(map[string]interface{}) 149 if !ok { 150 // The runtime is not a map. 151 return nil, fmt.Errorf("unexpected format: %v", r) 152 } 153 return rs, nil 154 } 155 156 // Save exports a container image to the given Writer. 157 // 158 // Note that the writer should be actively consuming the output, otherwise it 159 // is not guaranteed that the Save will make any progress and the call may 160 // stall indefinitely. 161 // 162 // This is called by criutil in order to import imports. 163 func Save(logger testutil.Logger, image string, w io.Writer) error { 164 cmd := testutil.Command(logger, "docker", "save", testutil.ImageByName(image)) 165 cmd.Stdout = w // Send directly to the writer. 166 return cmd.Run() 167 } 168 169 // Runtime returns the value of the flag runtime. 170 func Runtime() string { 171 return *runtime 172 }