From c12d3f8ecdd4dd7f635485a01fa4cd09331132f5 Mon Sep 17 00:00:00 2001 From: Ash Wilson Date: Thu, 11 Dec 2014 10:14:49 -0500 Subject: [PATCH] Initial cut at a Rackspace driver. * Wrap openstack.Client in an interface. * Alternate openstack Driver creation method. * Register the Rackspace driver in commands.go. Signed-off-by: Ash Wilson --- commands.go | 1 + drivers/openstack/client.go | 56 +++++++++++------- drivers/openstack/openstack.go | 10 +++- drivers/rackspace/client.go | 52 +++++++++++++++++ drivers/rackspace/rackspace.go | 101 +++++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+), 24 deletions(-) create mode 100644 drivers/rackspace/client.go create mode 100644 drivers/rackspace/rackspace.go diff --git a/commands.go b/commands.go index 451f99b4..236f2f53 100644 --- a/commands.go +++ b/commands.go @@ -19,6 +19,7 @@ import ( _ "github.com/docker/machine/drivers/google" _ "github.com/docker/machine/drivers/none" _ "github.com/docker/machine/drivers/openstack" + _ "github.com/docker/machine/drivers/rackspace" _ "github.com/docker/machine/drivers/virtualbox" _ "github.com/docker/machine/drivers/vmwarefusion" _ "github.com/docker/machine/drivers/vmwarevcloudair" diff --git a/drivers/openstack/client.go b/drivers/openstack/client.go index e21e48a6..f12cf797 100644 --- a/drivers/openstack/client.go +++ b/drivers/openstack/client.go @@ -15,13 +15,27 @@ import ( "github.com/rackspace/gophercloud/pagination" ) -type Client struct { +type Client interface { + CreateInstance(d *Driver) (string, error) + GetInstanceState(d *Driver) (string, error) + StartInstance(d *Driver) error + StopInstance(d *Driver) error + RestartInstance(d *Driver) error + DeleteInstance(d *Driver) error + WaitForInstanceStatus(d *Driver, status string, timeout int) error + GetInstanceIpAddresses(d *Driver) ([]IpAddress, error) + CreateKeyPair(d *Driver, name string, publicKey string) error + DeleteKeyPair(d *Driver, name string) error + Authenticate(d *Driver) error +} + +type GenericClient struct { Provider *gophercloud.ProviderClient Compute *gophercloud.ServiceClient Network *gophercloud.ServiceClient } -func (c *Client) CreateInstance(d *Driver) (string, error) { +func (c *GenericClient) CreateInstance(d *Driver) (string, error) { if err := c.initComputeClient(d); err != nil { return "", err } @@ -60,7 +74,7 @@ type IpAddress struct { Mac string } -func (c *Client) GetInstanceState(d *Driver) (string, error) { +func (c *GenericClient) GetInstanceState(d *Driver) (string, error) { server, err := c.getServerDetail(d) if err != nil { return "", err @@ -73,7 +87,7 @@ func (c *Client) GetInstanceState(d *Driver) (string, error) { return server.Status, nil } -func (c *Client) StartInstance(d *Driver) error { +func (c *GenericClient) StartInstance(d *Driver) error { if err := c.initComputeClient(d); err != nil { return err } @@ -83,7 +97,7 @@ func (c *Client) StartInstance(d *Driver) error { return nil } -func (c *Client) StopInstance(d *Driver) error { +func (c *GenericClient) StopInstance(d *Driver) error { if err := c.initComputeClient(d); err != nil { return err } @@ -93,7 +107,7 @@ func (c *Client) StopInstance(d *Driver) error { return nil } -func (c *Client) RestartInstance(d *Driver) error { +func (c *GenericClient) RestartInstance(d *Driver) error { if err := c.initComputeClient(d); err != nil { return err } @@ -103,7 +117,7 @@ func (c *Client) RestartInstance(d *Driver) error { return nil } -func (c *Client) DeleteInstance(d *Driver) error { +func (c *GenericClient) DeleteInstance(d *Driver) error { if err := c.initComputeClient(d); err != nil { return err } @@ -113,14 +127,14 @@ func (c *Client) DeleteInstance(d *Driver) error { return nil } -func (c *Client) WaitForInstanceStatus(d *Driver, status string, timeout int) error { +func (c *GenericClient) WaitForInstanceStatus(d *Driver, status string, timeout int) error { if err := servers.WaitForStatus(c.Compute, d.MachineId, status, timeout); err != nil { return err } return nil } -func (c *Client) GetInstanceIpAddresses(d *Driver) ([]IpAddress, error) { +func (c *GenericClient) GetInstanceIpAddresses(d *Driver) ([]IpAddress, error) { server, err := c.getServerDetail(d) if err != nil { return nil, err @@ -140,7 +154,7 @@ func (c *Client) GetInstanceIpAddresses(d *Driver) ([]IpAddress, error) { return addresses, nil } -func (c *Client) GetNetworkId(d *Driver, networkName string) (string, error) { +func (c *GenericClient) GetNetworkId(d *Driver, networkName string) (string, error) { if err := c.initNetworkClient(d); err != nil { return "", err } @@ -168,7 +182,7 @@ func (c *Client) GetNetworkId(d *Driver, networkName string) (string, error) { return networkId, err } -func (c *Client) GetFlavorId(d *Driver, flavorName string) (string, error) { +func (c *GenericClient) GetFlavorId(d *Driver, flavorName string) (string, error) { if err := c.initComputeClient(d); err != nil { return "", err } @@ -195,7 +209,7 @@ func (c *Client) GetFlavorId(d *Driver, flavorName string) (string, error) { return flavorId, err } -func (c *Client) GetImageId(d *Driver, imageName string) (string, error) { +func (c *GenericClient) GetImageId(d *Driver, imageName string) (string, error) { if err := c.initComputeClient(d); err != nil { return "", err } @@ -223,7 +237,7 @@ func (c *Client) GetImageId(d *Driver, imageName string) (string, error) { return imageId, err } -func (c *Client) CreateKeyPair(d *Driver, name string, publicKey string) error { +func (c *GenericClient) CreateKeyPair(d *Driver, name string, publicKey string) error { if err := c.initComputeClient(d); err != nil { return err } @@ -237,7 +251,7 @@ func (c *Client) CreateKeyPair(d *Driver, name string, publicKey string) error { return nil } -func (c *Client) DeleteKeyPair(d *Driver, name string) error { +func (c *GenericClient) DeleteKeyPair(d *Driver, name string) error { if err := c.initComputeClient(d); err != nil { return err } @@ -247,7 +261,7 @@ func (c *Client) DeleteKeyPair(d *Driver, name string) error { return nil } -func (c *Client) getServerDetail(d *Driver) (*servers.Server, error) { +func (c *GenericClient) getServerDetail(d *Driver) (*servers.Server, error) { if err := c.initComputeClient(d); err != nil { return nil, err } @@ -258,7 +272,7 @@ func (c *Client) getServerDetail(d *Driver) (*servers.Server, error) { return server, nil } -func (c *Client) getFloatingIPs(d *Driver) ([]string, error) { +func (c *GenericClient) getFloatingIPs(d *Driver) ([]string, error) { if err := c.initNetworkClient(d); err != nil { return nil, err @@ -283,7 +297,7 @@ func (c *Client) getFloatingIPs(d *Driver) ([]string, error) { return nil, nil } -func (c *Client) getPorts(d *Driver) ([]string, error) { +func (c *GenericClient) getPorts(d *Driver) ([]string, error) { if err := c.initNetworkClient(d); err != nil { return nil, err @@ -310,7 +324,7 @@ func (c *Client) getPorts(d *Driver) ([]string, error) { return nil, nil } -func (c *Client) initComputeClient(d *Driver) error { +func (c *GenericClient) initComputeClient(d *Driver) error { if c.Provider == nil { err := c.Authenticate(d) if err != nil { @@ -328,7 +342,7 @@ func (c *Client) initComputeClient(d *Driver) error { return nil } -func (c *Client) initNetworkClient(d *Driver) error { +func (c *GenericClient) initNetworkClient(d *Driver) error { if c.Provider == nil { err := c.Authenticate(d) if err != nil { @@ -346,7 +360,7 @@ func (c *Client) initNetworkClient(d *Driver) error { return nil } -func (c *Client) getEndpointType(d *Driver) gophercloud.Availability { +func (c *GenericClient) getEndpointType(d *Driver) gophercloud.Availability { switch d.EndpointType { case "internalURL": return gophercloud.AvailabilityInternal @@ -356,7 +370,7 @@ func (c *Client) getEndpointType(d *Driver) gophercloud.Availability { return gophercloud.AvailabilityPublic } -func (c *Client) Authenticate(d *Driver) error { +func (c *GenericClient) Authenticate(d *Driver) error { log.WithFields(log.Fields{ "AuthUrl": d.AuthUrl, "Username": d.Username, diff --git a/drivers/openstack/openstack.go b/drivers/openstack/openstack.go index 49c5463b..ac5f54b8 100644 --- a/drivers/openstack/openstack.go +++ b/drivers/openstack/openstack.go @@ -38,7 +38,7 @@ type Driver struct { SSHUser string SSHPort int storePath string - client *Client + client Client } type CreateFlags struct { @@ -159,12 +159,16 @@ func RegisterCreateFlags(cmd *flag.FlagSet) interface{} { } func NewDriver(storePath string) (drivers.Driver, error) { + return NewDerivedDriver(storePath, &GenericClient{}) +} + +func NewDerivedDriver(storePath string, client Client) (*Driver, error) { log.WithFields(log.Fields{ "storePath": storePath, - }).Debug("Instanciate OpenStack driver...") + }).Debug("Instantiating OpenStack driver...") return &Driver{ storePath: storePath, - client: &Client{}, + client: client, }, nil } diff --git a/drivers/rackspace/client.go b/drivers/rackspace/client.go new file mode 100644 index 00000000..ee289311 --- /dev/null +++ b/drivers/rackspace/client.go @@ -0,0 +1,52 @@ +package rackspace + +import ( + "fmt" + + log "github.com/Sirupsen/logrus" + "github.com/docker/machine/drivers/openstack" + "github.com/rackspace/gophercloud" + "github.com/rackspace/gophercloud/rackspace" +) + +func unsupportedOpErr(operation string) error { + return fmt.Errorf("Rackspace does not currently support the %s operation", operation) +} + +// Client is a Rackspace specialization of the generic OpenStack driver. +type Client struct { + openstack.GenericClient + + driver *Driver +} + +// Authenticate creates a Rackspace-specific Gophercloud client. +func (c *Client) Authenticate(d *openstack.Driver) error { + log.WithFields(log.Fields{ + "Username": d.Username, + }).Info("Authenticating...") + + apiKey := c.driver.APIKey + opts := gophercloud.AuthOptions{ + Username: d.Username, + APIKey: apiKey, + } + + provider, err := rackspace.AuthenticatedClient(opts) + if err != nil { + return err + } + c.Provider = provider + + return nil +} + +// StartInstance is unfortunately not supported on Rackspace at this time. +func (c *Client) StartInstance(d *openstack.Driver) error { + return unsupportedOpErr("start") +} + +// StopInstance is unfortunately not support on Rackspace at this time. +func (c *Client) StopInstance(d *openstack.Driver) error { + return unsupportedOpErr("stop") +} diff --git a/drivers/rackspace/rackspace.go b/drivers/rackspace/rackspace.go new file mode 100644 index 00000000..3d74a756 --- /dev/null +++ b/drivers/rackspace/rackspace.go @@ -0,0 +1,101 @@ +package rackspace + +import ( + "os" + + log "github.com/Sirupsen/logrus" + flag "github.com/docker/docker/pkg/mflag" + "github.com/docker/machine/drivers" + "github.com/docker/machine/drivers/openstack" +) + +// Driver is a machine driver for Rackspace. It's a specialization of the generic OpenStack one. +type Driver struct { + drivers.Driver + + APIKey string +} + +// CreateFlags stores the command-line arguments given to "machine create". +type CreateFlags struct { + Username *string + APIKey *string + Region *string + MachineName *string + EndpointType *string + ImageID *string + FlavorID *string + SSHUser *string + SSHPort *int +} + +func init() { + drivers.Register("rackspace", &drivers.RegisteredDriver{ + New: NewDriver, + RegisterCreateFlags: RegisterCreateFlags, + }) +} + +// RegisterCreateFlags registers the "machine create" flags recognized by this driver, including +// their help text and defaults. +func RegisterCreateFlags(cmd *flag.FlagSet) interface{} { + createFlags := new(CreateFlags) + createFlags.Username = cmd.String( + []string{"-rackspace-username"}, + os.Getenv("OS_USERNAME"), + "Rackspace account username", + ) + createFlags.APIKey = cmd.String( + []string{"-rackspace-api-key"}, + os.Getenv("OS_API_KEY"), + "Rackspace API key", + ) + createFlags.Region = cmd.String( + []string{"-rackspace-region"}, + os.Getenv("OS_REGION_NAME"), + "Rackspace region name", + ) + createFlags.EndpointType = cmd.String( + []string{"-rackspace-endpoint-type"}, + os.Getenv("OS_ENDPOINT_TYPE"), + "Rackspace endpoint type (adminURL, internalURL or the default publicURL)", + ) + createFlags.ImageID = cmd.String( + []string{"-rackspace-image-id"}, + "", + "Rackspace image ID. Default: Ubuntu 14.10 (Utopic Unicorn) (PVHVM)", + ) + createFlags.FlavorID = cmd.String( + []string{"-rackspace-flavor-id"}, + "general1-1", + "Rackspace flavor ID. Default: General Purpose 1GB", + ) + createFlags.SSHUser = cmd.String( + []string{"-rackspace-ssh-user"}, + "root", + "SSH user for the newly booted machine. Set to root by default", + ) + createFlags.SSHPort = cmd.Int( + []string{"-rackspace-ssh-port"}, + 22, + "SSH port for the newly booted machine. Set to 22 by default", + ) + return createFlags +} + +// NewDriver instantiates a Rackspace driver. +func NewDriver(storePath string) (drivers.Driver, error) { + log.WithFields(log.Fields{ + "storePath": storePath, + }).Info("Instantiating Rackspace driver.") + + client := &Client{} + inner, err := openstack.NewDerivedDriver(storePath, &Client{}) + if err != nil { + return nil, err + } + + driver := &Driver{Driver: inner} + client.driver = driver + return driver, nil +}