func List() error { c := config.ScalingoClient() apps, err := c.AppsList() if err != nil { return errgo.Mask(err, errgo.Any) } if len(apps) == 0 { fmt.Println(io.Indent("\nYou haven't created any app yet, create your first application using:\n→ scalingo create <app_name>\n", 2)) return nil } t := tablewriter.NewWriter(os.Stdout) t.SetHeader([]string{"Name", "Role", "Owner"}) for _, app := range apps { if app.Owner.Email == config.AuthenticatedUser.Email { t.Append([]string{app.Name, "owner", "-"}) } else { t.Append([]string{app.Name, "collaborator", fmt.Sprintf("%s <%s>", app.Owner.Username, app.Owner.Email)}) } } t.Render() return nil }
func Add(name string, path string) error { stat, err := os.Stat(path) if err != nil { return errgo.Mask(err) } if stat.Mode().IsDir() { return errgo.Newf("%s: is a directory", path) } if stat.Size() > 10*1024*1024 { return errgo.Newf("%s: is too large (%v bytes)", stat.Size()) } keyContent, err := ioutil.ReadFile(path) if err != nil { return errgo.Mask(err) } c := config.ScalingoClient() _, err = c.KeysAdd(name, string(keyContent)) if err != nil { return errgo.Mask(err) } fmt.Printf("Key '%s' has been added.\n", name) return nil }
func DbTunnelAutoComplete(c *cli.Context) error { appName := CurrentAppCompletion(c) if appName == "" { return nil } lastArg := "" if len(os.Args) > 2 { lastArg = os.Args[len(os.Args)-2] } if !strings.HasPrefix(lastArg, "-") { client := config.ScalingoClient() variables, err := client.VariablesList(appName) if err == nil { for _, v := range variables { if matched, err := regexp.Match("SCALINGO_.*_URL", []byte(v.Name)); matched && err == nil { fmt.Println(v.Name) } } } } return nil }
func Add(app string, params []string) error { var variables scalingo.Variables for _, param := range params { if err := isEnvEditValid(param); err != nil { return errgo.Newf("'%s' is invalid: %s", param, err) } name, value := parseVariable(param) variables = append(variables, &scalingo.Variable{ Name: name, Value: value, }) } c := config.ScalingoClient() _, _, err := c.VariableMultipleSet(app, variables) if err != nil { return errgo.Mask(err, errgo.Any) } for _, variable := range variables { fmt.Printf("%s has been set to '%s'.\n", variable.Name, variable.Value) } return nil }
func Delete(app string, varNames []string) error { c := config.ScalingoClient() vars, err := c.VariablesList(app) if err != nil { return errgo.Mask(err, errgo.Any) } var varsToUnset scalingo.Variables for _, varName := range varNames { v, ok := vars.Contains(varName) if !ok { return errgo.Newf("%s variable does not exist", varName) } varsToUnset = append(varsToUnset, v) } for _, v := range varsToUnset { err := c.VariableUnset(app, v.ID) if err != nil { return errgo.Mask(err, errgo.Any) } fmt.Printf("%s has been unset.\n", v.Name) } return nil }
func AddonsAddAutoComplete(c *cli.Context) error { client := config.ScalingoClient() resources, err := client.AddonProvidersList() if len(os.Args) > 1 && err == nil { lastArg := os.Args[len(os.Args)-2] isAddonNameSet := false for _, resource := range resources { if lastArg == resource.ID { isAddonNameSet = true break } } if isAddonNameSet { plans, err := client.AddonProviderPlansList(lastArg) if err == nil { for _, plan := range plans { fmt.Println(plan.Name) } } } else { for _, resource := range resources { fmt.Println(resource.ID) } } } return nil }
func Add(app, email string) error { c := config.ScalingoClient() collaborator, err := c.CollaboratorAdd(app, email) if err != nil { return errgo.Mask(err, errgo.Any) } io.Status(collaborator.Email, "has been invited to collaborate to", app) return nil }
func KeysRemoveAutoComplete(c *cli.Context) error { client := config.ScalingoClient() keys, err := client.KeysList() if err == nil { for _, key := range keys { fmt.Println(key.Name) } } return nil }
func Display(app string) error { c := config.ScalingoClient() vars, err := c.VariablesList(app) if err != nil { return errgo.Mask(err, errgo.Any) } for _, v := range vars { fmt.Printf("%s=%s\n", v.Name, v.Value) } return nil }
func keyByName(name string) (*scalingo.Key, error) { c := config.ScalingoClient() keys, err := c.KeysList() if err != nil { return nil, errgo.Mask(err) } for _, k := range keys { if k.Name == name { return &k, nil } } return nil, errgo.New("no such key") }
func getFromEmail(app, email string) (scalingo.Collaborator, error) { c := config.ScalingoClient() collaborators, err := c.CollaboratorsList(app) if err != nil { return scalingo.Collaborator{}, errgo.Mask(err, errgo.Any) } for _, collaborator := range collaborators { if collaborator.Email == email { return collaborator, nil } } return scalingo.Collaborator{}, notFound }
func handleOperation(app string, res *http.Response) error { opURL, err := url.Parse(res.Header.Get("Location")) if err != nil { return errgo.Mask(err) } var op *scalingo.Operation opID := filepath.Base(opURL.Path) done := make(chan struct{}) errs := make(chan error) defer close(done) defer close(errs) go func() { c := config.ScalingoClient() for { op, err = c.OperationsShow(app, opID) if err != nil { errs <- err break } if op.Status == "done" || op.Status == "error" { done <- struct{}{} break } time.Sleep(1 * time.Second) } }() fmt.Print("Status: ") spinner := io.NewSpinner(os.Stderr) go spinner.Start() defer spinner.Stop() for { select { case err := <-errs: return errgo.Mask(err) case <-done: if op.Status == "done" { fmt.Printf("\bDone in %.3f seconds\n", op.ElapsedDuration()) return nil } else if op.Status == "error" { fmt.Printf("\bOperation '%s' failed, an error occured: %v\n", op.Type, op.Error) return nil } } } }
func findDomain(app string, domain string) (scalingo.Domain, error) { c := config.ScalingoClient() domains, err := c.DomainsList(app) if err != nil { return scalingo.Domain{}, errgo.Mask(err) } for _, d := range domains { if d.Name == domain { return d, nil } } return scalingo.Domain{}, errgo.New("There is no such domain, please ensure you've added it correctly.\nhttps://my.scalingo.com/apps/" + app + "/domains") }
func DisableSSL(app string, domain string) error { d, err := findDomain(app, domain) if err != nil { return errgo.Mask(err) } c := config.ScalingoClient() _, err = c.DomainsUpdate(app, d.ID, "", "") if err != nil { return errgo.Mask(err) } io.Status("SSL of " + domain + " has been disabled.") return nil }
func Remove(name string) error { k, err := keyByName(name) if err != nil { return errgo.Mask(err) } c := config.ScalingoClient() err = c.KeysDelete(k.ID) if err != nil { return errgo.Mask(err) } fmt.Printf("Key '%s' has been deleted.\n", name) return nil }
func Remove(app string, domain string) error { d, err := findDomain(app, domain) if err != nil { return errgo.Mask(err) } c := config.ScalingoClient() err = c.DomainsRemove(app, d.ID) if err != nil { return errgo.Mask(err) } io.Status("The domain", d.Name, "has been deleted") return nil }
func DeploymentsAutoComplete(c *cli.Context) error { client := config.ScalingoClient() currentApp := appdetect.CurrentApp(c) deployments, err := client.DeploymentList(currentApp) if err != nil { return errgo.Mask(err, errgo.Any) } for _, deployment := range deployments { fmt.Println(deployment.ID) } return nil }
func List(app string) error { c := config.ScalingoClient() collaborators, err := c.CollaboratorsList(app) if err != nil { return errgo.Mask(err, errgo.Any) } t := tablewriter.NewWriter(os.Stdout) t.SetHeader([]string{"Email", "Username", "Status"}) for _, collaborator := range collaborators { t.Append([]string{collaborator.Email, collaborator.Username, collaborator.Status}) } t.Render() return nil }
func ScaleAutoComplete(c *cli.Context) error { appName := CurrentAppCompletion(c) if appName == "" { return nil } client := config.ScalingoClient() processes, err := client.AppsPs(appName) if err != nil { return errgo.Mask(err) } for _, ct := range processes { fmt.Println(fmt.Sprintf("%s:%d:%s", ct.Name, ct.Amount, ct.Size)) } return nil }
func RestartAutoComplete(c *cli.Context) error { appName := CurrentAppCompletion(c) if appName == "" { return nil } client := config.ScalingoClient() processes, err := client.AppsPs(appName) if err != nil { return errgo.Mask(err) } for _, ct := range processes { fmt.Println(ct.Name) } return nil }
func EnvUnsetAutoComplete(c *cli.Context) error { appName := CurrentAppCompletion(c) if appName == "" { return nil } client := config.ScalingoClient() variables, err := client.VariablesList(appName) if err == nil { for _, v := range variables { fmt.Println(v.Name) } } return nil }
func AddonsRemoveAutoComplete(c *cli.Context) error { appName := CurrentAppCompletion(c) if appName == "" { return nil } client := config.ScalingoClient() resources, err := client.AddonsList(appName) if err == nil { for _, resource := range resources { fmt.Println(resource.ResourceID) } } return nil }
func DomainsRemoveAutoComplete(c *cli.Context) error { appName := CurrentAppCompletion(c) if appName == "" { return nil } client := config.ScalingoClient() domains, err := client.DomainsList(appName) if err == nil { for _, domain := range domains { fmt.Println(domain.Name) } } return nil }
func Remove(app, email string) error { collaborator, err := getFromEmail(app, email) if err != nil { if err == notFound { io.Error(email + " is not a collaborator of " + app + ".") return nil } else { return errgo.Mask(err, errgo.Any) } } c := config.ScalingoClient() err = c.CollaboratorRemove(app, collaborator.ID) if err != nil { return errgo.Mask(err, errgo.Any) } io.Status(email, "has been removed from the collaborators of", app) return nil }
func EnableSSL(app, domain, certPath, keyPath string) error { d, err := findDomain(app, domain) if err != nil { return errgo.Mask(err) } certContent, keyContent, err := validateSSL(certPath, keyPath) if err != nil { return errgo.Mask(err) } c := config.ScalingoClient() d, err = c.DomainsUpdate(app, d.ID, certContent, keyContent) if err != nil { return errgo.Mask(err) } io.Status("The certificate and key have been installed for " + d.Name + " (Validity: " + d.Validity.UTC().String() + ")") return nil }
func Provision(app, webHookURL string) error { if app == "" { return errgo.New("no app defined") } else if webHookURL == "" { return errgo.New("no url defined") } c := config.ScalingoClient() params, err := c.NotificationProvision(app, webHookURL) if err != nil { return errgo.Mask(err, errgo.Any) } io.Status("Notifications to", webHookURL, "have been provisionned") if len(params.Variables) > 0 { io.Info("Modified variables:", params.Variables) } if len(params.Message) > 0 { io.Info("Message from notification provider:", params.Message) } return nil }
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 AddonsUpgradeAutoComplete(c *cli.Context) error { appName := CurrentAppCompletion(c) addonName := "" if appName == "" { return nil } client := config.ScalingoClient() resources, err := client.AddonsList(appName) if len(os.Args) > 1 && err == nil { lastArg := os.Args[len(os.Args)-2] isAddonIDSet := false for _, resource := range resources { if lastArg == resource.ResourceID { isAddonIDSet = true addonName = resource.AddonProvider.ID break } } if isAddonIDSet && addonName != "" { plans, err := client.AddonProviderPlansList(addonName) if err == nil { for _, plan := range plans { fmt.Println(plan.Name) } } } else { for _, resource := range resources { fmt.Println(resource.ResourceID) } } } return nil }
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 } client := config.ScalingoClient() currentAppCollaborators, err := client.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 := client.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 }
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 }