mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-10 21:53:57 +08:00
Merge pull request #827 from crosbymichael/create-start
Implement create and start
This commit is contained in:
@@ -57,7 +57,7 @@ unittest: runctestimage
|
||||
docker run -e TESTFLAGS -ti --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_TEST_IMAGE) make localunittest
|
||||
|
||||
localunittest: all
|
||||
go test -tags "$(BUILDTAGS)" ${TESTFLAGS} -v ./...
|
||||
go test -timeout 3m -tags "$(BUILDTAGS)" ${TESTFLAGS} -v ./...
|
||||
|
||||
integration: runctestimage
|
||||
docker run -e TESTFLAGS -t --privileged --rm -v $(CURDIR):/go/src/$(PROJECT) $(RUNC_TEST_IMAGE) make localintegration
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
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) error {
|
||||
spec, err := setupSpec(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status, err := startContainer(context, spec, true)
|
||||
if err != nil {
|
||||
return 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)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -3,8 +3,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
@@ -19,7 +22,7 @@ Where "<container-id>" is the name for the instance of the container.
|
||||
|
||||
EXAMPLE:
|
||||
For example, if the container id is "ubuntu01" and runc list currently shows the
|
||||
status of "ubuntu01" as "destroyed" the following will delete resources held for
|
||||
status of "ubuntu01" as "stopped" the following will delete resources held for
|
||||
"ubuntu01" removing "ubuntu01" from the runc list of containers:
|
||||
|
||||
# runc delete ubuntu01`,
|
||||
@@ -36,7 +39,26 @@ status of "ubuntu01" as "destroyed" the following will delete resources held for
|
||||
}
|
||||
return nil
|
||||
}
|
||||
destroy(container)
|
||||
s, err := container.Status()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch s {
|
||||
case libcontainer.Stopped:
|
||||
destroy(container)
|
||||
case libcontainer.Created:
|
||||
container.Signal(syscall.SIGKILL)
|
||||
for i := 0; i < 100; i++ {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
if err := container.Signal(syscall.Signal(0)); err != nil {
|
||||
destroy(container)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("container init still running")
|
||||
default:
|
||||
return fmt.Errorf("cannot delete container that is not stopped: %s", s)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@@ -116,7 +116,8 @@ information is displayed once every 5 seconds.`,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status == libcontainer.Destroyed {
|
||||
if status == libcontainer.Stopped {
|
||||
fatalf("container with id %s is not running", container.ID())
|
||||
return fmt.Errorf("container with id %s is not running", container.ID())
|
||||
}
|
||||
var (
|
||||
|
||||
+24
-17
@@ -15,20 +15,16 @@ import (
|
||||
type Status int
|
||||
|
||||
const (
|
||||
// Created is the status that denotes the container exists but has not been run yet
|
||||
// Created is the status that denotes the container exists but has not been run yet.
|
||||
Created Status = iota
|
||||
|
||||
// Running is the status that denotes the container exists and is running.
|
||||
Running
|
||||
|
||||
// Pausing is the status that denotes the container exists, it is in the process of being paused.
|
||||
Pausing
|
||||
|
||||
// Paused is the status that denotes the container exists, but all its processes are paused.
|
||||
Paused
|
||||
|
||||
// Destroyed is the status that denotes the container does not exist.
|
||||
Destroyed
|
||||
// Stopped is the status that denotes the container does not have a created or running process.
|
||||
Stopped
|
||||
)
|
||||
|
||||
func (s Status) String() string {
|
||||
@@ -41,8 +37,8 @@ func (s Status) String() string {
|
||||
return "pausing"
|
||||
case Paused:
|
||||
return "paused"
|
||||
case Destroyed:
|
||||
return "destroyed"
|
||||
case Stopped:
|
||||
return "stopped"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
@@ -80,13 +76,13 @@ type BaseContainer interface {
|
||||
//
|
||||
// errors:
|
||||
// ContainerDestroyed - Container no longer exists,
|
||||
// Systemerror - System error.
|
||||
// SystemError - System error.
|
||||
Status() (Status, error)
|
||||
|
||||
// State returns the current container's state information.
|
||||
//
|
||||
// errors:
|
||||
// Systemerror - System error.
|
||||
// SystemError - System error.
|
||||
State() (*State, error)
|
||||
|
||||
// Returns the current config of the container.
|
||||
@@ -96,7 +92,7 @@ type BaseContainer interface {
|
||||
//
|
||||
// errors:
|
||||
// ContainerDestroyed - Container no longer exists,
|
||||
// Systemerror - System error.
|
||||
// SystemError - System error.
|
||||
//
|
||||
// Some of the returned PIDs may no longer refer to processes in the Container, unless
|
||||
// the Container state is PAUSED in which case every PID in the slice is valid.
|
||||
@@ -106,7 +102,7 @@ type BaseContainer interface {
|
||||
//
|
||||
// errors:
|
||||
// ContainerDestroyed - Container no longer exists,
|
||||
// Systemerror - System error.
|
||||
// SystemError - System error.
|
||||
Stats() (*Stats, error)
|
||||
|
||||
// Set resources of container as configured
|
||||
@@ -114,7 +110,7 @@ type BaseContainer interface {
|
||||
// We can use this to change resources when containers are running.
|
||||
//
|
||||
// errors:
|
||||
// Systemerror - System error.
|
||||
// SystemError - System error.
|
||||
Set(config configs.Config) error
|
||||
|
||||
// Start a process inside the container. Returns error if process fails to
|
||||
@@ -124,21 +120,32 @@ type BaseContainer interface {
|
||||
// ContainerDestroyed - Container no longer exists,
|
||||
// ConfigInvalid - config is invalid,
|
||||
// ContainerPaused - Container is paused,
|
||||
// Systemerror - System error.
|
||||
// SystemError - System error.
|
||||
Start(process *Process) (err error)
|
||||
|
||||
// Run immediatly starts the process inside the conatiner. Returns error if process
|
||||
// fails to start. It does not block waiting for a SIGCONT after start returns but
|
||||
// sends the signal when the process has completed.
|
||||
//
|
||||
// errors:
|
||||
// ContainerDestroyed - Container no longer exists,
|
||||
// ConfigInvalid - config is invalid,
|
||||
// ContainerPaused - Container is paused,
|
||||
// SystemError - System error.
|
||||
Run(process *Process) (err error)
|
||||
|
||||
// Destroys the container after killing all running processes.
|
||||
//
|
||||
// Any event registrations are removed before the container is destroyed.
|
||||
// No error is returned if the container is already destroyed.
|
||||
//
|
||||
// errors:
|
||||
// Systemerror - System error.
|
||||
// SystemError - System error.
|
||||
Destroy() error
|
||||
|
||||
// Signal sends the provided signal code to the container's initial process.
|
||||
//
|
||||
// errors:
|
||||
// Systemerror - System error.
|
||||
// SystemError - System error.
|
||||
Signal(s os.Signal) error
|
||||
}
|
||||
|
||||
@@ -29,6 +29,10 @@ import (
|
||||
|
||||
const stdioFdCount = 3
|
||||
|
||||
// InitContinueSignal is used to signal the container init process to
|
||||
// start the users specified process after the container create has finished.
|
||||
const InitContinueSignal = syscall.SIGCONT
|
||||
|
||||
type linuxContainer struct {
|
||||
id string
|
||||
root string
|
||||
@@ -181,8 +185,28 @@ func (c *linuxContainer) Start(process *Process) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
doInit := status == Destroyed
|
||||
parent, err := c.newParentProcess(process, doInit)
|
||||
return c.start(process, status == Stopped)
|
||||
}
|
||||
|
||||
func (c *linuxContainer) Run(process *Process) error {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
status, err := c.currentStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isInit := status == Stopped
|
||||
if err := c.start(process, isInit); err != nil {
|
||||
return err
|
||||
}
|
||||
if isInit {
|
||||
return process.ops.signal(InitContinueSignal)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *linuxContainer) start(process *Process, isInit bool) error {
|
||||
parent, err := c.newParentProcess(process, isInit)
|
||||
if err != nil {
|
||||
return newSystemErrorWithCause(err, "creating new parent process")
|
||||
}
|
||||
@@ -195,11 +219,13 @@ func (c *linuxContainer) Start(process *Process) error {
|
||||
}
|
||||
// generate a timestamp indicating when the container was started
|
||||
c.created = time.Now().UTC()
|
||||
|
||||
c.state = &runningState{
|
||||
c: c,
|
||||
}
|
||||
if doInit {
|
||||
if isInit {
|
||||
c.state = &createdState{
|
||||
c: c,
|
||||
}
|
||||
if err := c.updateState(parent); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -371,15 +397,16 @@ func (c *linuxContainer) Pause() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status != Running {
|
||||
return newGenericError(fmt.Errorf("container not running"), ContainerNotRunning)
|
||||
switch status {
|
||||
case Running, Created:
|
||||
if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.state.transition(&pausedState{
|
||||
c: c,
|
||||
})
|
||||
}
|
||||
if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.state.transition(&pausedState{
|
||||
c: c,
|
||||
})
|
||||
return newGenericError(fmt.Errorf("container not running: %s", status), ContainerNotRunning)
|
||||
}
|
||||
|
||||
func (c *linuxContainer) Resume() error {
|
||||
@@ -1034,31 +1061,45 @@ func (c *linuxContainer) refreshState() error {
|
||||
if paused {
|
||||
return c.state.transition(&pausedState{c: c})
|
||||
}
|
||||
running, err := c.isRunning()
|
||||
t, err := c.runType()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if running {
|
||||
switch t {
|
||||
case Created:
|
||||
return c.state.transition(&createdState{c: c})
|
||||
case Running:
|
||||
return c.state.transition(&runningState{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 {
|
||||
return false, nil
|
||||
return Stopped, nil
|
||||
}
|
||||
pid := c.initProcess.pid()
|
||||
// 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 {
|
||||
// It means the process does not exist anymore, could happen when the
|
||||
// process exited just when we call the function, we should not return
|
||||
// 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")
|
||||
if bytes.Contains(environ, check) {
|
||||
return Created, nil
|
||||
}
|
||||
return Running, nil
|
||||
}
|
||||
|
||||
func (c *linuxContainer) isPaused() (bool, error) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
@@ -205,7 +206,7 @@ func (l *LinuxFactory) Load(id string) (Container, error) {
|
||||
root: containerRoot,
|
||||
created: state.Created,
|
||||
}
|
||||
c.state = &createdState{c: c, s: Created}
|
||||
c.state = &loadedState{c: c}
|
||||
if err := c.refreshState(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -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
|
||||
// This is a low level implementation detail of the reexec and should not be consumed externally
|
||||
func (l *LinuxFactory) StartInitialization() (err error) {
|
||||
// start the signal handler as soon as we can
|
||||
s := make(chan os.Signal, 1)
|
||||
signal.Notify(s, InitContinueSignal)
|
||||
fdStr := os.Getenv("_LIBCONTAINER_INITPIPE")
|
||||
pipefd, err := strconv.Atoi(fdStr)
|
||||
if err != nil {
|
||||
@@ -260,7 +264,7 @@ func (l *LinuxFactory) StartInitialization() (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.Init()
|
||||
return i.Init(s)
|
||||
}
|
||||
|
||||
func (l *LinuxFactory) loadState(root string) (*State, error) {
|
||||
|
||||
@@ -61,7 +61,7 @@ type initConfig struct {
|
||||
}
|
||||
|
||||
type initer interface {
|
||||
Init() error
|
||||
Init(s chan os.Signal) error
|
||||
}
|
||||
|
||||
func newContainerInit(t initType, pipe *os.File) (initer, error) {
|
||||
|
||||
@@ -89,7 +89,7 @@ func TestCheckpoint(t *testing.T) {
|
||||
Stdout: &stdout,
|
||||
}
|
||||
|
||||
err = container.Start(&pconfig)
|
||||
err = container.Run(&pconfig)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
if err != nil {
|
||||
|
||||
@@ -241,7 +241,7 @@ func TestEnter(t *testing.T) {
|
||||
Stdin: stdinR,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Start(&pconfig)
|
||||
err = container.Run(&pconfig)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
@@ -259,7 +259,7 @@ func TestEnter(t *testing.T) {
|
||||
pconfig2.Stdin = stdinR2
|
||||
pconfig2.Stdout = &stdout2
|
||||
|
||||
err = container.Start(&pconfig2)
|
||||
err = container.Run(&pconfig2)
|
||||
stdinR2.Close()
|
||||
defer stdinW2.Close()
|
||||
ok(t, err)
|
||||
@@ -330,7 +330,7 @@ func TestProcessEnv(t *testing.T) {
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Start(&pconfig)
|
||||
err = container.Run(&pconfig)
|
||||
ok(t, err)
|
||||
|
||||
// Wait for process
|
||||
@@ -378,7 +378,7 @@ func TestProcessCaps(t *testing.T) {
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Start(&pconfig)
|
||||
err = container.Run(&pconfig)
|
||||
ok(t, err)
|
||||
|
||||
// Wait for process
|
||||
@@ -448,7 +448,7 @@ func TestAdditionalGroups(t *testing.T) {
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Start(&pconfig)
|
||||
err = container.Run(&pconfig)
|
||||
ok(t, err)
|
||||
|
||||
// Wait for process
|
||||
@@ -508,7 +508,7 @@ func testFreeze(t *testing.T, systemd bool) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
}
|
||||
err = container.Start(pconfig)
|
||||
err = container.Run(pconfig)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
@@ -719,7 +719,7 @@ func TestContainerState(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
}
|
||||
err = container.Start(p)
|
||||
err = container.Run(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -772,7 +772,7 @@ func TestPassExtraFiles(t *testing.T) {
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Start(&process)
|
||||
err = container.Run(&process)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -853,7 +853,7 @@ func TestMountCmds(t *testing.T) {
|
||||
Args: []string{"sh", "-c", "env"},
|
||||
Env: standardEnvironment,
|
||||
}
|
||||
err = container.Start(&pconfig)
|
||||
err = container.Run(&pconfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -902,7 +902,7 @@ func TestSysctl(t *testing.T) {
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Start(&pconfig)
|
||||
err = container.Run(&pconfig)
|
||||
ok(t, err)
|
||||
|
||||
// Wait for process
|
||||
@@ -1042,7 +1042,7 @@ func TestOomScoreAdj(t *testing.T) {
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Start(&pconfig)
|
||||
err = container.Run(&pconfig)
|
||||
ok(t, err)
|
||||
|
||||
// Wait for process
|
||||
@@ -1114,7 +1114,7 @@ func TestHook(t *testing.T) {
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Start(&pconfig)
|
||||
err = container.Run(&pconfig)
|
||||
ok(t, err)
|
||||
|
||||
// Wait for process
|
||||
@@ -1231,7 +1231,7 @@ func TestRootfsPropagationSlaveMount(t *testing.T) {
|
||||
Stdin: stdinR,
|
||||
}
|
||||
|
||||
err = container.Start(pconfig)
|
||||
err = container.Run(pconfig)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
@@ -1260,7 +1260,7 @@ func TestRootfsPropagationSlaveMount(t *testing.T) {
|
||||
Stdout: &stdout2,
|
||||
}
|
||||
|
||||
err = container.Start(pconfig2)
|
||||
err = container.Run(pconfig2)
|
||||
stdinR2.Close()
|
||||
defer stdinW2.Close()
|
||||
ok(t, err)
|
||||
@@ -1348,7 +1348,7 @@ func TestRootfsPropagationSharedMount(t *testing.T) {
|
||||
Stdin: stdinR,
|
||||
}
|
||||
|
||||
err = container.Start(pconfig)
|
||||
err = container.Run(pconfig)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
@@ -1380,7 +1380,7 @@ func TestRootfsPropagationSharedMount(t *testing.T) {
|
||||
Capabilities: processCaps,
|
||||
}
|
||||
|
||||
err = container.Start(pconfig2)
|
||||
err = container.Run(pconfig2)
|
||||
stdinR2.Close()
|
||||
defer stdinW2.Close()
|
||||
ok(t, err)
|
||||
@@ -1452,7 +1452,7 @@ func TestInitJoinPID(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR1,
|
||||
}
|
||||
err = container1.Start(init1)
|
||||
err = container1.Run(init1)
|
||||
stdinR1.Close()
|
||||
defer stdinW1.Close()
|
||||
ok(t, err)
|
||||
@@ -1462,7 +1462,7 @@ func TestInitJoinPID(t *testing.T) {
|
||||
ok(t, err)
|
||||
pidns1 := state1.NamespacePaths[configs.NEWPID]
|
||||
|
||||
// Start a container inside the existing pidns but with different cgroups
|
||||
// Run a container inside the existing pidns but with different cgroups
|
||||
config2 := newTemplateConfig(rootfs)
|
||||
config2.Namespaces.Add(configs.NEWPID, pidns1)
|
||||
config2.Cgroups.Path = "integration/test2"
|
||||
@@ -1478,7 +1478,7 @@ func TestInitJoinPID(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR2,
|
||||
}
|
||||
err = container2.Start(init2)
|
||||
err = container2.Run(init2)
|
||||
stdinR2.Close()
|
||||
defer stdinW2.Close()
|
||||
ok(t, err)
|
||||
@@ -1508,7 +1508,7 @@ func TestInitJoinPID(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdout: buffers.Stdout,
|
||||
}
|
||||
err = container1.Start(ps)
|
||||
err = container1.Run(ps)
|
||||
ok(t, err)
|
||||
waitProcess(ps, t)
|
||||
|
||||
@@ -1557,7 +1557,7 @@ func TestInitJoinNetworkAndUser(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR1,
|
||||
}
|
||||
err = container1.Start(init1)
|
||||
err = container1.Run(init1)
|
||||
stdinR1.Close()
|
||||
defer stdinW1.Close()
|
||||
ok(t, err)
|
||||
@@ -1568,7 +1568,7 @@ func TestInitJoinNetworkAndUser(t *testing.T) {
|
||||
netns1 := state1.NamespacePaths[configs.NEWNET]
|
||||
userns1 := state1.NamespacePaths[configs.NEWUSER]
|
||||
|
||||
// Start a container inside the existing pidns but with different cgroups
|
||||
// Run a container inside the existing pidns but with different cgroups
|
||||
rootfs2, err := newRootfs()
|
||||
ok(t, err)
|
||||
defer remove(rootfs2)
|
||||
@@ -1591,7 +1591,7 @@ func TestInitJoinNetworkAndUser(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR2,
|
||||
}
|
||||
err = container2.Start(init2)
|
||||
err = container2.Run(init2)
|
||||
stdinR2.Close()
|
||||
defer stdinW2.Close()
|
||||
ok(t, err)
|
||||
|
||||
@@ -36,7 +36,7 @@ func TestExecIn(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
}
|
||||
err = container.Start(process)
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
@@ -51,7 +51,7 @@ func TestExecIn(t *testing.T) {
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
|
||||
err = container.Start(ps)
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
waitProcess(ps, t)
|
||||
stdinW.Close()
|
||||
@@ -103,7 +103,7 @@ func testExecInRlimit(t *testing.T, userns bool) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
}
|
||||
err = container.Start(process)
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
@@ -121,7 +121,7 @@ func testExecInRlimit(t *testing.T, userns bool) {
|
||||
{Type: syscall.RLIMIT_NOFILE, Hard: 1026, Soft: 1026},
|
||||
},
|
||||
}
|
||||
err = container.Start(ps)
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
waitProcess(ps, t)
|
||||
|
||||
@@ -155,7 +155,7 @@ func TestExecInError(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
}
|
||||
err = container.Start(process)
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer func() {
|
||||
stdinW.Close()
|
||||
@@ -173,7 +173,7 @@ func TestExecInError(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdout: &out,
|
||||
}
|
||||
err = container.Start(unexistent)
|
||||
err = container.Run(unexistent)
|
||||
if err == nil {
|
||||
t.Fatal("Should be an error")
|
||||
}
|
||||
@@ -207,7 +207,7 @@ func TestExecInTTY(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
}
|
||||
err = container.Start(process)
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
@@ -225,7 +225,7 @@ func TestExecInTTY(t *testing.T) {
|
||||
close(copy)
|
||||
}()
|
||||
ok(t, err)
|
||||
err = container.Start(ps)
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
select {
|
||||
case <-time.After(5 * time.Second):
|
||||
@@ -264,7 +264,7 @@ func TestExecInEnvironment(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
}
|
||||
err = container.Start(process)
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
@@ -283,7 +283,7 @@ func TestExecInEnvironment(t *testing.T) {
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
err = container.Start(process2)
|
||||
err = container.Run(process2)
|
||||
ok(t, err)
|
||||
waitProcess(process2, t)
|
||||
|
||||
@@ -328,7 +328,7 @@ func TestExecinPassExtraFiles(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
}
|
||||
err = container.Start(process)
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
if err != nil {
|
||||
@@ -346,7 +346,7 @@ func TestExecinPassExtraFiles(t *testing.T) {
|
||||
Stdin: nil,
|
||||
Stdout: &stdout,
|
||||
}
|
||||
err = container.Start(inprocess)
|
||||
err = container.Run(inprocess)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -401,7 +401,7 @@ func TestExecInOomScoreAdj(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
}
|
||||
err = container.Start(process)
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
@@ -415,7 +415,7 @@ func TestExecInOomScoreAdj(t *testing.T) {
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
err = container.Start(ps)
|
||||
err = container.Run(ps)
|
||||
ok(t, err)
|
||||
waitProcess(ps, t)
|
||||
|
||||
@@ -456,7 +456,7 @@ func TestExecInUserns(t *testing.T) {
|
||||
Env: standardEnvironment,
|
||||
Stdin: stdinR,
|
||||
}
|
||||
err = container.Start(process)
|
||||
err = container.Run(process)
|
||||
stdinR.Close()
|
||||
defer stdinW.Close()
|
||||
ok(t, err)
|
||||
@@ -476,7 +476,7 @@ func TestExecInUserns(t *testing.T) {
|
||||
Stdout: buffers.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
err = container.Start(process2)
|
||||
err = container.Run(process2)
|
||||
ok(t, err)
|
||||
waitProcess(process2, t)
|
||||
stdinW.Close()
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestSeccompDenyGetcwd(t *testing.T) {
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
|
||||
err = container.Start(pwd)
|
||||
err = container.Run(pwd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -125,7 +125,7 @@ func TestSeccompPermitWriteConditional(t *testing.T) {
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
|
||||
err = container.Start(dmesg)
|
||||
err = container.Run(dmesg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -186,7 +186,7 @@ func TestSeccompDenyWriteConditional(t *testing.T) {
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
|
||||
err = container.Start(dmesg)
|
||||
err = container.Run(dmesg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -89,12 +89,15 @@ func newTemplateConfig(rootfs string) *configs.Config {
|
||||
Data: "mode=1777,size=65536k",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
{
|
||||
Source: "mqueue",
|
||||
Destination: "/dev/mqueue",
|
||||
Device: "mqueue",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
/*
|
||||
CI is broken on the debian based kernels with this
|
||||
{
|
||||
Source: "mqueue",
|
||||
Destination: "/dev/mqueue",
|
||||
Device: "mqueue",
|
||||
Flags: defaultMountFlags,
|
||||
},
|
||||
*/
|
||||
{
|
||||
Source: "sysfs",
|
||||
Destination: "/sys",
|
||||
|
||||
@@ -123,7 +123,7 @@ func runContainer(config *configs.Config, console string, args ...string) (buffe
|
||||
Stderr: buffers.Stderr,
|
||||
}
|
||||
|
||||
err = container.Start(process)
|
||||
err = container.Run(process)
|
||||
if err != nil {
|
||||
return buffers, -1, err
|
||||
}
|
||||
|
||||
@@ -221,6 +221,7 @@ func (p *initProcess) execSetns() error {
|
||||
return err
|
||||
}
|
||||
p.cmd.Process = process
|
||||
p.process.ops = p
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ package libcontainer
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/apparmor"
|
||||
"github.com/opencontainers/runc/libcontainer/keys"
|
||||
@@ -23,7 +24,7 @@ func (l *linuxSetnsInit) getSessionRingName() string {
|
||||
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
|
||||
if _, err := keyctl.JoinSessionKeyring(l.getSessionRingName()); err != nil {
|
||||
return err
|
||||
@@ -47,5 +48,7 @@ func (l *linuxSetnsInit) Init() error {
|
||||
if err := label.SetProcessLabel(l.config.ProcessLabel); err != nil {
|
||||
return err
|
||||
}
|
||||
signal.Stop(s)
|
||||
close(s)
|
||||
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/apparmor"
|
||||
@@ -17,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
type linuxStandardInit struct {
|
||||
pipe io.ReadWriter
|
||||
pipe io.ReadWriteCloser
|
||||
parentPid int
|
||||
config *initConfig
|
||||
}
|
||||
@@ -42,7 +44,7 @@ func (l *linuxStandardInit) getSessionRingParams() (string, uint32, uint32) {
|
||||
// the kernel
|
||||
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()
|
||||
|
||||
// do not inherit the parent's session keyring
|
||||
@@ -150,6 +152,18 @@ func (l *linuxStandardInit) Init() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return system.Execv(l.config.Args[0], l.config.Args[0:], os.Environ())
|
||||
// check for the arg before waiting to make sure it exists and it is returned
|
||||
// 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())
|
||||
}
|
||||
|
||||
+37
-12
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
@@ -77,7 +78,7 @@ type stoppedState struct {
|
||||
}
|
||||
|
||||
func (b *stoppedState) status() Status {
|
||||
return Destroyed
|
||||
return Stopped
|
||||
}
|
||||
|
||||
func (b *stoppedState) transition(s containerState) error {
|
||||
@@ -110,11 +111,11 @@ func (r *runningState) status() Status {
|
||||
func (r *runningState) transition(s containerState) error {
|
||||
switch s.(type) {
|
||||
case *stoppedState:
|
||||
running, err := r.c.isRunning()
|
||||
t, err := r.c.runType()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if running {
|
||||
if t == Running {
|
||||
return newGenericError(fmt.Errorf("container still running"), ContainerNotStopped)
|
||||
}
|
||||
r.c.state = s
|
||||
@@ -129,16 +130,40 @@ func (r *runningState) transition(s containerState) error {
|
||||
}
|
||||
|
||||
func (r *runningState) destroy() error {
|
||||
running, err := r.c.isRunning()
|
||||
t, err := r.c.runType()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if running {
|
||||
if t == Running {
|
||||
return newGenericError(fmt.Errorf("container is not destroyed"), ContainerNotStopped)
|
||||
}
|
||||
return destroy(r.c)
|
||||
}
|
||||
|
||||
type createdState struct {
|
||||
c *linuxContainer
|
||||
}
|
||||
|
||||
func (i *createdState) status() Status {
|
||||
return Created
|
||||
}
|
||||
|
||||
func (i *createdState) transition(s containerState) error {
|
||||
switch s.(type) {
|
||||
case *runningState, *pausedState, *stoppedState:
|
||||
i.c.state = s
|
||||
return nil
|
||||
case *createdState:
|
||||
return nil
|
||||
}
|
||||
return newStateTransitionError(i, s)
|
||||
}
|
||||
|
||||
func (i *createdState) destroy() error {
|
||||
i.c.initProcess.signal(syscall.SIGKILL)
|
||||
return destroy(i.c)
|
||||
}
|
||||
|
||||
// pausedState represents a container that is currently pause. It cannot be destroyed in a
|
||||
// paused state and must transition back to running first.
|
||||
type pausedState struct {
|
||||
@@ -161,11 +186,11 @@ func (p *pausedState) transition(s containerState) error {
|
||||
}
|
||||
|
||||
func (p *pausedState) destroy() error {
|
||||
isRunning, err := p.c.isRunning()
|
||||
t, err := p.c.runType()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isRunning {
|
||||
if t != Running && t != Created {
|
||||
if err := p.c.cgroupManager.Freeze(configs.Thawed); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -204,23 +229,23 @@ func (r *restoredState) destroy() error {
|
||||
return destroy(r.c)
|
||||
}
|
||||
|
||||
// createdState is used whenever a container is restored, loaded, or setting additional
|
||||
// loadedState is used whenever a container is restored, loaded, or setting additional
|
||||
// processes inside and it should not be destroyed when it is exiting.
|
||||
type createdState struct {
|
||||
type loadedState struct {
|
||||
c *linuxContainer
|
||||
s Status
|
||||
}
|
||||
|
||||
func (n *createdState) status() Status {
|
||||
func (n *loadedState) status() Status {
|
||||
return n.s
|
||||
}
|
||||
|
||||
func (n *createdState) transition(s containerState) error {
|
||||
func (n *loadedState) transition(s containerState) error {
|
||||
n.c.state = s
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *createdState) destroy() error {
|
||||
func (n *loadedState) destroy() error {
|
||||
if err := n.c.refreshState(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@ import "testing"
|
||||
|
||||
func TestStateStatus(t *testing.T) {
|
||||
states := map[containerState]Status{
|
||||
&stoppedState{}: Destroyed,
|
||||
&stoppedState{}: Stopped,
|
||||
&runningState{}: Running,
|
||||
&restoredState{}: Running,
|
||||
&pausedState{}: Paused,
|
||||
&createdState{}: Created,
|
||||
}
|
||||
for s, status := range states {
|
||||
if s.status() != status {
|
||||
|
||||
@@ -91,6 +91,7 @@ func main() {
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
checkpointCommand,
|
||||
createCommand,
|
||||
deleteCommand,
|
||||
eventsCommand,
|
||||
execCommand,
|
||||
@@ -101,6 +102,7 @@ func main() {
|
||||
psCommand,
|
||||
restoreCommand,
|
||||
resumeCommand,
|
||||
runCommand,
|
||||
specCommand,
|
||||
startCommand,
|
||||
stateCommand,
|
||||
|
||||
+29
-1
@@ -2,4 +2,32 @@
|
||||
|
||||
package main
|
||||
|
||||
import _ "github.com/opencontainers/runc/libcontainer/nsenter"
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if len(os.Args) > 1 && os.Args[1] == "init" {
|
||||
runtime.GOMAXPROCS(1)
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
}
|
||||
|
||||
var initCommand = cli.Command{
|
||||
Name: "init",
|
||||
Usage: `initialize the namespaces and launch the process (do not call it outside of runc)`,
|
||||
Action: func(context *cli.Context) {
|
||||
factory, _ := libcontainer.New("")
|
||||
if err := factory.StartInitialization(); err != nil {
|
||||
// as the error is sent back to the parent there is no need to log
|
||||
// or write it to stderr because the parent process will handle this
|
||||
os.Exit(1)
|
||||
}
|
||||
panic("libcontainer: container init failed to exec")
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
// default action is to start a container
|
||||
var runCommand = cli.Command{
|
||||
Name: "run",
|
||||
Usage: "create and run 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 run 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.BoolFlag{
|
||||
Name: "detach, d",
|
||||
Usage: "detach from the container's process",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pid-file",
|
||||
Value: "",
|
||||
Usage: "specify the file to write the process id to",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "no-subreaper",
|
||||
Usage: "disable the use of the subreaper used to reap reparented processes",
|
||||
},
|
||||
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) error {
|
||||
spec, err := setupSpec(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status, err := startContainer(context, spec, false)
|
||||
if err == nil {
|
||||
// exit with the container's exit status so any external supervisor is
|
||||
// notified of the exit with the correct exit status.
|
||||
os.Exit(status)
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.5.3
|
||||
FROM golang:1.6.2
|
||||
|
||||
# libseccomp in jessie is not _quite_ new enough -- need backports version
|
||||
RUN echo 'deb http://httpredir.debian.org/debian jessie-backports main' > /etc/apt/sources.list.d/backports.list
|
||||
|
||||
@@ -1,139 +1,39 @@
|
||||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/coreos/go-systemd/activation"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// default action is to start a container
|
||||
var startCommand = cli.Command{
|
||||
Name: "start",
|
||||
Usage: "create and run a container",
|
||||
Usage: "start signals a created container to execute the user 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 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.BoolFlag{
|
||||
Name: "detach,d",
|
||||
Usage: "detach from the container's process",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pid-file",
|
||||
Value: "",
|
||||
Usage: "specify the file to write the process id to",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "no-subreaper",
|
||||
Usage: "disable the use of the subreaper used to reap reparented processes",
|
||||
},
|
||||
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",
|
||||
},
|
||||
},
|
||||
Description: `The start command signals the container to start the user's defined process.`,
|
||||
Action: func(context *cli.Context) error {
|
||||
bundle := context.String("bundle")
|
||||
if bundle != "" {
|
||||
if err := os.Chdir(bundle); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
spec, err := loadSpec(specConfig)
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notifySocket := os.Getenv("NOTIFY_SOCKET")
|
||||
if notifySocket != "" {
|
||||
setupSdNotify(spec, notifySocket)
|
||||
status, err := container.Status()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if os.Geteuid() != 0 {
|
||||
return fmt.Errorf("runc should be run as root")
|
||||
switch status {
|
||||
case libcontainer.Created:
|
||||
return container.Signal(libcontainer.InitContinueSignal)
|
||||
case libcontainer.Stopped:
|
||||
return fmt.Errorf("cannot start a container that has run and stopped")
|
||||
case libcontainer.Running:
|
||||
return fmt.Errorf("cannot start an already running container")
|
||||
default:
|
||||
return fmt.Errorf("cannot start a container in the %s state", status)
|
||||
}
|
||||
|
||||
status, err := startContainer(context, spec)
|
||||
if err == nil {
|
||||
// exit with the container's exit status so any external supervisor is
|
||||
// notified of the exit with the correct exit status.
|
||||
os.Exit(status)
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if len(os.Args) > 1 && os.Args[1] == "init" {
|
||||
runtime.GOMAXPROCS(1)
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
}
|
||||
|
||||
var initCommand = cli.Command{
|
||||
Name: "init",
|
||||
Usage: `initialize the namespaces and launch the process (do not call it outside of runc)`,
|
||||
Action: func(context *cli.Context) error {
|
||||
factory, _ := libcontainer.New("")
|
||||
if err := factory.StartInitialization(); err != nil {
|
||||
// as the error is sent back to the parent there is no need to log
|
||||
// or write it to stderr because the parent process will handle this
|
||||
os.Exit(1)
|
||||
}
|
||||
panic("libcontainer: container init failed to exec")
|
||||
},
|
||||
}
|
||||
|
||||
func startContainer(context *cli.Context, spec *specs.Spec) (int, error) {
|
||||
id := context.Args().First()
|
||||
if id == "" {
|
||||
return -1, errEmptyID
|
||||
}
|
||||
container, err := createContainer(context, id, spec)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
detach := context.Bool("detach")
|
||||
// Support on-demand socket activation by passing file descriptors into the container init process.
|
||||
listenFDs := []*os.File{}
|
||||
if os.Getenv("LISTEN_FDS") != "" {
|
||||
listenFDs = activation.Files(false)
|
||||
}
|
||||
r := &runner{
|
||||
enableSubreaper: !context.Bool("no-subreaper"),
|
||||
shouldDestroy: true,
|
||||
container: container,
|
||||
listenFDs: listenFDs,
|
||||
console: context.String("console"),
|
||||
detach: detach,
|
||||
pidFile: context.String("pid-file"),
|
||||
}
|
||||
return r.run(&spec.Process)
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ EOF
|
||||
DATA=$(echo ${DATA} | sed 's/\n/\\n/g')
|
||||
sed -i "s/\(\"resources\": {\)/\1\n${DATA}/" ${BUSYBOX_BUNDLE}/config.json
|
||||
|
||||
# start a detached busybox to work with
|
||||
runc start -d --console /dev/pts/ptmx test_cgroups_kmem
|
||||
# run a detached busybox to work with
|
||||
runc run -d --console /dev/pts/ptmx test_cgroups_kmem
|
||||
[ "$status" -eq 0 ]
|
||||
wait_for_container 15 1 test_cgroups_kmem
|
||||
|
||||
@@ -64,8 +64,8 @@ EOF
|
||||
# Add cgroup path
|
||||
sed -i 's/\("linux": {\)/\1\n "cgroupsPath": "runc-cgroups-integration-test",/' ${BUSYBOX_BUNDLE}/config.json
|
||||
|
||||
# start a detached busybox to work with
|
||||
runc start -d --console /dev/pts/ptmx test_cgroups_kmem
|
||||
# run a detached busybox to work with
|
||||
runc run -d --console /dev/pts/ptmx test_cgroups_kmem
|
||||
[ "$status" -eq 0 ]
|
||||
wait_for_container 15 1 test_cgroups_kmem
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ function teardown() {
|
||||
sed -i 's/"sh"/"sh","-c","while :; do date; sleep 1; done"/' config.json
|
||||
|
||||
(
|
||||
# start busybox (not detached)
|
||||
runc start test_busybox
|
||||
# run busybox (not detached)
|
||||
runc run test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
) &
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
|
||||
function setup() {
|
||||
teardown_busybox
|
||||
setup_busybox
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
@test "runc create" {
|
||||
runc create --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox created
|
||||
|
||||
# start the command
|
||||
runc start test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox running
|
||||
}
|
||||
|
||||
@test "runc create exec" {
|
||||
runc create --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox created
|
||||
|
||||
runc exec test_busybox true
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# start the command
|
||||
runc start test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
testcontainer test_busybox running
|
||||
}
|
||||
@@ -12,15 +12,15 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "global --debug" {
|
||||
# start hello-world
|
||||
runc --debug start test_hello
|
||||
# run hello-world
|
||||
runc --debug run test_hello
|
||||
echo "${output}"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "global --debug to --log" {
|
||||
# start hello-world
|
||||
runc --log log.out --debug start test_hello
|
||||
# run hello-world
|
||||
runc --log log.out --debug run test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check output does not include debug info
|
||||
@@ -36,8 +36,8 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "global --debug to --log --log-format 'text'" {
|
||||
# start hello-world
|
||||
runc --log log.out --log-format "text" --debug start test_hello
|
||||
# run hello-world
|
||||
runc --log log.out --log-format "text" --debug run test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check output does not include debug info
|
||||
@@ -53,8 +53,8 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "global --debug to --log --log-format 'json'" {
|
||||
# start hello-world
|
||||
runc --log log.out --log-format "json" --debug start test_hello
|
||||
# run hello-world
|
||||
runc --log log.out --log-format "json" --debug run test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check output does not include debug info
|
||||
|
||||
@@ -12,8 +12,8 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "runc delete" {
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
@@ -23,7 +23,7 @@ function teardown() {
|
||||
|
||||
runc kill test_busybox KILL
|
||||
# wait for busybox to be in the destroyed state
|
||||
retry 10 1 eval "__runc state test_busybox | grep -q 'destroyed'"
|
||||
retry 10 1 eval "__runc state test_busybox | grep -q 'stopped'"
|
||||
|
||||
# delete test_busybox
|
||||
runc delete test_busybox
|
||||
|
||||
@@ -12,8 +12,8 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "events --stats" {
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
@@ -27,8 +27,8 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "events --interval default " {
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
@@ -54,8 +54,8 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "events --interval 1s " {
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
@@ -80,8 +80,8 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "events --interval 100ms " {
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
|
||||
@@ -12,8 +12,8 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "runc exec" {
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
wait_for_container 15 1 test_busybox
|
||||
@@ -25,8 +25,8 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "runc exec --pid-file" {
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
wait_for_container 15 1 test_busybox
|
||||
|
||||
@@ -64,7 +64,11 @@ load helpers
|
||||
runc start -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ start+ ]]
|
||||
|
||||
|
||||
runc run -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ run+ ]]
|
||||
|
||||
runc state -h
|
||||
[ "$status" -eq 0 ]
|
||||
[[ ${lines[1]} =~ runc\ state+ ]]
|
||||
|
||||
@@ -153,7 +153,7 @@ function teardown_running_container() {
|
||||
runc list
|
||||
if [[ "${output}" == *"$1"* ]]; then
|
||||
runc kill $1 KILL
|
||||
retry 10 1 eval "__runc state '$1' | grep -q 'destroyed'"
|
||||
retry 10 1 eval "__runc state '$1' | grep -q 'stopped'"
|
||||
runc delete $1
|
||||
fi
|
||||
}
|
||||
@@ -162,7 +162,7 @@ function teardown_running_container_inroot() {
|
||||
ROOT=$2 runc list
|
||||
if [[ "${output}" == *"$1"* ]]; then
|
||||
ROOT=$2 runc kill $1 KILL
|
||||
retry 10 1 eval "ROOT='$2' __runc state '$1' | grep -q 'destroyed'"
|
||||
retry 10 1 eval "ROOT='$2' __runc state '$1' | grep -q 'stopped'"
|
||||
ROOT=$2 runc delete $1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ function teardown() {
|
||||
|
||||
@test "kill detached busybox" {
|
||||
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
@@ -26,7 +26,7 @@ function teardown() {
|
||||
runc kill test_busybox KILL
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
retry 10 1 eval "__runc state test_busybox | grep -q 'destroyed'"
|
||||
retry 10 1 eval "__runc state test_busybox | grep -q 'stopped'"
|
||||
|
||||
runc delete test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
@@ -18,16 +18,16 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "list" {
|
||||
# start a few busyboxes detached
|
||||
ROOT=$HELLO_BUNDLE runc start -d --console /dev/pts/ptmx test_box1
|
||||
# run a few busyboxes detached
|
||||
ROOT=$HELLO_BUNDLE runc run -d --console /dev/pts/ptmx test_box1
|
||||
[ "$status" -eq 0 ]
|
||||
wait_for_container_inroot 15 1 test_box1 $HELLO_BUNDLE
|
||||
|
||||
ROOT=$HELLO_BUNDLE runc start -d --console /dev/pts/ptmx test_box2
|
||||
ROOT=$HELLO_BUNDLE runc run -d --console /dev/pts/ptmx test_box2
|
||||
[ "$status" -eq 0 ]
|
||||
wait_for_container_inroot 15 1 test_box2 $HELLO_BUNDLE
|
||||
|
||||
ROOT=$HELLO_BUNDLE runc start -d --console /dev/pts/ptmx test_box3
|
||||
ROOT=$HELLO_BUNDLE runc run -d --console /dev/pts/ptmx test_box3
|
||||
[ "$status" -eq 0 ]
|
||||
wait_for_container_inroot 15 1 test_box3 $HELLO_BUNDLE
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "runc pause and resume" {
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
wait_for_container 15 1 test_busybox
|
||||
|
||||
@@ -14,12 +14,12 @@ function teardown() {
|
||||
}
|
||||
|
||||
@test "global --root" {
|
||||
# start busybox detached using $HELLO_BUNDLE for state
|
||||
ROOT=$HELLO_BUNDLE runc start -d --console /dev/pts/ptmx test_dotbox
|
||||
# run busybox detached using $HELLO_BUNDLE for state
|
||||
ROOT=$HELLO_BUNDLE runc run -d --console /dev/pts/ptmx test_dotbox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# start busybox detached in default root
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached in default root
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state of the busyboxes are only in their respective root path
|
||||
@@ -42,13 +42,13 @@ function teardown() {
|
||||
|
||||
runc kill test_busybox KILL
|
||||
[ "$status" -eq 0 ]
|
||||
retry 10 1 eval "__runc state test_busybox | grep -q 'destroyed'"
|
||||
retry 10 1 eval "__runc state test_busybox | grep -q 'stopped'"
|
||||
runc delete test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
ROOT=$HELLO_BUNDLE runc kill test_dotbox KILL
|
||||
[ "$status" -eq 0 ]
|
||||
retry 10 1 eval "ROOT='$HELLO_BUNDLE' __runc state test_dotbox | grep -q 'destroyed'"
|
||||
retry 10 1 eval "ROOT='$HELLO_BUNDLE' __runc state test_dotbox | grep -q 'stopped'"
|
||||
ROOT=$HELLO_BUNDLE runc delete test_dotbox
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ function teardown() {
|
||||
# change the default args parameter from sh to hello
|
||||
sed -i 's;"sh";"/hello";' config.json
|
||||
|
||||
# ensure the generated spec works by starting hello-world
|
||||
runc start test_hello
|
||||
# ensure the generated spec works by running hello-world
|
||||
runc run test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@@ -60,8 +60,8 @@ function teardown() {
|
||||
# change the default args parameter from sh to hello
|
||||
sed -i 's;"sh";"/hello";' "$HELLO_BUNDLE"/config.json
|
||||
|
||||
# ensure the generated spec works by starting hello-world
|
||||
runc start --bundle "$HELLO_BUNDLE" test_hello
|
||||
# ensure the generated spec works by running hello-world
|
||||
runc run --bundle "$HELLO_BUNDLE" test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ function teardown() {
|
||||
teardown_busybox
|
||||
}
|
||||
|
||||
@test "runc start detached" {
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
@test "runc run detached" {
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
@@ -22,9 +22,9 @@ function teardown() {
|
||||
testcontainer test_busybox running
|
||||
}
|
||||
|
||||
@test "runc start detached --pid-file" {
|
||||
# start busybox detached
|
||||
runc start --pid-file pid.txt -d --console /dev/pts/ptmx test_busybox
|
||||
@test "runc run detached --pid-file" {
|
||||
# run busybox detached
|
||||
runc run --pid-file pid.txt -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
|
||||
@@ -11,30 +11,30 @@ function teardown() {
|
||||
teardown_hello
|
||||
}
|
||||
|
||||
@test "runc start" {
|
||||
# start hello-world
|
||||
runc start test_hello
|
||||
@test "runc run" {
|
||||
# run hello-world
|
||||
runc run test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check expected output
|
||||
[[ "${output}" == *"Hello"* ]]
|
||||
}
|
||||
|
||||
@test "runc start with rootfs set to ." {
|
||||
@test "runc run with rootfs set to ." {
|
||||
cp config.json rootfs/.
|
||||
rm config.json
|
||||
cd rootfs
|
||||
sed -i 's;"rootfs";".";' config.json
|
||||
|
||||
# start hello-world
|
||||
runc start test_hello
|
||||
# run hello-world
|
||||
runc run test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *"Hello"* ]]
|
||||
}
|
||||
|
||||
@test "runc start --pid-file" {
|
||||
# start hello-world
|
||||
runc start --pid-file pid.txt test_hello
|
||||
@test "runc run --pid-file" {
|
||||
# run hello-world
|
||||
runc run --pid-file pid.txt test_hello
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "${output}" == *"Hello"* ]]
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ function teardown() {
|
||||
runc state test_busybox
|
||||
[ "$status" -ne 0 ]
|
||||
|
||||
# start busybox detached
|
||||
runc start -d --console /dev/pts/ptmx test_busybox
|
||||
# run busybox detached
|
||||
runc run -d --console /dev/pts/ptmx test_busybox
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
# check state
|
||||
@@ -40,7 +40,7 @@ function teardown() {
|
||||
|
||||
runc kill test_busybox KILL
|
||||
# wait for busybox to be in the destroyed state
|
||||
retry 10 1 eval "__runc state test_busybox | grep -q 'destroyed'"
|
||||
retry 10 1 eval "__runc state test_busybox | grep -q 'stopped'"
|
||||
|
||||
# delete test_busybox
|
||||
runc delete test_busybox
|
||||
|
||||
@@ -48,8 +48,8 @@ function check_cgroup_value() {
|
||||
}
|
||||
|
||||
@test "update" {
|
||||
# start a few busyboxes detached
|
||||
runc start -d --console /dev/pts/ptmx test_update
|
||||
# run a few busyboxes detached
|
||||
runc run -d --console /dev/pts/ptmx test_update
|
||||
[ "$status" -eq 0 ]
|
||||
wait_for_container 15 1 test_update
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
// fatal prints the error's details if it is a libcontainer specific error type
|
||||
@@ -15,3 +17,25 @@ func fatal(err error) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// setupSpec performs inital setup based on the cli.Context for the container
|
||||
func setupSpec(context *cli.Context) (*specs.Spec, error) {
|
||||
bundle := context.String("bundle")
|
||||
if bundle != "" {
|
||||
if err := os.Chdir(bundle); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
spec, err := loadSpec(specConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
notifySocket := os.Getenv("NOTIFY_SOCKET")
|
||||
if notifySocket != "" {
|
||||
setupSdNotify(spec, notifySocket)
|
||||
}
|
||||
if os.Geteuid() != 0 {
|
||||
return nil, fmt.Errorf("runc should be run as root")
|
||||
}
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
+40
-2
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/coreos/go-systemd/activation"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||
"github.com/opencontainers/runc/libcontainer/specconv"
|
||||
@@ -198,6 +199,7 @@ type runner struct {
|
||||
pidFile string
|
||||
console string
|
||||
container libcontainer.Container
|
||||
create bool
|
||||
}
|
||||
|
||||
func (r *runner) run(config *specs.Process) (int, error) {
|
||||
@@ -220,7 +222,7 @@ func (r *runner) run(config *specs.Process) (int, error) {
|
||||
r.destroy()
|
||||
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 {
|
||||
r.destroy()
|
||||
return -1, err
|
||||
@@ -245,7 +247,15 @@ func (r *runner) run(config *specs.Process) (int, error) {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
if r.detach {
|
||||
if !r.create {
|
||||
if err := process.Signal(libcontainer.InitContinueSignal); err != nil {
|
||||
r.terminate(process)
|
||||
r.destroy()
|
||||
tty.Close()
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
if r.detach || r.create {
|
||||
tty.Close()
|
||||
return 0, nil
|
||||
}
|
||||
@@ -281,3 +291,31 @@ func validateProcessSpec(spec *specs.Process) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func startContainer(context *cli.Context, spec *specs.Spec, create bool) (int, error) {
|
||||
id := context.Args().First()
|
||||
if id == "" {
|
||||
return -1, errEmptyID
|
||||
}
|
||||
container, err := createContainer(context, id, spec)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
detach := context.Bool("detach")
|
||||
// Support on-demand socket activation by passing file descriptors into the container init process.
|
||||
listenFDs := []*os.File{}
|
||||
if os.Getenv("LISTEN_FDS") != "" {
|
||||
listenFDs = activation.Files(false)
|
||||
}
|
||||
r := &runner{
|
||||
enableSubreaper: !context.Bool("no-subreaper"),
|
||||
shouldDestroy: true,
|
||||
container: container,
|
||||
listenFDs: listenFDs,
|
||||
console: context.String("console"),
|
||||
detach: detach,
|
||||
pidFile: context.String("pid-file"),
|
||||
create: create,
|
||||
}
|
||||
return r.run(&spec.Process)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user