Merge lp:~dimitern/goamz/vpc-instance-addons into lp:goamz
- vpc-instance-addons
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Dimiter Naydenov (community) | Approve | ||
Review via email: mp+205148@code.launchpad.net |
Commit message
Description of the change
ec2: Added NIC support for RunInstances
This extends RunInstances options to include
a list of NetworkInterfac
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 NetworkInterfac
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 terminateInstan
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 TestInstanceFil
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()).
Dimiter Naydenov (dimitern) wrote : | # |
- 59. By Dimiter Naydenov
-
Removed test import; Added TestRunInstancesVPC live test
Dimiter Naydenov (dimitern) wrote : | # |
Please take a look.
Roger Peppe (rogpeppe) wrote : | # |
LGTM with some suggestions below.
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:299: // given, a VPC-enabled instances will be started.
Cannot specify
s/instances/
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:/
File ec2/ec2t_test.go (right):
https:/
ec2/ec2t_
RUNNING!!!", len(ids)))
Do we really need to fail the test if we can't terminate the instances
immediately?
https:/
ec2/ec2t_
if you did:
delete(
you wouldn't need the
if left {
test in the range loop below.
https:/
ec2/ec2t_
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].
- 60. By Dimiter Naydenov
-
Fixed a test
- 61. By Dimiter Naydenov
-
Changes after reivew
Dimiter Naydenov (dimitern) wrote : | # |
Please take a look.
https:/
File ec2/ec2.go (right):
https:/
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/
> 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:/
File ec2/ec2t_test.go (right):
https:/
ec2/ec2t_
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:/
ec2/ec2t_
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:/
ec2/ec2t_
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.
- 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.
Dimiter Naydenov (dimitern) wrote : | # |
Please take a look.
Gustavo Niemeyer (niemeyer) wrote : | # |
LGTM, but please ask for at least one more review.
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:232: SecondaryPrivat
s/IPsCount/
Gustavo Niemeyer (niemeyer) wrote : | # |
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:204: // NetworkInterfac
single network
Ah, and as we discussed, this type should be something along the lines
of
RunNetwork
It's an unfortunate name, but I cannot come up with anything better that
is not absurdly long.
For the record, the generic name "NetworkInterfa
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
CreateNetworkIn
NetworkInterfac
Nate Finch (natefinch) wrote : | # |
LGTM, just a couple nice-to-have changes
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:305: if options.SubnetId != "" ||
len(options.
I'd like to have this extracted into a standalone function so it can be
tested by itself.
https:/
ec2/ec2.go:362: for i, ni := range options.
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:/
File ec2/ec2t_test.go (right):
https:/
ec2/ec2t_
RUNNING!!!", ids))
either len(ids) or %v here, right?
https:/
ec2/ec2t_
do we really want tests that might wait 10 minutes?
Dimiter Naydenov (dimitern) wrote : | # |
*** Submitted:
ec2: Added NIC support for RunInstances
This extends RunInstances options to include
a list of NetworkInterfac
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 NetworkInterfac
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 terminateInstan
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 TestInstanceFil
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:/
https:/
File ec2/ec2.go (right):
https:/
ec2/ec2.go:204: // NetworkInterfac
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 "NetworkInterfa
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 CreateNetworkIn
need one
> more, will we name it NetworkInterfac
Done.
https:/
ec2/ec2.go:232: SecondaryPrivat
On 2014/02/12 14:49:53, niemeyer wrote:
> s/IPsCount/
Done.
https:/
ec2/ec2.go:305: if options.SubnetId != "" ||
len(options.
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:/
ec2/ec2.g...
Dimiter Naydenov (dimitern) wrote : | # |
Post-submit approval.
Preview Diff
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> |
Reviewers: mp+205148_ code.launchpad. net,
Message:
Please take a look.
Description:
ec2: Added NIC support for RunInstances
This extends RunInstances options to include eSpec options. They
a list of NetworkInterfac
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 NetworkInterfac eSpec options are provided,
RunInstances() will use the latest AWS API version
(2013-10-15), otherwise it uses the default version
(2011-12-15).
Modified terminateInstan ces() 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 tering to
Instance and securityGroup types, and also changed
TestGroupFiltering and TestInstanceFil
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: /code.launchpad .net/~dimitern/ goamz/nic- api-calls/ +merge/ 204912
https:/
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/60620043/
Affected files (+407, -59 lines): server. go rfaces_ test.go test.go
A [revision details]
M ec2/ec2.go
M ec2/ec2_test.go
M ec2/ec2t_test.go
M ec2/ec2test/
M ec2/networkinte
M ec2/responses_