Class: BR::Backups
- Inherits:
-
Object
- Object
- BR::Backups
- Defined in:
- lib/brbackup.rb
Constant Summary collapse
- ENGINES =
{}
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
Class Method Summary collapse
Instance Method Summary collapse
- #backup_database(database) ⇒ Object
- #cleanup ⇒ Object
- #clone(db_name) ⇒ Object
- #download(index) ⇒ Object
- #find_obj(name) ⇒ Object
-
#initialize(options) ⇒ Backups
constructor
A new instance of Backups.
- #list(database = 'all', printer = false) ⇒ Object
- #load_config(filename) ⇒ Object
-
#most_recent_index(db) ⇒ Object
dirty way to get most recent index.
- #new_backup ⇒ Object
- #normalize_name(obj) ⇒ Object
- #restore(index) ⇒ Object
Constructor Details
#initialize(options) ⇒ Backups
Returns a new instance of Backups.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/brbackup.rb', line 149 def initialize() if [:logger_path] @logger = Logger.new([:logger_path]) else @logger = Logger.new($stdout) end engine_klass = ENGINES[[:engine]] || raise("Invalid database engine: #{[:engine].inspect}") @engine = engine_klass.new(self) @engine.logger = @logger load_config([:config]) AWS::S3::Base.establish_connection!( :access_key_id => config[:aws_secret_id], :secret_access_key => config[:aws_secret_key] ) @databases = [:databases] || config[:databases] @keep = config[:keep] @bucket = "ey-backup-#{Digest::SHA1.hexdigest(config[:aws_secret_id])[0..11]}" @tmpname = "#{Time.now.strftime("%Y-%m-%dT%H:%M:%S").gsub(/:/, '-')}.sql.gz" @env = [:env] || config[:env] FileUtils.mkdir_p '/mnt/backups' FileUtils.mkdir_p '/mnt/tmp' begin AWS::S3::Bucket.find(@bucket) rescue AWS::S3::NoSuchBucket AWS::S3::Bucket.create(@bucket) end FileUtils.mkdir_p self.backup_dir end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
181 182 183 |
# File 'lib/brbackup.rb', line 181 def config @config end |
Class Method Details
.run(args) ⇒ Object
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/brbackup.rb', line 73 def self.run(args) = {} # Build a parser for the command line arguments opts = OptionParser.new do |opts| opts.version = VERSION opts. = "Usage: brbackup [-flag] [argument]" opts.define_head "brbackup: clone db backups across environments" opts.separator '*'*80 opts.on("-f", "--from ENVIRONMENT", "EY Cloud environment name want to clone from : prod_br, beta, alpha, etc") do |env| [:env] = env end opts.on("-l", "--list-backup DATABASE", "List mysql backups for DATABASE") do |db| [:db] = (db || 'all') [:command] = :list end # opts.on("-n", "--names DB1,DB2,DB3", "Only restore these databases") do |n| # options[:databases] = n.split(',') # end opts.on("-c", "--config CONFIG", "Use config file.") do |config| [:config] = config end opts.on("-d", "--download BACKUP_INDEX", "download the backup specified by index. Run brbackup -l to get the index.") do |index| [:command] = :download [:index] = index end opts.on("--clone DB_NAME", "Clones production database to staging") do |db_name| [:command] = :clone [:db_name] = db_name end opts.on("--logger /path/to/logger", "Path to log path, default is stdout") do |logger| [:logger_path] = logger end # opts.on("-r", "--restore BACKUP_INDEX", "Download and apply the backup specified by index WARNING! will overwrite the current db with the backup. Run brbackup -l to get the index.") do |index| # options[:command] = :restore # options[:index] = index # end end opts.parse!(args) [:engine] ||= 'mysql' [:config] ||= "/etc/.#{[:engine]}.backups.yml" brb = new() case [:command] when :list brb.list [:db], true when :download brb.download([:index]) # when :restore # brb.restore(options[:index]) when :clone brb.clone([:db_name]) end rescue SystemExit exit 1 rescue Exception => e $stderr.puts "An unknown exception was raised" $stderr.puts e.inspect $stderr.puts e.backtrace $stderr.puts raise end |
Instance Method Details
#backup_database(database) ⇒ Object
199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/brbackup.rb', line 199 def backup_database(database) File.open("#{self.backup_dir}/#{database}.#{@tmpname}", "w") do |f| log "doing database: #{database}" @engine.dump_database(database, f) end File.open("#{self.backup_dir}/#{database}.#{@tmpname}") do |f| path = "#{@env}.#{database}/#{database}.#{@tmpname}" AWS::S3::S3Object.store(path, f, @bucket, :access => :private) log "successful backup: #{database}.#{@tmpname}" end end |
#cleanup ⇒ Object
249 250 251 252 253 254 255 256 257 258 |
# File 'lib/brbackup.rb', line 249 def cleanup begin list('all',false)[0...-(@keep*@databases.size)].each do |o| log "deleting: #{o.key}" o.delete end rescue AWS::S3::S3Exception, AWS::S3::Error nil # see bucket_minder cleanup note regarding S3 consistency end end |
#clone(db_name) ⇒ Object
238 239 240 241 242 243 244 245 246 247 |
# File 'lib/brbackup.rb', line 238 def clone(db_name) index = most_recent_index(db_name) db, filename = download(index) log "db #{db.inspect}" log "filename #{filename.inspect}" staging_name = filename.split('.')[0].gsub('_production', '_staging') File.open(filename) do |f| @engine.clone_database(staging_name, f) end end |
#download(index) ⇒ Object
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/brbackup.rb', line 212 def download(index) idx, db = index.split(":") raise Error, "You didn't specify a database name: e.g. 1:rails_production" unless db if obj = list(db)[idx.to_i] filename = normalize_name(obj) log "downloading: #{filename}" File.open(filename, 'wb') do |f| print "." obj.value {|chunk| f.write chunk } end log "" log "finished" [db, filename] else raise BackupNotFound, "No backup found for database #{db.inspect}: requested index: #{idx}" end end |
#find_obj(name) ⇒ Object
264 265 266 |
# File 'lib/brbackup.rb', line 264 def find_obj(name) AWS::S3::S3Object.find name, @bucket end |
#list(database = 'all', printer = false) ⇒ Object
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/brbackup.rb', line 268 def list(database='all', printer = false) puts "Listing database backups for #{database}" if printer backups = [] if database == 'all' @databases.each do |db| backups << AWS::S3::Bucket.objects(@bucket, :prefix => "#{@env}.#{db}") end backups = backups.flatten.sort else backups = AWS::S3::Bucket.objects(@bucket, :prefix => "#{@env}.#{database}").sort end if printer puts "#{backups.size} backup(s) found" backups.each_with_index do |b,i| puts "#{i}:#{database} #{normalize_name(b)}" end end backups end |
#load_config(filename) ⇒ Object
183 184 185 186 187 188 189 190 191 |
# File 'lib/brbackup.rb', line 183 def load_config(filename) if File.exist?(filename) @config = YAML::load(File.read(filename)) else log "You need to have a backup file at #{filename}" $stderr.puts "You need to have a backup file at #{filename}" exit 1 end end |
#most_recent_index(db) ⇒ Object
dirty way to get most recent index
289 290 291 292 |
# File 'lib/brbackup.rb', line 289 def most_recent_index(db) index = list(db).size - 1 "#{index}:#{db}" end |
#new_backup ⇒ Object
193 194 195 196 197 |
# File 'lib/brbackup.rb', line 193 def new_backup @databases.each do |db| backup_database(db) end end |
#normalize_name(obj) ⇒ Object
260 261 262 |
# File 'lib/brbackup.rb', line 260 def normalize_name(obj) obj.key.gsub(/^.*?\//, '') end |
#restore(index) ⇒ Object
231 232 233 234 235 236 |
# File 'lib/brbackup.rb', line 231 def restore(index) db, filename = download(index) File.open(filename) do |f| @engine.restore_database(db, f) end end |