Add create and start command for container lifecycle
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
75fb70be01
commit
3fe7d7f31e
|
@ -0,0 +1,72 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var createCommand = cli.Command{
|
||||||
|
Name: "create",
|
||||||
|
Usage: "create a container",
|
||||||
|
ArgsUsage: `<container-id>
|
||||||
|
|
||||||
|
Where "<container-id>" is your name for the instance of the container that you
|
||||||
|
are starting. The name you provide for the container instance must be unique on
|
||||||
|
your host.`,
|
||||||
|
Description: `The create command creates an instance of a container for a bundle. The bundle
|
||||||
|
is a directory with a specification file named "` + specConfig + `" and a root
|
||||||
|
filesystem.
|
||||||
|
|
||||||
|
The specification file includes an args parameter. The args parameter is used
|
||||||
|
to specify command(s) that get run when the container is started. To change the
|
||||||
|
command(s) that get executed on start, edit the args parameter of the spec. See
|
||||||
|
"runc spec --help" for more explanation.`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "bundle, b",
|
||||||
|
Value: "",
|
||||||
|
Usage: `path to the root of the bundle directory, defaults to the current directory`,
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "console",
|
||||||
|
Value: "",
|
||||||
|
Usage: "specify the pty slave path for use with the container",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "pid-file",
|
||||||
|
Value: "",
|
||||||
|
Usage: "specify the file to write the process id to",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "no-pivot",
|
||||||
|
Usage: "do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
bundle := context.String("bundle")
|
||||||
|
if bundle != "" {
|
||||||
|
if err := os.Chdir(bundle); err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spec, err := loadSpec(specConfig)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
notifySocket := os.Getenv("NOTIFY_SOCKET")
|
||||||
|
if notifySocket != "" {
|
||||||
|
setupSdNotify(spec, notifySocket)
|
||||||
|
}
|
||||||
|
if os.Geteuid() != 0 {
|
||||||
|
fatalf("runc should be run as root")
|
||||||
|
}
|
||||||
|
status, err := startContainer(context, spec, true)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
// exit with the container's exit status so any external supervisor is
|
||||||
|
// notified of the exit with the correct exit status.
|
||||||
|
os.Exit(status)
|
||||||
|
},
|
||||||
|
}
|
|
@ -29,6 +29,13 @@ const (
|
||||||
|
|
||||||
// Destroyed is the status that denotes the container does not exist.
|
// Destroyed is the status that denotes the container does not exist.
|
||||||
Destroyed
|
Destroyed
|
||||||
|
|
||||||
|
// Stopped is the status that denotes the container does not have a created or running process.
|
||||||
|
Stopped
|
||||||
|
|
||||||
|
// Initialized is the status where the container has all the namespaces created but the user
|
||||||
|
// process has not been start.
|
||||||
|
Initialized
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s Status) String() string {
|
func (s Status) String() string {
|
||||||
|
@ -43,6 +50,10 @@ func (s Status) String() string {
|
||||||
return "paused"
|
return "paused"
|
||||||
case Destroyed:
|
case Destroyed:
|
||||||
return "destroyed"
|
return "destroyed"
|
||||||
|
case Stopped:
|
||||||
|
return "stopped"
|
||||||
|
case Initialized:
|
||||||
|
return "initialized"
|
||||||
default:
|
default:
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,6 @@ func (c *linuxContainer) Start(process *Process) error {
|
||||||
}
|
}
|
||||||
// generate a timestamp indicating when the container was started
|
// generate a timestamp indicating when the container was started
|
||||||
c.created = time.Now().UTC()
|
c.created = time.Now().UTC()
|
||||||
|
|
||||||
c.state = &runningState{
|
c.state = &runningState{
|
||||||
c: c,
|
c: c,
|
||||||
}
|
}
|
||||||
|
@ -1034,31 +1033,47 @@ func (c *linuxContainer) refreshState() error {
|
||||||
if paused {
|
if paused {
|
||||||
return c.state.transition(&pausedState{c: c})
|
return c.state.transition(&pausedState{c: c})
|
||||||
}
|
}
|
||||||
running, err := c.isRunning()
|
t, err := c.runType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if running {
|
switch t {
|
||||||
|
case Initialized:
|
||||||
|
return c.state.transition(&initializedState{c: c})
|
||||||
|
case Running:
|
||||||
return c.state.transition(&runningState{c: c})
|
return c.state.transition(&runningState{c: c})
|
||||||
}
|
}
|
||||||
return c.state.transition(&stoppedState{c: c})
|
return c.state.transition(&stoppedState{c: c})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *linuxContainer) isRunning() (bool, error) {
|
func (c *linuxContainer) runType() (Status, error) {
|
||||||
if c.initProcess == nil {
|
if c.initProcess == nil {
|
||||||
return false, nil
|
return Stopped, nil
|
||||||
}
|
}
|
||||||
|
pid := c.initProcess.pid()
|
||||||
// return Running if the init process is alive
|
// return Running if the init process is alive
|
||||||
if err := syscall.Kill(c.initProcess.pid(), 0); err != nil {
|
if err := syscall.Kill(pid, 0); err != nil {
|
||||||
if err == syscall.ESRCH {
|
if err == syscall.ESRCH {
|
||||||
// It means the process does not exist anymore, could happen when the
|
// It means the process does not exist anymore, could happen when the
|
||||||
// process exited just when we call the function, we should not return
|
// process exited just when we call the function, we should not return
|
||||||
// error in this case.
|
// error in this case.
|
||||||
return false, nil
|
return Stopped, nil
|
||||||
}
|
}
|
||||||
return false, newSystemErrorWithCausef(err, "sending signal 0 to pid %d", c.initProcess.pid())
|
return Stopped, newSystemErrorWithCausef(err, "sending signal 0 to pid %d", pid)
|
||||||
}
|
}
|
||||||
return true, nil
|
// check if the process that is running is the init process or the user's process.
|
||||||
|
// this is the difference between the container Running and Created.
|
||||||
|
environ, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/environ", pid))
|
||||||
|
if err != nil {
|
||||||
|
return Stopped, newSystemErrorWithCausef(err, "reading /proc/%d/environ", pid)
|
||||||
|
}
|
||||||
|
check := []byte("_LIBCONTAINER")
|
||||||
|
for _, v := range bytes.Split(environ, []byte("\x00")) {
|
||||||
|
if bytes.Contains(v, check) {
|
||||||
|
return Initialized, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Running, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *linuxContainer) isPaused() (bool, error) {
|
func (c *linuxContainer) isPaused() (bool, error) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
@ -219,6 +220,9 @@ func (l *LinuxFactory) Type() string {
|
||||||
// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
|
// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
|
||||||
// This is a low level implementation detail of the reexec and should not be consumed externally
|
// This is a low level implementation detail of the reexec and should not be consumed externally
|
||||||
func (l *LinuxFactory) StartInitialization() (err error) {
|
func (l *LinuxFactory) StartInitialization() (err error) {
|
||||||
|
// start the signal handler as soon as we can
|
||||||
|
s := make(chan os.Signal, 1024)
|
||||||
|
signal.Notify(s, syscall.SIGCONT)
|
||||||
fdStr := os.Getenv("_LIBCONTAINER_INITPIPE")
|
fdStr := os.Getenv("_LIBCONTAINER_INITPIPE")
|
||||||
pipefd, err := strconv.Atoi(fdStr)
|
pipefd, err := strconv.Atoi(fdStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -260,7 +264,7 @@ func (l *LinuxFactory) StartInitialization() (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return i.Init()
|
return i.Init(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LinuxFactory) loadState(root string) (*State, error) {
|
func (l *LinuxFactory) loadState(root string) (*State, error) {
|
||||||
|
|
|
@ -61,7 +61,7 @@ type initConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type initer interface {
|
type initer interface {
|
||||||
Init() error
|
Init(s chan os.Signal) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContainerInit(t initType, pipe *os.File) (initer, error) {
|
func newContainerInit(t initType, pipe *os.File) (initer, error) {
|
||||||
|
|
|
@ -127,6 +127,9 @@ func runContainer(config *configs.Config, console string, args ...string) (buffe
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return buffers, -1, err
|
return buffers, -1, err
|
||||||
}
|
}
|
||||||
|
if err := container.Signal(syscall.SIGCONT); err != nil {
|
||||||
|
return buffers, -1, err
|
||||||
|
}
|
||||||
ps, err := process.Wait()
|
ps, err := process.Wait()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return buffers, -1, err
|
return buffers, -1, err
|
||||||
|
|
|
@ -5,6 +5,7 @@ package libcontainer
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/apparmor"
|
"github.com/opencontainers/runc/libcontainer/apparmor"
|
||||||
"github.com/opencontainers/runc/libcontainer/keys"
|
"github.com/opencontainers/runc/libcontainer/keys"
|
||||||
|
@ -23,7 +24,7 @@ func (l *linuxSetnsInit) getSessionRingName() string {
|
||||||
return fmt.Sprintf("_ses.%s", l.config.ContainerId)
|
return fmt.Sprintf("_ses.%s", l.config.ContainerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linuxSetnsInit) Init() error {
|
func (l *linuxSetnsInit) Init(s chan os.Signal) error {
|
||||||
// do not inherit the parent's session keyring
|
// do not inherit the parent's session keyring
|
||||||
if _, err := keyctl.JoinSessionKeyring(l.getSessionRingName()); err != nil {
|
if _, err := keyctl.JoinSessionKeyring(l.getSessionRingName()); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -49,5 +50,7 @@ func (l *linuxSetnsInit) Init() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
signal.Stop(s)
|
||||||
|
close(s)
|
||||||
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
|
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/opencontainers/runc/libcontainer/apparmor"
|
"github.com/opencontainers/runc/libcontainer/apparmor"
|
||||||
|
@ -17,7 +19,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type linuxStandardInit struct {
|
type linuxStandardInit struct {
|
||||||
pipe io.ReadWriter
|
pipe io.ReadWriteCloser
|
||||||
parentPid int
|
parentPid int
|
||||||
config *initConfig
|
config *initConfig
|
||||||
}
|
}
|
||||||
|
@ -42,7 +44,7 @@ func (l *linuxStandardInit) getSessionRingParams() (string, uint32, uint32) {
|
||||||
// the kernel
|
// the kernel
|
||||||
const PR_SET_NO_NEW_PRIVS = 0x26
|
const PR_SET_NO_NEW_PRIVS = 0x26
|
||||||
|
|
||||||
func (l *linuxStandardInit) Init() error {
|
func (l *linuxStandardInit) Init(s chan os.Signal) error {
|
||||||
ringname, keepperms, newperms := l.getSessionRingParams()
|
ringname, keepperms, newperms := l.getSessionRingParams()
|
||||||
|
|
||||||
// do not inherit the parent's session keyring
|
// do not inherit the parent's session keyring
|
||||||
|
@ -150,6 +152,18 @@ func (l *linuxStandardInit) Init() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// check for the arg before waiting to make sure it exists and it is returned
|
||||||
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
|
// as a create time error
|
||||||
|
name, err := exec.LookPath(l.config.Args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// close the pipe to signal that we have completed our init
|
||||||
|
l.pipe.Close()
|
||||||
|
// wait for the signal to exec the users process
|
||||||
|
<-s
|
||||||
|
// clean up the signal handler
|
||||||
|
signal.Stop(s)
|
||||||
|
close(s)
|
||||||
|
return syscall.Exec(name, l.config.Args[0:], os.Environ())
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,11 +110,11 @@ func (r *runningState) status() Status {
|
||||||
func (r *runningState) transition(s containerState) error {
|
func (r *runningState) transition(s containerState) error {
|
||||||
switch s.(type) {
|
switch s.(type) {
|
||||||
case *stoppedState:
|
case *stoppedState:
|
||||||
running, err := r.c.isRunning()
|
t, err := r.c.runType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if running {
|
if t == Running {
|
||||||
return newGenericError(fmt.Errorf("container still running"), ContainerNotStopped)
|
return newGenericError(fmt.Errorf("container still running"), ContainerNotStopped)
|
||||||
}
|
}
|
||||||
r.c.state = s
|
r.c.state = s
|
||||||
|
@ -129,16 +129,38 @@ func (r *runningState) transition(s containerState) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *runningState) destroy() error {
|
func (r *runningState) destroy() error {
|
||||||
running, err := r.c.isRunning()
|
t, err := r.c.runType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if running {
|
if t == Running {
|
||||||
return newGenericError(fmt.Errorf("container is not destroyed"), ContainerNotStopped)
|
return newGenericError(fmt.Errorf("container is not destroyed"), ContainerNotStopped)
|
||||||
}
|
}
|
||||||
return destroy(r.c)
|
return destroy(r.c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type initializedState struct {
|
||||||
|
c *linuxContainer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initializedState) status() Status {
|
||||||
|
return Initialized
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initializedState) transition(s containerState) error {
|
||||||
|
switch s.(type) {
|
||||||
|
case *runningState:
|
||||||
|
i.c.state = s
|
||||||
|
case *initializedState:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return newStateTransitionError(i, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *initializedState) destroy() error {
|
||||||
|
return destroy(i.c)
|
||||||
|
}
|
||||||
|
|
||||||
// pausedState represents a container that is currently pause. It cannot be destroyed in a
|
// pausedState represents a container that is currently pause. It cannot be destroyed in a
|
||||||
// paused state and must transition back to running first.
|
// paused state and must transition back to running first.
|
||||||
type pausedState struct {
|
type pausedState struct {
|
||||||
|
@ -161,11 +183,11 @@ func (p *pausedState) transition(s containerState) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pausedState) destroy() error {
|
func (p *pausedState) destroy() error {
|
||||||
isRunning, err := p.c.isRunning()
|
t, err := p.c.runType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !isRunning {
|
if t != Running && t != Created {
|
||||||
if err := p.c.cgroupManager.Freeze(configs.Thawed); err != nil {
|
if err := p.c.cgroupManager.Freeze(configs.Thawed); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
2
main.go
2
main.go
|
@ -86,6 +86,7 @@ func main() {
|
||||||
}
|
}
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
checkpointCommand,
|
checkpointCommand,
|
||||||
|
createCommand,
|
||||||
deleteCommand,
|
deleteCommand,
|
||||||
eventsCommand,
|
eventsCommand,
|
||||||
execCommand,
|
execCommand,
|
||||||
|
@ -98,6 +99,7 @@ func main() {
|
||||||
resumeCommand,
|
resumeCommand,
|
||||||
runCommand,
|
runCommand,
|
||||||
specCommand,
|
specCommand,
|
||||||
|
startCommand,
|
||||||
stateCommand,
|
stateCommand,
|
||||||
updateCommand,
|
updateCommand,
|
||||||
}
|
}
|
||||||
|
|
8
run.go
8
run.go
|
@ -68,17 +68,14 @@ command(s) that get executed on start, edit the args parameter of the spec. See
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
notifySocket := os.Getenv("NOTIFY_SOCKET")
|
notifySocket := os.Getenv("NOTIFY_SOCKET")
|
||||||
if notifySocket != "" {
|
if notifySocket != "" {
|
||||||
setupSdNotify(spec, notifySocket)
|
setupSdNotify(spec, notifySocket)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Geteuid() != 0 {
|
if os.Geteuid() != 0 {
|
||||||
return fmt.Errorf("runc should be run as root")
|
return fmt.Errorf("runc should be run as root")
|
||||||
}
|
}
|
||||||
|
status, err := startContainer(context, spec, false)
|
||||||
status, err := startContainer(context, spec)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// exit with the container's exit status so any external supervisor is
|
// exit with the container's exit status so any external supervisor is
|
||||||
// notified of the exit with the correct exit status.
|
// notified of the exit with the correct exit status.
|
||||||
|
@ -88,7 +85,7 @@ command(s) that get executed on start, edit the args parameter of the spec. See
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func startContainer(context *cli.Context, spec *specs.Spec) (int, error) {
|
func startContainer(context *cli.Context, spec *specs.Spec, create bool) (int, error) {
|
||||||
id := context.Args().First()
|
id := context.Args().First()
|
||||||
if id == "" {
|
if id == "" {
|
||||||
return -1, errEmptyID
|
return -1, errEmptyID
|
||||||
|
@ -111,6 +108,7 @@ func startContainer(context *cli.Context, spec *specs.Spec) (int, error) {
|
||||||
console: context.String("console"),
|
console: context.String("console"),
|
||||||
detach: detach,
|
detach: detach,
|
||||||
pidFile: context.String("pid-file"),
|
pidFile: context.String("pid-file"),
|
||||||
|
create: create,
|
||||||
}
|
}
|
||||||
return r.run(&spec.Process)
|
return r.run(&spec.Process)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var startCommand = cli.Command{
|
||||||
|
Name: "start",
|
||||||
|
Usage: "start signals a created container to execute the users defined process",
|
||||||
|
ArgsUsage: `<container-id>
|
||||||
|
|
||||||
|
Where "<container-id>" is your name for the instance of the container that you
|
||||||
|
are starting. The name you provide for the container instance must be unique on
|
||||||
|
your host.`,
|
||||||
|
Description: `The start command signals the container to start the user's defined process.`,
|
||||||
|
Action: func(context *cli.Context) {
|
||||||
|
container, err := getContainer(context)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
if err := container.Signal(syscall.SIGCONT); err != nil {
|
||||||
|
fatal(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
|
@ -198,6 +198,7 @@ type runner struct {
|
||||||
pidFile string
|
pidFile string
|
||||||
console string
|
console string
|
||||||
container libcontainer.Container
|
container libcontainer.Container
|
||||||
|
create bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *runner) run(config *specs.Process) (int, error) {
|
func (r *runner) run(config *specs.Process) (int, error) {
|
||||||
|
@ -220,7 +221,7 @@ func (r *runner) run(config *specs.Process) (int, error) {
|
||||||
r.destroy()
|
r.destroy()
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
tty, err := setupIO(process, rootuid, rootgid, r.console, config.Terminal, r.detach)
|
tty, err := setupIO(process, rootuid, rootgid, r.console, config.Terminal, r.detach || r.create)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.destroy()
|
r.destroy()
|
||||||
return -1, err
|
return -1, err
|
||||||
|
@ -245,7 +246,15 @@ func (r *runner) run(config *specs.Process) (int, error) {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if r.detach {
|
if !r.create {
|
||||||
|
if err := process.Signal(syscall.SIGCONT); err != nil {
|
||||||
|
r.terminate(process)
|
||||||
|
r.destroy()
|
||||||
|
tty.Close()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.detach || r.create {
|
||||||
tty.Close()
|
tty.Close()
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue