stratocumulus 0.0.2 → 0.0.3
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/lib/stratocumulus.rb +1 -0
- data/lib/stratocumulus/database.rb +17 -5
- data/lib/stratocumulus/retention.rb +38 -0
- data/lib/stratocumulus/runner.rb +1 -1
- data/lib/stratocumulus/storage.rb +29 -1
- data/lib/stratocumulus/version.rb +1 -1
- data/spec/spec_helper.rb +0 -1
- data/spec/support/test_config_file.yml +3 -0
- data/spec/unit/database_spec.rb +28 -2
- data/spec/unit/retention_spec.rb +110 -0
- data/spec/unit/runner_spec.rb +3 -2
- data/spec/unit/storage_spec.rb +98 -2
- metadata +7 -4
data/lib/stratocumulus.rb
CHANGED
@@ -7,8 +7,8 @@ module Stratocumulus
|
|
7
7
|
@password = options['password']
|
8
8
|
@name = options['name']
|
9
9
|
|
10
|
-
@host = options['host']
|
11
|
-
@port = options['port']
|
10
|
+
@host = options['host']
|
11
|
+
@port = options['port']
|
12
12
|
|
13
13
|
check_dependencies(options['type'])
|
14
14
|
end
|
@@ -18,7 +18,7 @@ module Stratocumulus
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def filename
|
21
|
-
"#{@name}/#{@name}
|
21
|
+
@filename ||= Time.now.utc.strftime("#{@name}/#{@name}.%Y%m%d%H%M.sql.gz")
|
22
22
|
end
|
23
23
|
|
24
24
|
private
|
@@ -27,12 +27,24 @@ module Stratocumulus
|
|
27
27
|
command = 'mysqldump '
|
28
28
|
command << '--single-transaction '
|
29
29
|
command << "-u#{@username} "
|
30
|
-
command << "-h#{
|
31
|
-
command << "-P#{
|
30
|
+
command << "-h#{host} " unless socket?
|
31
|
+
command << "-P#{port} " unless socket?
|
32
32
|
command << "-p#{@password} " if @password
|
33
33
|
command << @name
|
34
34
|
end
|
35
35
|
|
36
|
+
def host
|
37
|
+
@host || 'localhost'
|
38
|
+
end
|
39
|
+
|
40
|
+
def port
|
41
|
+
@port || 3306
|
42
|
+
end
|
43
|
+
|
44
|
+
def socket?
|
45
|
+
!@host && !@port
|
46
|
+
end
|
47
|
+
|
36
48
|
def pipefail
|
37
49
|
'set -o pipefail;'
|
38
50
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
module Stratocumulus
|
4
|
+
class Retention
|
5
|
+
def initialize(schedule)
|
6
|
+
@schedule = schedule
|
7
|
+
end
|
8
|
+
|
9
|
+
def rule(key)
|
10
|
+
return unless expires_in_days
|
11
|
+
|
12
|
+
{
|
13
|
+
'ID' => key,
|
14
|
+
'Prefix' => key,
|
15
|
+
'Enabled' => true,
|
16
|
+
'Days' => expires_in_days
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def upload_today?
|
21
|
+
!@schedule || relevent_period
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def expires_in_days
|
27
|
+
return unless @schedule
|
28
|
+
|
29
|
+
relevent_period * @schedule[relevent_period]
|
30
|
+
end
|
31
|
+
|
32
|
+
def relevent_period
|
33
|
+
@schedule.keys.sort.reverse.find do |period|
|
34
|
+
0 == Date.today.yday % period
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/stratocumulus/runner.rb
CHANGED
@@ -9,9 +9,12 @@ module Stratocumulus
|
|
9
9
|
@secret_access_key = options.fetch('secret_access_key')
|
10
10
|
@region = options['region']
|
11
11
|
@bucket = options.fetch('bucket')
|
12
|
+
@retention = Retention.new(options['retention'])
|
12
13
|
end
|
13
14
|
|
14
15
|
def upload(database)
|
16
|
+
return unless @retention.upload_today?
|
17
|
+
add_expiry_rule(database.filename)
|
15
18
|
files.create(
|
16
19
|
key: database.filename,
|
17
20
|
body: database.dump,
|
@@ -32,7 +35,32 @@ module Stratocumulus
|
|
32
35
|
end
|
33
36
|
|
34
37
|
def files
|
35
|
-
|
38
|
+
@files ||= directories.get(@bucket).files
|
39
|
+
end
|
40
|
+
|
41
|
+
def directories
|
42
|
+
connection.directories
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_expiry_rule(key)
|
46
|
+
new_rule = @retention.rule(key)
|
47
|
+
return unless new_rule
|
48
|
+
directories.service.put_bucket_lifecycle(
|
49
|
+
@bucket,
|
50
|
+
'Rules' => current_rules << new_rule
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
def current_rules
|
55
|
+
existing_rules.select do |rule|
|
56
|
+
files.find { |file| file.key == rule['ID'] }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def existing_rules
|
61
|
+
directories.service.get_bucket_lifecycle(@bucket).data[:body]['Rules']
|
62
|
+
rescue Excon::Errors::NotFound
|
63
|
+
[]
|
36
64
|
end
|
37
65
|
end
|
38
66
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/unit/database_spec.rb
CHANGED
@@ -115,7 +115,9 @@ describe Stratocumulus::Database do
|
|
115
115
|
end
|
116
116
|
|
117
117
|
describe 'host' do
|
118
|
-
context 'default' do
|
118
|
+
context 'default with the port set' do
|
119
|
+
let(:config) { base_config.merge('port' => '3306') }
|
120
|
+
|
119
121
|
it 'uses localhost' do
|
120
122
|
expect(IO).to receive(:popen) do |command|
|
121
123
|
expect(command).to include(' -hlocalhost ')
|
@@ -123,6 +125,14 @@ describe Stratocumulus::Database do
|
|
123
125
|
end
|
124
126
|
end
|
125
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
|
+
|
126
136
|
context 'setting the host' do
|
127
137
|
let(:config) { base_config.merge('host' => 'db.awesome-server.net') }
|
128
138
|
|
@@ -135,7 +145,9 @@ describe Stratocumulus::Database do
|
|
135
145
|
end
|
136
146
|
|
137
147
|
describe 'port' do
|
138
|
-
context 'default' do
|
148
|
+
context 'default with the host set' do
|
149
|
+
let(:config) { base_config.merge('host' => 'db.awesome-server.net') }
|
150
|
+
|
139
151
|
it 'uses 3306' do
|
140
152
|
expect(IO).to receive(:popen) do |command|
|
141
153
|
expect(command).to include(' -P3306 ')
|
@@ -143,6 +155,14 @@ describe Stratocumulus::Database do
|
|
143
155
|
end
|
144
156
|
end
|
145
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
|
+
|
146
166
|
context 'setting the port' do
|
147
167
|
let(:config) { base_config.merge('port' => '4306') }
|
148
168
|
|
@@ -181,5 +201,11 @@ describe Stratocumulus::Database do
|
|
181
201
|
filename = "stratocumulus_test/stratocumulus_test.#{timestamp}.sql.gz"
|
182
202
|
expect(subject.filename).to eq filename
|
183
203
|
end
|
204
|
+
|
205
|
+
it 'stays the same for an instance even if time moves on' do
|
206
|
+
filename = subject.filename
|
207
|
+
allow(Time).to receive(:now).and_return(Time.now + 60 * 60 * 26)
|
208
|
+
expect(subject.filename).to eq filename
|
209
|
+
end
|
184
210
|
end
|
185
211
|
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe Stratocumulus::Retention do
|
5
|
+
subject { described_class.new(schedule) }
|
6
|
+
|
7
|
+
let(:schedule) do
|
8
|
+
{
|
9
|
+
1 => 30,
|
10
|
+
7 => 8,
|
11
|
+
30 => 12
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:keep_for_a_month) do
|
16
|
+
(1..365).to_a - keep_for_two_months - keep_for_a_year
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:keep_for_two_months) do
|
20
|
+
(1..56).map { |i| i * 7 } - keep_for_a_year
|
21
|
+
end
|
22
|
+
|
23
|
+
let(:keep_for_a_year) do
|
24
|
+
(1..12).map { |i| i * 30 }
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:key) { 'testdb/testdbTIMESTAMP.sql.gz' }
|
28
|
+
|
29
|
+
let(:expires_in_days) { subject.rule(key)['Days'] }
|
30
|
+
|
31
|
+
describe '#rule' do
|
32
|
+
|
33
|
+
it 'returns a rule with the key and an expiry' do
|
34
|
+
stub_yday(30)
|
35
|
+
expect(subject.rule(key)).to eq(
|
36
|
+
'Days' => 360,
|
37
|
+
'Enabled' => true,
|
38
|
+
'ID' => key,
|
39
|
+
'Prefix' => key
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'it keeps a daily backup for a month' do
|
44
|
+
keep_for_a_month.each do |day|
|
45
|
+
stub_yday(day)
|
46
|
+
expect(expires_in_days).to eq 30
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'keeps a weekly backup for 8 weeks' do
|
51
|
+
keep_for_two_months.each do |day|
|
52
|
+
stub_yday(day)
|
53
|
+
expect(expires_in_days).to eq 56
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'keeps a monthly backup for 12 months' do
|
58
|
+
keep_for_a_year.each do |day|
|
59
|
+
stub_yday(day)
|
60
|
+
expect(expires_in_days).to eq 360
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'a non daily schedule' do
|
65
|
+
let(:schedule) do
|
66
|
+
{ 14 => 2 }
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:days_to_keep) do
|
70
|
+
(1..28).map { |i| i * 14 }
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:days_not_to_keep) do
|
74
|
+
(1..365).to_a - days_to_keep
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'keeps fortightly backups for 2 fortnights' do
|
78
|
+
days_to_keep.each do |day|
|
79
|
+
stub_yday(day)
|
80
|
+
expect(subject.upload_today?).to be_truthy
|
81
|
+
expect(expires_in_days).to eq 28
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'keeps nothing on the other days' do
|
86
|
+
days_not_to_keep.each do |day|
|
87
|
+
stub_yday(day)
|
88
|
+
expect(subject.upload_today?).to be_falsy
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'with no schedule' do
|
94
|
+
let(:schedule) { nil }
|
95
|
+
|
96
|
+
it 'runs the backup' do
|
97
|
+
expect(subject.upload_today?).to eq true
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'keeps the backup forever' do
|
101
|
+
expect(subject.rule(key)).to be_nil
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def stub_yday(day)
|
106
|
+
day = double(yday: day)
|
107
|
+
allow(Date).to receive(:today).and_return(day)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/spec/unit/runner_spec.rb
CHANGED
@@ -16,11 +16,12 @@ describe Stratocumulus::Runner do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'passes the correct config to Storage' do
|
19
|
-
expect(Stratocumulus::Storage).to receive(:new).
|
19
|
+
expect(Stratocumulus::Storage).to receive(:new).twice.with(
|
20
20
|
'access_key_id' => 'I_AM_THE_KEY_ID',
|
21
21
|
'secret_access_key' => 'IamTHESekret',
|
22
22
|
'bucket' => 'stratocumulus-test',
|
23
|
-
'region' => 'eu-west1'
|
23
|
+
'region' => 'eu-west1',
|
24
|
+
'retention' => { 1 => 30, 30 => 12 }
|
24
25
|
).and_return(storage)
|
25
26
|
end
|
26
27
|
|
data/spec/unit/storage_spec.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Stratocumulus::Storage do
|
5
|
-
let(:
|
5
|
+
let(:base_config) do
|
6
6
|
{
|
7
7
|
'access_key_id' => 'IM_A_ID',
|
8
8
|
'secret_access_key' => 'IM_A_SEKRET_KEY',
|
@@ -11,6 +11,8 @@ describe Stratocumulus::Storage do
|
|
11
11
|
}
|
12
12
|
end
|
13
13
|
|
14
|
+
let(:config) { base_config }
|
15
|
+
|
14
16
|
subject { described_class.new(config) }
|
15
17
|
|
16
18
|
describe '#upload' do
|
@@ -54,6 +56,100 @@ describe Stratocumulus::Storage do
|
|
54
56
|
).and_return(connection)
|
55
57
|
end
|
56
58
|
end
|
57
|
-
end
|
58
59
|
|
60
|
+
context 'with a schedule' do
|
61
|
+
let(:config) do
|
62
|
+
base_config.merge(
|
63
|
+
'retention' => {
|
64
|
+
1 => 30
|
65
|
+
}
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:directories) do
|
70
|
+
double(:fog_dirs, get: double(files: files), service: service)
|
71
|
+
end
|
72
|
+
|
73
|
+
let(:service) { double(:fog_service) }
|
74
|
+
|
75
|
+
context 'no rules set on the bucket yet' do
|
76
|
+
before do
|
77
|
+
allow(service).to receive(:get_bucket_lifecycle)
|
78
|
+
.with('stratocumulus-test')
|
79
|
+
.and_raise(Excon::Errors::NotFound, '404 No rules set yet')
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'puts a rule for the uploaded file' do
|
83
|
+
expect(service).to receive(:put_bucket_lifecycle)
|
84
|
+
.with(
|
85
|
+
'stratocumulus-test',
|
86
|
+
'Rules' => [
|
87
|
+
{
|
88
|
+
'ID' => 'foo.sql.gz',
|
89
|
+
'Prefix' => 'foo.sql.gz',
|
90
|
+
'Enabled' => true,
|
91
|
+
'Days' => 30
|
92
|
+
}
|
93
|
+
]
|
94
|
+
)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'rules allready set on the bucket' do
|
99
|
+
let(:files) { [existing_file] }
|
100
|
+
let(:existing_file) { double(:fog_file, key: 'bar.sql.gz') }
|
101
|
+
let(:existing_rules) do
|
102
|
+
[
|
103
|
+
{
|
104
|
+
'ID' => 'bar.sql.gz',
|
105
|
+
'Prefix' => 'bar.sql.gz',
|
106
|
+
'Enabled' => true,
|
107
|
+
'Days' => 30
|
108
|
+
},
|
109
|
+
{
|
110
|
+
'ID' => 'baz.sql.gz',
|
111
|
+
'Prefix' => 'baz.sql.gz',
|
112
|
+
'Enabled' => true,
|
113
|
+
'Days' => 30
|
114
|
+
}
|
115
|
+
]
|
116
|
+
end
|
117
|
+
|
118
|
+
before do
|
119
|
+
allow(service).to receive(:get_bucket_lifecycle)
|
120
|
+
.with('stratocumulus-test')
|
121
|
+
.and_return(
|
122
|
+
double(
|
123
|
+
data: {
|
124
|
+
body: {
|
125
|
+
'Rules' => existing_rules
|
126
|
+
}
|
127
|
+
}
|
128
|
+
)
|
129
|
+
)
|
130
|
+
allow(files).to receive(:create).and_return(:true)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'adds the rule to the rules for existing files' do
|
134
|
+
expect(service).to receive(:put_bucket_lifecycle).with(
|
135
|
+
'stratocumulus-test',
|
136
|
+
'Rules' => [
|
137
|
+
{
|
138
|
+
'ID' => 'bar.sql.gz',
|
139
|
+
'Prefix' => 'bar.sql.gz',
|
140
|
+
'Enabled' => true,
|
141
|
+
'Days' => 30
|
142
|
+
},
|
143
|
+
{
|
144
|
+
'ID' => 'foo.sql.gz',
|
145
|
+
'Prefix' => 'foo.sql.gz',
|
146
|
+
'Enabled' => true,
|
147
|
+
'Days' => 30
|
148
|
+
}
|
149
|
+
]
|
150
|
+
)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
59
155
|
end
|
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.3
|
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-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fog
|
@@ -161,6 +161,7 @@ files:
|
|
161
161
|
- lib/stratocumulus/cli.rb
|
162
162
|
- lib/stratocumulus/database.rb
|
163
163
|
- lib/stratocumulus/ext/io.rb
|
164
|
+
- lib/stratocumulus/retention.rb
|
164
165
|
- lib/stratocumulus/runner.rb
|
165
166
|
- lib/stratocumulus/storage.rb
|
166
167
|
- lib/stratocumulus/version.rb
|
@@ -171,6 +172,7 @@ files:
|
|
171
172
|
- spec/support/test_config_file.yml
|
172
173
|
- spec/unit/cli_spec.rb
|
173
174
|
- spec/unit/database_spec.rb
|
175
|
+
- spec/unit/retention_spec.rb
|
174
176
|
- spec/unit/runner_spec.rb
|
175
177
|
- spec/unit/storage_spec.rb
|
176
178
|
- stratocumulus.gemspec
|
@@ -189,7 +191,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
189
191
|
version: '0'
|
190
192
|
segments:
|
191
193
|
- 0
|
192
|
-
hash: -
|
194
|
+
hash: -4542051147236532735
|
193
195
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
196
|
none: false
|
195
197
|
requirements:
|
@@ -198,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
200
|
version: '0'
|
199
201
|
segments:
|
200
202
|
- 0
|
201
|
-
hash: -
|
203
|
+
hash: -4542051147236532735
|
202
204
|
requirements: []
|
203
205
|
rubyforge_project:
|
204
206
|
rubygems_version: 1.8.25
|
@@ -213,5 +215,6 @@ test_files:
|
|
213
215
|
- spec/support/test_config_file.yml
|
214
216
|
- spec/unit/cli_spec.rb
|
215
217
|
- spec/unit/database_spec.rb
|
218
|
+
- spec/unit/retention_spec.rb
|
216
219
|
- spec/unit/runner_spec.rb
|
217
220
|
- spec/unit/storage_spec.rb
|