|
| 1 | +#!/usr/bin/env ruby |
| 2 | + |
| 3 | +require "yaml" |
| 4 | +require "fileutils" |
| 5 | + |
| 6 | +# TODO: Load these from the ruby bdsm modules |
| 7 | +def log(message) |
| 8 | + @log_file ||= "#{ENV["extension_log_path"]}/#{ENV["action"]}.log" |
| 9 | + |
| 10 | + FileUtils.mkdir_p(File.dirname(@log_file)) unless Dir.exist?(File.dirname(@log_file)) |
| 11 | + |
| 12 | + File.open(@log_file, 'w+') do |log_file| |
| 13 | + log_file.write "#{@timestamp} : #{@application} : #{@environment} : #{message}\n" |
| 14 | + end |
| 15 | + $stdout.puts message |
| 16 | +end |
| 17 | + |
| 18 | +def trace(message) |
| 19 | + @trace_flag ||= ENV["trace_flag"].to_i |
| 20 | + log(message) if @trace_flag |
| 21 | +end |
| 22 | + |
| 23 | +def fail(message) |
| 24 | + log "#{@timestamp} : #{@application} : #{@environment} : #{message}" |
| 25 | + $stderr.puts "#{@timestamp} : #{@application} : #{@environment} : #{message}" |
| 26 | + exit 1 |
| 27 | +end |
| 28 | + |
| 29 | +def succeed(message) |
| 30 | + log(message) |
| 31 | + exit 0 |
| 32 | +end |
| 33 | + |
| 34 | +trace "Loading BDSM #{ENV["extension"]} #{ENV["action"]} Utility." |
| 35 | +@timestamp = Time.now.strftime("%Y-%m-%dT%H:%M:%S") |
| 36 | +# /TODO |
| 37 | + |
| 38 | +# |
| 39 | +# Setup & Extract variables |
| 40 | +# |
| 41 | +@log_path = "#{ENV["HOME"]}/shared/backups/log" |
| 42 | +@log_file = "#{@log_path}/backup.log" |
| 43 | + |
| 44 | +@user = (ENV["USER"]|| %x{printf $USER}).strip |
| 45 | + |
| 46 | +@application = ARGV.shift unless ARGV[0].to_s.strip.empty? |
| 47 | +@application ||= ENV["project"] |
| 48 | + |
| 49 | +@environment = ARGV.shift unless ARGV[0].to_s.strip.empty? |
| 50 | +@environment ||= (ENV["environment"] || ENV["RAILS_ENV"] || ENV["MERB_ENV"] || ENV["RACK_ENV"] || "production") |
| 51 | + |
| 52 | +trace "user: #{@user}, application: #{@application}, environment: #{@environment}." |
| 53 | +fail "application must be specified as the first argument." if @application.nil? || @application.empty? |
| 54 | + |
| 55 | +@target_path = "#{ENV["HOME"]}/shared/backups/#{@application}/#{@environment}" |
| 56 | +@config = YAML.load_file("#{ENV["HOME"]}/shared/config/database.yml")[@environment] |
| 57 | +@adapter = @config["adapter"].to_s.strip |
| 58 | + |
| 59 | +trace "target_path: #{@target_path}, config: #{@config}, adapter: #{@adapter}." |
| 60 | +fail "adapter not specified in shared/config/database.yml" if @adapter.nil? || @adapter.empty? |
| 61 | + |
| 62 | +@username = @config["username"] || @user |
| 63 | +@username = @user if @username.nil? || @username.empty? |
| 64 | + |
| 65 | +@database = @config["database"].to_s.strip |
| 66 | + |
| 67 | +trace "username: #{@username}, database: #{@database}" |
| 68 | +fail "database not specified in shared/config/database.yml" if @database.nil? || @database.empty? |
| 69 | + |
| 70 | +@password = @config["password"].to_s.strip |
| 71 | + |
| 72 | +trace "password: #{@password}" |
| 73 | +fail "password: not specified in shared/config/database.yml" if @password.nil? || @password.empty? |
| 74 | + |
| 75 | +@hostname = @config["host"] || "localhost" |
| 76 | +@port = @config["port"] || "5432" |
| 77 | + |
| 78 | +trace "hostname: #{@hostname}, port: #{@port}" |
| 79 | +trace "Ensuring that target path '#{@target_path}' exists." |
| 80 | + |
| 81 | +FileUtils.mkdir_p(@target_path) unless File.exist?(@target_path) |
| 82 | + |
| 83 | +# |
| 84 | +# Perform the backup |
| 85 | +# |
| 86 | +case @adapter |
| 87 | +when "mysql","mysql2" |
| 88 | + @dump = %x{which mysqldump}.strip |
| 89 | + |
| 90 | + fail "mysqldump command not found" if @dump.empty? |
| 91 | + |
| 92 | + @flags = "--add-drop-table --complete-insert --default-character-set=utf8 --quote-names --extended-insert" |
| 93 | + @filename = "#{@database}%#{@timestamp}.mysql" |
| 94 | + @command = %Q(#{@dump} #{@flags} --user=#{@username} -p"#{@password}" --quick #{@database} | bzip2 -c > #{@target_path}/#{@filename}.bz2) |
| 95 | +when "postgresql" |
| 96 | + |
| 97 | + @dump = %x{which pg_dump}.strip |
| 98 | + |
| 99 | + fail "pg_dump command not found" if @dump.empty? |
| 100 | + |
| 101 | + @flags = "-b -c -E UTF8 -O -x --no-password" |
| 102 | + @filename = "#{@database}%#{@timestamp}.psql" |
| 103 | + credentials = "#{@hostname}:#{@port}:#{@database}:#{@username}:#{@password}" |
| 104 | + |
| 105 | + # Only write credentials if they are not already in the file. |
| 106 | + pgpass_filename = "#{ENV["HOME"]}/.pgpass" |
| 107 | + File.open(pgpass_filename, "w+") do |pgpass| |
| 108 | + trace "Ensuring '#{pgpass_filename}' contains credentials '#{credentials}'" |
| 109 | + pgpass.write credentials unless pgpass.read.match(credentials) |
| 110 | + |
| 111 | + trace "Ensuring '#{pgpass_filename}' has correct permissions 0600." |
| 112 | + pgpass.chmod 0600 |
| 113 | + end |
| 114 | + |
| 115 | + @command = "#{@dump} #{@flags}" |
| 116 | + @command << " -U#{@username} -h #{@hostname} #{@database} " |
| 117 | + @command << "| bzip2 -c - > #{@target_path}/#{@filename}.bz2" |
| 118 | + |
| 119 | +when "mongodb" |
| 120 | + log "Backups for MongoDB has not yet been implemented" |
| 121 | + |
| 122 | +when "sqlite3" |
| 123 | + log "Backups for sqlite3 has not yet been implemented" |
| 124 | + |
| 125 | +when "riak" |
| 126 | + log "Backups for riak has not yet been implemented" |
| 127 | + |
| 128 | +else |
| 129 | + fail "unknown adapter, supported adapters are currently mysql and postgresql" |
| 130 | + %x{rm -f ~/.pgpass} |
| 131 | + |
| 132 | +end |
| 133 | + |
| 134 | +log "Backing up #{@adapter} database." |
| 135 | + |
| 136 | +trace "( #{@command} )" |
| 137 | + |
| 138 | +if system(@command) |
| 139 | + succeed "Successfully backed up database to #{@filename}" |
| 140 | +else |
| 141 | + log "command: #{@command}." |
| 142 | + log "Log File: #{@filename}." |
| 143 | + %x{tail -10 #{@filename}} |
| 144 | + fail "Backup failed!" |
| 145 | +end |
0 commit comments