stratocumulus 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,6 @@
1
+ StringLiterals:
2
+ EnforcedStyle: "single_quotes"
3
+ Documentation:
4
+ Enabled: false
5
+ ModuleFunction:
6
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.2
4
+ - 2.0.0
5
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in stratocumulus.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Reevoo Developers
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # Stratocumulus
2
+
3
+ Stratocumulus is for backing up databases to cloud storage
4
+
5
+ ## Installation
6
+
7
+ $ gem install stratocumulus
8
+
9
+ ## Usage
10
+
11
+ TODO: Write usage instructions here
12
+
13
+ ## Contributing
14
+
15
+ 1. Fork it ( http://github.com/reevoo/stratocumulus/fork )
16
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
17
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
18
+ 4. Push to the branch (`git push origin my-new-feature`)
19
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # encoding: UTF-8
2
+ require 'bundler/gem_tasks'
3
+ require 'rubocop/rake_task'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RuboCop::RakeTask.new
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task default: [:spec, :rubocop]
data/bin/stratocumulus ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require 'thor'
5
+ require 'stratocumulus'
6
+
7
+ module Stratocumulus
8
+ class Cli < Thor
9
+ desc 'backup CONFIG',
10
+ 'runs a stratocumulus backup as specified in the config file'
11
+ def backup(config)
12
+ Stratocumulus::Runner.new(config).run
13
+ end
14
+ end
15
+ end
16
+
17
+ Stratocumulus::Cli.start
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+
3
+ module Stratocumulus
4
+ class Database
5
+ def initialize(options = {})
6
+ @username = options['username'] || 'root'
7
+ @password = options['password']
8
+ @name = options['name']
9
+
10
+ @host = options['host'] || 'localhost'
11
+ @port = options['port'] || 3306
12
+
13
+ check_dependencies(options['type'])
14
+ end
15
+
16
+ def dump
17
+ IO.popen("bash -c '#{pipefail} #{mysqldump_command} | gzip'")
18
+ end
19
+
20
+ def filename
21
+ "#{@name}/#{@name}.#{Time.now.utc.strftime('%Y%m%d%H%M')}.sql.gz"
22
+ end
23
+
24
+ private
25
+
26
+ def mysqldump_command
27
+ command = 'mysqldump '
28
+ command << '--single-transaction '
29
+ command << "-u#{@username} "
30
+ command << "-h#{@host} "
31
+ command << "-P#{@port} "
32
+ command << "-p#{@password} " if @password
33
+ command << @name
34
+ end
35
+
36
+ def pipefail
37
+ 'set -o pipefail;'
38
+ end
39
+
40
+ def check_dependencies(type)
41
+ fail 'database name not specified' unless @name
42
+ fail "#{type} is not a supported database" unless type == 'mysql'
43
+ fail 'mysqldump not available' unless system('which mysqldump >/dev/null')
44
+ fail 'gzip not available' unless system('which gzip >/dev/null')
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,8 @@
1
+ # encoding: UTF-8
2
+
3
+ # Rewind is undefined so fog won't try to call rewind on a pipe
4
+ # This should be a refinement, but we are supporting 1.9.3 (for now)
5
+
6
+ class IO
7
+ undef rewind
8
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: UTF-8
2
+
3
+ module Stratocumulus
4
+ class Runner
5
+ def initialize(config_path)
6
+ @config = YAML.load(File.read(config_path))
7
+ end
8
+
9
+ def run
10
+ @config['databases'].each do |database_config|
11
+ database = Database.new(database_config)
12
+ upload(database, database_config['storage'])
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def upload(database, storage_type)
19
+ fail "#{storage_type} is not supported" unless storage_type == 's3'
20
+ storage.upload(database)
21
+ end
22
+
23
+ def storage
24
+ @storage ||= Storage.new(@config['s3'])
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: UTF-8
2
+ require 'fog'
3
+ require 'stratocumulus/ext/io'
4
+
5
+ module Stratocumulus
6
+ class Storage
7
+ def initialize(options = {})
8
+ @access_key_id = options.fetch('access_key_id')
9
+ @secret_access_key = options.fetch('secret_access_key')
10
+ @region = options['region']
11
+ @bucket = options.fetch('bucket')
12
+ end
13
+
14
+ def upload(database)
15
+ files.create(
16
+ key: database.filename,
17
+ body: database.dump,
18
+ multipart_chunk_size: 104_857_600, # 100MB
19
+ public: false
20
+ )
21
+ end
22
+
23
+ private
24
+
25
+ def connection
26
+ Fog::Storage.new(
27
+ provider: 'AWS',
28
+ aws_access_key_id: @access_key_id,
29
+ aws_secret_access_key: @secret_access_key,
30
+ region: @region
31
+ )
32
+ end
33
+
34
+ def files
35
+ connection.directories.get(@bucket).files
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: UTF-8
2
+ module Stratocumulus
3
+ VERSION = '0.0.1'
4
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: UTF-8
2
+ require 'stratocumulus/version'
3
+ require 'stratocumulus/database'
4
+ require 'stratocumulus/storage'
5
+ require 'stratocumulus/runner'
@@ -0,0 +1,12 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe IO do
5
+ subject { IO.new(IO.sysopen('/dev/null', 'w')) }
6
+
7
+ describe '#rewind' do
8
+ it 'is not defined' do
9
+ expect(subject).to_not respond_to(:rewind)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Stratocumulus::Database do
5
+
6
+ subject do
7
+ described_class.new(config)
8
+ end
9
+
10
+ let('config') do
11
+ {
12
+ 'name' => 'stratocumulus_test',
13
+ 'type' => 'mysql'
14
+ }
15
+ end
16
+
17
+ describe '#dump' do
18
+ before do
19
+ system('mysql -u root < spec/support/test.sql')
20
+ end
21
+
22
+ let(:dump) { Zlib::GzipReader.new(subject.dump).read }
23
+
24
+ it 'dumps a gziped copy of the database' do
25
+ expect(dump).to include('CREATE TABLE `widgets`')
26
+ expect(dump).to include("INSERT INTO `widgets` VALUES (1,'Foo',3,1,2)")
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'bundler/setup'
4
+ Bundler.setup
5
+
6
+ require 'stratocumulus'
7
+ Fog.mock!
8
+
9
+ RSpec.configure do |config|
10
+ config.default_formatter = 'doc' if config.files_to_run.one?
11
+
12
+ config.profile_examples = 10
13
+ config.order = :random
14
+ Kernel.srand config.seed
15
+
16
+ config.expect_with :rspec do |expectations|
17
+ expectations.syntax = :expect
18
+ end
19
+
20
+ config.mock_with :rspec do |mocks|
21
+ mocks.syntax = :expect
22
+ mocks.verify_partial_doubles = true
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ DROP DATABASE IF EXISTS `stratocumulus_test`;
2
+ CREATE DATABASE `stratocumulus_test`;
3
+ USE `stratocumulus_test`;
4
+ DROP TABLE IF EXISTS `widgets`;
5
+ CREATE TABLE `widgets` (
6
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
7
+ `name` varchar(256) DEFAULT NULL,
8
+ `leavers` int(11) DEFAULT NULL,
9
+ `pivots` int(11) DEFAULT NULL,
10
+ `fulcrums` int(11) DEFAULT NULL,
11
+ PRIMARY KEY (`id`)
12
+ ) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;
13
+ LOCK TABLES `widgets` WRITE;
14
+ INSERT INTO `widgets` VALUES (1,'Foo',3,1,2),(2,'Bar',2,2,0),(3,'Baz',5,6,4),(4,'Qux',4,5,6),(5,'Quux',8,5,4),(6,'Corge',8,2,7),(7,'Grault',7,3,4),(8,'Garply',1,2,3),(9,'Waldo',0,0,0),(10,'Fred',1,1,1),(11,'Plugh',8,5,3),(12,'Xyzzy',3,3,3),(13,'Thud',1,2,3);
15
+ UNLOCK TABLES;
@@ -0,0 +1,18 @@
1
+ s3:
2
+ access_key_id: I_AM_THE_KEY_ID
3
+ secret_access_key: IamTHESekret
4
+ bucket: stratocumulus-test
5
+ region: eu-west1
6
+ databases:
7
+ -
8
+ type: mysql
9
+ name: stratocumulus_test
10
+ storage: s3
11
+ username: root
12
+ password: sekret
13
+ host: db1.example.com
14
+ port: 3307
15
+ -
16
+ type: mysql
17
+ name: stratocumulus_test_2
18
+ storage: s3
@@ -0,0 +1,185 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Stratocumulus::Database do
5
+
6
+ subject do
7
+ described_class.new(config)
8
+ end
9
+
10
+ let(:config) { base_config }
11
+
12
+ let(:base_config) do
13
+ {
14
+ 'name' => 'stratocumulus_test',
15
+ 'type' => 'mysql'
16
+ }
17
+ end
18
+
19
+ before do
20
+ allow_any_instance_of(described_class).to receive(:system).and_return(true)
21
+ end
22
+
23
+ describe '.new' do
24
+ context 'the database type is not mysql' do
25
+ let(:config) { base_config.merge('type' => 'nosqlioid') }
26
+
27
+ it 'thows an error unless the type is mysql' do
28
+ expect { subject }.to raise_error(
29
+ RuntimeError,
30
+ 'nosqlioid is not a supported database'
31
+ )
32
+ end
33
+ end
34
+
35
+ it 'throws an error if mysqldump is not installed' do
36
+ allow_any_instance_of(described_class)
37
+ .to receive(:system).with(/which mysqldump/)
38
+
39
+ expect { subject }.to raise_error(
40
+ RuntimeError,
41
+ 'mysqldump not available'
42
+ )
43
+ end
44
+
45
+ it 'throws an error if gzip is not installed' do
46
+ allow_any_instance_of(described_class)
47
+ .to receive(:system).with(/which gzip/)
48
+
49
+ expect { subject }.to raise_error(
50
+ RuntimeError,
51
+ 'gzip not available'
52
+ )
53
+ end
54
+
55
+ context 'when no database name is provided' do
56
+
57
+ let(:config) do
58
+ { 'type' => 'mysql' }
59
+ end
60
+
61
+ it 'throws an error' do
62
+ expect { subject }.to raise_error(
63
+ RuntimeError,
64
+ 'database name not specified'
65
+ )
66
+ end
67
+
68
+ end
69
+ end
70
+
71
+ describe 'dump' do
72
+
73
+ after do
74
+ subject.dump
75
+ end
76
+
77
+ describe 'username' do
78
+ context 'default' do
79
+ it 'uses root' do
80
+ expect(IO).to receive(:popen) do |command|
81
+ expect(command).to include(' -uroot ')
82
+ end
83
+ end
84
+ end
85
+
86
+ context 'setting the username' do
87
+ let(:config) { base_config.merge('username' => 'susan') }
88
+
89
+ it 'uses the correct username' do
90
+ expect(IO).to receive(:popen) do |command|
91
+ expect(command).to include(' -ususan ')
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ describe 'password' do
98
+ context 'default' do
99
+ it 'uses no password' do
100
+ expect(IO).to receive(:popen) do |command|
101
+ expect(command).to_not include('-p')
102
+ end
103
+ end
104
+ end
105
+
106
+ context 'setting the passsword' do
107
+ let(:config) { base_config.merge('password' => 'sekret') }
108
+
109
+ it 'uses the correct password' do
110
+ expect(IO).to receive(:popen) do |command|
111
+ expect(command).to include(' -psekret ')
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ describe 'host' do
118
+ context 'default' do
119
+ it 'uses localhost' do
120
+ expect(IO).to receive(:popen) do |command|
121
+ expect(command).to include(' -hlocalhost ')
122
+ end
123
+ end
124
+ end
125
+
126
+ context 'setting the host' do
127
+ let(:config) { base_config.merge('host' => 'db.awesome-server.net') }
128
+
129
+ it 'uses the correct hostname' do
130
+ expect(IO).to receive(:popen) do |command|
131
+ expect(command).to include(' -hdb.awesome-server.net ')
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ describe 'port' do
138
+ context 'default' do
139
+ it 'uses 3306' do
140
+ expect(IO).to receive(:popen) do |command|
141
+ expect(command).to include(' -P3306 ')
142
+ end
143
+ end
144
+ end
145
+
146
+ context 'setting the port' do
147
+ let(:config) { base_config.merge('port' => '4306') }
148
+
149
+ it 'uses the correct port' do
150
+ expect(IO).to receive(:popen) do |command|
151
+ expect(command).to include(' -P4306 ')
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ describe 'the dump command' do
158
+ it 'sets pipefail' do
159
+ expect(IO).to receive(:popen) do |command|
160
+ expect(command).to include("bash -c 'set -o pipefail;")
161
+ end
162
+ end
163
+
164
+ it 'uses mysqldump --single-transaction option to not lock tables' do
165
+ expect(IO).to receive(:popen) do |command|
166
+ expect(command).to include(' mysqldump --single-transaction ')
167
+ end
168
+ end
169
+
170
+ it 'pipes the output of mysql to gzip' do
171
+ expect(IO).to receive(:popen) do |command|
172
+ expect(command).to match(/.*mysqldump.*\| gzip/)
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ describe '#filename' do
179
+ it 'calculates a filename based on the name and timestamp' do
180
+ timestamp = Time.now.utc.strftime('%Y%m%d%H%M')
181
+ filename = "stratocumulus_test/stratocumulus_test.#{timestamp}.sql.gz"
182
+ expect(subject.filename).to eq filename
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,57 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Stratocumulus::Runner do
5
+ subject { described_class.new('spec/support/test_config_file.yml') }
6
+ let(:storage) { double(upload: true) }
7
+ let(:database) { double }
8
+ let(:database2) { double }
9
+
10
+ before do
11
+ allow(Stratocumulus::Storage).to receive(:new).and_return(storage)
12
+ end
13
+
14
+ after do
15
+ subject.run
16
+ end
17
+
18
+ it 'passes the correct config to Storage' do
19
+ expect(Stratocumulus::Storage).to receive(:new).once.with(
20
+ 'access_key_id' => 'I_AM_THE_KEY_ID',
21
+ 'secret_access_key' => 'IamTHESekret',
22
+ 'bucket' => 'stratocumulus-test',
23
+ 'region' => 'eu-west1'
24
+ ).and_return(storage)
25
+ end
26
+
27
+ it 'passes each database config to instances of Database' do
28
+ expect(Stratocumulus::Database).to receive(:new).once.with(
29
+ 'type' => 'mysql',
30
+ 'name' => 'stratocumulus_test',
31
+ 'storage' => 's3',
32
+ 'username' => 'root',
33
+ 'password' => 'sekret',
34
+ 'host' => 'db1.example.com',
35
+ 'port' => 3307
36
+ )
37
+
38
+ expect(Stratocumulus::Database).to receive(:new).once.with(
39
+ 'type' => 'mysql',
40
+ 'name' => 'stratocumulus_test_2',
41
+ 'storage' => 's3'
42
+ )
43
+ end
44
+
45
+ it 'uploads each database to storage' do
46
+ allow(Stratocumulus::Database).to receive(:new).once.with(
47
+ hash_including('name' => 'stratocumulus_test')
48
+ ).and_return(database)
49
+
50
+ allow(Stratocumulus::Database).to receive(:new).once.with(
51
+ hash_including('name' => 'stratocumulus_test_2')
52
+ ).and_return(database2)
53
+
54
+ expect(storage).to receive(:upload).once.with(database)
55
+ expect(storage).to receive(:upload).once.with(database2)
56
+ end
57
+ end
@@ -0,0 +1,59 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+
4
+ describe Stratocumulus::Storage do
5
+ let(:config) do
6
+ {
7
+ 'access_key_id' => 'IM_A_ID',
8
+ 'secret_access_key' => 'IM_A_SEKRET_KEY',
9
+ 'region' => 'eu-west-1',
10
+ 'bucket' => 'stratocumulus-test'
11
+ }
12
+ end
13
+
14
+ subject { described_class.new(config) }
15
+
16
+ describe '#upload' do
17
+ let(:connection) { double(:fog_conn, directories: directories) }
18
+ let(:directories) { double(:fog_dirs, get: double(files: files)) }
19
+ let(:files) { double(:fog_files, create: true) }
20
+
21
+ let(:database) do
22
+ double(:database, filename: 'foo.sql.gz', dump: :database_dump)
23
+ end
24
+
25
+ before do
26
+ allow(Fog::Storage).to receive(:new).and_return(connection)
27
+ end
28
+
29
+ after do
30
+ subject.upload(database)
31
+ end
32
+
33
+ it 'uploads the dump to s3' do
34
+ expect(files).to receive(:create).with(
35
+ key: 'foo.sql.gz',
36
+ body: :database_dump,
37
+ multipart_chunk_size: 104_857_600,
38
+ public: false
39
+ )
40
+ end
41
+
42
+ it 'uploads to the correct s3 bucket' do
43
+ expect(directories).to receive(:get).with('stratocumulus-test')
44
+ end
45
+
46
+ describe 'the fog connection' do
47
+ it 'is setup corectly' do
48
+ expect(Fog::Storage).to receive(:new)
49
+ .with(
50
+ provider: 'AWS',
51
+ aws_access_key_id: 'IM_A_ID',
52
+ aws_secret_access_key: 'IM_A_SEKRET_KEY',
53
+ region: 'eu-west-1'
54
+ ).and_return(connection)
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'stratocumulus/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'stratocumulus'
8
+ spec.version = Stratocumulus::VERSION
9
+ spec.authors = ['Ed Robinson']
10
+ spec.email = ['ed.robinson@reevoo.com']
11
+ spec.summary = %q(Backup Databases to Cloud Storage)
12
+ spec.description = %q(Backup Databases to Cloud Storage)
13
+ spec.homepage = 'https://github.com/reevoo/stratocumulus'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = ['stratocumulus']
18
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'fog', '~> 1.22'
22
+ spec.add_development_dependency 'bundler', '~> 1.5'
23
+ spec.add_development_dependency 'rake'
24
+ spec.add_development_dependency 'rubocop'
25
+ spec.add_development_dependency 'rspec', '~> 3.0'
26
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stratocumulus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ed Robinson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-06-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fog
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.22'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.22'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '1.5'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '1.5'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rubocop
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '3.0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '3.0'
94
+ description: Backup Databases to Cloud Storage
95
+ email:
96
+ - ed.robinson@reevoo.com
97
+ executables:
98
+ - stratocumulus
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - .gitignore
103
+ - .rspec
104
+ - .rubocop.yml
105
+ - .travis.yml
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - bin/stratocumulus
111
+ - lib/stratocumulus.rb
112
+ - lib/stratocumulus/database.rb
113
+ - lib/stratocumulus/ext/io.rb
114
+ - lib/stratocumulus/runner.rb
115
+ - lib/stratocumulus/storage.rb
116
+ - lib/stratocumulus/version.rb
117
+ - spec/ext/io_spec.rb
118
+ - spec/intergration/database_spec.rb
119
+ - spec/spec_helper.rb
120
+ - spec/support/test.sql
121
+ - spec/support/test_config_file.yml
122
+ - spec/unit/database_spec.rb
123
+ - spec/unit/runner_spec.rb
124
+ - spec/unit/storage_spec.rb
125
+ - stratocumulus.gemspec
126
+ homepage: https://github.com/reevoo/stratocumulus
127
+ licenses:
128
+ - MIT
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ! '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ segments:
140
+ - 0
141
+ hash: 138837949787604708
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ! '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ segments:
149
+ - 0
150
+ hash: 138837949787604708
151
+ requirements: []
152
+ rubyforge_project:
153
+ rubygems_version: 1.8.25
154
+ signing_key:
155
+ specification_version: 3
156
+ summary: Backup Databases to Cloud Storage
157
+ test_files:
158
+ - spec/ext/io_spec.rb
159
+ - spec/intergration/database_spec.rb
160
+ - spec/spec_helper.rb
161
+ - spec/support/test.sql
162
+ - spec/support/test_config_file.yml
163
+ - spec/unit/database_spec.rb
164
+ - spec/unit/runner_spec.rb
165
+ - spec/unit/storage_spec.rb