Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 86
- Log:
Initial import of I2, an Instiki clone.
- Author:
- adh
- Date:
- Mon Oct 16 10:40:36 +0100 2006
- Size:
- 4431 Bytes
- Properties:
- Property svn:executable is set
1 | #!/usr/local/bin/ruby |
2 | |
3 | require 'optparse' |
4 | require 'net/http' |
5 | require 'uri' |
6 | |
7 | def nudge(url, iterations) |
8 | print "Nudging #{url}: " |
9 | |
10 | iterations.times do |
11 | Net::HTTP.get_response(URI.parse(url)) rescue nil |
12 | print "." |
13 | STDOUT.flush |
14 | end |
15 | |
16 | puts |
17 | end |
18 | |
19 | if RUBY_PLATFORM =~ /mswin32/ then abort("Reaper is only for Unix") end |
20 | |
21 | class ProgramProcess |
22 | class << self |
23 | def process_keywords(action, *keywords) |
24 | processes = keywords.collect { |keyword| find_by_keyword(keyword) }.flatten |
25 | |
26 | if processes.empty? |
27 | puts "Couldn't find any process matching: #{keywords.join(" or ")}" |
28 | else |
29 | processes.each do |process| |
30 | puts "#{action.capitalize}ing #{process}" |
31 | process.send(action) |
32 | end |
33 | end |
34 | end |
35 | |
36 | def find_by_keyword(keyword) |
37 | process_lines_with_keyword(keyword).split("\n").collect { |line| |
38 | next if line.include?("inq") || line.include?("ps -wwax") || line.include?("grep") |
39 | pid, *command = line.split |
40 | new(pid, command.join(" ")) |
41 | }.compact |
42 | end |
43 | |
44 | private |
45 | def process_lines_with_keyword(keyword) |
46 | `ps -wwax -o 'pid command' | grep #{keyword}` |
47 | end |
48 | end |
49 | |
50 | def initialize(pid, command) |
51 | @pid, @command = pid, command |
52 | end |
53 | |
54 | def find |
55 | end |
56 | |
57 | def reload |
58 | `kill -s HUP #{@pid}` |
59 | end |
60 | |
61 | def graceful |
62 | `kill -s TERM #{@pid}` |
63 | end |
64 | |
65 | def kill |
66 | `kill -9 #{@pid}` |
67 | end |
68 | |
69 | def usr1 |
70 | `kill -s USR1 #{@pid}` |
71 | end |
72 | |
73 | def to_s |
74 | "[#{@pid}] #{@command}" |
75 | end |
76 | end |
77 | |
78 | OPTIONS = { |
79 | :action => "graceful", |
80 | :dispatcher => File.expand_path(File.dirname(__FILE__) + '/../../public/dispatch.fcgi'), |
81 | :spinner => File.expand_path(File.dirname(__FILE__) + '/spinner'), |
82 | :toggle_spin => true, |
83 | :iterations => 10, |
84 | :nudge => false |
85 | } |
86 | |
87 | ARGV.options do |opts| |
88 | opts.banner = "Usage: reaper [options]" |
89 | |
90 | opts.separator "" |
91 | |
92 | opts.on <<-EOF |
93 | Description: |
94 | The reaper is used to reload, gracefully exit, and forcefully exit FCGI processes |
95 | running a Rails Dispatcher. This is commonly done when a new version of the application |
96 | is available, so the existing processes can be updated to use the latest code. |
97 | |
98 | The reaper actions are: |
99 | |
100 | * reload : Only reloads the application, but not the framework (like the development environment) |
101 | * graceful: Marks all of the processes for exit after the next request |
102 | * kill : Forcefully exists all processes regardless of whether they're currently serving a request |
103 | |
104 | Graceful exist is the most common and default action. But since the processes won't exist until after |
105 | their next request, it's often necessary to ensure that such a request occurs right after they've been |
106 | marked. That's what nudging is for. |
107 | |
108 | A nudge is simply a request to a URL where the dispatcher is serving. You should perform one nudge per |
109 | FCGI process you have running if they're setup in a round-robin. Be sure to do one nudge per FCGI process |
110 | across all your servers. So three servers with 10 processes each should nudge 30 times to be sure all processes |
111 | are restarted. |
112 | |
113 | Examples: |
114 | reaper -a reload |
115 | reaper -n http://www.example.com -i 10 # gracefully exit, nudge 10 times |
116 | EOF |
117 | |
118 | opts.on(" Options:") |
119 | |
120 | opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String) { |OPTIONS[:action]| } |
121 | opts.on("-d", "--dispatcher=path", "default: #{OPTIONS[:dispatcher]}", String) { |OPTIONS[:dispatcher]| } |
122 | opts.on("-s", "--spinner=path", "default: #{OPTIONS[:spinner]}", String) { |OPTIONS[:spinner]| } |
123 | opts.on("-t", "--[no-]toggle-spin", "Whether to send a USR1 to the spinner before and after the reaping (default: true)") { |OPTIONS[:toggle_spin]| } |
124 | opts.on("-n", "--nudge=url", "Should point to URL that's handled by the FCGI process", String) { |OPTIONS[:nudge]| } |
125 | opts.on("-i", "--iterations=number", "One nudge per FCGI process running (default: #{OPTIONS[:iterations]})", Integer) { |OPTIONS[:iterations]| } |
126 | |
127 | opts.separator "" |
128 | |
129 | opts.on("-h", "--help", "Show this help message.") { puts opts; exit } |
130 | |
131 | opts.parse! |
132 | end |
133 | |
134 | ProgramProcess.process_keywords("usr1", OPTIONS[:spinner]) if OPTIONS[:toggle_spin] |
135 | ProgramProcess.process_keywords(OPTIONS[:action], OPTIONS[:dispatcher]) |
136 | nudge(OPTIONS[:nudge], OPTIONS[:iterations]) if OPTIONS[:nudge] |
137 | ProgramProcess.process_keywords("usr1", OPTIONS[:spinner]) if OPTIONS[:toggle_spin] |