From 65c782bb9b0b159f5644395cb291b8741b4400f4 Mon Sep 17 00:00:00 2001 From: lujingxiao Date: Sat, 19 Jan 2019 11:15:40 +0800 Subject: [PATCH 011/111] hookspec: Allow adding custom hooks reason: Add new flag "--hook-spec" which accept a file containing custom hook definitions, custom hooks will be appended to system hooks which means docker will execute its own hook first(libnetwork prestart hook) to make sure everything predefined is working normally, user custom programme can be executed afterwards One example hook spec file can be of format: ``` { "prestart": [ { "path": "/bin/ls", "args": ["ls"], "env": [] } ], "poststart":[], "poststop":[] } ``` Change-Id: Iee6f4e5b56ebf0647304c08c2948d599356192e6 Signed-off-by: Zhang Wei Signed-off-by: xiadanni Signed-off-by: lujingxiao --- components/cli/cli/command/container/opts.go | 3 ++ .../cli/docs/reference/commandline/create.md | 1 + .../cli/docs/reference/commandline/run.md | 1 + components/cli/man/docker-run.1.md | 28 +++++++++++++++++++ .../docker/api/types/container/host_config.go | 1 + .../engine/api/types/container/host_config.go | 1 + components/engine/container/container.go | 4 ++- components/engine/daemon/container.go | 24 ++++++++++++++++ components/engine/daemon/daemon_unix.go | 16 +++++++++++ components/engine/daemon/oci_linux.go | 6 ++++ 10 files changed, 84 insertions(+), 1 deletion(-) diff --git a/components/cli/cli/command/container/opts.go b/components/cli/cli/command/container/opts.go index af30dfcbf2..8e07aa77cb 100644 --- a/components/cli/cli/command/container/opts.go +++ b/components/cli/cli/command/container/opts.go @@ -111,6 +111,7 @@ type containerOptions struct { stopTimeout int isolation string shmSize opts.MemBytes + hookSpec string noHealthcheck bool healthCmd string healthInterval time.Duration @@ -283,6 +284,7 @@ func addFlags(flags *pflag.FlagSet) *containerOptions { flags.StringVar(&copts.isolation, "isolation", "", "Container isolation technology") flags.StringVar(&copts.pidMode, "pid", "", "PID namespace to use") flags.Var(&copts.shmSize, "shm-size", "Size of /dev/shm") + flags.StringVar(&copts.hookSpec, "hook-spec", "", "file containing hook definition(prestart, poststart, poststop)") flags.StringVar(&copts.utsMode, "uts", "", "UTS namespace to use") flags.StringVar(&copts.runtime, "runtime", "", "Runtime to use for this container") @@ -619,6 +621,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions) (*containerConfig, err VolumeDriver: copts.volumeDriver, Isolation: container.Isolation(copts.isolation), ShmSize: copts.shmSize.Value(), + HookSpec: copts.hookSpec, Resources: resources, Tmpfs: tmpfs, Sysctls: copts.sysctls.GetAll(), diff --git a/components/cli/docs/reference/commandline/create.md b/components/cli/docs/reference/commandline/create.md index 5d888183b3..34cb79a679 100644 --- a/components/cli/docs/reference/commandline/create.md +++ b/components/cli/docs/reference/commandline/create.md @@ -66,6 +66,7 @@ Options: --health-start-period duration Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h) (default 0s) --help Print usage -h, --hostname string Container host name + --hook-spec File containing hook definition(prestart, poststart, poststop) --init Run an init inside the container that forwards signals and reaps processes -i, --interactive Keep STDIN open even if not attached --io-maxbandwidth string Maximum IO bandwidth limit for the system drive (Windows only) diff --git a/components/cli/docs/reference/commandline/run.md b/components/cli/docs/reference/commandline/run.md index 21b4fdf261..1dc43ddcd9 100644 --- a/components/cli/docs/reference/commandline/run.md +++ b/components/cli/docs/reference/commandline/run.md @@ -70,6 +70,7 @@ Options: --health-start-period duration Start period for the container to initialize before counting retries towards unstable (ns|us|ms|s|m|h) (default 0s) --help Print usage -h, --hostname string Container host name + --hook-spec File containing hook definition(prestart, poststart, poststop) --init Run an init inside the container that forwards signals and reaps processes -i, --interactive Keep STDIN open even if not attached --io-maxbandwidth string Maximum IO bandwidth limit for the system drive (Windows only) diff --git a/components/cli/man/docker-run.1.md b/components/cli/man/docker-run.1.md index 41f501d5b9..b0cbcd2e87 100644 --- a/components/cli/man/docker-run.1.md +++ b/components/cli/man/docker-run.1.md @@ -43,6 +43,7 @@ docker-run - Run a command in a new container [**--group-add**[=*[]*]] [**-h**|**--hostname**[=*HOSTNAME*]] [**--help**] +[**--hook-spec**[=*HOOKFILE*]] [**--init**] [**-i**|**--interactive**] [**--ip**[=*IPv4-ADDRESS*]] @@ -330,6 +331,33 @@ redirection on the host system. **--help** Print usage statement +**--hook-spec**="" + Add custom hooks for container + + With this flag, user can specify a file containing custom hook, an example hook file can be like this: + +``` +{ + "prestart": [ + { + "path": "/usr/libexec/oci/hooks.d/oci-systemd-hook", + "args": ["oci-systemd-hook", "prestart"], + "env": ["container=runc"] + } + ], + "poststop":[ + { + "path": "/usr/libexec/oci/hooks.d/oci-systemd-hook", + "args": ["oci-systemd-hook", "poststop"], + "env": ["container=runc"] + } + ] +} +``` + + currently it supports three hooks: "prestart", "poststart", "poststop". + See OCI spec definition for more information about "hooks". + **--init** Run an init inside the container that forwards signals and reaps processes diff --git a/components/cli/vendor/github.com/docker/docker/api/types/container/host_config.go b/components/cli/vendor/github.com/docker/docker/api/types/container/host_config.go index 1565b5e091..701cae55f1 100644 --- a/components/cli/vendor/github.com/docker/docker/api/types/container/host_config.go +++ b/components/cli/vendor/github.com/docker/docker/api/types/container/host_config.go @@ -395,6 +395,7 @@ type HostConfig struct { // Applicable to Windows ConsoleSize [2]uint // Initial console size (height,width) Isolation Isolation // Isolation technology of the container (e.g. default, hyperv) + HookSpec string // specification file containing custom hook definition // Contains container's resources (cgroups, ulimits) Resources diff --git a/components/engine/api/types/container/host_config.go b/components/engine/api/types/container/host_config.go index 1565b5e091..701cae55f1 100644 --- a/components/engine/api/types/container/host_config.go +++ b/components/engine/api/types/container/host_config.go @@ -395,6 +395,7 @@ type HostConfig struct { // Applicable to Windows ConsoleSize [2]uint // Initial console size (height,width) Isolation Isolation // Isolation technology of the container (e.g. default, hyperv) + HookSpec string // specification file containing custom hook definition // Contains container's resources (cgroups, ulimits) Resources diff --git a/components/engine/container/container.go b/components/engine/container/container.go index f74676f7ee..02adc2019a 100644 --- a/components/engine/container/container.go +++ b/components/engine/container/container.go @@ -38,6 +38,7 @@ import ( volumemounts "github.com/docker/docker/volume/mounts" "github.com/docker/go-units" agentexec "github.com/docker/swarmkit/agent/exec" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -93,6 +94,8 @@ type Container struct { LogCopier *logger.Copier `json:"-"` restartManager restartmanager.RestartManager attachContext *attachContext + Hooks specs.Hooks + CgroupParent string // Fields here are specific to Unix platforms AppArmorProfile string @@ -106,7 +109,6 @@ type Container struct { // Fields here are specific to Windows NetworkSharedContainerID string `json:"-"` SharedEndpointList []string `json:"-"` - CgroupParent string } // NewBaseContainer creates a new container with its diff --git a/components/engine/daemon/container.go b/components/engine/daemon/container.go index bd96de2571..8e68904b16 100644 --- a/components/engine/daemon/container.go +++ b/components/engine/daemon/container.go @@ -1,6 +1,7 @@ package daemon // import "github.com/docker/docker/daemon" import ( + "encoding/json" "fmt" "os" "path" @@ -224,11 +225,34 @@ func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig * return err } + // register hooks to container + if err := daemon.registerHooks(container, hostConfig); err != nil { + return err + } + runconfig.SetDefaultNetModeIfBlank(hostConfig) container.HostConfig = hostConfig return container.CheckpointTo(daemon.containersReplica) } + +func (daemon *Daemon) registerHooks(container *container.Container, hostConfig *containertypes.HostConfig) error { + if hostConfig.HookSpec == "" { + return nil + } + // the hook spec has already been sanitized, so no need for validation again + f, err := os.Open(hostConfig.HookSpec) + if err != nil { + return fmt.Errorf("open hook spec file error: %v", err) + } + defer f.Close() + + if err = json.NewDecoder(f).Decode(&container.Hooks); err != nil { + return fmt.Errorf("malformed hook spec, is your spec file in json format? error: %v", err) + } + return nil +} + // verifyContainerSettings performs validation of the hostconfig and config // structures. func (daemon *Daemon) verifyContainerSettings(platform string, hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { diff --git a/components/engine/daemon/daemon_unix.go b/components/engine/daemon/daemon_unix.go index f4b75055f5..ebf4e067fb 100644 --- a/components/engine/daemon/daemon_unix.go +++ b/components/engine/daemon/daemon_unix.go @@ -627,6 +627,22 @@ func (daemon *Daemon) verifyPlatformContainerSettings(hostConfig *containertypes return warnings, fmt.Errorf("cgroup-parent for systemd cgroup should be a valid slice named as \"xxx.slice\"") } } + + if hostConfig.HookSpec != "" { + hostConfig.HookSpec = filepath.Clean(hostConfig.HookSpec) + if !filepath.IsAbs(hostConfig.HookSpec) { + return warnings, fmt.Errorf("Hook spec file must be an absolute path") + } + fi, err := os.Stat(hostConfig.HookSpec) + if err != nil { + return warnings, fmt.Errorf("stat hook spec file failed: %v", err) + } + if !fi.Mode().IsRegular() { + return warnings, fmt.Errorf("Hook spec file must be a regular text file") + } + } + + if hostConfig.Runtime == "" { hostConfig.Runtime = daemon.configStore.GetDefaultRuntimeName() } diff --git a/components/engine/daemon/oci_linux.go b/components/engine/daemon/oci_linux.go index 5018b21f0d..884739c07e 100644 --- a/components/engine/daemon/oci_linux.go +++ b/components/engine/daemon/oci_linux.go @@ -818,6 +818,12 @@ func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, e } } + // apppend user custom hooks after system hooks + // make sure docker's predefined hooks are executed before custom hooks + s.Hooks.Prestart = append(s.Hooks.Prestart, c.Hooks.Prestart...) + s.Hooks.Poststart = append(s.Hooks.Poststart, c.Hooks.Poststart...) + s.Hooks.Poststop = append(s.Hooks.Poststop, c.Hooks.Poststop...) + if apparmor.IsEnabled() { var appArmorProfile string if c.AppArmorProfile != "" { -- 2.17.1