aws-eni 0.2.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5bd70f82e45aa8e1c89003fefe1118b3c3e7a0ff
4
- data.tar.gz: 8f7c1cbbb2136f722d34f8d414a167327e6920f0
3
+ metadata.gz: fe0e076295a17d9cc4c63a7227f83be289fcb4b3
4
+ data.tar.gz: 51dfdb49ec4bba211e5105f3eafed74fc210cdcc
5
5
  SHA512:
6
- metadata.gz: 3d7d4e8422d6232c893375f284b4cbc33b232c2cbc2af731be6f679540c017ab97e6162a22ce52ca011a49ca11dac653f862b51bba9893ebf5ed6a78ca280b89
7
- data.tar.gz: 09dafcec8f9c13900c99754f0dfad80b7a912da55512de34f163a8a546fc4cdd2efaba04b8eb34032f5dd6857a95e35c9cce5c686272818cf32093c81af0ec24
6
+ metadata.gz: aa9fd08058dfda15e1c473108105d37e6aaf1c64a322fe249a1c5f469b494399895395f2df2c18d12df7a1d5cbac8811393693e48d032d5824c0b70fc6880b72
7
+ data.tar.gz: 7da3b4bd53b7944f744e02c76bc9dc7a691cde2b76ecb708dba83006045e21a3dd30e4c8f35cc0f149762079e4d94f4a9c732e38c2bfb4e54a0fe7c886ccdfae
@@ -24,6 +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 90
27
28
  Aws::ENI::IFconfig.verbose = opt[:verbose]
28
29
  true
29
30
  end
@@ -236,13 +237,13 @@ arg 'security-groups', :optional
236
237
  arg 'ip-address', :optional
237
238
  command [:attach] do |c|
238
239
  c.desc 'Do not configure or enable the device after attachment (implies block)'
239
- c.switch [:n,'no-config'], negatable: false
240
+ c.switch [:n,:noconfig], negatable: false
240
241
 
241
242
  c.desc 'Do not return until attachment is complete'
242
243
  c.switch [:b,:block], negatable: false
243
244
 
244
245
  c.action do |global,opts,args|
245
- config = !opts['no_config']
246
+ config = !opts[:noconfig]
246
247
  if args.first =~ /^eni-/
247
248
  help_now! 'Too many arguments' if args.count > 1
248
249
  id = args.first
@@ -275,6 +276,9 @@ command [:detach] do |c|
275
276
  c.desc 'Delete (or preserve) the unused ENI resource after dataching (implies block)'
276
277
  c.switch [:d,:delete], default_value: :not_provided # GLI behavior workaround
277
278
 
279
+ c.desc 'Release any associated public IP addresses'
280
+ c.switch [:r,:release], negatable: false
281
+
278
282
  c.desc 'Do not return until detachment is complete'
279
283
  c.switch [:b,:block], negatable: false
280
284
 
@@ -283,9 +287,18 @@ command [:detach] do |c|
283
287
  params = parse_args args, :interface_id, :device_name
284
288
  params[:block] = opts[:block]
285
289
  params[:delete] = opts[:delete] unless opts[:delete] == :not_provided
290
+ params[:release] = opts[:release]
286
291
  id = params[:device_name] || params[:interface_id]
287
292
 
288
293
  device = Aws::ENI.detach_interface(id, params)
294
+
295
+ device[:public_ips].each do |address|
296
+ if device[:released]
297
+ puts "EIP #{address[:public_ip]} (#{address[:allocation_id]}) dissociated and released"
298
+ else
299
+ puts "EIP #{address[:public_ip]} (#{address[:allocation_id]}) dissociated"
300
+ end
301
+ end
289
302
  if device[:deleted]
290
303
  puts "interface #{device[:interface_id]} detached from #{device[:device_name]} and deleted"
291
304
  else
@@ -310,10 +323,24 @@ command [:clean] do |c|
310
323
  c.desc 'Force deletion of all unattached interfaces which meet our criteria'
311
324
  c.switch [:f,:force], negatable: false
312
325
 
326
+ c.desc 'Release any associated public IP addresses'
327
+ c.switch [:r,:release], negatable: false
328
+
313
329
  c.action do |global,opts,args|
314
330
  help_now! "Too many arguments" if args.count > 1
