// Execute an API request and return its response/error func (req *APIRequest) Do() (*http.Response, error) { err := req.FillDefaultValues() if err != nil { return nil, errgo.Mask(err, errgo.Any) } endpoint := req.URL + req.Endpoint // Execute the HTTP request according to the HTTP method switch req.Method { case "PATCH": fallthrough case "POST": fallthrough case "WITH_BODY": buffer, err := json.Marshal(req.Params) if err != nil { return nil, errgo.Mask(err, errgo.Any) } reader := bytes.NewReader(buffer) req.HTTPRequest, err = http.NewRequest(req.Method, endpoint, reader) if err != nil { return nil, errgo.Mask(err, errgo.Any) } case "GET", "DELETE": values, err := req.BuildQueryFromParams() if err != nil { return nil, errgo.Mask(err, errgo.Any) } endpoint = fmt.Sprintf("%s?%s", endpoint, values.Encode()) req.HTTPRequest, err = http.NewRequest(req.Method, endpoint, nil) if err != nil { return nil, errgo.Mask(err, errgo.Any) } } debug.Printf("[API] %v %v\n", req.HTTPRequest.Method, req.HTTPRequest.URL) debug.Printf(io.Indent(fmt.Sprintf("Headers: %v", req.HTTPRequest.Header), 6)) debug.Printf(io.Indent("Params : %v", 6), req.Params) req.HTTPRequest.SetBasicAuth("", req.Token) res, err := httpclient.Do(req.HTTPRequest) if err != nil { fmt.Printf("Fail to query %s: %v\n", req.HTTPRequest.Host, err) os.Exit(1) } if req.Expected.Contains(res.StatusCode) { return res, nil } return nil, NewRequestFailedError(res, req) }
func updateTtySize(url string) error { cols, err := term.Cols() if err != nil { return errgo.Mask(err, errgo.Any) } lines, err := term.Lines() if err != nil { return errgo.Mask(err, errgo.Any) } params := UpdateTtyParams{ fmt.Sprintf("%d", cols), fmt.Sprintf("%d", lines), } paramsJson, err := json.Marshal(¶ms) if err != nil { return errgo.Mask(err, errgo.Any) } req, err := http.NewRequest("PUT", url, bytes.NewReader(paramsJson)) if err != nil { return errgo.Mask(err, errgo.Any) } req.SetBasicAuth("", config.ScalingoClient().APIToken) debug.Printf("Updating TTY Size: PUT %v %+v", url, params) res, err := httpclient.Do(req) if err != nil { return errgo.Mask(err, errgo.Any) } defer res.Body.Close() if res.StatusCode != 200 { return errgo.Newf("Invalid error code from run server: %s", res.Status) } 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 }
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 }