mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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>
This commit is contained in:
parent
0e50d946a2
commit
3781cde61f
17 changed files with 98 additions and 19 deletions
|
@ -17,6 +17,7 @@ const (
|
||||||
Expose = "expose"
|
Expose = "expose"
|
||||||
Volume = "volume"
|
Volume = "volume"
|
||||||
User = "user"
|
User = "user"
|
||||||
|
StopSignal = "stopsignal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Commands is list of all Dockerfile commands
|
// Commands is list of all Dockerfile commands
|
||||||
|
@ -35,4 +36,5 @@ var Commands = map[string]struct{}{
|
||||||
Expose: {},
|
Expose: {},
|
||||||
Volume: {},
|
Volume: {},
|
||||||
User: {},
|
User: {},
|
||||||
|
StopSignal: {},
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
flag "github.com/docker/docker/pkg/mflag"
|
||||||
"github.com/docker/docker/pkg/nat"
|
"github.com/docker/docker/pkg/nat"
|
||||||
|
"github.com/docker/docker/pkg/signal"
|
||||||
"github.com/docker/docker/pkg/stringutils"
|
"github.com/docker/docker/pkg/stringutils"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
|
@ -534,3 +535,21 @@ func volume(b *builder, args []string, attributes map[string]bool, original stri
|
||||||
}
|
}
|
||||||
return nil
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ var replaceEnvAllowed = map[string]struct{}{
|
||||||
command.Expose: {},
|
command.Expose: {},
|
||||||
command.Volume: {},
|
command.Volume: {},
|
||||||
command.User: {},
|
command.User: {},
|
||||||
|
command.StopSignal: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
var evaluateTable map[string]func(*builder, []string, map[string]bool, string) error
|
var evaluateTable map[string]func(*builder, []string, map[string]bool, string) error
|
||||||
|
@ -73,6 +74,7 @@ func init() {
|
||||||
command.Expose: expose,
|
command.Expose: expose,
|
||||||
command.Volume: volume,
|
command.Volume: volume,
|
||||||
command.User: user,
|
command.User: user,
|
||||||
|
command.StopSignal: stopSignal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ func init() {
|
||||||
command.Entrypoint: parseMaybeJSON,
|
command.Entrypoint: parseMaybeJSON,
|
||||||
command.Expose: parseStringsWhitespaceDelimited,
|
command.Expose: parseStringsWhitespaceDelimited,
|
||||||
command.Volume: parseMaybeJSONToList,
|
command.Volume: parseMaybeJSONToList,
|
||||||
|
command.StopSignal: parseString,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
<item> WORKDIR </item>
|
<item> WORKDIR </item>
|
||||||
<item> USER </item>
|
<item> USER </item>
|
||||||
<item> LABEL </item>
|
<item> LABEL </item>
|
||||||
|
<item> STOPSIGNAL </item>
|
||||||
</list>
|
</list>
|
||||||
|
|
||||||
<contexts>
|
<contexts>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>match</key>
|
<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>
|
||||||
<dict>
|
<dict>
|
||||||
<key>captures</key>
|
<key>captures</key>
|
||||||
|
|
|
@ -11,7 +11,7 @@ let b:current_syntax = "dockerfile"
|
||||||
|
|
||||||
syntax case ignore
|
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
|
highlight link dockerfileKeyword Keyword
|
||||||
|
|
||||||
syntax region dockerfileString start=/\v"/ skip=/\v\\./ end=/\v"/
|
syntax region dockerfileString start=/\v"/ skip=/\v\\./ end=/\v"/
|
||||||
|
|
|
@ -524,7 +524,7 @@ func (container *Container) Stop(seconds int) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
container.LogEvent("stop")
|
container.logEvent("stop")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package daemon
|
package daemon
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/signal"
|
||||||
|
"github.com/docker/docker/runconfig"
|
||||||
|
)
|
||||||
|
|
||||||
func TestGetFullName(t *testing.T) {
|
func TestGetFullName(t *testing.T) {
|
||||||
name, err := GetFullContainerName("testing")
|
name, err := GetFullContainerName("testing")
|
||||||
|
|
|
@ -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)
|
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 {
|
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
|
// Now do platform-specific verification
|
||||||
return verifyPlatformContainerSettings(daemon, hostConfig, config)
|
return verifyPlatformContainerSettings(daemon, hostConfig, config)
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,7 @@ Create a container
|
||||||
"ExposedPorts": {
|
"ExposedPorts": {
|
||||||
"22/tcp": {}
|
"22/tcp": {}
|
||||||
},
|
},
|
||||||
|
"StopSignal": "SIGTERM",
|
||||||
"HostConfig": {
|
"HostConfig": {
|
||||||
"Binds": ["/tmp:/tmp"],
|
"Binds": ["/tmp:/tmp"],
|
||||||
"Links": ["redis3:redis"],
|
"Links": ["redis3:redis"],
|
||||||
|
@ -250,6 +251,7 @@ Json Parameters:
|
||||||
container
|
container
|
||||||
- **ExposedPorts** - An object mapping ports to an empty object in the form of:
|
- **ExposedPorts** - An object mapping ports to an empty object in the form of:
|
||||||
`"ExposedPorts": { "<port>/<tcp|udp>: {}" }`
|
`"ExposedPorts": { "<port>/<tcp|udp>: {}" }`
|
||||||
|
- **StopSignal** - Signal to stop a container as a string or unsigned integer. `SIGTERM` by default.
|
||||||
- **HostConfig**
|
- **HostConfig**
|
||||||
- **Binds** – A list of volume bindings for this container. Each volume binding is a string in one of these forms:
|
- **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
|
+ `container_path` to create a new volume for the container
|
||||||
|
@ -367,7 +369,8 @@ Return low-level information on the container `id`
|
||||||
"Tty": false,
|
"Tty": false,
|
||||||
"User": "",
|
"User": "",
|
||||||
"Volumes": null,
|
"Volumes": null,
|
||||||
"WorkingDir": ""
|
"WorkingDir": "",
|
||||||
|
"StopSignal": "SIGTERM"
|
||||||
},
|
},
|
||||||
"Created": "2015-01-06T15:47:31.485331387Z",
|
"Created": "2015-01-06T15:47:31.485331387Z",
|
||||||
"Driver": "devicemapper",
|
"Driver": "devicemapper",
|
||||||
|
|
|
@ -158,6 +158,7 @@ the `Dockerfile`:
|
||||||
* `USER`
|
* `USER`
|
||||||
* `WORKDIR`
|
* `WORKDIR`
|
||||||
* `VOLUME`
|
* `VOLUME`
|
||||||
|
* `STOPSIGNAL`
|
||||||
|
|
||||||
as well as:
|
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.
|
> **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
|
## Dockerfile examples
|
||||||
|
|
||||||
# Nginx
|
# Nginx
|
||||||
|
|
|
@ -61,6 +61,7 @@ Creates a new container.
|
||||||
--read-only=false Mount the container's root filesystem as read only
|
--read-only=false Mount the container's root filesystem as read only
|
||||||
--restart="no" Restart policy (no, on-failure[:max-retry], always, unless-stopped)
|
--restart="no" Restart policy (no, on-failure[:max-retry], always, unless-stopped)
|
||||||
--security-opt=[] Security options
|
--security-opt=[] Security options
|
||||||
|
--stop-signal="SIGTERM" Signal to stop a container
|
||||||
-t, --tty=false Allocate a pseudo-TTY
|
-t, --tty=false Allocate a pseudo-TTY
|
||||||
--disable-content-trust=true Skip image verification
|
--disable-content-trust=true Skip image verification
|
||||||
-u, --user="" Username or UID
|
-u, --user="" Username or UID
|
||||||
|
|
|
@ -62,6 +62,7 @@ weight=1
|
||||||
--restart="no" Restart policy (no, on-failure[:max-retry], always, unless-stopped)
|
--restart="no" Restart policy (no, on-failure[:max-retry], always, unless-stopped)
|
||||||
--rm=false Automatically remove the container when it exits
|
--rm=false Automatically remove the container when it exits
|
||||||
--security-opt=[] Security Options
|
--security-opt=[] Security Options
|
||||||
|
--stop-signal="SIGTERM" Signal to stop a container
|
||||||
--sig-proxy=true Proxy received signals to the process
|
--sig-proxy=true Proxy received signals to the process
|
||||||
-t, --tty=false Allocate a pseudo-TTY
|
-t, --tty=false Allocate a pseudo-TTY
|
||||||
-u, --user="" Username or UID (format: <name|uid>[:<group|gid>])
|
-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.
|
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
|
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.
|
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.
|
||||||
|
|
|
@ -5660,3 +5660,18 @@ func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) {
|
||||||
_, err = buildImageFromContext(name, ctx, true)
|
_, err = buildImageFromContext(name, ctx, true)
|
||||||
c.Assert(err, check.IsNil)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -458,3 +458,15 @@ func (s *DockerTrustSuite) TestTrustedCreateFromBadTrustServer(c *check.C) {
|
||||||
c.Fatalf("Missing expected output on trusted push:\n%s", out)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -181,7 +181,7 @@ To get information on a container use its ID or instance name:
|
||||||
"MemorySwap": 0,
|
"MemorySwap": 0,
|
||||||
"CpuShares": 0,
|
"CpuShares": 0,
|
||||||
"Cpuset": "",
|
"Cpuset": "",
|
||||||
"StopSignal": 15,
|
"StopSignal": "SIGTERM"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue