rudy 0.8.5 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. data/CHANGES.txt +110 -18
  2. data/README.rdoc +40 -44
  3. data/Rudyfile +35 -50
  4. data/bin/rudy +88 -57
  5. data/bin/rudy-ec2 +2 -16
  6. data/bin/rudy-s3 +0 -10
  7. data/bin/rudy-sdb +11 -12
  8. data/lib/rudy.rb +59 -91
  9. data/lib/rudy/aws.rb +4 -45
  10. data/lib/rudy/aws/ec2.rb +57 -20
  11. data/lib/rudy/aws/ec2/address.rb +10 -11
  12. data/lib/rudy/aws/ec2/group.rb +10 -9
  13. data/lib/rudy/aws/ec2/image.rb +8 -8
  14. data/lib/rudy/aws/ec2/instance.rb +18 -19
  15. data/lib/rudy/aws/ec2/keypair.rb +14 -19
  16. data/lib/rudy/aws/ec2/snapshot.rb +16 -9
  17. data/lib/rudy/aws/ec2/volume.rb +39 -26
  18. data/lib/rudy/aws/ec2/zone.rb +5 -4
  19. data/lib/rudy/aws/s3.rb +2 -1
  20. data/lib/rudy/aws/sdb.rb +35 -86
  21. data/lib/rudy/backups.rb +24 -0
  22. data/lib/rudy/cli.rb +5 -131
  23. data/lib/rudy/cli/aws/ec2/addresses.rb +19 -27
  24. data/lib/rudy/cli/aws/ec2/candy.rb +45 -20
  25. data/lib/rudy/cli/aws/ec2/groups.rb +9 -13
  26. data/lib/rudy/cli/aws/ec2/images.rb +5 -133
  27. data/lib/rudy/cli/aws/ec2/instances.rb +25 -25
  28. data/lib/rudy/cli/aws/ec2/keypairs.rb +7 -11
  29. data/lib/rudy/cli/aws/ec2/snapshots.rb +5 -9
  30. data/lib/rudy/cli/aws/ec2/volumes.rb +22 -23
  31. data/lib/rudy/cli/aws/ec2/zones.rb +2 -3
  32. data/lib/rudy/cli/aws/sdb/domains.rb +5 -6
  33. data/lib/rudy/cli/aws/sdb/objects.rb +33 -0
  34. data/lib/rudy/cli/aws/sdb/select.rb +23 -0
  35. data/lib/rudy/cli/backups.rb +38 -0
  36. data/lib/rudy/cli/base.rb +104 -0
  37. data/lib/rudy/cli/candy.rb +1 -2
  38. data/lib/rudy/cli/config.rb +20 -7
  39. data/lib/rudy/cli/disks.rb +7 -9
  40. data/lib/rudy/cli/execbase.rb +56 -0
  41. data/lib/rudy/cli/machines.rb +242 -45
  42. data/lib/rudy/cli/metadata.rb +24 -10
  43. data/lib/rudy/cli/networks.rb +34 -0
  44. data/lib/rudy/cli/routines.rb +32 -6
  45. data/lib/rudy/cli/status.rb +60 -0
  46. data/lib/rudy/config.rb +55 -32
  47. data/lib/rudy/config/objects.rb +44 -30
  48. data/lib/rudy/disks.rb +25 -0
  49. data/lib/rudy/exceptions.rb +99 -0
  50. data/lib/rudy/global.rb +67 -28
  51. data/lib/rudy/guidelines.rb +3 -2
  52. data/lib/rudy/huxtable.rb +67 -58
  53. data/lib/rudy/machines.rb +41 -263
  54. data/lib/rudy/metadata.rb +212 -38
  55. data/lib/rudy/metadata/backup.rb +123 -78
  56. data/lib/rudy/metadata/disk.rb +153 -170
  57. data/lib/rudy/metadata/machine.rb +179 -0
  58. data/lib/rudy/mixins.rb +2 -1
  59. data/lib/rudy/mixins/hash.rb +3 -1
  60. data/lib/rudy/mixins/symbol.rb +8 -0
  61. data/lib/rudy/routines.rb +127 -344
  62. data/lib/rudy/routines/base.rb +229 -0
  63. data/lib/rudy/routines/handlers/base.rb +48 -0
  64. data/lib/rudy/routines/handlers/depends.rb +49 -0
  65. data/lib/rudy/routines/handlers/disks.rb +249 -0
  66. data/lib/rudy/routines/handlers/group.rb +44 -0
  67. data/lib/rudy/routines/handlers/host.rb +70 -0
  68. data/lib/rudy/routines/handlers/keypair.rb +70 -0
  69. data/lib/rudy/routines/handlers/machines.rb +15 -0
  70. data/lib/rudy/routines/handlers/script.rb +85 -0
  71. data/lib/rudy/routines/handlers/user.rb +45 -0
  72. data/lib/rudy/routines/passthrough.rb +19 -23
  73. data/lib/rudy/routines/reboot.rb +98 -50
  74. data/lib/rudy/routines/shutdown.rb +65 -14
  75. data/lib/rudy/routines/startup.rb +112 -17
  76. data/lib/rudy/utils.rb +35 -68
  77. data/rudy.gemspec +82 -25
  78. data/tryouts/01_mixins/01_hash_tryouts.rb +20 -0
  79. data/tryouts/10_require_time/10_rudy_tryouts.rb +33 -0
  80. data/tryouts/10_require_time/15_global_tryouts.rb +58 -0
  81. data/tryouts/12_config/10_load_config_tryouts.rb +43 -0
  82. data/tryouts/12_config/20_defaults_tryouts.rb +16 -0
  83. data/tryouts/12_config/30_accounts_tryouts.rb +17 -0
  84. data/tryouts/12_config/40_machines_tryouts.rb +53 -0
  85. data/tryouts/12_config/50_commands_tryouts.rb +17 -0
  86. data/tryouts/12_config/60_routines_tryouts.rb +16 -0
  87. data/tryouts/15_huxtable/10_huxtable_tryouts.rb +47 -0
  88. data/tryouts/15_huxtable/20_user_tryouts.rb +47 -0
  89. data/tryouts/20_simpledb/10_domains_tryouts.rb +36 -0
  90. data/tryouts/20_simpledb/20_objects_tryouts.rb +56 -0
  91. data/tryouts/25_ec2/10_keypairs_tryouts.rb +54 -0
  92. data/tryouts/25_ec2/20_groups_tryouts.rb +56 -0
  93. data/tryouts/25_ec2/21_groups_authorize_address_tryouts.rb +53 -0
  94. data/tryouts/25_ec2/22_groups_authorize_account_tryouts.rb +54 -0
  95. data/tryouts/25_ec2/30_addresses_tryouts.rb +42 -0
  96. data/tryouts/25_ec2/40_volumes_tryouts.rb +53 -0
  97. data/tryouts/25_ec2/50_snapshots_tryouts.rb +75 -0
  98. data/tryouts/26_ec2_instances/10_instance_tryouts.rb +107 -0
  99. data/tryouts/26_ec2_instances/50_images_tryouts.rb +7 -0
  100. data/tryouts/30_metadata/10_include_tryouts.rb +45 -0
  101. data/tryouts/30_metadata/13_object_tryouts.rb +19 -0
  102. data/tryouts/30_metadata/50_disk_tryouts.rb +115 -0
  103. data/tryouts/30_metadata/51_disk_digest_tryouts.rb +24 -0
  104. data/tryouts/30_metadata/53_disk_list_tryouts.rb +35 -0
  105. data/tryouts/30_metadata/56_disk_volume_tryouts.rb +68 -0
  106. data/tryouts/30_metadata/60_backup_tryouts.rb +101 -0
  107. data/tryouts/30_metadata/63_backup_list_tryouts.rb +38 -0
  108. data/tryouts/30_metadata/64_backup_disk_tryouts.rb +65 -0
  109. data/tryouts/30_metadata/66_backup_snapshot_tryouts.rb +76 -0
  110. data/tryouts/30_metadata/70_machine_tryouts.rb +85 -0
  111. data/tryouts/30_metadata/73_machine_list_tryouts.rb +58 -0
  112. data/tryouts/30_metadata/76_machine_instance_tryouts.rb +64 -0
  113. data/tryouts/30_metadata/77_machines_tryouts.rb +45 -0
  114. data/tryouts/40_routines/10_keypair_handler_tryouts.rb +52 -0
  115. data/tryouts/40_routines/11_group_handler_tryouts.rb +36 -0
  116. data/tryouts/80_cli/10_rudyec2_tryouts.rb +8 -0
  117. data/tryouts/80_cli/60_rudy_tryouts.rb +41 -0
  118. data/tryouts/exploration/console.rb +91 -0
  119. data/tryouts/exploration/machine.rb +23 -0
  120. data/tryouts/failer +6 -0
  121. metadata +116 -32
  122. data/bin/ird +0 -153
  123. data/lib/rudy/metadata/backups.rb +0 -67
  124. data/lib/rudy/metadata/debug.rb +0 -38
  125. data/lib/rudy/metadata/disks.rb +0 -67
  126. data/lib/rudy/metadata/objectbase.rb +0 -108
  127. data/lib/rudy/routines/helper.rb +0 -76
  128. data/lib/rudy/routines/helpers/dependshelper.rb +0 -34
  129. data/lib/rudy/routines/helpers/diskhelper.rb +0 -403
  130. data/lib/rudy/routines/helpers/scripthelper.rb +0 -197
  131. data/lib/rudy/routines/helpers/userhelper.rb +0 -37
  132. data/support/rudy-ec2-startup +0 -200
@@ -7,11 +7,10 @@ module AWS; module EC2;
7
7
 
8
8
 
9
9
  def zones
10
- rzone = Rudy::AWS::EC2::Zones.new(@@global.accesskey, @@global.secretkey, @@global.region)
11
- rzone.list_as_hash(@argv.name).each_value do |zon|
10
+ Rudy::AWS::EC2::Zones.list_as_hash(@argv.name).each_value do |zon|
12
11
  puts zon.dump(@@global.format)
13
12
  end
14
- puts "No zones" unless rzone.any?
13
+ puts "No zones" unless Rudy::AWS::EC2::Zones.any?
15
14
  end
16
15
 
17
16
 
@@ -4,32 +4,31 @@ module Rudy; module CLI;
4
4
  module AWS; module SDB;
5
5
 
6
6
  class Domains < Rudy::CLI::CommandBase
7
-
8
-
7
+
9
8
  def domains
10
- sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
11
- domains = sdb.list_domains
9
+ @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
10
+ domains = @sdb.list_domains
12
11
  puts domains
13
12
  puts "No domains" if domains.nil? || domains.empty?
14
13
  end
15
14
 
16
15
  def domains_create_valid?
17
- @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
18
16
  raise "No name specified" unless @argv.name
19
17
  true
20
18
  end
