Remove dep on larger packages
This removes most of the deps on the larger packages for the shim and reduces the binary size and memory footprint from a 7.1mb binary to a 2.6mb binary. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
96034177f9
commit
6f18acda73
5 changed files with 122 additions and 41 deletions
2
Makefile
2
Makefile
|
@ -50,7 +50,7 @@ daemon-static:
|
|||
cd containerd && go build -ldflags "-w -extldflags -static ${LDFLAGS}" -tags "$(BUILDTAGS)" -o ../bin/containerd
|
||||
|
||||
shim: bin
|
||||
cd containerd-shim && go build -tags "$(BUILDTAGS)" -o ../bin/containerd-shim
|
||||
cd containerd-shim && go build -tags "$(BUILDTAGS)" -ldflags "-w" -o ../bin/containerd-shim
|
||||
|
||||
shim-static:
|
||||
cd containerd-shim && go build -ldflags "-w -extldflags -static ${LDFLAGS}" -tags "$(BUILDTAGS)" -o ../bin/containerd-shim
|
||||
|
|
54
containerd-shim/console.go
Normal file
54
containerd-shim/console.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// NewConsole returns an initalized console that can be used within a container by copying bytes
|
||||
// from the master side to the slave that is attached as the tty for the container's init process.
|
||||
func newConsole(uid, gid int) (*os.File, string, error) {
|
||||
master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
console, err := ptsname(master)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err := unlockpt(master); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err := os.Chmod(console, 0600); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if err := os.Chown(console, uid, gid); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return master, console, nil
|
||||
}
|
||||
|
||||
func ioctl(fd uintptr, flag, data uintptr) error {
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||
// unlockpt should be called before opening the slave side of a pty.
|
||||
func unlockpt(f *os.File) error {
|
||||
var u int32
|
||||
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
|
||||
}
|
||||
|
||||
// ptsname retrieves the name of the first available pts for the given master.
|
||||
func ptsname(f *os.File) (string, error) {
|
||||
var n int32
|
||||
if err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("/dev/pts/%d", n), nil
|
||||
}
|
|
@ -8,11 +8,14 @@ import (
|
|||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/containerd/osutils"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
)
|
||||
|
||||
func writeMessage(f *os.File, level string, err error) {
|
||||
fmt.Fprintf(f, `{"level": "%s","msg": "%s"}`, level, err)
|
||||
}
|
||||
|
||||
// containerd-shim is a small shim that sits in front of a runtime implementation
|
||||
// that allows it to be repartented to init and handle reattach from the caller.
|
||||
//
|
||||
|
@ -28,9 +31,7 @@ func main() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
logrus.SetOutput(f)
|
||||
logrus.SetFormatter(&logrus.JSONFormatter{})
|
||||
if err := start(); err != nil {
|
||||
if err := start(f); err != nil {
|
||||
// this means that the runtime failed starting the container and will have the
|
||||
// proper error messages in the runtime log so we should to treat this as a
|
||||
// shim failure because the sim executed properly
|
||||
|
@ -41,13 +42,13 @@ func main() {
|
|||
// log the error instead of writing to stderr because the shim will have
|
||||
// /dev/null as it's stdio because it is supposed to be reparented to system
|
||||
// init and will not have anyone to read from it
|
||||
logrus.Error(err)
|
||||
writeMessage(f, "error", err)
|
||||
f.Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func start() error {
|
||||
func start(log *os.File) error {
|
||||
// start handling signals as soon as possible so that things are properly reaped
|
||||
// or if runtime exits before we hit the handler
|
||||
signals := make(chan os.Signal, 2048)
|
||||
|
@ -73,7 +74,7 @@ func start() error {
|
|||
}
|
||||
defer func() {
|
||||
if err := p.Close(); err != nil {
|
||||
logrus.Warn(err)
|
||||
writeMessage(log, "warn", err)
|
||||
}
|
||||
}()
|
||||
if err := p.start(); err != nil {
|
||||
|
@ -84,7 +85,7 @@ func start() error {
|
|||
for {
|
||||
var msg, w, h int
|
||||
if _, err := fmt.Fscanf(control, "%d %d %d\n", &msg, &w, &h); err != nil {
|
||||
logrus.Warn(err)
|
||||
continue
|
||||
}
|
||||
switch msg {
|
||||
case 0:
|
||||
|
@ -108,21 +109,12 @@ func start() error {
|
|||
for s := range signals {
|
||||
switch s {
|
||||
case syscall.SIGCHLD:
|
||||
exits, err := osutils.Reap()
|
||||
if err != nil {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
exits, _ := osutils.Reap()
|
||||
for _, e := range exits {
|
||||
// check to see if runtime is one of the processes that has exited
|
||||
if e.Pid == p.pid() {
|
||||
exitShim = true
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"pid": e.Pid,
|
||||
"status": e.Status,
|
||||
}).Info("shim: runtime exited")
|
||||
if err := writeInt("exitStatus", e.Status); err != nil {
|
||||
logrus.WithFields(logrus.Fields{"status": e.Status}).Warn(err)
|
||||
}
|
||||
writeInt("exitStatus", e.Status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,41 @@ import (
|
|||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/docker/containerd/runtime"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/docker/containerd/specs"
|
||||
)
|
||||
|
||||
var errRuntime = errors.New("shim: runtime execution error")
|
||||
|
||||
type checkpoint struct {
|
||||
// Timestamp is the time that checkpoint happened
|
||||
Created time.Time `json:"created"`
|
||||
// Name is the name of the checkpoint
|
||||
Name string `json:"name"`
|
||||
// Tcp checkpoints open tcp connections
|
||||
Tcp bool `json:"tcp"`
|
||||
// UnixSockets persists unix sockets in the checkpoint
|
||||
UnixSockets bool `json:"unixSockets"`
|
||||
// Shell persists tty sessions in the checkpoint
|
||||
Shell bool `json:"shell"`
|
||||
// Exit exits the container after the checkpoint is finished
|
||||
Exit bool `json:"exit"`
|
||||
}
|
||||
|
||||
type processState struct {
|
||||
specs.ProcessSpec
|
||||
Exec bool `json:"exec"`
|
||||
Stdin string `json:"containerdStdin"`
|
||||
Stdout string `json:"containerdStdout"`
|
||||
Stderr string `json:"containerdStderr"`
|
||||
RuntimeArgs []string `json:"runtimeArgs"`
|
||||
NoPivotRoot bool `json:"noPivotRoot"`
|
||||
Checkpoint string `json:"checkpoint"`
|
||||
RootUID int `json:"rootUID"`
|
||||
RootGID int `json:"rootGID"`
|
||||
}
|
||||
|
||||
type process struct {
|
||||
sync.WaitGroup
|
||||
id string
|
||||
|
@ -26,12 +54,12 @@ type process struct {
|
|||
stdio *stdio
|
||||
exec bool
|
||||
containerPid int
|
||||
checkpoint *runtime.Checkpoint
|
||||
checkpoint *checkpoint
|
||||
shimIO *IO
|
||||
stdinCloser io.Closer
|
||||
console libcontainer.Console
|
||||
console *os.File
|
||||
consolePath string
|
||||
state *runtime.ProcessState
|
||||
state *processState
|
||||
runtime string
|
||||
}
|
||||
|
||||
|
@ -59,26 +87,26 @@ func newProcess(id, bundle, runtimeName string) (*process, error) {
|
|||
return p, nil
|
||||
}
|
||||
|
||||
func loadProcess() (*runtime.ProcessState, error) {
|
||||
func loadProcess() (*processState, error) {
|
||||
f, err := os.Open("process.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
var s runtime.ProcessState
|
||||
var s processState
|
||||
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
func loadCheckpoint(bundle, name string) (*runtime.Checkpoint, error) {
|
||||
func loadCheckpoint(bundle, name string) (*checkpoint, error) {
|
||||
f, err := os.Open(filepath.Join(bundle, "checkpoints", name, "config.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
var cpt runtime.Checkpoint
|
||||
var cpt checkpoint
|
||||
if err := json.NewDecoder(f).Decode(&cpt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -200,25 +228,25 @@ func (p *process) openIO() error {
|
|||
}()
|
||||
|
||||
if p.state.Terminal {
|
||||
console, err := libcontainer.NewConsole(uid, gid)
|
||||
master, console, err := newConsole(uid, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.console = console
|
||||
p.consolePath = console.Path()
|
||||
p.console = master
|
||||
p.consolePath = console
|
||||
stdin, err := os.OpenFile(p.state.Stdin, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go io.Copy(console, stdin)
|
||||
go io.Copy(master, stdin)
|
||||
stdout, err := os.OpenFile(p.state.Stdout, syscall.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Add(1)
|
||||
go func() {
|
||||
io.Copy(stdout, console)
|
||||
console.Close()
|
||||
io.Copy(stdout, master)
|
||||
master.Close()
|
||||
p.Done()
|
||||
}()
|
||||
return nil
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
|
||||
package osutils
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
)
|
||||
import "syscall"
|
||||
|
||||
// Exit is the wait4 information from an exited process
|
||||
type Exit struct {
|
||||
|
@ -34,7 +30,18 @@ func Reap() (exits []Exit, err error) {
|
|||
}
|
||||
exits = append(exits, Exit{
|
||||
Pid: pid,
|
||||
Status: utils.ExitStatus(ws),
|
||||
Status: exitStatus(ws),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const exitSignalOffset = 128
|
||||
|
||||
// exitStatus returns the correct exit status for a process based on if it
|
||||
// was signaled or exited cleanly
|
||||
func exitStatus(status syscall.WaitStatus) int {
|
||||
if status.Signaled() {
|
||||
return exitSignalOffset + int(status.Signal())
|
||||
}
|
||||
return status.ExitStatus()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue