func runLog(cmd *Command, args []string, client *controller.Client) error { if len(args) != 1 { cmd.printUsage(true) } rc, err := client.GetJobLog(mustApp(), args[0]) if err != nil { return err } var stderr io.Writer if logSplitOut { stderr = os.Stderr } demultiplex.Copy(os.Stdout, stderr, rc) rc.Close() return nil }
func runRun(cmd *Command, args []string, client *controller.Client) error { if len(args) == 0 { cmd.printUsage(true) } if runRelease == "" { release, err := client.GetAppRelease(mustApp()) if err == controller.ErrNotFound { return errors.New("No app release, specify a release with -release") } if err != nil { return err } runRelease = release.ID } req := &ct.NewJob{ Cmd: args, TTY: term.IsTerminal(os.Stdin) && term.IsTerminal(os.Stdout) && !runDetached, ReleaseID: runRelease, } if req.TTY { cols, err := term.Cols() if err != nil { return err } lines, err := term.Lines() if err != nil { return err } req.Columns = cols req.Lines = lines req.Env = map[string]string{ "COLUMNS": strconv.Itoa(cols), "LINES": strconv.Itoa(lines), "TERM": os.Getenv("TERM"), } } if runDetached { job, err := client.RunJobDetached(mustApp(), req) if err != nil { return err } log.Println(job.ID) return nil } rwc, err := client.RunJobAttached(mustApp(), req) if err != nil { return err } defer rwc.Close() if req.TTY { if err := term.MakeRaw(os.Stdin); err != nil { return err } defer term.Restore(os.Stdin) } go func() { io.Copy(rwc, os.Stdin) rwc.CloseWrite() }() if req.TTY { _, err = io.Copy(os.Stdout, rwc) } else { err = demultiplex.Copy(os.Stdout, os.Stderr, rwc) } // TODO: get exit code and use it return err }
func (c *Cmd) Start() error { if c.started { return errors.New("exec: already started") } c.started = true if c.cluster == nil { var err error c.cluster, err = cluster.NewClient() if err != nil { return err } c.closeCluster = true } hosts, err := c.cluster.ListHosts() if err != nil { return err } if c.HostID == "" { // TODO: check if this is actually random for c.HostID = range hosts { break } } if c.JobID == "" { c.JobID = cluster.RandomJobID("") } job := &host.Job{ ID: c.JobID, Config: &docker.Config{ Image: c.Image, Cmd: c.Cmd, Tty: c.TTY, Env: formatEnv(c.Env), }, Attributes: c.Attrs, } if c.Stdout != nil || c.stdoutPipe != nil { job.Config.AttachStdout = true } if c.Stderr != nil || c.stderrPipe != nil { job.Config.AttachStderr = true } if c.Stdin != nil || c.stdinPipe != nil { job.Config.AttachStdin = true job.Config.OpenStdin = true job.Config.StdinOnce = true } c.host, err = c.cluster.DialHost(c.HostID) if err != nil { return err } // subscribe to host events ch := make(chan *host.Event) stream := c.host.StreamEvents(job.ID, ch) go func() { for event := range ch { if event.Event == "stop" || event.Event == "error" { close(c.done) return } } c.streamErr = stream.Err() close(c.done) // TODO: handle disconnections }() var rwc cluster.ReadWriteCloser var attachWait func() error if c.Stdout != nil || c.Stderr != nil || c.Stdin != nil || c.stdoutPipe != nil || c.stderrPipe != nil || c.stdinPipe != nil { req := &host.AttachReq{ JobID: job.ID, Height: c.TermHeight, Width: c.TermWidth, Flags: host.AttachFlagStream, } if job.Config.AttachStdout { req.Flags |= host.AttachFlagStdout } if job.Config.AttachStderr { req.Flags |= host.AttachFlagStderr } if job.Config.AttachStdin { req.Flags |= host.AttachFlagStdin } rwc, attachWait, err = c.host.Attach(req, true) if err != nil { c.close() return err } } goroutines := make([]func() error, 0, 4) c.attachConn = rwc if attachWait != nil { goroutines = append(goroutines, attachWait) } if c.stdinPipe != nil { c.stdinPipe.set(writeCloseCloser{rwc}) } else if c.Stdin != nil { goroutines = append(goroutines, func() error { _, err := io.Copy(rwc, c.Stdin) rwc.CloseWrite() return err }) } if !c.TTY { if c.stdoutPipe != nil || c.stderrPipe != nil { stdout, stderr := demultiplex.Streams(rwc) if c.stdoutPipe != nil { c.stdoutPipe.set(stdout) } else if c.Stdout != nil { goroutines = append(goroutines, cpFunc(c.Stdout, stdout)) } if c.stderrPipe != nil { c.stderrPipe.set(stderr) } else if c.Stderr != nil { goroutines = append(goroutines, cpFunc(c.Stderr, stderr)) } } else if c.Stdout != nil || c.Stderr != nil { goroutines = append(goroutines, func() error { return demultiplex.Copy(c.Stdout, c.Stderr, rwc) }) } } else if c.stdoutPipe != nil { c.stdoutPipe.set(rwc) } else if c.Stdout != nil { goroutines = append(goroutines, cpFunc(c.Stdout, rwc)) } c.errCh = make(chan error, len(goroutines)) for _, fn := range goroutines { go func(fn func() error) { c.errCh <- fn() }(fn) } _, err = c.cluster.AddJobs(&host.AddJobsReq{HostJobs: map[string][]*host.Job{c.HostID: {job}}}) return err }