stratocumulus 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 initialize(options = {}, backend_class = nil)
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
- setup_backend(backend_class, options)
12
- check_dependencies
30
+ @host = options['host']
31
+ @port = options['port']
13
32
  end
14
33
 
15
34
  def dump
16
- @dump ||= PipeIO.popen("bash -c '#{pipefail} #{@backend.command} | gzip'")
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 filename
26
- @filename ||= Time.now.utc.strftime("#{@name}/#{@name}.%Y%m%d%H%M.sql.gz")
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 dependencies
42
- ['gzip'] + @backend.dependencies
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 setup_backend(backend_class, options)
46
- backend_class ||= MySQL
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
- class MySQL
6
- def initialize(options = {})
7
- @username = options['username'] || 'root'
8
- @password = options['password']
9
- @name = options['name']
10
-
11
- @host = options['host']
12
- @port = options['port']
13
- end
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
- def dependencies
26
- ['mysqldump']
27
- end
16
+ def dependencies
17
+ super + ['mysqldump']
18
+ end
28
19
 
29
- private
20
+ private
30
21
 
31
- def host
32
- @host || 'localhost'
33
- end
22
+ def username
23
+ @username || 'root'
24
+ end
34
25
 
35
- def port
36
- @port || 3306
37
- end
26
+ def host
27
+ @host || 'localhost'
28
+ end
38
29
 
39
- def socket?
40
- !@host && !@port
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
@@ -8,7 +8,7 @@ module Stratocumulus
8
8
 
9
9
  def run
10
10
  @config['databases'].each do |database_config|
11
- database = Database.new(database_config)
11
+ database = Database.build(database_config)
12
12
  upload(database, database_config['storage'])
13
13
  end
14
14
  end
@@ -73,7 +73,7 @@ module Stratocumulus
73
73
  end
74
74
 
75
75
  def log
76
- Logger.new(STDERR)
76
+ Logger.new($stderr)
77
77
  end
78
78
  end
79
79
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: UTF-8
2
2
  module Stratocumulus
3
- VERSION = '0.0.4'
3
+ VERSION = '0.0.5'
4
4
  end
@@ -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.new(config)
9
+ described_class.build(config)
8
10
  end
9
11
 
10
12
  let('config') do
11
13
  {
12
14
  'name' => 'stratocumulus_test',
13
- 'type' => 'mysql'
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
- before do
19
- system('mysql -u root < spec/support/test.sql')
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
- let(:dump) { Zlib::GzipReader.new(subject.dump).read }
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
- it 'sucessfully 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
- expect(subject).to be_success
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
@@ -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);
@@ -4,7 +4,7 @@ require 'spec_helper'
4
4
  describe Stratocumulus::Database do
5
5
 
6
6
  subject do
7
- described_class.new(config)
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' => 'mysql'
15
+ 'type' => type
16
16
  }
17
17
  end
18
18
 
19
+ let(:type) { 'mysql' }
20
+
19
21
  before do
20
- allow_any_instance_of(described_class).to receive(:system).and_return(true)
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 mysql' do
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 mysql' do
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
- it 'throws an error if mysqldump is not installed' do
36
- allow_any_instance_of(described_class)
37
- .to receive(:system).with(/which mysqldump/)
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
- expect { subject }.to raise_error(
40
- RuntimeError,
41
- 'mysqldump not available'
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(described_class)
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
- subject do
214
- described_class.new(
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
- context 'the backend is sucessfull' do
230
- let(:backend) { SucessBackend }
115
+ describe 'the dump is sucessfull' do
116
+ subject { SucessDatabase.new('name' => 'test_database') }
231
117
 
232
- it 'returns true' do
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
- class FailingBackend < FakeBackend
248
- def command
249
- 'exit 127'
250
- end
124
+ class SucessDatabase < described_class
125
+ def command
126
+ 'echo boo'
251
127
  end
128
+ end
252
129
 
253
- class SucessBackend < FakeBackend
254
- def command
255
- 'exit 0'
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
@@ -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
- subject.upload(database)
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
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-18 00:00:00.000000000 Z
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/test.sql
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: 815129825214945472
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: 815129825214945472
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/test.sql
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