Browse Source

Add --openstack-key-pair and --openstack-private-file-name so to

import and reuse existing nova keypairs.

Signed-off-by: Fabrizio Soppelsa <fsoppelsa@mirantis.com>
Fabrizio Soppelsa 3 years ago
parent
commit
7400e6f965
3 changed files with 70 additions and 4 deletions
  1. 4
    0
      docs/drivers/openstack.md
  2. 9
    0
      drivers/openstack/client.go
  3. 57
    4
      drivers/openstack/openstack.go

+ 4
- 0
docs/drivers/openstack.md View File

@@ -28,10 +28,12 @@ Options:
28 28
 -   `--openstack-floatingip-pool`: The IP pool that will be used to get a public IP can assign it to the machine. If there is an
29 29
     IP address already allocated but not assigned to any machine, this IP will be chosen and assigned to the machine. If
30 30
     there is no IP address already allocated a new IP will be allocated and assigned to the machine.
31
+-   `--openstack-keypair-name`: Specify the existing Nova keypair to use.
31 32
 -   `--openstack-insecure`: Explicitly allow openstack driver to perform "insecure" SSL (https) requests. The server's certificate will not be verified against any certificate authorities. This option should be used with caution.
32 33
 -   `--openstack-ip-version`: If the instance has both IPv4 and IPv6 address, you can select IP version. If not provided `4` will be used.
33 34
 -   `--openstack-net-name` or `--openstack-net-id`: Identify the private network the machine will be connected on. If your OpenStack project project contains only one private network it will be use automatically.
34 35
 -   `--openstack-password`: User password. It can be omitted if the standard environment variable `OS_PASSWORD` is set.
36
+-   `--openstack-private-key-file`: Used with `--openstack-keypair-name`, associates the private key to the keypair.
35 37
 -   `--openstack-region`: The region to work on. Can be omitted if there is only one region on the OpenStack.
36 38
 -   `--openstack-sec-groups`: If security groups are available on your OpenStack you can specify a comma separated list
37 39
     to use for the machine (e.g. `secgrp001,secgrp002`).
@@ -57,9 +59,11 @@ Environment variables and default values:
57 59
 | `--openstack-image-name`        | `OS_IMAGE_NAME`        | -           |
58 60
 | `--openstack-insecure`          | `OS_INSECURE`          | `false`     |
59 61
 | `--openstack-ip-version`        | `OS_IP_VERSION`        | `4`         |
62
+| `--openstack-keypair-name`      | `OS_KEYPAIR_NAME`      | -           |
60 63
 | `--openstack-net-id`            | `OS_NETWORK_ID`        | -           |
61 64
 | `--openstack-net-name`          | `OS_NETWORK_NAME`      | -           |
62 65
 | `--openstack-password`          | `OS_PASSWORD`          | -           |
66
+| `--openstack-private-key-file`  | `OS_PRIVATE_KEY_FILE`  | -           |
63 67
 | `--openstack-region`            | `OS_REGION_NAME`       | -           |
64 68
 | `--openstack-sec-groups`        | `OS_SECURITY_GROUPS`   | -           |
65 69
 | `--openstack-ssh-port`          | `OS_SSH_PORT`          | `22`        |

+ 9
- 0
drivers/openstack/client.go View File

