This way, images creators can set the exit signal their programs use.
Signed-off-by: David Calavera <david.calavera@gmail.com>
| ... | ... |
@@ -17,6 +17,7 @@ const ( |
| 17 | 17 |
Expose = "expose" |
| 18 | 18 |
Volume = "volume" |
| 19 | 19 |
User = "user" |
| 20 |
+ StopSignal = "stopsignal" |
|
| 20 | 21 |
) |
| 21 | 22 |
|
| 22 | 23 |
// Commands is list of all Dockerfile commands |
| ... | ... |
@@ -35,4 +36,5 @@ var Commands = map[string]struct{}{
|
| 35 | 35 |
Expose: {},
|
| 36 | 36 |
Volume: {},
|
| 37 | 37 |
User: {},
|
| 38 |
+ StopSignal: {},
|
|
| 38 | 39 |
} |
| ... | ... |
@@ -20,6 +20,7 @@ import ( |
| 20 | 20 |
"github.com/Sirupsen/logrus" |
| 21 | 21 |
flag "github.com/docker/docker/pkg/mflag" |
| 22 | 22 |
"github.com/docker/docker/pkg/nat" |
| 23 |
+ "github.com/docker/docker/pkg/signal" |
|
| 23 | 24 |
"github.com/docker/docker/pkg/stringutils" |
| 24 | 25 |
"github.com/docker/docker/pkg/system" |
| 25 | 26 |
"github.com/docker/docker/runconfig" |
| ... | ... |
@@ -534,3 +535,21 @@ func volume(b *builder, args []string, attributes map[string]bool, original stri |
| 534 | 534 |
} |
| 535 | 535 |
return nil |
| 536 | 536 |
} |
| 537 |
+ |
|
| 538 |
+// STOPSIGNAL signal |
|
| 539 |
+// |
|
| 540 |
+// Set the signal that will be used to kill the container. |
|
| 541 |
+func stopSignal(b *builder, args []string, attributes map[string]bool, original string) error {
|
|
| 542 |
+ if len(args) != 1 {
|
|
| 543 |
+ return fmt.Errorf("STOPSIGNAL requires exactly one argument")
|
|
| 544 |
+ } |
|
| 545 |
+ |
|
| 546 |
+ sig := args[0] |
|
| 547 |
+ _, err := signal.ParseSignal(sig) |
|
| 548 |
+ if err != nil {
|
|
| 549 |
+ return err |
|
| 550 |
+ } |
|
| 551 |
+ |
|
| 552 |
+ b.Config.StopSignal = sig |
|
| 553 |
+ return b.commit("", b.Config.Cmd, fmt.Sprintf("STOPSIGNAL %v", args))
|
|
| 554 |
+} |
| ... | ... |
@@ -45,14 +45,15 @@ import ( |
| 45 | 45 |
|
| 46 | 46 |
// Environment variable interpolation will happen on these statements only. |
| 47 | 47 |
var replaceEnvAllowed = map[string]struct{}{
|
| 48 |
- command.Env: {},
|
|
| 49 |
- command.Label: {},
|
|
| 50 |
- command.Add: {},
|
|
| 51 |
- command.Copy: {},
|
|
| 52 |
- command.Workdir: {},
|
|
| 53 |
- command.Expose: {},
|
|
| 54 |
- command.Volume: {},
|
|
| 55 |
- command.User: {},
|
|
| 48 |
+ command.Env: {},
|
|
| 49 |
+ command.Label: {},
|
|
| 50 |
+ command.Add: {},
|
|
| 51 |
+ command.Copy: {},
|
|
| 52 |
+ command.Workdir: {},
|
|
| 53 |
+ command.Expose: {},
|
|
| 54 |
+ command.Volume: {},
|
|
| 55 |
+ command.User: {},
|
|
| 56 |
+ command.StopSignal: {},
|
|
| 56 | 57 |
} |
| 57 | 58 |
|
| 58 | 59 |
var evaluateTable map[string]func(*builder, []string, map[string]bool, string) error |
| ... | ... |
@@ -73,6 +74,7 @@ func init() {
|
| 73 | 73 |
command.Expose: expose, |
| 74 | 74 |
command.Volume: volume, |
| 75 | 75 |
command.User: user, |
| 76 |
+ command.StopSignal: stopSignal, |
|
| 76 | 77 |
} |
| 77 | 78 |
} |
| 78 | 79 |
|
| ... | ... |
@@ -25,7 +25,7 @@ |
| 25 | 25 |
</dict> |
| 26 | 26 |
</dict> |
| 27 | 27 |
<key>match</key> |
| 28 |
- <string>^\s*(?:(ONBUILD)\s+)?(FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|VOLUME|USER|WORKDIR|COPY|LABEL)\s</string> |
|
| 28 |
+ <string>^\s*(?:(ONBUILD)\s+)?(FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|VOLUME|USER|WORKDIR|COPY|LABEL|STOPSIGNAL)\s</string> |
|
| 29 | 29 |
</dict> |
| 30 | 30 |
<dict> |
| 31 | 31 |
<key>captures</key> |
| ... | ... |
@@ -11,7 +11,7 @@ let b:current_syntax = "dockerfile" |
| 11 | 11 |
|
| 12 | 12 |
syntax case ignore |
| 13 | 13 |
|
| 14 |
-syntax match dockerfileKeyword /\v^\s*(ONBUILD\s+)?(ADD|CMD|ENTRYPOINT|ENV|EXPOSE|FROM|MAINTAINER|RUN|USER|LABEL|VOLUME|WORKDIR|COPY)\s/ |
|
| 14 |
+syntax match dockerfileKeyword /\v^\s*(ONBUILD\s+)?(ADD|CMD|ENTRYPOINT|ENV|EXPOSE|FROM|MAINTAINER|RUN|USER|LABEL|VOLUME|WORKDIR|COPY|STOPSIGNAL)\s/ |
|
| 15 | 15 |
highlight link dockerfileKeyword Keyword |
| 16 | 16 |
|
| 17 | 17 |
syntax region dockerfileString start=/\v"/ skip=/\v\\./ end=/\v"/ |
| ... | ... |
@@ -1076,6 +1076,13 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig, |
| 1076 | 1076 |
return nil, fmt.Errorf("The working directory '%s' is invalid. It needs to be an absolute path.", config.WorkingDir)
|
| 1077 | 1077 |
} |
| 1078 | 1078 |
} |
| 1079 |
+ |
|
| 1080 |
+ if len(config.StopSignal) > 0 {
|
|
| 1081 |
+ _, err := signal.ParseSignal(config.StopSignal) |
|
| 1082 |
+ if err != nil {
|
|
| 1083 |
+ return nil, err |
|
| 1084 |
+ } |
|
| 1085 |
+ } |
|
| 1079 | 1086 |
} |
| 1080 | 1087 |
|
| 1081 | 1088 |
if hostConfig == nil {
|
| ... | ... |
@@ -1095,11 +1102,6 @@ func (daemon *Daemon) verifyContainerSettings(hostConfig *runconfig.HostConfig, |
| 1095 | 1095 |
} |
| 1096 | 1096 |
} |
| 1097 | 1097 |
|
| 1098 |
- _, err := signal.ParseSignal(config.StopSignal) |
|
| 1099 |
- if err != nil {
|
|
| 1100 |
- return nil, err |
|
| 1101 |
- } |
|
| 1102 |
- |
|
| 1103 | 1098 |
// Now do platform-specific verification |
| 1104 | 1099 |
return verifyPlatformContainerSettings(daemon, hostConfig, config) |
| 1105 | 1100 |
} |
| ... | ... |
@@ -166,6 +166,7 @@ Create a container |
| 166 | 166 |
"ExposedPorts": {
|
| 167 | 167 |
"22/tcp": {}
|
| 168 | 168 |
}, |
| 169 |
+ "StopSignal": "SIGTERM", |
|
| 169 | 170 |
"HostConfig": {
|
| 170 | 171 |
"Binds": ["/tmp:/tmp"], |
| 171 | 172 |
"Links": ["redis3:redis"], |
| ... | ... |
@@ -250,6 +251,7 @@ Json Parameters: |
| 250 | 250 |
container |
| 251 | 251 |
- **ExposedPorts** - An object mapping ports to an empty object in the form of: |
| 252 | 252 |
`"ExposedPorts": { "<port>/<tcp|udp>: {}" }`
|
| 253 |
+- **StopSignal** - Signal to stop a container as a string or unsigned integer. `SIGTERM` by default. |
|
| 253 | 254 |
- **HostConfig** |
| 254 | 255 |
- **Binds** – A list of volume bindings for this container. Each volume binding is a string in one of these forms: |
| 255 | 256 |
+ `container_path` to create a new volume for the container |
| ... | ... |
@@ -367,7 +369,8 @@ Return low-level information on the container `id` |
| 367 | 367 |
"Tty": false, |
| 368 | 368 |
"User": "", |
| 369 | 369 |
"Volumes": null, |
| 370 |
- "WorkingDir": "" |
|
| 370 |
+ "WorkingDir": "", |
|
| 371 |
+ "StopSignal": "SIGTERM" |
|
| 371 | 372 |
}, |
| 372 | 373 |
"Created": "2015-01-06T15:47:31.485331387Z", |
| 373 | 374 |
"Driver": "devicemapper", |
| ... | ... |
@@ -158,6 +158,7 @@ the `Dockerfile`: |
| 158 | 158 |
* `USER` |
| 159 | 159 |
* `WORKDIR` |
| 160 | 160 |
* `VOLUME` |
| 161 |
+* `STOPSIGNAL` |
|
| 161 | 162 |
|
| 162 | 163 |
as well as: |
| 163 | 164 |
|
| ... | ... |
@@ -1012,6 +1013,14 @@ For example you might add something like this: |
| 1012 | 1012 |
|
| 1013 | 1013 |
> **Warning**: The `ONBUILD` instruction may not trigger `FROM` or `MAINTAINER` instructions. |
| 1014 | 1014 |
|
| 1015 |
+## STOPSIGNAL |
|
| 1016 |
+ |
|
| 1017 |
+ STOPSIGNAL signal |
|
| 1018 |
+ |
|
| 1019 |
+The `STOPSIGNAL` instruction sets the system call signal that will be sent to the container to exit. |
|
| 1020 |
+This signal can be a valid unsigned number that matches a position in the kernel's syscall table, for instance 9, |
|
| 1021 |
+or a signal name in the format SIGNAME, for instance SIGKILL. |
|
| 1022 |
+ |
|
| 1015 | 1023 |
## Dockerfile examples |
| 1016 | 1024 |
|
| 1017 | 1025 |
# Nginx |
| ... | ... |
@@ -61,6 +61,7 @@ Creates a new container. |
| 61 | 61 |
--read-only=false Mount the container's root filesystem as read only |
| 62 | 62 |
--restart="no" Restart policy (no, on-failure[:max-retry], always, unless-stopped) |
| 63 | 63 |
--security-opt=[] Security options |
| 64 |
+ --stop-signal="SIGTERM" Signal to stop a container |
|
| 64 | 65 |
-t, --tty=false Allocate a pseudo-TTY |
| 65 | 66 |
--disable-content-trust=true Skip image verification |
| 66 | 67 |
-u, --user="" Username or UID |
| ... | ... |
@@ -62,6 +62,7 @@ weight=1 |
| 62 | 62 |
--restart="no" Restart policy (no, on-failure[:max-retry], always, unless-stopped) |
| 63 | 63 |
--rm=false Automatically remove the container when it exits |
| 64 | 64 |
--security-opt=[] Security Options |
| 65 |
+ --stop-signal="SIGTERM" Signal to stop a container |
|
| 65 | 66 |
--sig-proxy=true Proxy received signals to the process |
| 66 | 67 |
-t, --tty=false Allocate a pseudo-TTY |
| 67 | 68 |
-u, --user="" Username or UID (format: <name|uid>[:<group|gid>]) |
| ... | ... |
@@ -531,3 +532,9 @@ containers with `daemon` user: |
| 531 | 531 |
The 4th container fails and reports "[8] System error: resource temporarily unavailable" error. |
| 532 | 532 |
This fails because the caller set `nproc=3` resulting in the first three containers using up |
| 533 | 533 |
the three processes quota set for the `daemon` user. |
| 534 |
+ |
|
| 535 |
+### Stopping a container with a specific signal |
|
| 536 |
+ |
|
| 537 |
+The `--stop-signal` flag sets the system call signal that will be sent to the container to exit. |
|
| 538 |
+This signal can be a valid unsigned number that matches a position in the kernel's syscall table, for instance 9, |
|
| 539 |
+or a signal name in the format SIGNAME, for instance SIGKILL. |
| ... | ... |
@@ -5660,3 +5660,18 @@ func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) {
|
| 5660 | 5660 |
_, err = buildImageFromContext(name, ctx, true) |
| 5661 | 5661 |
c.Assert(err, check.IsNil) |
| 5662 | 5662 |
} |
| 5663 |
+ |
|
| 5664 |
+func (s *DockerSuite) TestBuildStopSignal(c *check.C) {
|
|
| 5665 |
+ name := "test_build_stop_signal" |
|
| 5666 |
+ _, err := buildImage(name, |
|
| 5667 |
+ `FROM busybox |
|
| 5668 |
+ STOPSIGNAL SIGKILL`, |
|
| 5669 |
+ true) |
|
| 5670 |
+ c.Assert(err, check.IsNil) |
|
| 5671 |
+ res, err := inspectFieldJSON(name, "Config.StopSignal") |
|
| 5672 |
+ c.Assert(err, check.IsNil) |
|
| 5673 |
+ |
|
| 5674 |
+ if res != `"SIGKILL"` {
|
|
| 5675 |
+ c.Fatalf("Signal %s, expected SIGKILL", res)
|
|
| 5676 |
+ } |
|
| 5677 |
+} |
| ... | ... |
@@ -458,3 +458,15 @@ func (s *DockerTrustSuite) TestTrustedCreateFromBadTrustServer(c *check.C) {
|
| 458 | 458 |
c.Fatalf("Missing expected output on trusted push:\n%s", out)
|
| 459 | 459 |
} |
| 460 | 460 |
} |
| 461 |
+ |
|
| 462 |
+func (s *DockerSuite) TestCreateStopSignal(c *check.C) {
|
|
| 463 |
+ name := "test_create_stop_signal" |
|
| 464 |
+ dockerCmd(c, "create", "--name", name, "--stop-signal", "9", "busybox") |
|
| 465 |
+ |
|
| 466 |
+ res, err := inspectFieldJSON(name, "Config.StopSignal") |
|
| 467 |
+ c.Assert(err, check.IsNil) |
|
| 468 |
+ |
|
| 469 |
+ if res != `"9"` {
|
|
| 470 |
+ c.Fatalf("Expected 9, got %s", res)
|
|
| 471 |
+ } |
|
| 472 |
+} |