Implement nsinit state command

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2015-02-11 17:42:58 -08:00
parent 5df859ad24
commit 91a3f162af
11 changed files with 127 additions and 43 deletions

View File

@ -15,6 +15,7 @@ const (
ContainerNotExists ContainerNotExists
ContainerPaused ContainerPaused
ContainerNotStopped ContainerNotStopped
ContainerNotRunning
// Common errors // Common errors
ConfigInvalid ConfigInvalid
@ -36,7 +37,9 @@ func (c ErrorCode) String() string {
case ContainerNotExists: case ContainerNotExists:
return "Container does not exist" return "Container does not exist"
case ContainerNotStopped: case ContainerNotStopped:
return "Container isn't stopped" return "Container is not stopped"
case ContainerNotRunning:
return "Container is not running"
default: default:
return "Unknown error" return "Unknown error"
} }

View File

@ -11,12 +11,14 @@ import (
var errorTemplate = template.Must(template.New("error").Parse(`Timestamp: {{.Timestamp}} var errorTemplate = template.Must(template.New("error").Parse(`Timestamp: {{.Timestamp}}
Code: {{.ECode}} Code: {{.ECode}}
{{if .Err }}
Message: {{.Err.Error}} Message: {{.Err.Error}}
{{end}}
Frames:{{range $i, $frame := .Stack.Frames}} Frames:{{range $i, $frame := .Stack.Frames}}
--- ---
{{$i}}: {{$frame.Function}} {{$i}}: {{$frame.Function}}
Package: {{$frame.Package}} Package: {{$frame.Package}}
File: {{$frame.File}}{{end}} File: {{$frame.File}}@{{$frame.Line}}{{end}}
`)) `))
func newGenericError(err error, c ErrorCode) Error { func newGenericError(err error, c ErrorCode) Error {
@ -27,7 +29,7 @@ func newGenericError(err error, c ErrorCode) Error {
Timestamp: time.Now(), Timestamp: time.Now(),
Err: err, Err: err,
ECode: c, ECode: c,
Stack: stacktrace.Capture(2), Stack: stacktrace.Capture(1),
} }
} }
@ -39,7 +41,7 @@ func newSystemError(err error) Error {
Timestamp: time.Now(), Timestamp: time.Now(),
Err: err, Err: err,
ECode: SystemError, ECode: SystemError,
Stack: stacktrace.Capture(2), Stack: stacktrace.Capture(1),
} }
} }

View File

@ -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 { func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe *os.File) *initProcess {
t := "_LIBCONTAINER_INITTYPE=standard" t := "_LIBCONTAINER_INITTYPE=standard"
cloneFlags := c.config.Namespaces.CloneFlags() cloneFlags := c.config.Namespaces.CloneFlags()
if cloneFlags&syscall.CLONE_NEWUSER != 0 { if cloneFlags&syscall.CLONE_NEWUSER != 0 {
c.addUidGidMappings(cmd.SysProcAttr) c.addUidGidMappings(cmd.SysProcAttr)
@ -225,6 +224,8 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
Config: c.config, Config: c.config,
Args: process.Args, Args: process.Args,
Env: process.Env, 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 { if rerr := os.RemoveAll(c.root); err == nil {
err = rerr err = rerr
} }
c.initProcess = nil
return err return err
} }
@ -285,6 +287,9 @@ func (c *linuxContainer) Resume() error {
} }
func (c *linuxContainer) Signal(signal os.Signal) error { func (c *linuxContainer) Signal(signal os.Signal) error {
if c.initProcess == nil {
return newGenericError(nil, ContainerNotRunning)
}
return c.initProcess.signal(signal) return c.initProcess.signal(signal)
} }

View File

