Skip to content

Commit

Permalink
Merge branch 'release-2.4' into 'master'
Browse files Browse the repository at this point in the history
Release 2.4

See merge request !167
  • Loading branch information
Phil Manavopoulos committed Jun 28, 2017
2 parents cb0b858 + f003fe8 commit 40c61eb
Show file tree
Hide file tree
Showing 34 changed files with 1,163 additions and 38 deletions.
2 changes: 1 addition & 1 deletion cmd/bytemark/config_test.go
Expand Up @@ -97,7 +97,7 @@ func TestCommandConfigSet(t *testing.T) {
config.When("GetV", "endpoint").Return(util.ConfigVar{"endpoint", "", ""})
config.When("GetV", "group").Return(util.ConfigVar{"group", "", ""})
config.When("GetV", "debug-level").Return(util.ConfigVar{"debug-level", "", ""})
config.When("Get", "token").Return("test-token", nil)
config.When("GetIgnoreErr", "token").Return("test-token")
config.When("GetIgnoreErr", "user").Return("old-test-user")
config.When("GetIgnoreErr", "yubikey").Return("")
config.When("GetIgnoreErr", "2fa-otp").Return("")
Expand Down
27 changes: 27 additions & 0 deletions cmd/bytemark/create.go
Expand Up @@ -197,6 +197,32 @@ Multiple --disc flags can be used to create multiple discs`,
Action: With(OptionalArgs("group"), RequiredFlags("group"), AuthProvider, createGroup),
}

createBackupCmd := cli.Command{
Name: "backup",
Usage: "create a backup of a disc's current state",
UsageText: "bytemark create backup <server name> <disc label>",
Description: `Creates a backup of the disc's current state. The backup is moved to another tail in the "iceberg" storage grade.`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "disc",
Usage: "the disc to create a backup of",
},
cli.GenericFlag{
Name: "server",
Usage: "the server whose disk you wish to backup",
Value: new(VirtualMachineNameFlag),
},
},
Action: With(OptionalArgs("server", "disc"), RequiredFlags("server", "disc"), AuthProvider, func(c *Context) error {
backup, err := global.Client.CreateBackup(c.VirtualMachineName("server"), c.String("disc"))
if err != nil {
return err
}
log.Errorf("Backup '%s' taken successfully!", backup.Label)
return nil
}),
}