21
19
  def domains_create
20
+ @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
22
21
  @sdb.create_domain @argv.name
23
22
  execute_check(:low)
24
23
  domains
25
24
  end
26
25
 
27
26
  def domains_destroy_valid?
28
- @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
29
27
  raise "No name specified" unless @argv.name
30
28
  true
31
29
  end
32
30
  def domains_destroy
31
+ @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
33
32
  execute_check(:medium)
34
33
  @sdb.destroy_domain @argv.name
35
34
  domains
@@ -0,0 +1,33 @@
1
+
2
+
3
+ module Rudy; module CLI;
4
+ module AWS; module SDB;
5
+
6
+ class Objects < Rudy::CLI::CommandBase
7
+
8
+
9
+ def objects_valid?
10
+ raise "Must supply domain" if @argv.empty?
11
+ true
12
+ end
13
+ def objects
14
+ @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
15
+
16
+ if @argv.key.nil?
17
+ query = "select * from #{@argv.name}"
18
+ items = @sdb.select query
19
+ else
20
+ items = [@sdb.get( @argv.name, @argv.key)]
21
+ end
22
+
23
+ exit unless items
24
+
25
+ items.each do |i|
26
+ p i
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ end; end
33
+ end; end
@@ -0,0 +1,23 @@
1
+
2
+ module Rudy; module CLI;
3
+ module AWS; module SDB;
4
+
5
+ class Select < Rudy::CLI::CommandBase
6
+
7
+ def query_valid?
8
+ raise "No select query supplied" if @argv.empty?
9
+ true
10
+ end
11
+
12
+ def query
13
+ @sdb = Rudy::AWS::SDB.new(@@global.accesskey, @@global.secretkey, @@global.region)
14
+ results = @sdb.select @argv.query
15
+ return if results.nil?
16
+ results.each do |r|
17
+ p r
18
+ end
19
+ end
20
+ end
21
+
22
+ end; end
23
+ end; end
@@ -0,0 +1,38 @@
1
+
2
+
3
+ module Rudy
4
+ module CLI
5
+ class Backups < Rudy::CLI::CommandBase
6
+
7
+
8
+ def backups
9
+ more, less = {}, []
10
+ less = [:environment, :role] if @option.all
11
+ # We first get the disk metadata
12
+ b_list = Rudy::Backups.list(more, less) || []
13
+ b_list.each do |back|
14
+ puts @global.verbose > 0 ? "#{back.name}: #{back.inspect}" : back.name
15
+ end
16
+ end
17
+
18
+ def backups_wash
19
+ dirt = (Rudy::Backups.list || []).select { |b| !b.snapshot_exists? }
20
+ if dirt.empty?
21
+ puts "Nothing to wash in #{current_machine_group}"
22
+ return
23
+ end
24
+
25
+ puts "The following backup metadata will be deleted:"
26
+ puts dirt.collect {|b| b.name }
27
+
28
+ execute_check(:medium)
29
+
30
+ dirt.each do |b|
31
+ b.destroy(:force)
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,104 @@
1
+
2
+
3
+ module Rudy::CLI
4
+
5
+ class CommandBase < Drydock::Command
6
+ include Rudy::Huxtable
7
+
8
+ attr_reader :config
9
+
10
+ protected
11
+ def init
12
+
13
+ if Drydock.debug?
14
+ #Caesars.enable_debug
15
+ Rudy.enable_debug
16
+ end
17
+
18
+ # The CLI wants output!
19
+ Rudy::Huxtable.update_logger STDOUT
20
+
21
+ # Send The Huxtables the global values from the command-line
22
+ Rudy::Huxtable.update_global @global
23
+
24
+ # Reload configuration. This must come after update_global
25
+ # so it will catch the @@global.config path (if supplied).
26
+ begin
27
+ Rudy::Huxtable.update_config
28
+ rescue Caesars::SyntaxError => ex
29
+ STDERR.puts ex.message
30
+ STDERR.puts ex.backtrace if @@global.verbose > 0
31
+ exit 81
32
+ end
33
+
34
+ @@global.nocolor ? String.disable_color : String.enable_color
35
+ @@global.auto ? Annoy.enable_skip : Annoy.disable_skip
36
+
37
+ # ANSI codes look like garbage in DOS
38
+ if Rudy.sysinfo.os.to_s == 'win32'
39
+ String.disable_color
40
+ raise Rudy::Error, 'Ruby 1.9 is not supported (yet)' if Rudy.sysinfo.ruby == [1,9,1]
41
+ end
42
+
43
+ unless @@global.accesskey && @@global.secretkey
44
+ STDERR.puts "No AWS credentials. Check your configs!"
45
+ STDERR.puts "Try: rudy init"
46
+ exit 1
47
+ end
48
+
49
+ if @@global.environment =~ /^prod/ && Rudy.debug?
50
+ puts Rudy::Utils.banner("PRODUCTION ACCESS IS DISABLED IN DEBUG MODE")
51
+ exit 1
52
+ end
53
+
54
+ if @@global.verbose >= 4 # -vvvv
55
+ format = @@global.format == :json ? :json : :yaml
56
+ gcopy = @@global.dup
57
+ gcopy.secretkey = "[HIDDEN]"
58
+ puts "# GLOBALS: ", gcopy.dump(format)
59
+ end
60
+
61
+ Rudy::Metadata.connect @@global.accesskey, @@global.secretkey, @@global.region
62
+ Rudy::AWS::EC2.connect @@global.accesskey, @@global.secretkey, @@global.region
63
+ end
64
+
65
+ def execute_action(emsg="Failed", &action)
66
+ begin
67
+ ret = action.call
68
+ raise emsg unless ret
69
+ ret
70
+ rescue Rudy::AWS::EC2::NoAMI => ex
71
+ raise Drydock::OptError.new('-a', @alias)
72
+ end
73
+ end
74
+
75
+ def execute_check(level=:medium)
76
+ ret = Annoy.are_you_sure?(level)
77
+ exit 0 unless ret
78
+ ret
79
+ end
80
+
81
+ # Print a default header to the screen for every command.
82
+ #
83
+ def print_header
84
+
85
+ # Send The Huxtables the global values again because they could be
86
+ # updated after initialization but before the command was executed
87
+ Rudy::Huxtable.update_global @global
88
+
89
+ puts Rudy::CLI.generate_header(@@global, @@config) if @@global.print_header
90
+
91
+ unless @@global.quiet
92
+ if @@global.environment == "prod"
93
+ msg = "YOU ARE PLAYING WITH PRODUCTION"
94
+ puts Rudy::Utils.banner(msg, :normal), $/
95
+ end
96
+ end
97
+ end
98
+
99
+ def machine_separator(name, awsid)
100
+ ('%s %-50s awsid: %s ' % [$/, name, awsid]).att(:reverse)
101
+ end
102
+
103
+ end
104
+ end
@@ -4,8 +4,7 @@ module Rudy
4
4
  class Candy < Rudy::CLI::CommandBase
