Skip to content

Commit

Permalink
iaas/ec2: support specifying the network interface for Docker nodes
Browse files Browse the repository at this point in the history
In order to properly test that, I needed to change a vendored package. I
definitely didn't enjoy doing so, I will send a pull request on
upstream, but since we're vendoring it, we don't need to wait for the pr
to get merged :-D
  • Loading branch information
fsouza committed Feb 28, 2016
1 parent b374fa6 commit b03ffc1
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 24 deletions.
9 changes: 6 additions & 3 deletions docs/releases/tsurud/1.0.0.rst
Expand Up @@ -90,14 +90,17 @@ General improvements

* EC2 IaaS is now feature complete, supporting parameter such as IAM roles,
extra volumes and multiple network interfaces. Since these parameters are
composed of multiple values, users must provide a JSON for using them. For
example, with IAM instance profiles and block devices:
composed of multiple values, users must provide a JSON for using them. It
also supports using private DNS names now, as long as the user specifies the
subnet-id and the index of the network interface that they want to use. For
example, with IAM instance profiles, block devices and running on a private
network:

.. highlight:: bash

::

% tsuru-admin docker-node-add iaas=ec2 iaminstanceprofile='{"name":"docker-instances"}' blockdevicemappings='[[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":100}}]' ...
% tsuru-admin docker-node-add iaas=ec2 'iaminstanceprofile={"name":"docker-instances"}' 'blockdevicemappings=[[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":100}}]' subnetid=subnet-1234 network-index=0 ...

