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
@@ -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