capistrano-ops 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6897d38c35cb8b0dfb713d16d0eb0c390c431636ea5c6565e1dc457b96a29c6b
4
- data.tar.gz: 9789f0e37f0767403324c1ca68c97759a8ebdbdb5a3e4ac6d362b76dfc5064ad
3
+ metadata.gz: 9ff5391932b65811de3330a802d309df6d78dc1d2b6b04cc60b5c86dfe0de6e1
4
+ data.tar.gz: 769c58ef06c175441bc53a673ee379394fc343cfdfe37612999b204508db742d
5
5
  SHA512:
6
- metadata.gz: 4607175b27284ed6d19ca2fe3032dccde17f54cb62dfbfc43c614af031abf9990b873b89b1d905c74d2acb48dc4e94a3eec6b50bbff18dee0079b2a5264d04dd
7
- data.tar.gz: 1ce1060464559ce566320e9ef7a1700d5d94ee688e6f8aea85f7dbd2548e78bfc5e6cb1f5e751b38e9b566e7411e89587d4207eb6c0349592d0cacab854e6ffc
6
+ metadata.gz: e2e68e6ebb4da63511fe2a8712eeee07368b3ab6f427faa0be00799784ba73aa67eb49787d6c09f67a6da9dd6dda08e0c57296e3b1f621505e5db40576aaab1a
7
+ data.tar.gz: 0752765d093c875d5d96eb7b32451dc38a2f15519920f1bf85ebb1c6c926e4161e7e9488f681ee0d2067ca22b0192efab994e3ae268a55521b8789b7bc077b93
@@ -3,7 +3,7 @@
3
3
  module Backup
4
4
  require 'aws-sdk-s3'
5
5
  class S3
6
- attr_accessor :endpoint, :region, :access_key_id, :secret_access_key, :s3_client
6
+ attr_accessor :endpoint, :region, :access_key_id, :secret_access_key, :s3_resource
7
7
 
8
8
  def initialize(endpoint: ENV['S3_BACKUP_ENDPOINT'], region: ENV['S3_BACKUP_REGION'], access_key_id: ENV['S3_BACKUP_KEY'],
9
9
  secret_access_key: ENV['S3_BACKUP_SECRET'])
@@ -17,36 +17,43 @@ module Backup
17
17
  secret_access_key: secret_access_key
18
18
  }
19
19
  config[:endpoint] = endpoint unless endpoint.nil?
20
- self.s3_client = Aws::S3::Client.new(config)
20
+ self.s3_resource = Aws::S3::Resource.new(config)
21
21
  end
22
22
 
23
23
  def upload(backup_file, key)
24
24
  begin
25
- s3_client.put_object(
26
- bucket: ENV['S3_BACKUP_BUCKET'],
27
- key: key,
28
- body: File.open(backup_file)
29
- )
30
- rescue StandardError => e
31
- puts "Error uploading backup to S3: #{e.message}"
25
+ s3_resource.bucket(ENV['S3_BACKUP_BUCKET']).object(key).upload_file(backup_file)
26
+ rescue Backup::Error => e
27
+ puts "Upload failed: #{e.message}"
32
28
  raise e
33
29
  end
34
30
  'File uploaded to S3'
35
31
  end
36
32
 
37
33
  def remove_old_backups(basename, keep: 5)
38
- bucket = ENV['S3_BACKUP_BUCKET']
39
- all_items = s3_client.list_objects_v2(bucket: bucket, prefix: basename).contents
34
+ all_items = s3_resource.bucket(ENV['S3_BACKUP_BUCKET']).objects(prefix: basename).map do |item|
35
+ { key: item.key, last_modified: item.last_modified }
36
+ end
37
+
38
+ all_items = all_items.sort_by { |hsh| hsh[:last_modified] }.reverse
39
+
40
40
  count = all_items.count
41
+
41
42
  if count <= keep
42
- p 'Nothing to remove'
43
- return
43
+ puts 'Nothing to remove'
44
+ exit(0)
44
45
  end
45
- items = all_items.sort_by(&:last_modified).reverse.slice(keep..-1).map(&:key)
46
- items.each do |item|
47
- p "Removing #{item} from S3"
48
- s3_client.delete_object(bucket: bucket, key: item)
46
+
47
+ delete_items = all_items.slice(keep..-1)
48
+
49
+ delete_items.each do |item_obj|
50
+ puts "Removing #{item_obj[:key]} from S3"
51
+ s3_resource.bucket(ENV['S3_BACKUP_BUCKET']).object(item_obj[:key]).delete
49
52
  end
53
+ puts 'Old backups removed from S3'
54
+ rescue Backup::Error => e
55
+ puts "Remove failed: #{e.message}"
56
+ raise e
50
57
  end
51
58
  end
