capistrano-ops 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,63 +7,63 @@ namespace :figaro_yml do
7
7
 
8
8
  desc 'get the `application.yml` file from server and create local if it does not exist'
9
9
  task :get do
10
- env = fetch(:stage)
11
- if !File.exist?('config/application.yml')
12
- puts "config/application.yml does not exist, creating it from all stages"
13
- run_locally do
14
- yamls={}
15
- stages = Dir.glob('config/deploy/*.rb')
16
- puts "found #{stages.count} stages"
17
- stages.map do |f|
18
- stage =File.basename(f, '.rb')
19
- puts "download #{stage} application.yml"
20
- begin
10
+ env = fetch(:stage)
11
+ if !File.exist?('config/application.yml')
12
+ puts 'config/application.yml does not exist, creating it from all stages'
13
+ run_locally do
14
+ yamls = {}
15
+ stages = Dir.glob('config/deploy/*.rb')
16
+ puts "found #{stages.count} stages"
17
+ stages.map do |f|
18
+ stage = File.basename(f, '.rb')
19
+ puts "download #{stage} application.yml"
20
+ begin
21
21
  res = capture "cap #{stage} figaro_yml:get_stage"
22
- yamls= yamls.merge(YAML.safe_load(res))
23
- rescue
24
- puts "could not get #{stage} application.yml"
25
- end
26
- yamls
22
+ yamls = yamls.merge(YAML.safe_load(res))
23
+ rescue StandardError
24
+ puts "could not get #{stage} application.yml"
27
25
  end
28
- # write to new file
29
- puts "writing to config/application.yml"
30
- write_to_file('config/application.yml', yamls.to_yaml)
26
+ yamls
31
27
  end
32
- else
33
- local_yml = YAML.safe_load(File.read('config/application.yml'))
34
- on roles(rake_roles) do
35
- remote = capture("cat #{shared_path}/config/application.yml")
36
- remote_yml = YAML.safe_load(remote)
37
- remote_stage = remote_yml[env.to_s]
38
- puts "remote application.yml stage '#{env.to_s}':\n\n"
39
- puts remote + "\r\n"
40
- puts "\r\n"
41
- loop do
42
- print "Overwrite local application.yml stage '#{env.to_s}'? (y/N): "
43
- input = $stdin.gets.strip.downcase
44
- answer = (input.empty? ? 'N' : input).downcase.to_s
45
-
46
- next unless %w(y n).include?(answer)
47
-
48
- if answer == 'y'
49
- puts 'Updating local application.yml'
50
- local_yml[env.to_s] = remote_stage
51
- write_to_file('config/application.yml', local_yml.to_yaml)
52
- exit
53
- end
54
- break
28
+ # write to new file
29
+ puts 'writing to config/application.yml'
30
+ write_to_file('config/application.yml', yamls.to_yaml)
31
+ end
32
+ else
33
+ local_yml = YAML.safe_load(File.read('config/application.yml'))
34
+ on roles(rake_roles) do
35
+ remote = capture("cat #{shared_path}/config/application.yml")
36
+ remote_yml = YAML.safe_load(remote)
37
+ remote_stage = remote_yml[env.to_s]
38
+ puts "remote application.yml stage '#{env}':\n\n"
39
+ puts "#{remote}\r\n"
40
+ puts "\r\n"
41
+ loop do
42
+ print "Overwrite local application.yml stage '#{env}'? (y/N): "
43
+ input = $stdin.gets.strip.downcase
44
+ answer = (input.empty? ? 'N' : input).downcase.to_s
45
+
46
+ next unless %w[y n].include?(answer)
47
+
48
+ if answer == 'y'
49
+ puts 'Updating local application.yml'
50
+ local_yml[env.to_s] = remote_stage
51
+ write_to_file('config/application.yml', local_yml.to_yaml)
52
+ exit
55
53
  end
56
- puts 'Nothing written to local application.yml'
57
- exit
54
+ break
58
55
  end
56
+ puts 'Nothing written to local application.yml'
57
+ exit
59
58
  end
60
59
  end
61
-
62
- task :get_stage do
63
- on roles(rake_roles) do
64
- puts capture "cat #{shared_path}/config/application.yml"
65
- end
60
+ end
61
+
62
+ task :get_stage do
63
+ on roles(rake_roles) do
64
+ puts capture "cat #{shared_path}/config/application.yml"
66
65
  end
66
+ end
67
67
 
68
68
  desc 'compare and set the figaro_yml file on the server'
69
69
  task :compare do
