github.com/mgoltzsche/ctnr@v0.7.1-alpha/run/librunner/container.go (about) 1 package librunner 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "runtime" 8 "time" 9 10 exterrors "github.com/mgoltzsche/ctnr/pkg/errors" 11 "github.com/mgoltzsche/ctnr/pkg/log" 12 "github.com/mgoltzsche/ctnr/run" 13 "github.com/opencontainers/runc/libcontainer" 14 _ "github.com/opencontainers/runc/libcontainer/nsenter" 15 "github.com/opencontainers/runc/libcontainer/specconv" 16 "github.com/opencontainers/runtime-spec/specs-go" 17 "github.com/pkg/errors" 18 ) 19 20 func init() { 21 if len(os.Args) > 1 && os.Args[1] == "init" { 22 // Initializes the previously created container in this new process 23 runtime.GOMAXPROCS(1) 24 runtime.LockOSThread() 25 factory, _ := libcontainer.New("") 26 if err := factory.StartInitialization(); err != nil { 27 fmt.Fprintf(os.Stderr, "Error: libcontainer factory initialization: %s\n", err) 28 os.Exit(1) 29 } 30 panic("factory initialization should block further execution - this should never be executed") 31 } 32 } 33 34 type Container struct { 35 process *Process 36 container libcontainer.Container 37 destroyOnClose bool 38 log log.Loggers 39 } 40 41 func LoadContainer(id string, factory libcontainer.Factory, loggers log.Loggers) (*Container, error) { 42 c, err := factory.Load(id) 43 return &Container{ 44 container: c, 45 log: loggers, 46 }, err 47 } 48 49 func NewContainer(cfg *run.ContainerConfig, rootless bool, factory libcontainer.Factory, loggers log.Loggers) (r *Container, err error) { 50 id := cfg.Id 51 if id == "" { 52 if id = cfg.Bundle.ID(); id == "" { 53 panic("no container ID provided and bundle ID is empty") 54 } 55 } 56 57 runtime.LockOSThread() 58 defer runtime.UnlockOSThread() 59 60 loggers = loggers.WithField("id", id) 61 loggers.Debug.Println("Creating container") 62 63 spec, err := cfg.Bundle.Spec() 64 if err != nil { 65 return nil, errors.Wrap(err, "new container") 66 } 67 if spec.Process == nil { 68 return nil, errors.Errorf("new container %s: bundle spec declares no process to run", id) 69 } 70 orgwd, err := os.Getwd() 71 if err != nil { 72 return nil, errors.Wrap(err, "new container") 73 } 74 75 // Must change to bundle dir because CreateLibcontainerConfig assumes it is in the bundle directory 76 if err = os.Chdir(cfg.Bundle.Dir()); err != nil { 77 return nil, errors.New("new container: change to bundle directory: " + err.Error()) 78 } 79 defer func() { 80 if e := os.Chdir(orgwd); e != nil { 81 e = errors.New("change back from bundle to previous directory: " + e.Error()) 82 err = exterrors.Append(err, e) 83 } 84 }() 85 86 config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{ 87 CgroupName: id, 88 UseSystemdCgroup: false, // TODO: expose as option 89 NoPivotRoot: cfg.NoPivotRoot, 90 NoNewKeyring: cfg.NoNewKeyring, 91 Spec: spec, 92 Rootless: rootless, 93 }) 94 if err != nil { 95 return nil, errors.Wrap(err, "create container config") 96 } 97 if spec.Root != nil { 98 if filepath.IsAbs(spec.Root.Path) { 99 config.Rootfs = spec.Root.Path 100 } else { 101 config.Rootfs = filepath.Join(cfg.Bundle.Dir(), spec.Root.Path) 102 } 103 } 104 container, err := factory.Create(id, config) 105 if err != nil { 106 return nil, errors.Wrap(err, "create container") 107 } 108 109 r = &Container{ 110 container: container, 111 destroyOnClose: cfg.DestroyOnClose, 112 log: loggers, 113 } 114 r.process, err = NewProcess(r, spec.Process, cfg.Io, loggers) 115 err = errors.Wrap(err, "configure container process") 116 return 117 } 118 119 func (c *Container) ID() string { 120 return c.container.ID() 121 } 122 123 func (c *Container) Rootfs() string { 124 return c.container.Config().Rootfs 125 } 126 127 // Prepare and start the container process from spec and with stdio 128 func (c *Container) Start() (err error) { 129 c.log.Debug.Println("Starting container") 130 return c.process.Start() 131 } 132 133 func (c *Container) Stop() { 134 c.log.Debug.Println("Stopping container") 135 if p := c.process; p != nil { 136 p.Stop() 137 } 138 } 139 140 func (c *Container) Exec(process *specs.Process, io run.ContainerIO) (proc run.Process, err error) { 141 p, err := NewProcess(c, process, io, c.log) 142 err = p.Start() 143 return p, err 144 } 145 146 // Waits for the container process to terminate and returns the process' error if any 147 func (c *Container) Wait() (err error) { 148 if p := c.process; p != nil { 149 err = p.Wait() 150 } 151 return 152 } 153 154 func (c *Container) Destroy() (err error) { 155 c.log.Debug.Println("Destroying container") 156 cc := c.container 157 if cc != nil { 158 err = exterrors.Append(err, cc.Destroy()) 159 c.container = nil 160 } 161 return 162 } 163 164 func (c *Container) Close() (err error) { 165 c.log.Debug.Println("Closing container") 166 167 // Reset bundle expiry time 168 bundleDir := filepath.Join(c.Rootfs(), "..") 169 now := time.Now() 170 e := errors.Wrap(os.Chtimes(bundleDir, now, now), "reset bundle expiry time") 171 err = exterrors.Append(err, e) 172 173 // Close process 174 p := c.process 175 if p != nil { 176 err = p.Close() 177 c.process = nil 178 } 179 180 // Destroy container 181 if c.destroyOnClose { 182 err = exterrors.Append(err, c.Destroy()) 183 } 184 185 return 186 }