func (listPoolsInTheSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error { t := cmd.Table{Headers: cmd.Row([]string{"Pools", "Teams"})} url, err := cmd.GetURL("/docker/pool") if err != nil { return err } req, err := http.NewRequest("GET", url, nil) if err != nil { return err } resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) var pools []Pool err = json.Unmarshal(body, &pools) for _, p := range pools { t.AddRow(cmd.Row([]string{p.Name, strings.Join(p.Teams, ", ")})) } t.Sort() ctx.Stdout.Write(t.Bytes()) return nil }
func (c *templateList) Run(context *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/iaas/templates") if err != nil { return err } request, err := http.NewRequest("GET", url, nil) if err != nil { return err } response, err := client.Do(request) if err != nil { return err } var templates []iaas.Template err = json.NewDecoder(response.Body).Decode(&templates) if err != nil { return err } table := cmd.NewTable() table.Headers = cmd.Row([]string{"Name", "IaaS", "Params"}) table.LineSeparator = true for _, template := range templates { var params []string for _, data := range template.Data { params = append(params, fmt.Sprintf("%s=%s", data.Name, data.Value)) } sort.Strings(params) table.AddRow(cmd.Row([]string{template.Name, template.IaaSName, strings.Join(params, "\n")})) } table.Sort() context.Stdout.Write(table.Bytes()) return nil }
func (c *appList) Show(result []byte, context *cmd.Context) error { var apps []app err := json.Unmarshal(result, &apps) if err != nil { return err } table := cmd.NewTable() table.Headers = cmd.Row([]string{"Application", "Units State Summary", "Address"}) for _, app := range apps { var available int var total int for _, unit := range app.Units { if unit.Name != "" { total++ if unit.Available() { available++ } } } summary := fmt.Sprintf("%d of %d units in-service", available, total) addrs := strings.Replace(app.Addr(), ", ", "\n", -1) table.AddRow(cmd.Row([]string{app.Name, summary, addrs})) } table.LineSeparator = true table.Sort() context.Stdout.Write(table.Bytes()) return nil }
func (poolList) Run(context *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/pools") if err != nil { return err } request, err := http.NewRequest("GET", url, nil) if err != nil { return err } resp, err := client.Do(request) if err != nil { return err } defer resp.Body.Close() var pools ListPoolResponse err = json.NewDecoder(resp.Body).Decode(&pools) if err != nil { return err } t := cmd.Table{Headers: cmd.Row([]string{"Team", "Pools"})} for _, pool := range pools.PoolsByTeam { t.AddRow(cmd.Row([]string{pool.Team, strings.Join(pool.Pools, ", ")})) } t.Sort() context.Stdout.Write(t.Bytes()) tp := cmd.Table{Headers: cmd.Row([]string{"Public Pools"})} for _, pool := range pools.PublicPools { tp.AddRow(cmd.Row([]string{pool.Name})) } tp.Sort() context.Stdout.Write([]byte("\n")) context.Stdout.Write(tp.Bytes()) return nil }
func (c serviceInfo) BuildPlansTable(serviceName string, ctx *cmd.Context, client *cmd.Client) error { ctx.Stdout.Write([]byte("\nPlans\n")) url, err := cmd.GetURL(fmt.Sprintf("/services/%s/plans", serviceName)) if err != nil { return err } request, err := http.NewRequest("GET", url, nil) if err != nil { return err } resp, err := client.Do(request) if err != nil { return err } defer resp.Body.Close() result, err := ioutil.ReadAll(resp.Body) if err != nil { return err } var plans []map[string]string err = json.Unmarshal(result, &plans) if err != nil { return err } if len(plans) > 0 { table := cmd.NewTable() for _, plan := range plans { data := []string{plan["Name"], plan["Description"]} table.AddRow(cmd.Row(data)) } table.Headers = cmd.Row([]string{"Name", "Description"}) ctx.Stdout.Write(table.Bytes()) } return nil }
func (c *listUsers) Run(ctx *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/users") if err != nil { return err } request, _ := http.NewRequest("GET", url, nil) resp, err := client.Do(request) if err != nil { return err } defer resp.Body.Close() var users []user err = json.NewDecoder(resp.Body).Decode(&users) if err != nil { return err } table := cmd.NewTable() table.Headers = cmd.Row([]string{"User", "Teams"}) for _, u := range users { table.AddRow(cmd.Row([]string{u.Email, strings.Join(u.Teams, ", ")})) } table.LineSeparator = true table.Sort() ctx.Stdout.Write(table.Bytes()) return nil }
func renderHistoryTable(history []healingEvent, filter string, ctx *cmd.Context) { fmt.Fprintln(ctx.Stdout, strings.ToUpper(filter[:1])+filter[1:]+":") headers := cmd.Row([]string{"Start", "Finish", "Success", "Failing", "Created", "Error"}) t := cmd.Table{Headers: headers} for i := len(history) - 1; i >= 0; i-- { event := history[i] if event.Action != filter+"-healing" { continue } data := make([]string, 2) if filter == "node" { data[0] = event.FailingNode.Address data[1] = event.CreatedNode.Address } else { data[0] = event.FailingContainer.ID data[1] = event.CreatedContainer.ID if len(data[0]) > 10 { data[0] = data[0][:10] } if len(data[1]) > 10 { data[1] = data[1][:10] } } t.AddRow(cmd.Row([]string{ event.StartTime.Local().Format(time.Stamp), event.EndTime.Local().Format(time.Stamp), fmt.Sprintf("%t", event.Successful), data[0], data[1], event.Error, })) } t.LineSeparator = true ctx.Stdout.Write(t.Bytes()) }
func (c *InfoCmd) Run(context *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/docker/bs") if err != nil { return err } request, err := http.NewRequest("GET", url, nil) if err != nil { return err } response, err := client.Do(request) if err != nil { return err } defer response.Body.Close() var conf provision.ScopedConfig err = json.NewDecoder(response.Body).Decode(&conf) if err != nil { return err } fmt.Fprintf(context.Stdout, "Image: %s\n\nEnvironment Variables [Default]:\n", conf.Extra["image"]) t := cmd.Table{Headers: cmd.Row([]string{"Name", "Value"})} for _, envVar := range conf.Envs { t.AddRow(cmd.Row([]string{envVar.Name, fmt.Sprintf("%v", envVar.Value)})) } context.Stdout.Write(t.Bytes()) for _, pool := range conf.Pools { t := cmd.Table{Headers: cmd.Row([]string{"Name", "Value"})} fmt.Fprintf(context.Stdout, "\nEnvironment Variables [%s]:\n", pool.Name) for _, envVar := range pool.Envs { t.AddRow(cmd.Row([]string{envVar.Name, fmt.Sprintf("%v", envVar.Value)})) } context.Stdout.Write(t.Bytes()) } return nil }
func (c *listUsers) Run(ctx *cmd.Context, client *cmd.Client) error { if c.userEmail != "" && c.role != "" { return errors.New("You cannot set more than one flag. Enter <tsuru user-list --help> for more information.") } url := fmt.Sprintf("/users?userEmail=%s&role=%s", c.userEmail, c.role) url, err := cmd.GetURL(url) if err != nil { return err } request, _ := http.NewRequest("GET", url, nil) resp, err := client.Do(request) if err != nil { return err } defer resp.Body.Close() var users []cmd.APIUser err = json.NewDecoder(resp.Body).Decode(&users) if err != nil { return err } table := cmd.NewTable() table.Headers = cmd.Row([]string{"User", "Roles"}) for _, u := range users { table.AddRow(cmd.Row([]string{ u.Email, strings.Join(u.RoleInstances(), "\n"), })) } table.LineSeparator = true table.Sort() ctx.Stdout.Write(table.Bytes()) return nil }
func (c *machineList) Run(context *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/iaas/machines") if err != nil { return err } request, err := http.NewRequest("GET", url, nil) if err != nil { return err } response, err := client.Do(request) if err != nil { return err } var machines []iaas.Machine err = json.NewDecoder(response.Body).Decode(&machines) if err != nil { return err } table := cmd.NewTable() table.Headers = cmd.Row([]string{"Id", "IaaS", "Address", "Creation Params"}) table.LineSeparator = true for _, machine := range machines { var params []string for k, v := range machine.CreationParams { params = append(params, fmt.Sprintf("%s=%s", k, v)) } sort.Strings(params) table.AddRow(cmd.Row([]string{machine.Id, machine.Iaas, machine.Address, strings.Join(params, "\n")})) } table.Sort() context.Stdout.Write(table.Bytes()) return nil }
func (c *planRoutersList) Run(context *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/plans/routers") if err != nil { return err } request, err := http.NewRequest("GET", url, nil) if err != nil { return err } response, err := client.Do(request) if err != nil { return err } var routers []router.PlanRouter err = json.NewDecoder(response.Body).Decode(&routers) if err != nil { return err } table := cmd.NewTable() table.Headers = cmd.Row([]string{"Name", "Type"}) table.LineSeparator = true for _, router := range routers { table.AddRow(cmd.Row([]string{router.Name, router.Type})) } context.Stdout.Write(table.Bytes()) return nil }
func (listNodesInTheSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/node") if err != nil { return err } req, err := http.NewRequest("GET", url, nil) if err != nil { return err } resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) var nodes []map[string]string err = json.Unmarshal(body, &nodes) t := cmd.Table{Headers: cmd.Row([]string{"Address"})} for _, n := range nodes { t.AddRow(cmd.Row([]string{n["Address"]})) } t.Sort() ctx.Stdout.Write(t.Bytes()) return nil }
func (listPoolsInTheSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error { t := cmd.Table{Headers: cmd.Row([]string{"Pools", "Teams", "Public"})} url, err := cmd.GetURL("/pool") if err != nil { return err } req, err := http.NewRequest("GET", url, nil) if err != nil { return err } resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) var pools []provision.Pool err = json.Unmarshal(body, &pools) for _, p := range pools { public := "" if p.Public { public = " X " } defaultFlag := "" if p.Default { defaultFlag = " (default)" } t.AddRow(cmd.Row([]string{p.Name + defaultFlag, strings.Join(p.Teams, ", "), public})) } t.Sort() ctx.Stdout.Write(t.Bytes()) return nil }
func (a *app) String() string { format := `Application: {{.Name}} Repository: {{.Repository}} Platform: {{.Platform}} Teams: {{.GetTeams}} Address: {{.Addr}} Owner: {{.Owner}} Team owner: {{.TeamOwner}} Deploys: {{.Deploys}} ` tmpl := template.Must(template.New("app").Parse(format)) units := cmd.NewTable() units.Headers = cmd.Row([]string{"Unit", "State"}) for _, unit := range a.Units { if unit.Name != "" { units.AddRow(cmd.Row([]string{unit.Name, unit.Status})) } } var buf bytes.Buffer tmpl.Execute(&buf, a) var suffix string if units.Rows() > 0 { suffix = fmt.Sprintf("Units:\n%s", units) } return buf.String() + suffix }
func (c serviceInfo) BuildInstancesTable(serviceName string, ctx *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/services/" + serviceName) if err != nil { return err } request, err := http.NewRequest("GET", url, nil) if err != nil { return err } resp, err := client.Do(request) if err != nil { return err } defer resp.Body.Close() result, err := ioutil.ReadAll(resp.Body) if err != nil { return err } var instances []ServiceInstanceModel err = json.Unmarshal(result, &instances) if err != nil { return err } ctx.Stdout.Write([]byte(fmt.Sprintf("Info for \"%s\"\n\n", serviceName))) if len(instances) > 0 { ctx.Stdout.Write([]byte("Instances\n")) table := cmd.NewTable() extraHeaders := c.ExtraHeaders(instances) hasPlan := false var data []string var headers []string for _, instance := range instances { if instance.PlanName != "" { hasPlan = true } } for _, instance := range instances { apps := strings.Join(instance.Apps, ", ") if hasPlan { data = []string{instance.Name, instance.PlanName, apps} } else { data = []string{instance.Name, apps} } for _, h := range extraHeaders { data = append(data, instance.Info[h]) } table.AddRow(cmd.Row(data)) } if hasPlan { headers = []string{"Instances", "Plan", "Apps"} } else { headers = []string{"Instances", "Apps"} } headers = append(headers, extraHeaders...) table.Headers = cmd.Row(headers) ctx.Stdout.Write(table.Bytes()) } return nil }
func (listNodesInTheSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/docker/node") if err != nil { return err } req, err := http.NewRequest("GET", url, nil) if err != nil { return err } resp, err := client.Do(req) if err != nil { return err } var result map[string]interface{} err = json.NewDecoder(resp.Body).Decode(&result) if err != nil { return err } machineMap := map[string]map[string]interface{}{} if result["machines"] != nil { machines := result["machines"].([]interface{}) for _, m := range machines { machine := m.(map[string]interface{}) machineMap[machine["Address"].(string)] = m.(map[string]interface{}) } } t := cmd.Table{Headers: cmd.Row([]string{"Address", "IaaS ID", "Status", "Metadata"}), LineSeparator: true} var nodes []interface{} if result["nodes"] != nil { nodes = result["nodes"].([]interface{}) } for _, n := range nodes { node := n.(map[string]interface{}) addr := node["Address"].(string) status := node["Status"].(string) result := []string{} metadataField, _ := node["Metadata"] if metadataField != nil { metadata := metadataField.(map[string]interface{}) for key, value := range metadata { result = append(result, fmt.Sprintf("%s=%s", key, value.(string))) } } sort.Strings(result) m, ok := machineMap[urlToHost(addr)] var iaasId string if ok { iaasId = m["Id"].(string) } t.AddRow(cmd.Row([]string{addr, iaasId, status, strings.Join(result, "\n")})) } t.Sort() ctx.Stdout.Write(t.Bytes()) return nil }
func (listNodesInTheSchedulerCmd) Run(ctx *cmd.Context, client *cmd.Client) error { t := cmd.Table{Headers: cmd.Row([]string{"ID", "Address", "Team"})} nodes, err := listNodesInTheScheduler() if err != nil { return err } for _, n := range nodes { t.AddRow(cmd.Row([]string{n.ID, n.Address, strings.Join(n.Teams, ", ")})) } t.Sort() ctx.Stdout.Write(t.Bytes()) return nil }
func (c *dockerLogInfo) Run(context *cmd.Context, client *cmd.Client) error { u, err := cmd.GetURL("/docker/logs") if err != nil { return err } request, err := http.NewRequest("GET", u, nil) if err != nil { return err } response, err := client.Do(request) if err != nil { return err } defer response.Body.Close() var conf map[string]container.DockerLogConfig err = json.NewDecoder(response.Body).Decode(&conf) if err != nil { return err } baseConf := conf[""] delete(conf, "") t := cmd.Table{Headers: cmd.Row([]string{"Name", "Value"})} fmt.Fprintf(context.Stdout, "Log driver [default]: %s\n", baseConf.Driver) for optName, optValue := range baseConf.LogOpts { t.AddRow(cmd.Row([]string{optName, optValue})) } if t.Rows() > 0 { t.Sort() context.Stdout.Write(t.Bytes()) } poolNames := make([]string, 0, len(baseConf.LogOpts)) for poolName := range conf { poolNames = append(poolNames, poolName) } sort.Strings(poolNames) for _, poolName := range poolNames { poolConf := conf[poolName] t := cmd.Table{Headers: cmd.Row([]string{"Name", "Value"})} fmt.Fprintf(context.Stdout, "\nLog driver [pool %s]: %s\n", poolName, poolConf.Driver) for optName, optValue := range poolConf.LogOpts { t.AddRow(cmd.Row([]string{optName, optValue})) } if t.Rows() > 0 { t.Sort() context.Stdout.Write(t.Bytes()) } } return nil }
func (c *dockerLogInfo) Run(context *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/docker/logs") if err != nil { return err } request, err := http.NewRequest("GET", url, nil) if err != nil { return err } response, err := client.Do(request) if err != nil { return err } defer response.Body.Close() var conf provision.ScopedConfig err = json.NewDecoder(response.Body).Decode(&conf) if err != nil { return err } t := cmd.Table{Headers: cmd.Row([]string{"Name", "Value"})} for _, envVar := range conf.Envs { if envVar.Name == container.DockerLogDriverConfig { fmt.Fprintf(context.Stdout, "Log driver [default]: %s\n", envVar.Value) continue } t.AddRow(cmd.Row([]string{envVar.Name, fmt.Sprintf("%v", envVar.Value)})) } if t.Rows() > 0 { t.Sort() context.Stdout.Write(t.Bytes()) } sort.Sort(provision.ConfigPoolEntryList(conf.Pools)) for _, pool := range conf.Pools { t := cmd.Table{Headers: cmd.Row([]string{"Name", "Value"})} for _, envVar := range pool.Envs { if envVar.Name == container.DockerLogDriverConfig { fmt.Fprintf(context.Stdout, "\nLog driver [pool %s]: %s\n", pool.Name, envVar.Value) continue } t.AddRow(cmd.Row([]string{envVar.Name, fmt.Sprintf("%v", envVar.Value)})) } if t.Rows() > 0 { t.Sort() context.Stdout.Write(t.Bytes()) } } return nil }
func (c *keyList) Run(context *cmd.Context, client *cmd.Client) error { url, err := cmd.GetURL("/users/keys") if err != nil { return err } request, err := http.NewRequest("GET", url, nil) if err != nil { return err } resp, err := client.Do(request) if err != nil { return err } defer resp.Body.Close() var keys map[string]string err = json.NewDecoder(resp.Body).Decode(&keys) if err != nil { return err } var table cmd.Table table.Headers = cmd.Row{"Name", "Content"} table.LineSeparator = c.notrunc for name, content := range keys { row := []string{name, content} if !c.notrunc && len(row[1]) > keyTruncate { row[1] = row[1][:keyTruncate] + "..." } table.AddRow(cmd.Row(row)) } table.SortByColumn(0) context.Stdout.Write(table.Bytes()) return nil }
func (c *listAutoScaleHistoryCmd) Run(ctx *cmd.Context, client *cmd.Client) error { if c.page < 1 { c.page = 1 } limit := 20 skip := (c.page - 1) * limit u, err := cmd.GetURL(fmt.Sprintf("/docker/autoscale?skip=%d&limit=%d", skip, limit)) if err != nil { return err } req, err := http.NewRequest("GET", u, nil) if err != nil { return err } resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() var history []autoScaleEvent if resp.StatusCode == 204 { ctx.Stdout.Write([]byte("There is no auto scales yet.\n")) return nil } err = json.NewDecoder(resp.Body).Decode(&history) if err != nil { return err } headers := cmd.Row([]string{"Start", "Finish", "Success", "Metadata", "Action", "Reason", "Error"}) t := cmd.Table{Headers: headers} for i := range history { event := &history[i] t.AddRow(cmd.Row([]string{ event.StartTime.Local().Format(time.Stamp), checkEndOfEvent(event), fmt.Sprintf("%t", event.Successful), event.MetadataValue, event.Action, event.Reason, event.Error, })) } t.LineSeparator = true ctx.Stdout.Write(t.Bytes()) return nil }
func (a *app) String() string { format := `Application: %s Repository: %s Platform: %s Teams: %s Address: %s Owner: %s Deploys: %d ` teams := strings.Join(a.Teams, ", ") units := cmd.NewTable() units.Headers = cmd.Row([]string{"Unit", "State"}) for _, unit := range a.Units { if unit.Name != "" { units.AddRow(cmd.Row([]string{unit.Name, unit.State})) } } args := []interface{}{a.Name, a.Repository, a.Platform, teams, a.Addr(), a.Owner, a.Deploys} if units.Rows() > 0 { format += "Units:\n%s" args = append(args, units) } return fmt.Sprintf(format, args...) }
func (c *appDeployList) Run(context *cmd.Context, client *cmd.Client) error { appName, err := c.Guess() if err != nil { return err } url, err := cmd.GetURL(fmt.Sprintf("/deploys?app=%s&limit=10", appName)) if err != nil { return err } request, err := http.NewRequest("GET", url, nil) if err != nil { return err } response, err := client.Do(request) if err != nil { return err } if response.StatusCode == http.StatusNoContent { fmt.Fprintf(context.Stdout, "App %s has no deploy.\n", appName) return nil } defer response.Body.Close() result, err := ioutil.ReadAll(response.Body) if err != nil { return err } var deploys []tsuruapp.DeployData err = json.Unmarshal(result, &deploys) if err != nil { return err } sort.Sort(sort.Reverse(deployList(deploys))) table := cmd.NewTable() table.Headers = cmd.Row([]string{"Image (Rollback)", "Origin", "User", "Date (Duration)", "Error"}) for _, deploy := range deploys { timestamp := deploy.Timestamp.Local().Format(time.Stamp) seconds := deploy.Duration / time.Second minutes := seconds / 60 seconds = seconds % 60 if deploy.Origin == "git" { if len(deploy.Commit) > 7 { deploy.Commit = deploy.Commit[:7] } deploy.Origin = fmt.Sprintf("git (%s)", deploy.Commit) } timestamp = fmt.Sprintf("%s (%02d:%02d)", timestamp, minutes, seconds) if deploy.CanRollback { deploy.Image += " (*)" } rowData := []string{deploy.Image, deploy.Origin, deploy.User, timestamp, deploy.Error} if deploy.Error != "" { for i, el := range rowData { if el != "" { rowData[i] = cmd.Colorfy(el, "red", "", "") } } } table.LineSeparator = true table.AddRow(cmd.Row(rowData)) } context.Stdout.Write(table.Bytes()) return nil }
func (a *app) String() string { format := `Application: {{.Name}} Repository: {{.Repository}} Platform: {{.Platform}} Teams: {{.GetTeams}} Address: {{.Addr}} Owner: {{.Owner}} Team owner: {{.TeamOwner}} Deploys: {{.Deploys}} Pool: {{.Pool}}{{if .Lock.Locked}} {{.Lock.String}}{{end}} ` var buf bytes.Buffer tmpl := template.Must(template.New("app").Parse(format)) unitsByProcess := map[string][]unit{} for _, u := range a.Units { units := unitsByProcess[u.ProcessName] unitsByProcess[u.ProcessName] = append(units, u) } processes := make([]string, 0, len(unitsByProcess)) for process := range unitsByProcess { processes = append(processes, process) } sort.Strings(processes) titles := []string{"Unit", "State"} contMap := map[string]container{} if len(a.containers) > 0 { for _, cont := range a.containers { id := cont.ID if len(cont.ID) > 10 { id = id[:10] } contMap[id] = cont } titles = append(titles, []string{"Host", "Port", "IP"}...) } for _, process := range processes { units := unitsByProcess[process] unitsTable := cmd.NewTable() unitsTable.Headers = cmd.Row(titles) for _, unit := range units { if unit.Name == "" { continue } id := unit.Name if len(unit.Name) > 10 { id = id[:10] } row := []string{id, unit.Status} cont, ok := contMap[id] if ok { row = append(row, []string{cont.HostAddr, cont.HostPort, cont.IP}...) } unitsTable.AddRow(cmd.Row(row)) } if unitsTable.Rows() > 0 { if len(a.containers) > 0 { unitsTable.SortByColumn(2) } buf.WriteString("\n") processStr := "" if process != "" { processStr = fmt.Sprintf(" [%s]", process) } buf.WriteString(fmt.Sprintf("Units%s: %d\n", processStr, unitsTable.Rows())) buf.WriteString(unitsTable.String()) } } servicesTable := cmd.NewTable() servicesTable.Headers = []string{"Service", "Instance"} for _, service := range a.services { if len(service.Instances) == 0 { continue } servicesTable.AddRow([]string{service.Service, strings.Join(service.Instances, ", ")}) } if servicesTable.Rows() > 0 { buf.WriteString("\n") buf.WriteString(fmt.Sprintf("Service instances: %d\n", servicesTable.Rows())) buf.WriteString(servicesTable.String()) } if a.Plan.Name != "" { buf.WriteString("\n") buf.WriteString("App Plan:\n") buf.WriteString(renderPlans([]tsuruapp.Plan{a.Plan}, true)) } var tplBuffer bytes.Buffer tmpl.Execute(&tplBuffer, a) return tplBuffer.String() + buf.String() }