@@ -93,12 +93,12 @@ namespace :figaro_yml do
93
93
  result2 = compare_hashes(local_stage_env, remote_stage_env)
94
94
  if !result1.empty? || !result2.empty?
95
95
  loop do
96
- print 'Update remote application.yml? (y/N): '
96
+ print 'Update remote application.yml? (y/N): '
97
97
  input = $stdin.gets.strip.downcase
98
98
  answer = (input.empty? ? 'N' : input).downcase.to_s
99
99
 
100
- next unless %w(y n).include?(answer)
101
-
100
+ next unless %w[y n].include?(answer)
101
+
102
102
  if answer == 'y'
103
103
  puts 'Updating remote application.yml'
104
104
  invoke 'figaro_yml:setup'
@@ -116,25 +116,24 @@ namespace :figaro_yml do
116
116
  changes = false
117
117
  local_server = hash1.to_a - hash2.to_a
118
118
  server_local = hash2.to_a - hash1.to_a
119
-
120
- [local_server + server_local].flatten(1).to_h.keys.each do |k|
119
+
120
+ [local_server + server_local].flatten(1).to_h.each_key do |k|
121
121
  new_value = hash1[k].to_s
122
- new_value = new_value.empty? ? "nil" : new_value
122
+ new_value = new_value.empty? ? 'nil' : new_value
123
123
  old_value = hash2[k].to_s
124
- old_value = old_value.empty? ? "nil" : old_value
124
+ old_value = old_value.empty? ? 'nil' : old_value
125
125
 
126
126
  if old_value != new_value
127
- puts "#{k}: #{old_value} => #{new_value} \r\n"
128
- changes = true
127
+ puts "#{k}: #{old_value} => #{new_value} \r\n"
128
+ changes = true
129
129
  end
130
130
  end
131
- end
131
+ end
132
132
 
133
133
  def write_to_file(file, content)
134
134
  File.open(file, 'w') do |f|
135
135
  f.write(content)
136
136
  end
137
137
  end
138
-
139
138
  end
140
139
  # rubocop:enable Metrics/BlockLength
@@ -1,24 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :invoke do
4
+ # Defalut to :app roles
5
+ rake_roles = fetch(:rake_roles, :app)
2
6
 
3
- # Defalut to :app roles
4
- rake_roles = fetch(:rake_roles, :app)
5
-
6
- desc "Execute a rake task on a remote server (cap invoke:rake TASK=db:migrate)"
7
- task :rake do
8
- if ENV['TASK']
9
- on roles(rake_roles) do
10
- within current_path do
11
- with rails_env: fetch(:rails_env) do
12
- execute :rake, ENV['TASK']
13
- end
7
+ desc 'Execute a rake task on a remote server (cap invoke:rake TASK=db:migrate)'
8
+ task :rake do
9
+ if ENV['TASK']
10
+ on roles(rake_roles) do
11
+ within current_path do
12
+ with rails_env: fetch(:rails_env) do
13
+ execute :rake, ENV['TASK']
14
14
  end
15
15
  end
16
-
17
- else
18
- puts "\n\nFailed! You need to specify the 'TASK' parameter!",
19
- "Usage: cap <stage> invoke:rake TASK=your:task"
20
16
  end
17
+
18
+ else
19
+ puts "\n\nFailed! You need to specify the 'TASK' parameter!",
20
+ 'Usage: cap <stage> invoke:rake TASK=your:task'
21
21
  end
22
-
23
22
  end
24
-
23
+ end
@@ -8,6 +8,8 @@ if defined?(Capistrano::VERSION) && Gem::Version.new(Capistrano::VERSION).releas
8
8
  load File.expand_path('capistrano/v3/tasks/figaro_yml.rake', __dir__)
9
9
  load File.expand_path('capistrano/v3/tasks/logs.rake', __dir__)
10
10
  load File.expand_path('capistrano/v3/tasks/invoke.rake', __dir__)
11
+ path = File.expand_path(__dir__)
12
+ Dir.glob("#{path}/capistrano/v3/tasks/backup/**/*.rake").each { |f| load f }
11
13
  else
12
14
  puts 'Capistrano 3 is required to use this gem'
13
15
  end
@@ -1,30 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Notification
2
- class Api
3
- attr_accessor :notification_type, :notification_level
4
+ class Api
5
+ attr_accessor :notification_type, :notification_level
6
+
7
+ def initialize(notification_type: ENV['NOTIFICATION_TYPE'], notification_level: ENV['NOTIFICATION_LEVEL'])
8
+ self.notification_type = notification_type
9
+ self.notification_level = notification_level || 'error'
10
+ end
4
11
 