5
5
 
6
6
  def open
7
- rmach = Rudy::Machines.new
8
- machines = rmach.list
7
+ machines = Rudy::Machines.list
9
8
 
10
9
  if machines
11
10
  `open http://#{machines.first.dns_public}`
@@ -21,17 +21,20 @@ module Rudy
21
21
  #
22
22
  # It will return the most specific configuration available. If the
23
23
  # attribute isn'e found it will check each parent for the same attribute.
24
- # i.e. if [prod][app][ami] is not available, it will check [prod][ami]
24
+ # e.g. if [prod][app][ami] is not available, it will check [prod][ami]
25
25
  # and then [ami].
26
26
  #
27
- # # Display the value for a specific machine.
28
- # $ rudy -e prod -r db config param-name
29
- #
30
27
  # # Display all configuration
31
28
  # $ rudy config --all
32
29
  #
30
+ # # Display just machines
31
+ # $ rudy config --defaults
32
+ #
33
33
  def config
34
- # if Rudy.in_situ? # TODO: do something intelligent when running on EC2
34
+
35
+ # TODO: Re-enable:
36
+ # # Display the value for a specific machine.
37
+ # $ rudy -e prod -r db config param-name
35
38
 
36
39
  if @@config.nil? || @@config.empty?
37
40
  return if @@global.quiet
@@ -58,7 +61,11 @@ module Rudy
58
61
  types.each do |conftype|
59
62
  puts "# #{conftype.to_s.upcase}"
60
63
  next unless @@config[conftype] # Nothing to output
61
- @@config[conftype][:aws][:secretkey] = '[hidden]' if conftype == :accounts
64
+ if conftype == :accounts
65
+ skey = @@config[conftype][:aws][:secretkey]
66
+ @@config[conftype][:aws][:secretkey] = hide_secret_key(skey)
67
+ end
68
+
62
69
  puts @@config[conftype].to_hash.send(outform)
