ソースを参照

Add `STOPSIGNAL` instruction to dockerfiles.

This way, images creators can set the exit signal their programs use.

Signed-off-by: David Calavera <david.calavera@gmail.com>
David Calavera 10 年 前
コミット
3781cde61f

+ 2 - 0
builder/command/command.go

@@ -17,6 +17,7 @@ const (
 	Expose     = "expose"
 	Volume     = "volume"
 	User       = "user"
+	StopSignal = "stopsignal"
 )
 
 // Commands is list of all Dockerfile commands
@@ -35,4 +36,5 @@ var Commands = map[string]struct{}{
 	Expose:     {},
 	Volume:     {},
 	User:       {},
+	StopSignal: {},
 }

+ 19 - 0
builder/dispatchers.go

@@ -20,6 +20,7 @@ import (
 	"github.com/Sirupsen/logrus"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/nat"
+	"github.com/docker/docker/pkg/signal"
 	"github.com/docker/docker/pkg/stringutils"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/runconfig"
@@ -534,3 +535,21 @@ func volume(b *builder, args []string, attributes map[string]bool, original stri
 	}
 	return nil
 }
+
+// STOPSIGNAL signal
+//
+// Set the signal that will be used to kill the container.
+func stopSignal(b *builder, args []string, attributes map[string]bool, original string) error {
+	if len(args) != 1 {
+		return fmt.Errorf("STOPSIGNAL requires exactly one argument")
+	}
+
+	sig := args[0]
+	_, err := signal.ParseSignal(sig)
+	if err != nil {
+		return err
+	}
+
+	b.Config.StopSignal = sig
+	return b.commit("", b.Config.Cmd, fmt.Sprintf("STOPSIGNAL %v", args))
+}

+ 10 - 8
builder/evaluator.go

@@ -45,14 +45,15 @@ import (
 
 // Environment variable interpolation will happen on these statements only.
 var replaceEnvAllowed = map[string]struct{}{
-	command.Env:     {},
-	command.Label:   {},
-	command.Add:     {},
-	command.Copy:    {},
-	command.Workdir: {},
-	command.Expose:  {},
-	command.Volume:  {},
-	command.User:    {},
+	command.Env:        {},
+	command.Label:      {},
+	command.Add:        {},
+	command.Copy:       {},
+	command.Workdir:    {},
+	command.Expose:     {},
+	command.Volume:     {},
+	command.User:       {},
+	command.StopSignal: {},
 }
 
 var evaluateTable map[string]func(*builder, []string, map[string]bool, string) error
@@ -73,6 +74,7 @@ func init() {
 		command.Expose:     expose,
 		command.Volume:     volume,
 		command.User:       user,
+		command.StopSignal: stopSignal,
 	}
 }
 

+ 1 - 0
builder/parser/parser.go

@@ -61,6 +61,7 @@ func init() {
 		command.Entrypoint: parseMaybeJSON,
 		command.Expose:     parseStringsWhitespaceDelimited,
 		command.Volume:     parseMaybeJSONToList,
+		command.StopSignal: parseString,
 	}
 }
 

+ 1 - 0
contrib/syntax/kate/Dockerfile.xml

@@ -23,6 +23,7 @@
       <item> WORKDIR </item>
       <item> USER </item>
       <item> LABEL </item>
+      <item> STOPSIGNAL </item>
     </list>
 
     <contexts>

+ 1 - 1
contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage

@@ -25,7 +25,7 @@
 				</dict>
 			</dict>
 			<key>match</key>
-			<string>^\s*(?:(ONBUILD)\s+)?(FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|VOLUME|USER|WORKDIR|COPY|LABEL)\s</string>
+			<string>^\s*(?:(ONBUILD)\s+)?(FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|VOLUME|USER|WORKDIR|COPY|LABEL|STOPSIGNAL)\s</string>
 		</dict>
 		<dict>
 			<key>captures</key>

