stratocumulus 0.0.4 → 0.0.5
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/.travis.yml +10 -0
- data/lib/stratocumulus/database.rb +42 -18
- data/lib/stratocumulus/database/mysql.rb +23 -33
- data/lib/stratocumulus/database/postgresql.rb +34 -0
- data/lib/stratocumulus/database/rethinkdb.rb +39 -0
- data/lib/stratocumulus/runner.rb +1 -1
- data/lib/stratocumulus/storage.rb +1 -1
- data/lib/stratocumulus/version.rb +1 -1
- data/spec/intergration/database_spec.rb +74 -9
- data/spec/spec_helper.rb +11 -0
- data/spec/support/{test.sql → mysql.sql} +0 -0
- data/spec/support/psql.sql +52 -0
- data/spec/support/rethinkdb.tar.gz +0 -0
- data/spec/unit/database_spec.rb +47 -171
- data/spec/unit/mysql_spec.rb +104 -0
- data/spec/unit/postgresql_spec.rb +104 -0
- data/spec/unit/storage_spec.rb +13 -1
- metadata +16 -6
data/.travis.yml
CHANGED
@@ -3,3 +3,13 @@ rvm:
|
|
3
3
|
- 2.1.2
|
4
4
|
- 2.0.0
|
5
5
|
- 1.9.3
|
6
|
+
before_install:
|
7
|
+
- source /etc/lsb-release && echo "deb http://download.rethinkdb.com/apt $DISTRIB_CODENAME main" | sudo tee /etc/apt/sources.list.d/rethinkdb.list
|
8
|
+
- wget -O- http://download.rethinkdb.com/apt/pubkey.gpg | sudo apt-key add -
|
9
|
+
- sudo apt-get update -qq
|
10
|
+
install:
|
11
|
+
- sudo apt-get install -y rethinkdb
|
12
|
+
- sudo pip install rethinkdb
|
13
|
+
- sudo cp /etc/rethinkdb/default.conf.sample /etc/rethinkdb/instances.d/instance1.conf
|
14
|
+
- sudo /etc/init.d/rethinkdb restart
|
15
|
+
- bundle install
|
@@ -1,19 +1,42 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require 'stratocumulus/database/mysql'
|
3
2
|
require 'stratocumulus/database/pipe_io'
|
3
|
+
require 'stratocumulus/database/mysql'
|
4
|
+
require 'stratocumulus/database/postgresql'
|
5
|
+
require 'stratocumulus/database/rethinkdb'
|
4
6
|
require 'English'
|
5
7
|
|
6
8
|
module Stratocumulus
|
7
9
|
class Database
|
8
|
-
def
|
10
|
+
def self.build(options = {})
|
11
|
+
backend_class = backends[options['type']]
|
12
|
+
fail "#{options['type']} is not a supported database" unless backend_class
|
13
|
+
backend_class.new(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.backends
|
17
|
+
{
|
18
|
+
'psql' => PostgreSQL,
|
19
|
+
'mysql' => MySQL,
|
20
|
+
'rethinkdb' => RethinkDB
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(options = {})
|
25
|
+
check_dependencies
|
26
|
+
@username = options['username']
|
27
|
+
@password = options['password']
|
9
28
|
@name = options['name']
|
10
29
|
fail 'database name not specified' unless @name
|
11
|
-
|
12
|
-
|
30
|
+
@host = options['host']
|
31
|
+
@port = options['port']
|
13
32
|
end
|
14
33
|
|
15
34
|
def dump
|
16
|
-
@dump ||= PipeIO.popen("bash -c '#{pipefail} #{
|
35
|
+
@dump ||= PipeIO.popen("bash -c '#{pipefail} #{command} | gzip'")
|
36
|
+
end
|
37
|
+
|
38
|
+
def filename
|
39
|
+
@name + '/' + file
|
17
40
|
end
|
18
41
|
|
19
42
|
def success?
|
@@ -22,31 +45,32 @@ module Stratocumulus
|
|
22
45
|
$CHILD_STATUS.success?
|
23
46
|
end
|
24
47
|
|
25
|
-
def
|
26
|
-
|
48
|
+
def dependencies
|
49
|
+
['gzip']
|
27
50
|
end
|
28
51
|
|
29
52
|
private
|
30
53
|
|
31
|
-
def pipefail
|
32
|
-
'set -o pipefail;'
|
33
|
-
end
|
34
|
-
|
35
54
|
def check_dependencies
|
36
55
|
dependencies.each do |cmd|
|
37
56
|
fail "#{cmd} not available" unless system("which #{cmd} >/dev/null")
|
38
57
|
end
|
39
58
|
end
|
40
59
|
|
41
|
-
def
|
42
|
-
|
60
|
+
def file
|
61
|
+
@file ||= Time.now.utc.strftime("#{@name}.%Y%m%d%H%M#{suffix}")
|
62
|
+
end
|
63
|
+
|
64
|
+
def suffix
|
65
|
+
'.sql.gz'
|
66
|
+
end
|
67
|
+
|
68
|
+
def pipefail
|
69
|
+
'set -o pipefail;'
|
43
70
|
end
|
44
71
|
|
45
|
-
def
|
46
|
-
|
47
|
-
type = options['type']
|
48
|
-
fail "#{type} is not a supported database" unless type == 'mysql'
|
49
|
-
@backend = backend_class.new(options)
|
72
|
+
def socket?
|
73
|
+
!@host && !@port
|
50
74
|
end
|
51
75
|
end
|
52
76
|
end
|
@@ -1,44 +1,34 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
require 'stratocumulus/database'
|
2
3
|
|
3
4
|
module Stratocumulus
|
4
|
-
class Database
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def command
|
16
|
-
command = 'mysqldump '
|
17
|
-
command << '--single-transaction '
|
18
|
-
command << "-u#{@username} "
|
19
|
-
command << "-h#{host} " unless socket?
|
20
|
-
command << "-P#{port} " unless socket?
|
21
|
-
command << "-p#{@password} " if @password
|
22
|
-
command << @name
|
23
|
-
end
|
5
|
+
class MySQL < Database
|
6
|
+
def command
|
7
|
+
command = 'mysqldump '
|
8
|
+
command << '--single-transaction '
|
9
|
+
command << "-u#{username} "
|
10
|
+
command << "-h#{host} " unless socket?
|
11
|
+
command << "-P#{port} " unless socket?
|
12
|
+
command << "-p#{@password} " if @password
|
13
|
+
command << @name
|
14
|
+
end
|
24
15
|
|
25
|
-
|
26
|
-
|
27
|
-
|
16
|
+
def dependencies
|
17
|
+
super + ['mysqldump']
|
18
|
+
end
|
28
19
|
|
29
|
-
|
20
|
+
private
|
30
21
|
|
31
|
-
|
32
|
-
|
33
|
-
|
22
|
+
def username
|
23
|
+
@username || 'root'
|
24
|
+
end
|
34
25
|
|
35
|
-
|
36
|
-
|
37
|
-
|
26
|
+
def host
|
27
|
+
@host || 'localhost'
|
28
|
+
end
|
38
29
|
|
39
|
-
|
40
|
-
|
41
|
-
end
|
30
|
+
def port
|
31
|
+
@port || 3306
|
42
32
|
end
|
43
33
|
end
|
44
34
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'stratocumulus/database'
|
3
|
+
|
4
|
+
module Stratocumulus
|
5
|
+
class PostgreSQL < Database
|
6
|
+
def command
|
7
|
+
command = ''
|
8
|
+
command << %Q(PGPASSWORD="#{@password}" ) if @password
|
9
|
+
command << 'pg_dump '
|
10
|
+
command << "-U#{username} "
|
11
|
+
command << "-h#{host} " unless socket?
|
12
|
+
command << "-p#{port} " unless socket?
|
13
|
+
command << @name
|
14
|
+
end
|
15
|
+
|
16
|
+
def dependencies
|
17
|
+
super + ['pg_dump']
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def username
|
23
|
+
@username || 'postgres'
|
24
|
+
end
|
25
|
+
|
26
|
+
def host
|
27
|
+
@host || 'localhost'
|
28
|
+
end
|
29
|
+
|
30
|
+
def port
|
31
|
+
@port || 5432
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'stratocumulus/database'
|
3
|
+
require 'tmpdir'
|
4
|
+
|
5
|
+
module Stratocumulus
|
6
|
+
class RethinkDB < Database
|
7
|
+
def dump
|
8
|
+
`#{command}`
|
9
|
+
@success = $CHILD_STATUS.success?
|
10
|
+
File.open(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def command
|
14
|
+
command = 'rethinkdb dump '
|
15
|
+
command << "-c #{host}:#{port} " unless socket?
|
16
|
+
command << "-f #{path} "
|
17
|
+
command << "-e #{@name}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def success?
|
21
|
+
File.delete(path)
|
22
|
+
@success
|
23
|
+
end
|
24
|
+
|
25
|
+
def dependencies
|
26
|
+
['rethinkdb-dump']
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def path
|
32
|
+
Dir.tmpdir + '/' + file
|
33
|
+
end
|
34
|
+
|
35
|
+
def suffix
|
36
|
+
'.tar.gz'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/stratocumulus/runner.rb
CHANGED
@@ -1,30 +1,95 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require 'spec_helper'
|
3
|
+
require 'zlib'
|
4
|
+
require 'rubygems/package'
|
3
5
|
|
4
6
|
describe Stratocumulus::Database do
|
5
7
|
|
6
8
|
subject do
|
7
|
-
described_class.
|
9
|
+
described_class.build(config)
|
8
10
|
end
|
9
11
|
|
10
12
|
let('config') do
|
11
13
|
{
|
12
14
|
'name' => 'stratocumulus_test',
|
13
|
-
'type' =>
|
15
|
+
'type' => type
|
14
16
|
}
|
15
17
|
end
|
16
18
|
|
19
|
+
let(:dump) { Zlib::GzipReader.new(subject.dump).read }
|
20
|
+
|
17
21
|
describe '#dump' do
|
18
|
-
|
19
|
-
|
22
|
+
context 'MySQL' do
|
23
|
+
before do
|
24
|
+
`mysql -u root < spec/support/mysql.sql`
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:type) { 'mysql' }
|
28
|
+
|
29
|
+
it 'sucessfully dumps a gziped copy of the database' do
|
30
|
+
expect(dump).to include('CREATE TABLE `widgets`')
|
31
|
+
expect(dump).to include("INSERT INTO `widgets` VALUES (1,'Foo',3,1,2)")
|
32
|
+
expect(subject).to be_success
|
33
|
+
end
|
20
34
|
end
|
21
35
|
|
22
|
-
|
36
|
+
context 'PostgreSQL' do
|
37
|
+
before do
|
38
|
+
`psql -U postgres < spec/support/psql.sql`
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:type) { 'psql' }
|
42
|
+
|
43
|
+
it 'sucessfully dumps a gziped copy of the database' do
|
44
|
+
expect(dump).to include('CREATE TABLE widgets')
|
45
|
+
expect(dump).to include(
|
46
|
+
'COPY widgets (id, name, leavers, pivots, fulcrums) FROM stdin;'
|
47
|
+
)
|
48
|
+
expect(dump).to include('1 Foo 3 1 2')
|
49
|
+
expect(subject).to be_success
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'RethinkDB' do
|
54
|
+
before do
|
55
|
+
`rethinkdb restore --force spec/support/rethinkdb.tar.gz`
|
56
|
+
end
|
57
|
+
|
58
|
+
let(:type) { 'rethinkdb' }
|
59
|
+
|
60
|
+
let(:tarball) { Zlib::GzipReader.new(subject.dump) }
|
61
|
+
|
62
|
+
let(:dump) do
|
63
|
+
Gem::Package::TarReader.new(tarball).each do |tarfile|
|
64
|
+
next unless tarfile.full_name =~ /widgets.json/
|
65
|
+
return JSON.load(tarfile.read)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:expected_data) do
|
70
|
+
[
|
71
|
+
# rubocop:disable Style/LineLength
|
72
|
+
{ 'pivots' => 5, 'fulcrums' => 3, 'leavers' => 8, 'id' => '1c66308d-492e-48ee-bfc5-83de96a15236', 'name' => 'Plugh' },
|
73
|
+
{ 'pivots' => 2, 'fulcrums' => 3, 'leavers' => 1, 'id' => '224ba4b1-2184-4905-b81b-ac6368480f43', 'name' => 'Thud' },
|
74
|
+
{ 'pivots' => 2, 'fulcrums' => 3, 'leavers' => 1, 'id' => '25b68062-7e9f-4fca-92bf-672287961096', 'name' => 'Garply' },
|
75
|
+
{ 'pivots' => 1, 'fulcrums' => 2, 'leavers' => 3, 'id' => '380c2a12-62d8-4faa-968e-79b2919bf9ad', 'name' => 'Foo' },
|
76
|
+
{ 'pivots' => 5, 'fulcrums' => 4, 'leavers' => 8, 'id' => '46cd4d4a-2cca-4ae2-835e-19c94c53ce02', 'name' => 'Quux' },
|
77
|
+
{ 'pivots' => 2, 'fulcrums' => 7, 'leavers' => 8, 'id' => '63441f79-cfe4-4460-8fc7-bda802e93646', 'name' => 'Corge' },
|
78
|
+
{ 'pivots' => 3, 'fulcrums' => 4, 'leavers' => 7, 'id' => '7d83f209-b4b1-4463-9eec-42dbf9217a39', 'name' => 'Grault' },
|
79
|
+
{ 'pivots' => 5, 'fulcrums' => 6, 'leavers' => 4, 'id' => '8e1623c8-e28d-48f9-8d74-dc5bf459cff5', 'name' => 'Qux' },
|
80
|
+
{ 'pivots' => 2, 'fulcrums' => 0, 'leavers' => 2, 'id' => 'a59c4505-c078-4186-a1d5-b0120dba4aa1', 'name' => 'Bar' },
|
81
|
+
{ 'pivots' => 3, 'fulcrums' => 3, 'leavers' => 3, 'id' => 'b535529c-c822-4c08-af65-f3662d981046', 'name' => 'Xyzzy' },
|
82
|
+
{ 'pivots' => 0, 'fulcrums' => 0, 'leavers' => 0, 'id' => 'f47bcc2a-ae2a-4d5f-9bbf-df71c8d81a7a', 'name' => 'Waldo' },
|
83
|
+
{ 'pivots' => 6, 'fulcrums' => 4, 'leavers' => 5, 'id' => 'f6745b27-b0c8-40ca-a472-dbe45310ee19', 'name' => 'Baz' },
|
84
|
+
{ 'pivots' => 1, 'fulcrums' => 1, 'leavers' => 1, 'id' => 'f88a0196-faaa-4695-88f1-808555a68ffa', 'name' => 'Fred' }
|
85
|
+
# rubocop:enable Style/LineLength
|
86
|
+
]
|
87
|
+
end
|
23
88
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
89
|
+
it 'sucessfully dumps a gziped copy of the database' do
|
90
|
+
expect(dump).to eq expected_data
|
91
|
+
expect(subject).to be_success
|
92
|
+
end
|
28
93
|
end
|
29
94
|
end
|
30
95
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require 'simplecov'
|
3
3
|
require 'coveralls'
|
4
|
+
require 'stringio'
|
4
5
|
|
5
6
|
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
6
7
|
SimpleCov::Formatter::HTMLFormatter,
|
@@ -32,3 +33,13 @@ RSpec.configure do |config|
|
|
32
33
|
mocks.syntax = :expect
|
33
34
|
end
|
34
35
|
end
|
36
|
+
|
37
|
+
def capture_stderr
|
38
|
+
old_stderr = $stderr
|
39
|
+
fake_stderr = StringIO.new
|
40
|
+
$stderr = fake_stderr
|
41
|
+
yield
|
42
|
+
fake_stderr.string
|
43
|
+
ensure
|
44
|
+
$stdout = old_stderr
|
45
|
+
end
|
File without changes
|
@@ -0,0 +1,52 @@
|
|
1
|
+
DROP DATABASE stratocumulus_test;
|
2
|
+
CREATE DATABASE stratocumulus_test
|
3
|
+
WITH OWNER = postgres
|
4
|
+
ENCODING = 'UTF8'
|
5
|
+
TABLESPACE = pg_default
|
6
|
+
CONNECTION LIMIT = -1;
|
7
|
+
\c stratocumulus_test
|
8
|
+
SET statement_timeout = 0;
|
9
|
+
SET lock_timeout = 0;
|
10
|
+
SET client_encoding = 'UTF8';
|
11
|
+
SET standard_conforming_strings = on;
|
12
|
+
SET check_function_bodies = false;
|
13
|
+
SET client_min_messages = warning;
|
14
|
+
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
|
15
|
+
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
|
16
|
+
SET search_path = public, pg_catalog;
|
17
|
+
SET default_tablespace = '';
|
18
|
+
SET default_with_oids = false;
|
19
|
+
CREATE TABLE widgets (
|
20
|
+
id integer NOT NULL,
|
21
|
+
name text,
|
22
|
+
leavers integer,
|
23
|
+
pivots integer,
|
24
|
+
fulcrums integer
|
25
|
+
);
|
26
|
+
ALTER TABLE public.widgets OWNER TO postgres;
|
27
|
+
CREATE SEQUENCE widgets_id_seq
|
28
|
+
START WITH 1
|
29
|
+
INCREMENT BY 1
|
30
|
+
NO MINVALUE
|
31
|
+
NO MAXVALUE
|
32
|
+
CACHE 1;
|
33
|
+
ALTER TABLE public.widgets_id_seq OWNER TO postgres;
|
34
|
+
ALTER SEQUENCE widgets_id_seq OWNED BY widgets.id;
|
35
|
+
ALTER TABLE ONLY widgets ALTER COLUMN id SET DEFAULT nextval('widgets_id_seq'::regclass);
|
36
|
+
COPY widgets (id, name, leavers, pivots, fulcrums) FROM stdin;
|
37
|
+
1 Foo 3 1 2
|
38
|
+
2 Bar 2 2 0
|
39
|
+
3 Baz 5 6 4
|
40
|
+
4 Qux 4 5 6
|
41
|
+
5 Quux 8 5 4
|
42
|
+
6 Corge 8 2 7
|
43
|
+
7 Grault 7 3 4
|
44
|
+
8 Garply 1 2 3
|
45
|
+
9 Waldo 0 0 0
|
46
|
+
10 Fred 1 1 1
|
47
|
+
11 Xyzzy 3 3 3
|
48
|
+
12 Thud 1 2 3
|
49
|
+
\.
|
50
|
+
SELECT pg_catalog.setval('widgets_id_seq', 12, true);
|
51
|
+
ALTER TABLE ONLY widgets
|
52
|
+
ADD CONSTRAINT widgets_pkey PRIMARY KEY (id);
|
Binary file
|
data/spec/unit/database_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require 'spec_helper'
|
|
4
4
|
describe Stratocumulus::Database do
|
5
5
|
|
6
6
|
subject do
|
7
|
-
described_class.
|
7
|
+
described_class.build(config)
|
8
8
|
end
|
9
9
|
|
10
10
|
let(:config) { base_config }
|
@@ -12,19 +12,23 @@ describe Stratocumulus::Database do
|
|
12
12
|
let(:base_config) do
|
13
13
|
{
|
14
14
|
'name' => 'stratocumulus_test',
|
15
|
-
'type' =>
|
15
|
+
'type' => type
|
16
16
|
}
|
17
17
|
end
|
18
18
|
|
19
|
+
let(:type) { 'mysql' }
|
20
|
+
|
19
21
|
before do
|
20
|
-
allow_any_instance_of(
|
22
|
+
allow_any_instance_of(Stratocumulus::Database).to(
|
23
|
+
receive(:system).and_return(true)
|
24
|
+
)
|
21
25
|
end
|
22
26
|
|
23
27
|
describe '.new' do
|
24
|
-
context 'the database type is not
|
28
|
+
context 'the database type is not supported' do
|
25
29
|
let(:config) { base_config.merge('type' => 'nosqlioid') }
|
26
30
|
|
27
|
-
it 'thows an error unless the type is
|
31
|
+
it 'thows an error unless the type is supported' do
|
28
32
|
expect { subject }.to raise_error(
|
29
33
|
RuntimeError,
|
30
34
|
'nosqlioid is not a supported database'
|
@@ -32,18 +36,35 @@ describe Stratocumulus::Database do
|
|
32
36
|
end
|
33
37
|
end
|
34
38
|
|
35
|
-
|
36
|
-
|
37
|
-
|
39
|
+
context 'mysql' do
|
40
|
+
it 'throws an error if mysqldump is not installed' do
|
41
|
+
allow_any_instance_of(Stratocumulus::Database).to(
|
42
|
+
receive(:system).with(/which mysqldump/)
|
43
|
+
)
|
38
44
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
45
|
+
expect { subject }.to raise_error(
|
46
|
+
RuntimeError,
|
47
|
+
'mysqldump not available'
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'postgresql' do
|
53
|
+
let(:type) { 'psql' }
|
54
|
+
|
55
|
+
it 'throws an error if pg_dump is not installed' do
|
56
|
+
allow_any_instance_of(Stratocumulus::Database)
|
57
|
+
.to receive(:system).with(/which pg_dump/)
|
58
|
+
|
59
|
+
expect { subject }.to raise_error(
|
60
|
+
RuntimeError,
|
61
|
+
'pg_dump not available'
|
62
|
+
)
|
63
|
+
end
|
43
64
|
end
|
44
65
|
|
45
66
|
it 'throws an error if gzip is not installed' do
|
46
|
-
allow_any_instance_of(
|
67
|
+
allow_any_instance_of(Stratocumulus::Database)
|
47
68
|
.to receive(:system).with(/which gzip/)
|
48
69
|
|
49
70
|
expect { subject }.to raise_error(
|
@@ -68,133 +89,6 @@ describe Stratocumulus::Database do
|
|
68
89
|
end
|
69
90
|
end
|
70
91
|
|
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 with the port set' do
|
119
|
-
let(:config) { base_config.merge('port' => '3306') }
|
120
|
-
|
121
|
-
it 'uses localhost' do
|
122
|
-
expect(IO).to receive(:popen) do |command|
|
123
|
-
expect(command).to include(' -hlocalhost ')
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
context 'default' do
|
129
|
-
it 'uses the default socket' do
|
130
|
-
expect(IO).to receive(:popen) do |command|
|
131
|
-
expect(command).to_not include(' -hlocalhost ')
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
context 'setting the host' do
|
137
|
-
let(:config) { base_config.merge('host' => 'db.awesome-server.net') }
|
138
|
-
|
139
|
-
it 'uses the correct hostname' do
|
140
|
-
expect(IO).to receive(:popen) do |command|
|
141
|
-
expect(command).to include(' -hdb.awesome-server.net ')
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
describe 'port' do
|
148
|
-
context 'default with the host set' do
|
149
|
-
let(:config) { base_config.merge('host' => 'db.awesome-server.net') }
|
150
|
-
|
151
|
-
it 'uses 3306' do
|
152
|
-
expect(IO).to receive(:popen) do |command|
|
153
|
-
expect(command).to include(' -P3306 ')
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
context 'default' do
|
159
|
-
it 'uses the default socket' do
|
160
|
-
expect(IO).to receive(:popen) do |command|
|
161
|
-
expect(command).to_not include(' -P3306 ')
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
context 'setting the port' do
|
167
|
-
let(:config) { base_config.merge('port' => '4306') }
|
168
|
-
|
169
|
-
it 'uses the correct port' do
|
170
|
-
expect(IO).to receive(:popen) do |command|
|
171
|
-
expect(command).to include(' -P4306 ')
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
describe 'the dump command' do
|
178
|
-
it 'sets pipefail' do
|
179
|
-
expect(IO).to receive(:popen) do |command|
|
180
|
-
expect(command).to include("bash -c 'set -o pipefail;")
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
it 'uses mysqldump --single-transaction option to not lock tables' do
|
185
|
-
expect(IO).to receive(:popen) do |command|
|
186
|
-
expect(command).to include(' mysqldump --single-transaction ')
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
it 'pipes the output of mysql to gzip' do
|
191
|
-
expect(IO).to receive(:popen) do |command|
|
192
|
-
expect(command).to match(/.*mysqldump.*\| gzip/)
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
92
|
describe '#filename' do
|
199
93
|
it 'calculates a filename based on the name and timestamp' do
|
200
94
|
timestamp = Time.now.utc.strftime('%Y%m%d%H%M')
|
@@ -210,50 +104,32 @@ describe Stratocumulus::Database do
|
|
210
104
|
end
|
211
105
|
|
212
106
|
describe '#success?' do
|
213
|
-
|
214
|
-
|
215
|
-
{ 'name' => 'foo', 'type' => 'mysql' },
|
216
|
-
backend
|
217
|
-
)
|
218
|
-
end
|
219
|
-
|
220
|
-
context 'the backend fails' do
|
221
|
-
let(:backend) { FailingBackend }
|
107
|
+
describe 'the dump fails' do
|
108
|
+
subject { FailingDatabase.new('name' => 'test_database') }
|
222
109
|
|
223
110
|
it 'returns false' do
|
224
111
|
expect(subject).to_not be_success
|
225
112
|
end
|
226
|
-
|
227
113
|
end
|
228
114
|
|
229
|
-
|
230
|
-
|
115
|
+
describe 'the dump is sucessfull' do
|
116
|
+
subject { SucessDatabase.new('name' => 'test_database') }
|
231
117
|
|
232
|
-
it 'returns
|
118
|
+
it 'returns false' do
|
233
119
|
expect(subject).to be_success
|
234
120
|
end
|
235
|
-
|
236
|
-
end
|
237
|
-
|
238
|
-
class FakeBackend
|
239
|
-
def initialize(*)
|
240
|
-
end
|
241
|
-
|
242
|
-
def dependencies
|
243
|
-
[]
|
244
|
-
end
|
245
121
|
end
|
122
|
+
end
|
246
123
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
end
|
124
|
+
class SucessDatabase < described_class
|
125
|
+
def command
|
126
|
+
'echo boo'
|
251
127
|
end
|
128
|
+
end
|
252
129
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
end
|
130
|
+
class FailingDatabase < described_class
|
131
|
+
def command
|
132
|
+
'exit 127'
|
257
133
|
end
|
258
134
|
end
|
259
135
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Stratocumulus::MySQL do
|
5
|
+
subject do
|
6
|
+
described_class.new(config)
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:config) do
|
10
|
+
{ 'name' => 'stratocumulus_test' }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#dependencies' do
|
14
|
+
specify do
|
15
|
+
expect(subject.dependencies).to eq %w(gzip mysqldump)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#command' do
|
20
|
+
context 'default' do
|
21
|
+
it 'generates the dump command with sensible defaults' do
|
22
|
+
expect(subject.command).to eq(
|
23
|
+
'mysqldump --single-transaction -uroot stratocumulus_test'
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with the password set' do
|
29
|
+
let(:config) do
|
30
|
+
{
|
31
|
+
'name' => 'stratocumulus_test',
|
32
|
+
'password' => 'seecrit'
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'generates the dump command with a default host' do
|
37
|
+
expect(subject.command).to eq(
|
38
|
+
'mysqldump --single-transaction -uroot -pseecrit stratocumulus_test'
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with the port set' do
|
44
|
+
let(:config) do
|
45
|
+
{
|
46
|
+
'name' => 'stratocumulus_test',
|
47
|
+
'port' => 13_306
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'generates the dump command with a default host' do
|
52
|
+
expect(subject.command).to eq(
|
53
|
+
'mysqldump --single-transaction -uroot -hlocalhost -P13306 stratocumulus_test' # rubocop:disable Style/LineLength
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with the host set' do
|
59
|
+
let(:config) do
|
60
|
+
{
|
61
|
+
'name' => 'stratocumulus_test',
|
62
|
+
'host' => 'db.example.com'
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'generates the dump command with a default port' do
|
67
|
+
expect(subject.command).to eq(
|
68
|
+
'mysqldump --single-transaction -uroot -hdb.example.com -P3306 stratocumulus_test' # rubocop:disable Style/LineLength
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with the port and host set' do
|
74
|
+
let(:config) do
|
75
|
+
{
|
76
|
+
'name' => 'stratocumulus_test',
|
77
|
+
'port' => 33_306,
|
78
|
+
'host' => 'db.example.com'
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'generates the dump command with the port and host' do
|
83
|
+
expect(subject.command).to eq(
|
84
|
+
'mysqldump --single-transaction -uroot -hdb.example.com -P33306 stratocumulus_test' # rubocop:disable Style/LineLength
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'with the username set' do
|
90
|
+
let(:config) do
|
91
|
+
{
|
92
|
+
'name' => 'stratocumulus_test',
|
93
|
+
'username' => 'susan'
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'generates the dump command with the username' do
|
98
|
+
expect(subject.command).to eq(
|
99
|
+
'mysqldump --single-transaction -ususan stratocumulus_test'
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Stratocumulus::PostgreSQL do
|
5
|
+
subject do
|
6
|
+
described_class.new(config)
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:config) do
|
10
|
+
{ 'name' => 'stratocumulus_test' }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#dependencies' do
|
14
|
+
specify do
|
15
|
+
expect(subject.dependencies).to eq %w(gzip pg_dump)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#command' do
|
20
|
+
context 'default' do
|
21
|
+
it 'generates the dump command with sensible defaults' do
|
22
|
+
expect(subject.command).to eq(
|
23
|
+
'pg_dump -Upostgres stratocumulus_test'
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with the password set' do
|
29
|
+
let(:config) do
|
30
|
+
{
|
31
|
+
'name' => 'stratocumulus_test',
|
32
|
+
'password' => 'sekret'
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'sets the password env var' do
|
37
|
+
expect(subject.command).to eq(
|
38
|
+
'PGPASSWORD="sekret" pg_dump -Upostgres stratocumulus_test'
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with the port set' do
|
44
|
+
let(:config) do
|
45
|
+
{
|
46
|
+
'name' => 'stratocumulus_test',
|
47
|
+
'port' => 15_432
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'generates the dump command with a default host' do
|
52
|
+
expect(subject.command).to eq(
|
53
|
+
'pg_dump -Upostgres -hlocalhost -p15432 stratocumulus_test'
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with the host set' do
|
59
|
+
let(:config) do
|
60
|
+
{
|
61
|
+
'name' => 'stratocumulus_test',
|
62
|
+
'host' => 'db.example.com'
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'generates the dump command with a default port' do
|
67
|
+
expect(subject.command).to eq(
|
68
|
+
'pg_dump -Upostgres -hdb.example.com -p5432 stratocumulus_test'
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with the port and host set' do
|
74
|
+
let(:config) do
|
75
|
+
{
|
76
|
+
'name' => 'stratocumulus_test',
|
77
|
+
'port' => 15_432,
|
78
|
+
'host' => 'db.example.com'
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'generates the dump command with port and host set' do
|
83
|
+
expect(subject.command).to eq(
|
84
|
+
'pg_dump -Upostgres -hdb.example.com -p15432 stratocumulus_test'
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'with the username set' do
|
90
|
+
let(:config) do
|
91
|
+
{
|
92
|
+
'name' => 'stratocumulus_test',
|
93
|
+
'username' => 'susan'
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'generates the dump command with the username' do
|
98
|
+
expect(subject.command).to eq(
|
99
|
+
'pg_dump -Ususan stratocumulus_test'
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/spec/unit/storage_spec.rb
CHANGED
@@ -30,12 +30,18 @@ describe Stratocumulus::Storage do
|
|
30
30
|
)
|
31
31
|
end
|
32
32
|
|
33
|
+
let(:stderr) do
|
34
|
+
capture_stderr do
|
35
|
+
subject.upload(database)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
33
39
|
before do
|
34
40
|
allow(Fog::Storage).to receive(:new).and_return(connection)
|
35
41
|
end
|
36
42
|
|
37
43
|
after do
|
38
|
-
|
44
|
+
stderr
|
39
45
|
end
|
40
46
|
|
41
47
|
it 'uploads the dump to s3' do
|
@@ -122,6 +128,12 @@ describe Stratocumulus::Storage do
|
|
122
128
|
it 'does not create a expiry rule' do
|
123
129
|
expect(service).to_not receive(:put_bucket_lifecycle)
|
124
130
|
end
|
131
|
+
|
132
|
+
it 'logs the error to stderr' do
|
133
|
+
expect(stderr).to include(
|
134
|
+
'ERROR -- : there was an error generating foo.sql.gz'
|
135
|
+
)
|
136
|
+
end
|
125
137
|
end
|
126
138
|
|
127
139
|
context 'rules allready set on the bucket' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stratocumulus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-06-
|
12
|
+
date: 2014-06-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fog
|
@@ -162,17 +162,23 @@ files:
|
|
162
162
|
- lib/stratocumulus/database.rb
|
163
163
|
- lib/stratocumulus/database/mysql.rb
|
164
164
|
- lib/stratocumulus/database/pipe_io.rb
|
165
|
+
- lib/stratocumulus/database/postgresql.rb
|
166
|
+
- lib/stratocumulus/database/rethinkdb.rb
|
165
167
|
- lib/stratocumulus/retention.rb
|
166
168
|
- lib/stratocumulus/runner.rb
|
167
169
|
- lib/stratocumulus/storage.rb
|
168
170
|
- lib/stratocumulus/version.rb
|
169
171
|
- spec/intergration/database_spec.rb
|
170
172
|
- spec/spec_helper.rb
|
171
|
-
- spec/support/
|
173
|
+
- spec/support/mysql.sql
|
174
|
+
- spec/support/psql.sql
|
175
|
+
- spec/support/rethinkdb.tar.gz
|
172
176
|
- spec/support/test_config_file.yml
|
173
177
|
- spec/unit/cli_spec.rb
|
174
178
|
- spec/unit/database_spec.rb
|
179
|
+
- spec/unit/mysql_spec.rb
|
175
180
|
- spec/unit/pipe_io_spec.rb
|
181
|
+
- spec/unit/postgresql_spec.rb
|
176
182
|
- spec/unit/retention_spec.rb
|
177
183
|
- spec/unit/runner_spec.rb
|
178
184
|
- spec/unit/storage_spec.rb
|
@@ -192,7 +198,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
192
198
|
version: '0'
|
193
199
|
segments:
|
194
200
|
- 0
|
195
|
-
hash:
|
201
|
+
hash: 646644050683609050
|
196
202
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
203
|
none: false
|
198
204
|
requirements:
|
@@ -201,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
207
|
version: '0'
|
202
208
|
segments:
|
203
209
|
- 0
|
204
|
-
hash:
|
210
|
+
hash: 646644050683609050
|
205
211
|
requirements: []
|
206
212
|
rubyforge_project:
|
207
213
|
rubygems_version: 1.8.25
|
@@ -211,11 +217,15 @@ summary: Backup Databases to Cloud Storage
|
|
211
217
|
test_files:
|
212
218
|
- spec/intergration/database_spec.rb
|
213
219
|
- spec/spec_helper.rb
|
214
|
-
- spec/support/
|
220
|
+
- spec/support/mysql.sql
|
221
|
+
- spec/support/psql.sql
|
222
|
+
- spec/support/rethinkdb.tar.gz
|
215
223
|
- spec/support/test_config_file.yml
|
216
224
|
- spec/unit/cli_spec.rb
|
217
225
|
- spec/unit/database_spec.rb
|
226
|
+
- spec/unit/mysql_spec.rb
|
218
227
|
- spec/unit/pipe_io_spec.rb
|
228
|
+
- spec/unit/postgresql_spec.rb
|
219
229
|
- spec/unit/retention_spec.rb
|
220
230
|
- spec/unit/runner_spec.rb
|
221
231
|
- spec/unit/storage_spec.rb
|