github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/integration-cli/docker_utils_test.go (about) 1 package main 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path" 12 "path/filepath" 13 "strconv" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/docker/docker/api/types" 19 "github.com/docker/docker/client" 20 "github.com/docker/docker/integration-cli/cli" 21 "github.com/docker/docker/integration-cli/daemon" 22 "gotest.tools/v3/assert" 23 "gotest.tools/v3/assert/cmp" 24 "gotest.tools/v3/icmd" 25 "gotest.tools/v3/poll" 26 ) 27 28 func deleteImages(images ...string) error { 29 args := []string{dockerBinary, "rmi", "-f"} 30 return icmd.RunCmd(icmd.Cmd{Command: append(args, images...)}).Error 31 } 32 33 // Deprecated: use cli.Docker or cli.DockerCmd 34 func dockerCmdWithError(args ...string) (string, int, error) { 35 result := cli.Docker(cli.Args(args...)) 36 if result.Error != nil { 37 return result.Combined(), result.ExitCode, result.Compare(icmd.Success) 38 } 39 return result.Combined(), result.ExitCode, result.Error 40 } 41 42 // Deprecated: use cli.Docker or cli.DockerCmd 43 func dockerCmd(c testing.TB, args ...string) (string, int) { 44 result := cli.DockerCmd(c, args...) 45 return result.Combined(), result.ExitCode 46 } 47 48 // Deprecated: use cli.Docker or cli.DockerCmd 49 func dockerCmdWithResult(args ...string) *icmd.Result { 50 return cli.Docker(cli.Args(args...)) 51 } 52 53 func findContainerIP(c *testing.T, id string, network string) string { 54 c.Helper() 55 out, _ := dockerCmd(c, "inspect", fmt.Sprintf("--format='{{ .NetworkSettings.Networks.%s.IPAddress }}'", network), id) 56 return strings.Trim(out, " \r\n'") 57 } 58 59 func getContainerCount(c *testing.T) int { 60 c.Helper() 61 const containers = "Containers:" 62 63 result := icmd.RunCommand(dockerBinary, "info") 64 result.Assert(c, icmd.Success) 65 66 lines := strings.Split(result.Combined(), "\n") 67 for _, line := range lines { 68 if strings.Contains(line, containers) { 69 output := strings.TrimSpace(line) 70 output = strings.TrimPrefix(output, containers) 71 output = strings.Trim(output, " ") 72 containerCount, err := strconv.Atoi(output) 73 assert.NilError(c, err) 74 return containerCount 75 } 76 } 77 return 0 78 } 79 80 func inspectFieldAndUnmarshall(c *testing.T, name, field string, output interface{}) { 81 c.Helper() 82 str := inspectFieldJSON(c, name, field) 83 err := json.Unmarshal([]byte(str), output) 84 if c != nil { 85 assert.Assert(c, err == nil, "failed to unmarshal: %v", err) 86 } 87 } 88 89 // Deprecated: use cli.Inspect 90 func inspectFilter(name, filter string) (string, error) { 91 format := fmt.Sprintf("{{%s}}", filter) 92 result := icmd.RunCommand(dockerBinary, "inspect", "-f", format, name) 93 if result.Error != nil || result.ExitCode != 0 { 94 return "", fmt.Errorf("failed to inspect %s: %s", name, result.Combined()) 95 } 96 return strings.TrimSpace(result.Combined()), nil 97 } 98 99 // Deprecated: use cli.Inspect 100 func inspectFieldWithError(name, field string) (string, error) { 101 return inspectFilter(name, fmt.Sprintf(".%s", field)) 102 } 103 104 // Deprecated: use cli.Inspect 105 func inspectField(c *testing.T, name, field string) string { 106 c.Helper() 107 out, err := inspectFilter(name, fmt.Sprintf(".%s", field)) 108 if c != nil { 109 assert.NilError(c, err) 110 } 111 return out 112 } 113 114 // Deprecated: use cli.Inspect 115 func inspectFieldJSON(c *testing.T, name, field string) string { 116 c.Helper() 117 out, err := inspectFilter(name, fmt.Sprintf("json .%s", field)) 118 if c != nil { 119 assert.NilError(c, err) 120 } 121 return out 122 } 123 124 // Deprecated: use cli.Inspect 125 func inspectFieldMap(c *testing.T, name, path, field string) string { 126 c.Helper() 127 out, err := inspectFilter(name, fmt.Sprintf("index .%s %q", path, field)) 128 if c != nil { 129 assert.NilError(c, err) 130 } 131 return out 132 } 133 134 // Deprecated: use cli.Inspect 135 func inspectMountSourceField(name, destination string) (string, error) { 136 m, err := inspectMountPoint(name, destination) 137 if err != nil { 138 return "", err 139 } 140 return m.Source, nil 141 } 142 143 // Deprecated: use cli.Inspect 144 func inspectMountPoint(name, destination string) (types.MountPoint, error) { 145 out, err := inspectFilter(name, "json .Mounts") 146 if err != nil { 147 return types.MountPoint{}, err 148 } 149 150 return inspectMountPointJSON(out, destination) 151 } 152 153 var errMountNotFound = errors.New("mount point not found") 154 155 // Deprecated: use cli.Inspect 156 func inspectMountPointJSON(j, destination string) (types.MountPoint, error) { 157 var mp []types.MountPoint 158 if err := json.Unmarshal([]byte(j), &mp); err != nil { 159 return types.MountPoint{}, err 160 } 161 162 var m *types.MountPoint 163 for _, c := range mp { 164 if c.Destination == destination { 165 m = &c 166 break 167 } 168 } 169 170 if m == nil { 171 return types.MountPoint{}, errMountNotFound 172 } 173 174 return *m, nil 175 } 176 177 func getIDByName(c *testing.T, name string) string { 178 c.Helper() 179 id, err := inspectFieldWithError(name, "Id") 180 assert.NilError(c, err) 181 return id 182 } 183 184 // Deprecated: use cli.Build 185 func buildImageSuccessfully(c *testing.T, name string, cmdOperators ...cli.CmdOperator) { 186 c.Helper() 187 buildImage(name, cmdOperators...).Assert(c, icmd.Success) 188 } 189 190 // Deprecated: use cli.Build 191 func buildImage(name string, cmdOperators ...cli.CmdOperator) *icmd.Result { 192 return cli.Docker(cli.Build(name), cmdOperators...) 193 } 194 195 // Write `content` to the file at path `dst`, creating it if necessary, 196 // as well as any missing directories. 197 // The file is truncated if it already exists. 198 // Fail the test when error occurs. 199 func writeFile(dst, content string, c *testing.T) { 200 c.Helper() 201 // Create subdirectories if necessary 202 assert.Assert(c, os.MkdirAll(path.Dir(dst), 0700) == nil) 203 f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700) 204 assert.NilError(c, err) 205 defer f.Close() 206 // Write content (truncate if it exists) 207 _, err = io.Copy(f, strings.NewReader(content)) 208 assert.NilError(c, err) 209 } 210 211 // Return the contents of file at path `src`. 212 // Fail the test when error occurs. 213 func readFile(src string, c *testing.T) (content string) { 214 c.Helper() 215 data, err := ioutil.ReadFile(src) 216 assert.NilError(c, err) 217 218 return string(data) 219 } 220 221 func containerStorageFile(containerID, basename string) string { 222 return filepath.Join(testEnv.PlatformDefaults.ContainerStoragePath, containerID, basename) 223 } 224 225 // docker commands that use this function must be run with the '-d' switch. 226 func runCommandAndReadContainerFile(c *testing.T, filename string, command string, args ...string) []byte { 227 c.Helper() 228 result := icmd.RunCommand(command, args...) 229 result.Assert(c, icmd.Success) 230 contID := strings.TrimSpace(result.Combined()) 231 if err := waitRun(contID); err != nil { 232 c.Fatalf("%v: %q", contID, err) 233 } 234 return readContainerFile(c, contID, filename) 235 } 236 237 func readContainerFile(c *testing.T, containerID, filename string) []byte { 238 c.Helper() 239 f, err := os.Open(containerStorageFile(containerID, filename)) 240 assert.NilError(c, err) 241 defer f.Close() 242 243 content, err := ioutil.ReadAll(f) 244 assert.NilError(c, err) 245 return content 246 } 247 248 func readContainerFileWithExec(c *testing.T, containerID, filename string) []byte { 249 c.Helper() 250 result := icmd.RunCommand(dockerBinary, "exec", containerID, "cat", filename) 251 result.Assert(c, icmd.Success) 252 return []byte(result.Combined()) 253 } 254 255 // daemonTime provides the current time on the daemon host 256 func daemonTime(c *testing.T) time.Time { 257 c.Helper() 258 if testEnv.IsLocalDaemon() { 259 return time.Now() 260 } 261 cli, err := client.NewClientWithOpts(client.FromEnv) 262 assert.NilError(c, err) 263 defer cli.Close() 264 265 info, err := cli.Info(context.Background()) 266 assert.NilError(c, err) 267 268 dt, err := time.Parse(time.RFC3339Nano, info.SystemTime) 269 assert.Assert(c, err == nil, "invalid time format in GET /info response") 270 return dt 271 } 272 273 // daemonUnixTime returns the current time on the daemon host with nanoseconds precision. 274 // It return the time formatted how the client sends timestamps to the server. 275 func daemonUnixTime(c *testing.T) string { 276 c.Helper() 277 return parseEventTime(daemonTime(c)) 278 } 279 280 func parseEventTime(t time.Time) string { 281 return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())) 282 } 283 284 // appendBaseEnv appends the minimum set of environment variables to exec the 285 // docker cli binary for testing with correct configuration to the given env 286 // list. 287 func appendBaseEnv(isTLS bool, env ...string) []string { 288 preserveList := []string{ 289 // preserve remote test host 290 "DOCKER_HOST", 291 292 // windows: requires preserving SystemRoot, otherwise dial tcp fails 293 // with "GetAddrInfoW: A non-recoverable error occurred during a database lookup." 294 "SystemRoot", 295 296 // testing help text requires the $PATH to dockerd is set 297 "PATH", 298 } 299 if isTLS { 300 preserveList = append(preserveList, "DOCKER_TLS_VERIFY", "DOCKER_CERT_PATH") 301 } 302 303 for _, key := range preserveList { 304 if val := os.Getenv(key); val != "" { 305 env = append(env, fmt.Sprintf("%s=%s", key, val)) 306 } 307 } 308 return env 309 } 310 311 func createTmpFile(c *testing.T, content string) string { 312 c.Helper() 313 f, err := ioutil.TempFile("", "testfile") 314 assert.NilError(c, err) 315 316 filename := f.Name() 317 318 err = ioutil.WriteFile(filename, []byte(content), 0644) 319 assert.NilError(c, err) 320 321 return filename 322 } 323 324 // waitRun will wait for the specified container to be running, maximum 5 seconds. 325 // Deprecated: use cli.WaitFor 326 func waitRun(contID string) error { 327 return waitInspect(contID, "{{.State.Running}}", "true", 5*time.Second) 328 } 329 330 // waitInspect will wait for the specified container to have the specified string 331 // in the inspect output. It will wait until the specified timeout (in seconds) 332 // is reached. 333 // Deprecated: use cli.WaitFor 334 func waitInspect(name, expr, expected string, timeout time.Duration) error { 335 return waitInspectWithArgs(name, expr, expected, timeout) 336 } 337 338 // Deprecated: use cli.WaitFor 339 func waitInspectWithArgs(name, expr, expected string, timeout time.Duration, arg ...string) error { 340 return daemon.WaitInspectWithArgs(dockerBinary, name, expr, expected, timeout, arg...) 341 } 342 343 func getInspectBody(c *testing.T, version, id string) []byte { 344 c.Helper() 345 cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion(version)) 346 assert.NilError(c, err) 347 defer cli.Close() 348 _, body, err := cli.ContainerInspectWithRaw(context.Background(), id, false) 349 assert.NilError(c, err) 350 return body 351 } 352 353 // Run a long running idle task in a background container using the 354 // system-specific default image and command. 355 func runSleepingContainer(c *testing.T, extraArgs ...string) string { 356 c.Helper() 357 return runSleepingContainerInImage(c, "busybox", extraArgs...) 358 } 359 360 // Run a long running idle task in a background container using the specified 361 // image and the system-specific command. 362 func runSleepingContainerInImage(c *testing.T, image string, extraArgs ...string) string { 363 c.Helper() 364 args := []string{"run", "-d"} 365 args = append(args, extraArgs...) 366 args = append(args, image) 367 args = append(args, sleepCommandForDaemonPlatform()...) 368 return strings.TrimSpace(cli.DockerCmd(c, args...).Combined()) 369 } 370 371 // minimalBaseImage returns the name of the minimal base image for the current 372 // daemon platform. 373 func minimalBaseImage() string { 374 return testEnv.PlatformDefaults.BaseImage 375 } 376 377 func getGoroutineNumber() (int, error) { 378 cli, err := client.NewClientWithOpts(client.FromEnv) 379 if err != nil { 380 return 0, err 381 } 382 defer cli.Close() 383 384 info, err := cli.Info(context.Background()) 385 if err != nil { 386 return 0, err 387 } 388 return info.NGoroutines, nil 389 } 390 391 func waitForGoroutines(expected int) error { 392 t := time.After(30 * time.Second) 393 for { 394 select { 395 case <-t: 396 n, err := getGoroutineNumber() 397 if err != nil { 398 return err 399 } 400 if n > expected { 401 return fmt.Errorf("leaked goroutines: expected less than or equal to %d, got: %d", expected, n) 402 } 403 default: 404 n, err := getGoroutineNumber() 405 if err != nil { 406 return err 407 } 408 if n <= expected { 409 return nil 410 } 411 time.Sleep(200 * time.Millisecond) 412 } 413 } 414 } 415 416 // getErrorMessage returns the error message from an error API response 417 func getErrorMessage(c *testing.T, body []byte) string { 418 c.Helper() 419 var resp types.ErrorResponse 420 assert.Assert(c, json.Unmarshal(body, &resp) == nil) 421 return strings.TrimSpace(resp.Message) 422 } 423 424 type checkF func(*testing.T) (interface{}, string) 425 type reducer func(...interface{}) interface{} 426 427 func pollCheck(t *testing.T, f checkF, compare func(x interface{}) assert.BoolOrComparison) poll.Check { 428 return func(poll.LogT) poll.Result { 429 t.Helper() 430 v, comment := f(t) 431 r := compare(v) 432 switch r := r.(type) { 433 case bool: 434 if r { 435 return poll.Success() 436 } 437 case cmp.Comparison: 438 if r().Success() { 439 return poll.Success() 440 } 441 default: 442 panic(fmt.Errorf("pollCheck: type %T not implemented", r)) 443 } 444 return poll.Continue(comment) 445 } 446 } 447 448 func reducedCheck(r reducer, funcs ...checkF) checkF { 449 return func(c *testing.T) (interface{}, string) { 450 c.Helper() 451 var values []interface{} 452 var comments []string 453 for _, f := range funcs { 454 v, comment := f(c) 455 values = append(values, v) 456 if len(comment) > 0 { 457 comments = append(comments, comment) 458 } 459 } 460 return r(values...), fmt.Sprintf("%v", strings.Join(comments, ", ")) 461 } 462 } 463 464 func sumAsIntegers(vals ...interface{}) interface{} { 465 var s int 466 for _, v := range vals { 467 s += v.(int) 468 } 469 return s 470 }