63
70
  end
64
71
  end
@@ -73,10 +80,16 @@ module Rudy
73
80
  end
74
81
  gtmp = @@global.clone
75
82
  gtmp.format = "yaml" if gtmp.format == :s || gtmp.format == :string
76
- gtmp.accesskey = gtmp.secretkey = "[not displayed]"
83
+ gtmp.secretkey = hide_secret_key(gtmp.secretkey)
77
84
  puts gtmp.dump(gtmp.format)
78
85
  end
79
86
 
87
+ private
88
+ def hide_secret_key(skey)
89
+ skey = skey.to_s
90
+ "%s%s%s" % [skey[0], '.'*18, skey[-1]]
91
+ end
92
+
80
93
  end
81
94
  end
82
95
  end
@@ -6,16 +6,14 @@ module Rudy
6
6
 
7
7
 
8
8
  def disks
9
- rdisk = Rudy::Disks.new
10
- rback = Rudy::Backups.new
11
- more, less = [], []
9
+ more, less = {}, []
12
10
  less = [:environment, :role] if @option.all
13
11
  # We first get the disk metadata
14
- disk_list = rdisk.list(more, less) || []
12
+ disk_list = Rudy::Disks.list(more, less) || []
15
13
  # If there are no disks currently, there could be backups
16
14
  # so we grab those to create a list of disks.
17
15
  if @option.backups
18
- backups = rback.list(more, less) || []
16
+ backups = Rudy::Backups.list(more, less) || []
19
17
  backups.each_with_index do |b, index|
20
18
  disk_list << b.disk
21
19
  end
@@ -27,7 +25,7 @@ module Rudy
27
25
  seen << d.name
28
26
  puts @@global.verbose > 0 ? d.inspect : d.dump(@@global.format)
29
27
  if @option.backups
30
- d.list_backups.each_with_index do |b, index|
28
+ d.backups.each_with_index do |b, index|
31
29
  puts ' %s' % b.name
32
30
  ##break if @option.all.nil? && index >= 2 # display only 3, unless all
33
31
  end
@@ -36,15 +34,15 @@ module Rudy
36
34
  end
37
35
 
38
36
  def disks_wash
39
- rdisk = Rudy::Disks.new
40
- dirt = (rdisk.list || [])#.select { |d| d.available? }
37
+ dirt = (Rudy::Disks.list || []).select { |d| !d.volume_exists? }
41
38
  if dirt.empty?
42
- puts "Nothing to wash in #{rdisk.current_machine_group}"
39
+ puts "Nothing to wash in #{current_machine_group}"
43
40
  return
44
41
  end
45
42
 
46
43
  puts "The following disk metadata will be deleted:"
47
44
  puts dirt.collect {|d| d.name }
45
+
48
46
  execute_check(:medium)
49
47
 
50
48
  dirt.each do |d|
@@ -0,0 +1,56 @@
1
+
2
+
3
+ module Rudy::CLI
4
+
5
+ # A base for all Drydock executables (bin/rudy etc...).
6
+ class Base
7
+ extend Drydock
8
+
9
+ debug :off
10
+
11
+ before do |obj|
12
+ # Don't print Rudy header unless requested to
13
+ obj.global.print_header = false if (obj.global.verbose == 0)
14
+ @start = Time.now
15
+ end
16
+
17
+ after do |obj|
18
+ if obj.global.verbose > 0
19
+ puts
20
+ @elapsed = Time.now - @start
21
+ puts "Elapsed: %.2f seconds" % @elapsed.to_f if @elapsed > 0.1
22
+ end
23
+ end
24
+
25
+ # These globals are used by all bin/ executables
26
+ global :A, :accesskey, String, "AWS Access Key"
27
+ global :S, :secretkey, String, "AWS Secret Access Key"
28
+ global :R, :region, String, "Amazon service region (e.g. #{Rudy::DEFAULT_REGION})"
29
+ global :z, :zone, String, "Amazon Availability zone (e.g. #{Rudy::DEFAULT_ZONE})"
30
+ global :u, :user, String, "Provide a username (ie: #{Rudy.sysinfo.user})"
31
+ global :l, :localhost, String, "Provide a localhost (e.g. #{Rudy.sysinfo.hostname})"
32
+ global :i, :identity, String, "Path to SSH identity (private key) for RSA or DSA authentication"
33
+ global :k, :pkey, String, "AWS Private Encryption Key (pk-****.pem)"
34
+ global :c, :cert, String, "AWS Private Certificate (cert-****.pem)"
35
+ global :f, :format, String, "Output format"
36
+ global :n, :nocolor, "Disable output colors"
37
+ global :Y, :auto, "Skip interactive confirmation"
38
+ global :q, :quiet, "Run with less output"
39
+ global :O, :offline, "Be cool about the internet being down"
40
+ global :C, :config, String, "Specify another configuration file to read (e.g. #{Rudy::CONFIG_FILE})" do |val|
41
+ @configs ||= []
42
+ @configs << val
43
+ end
44
+ global :v, :verbose, "Increase verbosity of output (e.g. -v or -vv or -vvv)" do
45
+ @verbose ||= 0
46
+ @verbose += 1
47
+ end
48
+ global :V, :version, "Display version number" do
49
+ puts "Rudy version: #{Rudy::VERSION}"
50
+ exit 0
51
+ end
52
+
53
+
54
+ end
55
+
56
+ end
@@ -11,29 +11,22 @@ module Rudy
11
11
  # When all is specified we want to find machines in every
