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