Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.

Commit fa0b048

Browse files
authored
Merge pull request #478 from tvelocity/process
interface additions: kill -a flag, ps command, pause & resume
2 parents 7446ee7 + 00dd316 commit fa0b048

File tree

5 files changed

+214
-8
lines changed

5 files changed

+214
-8
lines changed

kill.go

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ For example, if the container id is "ubuntu01" the following will send a "KILL"
6969
signal to the init process of the "ubuntu01" container:
7070
7171
# runv kill ubuntu01 KILL`,
72+
Flags: []cli.Flag{
73+
cli.BoolFlag{
74+
Name: "all, a",
75+
Usage: "send the signal to all processes in the container",
76+
},
77+
},
7278
Action: func(context *cli.Context) error {
7379
container := context.Args().First()
7480
if container == "" {
@@ -88,17 +94,50 @@ signal to the init process of the "ubuntu01" container:
8894
if err != nil {
8995
return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1)
9096
}
91-
if _, err = c.Signal(netcontext.Background(), &types.SignalRequest{
92-
Id: container,
93-
Pid: "init",
94-
Signal: uint32(signal),
95-
}); err != nil {
96-
return cli.NewExitError(fmt.Sprintf("kill signal failed, %v", err), -1)
97+
98+
plist := make([]string, 0)
99+
100+
if context.Bool("all") {
101+
if plist, err = getProcessList(c, container); err != nil {
102+
return cli.NewExitError(fmt.Sprintf("can't get process list, %v", err), -1)
103+
}
104+
} else {
105+
plist = append(plist, "init")
106+
}
107+
108+
for _, p := range plist {
109+
if _, err = c.Signal(netcontext.Background(), &types.SignalRequest{
110+
Id: container,
111+
Pid: p,
112+
Signal: uint32(signal),
113+
}); err != nil {
114+
return cli.NewExitError(fmt.Sprintf("kill signal failed, %v", err), -1)
115+
}
97116
}
98117
return nil
99118
},
100119
}
101120

121+
func getProcessList(c types.APIClient, container string) ([]string, error) {
122+
s, err := c.State(netcontext.Background(), &types.StateRequest{Id: container})
123+
if err != nil {
124+
return nil, fmt.Errorf("get container state failed, %v", err)
125+
}
126+
127+
for _, cc := range s.Containers {
128+
if cc.Id == container {
129+
plist := make([]string, 0)
130+
for _, p := range cc.Processes {
131+
plist = append(plist, p.Pid)
132+
}
133+
134+
return plist, nil
135+
}
136+
}
137+
138+
return nil, fmt.Errorf("container %s not found", container)
139+
}
140+
102141
func parseSignal(rawSignal string) (syscall.Signal, error) {
103142
s, err := strconv.Atoi(rawSignal)
104143
if err == nil {

main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,15 @@ func main() {
152152
execCommand,
153153
killCommand,
154154
listCommand,
155+
psCommand,
155156
runCommand,
156157
specCommand,
157158
startCommand,
158159
stateCommand,
159160
manageCommand,
160161
shimCommand,
162+
pauseCommand,
163+
resumeCommand,
161164
containerd.ContainerdCommand,
162165
}
163166
if err := app.Run(os.Args); err != nil {

pause.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
7+
"github.com/hyperhq/runv/containerd/api/grpc/types"
8+
"github.com/hyperhq/runv/lib/linuxsignal"
9+
"github.com/urfave/cli"
10+
netcontext "golang.org/x/net/context"
11+
)
12+
13+
var pauseCommand = cli.Command{
14+
Name: "pause",
15+
Usage: "suspend all processes in the container",
16+
ArgsUsage: `<container-id>`,
17+
Action: func(context *cli.Context) error {
18+
container := context.Args().First()
19+
if container == "" {
20+
return cli.NewExitError(fmt.Sprintf("container id cannot be empty"), -1)
21+
}
22+
23+
c, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock"))
24+
if err != nil {
25+
return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1)
26+
}
27+
28+
plist, err := getProcessList(c, container)
29+
if err != nil {
30+
return cli.NewExitError(fmt.Sprintf("can't get process list, %v", err), -1)
31+
}
32+
33+
for _, p := range plist {
34+
if _, err := c.Signal(netcontext.Background(), &types.SignalRequest{
35+
Id: container,
36+
Pid: p,
37+
Signal: uint32(linuxsignal.SIGSTOP),
38+
}); err != nil {
39+
return cli.NewExitError(fmt.Sprintf("suspend signal failed, %v", err), -1)
40+
}
41+
}
42+
43+
return nil
44+
},
45+
}
46+
47+
var resumeCommand = cli.Command{
48+
Name: "resume",
49+
Usage: "resume all processes in the container",
50+
ArgsUsage: `<container-id>`,
51+
Action: func(context *cli.Context) error {
52+
container := context.Args().First()
53+
if container == "" {
54+
return cli.NewExitError(fmt.Sprintf("container id cannot be empty"), -1)
55+
}
56+
57+
c, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock"))
58+
if err != nil {
59+
return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1)
60+
}
61+
62+
plist, err := getProcessList(c, container)
63+
if err != nil {
64+
return cli.NewExitError(fmt.Sprintf("can't get process list, %v", err), -1)
65+
}
66+
67+
for _, p := range plist {
68+
if _, err := c.Signal(netcontext.Background(), &types.SignalRequest{
69+
Id: container,
70+
Pid: p,
71+
Signal: uint32(linuxsignal.SIGCONT),
72+
}); err != nil {
73+
return cli.NewExitError(fmt.Sprintf("resume signal failed, %v", err), -1)
74+
}
75+
}
76+
77+
return nil
78+
},
79+
}