5
- def initialize(notification_type: ENV['NOTIFICATION_TYPE'], notification_level: ENV['NOTIFICATION_LEVEL'])
6
- self.notification_type = notification_type
7
- self.notification_level = notification_level || 'error'
8
- end
12
+ def send_backup_notification(result, title, content, webhook_data = nil)
13
+ return if notification_type.nil?
14
+
15
+ case notification_type
16
+ when 'slack'
17
+ Slack.new.backup_notification(result, title, content, notification_level)
18
+ when 'webhook'
19
+ Webhook.new.backup_notification(result, webhook_data, notification_level)
20
+ end
21
+ end
9
22
 
10
- def send_backup_notification(result, title, content)
11
- return if notification_type.nil?
12
- case notification_type
13
- when 'slack'
14
- Slack.new.backup_notification(result, title, content, notification_level)
15
- when 'webhook'
16
- Webhook.new.backup_notification(result, title, content, notification_level)
17
- end
18
- end
23
+ def send_notification(message)
24
+ return if notification_type.nil?
19
25
 
20
- def send_notification(message)
21
- return if notification_type.nil?
22
- case notification_type
23
- when 'slack'
24
- Slack.new.notify(message)
25
- when 'webhook'
26
- p 'webhook'
27
- end
28
- end
26
+ case notification_type
27
+ when 'slack'
28
+ Slack.new.notify(message)
29
+ when 'webhook'
30
+ p 'webhook'
31
+ end
29
32
  end
30
- end
33
+ end
34
+ end
@@ -1,66 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'json'
5
+
1
6
  module Notification
2
- class Slack
3
- require 'uri'
4
- require 'net/http'
5
- require 'net/https'
6
- def initialize
7
- @slack_secret = ENV['SLACK_SECRET']
8
- @slack_channel = ENV['SLACK_CHANNEL']
9
- @slack_base_url ='https://slack.com/api/'
10
- end
7
+ class Slack
8
+ def initialize
9
+ @slack_secret = ENV['SLACK_SECRET']
10
+ @slack_channel = ENV['SLACK_CHANNEL']
11
+ @conn = Faraday.new(url: 'https://slack.com/api/') do |faraday|
12
+ faraday.headers['Content-Type'] = 'application/json'
13
+ faraday.headers['Authorization'] = "Bearer #{@slack_secret}"
14
+ end
15
+ end
16
+
17
+ def notify(message)
18
+ return if @slack_secret.nil? || @slack_channel.nil?
11
19
 
12
- def notify(message)
13
- return if @slack_secret.nil? || @slack_channel.nil?
14
- uri = URI.parse("#{@slack_base_url}chat.postMessage")
15
- http = Net::HTTP.new(uri.host, uri.port)
16
- http.use_ssl = true
17
- request = Net::HTTP::Post.new(uri.request_uri, initHeader = {'Content-Type' =>'application/json', 'Authorization' => 'Bearer ' + @slack_secret})
18
- request.body = {
19
- channel: @slack_channel,
20
- text: message
21
- }.to_json
22
- response = http.request(request)
23
- puts response.body
20
+ begin
21
+ res = @conn.post('chat.postMessage') do |req|
22
+ req.body = {
23
+ "channel": @slack_channel,
24
+ "text": message
25
+ }.to_json
24
26
  end
27
+ response = JSON.parse(res.body)
28
+ raise Notification::Error, response['error'] if response['ok'] == false
25
29
 
