Пример #1
0
func decryptKey(payload []byte, iv []byte, key []byte, newCypherFunc func([]byte) (cipher.Block, error)) ([]byte, error) {
	decryptedPayload := make([]byte, len(payload))
	block, err := newCypherFunc(key)
	if err != nil {
		return nil, errgo.Mask(err)
	}
	decrypter := cipher.NewCBCDecrypter(block, iv)
	decrypter.CryptBlocks(decryptedPayload, payload)
	debug.Println("End of private key payload:", decryptedPayload[len(decryptedPayload)-50:])
	debug.Println("Length of payload:", len(decryptedPayload))
	decryptedPayload = PKCS5Or7Unpadding(decryptedPayload)
	debug.Println("Length of payload after trimming:", len(decryptedPayload))
	return decryptedPayload, nil
}
Пример #2
0
func (ctx *runContext) exitCode() (int, error) {
	if ctx.attachURL == "" {
		return -1, errgo.New("No attach URL to connect to")
	}

	req, err := http.NewRequest("GET", ctx.attachURL+"/wait", nil)
	if err != nil {
		return -1, errgo.Mask(err, errgo.Any)
	}
	req.SetBasicAuth("", config.AuthenticatedUser.AuthenticationToken)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return -1, errgo.Mask(err)
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return -1, errgo.Notef(err, "fail to read body when getting exit code")
	}
	debug.Println("exit code body:", string(body))

	waitRes := map[string]int{}
	err = json.NewDecoder(bytes.NewBuffer(body)).Decode(&waitRes)
	if err != nil {
		return -1, errgo.Notef(err, "invalid response when getting exit code")
	}

	return waitRes["exit_code"], nil
}
Пример #3
0
func loginWithSsh(identity string) error {
	debug.Println("Login through SSH, identity:", identity)
	client, _, err := netssh.Connect(identity)
	if err != nil {
		return errgo.Notef(err, "fail to connect to SSH server")
	}
	channel, reqs, err := client.OpenChannel("session", []byte{})
	if err != nil {
		return errgo.Notef(err, "fail to open SSH channel")
	}

	defer client.Close()

	_, err = channel.SendRequest("*****@*****.**", false, []byte{})
	if err != nil {
		return errgo.Notef(err, "SSH authentication request fails")
	}
	req := <-reqs
	if req == nil {
		return errgo.Newf("invalid response from auth request")
	}
	if req.Type != "*****@*****.**" {
		return errgo.Newf("invalid response from SSH server, type is %v", req.Type)
	}
	payload := req.Payload

	if len(payload) == 0 {
		return errgo.Newf("invalid response from SSH server")
	}
	return loginWithApiKey(string(payload))
}
Пример #4
0
func (a *CliAuthenticator) LoadAuth() (*scalingo.User, error) {
	file, err := os.OpenFile(C.AuthFile, os.O_RDONLY, 0600)
	if os.IsNotExist(err) {
		return Auth()
	}
	if err != nil {
		return nil, errgo.Mask(err, errgo.Any)
	}

	var authConfig auth.ConfigData
	if err := json.NewDecoder(file).Decode(&authConfig); err != nil {
		file.Close()
		return nil, errgo.Mask(err, errgo.Any)
	}
	file.Close()

	if authConfig.AuthDataVersion == "" {
		debug.Println("auth config should be updated")
		err = authConfig.MigrateToV1()
		if err != nil {
			return nil, errgo.Mask(err)
		}
		err = writeAuthFile(&authConfig)
		if err != nil {
			return nil, errgo.Mask(err)
		}
		debug.Println("auth config has been updated to V1")
	}

	var configPerHost auth.ConfigPerHostV1
	err = json.Unmarshal(authConfig.AuthConfigPerHost, &configPerHost)
	if err != nil {
		return nil, errgo.Mask(err)
	}

	if user, ok := configPerHost[C.apiHost]; !ok {
		return Auth()
	} else {
		if user == nil {
			return Auth()
		}
		return user, nil
	}
}
Пример #5
0
func FlagAppAutoComplete(c *cli.Context) bool {
	apps, err := appsList()
	if err != nil {
		debug.Println("fail to get apps list:", err)
		return false
	}

	for _, app := range apps {
		fmt.Println(app.Name)
	}

	return true
}
Пример #6
0
Файл: apps.go Проект: Zyko0/cli
func appsList() ([]*scalingo.App, error) {
	var (
		err  error
		apps []*scalingo.App
	)

	apps, err = appsAutoCompleteCache()
	if err != nil {
		debug.Println("fail to get applications autocomplete cache make GET request", err)
		apps, err = scalingo.AppsList()
		if err != nil || len(apps) == 0 {
			return nil, errgo.Mask(err)
		}

		err = writeAppsAutoCompleteCache(apps)
		if err != nil {
			debug.Println("fail to write applications autocomplete cache", err)
		}
	}

	return apps, nil
}
Пример #7
0
func (ctx *runContext) uploadFile(endpoint string, file string) error {
	body := new(bytes.Buffer)
	name := filepath.Base(file)
	multipartFile := multipart.NewWriter(body)
	writer, err := multipartFile.CreateFormFile("file", name)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	fd, err := os.OpenFile(file, os.O_RDONLY, 0600)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	_, err = stdio.Copy(writer, fd)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	err = fd.Close()
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}
	err = multipartFile.Close()
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	req, err := http.NewRequest("POST", endpoint, body)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}
	req.SetBasicAuth("", config.AuthenticatedUser.AuthenticationToken)

	req.Header.Set("Content-Type", multipartFile.FormDataContentType())

	fmt.Fprintln(ctx.waitingTextOutputWriter, "Upload", file, "to container.")
	debug.Println("Endpoint:", req.URL)

	res, err := httpclient.Do(req)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		b, _ := ioutil.ReadAll(res.Body)
		return errgo.Newf("Invalid return code %v (%s)", res.Status, strings.TrimSpace(string(b)))
	}
	return nil
}
Пример #8
0
func ParseJSON(res *http.Response, data interface{}) error {
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return errgo.Newf("fail to read body of request %v, %v", res.Request, err)
	}

	debug.Println(string(body))

	err = json.Unmarshal(body, data)
	if err != nil {
		return errgo.Newf("fail to parse JSON of request %v, %v", res.Request, err)
	}

	return nil
}
Пример #9
0
Файл: git.go Проект: Zyko0/cli
func scalingoRemotes(directory string) (gitremote.Remotes, error) {
	matchedRemotes := make(gitremote.Remotes, 0)
	remotes, err := gitremote.List(directory)
	if err != nil {
		return nil, err
	}
	for _, remote := range remotes {
		matched, err := regexp.Match(".*scalingo.com:.*.git", []byte(remote.URL))
		if err == nil && matched {
			debug.Println("[AppDetect] GIT remote found:", remote)
			matchedRemotes = append(matchedRemotes, remote)
		}
	}
	return matchedRemotes, nil
}
Пример #10
0
func HandleSignal(s os.Signal, socket net.Conn, runUrl string) {
	switch s {
	case syscall.SIGINT:
		socket.Write([]byte{0x03})
	case syscall.SIGQUIT:
		socket.Write([]byte{0x1c})
	case syscall.SIGTSTP:
		socket.Write([]byte{0x1a})
	case syscall.SIGWINCH:
		err := updateTtySize(runUrl)
		if err != nil {
			debug.Println("WARN: Error when updating terminal size:", err)
		}
	}
}
Пример #11
0
Файл: logs.go Проект: Zyko0/cli
func streamLogs(logsRawURL string, filter string) error {
	var (
		err    error
		buffer [20480]byte
		event  WSEvent
	)

	logsURL, err := url.Parse(logsRawURL)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}
	if logsURL.Scheme == "https" {
		logsURL.Scheme = "wss"
	} else {
		logsURL.Scheme = "ws"
	}

	logsURLString := fmt.Sprintf("%s&stream=true", logsURL.String())
	if filter != "" {
		logsURLString = fmt.Sprintf("%s&filter=%s", logsURLString, filter)
	}

	conn, err := websocket.Dial(logsURLString, "", "http://scalingo-cli.local/"+config.Version)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	for {
		n, err := conn.Read(buffer[:])
		if err != nil {
			return errgo.Mask(err, errgo.Any)
		}
		debug.Println(string(buffer[:n]))
		err = json.Unmarshal(buffer[:n], &event)
		if err != nil {
			return errgo.Notef(err, "invalid JSON %v", string(buffer[:n]))
		}
		switch event.Type {
		case "ping":
		case "log":
			fmt.Println(strings.TrimSpace(event.Log))
		}
	}
}
Пример #12
0
Файл: git.go Проект: yannski/cli
func ScalingoRepo(directory string, remoteName string) (string, error) {
	remotes, err := gitremote.List(directory)
	if err != nil {
		return "", err
	}
	for i := 0; i < 2; i++ {
		for _, remote := range remotes {
			if remote.Name == remoteName {
				matched, err := regexp.Match(".*scalingo.com:.*.git", []byte(remote.URL))
				if err == nil && matched {
					debug.Println("[AppDetect] GIT remote found:", remote)
					return filepath.Base(strings.TrimSuffix(remote.Repository(), ".git")), nil
				}
			}
		}
		remoteName = "scalingo-" + remoteName
	}
	return "", errgo.Newf("Scalingo GIT remote hasn't been found")
}
Пример #13
0
func CurrentApp(c *cli.Context) string {
	var repoName string
	if c.GlobalString("app") != "<name>" {
		repoName = c.GlobalString("app")
	} else if c.String("app") != "<name>" {
		repoName = c.String("app")
	} else if os.Getenv("SCALINGO_APP") != "" {
		repoName = os.Getenv("SCALINGO_APP")
	} else if dir, ok := DetectGit(); ok {
		repoName, _ = ScalingoRepo(dir, c.GlobalString("remote"))
	}
	if repoName == "" {
		fmt.Println("Unable to find the application name, please use --app flag.")
		os.Exit(1)
	}

	debug.Println("[AppDetect] App name is", repoName)
	return repoName
}
Пример #14
0
Файл: git.go Проект: Zyko0/cli
func ScalingoRepoComplete(dir string) []string {
	var repos []string

	remotes, err := scalingoRemotes(dir)
	if err != nil {
		debug.Println("[AppDetectCompletion] fail to get scalingo remotes in", dir)
		return repos
	}

	for _, remote := range remotes {
		if strings.HasPrefix(remote.Name, "scalingo-") {
			repos = append(repos, remote.Name[9:])
		} else {
			repos = append(repos, remote.Name)
		}
	}

	return repos
}
Пример #15
0
func NewRequestFailedError(res *http.Response, req *APIRequest) error {
	debug.Println("APIRequest Error:", res.StatusCode, req.Method, req.Endpoint)
	defer res.Body.Close()
	switch res.StatusCode {
	case 400:
		var badRequestError BadRequestError
		err := ParseJSON(res, &badRequestError)
		if err != nil {
			return errgo.Mask(err, errgo.Any)
		}
		return &RequestFailedError{res.StatusCode, badRequestError, req}
	case 401:
		return &RequestFailedError{res.StatusCode, errgo.New("unauthorized - you are not authorized to do this operation"), req}
	case 402:
		var paymentRequiredErr PaymentRequiredError
		err := ParseJSON(res, &paymentRequiredErr)
		if err != nil {
			return errgo.Mask(err, errgo.Any)
		}
		return &RequestFailedError{res.StatusCode, paymentRequiredErr, req}
	case 404:
		var notFoundErr NotFoundError
		err := ParseJSON(res, &notFoundErr)
		if err != nil {
			return errgo.Mask(err, errgo.Any)
		}
		return &RequestFailedError{res.StatusCode, notFoundErr, req}
	case 422:
		var unprocessableError UnprocessableEntity
		err := ParseJSON(res, &unprocessableError)
		if err != nil {
			return errgo.Mask(err, errgo.Any)
		}
		return &RequestFailedError{res.StatusCode, unprocessableError, req}
	case 500:
		return &RequestFailedError{res.StatusCode, errgo.New("server internal error - our team has been notified"), req}
	default:
		return &RequestFailedError{res.StatusCode, fmt.Errorf("invalid status from server: %v", res.Status), req}
	}
}
Пример #16
0
Файл: logs.go Проект: Zyko0/cli
func Logs(appName string, stream bool, n int, filter string) error {
	err := checkFilter(appName, filter)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	res, err := scalingo.LogsURL(appName)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		return errgo.Newf("fail to query logs: %s", res.Status)
	}

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	debug.Println("[API-Response] ", string(body))

	logsRes := &LogsRes{}
	if err = json.Unmarshal(body, &logsRes); err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	if err = dumpLogs(logsRes.LogsURL, n, filter); err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	if stream {
		if err = streamLogs(logsRes.LogsURL, filter); err != nil {
			return errgo.Mask(err, errgo.Any)
		}
	}
	return nil
}
Пример #17
0
func Scale(app string, sync bool, types []string) error {
	var (
		size        string
		containers  []scalingo.Container
		modificator byte
		err         error
	)

	scaleParams := &scalingo.AppsScaleParams{}

	for _, t := range types {
		splitT := strings.Split(t, ":")
		if len(splitT) != 2 && len(splitT) != 3 {
			return errgo.Newf("%s is invalid, format is <type>:<amount>[:<size>]", t)
		}
		typeName, typeAmount := splitT[0], splitT[1]
		if len(splitT) == 3 {
			size = splitT[2]
		}

		if typeAmount[0] == '-' || typeAmount[0] == '+' {
			modificator = typeAmount[0]
			typeAmount = typeAmount[1:]
			if size != "" {
				return errgo.Newf("%s is invalid, can't use relative modificator with size, change the size first", t)
			}
			if containers == nil {
				c := config.ScalingoClient()
				containers, err = c.AppsPs(app)
				if err != nil {
					return errgo.Notef(err, "fail to get list of running containers")
				}
				debug.Println("get container list", containers)
			}
		}

		amount, err := strconv.ParseInt(typeAmount, 10, 32)
		if err != nil {
			return errgo.Newf("%s in %s should be an integer", typeAmount, t)
		}

		newContainerConfig := scalingo.Container{Name: typeName, Size: size}
		if modificator != 0 {
			for _, container := range containers {
				if container.Name == typeName {
					if modificator == '-' {
						newContainerConfig.Amount = container.Amount - int(amount)
					} else if modificator == '+' {
						newContainerConfig.Amount = container.Amount + int(amount)
					}
					break
				}
			}
		} else {
			newContainerConfig.Amount = int(amount)
		}

		scaleParams.Containers = append(scaleParams.Containers, newContainerConfig)
	}

	c := config.ScalingoClient()
	res, err := c.AppsScale(app, scaleParams)
	if err != nil {
		return errgo.Mask(err)
	}
	defer res.Body.Close()

	var scaleRes ScaleRes
	err = scalingo.ParseJSON(res, &scaleRes)
	if err != nil {
		return errgo.Mask(err)
	}

	fmt.Printf("Your application is being scaled to:\n")
	for _, ct := range scaleRes.Containers {
		fmt.Println(io.Indent(fmt.Sprintf("%s: %d - %s", ct.Name, ct.Amount, ct.Size), 2))
	}

	if !sync {
		return nil
	}

	err = handleOperation(app, res)
	if err != nil {
		return errgo.Mask(err)
	}

	fmt.Println("Your application has been scaled.")
	return nil
}
Пример #18
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
}
Пример #19
0
func Stream(appName string) error {
	c := config.ScalingoClient()
	app, err := c.AppsShow(appName)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	debug.Println("Opening socket to: " + app.Links.DeploymentsStream)

	conn, err := c.DeploymentStream(app.Links.DeploymentsStream)
	if err != nil {
		return errgo.Mask(err, errgo.Any)
	}

	var event deployEvent
	oldStatus := ""
	for {
		err := websocket.JSON.Receive(conn, &event)
		if err != nil {
			conn.Close()
			if err == stdio.EOF {
				debug.Println("Remote server broke the connection, reconnecting")
				for err != nil {
					conn, err = c.DeploymentStream(app.Links.DeploymentsStream)
					time.Sleep(time.Second * 1)
				}
				continue
			} else {
				return errgo.Mask(err, errgo.Any)
			}
		} else {
			switch event.Type {
			case "ping":
			case "log":
				var logData logData
				err := json.Unmarshal(event.Data, &logData)
				if err != nil {
					config.C.Logger.Println(err)
				} else {
					fmt.Println("[LOG] " + strings.TrimSpace(logData.Content))
				}
			case "status":
				var statusData statusData
				err := json.Unmarshal(event.Data, &statusData)
				if err != nil {
					config.C.Logger.Println(err)
				} else {
					if oldStatus == "" {
						fmt.Println("[STATUS] New status: " + statusData.Content)
					} else {
						fmt.Println("[STATUS] New status: " + oldStatus + " →  " + statusData.Content)
					}
					oldStatus = statusData.Content
				}
			case "new":
				oldStatus = ""
				var newData map[string]*scalingo.Deployment
				err := json.Unmarshal(event.Data, &newData)
				if err != nil {
					config.C.Logger.Println(err)
				} else {
					fmt.Println("[NEW] New deploy: " + newData["deployment"].ID + " from " + newData["deployment"].User.Username)
				}
			}
		}
	}
}
Пример #20
0
func CollaboratorsAddAutoComplete(c *cli.Context) error {
	var err error
	appName := CurrentAppCompletion(c)
	if appName == "" {
		return nil
	}

	apps, err := appsList()
	if err != nil {
		debug.Println("fail to get apps list:", err)
		return nil
	}

	currentAppCollaborators, err := scalingo.CollaboratorsList(appName)
	if err != nil {
		return nil
	}

	var apiError error = nil
	ch := make(chan string)
	var wg sync.WaitGroup
	wg.Add(len(apps))
	for _, app := range apps {
		go func(app *scalingo.App) {
			defer wg.Done()
			appCollaborators, erro := scalingo.CollaboratorsList(app.Name)
			if erro != nil {
				config.C.Logger.Println(erro.Error())
				apiError = erro
				return
			}
			for _, col := range appCollaborators {
				ch <- col.Email
			}
		}(app)
	}

	setEmails := make(map[string]bool)
	go func() {
		for content := range ch {
			setEmails[content] = true
		}
	}()
	wg.Wait()
	close(ch)

	if apiError != nil {
		return nil
	}

	for email, _ := range setEmails {
		isAlreadyCollaborator := false
		for _, currentAppCol := range currentAppCollaborators {
			if currentAppCol.Email == email {
				isAlreadyCollaborator = true
			}
		}
		if !isAlreadyCollaborator {
			fmt.Println(email)
		}
	}
	return nil
}
Пример #21
0
Файл: run.go Проект: 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
}
Пример #22
0
func Tunnel(app string, dbEnvVar string, identity string, port int) error {
	environ, err := api.VariablesListWithoutAlias(app)
	if err != nil {
		return errgo.Mask(err)
	}

	dbUrlStr := dbEnvVarValue(dbEnvVar, environ)
	if dbUrlStr == "" {
		return errgo.Newf("no such environment variable: %s", dbEnvVar)
	}

	dbUrl, err := url.Parse(dbUrlStr)
	if err != nil {
		return errgo.Notef(err, "invalid database 'URL': %s", dbUrlStr)
	}
	fmt.Printf("Building tunnel to %s\n", dbUrl.Host)

	var privateKeys []ssh.Signer
	if identity == "ssh-agent" {
		var agentConnection io.Closer
		privateKeys, agentConnection, err = sshkeys.ReadPrivateKeysFromAgent()
		if err != nil {
			return errgo.Mask(err)
		}
		defer agentConnection.Close()
	}

	if len(privateKeys) == 0 {
		identity = sshkeys.DefaultKeyPath
		privateKey, err := sshkeys.ReadPrivateKey(identity)
		if err != nil {
			return errgo.Mask(err)
		}
		privateKeys = append(privateKeys, privateKey)
	}

	debug.Println("Identity used:", identity)

	var client *ssh.Client
	for _, privateKey := range privateKeys {
		sshConfig := &ssh.ClientConfig{
			User: "******",
			Auth: []ssh.AuthMethod{ssh.PublicKeys(privateKey)},
		}

		client, err = ssh.Dial("tcp", config.C.SshHost, sshConfig)
		if err == nil {
			break
		} else {
			config.C.Logger.Println("Fail to connect to the SSH server", err)
		}
	}
	if client == nil {
		return errgo.Newf("No authentication method has succeeded, please use the flag '-i /path/to/private/key' to specify your private key")
	}

	tcpAddr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("localhost:%d", port))
	if err != nil {
		return errgo.Mask(err)
	}

	sock, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		return errgo.Mask(err)
	}
	defer sock.Close()
	fmt.Printf("You can access your database on '%v'\n", sock.Addr())

	go startIDGenerator()
	errs := make(chan error)
	for {
		select {
		case err := <-errs:
			return errgo.Mask(err)
		default:
		}

		connToTunnel, err := sock.Accept()
		if err != nil {
			return errgo.Mask(err)
		}
		go handleConnToTunnel(client, dbUrl, connToTunnel, errs)
	}
}