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 }
func Run(opts RunOpts) error { if opts.CmdEnv == nil { opts.CmdEnv = []string{} } if opts.Files == nil { opts.Files = []string{} } if opts.StdinCopyFunc == nil { opts.StdinCopyFunc = io.Copy } if opts.StdoutCopyFunc == nil { opts.StdoutCopyFunc = io.Copy } env, err := buildEnv(opts.CmdEnv) if err != nil { return errgo.Mask(err, errgo.Any) } err = validateFiles(opts.Files) if err != nil { return errgo.Mask(err, errgo.Any) } res, err := api.Run(opts.App, opts.Cmd, env) if err != nil { return errgo.Mask(err, errgo.Any) } runStruct := make(map[string]interface{}) api.ParseJSON(res, &runStruct) debug.Printf("%+v\n", runStruct) if res.StatusCode == http.StatusNotFound { return errgo.Newf("application %s not found", opts.App) } attachURL, ok := runStruct["attach_url"].(string) if !ok { return errgo.New("unexpected answer from server") } debug.Println("Run Service URL is", attachURL) if len(opts.Files) > 0 { err := uploadFiles(attachURL+"/files", opts.Files) if err != nil { return err } } res, socket, err := connectToRunServer(attachURL) if err != nil { return errgo.Mask(err, errgo.Any) } if res.StatusCode != http.StatusOK { return errgo.Newf("Fail to attach: %s", res.Status) } 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, attachURL) case <-stopSignalsMonitoring: signal.Stop(signals) return } } }() go opts.StdinCopyFunc(socket, os.Stdin) _, err = opts.StdinCopyFunc(os.Stdout, socket) stopSignalsMonitoring <- true if err := term.Restore(os.Stdin); err != nil { return errgo.Mask(err, errgo.Any) } return nil }