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
@@ -0,0 +1,179 @@
1
+ ##--
2
+ ## CONSIDER: http://docs.rackspacecloud.com/servers/api/v1.0/cs-devguide-20090713.pdf
3
+ ##++
4
+
5
+ module Rudy
6
+ class Machine < Storable
7
+ include Rudy::Metadata
8
+ include Gibbler::Complex
9
+
10
+ field :rtype
11
+ field :instid
12
+
13
+ field :region
14
+ field :zone
15
+ field :environment
16
+ field :role
17
+ field :position
18
+
19
+ field :size
20
+ field :ami
21
+ field :group
22
+ field :keypair
23
+ field :address
24
+
25
+ field :created => Time
26
+ field :started => Time
27
+
28
+ field :dns_public
29
+ field :dns_private
30
+ field :state
31
+
32
+ field :os
33
+ field :impl
34
+
35
+ attr_reader :instance
36
+
37
+ # An ephemeral value which is set after checking whether
38
+ # the SSH daemon is running. By default this will be set
39
+ # to false but can be set to true to avoid checking again.
40
+ # See available?
41
+ attr_writer :available
42
+
43
+ # * +position+
44
+ # * +opts+ is a hash of machine options.
45
+ #
46
+ # Valid options are:
47
+ # * +:position+ (overridden by +position+ arg)
48
+ # * +:size+
49
+ # * +:os+
50
+ # * +:ami+
51
+ # * +:group+
52
+ # * +:keypair+
53
+ # * +:address+
54
+ #
55
+ def initialize(position='01', opts={})
56
+
57
+ opts = {
58
+ :size => current_machine_size,
59
+ :os => current_machine_os,
60
+ :ami => current_machine_image,
61
+ :group => current_group_name,
62
+ :keypair => root_keypairname
63
+ }.merge opts
64
+
65
+ opts[:address] = current_machine_address opts[:position] || position
66
+
67
+ super Rudy::Machines::RTYPE, opts # Rudy::Metadata#initialize
68
+
69
+ @position = position
70
+
71
+ # Defaults:
72
+ @created = Time.now.utc
73
+ @available = false
74
+ postprocess
75
+
76
+ end
77
+
78
+ def postprocess
79
+ @position &&= @position.to_s.rjust(2, '0')
80
+ end
81
+
82
+ def to_s(*args)
83
+ self.name
84
+ end
85
+
86
+ def get_instance
87
+ Rudy::AWS::EC2::Instances.get @instid
88
+ end
89
+
90
+ def create
91
+ raise "#{name} is already running" if instance_running?
92
+
93
+ # Options for Rudy::AWS::EC2::Instances#create
94
+ opts = {
95
+ :min => 1,
96
+ :size => @size,
97
+ :ami => @ami,
98
+ :group => @group,
99
+ :keypair => @keypair,
100
+ :zone => @zone,
101
+ :machine_data => self.generate_machine_data.to_yaml
102
+ }
103
+
104
+ Rudy::Huxtable.ld "OPTS: #{opts.inspect}"
105
+
106
+ Rudy::AWS::EC2::Instances.create(opts) do |inst|
107
+ @instid = inst.awsid
108
+ @created = @started = Time.now
109
+ @state = inst.state
110
+ # We need to be safe when creating machines because if an exception is
111
+ # raised, instances will have been created but the calling class won't know.
112
+ begin
113
+ # Assign IP address only if we have one for that position
114
+ if @address
115
+ # Make sure the address is associated to the current account
116
+ if Rudy::AWS::EC2::Addresses.exists?(@address)
117
+ puts "Associating #{@address} to #{@instid}"
118
+ Rudy::AWS::EC2::Addresses.associate(@address, @instid)
119
+ else
120
+ STDERR.puts "Unknown address: #{@address}"
121
+ end
122
+ end
123
+ rescue => ex
124
+ STDERR.puts "Error: #{ex.message}"
125
+ STDERR.puts ex.backtrace if Rudy.debug?
126
+ end
127
+ end
128
+ self.save
129
+ self
130
+ end
131
+
132
+ def destroy
133
+ Rudy::AWS::EC2::Instances.destroy(@instid) if instance_running?
134
+ super
135
+ end
136
+
137
+ def restart
138
+ Rudy::AWS::EC2::Instances.restart(@instid) if instance_running?
139
+ end
140
+
141
+ def refresh!(metadata=true)
142
+ ## Updating the metadata isn't necessary
143
+ ##super if metadata # update metadata
144
+ @instance = get_instance
145
+ if @instance.is_a?(Rudy::AWS::EC2::Instance)
146
+ @dns_public, @dns_private = @instance.dns_public, @instance.dns_private
147
+ @state = @instance.state
148
+ save :replace
149
+ elsif @instance.nil?
150
+ @awsid = @dns_public = @dns_private = nil
151
+ @state = 'rogue'
152
+ # Don't save it b/c it's possible the EC2 server is just down.
153
+ end
154
+ end
155
+
156
+ def generate_machine_data
157
+ d = {}
158
+ [:region, :zone, :environment, :role, :position].each do |k|
159
+ d[k] = self.send k
160
+ end
161
+ d
162
+ end
163
+
164
+ def dns_public?; !@dns_public.nil? && !@dns_public.empty?; end
165
+ def dns_private?; !@dns_private.nil? && !@dns_private.empty?; end
166
+
167
+ # See +available+ attribute
168
+ def available?; @available; end
169
+
170
+ # Create instance_*? methods
171
+ %w[exists? running? pending? terminated? shutting_down? unavailable?].each do |state|
172
+ define_method("instance_#{state}") do
173
+ return false if @instid.nil? || @instid.empty?
174
+ Rudy::AWS::EC2::Instances.send(state, @instid) rescue false # exists?, running?, etc...
175
+ end
176
+ end
177
+
178
+ end
179
+ end
data/lib/rudy/mixins.rb CHANGED
@@ -1,2 +1,3 @@
1
1
 
