diff --git a/account.go b/account.go index 58a427f..81bd85c 100644 --- a/account.go +++ b/account.go @@ -9,7 +9,7 @@ import ( ) func AccountGet(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) err := accountGet(client, c.App.Writer) if err != nil { log.WithField("err", err).Fatal("could not display account") diff --git a/account_test.go b/account_test.go index efbb88b..f9cfc9b 100644 --- a/account_test.go +++ b/account_test.go @@ -31,7 +31,7 @@ func TestAccountAction(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) WithinTest(cs, nil, func(c *cli.Context) { AccountGet(c) diff --git a/actions.go b/actions.go index 27f1880..c002784 100644 --- a/actions.go +++ b/actions.go @@ -10,7 +10,7 @@ import ( ) func ActionList(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) err := actionsList(client, opts, c.App.Writer) if err != nil { @@ -19,7 +19,7 @@ func ActionList(c *cli.Context) { } func ActionGet(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int("action-id") if id < 1 { diff --git a/actions_test.go b/actions_test.go index 6da1018..4eabd04 100644 --- a/actions_test.go +++ b/actions_test.go @@ -32,7 +32,7 @@ func TestActionList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) WithinTest(cs, nil, func(c *cli.Context) { ActionList(c) @@ -54,7 +54,7 @@ func TestActionGet(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int("action-id", testAction.ID, "action-id") diff --git a/cmd/docli/main.go b/cmd/docli/main.go index cef6427..af45a5f 100644 --- a/cmd/docli/main.go +++ b/cmd/docli/main.go @@ -21,7 +21,7 @@ func (t *tokenSource) Token() (*oauth2.Token, error) { func init() { logrus.SetOutput(os.Stderr) - logrus.SetLevel(logrus.WarnLevel) + logrus.SetLevel(logrus.InfoLevel) docli.Bail = func(err error, msg string) { logrus.WithField("err", err).Fatal(msg) @@ -46,9 +46,10 @@ func main() { dropletActionCommands(), imageActionCommands(), imageCommands(), + sshKeyCommands(), regionCommands(), sizeCommands(), - sshKeyCommands(), + sshCommands(), } app.RunAndExitOnError() diff --git a/cmd/docli/ssh_commands.go b/cmd/docli/ssh_commands.go new file mode 100644 index 0000000..692dd2e --- /dev/null +++ b/cmd/docli/ssh_commands.go @@ -0,0 +1,24 @@ +package main + +import ( + "github.com/bryanl/docli" + "github.com/codegangsta/cli" +) + +func sshCommands() cli.Command { + return cli.Command{ + Name: "ssh", + Usage: "SSH to droplet. Provide name or id", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: docli.ArgDropletName, + Usage: "droplet name", + }, + cli.IntFlag{ + Name: docli.ArgDropletID, + Usage: "droplet id", + }, + }, + Action: docli.SSH, + } +} diff --git a/docli_test.go b/docli_test.go index 8216740..c003d25 100644 --- a/docli_test.go +++ b/docli_test.go @@ -3,9 +3,27 @@ package docli import ( "os" "testing" + + "github.com/digitalocean/godo" ) -var lastBailOut bailOut +var ( + testDroplet = godo.Droplet{ + ID: 1, + Name: "a-droplet", + Networks: &godo.Networks{ + V4: []godo.NetworkV4{ + {IPAddress: "8.8.8.8", Type: "public"}, + {IPAddress: "172.16.1.2", Type: "private"}, + }, + }, + } + testDropletList = []godo.Droplet{testDroplet} + testKernel = godo.Kernel{ID: 1} + testKernelList = []godo.Kernel{testKernel} + + lastBailOut bailOut +) type bailOut struct { err error diff --git a/domainrecs.go b/domainrecs.go index 00e80d9..c9753c5 100644 --- a/domainrecs.go +++ b/domainrecs.go @@ -7,7 +7,7 @@ import ( ) func RecordCreate(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) domainName := c.String("domain-name") drcr := &godo.DomainRecordEditRequest{ @@ -28,7 +28,7 @@ func RecordCreate(c *cli.Context) { } func RecordDelete(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) domainName := c.String("domain-name") recordID := c.Int("record-id") @@ -40,7 +40,7 @@ func RecordDelete(c *cli.Context) { // List records for a domain. func RecordList(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) name := c.String("domain-name") @@ -73,7 +73,7 @@ func RecordList(c *cli.Context) { // Retrieve a domain record. func RecordGet(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) domainName := c.String("domain-name") recordID := c.Int("record-id") @@ -86,7 +86,7 @@ func RecordGet(c *cli.Context) { } func RecordUpdate(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) domainName := c.String("domain-name") recordID := c.Int("record-id") diff --git a/domainrecs_test.go b/domainrecs_test.go index a975a06..acefd99 100644 --- a/domainrecs_test.go +++ b/domainrecs_test.go @@ -26,7 +26,7 @@ func TestRecordsList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String("domain-name", "example.com", "domain-name") @@ -53,7 +53,7 @@ func TestRecordsGet(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String("domain-name", "example.com", "domain-name") fs.Int("record-id", testRecord.ID, "record-id") @@ -84,7 +84,7 @@ func TestRecordsCreate(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String("domain-name", "example.com", "domain-name") fs.String("record-type", "A", "record-type") @@ -120,7 +120,7 @@ func TestRecordsUpdate(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String("domain-name", "example.com", "domain-name") fs.Int("record-id", 1, "record-id") @@ -148,7 +148,7 @@ func TestRecordsDelete(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String("domain-name", "example.com", "domain-name") fs.Int("record-id", 1, "record-id") diff --git a/domains.go b/domains.go index 4d13c83..5eca47f 100644 --- a/domains.go +++ b/domains.go @@ -7,7 +7,7 @@ import ( ) func DomainCreate(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) req := &godo.DomainCreateRequest{ Name: c.String("domain-name"), IPAddress: c.String("ip-address"), @@ -21,7 +21,7 @@ func DomainCreate(c *cli.Context) { } func DomainDelete(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) name := c.String("domain-name") _, err := client.Domains.Delete(name) if err != nil { @@ -31,7 +31,7 @@ func DomainDelete(c *cli.Context) { // List lists all domains. func DomainList(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) f := func(opt *godo.ListOptions) ([]interface{}, *godo.Response, error) { @@ -62,7 +62,7 @@ func DomainList(c *cli.Context) { } func DomainGet(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.String("domain-name") a, _, err := client.Domains.Get(id) if err != nil { diff --git a/domains_test.go b/domains_test.go index 27cb9f0..99c0345 100644 --- a/domains_test.go +++ b/domains_test.go @@ -31,7 +31,7 @@ func TestDomainsList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) WithinTest(cs, nil, func(c *cli.Context) { DomainList(c) @@ -53,7 +53,7 @@ func TestDomainsGet(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String("domain-name", testDomain.Name, "domain-id") @@ -78,7 +78,7 @@ func TestDomainsCreate(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String("domain-name", testDomain.Name, "domain-name") fs.String("ip-address", "127.0.0.1", "ip- address") @@ -100,7 +100,7 @@ func TestDomainsDelete(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String("domain-name", testDomain.Name, "domain-name") diff --git a/dropletactions.go b/dropletactions.go index a48f765..c3dd4ec 100644 --- a/dropletactions.go +++ b/dropletactions.go @@ -10,7 +10,7 @@ import ( // Get returns a droplet action by id. func DropletActionGet(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) dropletID := c.Int(ArgDropletID) actionID := c.Int(ArgActionID) @@ -27,7 +27,7 @@ func DropletActionGet(c *cli.Context) { // DisableBackups disables backups for a droplet. func DropletActionDisableBackups(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) r, _, err := client.DropletActions.DisableBackups(id) @@ -43,7 +43,7 @@ func DropletActionDisableBackups(c *cli.Context) { // Reboot reboots a droplet. func DropletActionReboot(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) r, _, err := client.DropletActions.Reboot(id) @@ -59,7 +59,7 @@ func DropletActionReboot(c *cli.Context) { // PowerCycle power cycles a droplet. func DropletActionPowerCycle(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) r, _, err := client.DropletActions.PowerCycle(id) if err != nil { @@ -74,7 +74,7 @@ func DropletActionPowerCycle(c *cli.Context) { // Shutdown shuts a droplet down. func DropletActionShutdown(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) r, _, err := client.DropletActions.Shutdown(id) @@ -90,7 +90,7 @@ func DropletActionShutdown(c *cli.Context) { // PowerOff turns droplet power off. func DropletActionPowerOff(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) r, _, err := client.DropletActions.PowerOff(id) @@ -106,7 +106,7 @@ func DropletActionPowerOff(c *cli.Context) { // PowerOn turns droplet power on. func DropletActionPowerOn(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) r, _, err := client.DropletActions.PowerOn(id) @@ -122,7 +122,7 @@ func DropletActionPowerOn(c *cli.Context) { // PasswordReset resets the droplet root password. func DropletActionPasswordReset(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) r, _, err := client.DropletActions.PasswordReset(id) @@ -138,7 +138,7 @@ func DropletActionPasswordReset(c *cli.Context) { // EnableIPv6 enables IPv6 for a droplet. func DropletActionEnableIPv6(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) r, _, err := client.DropletActions.EnableIPv6(id) @@ -154,7 +154,7 @@ func DropletActionEnableIPv6(c *cli.Context) { // EnablePrivateNetworking enables private networking for a droplet. func DropletActionEnablePrivateNetworking(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) r, _, err := client.DropletActions.EnablePrivateNetworking(id) @@ -170,7 +170,7 @@ func DropletActionEnablePrivateNetworking(c *cli.Context) { // Upgrade upgrades a droplet. func DropletActionUpgrade(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) r, _, err := client.DropletActions.Upgrade(id) @@ -186,7 +186,7 @@ func DropletActionUpgrade(c *cli.Context) { // Restore restores a droplet using an image id. func DropletActionRestore(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) image := c.Int(ArgImageID) @@ -203,7 +203,7 @@ func DropletActionRestore(c *cli.Context) { // Resize resizesx a droplet giving a size slug and optionally expands the disk. func DropletActionResize(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) size := c.String(ArgImageSlug) disk := c.Bool(ArgResizeDisk) @@ -221,7 +221,7 @@ func DropletActionResize(c *cli.Context) { // Rebuild rebuilds a droplet using an image id or slug. func DropletActionRebuild(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) image := c.String(ArgImage) @@ -244,7 +244,7 @@ func DropletActionRebuild(c *cli.Context) { // Rename renames a droplet. func DropletActionRename(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) name := c.String(ArgDropletName) @@ -261,7 +261,7 @@ func DropletActionRename(c *cli.Context) { // ChangeKernel changes the kernel for a droplet. func DropletActionChangeKernel(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) kernel := c.Int(ArgKernelID) @@ -278,7 +278,7 @@ func DropletActionChangeKernel(c *cli.Context) { // Snapshot creates a snapshot for a droplet. func DropletActionSnapshot(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) name := c.String(ArgSnapshotName) diff --git a/dropletactions_test.go b/dropletactions_test.go index fc0a681..af8154a 100644 --- a/dropletactions_test.go +++ b/dropletactions_test.go @@ -23,7 +23,7 @@ func TestDropletActionsChangeKernel(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) fs.Int(ArgKernelID, 2, "kernel-id") @@ -45,7 +45,7 @@ func TestDropletActionsDisableBackups(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -66,7 +66,7 @@ func TestDropletActionsEnableIPv6(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -87,7 +87,7 @@ func TestDropletActionsEnablePrivateNetworking(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -110,7 +110,7 @@ func TestDropletActionsGet(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) fs.Int(ArgActionID, 2, ArgActionID) @@ -132,7 +132,7 @@ func TestDropletActionsPasswordReset(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -153,7 +153,7 @@ func TestDropletActionsPowerCycle(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -173,7 +173,7 @@ func TestDropletActionsPowerOff(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -193,7 +193,7 @@ func TestDropletActionsPowerOn(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -213,7 +213,7 @@ func TestDropletActionsReboot(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -237,7 +237,7 @@ func TestDropletActionsRebuildByImageID(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) fs.String(ArgImage, "2", ArgImageID) @@ -262,7 +262,7 @@ func TestDropletActionsRebuildByImageSlug(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) fs.String(ArgImage, "slug", "slug") @@ -286,7 +286,7 @@ func TestDropletActionsRename(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) fs.String(ArgDropletName, "name", "name") @@ -313,7 +313,7 @@ func TestDropletActionsResize(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) fs.String(ArgImageSlug, "slug", "image-slug") @@ -339,7 +339,7 @@ func TestDropletActionsRestore(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) fs.Int(ArgImageID, 2, ArgImageID) @@ -360,7 +360,7 @@ func TestDropletActionsShutdown(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -383,7 +383,7 @@ func TestDropletActionsSnapshot(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) fs.String(ArgSnapshotName, "name", "name") @@ -404,7 +404,7 @@ func TestDropletActionsUpgrade(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) diff --git a/droplets.go b/droplets.go index 82a3201..8e6a89a 100644 --- a/droplets.go +++ b/droplets.go @@ -1,7 +1,6 @@ package docli import ( - "fmt" "strconv" "github.com/Sirupsen/logrus" @@ -11,7 +10,7 @@ import ( // Actions returns a list of actions for a droplet. func DropletActions(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) opts := LoadOpts(c) @@ -47,7 +46,7 @@ func DropletActions(c *cli.Context) { // Backups returns a list of backup images for a droplet. func DropletBackups(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) opts := LoadOpts(c) @@ -83,7 +82,7 @@ func DropletBackups(c *cli.Context) { // Create creates a droplet. func DropletCreate(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) sshKeys := []godo.DropletCreateSSHKey{} for _, rawKey := range c.StringSlice(ArgSSHKeys) { @@ -126,7 +125,7 @@ func DropletCreate(c *cli.Context) { // Delete destroy a droplet by id. func DropletDelete(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) _, err := client.Droplets.Delete(id) @@ -137,14 +136,10 @@ func DropletDelete(c *cli.Context) { // Get returns a droplet. func DropletGet(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) - if id < 1 { - Bail(fmt.Errorf("missing droplet id"), "could not get droplet") - } - - droplet, _, err := client.Droplets.Get(id) + droplet, err := getDropletByID(client, id) if err != nil { Bail(err, "could not get droplet") } @@ -157,7 +152,7 @@ func DropletGet(c *cli.Context) { // Kernels returns a list of available kernels for a droplet. func DropletKernels(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) opts := LoadOpts(c) @@ -193,7 +188,7 @@ func DropletKernels(c *cli.Context) { // List returns a list of droplets. func DropletList(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) f := func(opt *godo.ListOptions) ([]interface{}, *godo.Response, error) { @@ -228,7 +223,7 @@ func DropletList(c *cli.Context) { // Neighbors returns a list of droplet neighbors. func DropletNeighbors(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) list, _, err := client.Droplets.Neighbors(id) @@ -244,7 +239,7 @@ func DropletNeighbors(c *cli.Context) { // Snapshots returns a list of available kernels for a droplet. func DropletSnapshots(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgDropletID) opts := LoadOpts(c) diff --git a/droplets_test.go b/droplets_test.go index 37f09cb..5bab7b9 100644 --- a/droplets_test.go +++ b/droplets_test.go @@ -9,13 +9,6 @@ import ( "github.com/stretchr/testify/assert" ) -var ( - testDroplet = godo.Droplet{ID: 1} - testDropletList = []godo.Droplet{testDroplet} - testKernel = godo.Kernel{ID: 1} - testKernelList = []godo.Kernel{testKernel} -) - func TestDropletActionList(t *testing.T) { client := &godo.Client{ Droplets: &DropletsServiceMock{ @@ -34,7 +27,7 @@ func TestDropletActionList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -61,7 +54,7 @@ func TestDropletBackupList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -89,7 +82,7 @@ func TestDropletCreate(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String(ArgDropletName, "droplet", ArgDropletName) fs.String(ArgRegionSlug, "dev0", ArgRegionSlug) @@ -111,7 +104,7 @@ func TestDropletDelete(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, testDroplet.ID, ArgDropletID) @@ -130,7 +123,7 @@ func TestDropletGet(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, testDroplet.ID, ArgDropletID) @@ -157,7 +150,7 @@ func TestDropletKernelList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -184,7 +177,7 @@ func TestDropletNeighbors(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -210,7 +203,7 @@ func TestDropletSnapshotList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgDropletID, 1, ArgDropletID) @@ -235,7 +228,7 @@ func TestDropletsList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) WithinTest(cs, nil, func(c *cli.Context) { DropletList(c) diff --git a/godo_util.go b/godo_util.go new file mode 100644 index 0000000..f34cdb5 --- /dev/null +++ b/godo_util.go @@ -0,0 +1,44 @@ +package docli + +import ( + "fmt" + + "github.com/digitalocean/godo" +) + +func getDropletByID(client *godo.Client, id int) (*godo.Droplet, error) { + if id < 1 { + return nil, fmt.Errorf("missing droplet id") + } + + droplet, _, err := client.Droplets.Get(id) + return droplet, err +} + +func listDroplets(client *godo.Client, opts *Opts) ([]godo.Droplet, error) { + f := func(opt *godo.ListOptions) ([]interface{}, *godo.Response, error) { + list, resp, err := client.Droplets.List(opt) + if err != nil { + return nil, nil, err + } + + si := make([]interface{}, len(list)) + for i := range list { + si[i] = list[i] + } + + return si, resp, err + } + + si, err := PaginateResp(f, opts) + if err != nil { + return nil, err + } + + list := make([]godo.Droplet, len(si)) + for i := range si { + list[i] = si[i].(godo.Droplet) + } + + return list, nil +} diff --git a/image_actions.go b/image_actions.go index 0c85a2f..86dadae 100644 --- a/image_actions.go +++ b/image_actions.go @@ -8,7 +8,7 @@ import ( // Get retrieves an action for an image. func ImageActionsGet(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) imageID := c.Int(ArgImageID) actionID := c.Int(ArgActionID) @@ -25,7 +25,7 @@ func ImageActionsGet(c *cli.Context) { // Tranfer an image. func ImageActionsTransfer(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgImageID) req := &godo.ActionRequest{ "region": c.String(ArgRegionSlug), diff --git a/image_actions_test.go b/image_actions_test.go index e5170ea..84094ee 100644 --- a/image_actions_test.go +++ b/image_actions_test.go @@ -20,7 +20,7 @@ func TestImageActionsGet(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgImageID, 1, "image-id") fs.Int(ArgActionID, 2, "action-id") @@ -44,7 +44,7 @@ func TestImageActionsTransfer(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgImageID, 1, "image-id") fs.String(ArgRegionSlug, "dev0", "region") diff --git a/images.go b/images.go index 8067ae6..bce8ec2 100644 --- a/images.go +++ b/images.go @@ -11,7 +11,7 @@ import ( // List images. func ImagesList(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) f := func(opt *godo.ListOptions) ([]interface{}, *godo.Response, error) { @@ -46,7 +46,7 @@ func ImagesList(c *cli.Context) { // ListDistribution lists distributions that are available. func ImagesListDistribution(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) f := func(opt *godo.ListOptions) ([]interface{}, *godo.Response, error) { @@ -81,7 +81,7 @@ func ImagesListDistribution(c *cli.Context) { // ListApplication lists application iamges. func ImagesListApplication(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) f := func(opt *godo.ListOptions) ([]interface{}, *godo.Response, error) { @@ -116,7 +116,7 @@ func ImagesListApplication(c *cli.Context) { // ListUser lists user images. func ImagesListUser(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) f := func(opt *godo.ListOptions) ([]interface{}, *godo.Response, error) { @@ -151,7 +151,7 @@ func ImagesListUser(c *cli.Context) { // Get retrieves an image by id or slug. func ImagesGet(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) rawID := c.String(ArgImage) var err error @@ -178,7 +178,7 @@ func ImagesGet(c *cli.Context) { // Update updates an image. func ImagesUpdate(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgImageID) req := &godo.ImageUpdateRequest{ @@ -197,7 +197,7 @@ func ImagesUpdate(c *cli.Context) { } func ImagesDelete(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) id := c.Int(ArgImageID) _, err := client.Images.Delete(id) diff --git a/images_test.go b/images_test.go index b1b4860..8d95690 100644 --- a/images_test.go +++ b/images_test.go @@ -32,7 +32,7 @@ func TestImagesList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) WithinTest(cs, fs, func(c *cli.Context) { @@ -59,7 +59,7 @@ func TestImagesListDistribution(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) WithinTest(cs, fs, func(c *cli.Context) { @@ -86,7 +86,7 @@ func TestImagesListApplication(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) WithinTest(cs, fs, func(c *cli.Context) { @@ -113,7 +113,7 @@ func TestImagesListUser(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) WithinTest(cs, fs, func(c *cli.Context) { @@ -136,7 +136,7 @@ func TestImagesGetByID(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgImage, testImage.ID, ArgImage) @@ -159,7 +159,7 @@ func TestImagesGetBySlug(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String(ArgImage, testImage.Slug, ArgImage) @@ -182,7 +182,7 @@ func TestImagesNoID(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) WithinTest(cs, fs, func(c *cli.Context) { @@ -203,7 +203,7 @@ func TestImagesUpdate(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgImageID, testImage.ID, ArgImageID) fs.String(ArgImageName, "new-name", ArgImageName) @@ -223,7 +223,7 @@ func TestImagesDelete(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.Int(ArgImageID, testImage.ID, ArgImageID) diff --git a/regions.go b/regions.go index d0c84cd..f3f4c5f 100644 --- a/regions.go +++ b/regions.go @@ -8,7 +8,7 @@ import ( // List all regions. func RegionList(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) f := func(opt *godo.ListOptions) ([]interface{}, *godo.Response, error) { diff --git a/regions_test.go b/regions_test.go index 6889774..c43f7aa 100644 --- a/regions_test.go +++ b/regions_test.go @@ -32,7 +32,7 @@ func TestRegionsList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) WithinTest(cs, fs, func(c *cli.Context) { diff --git a/sizes.go b/sizes.go index 843e950..7cf104a 100644 --- a/sizes.go +++ b/sizes.go @@ -8,7 +8,7 @@ import ( // List all sizes. func SizeList(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) f := func(opt *godo.ListOptions) ([]interface{}, *godo.Response, error) { diff --git a/sizes_test.go b/sizes_test.go index 53df6e0..041d514 100644 --- a/sizes_test.go +++ b/sizes_test.go @@ -32,7 +32,7 @@ func TestSizesList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) WithinTest(cs, fs, func(c *cli.Context) { diff --git a/ssh.go b/ssh.go new file mode 100644 index 0000000..62ed3bd --- /dev/null +++ b/ssh.go @@ -0,0 +1,70 @@ +package docli + +import ( + "fmt" + + "github.com/codegangsta/cli" + "github.com/digitalocean/godo" +) + +const ( + sshNoAddress = "could not find droplet address" +) + +var ( + errSSHInvalidOptions = fmt.Errorf("neither id or name were supplied") +) + +// SSH finds a droplet to ssh to given input parameters (name or id). +func SSH(c *cli.Context) { + client := NewClient(c, DefaultConfig) + id := c.Int(ArgDropletID) + name := c.String(ArgDropletName) + + var droplet *godo.Droplet + var err error + + switch { + case id > 0 && len(name) < 1: + droplet, err = getDropletByID(client, id) + if err != nil { + Bail(err, sshNoAddress) + return + } + case len(name) > 0 && id < 1: + var droplets []godo.Droplet + droplets, err = listDroplets(client, LoadOpts(c)) + for _, d := range droplets { + if d.Name == name { + droplet = &d + break + } + } + + if droplet == nil { + Bail(fmt.Errorf("could not find droplet by name"), sshNoAddress) + return + } + + default: + Bail(errSSHInvalidOptions, sshNoAddress) + return + } + + var publicInt godo.NetworkV4 + for _, in := range droplet.Networks.V4 { + if in.Type == "public" { + publicInt = in + break + } + } + + if len(publicInt.Type) < 1 { + Bail(fmt.Errorf("no public interface for droplet"), sshNoAddress) + } + + err = DefaultConfig.SSH("root", publicInt.IPAddress) + if err != nil { + Bail(err, "unable to ssh to host") + } +} diff --git a/ssh_test.go b/ssh_test.go new file mode 100644 index 0000000..f7a43e9 --- /dev/null +++ b/ssh_test.go @@ -0,0 +1,157 @@ +package docli + +import ( + "flag" + "fmt" + "testing" + + "github.com/codegangsta/cli" + "github.com/digitalocean/godo" + "github.com/stretchr/testify/assert" +) + +type sshMock struct { + user string + host string + didRun bool + isError bool +} + +func (s *sshMock) cmd() func(u, h string) error { + return func(u, h string) error { + s.didRun = true + s.user = u + s.host = h + + if s.isError { + return fmt.Errorf("ssh forced failure") + } + + return nil + } +} + +func TestSSH_ID(t *testing.T) { + didFetchDroplet := false + + client := &godo.Client{ + Droplets: &DropletsServiceMock{ + GetFn: func(id int) (*godo.Droplet, *godo.Response, error) { + assert.Equal(t, id, testDroplet.ID, "droplet ids did not match") + didFetchDroplet = true + return &testDroplet, nil, nil + }, + }, + } + + ms := &sshMock{} + cs := NewTestConfig(client) + cs.SSHFn = ms.cmd() + + fs := flag.NewFlagSet("flag set", 0) + fs.Int(ArgDropletID, testDroplet.ID, ArgDropletID) + + WithinTest(cs, fs, func(c *cli.Context) { + SSH(c) + assert.True(t, didFetchDroplet) + assert.True(t, ms.didRun) + assert.Equal(t, "root", ms.user) + assert.Equal(t, testDroplet.Networks.V4[0].IPAddress, ms.host) + }) +} + +func TestSSH_InvalidID(t *testing.T) { + didFetchDroplet := false + + client := &godo.Client{ + Droplets: &DropletsServiceMock{ + GetFn: func(id int) (*godo.Droplet, *godo.Response, error) { + didFetchDroplet = true + return nil, nil, fmt.Errorf("not here") + }, + }, + } + + ms := &sshMock{} + cs := NewTestConfig(client) + cs.SSHFn = ms.cmd() + + fs := flag.NewFlagSet("flag set", 0) + fs.Int(ArgDropletID, testDroplet.ID, ArgDropletID) + + WithinTest(cs, fs, func(c *cli.Context) { + SSH(c) + assert.True(t, didFetchDroplet) + assert.False(t, ms.didRun) + }) +} + +func TestSSH_Name(t *testing.T) { + didFetchDroplet := false + + client := &godo.Client{ + Droplets: &DropletsServiceMock{ + ListFn: func(*godo.ListOptions) ([]godo.Droplet, *godo.Response, error) { + didFetchDroplet = true + return testDropletList, nil, nil + }, + }, + } + + ms := &sshMock{} + cs := NewTestConfig(client) + cs.SSHFn = ms.cmd() + + fs := flag.NewFlagSet("flag set", 0) + fs.String(ArgDropletName, testDroplet.Name, ArgDropletName) + + WithinTest(cs, fs, func(c *cli.Context) { + SSH(c) + assert.True(t, didFetchDroplet) + assert.True(t, ms.didRun) + assert.Equal(t, "root", ms.user) + assert.Equal(t, testDroplet.Networks.V4[0].IPAddress, ms.host) + }) +} + +func TestSSH_InvalidName(t *testing.T) { + didFetchDroplet := false + + client := &godo.Client{ + Droplets: &DropletsServiceMock{ + ListFn: func(*godo.ListOptions) ([]godo.Droplet, *godo.Response, error) { + didFetchDroplet = true + return nil, nil, fmt.Errorf("not here") + }, + }, + } + + ms := &sshMock{} + cs := NewTestConfig(client) + cs.SSHFn = ms.cmd() + + fs := flag.NewFlagSet("flag set", 0) + fs.String(ArgDropletName, "nope", ArgDropletName) + + WithinTest(cs, fs, func(c *cli.Context) { + SSH(c) + assert.True(t, didFetchDroplet) + assert.False(t, ms.didRun) + }) +} + +func TestSSH_InvalidOpts(t *testing.T) { + + client := &godo.Client{} + + ms := &sshMock{} + cs := NewTestConfig(client) + cs.SSHFn = ms.cmd() + + fs := flag.NewFlagSet("flag set", 0) + + WithinTest(cs, fs, func(c *cli.Context) { + SSH(c) + assert.False(t, ms.didRun) + }) +} diff --git a/sshkeys.go b/sshkeys.go index dcdc0ad..b7fa10b 100644 --- a/sshkeys.go +++ b/sshkeys.go @@ -10,7 +10,7 @@ import ( ) func KeyList(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) opts := LoadOpts(c) f := func(opt *godo.ListOptions) ([]interface{}, *godo.Response, error) { @@ -44,7 +44,7 @@ func KeyList(c *cli.Context) { } func KeyGet(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) rawKey := c.String(ArgKey) var err error @@ -71,7 +71,7 @@ func KeyGet(c *cli.Context) { // Create uploads a SSH key. func KeyCreate(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) kcr := &godo.KeyCreateRequest{ Name: c.String(ArgKeyName), @@ -90,7 +90,7 @@ func KeyCreate(c *cli.Context) { } func KeyDelete(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) rawKey := c.String(ArgKey) var err error @@ -106,7 +106,7 @@ func KeyDelete(c *cli.Context) { } func KeyUpdate(c *cli.Context) { - client := NewClient(c, DefaultClientSource) + client := NewClient(c, DefaultConfig) rawKey := c.String(ArgKey) req := &godo.KeyUpdateRequest{ diff --git a/sshkeys_test.go b/sshkeys_test.go index 29f574d..c6f5cfe 100644 --- a/sshkeys_test.go +++ b/sshkeys_test.go @@ -33,11 +33,12 @@ func TestKeysList(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) WithinTest(cs, fs, func(c *cli.Context) { KeyList(c) + assert.True(t, didList) }) } @@ -55,7 +56,7 @@ func TestKeysGetByID(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String(ArgKey, "1", ArgKey) @@ -78,7 +79,7 @@ func TestKeysGetByFingerprint(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String(ArgKey, testKey.Fingerprint, ArgKey) @@ -101,7 +102,7 @@ func TestKeysCreate(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String(ArgKeyName, "the key", ArgKeyName) fs.String(ArgKeyPublicKey, "fingerprint", ArgKeyPublicKey) @@ -125,7 +126,7 @@ func TestKeysDeleteByID(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String(ArgKey, "1", ArgKey) @@ -148,7 +149,7 @@ func TestKeysDeleteByFingerprint(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String(ArgKey, "fingerprint", ArgKey) @@ -175,7 +176,7 @@ func TestKeysUpdateByID(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String(ArgKey, "1", ArgKey) fs.String(ArgKeyName, "the key", ArgKeyName) @@ -203,7 +204,7 @@ func TestKeysUpdateByFingerprint(t *testing.T) { }, } - cs := &TestClientSource{client} + cs := NewTestConfig(client) fs := flag.NewFlagSet("flag set", 0) fs.String(ArgKey, "fingerprint", ArgKey) fs.String(ArgKeyName, "the key", ArgKeyName) diff --git a/util.go b/util.go index afa4063..fd1a42f 100644 --- a/util.go +++ b/util.go @@ -5,7 +5,10 @@ import ( "bytes" "encoding/json" "flag" + "fmt" "io" + "os" + "os/exec" "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" @@ -13,20 +16,43 @@ import ( "golang.org/x/oauth2" ) -var DefaultClientSource ClientSource = &LiveClientSource{} +// DefaultConfig is the current configuration for the commands. +var DefaultConfig Config = &LiveConfig{} type TokenSource struct { AccessToken string } -type TestClientSource struct { +// TestConfig is an implemenation of Config that can be inspected during tests. +type TestConfig struct { Client *godo.Client + SSHFn func(user, host string) error } -func (cs *TestClientSource) NewClient(_ string) *godo.Client { +// NewTestConfig creates a TestConfig. +func NewTestConfig(client *godo.Client) *TestConfig { + return &TestConfig{ + Client: client, + SSHFn: func(u, h string) error { + logrus.WithFields(logrus.Fields{ + "user": u, + "host": h, + }).Info("ssh") + return nil + }, + } +} + +// NewClient returns the specified godo.Client. +func (cs *TestConfig) NewClient(_ string) *godo.Client { return cs.Client } +// SSH allows the developer to inspect the status of the ssh connection during tests. +func (cs *TestConfig) SSH(user, host string) error { + return cs.SSHFn(user, host) +} + func (t *TokenSource) Token() (*oauth2.Token, error) { return &oauth2.Token{ AccessToken: t.AccessToken, @@ -52,13 +78,18 @@ func WriteJSON(item interface{}, w io.Writer) error { } -type ClientSource interface { +// Config holds configuration values for commands. It currently contains a godo Client +// and a method for running SSH. +type Config interface { NewClient(token string) *godo.Client + SSH(user, host string) error } -type LiveClientSource struct{} +// LiveConfig +type LiveConfig struct{} -func (cs *LiveClientSource) NewClient(token string) *godo.Client { +// NewClient creates creates a godo.Client givent a token. +func (cs *LiveConfig) NewClient(token string) *godo.Client { tokenSource := &TokenSource{ AccessToken: token, } @@ -67,21 +98,38 @@ func (cs *LiveClientSource) NewClient(token string) *godo.Client { return godo.NewClient(oauthClient) } -func NewClient(c *cli.Context, cs ClientSource) *godo.Client { +// SSH runs the ssh binary given a user and a host. It preserves stdin, stdout, and stderr. +func (cs *LiveConfig) SSH(user, host string) error { + logrus.WithFields(logrus.Fields{ + "user": user, + "host": host, + }).Info("ssh") + + sshHost := fmt.Sprintf("%s@%s", user, host) + cmd := exec.Command("ssh", sshHost) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + return cmd.Run() +} + +// NewClient creates a Client. +func NewClient(c *cli.Context, cs Config) *godo.Client { if cs == nil { - cs = &LiveClientSource{} + cs = &LiveConfig{} } pat := c.GlobalString("token") return cs.NewClient(pat) } -func WithinTest(cs ClientSource, fs *flag.FlagSet, fn func(*cli.Context)) { - ogSource := DefaultClientSource - DefaultClientSource = cs +func WithinTest(cs Config, fs *flag.FlagSet, fn func(*cli.Context)) { + ogSource := DefaultConfig + DefaultConfig = cs defer func() { - DefaultClientSource = ogSource + DefaultConfig = ogSource }() var b bytes.Buffer