rds-rotate-db-snapshots 0.5.1 → 0.5.2
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +33 -13
- data/.rubocop.yml +93 -0
- data/.rubocop_todo.yml +127 -0
- data/Gemfile +3 -2
- data/README.md +6 -1
- data/Rakefile +4 -4
- data/VERSION +1 -1
- data/bin/rds-rotate-db-snapshots +16 -19
- data/lib/rds_rotate_db_snapshots/action_wrappers.rb +3 -3
- data/lib/rds_rotate_db_snapshots/actions.rb +71 -64
- data/lib/rds_rotate_db_snapshots/options_parser/options.rb +80 -0
- data/lib/rds_rotate_db_snapshots/options_parser.rb +23 -75
- data/lib/rds_rotate_db_snapshots.rb +6 -5
- data/rds-rotate-db-snapshots.gemspec +15 -20
- data/spec/helper.rb +1 -1
- data/spec/lib/rds_rotate_db_snapshots/action_wrappers_spec.rb +3 -3
- data/spec/lib/rds_rotate_db_snapshots/actions_spec.rb +80 -60
- data/spec/lib/rds_rotate_db_snapshots/options_parser_spec.rb +6 -9
- data/spec/lib/rds_rotate_db_snapshots/rds_client_spec.rb +11 -9
- data/spec/lib/rds_rotate_db_snapshots_spec.rb +6 -5
- metadata +35 -4
@@ -0,0 +1,80 @@
|
|
1
|
+
class RdsRotateDbSnapshots
|
2
|
+
class OptionsParser
|
3
|
+
module Options
|
4
|
+
def split_tag(hash, value)
|
5
|
+
value.split(',').each do |pair|
|
6
|
+
tag, value = pair.split('=', 2)
|
7
|
+
raise InvalidArgument, "invalid tag=value format" if value.nil?
|
8
|
+
|
9
|
+
hash[tag] = value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def banner_opts(opt)
|
14
|
+
opt.banner = "Usage: #{script_name} [options] <db_indentifier>\nUsage: #{script_name} " \
|
15
|
+
"--by-tags <tag=value,...> [other options]"
|
16
|
+
opt.separator ""
|
17
|
+
end
|
18
|
+
|
19
|
+
def aws_opts(opt)
|
20
|
+
opt.on("--aws-access-key ACCESS_KEY", "AWS Access Key") do |v|
|
21
|
+
@options[:aws_access_key] = v
|
22
|
+
end
|
23
|
+
|
24
|
+
opt.on("--aws-secret-access-key SECRET_KEY", "AWS Secret Access Key") do |v|
|
25
|
+
@options[:aws_secret_access_key] = v
|
26
|
+
end
|
27
|
+
|
28
|
+
opt.on("--aws-region REGION", "AWS Region") do |v|
|
29
|
+
@options[:aws_region] = v
|
30
|
+
end
|
31
|
+
|
32
|
+
opt.on("--aws-session-token SESSION_TOKEN", "AWS session token") do |v|
|
33
|
+
@options[:aws_session_token] = v
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def not_supported_opts(opt)
|
38
|
+
opt.on("--by-tags TAG=VALUE,TAG=VALUE",
|
39
|
+
"Instead of rotating specific snapshots, rotate over all the snapshots having the intersection of all " \
|
40
|
+
"given TAG=VALUE pairs.") do |_v|
|
41
|
+
@options[:by_tags] = {}
|
42
|
+
raise NotImplementedError,
|
43
|
+
'Hey! It\'s not implemented in RDS yet. Who knows, maybe they will add Tagging in RDS later.'
|
44
|
+
# split_tag(@options[:by_tags], v)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def snapshot_create_opts(opt)
|
49
|
+
opt.on("--create-snapshot STRING", "Use this option if you want to create a snapshot") do |v|
|
50
|
+
@options[:create_snapshot] = v
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def time_period_opts(opt)
|
55
|
+
@time_periods.keys.sort { |a, b| @time_periods[a][:seconds] <=> @time_periods[b][:seconds] }.each do |period|
|
56
|
+
opt.on("--keep-#{period} NUMBER", Integer, "Number of #{period} snapshots to keep") do |v|
|
57
|
+
@time_periods[period][:keep] = v
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
opt.on("--keep-last", "Keep the most recent snapshot, regardless of time-based policy") do |_v|
|
62
|
+
@options[:keep_last] = true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def extra_opts(opt)
|
67
|
+
opt.on("--pattern STRING", "Snapshots without this string in the description will be ignored") do |v|
|
68
|
+
@options[:pattern] = v
|
69
|
+
end
|
70
|
+
opt.on("--backoff-limit LIMIT",
|
71
|
+
"Backoff and retry when hitting RDS Error exceptions no more than this many times. Default is 15") do |v|
|
72
|
+
@options[:backoff_limit] = v
|
73
|
+
end
|
74
|
+
opt.on("--dry-run", "Shows what would happen without doing anything") do |_v|
|
75
|
+
@options[:dry_run] = true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -1,31 +1,33 @@
|
|
1
1
|
require 'optparse'
|
2
|
-
|
2
|
+
require_relative 'options_parser/options'
|
3
3
|
class RdsRotateDbSnapshots
|
4
4
|
class OptionsParser
|
5
5
|
class NotImplementedError < StandardError; end
|
6
6
|
class InvalidArgument < StandardError; end
|
7
7
|
|
8
|
+
include RdsRotateDbSnapshots::OptionsParser::Options
|
9
|
+
|
8
10
|
attr_reader :options, :script_name, :time_periods
|
9
11
|
|
10
12
|
def initialize(script_name: nil, cli: false)
|
11
13
|
@script_name = script_name
|
12
14
|
@options = {
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
15
|
+
aws_access_key: ENV.fetch("AWS_ACCESS_KEY_ID", nil),
|
16
|
+
aws_secret_access_key: ENV.fetch("AWS_SECRET_ACCESS_KEY", nil),
|
17
|
+
aws_session_token: ENV.fetch("AWS_SESSION_TOKEN", nil),
|
18
|
+
aws_region: ENV.fetch("AWS_REGION", nil),
|
19
|
+
pattern: nil,
|
20
|
+
by_tags: nil,
|
21
|
+
dry_run: false,
|
22
|
+
backoff_limit: 15,
|
23
|
+
create_snapshot: nil
|
22
24
|
}
|
23
25
|
@time_periods = {
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
26
|
+
hourly: { seconds: 60 * 60, format: '%Y-%m-%d-%H', keep: 0, keeping: {} },
|
27
|
+
daily: { seconds: 24 * 60 * 60, format: '%Y-%m-%d', keep: 0, keeping: {} },
|
28
|
+
weekly: { seconds: 7 * 24 * 60 * 60, format: '%Y-%W', keep: 0, keeping: {} },
|
29
|
+
monthly: { seconds: 30 * 24 * 60 * 60, format: '%Y-%m', keep: 0, keeping: {} },
|
30
|
+
yearly: { seconds: 12 * 30 * 24 * 60 * 60, format: '%Y', keep: 0, keeping: {} }
|
29
31
|
}
|
30
32
|
@cli = cli
|
31
33
|
init_cli_parser if cli?
|
@@ -44,66 +46,12 @@ class RdsRotateDbSnapshots
|
|
44
46
|
|
45
47
|
def init_cli_parser
|
46
48
|
@parser ||= OptionParser.new do |o|
|
47
|
-
o
|
48
|
-
o
|
49
|
-
|
50
|
-
o
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
o.on("--aws-secret-access-key SECRET_KEY", "AWS Secret Access Key") do |v|
|
55
|
-
@options[:aws_secret_access_key] = v
|
56
|
-
end
|
57
|
-
|
58
|
-
o.on("--aws-region REGION", "AWS Region") do |v|
|
59
|
-
@options[:aws_region] = v
|
60
|
-
end
|
61
|
-
|
62
|
-
o.on("--aws-session-token SESSION_TOKEN", "AWS session token") do |v|
|
63
|
-
@options[:aws_session_token] = v
|
64
|
-
end
|
65
|
-
|
66
|
-
o.on("--pattern STRING", "Snapshots without this string in the description will be ignored") do |v|
|
67
|
-
@options[:pattern] = v
|
68
|
-
end
|
69
|
-
|
70
|
-
o.on("--by-tags TAG=VALUE,TAG=VALUE", "Instead of rotating specific snapshots, rotate over all the snapshots having the intersection of all given TAG=VALUE pairs.") do |v|
|
71
|
-
@options[:by_tags] = {}
|
72
|
-
raise NotImplementedError, 'Hey! It\'s not implemented in RDS yet. Who knows, maybe they will add Tagging in RDS later.'
|
73
|
-
split_tag(@options[:by_tags],v)
|
74
|
-
end
|
75
|
-
|
76
|
-
o.on("--backoff-limit LIMIT", "Backoff and retry when hitting RDS Error exceptions no more than this many times. Default is 15") do |v|
|
77
|
-
@options[:backoff_limit] = v
|
78
|
-
end
|
79
|
-
|
80
|
-
o.on("--create-snapshot STRING", "Use this option if you want to create a snapshot") do |v|
|
81
|
-
@options[:create_snapshot] = v
|
82
|
-
end
|
83
|
-
|
84
|
-
@time_periods.keys.sort { |a, b| @time_periods[a][:seconds] <=> @time_periods[b][:seconds] }.each do |period|
|
85
|
-
o.on("--keep-#{period} NUMBER", Integer, "Number of #{period} snapshots to keep") do |v|
|
86
|
-
@time_periods[period][:keep] = v
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
o.on("--keep-last", "Keep the most recent snapshot, regardless of time-based policy") do |v|
|
91
|
-
@options[:keep_last] = true
|
92
|
-
end
|
93
|
-
|
94
|
-
o.on("--dry-run", "Shows what would happen without doing anything") do |v|
|
95
|
-
@options[:dry_run] = true
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def split_tag(hash,v)
|
101
|
-
v.split(',').each do |pair|
|
102
|
-
tag, value = pair.split('=',2)
|
103
|
-
if value.nil?
|
104
|
-
raise InvalidArgument, "invalid tag=value format"
|
105
|
-
end
|
106
|
-
hash[tag] = value
|
49
|
+
banner_opts o
|
50
|
+
aws_opts o
|
51
|
+
snapshot_create_opts o
|
52
|
+
time_period_opts o
|
53
|
+
extra_opts o
|
54
|
+
not_supported_opts o
|
107
55
|
end
|
108
56
|
end
|
109
57
|
end
|
@@ -9,6 +9,7 @@ class RdsRotateDbSnapshots
|
|
9
9
|
include RdsRotateDbSnapshots::Actions
|
10
10
|
|
11
11
|
attr_reader :options
|
12
|
+
|
12
13
|
with_backoff :get_db_snapshots, :create_snapshot, :rotate_em
|
13
14
|
|
14
15
|
def initialize(script_name: nil, cli: false, options: {})
|
@@ -22,7 +23,7 @@ class RdsRotateDbSnapshots
|
|
22
23
|
def rds_client
|
23
24
|
@rds_client ||= RdsRotateDbSnapshots::RdsClient.new(@options)
|
24
25
|
end
|
25
|
-
|
26
|
+
alias client rds_client
|
26
27
|
|
27
28
|
def reset_backoff
|
28
29
|
@backoff_counter = 0
|
@@ -43,14 +44,14 @@ class RdsRotateDbSnapshots
|
|
43
44
|
end
|
44
45
|
|
45
46
|
def backoff
|
46
|
-
@backoff_counter
|
47
|
-
|
47
|
+
@backoff_counter += 1
|
48
|
+
|
48
49
|
# TODO: re-work
|
49
|
-
if options && options[:backoff_limit]
|
50
|
+
if options && options[:backoff_limit].positive? && options[:backoff_limit] < @backoff_counter
|
50
51
|
puts "Too many backoff attempts. Sorry it didn't work out."
|
51
52
|
exit 2
|
52
53
|
end
|
53
|
-
|
54
|
+
|
54
55
|
naptime = rand(60) * @backoff_counter
|
55
56
|
puts "Backing off for #{naptime} seconds..."
|
56
57
|
sleep naptime
|
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: rds-rotate-db-snapshots 0.5.
|
5
|
+
# stub: rds-rotate-db-snapshots 0.5.2 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "rds-rotate-db-snapshots".freeze
|
9
|
-
s.version = "0.5.
|
9
|
+
s.version = "0.5.2"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Siarhei Kavaliou".freeze]
|
14
|
-
s.date = "2023-01-
|
14
|
+
s.date = "2023-01-11"
|
15
15
|
s.description = "Provides a simple way to rotate RDS DB snapshots with configurable retention periods.".freeze
|
16
16
|
s.email = "kovserg@gmail.com".freeze
|
17
17
|
s.executables = ["rds-rotate-db-snapshots".freeze]
|
@@ -25,6 +25,8 @@ Gem::Specification.new do |s|
|
|
25
25
|
".github/workflows/ci.yml",
|
26
26
|
".github/workflows/codeql.yml",
|
27
27
|
".rspec",
|
28
|
+
".rubocop.yml",
|
29
|
+
".rubocop_todo.yml",
|
28
30
|
"Gemfile",
|
29
31
|
"LICENSE.txt",
|
30
32
|
"README.md",
|
@@ -35,6 +37,7 @@ Gem::Specification.new do |s|
|
|
35
37
|
"lib/rds_rotate_db_snapshots/action_wrappers.rb",
|
36
38
|
"lib/rds_rotate_db_snapshots/actions.rb",
|
37
39
|
"lib/rds_rotate_db_snapshots/options_parser.rb",
|
40
|
+
"lib/rds_rotate_db_snapshots/options_parser/options.rb",
|
38
41
|
"lib/rds_rotate_db_snapshots/rds_client.rb",
|
39
42
|
"rds-rotate-db-snapshots.gemspec",
|
40
43
|
"spec/helper.rb",
|
@@ -46,25 +49,17 @@ Gem::Specification.new do |s|
|
|
46
49
|
]
|
47
50
|
s.homepage = "http://github.com/serg-kovalev/rds-rotate-db-snapshots".freeze
|
48
51
|
s.licenses = ["MIT".freeze]
|
49
|
-
s.rubygems_version = "3.
|
52
|
+
s.rubygems_version = "3.4.1".freeze
|
50
53
|
s.summary = "Amazon RDS DB snapshot rotator".freeze
|
51
54
|
|
52
|
-
|
53
|
-
s.specification_version = 4
|
54
|
-
end
|
55
|
+
s.specification_version = 4
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
s.add_dependency(%q<aws-sdk-rds>.freeze, ["~> 1"])
|
64
|
-
s.add_dependency(%q<rake>.freeze, [">= 0"])
|
65
|
-
s.add_dependency(%q<juwelier>.freeze, [">= 0"])
|
66
|
-
s.add_dependency(%q<pry>.freeze, [">= 0"])
|
67
|
-
s.add_dependency(%q<pry-byebug>.freeze, [">= 0"])
|
68
|
-
end
|
57
|
+
s.add_runtime_dependency(%q<aws-sdk-rds>.freeze, ["~> 1"])
|
58
|
+
s.add_development_dependency(%q<rake>.freeze, [">= 0"])
|
59
|
+
s.add_development_dependency(%q<juwelier>.freeze, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<pry>.freeze, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<pry-byebug>.freeze, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<rubocop>.freeze, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<rubocop-rspec>.freeze, [">= 0"])
|
69
64
|
end
|
70
65
|
|
data/spec/helper.rb
CHANGED
@@ -27,7 +27,7 @@ require "diff/lcs" # You need diff/lcs installed to run specs.
|
|
27
27
|
# require 'stringio'
|
28
28
|
require "webmock/rspec"
|
29
29
|
|
30
|
-
WebMock.disable_net_connect!(:
|
30
|
+
WebMock.disable_net_connect!(allow: "coveralls.io")
|
31
31
|
|
32
32
|
$0 = "rds_rotate_db_snapshots"
|
33
33
|
ARGV.clear
|
@@ -15,7 +15,7 @@ class TestClass
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def backoff
|
18
|
-
@backoff_counter
|
18
|
+
@backoff_counter += 1
|
19
19
|
|
20
20
|
raise StandardError, 'gave up' if @options[:backoff_limit] > 0 && @options[:backoff_limit] < @backoff_counter
|
21
21
|
end
|
@@ -33,13 +33,13 @@ describe RdsRotateDbSnapshots::ActionWrappers do
|
|
33
33
|
allow(subject).to receive(:test_method).and_raise(Aws::RDS::Errors::ExpiredToken.new(nil, 'token expired'))
|
34
34
|
expect(subject).not_to receive(:reset_backoff)
|
35
35
|
expect(subject).not_to receive(:backoff)
|
36
|
-
expect{subject.test_method}.to raise_error(Aws::RDS::Errors::ExpiredToken)
|
36
|
+
expect { subject.test_method }.to raise_error(Aws::RDS::Errors::ExpiredToken)
|
37
37
|
end
|
38
38
|
|
39
39
|
it "retries if the exception raised is Aws::RDS::Errors::ServiceError" do
|
40
40
|
expect(subject).to receive(:backoff).exactly(6).and_call_original
|
41
41
|
|
42
|
-
expect{ subject.test_method }.to raise_error(StandardError, 'gave up')
|
42
|
+
expect { subject.test_method }.to raise_error(StandardError, 'gave up')
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -1,42 +1,105 @@
|
|
1
1
|
require 'helper'
|
2
|
+
class Test
|
3
|
+
include RdsRotateDbSnapshots::Actions
|
4
|
+
|
5
|
+
attr_reader :options
|
6
|
+
|
7
|
+
def initialize(script_name: nil, options: {})
|
8
|
+
@script_name = script_name
|
9
|
+
@options = options
|
10
|
+
@backoff_counter = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def rds_client
|
14
|
+
@rds_client ||= RdsRotateDbSnapshots::RdsClient.new(@options)
|
15
|
+
end
|
16
|
+
alias client rds_client
|
17
|
+
|
18
|
+
def reset_backoff
|
19
|
+
@backoff_counter = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def time_periods
|
23
|
+
@options[:time_periods]
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def backoff
|
29
|
+
@backoff_counter += 1
|
30
|
+
end
|
31
|
+
end
|
2
32
|
|
3
|
-
|
4
|
-
let(:
|
5
|
-
let(:client) {
|
33
|
+
describe RdsRotateDbSnapshots::Actions do
|
34
|
+
let(:test_class) { Test.new(script_name: 'test', options: options) }
|
35
|
+
let(:client) { double("client") }
|
36
|
+
let(:time_periods) do
|
37
|
+
{
|
38
|
+
hourly: { seconds: 60 * 60, format: '%Y-%m-%d-%H', keep: 0, keeping: {} },
|
39
|
+
daily: { seconds: 24 * 60 * 60, format: '%Y-%m-%d', keep: 1, keeping: {} },
|
40
|
+
weekly: { seconds: 7 * 24 * 60 * 60, format: '%Y-%W', keep: 0, keeping: {} },
|
41
|
+
monthly: { seconds: 30 * 24 * 60 * 60, format: '%Y-%m', keep: 0, keeping: {} }
|
42
|
+
}
|
43
|
+
end
|
6
44
|
let(:options) do
|
7
|
-
{
|
45
|
+
{
|
8
46
|
aws_access_key: 'ACCESS_KEY',
|
9
47
|
aws_secret_access_key: 'SECRET_KEY',
|
10
48
|
aws_region: 'REGION',
|
11
49
|
pattern: 'test',
|
12
|
-
dry_run:
|
13
|
-
backoff_limit: 15
|
50
|
+
dry_run: false,
|
51
|
+
backoff_limit: 15,
|
52
|
+
time_periods: time_periods
|
14
53
|
}
|
15
54
|
end
|
16
55
|
let(:rds_snapshots) do
|
17
56
|
[
|
18
|
-
{ snapshot_create_time: Time.now, db_instance_identifier: 'test_db',
|
57
|
+
{ snapshot_create_time: Time.now - (24 * 3600), db_instance_identifier: 'test_db',
|
58
|
+
db_snapshot_identifier: 'test_snapshot' },
|
59
|
+
{ snapshot_create_time: Time.now, db_instance_identifier: 'test_db', db_snapshot_identifier: 'test_snapshot2' }
|
19
60
|
]
|
20
61
|
end
|
21
62
|
|
22
63
|
before do
|
23
|
-
allow(
|
24
|
-
allow(
|
25
|
-
allow(rds_client).to receive(:create_db_snapshot)
|
26
|
-
allow(rds_client).to receive(:delete_db_snapshot)
|
64
|
+
allow(client).to receive(:describe_db_snapshots).and_return(rds_snapshots)
|
65
|
+
allow(test_class).to receive(:client).and_return(client)
|
27
66
|
end
|
28
67
|
|
29
68
|
describe "#rotate_em" do
|
30
69
|
it "deletes the snapshots that are not part of the specified time periods" do
|
31
|
-
expect(
|
32
|
-
|
70
|
+
expect(client).to receive(:delete_db_snapshot)
|
71
|
+
test_class.rotate_em(rds_snapshots)
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when the snapshot is not matched with pattern' do
|
75
|
+
let(:rds_snapshots) do
|
76
|
+
[
|
77
|
+
{ snapshot_create_time: Time.now - (49 * 3600), db_instance_identifier: 'test_db',
|
78
|
+
db_snapshot_identifier: 'other_snapshot' },
|
79
|
+
{ snapshot_create_time: Time.now - (48 * 3600), db_instance_identifier: 'test_db',
|
80
|
+
db_snapshot_identifier: 'test_snapshot' },
|
81
|
+
{ snapshot_create_time: Time.now, db_instance_identifier: 'test_db',
|
82
|
+
db_snapshot_identifier: 'test_snapshot2' }
|
83
|
+
]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "deletes the snapshots that are matched with pattern" do
|
87
|
+
expect(client).to receive(:delete_db_snapshot).with(db_snapshot_identifier: 'test_snapshot')
|
88
|
+
test_class.rotate_em(rds_snapshots)
|
89
|
+
end
|
33
90
|
end
|
34
91
|
end
|
35
92
|
|
36
93
|
describe "#create_snapshot" do
|
37
94
|
it "creates a snapshot with the specified name" do
|
38
|
-
expect(
|
39
|
-
|
95
|
+
expect(client).to receive(:create_db_snapshot)
|
96
|
+
test_class.create_snapshot('test', ['test_db'])
|
97
|
+
end
|
98
|
+
|
99
|
+
context "when snaphost name is invalid" do
|
100
|
+
it "raises an error SystemExit" do
|
101
|
+
expect { test_class.create_snapshot('$#', ['test_db']) }.to raise_error(SystemExit)
|
102
|
+
end
|
40
103
|
end
|
41
104
|
end
|
42
105
|
|
@@ -45,52 +108,9 @@ RSpec.shared_examples 'rds_rotate_db_snapshots actions' do
|
|
45
108
|
|
46
109
|
it "returns the list of snapshots from the client" do
|
47
110
|
allow(snapshots).to receive(:[]).with(:marker).and_return(nil)
|
48
|
-
expect(
|
49
|
-
snapshots =
|
111
|
+
expect(client).to receive(:describe_db_snapshots).and_return(snapshots)
|
112
|
+
snapshots = test_class.get_db_snapshots(options)
|
50
113
|
expect(snapshots).to eq(rds_snapshots)
|
51
114
|
end
|
52
115
|
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
class Test
|
57
|
-
include RdsRotateDbSnapshots::Actions
|
58
|
-
|
59
|
-
attr_reader :options
|
60
|
-
|
61
|
-
def initialize(script_name: nil, cli: false, options: {})
|
62
|
-
@script_name = script_name
|
63
|
-
@options = options
|
64
|
-
@cli = cli
|
65
|
-
parse_options if cli?
|
66
|
-
@backoff_counter = 0
|
67
|
-
end
|
68
|
-
|
69
|
-
def reset_backoff
|
70
|
-
@backoff_counter = 0
|
71
|
-
end
|
72
|
-
|
73
|
-
def time_periods
|
74
|
-
@options[:time_periods] || {}
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
def cli?
|
80
|
-
!!@cli
|
81
|
-
end
|
82
|
-
|
83
|
-
def parse_options
|
84
|
-
@options = RdsRotateDbSnapshots::OptionsParser.new(script_name: @script_name, cli: @cli).parse!
|
85
|
-
end
|
86
|
-
|
87
|
-
def backoff
|
88
|
-
@backoff_counter = @backoff_counter + 1
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
describe RdsRotateDbSnapshots::Actions do
|
93
|
-
subject { Test.new }
|
94
|
-
|
95
|
-
it_behaves_like 'rds_rotate_db_snapshots actions'
|
96
116
|
end
|
@@ -1,19 +1,16 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
describe RdsRotateDbSnapshots::OptionsParser do
|
4
|
+
subject { described_class.new(script_name: script_name, cli: true).parse! }
|
5
|
+
|
4
6
|
let(:script_name) { "rds_rotate_snapshots.rb" }
|
5
|
-
subject { RdsRotateDbSnapshots::OptionsParser.new(script_name: script_name, cli: true).parse! }
|
6
7
|
|
7
8
|
describe "#parse!" do
|
8
9
|
before { ARGV.clear }
|
9
|
-
|
10
|
+
|
10
11
|
it "parses options correctly" do
|
11
|
-
ARGV.
|
12
|
-
|
13
|
-
"--aws-region", "REGION",
|
14
|
-
"--pattern", "PATTERN",
|
15
|
-
"--backoff-limit", "20",
|
16
|
-
"--create-snapshot", "snapshot"])
|
12
|
+
ARGV.push("--aws-access-key", "ACCESS_KEY", "--aws-secret-access-key", "SECRET_KEY", "--aws-region", "REGION",
|
13
|
+
"--pattern", "PATTERN", "--backoff-limit", "20", "--create-snapshot", "snapshot")
|
17
14
|
options = subject
|
18
15
|
|
19
16
|
expect(options[:aws_access_key]).to eq("ACCESS_KEY")
|
@@ -25,7 +22,7 @@ describe RdsRotateDbSnapshots::OptionsParser do
|
|
25
22
|
end
|
26
23
|
|
27
24
|
it "raises NotImplementedError when by-tags option is passed and it is not implemented" do
|
28
|
-
ARGV.
|
25
|
+
ARGV.push("--by-tags", "tag=value,tag2=value")
|
29
26
|
|
30
27
|
expect { subject }.to raise_error(RdsRotateDbSnapshots::OptionsParser::NotImplementedError)
|
31
28
|
end
|
@@ -1,17 +1,19 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
describe RdsRotateDbSnapshots::RdsClient do
|
4
|
-
let(:options)
|
5
|
-
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
9
|
-
|
10
|
-
|
4
|
+
let(:options) do
|
5
|
+
{
|
6
|
+
aws_access_key: "ACCESS_KEY",
|
7
|
+
aws_secret_access_key: "SECRET_KEY",
|
8
|
+
aws_session_token: "SESSION_TOKEN",
|
9
|
+
aws_region: "REGION"
|
10
|
+
}
|
11
|
+
end
|
12
|
+
let(:rds_client) { described_class.new(options) }
|
11
13
|
|
12
14
|
it 'configures the client with the correct credentials and region' do
|
13
|
-
expect(rds_client.instance_variable_get(:@client).config.credentials)
|
14
|
-
to have_attributes(access_key_id: "ACCESS_KEY", secret_access_key: "SECRET_KEY", session_token: "SESSION_TOKEN")
|
15
|
+
expect(rds_client.instance_variable_get(:@client).config.credentials)
|
16
|
+
.to have_attributes(access_key_id: "ACCESS_KEY", secret_access_key: "SECRET_KEY", session_token: "SESSION_TOKEN")
|
15
17
|
expect(rds_client.instance_variable_get(:@client).config.region).to eq("REGION")
|
16
18
|
end
|
17
19
|
|
@@ -5,6 +5,7 @@ describe RdsRotateDbSnapshots do
|
|
5
5
|
|
6
6
|
let(:script_name) { "test" }
|
7
7
|
let(:cli) { true }
|
8
|
+
|
8
9
|
before do
|
9
10
|
allow(Aws::RDS::Client).to receive(:new)
|
10
11
|
end
|
@@ -43,11 +44,11 @@ describe RdsRotateDbSnapshots do
|
|
43
44
|
describe "#time_periods" do
|
44
45
|
it "returns time periods" do
|
45
46
|
expect(subject.time_periods).to eq(
|
46
|
-
:
|
47
|
-
:
|
48
|
-
:
|
49
|
-
:
|
50
|
-
:
|
47
|
+
daily: { format: "%Y-%m-%d", keep: 0, keeping: {}, seconds: 86_400 },
|
48
|
+
hourly: { format: "%Y-%m-%d-%H", keep: 0, keeping: {}, seconds: 3600 },
|
49
|
+
monthly: { format: "%Y-%m", keep: 0, keeping: {}, seconds: 2_592_000 },
|
50
|
+
weekly: { format: "%Y-%W", keep: 0, keeping: {}, seconds: 604_800 },
|
51
|
+
yearly: { format: "%Y", keep: 0, keeping: {}, seconds: 31_104_000 }
|
51
52
|
)
|
52
53
|
end
|
53
54
|
end
|