drivers/exoscale/exoscale.go
func (d *Driver) Create() error {
	cloudInit, err := d.getCloudInit()
	if err != nil {
		return err
	}

	log.Infof("Querying exoscale for the requested parameters...")
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)

	resp, err := client.RequestWithContext(context.TODO(), &egoscale.ListZones{
		Name: d.AvailabilityZone,
	})
	if err != nil {
		return err
	}

	zones := resp.(*egoscale.ListZonesResponse)
	if len(zones.Zone) != 1 {
		return fmt.Errorf("Availability zone %v doesn't exist",
			d.AvailabilityZone)
	}
	zone := zones.Zone[0].ID
	log.Debugf("Availability zone %v = %s", d.AvailabilityZone, zone)

	// Image
	template := egoscale.Template{
		IsFeatured: true,
		ZoneID:     "1", // GVA2
	}

	templates, err := client.ListWithContext(context.TODO(), &template)
	if err != nil {
		return err
	}

	image := strings.ToLower(d.Image)
	re := regexp.MustCompile(`^Linux (?P<name>.+?) (?P<version>[0-9.]+)\b`)

	for _, t := range templates {
		tpl := t.(*egoscale.Template)

		// Keep only 10GiB images
		if tpl.Size>>30 != 10 {
			continue
		}

		fullname := strings.ToLower(tpl.Name)
		if image == fullname {
			template = *tpl
			break
		}

		submatch := re.FindStringSubmatch(tpl.Name)
		if len(submatch) > 0 {
			name := strings.Replace(strings.ToLower(submatch[1]), " ", "-", -1)
			version := submatch[2]
			shortname := fmt.Sprintf("%s-%s", name, version)

			if image == shortname {
				template = *tpl
				break
			}
		}
	}
	if template.ID == "" {
		return fmt.Errorf("Unable to find image %v", d.Image)
	}

	// Reading the username from the template
	if name, ok := template.Details["username"]; ok {
		d.SSHUser = name
	}
	log.Debugf("Image %v(10) = %s (%s)", d.Image, template.ID, d.SSHUser)

	// Profile UUID
	resp, err = client.RequestWithContext(context.TODO(), &egoscale.ListServiceOfferings{
		Name: d.InstanceProfile,
	})
	if err != nil {
		return err
	}
	profiles := resp.(*egoscale.ListServiceOfferingsResponse)
	if len(profiles.ServiceOffering) != 1 {
		return fmt.Errorf("Unable to find the %s profile",
			d.InstanceProfile)
	}
	profile := profiles.ServiceOffering[0].ID
	log.Debugf("Profile %v = %s", d.InstanceProfile, profile)

	// Security groups
	sgs := make([]string, 0, len(d.SecurityGroups))
	for _, group := range d.SecurityGroups {
		if group == "" {
			continue
		}

		sg := &egoscale.SecurityGroup{Name: group}
		if err := client.Get(sg); err != nil {
			if _, ok := err.(*egoscale.ErrorResponse); !ok {
				return err
			}
			log.Infof("Security group %v does not exist. Creating it...", group)
			securityGroup, err := d.createDefaultSecurityGroup(group)
			if err != nil {
				return err
			}
			sg.ID = securityGroup.ID
		}

		log.Debugf("Security group %v = %s", group, sg.ID)
		sgs = append(sgs, sg.ID)
	}

	// Affinity Groups
	ags := make([]string, 0, len(d.AffinityGroups))
	for _, group := range d.AffinityGroups {
		if group == "" {
			continue
		}
		ag := &egoscale.AffinityGroup{Name: group}
		if err := client.Get(ag); err != nil {
			if _, ok := err.(*egoscale.ErrorResponse); !ok {
				return err
			}
			log.Infof("Affinity Group %v does not exist, create it", group)
			affinityGroup, err := d.createDefaultAffinityGroup(group)
			if err != nil {
				return err
			}
			ag.ID = affinityGroup.ID
		}
		log.Debugf("Affinity group %v = %s", group, ag.ID)
		ags = append(ags, ag.ID)
	}

	// SSH key pair
	if d.SSHKey == "" {
		var keyPairName string
		keyPairName = fmt.Sprintf("docker-machine-%s", d.MachineName)
		log.Infof("Generate an SSH keypair...")
		resp, err := client.RequestWithContext(context.TODO(), &egoscale.CreateSSHKeyPair{
			Name: keyPairName,
		})
		if err != nil {
			return fmt.Errorf("SSH Key pair creation failed %s", err)
		}
		keyPair := resp.(*egoscale.CreateSSHKeyPairResponse).KeyPair
		if err = os.MkdirAll(filepath.Dir(d.GetSSHKeyPath()), 0750); err != nil {
			return fmt.Errorf("Cannot create the folder to store the SSH private key. %s", err)
		}
		if err = ioutil.WriteFile(d.GetSSHKeyPath(), []byte(keyPair.PrivateKey), 0600); err != nil {
			return fmt.Errorf("SSH private key could not be written. %s", err)
		}
		d.KeyPair = keyPairName
	} else {
		log.Infof("Importing SSH key from %s", d.SSHKey)

		sshKey := d.SSHKey
		if strings.HasPrefix(sshKey, "~/") {
			usr, _ := user.Current()
			sshKey = filepath.Join(usr.HomeDir, sshKey[2:])
		} else {
			var err error
			if sshKey, err = filepath.Abs(sshKey); err != nil {
				return err
			}
		}

		// Sending the SSH public key through the cloud-init config
		pubKey, err := ioutil.ReadFile(sshKey + ".pub")
		if err != nil {
			return fmt.Errorf("Cannot read SSH public key %s", err)
		}

		sshAuthorizedKeys := `
ssh_authorized_keys:
- `
		cloudInit = bytes.Join([][]byte{cloudInit, []byte(sshAuthorizedKeys), pubKey}, []byte(""))

		// Copying the private key into docker-machine
		if err := mcnutils.CopyFile(sshKey, d.GetSSHKeyPath()); err != nil {
			return fmt.Errorf("Unable to copy SSH file: %s", err)
		}
		if err := os.Chmod(d.GetSSHKeyPath(), 0600); err != nil {
			return fmt.Errorf("Unable to set permissions on the SSH file: %s", err)
		}
	}

	log.Infof("Spawn exoscale host...")
	log.Debugf("Using the following cloud-init file:")
	log.Debugf("%s", string(cloudInit))

	// Base64 encode the userdata
	d.UserData = cloudInit
	encodedUserData := base64.StdEncoding.EncodeToString(d.UserData)

	req := &egoscale.DeployVirtualMachine{
		TemplateID:        template.ID,
		ServiceOfferingID: profile,
		UserData:          encodedUserData,
		ZoneID:            zone,
		Name:              d.MachineName,
		KeyPair:           d.KeyPair,
		DisplayName:       d.MachineName,
		RootDiskSize:      d.DiskSize,
		SecurityGroupIDs:  sgs,
		AffinityGroupIDs:  ags,
	}
	log.Infof("Deploy %#v", req)
	resp, err = client.RequestWithContext(context.TODO(), req)
	if err != nil {
		return err
	}

	vm := resp.(*egoscale.DeployVirtualMachineResponse).VirtualMachine

	IPAddress := vm.Nic[0].IPAddress
	if IPAddress != nil {
		d.IPAddress = IPAddress.String()
	}
	d.ID = vm.ID
	log.Infof("IP Address: %v, SSH User: %v", d.IPAddress, d.GetSSHUsername())

	if vm.PasswordEnabled {
		d.Password = vm.Password
	}

	// Destroy the SSH key from CloudStack
	if d.KeyPair != "" {
		if err := drivers.WaitForSSH(d); err != nil {
			return err
		}

		key := &egoscale.SSHKeyPair{
			Name: d.KeyPair,
		}
		if err := client.DeleteWithContext(context.TODO(), key); err != nil {
			return err
		}
		d.KeyPair = ""
	}

	return nil
}
func (d *Driver) Create() error {
	cloudInit, err := d.getCloudInit()
	if err != nil {
		return err
	}

	log.Infof("Querying exoscale for the requested parameters...")
	client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)

	resp, err := client.RequestWithContext(context.TODO(), &egoscale.ListZones{
		Name: d.AvailabilityZone,
	})
	if err != nil {
		return err
	}

	zones := resp.(*egoscale.ListZonesResponse)
	if len(zones.Zone) != 1 {
		return fmt.Errorf("Availability zone %v doesn't exist",
			d.AvailabilityZone)
	}
	zone := zones.Zone[0].ID
	log.Debugf("Availability zone %v = %s", d.AvailabilityZone, zone)

	// Image
	template := egoscale.Template{
		IsFeatured: true,
		ZoneID:     "1", // GVA2
	}

	templates, err := client.ListWithContext(context.TODO(), &template)
	if err != nil {
		return err
	}

	image := strings.ToLower(d.Image)
	re := regexp.MustCompile(`^Linux (?P<name>.+?) (?P<version>[0-9.]+)\b`)

	for _, t := range templates {
		tpl := t.(*egoscale.Template)

		// Keep only 10GiB images
		if tpl.Size>>30 != 10 {
			continue
		}

		fullname := strings.ToLower(tpl.Name)
		if image == fullname {
			template = *tpl
			break
		}

		submatch := re.FindStringSubmatch(tpl.Name)
		if len(submatch) > 0 {
			name := strings.Replace(strings.ToLower(submatch[1]), " ", "-", -1)
			version := submatch[2]
			shortname := fmt.Sprintf("%s-%s", name, version)

			if image == shortname {
				template = *tpl
				break
			}
		}
	}
	if template.ID == "" {
		return fmt.Errorf("Unable to find image %v", d.Image)
	}

	// Reading the username from the template
	if name, ok := template.Details["username"]; ok {
		d.SSHUser = name
	}
	log.Debugf("Image %v(10) = %s (%s)", d.Image, template.ID, d.SSHUser)

	// Profile UUID
	resp, err = client.RequestWithContext(context.TODO(), &egoscale.ListServiceOfferings{
		Name: d.InstanceProfile,
	})
	if err != nil {
		return err
	}
	profiles := resp.(*egoscale.ListServiceOfferingsResponse)
	if len(profiles.ServiceOffering) != 1 {
		return fmt.Errorf("Unable to find the %s profile",
			d.InstanceProfile)
	}
	profile := profiles.ServiceOffering[0].ID
	log.Debugf("Profile %v = %s", d.InstanceProfile, profile)

	// Security groups
	sgs := make([]string, 0, len(d.SecurityGroups))
	for _, group := range d.SecurityGroups {
		if group == "" {
			continue
		}

		sg := &egoscale.SecurityGroup{Name: group}
		if err := client.Get(sg); err != nil {
			if _, ok := err.(*egoscale.ErrorResponse); !ok {
				return err
			}
			log.Infof("Security group %v does not exist. Creating it...", group)
			securityGroup, err := d.createDefaultSecurityGroup(group)
			if err != nil {
				return err
			}
			sg.ID = securityGroup.ID
		}

		log.Debugf("Security group %v = %s", group, sg.ID)
		sgs = append(sgs, sg.ID)
	}

	// Affinity Groups
	ags := make([]string, 0, len(d.AffinityGroups))
	for _, group := range d.AffinityGroups {
		if group == "" {
			continue
		}
		ag := &egoscale.AffinityGroup{Name: group}
		if err := client.Get(ag); err != nil {
			if _, ok := err.(*egoscale.ErrorResponse); !ok {
				return err
			}
			log.Infof("Affinity Group %v does not exist, create it", group)
			affinityGroup, err := d.createDefaultAffinityGroup(group)
			if err != nil {
				return err
			}
			ag.ID = affinityGroup.ID
		}
		log.Debugf("Affinity group %v = %s", group, ag.ID)
		ags = append(ags, ag.ID)
	}

	// SSH key pair
	if d.SSHKey == "" {
		var keyPairName string
		keyPairName = fmt.Sprintf("docker-machine-%s", d.MachineName)
		log.Infof("Generate an SSH keypair...")
		resp, err := client.RequestWithContext(context.TODO(), &egoscale.CreateSSHKeyPair{
			Name: keyPairName,
		})
		if err != nil {
			return fmt.Errorf("SSH Key pair creation failed %s", err)
		}
		keyPair := resp.(*egoscale.CreateSSHKeyPairResponse).KeyPair
		if err = os.MkdirAll(filepath.Dir(d.GetSSHKeyPath()), 0750); err != nil {
			return fmt.Errorf("Cannot create the folder to store the SSH private key. %s", err)
		}
		if err = ioutil.WriteFile(d.GetSSHKeyPath(), []byte(keyPair.PrivateKey), 0600); err != nil {
			return fmt.Errorf("SSH private key could not be written. %s", err)
		}
		d.KeyPair = keyPairName
	} else {
		log.Infof("Importing SSH key from %s", d.SSHKey)

		sshKey := d.SSHKey
		if strings.HasPrefix(sshKey, "~/") {
			usr, _ := user.Current()
			sshKey = filepath.Join(usr.HomeDir, sshKey[2:])
		} else {
			var err error
			if sshKey, err = filepath.Abs(sshKey); err != nil {
				return err
			}
		}

		// Sending the SSH public key through the cloud-init config
		pubKey, err := ioutil.ReadFile(sshKey + ".pub")
		if err != nil {
			return fmt.Errorf("Cannot read SSH public key %s", err)
		}

		sshAuthorizedKeys := `
ssh_authorized_keys:
- `
		cloudInit = bytes.Join([][]byte{cloudInit, []byte(sshAuthorizedKeys), pubKey}, []byte(""))

		// Copying the private key into docker-machine
		if err := mcnutils.CopyFile(sshKey, d.GetSSHKeyPath()); err != nil {
			return fmt.Errorf("Unable to copy SSH file: %s", err)
		}
		if err := os.Chmod(d.GetSSHKeyPath(), 0600); err != nil {
			return fmt.Errorf("Unable to set permissions on the SSH file: %s", err)
		}
	}

	log.Infof("Spawn exoscale host...")
	log.Debugf("Using the following cloud-init file:")
	log.Debugf("%s", string(cloudInit))

	// Base64 encode the userdata
	d.UserData = cloudInit
	encodedUserData := base64.StdEncoding.EncodeToString(d.UserData)

	req := &egoscale.DeployVirtualMachine{
		TemplateID:        template.ID,
		ServiceOfferingID: profile,
		UserData:          encodedUserData,
		ZoneID:            zone,
		Name:              d.MachineName,
		KeyPair:           d.KeyPair,
		DisplayName:       d.MachineName,
		RootDiskSize:      d.DiskSize,
		SecurityGroupIDs:  sgs,
		AffinityGroupIDs:  ags,
	}
	log.Infof("Deploy %#v", req)
	resp, err = client.RequestWithContext(context.TODO(), req)
	if err != nil {
		return err
	}

	vm := resp.(*egoscale.DeployVirtualMachineResponse).VirtualMachine

	IPAddress := vm.Nic[0].IPAddress
	if IPAddress != nil {
		d.IPAddress = IPAddress.String()
	}
	d.ID = vm.ID
	log.Infof("IP Address: %v, SSH User: %v", d.IPAddress, d.GetSSHUsername())

	if vm.PasswordEnabled {
		d.Password = vm.Password
	}

	// Destroy the SSH key from CloudStack
	if d.KeyPair != "" {
		if err := drivers.WaitForSSH(d); err != nil {
			return err
		}

		key := &egoscale.SSHKeyPair{
			Name: d.KeyPair,
		}
		if err := client.DeleteWithContext(context.TODO(), key); err != nil {
			return err
		}
		d.KeyPair = ""
	}

	return nil
}
// Create creates the VM instance acting as the docker host