backupgemsteven 0.1.2

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/doc/styles.css ADDED
@@ -0,0 +1,157 @@
1
+ /* Layout */
2
+ html,body{margin:0;padding:0}
3
+ body{font: 100% arial,sans-serif}
4
+ p{margin:0 10px 10px}
5
+ a{color: #981793;}
6
+ div#header h1{height:80px;line-height:80px;margin:0;
7
+ padding-left:10px;background: #EEE;color: #79B30B}
8
+ div#content p{line-height:1.4}
9
+ div#navigation{background:#B9CAFF}
10
+ div#extra{background:#FF8539}
11
+ div#footer{background: #333;color: #FFF}
12
+ div#footer p{margin:0;padding:5px 10px}
13
+ div#wrapper{float:right;width:100%;margin-left:-205px}
14
+ div#content{margin-left:205px}
15
+
16
+ div#navigation {
17
+ float:left;
18
+ width:200px;
19
+ margin-right: 5px;
20
+ }
21
+
22
+ div#extra{float:left;clear:left;width:200px}
23
+ div#footer{clear:both;width:100%}
24
+
25
+
26
+ /* Styles */
27
+ body {
28
+ background-color: white;
29
+ color: #222;
30
+ font-family: Georgia, Times, serif;
31
+ line-height: 110%;
32
+ }
33
+
34
+ /* Code styling */
35
+ .example {
36
+ border: solid 1px #C9B;
37
+ background-color: #FFF5F9;
38
+ padding: 3px;
39
+ margin: 5px 40px 5px 22px;
40
+ }
41
+
42
+ pre {
43
+ background-color: #FFFEF9;
44
+ color: #936;
45
+ padding: 5px;
46
+ margin: 0px;
47
+ line-height: 170%;
48
+ }
49
+
50
+ blockquote code, p code, li code {
51
+ color: #936;
52
+ font-style: normal;
53
+ background-color: #FFFEF9;
54
+ padding: 2px;
55
+ line-height: 170%;
56
+ font-size: 12pt;
57
+ }
58
+
59
+ p code, li code {
60
+ line-height: 100%;
61
+ padding: 0px;
62
+ font-size: 130%;
63
+ }
64
+
65
+ blockquote code {
66
+ padding: 5px;
67
+ font-size: .9em;
68
+ }
69
+
70
+ div#content li {
71
+ margin-bottom: 5px;
72
+ }
73
+
74
+ div.longlist li {
75
+ padding-bottom: 8px;
76
+ }
77
+
78
+ .last_updated {
79
+ margin-left: 60%;
80
+ text-align: right;
81
+ font-size:80%;
82
+ }
83
+
84
+
85
+ /* syntax highlighting */
86
+ .keyword {
87
+ font-weight: bold;
88
+ }
89
+ .comment {
90
+ color: #555;
91
+ }
92
+ .string, .number {
93
+ color: #396;
94
+ }
95
+ .string {
96
+ background-color: #E9F5F5;
97
+ }
98
+ .regex {
99
+ background-color: #E9F1F5;
100
+ color: #435;
101
+ }
102
+ .ident {
103
+ color: #369;
104
+ }
105
+ .symbol {
106
+ color: #000;
107
+ }
108
+ .constant, .class {
109
+ color: #630;
110
+ font-weight: bold;
111
+ }
112
+
113
+ /* tables */
114
+ table {
115
+ margin: 1em 1em 1em 0;
116
+ background: #f9f9f9;
117
+ border: 1px #aaaaaa solid;
118
+ border-collapse: collapse;
119
+ }
120
+
121
+ th, td {
122
+ border: 1px #aaaaaa solid;
123
+ padding: 0.2em;
124
+ }
125
+
126
+ th {
127
+ background: #f2f2f2;
128
+ text-align: center;
129
+ }
130
+
131
+ /* navigation */
132
+ div#navigation a, div#extra a {
133
+ color: #08052B;
134
+ text-decoration: none;
135
+ }
136
+
137
+ div#navigation a:hover, div#extra a:hover {
138
+ text-decoration: underline;
139
+ }
140
+
141
+ div#navigation ol, div#extra ul {
142
+ margin-left: 6px;
143
+ padding-left: 20px;
144
+ }
145
+
146
+ div#navigation ol ol {
147
+ padding-left: 20px;
148
+ }
149
+
150
+ div#navigation ol ol li a, ol ol li {
151
+ color: #424E75;
152
+ font-size: 90%;
153
+ }
154
+
155
+ .rightad {
156
+ float: right;
157
+ }
@@ -0,0 +1,28 @@
1
+ #------------------------------------------------------------------------------
2
+ # Global Settings for Backup
3
+ # This file sets the global settings for all recipes in the same directory
4
+ # @author: Nate Murray <nate@natemurray.com>
5
+ #------------------------------------------------------------------------------
6
+
7
+ # This file specifies many, but not all, of the setting you can change for Backup.
8
+ # Any setting that is set to the default is commented out. Uncomment and
9
+ # change any to your liking.
10
+
11
+ # A single or array of servers to execute the backup tasks on
12
+ # set :servers, %w{ localhost someotherhost } # default: localhost
13
+
14
+ # The directory to place the backup on the backup server
15
+ set :backup_path, "/var/local/backups/mediawiki"
16
+
17
+ # Name of the SSH user
18
+ # set :ssh_user, ENV['USER']
19
+
20
+ # Path to your SSH key
21
+ # set :identity_key, ENV['HOME'] + "/.ssh/id_rsa"
22
+
23
+ # Set global actions
24
+ # action :compress, :method => :tar_bz2 # tar_bz2 is the default
25
+ # action :deliver, :method => :mv # mv is the default
26
+ # action :encrypt, :method => :gpg # gpg is the default
27
+
28
+ set :tmp_dir, File.dirname(__FILE__) + "/../tmp"
@@ -0,0 +1,24 @@
1
+ #------------------------------------------------------------------------------
2
+ # Mediawiki Backup script
3
+ # @author: Nate Murray <nate@natemurray.com>
4
+ #------------------------------------------------------------------------------
5
+ action(:content) do
6
+ dump = c[:tmp_dir] + "/test.sql"
7
+ sh "mysqldump -uroot test > #{dump}"
8
+ # should something happen here to cleanup the last task? maybe something
9
+ # should happen to the stack. like if the next task goes through then you
10
+ # remove he last file from the stack. Ah. if everything goes well you clean
11
+ # up everything from the stack.
12
+ dump
13
+ end
14
+
15
+ # action :compress, :method => :tar_bz2 # could be set in global
16
+ # action :deliver, :method => :scp # could be set in global
17
+
18
+ # settings for backup servers are global unless specified otherwise
19
+ # rotate settings are global unless specified herer
20
+
21
+
22
+
23
+
24
+
@@ -0,0 +1,19 @@
1
+ #------------------------------------------------------------------------------
2
+ # Mediawiki Backup script
3
+ # Uses numeric rotation instead of temporal
4
+ # @author: Nate Murray <nate@natemurray.com>
5
+ #------------------------------------------------------------------------------
6
+ action(:content) do
7
+ dump = c[:tmp_dir] + "/test.sql"
8
+ sh "mysqldump -uroot test > #{dump}"
9
+ dump
10
+ end
11
+
12
+ set :rotation_mode, :numeric # base our rotation on numbers not dates
13
+ set :sons_promoted_after, 3 # upgrade to father after 3 sons
14
+ set :fathers_promoted_after, 1
15
+
16
+
17
+
18
+
19
+
data/examples/s3.rb ADDED
@@ -0,0 +1,35 @@
1
+ #------------------------------------------------------------------------------
2
+ # Example S3 Backup script
3
+ # @author: Jason L. Perry <jasper@ambethia.com>
4
+ #------------------------------------------------------------------------------
5
+
6
+ # Set the name of the s3 bucket you want to store your backups in.
7
+ # Your Access ID is prepended to this to avoid naming conflicts.
8
+ set :backup_path, "database_backup"
9
+
10
+ # You can specify your keys here, or set them as environment variables:
11
+ # AMAZON_ACCESS_KEY_ID
12
+ # AMAZON_SECRET_ACCESS_KEY
13
+ set :aws_access, '123'
14
+ set :aws_secret, 'ABC'
15
+
16
+ # S3 does not support renaming objects, so rotation data is stored in an
17
+ # index. You can specify a different key for index here, if you need to.
18
+ #
19
+ # set :rotation_object_key, 'backup_rotation_index.yml'
20
+
21
+ action(:content) do
22
+ dump = c[:tmp_dir] + "/databases.sql"
23
+ sh "mysqldump -uroot --all-databases > #{dump}"
24
+ dump
25
+ end
26
+
27
+ action :deliver, :method => :s3
28
+ action :rotate, :method => :via_s3
29
+
30
+ set :son_promoted_on, :fri
31
+ set :father_promoted_on, :last_fri_of_the_month
32
+
33
+ set :sons_to_keep, 7
34
+ set :fathers_to_keep, 5
35
+ set :grandfathers_to_keep, 12
data/lib/backup.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'runt'
3
+ require 'date'
4
+ require 'backup/configuration'
5
+ require 'backup/extensions'
6
+ require 'backup/ssh_helpers'
7
+ require 'backup/date_parser'
8
+ require 'backup/state_recorder'
9
+
10
+ begin
11
+ require 'right_aws'
12
+ require 'backup/s3_helpers'
13
+ rescue LoadError
14
+ # If RightAWS is not installed, no worries, we just
15
+ # wont have access to s3 methods.
16
+ end
17
+
18
+ begin
19
+ require 'madeleine'
20
+ rescue LoadError
21
+ # If you don't have madeleine then you just cant use numeric rotation mode
22
+ ::NO_NUMERIC_ROTATION = true
23
+ end
@@ -0,0 +1,208 @@
1
+ require 'rake'
2
+
3
+ module Backup
4
+ include FileUtils
5
+ # An Actor is the entity that actually does the work of determining which
6
+ # servers should be the target of a particular task, and of executing the
7
+ # task on each of them in parallel. An Actor is never instantiated
8
+ # directly--rather, you create a new Configuration instance, and access the
9
+ # new actor via Configuration#actor.
10
+ class Actor
11
+ # The configuration instance associated with this actor.
12
+ attr_reader :configuration
13
+
14
+ # Alias for #configuration
15
+ alias_method :c, :configuration
16
+
17
+ # A hash of the tasks known to this actor, keyed by name. The values are
18
+ # instances of Actor::Action.
19
+ attr_reader :action
20
+
21
+ # A stack of the results of the actions called
22
+ attr_reader :result_history
23
+
24
+ # the rotator instance
25
+ attr_reader :rotator
26
+
27
+ # Returns an Array of Strings specifying dirty files. These files will be
28
+ # +rm -rf+ when the +cleanup+ task is called.
29
+ attr_reader :dirty_files
30
+
31
+ class Action #:nodoc:
32
+ attr_reader :name, :actor, :options
33
+
34
+ def initialize(name, actor, options)
35
+ @name, @actor, @options = name, actor, options
36
+ end
37
+ end
38
+
39
+ def initialize(config) #:nodoc:
40
+ @configuration = config
41
+ @action = {}
42
+ @result_history = []
43
+ @dirty_files = []
44
+ @rotator = Backup::Rotator.new(self)
45
+ end
46
+
47
+ # each action in the action_order is part of the chain. so you start by
48
+ # setting the output as 'nil' then you try to call before_ action, then
49
+ # store the output, then cal action with the args if action takes the args
50
+ # you are sending. if it doesnt give an intelligent error message. do this
51
+ # for all actions. then call after_action with the output if it exists.
52
+ # each time out are calilng the method with the arguemtns f the method
53
+ # exists and the method takes the arguments.
54
+ def start_process!
55
+ configuration[:action_order].each do |a|
56
+ self.send_and_store("before_" + a)
57
+ self.send_and_store(a)
58
+ self.send_and_store("after_" + a)
59
+ end
60
+ last_result
61
+ end
62
+
63
+ def send_and_store(name)
64
+ store_result self.send(name) if self.respond_to? name
65
+ end
66
+
67
+ # Define a new task for this actor. The block will be invoked when this
68
+ # task is called.
69
+ # todo, this might be more complex if the before and after tasks are going
70
+ # to be part of the input and output chain
71
+ def define_action(name, options={}, &block)
72
+ @action[name] = (options[:action_class] || Action).new(name, self, options)
73
+
74
+ if self.respond_to?(name) && !( block_given? || options[:method] )
75
+ # if it was already defined and we aren't trying to re-define it then
76
+ # what we are trying to do is define it the same way it is defined now
77
+ # only with options being sent to it.
78
+ metaclass.send(:alias_method, "old_#{name}".intern, name)
79
+ #self.class.send(:alias_method, "old_#{name}".intern, name)
80
+ #define_method("#{name.to_s}_new".intern) do
81
+ define_method(name) do
82
+ begin
83
+ result = self.send("old_#{name}", options)
84
+ end
85
+ result
86
+ end
87
+ return
88
+ end
89
+
90
+ define_method(name) do
91
+ #logger.debug "executing task #{name}"
92
+ begin
93
+ if block_given?
94
+ result = instance_eval( &block )
95
+ elsif options[:method]
96
+ #result = self.send(options[:method], options[:args])
97
+ result = self.send(options[:method])
98
+ # here we need to have a thing where we can send the arguments
99
+ # define the method 'content' so that would take the other options
100
+ # if there are options (any hash) just send along that hash. this needs more work
101
+ end
102
+ end
103
+ result
104
+ end
105
+
106
+ end
107
+
108
+ def metaclass
109
+ class << self; self; end
110
+ end
111
+
112
+ # rotate Actions
113
+ def via_mv; rotator.rotate_via_mv(last_result); end
114
+ def via_ssh; rotator.rotate_via_ssh(last_result); end
115
+ def via_ftp; rotator.rotate_via_ftp(last_result); end
116
+ def via_s3; rotator.rotate_via_s3(last_result); end
117
+
118
+ # By default, +:content+ can perform one of three actions
119
+ # * +:is_file+
120
+ # * +:is_folder+
121
+ # + +:is_contents_of+
122
+ #
123
+ # Examples:
124
+ # action :content, :is_file => "/path/to/file" # content is a single file
125
+ # action :content, :is_folder => "/path/to/folder" # content is the folder itself
126
+ # action :content, :is_contents_of => "/path/to/other/folder" # files in folder/
127
+ #
128
+ # +:is_file+ and +:is_folder+ are basically the same thing in that they
129
+ # backup the whole file/folder whereas +:is_contents_of+ backs up the
130
+ # <em>contents</em> of the given folder.
131
+ #
132
+ # Note that +:is_contents_of+ performs a very spcific action:
133
+ # * a temporary directory is created
134
+ # * all of the files are moved (including subdirectories) into the temporary directory
135
+ # * the archive is created from the temporary directory
136
+ #
137
+ # If you wish to copy the files out of the original directory instead of
138
+ # moving them. Then you may specify the +copy+ option passing a +true+
139
+ # value like so:
140
+ #
141
+ # action :content, :is_contents_of => "/path/to/other/folder",
142
+ # :copy => true
143
+ #
144
+ # This will copy recursively.
145
+ #
146
+ # If this is not your desired behavior then you can easily write your own.
147
+ # Also, these options only work for local files. If you are getting the
148
+ # files from a foreign server you will have to write a custom +:content+
149
+ # method.
150
+ #
151
+ def content(opts={})
152
+ return opts[:is_file] if opts[:is_file]
153
+ return opts[:is_folder] if opts[:is_folder]
154
+ if opts[:is_contents_of]
155
+ orig = opts[:is_contents_of]
156
+ tmpdir = c[:tmp_dir] + "/tmp_" + Time.now.strftime("%Y%m%d%H%M%S") +"_#{rand}"
157
+ new_orig = tmpdir + "/" + File.basename(orig)
158
+ mkdir_p tmpdir
159
+ mkdir_p new_orig
160
+ if opts[:copy]
161
+ cp_r orig + '/.', new_orig
162
+ else
163
+ mv orig + '/.', new_orig
164
+ end
165
+ dirty_file new_orig
166
+ return new_orig
167
+ end
168
+ if opts[:is_hg_repository]
169
+ orig = opts[:is_hg_repository]
170
+ name = opts[:as] || File.basename(orig)
171
+ new_orig = c[:tmp_dir] + '/' + name
172
+ sh "hg clone #{orig} #{new_orig}"
173
+ dirty_file new_orig
174
+ return new_orig
175
+ end
176
+ raise "Unknown option in :content. Try :is_file, :is_folder " +
177
+ ":is_contents_of or :is_hg_repository"
178
+ end
179
+
180
+ # Given name of a file in +string+ adds that file to @dirty_files. These
181
+ # files will be removed when the +cleanup+ task is called.
182
+ def dirty_file(string)
183
+ @dirty_files << string
184
+ end
185
+
186
+ # +cleanup+ takes every element from @dirty_files and performs an +rm -rf+ on the value
187
+ def cleanup(opts={})
188
+ dirty_files.each do |f|
189
+ rm_rf f
190
+ end
191
+ end
192
+
193
+ private
194
+ def define_method(name, &block)
195
+ metaclass.send(:define_method, name, &block)
196
+ end
197
+
198
+ def store_result(result)
199
+ @result_history.push result
200
+ end
201
+
202
+ def last_result
203
+ @result_history.last
204
+ end
205
+
206
+ end
207
+
208
+ end