etsy-deployinator 1.0.2 → 1.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 +5 -13
- data/.gitignore +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +0 -1
- data/README.md +47 -10
- data/Rakefile +0 -1
- data/bin/deployinator-tailer.rb +2 -2
- data/deployinator.gemspec +5 -1
- data/lib/deployinator.rb +20 -1
- data/lib/deployinator/app.rb +53 -12
- data/lib/deployinator/controller.rb +8 -3
- data/lib/deployinator/helpers.rb +109 -13
- data/lib/deployinator/helpers/concurrency.rb +70 -0
- data/lib/deployinator/helpers/dsh.rb +15 -5
- data/lib/deployinator/helpers/git.rb +16 -9
- data/lib/deployinator/helpers/version.rb +52 -52
- data/lib/deployinator/static/css/maintenance.css +9 -0
- data/lib/deployinator/static/css/style.css +25 -5
- data/lib/deployinator/static/images/maintenance.gif +0 -0
- data/lib/deployinator/static/js/stats_load.js +10 -0
- data/lib/deployinator/templates/exception.mustache +11 -0
- data/lib/deployinator/templates/generic_single_push.mustache +2 -1
- data/lib/deployinator/templates/layout.mustache +5 -4
- data/lib/deployinator/templates/log_table.mustache +18 -5
- data/lib/deployinator/templates/maintenance.mustache +5 -0
- data/lib/deployinator/templates/stats.mustache +180 -0
- data/lib/deployinator/version.rb +1 -1
- data/lib/deployinator/views/log_table.rb +36 -0
- data/lib/deployinator/views/maintenance.rb +15 -0
- data/lib/deployinator/views/stats.rb +96 -0
- data/test/unit/concurrency_test.rb +72 -0
- data/test/unit/git_test.rb +70 -0
- data/test/unit/helpers_test.rb +61 -2
- data/test/unit/version_test.rb +12 -12
- metadata +74 -17
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
YjE2MjYwOGUyOTRmYzI1MmI0Y2RkMjQ3ZGQzOTYyZjZkYzYyOGY3Yg==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f825a038dbf37c774aada076bb6d5c16185ed23e
|
4
|
+
data.tar.gz: df3bcfdc241bb68177693fa2a3603510844dd989
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
NDBiYWM5OTkwYjYyZGQ4MjhiYTliOWJmNDU2YTEzNjFmMmRlOWRiYzMyNzdj
|
11
|
-
Y2E5MGU5Zjc2M2ExMmU2NjU0ZjRmZDVhNDZjZDM0NWY2ZGZmM2I=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZTQ5OWJiNzNkZmQzNzJmOTI1ZTkzMjFmNzNjMzBkZDIxNjhmN2Y0YWVmYjkw
|
14
|
-
NjlhODNjZjc4YzQ4MDgxOWQzYmY1NjdmNzA5NGU2NDEwNzg5NTY5MTFmNzYy
|
15
|
-
YjZmODRmMTE5ZTYwMjA4ZmY4MjY2OTI1NzQ3ZDk0YzczZmVhYWY=
|
6
|
+
metadata.gz: 8a63d5192abcbc7d262f61ef706713988e74c1080111329c0c6fe28781214ec82b441e7e06f6a7fc79e31b371384597f263a3a814f40296c3c846e600b28b2d9
|
7
|
+
data.tar.gz: 21122e91e6b8a87f49c69bb567c4c8a710359f467d98498e65de600e43937c3905cef52c5e13cbd388a2c5514f0d4c06e58cae9bc688492bb0cbce121ab2682b
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
_________ ______ _____ _____
|
3
3
|
______ /_____ ________ ___ /______ _____ _____(_)_______ ______ ___ /_______ ________
|
4
4
|
_ __ / _ _ \___ __ \__ / _ __ \__ / / /__ / __ __ \_ __ `/_ __/_ __ \__ ___/
|
5
|
-
/ /_/ / / __/__ /_/ /_ / / /_/ /_ /_/ / _ / _ / / // /_/ / / /_ / /_/ /_ /
|
6
|
-
\__,_/ \___/ _ .___/ /_/ \____/ _\__, / /_/ /_/ /_/ \__,_/ \__/ \____/ /_/
|
5
|
+
/ /_/ / / __/__ /_/ /_ / / /_/ /_ /_/ / _ / _ / / // /_/ / / /_ / /_/ /_ /
|
6
|
+
\__,_/ \___/ _ .___/ /_/ \____/ _\__, / /_/ /_/ /_/ \__,_/ \__/ \____/ /_/
|
7
7
|
/_/ /____/ Deploy with style!
|
8
8
|
</pre>
|
9
9
|
|
@@ -28,9 +28,9 @@ Deployinator is a deployment framework extracted from Etsy. We've been using it
|
|
28
28
|
## Stacks
|
29
29
|
Deployments are grouped by "stacks". You might have a "web" and "search" stack.
|
30
30
|
|
31
|
-
Each of those stacks might have different deployment environments, such as "staging" or "production".
|
31
|
+
Each of those stacks might have different deployment environments, such as "staging" or "production".
|
32
32
|
|
33
|
-
You can map a button to each of these environments, to create multi-stage pushes within each stack.
|
33
|
+
You can map a button to each of these environments, to create multi-stage pushes within each stack.
|
34
34
|
|
35
35
|
## Installation
|
36
36
|
|
@@ -55,13 +55,14 @@ you can skip the bundler steps.
|
|
55
55
|
- Run the following command to make deployinator gem's rake tasks available to you:
|
56
56
|
|
57
57
|
```sh
|
58
|
+
$ shopt -s xpg_echo
|
58
59
|
$ echo "require 'deployinator'\nload 'deployinator/tasks/initialize.rake' " > Rakefile
|
59
60
|
```
|
60
61
|
|
61
62
|
- Create a binstub for the deploy log tailing backend:
|
62
63
|
|
63
64
|
```sh
|
64
|
-
bundle
|
65
|
+
bundle binstub etsy-deployinator
|
65
66
|
```
|
66
67
|
|
67
68
|
- Initialize the project directory by running the following command replacing ___Company___ with the name of your company/organization. This must start with a capital letter.
|
@@ -116,14 +117,14 @@ underscores but if you forget the rake task will convert from camelcase for you.
|
|
116
117
|
$ bundle exec shotgun --host localhost -p 7777 config.ru
|
117
118
|
```
|
118
119
|
|
119
|
-
- You will probably want a robust server like apache to handle production traffic.
|
120
|
+
- You will probably want a robust server like apache to handle production traffic.
|
120
121
|
|
121
122
|
- The `config/base.rb` file is the base config for the application. Replace all
|
122
123
|
occurences of ___test_stack___ with the name you chose above. Also the example
|
123
124
|
below uses a git repository of http://github.com/etsy/deployinator, feel free to
|
124
125
|
replace this with your specific repository
|
125
126
|
|
126
|
-
```ruby
|
127
|
+
```ruby
|
127
128
|
Deployinator.app_context['test_stack_config'] = {
|
128
129
|
:prod_host => "localhost",
|
129
130
|
:checkout_path => "/tmp/deployinator_dev/"
|
@@ -232,7 +233,7 @@ For example you could wrap your capistrano deploy:
|
|
232
233
|
run_cmd %Q{cap deploy}
|
233
234
|
|
234
235
|
|
235
|
-
#### log_and_stream
|
236
|
+
#### log_and_stream
|
236
237
|
|
237
238
|
Output information to the log file, and the streaming output handler.
|
238
239
|
The real time output console renders HTML so you should use markup here.
|
@@ -241,7 +242,7 @@ The real time output console renders HTML so you should use markup here.
|
|
241
242
|
|
242
243
|
#### log_and_shout
|
243
244
|
|
244
|
-
Output an announcement message with build related information.
|
245
|
+
Output an announcement message with build related information.
|
245
246
|
Also includes hooks for Email.
|
246
247
|
|
247
248
|
log_and_shout({
|
@@ -348,8 +349,20 @@ To set these simple override the methods in your view class. For example:
|
|
348
349
|
This can be done on a global layout that extends the gem's default layout or on
|
349
350
|
a stack by stack basis in their own view.
|
350
351
|
|
352
|
+
### Maintenance mode
|
353
|
+
Deployinator has a setting for maintenance mode, which is mostly useful if you
|
354
|
+
have major changes that affect all stacks and you want to make sure no deploys
|
355
|
+
are going on while you make the change. In order to enable it, you have to set
|
356
|
+
`Deployinator.maintenance_mode = true`. This will make all pages for anyone
|
357
|
+
not in `Deployinator.admin_groups` go to `/maintenance`. As a Deployinator
|
358
|
+
admin you can then still deploy stacks and use the app.
|
359
|
+
|
360
|
+
On the maintenance page Deployinator will show the value of
|
361
|
+
`Deployinator.maintenance_contact` as the place to get help in case you need
|
362
|
+
any or are confused about maintenance mode.
|
363
|
+
|
351
364
|
## Hacking on the gem
|
352
|
-
If you find issues with the gem, or would like to play around with it, you can check it out from git and start hacking on it.
|
365
|
+
If you find issues with the gem, or would like to play around with it, you can check it out from git and start hacking on it.
|
353
366
|
First tell bundler to use your local copy instead by running:
|
354
367
|
```sh
|
355
368
|
$ bundle config local.deployinator /path/to/DeployinatorGem
|
@@ -359,6 +372,30 @@ Next, on every code change, you can install from the checked out gem by running
|
|
359
372
|
$ bundle install --no-deployment && bundle install --deployment
|
360
373
|
```
|
361
374
|
|
375
|
+
### Stats dashboard
|
376
|
+
The `/stats` page pulls from `log/deployinator.log` to show a graph of deployments per day for each stack over time. By default, it shows all stacks. To blacklist or whitelist certain stacks, update `config/base.rb` with:
|
377
|
+
```rb
|
378
|
+
Deployinator.stats_included_stacks = ['my_whitelisted_stack', 'another_whitelisted_stack']
|
379
|
+
Deployinator.stats_ignored_stacks = ['my_stack_to_ignore', 'another_stack_to_ignore']
|
380
|
+
Deployinator.stats_extra_grep = 'Production deploy' # filter out log entries matching this string
|
381
|
+
```
|
382
|
+
|
383
|
+
Whitelisting stacks or applying a custom extra grep can help speed up graph rendering when you have a large log file.
|
384
|
+
|
385
|
+
If at some point you change the name of a stack, you can group the old log entries with the new by adding the following to `config/base.rb`:
|
386
|
+
|
387
|
+
```rb
|
388
|
+
Deployinator.stats_renamed_stacks = [
|
389
|
+
{
|
390
|
+
:previous_stack => {
|
391
|
+
:stack => [ "old_stack_name" ]
|
392
|
+
},
|
393
|
+
:new_name => "new_stack_name"
|
394
|
+
}
|
395
|
+
]
|
396
|
+
```
|
397
|
+
|
398
|
+
|
362
399
|
### Contributing
|
363
400
|
|
364
401
|
1. Fork it
|
data/Rakefile
CHANGED
data/bin/deployinator-tailer.rb
CHANGED
@@ -54,8 +54,8 @@ EM.run do
|
|
54
54
|
if stack && @@channels.key?(stack)
|
55
55
|
@@channels[stack].unsubscribe(session_id)
|
56
56
|
@@channels_count[stack] -= 1
|
57
|
-
if @@channels_count[stack] == 0
|
58
|
-
@@tailers[stack].close
|
57
|
+
if @@channels_count[stack] == 0 && @@tailers.has_key?(stack)
|
58
|
+
@@tailers[stack].close
|
59
59
|
@@tailers.delete(stack)
|
60
60
|
end
|
61
61
|
end
|
data/deployinator.gemspec
CHANGED
@@ -20,14 +20,18 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.required_ruby_version = '>= 1.9.3'
|
21
21
|
|
22
22
|
gem.add_development_dependency "mocha", "~> 0.14"
|
23
|
+
gem.add_development_dependency "test-unit", ">= 3"
|
23
24
|
|
24
25
|
gem.add_runtime_dependency "rake", "~> 10", ">= 10.3.2"
|
25
26
|
gem.add_runtime_dependency "json", "~> 1.8"
|
26
27
|
gem.add_runtime_dependency "mustache", "~> 0.99"
|
28
|
+
# pony pulls in mail >2.0. mail 3.0 needs ruby 2 or greater. to maintain ruby 1.9 compat we're pinning mail here
|
29
|
+
gem.add_runtime_dependency "mail", "2.6.3"
|
27
30
|
gem.add_runtime_dependency "pony", "~> 1.5"
|
28
31
|
gem.add_runtime_dependency "tlsmail", "~> 0.0"
|
29
32
|
gem.add_runtime_dependency "eventmachine", "~> 1.0", ">= 1.0.4"
|
30
|
-
gem.add_runtime_dependency "eventmachine-tail", "~> 0.6", ">= 0.6.
|
33
|
+
gem.add_runtime_dependency "eventmachine-tail", "~> 0.6", ">= 0.6.5"
|
31
34
|
gem.add_runtime_dependency "em-websocket", "~> 0.5", ">= 0.5.1"
|
32
35
|
gem.add_runtime_dependency "sinatra", "~> 1.4", ">=1.4.3"
|
36
|
+
gem.add_runtime_dependency "celluloid", "~> 0.16"
|
33
37
|
end
|
data/lib/deployinator.rb
CHANGED
@@ -54,10 +54,27 @@ module Deployinator
|
|
54
54
|
attr_accessor :admin_groups
|
55
55
|
@admin_groups = []
|
56
56
|
|
57
|
+
attr_accessor :maintenance_mode
|
58
|
+
|
59
|
+
attr_accessor :maintenance_contact
|
60
|
+
|
57
61
|
# the controller class. defaults to Deployinator::Controller
|
58
62
|
# if you override this it should be a subclass of Deployinator::Controller
|
59
63
|
attr_accessor :deploy_controller
|
60
64
|
|
65
|
+
# include stacks in /stats
|
66
|
+
attr_accessor :stats_included_stacks
|
67
|
+
|
68
|
+
# exclude stacks in /stats, even if present in stats_included_stacks
|
69
|
+
attr_accessor :stats_ignored_stacks
|
70
|
+
|
71
|
+
# filter log entries in /stats
|
72
|
+
attr_accessor :stats_extra_grep
|
73
|
+
|
74
|
+
# list of configurations for grouping historical / renamed stacks in /stats
|
75
|
+
attr_accessor :stats_renamed_stacks
|
76
|
+
@@stats_renamed_stacks = []
|
77
|
+
|
61
78
|
def initialize
|
62
79
|
@stack_plugins = {}
|
63
80
|
@global_plugins = []
|
@@ -101,12 +118,14 @@ end
|
|
101
118
|
Deployinator.root_dir = Dir.pwd
|
102
119
|
Deployinator.app_context = {}
|
103
120
|
Deployinator.admin_groups = []
|
121
|
+
Deployinator.maintenance_mode = false
|
122
|
+
Deployinator.maintenance_contact = "admin@deployinator.example.com"
|
104
123
|
Deployinator.global_plugins = []
|
105
124
|
Deployinator.log_file = Deployinator.root(["log", "development.log"])
|
106
125
|
Deployinator.log_path = Deployinator.root(["log", "deployinator.log"])
|
107
126
|
Deployinator.timing_log_path = Deployinator.root(["log", "deployinator-timing.log"])
|
108
127
|
Deployinator.git_sha_length = "10"
|
109
|
-
Deployinator.default_user =
|
128
|
+
Deployinator.default_user = `/usr/bin/whoami`
|
110
129
|
Deployinator.stack_tailer_port = 7778
|
111
130
|
Deployinator.github_host = 'github.com'
|
112
131
|
Deployinator.app_context['context'] = 'dev'
|
data/lib/deployinator/app.rb
CHANGED
@@ -12,6 +12,8 @@ require 'deployinator/views/log'
|
|
12
12
|
require 'deployinator/views/run_logs'
|
13
13
|
require 'deployinator/views/log_table'
|
14
14
|
require 'deployinator/views/deploys_status'
|
15
|
+
require 'deployinator/views/stats'
|
16
|
+
require 'deployinator/views/maintenance'
|
15
17
|
|
16
18
|
module Deployinator
|
17
19
|
class DeployinatorApp < Sinatra::Base
|
@@ -41,12 +43,32 @@ module Deployinator
|
|
41
43
|
register_plugins(nil)
|
42
44
|
init(env)
|
43
45
|
@disabled_override = params[:override].nil? ? false : true
|
46
|
+
pass if [
|
47
|
+
"/maintenance",
|
48
|
+
"/css/maintenance.css",
|
49
|
+
"/images/maintenance.gif",
|
50
|
+
"/static/css/style.css"
|
51
|
+
].include? request.path_info
|
52
|
+
if Deployinator.maintenance_mode == true && !is_admin?
|
53
|
+
redirect "/maintenance"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
before do
|
44
58
|
end
|
45
59
|
|
46
60
|
get '/' do
|
47
61
|
mustache Deployinator::Views::Index
|
48
62
|
end
|
49
63
|
|
64
|
+
get '/stats' do
|
65
|
+
mustache Deployinator::Views::Stats
|
66
|
+
end
|
67
|
+
|
68
|
+
get '/maintenance' do
|
69
|
+
mustache Deployinator::Views::Maintenance
|
70
|
+
end
|
71
|
+
|
50
72
|
get '/run_logs/view/:log' do
|
51
73
|
template = open("#{File.dirname(__FILE__)}/templates/stream.mustache").read
|
52
74
|
template.gsub!("{{ yield }}", "{{{yield}}}")
|
@@ -95,17 +117,6 @@ module Deployinator
|
|
95
117
|
git_head_rev(params[:stack]).chomp
|
96
118
|
end
|
97
119
|
|
98
|
-
get '/:thing' do
|
99
|
-
@stack = params[:thing]
|
100
|
-
@params = params
|
101
|
-
register_plugins(@stack)
|
102
|
-
begin
|
103
|
-
mustache @stack
|
104
|
-
rescue Errno::ENOENT
|
105
|
-
pass
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
120
|
get '/:stack/remove-lock' do
|
110
121
|
stack = params[:stack]
|
111
122
|
unlock_pushes(stack) if can_remove_stack_lock?
|
@@ -129,6 +140,10 @@ module Deployinator
|
|
129
140
|
send_file "#{File.dirname(__FILE__)}/static/css/highlight.css"
|
130
141
|
end
|
131
142
|
|
143
|
+
get '/css/maintenance.css' do
|
144
|
+
send_file "#{File.dirname(__FILE__)}/static/css/maintenance.css"
|
145
|
+
end
|
146
|
+
|
132
147
|
get '/js/flot/jquery.flot.min.js' do
|
133
148
|
send_file "#{File.dirname(__FILE__)}/static/js/flot/jquery.flot.min.js"
|
134
149
|
end
|
@@ -149,6 +164,14 @@ module Deployinator
|
|
149
164
|
send_file "#{File.dirname(__FILE__)}/static/js/jquery.timed_bar.js"
|
150
165
|
end
|
151
166
|
|
167
|
+
get '/js/stats_load.js' do
|
168
|
+
send_file "#{File.dirname(__FILE__)}/static/js/stats_load.js"
|
169
|
+
end
|
170
|
+
|
171
|
+
get '/images/maintenance.gif' do
|
172
|
+
send_file "#{File.dirname(__FILE__)}/static/images/maintenance.gif"
|
173
|
+
end
|
174
|
+
|
152
175
|
get '/deploys_status' do
|
153
176
|
mustache Deployinator::Views::DeploysStatus
|
154
177
|
end
|
@@ -172,6 +195,7 @@ module Deployinator
|
|
172
195
|
# the background.
|
173
196
|
post '/deploys/?' do
|
174
197
|
params[:username] = @username
|
198
|
+
params[:groups] = @groups
|
175
199
|
params[:block] = Proc.new { |line| foo = line }
|
176
200
|
deploy_running = is_deploy_active?(params[:stack], params[:stage])
|
177
201
|
|
@@ -180,7 +204,7 @@ module Deployinator
|
|
180
204
|
return 403
|
181
205
|
end
|
182
206
|
|
183
|
-
fork {
|
207
|
+
pid = fork {
|
184
208
|
Signal.trap("HUP") { exit }
|
185
209
|
Deployinator.setup_logging
|
186
210
|
$0 = get_deploy_process_title(params[:stack], params[:stage])
|
@@ -188,6 +212,7 @@ module Deployinator
|
|
188
212
|
d = controller.new
|
189
213
|
d.run(params)
|
190
214
|
}
|
215
|
+
Process.detach(pid)
|
191
216
|
200
|
192
217
|
end
|
193
218
|
|
@@ -200,5 +225,21 @@ module Deployinator
|
|
200
225
|
|
201
226
|
200
|
202
227
|
end
|
228
|
+
|
229
|
+
get '/:thing' do
|
230
|
+
@stack = params[:thing]
|
231
|
+
|
232
|
+
unless Deployinator.get_stacks.include?(@stack)
|
233
|
+
raise "No such stack #{@stack}"
|
234
|
+
end
|
235
|
+
|
236
|
+
@params = params
|
237
|
+
register_plugins(@stack)
|
238
|
+
begin
|
239
|
+
mustache @stack
|
240
|
+
rescue Errno::ENOENT
|
241
|
+
pass
|
242
|
+
end
|
243
|
+
end
|
203
244
|
end
|
204
245
|
end
|
@@ -29,6 +29,7 @@ module Deployinator
|
|
29
29
|
@deploy_start_time = Time.now.to_i
|
30
30
|
@start_time = Time.now.to_i
|
31
31
|
@username = args[:username]
|
32
|
+
@groups = args[:groups]
|
32
33
|
@host = `hostname -s`
|
33
34
|
@stack = args[:stack]
|
34
35
|
@method = args[:method]
|
@@ -37,7 +38,7 @@ module Deployinator
|
|
37
38
|
|
38
39
|
# This gets the runlog output on the console; is used by log_and_stream
|
39
40
|
@block = args[:block] || Proc.new do |output|
|
40
|
-
$stdout.write output.gsub!(/(<[^>]*>)|\n|\t/
|
41
|
+
$stdout.write output.gsub!(/(<[^>]*>)|\n|\t/m) {" "}
|
41
42
|
$stdout.write "\n"
|
42
43
|
end
|
43
44
|
end
|
@@ -102,7 +103,11 @@ module Deployinator
|
|
102
103
|
deploy_instance = deploy_class.new(options)
|
103
104
|
deploy_instance.register_plugins(options[:stack])
|
104
105
|
|
105
|
-
deploy_instance.lock_pushes(options[:stack], options[:username], options[:method])
|
106
|
+
locked = deploy_instance.lock_pushes(options[:stack], options[:username], options[:method])
|
107
|
+
|
108
|
+
unless locked
|
109
|
+
return deploy_instance
|
110
|
+
end
|
106
111
|
|
107
112
|
@start_time = Time.now
|
108
113
|
deploy_instance.log_and_stream "Push started at #{@start_time.to_i}\n"
|
@@ -115,7 +120,7 @@ module Deployinator
|
|
115
120
|
state = deploy_instance.send(options[:method], options)
|
116
121
|
rescue Exception => e
|
117
122
|
deploy_instance.log_error("There was an exception during this deploy. Aborted!", e)
|
118
|
-
deploy_instance.raise_event(:deploy_error)
|
123
|
+
deploy_instance.raise_event(:deploy_error, {:exception => e})
|
119
124
|
end
|
120
125
|
|
121
126
|
if state.nil? || !state.is_a?(Hash)
|
data/lib/deployinator/helpers.rb
CHANGED
@@ -75,11 +75,37 @@ module Deployinator
|
|
75
75
|
#
|
76
76
|
# Returns nothing
|
77
77
|
def log_and_stream(output)
|
78
|
-
write_file output,
|
78
|
+
write_file output, runlog_filename if runlog_filename
|
79
79
|
return @block.call(output) unless @block.nil?
|
80
80
|
""
|
81
81
|
end
|
82
82
|
|
83
|
+
# gives the filename to send runlog to based on whether we are in the main thread or not
|
84
|
+
# We do this because we want to be able to use log_and_stream seamlessly in a
|
85
|
+
# parallel thread. So, all log_and_stream calls in all but the main thread will
|
86
|
+
# log to a seaparate file
|
87
|
+
# output - String filename to log to
|
88
|
+
def runlog_filename(name=nil)
|
89
|
+
if @filename
|
90
|
+
if Thread.main == Thread.current
|
91
|
+
@filename
|
92
|
+
elsif Thread.current[:logfile_name]
|
93
|
+
Thread.current[:logfile_name]
|
94
|
+
elsif name
|
95
|
+
Thread.current[:logfile_name] = runlog_thread_filename(name)
|
96
|
+
Thread.current[:logfile_name]
|
97
|
+
else
|
98
|
+
raise 'Logfile name not defined in thread. Expecting name parameter to be passed in.'
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# gives us the filename to log to in thread
|
104
|
+
# output - String filename for thread
|
105
|
+
def runlog_thread_filename(name)
|
106
|
+
@filename + '-' + name.to_s
|
107
|
+
end
|
108
|
+
|
83
109
|
# Run external command with timing information
|
84
110
|
# streams and logs the output of the command as well
|
85
111
|
# If the command fails, it is retried some number of times
|
@@ -343,7 +369,7 @@ module Deployinator
|
|
343
369
|
end
|
344
370
|
end
|
345
371
|
|
346
|
-
# Public: gets the
|
372
|
+
# Public: gets the contents from a cache file if it hasn't expired
|
347
373
|
#
|
348
374
|
# Paramaters:
|
349
375
|
# cache_file: path to a cache file
|
@@ -370,14 +396,30 @@ module Deployinator
|
|
370
396
|
end
|
371
397
|
end
|
372
398
|
|
399
|
+
# Public: writes the supplied contents to the cache file, ensuring that
|
400
|
+
# encoding is correct
|
401
|
+
#
|
402
|
+
# Parameters:
|
403
|
+
# cache_file: path to the cache file
|
404
|
+
# content: the data to write to the file
|
405
|
+
#
|
406
|
+
# Returns nothing
|
407
|
+
def write_to_cache(cache_file, contents)
|
408
|
+
File.open(cache_file, 'w:UTF-8') do |f|
|
409
|
+
f.write(contents.force_encoding('UTF-8'))
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
373
413
|
def lock_pushes(stack, who, method)
|
374
|
-
log_and_stream("LOCKING #{stack}")
|
414
|
+
log_and_stream("LOCKING #{stack}<br>")
|
375
415
|
if lock_info = push_lock_info(stack)
|
376
|
-
|
416
|
+
log_and_stream("Pushes locked by #{lock_info[:who]} - #{lock_info[:method]}<br>")
|
417
|
+
return false
|
377
418
|
end
|
378
419
|
|
379
420
|
dt = Time.now.strftime("%m/%d/%Y %H:%M")
|
380
421
|
log_string_to_file("#{who}|#{method}|#{dt}", push_lock_path(stack))
|
422
|
+
return true
|
381
423
|
end
|
382
424
|
|
383
425
|
def unlock_pushes(stack)
|
@@ -424,16 +466,19 @@ module Deployinator
|
|
424
466
|
|
425
467
|
# Public: wrap a block into a timeout
|
426
468
|
#
|
427
|
-
# seconds
|
428
|
-
# description
|
429
|
-
#
|
469
|
+
# seconds - timeout in seconds
|
470
|
+
# description - optional description for logging (default:"")
|
471
|
+
# throw_exception - options param to throw exception back up stack
|
472
|
+
# quiet - optional boolean for logging as a big red warning using the stderr div class
|
473
|
+
# extra_opts - optional hash to pass along to plugins
|
474
|
+
# &block - block to call
|
430
475
|
#
|
431
476
|
# Example
|
432
477
|
# with_timeout(20){system("curl -s http://google.com")}
|
433
478
|
# with_timeout 30 do; system("curl -s http://google.com"); end
|
434
479
|
#
|
435
480
|
# Returns nothing
|
436
|
-
def with_timeout(seconds, description=nil,
|
481
|
+
def with_timeout(seconds, description=nil, throw_exception=false, quiet=false, extra_opts={}, &block)
|
437
482
|
begin
|
438
483
|
Timeout.timeout(seconds) do
|
439
484
|
yield
|
@@ -443,13 +488,22 @@ module Deployinator
|
|
443
488
|
info += " for #{description}" unless description.nil?
|
444
489
|
# log and stream if log filename is not undefined
|
445
490
|
if (/undefined/ =~ @filename).nil?
|
446
|
-
|
491
|
+
if quiet
|
492
|
+
log_and_stream "#{info}<br>"
|
493
|
+
else
|
494
|
+
log_and_stream "<div class=\"stderr\">#{info}</div>"
|
495
|
+
end
|
447
496
|
end
|
448
497
|
state = {
|
449
498
|
:seconds => seconds,
|
450
|
-
:info => info
|
499
|
+
:info => info,
|
500
|
+
:stack => stack,
|
501
|
+
:extra_opts => extra_opts
|
451
502
|
}
|
452
503
|
raise_event(:timeout, state)
|
504
|
+
if throw_exception
|
505
|
+
raise e
|
506
|
+
end
|
453
507
|
""
|
454
508
|
end
|
455
509
|
end
|
@@ -492,7 +546,7 @@ module Deployinator
|
|
492
546
|
raise "Must have stack" unless options[:stack]
|
493
547
|
|
494
548
|
options[:env] ||= "PROD"
|
495
|
-
options[:nice_env]
|
549
|
+
options[:nice_env] ||= nicify_env(options[:env])
|
496
550
|
options[:user] ||= @username
|
497
551
|
options[:start] = @start_time unless options[:start] || ! @start_time
|
498
552
|
|
@@ -548,7 +602,7 @@ module Deployinator
|
|
548
602
|
# stack should be the last part of the log line from the last pipe to the end
|
549
603
|
# modified this to take into account the run_log entry at the end
|
550
604
|
unless stacks.empty? && env.empty?
|
551
|
-
grep = "| egrep '#{env}.*\\|\(#{stacks.join("|")}\)
|
605
|
+
grep = "| egrep '#{env}.*\\|\(#{stacks.join("|")}\)\\|'"
|
552
606
|
end
|
553
607
|
|
554
608
|
# extra grep does another filter to the line, needed to get CONFIG PRODUCTION
|
@@ -600,11 +654,53 @@ module Deployinator
|
|
600
654
|
log_msg = e.nil? ? msg : "#{msg} (#{e.message})"
|
601
655
|
log_and_stream "<div class=\"stderr\">#{log_msg}</div>"
|
602
656
|
if !e.nil?
|
603
|
-
|
657
|
+
begin
|
658
|
+
template = open("#{File.dirname(__FILE__)}/templates/exception.mustache").read
|
659
|
+
|
660
|
+
regex = /(?<file>.*?):(?<line>\d+):.*?`(?<method>.*)'/
|
661
|
+
context = e.backtrace.map do |line|
|
662
|
+
match = regex.match(line)
|
663
|
+
{
|
664
|
+
:file => match['file'],
|
665
|
+
:line => match['line'],
|
666
|
+
:method => match['method']
|
667
|
+
}
|
668
|
+
end
|
669
|
+
|
670
|
+
output = Mustache.render(template, {:exceptions => context})
|
671
|
+
log_and_stream output
|
672
|
+
rescue
|
673
|
+
log_and_stream e.backtrace.inspect
|
674
|
+
end
|
604
675
|
end
|
605
676
|
# This is so we have something in the log if/when this fails
|
606
677
|
puts log_msg
|
607
678
|
end
|
608
679
|
|
680
|
+
# Public: helper method to check if the current user is part of
|
681
|
+
# an group. This method let's you pass in groups for explicitness and
|
682
|
+
# testing but will fall back on @group and Deployinator.admin_groups.
|
683
|
+
#
|
684
|
+
# Param:
|
685
|
+
# user_groups - groups a user is part of
|
686
|
+
# admin_groups - admin groups
|
687
|
+
#
|
688
|
+
# Returns true if the current user is part of and admin group and false
|
689
|
+
# otherwise
|
690
|
+
def is_admin?(user_groups=nil, admin_groups=nil)
|
691
|
+
user_groups ||= @groups
|
692
|
+
admin_groups ||= Deployinator.admin_groups
|
693
|
+
|
694
|
+
return false if user_groups.nil?
|
695
|
+
return false if admin_groups.nil?
|
696
|
+
|
697
|
+
intersected_groups = user_groups & admin_groups
|
698
|
+
|
699
|
+
# if we found an intersection between admin and user groups, the current
|
700
|
+
# user is an admin
|
701
|
+
return (intersected_groups.length > 0)
|
702
|
+
|
703
|
+
end
|
704
|
+
|
609
705
|
end
|
610
706
|
end
|