Support VPC and various improvements.

* Support VPC
* Lookup available AZ instead hardcode to us-east-1a.
* Lookup correct AMI id by name instead of hardcoded ami id.
* Change security_group name to security_group_id .
* Allow custom SSH port.

Docker-DCO-1.1-Signed-off-by: Aaron Feng aaron.feng@gmail.com (github: aaronfeng)
This commit is contained in:
Aaron Feng 2014-07-26 08:08:41 -04:00
parent 73c18013c4
commit f816f44273
1 changed files with 67 additions and 40 deletions

View File

@ -3,7 +3,6 @@ package backends
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/docker/libswarm"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -15,21 +14,24 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/docker/libswarm"
"launchpad.net/goamz/aws" "launchpad.net/goamz/aws"
"launchpad.net/goamz/ec2" "launchpad.net/goamz/ec2"
) )
type ec2Config struct { type ec2Config struct {
securityGroup string securityGroupId string
instanceType string instanceType string
zone string zone string
ami string ami string
tag string tag string
sshUser string sshUser string
sshKey string sshKey string
sshPort string
sshLocalPort string sshLocalPort string
sshRemotePort string sshRemotePort string
keypair string keypair string
subnetId string
region aws.Region region aws.Region
} }
@ -71,8 +73,9 @@ func (c *ec2Client) start(ctx *libswarm.Message) error {
c.waitForSsh() c.waitForSsh()
c.startSshTunnel() c.startSshTunnel()
c.waitForDockerDaemon() c.waitForDockerDaemon()
fmt.Printf("ec2 service up and running: region: %s zone: %s\n", // TODO (aaron): display zone info
c.config.region.Name, c.config.zone) fmt.Printf("ec2 service up and running: region: %s\n",
c.config.region.Name)
ctx.Ret.Send(&libswarm.Message{Verb: libswarm.Ack, Ret: c.Server}) ctx.Ret.Send(&libswarm.Message{Verb: libswarm.Ack, Ret: c.Server})
return nil return nil
@ -122,25 +125,32 @@ func defaultSshKeyPath() string {
return path.Join(dir, ".ssh", "id_rsa") return path.Join(dir, ".ssh", "id_rsa")
} }
func (c *ec2Client) defaultAvailabilityZones() string {
filter := ec2.NewFilter()
resp, _ := c.ec2Conn.AvailabilityZones(filter)
return resp.Zones[0].Name
}
func defaultConfigValues() (config *ec2Config) { func defaultConfigValues() (config *ec2Config) {
config = new(ec2Config) config = new(ec2Config)
config.region = aws.USEast config.region = aws.USEast
config.ami = "ami-7c807d14" config.instanceType = "t2.micro"
config.instanceType = "t1.micro"
config.sshUser = "ec2-user" config.sshUser = "ec2-user"
config.tag = "docker-ec2-swarm" config.sshPort = "22"
config.tag = "docker-ec2-libswarm"
config.sshLocalPort = "4910" config.sshLocalPort = "4910"
config.sshRemotePort = "4243" config.sshRemotePort = "4243"
config.sshKey = defaultSshKeyPath() config.sshKey = defaultSshKeyPath()
return config return config
} }
func defaultAMI(conn *ec2.EC2) (string, error) {
filter := ec2.NewFilter()
imageName := "amzn-ami-hvm-2014.03.2.x86_64-ebs"
filter.Add("name", imageName)
resp, _ := conn.Images(nil, filter)
if len(resp.Images) == 0 {
return "", fmt.Errorf("Unable to retrieve ami id for image name: %s", imageName)
}
return resp.Images[0].Id, nil
}
func newConfig(args []string) (config *ec2Config, err error) { func newConfig(args []string) (config *ec2Config, err error) {
var optValPair []string var optValPair []string
var opt, val string var opt, val string
@ -162,18 +172,23 @@ func newConfig(args []string) (config *ec2Config, err error) {
config.ami = val config.ami = val
case "--keypair": case "--keypair":
config.keypair = val config.keypair = val
case "--security_group": case "--security_group_id":
config.securityGroup = val config.securityGroupId = val
case "--instance_type": case "--instance_type":
config.instanceType = val config.instanceType = val
case "--ssh_user": case "--ssh_user":
config.sshUser = val config.sshUser = val
case "--ssh_port":
config.sshPort = val
case "--ssh_key": case "--ssh_key":
config.sshKey = val config.sshKey = val
case "--subnet_id":
config.subnetId = val
default: default:
fmt.Printf("Unrecognizable option: %s value: %s", opt, val) fmt.Printf("Unrecognizable option: %s value: %s", opt, val)
} }
} }
return config, nil return config, nil
} }
@ -208,7 +223,20 @@ func awsInit(config *ec2Config) (ec2Conn *ec2.EC2, err error) {
return nil, err return nil, err
} }
return ec2.New(auth, config.region), nil conn := ec2.New(auth, config.region)
if config.ami == "" {
fmt.Println("No AMI specified. Retrieving default ami")
amiId, err := defaultAMI(conn)
if err != nil {
return nil, fmt.Errorf("Please manually specify an ami.")
}
config.ami = amiId
}
return conn, nil
} }
func (c *ec2Client) findInstance() (instance *ec2.Instance, err error) { func (c *ec2Client) findInstance() (instance *ec2.Instance, err error) {
@ -242,13 +270,14 @@ func (c *ec2Client) tagtInstance() error {
} }
func (c *ec2Client) startInstance() error { func (c *ec2Client) startInstance() error {
// TODO: allow more than one sg in the future
options := ec2.RunInstances{ options := ec2.RunInstances{
SubnetId: c.config.subnetId,
ImageId: c.config.ami, ImageId: c.config.ami,
InstanceType: c.config.instanceType, InstanceType: c.config.instanceType,
KeyName: c.config.keypair, KeyName: c.config.keypair,
AvailZone: c.config.zone, AvailZone: c.config.zone,
// TODO: allow more than one sg in the future SecurityGroups: []ec2.SecurityGroup{ec2.SecurityGroup{Id: c.config.securityGroupId}},
SecurityGroups: []ec2.SecurityGroup{ec2.SecurityGroup{Name: c.config.securityGroup}},
UserData: []byte(userdata), UserData: []byte(userdata),
} }
@ -298,7 +327,7 @@ func (c *ec2Client) initDockerClientInstance(instance *ec2.Instance) error {
} }
func (c *ec2Client) waitForDockerDaemon() { func (c *ec2Client) waitForDockerDaemon() {
fmt.Println("waiting for docker daemon on remote machine to be available.") fmt.Println("Waiting for docker daemon on remote machine to be available.")
for { for {
resp, _ := http.Get("http://localhost:" + c.config.sshLocalPort) resp, _ := http.Get("http://localhost:" + c.config.sshLocalPort)
// wait for a response. any response to know docker daemon is up // wait for a response. any response to know docker daemon is up
@ -312,8 +341,8 @@ func (c *ec2Client) waitForDockerDaemon() {
} }
func (c *ec2Client) waitForSsh() { func (c *ec2Client) waitForSsh() {
fmt.Println("waiting for ssh to be available. make sure ssh is open on port 22.") fmt.Printf("Waiting for ssh to be available. make sure ssh is open on port %s.\n", c.config.sshPort)
conn := waitFor(c.instance.IPAddress, "22") conn := waitFor(c.instance.IPAddress, c.config.sshPort)
conn.Close() conn.Close()
} }
@ -356,11 +385,13 @@ func Ec2() libswarm.Sender {
} }
ec2Conn, err := awsInit(config) ec2Conn, err := awsInit(config)
if err != nil { if err != nil {
return err return err
} }
client := &ec2Client{config, ec2Conn, libswarm.NewServer(), nil, nil, nil} client := &ec2Client{config, ec2Conn, libswarm.NewServer(), nil, nil, nil}
client.Server.OnVerb(libswarm.Spawn, libswarm.Handler(client.spawn)) client.Server.OnVerb(libswarm.Spawn, libswarm.Handler(client.spawn))
client.Server.OnVerb(libswarm.Start, libswarm.Handler(client.start)) client.Server.OnVerb(libswarm.Start, libswarm.Handler(client.start))
client.Server.OnVerb(libswarm.Stop, libswarm.Handler(client.stop)) client.Server.OnVerb(libswarm.Stop, libswarm.Handler(client.stop))
@ -368,10 +399,6 @@ func Ec2() libswarm.Sender {
client.Server.OnVerb(libswarm.Ls, libswarm.Handler(client.ls)) client.Server.OnVerb(libswarm.Ls, libswarm.Handler(client.ls))
client.Server.OnVerb(libswarm.Get, libswarm.Handler(client.get)) client.Server.OnVerb(libswarm.Get, libswarm.Handler(client.get))
if config.zone == "" {
config.zone = client.defaultAvailabilityZones()
}
signalHandler(client) signalHandler(client)
_, err = ctx.Ret.Send(&libswarm.Message{Verb: libswarm.Ack, Ret: client.Server}) _, err = ctx.Ret.Send(&libswarm.Message{Verb: libswarm.Ack, Ret: client.Server})
@ -405,7 +432,7 @@ func (c *ec2Client) startSshTunnel() error {
"-o", "StrictHostKeyChecking=no", "-o", "StrictHostKeyChecking=no",
"-i", c.config.sshKey, "-i", c.config.sshKey,
"-A", "-A",
"-p", "22", "-p", c.config.sshPort,
fmt.Sprintf("%s@%s", c.config.sshUser, c.instance.IPAddress), fmt.Sprintf("%s@%s", c.config.sshUser, c.instance.IPAddress),
"-N", "-N",
"-f", "-f",