Get runc to build clean on Solaris
Signed-off-by: Amit Krishnan <krish.amit@gmail.com>
This commit is contained in:
parent
99b423c6cb
commit
82050a5b8f
|
@ -0,0 +1,11 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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) (Console, error) {
|
||||||
|
return nil, errors.New("libcontainer console is not supported on Solaris")
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
// State represents a running container's state
|
||||||
|
type State struct {
|
||||||
|
BaseState
|
||||||
|
|
||||||
|
// Platform specific fields below here
|
||||||
|
}
|
||||||
|
|
||||||
|
// A libcontainer container object.
|
||||||
|
//
|
||||||
|
// Each container is thread-safe within the same process. Since a container can
|
||||||
|
// be destroyed by a separate process, any function may return that the container
|
||||||
|
// was not found.
|
||||||
|
type Container interface {
|
||||||
|
BaseContainer
|
||||||
|
|
||||||
|
// Methods below here are platform specific
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package libcontainer
|
||||||
|
|
||||||
|
// Solaris - TODO
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
Interfaces []*NetworkInterface
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
// +build solaris
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/codegangsta/cli"
|
||||||
|
|
||||||
|
var (
|
||||||
|
checkpointCommand cli.Command
|
||||||
|
eventsCommand cli.Command
|
||||||
|
restoreCommand cli.Command
|
||||||
|
specCommand cli.Command
|
||||||
|
killCommand cli.Command
|
||||||
|
deleteCommand cli.Command
|
||||||
|
execCommand cli.Command
|
||||||
|
initCommand cli.Command
|
||||||
|
listCommand cli.Command
|
||||||
|
pauseCommand cli.Command
|
||||||
|
resumeCommand cli.Command
|
||||||
|
startCommand cli.Command
|
||||||
|
stateCommand cli.Command
|
||||||
|
)
|
|
@ -1,4 +1,4 @@
|
||||||
// +build !linux
|
// +build !linux,!solaris
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
270
utils.go
270
utils.go
|
@ -1,61 +1,12 @@
|
||||||
// +build linux
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/opencontainers/runc/libcontainer"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
|
||||||
"github.com/opencontainers/runc/libcontainer/specconv"
|
|
||||||
"github.com/opencontainers/runtime-spec/specs-go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var errEmptyID = errors.New("container id cannot be empty")
|
|
||||||
|
|
||||||
var container libcontainer.Container
|
|
||||||
|
|
||||||
// loadFactory returns the configured factory instance for execing containers.
|
|
||||||
func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
|
|
||||||
root := context.GlobalString("root")
|
|
||||||
abs, err := filepath.Abs(root)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cgroupManager := libcontainer.Cgroupfs
|
|
||||||
if context.GlobalBool("systemd-cgroup") {
|
|
||||||
if systemd.UseSystemd() {
|
|
||||||
cgroupManager = libcontainer.SystemdCgroups
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("systemd cgroup flag passed, but systemd support for managing cgroups is not available")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return libcontainer.New(abs, cgroupManager, func(l *libcontainer.LinuxFactory) error {
|
|
||||||
l.CriuPath = context.GlobalString("criu")
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// getContainer returns the specified container instance by loading it from state
|
|
||||||
// with the default factory.
|
|
||||||
func getContainer(context *cli.Context) (libcontainer.Container, error) {
|
|
||||||
id := context.Args().First()
|
|
||||||
if id == "" {
|
|
||||||
return nil, errEmptyID
|
|
||||||
}
|
|
||||||
factory, err := loadFactory(context)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return factory.Load(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fatal prints the error's details if it is a libcontainer specific error type
|
// fatal prints the error's details if it is a libcontainer specific error type
|
||||||
// then exits the program with an exit status of 1.
|
// then exits the program with an exit status of 1.
|
||||||
func fatal(err error) {
|
func fatal(err error) {
|
||||||
|
@ -64,224 +15,3 @@ func fatal(err error) {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fatalf(t string, v ...interface{}) {
|
|
||||||
fatal(fmt.Errorf(t, v...))
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultImagePath(context *cli.Context) string {
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return filepath.Join(cwd, "checkpoint")
|
|
||||||
}
|
|
||||||
|
|
||||||
// newProcess returns a new libcontainer Process with the arguments from the
|
|
||||||
// spec and stdio from the current process.
|
|
||||||
func newProcess(p specs.Process) (*libcontainer.Process, error) {
|
|
||||||
lp := &libcontainer.Process{
|
|
||||||
Args: p.Args,
|
|
||||||
Env: p.Env,
|
|
||||||
// TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
|
|
||||||
User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
|
|
||||||
Cwd: p.Cwd,
|
|
||||||
Capabilities: p.Capabilities,
|
|
||||||
Label: p.SelinuxLabel,
|
|
||||||
NoNewPrivileges: &p.NoNewPrivileges,
|
|
||||||
AppArmorProfile: p.ApparmorProfile,
|
|
||||||
}
|
|
||||||
for _, rlimit := range p.Rlimits {
|
|
||||||
rl, err := createLibContainerRlimit(rlimit)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
lp.Rlimits = append(lp.Rlimits, rl)
|
|
||||||
}
|
|
||||||
return lp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dupStdio(process *libcontainer.Process, rootuid int) error {
|
|
||||||
process.Stdin = os.Stdin
|
|
||||||
process.Stdout = os.Stdout
|
|
||||||
process.Stderr = os.Stderr
|
|
||||||
for _, fd := range []uintptr{
|
|
||||||
os.Stdin.Fd(),
|
|
||||||
os.Stdout.Fd(),
|
|
||||||
os.Stderr.Fd(),
|
|
||||||
} {
|
|
||||||
if err := syscall.Fchown(int(fd), rootuid, rootuid); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// If systemd is supporting sd_notify protocol, this function will add support
|
|
||||||
// for sd_notify protocol from within the container.
|
|
||||||
func setupSdNotify(spec *specs.Spec, notifySocket string) {
|
|
||||||
spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}})
|
|
||||||
spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket))
|
|
||||||
}
|
|
||||||
|
|
||||||
func destroy(container libcontainer.Container) {
|
|
||||||
if err := container.Destroy(); err != nil {
|
|
||||||
logrus.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupIO sets the proper IO on the process depending on the configuration
|
|
||||||
// If there is a nil error then there must be a non nil tty returned
|
|
||||||
func setupIO(process *libcontainer.Process, rootuid int, console string, createTTY, detach bool) (*tty, error) {
|
|
||||||
// detach and createTty will not work unless a console path is passed
|
|
||||||
// so error out here before changing any terminal settings
|
|
||||||
if createTTY && detach && console == "" {
|
|
||||||
return nil, fmt.Errorf("cannot allocate tty if runc will detach")
|
|
||||||
}
|
|
||||||
if createTTY {
|
|
||||||
return createTty(process, rootuid, console)
|
|
||||||
}
|
|
||||||
if detach {
|
|
||||||
if err := dupStdio(process, rootuid); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &tty{}, nil
|
|
||||||
}
|
|
||||||
return createStdioPipes(process, rootuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// createPidFile creates a file with the processes pid inside it atomically
|
|
||||||
// it creates a temp file with the paths filename + '.' infront of it
|
|
||||||
// then renames the file
|
|
||||||
func createPidFile(path string, process *libcontainer.Process) error {
|
|
||||||
pid, err := process.Pid()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
tmpDir = filepath.Dir(path)
|
|
||||||
tmpName = filepath.Join(tmpDir, fmt.Sprintf(".%s", filepath.Base(path)))
|
|
||||||
)
|
|
||||||
f, err := os.OpenFile(tmpName, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = fmt.Fprintf(f, "%d", pid)
|
|
||||||
f.Close()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return os.Rename(tmpName, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) {
|
|
||||||
config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{
|
|
||||||
CgroupName: id,
|
|
||||||
UseSystemdCgroup: context.GlobalBool("systemd-cgroup"),
|
|
||||||
NoPivotRoot: context.Bool("no-pivot"),
|
|
||||||
Spec: spec,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(config.Rootfs); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil, fmt.Errorf("rootfs (%q) does not exist", config.Rootfs)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
factory, err := loadFactory(context)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return factory.Create(id, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
type runner struct {
|
|
||||||
enableSubreaper bool
|
|
||||||
shouldDestroy bool
|
|
||||||
detach bool
|
|
||||||
listenFDs []*os.File
|
|
||||||
pidFile string
|
|
||||||
console string
|
|
||||||
container libcontainer.Container
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *runner) run(config *specs.Process) (int, error) {
|
|
||||||
process, err := newProcess(*config)
|
|
||||||
if err != nil {
|
|
||||||
r.destroy()
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
if len(r.listenFDs) > 0 {
|
|
||||||
process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(r.listenFDs)), "LISTEN_PID=1")
|
|
||||||
process.ExtraFiles = append(process.ExtraFiles, r.listenFDs...)
|
|
||||||
}
|
|
||||||
rootuid, err := r.container.Config().HostUID()
|
|
||||||
if err != nil {
|
|
||||||
r.destroy()
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
tty, err := setupIO(process, rootuid, r.console, config.Terminal, r.detach)
|
|
||||||
if err != nil {
|
|
||||||
r.destroy()
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
handler := newSignalHandler(tty, r.enableSubreaper)
|
|
||||||
if err := r.container.Start(process); err != nil {
|
|
||||||
r.destroy()
|
|
||||||
tty.Close()
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
if err := tty.ClosePostStart(); err != nil {
|
|
||||||
r.terminate(process)
|
|
||||||
r.destroy()
|
|
||||||
tty.Close()
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
if r.pidFile != "" {
|
|
||||||
if err := createPidFile(r.pidFile, process); err != nil {
|
|
||||||
r.terminate(process)
|
|
||||||
r.destroy()
|
|
||||||
tty.Close()
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if r.detach {
|
|
||||||
tty.Close()
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
status, err := handler.forward(process)
|
|
||||||
if err != nil {
|
|
||||||
r.terminate(process)
|
|
||||||
}
|
|
||||||
r.destroy()
|
|
||||||
tty.Close()
|
|
||||||
return status, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *runner) destroy() {
|
|
||||||
if r.shouldDestroy {
|
|
||||||
destroy(r.container)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *runner) terminate(p *libcontainer.Process) {
|
|
||||||
p.Signal(syscall.SIGKILL)
|
|
||||||
p.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateProcessSpec(spec *specs.Process) error {
|
|
||||||
if spec.Cwd == "" {
|
|
||||||
return fmt.Errorf("Cwd property must not be empty")
|
|
||||||
}
|
|
||||||
if !filepath.IsAbs(spec.Cwd) {
|
|
||||||
return fmt.Errorf("Cwd must be an absolute path")
|
|
||||||
}
|
|
||||||
if len(spec.Args) == 0 {
|
|
||||||
return fmt.Errorf("args must not be empty")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,278 @@
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/cgroups/systemd"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/specconv"
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errEmptyID = errors.New("container id cannot be empty")
|
||||||
|
|
||||||
|
var container libcontainer.Container
|
||||||
|
|
||||||
|
// loadFactory returns the configured factory instance for execing containers.
|
||||||
|
func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
|
||||||
|
root := context.GlobalString("root")
|
||||||
|
abs, err := filepath.Abs(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cgroupManager := libcontainer.Cgroupfs
|
||||||
|
if context.GlobalBool("systemd-cgroup") {
|
||||||
|
if systemd.UseSystemd() {
|
||||||
|
cgroupManager = libcontainer.SystemdCgroups
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("systemd cgroup flag passed, but systemd support for managing cgroups is not available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return libcontainer.New(abs, cgroupManager, func(l *libcontainer.LinuxFactory) error {
|
||||||
|
l.CriuPath = context.GlobalString("criu")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// getContainer returns the specified container instance by loading it from state
|
||||||
|
// with the default factory.
|
||||||
|
func getContainer(context *cli.Context) (libcontainer.Container, error) {
|
||||||
|
id := context.Args().First()
|
||||||
|
if id == "" {
|
||||||
|
return nil, errEmptyID
|
||||||
|
}
|
||||||
|
factory, err := loadFactory(context)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return factory.Load(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fatalf(t string, v ...interface{}) {
|
||||||
|
fatal(fmt.Errorf(t, v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultImagePath(context *cli.Context) string {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return filepath.Join(cwd, "checkpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
// newProcess returns a new libcontainer Process with the arguments from the
|
||||||
|
// spec and stdio from the current process.
|
||||||
|
func newProcess(p specs.Process) (*libcontainer.Process, error) {
|
||||||
|
lp := &libcontainer.Process{
|
||||||
|
Args: p.Args,
|
||||||
|
Env: p.Env,
|
||||||
|
// TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
|
||||||
|
User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
|
||||||
|
Cwd: p.Cwd,
|
||||||
|
Capabilities: p.Capabilities,
|
||||||
|
Label: p.SelinuxLabel,
|
||||||
|
NoNewPrivileges: &p.NoNewPrivileges,
|
||||||
|
AppArmorProfile: p.ApparmorProfile,
|
||||||
|
}
|
||||||
|
for _, rlimit := range p.Rlimits {
|
||||||
|
rl, err := createLibContainerRlimit(rlimit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lp.Rlimits = append(lp.Rlimits, rl)
|
||||||
|
}
|
||||||
|
return lp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dupStdio(process *libcontainer.Process, rootuid int) error {
|
||||||
|
process.Stdin = os.Stdin
|
||||||
|
process.Stdout = os.Stdout
|
||||||
|
process.Stderr = os.Stderr
|
||||||
|
for _, fd := range []uintptr{
|
||||||
|
os.Stdin.Fd(),
|
||||||
|
os.Stdout.Fd(),
|
||||||
|
os.Stderr.Fd(),
|
||||||
|
} {
|
||||||
|
if err := syscall.Fchown(int(fd), rootuid, rootuid); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If systemd is supporting sd_notify protocol, this function will add support
|
||||||
|
// for sd_notify protocol from within the container.
|
||||||
|
func setupSdNotify(spec *specs.Spec, notifySocket string) {
|
||||||
|
spec.Mounts = append(spec.Mounts, specs.Mount{Destination: notifySocket, Type: "bind", Source: notifySocket, Options: []string{"bind"}})
|
||||||
|
spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket))
|
||||||
|
}
|
||||||
|
|
||||||
|
func destroy(container libcontainer.Container) {
|
||||||
|
if err := container.Destroy(); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupIO sets the proper IO on the process depending on the configuration
|
||||||
|
// If there is a nil error then there must be a non nil tty returned
|
||||||
|
func setupIO(process *libcontainer.Process, rootuid int, console string, createTTY, detach bool) (*tty, error) {
|
||||||
|
// detach and createTty will not work unless a console path is passed
|
||||||
|
// so error out here before changing any terminal settings
|
||||||
|
if createTTY && detach && console == "" {
|
||||||
|
return nil, fmt.Errorf("cannot allocate tty if runc will detach")
|
||||||
|
}
|
||||||
|
if createTTY {
|
||||||
|
return createTty(process, rootuid, console)
|
||||||
|
}
|
||||||
|
if detach {
|
||||||
|
if err := dupStdio(process, rootuid); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &tty{}, nil
|
||||||
|
}
|
||||||
|
return createStdioPipes(process, rootuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createPidFile creates a file with the processes pid inside it atomically
|
||||||
|
// it creates a temp file with the paths filename + '.' infront of it
|
||||||
|
// then renames the file
|
||||||
|
func createPidFile(path string, process *libcontainer.Process) error {
|
||||||
|
pid, err := process.Pid()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
tmpDir = filepath.Dir(path)
|
||||||
|
tmpName = filepath.Join(tmpDir, fmt.Sprintf(".%s", filepath.Base(path)))
|
||||||
|
)
|
||||||
|
f, err := os.OpenFile(tmpName, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprintf(f, "%d", pid)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.Rename(tmpName, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error) {
|
||||||
|
config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{
|
||||||
|
CgroupName: id,
|
||||||
|
UseSystemdCgroup: context.GlobalBool("systemd-cgroup"),
|
||||||
|
NoPivotRoot: context.Bool("no-pivot"),
|
||||||
|
Spec: spec,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(config.Rootfs); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("rootfs (%q) does not exist", config.Rootfs)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
factory, err := loadFactory(context)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return factory.Create(id, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
type runner struct {
|
||||||
|
enableSubreaper bool
|
||||||
|
shouldDestroy bool
|
||||||
|
detach bool
|
||||||
|
listenFDs []*os.File
|
||||||
|
pidFile string
|
||||||
|
console string
|
||||||
|
container libcontainer.Container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runner) run(config *specs.Process) (int, error) {
|
||||||
|
process, err := newProcess(*config)
|
||||||
|
if err != nil {
|
||||||
|
r.destroy()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
if len(r.listenFDs) > 0 {
|
||||||
|
process.Env = append(process.Env, fmt.Sprintf("LISTEN_FDS=%d", len(r.listenFDs)), "LISTEN_PID=1")
|
||||||
|
process.ExtraFiles = append(process.ExtraFiles, r.listenFDs...)
|
||||||
|
}
|
||||||
|
rootuid, err := r.container.Config().HostUID()
|
||||||
|
if err != nil {
|
||||||
|
r.destroy()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
tty, err := setupIO(process, rootuid, r.console, config.Terminal, r.detach)
|
||||||
|
if err != nil {
|
||||||
|
r.destroy()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
handler := newSignalHandler(tty, r.enableSubreaper)
|
||||||
|
if err := r.container.Start(process); err != nil {
|
||||||
|
r.destroy()
|
||||||
|
tty.Close()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
if err := tty.ClosePostStart(); err != nil {
|
||||||
|
r.terminate(process)
|
||||||
|
r.destroy()
|
||||||
|
tty.Close()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
if r.pidFile != "" {
|
||||||
|
if err := createPidFile(r.pidFile, process); err != nil {
|
||||||
|
r.terminate(process)
|
||||||
|
r.destroy()
|
||||||
|
tty.Close()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if r.detach {
|
||||||
|
tty.Close()
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
status, err := handler.forward(process)
|
||||||
|
if err != nil {
|
||||||
|
r.terminate(process)
|
||||||
|
}
|
||||||
|
r.destroy()
|
||||||
|
tty.Close()
|
||||||
|
return status, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runner) destroy() {
|
||||||
|
if r.shouldDestroy {
|
||||||
|
destroy(r.container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *runner) terminate(p *libcontainer.Process) {
|
||||||
|
p.Signal(syscall.SIGKILL)
|
||||||
|
p.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateProcessSpec(spec *specs.Process) error {
|
||||||
|
if spec.Cwd == "" {
|
||||||
|
return fmt.Errorf("Cwd property must not be empty")
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(spec.Cwd) {
|
||||||
|
return fmt.Errorf("Cwd must be an absolute path")
|
||||||
|
}
|
||||||
|
if len(spec.Args) == 0 {
|
||||||
|
return fmt.Errorf("args must not be empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue