Skip to content

Commit 7ac117a

Browse files
author
Zhou Hao
authored
Merge pull request #557 from liangchenye/cli-complete
Fix #556
2 parents e241035 + 5e8b51e commit 7ac117a

File tree

3 files changed

+217
-4
lines changed

3 files changed

+217
-4
lines changed

validation/pidfile.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"io/ioutil"
6+
"os"
7+
"path/filepath"
8+
"strconv"
9+
"time"
10+
11+
tap "github.com/mndrix/tap-go"
12+
"github.com/opencontainers/runtime-tools/validation/util"
13+
)
14+
15+
func main() {
16+
t := tap.New()
17+
t.Header(0)
18+
19+
tempDir, err := ioutil.TempDir("", "oci-pid")
20+
if err != nil {
21+
util.Fatal(err)
22+
}
23+
defer os.RemoveAll(tempDir)
24+
tempPidFile := filepath.Join(tempDir, "pidfile")
25+
26+
config := util.LifecycleConfig{
27+
Actions: util.LifecycleActionCreate | util.LifecycleActionDelete,
28+
PreCreate: func(r *util.Runtime) error {
29+
r.PidFile = tempPidFile
30+
return nil
31+
},
32+
PostCreate: func(r *util.Runtime) error {
33+
pidData, err := ioutil.ReadFile(tempPidFile)
34+
if err != nil {
35+
return err
36+
}
37+
pid, err := strconv.Atoi(string(pidData))
38+
if err != nil {
39+
return err
40+
}
41+
state, err := r.State()
42+
if err != nil {
43+
return err
44+
}
45+
if state.Pid != pid {
46+
return errors.New("Wrong pid in the pidfile")
47+
}
48+
return nil
49+
},
50+
PreDelete: func(r *util.Runtime) error {
51+
return util.WaitingForStatus(*r, util.LifecycleStatusCreated, time.Second*10, time.Second*1)
52+
},
53+
}
54+
55+
g := util.GetDefaultGenerator()
56+
g.SetProcessArgs([]string{"true"})
57+
err = util.RuntimeLifecycleValidate(g, config)
58+
if err != nil {
59+
util.Fatal(err)
60+
}
61+
62+
t.AutoPlan()
63+
}