52
59
  end
@@ -6,7 +6,7 @@ namespace :backup do
6
6
  include BackupHelper
7
7
  # Default to :app role
8
8
  rake_roles = fetch(:rake_roles, :app)
9
-
9
+
10
10
  desc 'create a backup of the server database'
11
11
  task :create do
12
12
  on roles(rake_roles) do
@@ -6,7 +6,7 @@ namespace :backup do
6
6
  include BackupHelper
7
7
  # Default to :app role
8
8
  rake_roles = fetch(:rake_roles, :app)
9
-
9
+
10
10
  desc 'create storage dump on server'
11
11
  task :create do
12
12
  on roles(rake_roles) do
@@ -1,27 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Metrics/BlockLength
3
+ require_relative './postgres_helper'
4
4
  namespace :pg do
5
- @database = Rails.configuration.database_configuration[Rails.env]['database']
6
- @username = Rails.configuration.database_configuration[Rails.env]['username']
7
- @password = Rails.configuration.database_configuration[Rails.env]['password']
8
- @hostname = Rails.configuration.database_configuration[Rails.env]['host']
9
- @portnumber = Rails.configuration.database_configuration[Rails.env]['port']
10
- @backup_path = Rails.root.join(Rails.env.development? ? 'tmp/backups' : '../../shared/backups').to_s
11
- backups_enabled = Rails.env.production? || ENV['BACKUPS_ENABLED'] == 'true'
12
- external_backup = Rails.env.production? || ENV['EXTERNAL_BACKUP_ENABLED'] == 'true'
5
+ include PostgresHelper
6
+
13
7
  task :dump do
8
+ backup_path = config[:backup_path]
9
+ backups_enabled = config[:backups_enabled]
10
+ external_backup = config[:external_backup]
11
+
14
12
  unless backups_enabled
15
13
  puts 'dump: Backups are disabled'
16
14
  exit(0)
17
15
  end
18
16
 
19
17
  notification = Notification::Api.new
20
- commandlist = dump_cmd
18
+ commandlist = dump_cmd(config)
21
19
 
22
- system "mkdir -p #{@backup_path}" unless Dir.exist?(@backup_path)
20
+ system "mkdir -p #{backup_path}" unless Dir.exist?(backup_path)
23
21
 
24
- result = system(commandlist.join(' && '))
22
+ result = system(commandlist)
25
23
 
26
24
  if ENV['BACKUP_PROVIDER'].present? && external_backup && result
27
25
  puts "Uploading #{@filename} to #{ENV['BACKUP_PROVIDER']}..."
@@ -33,40 +31,10 @@ namespace :pg do
33
31
  puts "#{@filename} upload failed: #{e.message}"
34
32
  end
35
33
  end
36
- notification.send_backup_notification(result, title, content(result), { date: @date, database: @database, backup_path: @backup_path })
37
- puts result ? "Backup created: #{@backup_path}/#{@filename}" : 'Backup failed removing dump file'
38
- system "rm #{@backup_path}/#{@filename}" unless result
39
- end
40
-
41
- def title
42
- ENV['DEFAULT_URL'] || "#{Rails.env} Backup"
43
- end
44
-
45
- def content(result)
46
- messages = []
47
- if result
48
- messages << "Backup of #{@database} successfully finished at #{Time.now}"
49
- messages << "Backup path:\`#{@backup_path}/#{@filename}\`"
50
- else
51
- messages << "Backup of #{@database} failed at #{Time.now}"
52
- end
53
- messages.join("\n")
54
- end
34
+ notification.send_backup_notification(result, title, content(result, { database: @database, backup_path: @backup_path, filename: @filename }),
35
+ { date: @date, backup_path: @backup_path, database: @database })
36
+ puts result ? "Backup created: #{@backup_path}/#{@filename} (#{size_str(File.size("#{@backup_path}/#{@filename}"))})" : 'Backup failed removing dump file'
55
37
 
56
- def dump_cmd
57
- @date = Time.now.to_i
58
- options = []
59
- options << " -d #{@database}" if @database.present?
60
- options << " -U #{@username}" if @username.present?
61
- options << " -h #{@hostname}" if @hostname.present?
62
- options << " -p #{@portnumber}" if @portnumber.present?
63
-
64
- @filename = "#{@database}_#{@date}.dump"
65
-
66
- commandlist = []
67
- commandlist << "export PGPASSWORD='#{@password}'"
68
- commandlist << "cd #{@backup_path}"
69
- commandlist << "pg_dump -Fc #{options.join('')} > #{@filename}"
38
+ system "rm #{@backup_path}/#{@filename}" unless result
70
39
  end
71
40
  end
