From f816f44273a6a0d17d1938aaa4313451fcde0e57 Mon Sep 17 00:00:00 2001 From: Aaron Feng Date: Sat, 26 Jul 2014 08:08:41 -0400 Subject: [PATCH] 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) --- backends/ec2.go | 107 ++++++++++++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 40 deletions(-) diff --git a/backends/ec2.go b/backends/ec2.go index 6f6ce33391..02576b943e 100644 --- a/backends/ec2.go +++ b/backends/ec2.go @@ -3,7 +3,6 @@ package backends import ( "errors" "fmt" - "github.com/docker/libswarm" "net" "net/http" "os" @@ -15,22 +14,25 @@ import ( "syscall" "time" + "github.com/docker/libswarm" "launchpad.net/goamz/aws" "launchpad.net/goamz/ec2" ) type ec2Config struct { - securityGroup string - instanceType string - zone string - ami string - tag string - sshUser string - sshKey string - sshLocalPort string - sshRemotePort string - keypair string - region aws.Region + securityGroupId string + instanceType string + zone string + ami string + tag string + sshUser string + sshKey string + sshPort string + sshLocalPort string + sshRemotePort string + keypair string + subnetId string + region aws.Region } type ec2Client struct { @@ -71,8 +73,9 @@ func (c *ec2Client) start(ctx *libswarm.Message) error { c.waitForSsh() c.startSshTunnel() c.waitForDockerDaemon() - fmt.Printf("ec2 service up and running: region: %s zone: %s\n", - c.config.region.Name, c.config.zone) + // TODO (aaron): display zone info + 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}) return nil @@ -122,25 +125,32 @@ func defaultSshKeyPath() string { 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) { config = new(ec2Config) config.region = aws.USEast - config.ami = "ami-7c807d14" - config.instanceType = "t1.micro" + config.instanceType = "t2.micro" config.sshUser = "ec2-user" - config.tag = "docker-ec2-swarm" + config.sshPort = "22" + config.tag = "docker-ec2-libswarm" config.sshLocalPort = "4910" config.sshRemotePort = "4243" config.sshKey = defaultSshKeyPath() 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) { var optValPair []string var opt, val string @@ -162,18 +172,23 @@ func newConfig(args []string) (config *ec2Config, err error) { config.ami = val case "--keypair": config.keypair = val - case "--security_group": - config.securityGroup = val + case "--security_group_id": + config.securityGroupId = val case "--instance_type": config.instanceType = val case "--ssh_user": config.sshUser = val + case "--ssh_port": + config.sshPort = val case "--ssh_key": config.sshKey = val + case "--subnet_id": + config.subnetId = val default: fmt.Printf("Unrecognizable option: %s value: %s", opt, val) } } + return config, nil } @@ -208,7 +223,20 @@ func awsInit(config *ec2Config) (ec2Conn *ec2.EC2, err error) { 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) { @@ -242,13 +270,14 @@ func (c *ec2Client) tagtInstance() error { } func (c *ec2Client) startInstance() error { + // TODO: allow more than one sg in the future options := ec2.RunInstances{ - ImageId: c.config.ami, - InstanceType: c.config.instanceType, - KeyName: c.config.keypair, - AvailZone: c.config.zone, - // TODO: allow more than one sg in the future - SecurityGroups: []ec2.SecurityGroup{ec2.SecurityGroup{Name: c.config.securityGroup}}, + SubnetId: c.config.subnetId, + ImageId: c.config.ami, + InstanceType: c.config.instanceType, + KeyName: c.config.keypair, + AvailZone: c.config.zone, + SecurityGroups: []ec2.SecurityGroup{ec2.SecurityGroup{Id: c.config.securityGroupId}}, UserData: []byte(userdata), } @@ -298,7 +327,7 @@ func (c *ec2Client) initDockerClientInstance(instance *ec2.Instance) error { } 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 { resp, _ := http.Get("http://localhost:" + c.config.sshLocalPort) // wait for a response. any response to know docker daemon is up @@ -312,8 +341,8 @@ func (c *ec2Client) waitForDockerDaemon() { } func (c *ec2Client) waitForSsh() { - fmt.Println("waiting for ssh to be available. make sure ssh is open on port 22.") - conn := waitFor(c.instance.IPAddress, "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, c.config.sshPort) conn.Close() } @@ -356,11 +385,13 @@ func Ec2() libswarm.Sender { } ec2Conn, err := awsInit(config) + if err != nil { return err } client := &ec2Client{config, ec2Conn, libswarm.NewServer(), nil, nil, nil} + client.Server.OnVerb(libswarm.Spawn, libswarm.Handler(client.spawn)) client.Server.OnVerb(libswarm.Start, libswarm.Handler(client.start)) 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.Get, libswarm.Handler(client.get)) - if config.zone == "" { - config.zone = client.defaultAvailabilityZones() - } - signalHandler(client) _, err = ctx.Ret.Send(&libswarm.Message{Verb: libswarm.Ack, Ret: client.Server}) @@ -405,7 +432,7 @@ func (c *ec2Client) startSshTunnel() error { "-o", "StrictHostKeyChecking=no", "-i", c.config.sshKey, "-A", - "-p", "22", + "-p", c.config.sshPort, fmt.Sprintf("%s@%s", c.config.sshUser, c.instance.IPAddress), "-N", "-f",