validation/util/container.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
type Runtime struct {
1919
RuntimeCommand string
2020
BundleDir string
21+
PidFile string
2122
ID string
2223
stdout *os.File
2324
stderr *os.File
@@ -63,7 +64,9 @@ func (r *Runtime) Create() (stderr []byte, err error) {
6364
if r.ID != "" {
6465
args = append(args, r.ID)
6566
}
66-
67+
if r.PidFile != "" {
68+
args = append(args, "--pid-file", r.PidFile)
69+
}
6770
if r.BundleDir != "" {
6871
args = append(args, "--bundle", r.BundleDir)
6972
}
@@ -146,23 +149,32 @@ func (r *Runtime) State() (rspecs.State, error) {
146149
}
147150

148151
// Delete a container
149-
func (r *Runtime) Delete() error {
152+
func (r *Runtime) Delete() ([]byte, error) {
150153
var args []string
154+
var stderr []byte
151155
args = append(args, "delete")
152156
if r.ID != "" {
153157
args = append(args, r.ID)
154158
}
155159

156160
cmd := exec.Command(r.RuntimeCommand, args...)
157-
return cmd.Run()
161+
stdout, err := cmd.Output()
162+
if e, ok := err.(*exec.ExitError); ok {
163+
stderr = e.Stderr
164+
}
165+
if err != nil && len(stderr) == 0 {
166+
stderr = stdout
167+
}
168+
169+
return stderr, err
158170
}
159171

160172
// Clean deletes the container. If removeBundle is set, the bundle
161173
// directory is removed after the container is deleted succesfully or, if
162174
// forceRemoveBundle is true, after the deletion attempt regardless of
163175
// whether it was successful or not.
164176
func (r *Runtime) Clean(removeBundle bool, forceRemoveBundle bool) error {
165-
err := r.Delete()
177+
_, err := r.Delete()
166178

167179
if removeBundle && (err == nil || forceRemoveBundle) {
168180
err2 := os.RemoveAll(r.bundleDir())

validation/util/test.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package util
22

33
import (
4+
"errors"
45
"fmt"
56
"io/ioutil"
67
"os"
@@ -21,6 +22,50 @@ var (
2122
RuntimeCommand = "runc"
2223
)
2324

25+
// LifecycleAction defines the phases will be called.
26+
type LifecycleAction int
27+
28+
const (
29+
// LifecycleActionCreate creates a container
30+
LifecycleActionCreate = 1 << iota
31+
// LifecycleActionStart starts a container
32+
LifecycleActionStart
33+
// LifecycleActionDelete deletes a container
34+
LifecycleActionDelete
35+
)
36+
37+
// LifecycleStatus follows https://github.com/opencontainers/runtime-spec/blob/master/runtime.md#state
38+
type LifecycleStatus int
39+
40+
const (
41+
// LifecycleStatusCreating "creating"
42+
LifecycleStatusCreating = 1 << iota
43+
// LifecycleStatusCreated "created"
44+
LifecycleStatusCreated
45+
// LifecycleStatusRunning "running"
46+
LifecycleStatusRunning
47+
// LifecycleStatusStopped "stopped"
48+
LifecycleStatusStopped
49+
)
50+
51+
var lifecycleStatusMap = map[string]LifecycleStatus{
52+
"creating": LifecycleStatusCreating,
53+
"created": LifecycleStatusCreated,
54+
"running": LifecycleStatusRunning,
55+
"stopped": LifecycleStatusStopped,
56+
}
57+
58+
// LifecycleConfig includes
59+
// 1. Actions to define the default running lifecycles.
60+
// 2. Four phases for user to add his/her own operations.
61+
type LifecycleConfig struct {
62+
Actions LifecycleAction
63+
PreCreate func(runtime *Runtime) error
64+
PostCreate func(runtime *Runtime) error
65+
PreDelete func(runtime *Runtime) error
66+
PostDelete func(runtime *Runtime) error
67+
}
68+
2469
// PreFunc initializes the test environment after preparing the bundle
2570
// but before creating the container.
2671
type PreFunc func(string) error
@@ -78,6 +123,28 @@ func GetDefaultGenerator() *generate.Generator {
78123
return &g
79124
}
80125

126+
// WaitingForStatus waits an expected runtime status, return error if
127+
// 1. fail to query the status
128+
// 2. timeout
129+
func WaitingForStatus(r Runtime, status LifecycleStatus, retryTimeout time.Duration, pollInterval time.Duration) error {
130+
for start := time.Now(); time.Since(start) < retryTimeout; time.Sleep(pollInterval) {
131+
state, err := r.State()
132+
if err != nil {
133+
return err
134+
}
135+
if v, ok := lifecycleStatusMap[state.Status]; ok {
136+
if status&v != 0 {
137+
return nil
138+
}
139+
} else {
140+
// In spec, it says 'Additional values MAY be defined by the runtime'.
141+
continue
142+
}
143+
}
144+
145+
return errors.New("timeout in waiting for the container status")
146+
}
147+
81148
// RuntimeInsideValidate runs runtimetest inside a container.
82149
func RuntimeInsideValidate(g *generate.Generator, f PreFunc) (err error) {
83150
bundleDir, err := PrepareBundle()
@@ -184,3 +251,74 @@ func RuntimeOutsideValidate(g *generate.Generator, f AfterFunc) error {
184251
}
185252
return nil
186253
}
254+
255+
// RuntimeLifecycleValidate validates runtime lifecycle.
256+
func RuntimeLifecycleValidate(g *generate.Generator, config LifecycleConfig) error {
257+
bundleDir, err := PrepareBundle()
258+
if err != nil {
259+
return err
260+
}
261+
r, err := NewRuntime(RuntimeCommand, bundleDir)
262+
if err != nil {
263+
os.RemoveAll(bundleDir)
264+
return err
265+
}
266+
defer r.Clean(true, true)
267+
err = r.SetConfig(g)
268+
if err != nil {
269+
return err
270+
}
271+
r.SetID(uuid.NewV4().String())
272+
273+
if config.PreCreate != nil {
274+
if err := config.PreCreate(&r); err != nil {
275+
return err
276+
}
277+
}
278+
279+
if config.Actions&LifecycleActionCreate != 0 {
280+
stderr, err := r.Create()
281+
if err != nil {
282+
os.Stderr.WriteString("failed to create the container\n")
283+
os.Stderr.Write(stderr)
284+
return err
285+
}
286+
}
287+
288+
if config.PostCreate != nil {
289+
if err := config.PostCreate(&r); err != nil {
290+
return err
291+
}
292+
}
293+
294+
if config.Actions&LifecycleActionStart != 0 {
295+
stderr, err := r.Start()
296+
if err != nil {
297+
os.Stderr.WriteString("failed to start the container\n")
298+
os.Stderr.Write(stderr)
299+
return err
300+
}
301+
}
302+
303+
if config.PreDelete != nil {
304+
if err := config.PreDelete(&r); err != nil {
305+
return err
306+
}
307+
}
308+
309+
if config.Actions&LifecycleActionDelete != 0 {
310+
stderr, err := r.Delete()
311+
if err != nil {
312+
os.Stderr.WriteString("failed to delete the container\n")
313+
os.Stderr.Write(stderr)
314+
return err
315+
}
316+
}
317+
318+
if config.PostDelete != nil {
319+
if err := config.PostDelete(&r); err != nil {
320+
return err
321+
}
322+
}
323+
return nil
324+
}

0 commit comments

Comments
 (0)