Пример #1
0
func (d *Driver) Create() error {
	spec := d.buildHostSpec()

	log.Infof("Creating SSH key...")
	key, err := d.createSSHKey()
	if err != nil {
		return err
	}

	log.Infof("SSH key %s (%d) created in SoftLayer", key.Label, key.Id)
	d.SSHKeyID = key.Id

	spec.SshKeys = []*SshKey{key}

	id, err := d.getClient().VirtualGuest().Create(spec)
	if err != nil {
		return fmt.Errorf("Error creating host: %q", err)
	}
	d.Id = id
	d.getIp()
	d.waitForStart()
	d.waitForSetupTransactions()

	return nil
}
Пример #2
0
func ClientOK(endpoint string, test func() bool) error {
	backoff := util.Backoff{}
	defer backoff.Close()

	var err error
	retry := false
	for ok := range backoff.Start() {
		if !ok {
			err = fmt.Errorf("Timeout waiting for Docker at %s", endpoint)
			break
		}
		if test() {
			break
		}
		retry = true
		log.Infof("Waiting for Docker at %s", endpoint)
	}

	if err != nil {
		return err
	}

	if retry {
		log.Infof("Connected to Docker at %s", endpoint)
	}

	return nil
}
Пример #3
0
func (d *Driver) Create() error {
	log.Infof("Creating SSH key...")

	key, err := d.createSSHKey()
	if err != nil {
		return err
	}

	d.SSHKeyID = key.ID

	var sshKey *godo.DropletCreateSSHKey
	sshKey = &godo.DropletCreateSSHKey{ID: d.SSHKeyID}

	log.Infof("Creating Digital Ocean droplet...")

	client := d.getClient()

	createRequest := &godo.DropletCreateRequest{
		Image:             godo.DropletCreateImage{Slug: "digitalocean-image"},
		Name:              d.MachineName,
		Region:            d.Region,
		Size:              d.Size,
		IPv6:              d.IPv6,
		PrivateNetworking: d.PrivateNetworking,
		Backups:           d.Backups,
		SSHKeys:           []godo.DropletCreateSSHKey{*sshKey},
	}

	newDroplet, _, err := client.Droplets.Create(createRequest)
	if err != nil {
		return err
	}

	d.DropletID = newDroplet.ID

	droplets, _, err := client.Droplets.List(nil)
	if err != nil {
		return err
	}

	for _, n := range droplets {
		for _, network := range n.Networks.V4 {
			if network.Type == "public" {
				d.IPAddress = network.IPAddress
			}
		}

		if d.IPAddress != "" {
			break
		}

		time.Sleep(1 * time.Second)
	}

	log.Debugf("Created droplet ID %d, IP address %s",
		newDroplet.ID,
		d.IPAddress)

	return nil
}
Пример #4
0
func (b *B2dUtils) CopyIsoToMachineDir(isoURL, machineName string) error {
	machinesDir := GetMachineDir()
	machineIsoPath := filepath.Join(machinesDir, machineName, b.isoFilename)

	// just in case the cache dir has been manually deleted,
	// check for it and recreate it if it's gone
	if _, err := os.Stat(b.imgCachePath); os.IsNotExist(err) {
		log.Infof("Image cache does not exist, creating it at %s...", b.imgCachePath)
		if err := os.Mkdir(b.imgCachePath, 0700); err != nil {
			return err
		}
	}

	// By default just copy the existing "cached" iso to
	// the machine's directory...
	if isoURL == "" {
		if err := b.copyDefaultIsoToMachine(machineIsoPath); err != nil {
			return err
		}
	} else {
		// But if ISO is specified go get it directly
		log.Infof("Downloading %s from %s...", b.isoFilename, isoURL)
		if err := b.DownloadISO(filepath.Join(machinesDir, machineName), b.isoFilename, isoURL); err != nil {
			return err
		}
	}

	return nil
}
Пример #5
0
// deleteInstance deletes the instance, leaving the persistent disk.
func (c *ComputeUtil) deleteInstance() error {
	log.Infof("Deleting instance.")
	op, err := c.service.Instances.Delete(c.project, c.zone, c.instanceName).Do()
	if err != nil {
		return err
	}
	log.Infof("Waiting for instance to delete.")
	return c.waitForRegionalOp(op.Name)
}
Пример #6
0
// deleteDisk deletes the persistent disk.
func (c *ComputeUtil) deleteDisk() error {
	log.Infof("Deleting disk.")
	op, err := c.service.Disks.Delete(c.project, c.zone, c.diskName()).Do()
	if err != nil {
		return err
	}
	log.Infof("Waiting for disk to delete.")
	return c.waitForRegionalOp(op.Name)
}
Пример #7
0
func (d *Driver) Create() error {
	log.Infof("Creating SSH key...")

	key, err := d.createSSHKey()
	if err != nil {
		return err
	}

	d.SSHKeyID = key.ID

	log.Infof("Creating Digital Ocean droplet...")

	client := d.getClient()

	createRequest := &godo.DropletCreateRequest{
		Image:             d.Image,
		Name:              d.MachineName,
		Region:            d.Region,
		Size:              d.Size,
		IPv6:              d.IPv6,
		PrivateNetworking: d.PrivateNetworking,
		Backups:           d.Backups,
		SSHKeys:           []interface{}{d.SSHKeyID},
	}

	newDroplet, _, err := client.Droplets.Create(createRequest)
	if err != nil {
		return err
	}

	d.DropletID = newDroplet.Droplet.ID

	for {
		newDroplet, _, err = client.Droplets.Get(d.DropletID)
		if err != nil {
			return err
		}
		for _, network := range newDroplet.Droplet.Networks.V4 {
			if network.Type == "public" {
				d.IPAddress = network.IPAddress
			}
		}

		if d.IPAddress != "" {
			break
		}

		time.Sleep(1 * time.Second)
	}

	log.Debugf("Created droplet ID %d, IP address %s",
		newDroplet.Droplet.ID,
		d.IPAddress)

	return nil
}
Пример #8
0
func (d *Driver) Restart() error {

	p, err := govcloudair.NewClient()
	if err != nil {
		return err
	}

	log.Infof("Connecting to vCloud Air...")
	// Authenticate to vCloud Air
	v, err := p.Authenticate(d.UserName, d.UserPassword, d.ComputeID, d.VDCID)
	if err != nil {
		return err
	}

	vapp, err := v.FindVAppByID(d.VAppID)
	if err != nil {
		return err
	}

	status, err := vapp.GetStatus()
	if err != nil {
		return err
	}

	if status == "POWERED_ON" {
		// If it's powered on, restart the machine
		log.Infof("Restarting %s...", d.MachineName)
		task, err := vapp.Reset()
		if err != nil {
			return err
		}
		if err = task.WaitTaskCompletion(); err != nil {
			return err
		}

	} else {
		// If it's not powered on, start it.
		log.Infof("Docker host %s is powered off, powering it back on...", d.MachineName)
		task, err := vapp.PowerOn()
		if err != nil {
			return err
		}
		if err = task.WaitTaskCompletion(); err != nil {
			return err
		}

	}

	if err = p.Disconnect(); err != nil {
		return err
	}

	d.IPAddress, err = d.GetIP()
	return err
}
Пример #9
0
func saveToken(storePath string, token *oauth.Token) {
	tokenPath := path.Join(storePath, "gce_token")
	log.Infof("Saving token in %v", tokenPath)
	f, err := os.Create(tokenPath)
	if err != nil {
		log.Infof("Warning: failed to cache oauth token: %v", err)
		return
	}
	defer f.Close()
	gob.NewEncoder(f).Encode(token)
}
Пример #10
0
func setupCertificates(caCertPath, caKeyPath, clientCertPath, clientKeyPath string) error {
	org := utils.GetUsername()
	bits := 2048

	if _, err := os.Stat(utils.GetMachineCertDir()); err != nil {
		if os.IsNotExist(err) {
			if err := os.MkdirAll(utils.GetMachineCertDir(), 0700); err != nil {
				log.Fatalf("Error creating machine config dir: %s", err)
			}
		} else {
			log.Fatal(err)
		}
	}

	if _, err := os.Stat(caCertPath); os.IsNotExist(err) {
		log.Infof("Creating CA: %s", caCertPath)

		// check if the key path exists; if so, error
		if _, err := os.Stat(caKeyPath); err == nil {
			log.Fatalf("The CA key already exists.  Please remove it or specify a different key/cert.")
		}

		if err := utils.GenerateCACertificate(caCertPath, caKeyPath, org, bits); err != nil {
			log.Infof("Error generating CA certificate: %s", err)
		}
	}

	if _, err := os.Stat(clientCertPath); os.IsNotExist(err) {
		log.Infof("Creating client certificate: %s", clientCertPath)

		if _, err := os.Stat(utils.GetMachineCertDir()); err != nil {
			if os.IsNotExist(err) {
				if err := os.Mkdir(utils.GetMachineCertDir(), 0700); err != nil {
					log.Fatalf("Error creating machine client cert dir: %s", err)
				}
			} else {
				log.Fatal(err)
			}
		}

		// check if the key path exists; if so, error
		if _, err := os.Stat(clientKeyPath); err == nil {
			log.Fatalf("The client key already exists.  Please remove it or specify a different key/cert.")
		}

		if err := utils.GenerateCert([]string{""}, clientCertPath, clientKeyPath, caCertPath, caKeyPath, org, bits); err != nil {
			log.Fatalf("Error generating client certificate: %s", err)
		}
	}

	return nil
}
Пример #11
0
func (d *Driver) Start() error {
	machineState, err := d.GetState()
	if err != nil {
		return err
	}

	switch machineState {
	case state.Running:
		log.Infof("VM %s has already been started", d.MachineName)
		return nil
	case state.Stopped:
		// TODO add transactional or error handling in the following steps
		vcConn := NewVcConn(d)
		err := vcConn.VMPowerOn()
		if err != nil {
			return err
		}
		// this step waits for the vm to start and fetch its ip address;
		// this guarantees that the opem-vmtools has started working...
		_, err = vcConn.VMFetchIP()
		if err != nil {
			return err
		}

		d.IPAddress, err = d.GetIP()
		return err
	}
	return errors.NewInvalidStateError(d.MachineName)
}
Пример #12
0
func (conn VcConn) DatastoreMkdir(dirName string) error {
	_, err := conn.DatastoreLs(dirName)
	if err == nil {
		return nil
	}

	log.Infof("Creating directory %s on datastore %s of vCenter %s... ",
		dirName, conn.driver.Datastore, conn.driver.IP)

	args := []string{"datastore.mkdir"}
	args = conn.AppendConnectionString(args)
	args = append(args, fmt.Sprintf("--ds=%s", conn.driver.Datastore))
	args = append(args, fmt.Sprintf("--dc=%s", conn.driver.Datacenter))
	args = append(args, dirName)
	_, stderr, err := govcOutErr(args...)

	if stderr != "" {
		return errors.NewDatastoreError(conn.driver.Datastore, "mkdir", stderr)
	}

	if err != nil {
		return err
	}

	return nil
}
Пример #13
0
func (conn VcConn) VMCreate(isoPath string) error {
	log.Infof("Creating virtual machine %s of vCenter %s... ",
		conn.driver.MachineName, conn.driver.IP)

	args := []string{"vm.create"}
	args = conn.AppendConnectionString(args)
	args = append(args, fmt.Sprintf("--net=%s", conn.driver.Network))
	args = append(args, fmt.Sprintf("--dc=%s", conn.driver.Datacenter))
	args = append(args, fmt.Sprintf("--ds=%s", conn.driver.Datastore))
	args = append(args, fmt.Sprintf("--iso=%s", isoPath))
	memory := strconv.Itoa(conn.driver.Memory)
	args = append(args, fmt.Sprintf("--m=%s", memory))
	cpu := strconv.Itoa(conn.driver.CPU)
	args = append(args, fmt.Sprintf("--c=%s", cpu))
	args = append(args, "--disk.controller=pvscsi")
	args = append(args, "--net.adapter=vmxnet3")
	args = append(args, "--on=false")
	if conn.driver.Pool != "" {
		args = append(args, fmt.Sprintf("--pool=%s", conn.driver.Pool))
	}
	if conn.driver.HostIP != "" {
		args = append(args, fmt.Sprintf("--host.ip=%s", conn.driver.HostIP))
	}
	args = append(args, conn.driver.MachineName)
	_, stderr, err := govcOutErr(args...)

	if stderr == "" && err == nil {
		return nil
	}
	return errors.NewVMError("create", conn.driver.MachineName, stderr)
}
Пример #14
0
func (d *Driver) Start() error {
	log.Infof("Starting %s...", d.MachineName)
	vmrun("start", d.vmxPath(), "nogui")

	// Do not execute the rest of boot2docker specific configuration, exit here
	if d.ConfigDriveURL != "" {
		log.Debugf("Leaving start sequence early, configdrive found")
		return nil
	}

	log.Debugf("Mounting Shared Folders...")
	var shareName, shareDir string // TODO configurable at some point
	switch runtime.GOOS {
	case "darwin":
		shareName = "Users"
		shareDir = "/Users"
		// TODO "linux" and "windows"
	}

	if shareDir != "" {
		if _, err := os.Stat(shareDir); err != nil && !os.IsNotExist(err) {
			return err
		} else if !os.IsNotExist(err) {
			// create mountpoint and mount shared folder
			vmrun("-gu", B2DUser, "-gp", B2DPass, "runScriptInGuest", d.vmxPath(), "/bin/sh", "sudo mkdir "+shareDir+" && sudo mount -t vmhgfs .host:/"+shareName+" "+shareDir)
		}
	}

	return nil
}
Пример #15
0
func (d *Driver) waitForSetupTransactions() {
	log.Infof("Waiting for host setup transactions to complete")
	// sometimes we'll hit a case where there's no active transaction, but if
	// we check again in a few seconds, it moves to the next transaction. We
	// don't want to get false-positives, so we check a few times in a row to make sure!
	noActiveCount, maxNoActiveCount := 0, 3
	for {
		t, err := d.GetActiveTransaction()
		if err != nil {
			noActiveCount = 0
			log.Debugf("Failed to GetActiveTransaction - %+v", err)
			continue
		}

		if t == "" {
			if noActiveCount == maxNoActiveCount {
				break
			}
			noActiveCount++
		} else {
			noActiveCount = 0
			log.Debugf("Still waiting - active transaction is %s...", t)
		}
		time.Sleep(2 * time.Second)
	}
}
Пример #16
0
func (d *Driver) getIp() (string, error) {
	log.Infof("Getting Host IP")
	for {
		var (
			ip  string
			err error
		)
		if d.deviceConfig.PrivateNet {
			ip, err = d.getClient().VirtualGuest().GetPrivateIp(d.Id)
		} else {
			ip, err = d.getClient().VirtualGuest().GetPublicIp(d.Id)
		}
		if err != nil {
			time.Sleep(2 * time.Second)
			continue
		}
		// not a perfect regex, but should be just fine for our needs
		exp := regexp.MustCompile(`\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`)
		if exp.MatchString(ip) {
			d.IPAddress = ip
			return ip, nil
		}
		time.Sleep(2 * time.Second)
	}
}
Пример #17
0
func (d *Driver) Start() error {
	s, err := d.GetState()
	if err != nil {
		return err
	}

	if s == state.Stopped {
		// check network to re-create if needed
		if err := d.setupHostOnlyNetwork(d.MachineName); err != nil {
			return fmt.Errorf("Error setting up host only network on machine start: %s", err)
		}
	}

	switch s {
	case state.Stopped, state.Saved:
		d.SSHPort, err = setPortForwarding(d.MachineName, 1, "ssh", "tcp", 22, d.SSHPort)
		if err != nil {
			return err
		}
		if err := vbm("startvm", d.MachineName, "--type", "headless"); err != nil {
			return err
		}
		log.Infof("Starting VM...")
	case state.Paused:
		if err := vbm("controlvm", d.MachineName, "resume", "--type", "headless"); err != nil {
			return err
		}
		log.Infof("Resuming VM ...")
	default:
		log.Infof("VM not in restartable state")
	}

	// Wait for SSH over NAT to be available before returning to user
	if err := drivers.WaitForSSH(d); err != nil {
		return err
	}

	// Bail if we don't get an IP from DHCP after a given number of seconds.
	if err := utils.WaitForSpecific(d.hostOnlyIpAvailable, 5, 4*time.Second); err != nil {
		return err
	}

	d.IPAddress, err = d.GetIP()

	return err
}
Пример #18
0
func (provisioner *RancherProvisioner) upgrade() error {
	switch provisioner.Driver.DriverName() {
	case "virtualbox":
		return provisioner.upgradeIso()
	default:
		log.Infof("Running upgrade")
		if _, err := provisioner.SSHCommand("sudo rancherctl os upgrade -f --no-reboot"); err != nil {
			return err
		}

		log.Infof("Upgrade succeeded, rebooting")
		// ignore errors here because the SSH connection will close
		provisioner.SSHCommand("sudo reboot")

		return nil
	}
}
Пример #19
0
func (b *B2dUtils) DownloadISOFromURL(latestReleaseUrl string) error {
	log.Infof("Downloading %s to %s...", latestReleaseUrl, b.commonIsoPath)
	if err := b.DownloadISO(b.imgCachePath, b.isoFilename, latestReleaseUrl); err != nil {
		return err
	}

	return nil
}
Пример #20
0
func cmdRegenerateCerts(c *cli.Context) {
	force := c.Bool("force")
	if force || confirmInput("Regenerate TLS machine certs?  Warning: this is irreversible.") {
		log.Infof("Regenerating TLS certificates")
		if err := runActionWithContext("configureAuth", c); err != nil {
			log.Fatal(err)
		}
	}
}
Пример #21
0
func (d *Driver) Remove() error {
	client := d.getClient()
	if resp, err := client.Keys.DeleteByID(d.SSHKeyID); err != nil {
		if resp.StatusCode == 404 {
			log.Infof("Digital Ocean SSH key doesn't exist, assuming it is already deleted")
		} else {
			return err
		}
	}
	if resp, err := client.Droplets.Delete(d.DropletID); err != nil {
		if resp.StatusCode == 404 {
			log.Infof("Digital Ocean droplet doesn't exist, assuming it is already deleted")
		} else {
			return err
		}
	}
	return nil
}
Пример #22
0
func (d *Driver) Stop() error {

	p, err := govcloudair.NewClient()
	if err != nil {
		return err
	}

	log.Infof("Connecting to vCloud Air...")
	// Authenticate to vCloud Air
	v, err := p.Authenticate(d.UserName, d.UserPassword, d.ComputeID, d.VDCID)
	if err != nil {
		return err
	}

	vapp, err := v.FindVAppByID(d.VAppID)
	if err != nil {
		return err
	}

	status, err := vapp.GetStatus()
	if err != nil {
		return err
	}

	if status == "POWERED_ON" {
		log.Infof("Shutting down %s...", d.MachineName)
		task, err := vapp.Shutdown()
		if err != nil {
			return err
		}
		if err = task.WaitTaskCompletion(); err != nil {
			return err
		}

	}

	if err = p.Disconnect(); err != nil {
		return err
	}

	d.IPAddress = ""

	return nil
}
Пример #23
0
func (d *Driver) waitTask(task *xsclient.Task, timeout time.Duration) error {
	timeout_at := time.Now().Add(timeout)

	for {
		status, err := task.GetStatus()
		if err != nil {
			return fmt.Errorf("Failed to get task status: %s", err.Error())
		}

		if status == xsclient.Success {
			log.Infof("Upload VDI task(%s) completed", task.Ref)
			break
		}

		switch status {
		case xsclient.Pending:
			progress, err := task.GetProgress()
			if err != nil {
				return fmt.Errorf("Failed to get progress: %s", err.Error())
			}
			log.Debugf("Upload %.0f%% complete", progress*100)
			log.Infof("Uploading...")
		case xsclient.Failure:
			errorInfo, err := task.GetErrorInfo()
			if err != nil {
				errorInfo = []string{fmt.Sprintf("furthermore, failed to get error info: %s", err.Error())}
			}
			return fmt.Errorf("Task failed: %s", errorInfo)
		case xsclient.Cancelling, xsclient.Cancelled:
			return fmt.Errorf("Task Cancelled")
		default:
			return fmt.Errorf("Unknown task status %v", status)
		}

		if time.Now().After(timeout_at) {
			return fmt.Errorf("Upload VDI task(%s) timed out", task.Ref)
		}

		time.Sleep(5 * time.Second)
	}

	return nil
}
Пример #24
0
// Create creates a GCE VM instance acting as a docker host.
func (d *Driver) Create() error {
	c, err := newComputeUtil(d)
	if err != nil {
		return err
	}
	log.Infof("Creating host...")
	// Check if the instance already exists. There will be an error if the instance
	// doesn't exist, so just check instance for nil.
	if instance, _ := c.instance(); instance != nil {
		return fmt.Errorf("Instance %v already exists.", d.MachineName)
	}

	log.Infof("Generating SSH Key")
	if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
		return err
	}

	return c.createInstance(d)
}
Пример #25
0
func (d *Driver) Start() error {
	log.Infof("Starting %s...", d.MachineName)
	vmrun("start", d.vmxPath(), "nogui")

	log.Debugf("Mounting Shared Folders...")
	if err := d.setupSharedDirs(); err != nil {
		return err
	}

	return nil
}
Пример #26
0
func (d *Driver) wait() error {
	log.Infof("Waiting for host to start...")
	for {
		ip, _ := d.GetIP()
		if ip != "" {
			break
		}
		time.Sleep(1 * time.Second)
	}
	return nil
}
Пример #27
0
func (d *Driver) generateDiskImage() error {
	// Create a small fixed vhd, put the tar in,
	// convert to dynamic, then resize

	d.diskImage = d.ResolveStorePath("disk.vhd")
	fixed := d.ResolveStorePath("fixed.vhd")
	log.Infof("Creating VHD")
	command := []string{
		"New-VHD",
		"-Path", fmt.Sprintf("'%s'", fixed),
		"-SizeBytes", "10MB",
		"-Fixed"}
	_, err := execute(command)
	if err != nil {
		return err
	}

	tarBuf, err := d.generateTar()
	if err != nil {
		return err
	}

	file, err := os.OpenFile(fixed, os.O_WRONLY, 0644)
	if err != nil {
		return err
	}
	defer file.Close()
	file.Seek(0, os.SEEK_SET)
	_, err = file.Write(tarBuf.Bytes())
	if err != nil {
		return err
	}
	file.Close()

	command = []string{
		"Convert-VHD",
		"-Path", fmt.Sprintf("'%s'", fixed),
		"-DestinationPath", fmt.Sprintf("'%s'", d.diskImage),
		"-VHDType", "Dynamic"}
	_, err = execute(command)
	if err != nil {
		return err
	}
	command = []string{
		"Resize-VHD",
		"-Path", fmt.Sprintf("'%s'", d.diskImage),
		"-SizeBytes", fmt.Sprintf("%dMB", d.diskSize)}
	_, err = execute(command)
	if err != nil {
		return err
	}

	return err
}
Пример #28
0
func (d *Driver) wait(timeout time.Duration) (err error) {
	log.Infof("Waiting for VM to start...")

	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	out := make(chan error, 1)
	go func(ctx context.Context, out chan<- error) {
		var ip string
		for {
			ip, _ = d.GetIP()
			if ip != "" {
				break
			}
			if t, ok := ctx.Deadline(); ok && time.Now().After(t) {
				out <- fmt.Errorf("Wait GetIP timed out")
				return
			}
			time.Sleep(1 * time.Second)
		}

		port, err := d.GetSSHPort()
		if err != nil {
			out <- err
			return
		}

		addr := fmt.Sprintf("%s:%d", ip, port)
		log.Infof("Got VM address(%v), Now waiting for SSH", addr)
		out <- ssh.WaitForTCP(addr)
	}(ctx, out)

	select {
	case err := <-out:
		return err
	case <-ctx.Done():
		return fmt.Errorf("Wait for VM to start timed out: %v", ctx.Err())
	}

	return nil
}
Пример #29
0
func (d *Driver) Remove() error {

	s, _ := d.GetState()
	if s == state.Running {
		if err := d.Kill(); err != nil {
			return fmt.Errorf("Error stopping VM before deletion")
		}
	}
	log.Infof("Deleting %s...", d.MachineName)
	vmrun("deleteVM", d.vmxPath(), "nogui")
	return nil
}
Пример #30
0
func (d *Driver) Remove() error {
	log.Infof("Canceling SoftLayer instance %d...", d.Id)
	var err error
	for i := 0; i < 5; i++ {
		if err = d.getClient().VirtualGuest().Cancel(d.Id); err != nil {
			time.Sleep(2 * time.Second)
			continue
		}
		break
	}
	if err != nil {
		return err
	}

	log.Infof("Removing SSH Key %d...", d.SSHKeyID)
	if err = d.getClient().SshKey().Delete(d.SSHKeyID); err != nil {
		return err
	}

	return nil
}