Ejemplo n.º 1
0
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
}
Ejemplo n.º 2
0
Archivo: run.go Proyecto: yannski/cli
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
}