Merge pull request #2577 from samuelkarp/stop-signal

ctr: make kill optionally use stop-signal
This commit is contained in:
Phil Estes 2018-09-28 12:05:56 -04:00 committed by GitHub
commit ac01f20a8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 144 additions and 31 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

@ -18,11 +18,8 @@ package commands
import (
gocontext "context"
"fmt"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"github.com/containerd/containerd"
@ -53,23 +50,3 @@ func StopCatch(sigc chan os.Signal) {
signal.Stop(sigc)
close(sigc)
}
// 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) {
s, err := strconv.Atoi(rawSignal)
if err == nil {
sig := syscall.Signal(s)
for _, msig := range signalMap {
if sig == msig {
return sig, nil
}
}
return -1, fmt.Errorf("unknown signal %q", rawSignal)
}
signal, ok := signalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")]
if !ok {
return -1, fmt.Errorf("unknown signal %q", rawSignal)
}
return signal, nil
}

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 := commands.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

@ -14,7 +14,7 @@
limitations under the License.
*/
package commands
package containerd
import (
"syscall"

View File

@ -16,7 +16,7 @@
limitations under the License.
*/
package commands
package containerd
import (
"syscall"

View File

@ -14,7 +14,7 @@
limitations under the License.
*/
package commands
package containerd
import (
"syscall"

105
signals.go Normal file
View File

@ -0,0 +1,105 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
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) {
s, err := strconv.Atoi(rawSignal)
if err == nil {
sig := syscall.Signal(s)
for _, msig := range signalMap {
if sig == msig {
return sig, nil
}
}
return -1, fmt.Errorf("unknown signal %q", rawSignal)
}
signal, ok := signalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")]
if !ok {
return -1, fmt.Errorf("unknown signal %q", rawSignal)
}
return signal, nil
}