12
12
  # environment and role to we remove these attributes from
13
13
  # the select.
14
- more, less = nil, nil
15
- less = [:environment, :role] if @option.all
14
+ fields, less = {}, []
15
+ less = Rudy::Metadata::COMMON_FIELDS if @option.all
16
16
 
17
- rmach = Rudy::Machines.new
18
- mlist = rmach.list(more, less) || []
17
+ mlist = Rudy::Machines.list(fields, less) || []
19
18
  if mlist.empty?
20
- if @option.all
21
- puts "No machines running"
22
- else
23
- puts "No machines running in #{current_machine_group}"
24
- puts "Try: rudy machines --all"
25
- end
19
+ raise( NoMachines, @option.all ? nil : current_group_name)
26
20
  end
27
21
  mlist.each do |m|
28
- puts @@global.verbose > 0 ? m.inspect : m.dump(@@global.format)
22
+ puts @@global.verbose > 0 ? m.to_yaml : "#{m.name}: #{m.dns_public}"
29
23
  end
30
24
  end
31
25
 
32
26
  def machines_wash
33
- rmach = Rudy::Machines.new
34
- dirt = (rmach.list || []).select { |m| !m.running? }
27
+ dirt = (Rudy::Machines.list || []).select { |m| !m.instance_running? }
35
28
  if dirt.empty?
36
- puts "Nothing to wash in #{rmach.current_machine_group}"
29
+ puts "Nothing to wash in #{current_machine_group}"
37
30
  return
38
31
  end
39
32
 
@@ -47,70 +40,274 @@ module Rudy
47
40
 
48
41
  end
49
42
 