2
- require "rudy/mixins/hash"
2
+ require "rudy/mixins/hash"
3
+ require "rudy/mixins/symbol"
@@ -20,6 +20,8 @@ class Hash
20
20
  end
21
21
  steps
22
22
  end
23
-
23
+
24
+
25
+
24
26
  end
25
27
 
@@ -0,0 +1,8 @@
1
+
2
+ class Symbol
3
+ unless method_defined? :empty?
4
+ def empty?
5
+ self.to_s.empty?
6
+ end
7
+ end
8
+ end
data/lib/rudy/routines.rb CHANGED
@@ -1,363 +1,146 @@
1
1
 
2
-
3
2
  module Rudy
3
+
4
+ # = Rudy::Routines
5
+ #
6
+ # Every Rudy routine is associated to a handler. There are four standard
7
+ # handler types: Startup, Shutdown, Reboot, and Passthrough. The first
8
+ # three are associated to routines of the same same. All other routines
9
+ # are handled by Rudy::Routines::Passthrough.
10
+ #
11
+ # An individual routine is made up of various actions. Each action is
12
+ # associated to one of the following handlers: depends, disk, script,
13
+ # user. See each handler for the list of actions it is responsible for.
4
14
  module Routines
15
+
16
+ require 'rudy/routines/base'
17
+ require 'rudy/routines/handlers/base'
18
+
19
+ # A Hash of routine names pointing to a specific handler.
20
+ # See Rudy::Routines.add_routine
21
+ @@routine = {}
22
+
23
+ # A Hash of routine keywords pointing to a specifc handler.
24
+ # See Rudy::Routines.add_routine
25
+ @@handler = {}
26
+
5
27
  class NoRoutine < Rudy::Error
6
- def message; "No routine configuration for #{@obj}"; end
28
+ def message; "Unknown routine '#{@obj}'"; end
7
29
  end
8
30
 
9
- class Base
10
- include Rudy::Huxtable
11
-
12
- # * +cmdname+ The name of the command specified on the command line
13
- # * +option+ An OpenStruct of named command line options
14
- # * +argv+ An Array of command line argument
15
- def initialize(cmdname, option, argv, *args)
16
- a, s, r = @@global.accesskey, @@global.secretkey, @@global.region
17
- @sdb = Rudy::AWS::SDB.new(a, s, r)
18
- @rinst = Rudy::AWS::EC2::Instances.new(a, s, r)
19
- @rgrp = Rudy::AWS::EC2::Groups.new(a, s, r)
20
- @rkey = Rudy::AWS::EC2::KeyPairs.new(a, s, r)
21
- @rvol = Rudy::AWS::EC2::Volumes.new(a, s, r)
22
- @rsnap = Rudy::AWS::EC2::Snapshots.new(a, s, r)
23
- @cmdname, @option, @argv = cmdname, option, argv
24
- @option ||= OpenStruct.new
25
- init(*args)
26
- end
27
-
28
- def init; raise "Must override init"; end
29
-
30
- def execute
31
- raise "Override execute method"
32
- end
33
-
34
- def raise_early_exceptions
35
- raise "Must override raise_early_exceptions"
31
+ class NoHandler < Rudy::Error
32
+ def message; "Unknown routine action '#{@obj}'"; end
33
+ end
34
+
35
+ class EmptyDepends < Rudy::Error
36
+ def message; "Empty depends block in routine."; end
37
+ end
38
+
39
+ class GroupNotRunning < Rudy::Error
40
+ def message; "Some machines are not running:#{$/}#{@obj.inspect}"; end
41
+ end
42
+
43
+ class GroupNotAvailable < Rudy::Error
44
+ def message; "Some machines are not available:#{$/}#{@obj.inspect}"; end
45
+ end
46
+
47
+ class UnsupportedActions < Rudy::Error
48
+ def initialize(klass, actions)
49
+ @klass, @actions = klass, [actions].flatten
36
50
  end
