diff --git a/containerd/containerd.go b/containerd/containerd.go index 5d115721..0088a9d6 100644 --- a/containerd/containerd.go +++ b/containerd/containerd.go @@ -76,7 +76,7 @@ var ContainerdCommand = cli.Command{ return nil }, - Action: func(context *cli.Context) { + Action: func(context *cli.Context) error { driver := context.GlobalString("driver") kernel := context.GlobalString("kernel") initrd := context.GlobalString("initrd") @@ -95,13 +95,15 @@ var ContainerdCommand = cli.Command{ path := filepath.Join(template, "config.json") f, err := os.Open(path) if err != nil { - glog.Errorf("open template JSON configuration file failed: %v", err) - os.Exit(-1) + err = fmt.Errorf("open template JSON configuration file failed: %v", err) + glog.Error(err) + return cli.NewExitError(err.Error(), -1) } if err := json.NewDecoder(f).Decode(&tconfig); err != nil { - glog.Errorf("parse template JSON configuration file failed: %v", err) + err = fmt.Errorf("parse template JSON configuration file failed: %v", err) + glog.Error(err) f.Close() - os.Exit(-1) + return cli.NewExitError(err.Error(), -1) } f.Close() @@ -116,15 +118,16 @@ var ContainerdCommand = cli.Command{ driver = tconfig.Driver } } else if (bios == "" || cbfs == "") && (kernel == "" || initrd == "") { - glog.Error("argument kernel+initrd or bios+cbfs must be set") - os.Exit(1) + err := fmt.Errorf("argument kernel+initrd or bios+cbfs must be set") + glog.Error(err) + return cli.NewExitError(err.Error(), -1) } hypervisor.InterfaceCount = 0 var err error if hypervisor.HDriver, err = driverloader.Probe(driver); err != nil { glog.Errorf("%v", err) - os.Exit(1) + return cli.NewExitError(err.Error(), -1) } var f factory.Factory @@ -144,7 +147,7 @@ var ContainerdCommand = cli.Command{ context.GlobalInt("default_cpus"), context.GlobalInt("default_memory")) if err != nil { glog.Errorf("%v", err) - os.Exit(1) + return cli.NewExitError(err.Error(), -1) } if context.Bool("solo-namespaced") { @@ -153,12 +156,13 @@ var ContainerdCommand = cli.Command{ if err = daemon(sv, context.String("listen")); err != nil { glog.Errorf("%v", err) - os.Exit(1) + return cli.NewExitError(err.Error(), -1) } if context.Bool("solo-namespaced") { os.RemoveAll(containerdDir) } + return nil }, } diff --git a/create.go b/create.go index 873203a5..51deba2d 100644 --- a/create.go +++ b/create.go @@ -57,39 +57,39 @@ command(s) that get executed on start, edit the args parameter of the spec. See Usage: "[ignore on runv] do not use pivot root to jail process inside rootfs. This should be used whenever the rootfs is on top of a ramdisk", }, }, - Action: func(context *cli.Context) { - runContainer(context, true) + Action: func(context *cli.Context) error { + if err := runContainer(context, true); err != nil { + return cli.NewExitError(fmt.Sprintf("Run Container error: %v", err), -1) + } + return nil }, } -func runContainer(context *cli.Context, createOnly bool) { +func runContainer(context *cli.Context, createOnly bool) error { root := context.GlobalString("root") bundle := context.String("bundle") container := context.Args().First() ocffile := filepath.Join(bundle, specConfig) spec, err := loadSpec(ocffile) if err != nil { - fmt.Fprintf(os.Stderr, "load config failed: %v\n", err) - os.Exit(-1) + return fmt.Errorf("load config failed: %v", err) } if spec.Linux == nil { - fmt.Fprintf(os.Stderr, "it is not linux container config\n") - os.Exit(-1) + return fmt.Errorf("it is not linux container config") } if os.Geteuid() != 0 { - fmt.Fprintf(os.Stderr, "runv should be run as root\n") - os.Exit(-1) + return fmt.Errorf("runv should be run as root") } if container == "" { - fmt.Fprintf(os.Stderr, "no container id provided\n") - os.Exit(-1) + return fmt.Errorf("no container id provided") } _, err = os.Stat(filepath.Join(root, container)) if err == nil { - fmt.Fprintf(os.Stderr, "Container %q exists\n", container) - os.Exit(-1) + return fmt.Errorf("container %q exists", container) + } + if err = checkConsole(context, &spec.Process, createOnly); err != nil { + return err } - checkConsole(context, &spec.Process, createOnly) var sharedContainer string if containerType, ok := spec.Annotations["ocid/container_type"]; ok { @@ -100,24 +100,20 @@ func runContainer(context *cli.Context, createOnly bool) { for _, ns := range spec.Linux.Namespaces { if ns.Path != "" { if strings.Contains(ns.Path, "/") { - fmt.Fprintf(os.Stderr, "Runv doesn't support path to namespace file, it supports containers name as shared namespaces only\n") - os.Exit(-1) + return fmt.Errorf("Runv doesn't support path to namespace file, it supports containers name as shared namespaces only") } if ns.Type == "mount" { // TODO support it! - fmt.Fprintf(os.Stderr, "Runv doesn't support shared mount namespace currently\n") - os.Exit(-1) + return fmt.Errorf("Runv doesn't support shared mount namespace currently") } sharedContainer = ns.Path _, err = os.Stat(filepath.Join(root, sharedContainer, stateJson)) if err != nil { - fmt.Fprintf(os.Stderr, "The container %q is not existing or not ready\n", sharedContainer) - os.Exit(-1) + return fmt.Errorf("The container %q is not existing or not ready", sharedContainer) } _, err = os.Stat(filepath.Join(root, sharedContainer, "namespace")) if err != nil { - fmt.Fprintf(os.Stderr, "The container %q is not ready\n", sharedContainer) - os.Exit(-1) + return fmt.Errorf("The container %q is not ready", sharedContainer) } } } @@ -129,26 +125,22 @@ func runContainer(context *cli.Context, createOnly bool) { namespace = filepath.Join(root, sharedContainer, "namespace") namespace, err = os.Readlink(namespace) if err != nil { - fmt.Fprintf(os.Stderr, "Cannot get namespace link of the shared container: %v\n", err) - os.Exit(-1) + return fmt.Errorf("cannot get namespace link of the shared container: %v", err) } } else { path, err := osext.Executable() if err != nil { - fmt.Fprintf(os.Stderr, "cannot find self executable path for %s: %v\n", os.Args[0], err) - os.Exit(-1) + return fmt.Errorf("cannot find self executable path for %s: %v", os.Args[0], err) } kernel, initrd, bios, cbfs, err := getKernelFiles(context, spec.Root.Path) if err != nil { - fmt.Fprintf(os.Stderr, "Can't find kernel/initrd/bios/cbfs files") - os.Exit(-1) + return fmt.Errorf("can't find kernel/initrd/bios/cbfs files") } namespace, err = ioutil.TempDir("/run", "runv-namespace-") if err != nil { - fmt.Fprintf(os.Stderr, "Failed to create runv namespace path: %v", err) - os.Exit(-1) + return fmt.Errorf("failed to create runv namespace path: %v", err) } args := []string{ @@ -173,8 +165,7 @@ func runContainer(context *cli.Context, createOnly bool) { if context.GlobalIsSet(goption) { abs_path, err := filepath.Abs(context.GlobalString(goption)) if err != nil { - fmt.Fprintf(os.Stderr, "Cannot get abs path for %s: %v\n", goption, err) - os.Exit(-1) + return fmt.Errorf("Cannot get abs path for %s: %v\n", goption, err) } args = append(args, "--"+goption, abs_path) } @@ -195,8 +186,7 @@ func runContainer(context *cli.Context, createOnly bool) { } err = cmd.Start() if err != nil { - fmt.Fprintf(os.Stderr, "failed to launch runv containerd: %v\n", err) - os.Exit(-1) + return fmt.Errorf("failed to launch runv containerd: %v", err) } if _, err = os.Stat(filepath.Join(namespace, "namespaced.sock")); os.IsNotExist(err) { time.Sleep(3 * time.Second) @@ -205,34 +195,31 @@ func runContainer(context *cli.Context, createOnly bool) { err = createContainer(context, container, namespace, spec) if err != nil { - fmt.Fprintf(os.Stderr, "error %v\n", err) cmd.Process.Signal(syscall.SIGINT) - os.Exit(-1) + return fmt.Errorf("failed to create container: %v", err) } if !createOnly { address := filepath.Join(namespace, "namespaced.sock") startContainer(context, bundle, container, address, spec, context.Bool("detach")) } - os.Exit(0) + return nil } -func checkConsole(context *cli.Context, p *specs.Process, createOnly bool) { +func checkConsole(context *cli.Context, p *specs.Process, createOnly bool) error { if context.String("console") != "" && context.String("console-socket") != "" { - fmt.Fprintf(os.Stderr, "only one of --console & --console-socket can be specified") - os.Exit(-1) + return fmt.Errorf("only one of --console & --console-socket can be specified") } detach := createOnly if !createOnly { detach = context.Bool("detach") } if (context.String("console") != "" || context.String("console-socket") != "") && !detach { - fmt.Fprintf(os.Stderr, "--console[-socket] should be used on detached mode\n") - os.Exit(-1) + return fmt.Errorf("--console[-socket] should be used on detached mode\n") } if (context.String("console") != "" || context.String("console-socket") != "") && !p.Terminal { - fmt.Fprintf(os.Stderr, "--console[-socket] should be used on tty mode\n") - os.Exit(-1) + return fmt.Errorf("--console[-socket] should be used on tty mode\n") } + return nil } // Shared namespaces multiple containers suppurt @@ -250,7 +237,10 @@ func checkConsole(context *cli.Context, p *specs.Process, createOnly bool) { func createContainer(context *cli.Context, container, namespace string, config *specs.Spec) error { address := filepath.Join(namespace, "namespaced.sock") - c := getClient(address) + c, err := getClient(address) + if err != nil { + return err + } return ociCreate(context, container, func(stdin, stdout, stderr string) error { r := &types.CreateContainerRequest{ @@ -279,8 +269,7 @@ func createContainer(context *cli.Context, container, namespace string, config * func ociCreate(context *cli.Context, container string, createFunc func(stdin, stdout, stderr string) error) error { path, err := osext.Executable() if err != nil { - fmt.Fprintf(os.Stderr, "cannot find self executable path for %s: %v\n", os.Args[0], err) - os.Exit(-1) + return fmt.Errorf("cannot find self executable path for %s: %v\n", os.Args[0], err) } var stdin, stdout, stderr string diff --git a/exec.go b/exec.go index 826068c9..39124090 100644 --- a/exec.go +++ b/exec.go @@ -85,44 +85,46 @@ following will output a list of processes running in the container: Usage: "[ignore on runv] disable the use of the subreaper used to reap reparented processes", }, }, - Action: func(context *cli.Context) { + Action: func(context *cli.Context) error { root := context.GlobalString("root") container := context.Args().First() if container == "" { - fmt.Printf("Please specify container ID\n") - os.Exit(-1) + return cli.NewExitError("Please specify container ID", -1) } if os.Geteuid() != 0 { - fmt.Printf("runv should be run as root\n") - os.Exit(-1) + return cli.NewExitError("runv should be run as root", -1) } // get bundle path from state path := filepath.Join(root, container, stateJson) f, err := os.Open(path) if err != nil { - fmt.Printf("open JSON configuration file failed: %v\n", err) - os.Exit(-1) + return cli.NewExitError(fmt.Sprintf("open JSON configuration file failed: %v", err), -1) } defer f.Close() var s *specs.State if err := json.NewDecoder(f).Decode(&s); err != nil { - fmt.Printf("parse JSON configuration file failed: %v\n", err) - os.Exit(-1) + return cli.NewExitError(fmt.Sprintf("parse JSON configuration file failed: %v", err), -1) } bundle := s.Bundle // get process config, err := getProcess(context, bundle) if err != nil { - fmt.Printf("get process config failed %v\n", err) - os.Exit(-1) + return cli.NewExitError(fmt.Sprintf("get process config failed %v", err), -1) + } + if err := checkConsole(context, config, false); err != nil { + return cli.NewExitError(err.Error(), -1) } - checkConsole(context, config, false) - code := runProcess(context, container, config) - os.Exit(code) + code, err := runProcess(context, container, config) + if code != 0 { + return cli.NewExitError(err, code) + } else if err != nil { + return cli.NewExitError(err, -1) + } + return nil }, } @@ -188,23 +190,25 @@ func getProcess(context *cli.Context, bundle string) (*specs.Process, error) { return &p, nil } -func runProcess(context *cli.Context, container string, config *specs.Process) int { +func runProcess(context *cli.Context, container string, config *specs.Process) (int, error) { pid := os.Getpid() process := fmt.Sprintf("p-%x", pid+0xabcdef) // uniq name - c := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock")) + c, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock")) + if err != nil { + return -1, fmt.Errorf("failed to get client: %v", err) + } evChan := containerEvents(c, container) if !context.Bool("detach") && config.Terminal { s, err := term.SetRawTerminal(os.Stdin.Fd()) if err != nil { - fmt.Printf("error %v\n", err) - return -1 + return -1, fmt.Errorf("failed to set raw terminal: %v", err) } defer term.RestoreTerminal(os.Stdin.Fd(), s) monitorTtySize(c, container, process) } - err := ociCreate(context, container, func(stdin, stdout, stderr string) error { + err = ociCreate(context, container, func(stdin, stdout, stderr string) error { p := &types.AddProcessRequest{ Id: container, Pid: process, @@ -226,16 +230,16 @@ func runProcess(context *cli.Context, container string, config *specs.Process) i return nil }) if err != nil { - return -1 + return -1, err } if !context.Bool("detach") { for e := range evChan { if e.Type == "exit" && e.Pid == process { - return int(e.Status) + return int(e.Status), nil } } - return -1 + return -1, fmt.Errorf("unknown error") } - return 0 + return 0, nil } diff --git a/kill.go b/kill.go index 6d927167..caf84412 100644 --- a/kill.go +++ b/kill.go @@ -2,7 +2,6 @@ package main import ( "fmt" - "os" "path/filepath" "strconv" "strings" @@ -70,11 +69,10 @@ For example, if the container id is "ubuntu01" the following will send a "KILL" signal to the init process of the "ubuntu01" container: # runv kill ubuntu01 KILL`, - Action: func(context *cli.Context) { + Action: func(context *cli.Context) error { container := context.Args().First() if container == "" { - fmt.Printf("container id cannot be empty") - os.Exit(-1) + return cli.NewExitError("container id cannot be empty", -1) } sigstr := context.Args().Get(1) @@ -83,19 +81,21 @@ signal to the init process of the "ubuntu01" container: } signal, err := parseSignal(sigstr) if err != nil { - fmt.Printf("parse signal failed %v, signal string:%s\n", err, sigstr) - os.Exit(-1) + return cli.NewExitError(fmt.Sprintf("parse signal failed %v, signal string:%s", err, sigstr), -1) } - c := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock")) - if _, err := c.Signal(netcontext.Background(), &types.SignalRequest{ + c, err := getClient(filepath.Join(context.GlobalString("root"), container, "namespace/namespaced.sock")) + if err != nil { + return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1) + } + if _, err = c.Signal(netcontext.Background(), &types.SignalRequest{ Id: container, Pid: "init", Signal: uint32(signal), }); err != nil { - fmt.Printf("kill signal failed, %v", err) - os.Exit(-1) + return cli.NewExitError(fmt.Sprintf("kill signal failed, %v", err), -1) } + return nil }, } diff --git a/main.go b/main.go index ca254907..13de13f5 100644 --- a/main.go +++ b/main.go @@ -165,7 +165,7 @@ func main() { } } -func getClient(address string) types.APIClient { +func getClient(address string) (types.APIClient, error) { // reset the logger for grpc to log to dev/null so that it does not mess with our stdio grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithTimeout(5 * time.Second)} @@ -176,10 +176,9 @@ func getClient(address string) types.APIClient { )) conn, err := grpc.Dial(address, dialOpts...) if err != nil { - fmt.Printf("grpc.Dial error: %v", err) - os.Exit(-1) + return nil, fmt.Errorf("grpc.Dial error: %v", err) } - return types.NewAPIClient(conn) + return types.NewAPIClient(conn), nil } func containerEvents(c types.APIClient, container string) <-chan *types.Event { diff --git a/manage.go b/manage.go index 744c10fb..b0179109 100644 --- a/manage.go +++ b/manage.go @@ -44,27 +44,33 @@ var createTemplateCommand = cli.Command{ }, }, Usage: "create a template VM on the directory specified by the global option --template", - Action: func(context *cli.Context) { - absOption := func(option string) string { + Action: func(context *cli.Context) error { + absOption := func(option string) (string, error) { path := context.GlobalString(option) if path == "" { - fmt.Printf("The global option --%s should be specified\n", option) - os.Exit(-1) + return "", fmt.Errorf("The global option --%s should be specified", option) } path, eabs := filepath.Abs(path) if eabs != nil { - fmt.Printf("Failed to get the abs path of %s: %v\n", option, eabs) - os.Exit(-1) + return "", fmt.Errorf("Failed to get the abs path of %s: %v", option, eabs) } - return path + return path, nil + } + kernel, err := absOption("kernel") + if err != nil { + return cli.NewExitError(err, -1) + } + initrd, err := absOption("initrd") + if err != nil { + return cli.NewExitError(err, -1) + } + template, err := absOption("template") + if err != nil { + return cli.NewExitError(err, -1) } - kernel := absOption("kernel") - initrd := absOption("initrd") - template := absOption("template") if err := os.MkdirAll(template, 0700); err != nil { - fmt.Printf("Failed to create the template directory: %v\n", err) - os.Exit(-1) + return cli.NewExitError(fmt.Errorf("Failed to create the template directory: %v", err), -1) } if context.GlobalBool("debug") { @@ -73,11 +79,8 @@ var createTemplateCommand = cli.Command{ flag.CommandLine.Parse([]string{"-v", "1", "--log_dir", context.GlobalString("log_dir")}) } - var err error if hypervisor.HDriver, err = driverloader.Probe(context.GlobalString("driver")); err != nil { - glog.V(1).Infof("%v\n", err) - fmt.Printf("Failed to setup the driver: %v\n", err) - os.Exit(-1) + return cli.NewExitError(fmt.Errorf("Failed to setup the driver: %v", err), -1) } boot := hypervisor.BootConfig{ @@ -88,19 +91,21 @@ var createTemplateCommand = cli.Command{ EnableVsock: context.GlobalBool("vsock"), } if _, err := templatecore.CreateTemplateVM(template, "", boot); err != nil { - fmt.Printf("Failed to create the template: %v\n", err) - os.Exit(-1) + return cli.NewExitError(fmt.Errorf("Failed to create the template: %v", err), -1) } + return nil }, } var removeTemplateCommand = cli.Command{ Name: "remove-template", Usage: "remove the template VM on the directory specified by the global option --template", - Action: func(context *cli.Context) { + Action: func(context *cli.Context) error { if err := syscall.Unmount(context.GlobalString("template"), 0); err != nil { - fmt.Printf("Failed to remove the template: %v\n", err) - os.Exit(-1) + err := fmt.Errorf("Failed to remove the template: %v", err) + glog.Error(err) + return cli.NewExitError(err.Error(), -1) } + return nil }, } diff --git a/run.go b/run.go index ddd1b33f..e3a9ca22 100644 --- a/run.go +++ b/run.go @@ -1,6 +1,8 @@ package main import ( + "fmt" + "github.com/urfave/cli" ) @@ -47,7 +49,10 @@ command(s) that get executed on start, edit the args parameter of the spec. See Usage: "detach from the container's process", }, }, - Action: func(context *cli.Context) { - runContainer(context, false) + Action: func(context *cli.Context) error { + if err := runContainer(context, false); err != nil { + return cli.NewExitError(fmt.Sprintf("Run Container error: %v", err), -1) + } + return nil }, } diff --git a/shim.go b/shim.go index 874a39f0..6f7ce13d 100644 --- a/shim.go +++ b/shim.go @@ -34,11 +34,14 @@ var shimCommand = cli.Command{ Name: "proxy-winsize", }, }, - Action: func(context *cli.Context) { + Action: func(context *cli.Context) error { root := context.GlobalString("root") container := context.String("container") process := context.String("process") - c := getClient(filepath.Join(root, container, "namespace", "namespaced.sock")) + c, err := getClient(filepath.Join(root, container, "namespace", "namespaced.sock")) + if err != nil { + return cli.NewExitError(fmt.Sprintf("failed to get client: %v", err), -1) + } exitcode := -1 if context.Bool("proxy-exit-code") { glog.V(3).Infof("using shim to proxy exit code") @@ -49,8 +52,7 @@ var shimCommand = cli.Command{ glog.V(3).Infof("using shim to proxy winsize") s, err := term.SetRawTerminal(os.Stdin.Fd()) if err != nil { - fmt.Printf("error %v\n", err) - return + return cli.NewExitError(fmt.Sprintf("failed to set raw terminal: %v", err), -1) } defer term.RestoreTerminal(os.Stdin.Fd(), s) monitorTtySize(c, container, process) @@ -71,6 +73,7 @@ var shimCommand = cli.Command{ break } } + return nil }, } diff --git a/start.go b/start.go index 4b30f1a4..c30e26e0 100644 --- a/start.go +++ b/start.go @@ -23,31 +23,27 @@ are starting. The name you provide for the container instance must be unique on your host.`, Description: `The start command executes the user defined process in a created container`, Flags: []cli.Flag{}, - Action: func(context *cli.Context) { + Action: func(context *cli.Context) error { root := context.GlobalString("root") container := context.Args().First() if container == "" { - fmt.Fprintf(os.Stderr, "Please specify container ID\n") - os.Exit(-1) + return cli.NewExitError("Please specify container ID", -1) } if os.Geteuid() != 0 { - fmt.Fprintf(os.Stderr, "runv should be run as root\n") - os.Exit(-1) + return cli.NewExitError("runv should be run as root", -1) } // get bundle path from state path := filepath.Join(root, container, stateJson) f, err := os.Open(path) if err != nil { - fmt.Printf("open JSON configuration file failed: %v\n", err) - os.Exit(-1) + return cli.NewExitError(fmt.Sprintf("open JSON configuration file failed: %v", err), -1) } defer f.Close() var s *specs.State if err := json.NewDecoder(f).Decode(&s); err != nil { - fmt.Printf("parse JSON configuration file failed: %v\n", err) - os.Exit(-1) + return cli.NewExitError(fmt.Sprintf("parse JSON configuration file failed: %v", err), -1) } bundle := s.Bundle @@ -55,35 +51,40 @@ your host.`, ocffile := filepath.Join(bundle, specConfig) spec, err := loadSpec(ocffile) if err != nil { - fmt.Fprintf(os.Stderr, "load config failed: %v\n", err) - os.Exit(-1) + return cli.NewExitError(fmt.Sprintf("load config failed: %v", err), -1) } address := filepath.Join(root, container, "namespace/namespaced.sock") - status := startContainer(context, bundle, container, address, spec, true) + status, err := startContainer(context, bundle, container, address, spec, true) // TODO, kill the containerd if it is the first container - os.Exit(status) + if status != 0 { + return cli.NewExitError(err, status) + } else if err != nil { + return cli.NewExitError(err, -1) + } + return nil }, } -func startContainer(context *cli.Context, bundle, container, address string, config *specs.Spec, detach bool) int { +func startContainer(context *cli.Context, bundle, container, address string, config *specs.Spec, detach bool) (int, error) { r := &types.CreateContainerRequest{ Id: container, Runtime: "runv-start", BundlePath: bundle, } - c := getClient(address) + c, err := getClient(address) + if err != nil { + return -1, fmt.Errorf("failed to get client: %v", err) + } evChan := containerEvents(c, container) if _, err := c.CreateContainer(netcontext.Background(), r); err != nil { - fmt.Printf("error %v\n", err) - return -1 + return -1, fmt.Errorf("failed to create container %v", err) } if !detach && config.Process.Terminal { s, err := term.SetRawTerminal(os.Stdin.Fd()) if err != nil { - fmt.Printf("error %v\n", err) - return -1 + return -1, fmt.Errorf("failed to set raw terminal %v", err) } defer term.RestoreTerminal(os.Stdin.Fd(), s) monitorTtySize(c, container, "init") @@ -91,8 +92,7 @@ func startContainer(context *cli.Context, bundle, container, address string, con var started bool for e := range evChan { if e.Type == "exit" && e.Pid == "init" { - fmt.Printf("get exit event before start event\n") - return int(e.Status) + return int(e.Status), fmt.Errorf("get exit event before start event") } if e.Type == "start-container" { started = true @@ -100,16 +100,15 @@ func startContainer(context *cli.Context, bundle, container, address string, con } } if !started { - fmt.Printf("failed to get the start event\n") - return -1 + return -1, fmt.Errorf("failed to get the start event") } if !detach { for e := range evChan { if e.Type == "exit" && e.Pid == "init" { - return int(e.Status) + return int(e.Status), nil } } - return -1 + return -1, fmt.Errorf("unknown error") } - return 0 + return 0, nil }