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
=== modified file 'ec2/ec2.go'
--- ec2/ec2.go 2014-02-07 12:59:16 +0000
+++ ec2/ec2.go 2014-02-07 12:59:16 +0000
@@ -201,6 +201,37 @@
201// ----------------------------------------------------------------------------201// ----------------------------------------------------------------------------
202// Instance management functions and types.202// Instance management functions and types.
203203
204// NetworkInterfaceSpec encapsulates options for a single network
205// interface, specified when calling RunInstances.
206//
207// If Id is set, it must match an existing VPC network interface, and
208// in this case only a single instance can be launched. If Id is not
209// set, a new network interface will be created for each instance.
210//
211// The following fields are required when creating a new network
212// interface (i.e. Id is empty): DeviceIndex, SubnetId, Description
213// (only used if set), SecurityGroupIds.
214//
215// PrivateIPs can be used to add one or more private IP addresses to a
216// network interface. Only one of the IP addresses can be set as
217// primary. If none are given, EC2 selects a primary IP for each
218// created interface from the subnet pool.
219//
220// When SecondaryPrivateIPsCount is non-zero, EC2 allocates that
221// number of IP addresses from within the subnet range and sets them
222// as secondary IPs. The number of IP addresses you can assign to a
223// network interface varies by instance type.
224type NetworkInterfaceSpec struct {
225 Id string
226 DeviceIndex int
227 SubnetId string
228 Description string
229 PrivateIPs []PrivateIP
230 SecurityGroupIds []string
231 DeleteOnTermination bool
232 SecondaryPrivateIPsCount int
233}
234
204// The RunInstances type encapsulates options for the respective request in EC2.235// The RunInstances type encapsulates options for the respective request in EC2.
205//236//
206// See http://goo.gl/Mcm3b for more details.237// See http://goo.gl/Mcm3b for more details.
@@ -222,6 +253,7 @@
222 ShutdownBehavior string253 ShutdownBehavior string
223 PrivateIPAddress string254 PrivateIPAddress string
224 BlockDeviceMappings []BlockDeviceMapping255 BlockDeviceMappings []BlockDeviceMapping
256 NetworkInterfaces []NetworkInterfaceSpec
225}257}
226258
227// Response to a RunInstances request.259// Response to a RunInstances request.
@@ -239,33 +271,44 @@
239//271//
240// See http://goo.gl/OCH8a for more details.272// See http://goo.gl/OCH8a for more details.
241type Instance struct {273type Instance struct {
242 InstanceId string `xml:"instanceId"`274 InstanceId string `xml:"instanceId"`
243 InstanceType string `xml:"instanceType"`275 InstanceType string `xml:"instanceType"`
244 ImageId string `xml:"imageId"`276 ImageId string `xml:"imageId"`
245 PrivateDNSName string `xml:"privateDnsName"`277 PrivateDNSName string `xml:"privateDnsName"`
246 DNSName string `xml:"dnsName"`278 DNSName string `xml:"dnsName"`
247 IPAddress string `xml:"ipAddress"`279 IPAddress string `xml:"ipAddress"`
248 PrivateIPAddress string `xml:"privateIpAddress"`280 PrivateIPAddress string `xml:"privateIpAddress"`
249 KeyName string `xml:"keyName"`281 SubnetId string `xml:"subnetId"`
250 AMILaunchIndex int `xml:"amiLaunchIndex"`282 VPCId string `xml:"vpcId"`
251 Hypervisor string `xml:"hypervisor"`283 SourceDestCheck bool `xml:"sourceDestCheck"`
252 VirtType string `xml:"virtualizationType"`284 KeyName string `xml:"keyName"`
253 Monitoring string `xml:"monitoring>state"`285 AMILaunchIndex int `xml:"amiLaunchIndex"`
254 AvailZone string `xml:"placement>availabilityZone"`286 Hypervisor string `xml:"hypervisor"`
255 PlacementGroupName string `xml:"placement>groupName"`287 VirtType string `xml:"virtualizationType"`
256 State InstanceState `xml:"instanceState"`288 Monitoring string `xml:"monitoring>state"`
257 Tags []Tag `xml:"tagSet>item"`289 AvailZone string `xml:"placement>availabilityZone"`
258 SecurityGroups []SecurityGroup `xml:"groupSet>item"`290 PlacementGroupName string `xml:"placement>groupName"`
291 State InstanceState `xml:"instanceState"`
292 Tags []Tag `xml:"tagSet>item"`
293 SecurityGroups []SecurityGroup `xml:"groupSet>item"`
294 NetworkInterfaces []NetworkInterface `xml:"networkInterfaceSet>item"`
259}295}
260296
261// RunInstances starts new instances in EC2.297// RunInstances starts new instances in EC2.
262// If options.MinCount and options.MaxCount are both zero, a single instance298// If options.MinCount and options.MaxCount are both zero, a single instance
263// will be started; otherwise if options.MaxCount is zero, options.MinCount299// will be started; otherwise if options.MaxCount is zero, options.MinCount
264// will be used insteead.300// will be used instead.
265//301//
266// See http://goo.gl/Mcm3b for more details.302// See http://goo.gl/Mcm3b for more details.
267func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err error) {303func (ec2 *EC2) RunInstances(options *RunInstances) (resp *RunInstancesResp, err error) {
268 params := makeParams("RunInstances")304 var params map[string]string
305 if options.SubnetId != "" || len(options.NetworkInterfaces) > 0 {
306 // When either SubnetId or NetworkInterfaces are specified, we
307 // need to use the API version with complete VPC support.
308 params = makeParamsVPC("RunInstances")
309 } else {
310 params = makeParams("RunInstances")
311 }
269 params["ImageId"] = options.ImageId312 params["ImageId"] = options.ImageId
270 params["InstanceType"] = options.InstanceType313 params["InstanceType"] = options.InstanceType
271 var min, max int314 var min, max int
@@ -293,26 +336,61 @@
293 }336 }
294 for i, b := range options.BlockDeviceMappings {337 for i, b := range options.BlockDeviceMappings {
295 n := strconv.Itoa(i + 1)338 n := strconv.Itoa(i + 1)
339 prefix := "BlockDeviceMapping." + n
296 if b.DeviceName != "" {340 if b.DeviceName != "" {
297 params["BlockDeviceMapping."+n+".DeviceName"] = b.DeviceName341 params[prefix+".DeviceName"] = b.DeviceName
298 }342 }
299 if b.VirtualName != "" {343 if b.VirtualName != "" {
300 params["BlockDeviceMapping."+n+".VirtualName"] = b.VirtualName344 params[prefix+".VirtualName"] = b.VirtualName
301 }345 }
302 if b.SnapshotId != "" {346 if b.SnapshotId != "" {
303 params["BlockDeviceMapping."+n+".Ebs.SnapshotId"] = b.SnapshotId347 params[prefix+".Ebs.SnapshotId"] = b.SnapshotId
304 }348 }
305 if b.VolumeType != "" {349 if b.VolumeType != "" {
306 params["BlockDeviceMapping."+n+".Ebs.VolumeType"] = b.VolumeType350 params[prefix+".Ebs.VolumeType"] = b.VolumeType
307 }351 }
308 if b.VolumeSize > 0 {352 if b.VolumeSize > 0 {
309 params["BlockDeviceMapping."+n+".Ebs.VolumeSize"] = strconv.FormatInt(b.VolumeSize, 10)353 params[prefix+".Ebs.VolumeSize"] = strconv.FormatInt(b.VolumeSize, 10)
310 }354 }
311 if b.IOPS > 0 {355 if b.IOPS > 0 {
312 params["BlockDeviceMapping."+n+".Ebs.Iops"] = strconv.FormatInt(b.IOPS, 10)356 params[prefix+".Ebs.Iops"] = strconv.FormatInt(b.IOPS, 10)
313 }357 }
314 if b.DeleteOnTermination {358 if b.DeleteOnTermination {
315 params["BlockDeviceMapping."+n+".Ebs.DeleteOnTermination"] = "true"359 params[prefix+".Ebs.DeleteOnTermination"] = "true"
360 }
361 }
362 for i, ni := range options.NetworkInterfaces {
363 // Unlike other lists, NetworkInterface and PrivateIpAddresses
364 // should start from 0, not 1, according to the examples
365 // requests in the API documentation here http://goo.gl/Mcm3b.
366 n := strconv.Itoa(i)
367 prefix := "NetworkInterface." + n
368 if ni.Id != "" {
369 params[prefix+".NetworkInterfaceId"] = ni.Id
370 }
371 params[prefix+".DeviceIndex"] = strconv.Itoa(ni.DeviceIndex)
372 if ni.SubnetId != "" {
373 params[prefix+".SubnetId"] = ni.SubnetId
374 }
375 if ni.Description != "" {
376 params[prefix+".Description"] = ni.Description
377 }
378 for j, gid := range ni.SecurityGroupIds {
379 k := strconv.Itoa(j + 1)
380 params[prefix+".SecurityGroupId."+k] = gid
381 }
382 if ni.DeleteOnTermination {
383 params[prefix+".DeleteOnTermination"] = "true"
384 }
385 if ni.SecondaryPrivateIPsCount > 0 {
386 val := strconv.Itoa(ni.SecondaryPrivateIPsCount)
387 params[prefix+".SecondaryPrivateIpAddressCount"] = val
388 }
389 for j, ip := range ni.PrivateIPs {
390 k := strconv.Itoa(j)
391 subprefix := prefix + ".PrivateIpAddresses." + k
392 params[subprefix+".PrivateIpAddress"] = ip.Address
393 params[subprefix+".Primary"] = strconv.FormatBool(ip.IsPrimary)
316 }394 }
317 }395 }
318 token, err := clientToken()396 token, err := clientToken()
319397
=== modified file 'ec2/ec2_test.go'
--- ec2/ec2_test.go 2014-02-07 12:59:16 +0000
+++ ec2/ec2_test.go 2014-02-07 12:59:16 +0000
@@ -105,6 +105,27 @@
105 DeleteOnTermination: true,105 DeleteOnTermination: true,
106 IOPS: 1000,106 IOPS: 1000,
107 }},107 }},
108 NetworkInterfaces: []ec2.NetworkInterfaceSpec{{
109 DeviceIndex: 0,
110 SubnetId: "subnet-id",
111 Description: "eth0",
112 PrivateIPs: []ec2.PrivateIP{
113 {Address: "10.0.0.25", IsPrimary: true},
114 },
115 DeleteOnTermination: true,
116 SecurityGroupIds: []string{"sg-1", "sg-2"},
117 SecondaryPrivateIPsCount: 2,
118 }, {
119 Id: "eni-id",
120 DeviceIndex: 1,
121 PrivateIPs: []ec2.PrivateIP{{
122 Address: "10.0.1.10",
123 IsPrimary: true,
124 }, {
125 Address: "10.0.1.20",
126 IsPrimary: false,
127 }},
128 }},
108 }129 }
109 resp, err := s.ec2.RunInstances(&options)130 resp, err := s.ec2.RunInstances(&options)
110131
@@ -136,6 +157,19 @@
136 c.Assert(req.Form["BlockDeviceMapping.1.Ebs.VolumeSize"], DeepEquals, []string{"10"})157 c.Assert(req.Form["BlockDeviceMapping.1.Ebs.VolumeSize"], DeepEquals, []string{"10"})
137 c.Assert(req.Form["BlockDeviceMapping.1.Ebs.Iops"], DeepEquals, []string{"1000"})158 c.Assert(req.Form["BlockDeviceMapping.1.Ebs.Iops"], DeepEquals, []string{"1000"})
138 c.Assert(req.Form["BlockDeviceMapping.1.Ebs.DeleteOnTermination"], DeepEquals, []string{"true"})159 c.Assert(req.Form["BlockDeviceMapping.1.Ebs.DeleteOnTermination"], DeepEquals, []string{"true"})
160 c.Assert(req.Form["NetworkInterface.0.DeviceIndex"], DeepEquals, []string{"0"})
161 c.Assert(req.Form["NetworkInterface.0.SubnetId"], DeepEquals, []string{"subnet-id"})
162 c.Assert(req.Form["NetworkInterface.0.Description"], DeepEquals, []string{"eth0"})
163 c.Assert(req.Form["NetworkInterface.0.SecurityGroupId.1"], DeepEquals, []string{"sg-1"})
164 c.Assert(req.Form["NetworkInterface.0.SecurityGroupId.2"], DeepEquals, []string{"sg-2"})
165 c.Assert(req.Form["NetworkInterface.0.DeleteOnTermination"], DeepEquals, []string{"true"})
166 c.Assert(req.Form["NetworkInterface.0.SecondaryPrivateIpAddressCount"], DeepEquals, []string{"2"})
167 c.Assert(req.Form["NetworkInterface.1.NetworkInterfaceId"], DeepEquals, []string{"eni-id"})
168 c.Assert(req.Form["NetworkInterface.1.DeviceIndex"], DeepEquals, []string{"1"})
169 c.Assert(req.Form["NetworkInterface.1.PrivateIpAddresses.0.PrivateIpAddress"], DeepEquals, []string{"10.0.1.10"})
170 c.Assert(req.Form["NetworkInterface.1.PrivateIpAddresses.0.Primary"], DeepEquals, []string{"true"})
171 c.Assert(req.Form["NetworkInterface.1.PrivateIpAddresses.1.PrivateIpAddress"], DeepEquals, []string{"10.0.1.20"})
172 c.Assert(req.Form["NetworkInterface.1.PrivateIpAddresses.1.Primary"], DeepEquals, []string{"false"})
139173
140 c.Assert(err, IsNil)174 c.Assert(err, IsNil)
141 c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE")175 c.Assert(resp.RequestId, Equals, "59dbff89-35bd-4eac-99ed-be587EXAMPLE")
@@ -153,6 +187,53 @@
153 c.Assert(i0.AMILaunchIndex, Equals, 0)187 c.Assert(i0.AMILaunchIndex, Equals, 0)
154 c.Assert(i0.VirtType, Equals, "paravirtual")188 c.Assert(i0.VirtType, Equals, "paravirtual")
155 c.Assert(i0.Hypervisor, Equals, "xen")189 c.Assert(i0.Hypervisor, Equals, "xen")
190 c.Assert(i0.SubnetId, Equals, "subnet-id")
191 c.Assert(i0.VPCId, Equals, "vpc-id")
192 c.Assert(i0.NetworkInterfaces, HasLen, 2)
193 c.Assert(i0.NetworkInterfaces, DeepEquals, []ec2.NetworkInterface{{
194 Id: "eni-c6bb50ae",
195 SubnetId: "subnet-id",
196 VPCId: "vpc-id",
197 Description: "eth0",
198 SourceDestCheck: true,
199 OwnerId: "111122223333",
200 Status: "in-use",
201 Groups: []ec2.SecurityGroup{
202 {Name: "vpc sg-1", Id: "sg-1"},
203 {Name: "vpc sg-2", Id: "sg-2"},
204 },
205 MACAddress: "11:22:33:44:55:66",
206 PrivateIPAddress: "10.0.0.25",
207 PrivateIPs: []ec2.PrivateIP{{Address: "10.0.0.25", IsPrimary: true}},
208 Attachment: ec2.NetworkInterfaceAttachment{
209 Id: "eni-attach-0326646a",
210 DeviceIndex: 0,
211 Status: "attaching",
212 AttachTime: "2011-12-20T08:29:31.000Z",
213 DeleteOnTermination: true,
214 },
215 }, {
216 Id: "eni-id",
217 SubnetId: "subnet-id",
218 VPCId: "vpc-id",
219 SourceDestCheck: true,
220 OwnerId: "111122223333",
221 Status: "in-use",
222 Groups: []ec2.SecurityGroup{{Name: "vpc default", Id: "sg-id"}},
223 MACAddress: "11:22:33:44:55:66",
224 PrivateIPAddress: "10.0.1.10",
225 PrivateIPs: []ec2.PrivateIP{
226 {Address: "10.0.1.10", IsPrimary: true},
227 {Address: "10.0.1.20", IsPrimary: false},
228 },
229 Attachment: ec2.NetworkInterfaceAttachment{
230 Id: "eni-attach-id",
231 DeviceIndex: 1,
232 Status: "attaching",
233 AttachTime: "2011-12-20T08:29:31.000Z",
234 DeleteOnTermination: false,
235 },
236 }})
156237
157 i1 := resp.Instances[1]238 i1 := resp.Instances[1]
158 c.Assert(i1.InstanceId, Equals, "i-2bc64242")239 c.Assert(i1.InstanceId, Equals, "i-2bc64242")
159240
=== modified file 'ec2/ec2t_test.go'
--- ec2/ec2t_test.go 2014-02-07 12:59:16 +0000
+++ ec2/ec2t_test.go 2014-02-07 12:59:16 +0000
@@ -136,15 +136,43 @@
136 ec2 *ec2.EC2136 ec2 *ec2.EC2
137}137}
138138
139func terminateInstances(c *C, e *ec2.EC2, insts []*ec2.Instance) {139func terminateInstances(c *C, e *ec2.EC2, ids []string) {
140 var ids []string
141 for _, inst := range insts {
142 if inst != nil {
143 ids = append(ids, inst.InstanceId)
144 }
145 }
146 _, err := e.TerminateInstances(ids)140 _, err := e.TerminateInstances(ids)
147 c.Check(err, IsNil, Commentf("%d INSTANCES LEFT RUNNING!!!", len(ids)))141 c.Assert(err, IsNil, Commentf("%d INSTANCES LEFT RUNNING!!!", ids))
142 // We need to wait until the instances are really off, because
143 // entities that depend on them won't be deleted (i.e. groups,
144 // NICs, subnets, etc.)
145 testAttempt := aws.AttemptStrategy{
146 Total: 10 * time.Minute,
147 Delay: 5 * time.Second,
148 }
149 f := ec2.NewFilter()
150 f.Add("instance-state-name", "terminated")
151 idsLeft := make(map[string]bool)
152 for _, id := range ids {
153 idsLeft[id] = true
154 }
155 for a := testAttempt.Start(); a.Next(); {
156 c.Logf("waiting for %v to get terminated", ids)
157 resp, err := e.Instances(ids, f)
158 if err != nil {
159 c.Fatalf("not waiting for %v to terminate: %v", ids, err)
160 }
161 for _, r := range resp.Reservations {
162 for _, inst := range r.Instances {
163 delete(idsLeft, inst.InstanceId)
164 }
165 }
166 ids = []string{}
167 for id, _ := range idsLeft {
168 ids = append(ids, id)
169 }
170 if len(ids) == 0 {
171 c.Logf("all instances terminated.")
172 return
173 }
174 }
175 c.Fatalf("%v INSTANCES LEFT RUNNING!!!", ids)
148}176}
149177
150func (s *ServerTests) makeTestGroup(c *C, name, descr string) ec2.SecurityGroup {178func (s *ServerTests) makeTestGroup(c *C, name, descr string) ec2.SecurityGroup {
@@ -313,11 +341,27 @@
313}341}
314342
315func (s *ServerTests) TestInstanceFiltering(c *C) {343func (s *ServerTests) TestInstanceFiltering(c *C) {
316 groupResp, err := s.ec2.CreateSecurityGroup(sessionName("testgroup1"), "testgroup one description")344 vpcResp, err := s.ec2.CreateVPC("10.4.0.0/16", "")
345 c.Assert(err, IsNil)
346 vpcId := vpcResp.VPC.Id
347 defer s.deleteVPCs(c, []string{vpcId})
348
349 subResp := s.createSubnet(c, vpcId, "10.4.1.0/24", "")
350 subId := subResp.Subnet.Id
351 defer s.deleteSubnets(c, []string{subId})
352
353 groupResp, err := s.ec2.CreateSecurityGroup(
354 sessionName("testgroup1"),
355 "testgroup one description",
356 )
317 c.Assert(err, IsNil)357 c.Assert(err, IsNil)
318 group1 := groupResp.SecurityGroup358 group1 := groupResp.SecurityGroup
319359
320 groupResp, err = s.ec2.CreateSecurityGroup(sessionName("testgroup2"), "testgroup two description")360 groupResp, err = s.ec2.CreateSecurityGroupVPC(
361 vpcId,
362 sessionName("testgroup2"),
363 "testgroup two description vpc",
364 )
321 c.Assert(err, IsNil)365 c.Assert(err, IsNil)
322 group2 := groupResp.SecurityGroup366 group2 := groupResp.SecurityGroup
323367
@@ -333,12 +377,12 @@
333 c.Assert(err, IsNil)377 c.Assert(err, IsNil)
334 insts[0] = &inst.Instances[0]378 insts[0] = &inst.Instances[0]
335 insts[1] = &inst.Instances[1]379 insts[1] = &inst.Instances[1]
336 defer terminateInstances(c, s.ec2, insts)
337380
338 imageId2 := "ami-e358958a" // Natty server, i386, EBS store381 imageId2 := "ami-e358958a" // Natty server, i386, EBS store
339 inst, err = s.ec2.RunInstances(&ec2.RunInstances{382 inst, err = s.ec2.RunInstances(&ec2.RunInstances{
340 ImageId: imageId2,383 ImageId: imageId2,
341 InstanceType: "t1.micro",384 InstanceType: "t1.micro",
385 SubnetId: subId,
342 SecurityGroups: []ec2.SecurityGroup{group2},386 SecurityGroups: []ec2.SecurityGroup{group2},
343 })387 })
344 c.Assert(err, IsNil)388 c.Assert(err, IsNil)
@@ -351,6 +395,8 @@
351 return395 return
352 }396 }
353397
398 defer terminateInstances(c, s.ec2, ids(0, 1, 2))
399
354 tests := []struct {400 tests := []struct {
355 about string401 about string
356 instanceIds []string // instanceIds argument to Instances method.402 instanceIds []string // instanceIds argument to Instances method.
@@ -432,6 +478,13 @@
432 {"image-id", []string{imageId2}},478 {"image-id", []string{imageId2}},
433 {"group-name", []string{group1.Name}},479 {"group-name", []string{group1.Name}},
434 },480 },
481 }, {
482 about: "VPC filters in combination",
483 filters: []filterSpec{
484 {"vpc-id", []string{vpcId}},
485 {"subnet-id", []string{subId}},
486 },
487 resultIds: ids(2),
435 },488 },
436 }489 }
437 for i, t := range tests {490 for i, t := range tests {
@@ -466,6 +519,91 @@
466 }519 }
467}520}
468521
522func (s *AmazonServerSuite) TestRunInstancesVPC(c *C) {
523 vpcResp, err := s.ec2.CreateVPC("10.6.0.0/16", "")
524 c.Assert(err, IsNil)
525 vpcId := vpcResp.VPC.Id
526 defer s.deleteVPCs(c, []string{vpcId})
527
528 subResp := s.createSubnet(c, vpcId, "10.6.1.0/24", "")
529 subId := subResp.Subnet.Id
530 defer s.deleteSubnets(c, []string{subId})
531
532 groupResp, err := s.ec2.CreateSecurityGroupVPC(
533 vpcId,
534 sessionName("testgroup1 vpc"),
535 "testgroup description vpc",
536 )
537 c.Assert(err, IsNil)
538 group := groupResp.SecurityGroup
539
540 defer s.deleteGroups(c, []ec2.SecurityGroup{group})
541
542 // Run a single instance with a new network interface.
543 ips := []ec2.PrivateIP{
544 {Address: "10.6.1.10", IsPrimary: true},
545 {Address: "10.6.1.20", IsPrimary: false},
546 }
547 instResp, err := s.ec2.RunInstances(&ec2.RunInstances{
548 MinCount: 1,
549 ImageId: imageId,
550 InstanceType: "t1.micro",
551 NetworkInterfaces: []ec2.NetworkInterfaceSpec{{
552 DeviceIndex: 0,
553 SubnetId: subId,
554 PrivateIPs: ips,
555 SecurityGroupIds: []string{group.Id},
556 DeleteOnTermination: true,
557 }},
558 })
559 c.Assert(err, IsNil)
560 inst := &instResp.Instances[0]
561
562 defer terminateInstances(c, s.ec2, []string{inst.InstanceId})
563
564 // Now list the network interfaces and find ours.
565 testAttempt := aws.AttemptStrategy{
566 Total: 5 * time.Minute,
567 Delay: 5 * time.Second,
568 }
569 f := ec2.NewFilter()
570 f.Add("subnet-id", subId)
571 var newNIC *ec2.NetworkInterface
572 for a := testAttempt.Start(); a.Next(); {
573 c.Logf("waiting for NIC to become available")
574 listNICs, err := s.ec2.NetworkInterfaces(nil, f)
575 if err != nil {
576 c.Logf("retrying; NetworkInterfaces returned: %v", err)
577 continue
578 }
579 for _, iface := range listNICs.Interfaces {
580 c.Logf("found NIC %v", iface)
581 if iface.Attachment.InstanceId == inst.InstanceId {
582 c.Logf("instance %v new NIC appeared", inst.InstanceId)
583 newNIC = &iface
584 break
585 }
586 }
587 if newNIC != nil {
588 break
589 }
590 }
591 if newNIC == nil {
592 c.Fatalf("timeout while waiting for NIC to appear.")
593 }
594 c.Check(newNIC.Id, Matches, `^eni-[0-9a-f]+$`)
595 c.Check(newNIC.SubnetId, Equals, subId)
596 c.Check(newNIC.VPCId, Equals, vpcId)
597 c.Check(newNIC.Status, Matches, `^(attaching|in-use)$`)
598 c.Check(newNIC.PrivateIPAddress, Equals, ips[0].Address)
599 c.Check(newNIC.PrivateIPs, DeepEquals, ips)
600 c.Check(newNIC.Groups, HasLen, 1)
601 c.Check(newNIC.Groups[0].Id, Equals, group.Id)
602 c.Check(newNIC.Attachment.Status, Matches, `^(attaching|attached)$`)
603 c.Check(newNIC.Attachment.DeviceIndex, Equals, 0)
604 c.Check(newNIC.Attachment.DeleteOnTermination, Equals, true)
605}
606
469func idsOnly(gs []ec2.SecurityGroup) []ec2.SecurityGroup {607func idsOnly(gs []ec2.SecurityGroup) []ec2.SecurityGroup {
470 for i := range gs {608 for i := range gs {
471 gs[i].Name = ""609 gs[i].Name = ""
@@ -481,14 +619,37 @@
481}619}
482620
483func (s *ServerTests) TestGroupFiltering(c *C) {621func (s *ServerTests) TestGroupFiltering(c *C) {
484 g := make([]ec2.SecurityGroup, 4)622 vpcResp, err := s.ec2.CreateVPC("10.5.0.0/16", "")
623 c.Assert(err, IsNil)
624 vpcId := vpcResp.VPC.Id
625 defer s.deleteVPCs(c, []string{vpcId})
626
627 subResp := s.createSubnet(c, vpcId, "10.5.1.0/24", "")
628 subId := subResp.Subnet.Id
629 defer s.deleteSubnets(c, []string{subId})
630
631 g := make([]ec2.SecurityGroup, 5)
485 for i := range g {632 for i := range g {
486 resp, err := s.ec2.CreateSecurityGroup(sessionName(fmt.Sprintf("testgroup%d", i)), fmt.Sprintf("testdescription%d", i))633 var resp *ec2.CreateSecurityGroupResp
634 gid := sessionName(fmt.Sprintf("testgroup%d", i))
635 desc := fmt.Sprintf("testdescription%d", i)
636 if i == 0 {
637 // Create the first one as a VPC group.
638 gid += " vpc"
639 desc += " vpc"
640 resp, err = s.ec2.CreateSecurityGroupVPC(vpcId, gid, desc)
641 } else {
642 resp, err = s.ec2.CreateSecurityGroup(gid, desc)
643 }
487 c.Assert(err, IsNil)644 c.Assert(err, IsNil)
488 g[i] = resp.SecurityGroup645 g[i] = resp.SecurityGroup
489 c.Logf("group %d: %v", i, g[i])646 c.Logf("group %d: %v", i, g[i])
490 }647 }
491 defer s.deleteGroups(c, g)648 // Reorder the groups below, so that g[3] is first (some of the
649 // reset depend on it, so they can't be deleted before g[3]). A
650 // slight optimization for local live tests, so that we don't need
651 // to wait 5s each time deleteGroups runs.
652 defer s.deleteGroups(c, []ec2.SecurityGroup{g[3], g[0], g[1], g[2], g[4]})
492653
493 perms := [][]ec2.IPPerm{654 perms := [][]ec2.IPPerm{
494 {{655 {{
@@ -501,17 +662,17 @@
501 Protocol: "tcp",662 Protocol: "tcp",
502 FromPort: 200,663 FromPort: 200,
503 ToPort: 300,664 ToPort: 300,
504 SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}},665 SourceGroups: []ec2.UserSecurityGroup{{Id: g[2].Id}},
505 }},666 }},
506 {{667 {{
507 Protocol: "udp",668 Protocol: "udp",
508 FromPort: 200,669 FromPort: 200,
509 ToPort: 400,670 ToPort: 400,
510 SourceGroups: []ec2.UserSecurityGroup{{Id: g[1].Id}},671 SourceGroups: []ec2.UserSecurityGroup{{Id: g[2].Id}},
511 }},672 }},
512 }673 }
513 for i, ps := range perms {674 for i, ps := range perms {
514 _, err := s.ec2.AuthorizeSecurityGroup(g[i], ps)675 _, err := s.ec2.AuthorizeSecurityGroup(g[i+1], ps)
515 c.Assert(err, IsNil)676 c.Assert(err, IsNil)
516 }677 }
517678
@@ -541,7 +702,7 @@
541 tests := []groupTest{702 tests := []groupTest{
542 {703 {
543 about: "check that SecurityGroups returns all groups",704 about: "check that SecurityGroups returns all groups",
544 results: groups(0, 1, 2, 3),705 results: groups(0, 1, 2, 3, 4),
545 allowExtra: true,706 allowExtra: true,
546 }, {707 }, {
547 about: "check that specifying two group ids returns them",708 about: "check that specifying two group ids returns them",
@@ -549,8 +710,8 @@
549 results: groups(0, 2),710 results: groups(0, 2),
550 }, {711 }, {
551 about: "check that specifying names only works",712 about: "check that specifying names only works",
552 groups: namesOnly(groups(0, 2)),713 groups: namesOnly(groups(1, 2)),
553 results: groups(0, 2),714 results: groups(1, 2),
554 }, {715 }, {
555 about: "check that specifying a non-existent group id gives an error",716 about: "check that specifying a non-existent group id gives an error",
556 groups: append(groups(0), ec2.SecurityGroup{Id: "sg-eeeeeeeee"}),717 groups: append(groups(0), ec2.SecurityGroup{Id: "sg-eeeeeeeee"}),
@@ -577,11 +738,12 @@
577 },738 },
578 filterCheck("description", "testdescription1", groups(1)),739 filterCheck("description", "testdescription1", groups(1)),
579 filterCheck("group-name", g[2].Name, groups(2)),740 filterCheck("group-name", g[2].Name, groups(2)),
580 filterCheck("ip-permission.cidr", "1.2.3.4/32", groups(0)),741 filterCheck("ip-permission.cidr", "1.2.3.4/32", groups(1)),
581 filterCheck("ip-permission.group-name", g[1].Name, groups(1, 2)),742 filterCheck("ip-permission.group-name", g[2].Name, groups(2, 3)),
582 filterCheck("ip-permission.protocol", "udp", groups(2)),743 filterCheck("ip-permission.protocol", "udp", groups(3)),
583 filterCheck("ip-permission.from-port", "200", groups(1, 2)),744 filterCheck("ip-permission.from-port", "200", groups(2, 3)),
584 filterCheck("ip-permission.to-port", "200", groups(0)),745 filterCheck("ip-permission.to-port", "200", groups(1)),
746 filterCheck("vpc-id", vpcId, groups(0)),
585 // TODO owner-id747 // TODO owner-id
586 }748 }
587 for i, t := range tests {749 for i, t := range tests {
588750
=== modified file 'ec2/ec2test/server.go'
--- ec2/ec2test/server.go 2014-02-07 12:59:16 +0000
+++ ec2/ec2test/server.go 2014-02-07 12:59:16 +0000
@@ -85,6 +85,8 @@
85 reservation *reservation85 reservation *reservation
86 instType string86 instType string
87 state ec2.InstanceState87 state ec2.InstanceState
88 subnetId string
89 vpcId string
88}90}
8991
90// permKey represents permission for a given security92// permKey represents permission for a given security
@@ -150,6 +152,8 @@
150 return g.hasPerm(func(k permKey) bool { return k.protocol == value }), nil152 return g.hasPerm(func(k permKey) bool { return k.protocol == value }), nil
151 case "owner-id":153 case "owner-id":
152 return value == ownerId, nil154 return value == ownerId, nil
155 case "vpc-id":
156 return g.vpcId == value, nil
153 }157 }
154 return false, fmt.Errorf("unknown attribute %q", attr)158 return false, fmt.Errorf("unknown attribute %q", attr)
155}159}
@@ -527,7 +531,6 @@
527 // AvailZone ?531 // AvailZone ?
528 // GroupName tag532 // GroupName tag
529 // Monitoring ignore?533 // Monitoring ignore?
530 // SubnetId ?
531 // DisableAPITermination bool534 // DisableAPITermination bool
532 // ShutdownBehavior string535 // ShutdownBehavior string
533 // PrivateIPAddress string536 // PrivateIPAddress string
@@ -535,6 +538,16 @@
535 srv.mu.Lock()538 srv.mu.Lock()
536 defer srv.mu.Unlock()539 defer srv.mu.Unlock()
537540
541 var vpcId string
542 subnetId := req.Form.Get("SubnetId")
543 if subnetId != "" {
544 sub, found := srv.subnets[subnetId]
545 if !found {
546 fatalf(400, "InvalidSubnetID.NotFound", "subnet %s not found", subnetId)
547 }
548 vpcId = sub.VPCId
549 }
550
538 // make sure that form fields are correct before creating the reservation.551 // make sure that form fields are correct before creating the reservation.
539 instType := req.Form.Get("InstanceType")552 instType := req.Form.Get("InstanceType")
540 imageId := req.Form.Get("ImageId")553 imageId := req.Form.Get("ImageId")
@@ -547,7 +560,7 @@
547 resp.OwnerId = ownerId560 resp.OwnerId = ownerId
548561
549 for i := 0; i < max; i++ {562 for i := 0; i < max; i++ {
550 inst := srv.newInstance(r, instType, imageId, srv.initialInstanceState)563 inst := srv.newInstance(r, instType, imageId, srv.initialInstanceState, subnetId, vpcId)
551 inst.UserData = userData564 inst.UserData = userData
552 resp.Instances = append(resp.Instances, inst.ec2instance())565 resp.Instances = append(resp.Instances, inst.ec2instance())
553 }566 }
@@ -566,10 +579,15 @@
566 return nil579 return nil
567}580}
568581
569// NewInstances creates n new instances in srv with the given instance type,582// NewInstancesVPC creates n new VPC instances in srv with the given
570// image ID, initial state and security groups. If any group does not already583// instance type, image ID, initial state, and security groups,
571// exist, it will be created. NewInstances returns the ids of the new instances.584// belonging to the given vpcId and subnetId. If any group does not
572func (srv *Server) NewInstances(n int, instType string, imageId string, state ec2.InstanceState, groups []ec2.SecurityGroup) []string {585// already exist, it will be created. NewInstancesVPC returns the ids
586// of the new instances.
587//
588// If vpcId and subnetId are both empty, this call is equivalent to
589// calling NewInstances.
590func (srv *Server) NewInstancesVPC(vpcId, subnetId string, n int, instType string, imageId string, state ec2.InstanceState, groups []ec2.SecurityGroup) []string {
573 srv.mu.Lock()591 srv.mu.Lock()
574 defer srv.mu.Unlock()592 defer srv.mu.Unlock()
575593
@@ -585,13 +603,21 @@
585603
586 ids := make([]string, n)604 ids := make([]string, n)
587 for i := 0; i < n; i++ {605 for i := 0; i < n; i++ {
588 inst := srv.newInstance(r, instType, imageId, state)606 inst := srv.newInstance(r, instType, imageId, state, subnetId, vpcId)
589 ids[i] = inst.id()607 ids[i] = inst.id()
590 }608 }
591 return ids609 return ids
592}610}
593611
594func (srv *Server) newInstance(r *reservation, instType string, imageId string, state ec2.InstanceState) *Instance {612// NewInstances creates n new instances in srv with the given instance
613// type, image ID, initial state, and security groups. If any group
614// does not already exist, it will be created. NewInstances returns
615// the ids of the new instances.
616func (srv *Server) NewInstances(n int, instType string, imageId string, state ec2.InstanceState, groups []ec2.SecurityGroup) []string {
617 return srv.NewInstancesVPC("", "", n, instType, imageId, state, groups)
618}
619
620func (srv *Server) newInstance(r *reservation, instType string, imageId string, state ec2.InstanceState, subnetId, vpcId string) *Instance {
595 inst := &Instance{621 inst := &Instance{
596 seq: srv.maxId.next(),622 seq: srv.maxId.next(),
597 instType: instType,623 instType: instType,
@@ -599,6 +625,10 @@
599 state: state,625 state: state,
600 reservation: r,626 reservation: r,
601 }627 }
628 if vpcId != "" && subnetId != "" {
629 inst.vpcId = vpcId
630 inst.subnetId = subnetId
631 }
602 id := inst.id()632 id := inst.id()
603 srv.instances[id] = inst633 srv.instances[id] = inst
604 r.instances[id] = inst634 r.instances[id] = inst
@@ -669,6 +699,8 @@
669 IPAddress: fmt.Sprintf("8.0.0.%d", inst.seq%256),699 IPAddress: fmt.Sprintf("8.0.0.%d", inst.seq%256),
670 PrivateIPAddress: fmt.Sprintf("127.0.0.%d", inst.seq%256),700 PrivateIPAddress: fmt.Sprintf("127.0.0.%d", inst.seq%256),
671 State: inst.state,701 State: inst.state,
702 VPCId: inst.vpcId,
703 SubnetId: inst.subnetId,
672 // TODO the rest704 // TODO the rest
673 }705 }
674}706}
@@ -679,6 +711,10 @@
679 return value == "i386", nil711 return value == "i386", nil
680 case "instance-id":712 case "instance-id":
681 return inst.id() == value, nil713 return inst.id() == value, nil
714 case "subnet-id":
715 return inst.subnetId == value, nil
716 case "vpc-id":
717 return inst.vpcId == value, nil
682 case "instance.group-id", "group-id":718 case "instance.group-id", "group-id":
683 for _, g := range inst.reservation.groups {719 for _, g := range inst.reservation.groups {
684 if g.id == value {720 if g.id == value {
@@ -731,6 +767,10 @@
731 id: fmt.Sprintf("sg-%d", srv.groupId.next()),767 id: fmt.Sprintf("sg-%d", srv.groupId.next()),
732 perms: make(map[permKey]bool),768 perms: make(map[permKey]bool),
733 }769 }
770 vpcId := req.Form.Get("VpcId")
771 if vpcId != "" {
772 g.vpcId = vpcId
773 }
734 srv.groups[g.id] = g774 srv.groups[g.id] = g
735 // we define a local type for this because ec2.CreateSecurityGroupResp775 // we define a local type for this because ec2.CreateSecurityGroupResp
736 // contains SecurityGroup, but the response to this request776 // contains SecurityGroup, but the response to this request
@@ -782,6 +822,14 @@
782 if len(insts) > 0 && !insts[inst] {822 if len(insts) > 0 && !insts[inst] {
783 continue823 continue
784 }824 }
825 // make instances in state "shutting-down" to transition
826 // to "terminated" first, so we can simulate: shutdown,
827 // subsequent refresh of the state with Instances(),
828 // terminated.
829 if inst.state == ShuttingDown {
830 inst.state = Terminated
831 }
832
785 ok, err := f.ok(inst)833 ok, err := f.ok(inst)
786 if ok {834 if ok {
787 instance := inst.ec2instance()835 instance := inst.ec2instance()
788836
=== modified file 'ec2/networkinterfaces_test.go'
--- ec2/networkinterfaces_test.go 2014-02-07 12:59:16 +0000
+++ ec2/networkinterfaces_test.go 2014-02-07 12:59:16 +0000
@@ -214,7 +214,7 @@
214 inst := instList.Instances[0]214 inst := instList.Instances[0]
215 c.Assert(inst, NotNil)215 c.Assert(inst, NotNil)
216 instId := inst.InstanceId216 instId := inst.InstanceId
217 defer s.ec2.TerminateInstances([]string{instId})217 defer terminateInstances(c, s.ec2, []string{instId})
218218
219 ips1 := []ec2.PrivateIP{{Address: "10.3.1.10", IsPrimary: true}}219 ips1 := []ec2.PrivateIP{{Address: "10.3.1.10", IsPrimary: true}}
220 resp1, err := s.ec2.CreateNetworkInterface(ec2.NetworkInterfaceOptions{220 resp1, err := s.ec2.CreateNetworkInterface(ec2.NetworkInterfaceOptions{
221221
=== modified file 'ec2/responses_test.go'
--- ec2/responses_test.go 2014-02-07 12:59:16 +0000
+++ ec2/responses_test.go 2014-02-07 12:59:16 +0000
@@ -9,7 +9,7 @@
99
10// http://goo.gl/Mcm3b10// http://goo.gl/Mcm3b
11var RunInstancesExample = `11var RunInstancesExample = `
12<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2011-12-15/"> 12<RunInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-13/">
13 <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId> 13 <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
14 <reservationId>r-47a5402e</reservationId> 14 <reservationId>r-47a5402e</reservationId>
15 <ownerId>999988887777</ownerId>15 <ownerId>999988887777</ownerId>
@@ -40,9 +40,82 @@
40 <state>enabled</state>40 <state>enabled</state>
41 </monitoring>41 </monitoring>
42 <virtualizationType>paravirtual</virtualizationType>42 <virtualizationType>paravirtual</virtualizationType>
43 <subnetId>subnet-id</subnetId>
44 <vpcId>vpc-id</vpcId>
45 <sourceDestCheck>true</sourceDestCheck>
43 <clientToken/>46 <clientToken/>
44 <tagSet/>47 <tagSet/>
45 <hypervisor>xen</hypervisor>48 <hypervisor>xen</hypervisor>
49 <networkInterfaceSet>
50 <item>
51 <networkInterfaceId>eni-c6bb50ae</networkInterfaceId>
52 <subnetId>subnet-id</subnetId>
53 <vpcId>vpc-id</vpcId>
54 <description>eth0</description>
55 <ownerId>111122223333</ownerId>
56 <status>in-use</status>
57 <privateIpAddress>10.0.0.25</privateIpAddress>
58 <macAddress>11:22:33:44:55:66</macAddress>
59 <sourceDestCheck>true</sourceDestCheck>
60 <groupSet>
61 <item>
62 <groupId>sg-1</groupId>
63 <groupName>vpc sg-1</groupName>
64 </item>
65 <item>
66 <groupId>sg-2</groupId>
67 <groupName>vpc sg-2</groupName>
68 </item>
69 </groupSet>
70 <attachment>
71 <attachmentId>eni-attach-0326646a</attachmentId>
72 <deviceIndex>0</deviceIndex>
73 <status>attaching</status>
74 <attachTime>2011-12-20T08:29:31.000Z</attachTime>
75 <deleteOnTermination>true</deleteOnTermination>
76 </attachment>
77 <privateIpAddressesSet>
78 <item>
79 <privateIpAddress>10.0.0.25</privateIpAddress>
80 <primary>true</primary>
81 </item>
82 </privateIpAddressesSet>
83 </item>
84 <item>
85 <networkInterfaceId>eni-id</networkInterfaceId>
86 <subnetId>subnet-id</subnetId>
87 <vpcId>vpc-id</vpcId>
88 <description/>
89 <ownerId>111122223333</ownerId>
90 <status>in-use</status>
91 <privateIpAddress>10.0.1.10</privateIpAddress>
92 <macAddress>11:22:33:44:55:66</macAddress>
93 <sourceDestCheck>true</sourceDestCheck>
94 <groupSet>
95 <item>
96 <groupId>sg-id</groupId>
97 <groupName>vpc default</groupName>
98 </item>
99 </groupSet>
100 <attachment>
101 <attachmentId>eni-attach-id</attachmentId>
102 <deviceIndex>1</deviceIndex>
103 <status>attaching</status>
104 <attachTime>2011-12-20T08:29:31.000Z</attachTime>
105 <deleteOnTermination>false</deleteOnTermination>
106 </attachment>
107 <privateIpAddressesSet>
108 <item>
109 <privateIpAddress>10.0.1.10</privateIpAddress>
110 <primary>true</primary>
111 </item>
112 <item>
113 <privateIpAddress>10.0.1.20</privateIpAddress>
114 <primary>false</primary>
115 </item>
116 </privateIpAddressesSet>
117 </item>
118 </networkInterfaceSet>
46 </item>119 </item>
47 <item>120 <item>
48 <instanceId>i-2bc64242</instanceId>121 <instanceId>i-2bc64242</instanceId>

Subscribers

People subscribed via source and target branches