1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package docker
17
18 import (
19 "context"
20 "fmt"
21 "net/http"
22
23 "github.com/docker/docker/api/types"
24 "github.com/docker/docker/api/types/filters"
25 dockerclient "github.com/docker/docker/client"
26 "github.com/pkg/errors"
27
28 "github.com/chaos-mesh/chaos-mesh/pkg/mock"
29 )
30
31 const (
32 dockerProtocolPrefix = "docker://"
33
34
35
36 containerKindLabel = "io.kubernetes.docker.type"
37 containerKindContainer = "container"
38 )
39
40
41 type DockerClientInterface interface {
42 ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error)
43 ContainerKill(ctx context.Context, containerID, signal string) error
44 ContainerList(ctx context.Context, options types.ContainerListOptions) ([]types.Container, error)
45 }
46
47
48 type DockerClient struct {
49 client DockerClientInterface
50 }
51
52
53 func (c DockerClient) FormatContainerID(ctx context.Context, containerID string) (string, error) {
54 if len(containerID) < len(dockerProtocolPrefix) {
55 return "", errors.Errorf("container id %s is not a docker container id", containerID)
56 }
57 if containerID[0:len(dockerProtocolPrefix)] != dockerProtocolPrefix {
58 return "", errors.Errorf("expected %s but got %s", dockerProtocolPrefix, containerID[0:len(dockerProtocolPrefix)])
59 }
60 return containerID[len(dockerProtocolPrefix):], nil
61 }
62
63
64 func (c DockerClient) GetPidFromContainerID(ctx context.Context, containerID string) (uint32, error) {
65 id, err := c.FormatContainerID(ctx, containerID)
66 if err != nil {
67 return 0, err
68 }
69 container, err := c.client.ContainerInspect(ctx, id)
70 if err != nil {
71 return 0, err
72 }
73
74 if container.State.Pid == 0 {
75 return 0, errors.Errorf("container is not running, status: %s", container.State.Status)
76 }
77
78 return uint32(container.State.Pid), nil
79 }
80
81
82 func (c DockerClient) ContainerKillByContainerID(ctx context.Context, containerID string) error {
83 id, err := c.FormatContainerID(ctx, containerID)
84 if err != nil {
85 return err
86 }
87 err = c.client.ContainerKill(ctx, id, "SIGKILL")
88
89 return err
90 }
91
92
93 func (c DockerClient) ListContainerIDs(ctx context.Context) ([]string, error) {
94
95 filterArg := filters.Arg("label", fmt.Sprintf("%s=%s", containerKindLabel, containerKindContainer))
96 containers, err := c.client.ContainerList(ctx, types.ContainerListOptions{
97 Filters: filters.NewArgs(filterArg),
98 })
99 if err != nil {
100 return nil, err
101 }
102
103 var ids []string
104 for _, container := range containers {
105 id := fmt.Sprintf("%s%s", dockerProtocolPrefix, container.ID)
106 ids = append(ids, id)
107 }
108 return ids, nil
109 }
110
111
112 func (c DockerClient) GetLabelsFromContainerID(ctx context.Context, containerID string) (map[string]string, error) {
113 id, err := c.FormatContainerID(ctx, containerID)
114 if err != nil {
115 return nil, err
116 }
117 container, err := c.client.ContainerInspect(ctx, id)
118 if err != nil {
119 return nil, err
120 }
121
122 return container.Config.Labels, nil
123 }
124
125 func New(host string, version string, client *http.Client, httpHeaders map[string]string) (*DockerClient, error) {
126
127 if err := mock.On("NewDockerClientError"); err != nil {
128 return nil, err.(error)
129 }
130 if client := mock.On("MockDockerClient"); client != nil {
131 return &DockerClient{
132 client: client.(DockerClientInterface),
133 }, nil
134 }
135
136 c, err := dockerclient.NewClientWithOpts(
137 dockerclient.FromEnv,
138 dockerclient.WithHost(host),
139 dockerclient.WithVersion(version),
140 dockerclient.WithHTTPClient(client),
141 dockerclient.WithHTTPHeaders(httpHeaders))
142 if err != nil {
143 return nil, err
144 }
145
146 return &DockerClient{
147 client: c,
148 }, nil
149 }
150