43
+ def associate_machines_valid?
44
+ @mlist = Rudy::Machines.list || []
45
+ @alist = Rudy::AWS::EC2::Addresses.list || []
46
+ @alist_used = @alist.select { |a| a.associated? }
47
+ @alist_unused = @alist.select { |a| !a.associated? }
48
+ @alist_unused.collect! { |a| a.ipaddress }
49
+ @alist_instids = @alist_used.collect { |a| a.instid }
50
+ @mlist_static = @mlist.select do |m|
51
+ @alist_instids.member?(m.instid)
52
+ end
53
+
54
+ unless @@global.force
55
+ unless @mlist_static.empty?
56
+ msg = "Some machines already have static IP addresses: #{$/}"
57
+ msg << @mlist_static.collect { |m| "#{m.name}: #{m.dns_public}" }.join($/)
58
+ raise Rudy::Error, msg
59
+ end
60
+
61
+ if !@argv.empty? && @mlist.size > @argv.size
62
+ msg = "You supplied #{@argv.size} addresses for #{@mlist.size} "
63
+ msg << "machines. Try: rudy --force machines -S #{@argv.join(' ')}"
64
+ raise Rudy::Error, msg
65
+ end
66
+
67
+ if @alist_unused.size > 0 && @alist_unused.size < @mlist.size
68
+ msg = "There are only #{@alist_unused.size} available addresses for "
69
+ msg << "#{@mlist.size} machines. Try: rudy --force machines -S #{@argv.join(' ')}"
70
+ raise Rudy::Error, msg
71
+ end
72
+ end
73
+
74
+ @argv.each do |address|
75
+ unless Rudy::AWS::EC2::Addresses.exists?(address)
76
+ raise "#{address} is not allocated to you"
77
+ end
78
+ if Rudy::AWS::EC2::Addresses.associated?(address)
79
+ raise "#{address} is already associated!"
80
+ end
81
+ end
82
+
83
+ @alist_unused = @argv unless @argv.empty?
84
+
85
+ true
86
+ end
87
+
88
+ def associate_machines
89
+
90
+ puts "Assigning static IP addresses for:"
91
+ puts @mlist.collect { |m| m.name }
92
+
93
+ execute_check(:medium)
94
+
95
+ @mlist.each do |m|
96
+ next if @mlist_static.member?(m)
97
+ address = @alist_unused.shift
98
+ address ||= Rudy::AWS::EC2::Addresses.create.ipaddress
99
+ puts "Associating #{address} to #{m.name} (#{m.instid})"
100
+ Rudy::AWS::EC2::Addresses.associate(address, m.instid)
101
+ sleep 2
102
+ m.refresh!
103
+ end
104
+
105
+ @alist = Rudy::AWS::EC2::Addresses.list || []
106
+ @alist_used = @alist.select { |a| a.associated? }
107
+ @alist_instids = @alist_used.collect { |a| a.instid }
108
+ @mlist_static = @mlist.select do |m|
109
+ @alist_instids.member?(m.instid)
110
+ end
111
+
112
+ unless @mlist_static.empty?
113
+ @mlist_static.each do |m|
114
+ puts "%s: %s" % [m.name, m.dns_public]
115
+ end
116
+ end
117
+ end
118
+
119
+
120
+ def disassociate_machines_valid?
121
+ @mlist = Rudy::Machines.list || []
122
+ @alist = Rudy::AWS::EC2::Addresses.list || []
123
+ @alist_used = @alist.select { |a| a.associated? }
124
+ @alist_instids = @alist_used.collect { |a| a.instid }
125
+ @mlist_static = @mlist.select do |m|
126
+ @alist_instids.member?(m.instid)
127
+ end
128
+ raise NoMachines, current_group_name if @mlist.empty?
129
+ true
130
+ end
131
+
132
+
133
+ def disassociate_machines
134
+ if @mlist_static.empty?
135
+ puts "No machines in #{current_group_name} have static IP addresses"
136
+ else
137
+ puts "The following machines will be updated:"
138
+ puts @mlist_static.collect { |m| m.name }
139
+ puts "NOTE: Unassigned IP addresses are not removed from your account"
140
+ execute_check(:medium)
141
+ @mlist_static.each do |m|
142
+ address = Resolv.getaddress m.dns_public
143
+ puts "Disassociating #{address} from #{m.name} (#{m.instid})"
144
+ Rudy::AWS::EC2::Addresses.disassociate(address)
145
+ end
146
+ end
147
+ end
148
+
149
+ def update_machines
150
+ fields, less = {}, []
151
+ less = Rudy::Metadata::COMMON_FIELDS if @option.all
152
+ mlist = Rudy::Machines.list(fields, less) || []
153
+ rset = Rye::Set.new(current_group_name, :parallel => @@global.parallel, :user => 'root')
154
+ os = current_machine_os
155
+ mlist.each do |m|
156
+ m.refresh!
157
+ rbox = Rye::Box.new(m.dns_public, :user => 'root')
158
+ rbox.add_key user_keypairpath('root')
159
+ rbox.nickname = m.name
160
+ rbox.stash = m
161
+ rset.add_boxes rbox
162
+ puts "Updating metadata"
163
+ if m.os.to_s != os.to_s
164
+ puts "os: #{os}"
165
+ m.os = os
166
+ end
167
+ m.save :replace
168
+ end
169
+
170
+ unless os.to_s == 'win32'
171
+ puts "Updating hostnames for #{current_group_name}"
172
+ Rudy::Routines::Handlers::Host.set_hostname rset
173
+ puts rset.hostname.flatten
174
+ end
175
+
176
+ end
177
+
178
+ def available_machines
179
+ fields, less = {}, []
180
+ less = Rudy::Metadata::COMMON_FIELDS if @option.all
181
+ mlist = Rudy::Machines.list(fields, less) || []
182
+ mlist.each do |m|
183
+ print "#{m.name}: "
184
+ m.refresh!
185
+ Rudy::Utils.waiter(2, 60, STDOUT, nil, 0) {
186
+ Rudy::Utils.service_available?(m.dns_public, 22)
187
+ }
188
+ available = Rudy::Utils.service_available?(m.dns_public, 22)
189
+ puts available ? 'up' : 'down'
190
+ end
191
+
192
+ end
193
+
50
194
 
51
195
  def ssh
52
- # TODO: Give this methos a good look over
53
- pkey = user_keypairpath(@@global.user)
196
+ # TODO: Give this method a good look over
197
+ pkey = current_user_keypairpath
54
198
  unless pkey
55
- puts "No private key configured for #{@@global.user} in #{current_machine_group}"
199
+ puts "No private key configured for #{current_machine_user} in #{current_machine_group}"
56
200
  end
57
201
 
58
- # Options to be sent to Net::SSH
59
- ssh_opts = { :user => @@global.user || Rudy.sysinfo.user, :debug => nil }
202
+ # Options to be sent to Rye::Box
203
+ rye_opts = { :user => current_machine_user, :debug => nil }
60
204
  if pkey
61
205
  raise "Cannot find file #{pkey}" unless File.exists?(pkey)
62
- raise InsecureKeyPermissions, @pkey unless File.stat(pkey).mode == 33152
63
- ssh_opts[:keys] = pkey
206
+ if Rudy.sysinfo.os != :win32 && File.stat(pkey).mode != 33152
207
+ raise InsecureKeyPermissions, pkey
208
+ end
209
+ rye_opts[:keys] = pkey
64
210
  end
65
-
66
-
211
+
67
212
  # The user specified a command to run. We won't create an interactive
68
213
  # session so we need to prepare the command and its arguments
