Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 45 additions & 6 deletions kill.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ For example, if the container id is "ubuntu01" the following will send a "KILL"
signal to the init process of the "ubuntu01" container:

# runv kill ubuntu01 KILL`,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "all, a",
Usage: "send the signal to all processes in the container",
},
},
Action: func(context *cli.Context) error {
container := context.Args().First()
if container == "" {
Expand All @@ -88,17 +94,50 @@ signal to the init process of the "ubuntu01" container:
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1)
}
if _, err = c.Signal(netcontext.Background(), &types.SignalRequest{
Id: container,
Pid: "init",
Signal: uint32(signal),
}); err != nil {
return cli.NewExitError(fmt.Sprintf("kill signal failed, %v", err), -1)

plist := make([]string, 0)

if context.Bool("all") {
if plist, err = getProcessList(c, container); err != nil {
return cli.NewExitError(fmt.Sprintf("can't get process list, %v", err), -1)
}
} else {
plist = append(plist, "init")
}

for _, p := range plist {
if _, err = c.Signal(netcontext.Background(), &types.SignalRequest{
Id: container,
Pid: p,
Signal: uint32(signal),
}); err != nil {
return cli.NewExitError(fmt.Sprintf("kill signal failed, %v", err), -1)
}
}
return nil
},
}

func getProcessList(c types.APIClient, container string) ([]string, error) {
s, err := c.State(netcontext.Background(), &types.StateRequest{Id: container})
if err != nil {
return nil, fmt.Errorf("get container state failed, %v", err)
}

for _, cc := range s.Containers {
if cc.Id == container {
plist := make([]string, 0)
for _, p := range cc.Processes {
plist = append(plist, p.Pid)
}

return plist, nil
}
}

return nil, fmt.Errorf("container %s not found", container)
}

func parseSignal(rawSignal string) (syscall.Signal, error) {
s, err := strconv.Atoi(rawSignal)
if err == nil {
Expand Down
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,15 @@ func main() {
execCommand,
killCommand,
listCommand,
psCommand,
runCommand,
specCommand,
startCommand,
stateCommand,
manageCommand,
shimCommand,
pauseCommand,
resumeCommand,
containerd.ContainerdCommand,
}
if err := app.Run(os.Args); err != nil {
Expand Down
79 changes: 79 additions & 0 deletions pause.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package main

import (
"fmt"
"path/filepath"

"github.com/hyperhq/runv/containerd/api/grpc/types"
"github.com/hyperhq/runv/lib/linuxsignal"
"github.com/urfave/cli"
netcontext "golang.org/x/net/context"
)

var pauseCommand = cli.Command{
Name: "pause",
Usage: "suspend all processes in the container",
ArgsUsage: `<container-id>`,
Action: func(context *cli.Context) error {
container := context.Args().First()
if container == "" {
return cli.NewExitError(fmt.Sprintf("container id cannot be empty"), -1)
}

c, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock"))
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1)
}

plist, err := getProcessList(c, container)
if err != nil {
return cli.NewExitError(fmt.Sprintf("can't get process list, %v", err), -1)
}

for _, p := range plist {
if _, err := c.Signal(netcontext.Background(), &types.SignalRequest{
Id: container,
Pid: p,
Signal: uint32(linuxsignal.SIGSTOP),
}); err != nil {
return cli.NewExitError(fmt.Sprintf("suspend signal failed, %v", err), -1)
}
}

return nil
},
}

var resumeCommand = cli.Command{
Name: "resume",
Usage: "resume all processes in the container",
ArgsUsage: `<container-id>`,
Action: func(context *cli.Context) error {
container := context.Args().First()
if container == "" {
return cli.NewExitError(fmt.Sprintf("container id cannot be empty"), -1)
}

c, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock"))
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1)
}

plist, err := getProcessList(c, container)
if err != nil {
return cli.NewExitError(fmt.Sprintf("can't get process list, %v", err), -1)
}

for _, p := range plist {
if _, err := c.Signal(netcontext.Background(), &types.SignalRequest{
Id: container,
Pid: p,
Signal: uint32(linuxsignal.SIGCONT),
}); err != nil {
return cli.NewExitError(fmt.Sprintf("resume signal failed, %v", err), -1)
}
}

return nil
},
}
86 changes: 86 additions & 0 deletions ps.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package main

import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"text/tabwriter"