* All long running API handlers for the Docker provisioner now use a keep-alive
to keep the connection open, properly handling low network timeouts (specially
Expand Down
35 changes: 20 additions & 15 deletions iaas/ec2/iaas.go
Expand Up @@ -61,36 +61,41 @@ func (i *EC2IaaS) createEC2Handler(regionOrEndpoint string) (*ec2.EC2, error) {
return ec2.New(session.New(&config)), nil
}

func (i *EC2IaaS) waitForDnsName(ec2Inst *ec2.EC2, instance *ec2.Instance) (*ec2.Instance, error) {
func (i *EC2IaaS) waitForDnsName(ec2Inst *ec2.EC2, instanceID string, createParams map[string]string) (string, error) {
rawWait, _ := i.base.GetConfigString("wait-timeout")
maxWaitTime, _ := strconv.Atoi(rawWait)
if maxWaitTime == 0 {
maxWaitTime = 300
}
q, err := queue.Queue()
if err != nil {
return nil, err
return "", err
}
taskName := fmt.Sprintf("ec2-wait-machine-%s", i.base.IaaSName)
waitDuration := time.Duration(maxWaitTime) * time.Second
job, err := q.EnqueueWait(taskName, monsterqueue.JobParams{
jobParams := monsterqueue.JobParams{
"region": ec2Inst.Config.Region,
"endpoint": ec2Inst.Config.Endpoint,
"machineId": *instance.InstanceId,
"machineId": instanceID,
"timeout": maxWaitTime,
}, waitDuration)
}
if rawInterfaceIdx, ok := createParams["network-index"]; ok {
if interfaceIdx, err := strconv.Atoi(rawInterfaceIdx); err == nil {
jobParams["networkIndex"] = interfaceIdx
}
}
waitDuration := time.Duration(maxWaitTime) * time.Second
job, err := q.EnqueueWait(taskName, jobParams, waitDuration)
if err != nil {
if err == monsterqueue.ErrQueueWaitTimeout {
return nil, fmt.Errorf("ec2: time out after %v waiting for instance %s to start", waitDuration, *instance.InstanceId)
return "", fmt.Errorf("ec2: time out after %v waiting for instance %s to start", waitDuration, instanceID)
}
return nil, err
return "", err
}
result, err := job.Result()
if err != nil {
return nil, err
return "", err
}
instance.PublicDnsName = aws.String(result.(string))
return instance, nil
return result.(string), nil
}

func (i *EC2IaaS) Initialize() error {
Expand Down Expand Up @@ -326,14 +331,14 @@ func (i *EC2IaaS) CreateMachine(params map[string]string) (*iaas.Machine, error)
}
}
}
instance, err := i.waitForDnsName(ec2Inst, runInst)
dnsName, err := i.waitForDnsName(ec2Inst, aws.StringValue(runInst.InstanceId), params)
if err != nil {
return nil, err
}
machine := iaas.Machine{
Id: *instance.InstanceId,
Status: *instance.State.Name,
Address: *instance.PublicDnsName,
Id: aws.StringValue(runInst.InstanceId),
Status: aws.StringValue(runInst.State.Name),
Address: dnsName,
}
return &machine, nil
}
Expand Down
39 changes: 37 additions & 2 deletions iaas/ec2/iaas_test.go
Expand Up @@ -34,6 +34,15 @@ func (s *S) SetUpTest(c *check.C) {
var err error
s.srv, err = ec2test.NewServer()
c.Assert(err, check.IsNil)
s.srv.SetAvailabilityZones([]ec2amz.AvailabilityZoneInfo{
{
AvailabilityZone: ec2amz.AvailabilityZone{
Name: "us-east-1b",
Region: "us-east-1",
},
},
})
s.srv.SetInitialAttributes(map[string][]string{"default-vpc": {"vpc-123"}})
config.Set("iaas:ec2:key-id", "mykey")
config.Set("iaas:ec2:secret-key", "mysecret")
config.Set("queue:mongo-url", "127.0.0.1:27017")
Expand Down Expand Up @@ -249,9 +258,35 @@ func (s *S) TestWaitForDnsName(c *check.C) {
c.Assert(err, check.IsNil)
instance := resp.Instances[0]
instance.PublicDnsName = aws.String("")
instance, err = ec2iaas.waitForDnsName(handler, instance)
dnsName, err := ec2iaas.waitForDnsName(handler, aws.StringValue(instance.InstanceId), nil)
c.Assert(err, check.IsNil)
c.Assert(dnsName, check.Matches, `i-\d.testing.invalid`)
}

func (s *S) TestWaitForDnsNamePrivateDNSName(c *check.C) {
myiaas := newEC2IaaS("ec2")
ec2iaas := myiaas.(*EC2IaaS)
err := ec2iaas.Initialize()
c.Assert(err, check.IsNil)
handler, err := ec2iaas.createEC2Handler(s.srv.URL())
c.Assert(err, check.IsNil)
options := ec2.RunInstancesInput{
ImageId: aws.String("ami-xxx"),
InstanceType: aws.String("m1.small"),
MinCount: aws.Int64(1),
MaxCount: aws.Int64(1),
SubnetId: aws.String("subnet-0"),
}
resp, err := handler.RunInstances(&options)
c.Assert(err, check.IsNil)
instance := resp.Instances[0]
instance.PublicDnsName = aws.String("")
params := map[string]string{
"network-index": "0",
}
dnsName, err := ec2iaas.waitForDnsName(handler, aws.StringValue(instance.InstanceId), params)
c.Assert(err, check.IsNil)
c.Assert(*instance.PublicDnsName, check.Matches, `i-\d.testing.invalid`)
c.Assert(dnsName, check.Matches, `i-\d.internal.invalid`)
}

func (s *S) TestCreateMachineValidations(c *check.C) {
Expand Down
21 changes: 18 additions & 3 deletions iaas/ec2/task.go
@@ -1,4 +1,4 @@
// Copyright 2015 tsuru authors. All rights reserved.
// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

Expand Down Expand Up @@ -37,6 +37,15 @@ func (t *ec2WaitTask) Run(job monsterqueue.Job) {
case float64:
timeout = int(val)
}
networkIdx := -1
if idx, ok := params["networkIndex"]; ok {
switch val := idx.(type) {
case int:
networkIdx = val
case float64:
networkIdx = int(val)
}
}
ec2Inst, err := t.iaas.createEC2Handler(regionOrEndpoint)
if err != nil {
job.Error(err)
Expand All @@ -60,8 +69,14 @@ func (t *ec2WaitTask) Run(job monsterqueue.Job) {
break
}
instance := resp.Reservations[0].Instances[0]
if instance.PublicDnsName != nil {
dnsName = *instance.PublicDnsName
if networkIdx < 0 {
dnsName = aws.StringValue(instance.PublicDnsName)
} else {
if len(instance.NetworkInterfaces) < networkIdx {
job.Error(errors.New("invalid network-index. "))
break
}
dnsName = aws.StringValue(instance.NetworkInterfaces[networkIdx].PrivateDnsName)
}
if dnsName != "" {
notifiedSuccess, _ = job.Success(dnsName)
Expand Down
2 changes: 1 addition & 1 deletion vendor/gopkg.in/amz.v2/ec2/ec2test/server.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit b03ffc1

Please sign in to comment.