26
- def backup_notification(result, title, content, notification_level)
27
- return if @slack_secret.nil? || @slack_channel.nil?
28
- return if notification_level == 'error' && result
29
- uri = URI.parse("#{@slack_base_url}chat.postMessage")
30
- http = Net::HTTP.new(uri.host, uri.port)
31
- http.use_ssl = true
32
- request = Net::HTTP::Post.new(uri.request_uri, initHeader = {'Content-Type' =>'application/json', 'Authorization' => 'Bearer ' + @slack_secret})
33
-
34
- data = {
35
- channel: @slack_channel,
36
- blocks: [
37
- {
38
- type: 'header',
39
- text: {
40
- type: 'plain_text',
41
- text: title || "#{Rails.env} Message",
42
- emoji: true
43
- }
44
- },
45
- {
46
- type: 'section',
47
- text: {
48
- type: 'mrkdwn',
49
- text: content || 'No content'
50
- }
51
- }
52
- ]
53
- }
54
- request.body = data.to_json
55
- begin
56
- response = JSON.parse(http.request(request).body)
57
- if response['ok'] == false
58
- raise Notification::Error, response['error']
59
- end
60
- response
61
- rescue => e
62
- puts "Slack error: \n\t#{e.message}"
63
- end
30
+ response
31
+ rescue Notification::Error => e
32
+ puts "Slack error: \n\t#{e.message}"
33
+ end
34
+ end
35
+
36
+ def backup_notification(result, title, content, notification_level)
37
+ return if @slack_secret.nil? || @slack_channel.nil?
38
+ return if notification_level == 'error' && result
39
+
40
+ begin
41
+ res = @conn.post('chat.postMessage') do |req|
42
+ req.body = {
43
+ channel: @slack_channel,
44
+ blocks: [
45
+ {
46
+ type: 'header',
47
+ text: {
48
+ type: 'plain_text',
49
+ text: title || "#{Rails.env} Message",
50
+ emoji: true
51
+ }
52
+ },
53
+ {
54
+ type: 'section',
55
+ text: {
56
+ type: 'mrkdwn',
57
+ text: content || 'No content'
58
+ }
59
+ }
60
+ ]
61
+ }.to_json
64
62
  end
63
+ response = JSON.parse(res.body)
64
+ raise Notification::Error, response['error'] if response['ok'] == false
65
+
66
+ response
67
+ rescue Notification::Error => e
68
+ puts "Slack error: \n\t#{e.message}"
69
+ end
65
70
  end
71
+ end
66
72
  end
@@ -1,40 +1,47 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Notification
2
- class Webhook
3
- require 'uri'
4
- require 'net/http'
5
- require 'net/https'
6
- require 'openssl'
7
- require 'json'
8
-
9
- def initialize
10
- @webhook_url = ENV['WEBHOOK_URL']
11
- @secret = ENV['WEBHOOK_SECRET']
12
- end
13
-
14
- def generate_signature(payload_body)
15
- "md5=#{OpenSSL::HMAC.hexdigest('md5', ENV['WEBHOOK_SECRET'], payload_body)}"
16
- end
4
+ class Webhook
5
+ require 'faraday'
6
+ require 'openssl'
7
+ require 'json'
17
8
 
18
- def backup_notification(result, date, database, backup_path, notification_level)
19
- return if @webhook_url.nil? || @secret.nil?
20
- return if result && ENV['NOTIFICATION_LEVEL'] == 'error'
21
- data = {
22
- domain: ENV['DEFAULT_URL'] || "#{database} Backup",
23
- backupPath: result ? backup_path : nil,
24
- backupDate: date,
25
- }.to_json
26
-
27
- uri = URI.parse(@webhook_url)
28
- https = Net::HTTP.new(uri.host, uri.port)
29
- https.use_ssl = uri.scheme == "https"
30
- request = Net::HTTP::Post.new(uri.path.empty? ? "/" : uri.path, initHeader = {'Content-Type' =>'application/json', 'x-hub-signature' => generate_signature("#{data}")})
31
- request.body = "#{data}"
32
- begin
33
- response = https.request(request)
34
- response.to_hash
35
- rescue => e
36
- puts "Webhook error: \n\t#{e.message}"
37
- end
9
+ def initialize
10
+ @webhook_url = ENV['WEBHOOK_URL']
11
+ @secret = ENV['WEBHOOK_SECRET']
12
+ @conn = Faraday.new(url: @webhook_url) do |faraday|
13
+ faraday.headers['Content-Type'] = 'application/json'
14
+ end
15
+ end
16
+
17
+ def generate_signature(payload_body)
18
+ "md5=#{OpenSSL::HMAC.hexdigest('md5', ENV['WEBHOOK_SECRET'], payload_body)}"
19
+ end
20
+
21
+ def backup_notification(result, webhook_data, _notification_level)
22
+ return if @webhook_url.nil? || @secret.nil?
23
+ return if result && notification_level == 'error'
24
+
25
+ @date = webhook_data[:date]
26
+ @database = webhook_data[:database]
27
+ @backup_path = webhook_data[:backup_path]
28
+
29
+ @data = {
30
+ domain: ENV['DEFAULT_URL'] || "#{@database} Backup",
31
+ backupPath: result ? @backup_path : nil,
32
+ backupDate: @date
33
+ }.to_json
34
+
35
+ begin
36
+ @response = @conn.post do |req|
37
+ req.headers['x-hub-signature'] = generate_signature(@data.to_s)
38
+ req.body = @data
38
39
  end
