aws-eni 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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