exoscale: add support for CoreOS

The egoscale binding for exoscale was limiting the images that could be
selected by a user to Ubuntu one only. Enable the use of arbitrary
images. For example, the following images are now available:

 - centos-6.6
 - centos-7.1
 - coreos-stable-835
 - debian-7
 - debian-8
 - ubuntu-12.04
 - ubuntu-14.04
 - ubuntu-15.10

The default user for non-Ubuntu images are not "ubuntu". Let the user
choose the appropriate user ("core" for CoreOS and "debian" for Debian).

CoreOS user are likely to want the ability to provide custom user data
as it enables the configuration of the etcd cluster. We add an option to
provide custom user data file. Moreover, we remove some of the default
options as they are not supported in CoreOS and are useless on
Debian/Ubuntu:

 - Resizing of rootfs is already done on all images by default.
 - Docker Machine will set the hostname as part of the provisioning.

Signed-off-by: Vincent Bernat <Vincent.Bernat@exoscale.ch>
This commit is contained in:
Vincent Bernat 2016-03-21 16:16:04 +01:00
parent e536ed6738
commit 05aea83271
4 changed files with 54 additions and 27 deletions

2
Godeps/Godeps.json generated
View File

@ -293,7 +293,7 @@
}, },
{ {
"ImportPath": "github.com/pyr/egoscale/src/egoscale", "ImportPath": "github.com/pyr/egoscale/src/egoscale",
"Rev": "bbaa67324aeeacc90430c1fe0a9c620d3929512e" "Rev": "347f81398d2ea1f3eebf1cd27ee3183669e34819"
}, },
{ {
"ImportPath": "github.com/rackspace/gophercloud", "ImportPath": "github.com/rackspace/gophercloud",

View File

@ -25,7 +25,9 @@ Options:
- `--exoscale-disk-size`: Disk size for the host in GB (10, 50, 100, 200, 400). - `--exoscale-disk-size`: Disk size for the host in GB (10, 50, 100, 200, 400).
- `--exoscale-image`: Image template (eg. ubuntu-14.04, ubuntu-15.10). - `--exoscale-image`: Image template (eg. ubuntu-14.04, ubuntu-15.10).
- `--exoscale-security-group`: Security group. It will be created if it doesn't exist. - `--exoscale-security-group`: Security group. It will be created if it doesn't exist.
- `--exoscale-availability-zone`: exoscale availability zone. - `--exoscale-availability-zone`: Exoscale availability zone.
- `--exoscale-ssh-user`: SSH username, which must match the default SSH user for the used image.
- `--exoscale-userdata`: Path to file containing user data for cloud-init.
If a custom security group is provided, you need to ensure that you allow TCP ports 22 and 2376 in an ingress rule. Moreover, if you want to use Swarm, also add TCP port 3376. If a custom security group is provided, you need to ensure that you allow TCP ports 22 and 2376 in an ingress rule. Moreover, if you want to use Swarm, also add TCP port 3376.
@ -41,3 +43,5 @@ Environment variables and default values:
| `--exoscale-image` | `EXOSCALE_IMAGE` | `ubuntu-15.10` | | `--exoscale-image` | `EXOSCALE_IMAGE` | `ubuntu-15.10` |
| `--exoscale-security-group` | `EXOSCALE_SECURITY_GROUP` | `docker-machine` | | `--exoscale-security-group` | `EXOSCALE_SECURITY_GROUP` | `docker-machine` |
| `--exoscale-availability-zone` | `EXOSCALE_AVAILABILITY_ZONE` | `ch-gva-2` | | `--exoscale-availability-zone` | `EXOSCALE_AVAILABILITY_ZONE` | `ch-gva-2` |
| `--exoscale-ssh-user` | `EXOSCALE_SSH_USER` | `ubuntu` |
| `--exoscale-userdata` | `EXOSCALE_USERDATA` | - |

View File

@ -1,12 +1,11 @@
package exoscale package exoscale
import ( import (
"bytes"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net" "net"
"os"
"strings" "strings"
"text/template"
"time" "time"
"github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/drivers"
@ -29,6 +28,7 @@ type Driver struct {
AvailabilityZone string AvailabilityZone string
KeyPair string KeyPair string
PublicKey string PublicKey string
UserDataFile string
ID string `json:"Id"` ID string `json:"Id"`
} }
@ -37,6 +37,7 @@ const (
defaultDiskSize = 50 defaultDiskSize = 50
defaultImage = "ubuntu-15.10" defaultImage = "ubuntu-15.10"
defaultAvailabilityZone = "ch-gva-2" defaultAvailabilityZone = "ch-gva-2"
defaultSSHUser = "ubuntu"
) )
// GetCreateFlags registers the flags this driver adds to // GetCreateFlags registers the flags this driver adds to
@ -88,6 +89,17 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
Value: defaultAvailabilityZone, Value: defaultAvailabilityZone,
Usage: "exoscale availibility zone", Usage: "exoscale availibility zone",
}, },
mcnflag.StringFlag{
EnvVar: "EXOSCALE_SSH_USER",
Name: "exoscale-ssh-user",
Value: defaultSSHUser,
Usage: "Set the name of the ssh user",
},
mcnflag.StringFlag{
EnvVar: "EXOSCALE_USERDATA",
Name: "exoscale-userdata",
Usage: "path to file with cloud-init user-data",
},
} }
} }
@ -109,7 +121,11 @@ func (d *Driver) GetSSHHostname() (string, error) {
} }
func (d *Driver) GetSSHUsername() string { func (d *Driver) GetSSHUsername() string {
return "ubuntu" if d.SSHUser == "" {
d.SSHUser = defaultSSHUser
}
return d.SSHUser
} }
// DriverName returns the name of the driver // DriverName returns the name of the driver
@ -130,6 +146,8 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
} }
d.SecurityGroup = strings.Join(securityGroups, ",") d.SecurityGroup = strings.Join(securityGroups, ",")
d.AvailabilityZone = flags.String("exoscale-availability-zone") d.AvailabilityZone = flags.String("exoscale-availability-zone")
d.SSHUser = flags.String("exoscale-ssh-user")
d.UserDataFile = flags.String("exoscale-userdata")
d.SetSwarmConfigFromFlags(flags) d.SetSwarmConfigFromFlags(flags)
if d.URL == "" { if d.URL == "" {
@ -142,6 +160,16 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
return nil return nil
} }
func (d *Driver) PreCreateCheck() error {
if d.UserDataFile != "" {
if _, err := os.Stat(d.UserDataFile); os.IsNotExist(err) {
return fmt.Errorf("user-data file %s could not be found", d.UserDataFile)
}
}
return nil
}
func (d *Driver) GetURL() (string, error) { func (d *Driver) GetURL() (string, error) {
if err := drivers.MustBeRunning(d); err != nil { if err := drivers.MustBeRunning(d); err != nil {
return "", err return "", err
@ -226,6 +254,11 @@ func (d *Driver) createDefaultSecurityGroup(client *egoscale.Client, group strin
} }
func (d *Driver) Create() error { func (d *Driver) Create() error {
userdata, err := d.getCloudInit()
if err != nil {
return err
}
log.Infof("Querying exoscale for the requested parameters...") log.Infof("Querying exoscale for the requested parameters...")
client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey) client := egoscale.NewClient(d.URL, d.APIKey, d.APISecretKey)
topology, err := client.GetTopology() topology, err := client.GetTopology()
@ -291,11 +324,6 @@ func (d *Driver) Create() error {
d.KeyPair = keypairName d.KeyPair = keypairName
log.Infof("Spawn exoscale host...") log.Infof("Spawn exoscale host...")
userdata, err := d.getCloudInit()
if err != nil {
return err
}
log.Debugf("Using the following cloud-init file:") log.Debugf("Using the following cloud-init file:")
log.Debugf("%s", userdata) log.Debugf("%s", userdata)
@ -422,20 +450,15 @@ func (d *Driver) waitForVM(client *egoscale.Client, jobid string) (*egoscale.Dep
// Build a cloud-init user data string that will install and run // Build a cloud-init user data string that will install and run
// docker. // docker.
func (d *Driver) getCloudInit() (string, error) { func (d *Driver) getCloudInit() (string, error) {
const tpl = `#cloud-config if d.UserDataFile != "" {
manage_etc_hosts: true buf, err := ioutil.ReadFile(d.UserDataFile)
fqdn: {{ .MachineName }} if err != nil {
resize_rootfs: true return "", err
` }
var buffer bytes.Buffer return string(buf), nil
}
tmpl, err := template.New("cloud-init").Parse(tpl) return `#cloud-config
if err != nil { manage_etc_hosts: true
return "", err `, nil
}
err = tmpl.Execute(&buffer, d)
if err != nil {
return "", err
}
return buffer.String(), nil
} }

View File

@ -114,12 +114,12 @@ func (exo *Client) GetImages() (map[string]map[int]string, error) {
return nil, err return nil, err
} }
re := regexp.MustCompile(`^Linux (?P<name>Ubuntu|Debian) (?P<version>[0-9.]+).*$`) re := regexp.MustCompile(`^Linux (?P<name>.+?) (?P<version>[0-9.]+).*$`)
for _, template := range r.Templates { for _, template := range r.Templates {
size := int(template.Size / (1024 * 1024 * 1024)) size := int(template.Size / (1024 * 1024 * 1024))
submatch := re.FindStringSubmatch(template.Name) submatch := re.FindStringSubmatch(template.Name)
if len(submatch) > 0 { if len(submatch) > 0 {
name := strings.ToLower(submatch[1]) name := strings.Replace(strings.ToLower(submatch[1]), " ", "-", -1)
version := submatch[2] version := submatch[2]
image := fmt.Sprintf("%s-%s", name, version) image := fmt.Sprintf("%s-%s", name, version)