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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca82ea61b9da51d2888c98f21fda30ce73850b265dcb01b75c2e99c0b84d597d
4
- data.tar.gz: 778fc695f23d0c713bedad76b2ce4893401c50d270df596c1324b52dcd0546b7
3
+ metadata.gz: 867b60671bdab535228d9730a2b7fbee3e1b9e44568f07215cc0c2eee6881a4b
4
+ data.tar.gz: f204ef40c89395f6fc652d049854fd04cf10759e915907c9b04172873b1a0772
5
5
  SHA512:
6
- metadata.gz: f8bca5be1bb1d4324ae674677f574707782f92cb9717ec66010a8686ceca450d0127017c109289738c487f25131f6cf355ad60d4a33d8c71d0b2c2278ea24b68
7
- data.tar.gz: b00fe71aab391fa82e64b52f1de28bfc68632f0ae5a7961b5e1358fb55a28d19f1d9dd44dbcd59e81ce06204b8d26f290c2c253e303386144b0525c75ff1c3ec
6
+ metadata.gz: 85ff15bc60ddf07b213671d9449005135428cb477b9c6bc51ee570d459ce238b4672dac8945e14ec5dc543fb8d538b52dad742bfd5e342c6901573bda390309e
7
+ data.tar.gz: 8b01afd618121cf7f6f8e237f81fadd782f056f6a1ab20eb1d9d7e469e0fb2e49c6872ed2a2fe7ecbfb008b418a0db2967ff281bfa358b0fafa372dea5f294ca
@@ -6,38 +6,58 @@ on:
6
6
  pull_request:
7
7
  branches: ["main"]
8
8
 
9
+ env:
10
+ RUBY_MAIN_VERSION: '3.2'
11
+
9
12
  jobs:
13
+ rubocop:
14
+ runs-on: ubuntu-20.04
15
+ steps:
16
+ - name: Checkout code
17
+ uses: actions/checkout@v3
18
+ - name: Install Ruby and gems
19
+ uses: ruby/setup-ruby@319066216501fbd5e2d568f14b7d68c19fb67a5d #v1.133.1
20
+ with:
21
+ bundler-cache: true
22
+ ruby-version: ${{ env.RUBY_MAIN_VERSION }}.0
23
+ - name: Install Bundler
24
+ run: gem install bundler
25
+ - name: Bundle Install
26
+ run: bundle install
27
+ - name: Rubocop
28
+ run: bundle exec rubocop
29
+
10
30
  test:
11
31
  runs-on: ubuntu-20.04
12
32
  strategy:
13
33
  matrix:
14
- ruby_version: [2.7, 3.0, 3.1]
34
+ ruby_version: ['2.7', '3.0', '3.1', '3.2']
15
35
  steps:
16
36
  - name: Checkout code
17
37
  uses: actions/checkout@v3
18
38
  - name: Install Ruby and gems
19
- uses: ruby/setup-ruby@03b78bdda287ae04217ee12e4b64996630a03542 #v1.131.0
39
+ uses: ruby/setup-ruby@319066216501fbd5e2d568f14b7d68c19fb67a5d #v1.133.1
20
40
  with:
21
41
  bundler-cache: true
22
42
  ruby-version: ${{ matrix.ruby_version }}
23
43
  - name: Install Bundler
24
44
  run: gem install bundler
45
+ - name: Setup Code Climate test-reporter
46
+ if: ${{ matrix.ruby_version == env.RUBY_MAIN_VERSION }}
47
+ run: |
48
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
49
+ chmod +x ./cc-test-reporter
50
+ ./cc-test-reporter before-build
25
51
  - name: Bundle Install
26
52
  run: bundle install
27
53
  - name: Test
28
54
  run: bundle exec rspec
55
+ - name: Publish Codeclimate Code Coverage
56
+ if: ${{ matrix.ruby_version == env.RUBY_MAIN_VERSION }}
57
+ run: |
58
+ ./cc-test-reporter after-build -r ${{secrets.CC_TEST_REPORTER_ID}}
29
59
  - name: Coveralls Parallel
60
+ if: ${{ matrix.ruby_version == env.RUBY_MAIN_VERSION }}
30
61
  uses: coverallsapp/github-action@master
31
62
  with:
32
63
  github-token: ${{ secrets.github_token }}
33
- flag-name: run-${{ matrix.ruby_version }}
34
- parallel: true
35
- finish:
36
- needs: test
37
- runs-on: ubuntu-latest
38
- steps:
39
- - name: Coveralls Finished
40
- uses: coverallsapp/github-action@master
41
- with:
42
- github-token: ${{ secrets.github_token }}
43
- parallel-finished: true
data/.rubocop.yml ADDED
@@ -0,0 +1,93 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ require:
4
+ - rubocop-rspec
5
+
6
+ AllCops:
7
+ NewCops: enable
8
+ TargetRubyVersion: 3.2.0
9
+ Exclude:
10
+ - 'bin/{bundle,rails,rake}'
11
+ - vendor/**/*
12
+
13
+ Layout/LineLength:
14
+ Max: 120
15
+
16
+ Bundler/OrderedGems:
17
+ Enabled: false
18
+
19
+ Lint/RescueException:
20
+ Enabled: true
21
+
22
+ Style/EmptyElse:
23
+ EnforcedStyle: empty
24
+
25
+ Style/FrozenStringLiteralComment:
26
+ Enabled: false
27
+
28
+ Style/RescueStandardError:
29
+ Enabled: false
30
+
31
+ Style/StringLiterals:
32
+ Enabled: false
33
+
34
+ Layout/SpaceInLambdaLiteral:
35
+ Enabled: false
36
+
37
+ Style/Lambda:
38
+ Enabled: false
39
+
40
+ Style/NumericLiteralPrefix:
41
+ Enabled: false
42
+
43
+ Style/EmptyMethod:
44
+ Enabled: false
45
+
46
+ Style/ParallelAssignment:
47
+ Enabled: false
48
+
49
+ Layout/EmptyLinesAroundExceptionHandlingKeywords:
50
+ Enabled: false
51
+
52
+ Layout/FirstHashElementIndentation:
53
+ Enabled: false
54
+
55
+ Layout/ParameterAlignment:
56
+ Enabled: false
57
+
58
+ Naming/RescuedExceptionsVariableName:
59
+ Enabled: false
60
+
61
+ Style/Documentation:
62
+ Enabled: false
63
+
64
+ Lint/DuplicateRegexpCharacterClassElement: # (new in 1.1)
65
+ Enabled: true
66
+ Lint/EmptyBlock: # (new in 1.1)
67
+ Enabled: true
68
+ Lint/NoReturnInBeginEndBlocks: # (new in 1.2)
69
+ Enabled: true
70
+ Lint/ToEnumArguments: # (new in 1.1)
71
+ Enabled: true
72
+ Lint/UnmodifiedReduceAccumulator: # (new in 1.1)
73
+ Enabled: true
74
+ Style/ArgumentsForwarding: # (new in 1.1)
75
+ Enabled: true
76
+ Style/CollectionCompact: # (new in 1.2)
77
+ Enabled: true
78
+ Style/DocumentDynamicEvalDefinition: # (new in 1.1)
79
+ Enabled: true
80
+ Style/NegatedIfElseCondition: # (new in 1.2)
81
+ Enabled: true
82
+ Style/SwapValues: # (new in 1.1)
83
+ Enabled: true
84
+
85
+ Metrics/BlockLength:
86
+ Exclude:
87
+ - 'spec/**/*' # Swagger specs require large blocks
88
+
89
+ Style/HashSyntax:
90
+ EnforcedShorthandSyntax: never
91
+
92
+ RSpec/MultipleMemoizedHelpers:
93
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,127 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2023-01-10 16:06:29 UTC using RuboCop version 1.43.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: Severity, Include.
11
+ # Include: **/*.gemspec
12
+ Gemspec/RequiredRubyVersion:
13
+ Exclude:
14
+ - 'rds-rotate-db-snapshots.gemspec'
15
+
16
+ # Offense count: 3
17
+ # This cop supports safe autocorrection (--autocorrect).
18
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns.
19
+ # URISchemes: http, https
20
+ Layout/LineLength:
21
+ Max: 146
22
+
23
+ # Offense count: 1
24
+ Lint/UnreachableCode:
25
+ Exclude:
26
+ - 'lib/rds_rotate_db_snapshots/options_parser.rb'
27
+
28
+ # Offense count: 4
29
+ Lint/UselessAssignment:
30
+ Exclude:
31
+ - 'bin/rds-rotate-db-snapshots'
32
+ - 'lib/rds_rotate_db_snapshots/actions.rb'
33
+
34
+ # Offense count: 5
35
+ # Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes.
36
+ Metrics/AbcSize:
37
+ Max: 56
38
+
39
+ # Offense count: 2
40
+ # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods, inherit_mode.
41
+ # AllowedMethods: refine
42
+ Metrics/BlockLength:
43
+ Max: 42
44
+
45
+ # Offense count: 1
46
+ # Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods.
47
+ Metrics/CyclomaticComplexity:
48
+ Max: 15
49
+
50
+ # Offense count: 6
51
+ # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
52
+ Metrics/MethodLength:
53
+ Max: 44
54
+
55
+ # Offense count: 1
56
+ # Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods.
57
+ Metrics/PerceivedComplexity:
58
+ Max: 16
59
+
60
+ # Offense count: 1
61
+ # Configuration parameters: EnforcedStyleForLeadingUnderscores.
62
+ # SupportedStylesForLeadingUnderscores: disallowed, required, optional
63
+ Naming/MemoizedInstanceVariableName:
64
+ Exclude:
65
+ - 'lib/rds_rotate_db_snapshots/options_parser.rb'
66
+
67
+ # Offense count: 1
68
+ # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
69
+ # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
70
+ Naming/MethodParameterName:
71
+ Exclude:
72
+ - 'lib/rds_rotate_db_snapshots/options_parser.rb'
73
+
74
+ # Offense count: 3
75
+ # Configuration parameters: CountAsOne.
76
+ RSpec/ExampleLength:
77
+ Max: 9
78
+
79
+ # Offense count: 10
80
+ # Configuration parameters: .
81
+ # SupportedStyles: have_received, receive
82
+ RSpec/MessageSpies:
83
+ EnforcedStyle: receive
84
+
85
+ # Offense count: 6
86
+ RSpec/MultipleExpectations:
87
+ Max: 6
88
+
89
+ # Offense count: 17
90
+ # Configuration parameters: EnforcedStyle, IgnoreSharedExamples.
91
+ # SupportedStyles: always, named_only
92
+ RSpec/NamedSubject:
93
+ Exclude:
94
+ - 'spec/lib/rds_rotate_db_snapshots/action_wrappers_spec.rb'
95
+ - 'spec/lib/rds_rotate_db_snapshots/options_parser_spec.rb'
96
+ - 'spec/lib/rds_rotate_db_snapshots_spec.rb'
97
+
98
+ # Offense count: 1
99
+ RSpec/StubbedMock:
100
+ Exclude:
101
+ - 'spec/lib/rds_rotate_db_snapshots/actions_spec.rb'
102
+
103
+ # Offense count: 6
104
+ RSpec/SubjectStub:
105
+ Exclude:
106
+ - 'spec/lib/rds_rotate_db_snapshots/action_wrappers_spec.rb'
107
+ - 'spec/lib/rds_rotate_db_snapshots/actions_spec.rb'
108
+ - 'spec/lib/rds_rotate_db_snapshots_spec.rb'
109
+
110
+ # Offense count: 1
111
+ # Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
112
+ RSpec/VerifiedDoubles:
113
+ Exclude:
114
+ - 'spec/lib/rds_rotate_db_snapshots/actions_spec.rb'
115
+
116
+ # Offense count: 1
117
+ # Configuration parameters: AllowedVariables.
118
+ Style/GlobalVars:
119
+ Exclude:
120
+ - 'spec/helper.rb'
121
+
122
+ # Offense count: 3
123
+ # This cop supports safe autocorrection (--autocorrect).
124
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns.
125
+ # URISchemes: http, https
126
+ Layout/LineLength:
127
+ Max: 146
data/Gemfile CHANGED
@@ -1,18 +1,19 @@
1
1
  source "https://rubygems.org"