315
- deleted = Aws::ENI.clean_interfaces(args.first, safe_mode: !opts[:force])
316
- puts "#{deleted[:count]} interfaces deleted"
331
+ deleted = Aws::ENI.clean_interfaces(args.first, safe_mode: !opts[:force], release: opts[:release])
332
+
333
+ deleted[:public_ips].each do |address|
334
+ if deleted[:released]
335
+ puts "EIP #{address[:public_ip]} (#{address[:allocation_id]}) dissociated and released"
336
+ else
337
+ puts "EIP #{address[:public_ip]} (#{address[:allocation_id]}) dissociated"
338
+ end
339
+ end
340
+ deleted[:interfaces].each do |interface_id|
341
+ puts "interface #{interface_id} deleted"
342
+ end
343
+ puts "#{deleted[:interfaces].count} interfaces deleted"
317
344
  end
318
345
  end
319
346
 
@@ -328,7 +355,7 @@ arg 'ip-address', :optional
328
355
  arg 'interface-id OR device-name'
329
356
  command [:assign] do |c|
330
357
  c.desc 'Do not configure the interface after assignment'
331
- c.switch [:n,'no-config'], negatable: false
358
+ c.switch [:n,:noconfig], negatable: false
332
359
 
333
360
  c.desc 'Do not return until connection is verified'
334
361
  c.switch [:b,:block], negatable: false
@@ -338,7 +365,7 @@ command [:assign] do |c|
338
365
  device = params[:device_name] || params[:interface_id]
339
366
  help_now! "Missing argument" if device.nil?
340
367
 
341
- params.merge! configure: !opts['no-config'], block: opts[:block]
368
+ params.merge! configure: !opts[:noconfig], block: opts[:block]
342
369
  Aws::ENI.assert_ifconfig_access if params[:configure]
343
370
 
344
371
  assignment = Aws::ENI.assign_secondary_ip(device, params)
@@ -395,10 +422,11 @@ command [:associate] do |c|
395
422
  c.switch [:b,:block], negatable: false
396
423
 
397
424
  c.action do |global,opts,args|
398
- help_now! "Missing argument" if args.empty?
399
425
  args.delete('new')
400
426
  params = parse_args args, :private_ip, :public_ip, :allocation_id, :interface_id, :device_name
401
427
  params[:block] = opts[:block]
428
+ help_now! "Missing argument" unless params[:private_ip]
429
+
402
430
  assoc = Aws::ENI.associate_elastic_ip(params[:private_ip], params)
403
431
  puts "EIP #{assoc[:public_ip]} (#{assoc[:allocation_id]}) associated with #{assoc[:private_ip]} on #{assoc[:device_name]} (#{assoc[:interface_id]})"
404
432
  end
@@ -418,6 +446,7 @@ command [:dissociate] do |c|
418
446
 
419
447
  c.action do |global,opts,args|
420
448
  params = parse_args args, :private_ip, :public_ip, :allocation_id, :association_id, :interface_id, :device_name
449
+ params[:release] = opts[:release]
421
450
  address = params[:private_ip] || params[:public_ip] || params[:association_id] || params[:allocation_id]
422
451
  help_now! "Missing argument" unless address
423
452
  dissoc = Aws::ENI.dissociate_elastic_ip(address, params)
@@ -123,6 +123,9 @@ module Aws
123
123
  interface_id: options[:interface_id],
124
124
  device_number: options[:device_number]
125
125
  )
126
+ if device.name == 'eth0'
127
+ raise InvalidParameterError, "For safety, interface eth0 cannot be detached."
128
+ end
126
129
  interface_id = device.interface_id
127
130
 
