Skip to content

Commit 55d7e14

Browse files
committed
implement kill tests
Signed-off-by: Liang Chenye <[email protected]>
1 parent 4902e9c commit 55d7e14

File tree

4 files changed

+161
-1
lines changed

4 files changed

+161
-1
lines changed

validation/kill.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"time"
7+
8+
"github.com/mndrix/tap-go"
9+
rspecs "github.com/opencontainers/runtime-spec/specs-go"
10+
"github.com/opencontainers/runtime-tools/generate"
11+
"github.com/opencontainers/runtime-tools/specerror"
12+
"github.com/opencontainers/runtime-tools/validation/util"
13+
uuid "github.com/satori/go.uuid"
14+
)
15+
16+
func main() {
17+
t := tap.New()
18+
t.Header(0)
19+
bundleDir, err := util.PrepareBundle()
20+
if err != nil {
21+
util.Fatal(err)
22+
}
23+
defer os.RemoveAll(bundleDir)
24+
25+
stoppedConfig := util.GetDefaultGenerator()
26+
stoppedConfig.SetProcessArgs([]string{"true"})
27+
runningConfig := util.GetDefaultGenerator()
28+
runningConfig.SetProcessArgs([]string{"sleep", "30"})
29+
containerID := uuid.NewV4().String()
30+
31+
cases := []struct {
32+
config *generate.Generator
33+
id string
34+
action util.LifecycleAction
35+
errExpected bool
36+
err error
37+
}{
38+
// Note: the nil config test case should run first since we are re-using the bundle
39+
// kill without id
40+
{nil, "", util.LifecycleActionNone, false, specerror.NewError(specerror.KillWithoutIDGenError, fmt.Errorf("`kill` operation MUST generate an error if it is not provided the container ID"), rspecs.Version)},
41+
// kill a non exist container
42+
{nil, containerID, util.LifecycleActionNone, false, specerror.NewError(specerror.KillNonCreateRunGenError, fmt.Errorf("attempting to send a signal to a container that is neither `created` nor `running` MUST generate an error"), rspecs.Version)},
43+
// kill a created
44+
{stoppedConfig, containerID, util.LifecycleActionCreate | util.LifecycleActionDelete, true, specerror.NewError(specerror.KillSignalImplement, fmt.Errorf("`kill` operation MUST send the specified signal to the container process"), rspecs.Version)},
45+
// kill a stopped
46+
{stoppedConfig, containerID, util.LifecycleActionCreate | util.LifecycleActionStart | util.LifecycleActionDelete, false, specerror.NewError(specerror.KillSignalImplement, fmt.Errorf("`kill` operation MUST send the specified signal to the container process"), rspecs.Version)},
47+
// kill a running
48+
{runningConfig, containerID, util.LifecycleActionCreate | util.LifecycleActionStart | util.LifecycleActionDelete, true, specerror.NewError(specerror.KillSignalImplement, fmt.Errorf("`kill` operation MUST send the specified signal to the container process"), rspecs.Version)},
49+
}
50+
51+
for _, c := range cases {
52+
config := util.LifecycleConfig{
53+
Config: c.config,
54+
BundleDir: bundleDir,
55+
Actions: c.action,
56+
PreCreate: func(r *util.Runtime) error {
57+
r.SetID(c.id)
58+
return nil
59+
},
60+
PreDelete: func(r *util.Runtime) error {
61+
// waiting the 'stoppedConfig' testcase to stop
62+
// the 'runningConfig' testcase sleeps 30 seconds, so 10 seconds are enough for this case
63+
util.WaitingForStatus(*r, util.LifecycleStatusCreated|util.LifecycleStatusStopped, time.Second*10, time.Second*1)
64+
// KILL MUST be supported and KILL cannot be trapped
65+
err := r.Kill("KILL")
66+
util.WaitingForStatus(*r, util.LifecycleStatusStopped, time.Second*10, time.Second*1)
67+
return err
68+
},
69+
}
70+
err := util.RuntimeLifecycleValidate(config)
71+
util.SpecErrorOK(t, (err == nil) == c.errExpected, c.err, err)
72+
}
73+
74+
t.AutoPlan()
75+
}

