1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package containerd
17
18 import (
19 "context"
20 "fmt"
21 "syscall"
22
23 "github.com/containerd/containerd"
24 "github.com/pkg/errors"
25
26 "github.com/chaos-mesh/chaos-mesh/pkg/mock"
27 )
28
29 const (
30 containerdProtocolPrefix = "containerd://"
31
32
33
34 containerKindLabel = "io.cri-containerd.kind"
35 containerKindContainer = "container"
36 )
37
38
39 type ContainerdClientInterface interface {
40 LoadContainer(ctx context.Context, id string) (containerd.Container, error)
41 Containers(ctx context.Context, filters ...string) ([]containerd.Container, error)
42 }
43
44
45 type ContainerdClient struct {
46 client ContainerdClientInterface
47 }
48
49
50 func (c ContainerdClient) FormatContainerID(ctx context.Context, containerID string) (string, error) {
51 if len(containerID) < len(containerdProtocolPrefix) {
52 return "", errors.Errorf("container id %s is not a containerd container id", containerID)
53 }
54 if containerID[0:len(containerdProtocolPrefix)] != containerdProtocolPrefix {
55 return "", errors.Errorf("expected %s but got %s", containerdProtocolPrefix, containerID[0:len(containerdProtocolPrefix)])
56 }
57 return containerID[len(containerdProtocolPrefix):], nil
58 }
59
60
61 func (c ContainerdClient) GetPidFromContainerID(ctx context.Context, containerID string) (uint32, error) {
62 id, err := c.FormatContainerID(ctx, containerID)
63 if err != nil {
64 return 0, err
65 }
66 container, err := c.client.LoadContainer(ctx, id)
67 if err != nil {
68 return 0, err
69 }
70 task, err := container.Task(ctx, nil)
71 if err != nil {
72 return 0, err
73 }
74 return task.Pid(), nil
75 }
76
77
78 func (c ContainerdClient) ContainerKillByContainerID(ctx context.Context, containerID string) error {
79 containerID, err := c.FormatContainerID(ctx, containerID)
80 if err != nil {
81 return err
82 }
83
84 container, err := c.client.LoadContainer(ctx, containerID)
85 if err != nil {
86 return err
87 }
88 task, err := container.Task(ctx, nil)
89 if err != nil {
90 return err
91 }
92
93 err = task.Kill(ctx, syscall.SIGKILL)
94
95 return err
96 }
97
98
99 func (c ContainerdClient) ListContainerIDs(ctx context.Context) ([]string, error) {
100
101
102 filter := fmt.Sprintf("labels.%q==%q", containerKindLabel, containerKindContainer)
103 containers, err := c.client.Containers(ctx, filter)
104 if err != nil {
105 return nil, err
106 }
107
108 var ids []string
109 for _, container := range containers {
110 id := fmt.Sprintf("%s%s", containerdProtocolPrefix, container.ID())
111 ids = append(ids, id)
112 }
113 return ids, nil
114 }
115
116
117 func (c ContainerdClient) GetLabelsFromContainerID(ctx context.Context, containerID string) (map[string]string, error) {
118 id, err := c.FormatContainerID(ctx, containerID)
119 if err != nil {
120 return nil, err
121 }
122
123 container, err := c.client.LoadContainer(ctx, id)
124 if err != nil {
125 return nil, err
126 }
127
128 labels, err := container.Labels(ctx)
129 if err != nil {
130 return nil, err
131 }
132
133 return labels, nil
134 }
135
136 func New(address string, opts ...containerd.ClientOpt) (*ContainerdClient, error) {
137
138 if err := mock.On("NewContainerdClientError"); err != nil {
139 return nil, err.(error)
140 }
141 if client := mock.On("MockContainerdClient"); client != nil {
142 return &ContainerdClient{
143 client.(ContainerdClientInterface),
144 }, nil
145 }
146
147 c, err := containerd.New(address, opts...)
148 if err != nil {
149 return nil, err
150 }
151
152 return &ContainerdClient{
153 client: c,
154 }, nil
155 }
156
157
158 var WithDefaultNamespace = containerd.WithDefaultNamespace
159