ps.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"text/tabwriter"
9+
10+
"github.com/hyperhq/runv/containerd/api/grpc/types"
11+
"github.com/urfave/cli"
12+
netcontext "golang.org/x/net/context"
13+
)
14+
15+
var psCommand = cli.Command{
16+
Name: "ps",
17+
Usage: "ps displays the processes running inside a container",
18+
ArgsUsage: `<container-id> [ps options]`, Flags: []cli.Flag{
19+
cli.StringFlag{
20+
Name: "format, f",
21+
Value: "table",
22+
Usage: `select one of: ` + formatOptions,
23+
},
24+
},
25+
Action: func(context *cli.Context) error {
26+
container := context.Args().First()
27+
if container == "" {
28+
return cli.NewExitError("container id cannot be empty", -1)
29+
}
30+
c, err := getContainerApi(context, container)
31+
if err != nil {
32+
return cli.NewExitError(fmt.Sprintf("can't access container, %v", err), -1)
33+
}
34+
35+
switch context.String("format") {
36+
case "table":
37+
w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0)
38+
fmt.Fprint(w, "PROCESS\tCMD\n")
39+
// we are limited by the containerd interface for now
40+
for _, p := range c.Processes {
41+
fmt.Fprintf(w, "%s\t%s\n",
42+
p.Pid,
43+
p.Args)
44+
}
45+
if err := w.Flush(); err != nil {
46+
fatal(err)
47+
}
48+
case "json":
49+
pids := make([]string, 0)
50+
for _, p := range c.Processes {
51+
pids = append(pids, p.Pid)
52+
}
53+
54+
data, err := json.Marshal(pids)
55+
if err != nil {
56+
fatal(err)
57+
}
58+
os.Stdout.Write(data)
59+
return nil
60+
default:
61+
return cli.NewExitError(fmt.Sprintf("invalid format option"), -1)
62+
}
63+
64+
return nil
65+
},
66+
}
67+
68+
func getContainerApi(context *cli.Context, container string) (*types.Container, error) {
69+
api, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock"))
70+
if err != nil {
71+
return nil, fmt.Errorf("failed to get client: %v", err)
72+
}
73+
74+
s, err := api.State(netcontext.Background(), &types.StateRequest{Id: container})
75+
if err != nil {
76+
return nil, fmt.Errorf("get container state failed, %v", err)
77+
}
78+
79+
for _, c := range s.Containers {
80+
if c.Id == container {
81+
return c, nil
82+
}
83+
}
84+
85+
return nil, fmt.Errorf("container %s not found", container)
86+
}

supervisor/process.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ func (p *Process) signal(sig int) error {
9393
// TODO: change vm.KillContainer()
9494
return p.ownerCont.ownerPod.vm.KillContainer(p.ownerCont.Id, syscall.Signal(sig))
9595
} else {
96-
// TODO support it
97-
return fmt.Errorf("Kill to non-init process of container is unsupported")
96+
return p.ownerCont.ownerPod.vm.SignalProcess(p.ownerCont.Id, p.Id, syscall.Signal(sig))
9897
}
9998
}
10099

0 commit comments

Comments
 (0)