1package main
2
3import (
4	"context"
5	"math/rand"
6	"strconv"
7	"testing"
8	"time"
9
10	"github.com/containerd/containerd/errdefs"
11)
12
13func setupTestHcsTask(t *testing.T) (*hcsTask, *testShimExec, *testShimExec) {
14	initExec := newTestShimExec(t.Name(), t.Name(), int(rand.Int31()))
15	lt := &hcsTask{
16		events: newFakePublisher(),
17		id:     t.Name(),
18		init:   initExec,
19		closed: make(chan struct{}),
20	}
21	secondExecID := strconv.Itoa(rand.Int())
22	secondExec := newTestShimExec(t.Name(), secondExecID, int(rand.Int31()))
23	lt.execs.Store(secondExecID, secondExec)
24	return lt, initExec, secondExec
25}
26
27func Test_hcsTask_ID(t *testing.T) {
28	lt, _, _ := setupTestHcsTask(t)
29
30	if lt.ID() != t.Name() {
31		t.Fatalf("expect ID: '%s', got: '%s'", t.Name(), lt.ID())
32	}
33}
34
35func Test_hcsTask_GetExec_Empty_Success(t *testing.T) {
36	lt, i, _ := setupTestHcsTask(t)
37
38	e, err := lt.GetExec("")
39	if err != nil {
40		t.Fatalf("should not have failed with error: %v", err)
41	}
42	if i != e {
43		t.Fatal("should of returned the init exec on empty")
44	}
45}
46
47func Test_hcsTask_GetExec_UnknownExecID_Error(t *testing.T) {
48	lt, _, _ := setupTestHcsTask(t)
49
50	e, err := lt.GetExec("shouldnotmatch")
51
52	verifyExpectedError(t, e, err, errdefs.ErrNotFound)
53}
54
55func Test_hcsTask_GetExec_2ndID_Success(t *testing.T) {
56	lt, _, second := setupTestHcsTask(t)
57
58	e, err := lt.GetExec(second.id)
59	if err != nil {
60		t.Fatalf("should not have failed with error: %v", err)
61	}
62	if second != e {
63		t.Fatal("should of returned the second exec")
64	}
65}
66
67func Test_hcsTask_KillExec_UnknownExecID_Error(t *testing.T) {
68	lt, _, _ := setupTestHcsTask(t)
69
70	err := lt.KillExec(context.TODO(), "thisshouldnotmatch", 0xf, false)
71
72	verifyExpectedError(t, nil, err, errdefs.ErrNotFound)
73}
74
75func Test_hcsTask_KillExec_InitExecID_Unexited2ndExec_Success(t *testing.T) {
76	lt, init, second := setupTestHcsTask(t)
77
78	err := lt.KillExec(context.TODO(), "", 0xf, false)
79	if err != nil {
80		t.Fatalf("should not have failed, got: %v", err)
81	}
82	if init.state != shimExecStateExited {
83		t.Fatalf("init should be in exited state got: %v", init.state)
84	}
85	// A real platform would take this down when the pid namespace or silo goes
86	// down. For the test verify the shim did not issue the signal.
87	if second.state != shimExecStateCreated {
88		t.Fatalf("2nd exec should be in created state, got: %v", second.state)
89	}
90}
91
92func Test_hcsTask_KillExec_InitExecID_All_Success(t *testing.T) {
93	lt, init, second := setupTestHcsTask(t)
94
95	err := lt.KillExec(context.TODO(), "", 0xf, true)
96	if err != nil {
97		t.Fatalf("should not have failed, got: %v", err)
98	}
99	if init.state != shimExecStateExited {
100		t.Fatalf("init should be in exited state got: %v", init.state)
101	}
102	if second.state != shimExecStateExited {
103		t.Fatalf("2nd exec should be in exited state got: %v", second.state)
104	}
105}
106
107func Test_hcsTask_KillExec_2ndExecID_Success(t *testing.T) {
108	lt, _, second := setupTestHcsTask(t)
109
110	err := lt.KillExec(context.TODO(), second.id, 0xf, false)
111	if err != nil {
112		t.Fatalf("should not have failed, got: %v", err)
113	}
114	if second.state != shimExecStateExited {
115		t.Fatalf("2nd exec should be in exited state got: %v", second.state)
116	}
117}
118
119func Test_hcsTask_KillExec_2ndExecID_All_Error(t *testing.T) {
120	lt, _, second := setupTestHcsTask(t)
121
122	err := lt.KillExec(context.TODO(), second.id, 0xf, true)
123
124	verifyExpectedError(t, nil, err, errdefs.ErrFailedPrecondition)
125}
126
127func verifyDeleteFailureValues(t *testing.T, pid int, status uint32, at time.Time) {
128	if pid != 0 {
129		t.Fatalf("pid expected '0' got: '%d'", pid)
130	}
131	if status != 0 {
132		t.Fatalf("status expected '0' got: '%d'", status)
133	}
134	if !at.IsZero() {
135		t.Fatalf("at expected 'zero' got: '%v'", at)
136	}
137}
138
139func verifyDeleteSuccessValues(t *testing.T, pid int, status uint32, at time.Time, e *testShimExec) {
140	if pid != e.pid {
141		t.Fatalf("pid expected '%d' got: '%d'", e.pid, pid)
142	}
143	if status != e.status {
144		t.Fatalf("status expected '%d' got: '%d'", e.status, status)
145	}
146	if at != e.at {
147		t.Fatalf("at expected '%v' got: '%v'", e.at, at)
148	}
149}
150
151func Test_hcsTask_DeleteExec_UnknownExecID_Error(t *testing.T) {
152	lt, _, _ := setupTestHcsTask(t)
153
154	pid, status, at, err := lt.DeleteExec(context.TODO(), "thisshouldnotmatch")
155	verifyExpectedError(t, nil, err, errdefs.ErrNotFound)
156	verifyDeleteFailureValues(t, pid, status, at)
157}
158
159func Test_hcsTask_DeleteExec_InitExecID_CreatedState_Success(t *testing.T) {
160	lt, init, second := setupTestHcsTask(t)
161	// remove the 2nd exec so we just check without it.
162	lt.execs.Delete(second.id)
163
164	// try to delete the init exec
165	pid, status, at, err := lt.DeleteExec(context.TODO(), "")
166
167	if err != nil {
168		t.Fatalf("expected nil err got: %v", err)
169	}
170	verifyDeleteSuccessValues(t, pid, status, at, init)
171}
172
173func Test_hcsTask_DeleteExec_InitExecID_RunningState_Error(t *testing.T) {
174	lt, init, second := setupTestHcsTask(t)
175	// remove the 2nd exec so we just check without it.
176	lt.execs.Delete(second.id)
177
178	// Start the init exec
179	init.Start(context.TODO())
180
181	// try to delete the init exec
182	pid, status, at, err := lt.DeleteExec(context.TODO(), "")
183
184	verifyExpectedError(t, nil, err, errdefs.ErrFailedPrecondition)
185	verifyDeleteFailureValues(t, pid, status, at)
186}
187
188func Test_hcsTask_DeleteExec_InitExecID_ExitedState_Success(t *testing.T) {
189	lt, init, second := setupTestHcsTask(t)
190	// remove the 2nd exec so we just check without it.
191	lt.execs.Delete(second.id)
192
193	init.Kill(context.TODO(), 0xf)
194
195	// try to delete the init exec
196	pid, status, at, err := lt.DeleteExec(context.TODO(), "")
197
198	if err != nil {
199		t.Fatalf("expected nil err got: %v", err)
200	}
201	verifyDeleteSuccessValues(t, pid, status, at, init)
202}
203
204func Test_hcsTask_DeleteExec_InitExecID_2ndExec_CreatedState_Error(t *testing.T) {
205	lt, init, second := setupTestHcsTask(t)
206
207	// start the init exec (required to have 2nd exec)
208	init.Start(context.TODO())
209
210	// try to delete the init exec
211	pid, status, at, err := lt.DeleteExec(context.TODO(), "")
212
213	verifyExpectedError(t, nil, err, errdefs.ErrFailedPrecondition)
214	verifyDeleteFailureValues(t, pid, status, at)
215	if second.state != shimExecStateExited {
216		t.Fatalf("2nd exec should be in exited state, got: %v", second.state)
217	}
218}
219
220func Test_hcsTask_DeleteExec_InitExecID_2ndExec_RunningState_Error(t *testing.T) {
221	lt, init, second := setupTestHcsTask(t)
222
223	// start the init exec (required to have 2nd exec)
224	init.Start(context.TODO())
225
226	// put the 2nd exec into the running state
227	second.Start(context.TODO())
228
229	// try to delete the init exec
230	pid, status, at, err := lt.DeleteExec(context.TODO(), "")
231
232	verifyExpectedError(t, nil, err, errdefs.ErrFailedPrecondition)
233	verifyDeleteFailureValues(t, pid, status, at)
234	if second.state != shimExecStateExited {
235		t.Fatalf("2nd exec should be in exited state, got: %v", second.state)
236	}
237}
238
239func Test_hcsTask_DeleteExec_InitExecID_2ndExec_ExitedState_Success(t *testing.T) {
240	lt, init, second := setupTestHcsTask(t)
241
242	// put the init exec into the exited state
243	init.Kill(context.TODO(), 0xf)
244	// put the 2nd exec into the exited state
245	second.Kill(context.TODO(), 0xf)
246
247	// try to delete the init exec
248	pid, status, at, err := lt.DeleteExec(context.TODO(), "")
249
250	if err != nil {
251		t.Fatalf("expected nil err got: %v", err)
252	}
253	verifyDeleteSuccessValues(t, pid, status, at, init)
254}
255
256func Test_hcsTask_DeleteExec_2ndExecID_CreatedState_Success(t *testing.T) {
257	lt, init, second := setupTestHcsTask(t)
258
259	// start the init exec (required to have 2nd exec)
260	init.Start(context.TODO())
261
262	// try to delete the 2nd exec
263	pid, status, at, err := lt.DeleteExec(context.TODO(), second.id)
264
265	if err != nil {
266		t.Fatalf("expected nil err got: %v", err)
267	}
268	verifyDeleteSuccessValues(t, pid, status, at, second)
269}
270
271func Test_hcsTask_DeleteExec_2ndExecID_RunningState_Error(t *testing.T) {
272	lt, init, second := setupTestHcsTask(t)
273
274	// start the init exec (required to have 2nd exec)
275	init.Start(context.TODO())
276
277	// put the 2nd exec into the running state
278	second.Start(context.TODO())
279
280	// try to delete the 2nd exec
281	pid, status, at, err := lt.DeleteExec(context.TODO(), second.id)
282
283	verifyExpectedError(t, nil, err, errdefs.ErrFailedPrecondition)
284	verifyDeleteFailureValues(t, pid, status, at)
285}
286
287func Test_hcsTask_DeleteExec_2ndExecID_ExitedState_Success(t *testing.T) {
288	lt, init, second := setupTestHcsTask(t)
289
290	// start the init exec (required to have 2nd exec)
291	init.Kill(context.TODO(), 0xf)
292
293	// put the 2nd exec into the exited state
294	second.Kill(context.TODO(), 0xf)
295
296	// try to delete the 2nd exec
297	pid, status, at, err := lt.DeleteExec(context.TODO(), second.id)
298
299	if err != nil {
300		t.Fatalf("expected nil err got: %v", err)
301	}
302	verifyDeleteSuccessValues(t, pid, status, at, second)
303}
304