omnibus-ctl 0.3.6 → 0.5.0

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
  SHA1:
3
- metadata.gz: bc652bff024b528dbb20225d090ee3b6d89cac3e
4
- data.tar.gz: 591824a229726e433072259539bd8d059fb167a3
3
+ metadata.gz: ba3442fde147f0b637bb2ce3dfe8f61952c00c5d
4
+ data.tar.gz: 69b92b796d3f0afbbfe13bc429fcc09cb8ad8043
5
5
  SHA512:
6
- metadata.gz: 8b3890b07473e49d84fc0e44990665c309873529f2ca5feccef948687d44045b930eb6842649c342c67c98d2cb84b96165bea57474867b2c18345e910382f3b2
7
- data.tar.gz: 3f875478e87ddf9c6184f84e0bf331753018d8af638f9a141bcc6d3dd55fb43901cadbe290981558e596f0f9011c0d7d75e653c6721ca32805a782ef54b7b3f9
6
+ metadata.gz: 5d184d6b71831ed278e907ee83d3b73d26876dbcd4d1aae0a0ed5ac6b7e8990629e6a362b31132776878f6cf3ac0964e093c72bc098c385b9a746f100c1c2f34
7
+ data.tar.gz: e2b700aa51a77c273846a1669ebbbda5c9a5c7bb520e277c1199dbabe66edbe248094b6614bac68a5679d5738e293feb26ea4f1265541b860d1bc782c84d8e46
data/README.md CHANGED
@@ -1,32 +1,32 @@
1
- ## omnibus-ctl
1
+ # omnibus-ctl
2
2
 