"github.com/hyperhq/runv/containerd/api/grpc/types"
"github.com/urfave/cli"
netcontext "golang.org/x/net/context"
)

var psCommand = cli.Command{
Name: "ps",
Usage: "ps displays the processes running inside a container",
ArgsUsage: `<container-id> [ps options]`, Flags: []cli.Flag{
cli.StringFlag{
Name: "format, f",
Value: "table",
Usage: `select one of: ` + formatOptions,
},
},
Action: func(context *cli.Context) error {
container := context.Args().First()
if container == "" {
return cli.NewExitError("container id cannot be empty", -1)
}
c, err := getContainerApi(context, container)
if err != nil {
return cli.NewExitError(fmt.Sprintf("can't access container, %v", err), -1)
}

switch context.String("format") {
case "table":
w := tabwriter.NewWriter(os.Stdout, 12, 1, 3, ' ', 0)
fmt.Fprint(w, "PROCESS\tCMD\n")
// we are limited by the containerd interface for now
for _, p := range c.Processes {
fmt.Fprintf(w, "%s\t%s\n",
p.Pid,
p.Args)
}
if err := w.Flush(); err != nil {
fatal(err)
}
case "json":
pids := make([]string, 0)
for _, p := range c.Processes {
pids = append(pids, p.Pid)
}

data, err := json.Marshal(pids)
if err != nil {
fatal(err)
}
os.Stdout.Write(data)
Copy link
Contributor

@WeiZhang555 WeiZhang555 Jun 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we print more information such as command args?
Never mind, this format is right.

But there's another problem, the pids should be '[]int' not '[]string', and it must be real pid of int type, now I can see pid "init" which makes docker top not work with runv

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use p.SystemPid instead of p.Pid, it works fine according to my test

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SystemPid reports the PID of the qemu process on the host for the container's main process, and AFAIU is invalid for the rest. See my original comment on this topic, in the first comment of this thread

Runv currently implements the OCI interface by wrapping runv-containerd calls, so we are limited by the containerd interface as supported by runv at the moment.
This introduces some limitations on these implementations, which to resolve would require the refactoring or runv, which IMHO should be discussed.

First, the processes runv is aware of from each container is limited to only the processes started directly from runv (via run and exec). Children are not being tracked. An extension in hyperstart would be needed to overcome this limitation (to always fetch the process list dynamically).

Additionally, runv-containerd reports only limited information about the processes running inside the container. We can not show the PID for example. I'm not sure whether extending hyperstart to provide this info is enough, as we need to consider how it will affect runv-containerd. Even then, we will be limited by the current fields in the containerd API. In any case runv-containerd and the runv OCI interface need to be considered at the same time to improve upon this point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tvelocity SystemPid reports PID of "nslistener" but not the qemu process, this is what we want. Because we need to make the nslistener a pseudo-container for network management. In more detail, we report "nslistener" pid as container init pid so that docker will treat "nslistener" as it's container, when CNI network plugins try to insert a network interface into the "nslistener" namespace, "nslistener" will know it and copy the network configuration into VM. This is how the network part works in runv.

PIDs in guestOS of VM is unmeaning to docker or other processes in host.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will merge it although the problems your reported are not addressed... You can fix it later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WeiZhang555 ok, thanks for the clarification

return nil
default:
return cli.NewExitError(fmt.Sprintf("invalid format option"), -1)
}

return nil
},
}

func getContainerApi(context *cli.Context, container string) (*types.Container, error) {
api, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock"))
if err != nil {
return nil, fmt.Errorf("failed to get client: %v", err)
}

s, err := api.State(netcontext.Background(), &types.StateRequest{Id: container})
if err != nil {
return nil, fmt.Errorf("get container state failed, %v", err)
}

for _, c := range s.Containers {
if c.Id == container {
return c, nil
}
}

return nil, fmt.Errorf("container %s not found", container)
}
3 changes: 1 addition & 2 deletions supervisor/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,7 @@ func (p *Process) signal(sig int) error {
// TODO: change vm.KillContainer()
return p.ownerCont.ownerPod.vm.KillContainer(p.ownerCont.Id, syscall.Signal(sig))
} else {
// TODO support it
return fmt.Errorf("Kill to non-init process of container is unsupported")
return p.ownerCont.ownerPod.vm.SignalProcess(p.ownerCont.Id, p.Id, syscall.Signal(sig))
}
}

Expand Down