@@ -38,6 +38,7 @@ type Client interface {
38 38
 	DeleteInstance(d *Driver) error
39 39
 	WaitForInstanceStatus(d *Driver, status string) error
40 40
 	GetInstanceIPAddresses(d *Driver) ([]IPAddress, error)
41
+	GetPublicKey(keyPairName string) ([]byte, error)
41 42
 	CreateKeyPair(d *Driver, name string, publicKey string) error
42 43
 	DeleteKeyPair(d *Driver, name string) error
43 44
 	GetNetworkID(d *Driver) (string, error)
@@ -300,6 +301,14 @@ func (c *GenericClient) GetTenantID(d *Driver) (string, error) {
300 301
 	return tenantId, err
301 302
 }
302 303
 
304
+func (c *GenericClient) GetPublicKey(keyPairName string) ([]byte, error) {
305
+	kp, err := keypairs.Get(c.Compute, keyPairName).Extract()
306
+	if err != nil {
307
+		return nil, err
308
+	}
309
+	return []byte(kp.PublicKey), nil
310
+}
311
+
303 312
 func (c *GenericClient) CreateKeyPair(d *Driver, name string, publicKey string) error {
304 313
 	opts := keypairs.CreateOpts{
305 314
 		Name:      name,

+ 57
- 4
drivers/openstack/openstack.go View File

@@ -37,6 +37,7 @@ type Driver struct {
37 37
 	KeyPairName      string
38 38
 	NetworkName      string
39 39
 	NetworkId        string
40
+	PrivateKeyFile   string
40 41
 	SecurityGroups   []string
41 42
 	FloatingIpPool   string
42 43
 	ComputeNetwork   bool
@@ -142,12 +143,24 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
142 143
 			Usage:  "OpenStack image name to use for the instance",
143 144
 			Value:  "",
144 145
 		},
146
+		mcnflag.StringFlag{
147
+			EnvVar: "OS_KEYPAIR_NAME",
148
+			Name:   "openstack-keypair-name",
149
+			Usage:  "OpenStack keypair to use to SSH to the instance",
150
+			Value:  "",
151
+		},
145 152
 		mcnflag.StringFlag{
146 153
 			EnvVar: "OS_NETWORK_ID",
147 154
 			Name:   "openstack-net-id",
148 155
 			Usage:  "OpenStack network id the machine will be connected on",
149 156
 			Value:  "",
150 157
 		},
158
+		mcnflag.StringFlag{
159
+			EnvVar: "OS_PRIVATE_KEY_FILE",
160
+			Name:   "openstack-private-key-file",
161
+			Usage:  "Private keyfile to use for SSH (absolute path)",
162
+			Value:  "",
163
+		},
151 164
 		mcnflag.StringFlag{
152 165
 			EnvVar: "OS_NETWORK_NAME",
153 166
 			Name:   "openstack-net-name",
@@ -255,6 +268,8 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
255 268
 	d.ComputeNetwork = flags.Bool("openstack-nova-network")
256 269
 	d.SSHUser = flags.String("openstack-ssh-user")
257 270
 	d.SSHPort = flags.Int("openstack-ssh-port")
271
+	d.KeyPairName = flags.String("openstack-keypair-name")
272
+	d.PrivateKeyFile = flags.String("openstack-private-key-file")
258 273
 	d.SwarmMaster = flags.Bool("swarm-master")
259 274
 	d.SwarmHost = flags.String("swarm-host")
260 275
 	d.SwarmDiscovery = flags.String("swarm-discovery")
@@ -339,13 +354,18 @@ func (d *Driver) GetState() (state.State, error) {
339 354
 }
340 355
 
341 356
 func (d *Driver) Create() error {
342
-	d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, mcnutils.GenerateRandomID())
343
-
344 357
 	if err := d.resolveIds(); err != nil {
345 358
 		return err
346 359
 	}
347
-	if err := d.createSSHKey(); err != nil {
348
-		return err
360
+	if d.KeyPairName != "" {
361
+		if err := d.loadSSHKey(); err != nil {
362
+			return err
363
+		}
364
+	} else {
365
+		d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, mcnutils.GenerateRandomID())
366
+		if err := d.createSSHKey(); err != nil {
367
+			return err
368
+		}
349 369
 	}
350 370
 	if err := d.createMachine(); err != nil {
351 371
 		return err
@@ -397,6 +417,7 @@ func (d *Driver) Remove() error {
397 417
 		return err
398 418
 	}
399 419
 	log.Debug("deleting key pair...", map[string]string{"Name": d.KeyPairName})
420
+	// TODO (fsoppelsa) maybe we want to check this, in case of shared keypairs, before removal
400 421
 	if err := d.client.DeleteKeyPair(d, d.KeyPairName); err != nil {
401 422
 		return err
402 423
 	}
@@ -422,6 +443,7 @@ const (
422 443
 	errorMandatoryEnvOrOption    string = "%s must be specified either using the environment variable %s or the CLI option %s"
423 444
 	errorMandatoryOption         string = "%s must be specified using the CLI option %s"
424 445
 	errorExclusiveOptions        string = "Either %s or %s must be specified, not both"
446
+	errorBothOptions             string = "Both %s and %s must be specified"
425 447
 	errorMandatoryTenantNameOrID string = "Tenant id or name must be provided either using one of the environment variables OS_TENANT_ID and OS_TENANT_NAME or one of the CLI options --openstack-tenant-id and --openstack-tenant-name"
426 448
 	errorWrongEndpointType       string = "Endpoint type must be 'publicURL', 'adminURL' or 'internalURL'"
427 449
 	errorUnknownFlavorName       string = "Unable to find flavor named %s"
@@ -464,6 +486,9 @@ func (d *Driver) checkConfig() error {
464 486
 	if d.EndpointType != "" && (d.EndpointType != "publicURL" && d.EndpointType != "adminURL" && d.EndpointType != "internalURL") {
465 487
 		return fmt.Errorf(errorWrongEndpointType)
466 488
 	}
489
+	if (d.KeyPairName != "" && d.PrivateKeyFile == "") || (d.KeyPairName == "" && d.PrivateKeyFile != "") {
490
+		return fmt.Errorf(errorBothOptions, "KeyPairName", "PrivateKeyFile")
491
+	}
467 492
 	return nil
468 493
 }
469 494
 
@@ -607,6 +632,30 @@ func (d *Driver) initNetwork() error {
607 632
 	return nil
608 633
 }
609 634
 
635
+func (d *Driver) loadSSHKey() error {
636
+	log.Debug("Loading Key Pair", d.KeyPairName)
637
+	if err := d.initCompute(); err != nil {
638
+		return err
639
+	}
640
+	log.Debug("Loading Private Key from", d.PrivateKeyFile)
641
+	privateKey, err := ioutil.ReadFile(d.PrivateKeyFile)
642
+	if err != nil {
643
+		return err
644
+	}
645
+	publicKey, err := d.client.GetPublicKey(d.KeyPairName)
646
+	if err != nil {
647
+		return err
648
+	}
649
+	if err := ioutil.WriteFile(d.privateSSHKeyPath(), privateKey, 0600); err != nil {
650
+		return err
651
+	}
652
+	if err := ioutil.WriteFile(d.publicSSHKeyPath(), publicKey, 0600); err != nil {
653
+		return err
654
+	}
655
+
656
+	return nil
657
+}
658
+
610 659
 func (d *Driver) createSSHKey() error {
611 660
 	sanitizeKeyPairName(&d.KeyPairName)
612 661
 	log.Debug("Creating Key Pair...", map[string]string{"Name": d.KeyPairName})
@@ -715,6 +764,10 @@ func (d *Driver) lookForIPAddress() error {
715 764
 	return nil
716 765
 }
717 766
 
767
+func (d *Driver) privateSSHKeyPath() string {
768
+	return d.GetSSHKeyPath()
769
+}
770
+
718 771
 func (d *Driver) publicSSHKeyPath() string {
719 772
 	return d.GetSSHKeyPath() + ".pub"
720 773
 }

Loading…
Cancel
Save