Implement nsinit state command
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
5df859ad24
commit
91a3f162af
5
error.go
5
error.go
|
@ -15,6 +15,7 @@ const (
|
|||
ContainerNotExists
|
||||
ContainerPaused
|
||||
ContainerNotStopped
|
||||
ContainerNotRunning
|
||||
|
||||
// Common errors
|
||||
ConfigInvalid
|
||||
|
@ -36,7 +37,9 @@ func (c ErrorCode) String() string {
|
|||
case ContainerNotExists:
|
||||
return "Container does not exist"
|
||||
case ContainerNotStopped:
|
||||
return "Container isn't stopped"
|
||||
return "Container is not stopped"
|
||||
case ContainerNotRunning:
|
||||
return "Container is not running"
|
||||
default:
|
||||
return "Unknown error"
|
||||
}
|
||||
|
|
|
@ -11,12 +11,14 @@ import (
|
|||
|
||||
var errorTemplate = template.Must(template.New("error").Parse(`Timestamp: {{.Timestamp}}
|
||||
Code: {{.ECode}}
|
||||
{{if .Err }}
|
||||
Message: {{.Err.Error}}
|
||||
{{end}}
|
||||
Frames:{{range $i, $frame := .Stack.Frames}}
|
||||
---
|
||||
{{$i}}: {{$frame.Function}}
|
||||
Package: {{$frame.Package}}
|
||||
File: {{$frame.File}}{{end}}
|
||||
File: {{$frame.File}}@{{$frame.Line}}{{end}}
|
||||
`))
|
||||
|
||||
func newGenericError(err error, c ErrorCode) Error {
|
||||
|
@ -27,7 +29,7 @@ func newGenericError(err error, c ErrorCode) Error {
|
|||
Timestamp: time.Now(),
|
||||
Err: err,
|
||||
ECode: c,
|
||||
Stack: stacktrace.Capture(2),
|
||||
Stack: stacktrace.Capture(1),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +41,7 @@ func newSystemError(err error) Error {
|
|||
Timestamp: time.Now(),
|
||||
Err: err,
|
||||
ECode: SystemError,
|
||||
Stack: stacktrace.Capture(2),
|
||||
Stack: stacktrace.Capture(1),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -184,7 +184,6 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
|
|||
|
||||
func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) *initProcess {
|
||||
t := "_LIBCONTAINER_INITTYPE=standard"
|
||||
|
||||
cloneFlags := c.config.Namespaces.CloneFlags()
|
||||
if cloneFlags&syscall.CLONE_NEWUSER != 0 {
|
||||
c.addUidGidMappings(cmd.SysProcAttr)
|
||||
|
@ -225,6 +224,8 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
|
|||
Config: c.config,
|
||||
Args: process.Args,
|
||||
Env: process.Env,
|
||||
User: process.User,
|
||||
Cwd: process.Cwd,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,6 +274,7 @@ func (c *linuxContainer) Destroy() error {
|
|||
if rerr := os.RemoveAll(c.root); err == nil {
|
||||
err = rerr
|
||||
}
|
||||
c.initProcess = nil
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -285,6 +287,9 @@ func (c *linuxContainer) Resume() error {
|
|||
}
|
||||
|
||||
func (c *linuxContainer) Signal(signal os.Signal) error {
|
||||
if c.initProcess == nil {
|
||||
return newGenericError(nil, ContainerNotRunning)
|
||||
}
|
||||
return c.initProcess.signal(signal)
|
||||
}
|
||||
|
||||
|
|
|
@ -54,15 +54,15 @@ func (p *setnsProcess) signal(s os.Signal) error {
|
|||
func (p *setnsProcess) start() (err error) {
|
||||
defer p.parentPipe.Close()
|
||||
if p.forkedProcess, err = p.execSetns(); err != nil {
|
||||
return err
|
||||
return newSystemError(err)
|
||||
}
|
||||
if len(p.cgroupPaths) > 0 {
|
||||
if err := cgroups.EnterPid(p.cgroupPaths, p.forkedProcess.Pid); err != nil {
|
||||
return err
|
||||
return newSystemError(err)
|
||||
}
|
||||
}
|
||||
if err := json.NewEncoder(p.parentPipe).Encode(p.config); err != nil {
|
||||
return err
|
||||
return newSystemError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -75,18 +75,18 @@ func (p *setnsProcess) execSetns() (*os.Process, error) {
|
|||
err := p.cmd.Start()
|
||||
p.childPipe.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, newSystemError(err)
|
||||
}
|
||||
status, err := p.cmd.Process.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, newSystemError(err)
|
||||
}
|
||||
if !status.Success() {
|
||||
return nil, &exec.ExitError{status}
|
||||
return nil, newSystemError(&exec.ExitError{status})
|
||||
}
|
||||
var pid *pid
|
||||
if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil {
|
||||
return nil, err
|
||||
return nil, newSystemError(err)
|
||||
}
|
||||
return os.FindProcess(pid.Pid)
|
||||
}
|
||||
|
@ -129,12 +129,12 @@ func (p *initProcess) start() error {
|
|||
err := p.cmd.Start()
|
||||
p.childPipe.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
return newSystemError(err)
|
||||
}
|
||||
// Do this before syncing with child so that no children
|
||||
// can escape the cgroup
|
||||
if err := p.manager.Apply(p.pid()); err != nil {
|
||||
return err
|
||||
return newSystemError(err)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
|
@ -143,13 +143,13 @@ func (p *initProcess) start() error {
|
|||
}
|
||||
}()
|
||||
if err := p.createNetworkInterfaces(); err != nil {
|
||||
return err
|
||||
return newSystemError(err)
|
||||
}
|
||||
// Start the setup process to setup the init process
|
||||
if p.cmd.SysProcAttr.Cloneflags&syscall.CLONE_NEWUSER != 0 {
|
||||
parent, err := p.newUsernsSetupProcess()
|
||||
if err != nil {
|
||||
return err
|
||||
return newSystemError(err)
|
||||
}
|
||||
if err := parent.start(); err != nil {
|
||||
if err := parent.terminate(); err != nil {
|
||||
|
@ -158,20 +158,20 @@ func (p *initProcess) start() error {
|
|||
return err
|
||||
}
|
||||
if _, err := parent.wait(); err != nil {
|
||||
return err
|
||||
return newSystemError(err)
|
||||
}
|
||||
}
|
||||
if err := p.sendConfig(); err != nil {
|
||||
return err
|
||||
return newSystemError(err)
|
||||
}
|
||||
// wait for the child process to fully complete and receive an error message
|
||||
// if one was encoutered
|
||||
var ierr *initError
|
||||
if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF {
|
||||
return err
|
||||
return newSystemError(err)
|
||||
}
|
||||
if ierr != nil {
|
||||
return ierr
|
||||
return newSystemError(ierr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ func (p *initProcess) createNetworkInterfaces() error {
|
|||
func (p *initProcess) newUsernsSetupProcess() (parentProcess, error) {
|
||||
parentPipe, childPipe, err := newPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, newSystemError(err)
|
||||
}
|
||||
cmd := exec.Command(p.cmd.Args[0], p.cmd.Args[1:]...)
|
||||
cmd.ExtraFiles = []*os.File{childPipe}
|
||||
|
|
|
@ -21,7 +21,8 @@ func (l *linuxUsernsInit) Init() error {
|
|||
}
|
||||
consolePath := l.config.Config.Console
|
||||
if consolePath != "" {
|
||||
console := newConsoleFromPath(consolePath)
|
||||
// TODO: why is this hard coded?
|
||||
console := newConsoleFromPath("/dev/console")
|
||||
if err := console.dupStdio(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -12,14 +12,30 @@ import (
|
|||
"github.com/docker/libcontainer/configs"
|
||||
)
|
||||
|
||||
var createFlags = []cli.Flag{
|
||||
cli.IntFlag{Name: "parent-death-signal", Usage: "set the signal that will be delivered to the process incase the parent dies"},
|
||||
cli.BoolFlag{Name: "read-only", Usage: "set the container's rootfs as read-only"},
|
||||
cli.StringSliceFlag{Name: "bind", Value: &cli.StringSlice{}, Usage: "add bind mounts to the container"},
|
||||
cli.StringSliceFlag{Name: "tmpfs", Value: &cli.StringSlice{}, Usage: "add tmpfs mounts to the container"},
|
||||
cli.IntFlag{Name: "cpushares", Usage: "set the cpushares for the container"},
|
||||
cli.IntFlag{Name: "memory-limit", Usage: "set the memory limit for the container"},
|
||||
cli.IntFlag{Name: "memory-swap", Usage: "set the memory swap limit for the container"},
|
||||
cli.StringFlag{Name: "cpuset-cpus", Usage: "set the cpuset cpus"},
|
||||
cli.StringFlag{Name: "cpuset-mems", Usage: "set the cpuset mems"},
|
||||
cli.StringFlag{Name: "apparmor-profile", Usage: "set the apparmor profile"},
|
||||
cli.StringFlag{Name: "process-label", Usage: "set the process label"},
|
||||
cli.StringFlag{Name: "mount-label", Usage: "set the mount label"},
|
||||
}
|
||||
|
||||
var configCommand = cli.Command{
|
||||
Name: "config",
|
||||
Usage: "generate a standard configuration file for a container",
|
||||
Flags: []cli.Flag{
|
||||
Flags: append([]cli.Flag{
|
||||
cli.StringFlag{Name: "file,f", Value: "stdout", Usage: "write the configuration to the specified file"},
|
||||
},
|
||||
}, createFlags...),
|
||||
Action: func(context *cli.Context) {
|
||||
template := getTemplate()
|
||||
modify(template, context)
|
||||
data, err := json.MarshalIndent(template, "", "\t")
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
|
@ -41,6 +57,19 @@ var configCommand = cli.Command{
|
|||
},
|
||||
}
|
||||
|
||||
func modify(config *configs.Config, context *cli.Context) {
|
||||
config.ParentDeathSignal = context.Int("parent-death-signal")
|
||||
config.Readonlyfs = context.Bool("read-only")
|
||||
config.Cgroups.CpusetCpus = context.String("cpuset-cpus")
|
||||
config.Cgroups.CpusetMems = context.String("cpuset-mems")
|
||||
config.Cgroups.CpuShares = int64(context.Int("cpushares"))
|
||||
config.Cgroups.Memory = int64(context.Int("memory-limit"))
|
||||
config.Cgroups.MemorySwap = int64(context.Int("memory-swap"))
|
||||
config.AppArmorProfile = context.String("apparmor-profile")
|
||||
config.ProcessLabel = context.String("process-label")
|
||||
config.MountLabel = context.String("mount-label")
|
||||
}
|
||||
|
||||
func getTemplate() *configs.Config {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
|
@ -91,14 +120,8 @@ func getTemplate() *configs.Config {
|
|||
Rlimits: []configs.Rlimit{
|
||||
{
|
||||
Type: syscall.RLIMIT_NOFILE,
|
||||
Hard: uint64(1024),
|
||||
Soft: uint64(1024),
|
||||
},
|
||||
},
|
||||
Mounts: []*configs.Mount{
|
||||
{
|
||||
Type: "tmpfs",
|
||||
Destination: "/tmp",
|
||||
Hard: 1024,
|
||||
Soft: 1024,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -20,13 +20,14 @@ var execCommand = cli.Command{
|
|||
Name: "exec",
|
||||
Usage: "execute a new command inside a container",
|
||||
Action: execAction,
|
||||
Flags: []cli.Flag{
|
||||
Flags: append([]cli.Flag{
|
||||
cli.BoolFlag{Name: "tty,t", Usage: "allocate a TTY to the container"},
|
||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||
cli.StringFlag{Name: "config", Value: "container.json", Usage: "path to the configuration file"},
|
||||
cli.BoolFlag{Name: "create", Usage: "create the container's configuration on the fly with arguments"},
|
||||
cli.StringFlag{Name: "user,u", Value: "root", Usage: "set the user, uid, and/or gid for the process"},
|
||||
cli.StringSliceFlag{Name: "env", Value: standardEnvironment, Usage: "set environment variables for the process"},
|
||||
},
|
||||
}, createFlags...),
|
||||
}
|
||||
|
||||
func execAction(context *cli.Context) {
|
||||
|
@ -38,6 +39,7 @@ func execAction(context *cli.Context) {
|
|||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
created := false
|
||||
container, err := factory.Load(context.String("id"))
|
||||
if err != nil {
|
||||
if lerr, ok := err.(libcontainer.Error); !ok || lerr.Code() != libcontainer.ContainerNotExists {
|
||||
|
@ -45,10 +47,15 @@ func execAction(context *cli.Context) {
|
|||
}
|
||||
config, err := loadConfig(context)
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
config.Console = tty.console.Path()
|
||||
if tty.console != nil {
|
||||
config.Console = tty.console.Path()
|
||||
}
|
||||
created = true
|
||||
if container, err = factory.Create(context.String("id"), config); err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -64,19 +71,26 @@ func execAction(context *cli.Context) {
|
|||
tty.attach(process)
|
||||
pid, err := container.Start(process)
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
proc, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
status, err := proc.Wait()
|
||||
if err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
if err := container.Destroy(); err != nil {
|
||||
fatal(err)
|
||||
if created {
|
||||
if err := container.Destroy(); err != nil {
|
||||
tty.Close()
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
tty.Close()
|
||||
os.Exit(utils.ExitStatus(status.Sys().(syscall.WaitStatus)))
|
||||
}
|
||||
|
||||
|
|
|
@ -18,13 +18,14 @@ func main() {
|
|||
cli.StringFlag{Name: "root", Value: ".", Usage: "root directory for containers"},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
configCommand,
|
||||
execCommand,
|
||||
initCommand,
|
||||
oomCommand,
|
||||
pauseCommand,
|
||||
statsCommand,
|
||||
unpauseCommand,
|
||||
configCommand,
|
||||
stateCommand,
|
||||
}
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var stateCommand = cli.Command{
|
||||
Name: "state",
|
||||
Usage: "get the container's current state",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "id", Value: "nsinit", Usage: "specify the ID for a container"},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
state, err := container.State()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
data, err := json.MarshalIndent(state, "", "\t")
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
fmt.Printf("%s", data)
|
||||
},
|
||||
}
|
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
@ -17,15 +16,15 @@ var statsCommand = cli.Command{
|
|||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatal(err)
|
||||
}
|
||||
stats, err := container.Stats()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fatal(err)
|
||||
}
|
||||
data, jerr := json.MarshalIndent(stats, "", "\t")
|
||||
data, err := json.MarshalIndent(stats, "", "\t")
|
||||
if err != nil {
|
||||
log.Fatal(jerr)
|
||||
fatal(err)
|
||||
}
|
||||
fmt.Printf("%s", data)
|
||||
},
|
||||
|
|
|
@ -11,6 +11,11 @@ import (
|
|||
)
|
||||
|
||||
func loadConfig(context *cli.Context) (*configs.Config, error) {
|
||||
if context.Bool("create") {
|
||||
config := getTemplate()
|
||||
modify(config, context)
|
||||
return config, nil
|
||||
}
|
||||
f, err := os.Open(context.String("config"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Reference in New Issue