func cmdEnvSet(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } resp, err := fetchEnv(app) if err != nil { stdcli.Error(err) return } var old map[string]string err = json.Unmarshal(resp, &old) if err != nil { stdcli.Error(err) return } data := "" for key, value := range old { data += fmt.Sprintf("%s=%s\n", key, value) } stat, err := os.Stdin.Stat() if err != nil { stdcli.Error(err) return } if (stat.Mode() & os.ModeCharDevice) == 0 { in, err := ioutil.ReadAll(os.Stdin) if err != nil { stdcli.Error(err) return } data += string(in) } for _, value := range c.Args() { data += fmt.Sprintf("%s\n", value) } path := fmt.Sprintf("/apps/%s/environment", app) resp, err = ConvoxPost(path, data) if err != nil { stdcli.Error(err) return } }
func cmdEnvGetAll(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } resp, err := fetchEnv(app) if err != nil { stdcli.Error(err) return } var env map[string]string err = json.Unmarshal(resp, &env) if err != nil { stdcli.Error(err) return } keys := []string{} for key, _ := range env { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { fmt.Printf("%s=%s\n", key, env[key]) } }
func cmdEnvUnset(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) == 0 { stdcli.Error(errors.New("No variable specified")) return } if len(c.Args()) > 1 { stdcli.Error(errors.New("Only 1 variable can be unset at a time")) return } variable := c.Args()[0] path := fmt.Sprintf("/apps/%s/environment/%s", app, variable) _, err = ConvoxDelete(path) if err != nil { stdcli.Error(err) return } }
func cmdEnvUnset(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) == 1 { variable := c.Args()[0] path := fmt.Sprintf("/apps/%s/environment/%s", app, variable) _, err = ConvoxDelete(path) if err != nil { stdcli.Error(err) return } else { fmt.Printf("You supplied %d arguments to convox env unset, 1 is required\n", len(c.Args())) } } func fetchEnv(app string) ([]byte, error) { path := fmt.Sprintf("/apps/%s/environment", app) resp, err := ConvoxGet(path) if err != nil { return nil, err } return resp, nil }
func cmdBuilds(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } path := fmt.Sprintf("/apps/%s/builds", app) resp, err := ConvoxGet(path) if err != nil { stdcli.Error(err) return } var builds []Build err = json.Unmarshal(resp, &builds) if err != nil { stdcli.Error(err) return } fmt.Printf("%-12s %-12s %-9s %-22s %s\n", "ID", "RELEASE", "STATUS", "STARTED", "ENDED") for _, build := range builds { started := build.Started ended := build.Ended fmt.Printf("%-12s %-12s %-9s %-22s %s\n", build.Id, build.Release, build.Status, started.Format(time.RFC822Z), ended.Format(time.RFC822Z)) } }
func cmdEnvGet(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) == 1 { variable := c.Args()[0] resp, err := fetchEnv(app) if err != nil { stdcli.Error(err) return } var env map[string]string json.Unmarshal(resp, &env) fmt.Println(env[variable]) } else { fmt.Printf("You supplied %d arguments to convox env get, 1 is required\n", len(c.Args())) } }
func cmdExec(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) < 1 { stdcli.Usage(c, "exec") return } ps := c.Args()[0] command := "" if len(c.Args()) > 1 { args := c.Args()[1:] command = strings.Join(args, " ") } err = stdcli.Run("docker", "exec", "-it", fmt.Sprintf("%s-%s", app, ps), "sh", "-c", command) if err != nil { stdcli.Error(err) return } }
func cmdRun(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) < 1 { stdcli.Usage(c, "run") return } ps := c.Args()[0] command := "" if len(c.Args()) > 1 { args := c.Args()[1:] command = strings.Join(args, " ") } v := url.Values{} v.Set("command", command) _, err = ConvoxPostForm(fmt.Sprintf("/apps/%s/processes/%s/run", app, ps), v) if err != nil { stdcli.Error(err) return } fmt.Printf("Running %s `%s`\n", ps, command) }
func cmdBuildsInfo(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) != 1 { stdcli.Usage(c, "info") return } build := c.Args()[0] path := fmt.Sprintf("/apps/%s/builds/%s", app, build) resp, err := ConvoxGet(path) if err != nil { stdcli.Error(err) return } var b Build err = json.Unmarshal(resp, &b) if err != nil { stdcli.Error(err) return } fmt.Println(b.Logs) }
func cmdEnvGet(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) == 0 { stdcli.Error(errors.New("No variable specified")) return } if len(c.Args()) > 1 { stdcli.Error(errors.New("Only 1 variable can be retrieved at a time")) return } variable := c.Args()[0] resp, err := fetchEnv(app) if err != nil { stdcli.Error(err) return } var env map[string]string json.Unmarshal(resp, &env) fmt.Println(env[variable]) }
func cmdPs(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) == 0 { processList(app) } else { processTop(app, c.Args()[0]) } }
func cmdReleases(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } data, err := ConvoxGet("/apps/" + app) if err != nil { stdcli.Error(err) return } var a *App err = json.Unmarshal(data, &a) data, err = ConvoxGet(fmt.Sprintf("/apps/%s/releases", a.Name)) if err != nil { stdcli.Error(err) return } var releases *Releases err = json.Unmarshal(data, &releases) if err != nil { stdcli.Error(err) return } t := stdcli.NewTable("ID", "CREATED", "ACTIVE") for _, r := range *releases { active := "" if a.Parameters["Release"] == r.Id { active = "yes" } t.AddRow(r.Id, humanize.Time(r.Created), active) } t.Print() }
func cmdScale(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } v := url.Values{} if c.IsSet("count") { v.Set("count", c.String("count")) } if c.IsSet("memory") { v.Set("mem", c.String("memory")) } if len(v) > 0 { _, err = ConvoxPostForm("/apps/"+app, v) if err != nil { stdcli.Error(err) return } } data, err := ConvoxGet("/apps/" + app) if err != nil { stdcli.Error(err) return } var a *App err = json.Unmarshal(data, &a) if err != nil { stdcli.Error(err) return } fmt.Printf("Count %v\n", a.Parameters["DesiredCount"]) fmt.Printf("Memory %v\n", a.Parameters["Memory"]) }
func cmdEnvUnset(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } variable := c.Args()[0] path := fmt.Sprintf("/apps/%s/environment/%s", app, variable) _, err = ConvoxDelete(path) if err != nil { stdcli.Error(err) return } }
func cmdDebug(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } data, err := ConvoxGet(fmt.Sprintf("/apps/%s/events", app)) if err != nil { stdcli.Error(err) return } lines := strings.Split(string(data), "\n") for i := len(lines) - 1; i >= 0; i-- { fmt.Printf("%v\n", lines[i]) } }
func cmdBuild(c *cli.Context) { wd := "." if len(c.Args()) > 0 { wd = c.Args()[0] } dir, app, err := stdcli.DirApp(c, wd) if err != nil { stdcli.Error(err) return } _, err = executeBuild(dir, app) if err != nil { stdcli.Error(err) return } }
func cmdEnvGet(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } variable := c.Args()[0] resp, err := fetchEnv(app) if err != nil { stdcli.Error(err) return } var env map[string]string json.Unmarshal(resp, &env) fmt.Println(env[variable]) }
func cmdReleasePromote(c *cli.Context) { if len(c.Args()) < 1 { stdcli.Usage(c, "release promote") return } release := c.Args()[0] _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } _, err = postRelease(app, release) if err != nil { stdcli.Error(err) return } }
func cmdReleaseInfo(c *cli.Context) { if len(c.Args()) < 1 { stdcli.Usage(c, "release info") return } release := c.Args()[0] _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } data, err := ConvoxGet(fmt.Sprintf("/apps/%s/releases/%s", app, release)) if err != nil { stdcli.Error(err) return } var r Release err = json.Unmarshal(data, &r) if err != nil { stdcli.Error(err) return } fmt.Printf("Id %s\n", r.Id) fmt.Printf("Build %s\n", r.Build) fmt.Printf("Created %s\n", r.Created) fmt.Printf("Env ") fmt.Println(strings.Replace(r.Env, "\n", "\n ", -1)) }
func cmdPs(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } data, err := ConvoxGet(fmt.Sprintf("/apps/%s/processes", app)) if err != nil { stdcli.Error(err) return } var processes *Processes err = json.Unmarshal(data, &processes) if err != nil { stdcli.Error(err) return } longest := 7 for _, ps := range *processes { if len(ps.Name) > longest { longest = len(ps.Name) } } fmt.Printf(fmt.Sprintf("%%-12s %%-%ds %%-11s %%-5s %%s\n", longest), "ID", "PROCESS", "RELEASE", "MEM", "COMMAND") for _, ps := range *processes { fmt.Printf(fmt.Sprintf("%%-12s %%-%ds %%-11s %%-5d %%s\n", longest), ps.Id, ps.Name, ps.Release, ps.Memory, ps.Command) } }
func cmdPsStop(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) != 1 { stdcli.Usage(c, "stop") return } id := c.Args()[0] _, err = ConvoxDelete(fmt.Sprintf("/apps/%s/processes/%s", app, id)) if err != nil { stdcli.Error(err) return } fmt.Printf("Stopping %s\n", id) }
func cmdDeploy(c *cli.Context) { wd := "." if len(c.Args()) > 0 { wd = c.Args()[0] } dir, app, err := stdcli.DirApp(c, wd) if err != nil { stdcli.Error(err) return } fmt.Printf("Deploying %s\n", app) _, err = ConvoxGet(fmt.Sprintf("/apps/%s", app)) if err != nil { stdcli.Error(err) return } // build release, err := executeBuild(dir, app) if err != nil { stdcli.Error(err) return } if release == "" { return } a, err := postRelease(app, release) if err != nil { stdcli.Error(err) return } urls := []string{} hosts := []string{} matcher := regexp.MustCompile(`^(\w+)Port\d+Balancer`) if host, ok := a.Outputs["BalancerHost"]; ok { for key, value := range a.Outputs { if m := matcher.FindStringSubmatch(key); m != nil { url := fmt.Sprintf("http://%s:%s", host, value) urls = append(urls, url) hosts = append(hosts, fmt.Sprintf("%s: %s", strings.ToLower(m[1]), url)) } } } fmt.Print("Waiting for app... ") ch := make(chan error) for _, url := range urls { go func() { waitForAvailability(url) ch <- nil }() } for _ = range urls { <-ch } fmt.Println("OK") for _, host := range hosts { fmt.Println(host) } }
func cmdScale(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) < 1 { stdcli.Usage(c, "scale") return } v := url.Values{} if c.IsSet("count") { v.Set("count", c.String("count")) } if c.IsSet("memory") { v.Set("mem", c.String("memory")) } if len(v) > 0 { v.Set("process", c.Args()[0]) _, err = ConvoxPostForm("/apps/"+app, v) if err != nil { stdcli.Error(err) return } } data, err := ConvoxGet("/apps/" + app) if err != nil { stdcli.Error(err) return } var a *App err = json.Unmarshal(data, &a) if err != nil { stdcli.Error(err) return } processes := make(map[string]Process, 0) longest := 7 for k, v := range a.Parameters { if !strings.HasSuffix(k, "DesiredCount") { continue } ps := strings.Replace(k, "DesiredCount", "", 1) p := strings.ToLower(ps) i, err := strconv.ParseInt(v, 10, 64) if err != nil { stdcli.Error(err) return } m, err := strconv.ParseInt(a.Parameters[ps+"Memory"], 10, 64) if err != nil { stdcli.Error(err) return } processes[p] = Process{Name: p, Count: i, Memory: m} } fmt.Printf(fmt.Sprintf("%%-%ds %%-5s %%-5s\n", longest), "PROCESS", "COUNT", "MEM") for _, ps := range processes { fmt.Printf(fmt.Sprintf("%%-%ds %%-5d %%-5d\n", longest), ps.Name, ps.Count, ps.Memory) } }
func cmdAppCreate(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) > 0 { app = c.Args()[0] } if app == "" { fmt.Printf("Creating app: ") } else { fmt.Printf("Creating app %s: ", app) } v := url.Values{} v.Set("name", app) data, err := ConvoxPostForm("/apps", v) if err != nil { stdcli.Error(err) return } data, err = ConvoxGet("/apps/" + app) if err != nil { stdcli.Error(err) return } var a *App err = json.Unmarshal(data, &a) if err != nil { stdcli.Error(err) return } // poll for complete for { data, err = ConvoxGet(fmt.Sprintf("/apps/%s/status", app)) if err != nil { stdcli.Error(err) return } if string(data) == "running" { break } time.Sleep(3 * time.Second) } if app == "" { fmt.Printf("OK, %s\n", a.Name) } else { fmt.Println("OK") } }
func cmdRunAttached(c *cli.Context) { fd := os.Stdin.Fd() oldState, err := term.SetRawTerminal(fd) if err != nil { stdcli.Error(err) return } defer term.RestoreTerminal(fd, oldState) _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } if len(c.Args()) < 2 { stdcli.Usage(c, "run") return } ps := c.Args()[0] host, password, err := currentLogin() if err != nil { stdcli.Error(err) return } origin := fmt.Sprintf("https://%s", host) url := fmt.Sprintf("wss://%s/apps/%s/processes/%s/run", host, app, ps) config, err := websocket.NewConfig(url, origin) if err != nil { stdcli.Error(err) return } command := strings.Join(c.Args()[1:], " ") config.Header.Set("Command", command) userpass := fmt.Sprintf("convox:%s", password) userpass_encoded := b64.StdEncoding.EncodeToString([]byte(userpass)) config.Header.Add("Authorization", fmt.Sprintf("Basic %s", userpass_encoded)) config.TlsConfig = &tls.Config{ InsecureSkipVerify: true, } ws, err := websocket.DialConfig(config) if err != nil { stdcli.Error(err) return } defer ws.Close() ch := make(chan int) go io.Copy(ws, os.Stdin) go messageReceive(ws, os.Stdout, ch) code := <-ch term.RestoreTerminal(os.Stdin.Fd(), oldState) os.Exit(code) }
func cmdStart(c *cli.Context) { wd := "." if len(c.Args()) > 0 { wd = c.Args()[0] } dir, app, err := stdcli.DirApp(c, wd) if err != nil { stdcli.Error(err) return } m, err := manifest.Generate(dir) if err != nil { stdcli.Error(err) return } missing := m.MissingEnvironment() if len(missing) > 0 { stdcli.Error(fmt.Errorf("env expected: %s", strings.Join(missing, ", "))) return } wanted, err := m.PortsWanted() if err != nil { stdcli.Error(err) return } conflicts := make([]string, 0) host := "127.0.0.1" if h := os.Getenv("DOCKER_HOST"); h != "" { u, err := url.Parse(h) if err != nil { stdcli.Error(err) return } parts := strings.Split(u.Host, ":") host = parts[0] } for _, p := range wanted { conn, err := net.Dial("tcp", fmt.Sprintf("%s:%s", host, p)) if err == nil { conflicts = append(conflicts, p) defer conn.Close() } } if len(conflicts) > 0 { stdcli.Error(fmt.Errorf("ports in use: %s", strings.Join(conflicts, ", "))) return } errors := m.Build(app, dir) if len(errors) != 0 { fmt.Printf("errors: %+v\n", errors) return } errors = m.Run(app) if len(errors) != 0 { // TODO figure out what to do here // fmt.Printf("errors: %+v\n", errors) return } }
func cmdScale(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } v := url.Values{} if c.IsSet("count") { v.Set("count", c.String("count")) } if c.IsSet("memory") { v.Set("mem", c.String("memory")) } if len(v) > 0 { v.Set("process", c.Args()[0]) _, err = ConvoxPostForm("/apps/"+app, v) if err != nil { stdcli.Error(err) return } } data, err := ConvoxGet("/apps/" + app) if err != nil { stdcli.Error(err) return } var a *App err = json.Unmarshal(data, &a) if err != nil { stdcli.Error(err) return } processes := map[string]Process{} names := []string{} for k, v := range a.Parameters { if !strings.HasSuffix(k, "DesiredCount") { continue } ps := strings.Replace(k, "DesiredCount", "", 1) p := strings.ToLower(ps) i, err := strconv.ParseInt(v, 10, 64) if err != nil { stdcli.Error(err) return } m, err := strconv.ParseInt(a.Parameters[ps+"Memory"], 10, 64) if err != nil { stdcli.Error(err) return } processes[p] = Process{Name: p, Count: i, Memory: m} names = append(names, p) } sort.Strings(names) t := stdcli.NewTable("PROCESS", "COUNT", "MEM") for _, name := range names { ps := processes[name] t.AddRow(ps.Name, fmt.Sprintf("%d", ps.Count), fmt.Sprintf("%d", ps.Memory)) } t.Print() }
func cmdLogsStream(c *cli.Context) { for { _, app, err := stdcli.DirApp(c, ".") if err != nil { stdcli.Error(err) return } host, password, err := currentLogin() if err != nil { stdcli.Error(err) return } origin := fmt.Sprintf("https://%s", host) url := fmt.Sprintf("wss://%s/apps/%s/logs/stream", host, app) config, err := websocket.NewConfig(url, origin) if err != nil { stdcli.Error(err) return } userpass := fmt.Sprintf("convox:%s", password) userpass_encoded := b64.StdEncoding.EncodeToString([]byte(userpass)) config.Header.Add("Authorization", fmt.Sprintf("Basic %s", userpass_encoded)) config.TlsConfig = &tls.Config{ InsecureSkipVerify: true, } ws, err := websocket.DialConfig(config) if err != nil { stdcli.Error(err) return } defer ws.Close() var message []byte for { err := websocket.Message.Receive(ws, &message) if err == io.EOF { return } if err != nil { break } fmt.Print(string(message)) } fmt.Println("out") } }
func cmdAppInfo(c *cli.Context) { _, app, err := stdcli.DirApp(c, ".") data, err := ConvoxGet("/apps/" + app) if err != nil { stdcli.Error(err) return } var a *App err = json.Unmarshal(data, &a) if err != nil { stdcli.Error(err) return } matcher := regexp.MustCompile(`^(\w+)Port\d+Balancer`) ports := []string{} for key, value := range a.Outputs { if m := matcher.FindStringSubmatch(key); m != nil { ports = append(ports, fmt.Sprintf("%s:%s", strings.ToLower(m[1]), value)) } } processes := []string{} for key, _ := range a.Parameters { if strings.HasSuffix(key, "Image") { processes = append(processes, strings.ToLower(key[0:len(key)-5])) } } sort.Strings(processes) if len(processes) == 0 { processes = append(processes, "(none)") } if len(ports) == 0 { ports = append(ports, "(none)") } release := a.Parameters["Release"] if release == "" { release = "(none)" } fmt.Printf("Name %s\n", a.Name) fmt.Printf("Status %s\n", a.Status) fmt.Printf("Release %s\n", release) fmt.Printf("Processes %s\n", strings.Join(processes, " ")) if a.Outputs["BalancerHost"] != "" { fmt.Printf("Hostname %s\n", a.Outputs["BalancerHost"]) fmt.Printf("Ports %s\n", strings.Join(ports, " ")) } }
func cmdDeploy(c *cli.Context) { wd := "." if len(c.Args()) > 0 { wd = c.Args()[0] } dir, app, err := stdcli.DirApp(c, wd) if err != nil { stdcli.Error(err) return } data, err := ConvoxGet(fmt.Sprintf("/apps/%s", app)) if err != nil { stdcli.Error(err) return } // build release, err := executeBuild(dir, app) if err != nil { stdcli.Error(err) return } if release == "" { return } fmt.Print("Releasing... ") // promote release data, err = ConvoxPost(fmt.Sprintf("/apps/%s/releases/%s/promote", app, release), "") if err != nil { stdcli.Error(err) return } // poll for complete for { data, err = ConvoxGet(fmt.Sprintf("/apps/%s/status", app)) if err != nil { stdcli.Error(err) return } if string(data) == "running" { break } time.Sleep(1 * time.Second) } data, err = ConvoxGet("/apps/" + app) if err != nil { stdcli.Error(err) return } var a *App err = json.Unmarshal(data, &a) if err != nil { stdcli.Error(err) return } fmt.Printf("OK, %s\n", a.Parameters["Release"]) urls := []string{} hosts := []string{} matcher := regexp.MustCompile(`^(\w+)Port\d+Balancer`) if host, ok := a.Outputs["BalancerHost"]; ok { for key, value := range a.Outputs { if m := matcher.FindStringSubmatch(key); m != nil { url := fmt.Sprintf("http://%s:%s", host, value) urls = append(urls, url) hosts = append(hosts, fmt.Sprintf("%s: %s", strings.ToLower(m[1]), url)) } } } fmt.Print("Waiting for app... ") ch := make(chan error) for _, url := range urls { go func() { waitForAvailability(url) ch <- nil }() } for _ = range urls { <-ch } fmt.Println("OK") for _, host := range hosts { fmt.Println(host) } }