37
-
38
- # * +machine_action+ a method on Rudy::Machines, one of: create, destroy, list
39
- # * +routine+ Override +routine+ with another routine (default: nil)
40
- # * +skip_check+ Don't check that the machine is up and SSH is available (default: false)
41
- # * +skip_header+ Don't print machine header (default: false)
42
- # * +routine_action+ is an optional block which will be executed for each
43
- # machine between the disk routine and after blocks. The block receives
44
- # two argument: an instance of Rudy::Machine and one of Rye::Box.
45
- def generic_machine_runner(machine_action, routine=nil, skip_check=false, skip_header=false, &routine_action)
46
- is_available= false
47
-
48
- if @@global.offline
49
- rmach = Rudy::Machines::Offline.new
50
- skip_check = true
51
- remote_user = Rudy.sysinfo.user
52
- else
53
- rmach = Rudy::Machines.new
54
- remote_user = 'root'
55
- end
56
-
57
- routine ||= @routine
58
- raise "No routine supplied" unless routine
59
- raise "No machine action supplied" unless machine_action
60
- unless rmach.respond_to?(machine_action)
61
- raise "Unknown machine action #{machine_action}"
62
- end
63
-
64
- # Declare a couple vars so they're available outide the block
65
- before_dependencies = after_dependencies = nil
66
- enjoy_every_sandwich {
67
- # This gets and removes the dependencies from the routines hash.
68
- if Rudy::Routines::DependsHelper.has_depends?(:before, routine)
69
- before_dependencies = Rudy::Routines::DependsHelper.get(:before, routine)
70
- end
71
-
72
- # We grab the after ones now too, so we don't fool the ScriptHelper
73
- # later on in this routine (after keyword is used for scripts too)
74
- if Rudy::Routines::DependsHelper.has_depends?(:after, routine)
75
- after_dependencies = Rudy::Routines::DependsHelper.get(:after, routine)
76
- end
77
-
78
- # This calls generic_machine_runner for every dependent before routine.
79
- execute_dependency(before_dependencies, skip_check, skip_header)
80
- }
81
-
82
-
83
- lbox = Rye::Box.new('localhost', :info => (@@global.verbose > 3), :debug => false)
84
- sconf = fetch_script_config
85
-
86
- enjoy_every_sandwich {
87
- if Rudy::Routines::ScriptHelper.before_local?(routine) # before_local
88
- # Runs "before_local" scripts of routines config.
89
- task_separator("LOCAL SHELL")
90
- lbox.cd Dir.pwd # Run local command block from current working directory
91
- Rudy::Routines::ScriptHelper.before_local(routine, sconf, lbox, @option, @argv)
92
- end
93
- }
94
-
95
- enjoy_every_sandwich {
96
- if Rudy::Routines::ScriptHelper.script_local?(routine) # script_local
97
- # Runs "script_local" scripts of routines config.
98
- # NOTE: This is synonymous with before_local
99
- task_separator("LOCAL SHELL")
100
- lbox.cd Dir.pwd # Run local command block from current working directory
101
- Rudy::Routines::ScriptHelper.script_local(routine, sconf, lbox, @option, @argv)
102
- end
103
- }
104
-
105
- # Execute the action (create, list, destroy, restart)
106
- machines = enjoy_every_sandwich([]) { rmach.send(machine_action) } || []
107
-
108
- machines.each do |machine|
109
- puts machine_separator(machine.name, machine.awsid) unless skip_header
110
-
111
- unless skip_check
112
- msg = preliminary_separator("Checking if instance is running...")
113
- Rudy::Utils.waiter(3, 120, STDOUT, msg, 0) {
114
- inst = machine.get_instance
115
- inst && inst.running?
116
- }
117
- end
118
-
119
- # Add instance info to machine and save it. This is really important
120
- # for the initial startup so the metadata is updated right away. But
121
- # it's also important to call here because if a routine was executed
122
- # and an unexpected exception occurs before this update is executed
123
- # the machine metadata won't contain the DNS information. Calling it
124
- # here ensure that the metadata is always up-to-date.
125
- machine.update
126
-
127
- # This is a short-circuit for Windows instances. We don't support
128
- # disks for windows yet and there's no SSH so routines are out of
129
- # the picture too. Here we simply run the per machine block which
130
- # is crucial for shutdown and possibly others as well.
131
- if (machine.os || '').to_s == 'win32'
132
- enjoy_every_sandwich {
133
- # Startup, shutdown, release, deploy, etc...
134
- routine_action.call(machine, nil) if routine_action
135
- }
136
-
137
- next # The short circuit
138
- end
139
-
140
- if !skip_check && has_remote_task?(routine)
141
- enjoy_every_sandwich {
142
- msg = preliminary_separator("Waiting for SSH daemon...")
143
- ret = Rudy::Utils.waiter(2, 30, STDOUT, msg, 0) {
144
- Rudy::Utils.service_available?(machine.dns_public, 22)
145
- }
146
- is_available = ret
147
- }
148
- end
149
-
150
- if is_available
151
- # TODO: trap rbox errors. We could get an authentication error.
152
- opts = { :keys => root_keypairpath, :user => remote_user,
153
- :info => @@global.verbose > 3, :debug => false }
154
- begin
155
- rbox = Rye::Box.new(machine.dns_public, opts)
156
- Rudy::Utils.waiter(2, 10, STDOUT, nil, 0) { rbox.connect }
157
- rescue Rye::NoHost => ex
158
- STDERR.puts "No host: #{ex.message}"
159
- exit 65
160
- end
161
-
162
- unless skip_check
163
- # Set the hostname if specified in the machines config.
164
- # :rudy -> change to Rudy's machine name
165
- # :default -> leave the hostname as it is
166
- # Anything else other than nil -> change to that value
167
- # NOTE: This will set hostname every time a routine is
168
- # run so we may want to make this an explicit action.
169
- enjoy_every_sandwich {
170
- hn = current_machine_hostname || :rudy
171
- if hn != :default
172
- hn = machine.name if hn == :rudy
173
- print preliminary_separator("Setting hostname to #{hn}... ")
174
- rbox.hostname(hn)
175
- puts "done"
176
- end
177
- }
178
- end
179
-
180
-
181
- ## NOTE: This prevents shutdown from doing its thing and prob
182
- ## isn't necessary.
183
- ##unless has_remote_task?(routine)
184
- ## puts "[no remote tasks]"
185
- ## next
186
- ##end
187
-
188
- enjoy_every_sandwich {
189
- if Rudy::Routines::UserHelper.adduser?(routine) # adduser
190
- task_separator("ADD USER")
191
- Rudy::Routines::UserHelper.adduser(routine, machine, rbox)
192
- end
193
- }
194
-
195
- enjoy_every_sandwich {
196
- if Rudy::Routines::UserHelper.authorize?(routine) # authorize
197
- task_separator("AUTHORIZE USER")
198
- Rudy::Routines::UserHelper.authorize(routine, machine, rbox)
199
- end
200
- }
201
-
202
- enjoy_every_sandwich {
203
- if Rudy::Routines::ScriptHelper.before?(routine) # before
204
- task_separator("REMOTE SHELL")
205
- Rudy::Routines::ScriptHelper.before(routine, sconf, machine, rbox, @option, @argv)
206
- end
207
- }
208
-
209
- enjoy_every_sandwich {
210
- if Rudy::Routines::DiskHelper.disks?(routine) # disk
211
- task_separator("DISKS")
212
- ##if rbox.ostype == "sunos"
213
- ## puts "Sorry, Solaris disks are not supported yet!"
214
- ##else
215
- Rudy::Routines::DiskHelper.execute(routine, machine, rbox)
216
- ##end
217
- end
218
- }
219
-
220
- end
221
-
222
- enjoy_every_sandwich {
223
- # Startup, shutdown, release, deploy, etc...
224
- routine_action.call(machine, rbox) if routine_action
225
- }
226
-
227
-
228
- if is_available
229
- # The "after" blocks are synonymous with "script" blocks.
230
- # For some routines, like startup, it makes sense to an
231
- # "after" block b/c "script" is ambiguous. In generic
232
- # routines, there is no concept of before or after. The
233
- # definition is the entire routine so we use "script".
234
- # NOTE: If both after and script are supplied they will
235
- # both be executed.
236
- enjoy_every_sandwich {
237
- if Rudy::Routines::ScriptHelper.script?(routine) # script
238
- task_separator("REMOTE SHELL")
239
- # Runs "after" scripts of routines config
240
- Rudy::Routines::ScriptHelper.script(routine, sconf, machine, rbox, @option, @argv)
241
- end
242
- }
243
-
244
- enjoy_every_sandwich {
245
- if Rudy::Routines::ScriptHelper.after?(routine) # after
246
- task_separator("REMOTE SHELL")
247
- # Runs "after" scripts of routines config
248
- Rudy::Routines::ScriptHelper.after(routine, sconf, machine, rbox, @option, @argv)
249
- end
250
- }
251
-
252
- rbox.disconnect
253
- end
254
- end
255
-
256
- enjoy_every_sandwich {
257
- if Rudy::Routines::ScriptHelper.after_local?(routine) # after_local
258
- task_separator("LOCAL SHELL")
259
- lbox.cd Dir.pwd # Run local command block from current working directory
260
- # Runs "after_local" scripts of routines config
261
- Rudy::Routines::ScriptHelper.after_local(routine, sconf, lbox, @option, @argv)
262
- end
263
- }
264
-
265
- # This calls generic_machine_runner for every dependent after routine
266
- enjoy_every_sandwich {
267
- execute_dependency(after_dependencies, skip_check, skip_header)
51
+ def message; "#{@klass} does not support: #{@actions.join(', ')}"; end
52
+ end
53
+
54
+ # Add a routine handler to @@routine.
55
+ #
56
+ # * +routine_name+ Literally the name of the routine that will
57
+ # have a special handler, like startup, shutdown, and reboot.
58
+ # * +handler+ The class that will handle this routine. It must
59
+ # inherit Rudy::Routine::Base
60
+ #
61
+ # Returns the value of +handler+.
62
+ def self.add_routine(name, klass)
63
+ add_some_class @@routine, Rudy::Routines::Base, name, klass
64
+ end
65
+
66
+ # Returns the value in the @@routine associated to the key +routine_name+
67
+ # if it exists, otherwise it returns Rudy::Routines::Passthrough
68
+ def self.get_routine(name)
69
+ get_some_class(@@routine, name) || Rudy::Routines::Passthrough
70
+ end
71
+
72
+ # Add a routine handler to @@handler.
73
+ def self.add_handler(name, klass)
74
+ add_some_class @@handler, Rudy::Routines::Handlers::Base, name, klass
75
+ end
76
+
77
+ # Returns the value in the @@handler associated to the key +name+
78
+ # if it exists, otherwise it returns nil
79
+ def self.get_handler(name)
80
+ get_some_class(@@handler, name) || nil
81
+ end
82
+
83
+ def self.has_routine?(name); @@routine.has_key?(name); end
84
+ def self.has_handler?(name); @@handler.has_key?(name); end
85
+
86
+ # Executes a routine block
87
+ def self.runner(routine, rset, lbox, argv=nil)
88
+ routine.each_pair do |name,definition|
89
+ handler = Rudy::Routines.get_handler name
90
+ Rudy::Huxtable.ld " executing handler: #{name}"
91
+ self.rescue {
92
+ handler.execute(name, definition, rset, lbox, argv)
268
93
  }
269
-
270
- machines
271
94
  end
272
-
273
- def execute_dependency(depends, skip_check, skip_header)
274
- return unless depends
275
- unless depends.empty?
276
- depends.each_with_index do |d, index|
277
- task_separator("DEPENDENCY: #{d}")
278
- routine_dependency = fetch_routine_config(d)
279
- unless routine_dependency
280
- STDERR.puts " Unknown routine: #{d}".color(:red)
281
- next
282
- end
283
- # NOTE: running routines here means they do not have their own
284
- # payload and they must use the list action. I think this is ok
285
- # though b/c there should only be a few routines with payloads
286
- # (startup, shutdown, reboot)
287
- generic_machine_runner(:list, routine_dependency, skip_check, skip_header)
95
+ end
96
+
97
+ def self.rescue(ret=nil, &bloc_party)
98
+ begin
99
+ ret = bloc_party.call
100
+ rescue NameError, ArgumentError, RuntimeError => ex
101
+ STDERR.puts " #{ex.class}: #{ex.message}".color(:red)
102
+ STDERR.puts ex.backtrace if Rudy.debug?
103
+ unless Rudy::Huxtable.global.parallel
104
+ choice = Annoy.get_user_input('(S)kip (A)bort: ', nil, 3600) || ''
105
+ if choice.match(/\AS/i)
106
+ # do nothing
107
+ else
108
+ exit 12
288
109
  end
289
- end
290
- end
291
-
292
-
293
- # Does the given +routine+ define any remote tasks?
294
- def has_remote_task?(routine)
295
- any = [Rudy::Routines::DiskHelper.disks?(routine),
296
- Rudy::Routines::ScriptHelper.before?(routine),
297
- Rudy::Routines::ScriptHelper.after?(routine),
298
- Rudy::Routines::ScriptHelper.script?(routine),
299
- Rudy::Routines::UserHelper.authorize?(routine),
300
- Rudy::Routines::UserHelper.adduser?(routine),
301
- !@after_dependencies.nil?,
302
- !@before_dependencies.nil?]
303
- # Throw away all false answers (and nil answers)
304
- any = any.compact.select { |success| success }
305
- !any.empty? # Returns true if any element contains true
306
- end
307
-
308
- def preliminary_separator(msg)
309
- # TODO: Count number messages printed 1/3. ie:
310
- # m-us-east-1b-stage-app-01
311
- # (1/3) Checking if instance is running... done
312
- # (2/3) Waiting for SSH daemon... done
313
- # (3/3) Setting hostame to m-us-east-1b-stage-app-01... done
314
- (" -> #{msg}")
110
+ end
111
+ rescue Interrupt
112
+ puts "Aborting..."
113
+ exit 12
315
114
  end
316
-
317
- def task_separator(title)
318
- dashes = 59 - title.size
319
- dashes = 0 if dashes < 1
320
- puts ("%s--- %s %s" % [$/, title, '-'*dashes]) if @@global.verbose > 2
321
- end
322
-
323
- def machine_separator(name, awsid)
324
- ('%s %-50s awsid: %s ' % [$/, name, awsid]).att(:reverse)
325
- end
326
-
327
- def routine_separator(name)
328
- # Not used (for now)
329
- name = name.to_s
330
- dashes = 59 - name.size #
331
- dashes = 0 if dashes < 1
332
- #puts '%-40s' % [name.bright]
115
+ ret
116
+ end
117
+
118
+ def self.machine_separator(name, awsid)
119
+ ('%s %-50s awsid: %s ' % [$/, name, awsid]).att(:reverse)
120
+ end
121
+
122
+ private
123
+
124
+ # See Rudy::Routines.add_routine
125
+ def self.add_some_class(store, super_klass, name, klass)
126
+ if store.has_key? name
127
+ Rudy::Huxtable.li "Redefining class for #{name}"
333
128
  end
334
-
335
- def enjoy_every_sandwich(ret=nil, &bloc_party)
336
- begin
337
- ret = bloc_party.call
338
- rescue => ex
339
- STDERR.puts " Error: #{ex.message}".color(:red)
340
- STDERR.puts ex.backtrace if Rudy.debug?
341
- choice = Annoy.get_user_input('(S)kip (R)etry (A)bort: ') || ''
342
- if choice.match(/\AS/i)
343
- return
344
- elsif choice.match(/\AR/i)
345
- retry
346
- else
347
- exit 12
348
- end
349
- rescue Interrupt
350
- puts "Aborting..."
351
- exit 12
352
- end
353
- ret
129
+ unless klass.ancestors.member? super_klass
130
+ raise "#{klass} does not inherit #{super_klass}"
354
131
  end
355
-
132
+ store[name] = klass
356
133
  end
134
+
135
+ # See Rudy::Routines.get_routine
136
+ def self.get_some_class(store, routine_name)
137
+ routine_name &&= routine_name.to_sym
138
+ store[routine_name]
139
+ end
140
+
357
141
  end
358
142
  end
359
143
 
360
144
  Rudy::Utils.require_glob(RUDY_LIB, 'rudy', 'routines', '*.rb')
361
- Rudy::Utils.require_glob(RUDY_LIB, 'rudy', 'routines', 'helpers', '*.rb')
362
-
145
+ Rudy::Utils.require_glob(RUDY_LIB, 'rudy', 'routines', 'handlers', '*.rb')
363
146