Skip to content

Commit

Permalink
First working version
Browse files Browse the repository at this point in the history
  • Loading branch information
Ptico committed Dec 15, 2010
1 parent c40251b commit 75aec1c
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 99 deletions.
29 changes: 29 additions & 0 deletions aejis_backup.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
spec = Gem::Specification.new do |s|
s.name = "aejis_backup"

s.author = "Andrey Savchenko"
s.email = "[email protected]"
s.license = 'BSD'

s.version = "0.1.0"
s.date = Time.now

s.homepage = "http://github.com/Aejis/backup"
s.summary = "Swiss knife for unix backups"
s.description = "Gem for easy backup your database and files. Supported databases: Postgresql. Supported storages: Amazon S3, local disk"

s.files = Dir.glob("{bin,lib,spec}/**/*") + ["README"]

s.bindir = 'bin'
s.executables = ["backup"]
s.default_executable= 'backup'

s.require_path = "lib"

s.test_files = Dir.glob("spec/**/*")

s.add_development_dependency "rspec", ">= 2.0.0"
s.add_development_dependency "fog", ">= 0.3.20"

s.requirements = ["GNU or BSD tar"]
end
28 changes: 28 additions & 0 deletions bin/backup
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env ruby

require 'optparse'
require 'rubygems'
require 'aejis_backup'

options = {}
optparse = OptionParser.new do |opts|
opts.banner = "\nUsage: backup [options]\n "

opts.on('-c', '--config [PATH]', "Path to backup configuration") do |config|
options[:config] = config
end

opts.on('-b x,y,z', Array, "List of backups to run") do |list|
options[:list] = list
end

opts.on_tail('-s', '--silent', 'Do not print messages to STDOUT') do
BACKUP_SILENT = true
end
end

optparse.parse!

AejisBackup.config_path = options[:config]
AejisBackup.load
AejisBackup.run!(options[:list])
68 changes: 56 additions & 12 deletions lib/aejis_backup.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,62 @@
require "fileutils"
require "rubygems"
require "aejis_backup/helpers"
require "aejis_backup/configuration"

module AejisBackup

# def config_setter(*names)
# names.each do |name|
# instance_eval %{
# def #{name}(#{name}=nil)
# #{name} ? (@#{name} = #{name}) : @#{name}
# end
# alias :#{name}= :#{name}
# }
# end
# end
class <<self
include AejisBackup::Helpers

end
def config_path(path=false)
path ? (@@config_path = path) : @@config_path
end
alias :config_path= :config_path

require "aejis_backup/configuration"
def config(&block)
return @@config unless block_given?
@@config = AejisBackup::Configuration.new
@@config.instance_eval(&block)
end

def load
require @@config_path
end

def run!(*names)
# Do backup for all backup sets if sets not defined
names = config.backups.keys if names.first.nil?

names.each do |name|
say("Backup '#{name}'", :yellow)

# Get backup, define file- and dirnames
backup = config.backups[name]
dirname = "backup-#{Time.now.to_i.to_s}"
tmpdir = File.join(backup.tmpdir, dirname)

# Make temporary directory
# TODO - Create normal exceptions
FileUtils.makedirs(tmpdir) or raise "You have no access to #{backup.tmpdir} directory"

# Get all defined sources
backup.sources.each do |source_name|
source = config.sources[source_name]
source.get!(File.join(tmpdir, source_name))
end

# Pack backups to tar archive
archive = tmpdir + '.tar'
run("tar -C #{backup.tmpdir} -c -f #{archive} #{dirname}", "Create final archive", :green)

# Store this archive
config.storages[backup.target].store!(archive)

# Remove temp dirs and report
say("Cleanup", :green)
FileUtils.rm_rf([archive, tmpdir], :secure => true)
say("Done!", :yellow)
end
end
end
end
31 changes: 6 additions & 25 deletions lib/aejis_backup/adapter/abstract.rb
Original file line number Diff line number Diff line change
@@ -1,38 +1,19 @@
module AejisBackup
module Adapter
class Abstract
include AejisBackup::Helpers

def initialize
config_accessor :user, :password, :database
end

# Set config in one line, useful with authomatic #rails method
def set_config(conf)
(@user, @password, @database = conf[:user], conf[:password], conf[:database]) or raise ArgumentError
end

# config_setter :user, :password, :database

# Set login for access
def user(user=nil)
user ? (@user = user) : @user
end
alias :user= :user

# Set password for access
def password(password=nil)
password ? (@password = password) : @password
end
alias :password= :password

# Set database name
def database(database=nil)
database ? (@database = database) : @database
end
alias :database= :database

def backup_name
Time.now.to_i # TODO - add name
end