2
2
 
3
3
  gem 'aws-sdk-rds', '~> 1'
4
- gem "rake"
5
4
 
6
5
  group :development do
6
+ gem "rake"
7
7
  gem 'juwelier'
8
8
  gem "pry"
9
9
  gem "pry-byebug"
10
+ gem "rubocop"
11
+ gem "rubocop-rspec"
10
12
  end
11
13
 
12
14
  group :test do
13
15
  gem "rspec", ">= 3.2"
14
16
  gem "rspec-mocks", ">= 3"
15
- gem "rubocop", "~> 0.50.0"
16
17
  gem "simplecov", ">= 0.13"
17
18
  gem 'simplecov-lcov', '~> 0.8.0'
18
19
  gem "webmock"
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  [<img src="https://badge.fury.io/rb/rds-rotate-db-snapshots.svg" alt="Gem
4
4
  Version" />](https://badge.fury.io/rb/rds-rotate-db-snapshots) [![CI](https://github.com/serg-kovalev/rds-rotate-db-snapshots/actions/workflows/ci.yml/badge.svg?query=branch%3Amain+event%3Apush)](https://github.com/serg-kovalev/rds-rotate-db-snapshots/actions/workflows/ci.yml?query=branch%3Amain+event%3Apush) [![CodeQL](https://github.com/serg-kovalev/rds-rotate-db-snapshots/actions/workflows/codeql.yml/badge.svg?query=branch%3Amain+event%3Apush)](https://github.com/serg-kovalev/rds-rotate-db-snapshots/actions/workflows/codeql.yml?query=branch%3Amain+event%3Apush)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/45bfe7dc4f21ac1403cb/maintainability)](https://codeclimate.com/github/serg-kovalev/rds-rotate-db-snapshots/maintainability)
6
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/45bfe7dc4f21ac1403cb/test_coverage)](https://codeclimate.com/github/serg-kovalev/rds-rotate-db-snapshots/test_coverage)
5
7
 
6
8
  Provides a simple way to rotate db snapshots in Amazon Relational Database
7
9
  Service (RDS).
@@ -9,6 +11,7 @@ Service (RDS).
9
11
  ## Tested on Rubies
10
12
 
11
13
  - 2.7
14
+ - 3.0
12
15
  - 3.1
13
16
  - 3.2
14
17
 
@@ -32,18 +35,20 @@ Add this script to CRON (let's say it will run this script every X hours) and it
32
35
  #/usr/bin/bash
33
36
  AWS_ACCESS_KEY='xxxxxxxxxxxxxxxxxxxx'
34
37
  AWS_SECRET_ACCESS_KEY='yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
38
+ AWS_SESSION_TOKEN='session_token_goes_here'
35
39
  AWS_REGION='eu-west-1'
36
40
  DESCRIPTION_PREFIX='automatic-backup-'
37
41
  RDS_ROTATOR=/here/is/the/path/to/rds-rotate-db-snapshots
38
42
  DB_NAME='db_name_here'
39
43
 
40
- $RDS_ROTATOR --aws-region $AWS_REGION --aws-access-key $AWS_ACCESS_KEY --aws-secret-access-key $AWS_SECRET_ACCESS_KEY --pattern $DESCRIPTION_PREFIX --keep-hourly 24 --keep-daily 7 --keep-weekly 4 --keep-monthly 1 --keep-yearly 0 --create-snapshot $DESCRIPTION_PREFIX$DB_NAME $DB_NAME
44
+ $RDS_ROTATOR --aws-region $AWS_REGION --aws-access-key $AWS_ACCESS_KEY --aws-secret-access-key $AWS_SECRET_ACCESS_KEY --aws-session-token $AWS_SESSION_TOKEN --pattern $DESCRIPTION_PREFIX --keep-hourly 24 --keep-daily 7 --keep-weekly 4 --keep-monthly 1 --keep-yearly 0 --create-snapshot $DESCRIPTION_PREFIX$DB_NAME $DB_NAME
41
45
  ```
42
46
 
43
47
  ## Options
44
48
 
45
49
  - `--aws-access-key ACCESS_KEY` "AWS Access Key"
46
50
  - `--aws-secret-access-key SECRET_KEY` "AWS Secret Access Key"
51
+ - `--aws-session-token $AWS_SESSION_TOKEN` "AWS Session Token"
47
52
  - `--aws-region REGION` "AWS Region"
48
53
  - `--pattern STRING` "Snapshots without this string in the description will be ignored"
49
54
  - `--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."
data/Rakefile CHANGED
@@ -3,8 +3,8 @@ require 'bundler'
3
3
  begin
4
4
  Bundler.setup(:default, :development)
5
5
  rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
6
+ warn e.message
7
+ warn "Run `bundle install` to install missing gems"
8
8
  exit e.status_code
9
9
  end
10
10
  require 'rake'
@@ -14,8 +14,8 @@ Juwelier::Tasks.new do |gem|
14
14
  gem.name = "rds-rotate-db-snapshots"
15
15
  gem.homepage = "http://github.com/serg-kovalev/rds-rotate-db-snapshots"
16
16
  gem.license = "MIT"
17
- gem.summary = %Q{Amazon RDS DB snapshot rotator}
18
- gem.description = %Q{Provides a simple way to rotate RDS DB snapshots with configurable retention periods.}
17
+ gem.summary = %(Amazon RDS DB snapshot rotator)
18
+ gem.description = %(Provides a simple way to rotate RDS DB snapshots with configurable retention periods.)
19
19
  gem.email = "kovserg@gmail.com"
20
20
  gem.authors = ["Siarhei Kavaliou"]
21
21
  gem.version = File.exist?('VERSION') ? File.read('VERSION') : ""
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.1
1
+ 0.5.2
@@ -2,10 +2,11 @@
2
2
 
3
3
  require_relative '../lib/rds_rotate_db_snapshots'
4
4
 
5
- rrds = RdsRotateDbSnapshots.new(script_name: File.basename($0), cli: true)
5
+ rrds = RdsRotateDbSnapshots.new(script_name: File.basename($PROGRAM_NAME), cli: true)
6
6
 
7
7
  if rrds.options[:aws_access_key].nil? || rrds.options[:aws_secret_access_key].nil?
8
- puts "You must specify your Amazon credentials via --aws-access-key and --aws-secret_access-key and --aws-session-token"
8
+ puts "You must specify your Amazon credentials via --aws-access-key and --aws-secret_access-key and " \
9
+ "--aws-session-token"
9
10
  exit 1
10
11
  end
11
12
 
@@ -14,7 +15,7 @@ if rrds.options[:aws_region].nil?
14
15
  exit 1
15
16
  end
16
17
 
17
- if ARGV.empty? and rrds.options[:by_tags].nil?
18
+ if ARGV.empty? && rrds.options[:by_tags].nil?
18
19
  puts "You must provide at least one DB Indentifier when not rotating by tags"
19
20
  exit 1
20
21
  end
@@ -23,40 +24,36 @@ if rrds.options[:by_tags].nil?
23
24
  db_indentifier_ids = ARGV
24
25
 
25
26
  db_indentifier_ids.each do |db_id|
26
- if db_id.nil? or db_id.gsub(/\s/, '').size < 1
27
- # sanity check
28
- puts "Invalid db indentifier: #{db_id}"
29
- exit 1
30
- end
27
+ next unless db_id.nil? || db_id.gsub(/\s/, '').empty?
28
+
29
+ # sanity check
30
+ puts "Invalid db indentifier: #{db_id}"
31
+ exit 1
31
32
  end
32
33
  else
33
- if !ARGV.empty?
34
- puts "Ignoring supplied db_indentifier_ids because we're rotating by tags."
35
- end
36
- if rrds.options[:by_tags].length == 0
34
+ puts "Ignoring supplied db_indentifier_ids because we're rotating by tags." unless ARGV.empty?
35
+ if rrds.options[:by_tags].empty?
37
36
  puts "Rotating by tags but no tags specified? Refusing to rotate all snapshots!"
38
37
  exit 1
39
38
  end
40
39
  end
41
40
 
42
- if rrds.options[:backoff_limit] < 0
41
+ if rrds.options[:backoff_limit].negative?
43
42
  puts "A negative backoff limit doesn't make much sense."
44
43
  exit 1
45
44
  end
46
45
 
47
- if rrds.options[:create_snapshot]
48
- rrds.create_snapshot(rrds.options[:create_snapshot], db_indentifier_ids)
49
- end
46
+ rrds.create_snapshot(rrds.options[:create_snapshot], db_indentifier_ids) if rrds.options[:create_snapshot]
50
47
 
51
48
  all_snapshots = []
52
49
  if rrds.options[:by_tags]
53
50
  rrds.rotate_by_tags
54
51
  else
55
- snapshots = rrds.get_db_snapshots(snapshot_type: 'manual').delete_if{ |e| e[:status] != 'available' }
52
+ snapshots = rrds.get_db_snapshots(snapshot_type: 'manual').delete_if { |e| e[:status] != 'available' }
56
53
  db_indentifier_ids.each do |db_id|
57
54
  rrds.rotate_em(
58
- snapshots.select {|ss| ss[:db_instance_identifier] == db_id }.
59
- sort {|a,b| a[:snapshot_create_time] <=> b[:snapshot_create_time] }
55
+ snapshots.select { |ss| ss[:db_instance_identifier] == db_id }
56
+ .sort { |a, b| a[:snapshot_create_time] <=> b[:snapshot_create_time] }
60
57
  )
61
58
  end
62
59
  end
@@ -8,18 +8,18 @@ class RdsRotateDbSnapshots
8
8
  define_method(m) do |*args|
9
9
  reset_backoff
10
10
  begin
11
- super *args
11
+ super(*args)
12
12
  rescue Aws::RDS::Errors::ServiceError => e
13
13
  raise if e.is_a? Aws::RDS::Errors::ExpiredToken
14
+
14
15
  # TODO: re-work
15
16
  puts "Error: #{e}"
16
17
  backoff
17
18
  retry
18
19
  end
19
-
20
20
  end
21
21
  end
22
- self.prepend wrapper
22
+ prepend wrapper
23
23
  end
24
24
  end
25
25
  end
@@ -1,82 +1,41 @@
1
1
  class RdsRotateDbSnapshots
2
2
  module Actions
3
3
  def rotate_em(snapshots)
4
- # poor man's way to get a deep copy of our time_periods definition hash
5
- periods = Marshal.load(Marshal.dump(time_periods))
6
-
7
4
  snapshots.each do |snapshot|
8
5
  time = snapshot[:snapshot_create_time]
9
- db_id = snapshot[:db_instance_identifier]
10
6
  snapshot_id = snapshot[:db_snapshot_identifier]
11
7
  description = snapshot_id
12
- keep_reason = nil
13
-
8
+
14
9
  if options[:pattern] && description !~ /#{options[:pattern]}/
15
10
  puts " #{time.strftime '%Y-%m-%d %H:%M:%S'} #{snapshot_id} Skipping snapshot with description #{description}"
16
11
  next
17
12
  end
18
-
19
- periods.keys.sort { |a, b| periods[a][:seconds] <=> periods[b][:seconds] }.each do |period|
20
- period_info = periods[period]
21
- keep = period_info[:keep]
22
- keeping = period_info[:keeping]
23
-
24
- time_string = time.strftime period_info[:format]
25
- if Time.now - time < keep * period_info[:seconds]
26
- if !keeping.key?(time_string) && keeping.length < keep
27
- keep_reason = period
28
- keeping[time_string] = snapshot
29
- end
30
- break
31
- end
32
- end
33
-
34
- if keep_reason.nil? && snapshot == snapshots.last && options[:keep_last]
35
- keep_reason = 'last snapshot'
36
- end
37
-
38
- if !keep_reason.nil?
39
- puts " #{time.strftime '%Y-%m-%d %H:%M:%S'} #{snapshot_id} Keeping for #{keep_reason}"
40
- else
41
- puts " #{time.strftime '%Y-%m-%d %H:%M:%S'} #{snapshot_id} Deleting"
42
- begin
43
- client.delete_db_snapshot(db_snapshot_identifier: snapshot_id) unless options[:dry_run]
44
- rescue Aws::RDS::Errors => e
45
- backoff
46
- retry
47
- end
48
- end
13
+
14
+ keep_reason = rotate_period_based_decision_with(time, snapshot)
15
+ keep_reason = 'last snapshot' if keep_reason.nil? && snapshot == snapshots.last && options[:keep_last]
16
+ rotate_or_keep_snapshot(keep_reason, time, snapshot_id)
49
17
  end
50
18
  end
51
-
19
+
52
20
  def create_snapshot(name, db_indentifier_ids)
53
- if !!name
54
- name = name.gsub(/[^a-zA-Z0-9\-]/, '')
55
- if name.size > 0
56
- name = "#{name}-#{Time.now.strftime('%Y%m%d%H%M%S')}"
57
- db_indentifier_ids.each do |db_id|
58
- begin
59
- client.create_db_snapshot(db_snapshot_identifier: name, db_instance_identifier: db_id) unless options[:dry_run]
60
- puts " #{Time.now.strftime '%Y-%m-%d %H:%M:%S'} Creation snapshot #{name} is pending (db: #{db_id})"
61
- rescue Aws::RDS::Errors::InvalidDBInstanceStateFault => e
62
- backoff
63
- retry
64
- end
65
- end
66
- else
67
- puts "invalid snapshot name format - #{name}"
68
- exit 1
69
- end
21
+ return if name.nil?
22
+
23
+ name = name.gsub(/[^a-zA-Z0-9-]/, '')
24
+ if name.size.positive?
25
+ create_db_snapshots(name: name, db_indentifier_ids: db_indentifier_ids)
26
+ else
27
+ puts "invalid snapshot name format - #{name}"
28
+ exit 1
70
29
  end
71
30
  end
72
-
31
+
73
32
  def get_db_snapshots(options)
74
33
  snapshots = []
75
34
  response = client.describe_db_snapshots(options)
76
- while true do
35
+ loop do
77
36
  snapshots += response.db_snapshots
78
37
  break unless response[:marker]
79
-
38
+
80
39
  response = client.describe_db_snapshots(options.merge(marker: response[:marker]))
81
40
  end
82
41
  snapshots
@@ -86,20 +45,68 @@ class RdsRotateDbSnapshots
86
45
  snapshots = []
87
46
  options[:by_tags].each do |tag, value|
88
47
  snapshots = rrds.client.describe_tags(
89
- snapshot_type: 'manual', filters: {'resource-type'=>"snapshot", 'key'=>tag, 'value'=>value}
90
- ).delete_if{ |e| e.status != 'available' }
48
+ snapshot_type: 'manual', filters: { 'resource-type' => "snapshot", 'key' => tag, 'value' => value }
49
+ ).delete_if { |e| e.status != 'available' }
91
50
  # TODO: re-work
92
- if snapshots.length == 0
51
+ if snapshots.empty?
93
52
  puts "(tag,value)=(#{tag},#{value}) found no snapshots; nothing to rotate!"
94
53
  exit 0
95
54
  end
96
55
  end
97
56
 
98
- snapshots = get_db_snapshots(db_instance_identifier: snapshots.map(&:db_instance_identifier).uniq).
99
- delete_if{ |e| !snapshots.include?(e.db_snapshot_identifier) }.
100
- sort {|a,b| a[:snapshot_create_time] <=> b[:snapshot_create_time] }
57
+ snapshots = get_db_snapshots(db_instance_identifier: snapshots.map(&:db_instance_identifier).uniq)
58
+ .delete_if { |e| !snapshots.include?(e.db_snapshot_identifier) }
59
+ .sort { |a, b| a[:snapshot_create_time] <=> b[:snapshot_create_time] }
101
60
 
102
61
  rotate_em snapshots
103
62
  end
63
+
64
+ private
65
+
66
+ def rotate_period_based_decision_with(time, snapshot)
67
+ # poor man's way to get a deep copy of our time_periods definition hash
68
+ periods = Marshal.load(Marshal.dump(time_periods))
69
+ keep_reason = nil
70
+
71
+ periods.keys.sort { |a, b| periods[a][:seconds] <=> periods[b][:seconds] }.each do |period|
72
+ period_info = periods[period]
73
+ keep = period_info[:keep]
74
+ keeping = period_info[:keeping]
75
+
76
+ time_string = time.strftime period_info[:format]
77
+ next unless Time.now - time < keep * period_info[:seconds]
78
+
79
+ if !keeping.key?(time_string) && keeping.length < keep
80
+ keep_reason = period
81
+ keeping[time_string] = snapshot
82
+ end
83
+ break
84
+ end
85
+
86
+ keep_reason
87
+ end
88
+
89
+ def rotate_or_keep_snapshot(keep_reason, time, snapshot_id)
90
+ if keep_reason.nil?
91
+ puts " #{time.strftime '%Y-%m-%d %H:%M:%S'} #{snapshot_id} Deleting"
92
+ client.delete_db_snapshot(db_snapshot_identifier: snapshot_id) unless options[:dry_run]
93
+ else
94
+ puts " #{time.strftime '%Y-%m-%d %H:%M:%S'} #{snapshot_id} Keeping for #{keep_reason}"
95
+ end
96
+ end
97
+
98
+ def create_db_snapshots(name:, db_indentifier_ids:)
99
+ name = "#{name}-#{Time.now.strftime('%Y%m%d%H%M%S')}"
100
+ db_indentifier_ids.each do |db_id|
101
+ unless options[:dry_run]
102
+ client.create_db_snapshot(db_snapshot_identifier: name,
103
+ db_instance_identifier: db_id)
104
+ end
105
+ puts " #{Time.now.strftime '%Y-%m-%d %H:%M:%S'} Creation snapshot #{name} is pending (db: #{db_id})"
106
+ rescue Aws::RDS::Errors::InvalidDBInstanceStateFault
107
+ backoff
108
+ retry
109
+ end
110
+ end
104
111
  end
105
112
  end