commands = append(commands, cli.Command{
Name: "create",
Usage: "creates servers, discs, etc - see `bytemark create <kind of thing> help`",
Expand All @@ -217,6 +243,7 @@ Multiple --disc flags can be used to create multiple discs`,
createServerCmd,
createDiscsCmd,
createGroupCmd,
createBackupCmd,
},
})
}
Expand Down
22 changes: 22 additions & 0 deletions cmd/bytemark/create_test.go
Expand Up @@ -268,6 +268,28 @@ func TestCreateServer(t *testing.T) {
}
}

func TestCreateBackup(t *testing.T) {
is := is.New(t)
config, c := baseTestAuthSetup(t, false)

config.When("GetVirtualMachine").Return(&defVM)

vmname := lib.VirtualMachineName{
VirtualMachine: "test-server",
Group: "default",
Account: "default-account",
}

c.When("CreateBackup", vmname, "test-disc").Return(brain.Backup{}, nil).Times(1)

err := global.App.Run([]string{
"bytemark", "create", "backup", "test-server", "test-disc",
})
is.Nil(err)
if ok, err := c.Verify(); !ok {
t.Fatal(err)
}
}
func TestCreateVLANGroup(t *testing.T) {
is := is.New(t)
config, c := baseTestAuthSetup(t, true)
Expand Down
29 changes: 29 additions & 0 deletions cmd/bytemark/delete.go
Expand Up @@ -141,6 +141,27 @@ If --recursive is specified, all servers in the group will be purged. Otherwise,
},
},
Action: With(OptionalArgs("server"), RequiredFlags("server"), VirtualMachineProvider("server"), deleteServer),
}, {
Name: "backup",
Usage: "delete the given backup",
UsageText: `bytemark delete backup <server name> <disc label> <backup label>`,
Description: "Deletes the given backup. Backups cannot be recovered after deletion.",
Flags: []cli.Flag{
cli.StringFlag{
Name: "disc",
Usage: "the disc to delete a backup of",
},
cli.GenericFlag{
Name: "server",
Usage: "the server to delete a backup from",
Value: new(VirtualMachineNameFlag),
},
cli.StringFlag{
Name: "backup",
Usage: "the name or ID of the backup to delete",
},
},
Action: With(OptionalArgs("server", "disc", "backup"), RequiredFlags("server", "disc", "backup"), AuthProvider, deleteBackup),
}},
})
}
Expand Down Expand Up @@ -244,3 +265,11 @@ func recursiveDeleteGroup(name *lib.GroupName, group *brain.Group) error {
log.Log(" bytemark delete server [--force] [---purge] <server>")
log.Log(" bytemark undelete server <server>")
}*/
func deleteBackup(c *Context) (err error) {
err = global.Client.DeleteBackup(c.VirtualMachineName("server"), c.String("disc"), c.String("backup"))
if err != nil {
return
}
log.Logf("Backup '%s' deleted successfully", c.String("backup"))
return
}
31 changes: 26 additions & 5 deletions cmd/bytemark/delete_test.go
Expand Up @@ -93,12 +93,10 @@ func TestDeleteKey(t *testing.T) {
if ok, vErr := c.Verify(); !ok {
t.Fatal(vErr)
}
c.Reset()
config.Reset()
config.When("Get", "token").Return("test-token")

config, c = baseTestAuthSetup(t, false)

config.When("Force").Return(true)
config.When("GetIgnoreErr", "yubikey").Return("")
config.When("GetIgnoreErr", "2fa-otp").Return("")
config.When("GetIgnoreErr", "user").Return("test-user")

c.When("AuthWithToken", "test-token").Return(nil)
Expand All @@ -115,6 +113,29 @@ func TestDeleteKey(t *testing.T) {
c.Reset()
}

func TestDeleteBackup(t *testing.T) {
is := is.New(t)
config, c := baseTestAuthSetup(t, false)

vmname := lib.VirtualMachineName{
VirtualMachine: "test-server",
Group: "default",
Account: "default-account",
}

config.When("GetVirtualMachine").Return(&defVM)

c.When("DeleteBackup", vmname, "test-disc", "test-backup").Return(nil).Times(1)

err := global.App.Run([]string{
"bytemark", "delete", "backup", "test-server", "test-disc", "test-backup",
})
is.Nil(err)
if ok, err := c.Verify(); !ok {
t.Fatal(err)
}
}

func TestDeleteVLAN(t *testing.T) {
is := is.New(t)
_, c := baseTestAuthSetup(t, true)
Expand Down
44 changes: 44 additions & 0 deletions cmd/bytemark/list.go
Expand Up @@ -2,6 +2,7 @@ package main

import (
"github.com/BytemarkHosting/bytemark-client/lib/brain"
"github.com/BytemarkHosting/bytemark-client/lib/prettyprint"
"github.com/BytemarkHosting/bytemark-client/util/log"
"github.com/urfave/cli"
)
Expand Down Expand Up @@ -130,6 +131,49 @@ Deleted servers are included in the list, with ' (deleted)' appended.`,
return nil
})
}),
}, {
Name: "backups",
Usage: "list all the backups of a server or disc",
UsageText: "bytemark list backups <server name> [disc label]",
Description: "Lists all the backups of all the discs in the given server, or if you also give a disc label, just the backups of that disc.",
Flags: append(OutputFlags("backups", "array", DefaultBackupTableFields),
cli.StringFlag{
Name: "disc",
Usage: "the disc you wish to list the backups of",
},
cli.GenericFlag{
Name: "server",
Usage: "the server you wish to list the backups of",
Value: new(VirtualMachineNameFlag),
},
),
Action: With(OptionalArgs("server", "disc"), RequiredFlags("server", "disc"), AuthProvider, func(c *Context) (err error) {
vmName := c.VirtualMachineName("server")
label := c.String("disc")
var backups brain.Backups

if label != "" {
backups, err = global.Client.GetBackups(vmName, label)
if err != nil {
return
}
} else {
err = VirtualMachineProvider("server")(c)
if err != nil {
return
}
for _, disc := range c.VirtualMachine.Discs {
discbackups, err := global.Client.GetBackups(vmName, disc.Label)
if err != nil {
return err
}
backups = append(backups, discbackups...)
}
}
return c.OutputInDesiredForm(backups, func() error {
return backups.PrettyPrint(global.App.Writer, prettyprint.Full)
})
}),
}},
})
}
23 changes: 23 additions & 0 deletions cmd/bytemark/list_test.go
Expand Up @@ -97,3 +97,26 @@ func TestListServers(t *testing.T) {
t.Fatal(err)
}
}

func TestListBackups(t *testing.T) {
is := is.New(t)
config, c := baseTestAuthSetup(t, false)

vmname := lib.VirtualMachineName{
VirtualMachine: "test-server",
Group: "default",
Account: "default-account",
}

config.When("GetVirtualMachine").Return(&defVM)

c.When("GetBackups", vmname, "test-disc").Return(nil).Times(1)

err := global.App.Run([]string{
"bytemark", "list", "backups", "test-server", "test-disc",
})
is.Nil(err)
if ok, err := c.Verify(); !ok {
t.Fatal(err)
}
}
40 changes: 28 additions & 12 deletions cmd/bytemark/main.go
Expand Up @@ -128,11 +128,28 @@ func outputDebugInfo() {
log.Debugf(log.LvlFlags, "invocation: %s\r\n\r\n", strings.Join(os.Args, " "))
}

func makeCredentials() (credents map[string]string, err error) {
err = PromptForCredentials()
if err != nil {
return
}
credents = map[string]string{
"username": global.Config.GetIgnoreErr("user"),
"password": global.Config.GetIgnoreErr("pass"),
"validity": global.Config.GetIgnoreErr("session-validity"),
}
if useKey, _ := global.Config.GetBool("yubikey"); useKey {
credents["yubikey"] = global.Config.GetIgnoreErr("yubikey-otp")
}
return
}

