Adding RunIn to run a user specified command in an existing container, wait for it to exit and return the exit code.
RunIn will connect to a user specified Terminal before running the command. Docker-DCO-1.1-Signed-off-by: Vishnu Kannan <vishnuk@google.com> (github: vishh)
This commit is contained in:
parent
1f3d65f5fd
commit
145299f9d8
|
@ -5,31 +5,84 @@ package namespaces
|
|||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/label"
|
||||
"github.com/docker/libcontainer/system"
|
||||
)
|
||||
|
||||
// Runs the command under 'args' inside an existing container referred to by 'container'. This API currently does not support a Tty based 'term'.
|
||||
// Returns the exitcode of the command upon success and appropriate error on failure.
|
||||
func RunIn(container *libcontainer.Config, state *libcontainer.State, args []string, nsinitPath string, term Terminal, startCallback func()) (int, error) {
|
||||
containerJson, err := getContainerJson(container)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
var cmd exec.Cmd
|
||||
|
||||
cmd.Path = nsinitPath
|
||||
cmd.Args = getNsEnterCommand(nsinitPath, strconv.Itoa(state.InitPid), containerJson, args)
|
||||
|
||||
if err := term.Attach(&cmd); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
defer term.Close()
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if startCallback != nil {
|
||||
startCallback()
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
if _, ok := err.(*exec.ExitError); !ok {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
||||
}
|
||||
|
||||
// ExecIn uses an existing pid and joins the pid's namespaces with the new command.
|
||||
func ExecIn(container *libcontainer.Config, state *libcontainer.State, args []string) error {
|
||||
// TODO(vmarmol): If this gets too long, send it over a pipe to the child.
|
||||
// Marshall the container into JSON since it won't be available in the namespace.
|
||||
containerJson, err := json.Marshal(container)
|
||||
containerJson, err := getContainerJson(container)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Enter the namespace and then finish setup
|
||||
finalArgs := []string{os.Args[0], "nsenter", "--nspid", strconv.Itoa(state.InitPid), "--containerjson", string(containerJson), "--"}
|
||||
finalArgs = append(finalArgs, args...)
|
||||
finalArgs := getNsEnterCommand(os.Args[0], strconv.Itoa(state.InitPid), containerJson, args)
|
||||
|
||||
if err := system.Execv(finalArgs[0], finalArgs[0:], os.Environ()); err != nil {
|
||||
return err
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func getContainerJson(container *libcontainer.Config) (string, error) {
|
||||
// TODO(vmarmol): If this gets too long, send it over a pipe to the child.
|
||||
// Marshall the container into JSON since it won't be available in the namespace.
|
||||
containerJson, err := json.Marshal(container)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(containerJson), nil
|
||||
}
|
||||
|
||||
func getNsEnterCommand(nsinitPath, initPid, containerJson string, args []string) []string {
|
||||
return append([]string{
|
||||
nsinitPath,
|
||||
"nsenter",
|
||||
"--nspid", initPid,
|
||||
"--containerjson", containerJson,
|
||||
"--",
|
||||
}, args...)
|
||||
}
|
||||
|
||||
// Run a command in a container after entering the namespace.
|
||||
func NsEnter(container *libcontainer.Config, args []string) error {
|
||||
// clear the current processes env and replace it with the environment
|
||||
|
|
Loading…
Reference in New Issue