containerd/container.go
Michael Crosby 23adfe42f9 Add loading of existing container
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
2016-09-30 13:51:10 -07:00

186 lines
3.9 KiB
Go

package containerkit
import (
"encoding/json"
"io"
"os"
"path/filepath"
"sync"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
func NewContainer(root, id string, m Mount, s *specs.Spec, driver ExecutionDriver) (*Container, error) {
path := filepath.Join(root, id)
if err := os.MkdirAll(filepath.Join(path, s.Root.Path), 0711); err != nil {
return nil, err
}
// FIXME: find a better UI for this
s.Mounts = append([]specs.Mount{
{
Type: m.Type,
Source: m.Source,
Destination: "/",
Options: m.Options,
},
}, s.Mounts...)
f, err := os.Create(filepath.Join(path, "config.json"))
if err != nil {
return nil, err
}
// write the spec file to the container's directory
err = json.NewEncoder(f).Encode(s)
f.Close()
if err != nil {
return nil, err
}
return &Container{
id: id,
path: path,
s: s,
driver: driver,
}, nil
}
func LoadContainer(root, id string, driver ExecutionDriver) (*Container, error) {
path := filepath.Join(root, id)
spec, err := loadSpec(path)
if err != nil {
return nil, err
}
process, err := driver.Load(id)
if err != nil {
return nil, err
}
// TODO: load exec processes
return &Container{
id: id,
path: path,
s: spec,
driver: driver,
init: &Process{
d: process,
driver: driver,
},
}, nil
}
func loadSpec(path string) (*specs.Spec, error) {
f, err := os.Open(filepath.Join(path, "config.json"))
if err != nil {
return nil, err
}
var s specs.Spec
err = json.NewDecoder(f).Decode(&s)
f.Close()
if err != nil {
return nil, err
}
return &s, nil
}
type Container struct {
mu sync.Mutex
id string
path string
s *specs.Spec
driver ExecutionDriver
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
// init is the container's init processes
init *Process
// processes is a list of additional processes executed inside the container
// via the NewProcess method on the container
processes []*Process
}
// ID returns the id of the container
func (c *Container) ID() string {
return c.id
}
// Path returns the fully qualified path to the container on disk
func (c *Container) Path() string {
return c.path
}
// Create will create the container on the system by running the runtime's
// initial setup and process waiting for the user process to be started
func (c *Container) Create() error {
c.mu.Lock()
defer c.mu.Unlock()
d, err := c.driver.Create(c)
if err != nil {
return err
}
c.init = &Process{
d: d,
driver: c.driver,
}
return nil
}
// Start will start the container's user specified process
func (c *Container) Start() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.driver.Start(c)
}
// NewProcess will create a new process that will be executed inside the
// container and tied to the init processes lifecycle
func (c *Container) NewProcess(spec *specs.Process) (*Process, error) {
c.mu.Lock()
defer c.mu.Unlock()
process := &Process{
s: spec,
c: c,
exec: true,
driver: c.driver,
}
c.processes = append(c.processes, process)
return process, nil
}
// Pid returns the pid of the init or main process hosted inside the container
func (c *Container) Pid() int {
c.mu.Lock()
if c.init == nil {
c.mu.Unlock()
return -1
}
pid := c.init.Pid()
c.mu.Unlock()
return pid
}
// Wait will perform a blocking wait on the init process of the container
func (c *Container) Wait() (uint32, error) {
c.mu.Lock()
proc := c.init
c.mu.Unlock()
return proc.Wait()
}
// Signal will send the provided signal to the init process of the container
func (c *Container) Signal(s os.Signal) error {
c.mu.Lock()
defer c.mu.Unlock()
return c.init.Signal(s)
}
// Delete will delete the container if it no long has any processes running
// inside the container and removes all state on disk for the container
func (c *Container) Delete() error {
c.mu.Lock()
defer c.mu.Unlock()
err := c.driver.Delete(c)
if rerr := os.RemoveAll(c.path); err == nil {
err = rerr
}
return err
}