Skip to content

Commit

Permalink
Merge pull request #827 from crosbymichael/create-start
Browse files Browse the repository at this point in the history
Implement create and start
  • Loading branch information
crosbymichael committed Jun 3, 2016
2 parents 3211c9f + 1d61abe commit c5060ff
Show file tree
Hide file tree
Showing 44 changed files with 591 additions and 302 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
60 changes: 60 additions & 0 deletions create.go
Original file line number Diff line number Diff line change
@@ -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
},
}
26 changes: 24 additions & 2 deletions delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
package main

import (
"fmt"
"os"
"path/filepath"
"syscall"
"time"

"github.com/codegangsta/cli"
"github.com/opencontainers/runc/libcontainer"
Expand All @@ -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`,
Expand All @@ -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
},
}
3 changes: 2 additions & 1 deletion events.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
41 changes: 24 additions & 17 deletions libcontainer/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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"
}
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -106,15 +102,15 @@ type BaseContainer interface {
//
// errors:
// ContainerDestroyed - Container no longer exists,
// Systemerror - System error.
// SystemError - System error.
Stats() (*Stats, error)

// Set resources of container as configured
//
// 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
Expand All @@ -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
}
81 changes: 61 additions & 20 deletions libcontainer/container_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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)
}
if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
return err
switch status {
case Running, Created:
if err := c.cgroupManager.Freeze(configs.Frozen); err != nil {
return err
}
return c.state.transition(&pausedState{
c: c,
})
}
return c.state.transition(&pausedState{
c: c,
})
return newGenericError(fmt.Errorf("container not running: %s", status), ContainerNotRunning)
}

func (c *linuxContainer) Resume() error {
Expand Down Expand Up @@ -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)
}
// 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 true, nil
return Running, nil
}

func (c *linuxContainer) isPaused() (bool, error) {
Expand Down
Loading

0 comments on commit c5060ff

Please sign in to comment.