2016-09-28 22:09:22 +00:00
|
|
|
package containerkit
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
|
|
)
|
|
|
|
|
2016-10-03 22:48:39 +00:00
|
|
|
type ContainerConfig interface {
|
|
|
|
ID() string
|
|
|
|
Root() string
|
2016-10-28 22:51:31 +00:00
|
|
|
Spec() (*specs.Spec, error)
|
|
|
|
Runtime() (Runtime, error)
|
2016-10-03 22:48:39 +00:00
|
|
|
}
|
|
|
|
|
2016-10-28 22:51:31 +00:00
|
|
|
func NewContainer(config ContainerConfig) (*Container, error) {
|
2016-10-03 22:48:39 +00:00
|
|
|
var (
|
|
|
|
id = config.ID()
|
|
|
|
root = config.Root()
|
|
|
|
path = filepath.Join(root, id)
|
|
|
|
)
|
2016-10-28 22:51:31 +00:00
|
|
|
s, err := config.Spec()
|
2016-10-03 22:48:39 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// HACK: for runc to allow to use this path without a premounted rootfs
|
2016-09-28 22:09:22 +00:00
|
|
|
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
|
|
|
|
}
|
2016-10-28 22:51:31 +00:00
|
|
|
r, err := config.Runtime()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-09-28 22:09:22 +00:00
|
|
|
return &Container{
|
|
|
|
id: id,
|
|
|
|
path: path,
|
|
|
|
s: s,
|
2016-10-28 22:51:31 +00:00
|
|
|
driver: r,
|
2016-09-28 22:09:22 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2016-10-28 22:51:31 +00:00
|
|
|
func LoadContainer(config ContainerConfig) (*Container, error) {
|
2016-10-03 22:48:39 +00:00
|
|
|
var (
|
|
|
|
id = config.ID()
|
|
|
|
root = config.Root()
|
|
|
|
path = filepath.Join(root, id)
|
|
|
|
)
|
2016-09-30 20:51:10 +00:00
|
|
|
spec, err := loadSpec(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-10-28 22:51:31 +00:00
|
|
|
r, err := config.Runtime()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
process, err := r.Load(id)
|
2016-09-30 20:51:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// TODO: load exec processes
|
|
|
|
return &Container{
|
|
|
|
id: id,
|
|
|
|
path: path,
|
|
|
|
s: spec,
|
2016-10-28 22:51:31 +00:00
|
|
|
driver: r,
|
2016-09-30 20:51:10 +00:00
|
|
|
init: &Process{
|
|
|
|
d: process,
|
2016-10-28 22:51:31 +00:00
|
|
|
driver: r,
|
2016-09-30 20:51:10 +00:00
|
|
|
},
|
|
|
|
}, 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
|
|
|
|
}
|
|
|
|
|
2016-09-28 22:09:22 +00:00
|
|
|
type Container struct {
|
|
|
|
mu sync.Mutex
|
|
|
|
id string
|
|
|
|
path string
|
|
|
|
s *specs.Spec
|
|
|
|
|
2016-10-28 22:51:31 +00:00
|
|
|
driver Runtime
|
2016-09-28 22:09:22 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-10-05 22:07:20 +00:00
|
|
|
// Spec returns the OCI runtime spec for the container
|
|
|
|
func (c *Container) Spec() *specs.Spec {
|
|
|
|
return c.s
|
|
|
|
}
|
|
|
|
|
2016-09-28 22:09:22 +00:00
|
|
|
// 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,
|
2016-10-03 22:20:45 +00:00
|
|
|
Stdin: c.Stdin,
|
|
|
|
Stdout: c.Stdout,
|
|
|
|
Stderr: c.Stderr,
|
2016-09-28 22:09:22 +00:00
|
|
|
}
|
|
|
|
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{
|
2016-09-28 22:27:24 +00:00
|
|
|
s: spec,
|
|
|
|
c: c,
|
|
|
|
exec: true,
|
|
|
|
driver: c.driver,
|
2016-09-28 22:09:22 +00:00
|
|
|
}
|
|
|
|
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()
|
2016-09-28 22:27:24 +00:00
|
|
|
proc := c.init
|
|
|
|
c.mu.Unlock()
|
|
|
|
return proc.Wait()
|
2016-09-28 22:09:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|