rudy 0.7.0 → 0.7.1

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.
data/CHANGES.txt CHANGED
@@ -8,7 +8,22 @@ RUDY, CHANGES
8
8
  * TODO: Tests for AWS HTTPS
9
9
 
10
10
 
11
- #### 0.7.0 (2009-04-??) ###############################
11
+ #### 0.7.1 (2009-04-04) ###############################
12
+
13
+ * FIXED: rudy wasn't honouring -C option for specifying a config file
14
+ * FIXED: Was disabling, but not enabling colour
15
+ * FIXED: Symbol errors in 1.8
16
+ * FIXED: Command blocks for root user are always run first now in Routines config.
17
+ * ADDED: "script" keyword to routines config.
18
+ * CHANGE: routines before, after, and script blocks are now processed by Rye::Box.batch as Procs.
19
+ * CHANGE: deny was removed from commands config (it will come back later).
20
+ * ADDED: examples/
21
+ * CHANGE: Rudy now checks the current working directory for machines, commands, etc... configs
22
+ * ADDED: script_local
23
+ * ADDED: Routine now checks whether there are any remote tasks. If not, it doesn't loop through machines.
24
+
25
+
26
+ #### 0.7.0 (2009-04-02) ###############################
12
27
 
13
28
  * CHANGE: Upgrade to Drydock 0.6, Caesars 0.6, and Rye 0.6
14
29
  * CHANGE: Updated Routines configuration syntax for specifying shell commands
data/README.rdoc CHANGED
@@ -29,7 +29,7 @@ All configuration is organized into the zones, environments, and roles that you
29
29
  * Powerful command-line tools
30
30
  * <tt>$ rudy -u root ssh</tt>
31
31
  * <tt>$ rudy -e testing -r database startup</tt>
32
- * Use _any_ Amazon machine image (AMI)
32
+ * Use _any Linux_ Amazon machine image (AMI) (partial Solaris support)
33
33
  * Complete command-line interface for EC2. See <tt>bin/rudy-ec2</tt>.
34
34
 
35
35
  == Installation
@@ -40,8 +40,8 @@ Via Rubygems, one of:
40
40
  $ sudo gem install solutious-rudy --source http://gems.github.com/
41
41
 
42
42
  or via download:
43
- * rudy-latest.tar.gz[http://github.com/solutious/rudy/tarball/rudy-latest]
44
- * rudy-latest.zip[http://github.com/solutious/rudy/zipball/rudy-latest]
43
+ * rudy-latest.tar.gz[http://github.com/solutious/rudy/tarball/latest]
44
+ * rudy-latest.zip[http://github.com/solutious/rudy/zipball/latest]
45
45
 
46
46
  NOTE: <em>If you are not installing via RubyGems, you need to make sure the dependencies are in your LOAD_PATH (<tt>$:</tt>). Ryan Tomayko wrote a gist[http://gist.github.com/54177] about it.</em>
47
47
 
data/Rakefile CHANGED
@@ -11,6 +11,8 @@ include FileUtils
11
11
 
12
12
  task :default => :test
13
13
 
14
+
15
+
14
16
  # TESTS ===============================================================
15
17
 
16
18
  Rake::TestTask.new(:test_old) do |t|
data/Rudyfile CHANGED
@@ -81,13 +81,20 @@ end
81
81
 
82
82
 
83
83
  # ----------------------------------------------------------- COMMANDS --------
84
- # The commands block defines shell commands to be allowed or denied from the
85
- # default list defined by Rye::Cmd (Rudy executes all SSH commands via Rye).
86
- # Commands can have any name except for keywords already used by Rudy.
84
+ # The commands block defines shell commands that can be used in routines. The
85
+ # ones defined here are added to the default list defined by Rye::Cmd (Rudy
86
+ # executes all SSH commands via Rye).
87
+ #
88
+ # Usage:
89
+ #
90
+ # allow COMMAND-NAME
91
+ # allow COMMAND-NAME, '/path/2/COMMAND'
92
+ # allow COMMAND-NAME, '/path/2/COMMAND', 'default argument', 'another arg'
93
+ #
87
94
  commands do
95
+ allow :make
88
96
  allow :gem_install, "/usr/bin/gem", "install", :V, "--no-rdoc", "--no-ri"
89
97
  allow :apt_get, "/usr/bin/apt-get", :y, :q
90
- deny :kill
91
98
  end
92
99
 
93
100
 
data/bin/rudy CHANGED
@@ -23,7 +23,7 @@ require 'rudy/cli'
23
23
  # Command-line interface for /bin/rudy
24
24
  class RudyCLI < Rudy::CLI::Base
25
25
 
26
- debug :on
26
+ debug :off
27
27
  default :machines
28
28
  trawler :passthrough
29
29
 
@@ -86,6 +86,10 @@ class RudyCLI < Rudy::CLI::Base
86
86
  usage "rudy shutdown"
87
87
  command :shutdown => Rudy::CLI::Routines
88
88
 
89
+ #about "Restart a machine group"
90
+ #usage "rudy restart"
91
+ #command :restart => Rudy::CLI::Routines
92
+
89
93
  about "Create a release"
90
94
  usage "rudy release"
91
95
  command :release => Rudy::CLI::Routines
data/lib/rudy.rb CHANGED
@@ -45,7 +45,7 @@ module Rudy
45
45
  unless defined?(MAJOR)
46
46
  MAJOR = 0.freeze
47
47
  MINOR = 7.freeze
48
- TINY = 0.freeze
48
+ TINY = 1.freeze
49
49
  end
50
50
  def self.to_s; [MAJOR, MINOR, TINY].join('.'); end
51
51
  def self.to_f; self.to_s.to_f; end
data/lib/rudy/cli.rb CHANGED
@@ -26,8 +26,7 @@ module Rudy
26
26
  protected
27
27
  def init
28
28
 
29
-
30
- Rudy::Huxtable.update_config
29
+ #Caesars.enable_debug
31
30
 
32
31
  # The CLI wants output!
33
32
  Rudy::Huxtable.update_logger STDOUT
@@ -35,6 +34,16 @@ module Rudy
35
34
  # Send The Huxtables the global values from the command-line
36
35
  Rudy::Huxtable.update_global @global
37
36
 
37
+ # Reload configuration. This must come after update_global
38
+ # so it will catch the @@global.config path (if supplied).
39
+ begin
40
+ Rudy::Huxtable.update_config
41
+ rescue Caesars::SyntaxError => ex
42
+ STDERR.puts ex.message
43
+ STDERR.puts ex.backtrace if @@global.verbose > 0
44
+ exit 81
45
+ end
46
+
38
47
  unless @@global.accesskey && @@global.secretkey
39
48
  STDERR.puts "No AWS credentials. Check your configs!"
40
49
  STDERR.puts "Try: rudy init"
@@ -9,21 +9,25 @@ module Rudy; module CLI;
9
9
  true
10
10
  end
11
11
  def startup
12
-
13
- @rr.execute
14
-
12
+ machines = @rr.execute
15
13
  puts $/, "The following machines are now available:"
16
- rmach = Rudy::Machines.new
17
- rmach.list do |machine|
14
+ machines.each do |machine|
18
15
  puts machine.to_s
19
16
  end
20
-
21
- if @@global.environment == @@config.defaults.environment &&
22
- @@global.role == @@config.defaults.role
23
- #puts
24
- #puts "Login with: rudy -u root ssh"
25
- end
26
-
17
+ end
18
+
19
+ def restart_valid?
20
+ @rr = Rudy::Routines::Restart.new
21
+ @rr.raise_early_exceptions
22
+ true
23
+ end
24
+ def restart
25
+ #machines = @rr.execute
26
+ #puts $/, "The following machines have been restarted:"
27
+ #machines.each do |machine|
28
+ # puts machine.to_s
29
+ #end
30
+ #puts "Restart is disabled"
27
31
  end
28
32
 
29
33
  def release_valid?
data/lib/rudy/config.rb CHANGED
@@ -61,10 +61,13 @@ module Rudy
61
61
  break
62
62
  end
63
63
 
64
+ typelist = self.keys.collect { |g| "#{g}.rb" }.join(',')
65
+
64
66
  # Rudy then looks for the rest of the config in these locations
65
67
  @paths += Dir.glob(File.join(cwd, 'Rudyfile')) || []
66
68
  @paths += Dir.glob(File.join(cwd, 'config', 'rudy', '*.rb')) || []
67
69
  @paths += Dir.glob(File.join(cwd, '.rudy', '*.rb')) || []
70
+ @paths += Dir.glob(File.join(cwd, "{#{typelist}}")) || []
68
71
  @paths += Dir.glob(File.join('/etc', 'rudy', '*.rb')) || []
69
72
  @paths &&= @paths.uniq
70
73
 
@@ -91,21 +91,16 @@ class Rudy::Config
91
91
  arg.is_a?(Symbol) ? ":#{arg}" : "'#{arg}'"
92
92
  end
93
93
  hard_args = args.empty? ? "*args" : "#{args.join(', ')}, *args"
94
-
95
- # Command keywords must be parsed with forced_array. See ScriptHelper.
96
- Rudy::Config::Routines.forced_array cmd
94
+
95
+ # TODO: Use define_method
97
96
  Rye::Cmd.module_eval %Q{
98
97
  def #{cmd}(*args); cmd(:'#{path}', #{hard_args}); end
99
98
  }
99
+
100
100
  end
101
- # We deny commands by telling routines to not parse the given keywords
102
- self.deny.each do |cmd|
103
- Rudy::Config::Routines.forced_ignore cmd.first # cmd is a forced array
104
- # We don't remove the method from Rye:Cmd because we
105
- # may need elsewhere in Rudy. Forced ignore ensures
106
- # the config is not stored anyhow.
107
- end
108
- raise Caesars::Config::ForceRefresh.new(:routines)
101
+ ## NOTE: We now process command blocks as Procs rather than individual commands.
102
+ # There's currently no need to ForceRefresh here
103
+ ##raise Caesars::Config::ForceRefresh.new(:routines)
109
104
  end
110
105
  end
111
106
 
@@ -124,31 +119,26 @@ class Rudy::Config
124
119
  forced_hash :restore
125
120
  forced_hash :mount
126
121
 
127
- # Remote scripts
128
- forced_hash :before
129
- forced_hash :before_local
130
- forced_hash :after
131
- forced_hash :after_local
122
+ # Script blocks
123
+ forced_hash :before
124
+ forced_hash :after
125
+ forced_hash :script_local
126
+ forced_hash :before_local
127
+ forced_hash :after_local # We force hash the script keywords
128
+ forced_hash :script # b/c we want them to store the usernames
129
+ chill :before # as hash keys.
130
+ chill :after # We also chill them b/c we want to execute
131
+ chill :before_local # the command blocks with an instance_eval
132
+ chill :after_local # inside a Rye::Box object.
133
+ chill :script
134
+ chill :script_local
132
135
 
133
136
  # Version control systems
134
137
  forced_hash :git
135
138
  forced_hash :svn
136
139
 
137
- def init
138
-
139
- end
140
-
141
- # Add remote shell commands to the DSL as forced Arrays.
142
- # Example:
143
- # ls :a, :l, "/tmp" # => :ls => [[:a, :l, "/tmp"]]
144
- # ls :o # => :ls => [[:a, :l, "/tmp"], [:o]]
145
- # NOTE: Beware of namespace conflicts in other areas of the DSL,
146
- # specifically shell commands that have the same name as a keyword
147
- # we want to use in the DSL. This includes commands that were added
148
- # to Rye::Cmd before Rudy is 'require'd.
149
- Rye::Cmd.instance_methods.sort.each do |cmd|
150
- forced_array cmd
140
+ def init
151
141
  end
152
-
142
+
153
143
  end
154
144
  end
data/lib/rudy/disks.rb CHANGED
@@ -4,6 +4,7 @@ module Rudy
4
4
  class Disk < Storable
5
5
  include Rudy::MetaData::ObjectBase
6
6
 
7
+
7
8
  field :rtype
8
9
  field :awsid
9
10
  field :status
@@ -20,6 +21,11 @@ class Disk < Storable
20
21
  field :size
21
22
  #field :backups => Array
22
23
 
24
+ # Is the associated volume formatted? One of: true, false, [empty].
25
+ # [empty] means we don't know and it's the default.
26
+ field :raw
27
+
28
+ field :fstype
23
29
  field :mounted
24
30
 
25
31
  def init(path=nil, size=nil, device=nil, position=nil)
@@ -46,7 +52,6 @@ class Disk < Storable
46
52
 
47
53
  def to_s(with_titles=true)
48
54
  update
49
- puts "FUCK" if @mounted
50
55
  mtd = @mounted == true ? "mounted" : @status
51
56
  "%s; %3sGB; %s; %s" % [liner_note, @size, @device, mtd]
52
57
  end
@@ -75,6 +80,7 @@ class Disk < Storable
75
80
  raise "#{name} is already running" if exists?
76
81
  vol = @rvol.create(@size, @zone, snapshot)
77
82
  @awsid = vol.awsid
83
+ @raw = true
78
84
  self.save
79
85
  self
80
86
  end
data/lib/rudy/global.rb CHANGED
@@ -42,7 +42,7 @@ module Rudy
42
42
  postprocess
43
43
  # These attributes MUST have values.
44
44
  @verbose ||= 0
45
- @nocolor = true unless @nocolor == "false" || @nocolor.is_a?(FalseClass)
45
+ @nocolor = true unless @nocolor == "false" || @nocolor == false
46
46
  @quiet ||= false
47
47
  @format ||= :string # as in, to_s
48
48
  @print_header = true if @print_header == nil
@@ -97,9 +97,9 @@ module Rudy
97
97
  @position &&= @position.to_s.rjust(2, '0')
98
98
  @format &&= @format.to_sym rescue nil
99
99
 
100
- String.disable_color if @nocolor
101
- Rudy.enable_quiet if @quiet
102
- Annoy.enable_skip if @yes
100
+ @nocolor ? String.disable_color : String.enable_color
101
+ @quiet ? Rudy.enable_quiet : Rudy.disable_quiet
102
+ @yes ? Annoy.enable_skip : Annoy.disable_skip
103
103
  end
104
104
 
105
105
  def apply_environment_variables
data/lib/rudy/huxtable.rb CHANGED
@@ -38,8 +38,9 @@ module Rudy
38
38
  #def logger; @@logger; end
39
39
 
40
40
  def self.update_config(path=nil)
41
- # nil or otherwise bad paths send to look_and_load are ignored
42
- @@config.look_and_load(path || nil)
41
+ @@config.verbose = (@@global.verbose > 0)
42
+ # nil and bad paths sent to look_and_load are ignored
43
+ @@config.look_and_load(path || @@global.config)
43
44
  @@global.apply_config(@@config)
44
45
  end
45
46
 
data/lib/rudy/machines.rb CHANGED
@@ -144,6 +144,9 @@ module Rudy
144
144
  super
145
145
  end
146
146
 
147
+ def restart
148
+ @ec2inst.restart(@awsid) if running?
149
+ end
147
150
 
148
151
  def Machine.generate_machine_data
149
152
  data = { # Give the machine an identity
@@ -223,6 +226,19 @@ module Rudy
223
226
  end
224
227
  end
225
228
 
229
+
230
+ def restart(&each_mach)
231
+ raise MachineGroupNotDefined, current_machine_group unless known_machine_group?
232
+ raise MachineGroupNotRunning, current_machine_group unless running?
233
+ machines = list
234
+ machines.each do |mach|
235
+ each_mach.call(mach) if each_mach
236
+ puts "Restarting #{mach.name}"
237
+ mach.restart
238
+ end
239
+ machines
240
+ end
241
+
226
242
  def list(more=[], less=[], &each_mach)
227
243
  machines = list_as_hash(more, less, &each_mach)
228
244
  machines &&= machines.values
data/lib/rudy/routines.rb CHANGED
@@ -50,12 +50,23 @@ module Rudy
50
50
 
51
51
  if Rudy::Routines::ScriptHelper.before_local?(@routine) # before_local
52
52
  # Runs "before_local" scripts of routines config.
53
- # NOTE: Does not run "before" scripts b/c there are no remote machines
54
53
  puts task_separator("LOCAL SHELL")
55
54
  Rudy::Routines::ScriptHelper.before_local(@routine, sconf, lbox)
56
55
  end
57
56
 
58
- # Execute the action (create, list, destroy) & apply the block to each
57
+ if Rudy::Routines::ScriptHelper.script_local?(@routine) # before_local
58
+ # Runs "script_local" scripts of routines config.
59
+ # NOTE: This is synonymous with before_local
60
+ puts task_separator("LOCAL SHELL")
61
+ Rudy::Routines::ScriptHelper.script_local(@routine, sconf, lbox)
62
+ end
63
+
64
+ unless has_remote_task?(@routine)
65
+ puts "[no remote tasks]"
66
+ return
67
+ end
68
+
69
+ # Execute the action (create, list, destroy, restart) & apply the block to each
59
70
  rmach.send(machine_action) do |machine|
60
71
  puts machine_separator(machine.name, machine.awsid)
61
72
 
@@ -131,7 +142,21 @@ module Rudy
131
142
 
132
143
  # Startup, shutdown, release, deploy, etc...
133
144
  routine_action.call(machine, rbox) if routine_action
134
-
145
+
146
+ # The "after" blocks are synonymous with "script" blocks.
147
+ # For some routines, like startup, it makes sense to an
148
+ # "after" block b/c "script" is ambiguous. In generic
149
+ # routines, there is no concept of before or after. The
150
+ # definition is the entire routine so we use "script".
151
+ # NOTE: If both after and script are supplied they will
152
+ # both be executed.
153
+ if Rudy::Routines::ScriptHelper.script?(@routine) # script
154
+ puts task_separator("REMOTE SHELL")
155
+ # Runs "after" scripts of routines config
156
+ Rudy::Routines::ScriptHelper.script(@routine, sconf, machine, rbox)
157
+ end
158
+
159
+
135
160
  if Rudy::Routines::ScriptHelper.after?(@routine) # after
136
161
  puts task_separator("REMOTE SHELL")
137
162
  # Runs "after" scripts of routines config
@@ -156,6 +181,19 @@ module Rudy
156
181
 
157
182
  end
158
183
 
184
+ # Does the given +routine+ define any remote tasks?
185
+ def has_remote_task?(routine)
186
+ any = [Rudy::Routines::DiskHelper.disks?(routine),
187
+ Rudy::Routines::ScriptHelper.before?(routine),
188
+ Rudy::Routines::ScriptHelper.after?(routine),
189
+ Rudy::Routines::ScriptHelper.script?(routine),
190
+ Rudy::Routines::UserHelper.authorize?(routine),
191
+ Rudy::Routines::UserHelper.adduser?(routine)]
192
+ # Throw away all false answers (and nil answers)
193
+ any = any.compact.select { |success| success }
194
+ !any.empty? # Returns true if any element contains true
195
+ end
196
+
159
197
  def preliminary_separator(msg)
160
198
  # TODO: Count number messages printed 1/3. ie:
161
199
  # m-us-east-1b-stage-app-01
@@ -7,9 +7,13 @@ module Rudy
7
7
 
8
8
  def execute_rbox_command(ret=nil, &command)
9
9
  begin
10
- ret = command.call
10
+ ret = command.call if command
11
+ return unless ret.is_a?(Rye::Rap)
11
12
  puts ' ' << ret.stdout.join("#{$/} ") if !ret.stdout.empty?
12
13
  print_response(ret)
14
+ rescue IOError => ex
15
+ STDERR.puts " Connection Error (#{ex.message})".color(:red)
16
+ exit 12 unless keep_going?
13
17
  rescue Rye::CommandError => ex
14
18
  print_response(ex)
15
19
  exit 12 unless keep_going?
@@ -39,7 +43,8 @@ module Rudy
39
43
  colour = rap.exit_code != 0 ? :red : :normal
40
44
  [:stderr].each do |sumpin|
41
45
  next if rap.send(sumpin).empty?
42
- STDERR.puts (" #{sumpin.upcase.to_s} " << '-'*38).color(colour).bright
46
+ STDERR.puts
47
+ STDERR.puts((" #{sumpin.to_s.upcase} " << '-'*38).color(colour).bright)
43
48
  STDERR.puts " " << rap.send(sumpin).join("#{$/} ").color(colour)
44
49
  end
45
50
  STDERR.puts " Exit code: #{rap.exit_code}".color(colour) if rap.exit_code != 0
@@ -7,7 +7,7 @@ module Rudy; module Routines;
7
7
 
8
8
  def disks?(routine)
9
9
  (routine.is_a?(Caesars::Hash) && routine.disks &&
10
- routine.disks.is_a?(Caesars::Hash) && !routine.disks.empty?)
10
+ routine.disks.is_a?(Caesars::Hash) && !routine.disks.empty?) ? true : false
11
11
  end
12
12
 
13
13
  def paths(routine)
@@ -25,18 +25,24 @@ module Rudy; module Routines;
25
25
 
26
26
  # We need to add mkfs since it's not enabled by default.
27
27
  # We add it only to this instance we're using.
28
- def @rbox.mkfs(*args); cmd('mkfs', args); end
28
+ # We give it a funny name so we can delete it.
29
+ def @rbox.rudy_mkfs(*args); cmd('mkfs', args); end
29
30
 
30
- return unless disks?(routine)
31
31
 
32
+ return unless disks?(routine)
33
+
34
+ modified = []
32
35
  routine.disks.each_pair do |action, disks|
33
36
  unless DiskHelper.respond_to?(action)
34
37
  STDERR.puts %Q(DiskHelper: unknown action "#{action}")
35
38
  next
36
39
  end
37
40
  send(action, disks) # create, copy, destroy, ...
41
+ modified << disks
38
42
  end
39
43
 
44
+ # TODO: remove rudy_mkfs method
45
+
40
46
  end
41
47
 
42
48
  def create(disks)
@@ -46,17 +52,26 @@ module Rudy; module Routines;
46
52
  disk = Rudy::Disk.new(path, props[:size], props[:device], @machine.position)
47
53
  olddisk = rdisk.get(disk.name)
48
54
  if olddisk && olddisk.exists?
49
- puts "Disk exists: #{olddisk.name}".color(:red)
50
- return
55
+ olddisk.update
56
+ puts "Disk found: #{olddisk.name}"
57
+ if olddisk.attached?
58
+ puts "In use. Skipping...".color(:red)
59
+ return
60
+ else
61
+ disk = olddisk
62
+ end
63
+ else
64
+ puts "Creating #{disk.name} "
65
+ disk.fstype = props[:fstype] || 'ext3'
51
66
  end
52
67
 
53
- puts "Creating #{disk.name} "
54
-
55
- msg = "Creating volume... "
56
- disk.create
57
- Rudy::Utils.waiter(2, 60, STDOUT, msg) {
58
- disk.available?
59
- }
68
+ unless disk.exists? # Checks the EBS volume
69
+ msg = "Creating volume... "
70
+ disk.create
71
+ Rudy::Utils.waiter(2, 60, STDOUT, msg) {
72
+ disk.available?
73
+ }
74
+ end
60
75
 
61
76
  msg = "Attaching #{disk.awsid} to #{@machine.awsid}... "
62
77
  disk.attach(@machine.awsid)
@@ -71,30 +86,40 @@ module Rudy; module Routines;
71
86
 
72
87
  # TODO: Cleanup. See ScriptHelper
73
88
  begin
74
- print "Creating ext3 filesystem for #{disk.device}... "
75
- execute_rbox_command {
76
-
77
- @rbox.mkfs(:t, "ext3", :F, disk.device)
78
- @rbox.mkdir(:p, disk.path)
89
+ if disk.raw == true
90
+ print "Creating #{disk.fstype} filesystem for #{disk.device}... "
91
+ @rbox.rudy_mkfs(:t, disk.fstype, :F, disk.device)
92
+ disk.raw = false
93
+ disk.save
79
94
  puts "done"
80
-
81
- print "Mounting at #{disk.path}... "
82
-
83
- @rbox.mount(:t, 'ext3', disk.device, disk.path)
84
- }
95
+ end
96
+
97
+ @rbox.mkdir(:p, disk.path)
98
+
99
+ print "Mounting at #{disk.path}... "
100
+
101
+ ret = @rbox.mount(:t, disk.fstype, disk.device, disk.path)
102
+ print_response ret
103
+ if ret.exit_code > 0
104
+ STDERR.puts "Error creating disk".color(:red)
105
+ return
106
+ else
107
+ puts "done"
108
+ end
85
109
  disk.mounted = true
86
110
  disk.save
111
+
87
112
  rescue Net::SSH::AuthenticationFailed, Net::SSH::HostKeyMismatch => ex
88
113
  STDERR.puts "Error creating disk".color(:red)
89
114
  STDERR.puts ex.message.color(:red)
90
115
  rescue Rye::CommandNotFound => ex
91
116
  puts " CommandNotFound: #{ex.message}".color(:red)
117
+
92
118
  rescue
93
119
  STDERR.puts "Error creating disk" .color(:red)
94
120
  Rudy::Utils.bug
95
121
  end
96
- puts "done"
97
-
122
+
98
123
  end
99
124
  end
100
125
 
@@ -127,7 +152,7 @@ module Rudy; module Routines;
127
152
  sleep 0.5
128
153
  end
129
154
 
130
- puts "Destroying metadata... "
155
+ puts "Destroying volume and metadata... "
131
156
  disk.destroy
132
157
 
133
158
  end
@@ -8,28 +8,43 @@ module Rudy; module Routines;
8
8
  include Rudy::Routines::HelperBase # TODO: use execute_rbox_command
9
9
  extend self
10
10
 
11
- @@script_types = [:after, :before, :after_local, :before_local]
11
+ @@script_types = [:after, :before, :after_local, :before_local, :script, :script_local]
12
12
  @@script_config_file = "rudy-config.yml"
13
13
 
14
+ # TODO: refactor using this_method
15
+
14
16
  def before_local(routine, sconf, rbox)
15
-
17
+ execute_command(:before_local, routine, sconf, 'localhost', rbox)
18
+ end
19
+ def before_local?(routine)
16
20
  # before_local generally doesn't take a user name like the remote
17
21
  # before block so we add it here (unless the user did specify it)
18
22
  routine[:before_local] = {
19
- rbox.user.to_sym => routine.delete(:before_local)
20
- } unless routine.has_key?(rbox.user.to_sym) # use routine[timing].deepest_point ?
21
- execute_command(:before_local, routine, sconf, 'localhost', rbox)
23
+ Rudy.sysinfo.user.to_sym => routine.delete(:before_local)
24
+ } if routine[:before_local].is_a?(Proc)
25
+ execute_command?(:before_local, routine)
22
26
  end
23
- def before_local?(routine); execute_command?(:before_local, routine); end
24
-
27
+ def script_local(routine, sconf, rbox)
28
+ execute_command(:script_local, routine, sconf, 'localhost', rbox)
29
+ end
30
+ def script_local?(routine)
31
+ # before_local generally doesn't take a user name like the remote
32
+ # before block so we add it here (unless the user did specify it)
33
+ routine[:script_local] = {
34
+ Rudy.sysinfo.user.to_sym => routine.delete(:script_local)
35
+ } if routine[:script_local].is_a?(Proc)
36
+ execute_command?(:script_local, routine)
37
+ end
38
+
25
39
  def after_local(routine, sconf, rbox)
26
- routine[:after_local] = { # See before_local note
27
- rbox.user.to_sym => routine.delete(:after_local)
28
- } unless routine.has_key?(rbox.user.to_sym)
29
40
  execute_command(:after_local, routine, sconf, 'localhost', rbox)
30
41
  end
31
- def after_local?(routine); execute_command?(:after_local, routine); end
32
-
42
+ def after_local?(routine)
43
+ routine[:after_local] = { # See before_local note
44
+ Rudy.sysinfo.user.to_sym => routine.delete(:after_local)
45
+ } if routine[:after_local].is_a?(Proc)
46
+ execute_command?(:after_local, routine)
47
+ end
33
48
 
34
49
  def before(routine, sconf, machine, rbox)
35
50
  raise "ScriptHelper: Not a Rudy::Machine" unless machine.is_a?(Rudy::Machine)
@@ -43,6 +58,12 @@ module Rudy; module Routines;
43
58
  end
44
59
  def after?(routine); execute_command?(:after, routine); end
45
60
 
61
+ def script(routine, sconf, machine, rbox)
62
+ raise "ScriptHelper: Not a Rudy::Machine" unless machine.is_a?(Rudy::Machine)
63
+ execute_command(:script, routine, sconf, machine.name, rbox)
64
+ end
65
+ def script?(routine); execute_command?(:script, routine); end
66
+
46
67
 
47
68
  private
48
69
 
@@ -53,8 +74,9 @@ module Rudy; module Routines;
53
74
  def execute_command?(timing, routine)
54
75
  hasconf = (routine.is_a?(Caesars::Hash) && routine.has_key?(timing))
55
76
  return false unless hasconf
56
- routine[timing].each_pair do |user,conf|
57
- if conf.empty?
77
+ routine[timing].each_pair do |user,proc|
78
+ #p [timing, user, proc].join(', ')
79
+ if proc.nil? || !proc.is_a?(Proc)
58
80
  STDERR.puts "Empty #{timing} config for #{user}"
59
81
  else
60
82
  return true
@@ -77,16 +99,22 @@ module Rudy; module Routines;
77
99
  Rudy::Utils.write_to_file(tf.path, sconf.to_hash.to_yaml, 'w')
78
100
  end
79
101
 
80
- # We need to explicitly add the rm command for rbox so we
81
- # can delete the script config file when we're done. This
82
- # add the method on for the instance of rbox we are using.
83
- def rbox.rm(*args); cmd('rm', args); end
84
-
85
102
  if execute_command?(timing, routine) # i.e. before_local?
103
+
104
+ # We need to explicitly add the rm command for rbox so we
105
+ # can delete the script config file when we're done. This
106
+ # adds the method to this instance of rbox only.
107
+ # We give it a funny so we can delete it knowing we're not
108
+ # deleting a method added somewhere else.
109
+ def rbox.rudy_tmp_rm(*args); cmd('rm', args); end
86
110
 
87
111
  original_user = rbox.user
88
- users = routine[timing] || {}
89
- users.each_pair do |user, commands|
112
+ user_blocks = routine[timing] || {}
113
+ users = user_blocks.keys
114
+ # Root stuff is always run first.
115
+ users.unshift(users.delete(:root)) if users.member?(:root)
116
+ users.each do |user|
117
+ proc = user_blocks[user]
90
118
 
91
119
  begin
92
120
  rbox.switch_user user # does nothing if it's the same user
@@ -108,32 +136,45 @@ module Rudy; module Routines;
108
136
  rbox.upload(conf_str, @@script_config_file)
109
137
  rbox.chmod(600, @@script_config_file)
110
138
  }
111
-
112
- commands.each_pair do |command, calls|
113
- # If a command is only referred to once and it has no arguments
114
- # defined, we force it through by making an array with one element.
115
- calls = [[]] if calls.empty?
116
-
117
- # Force a CommandNotFound exception early
118
- unless rbox.can?(command)
119
- execute_rbox_command { rbox.send(command) }
120
- next
139
+
140
+ begin
141
+ # We define hooks so we can call the script block as a batch.
142
+ # We intentionally set and unset the hooks so the other commands
143
+ # (config file copy) don't get printed.
144
+ #
145
+ # This block gets called for every command method call.
146
+ rbox.pre_command_hook do |cmd, args, user|
147
+ puts command_separator(rbox.preview_command(cmd, args), user)
121
148
  end
122
-
123
- # Execute the command for every set of arguments
124
- calls.each do |args|
125
- puts command_separator(rbox.preview_command(command, args), user)
126
- execute_rbox_command { ret = rbox.send(command, args) }
149
+ # And this one gets called after each command method call.
150
+ rbox.post_command_hook do |ret|
151
+ puts ' ' << ret.stdout.join("#{$/} ") if !ret.stdout.empty?
152
+ print_response(ret)
127
153
  end
154
+
155
+ ### EXECUTE THE COMMANDS BLOCK
156
+ rbox.batch(&proc)
157
+
158
+ rbox.pre_command_hook = nil
159
+ rbox.post_command_hook = nil
160
+ rescue Rye::CommandError => ex
161
+ print_response(ex)
162
+ exit 12 unless keep_going?
163
+ rescue Rye::CommandNotFound => ex
164
+ STDERR.puts " CommandNotFound: #{ex.message}".color(:red)
165
+ STDERR.puts ex.backtrace if Rudy.debug?
166
+ exit 12 unless keep_going?
128
167
  end
129
168
 
130
169
  # I was gettings errors about script_config_file not existing. There
131
170
  # might be a race condition when the rm command is called too quickly.
132
171
  # It's also quite possible I'm off my rocker!
133
- sleep 0.1
172
+ ## NOTE: I believe this was an issue with Rye. I fixed it when I was
173
+ ## noticing the same error in another place. It hasn't repeated.
174
+ ## sleep 0.1
134
175
 
135
176
  rbox.cd # reset to home dir
136
- #rbox.rm(@@script_config_file)
177
+ rbox.rudy_tmp_rm(@@script_config_file)
137
178
  end
138
179
 
139
180
  # Return the borrowed rbox instance to the user it was provided with
@@ -5,7 +5,7 @@ module Rudy; module Routines;
5
5
  extend self
6
6
 
7
7
  def adduser?(routine)
8
- (!routine.adduser.nil? && !routine.adduser.empty?)
8
+ (!routine.adduser.nil? && !routine.adduser.to_s.empty?)
9
9
  end
10
10
  def adduser(routine, machine, rbox)
11
11
 
@@ -13,7 +13,7 @@ module Rudy; module Routines;
13
13
  # explicitly so we do it for linux too for fun.
14
14
  homedir = rbox.guess_user_home(routine.adduser.to_s)
15
15
  args = [:m, :d, homedir, :s, '/bin/bash', routine.adduser.to_s]
16
- puts command_separator(rbox.preview_command(:useradd, args), routine.adduser.to_s)
16
+ puts command_separator(rbox.preview_command(:useradd, args), rbox.user)
17
17
 
18
18
  # NOTE: We'll may to use platform specific code here.
19
19
  # Linux has adduser and useradd commands:
@@ -24,10 +24,10 @@ module Rudy; module Routines;
24
24
  end
25
25
 
26
26
  def authorize?(routine)
27
- (!routine.authorize.nil? && !routine.authorize.empty?)
27
+ (!routine.authorize.nil? && !routine.authorize.to_s.empty?)
28
28
  end
29
29
  def authorize(routine, machine, rbox)
30
- puts command_separator(:authorize_keys_remote, routine.authorize)
30
+ puts command_separator(:authorize_keys_remote, rbox.user)
31
31
  execute_rbox_command { rbox.authorize_keys_remote(routine.authorize) }
32
32
  end
33
33
 
@@ -14,7 +14,7 @@ module Rudy; module Routines;
14
14
  def execute(&each_mach)
15
15
  routine_separator(@routine_name)
16
16
  machines = []
17
- generic_machine_runner(:list) do |machine|
17
+ generic_machine_runner(:list) do |machine,rbox|
18
18
  puts $/ #, "[routine: #{@routine_name}]"
19
19
  machines << machine
20
20
  end
@@ -15,7 +15,7 @@ module Rudy; module Routines;
15
15
  end
16
16
  machines = []
17
17
  generic_machine_runner(:destroy) do |machine,rbox|
18
- #puts task_separator("SHUTDOWN")
18
+ puts $/, "Shutting down...", $/
19
19
  machines << machine
20
20
  end
21
21
  machines
@@ -16,8 +16,12 @@ module Rudy; module Routines;
16
16
  STDERR.puts "[this is a generic startup routine]"
17
17
  @routine = {}
18
18
  end
19
+ machines = []
19
20
  generic_machine_runner(:create) do |machine,rbox|
21
+ puts $/, "Starting up...", $/
22
+ machines << machine
20
23
  end
24
+ machines
21
25
  end
22
26
 
23
27
  # Called by generic_machine_runner
data/lib/rudy/scm/git.rb CHANGED
@@ -17,6 +17,7 @@ module Rudy
17
17
  attr_reader :rtag
18
18
  attr_reader :user
19
19
  attr_reader :pkey
20
+ attr_reader :commit
20
21
 
21
22
  # * +args+ a hash of params from the git block in the routines config
22
23
  #
@@ -26,10 +27,11 @@ module Rudy
26
27
  :remote => :origin,
27
28
  :branch => :master,
28
29
  :user => :root,
30
+ :commit => :enforce,
29
31
  :path => nil
30
32
  }.merge(args)
31
33
  @remote, @branch, @path = args[:remote], args[:branch], args[:path]
32
- @user, @pkey = args[:user], args[:privatekey]
34
+ @user, @pkey, @commit = args[:user], args[:privatekey], args[:commit]
33
35
  @repo = Repo.new(Dir.pwd) if GIT.working_copy?
34
36
  end
35
37
 
@@ -196,7 +198,7 @@ module Rudy
196
198
 
197
199
  def raise_early_exceptions
198
200
  raise NotAWorkingCopy, :git unless working_copy?
199
- #raise DirtyWorkingCopy, :git unless clean_working_copy?
201
+ raise DirtyWorkingCopy, :git unless @commit.to_s == 'ignore' || clean_working_copy?
200
202
  raise NoRemoteURI, "remote.#{@remote}.url not set" if get_remote_uri.nil?
201
203
  raise NoRemotePath, :git if @path.nil?
202
204
  raise PrivateKeyNotFound, @pkey if @pkey && !File.exists?(@pkey)
data/rudy.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  @spec = Gem::Specification.new do |s|
2
2
  s.name = "rudy"
3
3
  s.rubyforge_project = 'rudy'
4
- s.version = "0.7.0"
4
+ s.version = "0.7.1"
5
5
  s.summary = "Rudy: Not your grandparents' EC2 deployment tool."
6
6
  s.description = s.summary
7
7
  s.author = "Delano Mandelbaum"
@@ -14,9 +14,9 @@
14
14
  s.require_paths = %w[lib]
15
15
  s.rubygems_version = '1.1.1'
16
16
 
17
- s.add_dependency 'drydock', '>= 0.6.0'
18
- s.add_dependency 'caesars', '>= 0.6.1'
19
- s.add_dependency 'rye', '>= 0.6.3'
17
+ s.add_dependency 'drydock', '>= 0.6.1'
18
+ s.add_dependency 'caesars', '>= 0.6.3'
19
+ s.add_dependency 'rye', '>= 0.6.4'
20
20
 
21
21
  s.add_dependency 'grit'
22
22
  s.add_dependency 'echoe'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rudy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-02 00:00:00 -04:00
12
+ date: 2009-05-04 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.6.0
23
+ version: 0.6.1
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: caesars
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.6.1
33
+ version: 0.6.3
34
34
  version:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: rye
@@ -40,7 +40,7 @@ dependencies:
40
40
  requirements:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
- version: 0.6.3
43
+ version: 0.6.4
44
44
  version:
45
45
  - !ruby/object:Gem::Dependency
46
46
  name: grit