+ 1 - 1
contrib/syntax/vim/syntax/dockerfile.vim

@@ -11,7 +11,7 @@ let b:current_syntax = "dockerfile"
 
 syntax case ignore
 
-syntax match dockerfileKeyword /\v^\s*(ONBUILD\s+)?(ADD|CMD|ENTRYPOINT|ENV|EXPOSE|FROM|MAINTAINER|RUN|USER|LABEL|VOLUME|WORKDIR|COPY)\s/
+syntax match dockerfileKeyword /\v^\s*(ONBUILD\s+)?(ADD|CMD|ENTRYPOINT|ENV|EXPOSE|FROM|MAINTAINER|RUN|USER|LABEL|VOLUME|WORKDIR|COPY|STOPSIGNAL)\s/
 highlight link dockerfileKeyword Keyword
 
 syntax region dockerfileString start=/\v"/ skip=/\v\\./ end=/\v"/

+ 1 - 1
daemon/container.go

@@ -524,7 +524,7 @@ func (container *Container) Stop(seconds int) error {
 		}
 	}
 
-	container.LogEvent("stop")
+	container.logEvent("stop")
 	return nil
 }
 

+ 6 - 1
daemon/container_unit_test.go

@@ -1,6 +1,11 @@
 package daemon
 
-import "testing"
+import (
+	"testing"
+
+	"github.com/docker/docker/pkg/signal"
+	"github.com/docker/docker/runconfig"
+)
 
 func TestGetFullName(t *testing.T) {
 	name, err := GetFullContainerName("testing")

+ 7 - 5
daemon/daemon.go

@@ -1076,6 +1076,13 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig,
 				return nil, fmt.Errorf("The working directory '%s' is invalid. It needs to be an absolute path.", config.WorkingDir)
 			}
 		}
+
+		if len(config.StopSignal) > 0 {
+			_, err := signal.ParseSignal(config.StopSignal)
+			if err != nil {
+				return nil, err
+			}
+		}
 	}
 
 	if hostConfig == nil {
@@ -1095,11 +1102,6 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig,
 		}
 	}
 
-	_, err := signal.ParseSignal(config.StopSignal)
-	if err != nil {
-		return nil, err
-	}
-
 	// Now do platform-specific verification
 	return verifyPlatformContainerSettings(daemon, hostConfig, config)
 }

+ 4 - 1
docs/reference/api/docker_remote_api_v1.21.md

@@ -166,6 +166,7 @@ Create a container
            "ExposedPorts": {
                    "22/tcp": {}
            },
+           "StopSignal": "SIGTERM",
            "HostConfig": {
              "Binds": ["/tmp:/tmp"],
              "Links": ["redis3:redis"],
@@ -250,6 +251,7 @@ Json Parameters:
       container
 -   **ExposedPorts** - An object mapping ports to an empty object in the form of:
       `"ExposedPorts": { "<port>/<tcp|udp>: {}" }`
+-   **StopSignal** - Signal to stop a container as a string or unsigned integer. `SIGTERM` by default.
 -   **HostConfig**
     -   **Binds** – A list of volume bindings for this container. Each volume binding is a string in one of these forms:
            + `container_path` to create a new volume for the container
@@ -367,7 +369,8 @@ Return low-level information on the container `id`
 			"Tty": false,
 			"User": "",
 			"Volumes": null,
-			"WorkingDir": ""
+			"WorkingDir": "",
+			"StopSignal": "SIGTERM"
 		},
 		"Created": "2015-01-06T15:47:31.485331387Z",
 		"Driver": "devicemapper",

+ 9 - 0
docs/reference/builder.md

@@ -158,6 +158,7 @@ the `Dockerfile`:
 * `USER`
 * `WORKDIR`
 * `VOLUME`
+* `STOPSIGNAL`
 
 as well as:
 
@@ -1012,6 +1013,14 @@ For example you might add something like this:
 
 > **Warning**: The `ONBUILD` instruction may not trigger `FROM` or `MAINTAINER` instructions.
 