128
131
  response = client.describe_network_interfaces(filters: [{
@@ -142,7 +145,19 @@ module Aws
142
145
  force: true
143
146
  )
144
147
  created_by_us = interface.tag_set.any? { |tag| tag.key == 'created by' && tag.value == owner_tag }
145
- do_delete = options[:delete] || options[:delete].nil? && created_by_us
148
+ do_delete = options[:delete] != false && created_by_us
149
+ do_release = !!options[:release]
150
+
151
+ public_ips = []
152
+ interface[:private_ip_addresses].each do |addr|
153
+ if assoc = addr[:association]
154
+ public_ips << {
155
+ public_ip: assoc[:public_ip],
156
+ allocation_id: assoc[:allocation_id]
157
+ }
158
+ dissociate_elastic_ip(assoc[:allocation_id], release: true) if do_release
159
+ end
160
+ end
146
161
 
147
162
  if options[:block] || do_delete
148
163
  wait_for 'the interface to detach', interval: 0.3 do
@@ -156,12 +171,16 @@ module Aws
156
171
  device_number: device.device_number,
157
172
  created_by_us: created_by_us,
158
173
  deleted: do_delete,
174
+ released: do_release,
175
+ public_ips: public_ips,
159
176
  api_response: interface
160
177
  }
161
178
  end
162
179
 
163
180
  # delete unattached network interfaces
164
181
  def clean_interfaces(filter = nil, options = {})
182
+ public_ips = []
183
+ do_release = !!options[:release]
165
184
  safe_mode = true unless options[:safe_mode] == false
166
185
 
167
186
  filters = [
@@ -186,20 +205,30 @@ module Aws
186
205
 
187
206
  descriptions = client.describe_network_interfaces(filters: filters)
188
207
  interfaces = descriptions[:network_interfaces].select do |interface|
189
- unless safe_mode && interface.tag_set.any? do |tag|
208
+ created_recently = interface.tag_set.any? do |tag|
190
209
  begin
191
210
  tag.key == 'created on' && Time.now - Time.parse(tag.value) < 60
192
211
  rescue ArgumentError
193
- false
194
212
  end
195
213
  end
214
+ unless safe_mode && created_recently
215
+ interface[:private_ip_addresses].each do |addr|
216
+ if assoc = addr[:association]
217
+ public_ips << {
218
+ public_ip: assoc[:public_ip],
219
+ allocation_id: assoc[:allocation_id]
220
+ }
221
+ dissociate_elastic_ip(assoc[:allocation_id], release: true) if do_release
222
+ end
223
+ end
196
224
  client.delete_network_interface(network_interface_id: interface[:network_interface_id])
197
225
  true
198
226
  end
199
227
  end
200
228
  {
201
- count: interfaces.count,
202
- deleted: interfaces.map { |eni| eni[:network_interface_id] },
229
+ interfaces: interfaces.map { |eni| eni[:network_interface_id] },
230
+ public_ips: public_ips,
231
+ released: do_release,
203
232
  api_response: interfaces
204
233
  }
205
234
  end
@@ -216,7 +245,7 @@ module Aws
216
245
  current_ips = interface_ips(interface_id)
217
246
  new_ip = options[:private_ip]
218
247
 
219
- if new_ip = options[:private_ip]
248
+ if new_ip
220
249
  if current_ips.include?(new_ip)
221
250
  raise InvalidParameterError, "IP #{new_ip} already assigned to #{device.name}"
222
251
  end
@@ -226,7 +255,8 @@ module Aws
226
255
  allow_reassignment: false
227
256
  )
228
257
  wait_for 'private ip address to be assigned' do
229
- interface_ips(interface_id).include?(new_ip)
258
+ interface_ips(interface_id).include?(new_ip) ||
259
+ device.local_ips.include?(new_ip)
230
260
  end
231
261
  else
232
262
  client.assign_private_ip_addresses(
@@ -236,6 +266,7 @@ module Aws
236
266
  )
237
267
  wait_for 'new private ip address to be assigned' do
238
268
  new_ips = interface_ips(interface_id) - current_ips
269
+ new_ips = device.local_ips - current_ips if new_ips.empty?
239
270
  new_ip = new_ips.first if new_ips
240
271
  end
241
272
  end
@@ -279,7 +310,7 @@ module Aws
279
310
  end
280
311
 
281
312
  if assoc = addr_info[:association]
282
- client.release_address(allocation_id: assoc[:allocation_id]) if do_release
313
+ dissociate_elastic_ip(assoc[:allocation_id], release: do_release)
283
314
  end
284
315
 
285
316
  device.remove_alias(private_ip)
@@ -291,15 +322,17 @@ module Aws
291
322
  private_ip: private_ip,
292
323
  device_name: device.name,
293
324
  interface_id: device.interface_id,
294
- public_ip: assoc[:public_ip],
295
- allocation_id: assoc[:allocation_id],
296
- association_id: assoc[:association_id],
325
+ public_ip: assoc && assoc[:public_ip],
326
+ allocation_id: assoc && assoc[:allocation_id],
327
+ association_id: assoc && assoc[:association_id],
297
328
  released: assoc && do_release
298
329
  }
299
330
  end
300
331
 
301
332
  # associate a private ip with an elastic ip through the AWS api
302
333
  def associate_elastic_ip(private_ip, options = {})
