Skip to content

Commit

Permalink
Merge branch 'develop' into release-1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
telyn committed Jul 11, 2016
2 parents 978d888 + 916005d commit 812ae6c
Show file tree
Hide file tree
Showing 6 changed files with 399 additions and 41 deletions.
49 changes: 49 additions & 0 deletions cmd/bytemark/overview.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import (
"github.com/BytemarkHosting/bytemark-client/lib"
"github.com/urfave/cli"
"os"
)

func init() {

commands = append(commands, cli.Command{
Name: "overview",
Usage: `overview of your Bytemark hosting`,
UsageText: "bytemark show account [--json] [name]",
Description: `This command displays an overview of the hosting you have with Bytemark.
If the --json flag is specified, prints a complete overview of the account in JSON format, including all groups and their servers.`,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "json",
Usage: "Output account details as a JSON object",
},
},
Action: With(AuthProvider, func(c *Context) error {

allAccs, err := global.Client.GetAccounts()
if err != nil {
return err
}

accName := global.Config.GetIgnoreErr("account")
var def *lib.Account
if accName != "" {
def, err = global.Client.GetAccount(accName)
if err != nil {
return err
}
} else {
def, err = global.Client.GetDefaultAccount()
if err != nil {
return err
}
}

return lib.FormatOverview(os.Stdout, allAccs, def, global.Client.GetSessionUser())

}),
})
}
12 changes: 8 additions & 4 deletions cmd/bytemark/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ If the --json flag is specified, prints a complete overview of the account in JS
},
Action: With(AccountProvider, func(c *Context) error {
return c.IfNotMarshalJSON(c.Account, func() error {
err := lib.FormatAccount(os.Stderr, c.Account)
def, err := global.Client.GetDefaultAccount()
if err != nil {
return err
}
err = lib.FormatAccount(os.Stderr, c.Account, def, "account_overview")
if err != nil {
return err
}
Expand All @@ -39,7 +43,7 @@ If the --json flag is specified, prints a complete overview of the account in JS

for _, g := range c.Account.Groups {
for _, vm := range g.VirtualMachines {
err := lib.FormatVirtualMachine(os.Stderr, vm, "servertwoline")
err := lib.FormatVirtualMachine(os.Stderr, vm, lib.TwoLine)
log.Output()
log.Output()
if err != nil {
Expand Down Expand Up @@ -73,7 +77,7 @@ If the --json flag is specified, prints a complete overview of the group in JSON
log.Output()
for _, vm := range c.Group.VirtualMachines {

err := lib.FormatVirtualMachine(os.Stderr, vm, "servertwoline")
err := lib.FormatVirtualMachine(os.Stderr, vm, lib.TwoLine)
log.Output()
log.Output()
if err != nil {
Expand All @@ -97,7 +101,7 @@ If the --json flag is specified, prints a complete overview of the group in JSON
},
Action: With(VirtualMachineProvider, func(c *Context) error {
return c.IfNotMarshalJSON(c.VirtualMachine, func() error {
return lib.FormatVirtualMachine(os.Stderr, c.VirtualMachine, "serverfull")
return lib.FormatVirtualMachine(os.Stderr, c.VirtualMachine, lib.All)
})
}),
}, {
Expand Down
25 changes: 25 additions & 0 deletions lib/account_calls.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ func (c *bytemarkClient) getBillingAccount(name string) (account *billingAccount

// getBillingAccounts returns all the billing accounts the currently logged in user can see.
func (c *bytemarkClient) getBillingAccounts() (accounts []*billingAccount, err error) {
if c.billingEndpoint == "" {
return make([]*billingAccount, 0), nil
}
req, err := c.BuildRequest("GET", EP_BILLING, "/api/v1/accounts")
if err != nil {
return
Expand Down Expand Up @@ -197,3 +200,25 @@ func (c *bytemarkClient) GetAccounts() (accounts []*Account, err error) {
return

}

type Overview struct {
DefaultAccount *Account
Username string
Accounts []*Account
}

func (c *bytemarkClient) GetOverview() (*Overview, error) {
o := new(Overview)
acc, err := c.GetDefaultAccount()
if err != nil {
return nil, err
}
accs, err := c.GetAccounts()
if err != nil {
return nil, err
}
o.DefaultAccount = acc
o.Accounts = accs
o.Username = c.authSession.Username
return o, nil
}
40 changes: 40 additions & 0 deletions lib/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package lib

import (
"encoding/json"
"fmt"
"testing"
)

// IsEqualString checks strings for equalities and outputs the difference between them if not.
func IsEqualString(t *testing.T, expected string, actual string) {
if expected == actual {
return
}
expectedjs, err := json.Marshal(expected)
if err != nil {
t.Fatal(err)
}
actualjs, err := json.Marshal(actual)
if err != nil {
t.Fatal(err)
}
expr := []rune(expected)
actr := []rune(actual)

if len(expr) != len(actr) {
t.Errorf("String lengths differ. expected: %d actual: %d\r\n", len(expr), len(actr))
}
sz := len(expr)
if len(expr) > len(actr) {
sz = len(actr)
}
for i := 0; i < sz; i++ {
if expr[i] != actr[i] {
fmt.Printf("chr #%d differs. e:'%c' a:'%c'\r\n", i, expr[i], actr[i])
}
}

fmt.Printf("\r\n%s\r\n%s", map[string]string{"data": string(expectedjs)}, map[string]string{"data": string(actualjs)})
t.Fail()
}
164 changes: 129 additions & 35 deletions lib/prettyprints.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,77 @@ import (
"unicode"
)

// = name.group (power state) in Zone
// xxx.yyy.zzz.www n cores, nGiB RAM, nnnGiB storage on n discs
// discs:
// + vda - 35GiB, sata grade
// + vdd - 100GiB, sata grade
//
// ips: 213.13
const accountsTemplate = `{{ define "account_name" }}{{ if .BillingID }}{{ .BillingID }} - {{ end }}{{ if .Name }}{{ .Name }}{{ else }}[no bigv account]{{ end }}{{ end }}
const serverTemplate = `{{ define "serversummary" }} ▸ {{.ShortName }} ({{ if .Deleted }}deleted{{ else if .PowerOn }}powered on{{else}}powered off{{end}}) in {{capitalize .ZoneName}}{{ end }}
{{ define "serverspec" }} {{ .PrimaryIP }} - {{ pluralize "core" "cores" .Cores }}, {{ mibgib .Memory }}, {{.TotalDiscSize "" | gibtib }} on {{ len .Discs | pluralize "disc" "discs" }}{{ end }}
{{ define "account_bullet" }}• {{ template "account_name" . -}}
{{- if isDefaultAccount . }} (this is your default account){{ end -}}
{{- end }}
{{ define "whoami" }}You are '{{ .Username }}'{{ end }}
{{ define "owned_accounts" -}}
{{- if .OwnedAccounts -}}
Accounts you own:
{{- range .OwnedAccounts }}
{{ template "account_bullet" . -}}
{{ end -}}
{{- end -}}
{{- end -}}
{{ define "extra_accounts" -}}
{{- if .OtherAccounts -}}
{{- if .OwnedAccounts }}
Other accounts you can access:
{{- else -}}
Accounts you can access:
{{- end -}}
{{- range .OtherAccounts }}
{{template "account_bullet" . }}
{{- end -}}
{{- end -}}
{{- end }}
{{ define "group_overview" }} • {{ .Name }} - {{ pluralize "server" "servers" ( len .VirtualMachines ) }}
{{ if ( len .VirtualMachines ) le 5 -}}
{{- range .VirtualMachines }} {{ template "server_summary" . }}
{{ end -}}
{{- end -}}
{{- end -}}
{{/* account_overview needs $ to be defined, so use single_account_overview as entrypoint */}}
{{ define "account_overview" }}
{{- if isDefaultAccount . -}}
Your default account ({{ template "account_name" . }})
{{- else -}}
{{- template "account_name" . -}}
{{- end }}
{{ range .Groups -}}
{{ template "group_overview" . -}}
{{- end -}}
{{- end }}
{{ define "single_account_overview" }}
{{ template "account_overview" .Account }}
{{ end }}
{{ define "full_overview" -}}
{{- template "whoami" . }}
{{ template "owned_accounts" . -}}
{{- template "extra_accounts" . }}
{{ if .DefaultAccount -}}
{{- template "account_overview" .DefaultAccount }}
{{ else -}}
It was not possible to determine your default account. Please set one using bytemark config set account.
{{ end -}}
{{- end }}
`

const serverTemplate = `{{ define "server_summary" }} ▸ {{.ShortName }} ({{ if .Deleted }}deleted{{ else if .PowerOn }}powered on{{else}}powered off{{end}}) in {{capitalize .ZoneName}}{{ end }}
{{ define "server_spec" }} {{ .PrimaryIP }} - {{ pluralize "core" "cores" .Cores }}, {{ mibgib .Memory }}, {{.TotalDiscSize "" | gibtib }} on {{ len .Discs | pluralize "disc" "discs" }}{{ end }}
{{ define "discs" }} discs:
{{- range .Discs }}
Expand All @@ -35,11 +96,11 @@ const serverTemplate = `{{ define "serversummary" }} ▸ {{.ShortName }} ({{ if
{{- end }}
{{ end }}
{{ define "servertwoline" }}{{ template "serversummary" . }}
{{ template "serverspec" . }}{{ end }}
{{ define "server_twoline" }}{{ template "server_summary" . }}
{{ template "server_spec" . }}{{ end }}
{{ define "serverfull" -}}
{{ template "servertwoline" . }}
{{ define "server_full" -}}
{{ template "server_twoline" . }}
{{ template "discs" . }}
{{ template "ips" . }}
Expand All @@ -48,9 +109,9 @@ const serverTemplate = `{{ define "serversummary" }} ▸ {{.ShortName }} ({{ if
type TemplateChoice string

const (
OneLine TemplateChoice = "serversummary"
TwoLine = "servertwoline"
All = "serverfull"
OneLine TemplateChoice = "server_summary"
TwoLine = "server_twoline"
All = "server_full"
)

var templateFuncMap = map[string]interface{}{
Expand Down Expand Up @@ -212,31 +273,64 @@ func FormatVirtualMachineSpec(wr io.Writer, group *GroupName, spec *VirtualMachi
return err
}

func FormatAccount(wr io.Writer, a *Account) error {
output := make([]string, 0, 10)
func FormatAccount(wr io.Writer, a *Account, def *Account, tpl string) error {
tmpl, err := template.New("accounts").Funcs(templateFuncMap).Funcs(map[string]interface{}{
"isDefaultAccount": func(a *Account) bool {
if a.BillingID != 0 && a.BillingID == def.BillingID {
return true
}
return a.Name != "" && a.Name == def.Name
},
}).Parse(accountsTemplate + serverTemplate)

gs := ""
if len(a.Groups) != 1 {
gs = "s"
if err != nil {
return err
}
ss := ""
servers := a.CountVirtualMachines()
if servers != 1 {
ss = "s"

err = tmpl.ExecuteTemplate(wr, tpl, a)
if err != nil {
return err
}

groups := make([]string, len(a.Groups))
return nil
}

for i, g := range a.Groups {
groups[i] = g.Name
func FormatOverview(wr io.Writer, accounts []*Account, defaultAccount *Account, username string) error {
tmpl, err := template.New("accounts").Funcs(templateFuncMap).Funcs(map[string]interface{}{
"isDefaultAccount": func(a *Account) bool {
if a == nil || defaultAccount == nil {
return false
}
if a.BillingID != 0 && a.BillingID == defaultAccount.BillingID {
return true
}
return a.Name != "" && a.Name == defaultAccount.Name
},
}).Parse(accountsTemplate + serverTemplate)
if err != nil {
return err
}
output = append(output, fmt.Sprintf("%s - Account containing %d server%s across %d group%s", a.Name, servers, ss, len(a.Groups), gs))
if a.Owner != nil && a.TechnicalContact != nil {
output = append(output, fmt.Sprintf("Owner: %s %s (%s), Tech Contact: %s %s (%s)", a.Owner.FirstName, a.Owner.LastName, a.Owner.Username, a.TechnicalContact.FirstName, a.TechnicalContact.LastName, a.TechnicalContact.Username))
ownedAccounts := make([]*Account, 0)
otherAccounts := make([]*Account, 0)
for _, a := range accounts {
if a.Owner != nil && a.Owner.Username != "" && a.Owner.Username == username {
ownedAccounts = append(ownedAccounts, a)
} else {
otherAccounts = append(otherAccounts, a)
}
}
data := map[string]interface{}{
"Accounts": accounts,
"DefaultAccount": defaultAccount,
"Username": username,
"OwnedAccounts": ownedAccounts,
"OtherAccounts": otherAccounts,
}
output = append(output, "")
output = append(output, fmt.Sprintf("Groups in this account: %s", strings.Join(groups, ", ")))

_, err := wr.Write([]byte(strings.Join(output, "\r\n")))
return err
err = tmpl.ExecuteTemplate(wr, "full_overview", data)
if err != nil {
return err
}

return nil
}
Loading

0 comments on commit 812ae6c

Please sign in to comment.