+## STOPSIGNAL
+
+	STOPSIGNAL signal
+
+The `STOPSIGNAL` instruction sets the system call signal that will be sent to the container to exit.
+This signal can be a valid unsigned number that matches a position in the kernel's syscall table, for instance 9,
+or a signal name in the format SIGNAME, for instance SIGKILL.
+
 ## Dockerfile examples
 
     # Nginx

+ 1 - 0
docs/reference/commandline/create.md

@@ -61,6 +61,7 @@ Creates a new container.
       --read-only=false             Mount the container's root filesystem as read only
       --restart="no"                Restart policy (no, on-failure[:max-retry], always, unless-stopped)
       --security-opt=[]             Security options
+      --stop-signal="SIGTERM"       Signal to stop a container
       -t, --tty=false               Allocate a pseudo-TTY
       --disable-content-trust=true  Skip image verification
       -u, --user=""                 Username or UID

+ 7 - 0
docs/reference/commandline/run.md

@@ -62,6 +62,7 @@ weight=1
       --restart="no"                Restart policy (no, on-failure[:max-retry], always, unless-stopped)
       --rm=false                    Automatically remove the container when it exits
       --security-opt=[]             Security Options
+      --stop-signal="SIGTERM"       Signal to stop a container
       --sig-proxy=true              Proxy received signals to the process
       -t, --tty=false               Allocate a pseudo-TTY
       -u, --user=""                 Username or UID (format: <name|uid>[:<group|gid>])
@@ -531,3 +532,9 @@ containers with `daemon` user:
 The 4th container fails and reports "[8] System error: resource temporarily unavailable" error. 
 This fails because the caller set `nproc=3` resulting in the first three containers using up 
 the three processes quota set for the `daemon` user.
+
+### Stopping a container with a specific signal
+
+The `--stop-signal` flag sets the system call signal that will be sent to the container to exit.
+This signal can be a valid unsigned number that matches a position in the kernel's syscall table, for instance 9,
+or a signal name in the format SIGNAME, for instance SIGKILL.

+ 15 - 0
integration-cli/docker_cli_build_test.go

@@ -5660,3 +5660,18 @@ func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) {
 	_, err = buildImageFromContext(name, ctx, true)
 	c.Assert(err, check.IsNil)
 }
+
+func (s *DockerSuite) TestBuildStopSignal(c *check.C) {
+	name := "test_build_stop_signal"
+	_, err := buildImage(name,
+		`FROM busybox
+		 STOPSIGNAL SIGKILL`,
+		true)
+	c.Assert(err, check.IsNil)
+	res, err := inspectFieldJSON(name, "Config.StopSignal")
+	c.Assert(err, check.IsNil)
+
+	if res != `"SIGKILL"` {
+		c.Fatalf("Signal %s, expected SIGKILL", res)
+	}
+}

+ 12 - 0
integration-cli/docker_cli_create_test.go

@@ -458,3 +458,15 @@ func (s *DockerTrustSuite) TestTrustedCreateFromBadTrustServer(c *check.C) {
 		c.Fatalf("Missing expected output on trusted push:\n%s", out)
 	}
 }
+
+func (s *DockerSuite) TestCreateStopSignal(c *check.C) {
+	name := "test_create_stop_signal"
+	dockerCmd(c, "create", "--name", name, "--stop-signal", "9", "busybox")
+
+	res, err := inspectFieldJSON(name, "Config.StopSignal")
+	c.Assert(err, check.IsNil)
+
+	if res != `"9"` {
+		c.Fatalf("Expected 9, got %s", res)
+	}
+}

+ 1 - 1
man/docker-inspect.1.md

@@ -181,7 +181,7 @@ To get information on a container use its ID or instance name:
         "MemorySwap": 0,
         "CpuShares": 0,
         "Cpuset": "",
-        "StopSignal": 15,
+        "StopSignal": "SIGTERM"
     }
     }
     ]