// getDeveloper retrieves the local dev and updates if out of date, or no // dev exists. func getDeveloper() (*db.Developer, error) { dev, err := db.GetDeveloper() if err != nil && err != errors.ErrNoDeveloper { return dev, err } ok := false if err != nil { log.Println("yellow", "Oops! You must be logged in.") } else { ok, err = devUpToDate(dev) if err != nil { return dev, err } if !ok { log.Println("yellow", "Oops! Your login information is out of date.") } } if err != nil || !ok { err = getToken(dev) if err != nil { return dev, err } } return dev, updateDeveloper(dev) }
func logoutRun(keen *keen.Client, rollbar *rollbar.Client, args ...string) int { dev, err := db.GetDeveloper() if err != nil && err != errors.ErrNoDeveloper { rollbar.Report(err) return 1 } if err == nil { log.Println("", "Logging you out", strings.Split(dev.Developer.Name, " ")[0]+".") } else { log.Println("yellow", "No user logged in.") } dev.Token = "" dev.Developer = nil err = dev.Save() if err != nil { rollbar.Report(err) return 1 } keen.AddEvent("bowery logout", map[string]*db.Developer{"user": dev}) return 0 }
func removeRun(keen *keen.Client, rollbar *rollbar.Client, args ...string) int { force := Cmds["remove"].Force if len(args) <= 0 { fmt.Fprintln(os.Stderr, "Usage: bowery "+Cmds["remove"].Usage, "\n\n"+Cmds["remove"].Short) return 2 // --help uses 2. } services, err := db.GetServices() if err != nil { rollbar.Report(err) return 1 } for _, name := range args { _, ok := services.Data[name] if !ok { log.Println("yellow", "Service", name, "doesn't exist, skipping.") continue } if !force { ok, err = prompt.Ask("Are you sure you want to remove " + name) if err != nil { rollbar.Report(err) return 1 } if !ok { log.Println("yellow", "Skipping", name) continue } } delete(services.Data, name) log.Println("", "Removed service", name) } err = services.Save() if err != nil { rollbar.Report(err) return 1 } keen.AddEvent("bowery remove", map[string]interface{}{ "toRemove": args, "existing": services.Data, }) return 0 }
func loginRun(keen *keen.Client, rollbar *rollbar.Client, args ...string) int { dev, err := db.GetDeveloper() if err != nil && err != errors.ErrNoDeveloper { rollbar.Report(err) return 1 } // If dev was found then check if token is up to date. if err == nil { ok, err := devUpToDate(dev) if err != nil { rollbar.Report(err) return 1 } if ok { log.Println("", "You're logged in as", strings.Split(dev.Developer.Name, " ")[0]+".") return 0 } else { log.Println("yellow", "Oops! Your login information is out of date.") } } err = getToken(dev) if err != nil { rollbar.Report(err) return 1 } err = updateDeveloper(dev) if err != nil { rollbar.Report(err) return 1 } keen.AddEvent("bowery login", map[string]*db.Developer{"user": dev}) log.Println("magenta", "Hey there", strings.Split(dev.Developer.Name, " ")[0], "you're logged in now.") return 0 }
// getToken gets the login information for a developer and gets a new token. func getToken(dev *db.Developer) error { var err error i := 0 token := "" // Get email and password up to 5 times, then report the error. for token == "" && i < 5 { validEmail := false email := "" pass := "" for !validEmail { email, err = prompt.Basic("Email", true) if err != nil { return err } _, err = mail.ParseAddress(email) if err == nil { validEmail = true } else { log.Println("yellow", "Try again! Valid email address required.") } } pass, err = prompt.Password("Password") if err != nil { return err } log.Debug("Collected email", email, "pass", pass) token, err = broome.GetTokenByLogin(email, pass) if err != nil { if i < 4 { log.Fprintln(os.Stderr, "red", errors.Newf(errors.ErrLoginRetryTmpl, err)) } i++ } } if err != nil { if err == errors.ErrInvalidLogin { err = errors.ErrTooManyLogins } return err } log.Debug("Got token", token) dev.Token = token return dev.Save() }
func saveRun(keen *keen.Client, rollbar *rollbar.Client, args ...string) int { if len(args) <= 0 { fmt.Fprintln(os.Stderr, "Usage: bowery "+Cmds["save"].Usage, "\n\n"+Cmds["save"].Short) return 2 } dev, err := getDeveloper() if err != nil { rollbar.Report(err) return 1 } state, err := db.GetState() if err != nil { rollbar.Report(err) return 1 } // Create slices of service names, and find the requested service. var service *schemas.Service services := make([]string, len(state.App.Services)) for i, v := range state.App.Services { services[i] = v.Name if args[0] == v.Name { service = v break } } // Handle no service found. if service == nil { log.Fprintln(os.Stderr, "red", errors.ErrInvalidService, args[0]) log.Println("yellow", "Valid services:", strings.Join(services, ", ")) return 1 } log.Debug("Found service", service.Name, "public addr:", service.PublicAddr) // Get image name log.Println("yellow", "What would you like to call this image?") imageName, err := prompt.Basic("Image Name", true) if err != nil { rollbar.Report(err) return 1 } log.Debug("Collected Image Name", imageName) imageDesc, err := prompt.Basic("Description", true) if err != nil { rollbar.Report(err) return 1 } log.Debug("Collected Description", imageDesc) log.Println("yellow", "A new image is being created and saved to our registry...") log.Println("yellow", "This may take a couple minutes.") err = api.SaveService(state, dev, service.Name, service.PublicAddr, imageName, imageDesc) if err != nil { errmsg := err.Error() if errmsg == imageName+" is an invalid service name" || errmsg == "Image already exists" { log.Println("yellow", err) } else { rollbar.Report(err) } return 1 } log.Println("yellow", imageName+" successfully created.") keen.AddEvent("bowery save", map[string]string{ "serviceName": service.Name, "imageName": imageName, "appId": state.App.ID, }) return 0 }
// Shell opens a shell connection on the servives ssh address. func Shell(app *schemas.Application, service *schemas.Service) error { // Make sure we're in raw mode. termState, err := terminal.MakeRaw(int(os.Stdin.Fd())) if err != nil { if prompt.IsNotTerminal(err) { return errors.ErrIORedirection } return errors.NewStackError(err) } defer terminal.Restore(int(os.Stdin.Fd()), termState) // Get terminal size. cols, rows, err := terminal.GetSize(int(os.Stdout.Fd())) if err != nil { if prompt.IsNotTerminal(err) { return errors.ErrIORedirection } return errors.NewStackError(err) } // Open an SSH connection to the address. config := &ssh.ClientConfig{User: "******", Auth: []ssh.AuthMethod{ ssh.Password("password"), }} client, err := ssh.Dial("tcp", service.SSHAddr, config) if err != nil { return errors.NewStackError(err) } defer client.Close() // Start a session on the client. session, err := client.NewSession() if err != nil { return errors.NewStackError(err) } defer session.Close() session.Stdout = prompt.NewAnsiWriter(os.Stdout) session.Stderr = prompt.NewAnsiWriter(os.Stderr) // Create a stdin pipe copying os.Stdin to it. stdin, err := session.StdinPipe() if err != nil { return errors.NewStackError(err) } defer stdin.Close() go func() { io.Copy(stdin, prompt.NewAnsiReader(os.Stdin)) }() log.Println("magenta", "Welcome to Bowery Services.") log.Println("magenta", "---------------------------------------------") log.Println("magenta", "Name:", service.Name) log.Println("magenta", "Application:", app.ID) log.Println("magenta", "Time:", time.Now()) log.Println("magenta", "---------------------------------------------") // Start a shell session. termModes := ssh.TerminalModes{ ssh.ECHO: 1, ssh.TTY_OP_ISPEED: 14400, ssh.TTY_OP_OSPEED: 14400, } err = session.RequestPty("xterm", rows, cols, termModes) if err == nil { err = session.Shell() } if err != nil { return errors.NewStackError(err) } // Wait for the session. err = session.Wait() if err != nil && err != io.EOF { // Ignore the error if it's an ExitError with an empty message, // this occurs when you do CTRL+c and then run exit cmd which isn't an // actual error. waitMsg, ok := err.(*ssh.ExitError) if ok && waitMsg.Msg() == "" { return nil } return errors.NewStackError(err) } return nil }
func updateRun(keen *keen.Client, rollbar *rollbar.Client, args ...string) int { keen.AddEvent("cli update", map[string]string{"installed": version.Version}) ver, err := api.GetVersion() if err != nil { rollbar.Report(err) return 1 } if ver == version.Version { log.Println("", "Bowery is up to date.") return 0 } log.Println("yellow", "Bowery is out of date. Updating to", ver, "now...") newVer, releaseNotes, err := api.DownloadNewVersion(ver) if err != nil { rollbar.Report(err) return 1 } exec, err := osext.Executable() if err != nil { rollbar.Report(errors.NewStackError(err)) return 1 } tempExec := filepath.Join(filepath.Dir(exec), ".old_bowery"+filepath.Ext(exec)) // Open exec, should fail if execing somewhere else. file, err := os.Open(exec) if err != nil { rollbar.Report(errors.ErrUpdatePerm) return 1 } file.Close() // Create the temp exec file to test io permissions. file, err = os.Create(tempExec) if err != nil { rollbar.Report(errors.ErrUpdatePerm) return 1 } file.Close() // Remove it which also removes any previous executables. err = os.RemoveAll(tempExec) if err != nil { rollbar.Report(err) return 1 } // Move the exec to a temp file so we can write the new one. err = os.Rename(exec, tempExec) if err != nil { rollbar.Report(err) return 1 } file, err = os.OpenFile(exec, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777) if err != nil { rollbar.Report(err) return 1 } defer file.Close() _, err = io.Copy(file, newVer) if err != nil { rollbar.Report(err) return 1 } log.Println("magenta", "Updated bowery to version", ver+".") if notes, err := ioutil.ReadAll(releaseNotes); err != nil { log.Println("", string(notes)) return 0 } else { rollbar.Report(err) return 1 } return 0 }
func restartRun(keen *keen.Client, rollbar *rollbar.Client, args ...string) int { if len(args) <= 0 { fmt.Fprintln(os.Stderr, "Usage: bowery "+Cmds["restart"].Usage, "\n\n"+Cmds["restart"].Short) return 2 } dev, err := getDeveloper() if err != nil { rollbar.Report(err) return 1 } state, err := db.GetState() if err != nil { rollbar.Report(err) return 1 } // Create slices of service names, and find the requested service. var service *schemas.Service services := make([]string, len(state.App.Services)) serviceIdx := -1 for i, v := range state.App.Services { services[i] = v.Name if args[0] == v.Name { service = v serviceIdx = i break } } // Handle no service found. if service == nil { log.Fprintln(os.Stderr, "red", errors.ErrInvalidService, args[0]) log.Println("yellow", "Valid services:", strings.Join(services, ", ")) return 1 } log.Debug("Found service", service.Name) newService, err := api.RestartService(service.DockerID, dev.Token) if err != nil { rollbar.Report(err) return 1 } if newService != nil { state.App.Services[serviceIdx] = newService service = newService err = state.Save() if err != nil { rollbar.Report(err) return 1 } } keen.AddEvent("bowery restart", map[string]string{ "name": service.Name, "appId": state.App.ID, }) return 0 }