1package daemon // import "github.com/docker/docker/integration-cli/daemon"
2
3import (
4	"fmt"
5	"strings"
6	"testing"
7	"time"
8
9	"github.com/docker/docker/internal/test/daemon"
10	"github.com/pkg/errors"
11	"gotest.tools/assert"
12	"gotest.tools/icmd"
13)
14
15type testingT interface {
16	assert.TestingT
17	logT
18	Fatalf(string, ...interface{})
19}
20
21type logT interface {
22	Logf(string, ...interface{})
23}
24
25// Daemon represents a Docker daemon for the testing framework.
26type Daemon struct {
27	*daemon.Daemon
28	dockerBinary string
29}
30
31// New returns a Daemon instance to be used for testing.
32// This will create a directory such as d123456789 in the folder specified by $DOCKER_INTEGRATION_DAEMON_DEST or $DEST.
33// The daemon will not automatically start.
34func New(t testingT, dockerBinary string, dockerdBinary string, ops ...func(*daemon.Daemon)) *Daemon {
35	ops = append(ops, daemon.WithDockerdBinary(dockerdBinary))
36	d := daemon.New(t, ops...)
37	return &Daemon{
38		Daemon:       d,
39		dockerBinary: dockerBinary,
40	}
41}
42
43// Cmd executes a docker CLI command against this daemon.
44// Example: d.Cmd("version") will run docker -H unix://path/to/unix.sock version
45func (d *Daemon) Cmd(args ...string) (string, error) {
46	result := icmd.RunCmd(d.Command(args...))
47	return result.Combined(), result.Error
48}
49
50// Command creates a docker CLI command against this daemon, to be executed later.
51// Example: d.Command("version") creates a command to run "docker -H unix://path/to/unix.sock version"
52func (d *Daemon) Command(args ...string) icmd.Cmd {
53	return icmd.Command(d.dockerBinary, d.PrependHostArg(args)...)
54}
55
56// PrependHostArg prepend the specified arguments by the daemon host flags
57func (d *Daemon) PrependHostArg(args []string) []string {
58	for _, arg := range args {
59		if arg == "--host" || arg == "-H" {
60			return args
61		}
62	}
63	return append([]string{"--host", d.Sock()}, args...)
64}
65
66// GetIDByName returns the ID of an object (container, volume, …) given its name
67func (d *Daemon) GetIDByName(name string) (string, error) {
68	return d.inspectFieldWithError(name, "Id")
69}
70
71// InspectField returns the field filter by 'filter'
72func (d *Daemon) InspectField(name, filter string) (string, error) {
73	return d.inspectFilter(name, filter)
74}
75
76func (d *Daemon) inspectFilter(name, filter string) (string, error) {
77	format := fmt.Sprintf("{{%s}}", filter)
78	out, err := d.Cmd("inspect", "-f", format, name)
79	if err != nil {
80		return "", errors.Errorf("failed to inspect %s: %s", name, out)
81	}
82	return strings.TrimSpace(out), nil
83}
84
85func (d *Daemon) inspectFieldWithError(name, field string) (string, error) {
86	return d.inspectFilter(name, fmt.Sprintf(".%s", field))
87}
88
89// CheckActiveContainerCount returns the number of active containers
90// FIXME(vdemeester) should re-use ActivateContainers in some way
91func (d *Daemon) CheckActiveContainerCount(c *testing.T) (interface{}, string) {
92	out, err := d.Cmd("ps", "-q")
93	assert.NilError(c, err)
94	if len(strings.TrimSpace(out)) == 0 {
95		return 0, ""
96	}
97	return len(strings.Split(strings.TrimSpace(out), "\n")), fmt.Sprintf("output: %q", string(out))
98}
99
100// WaitRun waits for a container to be running for 10s
101func (d *Daemon) WaitRun(contID string) error {
102	args := []string{"--host", d.Sock()}
103	return WaitInspectWithArgs(d.dockerBinary, contID, "{{.State.Running}}", "true", 10*time.Second, args...)
104}
105
106// WaitInspectWithArgs waits for the specified expression to be equals to the specified expected string in the given time.
107// Deprecated: use cli.WaitCmd instead
108func WaitInspectWithArgs(dockerBinary, name, expr, expected string, timeout time.Duration, arg ...string) error {
109	after := time.After(timeout)
110
111	args := append(arg, "inspect", "-f", expr, name)
112	for {
113		result := icmd.RunCommand(dockerBinary, args...)
114		if result.Error != nil {
115			if !strings.Contains(strings.ToLower(result.Stderr()), "no such") {
116				return errors.Errorf("error executing docker inspect: %v\n%s",
117					result.Stderr(), result.Stdout())
118			}
119			select {
120			case <-after:
121				return result.Error
122			default:
123				time.Sleep(10 * time.Millisecond)
124				continue
125			}
126		}
127
128		out := strings.TrimSpace(result.Stdout())
129		if out == expected {
130			break
131		}
132
133		select {
134		case <-after:
135			return errors.Errorf("condition \"%q == %q\" not true in time (%v)", out, expected, timeout)
136		default:
137		}
138
139		time.Sleep(100 * time.Millisecond)
140	}
141	return nil
142}
143