Merge lp:~dimitern/goamz/vpc-instance-addons into lp:goamz

Proposed by Dimiter Naydenov
Status: Merged
Merged at revision: 49
Proposed branch: lp:~dimitern/goamz/vpc-instance-addons
Merge into: lp:goamz
Prerequisite: lp:~dimitern/goamz/nic-api-calls
Diff against target: 869 lines (+503/-61)
6 files modified
ec2/ec2.go (+104/-26)
ec2/ec2_test.go (+81/-0)
ec2/ec2t_test.go (+187/-25)
ec2/ec2test/server.go (+56/-8)
ec2/networkinterfaces_test.go (+1/-1)
ec2/responses_test.go (+74/-1)
To merge this branch: bzr merge lp:~dimitern/goamz/vpc-instance-addons
Reviewer Review Type Date Requested Status
Dimiter Naydenov (community) Approve
Review via email: mp+205148@code.launchpad.net

Description of the change

ec2: Added NIC support for RunInstances

This extends RunInstances options to include
a list of NetworkInterfaceSpec options. They
allow specifying NICs to attach to instances
at launch time (either existing or new NICs).

It also adds SubnetId, VPCId, SourceDestCheck,
and NetworkInterfaces fields to the Instance
type, so Instances() and RunInstances() can
return the extended VPC-related information
for an instance.

If any NetworkInterfaceSpec options are provided,
or SubnetID is given, RunInstances() will use the
latest AWS API version (2013-10-15), otherwise it
uses the default version (2011-12-15).

Modified terminateInstances() test helper to wait
and retry when running against live EC2 servers,
to make sure the instances are really gone and
not left hanging after the test (leaking related
resources with them). In order for this to work
with the local testing server, a slight change
was made - when an instance is terminated, the
next time you fetch it with Instances(), it will
report "terminated" (thus simulating the real
state transition).

Added vpc-id and subnet-id filters to ec2test's
Instance and securityGroup types, and also changed
TestGroupFiltering and TestInstanceFiltering to
include tests for them.

Test double changed to support VPC security groups
and verify subnet ID in runInstances, as EC2 does.
Added NewInstancesVPC() method with vpcId and subnetId,
so VPC-enabled instances can be created in the test
server.

Added a TestRunInstancesVPC live test for the new
functionality (only live, because it's not worth
it to change the test double to create NICs in
runInstances()).

https://codereview.appspot.com/60620043/

To post a comment you must log in.
Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Reviewers: mp+205148_code.launchpad.net,

Message:
Please take a look.

Description:
ec2: Added NIC support for RunInstances

This extends RunInstances options to include
a list of NetworkInterfaceSpec options. They
allow specifying NICs to attach to instances
at launch time (either existing or new NICs).

It also adds SubnetId, VPCId, SourceDestCheck,
and NetworkInterfaces fields to the Instance
type, so Instances() and RunInstances() can
return the extended VPC-related information
for an instance.

If any NetworkInterfaceSpec options are provided,
RunInstances() will use the latest AWS API version
(2013-10-15), otherwise it uses the default version
(2011-12-15).

Modified terminateInstances() test helper to wait
and retry when running against live EC2 servers,
to make sure the instances are really gone and
not left hanging after the test (leaking related
resources with them). In order for this to work
with the local testing server, a slight change
was made - when an instance is terminated, the
next time you fetch it with Instances(), it will
report "terminated" (thus simulating the real
state transition).

Added vpc-id and subnet-id filters to ec2test's
Instance and securityGroup types, and also changed
TestGroupFiltering and TestInstanceFiltering to
include tests for them.

Test double changed to support VPC security groups
and verify subnet ID in runInstances, as EC2 does.
NewInstances() interface changed to include a
vpcId and subnetId (both optional), so VPC-enabled
instances can be created in the test server.

https://code.launchpad.net/~dimitern/goamz/vpc-instance-addons/+merge/205148

Requires:
https://code.launchpad.net/~dimitern/goamz/nic-api-calls/+merge/204912

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/60620043/

Affected files (+407, -59 lines):
   A [revision details]
   M ec2/ec2.go
   M ec2/ec2_test.go
   M ec2/ec2t_test.go
   M ec2/ec2test/server.go
   M ec2/networkinterfaces_test.go
   M ec2/responses_test.go

59. By Dimiter Naydenov

Removed test import; Added TestRunInstancesVPC live test

Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Revision history for this message
Roger Peppe (rogpeppe) wrote :

LGTM with some suggestions below.

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2.go#newcode299
ec2/ec2.go:299: // given, a VPC-enabled instances will be started.
Cannot specify
s/instances/instance/

