From 0556812b6316b9157c98e0d2b4d63df796551332 Mon Sep 17 00:00:00 2001 From: Nick Jones Date: Mon, 18 Sep 2017 21:07:08 +0100 Subject: [PATCH] [OpenStack] Support specifying an SSH key name when creating an instance This commit introduces a new option - `keyname` - to the OpenStack runner, which allows the user to specify the name of a keypair they want to associate with the instance at the time of creation. Signed-off-by: Nick Jones --- docs/platform-openstack.md | 3 +- src/cmd/linuxkit/run_openstack.go | 13 ++- .../compute/v2/extensions/keypairs/doc.go | 3 + .../v2/extensions/keypairs/requests.go | 84 ++++++++++++++++++ .../compute/v2/extensions/keypairs/results.go | 86 +++++++++++++++++++ .../compute/v2/extensions/keypairs/urls.go | 25 ++++++ 6 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go create mode 100644 src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go diff --git a/docs/platform-openstack.md b/docs/platform-openstack.md index 56be6f59b..0b747aa79 100644 --- a/docs/platform-openstack.md +++ b/docs/platform-openstack.md @@ -1,6 +1,6 @@ # LinuxKit with OpenStack -LinuxKit interacts with OpenStack through its native APIs and requires access and provides basic support for pushing images and launching virtual instances. +LinuxKit interacts with OpenStack through its native APIs, providing basic support for pushing images and launching virtual instances. Supported (tested) versions of the relevant OpenStack APIs are: @@ -64,6 +64,7 @@ linuxkit run openstack \ -username=admin \ -password=xxx \ -project=linuxkit \ + -keyname=deadline_ed25519 \ -network c5d02c5f-c625-4539-8aed-1dab3aa85a0a \ LinuxKitTest ``` diff --git a/src/cmd/linuxkit/run_openstack.go b/src/cmd/linuxkit/run_openstack.go index e3636b488..096b77c6d 100644 --- a/src/cmd/linuxkit/run_openstack.go +++ b/src/cmd/linuxkit/run_openstack.go @@ -8,6 +8,7 @@ import ( "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" log "github.com/sirupsen/logrus" ) @@ -35,6 +36,7 @@ func runOpenStack(args []string) { flavorName := flags.String("flavor", defaultOSFlavor, "Instance size (flavor)") instanceName := flags.String("instancename", "", "Name of instance. Defaults to the name of the image if not specified") networkID := flags.String("network", "", "The ID of the network to attach the instance to") + keyName := flags.String("keyname", "", "The name of the SSH keypair to associate with the instance") passwordFlag := flags.String("password", "", "Password for the specified username") projectNameFlag := flags.String("project", "", "Name of the Project (aka Tenant) to be used") userDomainFlag := flags.String("domain", "Default", "Domain name") @@ -83,7 +85,9 @@ func runOpenStack(args []string) { UUID: *networkID, } - serverOpts := &servers.CreateOpts{ + var serverOpts servers.CreateOptsBuilder + + serverOpts = &servers.CreateOpts{ FlavorName: *flavorName, ImageName: name, Name: *instanceName, @@ -91,6 +95,13 @@ func runOpenStack(args []string) { ServiceClient: client, } + if *keyName != "" { + serverOpts = &keypairs.CreateOptsExt{ + CreateOptsBuilder: serverOpts, + KeyName: *keyName, + } + } + server, err := servers.Create(client, serverOpts).Extract() if err != nil { log.Fatalf("Unable to create server: %s", err) diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go new file mode 100644 index 000000000..856f41bac --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go @@ -0,0 +1,3 @@ +// Package keypairs provides information and interaction with the Keypairs +// extension for the OpenStack Compute service. +package keypairs diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go new file mode 100644 index 000000000..adf1e5596 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go @@ -0,0 +1,84 @@ +package keypairs + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/pagination" +) + +// CreateOptsExt adds a KeyPair option to the base CreateOpts. +type CreateOptsExt struct { + servers.CreateOptsBuilder + KeyName string `json:"key_name,omitempty"` +} + +// ToServerCreateMap adds the key_name and, optionally, key_data options to +// the base server creation options. +func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) { + base, err := opts.CreateOptsBuilder.ToServerCreateMap() + if err != nil { + return nil, err + } + + if opts.KeyName == "" { + return base, nil + } + + serverMap := base["server"].(map[string]interface{}) + serverMap["key_name"] = opts.KeyName + + return base, nil +} + +// List returns a Pager that allows you to iterate over a collection of KeyPairs. +func List(client *gophercloud.ServiceClient) pagination.Pager { + return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page { + return KeyPairPage{pagination.SinglePageBase(r)} + }) +} + +// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the +// CreateOpts struct in this package does. +type CreateOptsBuilder interface { + ToKeyPairCreateMap() (map[string]interface{}, error) +} + +// CreateOpts specifies keypair creation or import parameters. +type CreateOpts struct { + // Name is a friendly name to refer to this KeyPair in other services. + Name string `json:"name" required:"true"` + // PublicKey [optional] is a pregenerated OpenSSH-formatted public key. If provided, this key + // will be imported and no new key will be created. + PublicKey string `json:"public_key,omitempty"` +} + +// ToKeyPairCreateMap constructs a request body from CreateOpts. +func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) { + return gophercloud.BuildRequestBody(opts, "keypair") +} + +// Create requests the creation of a new keypair on the server, or to import a pre-existing +// keypair. +func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) { + b, err := opts.ToKeyPairCreateMap() + if err != nil { + r.Err = err + return + } + _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{ + OkCodes: []int{200}, + }) + return +} + +// Get returns public data about a previously uploaded KeyPair. +func Get(client *gophercloud.ServiceClient, name string) (r GetResult) { + _, r.Err = client.Get(getURL(client, name), &r.Body, nil) + return +} + +// Delete requests the deletion of a previous stored KeyPair from the server. +func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) { + _, r.Err = client.Delete(deleteURL(client, name), nil) + return +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go new file mode 100644 index 000000000..4c785a24c --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go @@ -0,0 +1,86 @@ +package keypairs + +import ( + "github.com/gophercloud/gophercloud" + "github.com/gophercloud/gophercloud/pagination" +) + +// KeyPair is an SSH key known to the OpenStack Cloud that is available to be injected into +// servers. +type KeyPair struct { + // Name is used to refer to this keypair from other services within this region. + Name string `json:"name"` + + // Fingerprint is a short sequence of bytes that can be used to authenticate or validate a longer + // public key. + Fingerprint string `json:"fingerprint"` + + // PublicKey is the public key from this pair, in OpenSSH format. "ssh-rsa AAAAB3Nz..." + PublicKey string `json:"public_key"` + + // PrivateKey is the private key from this pair, in PEM format. + // "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..." It is only present if this keypair was just + // returned from a Create call + PrivateKey string `json:"private_key"` + + // UserID is the user who owns this keypair. + UserID string `json:"user_id"` +} + +// KeyPairPage stores a single, only page of KeyPair results from a List call. +type KeyPairPage struct { + pagination.SinglePageBase +} + +// IsEmpty determines whether or not a KeyPairPage is empty. +func (page KeyPairPage) IsEmpty() (bool, error) { + ks, err := ExtractKeyPairs(page) + return len(ks) == 0, err +} + +// ExtractKeyPairs interprets a page of results as a slice of KeyPairs. +func ExtractKeyPairs(r pagination.Page) ([]KeyPair, error) { + type pair struct { + KeyPair KeyPair `json:"keypair"` + } + var s struct { + KeyPairs []pair `json:"keypairs"` + } + err := (r.(KeyPairPage)).ExtractInto(&s) + results := make([]KeyPair, len(s.KeyPairs)) + for i, pair := range s.KeyPairs { + results[i] = pair.KeyPair + } + return results, err +} + +type keyPairResult struct { + gophercloud.Result +} + +// Extract is a method that attempts to interpret any KeyPair resource response as a KeyPair struct. +func (r keyPairResult) Extract() (*KeyPair, error) { + var s struct { + KeyPair *KeyPair `json:"keypair"` + } + err := r.ExtractInto(&s) + return s.KeyPair, err +} + +// CreateResult is the response from a Create operation. Call its Extract method to interpret it +// as a KeyPair. +type CreateResult struct { + keyPairResult +} + +// GetResult is the response from a Get operation. Call its Extract method to interpret it +// as a KeyPair. +type GetResult struct { + keyPairResult +} + +// DeleteResult is the response from a Delete operation. Call its Extract method to determine if +// the call succeeded or failed. +type DeleteResult struct { + gophercloud.ErrResult +} diff --git a/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go new file mode 100644 index 000000000..fec38f367 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go @@ -0,0 +1,25 @@ +package keypairs + +import "github.com/gophercloud/gophercloud" + +const resourcePath = "os-keypairs" + +func resourceURL(c *gophercloud.ServiceClient) string { + return c.ServiceURL(resourcePath) +} + +func listURL(c *gophercloud.ServiceClient) string { + return resourceURL(c) +} + +func createURL(c *gophercloud.ServiceClient) string { + return resourceURL(c) +} + +func getURL(c *gophercloud.ServiceClient, name string) string { + return c.ServiceURL(resourcePath, name) +} + +func deleteURL(c *gophercloud.ServiceClient, name string) string { + return getURL(c, name) +}