72
- # rubocop:enable Metrics/BlockLength
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PostgresHelper
4
+ def config
5
+ @config ||=
6
+ {
7
+ database: Rails.configuration.database_configuration[Rails.env]['database'],
8
+ username: Rails.configuration.database_configuration[Rails.env]['username'],
9
+ password: Rails.configuration.database_configuration[Rails.env]['password'],
10
+ hostname: Rails.configuration.database_configuration[Rails.env]['host'],
11
+ portnumber: Rails.configuration.database_configuration[Rails.env]['port'],
12
+ backup_path: Rails.root.join(Rails.env.development? ? 'tmp/backups' : '../../shared/backups').to_s,
13
+ backups_enabled: Rails.env.production? || ENV['BACKUPS_ENABLED'] == 'true',
14
+ external_backup: Rails.env.production? || ENV['EXTERNAL_BACKUP_ENABLED'] == 'true'
15
+ }
16
+ end
17
+
18
+ def title
19
+ ENV['DEFAULT_URL'] || "#{Rails.env} Backup"
20
+ end
21
+
22
+ def content(result, config = {})
23
+ @database = config[:database]
24
+ @backup_path = config[:backup_path]
25
+ @filename = config[:filename]
26
+
27
+ messages = []
28
+ if result
29
+ messages << "Backup of #{@database} successfully finished at #{Time.now}"
30
+ messages << "Backup path:\`#{@backup_path}/#{@filename}\`"
31
+ else
32
+ messages << "Backup of #{@database} failed at #{Time.now}"
33
+ end
34
+ messages.join("\n")
35
+ end
36
+
37
+ def dump_cmd(config = {})
38
+ @hostname = config[:hostname]
39
+ @database = config[:database]
40
+ @username = config[:username]
41
+ @password = config[:password]
42
+ @portnumber = config[:portnumber]
43
+ @backup_path = config[:backup_path]
44
+
45
+ @date = Time.now.to_i
46
+ options = []
47
+ options << " -d #{@database}" if @database.present?
48
+ options << " -U #{@username}" if @username.present?
49
+ options << " -h #{@hostname}" if @hostname.present?
50
+ options << " -p #{@portnumber}" if @portnumber.present?
51
+
52
+ @filename = "#{@database}_#{@date}.dump"
53
+
54
+ commandlist = []
55
+ commandlist << "export PGPASSWORD='#{@password}'"
56
+ commandlist << "cd #{@backup_path}"
57
+ commandlist << "pg_dump -Fc #{options.join('')} > #{@filename}"
58
+ commandlist.join(' && ')
59
+ end
60
+
61
+ def size_str(size)
62
+ units = %w[B KB MB GB TB]
63
+ e = (Math.log(size) / Math.log(1024)).floor
64
+ s = format('%.2f', size.to_f / 1024**e)
65
+ s.sub(/\.?0*$/, units[e])
66
+ end
67
+ end
@@ -4,8 +4,8 @@ require 'rake'
4
4
  namespace :pg do
5
5
  @backup_path = Rails.root.join(Rails.env.development? ? 'tmp/backups' : '../../shared/backups').to_s
6
6
  @database = Rails.configuration.database_configuration[Rails.env]['database']
7
- @env_local_no = ENV['NUMBER_OF_LOCAL_BACKUPS']
8
- @env_external_no = ENV['NUMBER_OF_EXTERNAL_BACKUPS']
7
+ @env_local_no = ENV['NUMBER_OF_LOCAL_BACKUPS'].present? ? ENV['NUMBER_OF_LOCAL_BACKUPS'] : nil
8
+ @env_external_no = ENV['NUMBER_OF_EXTERNAL_BACKUPS'].present? ? ENV['NUMBER_OF_EXTERNAL_BACKUPS'] : nil
9
9
  @total_local_backups_no = (@env_local_no || ENV['NUMBER_OF_BACKUPS'] || 7).to_i
10
10
  @total_external_backups_no = (@env_external_no || ENV['NUMBER_OF_BACKUPS'] || 7).to_i
11
11
  backups_enabled = Rails.env.production? || ENV['BACKUPS_ENABLED'] == 'true'
@@ -19,7 +19,7 @@ namespace :pg do
19
19
  exit(0)
20
20
  end
21
21
  unless @total_local_backups_no.positive?
22
- puts "remove_old_dumps: No local cleanup because option '#{if @env_local_no
22
+ puts "remove_old_dumps: No local cleanup because option '#{if @env_local_no.present?
23
23
  'NUMBER_OF_LOCAL_BACKUPS='
24
24
  else
25
25
  'NUMBER_OF_BACKUPS='
@@ -35,11 +35,12 @@ namespace :pg do
35
35
  'xargs rm -rf'
36
36
  ]
37
37
 