3
3
  [![Build Status Master](https://travis-ci.org/chef/omnibus-ctl.svg?branch=master)](https://travis-ci.org/chef/omnibus-ctl)
4
4
 
5
5
  omnibus-ctl provides service control and configuration for omnibus packages.
6
6
 
7
- Not much to see here yet.
7
+ For an introduction, please see this blog post [Omnibus-ctl: What is it and what can it do for you?](https://www.chef.io/blog/2015/05/26/omnibus-ctl-what-is-it-and-what-can-it-do-for-you/).
8
8
 
9
- ### Run the Tests!
9
+ ## Run the Tests!
10
10
 
11
11
  There are tests in this repo that should be run before merging to master in the `spec` directory.
12
12
 
13
13
  To run them, first install rspec via bundler:
14
14
 
15
15
  ```
16
- bundle install --binstubs
16
+ bundle install
17
17
  ```
18
18
 
19
19
  Then run the tests:
20
20
 
21
21
  ```
22
- ./bin/rspec spec/
22
+ bin/rspec
23
23
  ```
24
24
 
25
- ### Framework API
25
+ ## Framework API
26
26
 
27
27
  There are two main functions you will use in your `*-ctl` project to add commands.
28
28
 
29
- #### add_command_under_category(string, string, string, int, ruby_block)
29
+ ### add_command_under_category(string, string, string, int, ruby_block)
30
30
 
31
31
  This method will add a new command to your ctl under a category, useful for grouping similar commands together logically in help output.
32
32
 
@@ -38,13 +38,13 @@ Input arguments:
38
38
  4. Arity. TODO: Due to current bug, this must be 2, I believe. We should fix this.
39
39
  5. Ruby block. Ruby code to be executed when your command is run (arguments to that command will be passed into the block).
40
40
 
41
- #### add_command(string, string, int, ruby_block)
41
+ ### add_command(string, string, int, ruby_block)
42
42
 
43
43
  This method will add a new command to your ctl without a category. It will be displayed above all categories when the help command is called.
44
44
 
45
45
  Input arguments are the same as `add_command_under_category` except 2 doesn't exist.
46
46
 
47
- #### Sample Output
47
+ ### Sample Output
48
48
 
49
49
  ```
50
50
  # sample-ctl help
@@ -67,11 +67,24 @@ Another Category:
67
67
 
68
68
  If you only use `add_command_under_category` to add your custom commands, everything will be outputted under a category.
69
69
 
70
- ### Licensing
70
+ ## Releasing
71
+
72
+ *NOTE: Versions prior to 0.3.6 do not use a "v" prefix for their tags. Current
73
+ versions do.*
74
+
75
+ * Update the version in lib/omnibus-ctl/version.rb.
76
+ * Update the [Change log](CHANGELOG.md).
77
+ * Commit those changes.
78
+ * Make sure you are an owner of the
79
+ [omnibus-ctl gem on RubyGems.org](https://rubygems.org/gems/omnibus-ctl). If
80
+ you aren't, contact one of the owners to be added.
81
+ * `rake release`. This will tag the version, push it to GitHub and RubyGems.
82
+
83
+ ## License
71
84
 
72
85
  See the LICENSE file for details.
73
86
 
74
- Copyright: Copyright (c) 2012 Opscode, Inc.
87
+ Copyright: Copyright (c) 2012-2015 Chef Software, Inc.
75
88
  License: Apache License, Version 2.0
76
89
 
77
90
  Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,5 +1,5 @@
1
1
  module Omnibus
2
2
  class Ctl
3
- VERSION = "0.3.6"
3
+ VERSION = "0.5.0"
4
4
  end
5
5
  end
data/lib/omnibus-ctl.rb CHANGED
@@ -1,5 +1,4 @@
1
- #
2
- # Copyright:: Copyright (c) 2012 Opscode, Inc.
1
+ # Copyright (c) 2012-2015 Chef Software, Inc.
3
2
  # License:: Apache License, Version 2.0
4
3
  #
5
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +18,10 @@ require "omnibus-ctl/version"
19
18
  require 'json'
20
19
  require 'fileutils'
21
20
 
21
+ # For license checks
22
+ require 'io/console'
23
+ require 'io/wait'
24
+
22
25
  module Omnibus
23
26
  class Ctl
24
27
 
@@ -26,16 +29,19 @@ module Omnibus
26
29
 
27
30
  SV_COMMAND_NAMES = %w[status up down once pause cont hup alarm interrupt quit
28
31
  term kill start stop restart shutdown force-stop
29
- force-reload force-restart force-shutdown check]
32
+ force-reload force-restart force-shutdown check usr1 usr2]
30
33
 
31
34
  attr_accessor :name, :display_name, :log_exclude, :base_path, :sv_path,
32
35
  :service_path, :etc_path, :data_path, :log_path, :command_map, :category_command_map,
33
36
  :fh_output, :kill_users, :verbose, :log_path_exclude
34
37
 
35
- def initialize(name, service_commands=true)
38
+ attr_reader :backup_dir, :exe_name
39
+
40
+
41
+ def initialize(name, merge_service_commands=true, disp_name = nil)
36
42
  @name = name
37
- @service_commands = service_commands
38
- @display_name = name
43
+ @service_commands = merge_service_commands
44
+ @display_name = disp_name || name
39
45
  @base_path = "/opt/#{name}"
40
46
  @sv_path = File.join(@base_path, "sv")
41
47
  @service_path = File.join(@base_path, "service")
@@ -47,6 +53,10 @@ module Omnibus
47
53
  @fh_output = STDOUT
48
54
  @kill_users = []
49
55
  @verbose = false
56
+ @quiet = false
57
+ @exe_name = File.basename($0)
58
+ @force_exit = false
59
+
50
60
  # backwards compat command map that does not have categories
51
61
  @command_map = { }
52
62
 
@@ -59,7 +69,7 @@ module Omnibus
59
69
  },
60
70
  "reconfigure" => {
61
71
  :desc => "Reconfigure the application.",
62
- :arity => 1
72
+ :arity => 2
63
73
  },
64
74
  "cleanse" => {
65
75
  :desc => "Delete *all* #{display_name} data, and start from scratch.",
@@ -124,19 +134,31 @@ module Omnibus
124
134
  "graceful-kill" => {
125
135
  :desc => "Attempt a graceful stop, then SIGKILL the entire process group.",
126
136
  :arity => 2
137
+ },
138
+ "usr1" => {
139
+ :desc => "Send the services a USR1.",
140
+ :arity => 2
141
+ },
142
+ "usr2" => {
143
+ :desc => "Send the services a USR2.",
144
+ :arity => 2
127
145
  }
128
146
  }
129
147
  }
130
148
  @category_command_map.merge!(service_command_map) if service_commands?
131
149
  end
132
150
 
151
+ def self.to_method_name(name)
152
+ name.gsub(/-/, '_').to_sym
153
+ end
154
+ def to_method_name(name)
155
+ Ctl.to_method_name(name)
156
+ end
157
+
133
158
  SV_COMMAND_NAMES.each do |sv_cmd|
134
- method_name = sv_cmd.gsub(/-/, "_")
135
- Omnibus::Ctl.class_eval <<-EOH
136
- def #{method_name}(*args)
159
+ define_method to_method_name(sv_cmd) do |*args|
137
160
  run_sv_command(*args)
138
161
  end
139
- EOH
140
162
  end
141
163
 
142
164
  # merges category_command_map and command_map,
@@ -160,15 +182,15 @@ module Omnibus
160
182
  end
161
183
 
162
184
  def load_file(filepath)
163
- eval(IO.read(filepath))
185
+ eval(IO.read(filepath), nil, filepath, 1)
164
186
  end
165
187
 
166
188
  def add_command(name, description, arity=1, &block)
167
189
  @command_map[name] = { :desc => description, :arity => arity }
168
190
  metaclass = class << self; self; end
169
191
  # Ruby does not like dashes in method names
170
- method_name = name.gsub(/-/, "_")
171
- metaclass.send(:define_method, method_name.to_sym) { |*args| block.call(*args) }
192
+ method_name = to_method_name(name).to_sym
193
+ metaclass.send(:define_method, method_name) { |*args| block.call(*args) }
172
194
  end
173
195
 
174
196
  def add_command_under_category(name, category, description, arity=1, &block)
@@ -176,13 +198,13 @@ module Omnibus
176
198
  @category_command_map[category] = {} unless @category_command_map.has_key?(category)
177
199
  @category_command_map[category][name] = { :desc => description, :arity => arity }
178
200
  metaclass = class << self; self; end
179
- # Ruby does not like dashes in method names
180
- method_name = name.gsub(/-/, "_")
181
- metaclass.send(:define_method, method_name.to_sym) { |*args| block.call(*args) }
201
+ method_name = to_method_name(name).to_sym
202
+ metaclass.send(:define_method, method_name) { |*args| block.call(*args) }
182
203
  end
183
204
 
184
- def exit!(error_code)
185
- exit error_code
205
+ def exit!(code)
206
+ @force_exit = true
207
+ code
186
208
  end
187
209
 
188
210
  def log(msg)
@@ -218,27 +240,22 @@ module Omnibus
218
240
  exit! 0
219
241
  end
220
242
 
221
- def cleanup_procs_and_nuke(filestr)
222
- begin
223
- run_sv_command("stop")
224
- rescue SystemExit
225
- end
243
+ def cleanup_procs_and_nuke(filestr, calling_method = nil)
244
+ run_sv_command("stop")
226
245
 
227
246
  FileUtils.rm_f("/etc/init/#{name}-runsvdir.conf") if File.exists?("/etc/init/#{name}-runsvdir.conf")
228
247
  run_command("egrep -v '#{base_path}/embedded/bin/runsvdir-start' /etc/inittab > /etc/inittab.new && mv /etc/inittab.new /etc/inittab") if File.exists?("/etc/inittab")
229
248
  run_command("kill -1 1")
230
249
 
231
- backup_dir = Time.now.strftime("/root/#{name}-cleanse-%FT%R")
250
+ @backup_dir = Time.now.strftime("/root/#{name}-cleanse-%FT%R")
251
+
232
252
  FileUtils.mkdir_p("/root") unless File.exists?("/root")
233
253
  FileUtils.rm_rf(backup_dir)
234
254
  FileUtils.cp_r(etc_path, backup_dir) if File.exists?(etc_path)
235
255
  run_command("rm -rf #{filestr}")
256
+ graceful_kill
236
257
 
237
- begin
238
- graceful_kill
239
- rescue SystemExit
240
- end
241
-
258
+ log "Terminating processes running under application users. This will take a few seconds."
242
259
  run_command("pkill -HUP -u #{kill_users.join(',')}") if kill_users.length > 0
243
260
  run_command("pkill -HUP -f 'runsvdir -P #{service_path}'")
244
261
  sleep 3
@@ -251,7 +268,6 @@ module Omnibus
251
268
  get_all_services.each do |die_daemon_die|
252
269
  run_command("pkill -KILL -f 'runsv #{die_daemon_die}'")
253
270
  end
254
-
255
271
  log "Your config files have been backed up to #{backup_dir}."
256
272
  exit! 0
257
273
  end
@@ -260,14 +276,55 @@ module Omnibus
260
276
  cleanup_procs_and_nuke("/tmp/opt")
261
277
  end
262
278
 
263
- def cleanse(*args)
264
- log "This will delete *all* configuration, log, and variable data associated with this application.\n\n*** You have 60 seconds to hit CTRL-C ***\n\n"
265
- unless args[1] == "yes"
266
- sleep 60
279
+ def scary_cleanse_warning(*args)
280
+ just_do_it = args.include?("yes")
281
+ with_external = ARGV.include?("--with-external")
282
+ log <<EOM
283
+ *******************************************************************
284
+ * * * * * * * * * * * STOP AND READ * * * * * * * * * *
285
+ *******************************************************************
286
+ This command will delete *all* local configuration, log, and
287
+ variable data associated with #{display_name}.
288
+ EOM
289
+ if (with_external)
290
+ log <<EOM
291
+ This will also delete externally hosted #{display_name} data.
292
+ This means that any service you have configured as 'external'
293
+ will have any #{display_name} permanently deleted.
294
+ EOM
295
+ elsif (not external_services.empty?)
296
+ log <<EOM
297
+
298
+ Important note: If you also wish to delete externally hosted #{display_name}
299
+ data, please hit CTRL+C now and run '#{exe_name} cleanse --with-external'
300
+ EOM
301
+ end
302
+
303
+ unless just_do_it
304
+ data = with_external ? "local, and remote data" : "and local data"
305
+ log <<EOM
306
+
307
+ You have 60 seconds to hit CTRL-C before configuration,
308
+ logs, #{data} for this application are permanently
309
+ deleted.
310
+ *******************************************************************
311
+
312
+ EOM
313
+ begin
314
+ sleep 60
315
+ rescue Interrupt
316
+ log ""
317
+ exit 0
318
+ end
267
319
  end
268
- cleanup_procs_and_nuke("#{service_path}/* /tmp/opt #{data_path} #{etc_path} #{log_path}")
269
320
  end
270
321
 
322
+ def cleanse(*args)
323
+ scary_cleanse_warning(*args)
324
+ cleanup_procs_and_nuke("#{service_path}/* /tmp/opt #{data_path} #{etc_path} #{log_path}", "cleanse")
325
+ end
326
+
327
+
271
328
  def get_all_services_files
272
329
  Dir[File.join(sv_path, '*')]
273
330
  end
@@ -282,6 +339,8 @@ module Omnibus
282
339
 
283
340
  def run_sv_command(sv_cmd, service=nil)
284
341
  exit_status = 0
342
+ sv_cmd = "1" if sv_cmd == "usr1"
343
+ sv_cmd = "2" if sv_cmd == "usr2"
285
344
  if service
286
345
  exit_status += run_sv_command_for_service(sv_cmd, service)
287
346
  else
@@ -296,10 +355,10 @@ module Omnibus
296
355
  def run_sv_command_for_service(sv_cmd, service_name)
297
356
  if service_enabled?(service_name)
298
357
  status = run_command("#{base_path}/init/#{service_name} #{sv_cmd}")
299
- return status.exitstatus
358
+ status.exitstatus
300
359
  else
301
360
  log "#{service_name} disabled" if sv_cmd == "status" && verbose
302
- return 0
361
+ 0
303
362
  end
304
363
  end
305
364
 
@@ -341,12 +400,7 @@ module Omnibus
341
400
  # not exist), we know that this will be a new server, and we don't
342
401
  # have to worry about pre-upgrade services hanging around. We can safely
343
402
  # return an empty array when running_config is nil
344
- if (cfg = running_config)
345
- key = package_name.gsub(/-/, '_')
346
- cfg[key]["removed_services"] || []
347
- else
348
- []
349
- end
403
+ running_package_config["removed_services"] || []
350
404
  end
351
405
 
352
406
  # hidden services are configured via the attributes file in
@@ -359,12 +413,7 @@ module Omnibus
359
413
  # not exist), we don't want to return nil, just return an empty array.
360
414
  # worse result with doing that is services that we don't want to show up in
361
415
  # c-s-c status will show up.
362
- if (cfg = running_config)
363
- key = package_name.gsub(/-/, '_')
364
- cfg[key]["hidden_services"] || []
365
- else
366
- []
367
- end
416
+ running_package_config["hidden_services"] || []
368
417
  end
369
418
 
370
419
  # translate the name from the config to the package name.
@@ -389,41 +438,134 @@ module Omnibus
389
438
  end
390
439
  end
391
440
 
441
+ # Helper function that returns the hash of config hashes that have the key 'external' : true
442
+ # in the running config. If none exist it will return an empty hash.
443
+ def external_services
444
+ @external_services ||= running_package_config.select { |k, v| v.class == Hash and v["external"] == true }
445
+ end
446
+
447
+ # Helper function that returns true if an external service entry exists for
448
+ # the named service
449
+ def service_external?(service)
450
+ return false if service.nil?
451
+ return external_services.has_key? service
452
+ end
453
+
454
+ # Gives package config from the running_config.
455
+ # If there is no running config or if package_name doens't
456
+ # reference a valid key, this will return an empty hash
457
+ def running_package_config
458
+ if (cfg = running_config)
459
+ cfg[package_name.gsub(/-/, '_')] || {}
460
+ else
461
+ {}
462
+ end
463
+ end
464
+
465
+ # This returns running_config[package][service].
466
+ #
467
+ # If there is no running_config or is no matching key
468
+ # it will return nil.
469
+ def running_service_config(service)
470
+ running_package_config[service]
471
+ end
472
+
392
473
  def remove_old_node_state
393
474
  node_cache_path = "#{base_path}/embedded/nodes/"
394
475
  status = run_command("rm -rf #{node_cache_path}")
395
- if ! status.success?
476
+ if !status.success?
396
477
  log "Could not remove cached node state!"
397
- exit! 1
478
+ exit 1
398
479
  end
399
480
  end
400
481
 
401
482
  def run_chef(attr_location, args='')
483
+ if @verbose
484
+ log_level = "-L debug"
485
+ elsif @quiet
486
+ # null formatter is awfully quiet, so let them know we're doing something.
487
+ log "Reconfiguring #{display_name}."
488
+ log_level = "-L fatal -F null"
489
+ else
490
+ log_level = ""
491
+ end
402
492
  remove_old_node_state
403
- cmd = "#{base_path}/embedded/bin/chef-client -z -c #{base_path}/embedded/cookbooks/solo.rb -j #{attr_location}"
493
+ cmd = "#{base_path}/embedded/bin/chef-client #{log_level} -z -c #{base_path}/embedded/cookbooks/solo.rb -j #{attr_location}"
404
494
  cmd += " #{args}" unless args.empty?
405
495
  run_command(cmd)
406
496
  end
407
497
 
408
498
  def show_config(*args)
409
- status = run_chef("#{base_path}/embedded/cookbooks/show-config.json", "-l fatal")
410
- if status.success?
411
- exit! 0
412
- else
413
- exit! 1
414
- end
499
+ status = run_chef("#{base_path}/embedded/cookbooks/show-config.json", "-l fatal -F null")
500
+ exit! status.success? ? 0 : 1
415
501
  end
416
502
 
417
- def reconfigure(exit_on_success=true)
503
+ def reconfigure(*args)
504
+ # args being passed to this command does not include the ones that are
505
+ # starting with "-". See #is_option? method. If it is starting with "-"
506
+ # then it is treated as a option and we need to look for them in ARGV.
507
+ check_license_acceptance(ARGV.include?("--accept-license"))
508
+
418
509
  status = run_chef("#{base_path}/embedded/cookbooks/dna.json")
419
510
  if status.success?
420
511
  log "#{display_name} Reconfigured!"
421
- exit! 0 if exit_on_success
512
+ exit! 0
422
513
  else
423
514
  exit! 1
424
515
  end
425
516
  end
426
517
 
518
+ def check_license_acceptance(override_accept = false)
519
+ license_guard_file_path = File.join(data_path, ".license.accepted")
520
+
521
+ # If the project does not have a license we do not have
522
+ # any license to accept.
523
+ return unless File.exist?(project_license_path)
524
+
525
+ if !File.exist?(license_guard_file_path)
526
+ if override_accept || ask_license_acceptance
527
+ FileUtils.mkdir_p(data_path)
528
+ FileUtils.touch(license_guard_file_path)
529
+ else
530
+ log "Please accept the software license agreement to continue."
531
+ exit(1)
532
+ end
533
+ end
534
+ end
535
+
536
+ def ask_license_acceptance
537
+ log "To use this software, you must agree to the terms of the software license agreement."
538
+
539
+ if !STDIN.tty?
540
+ log "Please view and accept the software license agreement, or pass --accept-license."
541
+ exit(1)
542
+ end
543
+
544
+ log "Press any key to continue."
545
+ user_input = STDIN.getch
546
+ user_input << STDIN.getch while STDIN.ready?
547
+ # No need to check for user input
548
+
549
+ system("less #{project_license_path}")
550
+
551
+ loop do
552
+ log "Type 'yes' to accept the software license agreement, or anything else to cancel."
553
+
554
+ user_input = STDIN.gets.chomp.downcase
555
+ case user_input
556
+ when "yes"
557
+ return true
558
+ else
559
+ log "You have not accepted the software license agreement."
560
+ return false
561
+ end
562
+ end
563
+ end
564
+
565
+ def project_license_path
566
+ File.join(base_path, "LICENSE")
567
+ end
568
+
427
569
  def tail(*args)
428
570
  # find /var/log -type f -not -path '*/sasl/*' | grep -E -v '(lock|@|tgz|gzip)' | xargs tail --follow=name --retry
429
571
  command = "find #{log_path}"
@@ -468,14 +610,14 @@ module Omnibus
468
610
  end
469
611
  else
470
612
  log "#{service_name} disabled, not stopping"
471
- exit_status = 1
613
+ exit_status =1
472
614
  end
473
615
  end
474
616
  exit! exit_status
475
617
  end
476
618
 
477
619
  def help(*args)
478
- log "#{$0}: command (subcommand)\n"
620
+ log "#{exe_name}: command (subcommand)\n"
479
621
  command_map.keys.sort.each do |command|
480
622
  log command
481
623
  log " #{command_map[command][:desc]}"
@@ -491,16 +633,22 @@ module Omnibus
491
633
  log " #{commands[command][:desc]}"
492
634
  end
493
635
  end
494
- exit! 1
636
+ # Help is not an error so exit with 0. In cases where we display help as a result of an error
637
+ # the framework will handle setting proper exit code.
638
+ exit! 0
495
639
  end
496
640
 
497
- # Set options. Silently ignore bad options.
498
- # This allows the test subcommand to pass on pedant options
499
- def parse_options!(args)
500
- args.each do |option|
641
+ # Set global options and remove them from the args list we pass
642
+ # into commands.
643
+ def parse_options(args)
644
+ args.select do |option|
501
645
  case option
646
+ when "--quiet", "-q"
647
+ @quiet = true
648
+ false
502
649
  when "--verbose", "-v"
503
650
  @verbose = true
651
+ false
504
652
  end
505
653
  end
506
654
  end
@@ -526,6 +674,10 @@ module Omnibus
526
674
  end
527
675
  end
528
676
 
677
+ # Previously this would exit immediately with the provided
678
+ # exit code; however this would prevent post-run hooks from continuing
679
+ # Instead, we'll just track whether a an exit was requested and use that
680
+ # to determine how we exit from 'run'
529
681
  def run(args)
530
682
  # Ensure Omnibus related binaries are in the PATH
531
683
  ENV["PATH"] = [File.join(base_path, "bin"),
@@ -549,31 +701,191 @@ module Omnibus
549
701
  service = args[1]
550
702
  end
551
703
 
552
- # returns either hash content of comamnd or nil
704
+ # returns either hash content of command or nil
553
705
  command = retrieve_command(command_to_run)
554
-
555
706
  if command.nil?
556
707
  log "I don't know that command."
557
708
  if args.length == 2
558
- log "Did you mean: #{$0} #{service} #{command_to_run}?"
709
+ log "Did you mean: #{exe_name} #{service} #{command_to_run}?"
559
710
  end
560
711
  help
712
+ Kernel.exit 1
561
713
  end
562
714
 
563
715
  if args.length > 1 && command[:arity] != 2
564
716
  log "The command #{command_to_run} does not accept any arguments"
565
- exit! 2
717
+ Kernel.exit 2
566
718
  end
567
719
 
568
- parse_options! options
720
+ parse_options options
721
+ @force_exit = false
722
+ exit_code = 0
569
723
 
570
- method_to_call = command_to_run.gsub(/-/, '_')
571
724
  # Filter args to just command and service. If you are loading
572
725
  # custom commands and need access to the command line argument,
573
726
  # use ARGV directly.
574
727
  actual_args = [command_to_run, service].reject(&:nil?)
575
- self.send(method_to_call.to_sym, *actual_args)
728
+ if command_pre_hook(*actual_args)
729
+ method_to_call = to_method_name(command_to_run)
730
+ begin
731
+ ret = send(method_to_call, *actual_args)
732
+ rescue SystemExit => e
733
+ @force_exit = true
734
+ ret = e.status
735
+ end
736
+ command_post_hook(*actual_args)
737
+ exit_code = ret unless ret.nil?
738
+ else
739
+ exit_code = 8
740
+ @force_exit = true
741
+ end
742
+
743
+ if @force_exit
744
+ Kernel.exit exit_code
745
+ else
746
+ exit_code
747
+ end
748
+ end
749
+
750
+ # Below are some basic command hooks that do the right thing
751
+ # when a service is configured as external via [package][service
752
+
753
+ # If a command has a pre-hook defined we will run it.
754
+ # Otherwise, if it is a run-sv command and the service it refers to
755
+ # is an external service, we will show an error since we
756
+ # can't control external services from here.
757
+ #
758
+ # If any pre-hook returns false, it will prevent execution of the command
759
+ # and exit the command with exit code 8.
760
+ def command_pre_hook(*args)
761
+ command = args.shift
762
+ method = to_method_name("#{command}_pre_hook")
763
+ if respond_to?(method)
764
+ send(method, *args)
765
+ else
766
+ return true if args.empty?
767
+ if SV_COMMAND_NAMES.include? command
768
+ if service_external? args[0]
769
+ log error_external_service(command, args[0])
770
+ return false;
771
+ end
772
+ end
773
+ true
774
+ end
576
775
  end
577
776
 
777
+ # Executes after successful completion of a command
778
+ # If a post-hook provides a numeric return code, it will
779
+ # replace the return/exit of the original command
780
+ def command_post_hook(*args)
781
+ command = args.shift
782
+ method = to_method_name("#{command}_post_hook")
783
+ if respond_to?(method)
784
+ send(method, *args)
785
+ end
786
+ end
787
+
788
+ # If we're listing status for all services and have external
789
+ # services to show, we'll include an output header to show that
790
+ # we're reporting internal services
791
+ def status_pre_hook(service = nil)
792
+ log_internal_service_header if service.nil?
793
+ true
794
+ end
795
+ # Status gets its own hook because each externalized service will
796
+ # have its own things to do in order to report status.
797
+ # As above, we may also include an output header to show that we're
798
+ # reporting on external services.
799
+ #
800
+ # Your callback for this function should be in the form
801
+ # 'external_status_#{service_name}(detail_level)
802
+ # where detail_level is :sparse|:verbose
803
+ # :sparse is used when it's a summary service status list, eg
804
+ # "$appname-ctl status"
805
+ # :verbose is used when the specific service has been named, eg
806
+ # "$appname-ctl status postgresql"
807
+ def status_post_hook(service = nil)
808
+ if service.nil?
809
+ log_external_service_header
810
+ external_services.each_key do |service_name|
811
+ status = send(to_method_name("external_status_#{service_name}"), :sparse)
812
+ log status
813
+ end
814
+ else
815
+ # Request verbose status if the service is asked for by name.
816
+ if service_external?(service)
817
+ status = send(to_method_name("external_status_#{service}"), :verbose)
818
+ log status
819
+ end
820
+ end
821
+ end
822
+
823
+ # Data cleanup requirements for external services aren't met by the standard
824
+ # 'nuke /var/opt' behavior - this hook allows each service to perform its own
825
+ # 'cleanse' operations.
826
+ #
827
+ # Your callback for this function should be in the
828
+ # form 'external_cleanup_#{service_name}(do_clean)
829
+ # where do_cliean is true if the delete should actually be
830
+ # performed, and false if it's expected to inform the user how to
831
+ # perform the data cleanup without doing any cleanup itself.
832
+ def cleanse_post_hook(*args)
833
+ external_services.each_key do |service_name|
834
+ perform_delete = ARGV.include?("--with-external")
835
+ if perform_delete
836
+ log "Deleting data from external service: #{service_name}"
837
+ end
838
+ send(to_method_name("external_cleanse_#{service_name}"), perform_delete)
839
+ end
840
+ end
841
+
842
+ # Add some output headers if we have external services enabled
843
+ def service_list_pre_hook
844
+ log_internal_service_header
845
+ return true
846
+ end
847
+
848
+ # Capture external services in the output list as well.
849
+ def service_list_post_hook
850
+ log_external_service_header
851
+ external_services.each do |name, settings|
852
+ log " > #{name} on #{settings['vip']}"
853
+ end
854
+ end
855
+
856
+ def error_external_service(command, service)
857
+ <<EOM
858
+ -------------------------------------------------------------------
859
+ The service #{service} is running externally and cannot be managed
860
+ vi chef-server-ctl. Please log into #{external_services[service]['vip'] }
861
+ to manage it directly.
862
+ -------------------------------------------------------------------
863
+ EOM
864
+ end
865
+
866
+ def format_multiline_message(indent, message)
867
+ if message.class == String
868
+ message = message.split("\n")
869
+ end
870
+ spaces = " "*indent
871
+ message.map!{|line| "#{spaces}#{line.strip}"}
872
+ message.join("\n")
873
+ end
874
+
875
+ def log_internal_service_header
876
+ # Don't decorate output unless we have
877
+ # external services to report on.
878
+ return if external_services.empty?
879
+ log "-------------------"
880
+ log " Internal Services "
881
+ log "-------------------"
882
+ end
883
+
884
+ def log_external_service_header
885
+ return if external_services.empty?
886
+ log "-------------------"
887
+ log " External Services "
888
+ log "-------------------"
889
+ end
578
890
  end
579
891
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omnibus-ctl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Opscode, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-24 00:00:00.000000000 Z
11
+ date: 2016-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rspec
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -41,7 +55,7 @@ dependencies:
41
55
  description: Provides command line control for omnibus pakcages, rarely used as a
42
56
  gem
43
57
  email:
44
- - legal@opscode.com
58
+ - legal@chef.io
45
59
  executables:
46
60
  - omnibus-ctl
47
61
  extensions: []
@@ -51,7 +65,7 @@ files:
51
65
  - bin/omnibus-ctl
52
66
  - lib/omnibus-ctl.rb
53
67
  - lib/omnibus-ctl/version.rb
54
- homepage: http://github.com/opscode/omnibus-ctl
68
+ homepage: http://github.com/chef/omnibus-ctl
55
69
  licenses: []
56
70
  metadata: {}
57
71
  post_install_message:
@@ -70,9 +84,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
84
  version: '0'
71
85
  requirements: []
72
86
  rubyforge_project:
73
- rubygems_version: 2.2.2
87
+ rubygems_version: 2.5.2
74
88
  signing_key:
75
89
  specification_version: 4
76
90
  summary: Provides command line control for omnibus packages
77
91
  test_files: []
78
- has_rdoc: