mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Update native execdriver to exploit libcontainer hooks
Using @mavenugo's patch for enabling the libcontainer pre-start hook to be used for network namespace initialization (correcting the conflict with user namespaces); updated the boolean check to the more generic SupportsHooks() name, and fixed the hook state function signature. Signed-off-by: Madhu Venugopal <madhu@docker.com> Docker-DCO-1.1-Signed-off-by: Phil Estes <estesp@linux.vnet.ibm.com> (github: estesp)
This commit is contained in:
parent
e91f2c26ce
commit
e148e763b8
13 changed files with 122 additions and 40 deletions
|
@ -811,7 +811,7 @@ func (container *Container) exec(ExecConfig *ExecConfig) error {
|
|||
container.Lock()
|
||||
defer container.Unlock()
|
||||
|
||||
callback := func(processConfig *execdriver.ProcessConfig, pid int) {
|
||||
callback := func(processConfig *execdriver.ProcessConfig, pid int) error {
|
||||
if processConfig.Tty {
|
||||
// The callback is called after the process Start()
|
||||
// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave
|
||||
|
@ -821,6 +821,7 @@ func (container *Container) exec(ExecConfig *ExecConfig) error {
|
|||
}
|
||||
}
|
||||
close(ExecConfig.waitStart)
|
||||
return nil
|
||||
}
|
||||
|
||||
// We use a callback here instead of a goroutine and an chan for
|
||||
|
@ -837,7 +838,7 @@ func (container *Container) exec(ExecConfig *ExecConfig) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (container *Container) monitorExec(ExecConfig *ExecConfig, callback execdriver.StartCallback) error {
|
||||
func (container *Container) monitorExec(ExecConfig *ExecConfig, callback execdriver.DriverCallback) error {
|
||||
var (
|
||||
err error
|
||||
exitCode int
|
||||
|
|
|
@ -174,8 +174,9 @@ func getDevicesFromPath(deviceMapping runconfig.DeviceMapping) (devs []*configs.
|
|||
func populateCommand(c *Container, env []string) error {
|
||||
var en *execdriver.Network
|
||||
if !c.Config.NetworkDisabled {
|
||||
en = &execdriver.Network{
|
||||
NamespacePath: c.NetworkSettings.SandboxKey,
|
||||
en = &execdriver.Network{}
|
||||
if !c.daemon.execDriver.SupportsHooks() || c.hostConfig.NetworkMode.IsHost() {
|
||||
en.NamespacePath = c.NetworkSettings.SandboxKey
|
||||
}
|
||||
|
||||
parts := strings.SplitN(string(c.hostConfig.NetworkMode), ":", 2)
|
||||
|
@ -405,6 +406,10 @@ func (container *Container) buildSandboxOptions() ([]libnetwork.SandboxOption, e
|
|||
sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox())
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts"))
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"))
|
||||
} else if container.daemon.execDriver.SupportsHooks() {
|
||||
// OptionUseExternalKey is mandatory for userns support.
|
||||
// But optional for non-userns support
|
||||
sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey())
|
||||
}
|
||||
|
||||
container.HostsPath, err = container.getRootResourcePath("hosts")
|
||||
|
@ -947,6 +952,20 @@ func (container *Container) initializeNetworking() error {
|
|||
return container.buildHostnameFile()
|
||||
}
|
||||
|
||||
// called from the libcontainer pre-start hook to set the network
|
||||
// namespace configuration linkage to the libnetwork "sandbox" entity
|
||||
func (container *Container) setNetworkNamespaceKey(pid int) error {
|
||||
path := fmt.Sprintf("/proc/%d/ns/net", pid)
|
||||
var sandbox libnetwork.Sandbox
|
||||
search := libnetwork.SandboxContainerWalker(&sandbox, container.ID)
|
||||
container.daemon.netController.WalkSandboxes(search)
|
||||
if sandbox == nil {
|
||||
return fmt.Errorf("no sandbox present for %s", container.ID)
|
||||
}
|
||||
|
||||
return sandbox.SetKey(path)
|
||||
}
|
||||
|
||||
func (container *Container) getIpcContainer() (*Container, error) {
|
||||
containerID := container.hostConfig.IpcMode.Container()
|
||||
c, err := container.daemon.Get(containerID)
|
||||
|
|
|
@ -138,6 +138,11 @@ func (container *Container) getSize() (int64, int64) {
|
|||
return 0, 0
|
||||
}
|
||||
|
||||
// setNetworkNamespaceKey is a no-op on Windows.
|
||||
func (container *Container) setNetworkNamespaceKey(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// allocateNetwork is a no-op on Windows.
|
||||
func (container *Container) allocateNetwork() error {
|
||||
return nil
|
||||
|
|
|
@ -875,8 +875,14 @@ func (daemon *Daemon) unmount(container *Container) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
|
||||
return daemon.execDriver.Run(c.command, pipes, startCallback)
|
||||
func (daemon *Daemon) run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (execdriver.ExitStatus, error) {
|
||||
hooks := execdriver.Hooks{
|
||||
Start: startCallback,
|
||||
}
|
||||
hooks.PreStart = append(hooks.PreStart, func(processConfig *execdriver.ProcessConfig, pid int) error {
|
||||
return c.setNetworkNamespaceKey(pid)
|
||||
})
|
||||
return daemon.execDriver.Run(c.command, pipes, hooks)
|
||||
}
|
||||
|
||||
func (daemon *Daemon) kill(c *Container, sig int) error {
|
||||
|
|
|
@ -267,8 +267,11 @@ func (d *Daemon) ContainerExecStart(execName string, stdin io.ReadCloser, stdout
|
|||
}
|
||||
|
||||
// Exec calls the underlying exec driver to run
|
||||
func (d *Daemon) Exec(c *Container, ExecConfig *ExecConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||
exitStatus, err := d.execDriver.Exec(c.command, ExecConfig.ProcessConfig, pipes, startCallback)
|
||||
func (d *Daemon) Exec(c *Container, ExecConfig *ExecConfig, pipes *execdriver.Pipes, startCallback execdriver.DriverCallback) (int, error) {
|
||||
hooks := execdriver.Hooks{
|
||||
Start: startCallback,
|
||||
}
|
||||
exitStatus, err := d.execDriver.Exec(c.command, ExecConfig.ProcessConfig, pipes, hooks)
|
||||
|
||||
// On err, make sure we don't leave ExitCode at zero
|
||||
if err != nil && exitStatus == 0 {
|
||||
|
|
|
@ -24,10 +24,22 @@ var (
|
|||
ErrDriverNotFound = errors.New("The requested docker init has not been found")
|
||||
)
|
||||
|
||||
// StartCallback defines a callback function.
|
||||
// It's used by 'Run' and 'Exec', does some work in parent process
|
||||
// after child process is started.
|
||||
type StartCallback func(*ProcessConfig, int)
|
||||
// DriverCallback defines a callback function which is used in "Run" and "Exec".
|
||||
// This allows work to be done in the parent process when the child is passing
|
||||
// through PreStart, Start and PostStop events.
|
||||
// Callbacks are provided a processConfig pointer and the pid of the child
|
||||
type DriverCallback func(processConfig *ProcessConfig, pid int) error
|
||||
|
||||
// Hooks is a struct containing function pointers to callbacks
|
||||
// used by any execdriver implementation exploiting hooks capabilities
|
||||
type Hooks struct {
|
||||
// PreStart is called before container's CMD/ENTRYPOINT is executed
|
||||
PreStart []DriverCallback
|
||||
// Start is called after the container's process is full started
|
||||
Start DriverCallback
|
||||
// PostStop is called after the container process exits
|
||||
PostStop []DriverCallback
|
||||
}
|
||||
|
||||
// Info is driver specific information based on
|
||||
// processes registered with the driver
|
||||
|
@ -56,11 +68,11 @@ type ExitStatus struct {
|
|||
type Driver interface {
|
||||
// Run executes the process, blocks until the process exits and returns
|
||||
// the exit code. It's the last stage on Docker side for running a container.
|
||||
Run(c *Command, pipes *Pipes, startCallback StartCallback) (ExitStatus, error)
|
||||
Run(c *Command, pipes *Pipes, hooks Hooks) (ExitStatus, error)
|
||||
|
||||
// Exec executes the process in an existing container, blocks until the
|
||||
// process exits and returns the exit code.
|
||||
Exec(c *Command, processConfig *ProcessConfig, pipes *Pipes, startCallback StartCallback) (int, error)
|
||||
Exec(c *Command, processConfig *ProcessConfig, pipes *Pipes, hooks Hooks) (int, error)
|
||||
|
||||
// Kill sends signals to process in container.
|
||||
Kill(c *Command, sig int) error
|
||||
|
@ -89,6 +101,9 @@ type Driver interface {
|
|||
|
||||
// Stats returns resource stats for a running container
|
||||
Stats(id string) (*ResourceStats, error)
|
||||
|
||||
// SupportsHooks refers to the driver capability to exploit pre/post hook functionality
|
||||
SupportsHooks() bool
|
||||
}
|
||||
|
||||
// Ipc settings of the container
|
||||
|
|
|
@ -125,7 +125,7 @@ func killNetNsProc(proc *os.Process) {
|
|||
|
||||
// Run implements the exec driver Driver interface,
|
||||
// it calls 'exec.Cmd' to launch lxc commands to run a container.
|
||||
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
|
||||
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
|
||||
var (
|
||||
term execdriver.Terminal
|
||||
err error
|
||||
|
@ -324,9 +324,9 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
|||
|
||||
c.ContainerPid = pid
|
||||
|
||||
if startCallback != nil {
|
||||
if hooks.Start != nil {
|
||||
logrus.Debugf("Invoking startCallback")
|
||||
startCallback(&c.ProcessConfig, pid)
|
||||
hooks.Start(&c.ProcessConfig, pid)
|
||||
}
|
||||
|
||||
oomKill := false
|
||||
|
@ -870,7 +870,7 @@ func (t *TtyConsole) Close() error {
|
|||
|
||||
// Exec implements the exec driver Driver interface,
|
||||
// it is not implemented by lxc.
|
||||
func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||
func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, hooks execdriver.Hooks) (int, error) {
|
||||
return -1, ErrExec
|
||||
}
|
||||
|
||||
|
@ -883,3 +883,9 @@ func (d *Driver) Stats(id string) (*execdriver.ResourceStats, error) {
|
|||
}
|
||||
return execdriver.Stats(d.containerDir(id), d.activeContainers[id].container.Cgroups.Memory, d.machineMemory)
|
||||
}
|
||||
|
||||
// SupportsHooks implements the execdriver Driver interface.
|
||||
// The LXC execdriver does not support the hook mechanism, which is currently unique to runC/libcontainer.
|
||||
func (d *Driver) SupportsHooks() bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
|
||||
// createContainer populates and configures the container type with the
|
||||
// data provided by the execdriver.Command
|
||||
func (d *Driver) createContainer(c *execdriver.Command) (*configs.Config, error) {
|
||||
func (d *Driver) createContainer(c *execdriver.Command, hooks execdriver.Hooks) (*configs.Config, error) {
|
||||
container := execdriver.InitContainer(c)
|
||||
|
||||
if err := d.createIpc(container, c); err != nil {
|
||||
|
@ -33,7 +33,7 @@ func (d *Driver) createContainer(c *execdriver.Command) (*configs.Config, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := d.createNetwork(container, c); err != nil {
|
||||
if err := d.createNetwork(container, c, hooks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ func generateIfaceName() (string, error) {
|
|||
return "", errors.New("Failed to find name for new interface")
|
||||
}
|
||||
|
||||
func (d *Driver) createNetwork(container *configs.Config, c *execdriver.Command) error {
|
||||
func (d *Driver) createNetwork(container *configs.Config, c *execdriver.Command, hooks execdriver.Hooks) error {
|
||||
if c.Network == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -135,11 +135,26 @@ func (d *Driver) createNetwork(container *configs.Config, c *execdriver.Command)
|
|||
return nil
|
||||
}
|
||||
|
||||
if c.Network.NamespacePath == "" {
|
||||
return fmt.Errorf("network namespace path is empty")
|
||||
if c.Network.NamespacePath != "" {
|
||||
container.Namespaces.Add(configs.NEWNET, c.Network.NamespacePath)
|
||||
return nil
|
||||
}
|
||||
// only set up prestart hook if the namespace path is not set (this should be
|
||||
// all cases *except* for --net=host shared networking)
|
||||
container.Hooks = &configs.Hooks{
|
||||
Prestart: []configs.Hook{
|
||||
configs.NewFunctionHook(func(s configs.HookState) error {
|
||||
if len(hooks.PreStart) > 0 {
|
||||
for _, fnHook := range hooks.PreStart {
|
||||
if err := fnHook(&c.ProcessConfig, s.Pid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
container.Namespaces.Add(configs.NEWNET, c.Network.NamespacePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -131,9 +131,9 @@ type execOutput struct {
|
|||
|
||||
// Run implements the exec driver Driver interface,
|
||||
// it calls libcontainer APIs to run a container.
|
||||
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
|
||||
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
|
||||
// take the Command and populate the libcontainer.Config from it
|
||||
container, err := d.createContainer(c)
|
||||
container, err := d.createContainer(c, hooks)
|
||||
if err != nil {
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
@ -165,14 +165,14 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
|||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
|
||||
if startCallback != nil {
|
||||
if hooks.Start != nil {
|
||||
pid, err := p.Pid()
|
||||
if err != nil {
|
||||
p.Signal(os.Kill)
|
||||
p.Wait()
|
||||
return execdriver.ExitStatus{ExitCode: -1}, err
|
||||
}
|
||||
startCallback(&c.ProcessConfig, pid)
|
||||
hooks.Start(&c.ProcessConfig, pid)
|
||||
}
|
||||
|
||||
oom := notifyOnOOM(cont)
|
||||
|
@ -477,3 +477,9 @@ func setupPipes(container *configs.Config, processConfig *execdriver.ProcessConf
|
|||
processConfig.Terminal = term
|
||||
return nil
|
||||
}
|
||||
|
||||
// SupportsHooks implements the execdriver Driver interface.
|
||||
// The libcontainer/runC-based native execdriver does exploit the hook mechanism
|
||||
func (d *Driver) SupportsHooks() bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
|
||||
// Exec implements the exec driver Driver interface,
|
||||
// it calls libcontainer APIs to execute a container.
|
||||
func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||
func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, hooks execdriver.Hooks) (int, error) {
|
||||
active := d.activeContainers[c.ID]
|
||||
if active == nil {
|
||||
return -1, fmt.Errorf("No active container exists with ID %s", c.ID)
|
||||
|
@ -45,14 +45,14 @@ func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
|
|||
return -1, err
|
||||
}
|
||||
|
||||
if startCallback != nil {
|
||||
if hooks.Start != nil {
|
||||
pid, err := p.Pid()
|
||||
if err != nil {
|
||||
p.Signal(os.Kill)
|
||||
p.Wait()
|
||||
return -1, err
|
||||
}
|
||||
startCallback(&c.ProcessConfig, pid)
|
||||
hooks.Start(&c.ProcessConfig, pid)
|
||||
}
|
||||
|
||||
ps, err := p.Wait()
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
// Exec implements the exec driver Driver interface.
|
||||
func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
|
||||
func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, hooks execdriver.Hooks) (int, error) {
|
||||
|
||||
var (
|
||||
term execdriver.Terminal
|
||||
|
@ -69,8 +69,8 @@ func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessCo
|
|||
processConfig.Terminal = term
|
||||
|
||||
// Invoke the start callback
|
||||
if startCallback != nil {
|
||||
startCallback(&c.ProcessConfig, int(pid))
|
||||
if hooks.Start != nil {
|
||||
hooks.Start(&c.ProcessConfig, int(pid))
|
||||
}
|
||||
|
||||
if exitCode, err = hcsshim.WaitForProcessInComputeSystem(c.ID, pid); err != nil {
|
||||
|
|
|
@ -77,7 +77,7 @@ type containerInit struct {
|
|||
const defaultOwner = "docker"
|
||||
|
||||
// Run implements the exec driver Driver interface
|
||||
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (execdriver.ExitStatus, error) {
|
||||
func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execdriver.Hooks) (execdriver.ExitStatus, error) {
|
||||
|
||||
var (
|
||||
term execdriver.Terminal
|
||||
|
@ -290,9 +290,8 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
|||
}
|
||||
d.Unlock()
|
||||
|
||||
// Invoke the start callback
|
||||
if startCallback != nil {
|
||||
startCallback(&c.ProcessConfig, int(pid))
|
||||
if hooks.Start != nil {
|
||||
hooks.Start(&c.ProcessConfig, int(pid))
|
||||
}
|
||||
|
||||
var exitCode int32
|
||||
|
@ -305,3 +304,9 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
|
|||
logrus.Debugf("Exiting Run() exitCode %d id=%s", exitCode, c.ID)
|
||||
return execdriver.ExitStatus{ExitCode: int(exitCode)}, nil
|
||||
}
|
||||
|
||||
// SupportsHooks implements the execdriver Driver interface.
|
||||
// The windows driver does not support the hook mechanism
|
||||
func (d *Driver) SupportsHooks() bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -250,7 +250,7 @@ func (m *containerMonitor) shouldRestart(exitCode int) bool {
|
|||
|
||||
// callback ensures that the container's state is properly updated after we
|
||||
// received ack from the execution drivers
|
||||
func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid int) {
|
||||
func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid int) error {
|
||||
if processConfig.Tty {
|
||||
// The callback is called after the process Start()
|
||||
// so we are in the parent process. In TTY mode, stdin/out/err is the PtySlave
|
||||
|
@ -273,6 +273,7 @@ func (m *containerMonitor) callback(processConfig *execdriver.ProcessConfig, pid
|
|||
if err := m.container.toDiskLocking(); err != nil {
|
||||
logrus.Errorf("Error saving container to disk: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// resetContainer resets the container's IO and ensures that the command is able to be executed again
|
||||
|
|
Loading…
Reference in a new issue