38
- system(commandlist.join(' | '))
38
+ result = system(commandlist.join(' | ')) if @total_local_backups_no.positive?
39
+ puts 'remove_old_dumps: local cleanup finished' if result
39
40
 
40
41
  if ENV['BACKUP_PROVIDER'].present? && external_backup
41
42
  unless @total_external_backups_no.positive?
42
- puts "remove_old_dumps: No external cleanup because option '#{if @env_external_no
43
+ puts "remove_old_dumps: No external cleanup because option '#{if @env_external_no.present?
43
44
  'NUMBER_OF_EXTERNAL_BACKUPS='
44
45
  else
45
46
  'NUMBER_OF_BACKUPS='
@@ -48,7 +49,7 @@ namespace :pg do
48
49
  end
49
50
  provider = Backup::Api.new
50
51
  begin
51
- result = provider.remove_old_backups(@database, @total_external_backups_no)
52
+ result = provider.remove_old_backups("#{@database}_", @total_external_backups_no)
52
53
  rescue StandardError => e
53
54
  puts "remove_old_dumps failed: #{e.message}"
54
55
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Metrics/BlockLength
4
3
  namespace :storage do
5
4
  @backup_path = Rails.root.join(Rails.env.development? ? 'tmp/backups' : '../../shared/backups').to_s
6
5
  @storage_path = Rails.root.join(Rails.env.development? ? 'storage' : '../../shared/storage').to_s
@@ -51,16 +50,9 @@ namespace :storage do
51
50
  end
52
51
 
53
52
  def size_str(size)
54
- case size
55
- when 0..1024
56
- "#{size} B"
57
- when 1024..1024 * 1024
58
- "#{size / 1024} KB"
59
- when 1024 * 1024..1024 * 1024 * 1024
60
- "#{size / 1024 / 1024} MB"
61
- when 1024 * 1024 * 1024..1024 * 1024 * 1024 * 1024
62
- "#{size / 1024 / 1024 / 1024} GB"
63
- end
53
+ units = %w[B KB MB GB TB]
54
+ e = (Math.log(size) / Math.log(1024)).floor
55
+ s = format('%.2f', size.to_f / 1024**e)
56
+ s.sub(/\.?0*$/, units[e])
64
57
  end
65
58
  end
66
- # rubocop:enable Metrics/BlockLength
@@ -6,8 +6,8 @@ namespace :storage do
6
6
  backups_enabled = Rails.env.production? || ENV['BACKUPS_ENABLED'] == 'true'
7
7
  external_backup = Rails.env.production? || ENV['EXTERNAL_BACKUP_ENABLED'] == 'true'
8
8
 
9
- @env_local_no = ENV['NUMBER_OF_LOCAL_BACKUPS']
10
- @env_external_no = ENV['NUMBER_OF_EXTERNAL_BACKUPS']
9
+ @env_local_no = ENV['NUMBER_OF_LOCAL_BACKUPS'].present? ? ENV['NUMBER_OF_LOCAL_BACKUPS'] : nil
10
+ @env_external_no = ENV['NUMBER_OF_EXTERNAL_BACKUPS'].present? ? ENV['NUMBER_OF_EXTERNAL_BACKUPS'] : nil
11
11
  @total_local_backups_no = (@env_local_no || ENV['NUMBER_OF_BACKUPS'] || 7).to_i
12
12
  @total_external_backups_no = (@env_external_no || ENV['NUMBER_OF_BACKUPS'] || 7).to_i
13
13
  desc 'remove old storage backups'
@@ -35,7 +35,8 @@ namespace :storage do
35
35
  'xargs rm -rf'
36
36
  ]
37
37
 
38
- system(commandlist.join(' | '))
38
+ result = system(commandlist.join(' | ')) if @total_local_backups_no.positive?
39
+ puts 'remove_old_backups: local cleanup finished' if result
39
40
 
40
41
  if ENV['BACKUP_PROVIDER'].present? && external_backup
41
42
  unless @total_external_backups_no.positive?
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Capistrano
4
4
  module Ops
5
- VERSION = '0.2.4'
5
+ VERSION = '0.2.5'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capistrano-ops
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Crusius
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-13 00:00:00.000000000 Z
11
+ date: 2023-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-s3
@@ -135,6 +135,7 @@ files:
135
135
  - lib/capistrano/ops/notification/webhook.rb
136
136
  - lib/capistrano/ops/railtie.rb
137
137
  - lib/capistrano/ops/tasks/pg/dump.rake
138
+ - lib/capistrano/ops/tasks/pg/postgres_helper.rb
138
139
  - lib/capistrano/ops/tasks/pg/remove_old_dumps.rake
139
140
  - lib/capistrano/ops/tasks/storage/backup.rake
140
141
  - lib/capistrano/ops/tasks/storage/remove_old_backups.rake