Merge pull request #2577 from samuelkarp/stop-signal
ctr: make kill optionally use stop-signal
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|    limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| package commands | ||||
| package containerd | ||||
| 
 | ||||
| import ( | ||||
| 	"syscall" | ||||
| @@ -16,7 +16,7 @@ | ||||
|    limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| package commands | ||||
| package containerd | ||||
| 
 | ||||
| import ( | ||||
| 	"syscall" | ||||
| @@ -14,7 +14,7 @@ | ||||
|    limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| package commands | ||||
| package containerd | ||||
| 
 | ||||
| import ( | ||||
| 	"syscall" | ||||
							
								
								
									
										105
									
								
								signals.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								signals.go
									
									
									
									
									
										Normal 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 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Phil Estes
					Phil Estes