# Get data for backup
def get!
def get!(tmpfile)
end
end
end
Expand Down
53 changes: 52 additions & 1 deletion lib/aejis_backup/adapter/archive.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,57 @@
module AejisBackup
module Adapter
class Archive < Abstract
class Archive
include AejisBackup::Helpers

def initialize
config_accessor :path, :format
compression_level 6
end

def compression_level(level=false)
return @compression_level unless level
if level.is_a?(Symbol) or level.is_a?(String)
@compression_level = case level.to_sym
when :lowest
1
when :low
3
when :middle
5
when :high
7
when :highest
9
end
elsif level.is_a? Numeric
level = level.round
@compression_level = if level < 1
1
elsif level > 9
9
end
end
end

def get!(tmpfile)
dir, target = File.dirname(path), File.basename(path)
run("tar -C #{dir} -c #{target} | #{archive(tmpfile)}", "Archiving #{path} with #{format}", :green)
end

private

def archive(tmpfile)
case format
when :bz2, :bzip, :bzip2
"bzip2 -z -#{compression_level} > #{tmpfile}.tar.bz2"
when :gz
"gzip -#{compression_level} > #{tmpfile}.tar.gz"
when :xz
"xz -z -#{compression_level} > #{tmpfile}.tar.xz"
else
"bzip2 -z -#{compression_level} > #{tmpfile}.tar.bz2"
end
end
end
end
end
9 changes: 4 additions & 5 deletions lib/aejis_backup/adapter/postgresql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ module AejisBackup
module Adapter
class Postgresql < Abstract

def get!
def get!(tmpfile)
ENV['PGPASSWORD'] = password
run "pg_dump -U #{user} #{arguments} #{database}"
run("pg_dump -U #{user} --file=#{tmpfile}.sql #{arguments} #{database}", "Dump database #{database}", :green)
ENV['PGPASSWORD'] = nil
end

private

def arguments
args = []
args << "--file=#{backup_name}.sql"
args << "--format=t" # Compress dump
args << "--compress=8" # TODO - compression level
args << "--format=t"
# TODO - compression level
args.join(' ')
end
end
Expand Down
20 changes: 20 additions & 0 deletions lib/aejis_backup/backup.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module AejisBackup
class Backup
def initialize
@tmpdir = '/tmp'
end

def sources(*names)
names.length == 0 ? @sources : (@sources = names)
end
alias :source :sources

def target(name=false)
name ? (@target = name) : @target
end

def tmpdir(path=false)
path ? (@tmpdir = path) : @tmpdir
end
end
end
37 changes: 22 additions & 15 deletions lib/aejis_backup/configuration.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
require "aejis_backup/adapter"
require "aejis_backup/store"
require "aejis_backup/backup"

module AejisBackup
class Configuration
def initialize(&block)
def initialize
@sources = Hash.new
@storages = Hash.new
instance_exec(&block)
@backups = Hash.new
end

attr_reader :sources, :storages
attr_reader :sources, :storages, :backups

def source(name, adapter, &block)
if adapter.is_a?(Hash)
Expand All @@ -22,15 +23,15 @@ def source(name, adapter, &block)

required_adapter = case adapter.to_sym
when :archive
AejisBackup::Adapter::Archive
Adapter::Archive
when :mongo
AejisBackup::Adapter::Mongo
Adapter::Mongo
when :mysql
AejisBackup::Adapter::Mysql
Adapter::Mysql
when :pg, :postgres, :postgresql
AejisBackup::Adapter::Postgresql
Adapter::Postgresql
when :sqlite, :sqlite3
AejisBackup::Adapter::Sqlite
Adapter::Sqlite
else
raise "Adapter #{adapter.to_s} not known"
end
Expand All @@ -43,19 +44,19 @@ def storage(name, store, &block)
raise "#{name.to_s.capitalize} storage not configured" unless block_given?
required_store = case store.to_sym
when :cloudfiles
AejisBackup::Store::CloudFiles
Store::CloudFiles
when :dropbox
AejisBackup::Store::Dropbox
Store::Dropbox
when :ftp
AejisBackup::Store::Ftp
Store::Ftp
when :local
AejisBackup::Store::Local
Store::Local
when :s3, :amazon
AejisBackup::Store::S3
Store::S3
when :scp
AejisBackup::Store::Scp
Store::Scp
when :sftp
AejisBackup::Store::Sftp
Store::Sftp
else
raise "Store #{adapter.to_s} not known"
end
Expand All @@ -64,6 +65,12 @@ def storage(name, store, &block)
@storages[name] = store
end

def backup(name, &block)
backup = Backup.new()
backup.instance_eval(&block)
@backups[name] = backup
end

def rails(environment=:production)
raise "You are not inside rails or use rails 2.X" unless defined?(Rails)
rails_config = ::Rails.configuration.database_configuration[environment.to_s]
Expand Down
Loading

0 comments on commit 75aec1c

Please sign in to comment.