ctr: make kill use stop-signal by default

The OCI image specification includes a `StopSignal` field in the image
configuration, denoting the system call signal to be sent to the
container to exit.  This commit adds a new `WithImageStopSignal` container
option that can be used for storing the `StopSignal` field as a label on
the container.  This commit also adjusts `ctr run` to call
`WithImageStopSignal` and `ctr tasks kill` to send the signal stored in
that label by default.

Signed-off-by: Samuel Karp <skarp@amazon.com>
This commit is contained in:
Samuel Karp 2018-09-27 14:56:34 -07:00
parent b392a3a8e4
commit 607888ce29
4 changed files with 97 additions and 5 deletions

View File

@ -27,7 +27,7 @@ import (
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/contrib/nvidia"
"github.com/containerd/containerd/oci"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
@ -58,6 +58,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
spec containerd.NewContainerOpts
)
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
if config {
opts = append(opts, oci.WithSpecFromFile(context.String("config")))
} else {
@ -98,7 +99,8 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
// Even when "readonly" is set, we don't use KindView snapshot here. (#1495)
// We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only,
// after creating some mount points on demand.
containerd.WithNewSnapshot(id, image))
containerd.WithNewSnapshot(id, image),
containerd.WithImageStopSignal(image, "SIGTERM"))
}
if context.Bool("readonly") {
opts = append(opts, oci.WithRootFSReadonly())
@ -141,7 +143,6 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
}
}
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
var s specs.Spec

View File

@ -23,6 +23,8 @@ import (
"github.com/urfave/cli"
)
const defaultSignal = "SIGTERM"
var killCommand = cli.Command{
Name: "kill",
Usage: "signal a container (default: SIGTERM)",
@ -30,7 +32,7 @@ var killCommand = cli.Command{
Flags: []cli.Flag{
cli.StringFlag{
Name: "signal, s",
Value: "SIGTERM",
Value: "",
Usage: "signal to send to the container",
},
cli.StringFlag{
@ -47,7 +49,7 @@ var killCommand = cli.Command{
if id == "" {
return errors.New("container id must be provided")
}
signal, err := containerd.ParseSignal(context.String("signal"))
signal, err := containerd.ParseSignal(defaultSignal)
if err != nil {
return err
}
@ -74,6 +76,17 @@ var killCommand = cli.Command{
if err != nil {
return err
}
if context.String("signal") != "" {
signal, err = containerd.ParseSignal(context.String("signal"))
if err != nil {
return err
}
} else {
signal, err = containerd.GetStopSignal(ctx, container, signal)
if err != nil {
return err
}
}
task, err := container.Task(ctx, nil)
if err != nil {
return err

View File

@ -76,6 +76,23 @@ func WithContainerLabels(labels map[string]string) NewContainerOpts {
}
}
// WithImageStopSignal sets a well-known containerd label (StopSignalLabel)
// on the container for storing the stop signal specified in the OCI image
// config
func WithImageStopSignal(image Image, defaultSignal string) NewContainerOpts {
return func(ctx context.Context, _ *Client, c *containers.Container) error {
if c.Labels == nil {
c.Labels = make(map[string]string)
}
stopSignal, err := GetOCIStopSignal(ctx, image, defaultSignal)
if err != nil {
return err
}
c.Labels[StopSignalLabel] = stopSignal
return nil
}
}
// WithSnapshotter sets the provided snapshotter for use by the container
//
// This option must appear before other snapshotter options to have an effect.

View File

@ -17,12 +17,73 @@
package containerd
import (
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"syscall"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/opencontainers/image-spec/specs-go/v1"
)
// StopSignalLabel is a well-known containerd label for storing the stop
// signal specified in the OCI image config
const StopSignalLabel = "io.containerd.image.config.stop-signal"
// GetStopSignal retrieves the container stop signal, specified by the
// well-known containerd label (StopSignalLabel)
func GetStopSignal(ctx context.Context, container Container, defaultSignal syscall.Signal) (syscall.Signal, error) {
labels, err := container.Labels(ctx)
if err != nil {
return -1, err
}
if stopSignal, ok := labels[StopSignalLabel]; ok {
return ParseSignal(stopSignal)
}
return defaultSignal, nil
}
// GetOCIStopSignal retrieves the stop signal specified in the OCI image config
func GetOCIStopSignal(ctx context.Context, image Image, defaultSignal string) (string, error) {
_, err := ParseSignal(defaultSignal)
if err != nil {
return "", err
}
ic, err := image.Config(ctx)
if err != nil {
return "", err
}
var (
ociimage v1.Image
config v1.ImageConfig
)
switch ic.MediaType {
case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config:
p, err := content.ReadBlob(ctx, image.ContentStore(), ic)
if err != nil {
return "", err
}
if err := json.Unmarshal(p, &ociimage); err != nil {
return "", err
}
config = ociimage.Config
default:
return "", fmt.Errorf("unknown image config media type %s", ic.MediaType)
}
if config.StopSignal == "" {
return defaultSignal, nil
}
return config.StopSignal, nil
}
// ParseSignal parses a given string into a syscall.Signal
// it checks that the signal exists in the platform-appropriate signalMap
func ParseSignal(rawSignal string) (syscall.Signal, error) {