334
+ raise MissingParameterError, "You must specify a private ip address" unless private_ip
335
+
303
336
  find = options[:device_name] || options[:device_number] || options[:interface_id] || private_ip
304
337
  device = IFconfig[find].assert(
305
338
  exists: true,
@@ -345,32 +378,54 @@ module Aws
345
378
 
346
379
  # dissociate a public ip from a private ip through the AWS api and
347
380
  # optionally release the public ip
348
- def dissociate_elastic_ip(ip, options = {})
381
+ def dissociate_elastic_ip(address, options = {})
349
382
  do_release = !!options[:release]
350
- eip = describe_address(ip)
351
383
 
352
- if find = options[:device_name] || options[:device_number] || options[:interface_id]
384
+ # assert device attributes if we've specified a device
385
+ if find = options[:device_name] || options[:device_number]
353
386
  device = IFconfig[find].assert(
354
387
  device_name: options[:device_name],
355
388
  device_number: options[:device_number],
356
- interface_id: options[:interface_id]
389
+ interface_id: options[:interface_id],
390
+ private_ip: options[:private_ip],
391
+ public_ip: options[:public_ip]
357
392
  )
358
- if device.interface_id != eip[:network_interface_id]
359
- raise UnknownInterfaceError, "EIP #{public_ip} is not associated with interface #{device.name} (#{device.interface_id})"
360
- end
361
- else
362
- begin
363
- device = IFconfig[eip[:network_interface_id]]
364
- rescue UnknownInterfaceError
365
- raise UnknownInterfaceError, "EIP #{public_ip} is not associated with an interface on this machine"
393
+ end
394
+
395
+ # get our address info
396
+ eip = describe_address(address)
397
+ device ||= IFconfig.find { |dev| dev.interface_id == eip[:network_interface_id] }
398
+
399
+ # assert eip attributes if options provided
400
+ if options[:private_ip] && eip[:private_ip_address] != options[:private_ip]
401
+ raise InvalidParameterError, "#{address} is not associated with IP #{options[:private_ip]}"
402
+ end
403
+ if options[:public_ip] && eip[:public_ip] != options[:public_ip]
404
+ raise InvalidParameterError, "#{address} is not associated with public IP #{options[:public_ip]}"
405
+ end
406
+ if options[:allocation_id] && eip[:allocation_id] != options[:allocation_id]
407
+ raise InvalidParameterError, "#{address} is not associated with allocation ID #{options[:allocation_id]}"
408
+ end
409
+ if options[:association_id] && eip[:association_id] != options[:association_id]
410
+ raise InvalidParameterError, "#{address} is not associated with association ID #{options[:association_id]}"
411
+ end
412
+ if options[:interface_id] && eip[:network_interface_id] != options[:interface_id]
413
+ raise InvalidParameterError, "#{address} is not associated with interface ID #{options[:interface_id]}"
414
+ end
415
+
416
+ if device
417
+ if device.name == 'eth0' && device.local_ips.first == eip[:private_ip_address]
418
+ raise InvalidParameterError, "For safety, a public address cannot be dissociated from the primary IP on eth0"
366
419
  end
420
+ elsif interface_status(eip[:network_interface_id]) != 'available'
421
+ raise InvalidParameterError, "#{address} is associated with an interface attached to another machine"
367
422
  end
368
423
 
369
424
  client.disassociate_address(association_id: eip[:association_id])
370
425
  client.release_address(allocation_id: eip[:allocation_id]) if do_release
371
426
  {
372
427
  private_ip: eip[:private_ip_address],
373
- device_name: device.name,
428
+ device_name: device && device.name,
374
429
  interface_id: eip[:network_interface_id],
375
430
  public_ip: eip[:public_ip],
376
431
  allocation_id: eip[:allocation_id],
@@ -478,7 +533,7 @@ module Aws
478
533
  when /^eipalloc-/
479
534
  'allocation-id'
480
535
  when /^eipassoc-/
481
- 'allocation-id'
536
+ 'association-id'
482
537
  else
483
538
  if IPAddr.new(environment[:vpc_cidr]) === IPAddr.new(address)
484
539
  'private-ip-address'
@@ -1,5 +1,5 @@
1
1
  module Aws
2
2
  module ENI
3
- VERSION = "0.2.0"
3
+ VERSION = "0.2.1"
4
4
  end
5
5
  end
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.2.0
4
+ version: 0.2.1
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-01 00:00:00.000000000 Z
11
+ date: 2015-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gli