69
214
  if @argv.first
70
215
  command, command_args = @argv.shift, @argv || []
71
- puts "#{command} #{command_args.join(' ')}" if @@global.verbose > 1
216
+ Rudy::Huxtable.ld "COMMAND: #{command} #{command_args.join(' ')}" if @@global.verbose > 1
72
217
 
73
218
  # otherwise, we'll open an ssh session or print command
74
219
  else
75
220
  command, command_args = :interactive_ssh, @option.print.nil?
76
221
  end
77
-
78
-
222
+
223
+ if command == :interactive_ssh && @global.parallel
224
+ raise "Cannot run interactive sessions in parallel"
225
+ end
226
+
79
227
  checked = false
80
- rudy = Rudy::Machines.new
81
- lt = rudy.list
228
+ lt = Rudy::Machines.list
82
229
  unless lt
83
- puts "No machines running in #{rudy.current_machine_group}"
84
- exit
230
+ puts "No machines running in #{current_machine_group}"
231
+ return
85
232
  end
233
+
234
+ rset = Rye::Set.new(current_machine_group, :parallel => @global.parallel)
86
235
  lt.each do |machine|
87
- machine.update # make sure we have the latest DNS info
88
-
89
- # mount -t ext3 /dev/sdr /rudy/disk1
90
-
91
- # Print header
92
- if @@global.quiet
93
- print "You are #{ssh_opts[:user].to_s.bright}. " if !checked # only the 1st
236
+ machine.refresh! # make sure we have the latest DNS info
237
+ rbox = Rye::Box.new(machine.dns_public, rye_opts)
238
+ rbox.nickname = machine.name
239
+ if command == :interactive_ssh
240
+ # Print header
241
+ if @@global.quiet
242
+ print "You are #{rye_opts[:user].to_s.bright}. " if !checked # only the 1st
243
+ else
244
+ puts machine_separator(machine.name, machine.instid)
245
+ puts "Connecting #{rye_opts[:user].to_s.bright}@#{machine.dns_public} "
246
+ puts
247
+ end
94
248
  else
95
- puts machine_separator(machine.name, machine.awsid)
96
- puts "Connecting #{ssh_opts[:user].to_s.bright}@#{machine.dns_public} "
97
- puts
249
+ unless @global.parallel
250
+ rbox.pre_command_hook do |cmd,user,host,nickname|
251
+ print_command user, nickname, cmd
252
+ end
253
+ end
254
+ rbox.post_command_hook do |ret|
255
+ print_response ret
256
+ end
98
257
  end
99
258
 
100
259
  # Make sure we want to run this command on all instances
101
260
  if !checked && command != :interactive_ssh
102
- execute_check(:low) if ssh_opts[:user] == "root"
261
+ execute_check(:low) if rye_opts[:user] == "root"
103
262
  checked = true
104
263
  end
105
264
 
106
- # Open the connection and run the command
107
- rbox = Rye::Box.new(machine.dns_public, ssh_opts)
108
- ret = rbox.send(command, command_args)
109
- puts ret unless command == :interactive_ssh
265
+ # Open the connection and run the command
266
+ if command == :interactive_ssh
267
+ rbox.send(command, command_args)
268
+ else
269
+ rset.add_box rbox
270
+ end
110
271
  end
272
+
273
+ rset.send(command, command_args) unless command == :interactive_ssh
274
+
111
275
  end
112
276
 
277
+
278
+ private
279
+ # Returns a formatted string for printing command info
280
+ def print_command(user, host, cmd)
281
+ #return if @@global.parallel
282
+ cmd ||= ""
283
+ cmd, user = cmd.to_s, user.to_s
284
+ prompt = user == "root" ? "#" : "$"
285
+ li ("%s@%s%s %s" % [user, host, prompt, cmd.bright])
286
+ end
287
+
288
+
289
+ def print_response(rap)
290
+ # Non zero exit codes raise exceptions so
291
+ # the erorrs have already been handled.
292
+ return if rap.exit_code != 0
113
293
 
294
+ if @@global.parallel
295
+ cmd, user = cmd.to_s, user.to_s
296
+ prompt = user == "root" ? "#" : "$"
297
+ li "%s@%s%s %s%s%s" % [rap.box.user, rap.box.nickname, prompt, rap.cmd.bright, $/, rap.stdout.inspect]
298
+ unless rap.stderr.empty?
299
+ le "#{rap.box.nickname}: " << rap.stderr.join("#{rap.box.nickname}: ")
300
+ end
301
+ else
302
+ li ' ' << rap.stdout.join("#{$/} ") if !rap.stdout.empty?
303
+ colour = rap.exit_code != 0 ? :red : :normal
304
+ unless rap.stderr.empty?
305
+ le (" STDERR " << '-'*38).color(colour).bright
306
+ le " " << rap.stderr.join("#{$/} ").color(colour)
307
+ end
308
+ end
309
+ end
310
+
114
311
  end
115
312
  end
116
313
  end