// EnsureAuth authenticates with the Bytemark authentication server, prompting for credentials if necessary.
// TODO(telyn): This REALLY, REALLY needs breaking apart into more manageable chunks
func EnsureAuth() error {
token, err := global.Config.Get("token")
token := global.Config.GetIgnoreErr("token")

err = global.Client.AuthWithToken(token)
err := global.Client.AuthWithToken(token)
if err != nil {
if aErr, ok := err.(*auth3.Error); ok {
if _, ok := aErr.Err.(*url.Error); ok {
Expand All @@ -145,18 +162,11 @@ func EnsureAuth() error {
for err != nil {
attempts--

err = PromptForCredentials()
credents, err := makeCredentials()

if err != nil {
return err
}
credents := map[string]string{
"username": global.Config.GetIgnoreErr("user"),
"password": global.Config.GetIgnoreErr("pass"),
}
if useKey, _ := global.Config.GetBool("yubikey"); useKey {
credents["yubikey"] = global.Config.GetIgnoreErr("yubikey-otp")
}

err = global.Client.AuthWithCredentials(credents)

// Handle the special case here where we just need to prompt for 2FA and try again
Expand All @@ -173,7 +183,7 @@ func EnsureAuth() error {

if err == nil {
// success!
// it doesn't _really_ matter if we can't write the token to the token place, right?
// TODO(telyn): warn on failure to write to token
_ = global.Config.SetPersistent("token", global.Client.GetSessionToken(), "AUTH")

// Check this here, as it is only relevant the initial login,
Expand Down Expand Up @@ -374,6 +384,12 @@ func globalFlags() (flags []cli.Flag) {
Name: "yubikey-otp",
Usage: "one-time password from your yubikey to use to login",
},
cli.IntFlag{
Name: "session-validity",
Usage: "seconds until your session is automatically invalidated (max 3600)",
Value: util.DefaultSessionValidity,
// TODO(telyn): add more defaults to these flags
},
}
}

Expand Down
4 changes: 3 additions & 1 deletion cmd/bytemark/main_test.go
Expand Up @@ -101,6 +101,7 @@ func TestEnsureAuth(t *testing.T) {
credentials := auth3.Credentials{
"username": test.InputUsername,
"password": test.InputPassword,
"validity": "1800",
}

c.When("AuthWithCredentials", credentials).Return(test.AuthWithCredentialsErrors[0]).Times(1)
Expand All @@ -110,6 +111,7 @@ func TestEnsureAuth(t *testing.T) {
credentials := auth3.Credentials{
"username": test.InputUsername,
"password": test.InputPassword,
"validity": "1800",
"2fa": test.Input2FA,
}
c.When("AuthWithCredentials", credentials).Return(test.AuthWithCredentialsErrors[1]).Times(1) // Returns nil means success
Expand Down Expand Up @@ -170,8 +172,8 @@ func baseTestSetup(t *testing.T, admin bool) (config *mocks.Config, client *mock
func baseTestAuthSetup(t *testing.T, admin bool) (config *mocks.Config, c *mocks.Client) {
config, c = baseTestSetup(t, admin)

config.When("Get", "token").Return("test-token")
config.When("Get", "account").Return("test-account")
config.When("GetIgnoreErr", "token").Return("test-token")
config.When("GetIgnoreErr", "user").Return("test-user")
config.When("GetIgnoreErr", "yubikey").Return("")
config.When("GetIgnoreErr", "2fa-otp").Return("")
Expand Down
29 changes: 29 additions & 0 deletions cmd/bytemark/restore.go
Expand Up @@ -41,6 +41,35 @@ Note that it cannot be used to restore a server that has been permanently delete
log.Logf("Successfully restored %s\r\n", c.VirtualMachine.Hostname)
return
}),
}, {
Name: "backup",
Usage: "restore the given backup",
UsageText: `bytemark restore backup <server name> <disc label> <backup label>`,
Description: "Restores the given backup. Before doing this, a new backup is made of the disc's current state.",
Flags: []cli.Flag{
cli.StringFlag{
Name: "disc",
Usage: "the name of the disc to restore to",
},
cli.GenericFlag{
Name: "server",
Usage: "the server that the disc is attached to",
Value: new(VirtualMachineNameFlag),
},
cli.StringFlag{
Name: "backup",
Usage: "the name or ID of the backup to restore",
},
},
Action: With(OptionalArgs("server", "disc", "backup"), RequiredFlags("server", "disc", "backup"), AuthProvider, func(c *Context) (err error) {
// TODO(telyn): eventually RestoreBackup will return backups as the first argument. We should process that and output info :)
_, err = global.Client.RestoreBackup(c.VirtualMachineName("server"), c.String("disc"), c.String("backup"))
if err != nil {
return
}
log.Logf("Disc '%s' is now being restored from backup '%s'", c.String("disc"), c.String("backup"))
return
}),
}},
})
}

0 comments on commit 40c61eb

Please sign in to comment.