40
+
41
+ @response.to_hash
42
+ rescue StandardError => e
43
+ puts "Webhook error: \n\t#{e.message}"
44
+ end
39
45
  end
40
- end
46
+ end
47
+ end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'capistrano/ops/notification/api'
2
4
  require 'capistrano/ops/notification/slack'
3
5
  require 'capistrano/ops/notification/webhook'
4
6
 
5
7
  module Notification
6
- class Error < StandardError; end
7
- end
8
+ class Error < StandardError; end
9
+ end
@@ -12,4 +12,4 @@ module Capistrano
12
12
  end
13
13
  end
14
14
  end
15
- end
15
+ end
@@ -2,7 +2,6 @@
2
2
 
3
3
  # rubocop:disable Metrics/BlockLength
4
4
  namespace :pg do
5
-
6
5
  @database = Rails.configuration.database_configuration[Rails.env]['database']
7
6
  @username = Rails.configuration.database_configuration[Rails.env]['username']
8
7
  @password = Rails.configuration.database_configuration[Rails.env]['password']
@@ -10,34 +9,33 @@ namespace :pg do
10
9
  @portnumber = Rails.configuration.database_configuration[Rails.env]['port']
11
10
  @backup_path = Rails.root.join(Rails.env.development? ? 'tmp/backups' : '../../shared/backups').to_s
12
11
  backups_enabled = Rails.env.production? || ENV['BACKUPS_ENABLED'] == 'true'
13
-
12
+ external_backup = Rails.env.production? || ENV['EXTERNAL_BACKUP_ENABLED'] == 'true'
14
13
  task :dump do
15
14
  unless backups_enabled
16
- p 'dump: Backups are disabled'
15
+ puts 'dump: Backups are disabled'
17
16
  exit(0)
18
17
  end
19
18
 
20
19
  notification = Notification::Api.new
21
20
  commandlist = dump_cmd
22
-
21
+
23
22
  system "mkdir -p #{@backup_path}" unless Dir.exist?(@backup_path)
24
-
23
+
25
24
  result = system(commandlist.join(' && '))
26
-
27
- if ENV['BACKUP_PROVIDER'].present? && result
28
- p "Uploading #{@filename} to #{ENV['BACKUP_PROVIDER']}..."
25
+
26
+ if ENV['BACKUP_PROVIDER'].present? && external_backup && result
27
+ puts "Uploading #{@filename} to #{ENV['BACKUP_PROVIDER']}..."
29
28
  provider = Backup::Api.new
30
29
  begin
31
- provider.upload("#{@backup_path}/#{@filename}", "#{@filename}")
32
- p "#{@filename} uploaded to #{ENV['BACKUP_PROVIDER']}"
30
+ provider.upload("#{@backup_path}/#{@filename}", @filename.to_s)
31
+ puts "#{@filename} uploaded to #{ENV['BACKUP_PROVIDER']}"
33
32
  rescue StandardError => e
34
- p "#{@filename} upload failed: #{e.message}"
33
+ puts "#{@filename} upload failed: #{e.message}"
35
34
  end
36
35
  end
37
- notification.send_backup_notification(result,title,content(result))
38
- p result ? "Backup created: #{@backup_path}/#{@filename}" : "Backup failed removing dump file"
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'
39
38
  system "rm #{@backup_path}/#{@filename}" unless result
40
-
41
39
  end
42
40
 
43
41
  def title
@@ -56,20 +54,19 @@ namespace :pg do
56
54
  end
57
55
 
58
56
  def dump_cmd
59
- date = Time.now.to_i
57
+ @date = Time.now.to_i
60
58
  options = []
61
59
  options << " -d #{@database}" if @database.present?
62
60
  options << " -U #{@username}" if @username.present?
63
61
  options << " -h #{@hostname}" if @hostname.present?
64
62
  options << " -p #{@portnumber}" if @portnumber.present?
65
-
66
- @filename = "#{@database}_#{date}.dump"
67
-
63
+
64
+ @filename = "#{@database}_#{@date}.dump"
65
+
68
66
  commandlist = []
69
67
  commandlist << "export PGPASSWORD='#{@password}'"
70
68
  commandlist << "cd #{@backup_path}"
71
69
  commandlist << "pg_dump -Fc #{options.join('')} > #{@filename}"
72
70
  end
73
-
74
71
  end
75
72
  # rubocop:enable Metrics/BlockLength