rudy 0.8.5 → 0.9.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.
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