├── .gitignore ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── bin └── edb ├── edb.gemspec ├── example └── edb.yml ├── lib ├── edb.rb └── edb │ ├── cryptography.rb │ ├── cryptography │ └── aes_256_cbc.rb │ ├── dbms.rb │ ├── dbms │ ├── mysql.rb │ └── postgresql.rb │ ├── dumper.rb │ ├── edb.rb │ ├── is_module_supported.rb │ ├── logger.rb │ ├── storage.rb │ ├── storage │ ├── filesystem.rb │ ├── ftp.rb │ └── s3.rb │ └── version.rb └── specs ├── cryptography └── aes_256_cbc_spec.rb ├── cryptography_spec.rb ├── dbms ├── mysql_spec.rb └── postgresql_spec.rb ├── dbms_spec.rb ├── spec_helper.rb └── storage_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .ruby-gemset 2 | *.gem 3 | *2015 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.1.0 4 | - 2.2.0 5 | 6 | notifications: 7 | irc: 'irc.rizon.net#aggvistnurummor' 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | 4 | gem 'rake', '~> 10.4' 5 | gem 'rspec', '~> 3.3' 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | edb (0.6.1) 5 | aws-sdk (= 1.59.1) 6 | fast_secure_compare (~> 1.0) 7 | ftp_sync (~> 0.4) 8 | hkdf (~> 0.2) 9 | 10 | GEM 11 | remote: https://rubygems.org/ 12 | specs: 13 | aws-sdk (1.59.1) 14 | aws-sdk-v1 (= 1.59.1) 15 | aws-sdk-v1 (1.59.1) 16 | json (~> 1.4) 17 | nokogiri (>= 1.4.4) 18 | diff-lcs (1.2.5) 19 | fast_secure_compare (1.0.1) 20 | ftp_sync (0.4.3) 21 | net-ftp-list (>= 2.1.1) 22 | hkdf (0.2.0) 23 | json (1.8.3) 24 | mini_portile (0.6.2) 25 | net-ftp-list (3.2.8) 26 | nokogiri (1.6.6.2) 27 | mini_portile (~> 0.6.0) 28 | rake (10.4.2) 29 | rspec (3.3.0) 30 | rspec-core (~> 3.3.0) 31 | rspec-expectations (~> 3.3.0) 32 | rspec-mocks (~> 3.3.0) 33 | rspec-core (3.3.2) 34 | rspec-support (~> 3.3.0) 35 | rspec-expectations (3.3.1) 36 | diff-lcs (>= 1.2.0, < 2.0) 37 | rspec-support (~> 3.3.0) 38 | rspec-mocks (3.3.2) 39 | diff-lcs (>= 1.2.0, < 2.0) 40 | rspec-support (~> 3.3.0) 41 | rspec-support (3.3.0) 42 | 43 | PLATFORMS 44 | ruby 45 | 46 | DEPENDENCIES 47 | edb! 48 | rake (~> 10.4) 49 | rspec (~> 3.3) 50 | 51 | BUNDLED WITH 52 | 1.10.5 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Backup, encrypt and manage your database 2 | 3 | *EDB* is a framework to make and manage backups of your database. 4 | It is composed by three macro areas that reflect themself inside `edb.yml` and are *DBMS*, *CRYPTOGRAPHY* and *STORAGE*. 5 | The first one deals with the actual backup process of your database. The second one will eventually encrypt the backup copies you made and the last one will copy them somewhere (S3 bucket, your local filesystem, etc.). 6 | 7 | 8 | **tl;dr** Make (optionally) ciphered backups of your database(s) and then upload them via FTP and Amazon S3 (or just keep them in your server). 9 | 10 | ## Install 11 | `$ gem install edb` 12 | 13 | ## Run 14 | Customize `example/edb.yml` (remember also to change the `secret` by running `$ edb -k`) and then: 15 | 16 | `$ edb example/edb.yml` 17 | 18 | To decrypt the encrypted files: 19 | 20 | `$ edb example/edb.yml -d [FILE]` 21 | 22 | Consider also to add *EDB* to your cronjobs. 23 | 24 | ## Available modules 25 | - *Cryptography*: `AES_256_CBC` 26 | - *DBMS*: `PostgreSQL`, `MySQL` 27 | - *Storage*: `S3`, `Filesystem`, `FTP` 28 | 29 | ## FAQ 30 | **Q:** What if I want to dump two or three MySQL databases? 31 | *A:* Just add a `:MySQL:` block for every database you need to dump. 32 | 33 | 34 | **Q:** What if I want to save a database to S3 and another one into my local filesystem? 35 | *A:* Well, you can't. By design, every macro-block (like, `:DBMS:`) is unaware of the other ones. So, for instance, I couldn't ask `:Filesystem:` to work only for the first `:MySQL:` block since it actually does not know what a `:MySQL:` block is. You need just to create two configuration files. 36 | 37 | 38 | **Q:** Is there something to run it automatically at every night? Let's say 2:30am 39 | *A:* Cronjobs to the rescue. Append the following line to your `crontab -e`: ```30 2 * * * bash -l -c '`which edb` /home/deployer/edb.yml'```. 40 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | require 'rake' 3 | 4 | task default: [ :build, :install, :test ] 5 | 6 | task :build do 7 | sh 'gem build edb.gemspec' 8 | end 9 | 10 | task :install do 11 | sh 'gem install *.gem' 12 | end 13 | 14 | task :test do 15 | FileUtils.cd 'specs' do 16 | Dir['./**/*_spec.rb'].each do |spec| 17 | sh "rspec #{spec} --backtrace --color --format doc" 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /bin/edb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | #-- 3 | # Copyright(C) 2015 Giovanni Capuano 4 | # 5 | # Redistribution and use in source and binary forms, with or without modification, are 6 | # permitted provided that the following conditions are met: 7 | # 8 | # 1. Redistributions of source code must retain the above copyright notice, this list of 9 | # conditions and the following disclaimer. 10 | # 11 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 12 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 13 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 14 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 15 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 16 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 17 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 18 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 19 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | # 21 | # The views and conclusions contained in the software and documentation are those of the 22 | # authors and should not be interpreted as representing official policies, either expressed 23 | # or implied, of Giovanni Capuano. 24 | #++ 25 | require 'edb' 26 | require 'yaml' 27 | require 'optparse' 28 | 29 | @tasks = {} 30 | 31 | OptionParser.new do |opts| 32 | opts.version = EDB::VERSION 33 | opts.banner = "Usage: edb [-kd] [CONFIG_FILE]" 34 | 35 | opts.on('-k', '--generate-key', 'Generate a random key of 32 characters') do 36 | require 'securerandom' 37 | 38 | puts SecureRandom.hex(32) 39 | exit 40 | end 41 | 42 | opts.on('-d', '--decrypt [FILE]', 'Decrypt given file') do |file| 43 | @tasks[:decrypt] = { file_to_decrypt: File.expand_path(file) } 44 | end 45 | end.parse! 46 | 47 | config_file = ARGV[0] 48 | if !config_file || !config_file.end_with?('.yml') || !File.exists?(config_file) 49 | abort 'Please provide a valid configuration file.' 50 | end 51 | 52 | EDB.opts = YAML.load_file(config_file) 53 | 54 | if @tasks.has_key?(:decrypt) 55 | ciphering_method = EDB.opts[:CRYPTOGRAPHY].first[0] 56 | EDB::Cryptography.decrypt ciphering_method, @tasks[:decrypt][:file_to_decrypt] 57 | else 58 | EDB::Dumper.new.run 59 | end 60 | -------------------------------------------------------------------------------- /edb.gemspec: -------------------------------------------------------------------------------- 1 | Kernel.load 'lib/edb/version.rb' 2 | 3 | Gem::Specification.new { |s| 4 | s.name = 'edb' 5 | s.version = EDB::VERSION 6 | s.author = 'Giovanni Capuano' 7 | s.email = 'webmaster@giovannicapuano.net' 8 | s.homepage = 'http://github.com/RoxasShadow/edb' 9 | s.platform = Gem::Platform::RUBY 10 | s.summary = 'A framework to make and manage backups of your database.' 11 | s.description = 'EDB aims to be a framework to make and manage backups of your database.' 12 | s.license = 'BSD' 13 | 14 | s.files = `git ls-files`.split("\n") 15 | s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) } 16 | s.require_paths = ['lib'] 17 | 18 | s.add_dependency 'aws-sdk', '1.59.1' 19 | s.add_dependency 'ftp_sync', '~> 0.4' 20 | s.add_dependency 'hkdf', '~> 0.2' 21 | s.add_dependency 'fast_secure_compare', '~> 1.0' 22 | } 23 | -------------------------------------------------------------------------------- /example/edb.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :DBMS: 3 | :PostgreSQL: 4 | :binpath: /Applications/Postgres.app/Contents/Versions/9.3/bin 5 | :username: username 6 | :password: password 7 | :database: sample_database 8 | :host: localhost 9 | :port: 5432 10 | :include_cluster: true 11 | :only_tables: [] 12 | :exclude_tables: ['trash_table'] 13 | :run_sql_after_backup: ~/Documents/clear_tables.sql 14 | 15 | :MySQL: 16 | :binpath: /Applications/MAMP/Library/bin 17 | :username: username 18 | :password: password 19 | :database: sample_database 20 | :host: localhost 21 | 22 | :CRYPTOGRAPHY: 23 | :AES_256_CBC: 24 | :secret: 2518f4361ef86fa307d02bfa9794d37e8dad5458af4e0424cf143b4b8f95e5e4 25 | 26 | :STORAGE: 27 | :S3: 28 | :access_key_id: '' 29 | :secret_access_key: '' 30 | :region: eu-west-1 31 | :bucket: 32 | :name: my-bucket-name 33 | :subfolder: backups 34 | 35 | :Filesystem: 36 | :path: ~/Downloads 37 | 38 | :FTP: 39 | :host: ftp.example.xyz 40 | :username: username 41 | :password: password 42 | :path: /home/username/ 43 | -------------------------------------------------------------------------------- /lib/edb.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | require 'edb/edb' 25 | require 'edb/logger' 26 | require 'edb/dumper' 27 | require 'edb/is_module_supported' 28 | require 'edb/version' 29 | 30 | require 'edb/dbms' 31 | require 'edb/cryptography' 32 | require 'edb/storage' 33 | -------------------------------------------------------------------------------- /lib/edb/cryptography.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | Dir[File.dirname(__FILE__) + '/cryptography/*.rb'].each { |file| require file } 25 | 26 | module EDB 27 | module Cryptography 28 | class << self 29 | include ::EDB::IsModuleSupported 30 | 31 | def encrypt(method, filename) 32 | ::EDB::Logger.log(:info, "Encrypting #{filename}...") 33 | 34 | ciphered_data = File.open(filename, 'rb') do |file| 35 | data = file.read 36 | 37 | this_module = to_module(method) 38 | ciphered_data = this_module.encrypt(data) 39 | end 40 | 41 | File.open(filename, 'wb') do |file| 42 | file.write(ciphered_data) 43 | end 44 | end 45 | 46 | def decrypt(method, filename) 47 | ::EDB::Logger.log(:info, "Decrypting #{filename}...") 48 | 49 | data = File.open(filename, 'rb') do |file| 50 | ciphered_data = file.read 51 | 52 | this_module = to_module(method) 53 | data = this_module.decrypt(ciphered_data) 54 | end 55 | 56 | File.open("#{filename}.dec", 'wb') do |file| 57 | file.write(data) 58 | end 59 | end 60 | 61 | private 62 | def to_module(method) 63 | Object.const_get("::EDB::Cryptography::#{method}") 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /lib/edb/cryptography/aes_256_cbc.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | require 'openssl' 25 | require 'hkdf' 26 | require 'fast_secure_compare/fast_secure_compare' 27 | 28 | module EDB 29 | module Cryptography 30 | module AES_256_CBC 31 | class << self 32 | def encrypt(data) 33 | raise "Cannot encrypt #{filename}: It's empty" if data.empty? 34 | 35 | cipher = OpenSSL::Cipher.new('AES-256-CBC') 36 | cipher.encrypt 37 | 38 | hkdf = HKDF.new(::EDB.opts[:CRYPTOGRAPHY][:AES_256_CBC][:secret]) 39 | cipher.key = hkdf.next_bytes(32) 40 | authentication_key = hkdf.next_bytes(64) 41 | cipher.iv = iv = cipher.random_iv 42 | 43 | ciphered_data = cipher.update(data) + cipher.final 44 | ciphered_data << iv 45 | 46 | authentication = OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA256'), authentication_key, ciphered_data) 47 | ciphered_data << authentication 48 | end 49 | 50 | def decrypt(ciphered_data) 51 | raise "Cannot decrypt #{filename}: It's empty" if ciphered_data.length < 64 52 | 53 | decipher = OpenSSL::Cipher.new('AES-256-CBC') 54 | decipher.decrypt 55 | 56 | authentication = slice_str!(ciphered_data, 32) 57 | 58 | hkdf = HKDF.new(::EDB.opts[:CRYPTOGRAPHY][:AES_256_CBC][:secret]) 59 | decipher.key = hkdf.next_bytes(32) 60 | authentication_key = hkdf.next_bytes(64) 61 | 62 | new_authentication = OpenSSL::HMAC.digest(OpenSSL::Digest.new('SHA256'), authentication_key, ciphered_data) 63 | raise 'Authentication failed.' unless FastSecureCompare.compare(authentication, new_authentication) 64 | 65 | decipher.iv = slice_str!(ciphered_data, 16) 66 | 67 | deciphered_data = decipher.update(ciphered_data) + decipher.final 68 | end 69 | 70 | private 71 | def slice_str!(str, n) 72 | len = str.length 73 | str.slice!(len - n, len) 74 | end 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/edb/dbms.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | Dir[File.dirname(__FILE__) + '/dbms/*.rb'].each { |file| require file } 25 | 26 | module EDB 27 | module DBMS 28 | class << self 29 | include ::EDB::IsModuleSupported 30 | 31 | def backup(dbms, *args) 32 | this_module = to_module(dbms) 33 | this_module.backup(*args) 34 | end 35 | 36 | private 37 | def to_module(dbms) 38 | Object.const_get("::EDB::DBMS::#{dbms}") 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/edb/dbms/mysql.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | 25 | module EDB 26 | module DBMS 27 | module MySQL 28 | class << self 29 | def backup(dir_name) 30 | db = ::EDB.opts[:DBMS][:MySQL] 31 | files = { 32 | dump: File.join(dir_name, "#{db[:database]}.sql") 33 | } 34 | 35 | ::EDB::Logger.log(:info, "Dumping #{db[:database]}...") 36 | 37 | mysqldump = db[:binpath] && !db[:binpath].empty? ? File.join(db[:binpath], 'mysqldump') : 'mysqldump' 38 | 39 | args = %W{ 40 | --user=#{db[:username]} 41 | --password=#{db[:password]} 42 | --single-transaction 43 | #{db[:database]} > #{files[:dump]} 44 | }.join(' ') 45 | 46 | Kernel.system "#{mysqldump} #{args}" 47 | 48 | files.values 49 | end 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/edb/dbms/postgresql.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | 25 | module EDB 26 | module DBMS 27 | module PostgreSQL 28 | class << self 29 | def backup(dir_name) 30 | db = ::EDB.opts[:DBMS][:PostgreSQL] 31 | files = { 32 | dump: File.join(dir_name, "#{db[:database]}.sql") 33 | } 34 | 35 | ::EDB::Logger.log(:info, "Dumping #{db[:database]}...") 36 | 37 | only_tables = (db[:only_tables] || []).reject(&:empty?).map { |t| "-t #{t}" }.join(' ') 38 | exclude_tables = (db[:exclude_tables] || []).reject(&:empty?).map { |t| "-T #{t}" }.join(' ') 39 | 40 | pg_dump = db[:binpath] && !db[:binpath].empty? ? File.join(db[:binpath], 'pg_dump') : 'pg_dump' 41 | Kernel.system "PGPASSWORD='#{db[:password]}' #{pg_dump} -h #{db[:host]} -p #{db[:port]} -U #{db[:username]} -F c -b -f '#{files[:dump]}' #{only_tables} #{exclude_tables} #{db[:database]}" 42 | 43 | if db[:include_cluster] == true 44 | ::EDB::Logger.log(:info, 'Dumping the cluster...') 45 | 46 | files[:cluster] = File.join(dir_name, 'cluster.sql') 47 | pg_dumpall = db[:binpath] && !db[:binpath].empty? ? File.join(db[:binpath], 'pg_dumpall') : 'pg_dumpall' 48 | Kernel.system "PGPASSWORD='#{db[:password]}' #{pg_dumpall} -h #{db[:host]} -p #{db[:port]} -U #{db[:username]} -f '#{files[:cluster]}'" 49 | end 50 | 51 | if db[:run_sql_after_backup] && File.exists?(db[:run_sql_after_backup]) 52 | ::EDB::Logger.log(:info, "Executing #{File.basename(db[:run_sql_after_backup])}...") 53 | 54 | psql = db[:binpath] && !db[:binpath].empty? ? File.join(db[:binpath], 'psql') : 'psql' 55 | Kernel.system "PGPASSWORD='#{db[:password]}' #{psql} -h #{db[:host]} -p #{db[:port]} -U #{db[:username]} -d #{db[:database]} -f #{db[:run_sql_after_backup]}" 56 | end 57 | 58 | files.values 59 | end 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/edb/dumper.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | 25 | module EDB 26 | class Dumper 27 | PATTERN = ->(time) { "#{time.day}_#{time.month}_#{time.year}" } 28 | 29 | def initialize(opts = nil) 30 | ::EDB.opts ||= opts if opts 31 | end 32 | 33 | def run 34 | create_dir 35 | 36 | ::EDB.opts[:DBMS].each do |dbms| 37 | dbms_name = dbms[0] 38 | 39 | unless EDB::DBMS.supports?(dbms_name) 40 | module_not_supported(dbms_name) 41 | next 42 | end 43 | 44 | dir_name = File.join(@dir_name, dbms_name.to_s) 45 | FileUtils.mkdir(dir_name) unless Dir.exists?(dir_name) 46 | 47 | files = EDB::DBMS.backup(dbms_name, dir_name) 48 | 49 | if ::EDB.opts[:CRYPTOGRAPHY] != nil 50 | ::EDB.opts[:CRYPTOGRAPHY].each do |cryptography| 51 | algorithm = cryptography[0] 52 | 53 | if ::EDB::Cryptography.supports?(algorithm) 54 | files.each { |file| ::EDB::Cryptography.encrypt(algorithm, file) } 55 | else 56 | module_not_supported(algorithm) 57 | end 58 | end 59 | end 60 | 61 | if ::EDB.opts[:STORAGE] != nil 62 | ::EDB.opts[:STORAGE].each do |storage| 63 | service = storage[0] 64 | 65 | if ::EDB::Storage.supports?(service) 66 | files.each { |file| ::EDB::Storage.upload(service, file) } 67 | else 68 | module_not_supported(service) 69 | end 70 | end 71 | end 72 | end 73 | end 74 | 75 | private 76 | def create_dir 77 | @dir_name = PATTERN.call(Time.now) 78 | Dir.mkdir(@dir_name) unless Dir.exists?(@dir_name) 79 | end 80 | 81 | def module_not_supported(module_name) 82 | ::EDB::Logger.log(:error, "No support for #{module_name}.") 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/edb/edb.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | 25 | module EDB 26 | class << self 27 | def opts 28 | @@opts 29 | end 30 | 31 | def opts=(options) 32 | @@opts = options 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/edb/is_module_supported.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | 25 | module EDB 26 | module IsModuleSupported 27 | def supports?(dbms) 28 | begin 29 | this_module = to_module(dbms) 30 | all_modules.include?(this_module) 31 | rescue NameError 32 | false 33 | end 34 | end 35 | 36 | private 37 | def all_modules 38 | constants.select { |c| const_get(c).is_a?(Module) }.map { |c| const_get(c) } 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/edb/logger.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | 25 | module EDB 26 | module Logger 27 | class << self 28 | def log(level, message) 29 | case level 30 | when :error 31 | puts "Error: #{message}" 32 | else 33 | puts message 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/edb/storage.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | Dir[File.dirname(__FILE__) + '/storage/*.rb'].each { |file| require file } 25 | 26 | module EDB 27 | module Storage 28 | class << self 29 | include ::EDB::IsModuleSupported 30 | 31 | def upload(service, source) 32 | this_module = to_module(service) 33 | this_module.upload(source) 34 | end 35 | 36 | private 37 | def to_module(service) 38 | Object.const_get("::EDB::Storage::#{service}") 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/edb/storage/filesystem.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | require 'fileutils' 25 | 26 | module EDB 27 | module Storage 28 | module Filesystem 29 | class << self 30 | def upload(source) 31 | filesystem = ::EDB.opts[:STORAGE][:Filesystem] 32 | 33 | path = File.expand_path(filesystem[:path]) 34 | folder = source.split('/')[0..-2].join('/') 35 | path = File.join(path, folder) 36 | ::EDB::Logger.log(:info, "Copying #{source} to #{path}...") 37 | 38 | FileUtils.mkdir_p(path) unless Dir.exists?(path) 39 | 40 | source = File.join('./', source) 41 | FileUtils.cp_r(source, path) 42 | end 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/edb/storage/ftp.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | require 'ftp_sync' 25 | 26 | module EDB 27 | module Storage 28 | module FTP 29 | class << self 30 | def upload(source) 31 | data = ::EDB.opts[:STORAGE][:FTP] 32 | ::EDB::Logger.log(:info, "Uploading #{source} to #{data[:username]}@#{data[:host]} via FTP...") 33 | 34 | ftp = FtpSync.new(data[:host], data[:username], data[:password]) 35 | ftp.push_files('.', data[:path], [source]) 36 | end 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/edb/storage/s3.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | gem 'aws-sdk', '=1.59.1' 25 | require 'aws-sdk' 26 | 27 | module EDB 28 | module Storage 29 | module S3 30 | class << self 31 | def upload(source) 32 | ::EDB::Logger.log(:info, "Uploading #{source} to S3...") 33 | 34 | aws = ::EDB.opts[:STORAGE][:S3] 35 | AWS.config(aws) 36 | 37 | target = File.join(aws[:bucket][:subfolder], source) 38 | source = File.join('./', source) 39 | 40 | bucket = AWS::S3.new.buckets[aws[:bucket][:name]] 41 | bucket.objects.create(target, Pathname.new(source)) 42 | end 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/edb/version.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # Copyright(C) 2015 Giovanni Capuano 3 | # 4 | # Redistribution and use in source and binary forms, with or without modification, are 5 | # permitted provided that the following conditions are met: 6 | # 7 | # 1. Redistributions of source code must retain the above copyright notice, this list of 8 | # conditions and the following disclaimer. 9 | # 10 | # THIS SOFTWARE IS PROVIDED BY Giovanni Capuano ''AS IS'' AND ANY EXPRESS OR IMPLIED 11 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 12 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Giovanni Capuano OR 13 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 14 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 16 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 18 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 19 | # 20 | # The views and conclusions contained in the software and documentation are those of the 21 | # authors and should not be interpreted as representing official policies, either expressed 22 | # or implied, of Giovanni Capuano. 23 | #++ 24 | 25 | module EDB 26 | VERSION = '0.7' 27 | end 28 | -------------------------------------------------------------------------------- /specs/cryptography/aes_256_cbc_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../spec_helper' 2 | 3 | describe EDB::Cryptography::AES_256_CBC do 4 | describe '#encrypt' do 5 | context 'given string is empty' do 6 | it 'raises an exception' do 7 | expect { EDB::Cryptography::AES_256_CBC.encrypt('') }.to raise_error 8 | end 9 | end 10 | 11 | context 'non-empty string is given' do 12 | let(:data) { 'kanbaru is mai waifu' } 13 | let(:ciphered_data) { EDB::Cryptography::AES_256_CBC.encrypt(data) } 14 | 15 | it 'returns a valid AES-256-CBC ciphered string' do 16 | expect(ciphered_data.length).to be 80 17 | end 18 | end 19 | end 20 | 21 | describe '#decrypt' do 22 | context 'given data is less than 64 chars long' do 23 | it 'raises an exception' do 24 | expect { EDB::Cryptography::AES_256_CBC.decrypt('H' * 63) }.to raise_error 25 | end 26 | end 27 | 28 | context 'valid data is given' do 29 | let(:real_data) { 'kanbaru is mai waifu' } 30 | let(:ciphered_data) { EDB::Cryptography::AES_256_CBC.encrypt(real_data) } 31 | let(:data) { EDB::Cryptography::AES_256_CBC.decrypt(ciphered_data) } 32 | 33 | it 'returns a string matching the original given one' do 34 | expect(data).to eq real_data 35 | end 36 | end 37 | end 38 | 39 | describe '#slice_str!' do 40 | let(:str) { 'senjougahara' } 41 | let(:sliced) { EDB::Cryptography::AES_256_CBC.instance_exec(str) { |str| slice_str!(str, 6) } } 42 | 43 | it 'returns and remove the last n characters from given string' do 44 | expect(sliced).to eq 'gahara' 45 | expect(str).to eq 'senjou' 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /specs/cryptography_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe EDB::Cryptography do 4 | describe '#supports?' do 5 | context 'given algorithm is supported' do 6 | it 'returns true' do 7 | expect(EDB::Cryptography.supports?('AES_256_CBC')).to be_truthy 8 | end 9 | end 10 | 11 | context 'given algorithm is not supported' do 12 | it 'returns false' do 13 | expect(EDB::Cryptography.supports?('LOL_WUT_128')).to be_falsy 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /specs/dbms/mysql_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../spec_helper' 2 | 3 | describe EDB::DBMS::MySQL do 4 | describe '#backup' do 5 | let(:dump) { '/Applications/MAMP/Library/bin/mysqldump --user=username --password=password --single-transaction sample_database > ./sample_database.sql' } 6 | 7 | it 'calls mysqldump correctly' do 8 | allow(Kernel).to receive(:system).and_wrap_original do |m, *args| 9 | expect(args[0]).to eq dump 10 | end 11 | 12 | EDB::DBMS::MySQL.backup('.') 13 | end 14 | 15 | it 'returns the the files that have been created' do 16 | allow(Kernel).to receive(:system).and_wrap_original {} 17 | 18 | results = EDB::DBMS::MySQL.backup('.') 19 | expect(results).to eq ['./sample_database.sql'] 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /specs/dbms/postgresql_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../spec_helper' 2 | 3 | describe EDB::DBMS::PostgreSQL do 4 | describe '#backup' do 5 | let(:dump) { "PGPASSWORD='password' /Applications/Postgres.app/Contents/Versions/9.3/bin/pg_dump -h localhost -p 5432 -U username -F c -b -f './sample_database.sql' -T trash_table sample_database" } 6 | let(:cluster) { "PGPASSWORD='password' /Applications/Postgres.app/Contents/Versions/9.3/bin/pg_dumpall -h localhost -p 5432 -U username -f './cluster.sql'" } 7 | let(:after_sql) { "PGPASSWORD='password' /Applications/Postgres.app/Contents/Versions/9.3/bin/psql -h localhost -p 5432 -U username -d sample_database -f ~/Documents/clear_tables.sql" } 8 | 9 | it 'calls pg_dump correctly' do 10 | allow(Kernel).to receive(:system).and_wrap_original do |m, *args| 11 | expect(args[0]).to satisfy { |c| [dump, cluster, after_sql].include?(c) } 12 | end 13 | 14 | EDB::DBMS::PostgreSQL.backup('.') 15 | end 16 | 17 | context 'include_cluster is true' do 18 | it 'returns the files that have been created' do 19 | allow(Kernel).to receive(:system).and_wrap_original {} 20 | 21 | results = EDB::DBMS::PostgreSQL.backup('.') 22 | expect(results).to eq ['./sample_database.sql', './cluster.sql'] 23 | end 24 | end 25 | 26 | context 'include_cluster is false' do 27 | before :each do 28 | EDB.opts[:DBMS][:PostgreSQL][:include_cluster] = false 29 | end 30 | 31 | it 'returns the files that have been created' do 32 | allow(Kernel).to receive(:system).and_wrap_original {} 33 | 34 | results = EDB::DBMS::PostgreSQL.backup('.') 35 | expect(results).to eq ['./sample_database.sql'] 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /specs/dbms_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe EDB::DBMS do 4 | describe '#supports?' do 5 | context 'given DBMS device is supported' do 6 | it 'returns true' do 7 | expect(EDB::DBMS.supports?('MySQL')).to be_truthy 8 | end 9 | end 10 | 11 | context 'given DBMS device is not supported' do 12 | it 'returns false' do 13 | expect(EDB::DBMS.supports?('CongoDB')).to be_falsy 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /specs/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'edb' 2 | require 'yaml' 3 | 4 | EDB.opts = YAML.load_file('../example/edb.yml') 5 | -------------------------------------------------------------------------------- /specs/storage_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative 'spec_helper' 2 | 3 | describe EDB::Cryptography do 4 | describe '#supports?' do 5 | context 'given storage device is supported' do 6 | it 'returns true' do 7 | expect(EDB::Storage.supports?('S3')).to be_truthy 8 | end 9 | end 10 | 11 | context 'given storage device is not supported' do 12 | it 'returns false' do 13 | expect(EDB::Storage.supports?('CappellaAmbrosiana')).to be_falsy 14 | end 15 | end 16 | end 17 | end 18 | --------------------------------------------------------------------------------