aws-eni 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/aws-eni +10 -3
- data/lib/aws-eni.rb +59 -24
- data/lib/aws-eni/interface.rb +34 -15
- data/lib/aws-eni/meta.rb +33 -22
- data/lib/aws-eni/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e3020c6ae20a0d6a7027f065560e00c5764dbfb
|
4
|
+
data.tar.gz: f4827152a5efad857d3a8818e7f882a5a25e15ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd1bb10082ba681b5452411d8f2e75a39cc9b6d7227e3e8b996de89b5d619072c3295a40c63db98e0e11bb2c938e8c01c7b736fd5122f6bd4da45d896a65b435
|
7
|
+
data.tar.gz: 9c0e454b7797af337b50829ee4231ee9ee2f3f558343980023f4bb5078f6fb0c17fe0d81e5954473b90e67669e25adbb710ffc808143bb9e7b9eeb2a47e52ed4
|
data/bin/aws-eni
CHANGED
@@ -24,7 +24,7 @@ desc 'Display all system commands and warnings'
|
|
24
24
|
switch [:V,:verbose], negatable: false
|
25
25
|
|
26
26
|
pre do |opt|
|
27
|
-
Aws::ENI.timeout
|
27
|
+
Aws::ENI.timeout 120
|
28
28
|
Aws::ENI.verbose opt[:verbose]
|
29
29
|
true
|
30
30
|
end
|
@@ -398,11 +398,14 @@ command [:unassign] do |c|
|
|
398
398
|
c.desc 'Release any associated public IP address'
|
399
399
|
c.switch [:r,:release], negatable: false
|
400
400
|
|
401
|
+
c.desc 'Do not wait for new interface state to propogate'
|
402
|
+
c.switch [:n,:noblock], negatable: false
|
403
|
+
|
401
404
|
c.action do |global,opts,args|
|
402
405
|
params = parse_args args, :interface_id, :device_name, :private_ip
|
403
406
|
help_now! "Missing argument" unless params[:private_ip]
|
404
|
-
params[:release] = opts[:release]
|
405
407
|
|
408
|
+
params.merge! release: opts[:release], block: !opts[:noblock]
|
406
409
|
unassign = Aws::ENI.unassign_secondary_ip(params[:private_ip], params)
|
407
410
|
if unassign[:released]
|
408
411
|
puts "EIP #{unassign[:public_ip]} (#{unassign[:allocation_id]}) dissociated from #{unassign[:private_ip]} and released"
|
@@ -463,11 +466,15 @@ command [:dissociate] do |c|
|
|
463
466
|
c.desc 'Release the associated public IP address'
|
464
467
|
c.switch [:r,:release], negatable: false
|
465
468
|
|
469
|
+
c.desc 'Do not wait for new interface state to propogate'
|
470
|
+
c.switch [:noblock], negatable: false
|
471
|
+
|
466
472
|
c.action do |global,opts,args|
|
467
473
|
params = parse_args args, :private_ip, :public_ip, :allocation_id, :association_id, :interface_id, :device_name
|
468
|
-
params[:release] = opts[:release]
|
469
474
|
address = params[:private_ip] || params[:public_ip] || params[:association_id] || params[:allocation_id]
|
470
475
|
help_now! "Missing argument" unless address
|
476
|
+
|
477
|
+
params.merge! release: opts[:release], block: !opts[:noblock]
|
471
478
|
dissoc = Aws::ENI.dissociate_elastic_ip(address, params)
|
472
479
|
if dissoc[:released]
|
473
480
|
puts "EIP #{dissoc[:public_ip]} (#{dissoc[:allocation_id]}) dissociated from #{dissoc[:private_ip]} on #{dissoc[:device_name]} (#{dissoc[:interface_id]}) and released"
|
data/lib/aws-eni.rb
CHANGED
@@ -9,16 +9,21 @@ module Aws
|
|
9
9
|
module ENI
|
10
10
|
extend self
|
11
11
|
|
12
|
+
# class level thread-safe lock
|
13
|
+
@lock = Mutex.new
|
14
|
+
|
12
15
|
def environment
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
@lock.synchronize do
|
17
|
+
@environment ||= Meta.connection do
|
18
|
+
hwaddr = Meta.instance('network/interfaces/macs/').lines.first.strip.chomp('/')
|
19
|
+
{
|
20
|
+
instance_id: Meta.instance('instance-id'),
|
21
|
+
availability_zone: Meta.instance('placement/availability-zone'),
|
22
|
+
region: Meta.instance('placement/availability-zone').sub(/^(.*)[a-z]$/,'\1'),
|
23
|
+
vpc_id: Meta.interface(hwaddr, 'vpc-id'),
|
24
|
+
vpc_cidr: Meta.interface(hwaddr, 'vpc-ipv4-cidr-block')
|
25
|
+
}.freeze
|
26
|
+
end
|
22
27
|
end
|
23
28
|
rescue Errors::MetaNotFound
|
24
29
|
raise Errors::EnvironmentError, 'Unable to detect VPC, library incompatible with EC2-Classic'
|
@@ -33,7 +38,7 @@ module Aws
|
|
33
38
|
|
34
39
|
def timeout(new_default = nil)
|
35
40
|
@timeout = new_default.to_i if new_default
|
36
|
-
@timeout ||=
|
41
|
+
@timeout ||= 120
|
37
42
|
end
|
38
43
|
|
39
44
|
def verbose(set_verbose = nil)
|
@@ -117,7 +122,7 @@ module Aws
|
|
117
122
|
end
|
118
123
|
|
119
124
|
if do_block || do_config || do_enable
|
120
|
-
wait_for 'the interface to attach', rescue: Errors::
|
125
|
+
wait_for 'the interface to attach', rescue: Errors::InvalidInterface do
|
121
126
|
device.exists? && Client.interface_attached(device.interface_id)
|
122
127
|
end
|
123
128
|
end
|
@@ -261,7 +266,7 @@ module Aws
|
|
261
266
|
new_ip = options[:private_ip]
|
262
267
|
|
263
268
|
if do_block && !device.enabled?
|
264
|
-
raise Errors::InvalidParameter, "Interface #{device.name} is not enabled (cannot
|
269
|
+
raise Errors::InvalidParameter, "Interface #{device.name} is not enabled (cannot test connection)"
|
265
270
|
end
|
266
271
|
|
267
272
|
if new_ip
|
@@ -273,29 +278,34 @@ module Aws
|
|
273
278
|
private_ip_addresses: [new_ip],
|
274
279
|
allow_reassignment: false
|
275
280
|
)
|
276
|
-
wait_for 'private ip address to be assigned' do
|
277
|
-
Client.interface_private_ips(interface_id).include?(new_ip) ||
|
278
|
-
device.local_ips.include?(new_ip)
|
279
|
-
end
|
280
281
|
else
|
281
282
|
Client.assign_private_ip_addresses(
|
282
283
|
network_interface_id: interface_id,
|
283
284
|
secondary_private_ip_address_count: 1,
|
284
285
|
allow_reassignment: false
|
285
286
|
)
|
286
|
-
wait_for 'new private
|
287
|
-
|
288
|
-
|
289
|
-
new_ip = new_ips.first if new_ips
|
287
|
+
wait_for 'new private IP address to be assigned' do
|
288
|
+
client_ips = Client.interface_private_ips(interface_id)
|
289
|
+
new_ip = (client_ips - current_ips).first || (device.meta_ips - current_ips).first
|
290
290
|
end
|
291
291
|
end
|
292
292
|
|
293
293
|
if do_config
|
294
294
|
device.add_alias(new_ip)
|
295
|
-
if do_block && !Interface.test(new_ip, target: device.gateway)
|
295
|
+
if do_block && !Interface.test(new_ip, target: device.gateway, timeout: timeout)
|
296
296
|
raise Errors::TimeoutError, 'Timed out waiting for IP address to become active'
|
297
297
|
end
|
298
298
|
end
|
299
|
+
|
300
|
+
if do_block
|
301
|
+
# ensure new state has propagated to avoid race conditions
|
302
|
+
wait_for 'new private IP address to appear in metadata' do
|
303
|
+
device.meta_ips.include?(new_ip)
|
304
|
+
end
|
305
|
+
wait_for 'new private IP address to appear in EC2 resource' do
|
306
|
+
Client.interface_private_ips(interface_id).include?(new_ip)
|
307
|
+
end
|
308
|
+
end
|
299
309
|
{
|
300
310
|
private_ip: new_ip,
|
301
311
|
interface_id: interface_id,
|
@@ -308,6 +318,7 @@ module Aws
|
|
308
318
|
# remove a private ip using the AWS api and remove it from local config
|
309
319
|
def unassign_secondary_ip(private_ip, options = {})
|
310
320
|
do_release = !!options[:release]
|
321
|
+
do_block = options[:block] != false
|
311
322
|
|
312
323
|
find = options[:device_name] || options[:device_number] || options[:interface_id] || private_ip
|
313
324
|
device = Interface[find].assert(
|
@@ -335,6 +346,16 @@ module Aws
|
|
335
346
|
network_interface_id: interface[:network_interface_id],
|
336
347
|
private_ip_addresses: [private_ip]
|
337
348
|
)
|
349
|
+
|
350
|
+
if do_block
|
351
|
+
# ensure new state has propagated to avoid race conditions
|
352
|
+
wait_for 'private IP address to be removed from metadata' do
|
353
|
+
!device.meta_ips.include?(private_ip)
|
354
|
+
end
|
355
|
+
wait_for 'private IP address to be removed from EC2 resource' do
|
356
|
+
!Client.interface_private_ips(device.interface_id).include?(private_ip)
|
357
|
+
end
|
358
|
+
end
|
338
359
|
{
|
339
360
|
private_ip: private_ip,
|
340
361
|
device_name: device.name,
|
@@ -402,8 +423,14 @@ module Aws
|
|
402
423
|
allow_reassociation: false
|
403
424
|
)
|
404
425
|
|
405
|
-
if do_block
|
406
|
-
|
426
|
+
if do_block
|
427
|
+
# ensure new state has propagated to avoid race conditions
|
428
|
+
wait_for 'public IP address to appear in metadata' do
|
429
|
+
device.public_ips.has_value?(eip[:public_ip])
|
430
|
+
end
|
431
|
+
if !Interface.test(private_ip, timeout: timeout)
|
432
|
+
raise Errors::TimeoutError, 'Timed out waiting for IP address to become active'
|
433
|
+
end
|
407
434
|
end
|
408
435
|
{
|
409
436
|
private_ip: private_ip,
|
@@ -420,6 +447,7 @@ module Aws
|
|
420
447
|
# optionally release the public ip
|
421
448
|
def dissociate_elastic_ip(address, options = {})
|
422
449
|
do_release = !!options[:release]
|
450
|
+
do_block = options[:block] != false
|
423
451
|
|
424
452
|
# assert device attributes if we've specified a device
|
425
453
|
if find = options[:device_name] || options[:device_number]
|
@@ -463,6 +491,13 @@ module Aws
|
|
463
491
|
|
464
492
|
Client.disassociate_address(association_id: eip[:association_id])
|
465
493
|
Client.release_address(allocation_id: eip[:allocation_id]) if do_release
|
494
|
+
|
495
|
+
if device && do_block
|
496
|
+
# ensure new state has propagated to avoid race conditions
|
497
|
+
wait_for 'public IP address to be removed from metadata' do
|
498
|
+
!device.public_ips.has_value?(eip[:public_ip])
|
499
|
+
end
|
500
|
+
end
|
466
501
|
{
|
467
502
|
private_ip: eip[:private_ip_address],
|
468
503
|
device_name: device && device.name,
|
@@ -578,7 +613,7 @@ module Aws
|
|
578
613
|
def wait_for(task, options = {}, &block)
|
579
614
|
errors = [*options[:rescue]]
|
580
615
|
timeout = options[:timeout] || self.timeout
|
581
|
-
interval = options[:interval] || 0.
|
616
|
+
interval = options[:interval] || 0.3
|
582
617
|
|
583
618
|
until timeout < 0
|
584
619
|
begin
|
data/lib/aws-eni/interface.rb
CHANGED
@@ -6,6 +6,9 @@ module Aws
|
|
6
6
|
module ENI
|
7
7
|
class Interface
|
8
8
|
|
9
|
+
# Class level thread-safe lock
|
10
|
+
@lock = Mutex.new
|
11
|
+
|
9
12
|
class << self
|
10
13
|
include Enumerable
|
11
14
|
|
@@ -15,8 +18,10 @@ module Aws
|
|
15
18
|
def [](index)
|
16
19
|
case index
|
17
20
|
when Integer
|
18
|
-
@
|
19
|
-
|
21
|
+
@lock.synchronize do
|
22
|
+
@instance_cache ||= []
|
23
|
+
@instance_cache[index] ||= new("eth#{index}", false)
|
24
|
+
end
|
20
25
|
when nil
|
21
26
|
self[next_available_index]
|
22
27
|
when /^(?:eth)?([0-9]+)$/
|
@@ -144,6 +149,7 @@ module Aws
|
|
144
149
|
@name = name
|
145
150
|
@device_number = $1.to_i
|
146
151
|
@route_table = @device_number + 10000
|
152
|
+
@lock = Mutex.new
|
147
153
|
configure if auto_config
|
148
154
|
end
|
149
155
|
|
@@ -166,19 +172,21 @@ module Aws
|
|
166
172
|
|
167
173
|
# Validate and return basic interface metadata
|
168
174
|
def info
|
169
|
-
|
170
|
-
|
171
|
-
@meta_cache
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
175
|
+
@lock.synchronize do
|
176
|
+
hwaddr = self.hwaddr
|
177
|
+
unless @meta_cache && @meta_cache[:hwaddr] == hwaddr
|
178
|
+
@meta_cache = Meta.connection do
|
179
|
+
raise Errors::MetaBadResponse unless Meta.interface(hwaddr, '', not_found: nil)
|
180
|
+
{
|
181
|
+
hwaddr: hwaddr,
|
182
|
+
interface_id: Meta.interface(hwaddr, 'interface-id'),
|
183
|
+
subnet_id: Meta.interface(hwaddr, 'subnet-id'),
|
184
|
+
subnet_cidr: Meta.interface(hwaddr, 'subnet-ipv4-cidr-block')
|
185
|
+
}.freeze
|
186
|
+
end
|
179
187
|
end
|
188
|
+
@meta_cache
|
180
189
|
end
|
181
|
-
@meta_cache
|
182
190
|
rescue Errors::MetaConnectionFailed
|
183
191
|
raise Errors::InvalidInterface, "Interface #{name} could not be found in the EC2 instance meta-data"
|
184
192
|
end
|
@@ -210,13 +218,25 @@ module Aws
|
|
210
218
|
list.lines.grep(/inet ([0-9\.]+)\/.* #{name}/i){ $1 }
|
211
219
|
end
|
212
220
|
|
221
|
+
# Return an array of ip addresses found in our instance metadata
|
222
|
+
def meta_ips
|
223
|
+
# hack to use cached hwaddr when available since this is often polled
|
224
|
+
# continuously for changes
|
225
|
+
hwaddr = (@meta_cache && @meta_cache[:hwaddr]) || hwaddr
|
226
|
+
Meta.interface(hwaddr, 'local-ipv4s', cache: false).lines.map(&:strip)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Return a hash of local/public ip associations found in instance metadata
|
213
230
|
def public_ips
|
214
231
|
hwaddr = self.hwaddr
|
215
232
|
Hash[
|
216
233
|
Meta.connection do
|
217
234
|
Meta.interface(hwaddr, 'ipv4-associations/', not_found: '', cache: false).lines.map do |public_ip|
|
218
235
|
public_ip.strip!
|
219
|
-
|
236
|
+
unless private_ip = Meta.interface(hwaddr, "ipv4-associations/#{public_ip}", not_found: nil, cache: false)
|
237
|
+
raise Errors::MetaBadResponse
|
238
|
+
end
|
239
|
+
[ private_ip, public_ip ]
|
220
240
|
end
|
221
241
|
end
|
222
242
|
]
|
@@ -244,7 +264,6 @@ module Aws
|
|
244
264
|
changes = 0
|
245
265
|
prefix = self.prefix # prevent exists? check on each use
|
246
266
|
|
247
|
-
meta_ips = Meta.interface(hwaddr, 'local-ipv4s', cache: false).lines.map(&:strip)
|
248
267
|
local_primary, *local_aliases = local_ips
|
249
268
|
meta_primary, *meta_aliases = meta_ips
|
250
269
|
|
data/lib/aws-eni/meta.rb
CHANGED
@@ -5,6 +5,10 @@ require 'aws-eni/errors'
|
|
5
5
|
module Aws
|
6
6
|
module ENI
|
7
7
|
module Meta
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# Per-thread open connection storage
|
11
|
+
@connections = {}
|
8
12
|
|
9
13
|
# These are the errors we trap when attempting to talk to the instance
|
10
14
|
# metadata service. Any of these imply the service is not present, not
|
@@ -21,7 +25,7 @@ module Aws
|
|
21
25
|
|
22
26
|
# Perform a GET request on an open HTTP connection to the EC2 instance
|
23
27
|
# meta-data and return the body of any 200 response.
|
24
|
-
def
|
28
|
+
def get(path, options = {})
|
25
29
|
@cache ||= {}
|
26
30
|
if @cache[path] && options[:cache] != false
|
27
31
|
@cache[path]
|
@@ -43,39 +47,46 @@ module Aws
|
|
43
47
|
|
44
48
|
# Perform a GET request on the instance metadata and return the body of
|
45
49
|
# any 200 response.
|
46
|
-
def
|
50
|
+
def instance(path, options = {})
|
47
51
|
get("/latest/meta-data/#{path}", options)
|
48
52
|
end
|
49
53
|
|
50
54
|
# Perform a GET request on the interface metadata and return the body of
|
51
55
|
# any 200 response.
|
52
|
-
def
|
56
|
+
def interface(hwaddr, path, options = {})
|
53
57
|
instance("network/interfaces/macs/#{hwaddr}/#{path}", options)
|
54
58
|
end
|
55
59
|
|
56
60
|
# Open a connection and attempt to execute the block `retries` times.
|
57
61
|
# Can specify open and read timeouts in addition to the number of retries.
|
58
|
-
def
|
59
|
-
|
62
|
+
def connection(options = {})
|
63
|
+
raise ArgumentError, 'Must be called with a block' unless block_given?
|
64
|
+
attempts = 0
|
60
65
|
retries = options[:retries] || 5
|
61
|
-
|
62
|
-
|
63
|
-
http
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
66
|
+
|
67
|
+
if (http = @connections[Thread.current]) && http.active?
|
68
|
+
yield(http)
|
69
|
+
else
|
70
|
+
begin
|
71
|
+
http = Net::HTTP.new('169.254.169.254', '80', nil)
|
72
|
+
http.open_timeout = options[:open_timeout] || 5
|
73
|
+
http.read_timeout = options[:read_timeout] || 5
|
74
|
+
|
75
|
+
@connections[Thread.current] = http.start
|
76
|
+
yield(http)
|
77
|
+
rescue *FAILURES => e
|
78
|
+
if attempts < retries
|
79
|
+
# increasing cooldown time on each attempt
|
80
|
+
Kernel.sleep(1.2 ** attempts)
|
81
|
+
attempts += 1
|
82
|
+
retry
|
83
|
+
else
|
84
|
+
raise Errors::MetaConnectionFailed, "EC2 Metadata request failed after #{retries} retries."
|
85
|
+
end
|
86
|
+
ensure
|
87
|
+
http = @connections.delete(Thread.current)
|
88
|
+
http.finish if http && http.active?
|
76
89
|
end
|
77
|
-
ensure
|
78
|
-
@open_connection = nil
|
79
90
|
end
|
80
91
|
end
|
81
92
|
end
|
data/lib/aws-eni/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws-eni
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Greiling
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gli
|