214 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package containerd
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"sync"
 | |
| 
 | |
| 	specs "github.com/opencontainers/runtime-spec/specs-go"
 | |
| )
 | |
| 
 | |
| type ContainerConfig interface {
 | |
| 	ID() string
 | |
| 	Root() string // bundle path
 | |
| 	Spec() (*specs.Spec, error)
 | |
| 	Runtime() (Runtime, error)
 | |
| }
 | |
| 
 | |
| func NewContainer(config ContainerConfig) (*Container, error) {
 | |
| 	var (
 | |
| 		id   = config.ID()
 | |
| 		root = config.Root()
 | |
| 		path = filepath.Join(root, id)
 | |
| 	)
 | |
| 	s, err := config.Spec()
 | |
| 	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
 | |
| 	}
 | |
| 	r, err := config.Runtime()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	return &Container{
 | |
| 		id:     id,
 | |
| 		path:   path,
 | |
| 		s:      s,
 | |
| 		driver: r,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func LoadContainer(config ContainerConfig) (*Container, error) {
 | |
| 	var (
 | |
| 		id   = config.ID()
 | |
| 		root = config.Root()
 | |
| 		path = filepath.Join(root, id)
 | |
| 	)
 | |
| 	spec, err := loadSpec(path)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	r, err := config.Runtime()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	process, err := r.Load(id)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	// TODO: load exec processes
 | |
| 	return &Container{
 | |
| 		id:     id,
 | |
| 		path:   path,
 | |
| 		s:      spec,
 | |
| 		driver: r,
 | |
| 		init: &Process{
 | |
| 			d:      process,
 | |
| 			driver: r,
 | |
| 		},
 | |
| 	}, 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 Runtime
 | |
| 
 | |
| 	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
 | |
| }
 | 
