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
|
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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 (
|
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)
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue