func handleOperation(app string, res *http.Response) error { opURL, err := url.Parse(res.Header.Get("Location")) if err != nil { return errgo.Mask(err) } var op *scalingo.Operation opID := filepath.Base(opURL.Path) done := make(chan struct{}) errs := make(chan error) defer close(done) defer close(errs) go func() { c := config.ScalingoClient() for { op, err = c.OperationsShow(app, opID) if err != nil { errs <- err break } if op.Status == "done" || op.Status == "error" { done <- struct{}{} break } time.Sleep(1 * time.Second) } }() fmt.Print("Status: ") spinner := io.NewSpinner(os.Stderr) go spinner.Start() defer spinner.Stop() for { select { case err := <-errs: return errgo.Mask(err) case <-done: if op.Status == "done" { fmt.Printf("\bDone in %.3f seconds\n", op.ElapsedDuration()) return nil } else if op.Status == "error" { fmt.Printf("\bOperation '%s' failed, an error occured: %v\n", op.Type, op.Error) return nil } } } }
func Run(opts RunOpts) error { c := config.ScalingoClient() firstReadDone := make(chan struct{}) ctx := &runContext{ waitingTextOutputWriter: os.Stderr, stdinCopyFunc: stdio.Copy, stdoutCopyFunc: io.CopyWithFirstReadChan(firstReadDone), } if opts.Type != "" { processes, err := c.AppsPs(opts.App) if err != nil { return errgo.Mask(err) } for _, p := range processes { if p.Name == opts.Type { opts.Cmd = strings.Split(p.Command, " ") } } if strings.Join(opts.Cmd, "") == "" { return errgo.New("no such type") } } if opts.CmdEnv == nil { opts.CmdEnv = []string{} } if opts.Files == nil { opts.Files = []string{} } if opts.Silent { ctx.waitingTextOutputWriter = new(bytes.Buffer) } if opts.StdinCopyFunc != nil { ctx.stdinCopyFunc = opts.StdinCopyFunc } if opts.StdoutCopyFunc != nil { ctx.stdoutCopyFunc = opts.StdoutCopyFunc } env, err := ctx.buildEnv(opts.CmdEnv) if err != nil { return errgo.Mask(err, errgo.Any) } err = ctx.validateFiles(opts.Files) if err != nil { return errgo.Mask(err, errgo.Any) } res, err := c.Run(opts.App, opts.Cmd, env) if err != nil { return errgo.Mask(err, errgo.Any) } runStruct := make(map[string]interface{}) scalingo.ParseJSON(res, &runStruct) debug.Printf("%+v\n", runStruct) if res.StatusCode == http.StatusNotFound { return errgo.Newf("application %s not found", opts.App) } var ok bool ctx.attachURL, ok = runStruct["attach_url"].(string) if !ok { return errgo.New("unexpected answer from server") } debug.Println("Run Service URL is", ctx.attachURL) if len(opts.Files) > 0 { err := ctx.uploadFiles(ctx.attachURL+"/files", opts.Files) if err != nil { return err } } fmt.Fprintf(ctx.waitingTextOutputWriter, "-----> Connecting to container [%v-%v]... ", runStruct["container"].(map[string]interface{})["type"], runStruct["container"].(map[string]interface{})["type_index"], ) attachSpinner := io.NewSpinner(ctx.waitingTextOutputWriter) attachSpinner.PostHook = func() { var displayCmd string if opts.DisplayCmd != "" { displayCmd = opts.DisplayCmd } else { displayCmd = strings.Join(opts.Cmd, " ") } fmt.Fprintf(ctx.waitingTextOutputWriter, "\n-----> Process '%v' is starting... ", displayCmd) } go attachSpinner.Start() res, socket, err := ctx.connectToRunServer() if err != nil { return errgo.Mask(err, errgo.Any) } if res.StatusCode != http.StatusOK { return errgo.Newf("Fail to attach: %s", res.Status) } if term.IsATTY(os.Stdin) { if err := term.MakeRaw(os.Stdin); err != nil { return errgo.Mask(err, errgo.Any) } } stopSignalsMonitoring := make(chan bool) defer close(stopSignalsMonitoring) go func() { signals.CatchQuitSignals = false signals := run.NotifiedSignals() defer close(signals) go run.NofityTermSizeUpdate(signals) for { select { case s := <-signals: run.HandleSignal(s, socket, ctx.attachURL) case <-stopSignalsMonitoring: signal.Stop(signals) return } } }() attachSpinner.Stop() startSpinner := io.NewSpinnerWithStopChan(ctx.waitingTextOutputWriter, firstReadDone) startSpinner.PostHook = func() { fmt.Fprintf(ctx.waitingTextOutputWriter, "\n\n") } go startSpinner.Start() go func() { _, err := ctx.stdinCopyFunc(socket, os.Stdin) if err != nil { debug.Println("error after reading stdin", err) } else { // Send EOT when stdin returns // 'scalingo run < file' socket.Write([]byte("\x04")) } }() _, err = ctx.stdoutCopyFunc(os.Stdout, socket) stopSignalsMonitoring <- true if term.IsATTY(os.Stdin) { if err := term.Restore(os.Stdin); err != nil { return errgo.Mask(err, errgo.Any) } } exitCode, err := ctx.exitCode() if err != nil { return errgo.Mask(err, errgo.Any) } os.Exit(exitCode) return nil }