itgwiki_mirror 0.0.18.pre

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.
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'itgwiki_mirror'
4
+ require 'itgwiki_mirror/backuper'
5
+ require 'optparse'
6
+
7
+ USAGE = 'Usage: itgwiki_mirror_backup [options] user@host[:port]'
8
+
9
+ def usage
10
+ puts USAGE
11
+ end
12
+
13
+ options = {}
14
+ OptionParser.new do |opts|
15
+
16
+ opts.banner = USAGE
17
+
18
+ opts.on '-v', '--verbose', 'Run verbosely (off by default)' do |v|
19
+ options[:verbose] = v
20
+ end
21
+
22
+ opts.on '-u', '--db-username USERNAME', 'USERNAME for mysqldump' do |u|
23
+ options[:db_user] = u
24
+ end
25
+
26
+ opts.on '-p', '--dp-password PASSWORD', 'PASSWORD for mysqldump' do |p|
27
+ options[:db_pass] = p
28
+ end
29
+
30
+ opts.on '-d', '--db-name NAME', 'NAME of the database for mysqldump' do |d|
31
+ options[:db_name] = d
32
+ end
33
+
34
+ opts.on '-w', '--wiki-root ROOT', 'The ROOT of the wiki (e.g. /var/www/w)' do |w|
35
+ options[:wiki_root] = w.sub(/\/$/, '') # remove trailing slash
36
+ end
37
+
38
+ opts.on '-t', '--remote-rsync-target TARGET', 'The remote rsync TARGET (i.e. destination directory)' do |t|
39
+ options[:rsync_target] = t
40
+ end
41
+
42
+ end.parse!
43
+
44
+ # set user, host, and port from ARGV (port is optional)
45
+ #
46
+ begin
47
+ options[:user] = /^(.+)@/.match(ARGV[0])[1]
48
+ options[:host] = /@([^:]+)/.match(ARGV[0])[1]
49
+ options[:port] = /:(\d+)/.match(ARGV[0])[1].to_i if /:/.match ARGV[0]
50
+ rescue
51
+ usage
52
+ exit 1
53
+ end
54
+
55
+ if $DEBUG
56
+ p options
57
+ end
58
+
59
+ if ITGwikiMirror::Backuper.new(options).backup
60
+ puts 'Success! No problems encountered during backup.' if options[:verbose]
61
+ else
62
+ STDERR.puts 'Errors were encountered. Backup was not completed.'
63
+ end
64
+
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'itgwiki_mirror'
4
+ require 'itgwiki_mirror/deployer'
5
+ require 'optparse'
6
+
7
+ USAGE = 'Usage: itgwiki_mirror_backup [options] user@host[:port]'
8
+
9
+ def usage
10
+ puts USAGE
11
+ end
12
+
13
+ options = {}
14
+ OptionParser.new do |opts|
15
+
16
+ opts.banner = USAGE
17
+
18
+ opts.on '-v', '--verbose', 'Run verbosely (off by default)' do |v|
19
+ options[:verbose] = v
20
+ end
21
+
22
+ opts.on '-u', '--db-username USERNAME', 'USERNAME for mysql import' do |u|
23
+ options[:db_user] = u
24
+ end
25
+
26
+ opts.on '-p', '--dp-password PASSWORD', 'PASSWORD for mysql import' do |p|
27
+ options[:db_pass] = p
28
+ end
29
+
30
+ opts.on '-d', '--db-name NAME', 'NAME of the database for mysql import' do |d|
31
+ options[:db_name] = d
32
+ end
33
+
34
+ opts.on '-w', '--wiki-root ROOT', 'The ROOT of the wiki (e.g. /var/www/w)' do |w|
35
+ options[:wiki] = w.sub(/\/$/, '') # remove trailing slash
36
+ end
37
+
38
+ opts.on '-m', '--mirror-directory DIRECTORY', 'Target of rsync from ITGwiki' do |m|
39
+ options[:mirror] = m
40
+ end
41
+
42
+ opts.on '-f', '--foreground', '--no-daemon', 'Run in foreground (do not daemonize)' do |f|
43
+ options[:foreground] = f
44
+ end
45
+
46
+ end.parse!
47
+
48
+ if $DEBUG
49
+ p options
50
+ end
51
+
52
+ #
53
+ # FIXME should this have some kind of "exec" or something? Ready about threads
54
+ # in Ruby. Be sure to handle signal processing (kill, term, etc.)
55
+ #
56
+ ITGwikiMirror::Deployer.new(options).run
57
+
@@ -0,0 +1,3 @@
1
+ module ITGwikiMirror
2
+ end
3
+
@@ -0,0 +1,163 @@
1
+ require 'tempfile'
2
+
3
+ module ITGwikiMirror
4
+
5
+
6
+ class Backuper
7
+
8
+
9
+ def initialize opts
10
+
11
+ # verbose mode
12
+ #
13
+ @verbose = opts[:verbose]
14
+
15
+ # mysql options
16
+ #
17
+ @db_user = opts[:db_user]
18
+ @db_pass = opts[:db_pass]
19
+ @db_name = opts[:db_name]
20
+
21
+ # ssh options
22
+ #
23
+ @user = opts[:user]
24
+ @host = opts[:host]
25
+ @port = opts[:port]
26
+
27
+ # MediaWiki root
28
+ #
29
+ @wiki_root = opts[:wiki_root]
30
+
31
+ # rsync target
32
+ #
33
+ @rsync_target = opts[:rsync_target]
34
+
35
+ # mysqldump temporary file
36
+ #
37
+ @dumpfile = Tempfile.new('itgwiki_mirror_mysqldump').path
38
+ end
39
+
40
+
41
+ #
42
+ # Perform the backup
43
+ #
44
+ # Dump the database, rsync the database, the wiki directory, and any other
45
+ # specified files (configuration, etc.) to the remote host. If any of these
46
+ # steps fails, stop and return false. If they're all successful, return
47
+ # true.
48
+ #
49
+ def backup
50
+
51
+ #
52
+ # FIXME I repeat the $?.success? block over and over. Probably extract to a method.
53
+ #
54
+
55
+ # print immediately so we get the nice 'msg...' -> 'msg...OK' transition
56
+ # instead of waiting until the jobs is finished to print the message.
57
+ #
58
+ STDOUT.sync = true
59
+
60
+
61
+ print 'mysqldump...' if @verbose
62
+ mysqldump
63
+ if $?.success?
64
+ puts 'OK' if @verbose
65
+ else
66
+ STDERR.puts 'mysqldump failed'
67
+ return false
68
+ end
69
+
70
+ print 'rsyncing mysqldump...' if @verbose
71
+ rsync_mysqldump
72
+ if $?.success?
73
+ puts 'OK' if @verbose
74
+ else
75
+ STDERR.puts 'rsyncing mysqldump failed'
76
+ return false
77
+ end
78
+
79
+
80
+ print 'rsyncing wiki directory...' if @verbose
81
+ rsync_wiki_dir
82
+ if $?.success?
83
+ puts 'OK' if @verbose
84
+ else
85
+ STDERR.puts 'rsyncing wiki directory failed'
86
+ return false
87
+ end
88
+
89
+ print 'Notifying mirror that backup is complete...' if @verbose
90
+ notify_complete
91
+ if $?.success?
92
+ puts 'OK' if @verbose
93
+ else
94
+ STDERR.puts 'Notifying mirror failed'
95
+ return false
96
+ end
97
+
98
+ return true
99
+ end
100
+
101
+
102
+ private
103
+
104
+
105
+ def mysqldump
106
+
107
+ # XXX You CANNOT have a space between -p and the password
108
+ #
109
+ %x( mysqldump -u#{@db_user} -p#{@db_pass} #{@db_name} > #{@dumpfile} )
110
+
111
+ puts %( mysqldump -u#{@db_user} -p#{@db_pass} #{@db_name} > #{@dumpfile} ) if $DEBUG
112
+ end
113
+
114
+
115
+ #
116
+ # rsync from src to dst with options
117
+ # -a (archive mode)
118
+ # -vP (verbose mode along with partial and progress display) if @verbose
119
+ # --rsh='ssh -p###' (use ### as ssh port for rsync ) if an alternate port is defined
120
+ #
121
+ def rsync src, dst
122
+
123
+ # archive mode
124
+ #
125
+ opts = '-a'
126
+
127
+ # verbose transfer if requested
128
+ #
129
+ opts += ' -vP' if @verbose
130
+
131
+ # specify an alternate port if requested
132
+ #
133
+ opts += " --rsh='ssh -p#{@port}'" if @port
134
+
135
+ puts %( rsync #{opts} #{src} #{dst} ) if $DEBUG
136
+
137
+ %x( rsync #{opts} #{src} #{dst} )
138
+ end
139
+
140
+
141
+ def rsync_mysqldump
142
+ rsync @dumpfile, "#{@user}@#{@host}:#{@rsync_target}/itgwiki.mysqldump"
143
+ end
144
+
145
+
146
+ def rsync_wiki_dir
147
+ rsync @wiki_root, "#{@user}@#{@host}:#{@rsync_target}"
148
+ end
149
+
150
+
151
+ def notify_complete
152
+
153
+ # specify port if defined
154
+ #
155
+ port = "-p#{@port}" if @port
156
+
157
+ # just touch a file called "complete" in the mirror target
158
+ #
159
+ %x( ssh #{@user}@#{@host} #{port} touch #{@rsync_target}/complete )
160
+ end
161
+ end
162
+ end
163
+
@@ -0,0 +1,211 @@
1
+ module ITGwikiMirror
2
+
3
+ class Deployer < SimpleDaemon::Base
4
+
5
+
6
+ READ_ONLY_MSG = 'This is a mirror of the wiki. Visit the live site to make changes.'
7
+
8
+
9
+ def initialize opts
10
+
11
+ # verbose mode
12
+ #
13
+ @verbose = opts[:verbose]
14
+
15
+ # mysql options
16
+ #
17
+ @db_user = opts[:db_user]
18
+ @db_pass = opts[:db_pass]
19
+ @db_name = opts[:db_name]
20
+
21
+ # MediaWiki root
22
+ #
23
+ @wiki = opts[:wiki]
24
+
25
+ # mirror directory (target of rsync from ITGwiki)
26
+ #
27
+ @mirror = opts[:mirror]
28
+ end
29
+
30
+ def run
31
+
32
+ # Watch the complete file. When it's touched, deploy again.
33
+ notifier = INotify:Notifier.new
34
+ notifier.watch("#{@wiki}/complete", :modify) { deploy }
35
+ end
36
+
37
+
38
+ private
39
+
40
+
41
+ def success
42
+ puts 'Success! No problems encountered during deployment.' if @verbose
43
+ end
44
+
45
+
46
+ def failure
47
+ STDERR.puts 'Errors were encountered. Deployment was not completed.'
48
+ end
49
+
50
+ #
51
+ # Deploy the newly synced content to the mirror server
52
+ #
53
+ # Drop the old database, create a new one, import the mysqldump, modify the
54
+ # local MediaWiki configuration:
55
+ #
56
+ # * disable the $wgServer string
57
+ # * disable Sphinx search
58
+ # * set maintenance mode (read-only)
59
+ #
60
+ # Finally, rsync the local modified version into the live mirror MediaWiki
61
+ # root.
62
+ #
63
+ def deploy
64
+
65
+ #
66
+ # FIXME I repeat the $?.success? block over and over. Probably extract to a method.
67
+ #
68
+
69
+ # print immediately so we get the nice 'msg...' -> 'msg...OK' transition
70
+ # instead of waiting until the jobs is finished to print the message.
71
+ #
72
+ STDOUT.sync = true
73
+
74
+ print 'Dropping database...' if @verbose
75
+ drop_db
76
+ if $?.success?
77
+ puts 'OK' if @verbose
78
+ else
79
+ STDERR.puts 'Dropping database failed'
80
+ failure
81
+ end
82
+
83
+ print 'Creating database...' if @verbose
84
+ create_db
85
+ if $?.success?
86
+ puts 'OK' if @verbose
87
+ else
88
+ STDERR.puts 'Creating database failed'
89
+ failure
90
+ end
91
+
92
+ print 'Importing mysqldump...' if @verbose
93
+ import_mysqldump
94
+ if $?.success?
95
+ puts 'OK' if @verbose
96
+ else
97
+ STDERR.puts 'Importing mysqldump failed'
98
+ failure
99
+ end
100
+
101
+ print 'Disabling $wgServer string...' if @verbose
102
+ disable_server_string
103
+ if $?.success?
104
+ puts 'OK' if @verbose
105
+ else
106
+ STDERR.puts 'Disabling $wgServer string failed'
107
+ failure
108
+ end
109
+
110
+ print 'Disabling sphinx search...' if @verbose
111
+ disable_sphinx_search
112
+ if $?.success?
113
+ puts 'OK' if @verbose
114
+ else
115
+ STDERR.puts 'Disabling sphinx search failed'
116
+ failure
117
+ end
118
+
119
+ print 'Setting maintenance mode...' if @verbose
120
+ set_maintenance_mode
121
+ if $?.success?
122
+ puts 'OK' if @verbose
123
+ else
124
+ STDERR.puts 'Setting maintenance mode failed'
125
+ failure
126
+ end
127
+
128
+ print 'rsyncing modified mirror content to mirror site content...' if @verbose
129
+ rsync
130
+ if $?.success?
131
+ puts 'OK' if @verbose
132
+ else
133
+ STDERR.puts 'rsyncing modified mirror content to mirror site content failed' if @verbose
134
+ failure
135
+ end
136
+
137
+ success
138
+ end
139
+
140
+
141
+ def drop_db
142
+
143
+ # XXX no space allowed between -p and password
144
+ #
145
+ %x( mysql -u#{@db_user} -p#{@db_pass} -e 'drop database #{@db_name}' )
146
+ end
147
+
148
+
149
+ def create_db
150
+
151
+ # XXX no space allowed between -p and password
152
+ #
153
+ %x( mysql -u#{@db_user} -p#{@db_pass} -e 'create database #{@db_name}' )
154
+ end
155
+
156
+
157
+ def import_mysqldump
158
+
159
+ # XXX no space allowed between -p and password
160
+ #
161
+ %x( mysql -u#{@db_user} -p#{@db_pass} #{@db_name} < #{@mirror}/itgwiki.mysqldump )
162
+ end
163
+
164
+
165
+ def disable_server_string
166
+
167
+ #
168
+ # The server string is necessary in the production instance because it's
169
+ # the only way to tell the service which is behind a proxy that it should
170
+ # have a certain domain name and that it should use https. For the mirror,
171
+ # it can successfully be ascertained automatically by the MediaWiki
172
+ # software.
173
+ #
174
+
175
+ # comment the wgServer line
176
+ #
177
+ %x( sed -i '/wgServer/ s/^/#/' #{@mirror}/w/LocalSettings.php )
178
+ end
179
+
180
+
181
+ def disable_sphinx_search
182
+
183
+ # comment lines with the word sphinx (case insensitive) in them that are
184
+ # not already commented
185
+ #
186
+ %x( sed -i '/^[^#].*sphinx/I s/^/#/' #{@mirror}/w/LocalSettings.php )
187
+ end
188
+
189
+
190
+ def set_maintenance_mode
191
+
192
+ # append readonly message to config file
193
+ #
194
+ %x( echo '$wgReadOnly = "#{READ_ONLY_MSG}";' >> #{@mirror}/w/LocalSettings.php )
195
+ end
196
+
197
+
198
+ def rsync
199
+
200
+ # XXX being a local rsync (a simple file copy) we're not going to bother with
201
+ # allowing verbose mode or other options. Just a straight archive (-a)
202
+ # copy.
203
+ #
204
+ # XXX There is a trailing slash on the rsync source because we want to
205
+ # sync the contents of the directory into the wiki directory. `man rsync`
206
+ # for more details.
207
+ #
208
+ %x( rsync -a #{@mirror}/w/ #{@wiki} )
209
+ end
210
+ end
211
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: itgwiki_mirror
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.18.pre
5
+ prerelease: 7
6
+ platform: ruby
7
+ authors:
8
+ - Justin Force
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rb-inotify
16
+ requirement: &78672600 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *78672600
25
+ description: ! "\n Use two scripts, itgwiki_mirror_backup and itgwiki_mirror_deploy,
26
+ to\n maintain a read-only mirror of ITGwiki. itgwiki_mirror_backup runs\n periodically
27
+ and automatically on the live instance of ITGwiki to create a\n backup and copy
28
+ it to the mirror. itgwiki_mirror_deploy runs on the mirror\n and is triggered
29
+ by itgwiki_mirror_backup, imports and adjusts the backup\n to be read-only, then
30
+ deploys it on the mirror server. Requires rsync and\n mysqldump.\n "
31
+ email: justin.force@gmail.com
32
+ executables:
33
+ - itgwiki_mirror_backup
34
+ - itgwiki_mirror_deploy
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - lib/itgwiki_mirror.rb
39
+ - lib/itgwiki_mirror/backuper.rb
40
+ - lib/itgwiki_mirror/deployer.rb
41
+ - bin/itgwiki_mirror_backup
42
+ - bin/itgwiki_mirror_deploy
43
+ homepage: https://github.com/sidewaysmilk/itgwiki_mirror
44
+ licenses: []
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>'
59
+ - !ruby/object:Gem::Version
60
+ version: 1.3.1
61
+ requirements:
62
+ - mysqldump
63
+ - rsync
64
+ - sed
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.15
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Maintain a read-only mirror of ITGwiki
70
+ test_files: []