But this whole paragraph is a bit hard to read.
Can't we just leave it out and let the user
get the gory details from the amz docs, like
most of the other params?

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2t_test.go
File ec2/ec2t_test.go (right):

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2t_test.go#newcode141
ec2/ec2t_test.go:141: c.Check(err, IsNil, Commentf("%d INSTANCES LEFT
RUNNING!!!", len(ids)))
Do we really need to fail the test if we can't terminate the instances
immediately?

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2t_test.go#newcode164
ec2/ec2t_test.go:164: idsLeft[inst.InstanceId] = false
if you did:

     delete(idsLeft, inst.InstanceId)

you wouldn't need the

     if left {

test in the range loop below.

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2t_test.go#newcode653
ec2/ec2t_test.go:653: // some of the reset depend on it, so they can't
be deleted before
s/the some of the reset/some of the rest/ ?

I don't understand how the comment applies to the statement
though - g[0] is first in the deleteGroups call, not g[3].

https://codereview.appspot.com/60620043/

60. By Dimiter Naydenov

Fixed a test

61. By Dimiter Naydenov

Changes after reivew

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Please take a look.

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2.go#newcode299
ec2/ec2.go:299: // given, a VPC-enabled instances will be started.
Cannot specify
On 2014/02/06 14:56:14, rog wrote:
> s/instances/instance/

> But this whole paragraph is a bit hard to read.
> Can't we just leave it out and let the user
> get the gory details from the amz docs, like
> most of the other params?

Done.

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2t_test.go
File ec2/ec2t_test.go (right):

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2t_test.go#newcode141
ec2/ec2t_test.go:141: c.Check(err, IsNil, Commentf("%d INSTANCES LEFT
RUNNING!!!", len(ids)))
On 2014/02/06 14:56:14, rog wrote:
> Do we really need to fail the test if we can't terminate the instances
> immediately?

Yes we do! We depend on TerminateInstances being successful. If it
returns an error there's no point in the loop below. I even changed it
to Assert, so this more obvious.

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2t_test.go#newcode164
ec2/ec2t_test.go:164: idsLeft[inst.InstanceId] = false
On 2014/02/06 14:56:14, rog wrote:
> if you did:

> delete(idsLeft, inst.InstanceId)

> you wouldn't need the

> if left {

> test in the range loop below.

Done.

https://codereview.appspot.com/60620043/diff/20001/ec2/ec2t_test.go#newcode653
ec2/ec2t_test.go:653: // some of the reset depend on it, so they can't
be deleted before
On 2014/02/06 14:56:14, rog wrote:
> s/the some of the reset/some of the rest/ ?

> I don't understand how the comment applies to the statement
> though - g[0] is first in the deleteGroups call, not g[3].

By "first" I meant "first of the set g[3], g[1], g[2]". I'll rephrase.

https://codereview.appspot.com/60620043/

62. By Dimiter Naydenov

Merged nic-api-calls into vpc-instance-addons.

63. By Dimiter Naydenov

Merged nic-api-calls into vpc-instance-addons.

64. By Dimiter Naydenov

Merged nic-api-calls into vpc-instance-addons.

Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

LGTM, but please ask for at least one more review.

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go#newcode232
ec2/ec2.go:232: SecondaryPrivateIPsCount int
s/IPsCount/IPCount/, as per the previous point?

https://codereview.appspot.com/60620043/

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go#newcode204
ec2/ec2.go:204: // NetworkInterfaceSpec encapsulates options for a
single network
Ah, and as we discussed, this type should be something along the lines
of

     RunNetworkInterface

It's an unfortunate name, but I cannot come up with anything better that
is not absurdly long.

For the record, the generic name "NetworkInterfaceSpec" sounds like a
problem because this is actually a set of options that only really make
sense in the specific context of RunInstances. We already have another
bag of data that only really makes sense in the context of
CreateNetworkInterface. If we need one more, will we name it
NetworkInterface..Data?..Stuff?

https://codereview.appspot.com/60620043/

Revision history for this message
Nate Finch (natefinch) wrote :

LGTM, just a couple nice-to-have changes

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go#newcode305
ec2/ec2.go:305: if options.SubnetId != "" ||
len(options.NetworkInterfaces) > 0 {
I'd like to have this extracted into a standalone function so it can be
tested by itself.

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go#newcode362
ec2/ec2.go:362: for i, ni := range options.NetworkInterfaces {
I'd really prefer if the internals to this and the loop above were
factored out into separate functions. I know you didn't write the loop
above, but it would cut down on the size of this already pretty large
function.

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2t_test.go
File ec2/ec2t_test.go (right):

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2t_test.go#newcode141
ec2/ec2t_test.go:141: c.Assert(err, IsNil, Commentf("%d INSTANCES LEFT
RUNNING!!!", ids))
either len(ids) or %v here, right?

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2t_test.go#newcode146
ec2/ec2t_test.go:146: Total: 10 * time.Minute,
do we really want tests that might wait 10 minutes?

https://codereview.appspot.com/60620043/

Revision history for this message
Dimiter Naydenov (dimitern) wrote :
Download full text (4.5 KiB)

*** Submitted:

ec2: Added NIC support for RunInstances

This extends RunInstances options to include
a list of NetworkInterfaceSpec options. They
allow specifying NICs to attach to instances
at launch time (either existing or new NICs).

It also adds SubnetId, VPCId, SourceDestCheck,
and NetworkInterfaces fields to the Instance
type, so Instances() and RunInstances() can
return the extended VPC-related information
for an instance.

If any NetworkInterfaceSpec options are provided,
or SubnetID is given, RunInstances() will use the
latest AWS API version (2013-10-15), otherwise it
uses the default version (2011-12-15).

Modified terminateInstances() test helper to wait
and retry when running against live EC2 servers,
to make sure the instances are really gone and
not left hanging after the test (leaking related
resources with them). In order for this to work
with the local testing server, a slight change
was made - when an instance is terminated, the
next time you fetch it with Instances(), it will
report "terminated" (thus simulating the real
state transition).

Added vpc-id and subnet-id filters to ec2test's
Instance and securityGroup types, and also changed
TestGroupFiltering and TestInstanceFiltering to
include tests for them.

Test double changed to support VPC security groups
and verify subnet ID in runInstances, as EC2 does.
Added NewInstancesVPC() method with vpcId and subnetId,
so VPC-enabled instances can be created in the test
server.

Added a TestRunInstancesVPC live test for the new
functionality (only live, because it's not worth
it to change the test double to create NICs in
runInstances()).

R=rog, niemeyer, nate.finch
CC=
https://codereview.appspot.com/60620043

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go
File ec2/ec2.go (right):

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go#newcode204
ec2/ec2.go:204: // NetworkInterfaceSpec encapsulates options for a
single network
On 2014/02/12 14:52:17, niemeyer wrote:
> Ah, and as we discussed, this type should be something along the lines
of

> RunNetworkInterface

> It's an unfortunate name, but I cannot come up with anything better
that is not
> absurdly long.

> For the record, the generic name "NetworkInterfaceSpec" sounds like a
problem
> because this is actually a set of options that only really make sense
in the
> specific context of RunInstances. We already have another bag of data
that only
> really makes sense in the context of CreateNetworkInterface. If we
need one
> more, will we name it NetworkInterface..Data?..Stuff?

Done.

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go#newcode232
ec2/ec2.go:232: SecondaryPrivateIPsCount int
On 2014/02/12 14:49:53, niemeyer wrote:
> s/IPsCount/IPCount/, as per the previous point?

Done.

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go#newcode305
ec2/ec2.go:305: if options.SubnetId != "" ||
len(options.NetworkInterfaces) > 0 {
On 2014/02/12 16:26:29, nate.finch wrote:
> I'd like to have this extracted into a standalone function so it can
be tested
> by itself.

Extracted and added a test.

https://codereview.appspot.com/60620043/diff/60001/ec2/ec2.go#newcode362
ec2/ec2.g...

Read more...

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

Post-submit approval.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ec2/ec2.go'
2--- ec2/ec2.go 2014-02-07 12:59:16 +0000
3+++ ec2/ec2.go 2014-02-07 12:59:16 +0000
4@@ -201,6 +201,37 @@
5 // ----------------------------------------------------------------------------
6 // Instance management functions and types.
7
8+// NetworkInterfaceSpec encapsulates options for a single network
9+// interface, specified when calling RunInstances.
10+//
11+// If Id is set, it must match an existing VPC network interface, and
12+// in this case only a single instance can be launched. If Id is not
13+// set, a new network interface will be created for each instance.
14+//
15+// The following fields are required when creating a new network
16+// interface (i.e. Id is empty): DeviceIndex, SubnetId, Description
17+// (only used if set), SecurityGroupIds.
18+//
19+// PrivateIPs can be used to add one or more private IP addresses to a
20+// network interface. Only one of the IP addresses can be set as
21+// primary. If none are given, EC2 selects a primary IP for each
22+// created interface from the subnet pool.
23+//
24+// When SecondaryPrivateIPsCount is non-zero, EC2 allocates that
25+// number of IP addresses from within the subnet range and sets them
26+// as secondary IPs. The number of IP addresses you can assign to a
27+// network interface varies by instance type.
28+type NetworkInterfaceSpec struct {
29+ Id string
30+ DeviceIndex int
31+ SubnetId string
32+ Description string
33+ PrivateIPs []PrivateIP
34+ SecurityGroupIds []string
35+ DeleteOnTermination bool
36+ SecondaryPrivateIPsCount int
37+}
38+
39 // The RunInstances type encapsulates options for the respective request in EC2.
40 //
41 // See http://goo.gl/Mcm3b for more details.
42@@ -222,6 +253,7 @@
43 ShutdownBehavior string
44 PrivateIPAddress string
45 BlockDeviceMappings []BlockDeviceMapping
46+ NetworkInterfaces []NetworkInterfaceSpec
47 }
48
49 // Response to a RunInstances request.
50@@ -239,33 +271,44 @@
51 //
52 // See http://goo.gl/OCH8a for more details.
53 type Instance struct {
54- InstanceId string `xml:"instanceId"`
55- InstanceType string `xml:"instanceType"`
56- ImageId string `xml:"imageId"`
57- PrivateDNSName string `xml:"privateDnsName"`
58- DNSName string `xml:"dnsName"`
59- IPAddress string `xml:"ipAddress"`
60- PrivateIPAddress string `xml:"privateIpAddress"`
61- KeyName string `xml:"keyName"`
62- AMILaunchIndex int `xml:"amiLaunchIndex"`
63- Hypervisor string `xml:"hypervisor"`
64- VirtType string `xml:"virtualizationType"`
65- Monitoring string `xml:"monitoring>state"`
66- AvailZone string `xml:"placement>availabilityZone"`
67- PlacementGroupName string `xml:"placement>groupName"`
68- State InstanceState `xml:"instanceState"`
69- Tags []Tag `xml:"tagSet>item"`
70- SecurityGroups []SecurityGroup `xml:"groupSet>item"`
71+ InstanceId string `xml:"instanceId"`
72+ InstanceType string `xml:"instanceType"`
73+ ImageId string `xml:"imageId"`
74+ PrivateDNSName string `xml:"privateDnsName"`
75+ DNSName string `xml:"dnsName"`
76+ IPAddress string `xml:"ipAddress"`
77+ PrivateIPAddress string `xml:"privateIpAddress"`
78+ SubnetId string `xml:"subnetId"`
79+ VPCId string `xml:"vpcId"`
80+ SourceDestCheck bool `xml:"sourceDestCheck"`
81+ KeyName string `xml:"keyName"`
82+ AMILaunchIndex int `xml:"amiLaunchIndex"`
83+ Hypervisor string `xml:"hypervisor"`
84+ VirtType string `xml:"virtualizationType"`
85+ Monitoring string `xml:"monitoring>state"`
86+ AvailZone string `xml:"placement>availabilityZone"`
87+ PlacementGroupName string `xml:"placement>groupName"`
88+ State InstanceState `xml:"instanceState"`
89+ Tags []Tag `xml:"tagSet>item"`
90+ SecurityGroups []SecurityGroup `xml:"groupSet>item"`
91+ NetworkInterfaces []NetworkInterface `xml:"networkInterfaceSet>item"`
92 }
93
94 // RunInstances starts new instances in EC2.
95 // If options.MinCount and options.MaxCount are both zero, a single instance
96 // will be started; otherwise if options.MaxCount is zero, options.MinCount
97-// will be used insteead.
98+// will be used instead.
99 //
100 // See http://goo.gl/Mcm3b for more details.
101 func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err error) {
102- params := makeParams("RunInstances")
103+ var params map[string]string
104+ if options.SubnetId != "" || len(options.NetworkInterfaces) > 0 {
105+ // When either SubnetId or NetworkInterfaces are specified, we
106+ // need to use the API version with complete VPC support.
107+ params = makeParamsVPC("RunInstances")
108+ } else {
109+ params = makeParams("RunInstances")
110+ }
111 params["ImageId"] = options.ImageId
112 params["InstanceType"] = options.InstanceType
113 var min, max int
114@@ -293,26 +336,61 @@
115 }
116 for i, b := range options.BlockDeviceMappings {
117 n := strconv.Itoa(i + 1)
118+ prefix := "BlockDeviceMapping." + n
119 if b.DeviceName != "" {
120- params["BlockDeviceMapping."+n+".DeviceName"] = b.DeviceName
121+ params[prefix+".DeviceName"] = b.DeviceName
122 }
123 if b.VirtualName != "" {
124- params["BlockDeviceMapping."+n+".VirtualName"] = b.VirtualName
125+ params[prefix+".VirtualName"] = b.VirtualName
126 }
127 if b.SnapshotId != "" {
128- params["BlockDeviceMapping."+n+".Ebs.SnapshotId"] = b.SnapshotId
129+ params[prefix+".Ebs.SnapshotId"] = b.SnapshotId
130 }
131 if b.VolumeType != "" {
132- params["BlockDeviceMapping."+n+".Ebs.VolumeType"] = b.VolumeType
133+ params[prefix+".Ebs.VolumeType"] = b.VolumeType
134 }
135 if b.VolumeSize > 0 {
136- params["BlockDeviceMapping."+n+".Ebs.VolumeSize"] = strconv.FormatInt(b.VolumeSize, 10)
137+ params[prefix+".Ebs.VolumeSize"] = strconv.FormatInt(b.VolumeSize, 10)
138 }
139 if b.IOPS > 0 {
140- params["BlockDeviceMapping."+n+".Ebs.Iops"] = strconv.FormatInt(b.IOPS, 10)
141+ params[prefix+".Ebs.Iops"] = strconv.FormatInt(b.IOPS, 10)
142 }
143 if b.DeleteOnTermination {
144- params["BlockDeviceMapping."+n+".Ebs.DeleteOnTermination"] = "true"
145+ params[prefix+".Ebs.DeleteOnTermination"] = "true"
146+ }
147+ }
148+ for i, ni := range options.NetworkInterfaces {
149+ // Unlike other lists, NetworkInterface and PrivateIpAddresses
150+ // should start from 0, not 1, according to the examples
151+ // requests in the API documentation here http://goo.gl/Mcm3b.
152+ n := strconv.Itoa(i)
153+ prefix := "NetworkInterface." + n
154+ if ni.Id != "" {
155+ params[prefix+".NetworkInterfaceId"] = ni.Id
156+ }
157+ params[prefix+".DeviceIndex"] = strconv.Itoa(ni.DeviceIndex)
158+ if ni.SubnetId != "" {
159+ params[prefix+".SubnetId"] = ni.SubnetId
160+ }
161+ if ni.Description != "" {
162+ params[prefix+".Description"] = ni.Description
163+ }
164+ for j, gid := range ni.SecurityGroupIds {
165+ k := strconv.Itoa(j + 1)
166+ params[prefix+".SecurityGroupId."+k] = gid
167+ }
168+ if ni.DeleteOnTermination {
169+ params[prefix+".DeleteOnTermination"] = "true"
170+ }
171+ if ni.SecondaryPrivateIPsCount > 0 {
172+ val := strconv.Itoa(ni.SecondaryPrivateIPsCount)
173+ params[prefix+".SecondaryPrivateIpAddressCount"] = val
174+ }
175+ for j, ip := range ni.PrivateIPs {
176+ k := strconv.Itoa(j)
177+ subprefix := prefix + ".PrivateIpAddresses." + k
178+ params[subprefix+".PrivateIpAddress"] = ip.Address
179+ params[subprefix+".Primary"] = strconv.FormatBool(ip.IsPrimary)
180 }
181 }
182 token, err := clientToken()
183
184=== modified file 'ec2/ec2_test.go'
185--- ec2/ec2_test.go 2014-02-07 12:59:16 +0000
186+++ ec2/ec2_test.go 2014-02-07 12:59:16 +0000
187@@ -105,6 +105,27 @@
188 DeleteOnTermination: true,
189 IOPS: 1000,
190 }},
191+ NetworkInterfaces: []ec2.NetworkInterfaceSpec{{
192+ DeviceIndex: 0,
193+ SubnetId: "subnet-id",
194+ Description: "eth0",
195+ PrivateIPs: []ec2.PrivateIP{
196+ {Address: "10.0.0.25", IsPrimary: true},
197+ },
198+ DeleteOnTermination: true,
199+ SecurityGroupIds: []string{"sg-1", "sg-2"},
200+ SecondaryPrivateIPsCount: 2,
201+ }, {
202+ Id: "eni-id",
203+ DeviceIndex: 1,
204+ PrivateIPs: []ec2.PrivateIP{{
205+ Address: "10.0.1.10",
206+ IsPrimary: true,
207+ }, {
208+ Address: "10.0.1.20",
209+ IsPrimary: false,
210+ }},
211+ }},
212 }
213 resp, err := s.ec2.RunInstances(&options)
214
215@@ -136,6 +157,19 @@
216 c.Assert(req.Form["BlockDeviceMapping.1.Ebs.VolumeSize"], DeepEquals, []string{"10"})
217 c.Assert(req.Form["BlockDeviceMapping.1.Ebs.Iops"], DeepEquals, []string{"1000"})
218 c.Assert(req.Form["BlockDeviceMapping.1.Ebs.DeleteOnTermination"], DeepEquals, []string{"true"})
219+ c.Assert(req.Form["NetworkInterface.0.DeviceIndex"], DeepEquals, []string{"0"})
220+ c.Assert(req.Form["NetworkInterface.0.SubnetId"], DeepEquals, []string{"subnet-id"})
221+ c.Assert(req.Form["NetworkInterface.0.Description"], DeepEquals, []string{"eth0"})
222+ c.Assert(req.Form["NetworkInterface.0.SecurityGroupId.1"], DeepEquals, []string{"sg-1"})
223+ c.Assert(req.Form["NetworkInterface.0.SecurityGroupId.2"], DeepEquals, []string{"sg-2"})
224+ c.Assert(req.Form["NetworkInterface.0.DeleteOnTermination"], DeepEquals, []string{"true"})
225+ c.Assert(req.Form["NetworkInterface.0.SecondaryPrivateIpAddressCount"], DeepEquals, []string{"2"})
226+ c.Assert(req.Form["NetworkInterface.1.NetworkInterfaceId"], DeepEquals, []string{"eni-id"})
227+ c.Assert(req.Form["NetworkInterface.1.DeviceIndex"], DeepEquals, []string{"1"})
228+ c.Assert(req.Form["NetworkInterface.1.PrivateIpAddresses.0.PrivateIpAddress"], DeepEquals, []string{"10.0.1.10"})
229+ c.Assert(req.Form["NetworkInterface.1.PrivateIpAddresses.0.Primary"], DeepEquals, []string{"true"})
230+ c.Assert(req.Form["NetworkInterface.1.PrivateIpAddresses.1.PrivateIpAddress"], DeepEquals, []string{"10.0.1.20"})
231+ c.Assert(req.Form["NetworkInterface.1.PrivateIpAddresses.1.Primary"], DeepEquals, []string{"false"})
232
233 c.Assert(err, IsNil)
234 c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE")
235@@ -153,6 +187,53 @@
236 c.Assert(i0.AMILaunchIndex, Equals, 0)
237 c.Assert(i0.VirtType, Equals, "paravirtual")
238 c.Assert(i0.Hypervisor, Equals, "xen")
239+ c.Assert(i0.SubnetId, Equals, "subnet-id")
240+ c.Assert(i0.VPCId, Equals, "vpc-id")
241+ c.Assert(i0.NetworkInterfaces, HasLen, 2)
242+ c.Assert(i0.NetworkInterfaces, DeepEquals, []ec2.NetworkInterface{{
243+ Id: "eni-c6bb50ae",
244+ SubnetId: "subnet-id",
245+ VPCId: "vpc-id",
246+ Description: "eth0",
247+ SourceDestCheck: true,
248+ OwnerId: "111122223333",
249+ Status: "in-use",
250+ Groups: []ec2.SecurityGroup{
251+ {Name: "vpc sg-1", Id: "sg-1"},
252+ {Name: "vpc sg-2", Id: "sg-2"},
253+ },
254+ MACAddress: "11:22:33:44:55:66",
255+ PrivateIPAddress: "10.0.0.25",
256+ PrivateIPs: []ec2.PrivateIP{{Address: "10.0.0.25", IsPrimary: true}},
257+ Attachment: ec2.NetworkInterfaceAttachment{
258+ Id: "eni-attach-0326646a",
259+ DeviceIndex: 0,
260+ Status: "attaching",
261+ AttachTime: "2011-12-20T08:29:31.000Z",
262+ DeleteOnTermination: true,
263+ },
264+ }, {
265+ Id: "eni-id",
266+ SubnetId: "subnet-id",
267+ VPCId: "vpc-id",
268+ SourceDestCheck: true,
269+ OwnerId: "111122223333",
270+ Status: "in-use",
271+ Groups: []ec2.SecurityGroup{{Name: "vpc default", Id: "sg-id"}},
272+ MACAddress: "11:22:33:44:55:66",
273+ PrivateIPAddress: "10.0.1.10",
274+ PrivateIPs: []ec2.PrivateIP{
275+ {Address: "10.0.1.10", IsPrimary: true},
276+ {Address: "10.0.1.20", IsPrimary: false},
277+ },
278+ Attachment: ec2.NetworkInterfaceAttachment{
279+ Id: "eni-attach-id",
280+ DeviceIndex: 1,
281+ Status: "attaching",
282+ AttachTime: "2011-12-20T08:29:31.000Z",
283+ DeleteOnTermination: false,
284+ },
285+ }})
286
287 i1 := resp.Instances[1]
288 c.Assert(i1.InstanceId, Equals, "i-2bc64242")
289
290=== modified file 'ec2/ec2t_test.go'
291--- ec2/ec2t_test.go 2014-02-07 12:59:16 +0000
292+++ ec2/ec2t_test.go 2014-02-07 12:59:16 +0000
293@@ -136,15 +136,43 @@
294 ec2 *ec2.EC2
295 }
296
297-func terminateInstances(c *C, e *ec2.EC2, insts []*ec2.Instance) {
298- var ids []string
299- for _, inst := range insts {
300- if inst != nil {
301- ids = append(ids, inst.InstanceId)
302- }
303- }
304+func terminateInstances(c *C, e *ec2.EC2, ids []string) {
305 _, err := e.TerminateInstances(ids)
306- c.Check(err, IsNil, Commentf("%d INSTANCES LEFT RUNNING!!!", len(ids)))
307+ c.Assert(err, IsNil, Commentf("%d INSTANCES LEFT RUNNING!!!", ids))
308+ // We need to wait until the instances are really off, because
309+ // entities that depend on them won't be deleted (i.e. groups,
310+ // NICs, subnets, etc.)
311+ testAttempt := aws.AttemptStrategy{
312+ Total: 10 * time.Minute,
313+ Delay: 5 * time.Second,
314+ }
315+ f := ec2.NewFilter()
316+ f.Add("instance-state-name", "terminated")
317+ idsLeft := make(map[string]bool)
318+ for _, id := range ids {
319+ idsLeft[id] = true
320+ }
321+ for a := testAttempt.Start(); a.Next(); {
322+ c.Logf("waiting for %v to get terminated", ids)
323+ resp, err := e.Instances(ids, f)
324+ if err != nil {
325+ c.Fatalf("not waiting for %v to terminate: %v", ids, err)
326+ }
327+ for _, r := range resp.Reservations {
328+ for _, inst := range r.Instances {
329+ delete(idsLeft, inst.InstanceId)
330+ }
331+ }
332+ ids = []string{}
333+ for id, _ := range idsLeft {
334+ ids = append(ids, id)
335+ }
336+ if len(ids) == 0 {
337+ c.Logf("all instances terminated.")
338+ return
339+ }
340+ }
341+ c.Fatalf("%v INSTANCES LEFT RUNNING!!!", ids)
342 }
343
344 func (s *ServerTests) makeTestGroup(c *C, name, descr string) ec2.SecurityGroup {
345@@ -313,11 +341,27 @@
346 }
347
348 func (s *ServerTests) TestInstanceFiltering(c *C) {
349- groupResp, err := s.ec2.CreateSecurityGroup(sessionName("testgroup1"), "testgroup one description")
350+ vpcResp, err := s.ec2.CreateVPC("10.4.0.0/16", "")
351+ c.Assert(err, IsNil)
352+ vpcId := vpcResp.VPC.Id
353+ defer s.deleteVPCs(c, []string{vpcId})
354+
355+ subResp := s.createSubnet(c, vpcId, "10.4.1.0/24", "")
356+ subId := subResp.Subnet.Id
357+ defer s.deleteSubnets(c, []string{subId})
358+
359+ groupResp, err := s.ec2.CreateSecurityGroup(
360+ sessionName("testgroup1"),
361+ "testgroup one description",
362+ )
363 c.Assert(err, IsNil)
364 group1 := groupResp.SecurityGroup
365
366- groupResp, err = s.ec2.CreateSecurityGroup(sessionName("testgroup2"), "testgroup two description")
367+ groupResp, err = s.ec2.CreateSecurityGroupVPC(
368+ vpcId,
369+ sessionName("testgroup2"),
370+ "testgroup two description vpc",
371+ )
372 c.Assert(err, IsNil)
373 group2 := groupResp.SecurityGroup
374
375@@ -333,12 +377,12 @@
376 c.Assert(err, IsNil)
377 insts[0] = &inst.Instances[0]
378 insts[1] = &inst.Instances[1]
379- defer terminateInstances(c, s.ec2, insts)
380
381 imageId2 := "ami-e358958a" // Natty server, i386, EBS store
382 inst, err = s.ec2.RunInstances(&ec2.RunInstances{
383 ImageId: imageId2,
384 InstanceType: "t1.micro",
385+ SubnetId: subId,
386 SecurityGroups: []ec2.SecurityGroup{group2},
387 })
388 c.Assert(err, IsNil)
389@@ -351,6 +395,8 @@
390 return
391 }
392
393+ defer terminateInstances(c, s.ec2, ids(0, 1, 2))
394+
395 tests := []struct {
396 about string
397 instanceIds []string // instanceIds argument to Instances method.
398@@ -432,6 +478,13 @@
399 {"image-id", []string{imageId2}},
400 {"group-name", []string{group1.Name}},
401 },
402+ }, {
403+ about: "VPC filters in combination",
404+ filters: []filterSpec{
405+ {"vpc-id", []string{vpcId}},
406+ {"subnet-id", []string{subId}},
407+ },
408+ resultIds: ids(2),
409 },
410 }
411 for i, t := range tests {
412@@ -466,6 +519,91 @@
413 }
414 }
415
416+func (s *AmazonServerSuite) TestRunInstancesVPC(c *C) {
417+ vpcResp, err := s.ec2.CreateVPC("10.6.0.0/16", "")
418+ c.Assert(err, IsNil)
419+ vpcId := vpcResp.VPC.Id
420+ defer s.deleteVPCs(c, []string{vpcId})
421+
422+ subResp := s.createSubnet(c, vpcId, "10.6.1.0/24", "")
423+ subId := subResp.Subnet.Id
424+ defer s.deleteSubnets(c, []string{subId})
425+
426+ groupResp, err := s.ec2.CreateSecurityGroupVPC(
427+ vpcId,
428+ sessionName("testgroup1 vpc"),
429+ "testgroup description vpc",
430+ )
431+ c.Assert(err, IsNil)
432+ group := groupResp.SecurityGroup
433+
434+ defer s.deleteGroups(c, []ec2.SecurityGroup{group})
435+
436+ // Run a single instance with a new network interface.
437+ ips := []ec2.PrivateIP{
438+ {Address: "10.6.1.10", IsPrimary: true},
439+ {Address: "10.6.1.20", IsPrimary: false},
440+ }
441+ instResp, err := s.ec2.RunInstances(&ec2.RunInstances{
442+ MinCount: 1,
443+ ImageId: imageId,
444+ InstanceType: "t1.micro",
445+ NetworkInterfaces: []ec2.NetworkInterfaceSpec{{
446+ DeviceIndex: 0,
447+ SubnetId: subId,
448+ PrivateIPs: ips,
449+ SecurityGroupIds: []string{group.Id},
450+ DeleteOnTermination: true,
451+ }},
452+ })
453+ c.Assert(err, IsNil)
454+ inst := &instResp.Instances[0]
455+
456+ defer terminateInstances(c, s.ec2, []string{inst.InstanceId})
457+
458+ // Now list the network interfaces and find ours.
459+ testAttempt := aws.AttemptStrategy{
460+ Total: 5 * time.Minute,
461+ Delay: 5 * time.Second,
462+ }
463+ f := ec2.NewFilter()
464+ f.Add("subnet-id", subId)
465+ var newNIC *ec2.NetworkInterface
466+ for a := testAttempt.Start(); a.Next(); {
467+ c.Logf("waiting for NIC to become available")
468+ listNICs, err := s.ec2.NetworkInterfaces(nil, f)
469+ if err != nil {
470+ c.Logf("retrying; NetworkInterfaces returned: %v", err)
471+ continue
472+ }
473+ for _, iface := range listNICs.Interfaces {
474+ c.Logf("found NIC %v", iface)
475+ if iface.Attachment.InstanceId == inst.InstanceId {
476+ c.Logf("instance %v new NIC appeared", inst.InstanceId)
477+ newNIC = &iface
478+ break
479+ }
480+ }
481+ if newNIC != nil {
482+ break
483+ }
484+ }
485+ if newNIC == nil {
486+ c.Fatalf("timeout while waiting for NIC to appear.")
487+ }
488+ c.Check(newNIC.Id, Matches, `^eni-[0-9a-f]+$`)
489+ c.Check(newNIC.SubnetId, Equals, subId)
490+ c.Check(newNIC.VPCId, Equals, vpcId)
491+ c.Check(newNIC.Status, Matches, `^(attaching|in-use)$`)
492+ c.Check(newNIC.PrivateIPAddress, Equals, ips[0].Address)
493+ c.Check(newNIC.PrivateIPs, DeepEquals, ips)
494+ c.Check(newNIC.Groups, HasLen, 1)
495+ c.Check(newNIC.Groups[0].Id, Equals, group.Id)
496+ c.Check(newNIC.Attachment.Status, Matches, `^(attaching|attached)$`)
497+ c.Check(newNIC.Attachment.DeviceIndex, Equals, 0)
498+ c.Check(newNIC.Attachment.DeleteOnTermination, Equals, true)
499+}
500+
501 func idsOnly(gs []ec2.SecurityGroup) []ec2.SecurityGroup {
502 for i := range gs {
503 gs[i].Name = ""
504@@ -481,14 +619,37 @@
505 }
506
507 func (s *ServerTests) TestGroupFiltering(c *C) {
508- g := make([]ec2.SecurityGroup, 4)
509+ vpcResp, err := s.ec2.CreateVPC("10.5.0.0/16", "")
510+ c.Assert(err, IsNil)
511+ vpcId := vpcResp.VPC.Id
512+ defer s.deleteVPCs(c, []string{vpcId})
513+
514+ subResp := s.createSubnet(c, vpcId, "10.5.1.0/24", "")
515+ subId := subResp.Subnet.Id
516+ defer s.deleteSubnets(c, []string{subId})
517+
518+ g := make([]ec2.SecurityGroup, 5)
519 for i := range g {
520- resp, err := s.ec2.CreateSecurityGroup(sessionName(fmt.Sprintf("testgroup%d", i)), fmt.Sprintf("testdescription%d", i))
521+ var resp *ec2.CreateSecurityGroupResp
522+ gid := sessionName(fmt.Sprintf("testgroup%d", i))
523+ desc := fmt.Sprintf("testdescription%d", i)
524+ if i == 0 {
525+ // Create the first one as a VPC group.
526+ gid += " vpc"
527+ desc += " vpc"
528+ resp, err = s.ec2.CreateSecurityGroupVPC(vpcId, gid, desc)
529+ } else {
530+ resp, err = s.ec2.CreateSecurityGroup(gid, desc)
531+ }
532 c.Assert(err, IsNil)
533 g[i] = resp.SecurityGroup
534 c.Logf("group %d: %v", i, g[i])
535 }
536- defer s.deleteGroups(c, g)
537+ // Reorder the groups below, so that g[3] is first (some of the
538+ // reset depend on it, so they can't be deleted before g[3]). A
539+ // slight optimization for local live tests, so that we don't need
540+ // to wait 5s each time deleteGroups runs.
541+ defer s.deleteGroups(c, []ec2.SecurityGroup{g[3], g[0], g[1], g[2], g[4]})
542
543 perms := [][]ec2.IPPerm{
544 {{
545@@ -501,17 +662,17 @@
546 Protocol: "tcp",
547 FromPort: 200,
548 ToPort: 300,
549- SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}},
550+ SourceGroups: []ec2.UserSecurityGroup{{Id: g[2].Id}},
551 }},
552 {{
553 Protocol: "udp",
554 FromPort: 200,
555 ToPort: 400,
556- SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}},
557+ SourceGroups: []ec2.UserSecurityGroup{{Id: g[2].Id}},
558 }},
559 }
560 for i, ps := range perms {
561- _, err := s.ec2.AuthorizeSecurityGroup(g[i], ps)
562+ _, err := s.ec2.AuthorizeSecurityGroup(g[i+1], ps)
563 c.Assert(err, IsNil)
564 }
565
566@@ -541,7 +702,7 @@
567 tests := []groupTest{
568 {
569 about: "check that SecurityGroups returns all groups",
570- results: groups(0, 1, 2, 3),
571+ results: groups(0, 1, 2, 3, 4),
572 allowExtra: true,
573 }, {
574 about: "check that specifying two group ids returns them",
575@@ -549,8 +710,8 @@
576 results: groups(0, 2),
577 }, {
578 about: "check that specifying names only works",
579- groups: namesOnly(groups(0, 2)),
580- results: groups(0, 2),
581+ groups: namesOnly(groups(1, 2)),
582+ results: groups(1, 2),
583 }, {
584 about: "check that specifying a non-existent group id gives an error",
585 groups: append(groups(0), ec2.SecurityGroup{Id: "sg-eeeeeeeee"}),
586@@ -577,11 +738,12 @@
587 },
588 filterCheck("description", "testdescription1", groups(1)),
589 filterCheck("group-name", g[2].Name, groups(2)),
590- filterCheck("ip-permission.cidr", "1.2.3.4/32", groups(0)),
591- filterCheck("ip-permission.group-name", g[1].Name, groups(1, 2)),
592- filterCheck("ip-permission.protocol", "udp", groups(2)),
593- filterCheck("ip-permission.from-port", "200", groups(1, 2)),
594- filterCheck("ip-permission.to-port", "200", groups(0)),
595+ filterCheck("ip-permission.cidr", "1.2.3.4/32", groups(1)),
596+ filterCheck("ip-permission.group-name", g[2].Name, groups(2, 3)),
597+ filterCheck("ip-permission.protocol", "udp", groups(3)),
598+ filterCheck("ip-permission.from-port", "200", groups(2, 3)),
599+ filterCheck("ip-permission.to-port", "200", groups(1)),
600+ filterCheck("vpc-id", vpcId, groups(0)),
601 // TODO owner-id
602 }
603 for i, t := range tests {
604
605=== modified file 'ec2/ec2test/server.go'
606--- ec2/ec2test/server.go 2014-02-07 12:59:16 +0000
607+++ ec2/ec2test/server.go 2014-02-07 12:59:16 +0000
608@@ -85,6 +85,8 @@
609 reservation *reservation
610 instType string
611 state ec2.InstanceState
612+ subnetId string
613+ vpcId string
614 }
615
616 // permKey represents permission for a given security
617@@ -150,6 +152,8 @@
618 return g.hasPerm(func(k permKey) bool { return k.protocol == value }), nil
619 case "owner-id":
620 return value == ownerId, nil
621+ case "vpc-id":
622+ return g.vpcId == value, nil
623 }
624 return false, fmt.Errorf("unknown attribute %q", attr)
625 }
626@@ -527,7 +531,6 @@
627 // AvailZone ?
628 // GroupName tag
629 // Monitoring ignore?
630- // SubnetId ?
631 // DisableAPITermination bool
632 // ShutdownBehavior string
633 // PrivateIPAddress string
634@@ -535,6 +538,16 @@
635 srv.mu.Lock()
636 defer srv.mu.Unlock()
637
638+ var vpcId string
639+ subnetId := req.Form.Get("SubnetId")
640+ if subnetId != "" {
641+ sub, found := srv.subnets[subnetId]
642+ if !found {
643+ fatalf(400, "InvalidSubnetID.NotFound", "subnet %s not found", subnetId)
644+ }
645+ vpcId = sub.VPCId
646+ }
647+
648 // make sure that form fields are correct before creating the reservation.
649 instType := req.Form.Get("InstanceType")
650 imageId := req.Form.Get("ImageId")
651@@ -547,7 +560,7 @@
652 resp.OwnerId = ownerId
653
654 for i := 0; i < max; i++ {
655- inst := srv.newInstance(r, instType, imageId, srv.initialInstanceState)
656+ inst := srv.newInstance(r, instType, imageId, srv.initialInstanceState, subnetId, vpcId)
657 inst.UserData = userData
658 resp.Instances = append(resp.Instances, inst.ec2instance())
659 }
660@@ -566,10 +579,15 @@
661 return nil
662 }
663
664-// NewInstances creates n new instances in srv with the given instance type,
665-// image ID, initial state and security groups. If any group does not already
666-// exist, it will be created. NewInstances returns the ids of the new instances.
667-func (srv *Server) NewInstances(n int, instType string, imageId string, state ec2.InstanceState, groups []ec2.SecurityGroup) []string {
668+// NewInstancesVPC creates n new VPC instances in srv with the given
669+// instance type, image ID, initial state, and security groups,
670+// belonging to the given vpcId and subnetId. If any group does not
671+// already exist, it will be created. NewInstancesVPC returns the ids
672+// of the new instances.
673+//
674+// If vpcId and subnetId are both empty, this call is equivalent to
675+// calling NewInstances.
676+func (srv *Server) NewInstancesVPC(vpcId, subnetId string, n int, instType string, imageId string, state ec2.InstanceState, groups []ec2.SecurityGroup) []string {
677 srv.mu.Lock()
678 defer srv.mu.Unlock()
679
680@@ -585,13 +603,21 @@
681
682 ids := make([]string, n)
683 for i := 0; i < n; i++ {
684- inst := srv.newInstance(r, instType, imageId, state)
685+ inst := srv.newInstance(r, instType, imageId, state, subnetId, vpcId)
686 ids[i] = inst.id()
687 }
688 return ids
689 }
690
691-func (srv *Server) newInstance(r *reservation, instType string, imageId string, state ec2.InstanceState) *Instance {
692+// NewInstances creates n new instances in srv with the given instance
693+// type, image ID, initial state, and security groups. If any group
694+// does not already exist, it will be created. NewInstances returns
695+// the ids of the new instances.
696+func (srv *Server) NewInstances(n int, instType string, imageId string, state ec2.InstanceState, groups []ec2.SecurityGroup) []string {
697+ return srv.NewInstancesVPC("", "", n, instType, imageId, state, groups)
698+}
699+
700+func (srv *Server) newInstance(r *reservation, instType string, imageId string, state ec2.InstanceState, subnetId, vpcId string) *Instance {
701 inst := &Instance{
702 seq: srv.maxId.next(),
703 instType: instType,
704@@ -599,6 +625,10 @@
705 state: state,
706 reservation: r,
707 }
708+ if vpcId != "" && subnetId != "" {
709+ inst.vpcId = vpcId
710+ inst.subnetId = subnetId
711+ }
712 id := inst.id()
713 srv.instances[id] = inst
714 r.instances[id] = inst
715@@ -669,6 +699,8 @@
716 IPAddress: fmt.Sprintf("8.0.0.%d", inst.seq%256),
717 PrivateIPAddress: fmt.Sprintf("127.0.0.%d", inst.seq%256),
718 State: inst.state,
719+ VPCId: inst.vpcId,
720+ SubnetId: inst.subnetId,
721 // TODO the rest
722 }
723 }
724@@ -679,6 +711,10 @@
725 return value == "i386", nil
726 case "instance-id":
727 return inst.id() == value, nil
728+ case "subnet-id":
729+ return inst.subnetId == value, nil
730+ case "vpc-id":
731+ return inst.vpcId == value, nil
732 case "instance.group-id", "group-id":
733 for _, g := range inst.reservation.groups {
734 if g.id == value {
735@@ -731,6 +767,10 @@
736 id: fmt.Sprintf("sg-%d", srv.groupId.next()),
737 perms: make(map[permKey]bool),
738 }
739+ vpcId := req.Form.Get("VpcId")
740+ if vpcId != "" {
741+ g.vpcId = vpcId
742+ }
743 srv.groups[g.id] = g
744 // we define a local type for this because ec2.CreateSecurityGroupResp
745 // contains SecurityGroup, but the response to this request
746@@ -782,6 +822,14 @@
747 if len(insts) > 0 && !insts[inst] {
748 continue
749 }
750+ // make instances in state "shutting-down" to transition
751+ // to "terminated" first, so we can simulate: shutdown,
752+ // subsequent refresh of the state with Instances(),
753+ // terminated.
754+ if inst.state == ShuttingDown {
755+ inst.state = Terminated
756+ }
757+
758 ok, err := f.ok(inst)
759 if ok {
760 instance := inst.ec2instance()
761
762=== modified file 'ec2/networkinterfaces_test.go'
763--- ec2/networkinterfaces_test.go 2014-02-07 12:59:16 +0000
764+++ ec2/networkinterfaces_test.go 2014-02-07 12:59:16 +0000
765@@ -214,7 +214,7 @@
766 inst := instList.Instances[0]
767 c.Assert(inst, NotNil)
768 instId := inst.InstanceId
769- defer s.ec2.TerminateInstances([]string{instId})
770+ defer terminateInstances(c, s.ec2, []string{instId})
771
772 ips1 := []ec2.PrivateIP{{Address: "10.3.1.10", IsPrimary: true}}
773 resp1, err := s.ec2.CreateNetworkInterface(ec2.NetworkInterfaceOptions{
774
775=== modified file 'ec2/responses_test.go'
776--- ec2/responses_test.go 2014-02-07 12:59:16 +0000
777+++ ec2/responses_test.go 2014-02-07 12:59:16 +0000
778@@ -9,7 +9,7 @@
779
780 // http://goo.gl/Mcm3b
781 var RunInstancesExample = `
782-<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/">
783+<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-13/">
784 <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
785 <reservationId>r-47a5402e</reservationId>
786 <ownerId>999988887777</ownerId>
787@@ -40,9 +40,82 @@
788 <state>enabled</state>
789 </monitoring>
790 <virtualizationType>paravirtual</virtualizationType>
791+ <subnetId>subnet-id</subnetId>
792+ <vpcId>vpc-id</vpcId>
793+ <sourceDestCheck>true</sourceDestCheck>
794 <clientToken/>
795 <tagSet/>
796 <hypervisor>xen</hypervisor>
797+ <networkInterfaceSet>
798+ <item>
799+ <networkInterfaceId>eni-c6bb50ae</networkInterfaceId>
800+ <subnetId>subnet-id</subnetId>
801+ <vpcId>vpc-id</vpcId>
802+ <description>eth0</description>
803+ <ownerId>111122223333</ownerId>
804+ <status>in-use</status>
805+ <privateIpAddress>10.0.0.25</privateIpAddress>
806+ <macAddress>11:22:33:44:55:66</macAddress>
807+ <sourceDestCheck>true</sourceDestCheck>
808+ <groupSet>
809+ <item>
810+ <groupId>sg-1</groupId>
811+ <groupName>vpc sg-1</groupName>
812+ </item>
813+ <item>
814+ <groupId>sg-2</groupId>
815+ <groupName>vpc sg-2</groupName>
816+ </item>
817+ </groupSet>
818+ <attachment>
819+ <attachmentId>eni-attach-0326646a</attachmentId>
820+ <deviceIndex>0</deviceIndex>
821+ <status>attaching</status>
822+ <attachTime>2011-12-20T08:29:31.000Z</attachTime>
823+ <deleteOnTermination>true</deleteOnTermination>
824+ </attachment>
825+ <privateIpAddressesSet>
826+ <item>
827+ <privateIpAddress>10.0.0.25</privateIpAddress>
828+ <primary>true</primary>
829+ </item>
830+ </privateIpAddressesSet>
831+ </item>
832+ <item>
833+ <networkInterfaceId>eni-id</networkInterfaceId>
834+ <subnetId>subnet-id</subnetId>
835+ <vpcId>vpc-id</vpcId>
836+ <description/>
837+ <ownerId>111122223333</ownerId>
838+ <status>in-use</status>
839+ <privateIpAddress>10.0.1.10</privateIpAddress>
840+ <macAddress>11:22:33:44:55:66</macAddress>
841+ <sourceDestCheck>true</sourceDestCheck>
842+ <groupSet>
843+ <item>
844+ <groupId>sg-id</groupId>
845+ <groupName>vpc default</groupName>
846+ </item>
847+ </groupSet>
848+ <attachment>
849+ <attachmentId>eni-attach-id</attachmentId>
850+ <deviceIndex>1</deviceIndex>
851+ <status>attaching</status>
852+ <attachTime>2011-12-20T08:29:31.000Z</attachTime>
853+ <deleteOnTermination>false</deleteOnTermination>
854+ </attachment>
855+ <privateIpAddressesSet>
856+ <item>
857+ <privateIpAddress>10.0.1.10</privateIpAddress>
858+ <primary>true</primary>
859+ </item>
860+ <item>
861+ <privateIpAddress>10.0.1.20</privateIpAddress>
862+ <primary>false</primary>
863+ </item>
864+ </privateIpAddressesSet>
865+ </item>
866+ </networkInterfaceSet>
867 </item>
868 <item>
869 <instanceId>i-2bc64242</instanceId>

Subscribers

People subscribed via source and target branches