capistrano-data_plane_api 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +5 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +128 -0
- data/LICENSE.txt +21 -0
- data/README.md +197 -0
- data/Rakefile +16 -0
- data/capistrano-data_plane_api.gemspec +41 -0
- data/exe/cap_data_plane_api +37 -0
- data/lib/capistrano/data_plane_api/configuration/backend.rb +18 -0
- data/lib/capistrano/data_plane_api/configuration/server.rb +22 -0
- data/lib/capistrano/data_plane_api/configuration/symbol.rb +16 -0
- data/lib/capistrano/data_plane_api/configuration.rb +33 -0
- data/lib/capistrano/data_plane_api/deploy/args.rb +241 -0
- data/lib/capistrano/data_plane_api/deploy/deployment_stats.rb +117 -0
- data/lib/capistrano/data_plane_api/deploy/group.rb +100 -0
- data/lib/capistrano/data_plane_api/deploy/helper.rb +51 -0
- data/lib/capistrano/data_plane_api/deploy/server_stats.rb +110 -0
- data/lib/capistrano/data_plane_api/deploy.rb +27 -0
- data/lib/capistrano/data_plane_api/diggable.rb +31 -0
- data/lib/capistrano/data_plane_api/equatable.rb +32 -0
- data/lib/capistrano/data_plane_api/helper.rb +56 -0
- data/lib/capistrano/data_plane_api/hooks.rb +7 -0
- data/lib/capistrano/data_plane_api/show_state.rb +86 -0
- data/lib/capistrano/data_plane_api/tasks.rb +30 -0
- data/lib/capistrano/data_plane_api/terminal_print_loop.rb +43 -0
- data/lib/capistrano/data_plane_api/type.rb +29 -0
- data/lib/capistrano/data_plane_api/version.rb +8 -0
- data/lib/capistrano/data_plane_api.rb +296 -0
- data/readme/failed_deployment_summary.png +0 -0
- data/readme/haproxy_state.png +0 -0
- data/sig/capistrano/data_plane_api.rbs +6 -0
- data/templates/bin/deploy +5 -0
- data/templates/bin/deploy.rb +6 -0
- data/templates/config/data_plane_api.rb +6 -0
- data/templates/config/data_plane_api.yml +51 -0
- metadata +177 -0
@@ -0,0 +1,241 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module Capistrano
|
6
|
+
module DataPlaneApi
|
7
|
+
module Deploy
|
8
|
+
# Class which parses all provided command-line arguments
|
9
|
+
# passed to the deployment script and saves them in
|
10
|
+
# an object.
|
11
|
+
class Args
|
12
|
+
# @return [Array<String>]
|
13
|
+
PRINTABLE_ENV_VARS = %w[BRANCH NO_MIGRATIONS].freeze
|
14
|
+
|
15
|
+
# @param options [Array, nil]
|
16
|
+
# @return [self]
|
17
|
+
def self.parse(options = nil) # rubocop:disable Metrics/MethodLength, Style/ClassMethodsDefinitions
|
18
|
+
args = new
|
19
|
+
|
20
|
+
opt_parser = ::OptionParser.new do |parser| # rubocop:disable Metrics/BlockLength
|
21
|
+
parser.banner = <<~BANNER
|
22
|
+
Usage: bin/deploy [options]
|
23
|
+
|
24
|
+
This script can be used to deploy this app to remote servers.
|
25
|
+
|
26
|
+
BANNER
|
27
|
+
|
28
|
+
parser.on(
|
29
|
+
'-c',
|
30
|
+
'--current',
|
31
|
+
'Deploy from the currently checked out branch'
|
32
|
+
) do |_val|
|
33
|
+
args.branch = `git branch --show-current`.strip
|
34
|
+
::ENV['BRANCH'] = args.branch
|
35
|
+
end
|
36
|
+
|
37
|
+
parser.on(
|
38
|
+
'-t',
|
39
|
+
'--test',
|
40
|
+
'Show the commands that would be executed but do not carry out the deployment'
|
41
|
+
) do |val|
|
42
|
+
args.test = val
|
43
|
+
end
|
44
|
+
|
45
|
+
parser.on(
|
46
|
+
'-g GROUP',
|
47
|
+
'--group=GROUP',
|
48
|
+
'Deploy the code to every server in the passed HAProxy backend/group'
|
49
|
+
) do |val|
|
50
|
+
args.group = val
|
51
|
+
end
|
52
|
+
|
53
|
+
parser.on(
|
54
|
+
'--no-haproxy',
|
55
|
+
'Do not modify the state of any server in HAProxy'
|
56
|
+
) do |val|
|
57
|
+
args.no_haproxy = val
|
58
|
+
::ENV['NO_HAPROXY'] = 'true'
|
59
|
+
end
|
60
|
+
|
61
|
+
parser.on(
|
62
|
+
'--force-haproxy',
|
63
|
+
'Ignore the current state of servers in HAProxy'
|
64
|
+
) do |val|
|
65
|
+
args.force_haproxy = val
|
66
|
+
::ENV['FORCE_HAPROXY'] = 'true'
|
67
|
+
end
|
68
|
+
|
69
|
+
parser.on(
|
70
|
+
'-o ONLY',
|
71
|
+
'--only=ONLY',
|
72
|
+
'Deploy the code only to the passed servers in the same order'
|
73
|
+
) do |val|
|
74
|
+
next unless val
|
75
|
+
|
76
|
+
args.only = val.split(',').map(&:strip).uniq
|
77
|
+
end
|
78
|
+
|
79
|
+
parser.on(
|
80
|
+
'-H',
|
81
|
+
'--haproxy-config',
|
82
|
+
'Show the current HAProxy configuration'
|
83
|
+
) do |val|
|
84
|
+
next unless val
|
85
|
+
|
86
|
+
::Signal.trap('INT') { exit }
|
87
|
+
::Capistrano::DataPlaneApi.show_config
|
88
|
+
exit
|
89
|
+
end
|
90
|
+
|
91
|
+
parser.on(
|
92
|
+
'-S',
|
93
|
+
'--haproxy-state',
|
94
|
+
'Show the current HAProxy state'
|
95
|
+
) do |val|
|
96
|
+
next unless val
|
97
|
+
|
98
|
+
::Signal.trap('INT') { exit }
|
99
|
+
::Capistrano::DataPlaneApi.show_state
|
100
|
+
exit
|
101
|
+
end
|
102
|
+
|
103
|
+
parser.on(
|
104
|
+
'-T',
|
105
|
+
'--tasks',
|
106
|
+
'Print a list of all available deployment Rake tasks'
|
107
|
+
) do |val|
|
108
|
+
next unless val
|
109
|
+
|
110
|
+
puts COLORS.bold.blue('Available Rake Tasks')
|
111
|
+
`cap -T`.each_line do |line|
|
112
|
+
puts line.delete_prefix('cap ')
|
113
|
+
end
|
114
|
+
exit
|
115
|
+
end
|
116
|
+
|
117
|
+
parser.on(
|
118
|
+
'-r RAKE',
|
119
|
+
'--rake=RAKE',
|
120
|
+
'Carry out a particular Rake task on the server'
|
121
|
+
) do |val|
|
122
|
+
next unless val
|
123
|
+
|
124
|
+
args.rake = val
|
125
|
+
end
|
126
|
+
|
127
|
+
parser.on('-h', '--help', 'Prints this help') do
|
128
|
+
puts parser
|
129
|
+
exit
|
130
|
+
end
|
131
|
+
|
132
|
+
parser.on(
|
133
|
+
'-b BRANCH',
|
134
|
+
'--branch=BRANCH',
|
135
|
+
'Deploy the code from the passed Git branch'
|
136
|
+
) do |val|
|
137
|
+
args.branch = val
|
138
|
+
::ENV['BRANCH'] = val
|
139
|
+
end
|
140
|
+
|
141
|
+
parser.on(
|
142
|
+
'--no-migrations',
|
143
|
+
'Do not carry out migrations'
|
144
|
+
) do |val|
|
145
|
+
args.no_migrations = val
|
146
|
+
::ENV['NO_MIGRATIONS'] = 'true'
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
opt_parser.parse!(options || ::ARGV)
|
151
|
+
args.stage = ::ARGV.first&.start_with?('-') ? nil : ::ARGV.first
|
152
|
+
args.prepare_if_one_server
|
153
|
+
args
|
154
|
+
end
|
155
|
+
|
156
|
+
# @return [String, nil] Git branch that the code will be deployed to
|
157
|
+
attr_accessor :branch
|
158
|
+
# @return [Boolean] Runs in test mode if true, only prints commands without executing them
|
159
|
+
attr_accessor :test
|
160
|
+
# @return [String, nil] Name of the HAProxy server group/backend
|
161
|
+
attr_accessor :group
|
162
|
+
# @return [Boolean]
|
163
|
+
attr_accessor :no_haproxy
|
164
|
+
# @return [Boolean]
|
165
|
+
attr_accessor :no_migrations
|
166
|
+
# @return [Boolean]
|
167
|
+
attr_accessor :force_haproxy
|
168
|
+
# @return [Array<String>, nil] Ordered list of servers to which the app will be deployed
|
169
|
+
attr_accessor :only
|
170
|
+
# @return [String, nil] Rake command that will be called remotely (`deploy` by default)
|
171
|
+
attr_accessor :rake
|
172
|
+
# @return [String, nil] Name of the deployment stage/server
|
173
|
+
attr_accessor :stage
|
174
|
+
|
175
|
+
alias test? test
|
176
|
+
|
177
|
+
def initialize
|
178
|
+
@rake = 'deploy'
|
179
|
+
end
|
180
|
+
|
181
|
+
# @return [Boolean]
|
182
|
+
def only?
|
183
|
+
return false if @only.nil?
|
184
|
+
|
185
|
+
@only.any?
|
186
|
+
end
|
187
|
+
|
188
|
+
# @return [void]
|
189
|
+
def prepare_if_one_server
|
190
|
+
return unless one_server?
|
191
|
+
|
192
|
+
server, backend = ::Capistrano::DataPlaneApi.find_server_and_backend(@stage)
|
193
|
+
@only = [server['name']]
|
194
|
+
@group = backend['name']
|
195
|
+
end
|
196
|
+
|
197
|
+
# @param stage [String, Symbol, nil]
|
198
|
+
# @return [String]
|
199
|
+
def deploy_command(stage = nil)
|
200
|
+
used_stage = stage || self.stage
|
201
|
+
"cap #{used_stage} #{rake}"
|
202
|
+
end
|
203
|
+
|
204
|
+
# @param stage [String, Symbol, nil]
|
205
|
+
# @return [String]
|
206
|
+
def humanized_deploy_command(stage = nil)
|
207
|
+
result = ::String.new
|
208
|
+
PRINTABLE_ENV_VARS.each do |env_var_name|
|
209
|
+
next unless (value = ::ENV[env_var_name])
|
210
|
+
|
211
|
+
result << "#{env_var_name}=#{value} "
|
212
|
+
end
|
213
|
+
|
214
|
+
result << deploy_command(stage)
|
215
|
+
result
|
216
|
+
end
|
217
|
+
|
218
|
+
# @param key [Symbol, String]
|
219
|
+
# @return [Object]
|
220
|
+
def [](key)
|
221
|
+
public_send(key)
|
222
|
+
end
|
223
|
+
|
224
|
+
# @param key [Symbol, String]
|
225
|
+
# @param val [Object]
|
226
|
+
# @return [Object]
|
227
|
+
def []=(key, val)
|
228
|
+
public_send("#{key}=", val)
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
# @return [Boolean]
|
234
|
+
def one_server?
|
235
|
+
@stage && @group.nil?
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
module Capistrano
|
6
|
+
module DataPlaneApi
|
7
|
+
module Deploy
|
8
|
+
# Represents a collection of deployment stats for particular servers.
|
9
|
+
class DeploymentStats
|
10
|
+
# @return [Capistrano::DataPlaneApi::Configuration::Backend, nil]
|
11
|
+
# Configuration data of a particular HAProxy backend
|
12
|
+
attr_accessor :backend
|
13
|
+
|
14
|
+
# @return [Time, nil]
|
15
|
+
attr_accessor :start_time
|
16
|
+
|
17
|
+
# @return [Time, nil]
|
18
|
+
attr_accessor :end_time
|
19
|
+
|
20
|
+
# @return [Hash{String => Deploy::ServerStats}]
|
21
|
+
attr_accessor :server_stats
|
22
|
+
|
23
|
+
# @return [Boolean]
|
24
|
+
attr_accessor :success
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@backend = nil
|
28
|
+
@start_time = nil
|
29
|
+
@end_time = nil
|
30
|
+
@success = true
|
31
|
+
@server_stats = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param key [String]
|
35
|
+
# @return [Deploy::ServerStats]
|
36
|
+
def [](key)
|
37
|
+
@server_stats[key]
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param key [String]
|
41
|
+
# @param val [Deploy::ServerStats]
|
42
|
+
def []=(key, val)
|
43
|
+
@server_stats[key] = val
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param servers [Array<Capistrano::DataPlaneApi::Configuration::Server>, Capistrano::DataPlaneApi::Configuration::Server]
|
47
|
+
# @return [void]
|
48
|
+
def create_stats_for(servers)
|
49
|
+
servers = *servers
|
50
|
+
|
51
|
+
servers.each do |server|
|
52
|
+
@server_stats[server.name] = ServerStats.new(server.name, @backend.name)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [String]
|
57
|
+
def to_s
|
58
|
+
update_states_in_stats
|
59
|
+
|
60
|
+
time_string = COLORS.bold.yellow ::Time.now.to_s
|
61
|
+
if success
|
62
|
+
state = COLORS.bold.green 'Successful'
|
63
|
+
time_sentence = 'took'
|
64
|
+
else
|
65
|
+
state = COLORS.bold.red 'Failed'
|
66
|
+
time_sentence = 'failed after'
|
67
|
+
end
|
68
|
+
|
69
|
+
result = ::String.new
|
70
|
+
result << "\n#{time_string}\n\n"
|
71
|
+
result << "#{state} deployment to #{::Capistrano::DataPlaneApi.humanize_backend_name(@backend)}\n"
|
72
|
+
result << " #{time_sentence} #{Helper.humanize_time(seconds)}\n"
|
73
|
+
|
74
|
+
@server_stats.each_value do |stats|
|
75
|
+
result << "\n#{stats}"
|
76
|
+
end
|
77
|
+
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return [Integer, nil] How much time has the deployment taken
|
82
|
+
def seconds
|
83
|
+
@seconds ||= Helper.seconds_since(@start_time, to: @end_time)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# @return [void]
|
89
|
+
def update_states_in_stats
|
90
|
+
return if @update_states_in_stats
|
91
|
+
|
92
|
+
@update_states_in_stats = true
|
93
|
+
update_states_in_stats!
|
94
|
+
end
|
95
|
+
|
96
|
+
def update_states_in_stats!
|
97
|
+
server_states = begin
|
98
|
+
::Capistrano::DataPlaneApi.get_backend_servers_settings(@backend.name).body
|
99
|
+
rescue Error
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
return unless server_states
|
104
|
+
|
105
|
+
server_states.each do |server_state|
|
106
|
+
@server_stats[server_state['name']]&.then do |s|
|
107
|
+
s.admin_state = server_state['admin_state']
|
108
|
+
s.operational_state = server_state['operational_state']
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
module DataPlaneApi
|
5
|
+
module Deploy
|
6
|
+
# Class which deploys the app to all servers
|
7
|
+
# in a particular HAProxy backend/group.
|
8
|
+
class Group
|
9
|
+
class << self
|
10
|
+
# @param args [DeployArgs]
|
11
|
+
# @return [void]
|
12
|
+
def call(args)
|
13
|
+
new(args).call
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param args [DeployArgs]
|
18
|
+
def initialize(args)
|
19
|
+
@args = args
|
20
|
+
@deployment_stats = DeploymentStats.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Boolean, nil] Whether the deployment has been successful
|
24
|
+
def call
|
25
|
+
@backend = ::Capistrano::DataPlaneApi.find_backend(@args.group)
|
26
|
+
@servers = servers(@backend)
|
27
|
+
start_deployment
|
28
|
+
|
29
|
+
success = nil
|
30
|
+
@servers.each do |server|
|
31
|
+
server_stats = @deployment_stats[server.name]
|
32
|
+
puts COLORS.bold.blue("Deploying the app to `#{server.stage}` -- `#{@backend.name}:#{server.name}`")
|
33
|
+
|
34
|
+
puts @args.humanized_deploy_command(server.stage)
|
35
|
+
puts
|
36
|
+
|
37
|
+
next if @args.test?
|
38
|
+
|
39
|
+
server_stats.start_time = ::Time.now
|
40
|
+
deploy_command = @args.deploy_command(server.stage)
|
41
|
+
success = system deploy_command
|
42
|
+
|
43
|
+
server_stats.end_time = ::Time.now
|
44
|
+
server_stats.success = success
|
45
|
+
|
46
|
+
next if success
|
47
|
+
|
48
|
+
puts COLORS.bold.red("Command `#{deploy_command}` failed")
|
49
|
+
break
|
50
|
+
end
|
51
|
+
|
52
|
+
return if @args.test?
|
53
|
+
|
54
|
+
finish_deployment(success: success)
|
55
|
+
print_summary
|
56
|
+
success
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# @return [void]
|
62
|
+
def start_deployment
|
63
|
+
@deployment_stats.tap do |d|
|
64
|
+
d.start_time = ::Time.now
|
65
|
+
d.backend = @backend
|
66
|
+
d.create_stats_for @servers
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param success [Boolean]
|
71
|
+
def finish_deployment(success: true)
|
72
|
+
@deployment_stats.end_time = ::Time.now
|
73
|
+
@deployment_stats.success = success
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [void]
|
77
|
+
def print_summary
|
78
|
+
puts @deployment_stats
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param backend [Capistrano::DataPlaneApi::Configuration::Backend]
|
82
|
+
# @return [Array<Capistrano::DataPlaneApi::Configuration::Server>]
|
83
|
+
def servers(backend)
|
84
|
+
return backend.servers unless @args.only?
|
85
|
+
|
86
|
+
chosen_servers = []
|
87
|
+
@args.only.each do |current_server_name|
|
88
|
+
backend.servers.each do |server|
|
89
|
+
next unless server.name == current_server_name || server.stage == current_server_name
|
90
|
+
|
91
|
+
chosen_servers << server
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
chosen_servers
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
module DataPlaneApi
|
5
|
+
module Deploy
|
6
|
+
# A module which provides some generic helper methods used
|
7
|
+
# in the deployment script.
|
8
|
+
module Helper
|
9
|
+
extend self
|
10
|
+
|
11
|
+
# @param seconds [Integer]
|
12
|
+
# @return [String]
|
13
|
+
def humanize_time(seconds)
|
14
|
+
hours = seconds / 3600
|
15
|
+
rest_seconds = seconds - (hours * 3600)
|
16
|
+
minutes = rest_seconds / 60
|
17
|
+
rest_seconds = seconds - (minutes * 60)
|
18
|
+
|
19
|
+
result = ::String.new
|
20
|
+
|
21
|
+
if rest_seconds.positive?
|
22
|
+
result.prepend "#{rest_seconds}s"
|
23
|
+
styles = %i[bright_green]
|
24
|
+
end
|
25
|
+
|
26
|
+
if minutes.positive?
|
27
|
+
result.prepend "#{minutes}min "
|
28
|
+
styles = %i[bright_yellow]
|
29
|
+
end
|
30
|
+
|
31
|
+
if hours.positive?
|
32
|
+
result.prepend "#{hours}h "
|
33
|
+
styles = %i[bright_red]
|
34
|
+
end
|
35
|
+
|
36
|
+
COLORS.decorate(result.strip, *styles)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Calculate how many seconds have passed
|
40
|
+
# since the given point in time.
|
41
|
+
#
|
42
|
+
# @param time [Time]
|
43
|
+
# @param to [Time]
|
44
|
+
# @return [Integer]
|
45
|
+
def seconds_since(time, to: ::Time.now)
|
46
|
+
(to - time).to_i
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
module DataPlaneApi
|
5
|
+
module Deploy
|
6
|
+
# Represents the stats of a deployment to a particular server
|
7
|
+
class ServerStats
|
8
|
+
# @return [Boolean, nil] `nil` when the deployment hasn't begun
|
9
|
+
# `true` when it has finished successfully, `false` when it has failed
|
10
|
+
attr_accessor :success
|
11
|
+
|
12
|
+
# @return [Time, nil]
|
13
|
+
attr_accessor :start_time
|
14
|
+
|
15
|
+
# @return [Time, nil]
|
16
|
+
attr_accessor :end_time
|
17
|
+
|
18
|
+
# @return [String]
|
19
|
+
attr_accessor :server_name
|
20
|
+
|
21
|
+
# @return [String]
|
22
|
+
attr_accessor :backend_name
|
23
|
+
|
24
|
+
# @return [String, nil]
|
25
|
+
attr_accessor :admin_state
|
26
|
+
|
27
|
+
# @return [String, nil]
|
28
|
+
attr_accessor :operational_state
|
29
|
+
|
30
|
+
# @param server_name [String]
|
31
|
+
# @param backend_name [String]
|
32
|
+
def initialize(server_name, backend_name)
|
33
|
+
@server_name = server_name
|
34
|
+
@backend_name = backend_name
|
35
|
+
@success = nil
|
36
|
+
@seconds = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [String]
|
40
|
+
def to_s
|
41
|
+
time_string =
|
42
|
+
case @success
|
43
|
+
when nil then 'skipped'
|
44
|
+
when false then "failed after #{Helper.humanize_time(seconds)}"
|
45
|
+
when true then "took #{Helper.humanize_time(seconds)}"
|
46
|
+
end
|
47
|
+
|
48
|
+
" #{state_emoji} #{server_title} #{time_string}#{haproxy_states}"
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Integer, nil] How much time has the deployment taken
|
52
|
+
def seconds
|
53
|
+
@seconds ||= Helper.seconds_since(@start_time, to: @end_time)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# @return [String, nil]
|
59
|
+
def humanize_admin_state
|
60
|
+
::Capistrano::DataPlaneApi.humanize_admin_state(@admin_state)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [String, nil]
|
64
|
+
def humanize_operational_state
|
65
|
+
::Capistrano::DataPlaneApi.humanize_operational_state(@operational_state)
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [String, nil]
|
69
|
+
def haproxy_states
|
70
|
+
<<-HAPROXY
|
71
|
+
|
72
|
+
admin_state: #{humanize_admin_state}
|
73
|
+
operational_state: #{humanize_operational_state}
|
74
|
+
HAPROXY
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Hash{String => Symbol}]
|
78
|
+
SERVER_TITLE_COLORS = {
|
79
|
+
nil => :yellow,
|
80
|
+
false => :red,
|
81
|
+
true => :green
|
82
|
+
}.freeze
|
83
|
+
private_constant :SERVER_TITLE_COLORS
|
84
|
+
|
85
|
+
# @return [String]
|
86
|
+
def server_title
|
87
|
+
COLORS.decorate(server_id, :bold, SERVER_TITLE_COLORS[@success])
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [String]
|
91
|
+
def server_id
|
92
|
+
"#{@backend_name}:#{@server_name}"
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [Hash{Boolean, nil => Symbol}]
|
96
|
+
STATE_EMOJIS = {
|
97
|
+
nil => '🟡',
|
98
|
+
false => '❌',
|
99
|
+
true => '✅'
|
100
|
+
}.freeze
|
101
|
+
private_constant :STATE_EMOJIS
|
102
|
+
|
103
|
+
# @return [String]
|
104
|
+
def state_emoji
|
105
|
+
STATE_EMOJIS[@success]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../data_plane_api'
|
4
|
+
|
5
|
+
module Capistrano
|
6
|
+
module DataPlaneApi
|
7
|
+
# Contains code used in the deployment script.
|
8
|
+
module Deploy
|
9
|
+
class << self
|
10
|
+
# @return [void]
|
11
|
+
def call
|
12
|
+
args = Args.parse
|
13
|
+
puts COLORS.bold.blue('Running the deployment script')
|
14
|
+
|
15
|
+
result = Group.call(args)
|
16
|
+
abort if result == false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require_relative 'deploy/args'
|
24
|
+
require_relative 'deploy/helper'
|
25
|
+
require_relative 'deploy/deployment_stats'
|
26
|
+
require_relative 'deploy/server_stats'
|
27
|
+
require_relative 'deploy/group'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
module DataPlaneApi
|
5
|
+
# Include in a class to grant it the `#dig` method.
|
6
|
+
# It's implemented so that it calls public methods.
|
7
|
+
module Diggable
|
8
|
+
# Extracts the nested value specified by the sequence of key objects by calling `dig` at each step,
|
9
|
+
# returning `nil` if any intermediate step is `nil`.
|
10
|
+
#
|
11
|
+
# This implementation of `dig` uses `public_send` under the hood.
|
12
|
+
#
|
13
|
+
# @raise [TypeError] value has no #dig method
|
14
|
+
# @return [Object]
|
15
|
+
def dig(*args)
|
16
|
+
return unless args.size.positive?
|
17
|
+
|
18
|
+
return unless respond_to?(key = args.shift)
|
19
|
+
|
20
|
+
value = public_send(key)
|
21
|
+
return if value.nil?
|
22
|
+
return value if args.size.zero?
|
23
|
+
raise ::TypeError, "#{value.class} does not have #dig method" unless value.respond_to?(:dig)
|
24
|
+
|
25
|
+
value.dig(*args)
|
26
|
+
rescue ::ArgumentError
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Capistrano
|
4
|
+
module DataPlaneApi
|
5
|
+
# Include in a class to make its instances capable
|
6
|
+
# of comparing themselves with other objects of the same class
|
7
|
+
# by calling `==` on their instance variables.
|
8
|
+
module Equatable
|
9
|
+
# @param other [Object]
|
10
|
+
# @return [Boolean]
|
11
|
+
def eql?(other)
|
12
|
+
return true if equal?(other)
|
13
|
+
return false unless other.is_a?(self.class) || is_a?(other.class)
|
14
|
+
|
15
|
+
# @type [Set<Symbol>]
|
16
|
+
self_ivars = instance_variables.to_set
|
17
|
+
# @type [Set<Symbol>]
|
18
|
+
other_ivars = other.instance_variables.to_set
|
19
|
+
|
20
|
+
return false unless self_ivars == other_ivars
|
21
|
+
|
22
|
+
self_ivars.each do |ivar|
|
23
|
+
return false if instance_variable_get(ivar) != other.instance_variable_get(ivar)
|
24
|
+
end
|
25
|
+
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
alias == eql?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|