147 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"os"
 | 
						|
	"path/filepath"
 | 
						|
	"time"
 | 
						|
 | 
						|
	gocontext "context"
 | 
						|
 | 
						|
	"github.com/docker/containerd/api/execution"
 | 
						|
	execEvents "github.com/docker/containerd/execution"
 | 
						|
	"github.com/docker/docker/pkg/term"
 | 
						|
	"github.com/nats-io/go-nats"
 | 
						|
	"github.com/urfave/cli"
 | 
						|
)
 | 
						|
 | 
						|
var runCommand = cli.Command{
 | 
						|
	Name:  "run",
 | 
						|
	Usage: "run a container",
 | 
						|
	Flags: []cli.Flag{
 | 
						|
		cli.StringFlag{
 | 
						|
			Name:  "bundle, b",
 | 
						|
			Usage: "path to the container's bundle",
 | 
						|
		},
 | 
						|
		cli.BoolFlag{
 | 
						|
			Name:  "tty, t",
 | 
						|
			Usage: "allocate a TTY for the container",
 | 
						|
		},
 | 
						|
	},
 | 
						|
	Action: func(context *cli.Context) error {
 | 
						|
		id := context.Args().First()
 | 
						|
		if id == "" {
 | 
						|
			return fmt.Errorf("container id must be provided")
 | 
						|
		}
 | 
						|
		executionService, err := getExecutionService(context)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		// setup our event subscriber
 | 
						|
		nc, err := nats.Connect(nats.DefaultURL)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		nec, err := nats.NewEncodedConn(nc, nats.JSON_ENCODER)
 | 
						|
		if err != nil {
 | 
						|
			nc.Close()
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		defer nec.Close()
 | 
						|
 | 
						|
		evCh := make(chan *execEvents.ContainerExitEvent, 64)
 | 
						|
		sub, err := nec.Subscribe(execEvents.ContainersEventsSubjectSubscriber, func(e *execEvents.ContainerExitEvent) {
 | 
						|
			evCh <- e
 | 
						|
		})
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		defer sub.Unsubscribe()
 | 
						|
 | 
						|
		tmpDir, err := getTempDir(id)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		defer os.RemoveAll(tmpDir)
 | 
						|
 | 
						|
		bundle, err := filepath.Abs(context.String("bundle"))
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		crOpts := &execution.CreateContainerRequest{
 | 
						|
			ID:         id,
 | 
						|
			BundlePath: bundle,
 | 
						|
			Console:    context.Bool("tty"),
 | 
						|
			Stdin:      filepath.Join(tmpDir, "stdin"),
 | 
						|
			Stdout:     filepath.Join(tmpDir, "stdout"),
 | 
						|
			Stderr:     filepath.Join(tmpDir, "stderr"),
 | 
						|
		}
 | 
						|
 | 
						|
		var oldState *term.State
 | 
						|
		restoreTerm := func() {
 | 
						|
			if oldState != nil {
 | 
						|
				term.RestoreTerminal(os.Stdin.Fd(), oldState)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if crOpts.Console {
 | 
						|
			oldState, err = term.SetRawTerminal(os.Stdin.Fd())
 | 
						|
			if err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
			defer restoreTerm()
 | 
						|
		}
 | 
						|
 | 
						|
		fwg, err := prepareStdio(crOpts.Stdin, crOpts.Stdout, crOpts.Stderr, crOpts.Console)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		cr, err := executionService.Create(gocontext.Background(), crOpts)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		if _, err := executionService.Start(gocontext.Background(), &execution.StartContainerRequest{
 | 
						|
			ID: cr.Container.ID,
 | 
						|
		}); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		var ec uint32
 | 
						|
	eventLoop:
 | 
						|
		for {
 | 
						|
			select {
 | 
						|
			case e, more := <-evCh:
 | 
						|
				if !more {
 | 
						|
					break eventLoop
 | 
						|
				}
 | 
						|
 | 
						|
				if e.ID == cr.Container.ID && e.PID == cr.InitProcess.ID {
 | 
						|
					ec = e.StatusCode
 | 
						|
					break eventLoop
 | 
						|
				}
 | 
						|
			case <-time.After(1 * time.Second):
 | 
						|
				if nec.Conn.Status() != nats.CONNECTED {
 | 
						|
					break eventLoop
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if _, err := executionService.Delete(gocontext.Background(), &execution.DeleteContainerRequest{
 | 
						|
			ID: cr.Container.ID,
 | 
						|
		}); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		// Ensure we read all io
 | 
						|
		fwg.Wait()
 | 
						|
 | 
						|
		restoreTerm()
 | 
						|
		os.Exit(int(ec))
 | 
						|
 | 
						|
		return nil
 | 
						|
	},
 | 
						|
}
 |