validation/killsig.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"time"
8+
9+
"github.com/mndrix/tap-go"
10+
rspecs "github.com/opencontainers/runtime-spec/specs-go"
11+
"github.com/opencontainers/runtime-tools/specerror"
12+
"github.com/opencontainers/runtime-tools/validation/util"
13+
uuid "github.com/satori/go.uuid"
14+
)
15+
16+
var signals = []string{
17+
"TERM",
18+
}
19+
20+
func main() {
21+
t := tap.New()
22+
t.Header(0)
23+
bundleDir, err := util.PrepareBundle()
24+
if err != nil {
25+
util.Fatal(err)
26+
}
27+
defer os.RemoveAll(bundleDir)
28+
29+
containerID := uuid.NewV4().String()
30+
sigConfig := util.GetDefaultGenerator()
31+
rootDir := filepath.Join(bundleDir, sigConfig.Spec().Root.Path)
32+
for _, signal := range signals {
33+
sigConfig.SetProcessArgs([]string{"sh", "-c", fmt.Sprintf("trap 'touch /%s' %s; sleep 10 & wait $!", signal, signal)})
34+
config := util.LifecycleConfig{
35+
Config: sigConfig,
36+
BundleDir: bundleDir,
37+
Actions: util.LifecycleActionCreate | util.LifecycleActionStart | util.LifecycleActionDelete,
38+
PreCreate: func(r *util.Runtime) error {
39+
r.SetID(containerID)
40+
return nil
41+
},
42+
PreDelete: func(r *util.Runtime) error {
43+
util.WaitingForStatus(*r, util.LifecycleStatusRunning, time.Second*5, time.Second*1)
44+
err := r.Kill(signal)
45+
// wait before the container been deleted
46+
util.WaitingForStatus(*r, util.LifecycleStatusStopped, time.Second*5, time.Second*1)
47+
return err
48+
},
49+
}
50+
err := util.RuntimeLifecycleValidate(config)
51+
if err != nil {
52+
util.SpecErrorOK(t, false, specerror.NewError(specerror.KillSignalImplement, fmt.Errorf("`kill` operation MUST send the specified signal to the container process"), rspecs.Version), err)
53+
} else {
54+
_, err = os.Stat(filepath.Join(rootDir, signal))
55+
util.SpecErrorOK(t, err == nil, specerror.NewError(specerror.KillSignalImplement, fmt.Errorf("`kill` operation MUST send the specified signal to the container process"), rspecs.Version), err)
56+
}
57+
}
58+
t.AutoPlan()
59+
}

validation/util/container.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ type Runtime struct {
2626
stderr *os.File
2727
}
2828

29+
// DefaultSignal represents the default signal sends to a container
30+
const DefaultSignal = "TERM"
31+
2932
// NewRuntime create a runtime by command and the bundle directory
3033
func NewRuntime(runtimeCommand string, bundleDir string) (Runtime, error) {
3134
var r Runtime
@@ -158,6 +161,25 @@ func (r *Runtime) State() (rspecs.State, error) {
158161
return state, err
159162
}
160163

164+
// Kill a container
165+
func (r *Runtime) Kill(sig string) (err error) {
166+
var args []string
167+
args = append(args, "kill")
168+
if r.ID != "" {
169+
args = append(args, r.ID)
170+
}
171+
if sig != "" {
172+
// TODO: runc does not support this
173+
// args = append(args, "--signal", sig)
174+
args = append(args, sig)
175+
} else {
176+
args = append(args, DefaultSignal)
177+
}
178+
179+
cmd := exec.Command(r.RuntimeCommand, args...)
180+
return execWithStderrFallbackToStdout(cmd)
181+
}
182+
161183
// Delete a container
162184
func (r *Runtime) Delete() (err error) {
163185
var args []string

validation/util/test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,12 @@ func RuntimeLifecycleValidate(config LifecycleConfig) error {
327327
if _, err := r.State(); err != nil {
328328
return
329329
}
330-
if err := WaitingForStatus(r, LifecycleStatusCreated|LifecycleStatusStopped, time.Second*10, time.Second*1); err != nil {
330+
err := WaitingForStatus(r, LifecycleStatusCreated|LifecycleStatusStopped, time.Second*10, time.Second*1)
331+
if err == nil {
331332
r.Delete()
333+
} else {
334+
os.Stderr.WriteString("failed to delete the container\n")
335+
os.Stderr.WriteString(err.Error())
332336
}
333337
}()
334338
}

0 commit comments

Comments
 (0)