@ -54,15 +54,15 @@ func (p *setnsProcess) signal(s os.Signal) error {
func (p *setnsProcess) start() (err error) { func (p *setnsProcess) start() (err error) {
defer p.parentPipe.Close() defer p.parentPipe.Close()
if p.forkedProcess, err = p.execSetns(); err != nil { if p.forkedProcess, err = p.execSetns(); err != nil {
return err return newSystemError(err)
} }
if len(p.cgroupPaths) > 0 { if len(p.cgroupPaths) > 0 {
if err := cgroups.EnterPid(p.cgroupPaths, p.forkedProcess.Pid); err != nil { 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 { if err := json.NewEncoder(p.parentPipe).Encode(p.config); err != nil {
return err return newSystemError(err)
} }
return nil return nil
} }
@ -75,18 +75,18 @@ func (p *setnsProcess) execSetns() (*os.Process, error) {
err := p.cmd.Start() err := p.cmd.Start()
p.childPipe.Close() p.childPipe.Close()
if err != nil { if err != nil {
return nil, err return nil, newSystemError(err)
} }
status, err := p.cmd.Process.Wait() status, err := p.cmd.Process.Wait()
if err != nil { if err != nil {
return nil, err return nil, newSystemError(err)
} }
if !status.Success() { if !status.Success() {
return nil, &exec.ExitError{status} return nil, newSystemError(&exec.ExitError{status})
} }
var pid *pid var pid *pid
if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil { if err := json.NewDecoder(p.parentPipe).Decode(&pid); err != nil {
return nil, err return nil, newSystemError(err)
} }
return os.FindProcess(pid.Pid) return os.FindProcess(pid.Pid)
} }
@ -129,12 +129,12 @@ func (p *initProcess) start() error {
err := p.cmd.Start() err := p.cmd.Start()
p.childPipe.Close() p.childPipe.Close()
if err != nil { if err != nil {
return err return newSystemError(err)
} }
// Do this before syncing with child so that no children // Do this before syncing with child so that no children
// can escape the cgroup // can escape the cgroup
if err := p.manager.Apply(p.pid()); err != nil { if err := p.manager.Apply(p.pid()); err != nil {
return err return newSystemError(err)
} }
defer func() { defer func() {
if err != nil { if err != nil {
@ -143,13 +143,13 @@ func (p *initProcess) start() error {
} }
}() }()
if err := p.createNetworkInterfaces(); err != nil { if err := p.createNetworkInterfaces(); err != nil {
return err return newSystemError(err)
} }
// Start the setup process to setup the init process // Start the setup process to setup the init process
if p.cmd.SysProcAttr.Cloneflags&syscall.CLONE_NEWUSER != 0 { if p.cmd.SysProcAttr.Cloneflags&syscall.CLONE_NEWUSER != 0 {
parent, err := p.newUsernsSetupProcess() parent, err := p.newUsernsSetupProcess()
if err != nil { if err != nil {
return err return newSystemError(err)
} }
if err := parent.start(); err != nil { if err := parent.start(); err != nil {
if err := parent.terminate(); err != nil { if err := parent.terminate(); err != nil {
@ -158,20 +158,20 @@ func (p *initProcess) start() error {
return err return err
} }
if _, err := parent.wait(); err != nil { if _, err := parent.wait(); err != nil {
return err return newSystemError(err)
} }
} }
if err := p.sendConfig(); err != nil { if err := p.sendConfig(); err != nil {
return err return newSystemError(err)
} }
// wait for the child process to fully complete and receive an error message // wait for the child process to fully complete and receive an error message
// if one was encoutered // if one was encoutered
var ierr *initError var ierr *initError
if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF { if err := json.NewDecoder(p.parentPipe).Decode(&ierr); err != nil && err != io.EOF {
return err return newSystemError(err)
} }
if ierr != nil { if ierr != nil {
return ierr return newSystemError(ierr)
} }
return nil return nil
} }
@ -232,7 +232,7 @@ func (p *initProcess) createNetworkInterfaces() error {
func (p *initProcess) newUsernsSetupProcess() (parentProcess, error) { func (p *initProcess) newUsernsSetupProcess() (parentProcess, error) {
parentPipe, childPipe, err := newPipe() parentPipe, childPipe, err := newPipe()
if err != nil { if err != nil {
return nil, err return nil, newSystemError(err)
} }
cmd := exec.Command(p.cmd.Args[0], p.cmd.Args[1:]...) cmd := exec.Command(p.cmd.Args[0], p.cmd.Args[1:]...)
cmd.ExtraFiles = []*os.File{childPipe} cmd.ExtraFiles = []*os.File{childPipe}

View File

@ -21,7 +21,8 @@ func (l *linuxUsernsInit) Init() error {
} }
consolePath := l.config.Config.Console consolePath := l.config.Config.Console
if consolePath != "" { if consolePath != "" {
console := newConsoleFromPath(consolePath) // TODO: why is this hard coded?
console := newConsoleFromPath("/dev/console")
if err := console.dupStdio(); err != nil { if err := console.dupStdio(); err != nil {
return err return err
} }

View File

@ -12,14 +12,30 @@ import (
"github.com/docker/libcontainer/configs" "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{ var configCommand = cli.Command{
Name: "config", Name: "config",
Usage: "generate a standard configuration file for a container", 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"}, cli.StringFlag{Name: "file,f", Value: "stdout", Usage: "write the configuration to the specified file"},
}, }, createFlags...),
Action: func(context *cli.Context) { Action: func(context *cli.Context) {
template := getTemplate() template := getTemplate()
modify(template, context)
data, err := json.MarshalIndent(template, "", "\t") data, err := json.MarshalIndent(template, "", "\t")
if err != nil { if err != nil {
fatal(err) 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 { func getTemplate() *configs.Config {
cwd, err := os.Getwd() cwd, err := os.Getwd()
if err != nil { if err != nil {
@ -91,14 +120,8 @@ func getTemplate() *configs.Config {
Rlimits: []configs.Rlimit{ Rlimits: []configs.Rlimit{
{ {
Type: syscall.RLIMIT_NOFILE, Type: syscall.RLIMIT_NOFILE,
Hard: uint64(1024), Hard: 1024,
Soft: uint64(1024), Soft: 1024,
},
},
Mounts: []*configs.Mount{
{
Type: "tmpfs",
Destination: "/tmp",
}, },
}, },
} }

View File

@ -20,13 +20,14 @@ var execCommand = cli.Command{
Name: "exec", Name: "exec",
Usage: "execute a new command inside a container", Usage: "execute a new command inside a container",
Action: execAction, Action: execAction,
Flags: []cli.Flag{ Flags: append([]cli.Flag{
cli.BoolFlag{Name: "tty,t", Usage: "allocate a TTY to the container"}, 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: "id", Value: "nsinit", Usage: "specify the ID for a container"},
cli.StringFlag{Name: "config", Value: "container.json", Usage: "path to the configuration file"}, 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.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"}, cli.StringSliceFlag{Name: "env", Value: standardEnvironment, Usage: "set environment variables for the process"},
}, }, createFlags...),
} }
func execAction(context *cli.Context) { func execAction(context *cli.Context) {
@ -38,6 +39,7 @@ func execAction(context *cli.Context) {
if err != nil { if err != nil {
fatal(err) fatal(err)
} }
created := false
container, err := factory.Load(context.String("id")) container, err := factory.Load(context.String("id"))
if err != nil { if err != nil {
if lerr, ok := err.(libcontainer.Error); !ok || lerr.Code() != libcontainer.ContainerNotExists { if lerr, ok := err.(libcontainer.Error); !ok || lerr.Code() != libcontainer.ContainerNotExists {
@ -45,10 +47,15 @@ func execAction(context *cli.Context) {
} }
config, err := loadConfig(context) config, err := loadConfig(context)
if err != nil { if err != nil {
tty.Close()
fatal(err) 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 { if container, err = factory.Create(context.String("id"), config); err != nil {
tty.Close()
fatal(err) fatal(err)
} }
} }
@ -64,19 +71,26 @@ func execAction(context *cli.Context) {
tty.attach(process) tty.attach(process)
pid, err := container.Start(process) pid, err := container.Start(process)
if err != nil { if err != nil {
tty.Close()
fatal(err) fatal(err)
} }
proc, err := os.FindProcess(pid) proc, err := os.FindProcess(pid)
if err != nil { if err != nil {
tty.Close()
fatal(err) fatal(err)
} }
status, err := proc.Wait() status, err := proc.Wait()
if err != nil { if err != nil {
tty.Close()
fatal(err) fatal(err)
} }
if err := container.Destroy(); err != nil { if created {
fatal(err) if err := container.Destroy(); err != nil {
tty.Close()
fatal(err)
}
} }
tty.Close()
os.Exit(utils.ExitStatus(status.Sys().(syscall.WaitStatus))) os.Exit(utils.ExitStatus(status.Sys().(syscall.WaitStatus)))
} }

View File

@ -18,13 +18,14 @@ func main() {
cli.StringFlag{Name: "root", Value: ".", Usage: "root directory for containers"}, cli.StringFlag{Name: "root", Value: ".", Usage: "root directory for containers"},
} }
app.Commands = []cli.Command{ app.Commands = []cli.Command{
configCommand,
execCommand, execCommand,
initCommand, initCommand,
oomCommand, oomCommand,
pauseCommand, pauseCommand,
statsCommand, statsCommand,
unpauseCommand, unpauseCommand,
configCommand, stateCommand,
} }
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {
log.Fatal(err) log.Fatal(err)

31
nsinit/state.go Normal file
View File

@ -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)
},
}

View File

@ -3,7 +3,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
) )
@ -17,15 +16,15 @@ var statsCommand = cli.Command{
Action: func(context *cli.Context) { Action: func(context *cli.Context) {
container, err := getContainer(context) container, err := getContainer(context)
if err != nil { if err != nil {
log.Fatal(err) fatal(err)
} }
stats, err := container.Stats() stats, err := container.Stats()
if err != nil { if err != nil {
log.Fatal(err) fatal(err)
} }
data, jerr := json.MarshalIndent(stats, "", "\t") data, err := json.MarshalIndent(stats, "", "\t")
if err != nil { if err != nil {
log.Fatal(jerr) fatal(err)
} }
fmt.Printf("%s", data) fmt.Printf("%s", data)
}, },

View File

@ -11,6 +11,11 @@ import (
) )
func loadConfig(context *cli.Context) (*configs.Config, error) { 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")) f, err := os.Open(context.String("config"))
if err != nil { if err != nil {
return nil, err return nil, err