capistrano-data_plane_api 0.1.0
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 +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
|