containerd/container.go

213 lines
4.4 KiB
Go

package containerkit
import (
"encoding/json"
"io"
"os"
"path/filepath"
"sync"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
type ContainerConfig interface {
ID() string
Root() string
Spec(*Mount) (*specs.Spec, error)
}
type GraphDriver interface {
Mount(id string) (*Mount, error)
}
func NewContainer(config ContainerConfig, graph GraphDriver, exec ExecutionDriver) (*Container, error) {
var (
id = config.ID()
root = config.Root()
path = filepath.Join(root, id)
)
mount, err := graph.Mount(id)
if err != nil {
return nil, err
}
s, err := config.Spec(mount)
if err != nil {
return nil, err
}
// HACK: for runc to allow to use this path without a premounted rootfs
if err := os.MkdirAll(filepath.Join(path, s.Root.Path), 0711); err != nil {
return nil, err
}
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: exec,
}, nil
}
func LoadContainer(config ContainerConfig, exec ExecutionDriver) (*Container, error) {
var (
id = config.ID()
root = config.Root()
path = filepath.Join(root, id)
)
spec, err := loadSpec(path)
if err != nil {
return nil, err
}
process, err := exec.Load(id)
if err != nil {
return nil, err
}
// TODO: load exec processes
return &Container{
id: id,
path: path,
s: spec,
driver: exec,
init: &Process{
d: process,
driver: exec,
},
}, 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
}
// Spec returns the OCI runtime spec for the container
func (c *Container) Spec() *specs.Spec {
return c.s
}
// 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,
Stdin: c.Stdin,
Stdout: c.Stdout,
Stderr: c.Stderr,
}
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
}