gitlab-omnibus-ctl 0.6.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/bin/omnibus-ctl +33 -0
- data/lib/omnibus-ctl/version.rb +5 -0
- data/lib/omnibus-ctl.rb +923 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f995169a2d2b7f4de234a0133810a8ef4c7a5a4a34bf6ffc3621f1b7490ce1aa
|
4
|
+
data.tar.gz: 9abb7fcee800c82d11c5d6afda09c3a68da34c2a669b3c8878279fd961918066
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d62e022f85e323b9f4516c89c6c6b2c353ee9fd185da2ac17e1d3d412447e976652f914180724f6aec64c38fecfa027f15411248997169a72341ef2abf2d8fbb
|
7
|
+
data.tar.gz: 124db712e05e00c1e6b4f20740d6ecc6af03970d2e0e2a0c1552791a7162bbdfbdce93dcb6c3978737318cd59d5d58bb0d6948330deb1363284e9f17bb596045
|
data/LICENSE
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
Apache License
|
2
|
+
Version 2.0, January 2004
|
3
|
+
http://www.apache.org/licenses/
|
4
|
+
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
6
|
+
|
7
|
+
1. Definitions.
|
8
|
+
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
11
|
+
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
13
|
+
the copyright owner that is granting the License.
|
14
|
+
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
16
|
+
other entities that control, are controlled by, or are under common
|
17
|
+
control with that entity. For the purposes of this definition,
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
19
|
+
direction or management of such entity, whether by contract or
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
22
|
+
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
24
|
+
exercising permissions granted by this License.
|
25
|
+
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
27
|
+
including but not limited to software source code, documentation
|
28
|
+
source, and configuration files.
|
29
|
+
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
31
|
+
transformation or translation of a Source form, including but
|
32
|
+
not limited to compiled object code, generated documentation,
|
33
|
+
and conversions to other media types.
|
34
|
+
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
36
|
+
Object form, made available under the License, as indicated by a
|
37
|
+
copyright notice that is included in or attached to the work
|
38
|
+
(an example is provided in the Appendix below).
|
39
|
+
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
46
|
+
the Work and Derivative Works thereof.
|
47
|
+
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
49
|
+
the original version of the Work and any modifications or additions
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
61
|
+
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
64
|
+
subsequently incorporated within the Work.
|
65
|
+
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
72
|
+
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
78
|
+
where such license applies only to those patent claims licensable
|
79
|
+
by such Contributor that are necessarily infringed by their
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
82
|
+
institute patent litigation against any entity (including a
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
85
|
+
or contributory patent infringement, then any patent licenses
|
86
|
+
granted to You under this License for that Work shall terminate
|
87
|
+
as of the date such litigation is filed.
|
88
|
+
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
91
|
+
modifications, and in Source or Object form, provided that You
|
92
|
+
meet the following conditions:
|
93
|
+
|
94
|
+
(a) You must give any other recipients of the Work or
|
95
|
+
Derivative Works a copy of this License; and
|
96
|
+
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
98
|
+
stating that You changed the files; and
|
99
|
+
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
102
|
+
attribution notices from the Source form of the Work,
|
103
|
+
excluding those notices that do not pertain to any part of
|
104
|
+
the Derivative Works; and
|
105
|
+
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
108
|
+
include a readable copy of the attribution notices contained
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
111
|
+
of the following places: within a NOTICE text file distributed
|
112
|
+
as part of the Derivative Works; within the Source form or
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
114
|
+
within a display generated by the Derivative Works, if and
|
115
|
+
wherever such third-party notices normally appear. The contents
|
116
|
+
of the NOTICE file are for informational purposes only and
|
117
|
+
do not modify the License. You may add Your own attribution
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
120
|
+
that such additional attribution notices cannot be construed
|
121
|
+
as modifying the License.
|
122
|
+
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
124
|
+
may provide additional or different license terms and conditions
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
128
|
+
the conditions stated in this License.
|
129
|
+
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
133
|
+
this License, without any additional terms or conditions.
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
135
|
+
the terms of any separate license agreement you may have executed
|
136
|
+
with Licensor regarding such Contributions.
|
137
|
+
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
140
|
+
except as required for reasonable and customary use in describing the
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
142
|
+
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
152
|
+
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
158
|
+
incidental, or consequential damages of any character arising as a
|
159
|
+
result of this License or out of the use or inability to use the
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
162
|
+
other commercial damages or losses), even if such Contributor
|
163
|
+
has been advised of the possibility of such damages.
|
164
|
+
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
168
|
+
or other liability obligations and/or rights consistent with this
|
169
|
+
License. However, in accepting such obligations, You may act only
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
174
|
+
of your accepting any such warranty or additional liability.
|
175
|
+
|
176
|
+
END OF TERMS AND CONDITIONS
|
177
|
+
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
179
|
+
|
180
|
+
To apply the Apache License to your work, attach the following
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
182
|
+
replaced with your own identifying information. (Don't include
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
184
|
+
comment syntax for the file format. We also recommend that a
|
185
|
+
file or class name and description of purpose be included on the
|
186
|
+
same "printed page" as the copyright notice for easier
|
187
|
+
identification within third-party archives.
|
188
|
+
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
190
|
+
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
192
|
+
you may not use this file except in compliance with the License.
|
193
|
+
You may obtain a copy of the License at
|
194
|
+
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
196
|
+
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200
|
+
See the License for the specific language governing permissions and
|
201
|
+
limitations under the License.
|
data/bin/omnibus-ctl
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# Author:: Adam Jacob (<adam@opscode.com>)
|
4
|
+
# Copyright:: Copyright (c) 2011 Opscode, Inc.
|
5
|
+
# License:: Apache License, Version 2.0
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require "omnibus-ctl"
|
21
|
+
|
22
|
+
# service name: ARGV[0]
|
23
|
+
# additional modules: ARGV[1]
|
24
|
+
# command: ARGV[2]
|
25
|
+
# service: ARGV[3]
|
26
|
+
# options: ARGV[4..]
|
27
|
+
|
28
|
+
ctl = Omnibus::Ctl.new(ARGV[0])
|
29
|
+
ctl.load_files(ARGV[1])
|
30
|
+
arguments = ARGV[2..-1] # Get the rest of the command line arguments
|
31
|
+
ctl.run(arguments)
|
32
|
+
exit 0
|
33
|
+
|
data/lib/omnibus-ctl.rb
ADDED
@@ -0,0 +1,923 @@
|
|
1
|
+
# Copyright (c) 2012-2015 Chef Software, Inc.
|
2
|
+
# License:: Apache License, Version 2.0
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
|
17
|
+
require "omnibus-ctl/version"
|
18
|
+
require "chef-utils/dist" unless defined?(ChefUtils)
|
19
|
+
require "json" unless defined?(JSON)
|
20
|
+
require "fileutils" unless defined?(FileUtils)
|
21
|
+
|
22
|
+
# For license checks
|
23
|
+
require "io/console"
|
24
|
+
require "io/wait"
|
25
|
+
|
26
|
+
module Omnibus
|
27
|
+
class Ctl
|
28
|
+
|
29
|
+
File.umask(022)
|
30
|
+
|
31
|
+
SV_COMMAND_NAMES = %w{status up down once pause cont hup alarm int quit
|
32
|
+
term kill start stop restart shutdown force-stop
|
33
|
+
force-reload force-restart force-shutdown check usr1 usr2}.freeze
|
34
|
+
|
35
|
+
attr_accessor :name, :display_name, :log_exclude, :base_path, :sv_path,
|
36
|
+
:service_path, :etc_path, :data_path, :log_path, :command_map, :category_command_map,
|
37
|
+
:fh_output, :kill_users, :verbose, :log_path_exclude
|
38
|
+
|
39
|
+
attr_reader :backup_dir, :exe_name
|
40
|
+
|
41
|
+
def initialize(name, merge_service_commands = true, disp_name = nil)
|
42
|
+
@name = name
|
43
|
+
@service_commands = merge_service_commands
|
44
|
+
@display_name = disp_name || name
|
45
|
+
@base_path = "/opt/#{name}"
|
46
|
+
@sv_path = File.join(@base_path, "sv")
|
47
|
+
@service_path = File.join(@base_path, "service")
|
48
|
+
@log_path = "/var/log/#{name}"
|
49
|
+
@data_path = "/var/opt/#{name}"
|
50
|
+
@etc_path = "/etc/#{name}"
|
51
|
+
@log_exclude = "(config|lock|@|bz2|gz|gzip|tbz2|tgz|txz|xz|zip)"
|
52
|
+
@log_path_exclude = ["*/sasl/*"]
|
53
|
+
@fh_output = STDOUT
|
54
|
+
@kill_users = []
|
55
|
+
@verbose = false
|
56
|
+
@quiet = false
|
57
|
+
@exe_name = File.basename($0)
|
58
|
+
@force_exit = false
|
59
|
+
@global_pre_hooks = {}
|
60
|
+
|
61
|
+
# TODO(ssd) 2017-03-28: Set SVDIR explicitly. Once we fix a bug
|
62
|
+
# in our debian support, where we rely on system-installed
|
63
|
+
# runit, we can likely change this back to ENV.delete("SVDIR")
|
64
|
+
ENV["SVDIR"] = service_path
|
65
|
+
|
66
|
+
# backwards compat command map that does not have categories
|
67
|
+
@command_map = {}
|
68
|
+
|
69
|
+
# categoired commands that we want by default
|
70
|
+
@category_command_map = {
|
71
|
+
"general" => {
|
72
|
+
"show-config" => {
|
73
|
+
desc: "Show the configuration that would be generated by reconfigure.",
|
74
|
+
arity: 1,
|
75
|
+
},
|
76
|
+
"reconfigure" => {
|
77
|
+
desc: "Reconfigure the application.",
|
78
|
+
arity: 2,
|
79
|
+
},
|
80
|
+
"cleanse" => {
|
81
|
+
desc: "Delete *all* #{display_name} data, and start from scratch.",
|
82
|
+
arity: 2,
|
83
|
+
},
|
84
|
+
"uninstall" => {
|
85
|
+
arity: 1,
|
86
|
+
desc: "Kill all processes and uninstall the process supervisor (data will be preserved).",
|
87
|
+
},
|
88
|
+
"help" => {
|
89
|
+
arity: 1,
|
90
|
+
desc: "Print this help message.",
|
91
|
+
},
|
92
|
+
},
|
93
|
+
}
|
94
|
+
service_command_map = {
|
95
|
+
"service-management" => {
|
96
|
+
"service-list" => {
|
97
|
+
arity: 1,
|
98
|
+
desc: "List all the services (enabled services appear with a *.)",
|
99
|
+
},
|
100
|
+
"status" => {
|
101
|
+
desc: "Show the status of all the services.",
|
102
|
+
arity: 2,
|
103
|
+
},
|
104
|
+
"tail" => {
|
105
|
+
desc: "Watch the service logs of all enabled services.",
|
106
|
+
arity: 2,
|
107
|
+
},
|
108
|
+
"start" => {
|
109
|
+
desc: "Start services if they are down, and restart them if they stop.",
|
110
|
+
arity: 2,
|
111
|
+
},
|
112
|
+
"stop" => {
|
113
|
+
desc: "Stop the services, and do not restart them.",
|
114
|
+
arity: 2,
|
115
|
+
},
|
116
|
+
"restart" => {
|
117
|
+
desc: "Stop the services if they are running, then start them again.",
|
118
|
+
arity: 2,
|
119
|
+
},
|
120
|
+
"once" => {
|
121
|
+
desc: "Start the services if they are down. Do not restart them if they stop.",
|
122
|
+
arity: 2,
|
123
|
+
},
|
124
|
+
"hup" => {
|
125
|
+
desc: "Send the services a HUP.",
|
126
|
+
arity: 2,
|
127
|
+
},
|
128
|
+
"term" => {
|
129
|
+
desc: "Send the services a TERM.",
|
130
|
+
arity: 2,
|
131
|
+
},
|
132
|
+
"int" => {
|
133
|
+
desc: "Send the services an INT.",
|
134
|
+
arity: 2,
|
135
|
+
},
|
136
|
+
"kill" => {
|
137
|
+
desc: "Send the services a KILL.",
|
138
|
+
arity: 2,
|
139
|
+
},
|
140
|
+
"graceful-kill" => {
|
141
|
+
desc: "Attempt a graceful stop, then SIGKILL the entire process group.",
|
142
|
+
arity: 2,
|
143
|
+
},
|
144
|
+
"usr1" => {
|
145
|
+
desc: "Send the services a USR1.",
|
146
|
+
arity: 2,
|
147
|
+
},
|
148
|
+
"usr2" => {
|
149
|
+
desc: "Send the services a USR2.",
|
150
|
+
arity: 2,
|
151
|
+
},
|
152
|
+
},
|
153
|
+
}
|
154
|
+
@category_command_map.merge!(service_command_map) if service_commands?
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.to_method_name(name)
|
158
|
+
name.gsub(/-/, "_").to_sym
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_method_name(name)
|
162
|
+
Ctl.to_method_name(name)
|
163
|
+
end
|
164
|
+
|
165
|
+
SV_COMMAND_NAMES.each do |sv_cmd|
|
166
|
+
define_method to_method_name(sv_cmd) do |*args|
|
167
|
+
run_sv_command(*args)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# merges category_command_map and command_map,
|
172
|
+
# removing categories
|
173
|
+
def get_all_commands_hash
|
174
|
+
without_categories = {}
|
175
|
+
category_command_map.each do |category, commands|
|
176
|
+
without_categories.merge!(commands)
|
177
|
+
end
|
178
|
+
command_map.merge(without_categories)
|
179
|
+
end
|
180
|
+
|
181
|
+
def service_commands?
|
182
|
+
@service_commands
|
183
|
+
end
|
184
|
+
|
185
|
+
def load_files(path)
|
186
|
+
Dir["#{path}/*.rb"].each do |file|
|
187
|
+
load_file(file)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def load_file(filepath)
|
192
|
+
eval(IO.read(filepath), nil, filepath, 1) # rubocop: disable Security/Eval
|
193
|
+
end
|
194
|
+
|
195
|
+
def add_command(name, description, arity = 1, &block)
|
196
|
+
@command_map[name] = { desc: description, arity: arity }
|
197
|
+
self.class.send(:define_method, to_method_name(name).to_sym) { |*args| block.call(*args) }
|
198
|
+
end
|
199
|
+
|
200
|
+
def add_command_under_category(name, category, description, arity = 1, &block)
|
201
|
+
# add new category if it doesn't exist
|
202
|
+
@category_command_map[category] ||= {}
|
203
|
+
@category_command_map[category][name] = { desc: description, arity: arity }
|
204
|
+
self.class.send(:define_method, to_method_name(name).to_sym) { |*args| block.call(*args) }
|
205
|
+
end
|
206
|
+
|
207
|
+
def add_global_pre_hook(name, &block)
|
208
|
+
method_name = to_method_name("#{name}_global_pre_hook").to_sym
|
209
|
+
@global_pre_hooks[name] = method_name
|
210
|
+
self.class.send(:define_method, method_name, block)
|
211
|
+
end
|
212
|
+
|
213
|
+
def exit!(code)
|
214
|
+
@force_exit = true
|
215
|
+
code
|
216
|
+
end
|
217
|
+
|
218
|
+
def log(msg)
|
219
|
+
fh_output.puts msg
|
220
|
+
end
|
221
|
+
|
222
|
+
def get_pgrp_from_pid(pid)
|
223
|
+
ps = `which ps`.chomp
|
224
|
+
`#{ps} -p #{pid} -o pgrp=`.chomp
|
225
|
+
end
|
226
|
+
|
227
|
+
def get_pids_from_pgrp(pgrp)
|
228
|
+
pgrep = `which pgrep`.chomp
|
229
|
+
`#{pgrep} -g #{pgrp}`.split(/\n/).join(" ")
|
230
|
+
end
|
231
|
+
|
232
|
+
def sigkill_pgrp(pgrp)
|
233
|
+
pkill = `which pkill`.chomp
|
234
|
+
run_command("#{pkill} -9 -g #{pgrp}")
|
235
|
+
end
|
236
|
+
|
237
|
+
def run_command(command)
|
238
|
+
system(command)
|
239
|
+
$?
|
240
|
+
end
|
241
|
+
|
242
|
+
def service_list(*args)
|
243
|
+
get_all_services.each do |service_name|
|
244
|
+
print "#{service_name}"
|
245
|
+
print "*" if service_enabled?(service_name)
|
246
|
+
print "\n"
|
247
|
+
end
|
248
|
+
exit! 0
|
249
|
+
end
|
250
|
+
|
251
|
+
def cleanup_procs_and_nuke(filestr, calling_method = nil)
|
252
|
+
run_sv_command("stop")
|
253
|
+
|
254
|
+
FileUtils.rm_f("/etc/init/#{name}-runsvdir.conf") if File.exist?("/etc/init/#{name}-runsvdir.conf")
|
255
|
+
run_command("egrep -v '#{base_path}/embedded/bin/runsvdir-start' /etc/inittab > /etc/inittab.new && mv /etc/inittab.new /etc/inittab") if File.exist?("/etc/inittab")
|
256
|
+
run_command("kill -1 1")
|
257
|
+
|
258
|
+
@backup_dir = Time.now.strftime("/root/#{name}-cleanse-%FT%R")
|
259
|
+
|
260
|
+
FileUtils.mkdir_p("/root") unless File.exist?("/root")
|
261
|
+
FileUtils.rm_rf(backup_dir)
|
262
|
+
FileUtils.cp_r(etc_path, backup_dir) if File.exist?(etc_path)
|
263
|
+
run_command("rm -rf #{filestr}")
|
264
|
+
graceful_kill
|
265
|
+
|
266
|
+
log "Terminating processes running under application users. This will take a few seconds."
|
267
|
+
run_command("pkill -HUP -u #{kill_users.join(",")}") if kill_users.length > 0
|
268
|
+
run_command("pkill -HUP -f 'runsvdir -P #{service_path}'")
|
269
|
+
sleep 3
|
270
|
+
run_command("pkill -TERM -u #{kill_users.join(",")}") if kill_users.length > 0
|
271
|
+
run_command("pkill -TERM -f 'runsvdir -P #{service_path}'")
|
272
|
+
sleep 3
|
273
|
+
run_command("pkill -KILL -u #{kill_users.join(",")}") if kill_users.length > 0
|
274
|
+
run_command("pkill -KILL -f 'runsvdir -P #{service_path}'")
|
275
|
+
|
276
|
+
get_all_services.each do |die_daemon_die|
|
277
|
+
run_command("pkill -KILL -f 'runsv #{die_daemon_die}'")
|
278
|
+
end
|
279
|
+
log "Your config files have been backed up to #{backup_dir}."
|
280
|
+
exit! 0
|
281
|
+
end
|
282
|
+
|
283
|
+
def uninstall(*args)
|
284
|
+
cleanup_procs_and_nuke("/tmp/opt")
|
285
|
+
end
|
286
|
+
|
287
|
+
def scary_cleanse_warning(*args)
|
288
|
+
just_do_it = args.include?("yes")
|
289
|
+
with_external = ARGV.include?("--with-external")
|
290
|
+
log <<EOM
|
291
|
+
*******************************************************************
|
292
|
+
* * * * * * * * * * * STOP AND READ * * * * * * * * * *
|
293
|
+
*******************************************************************
|
294
|
+
This command will delete *all* local configuration, log, and
|
295
|
+
variable data associated with #{display_name}.
|
296
|
+
EOM
|
297
|
+
if with_external
|
298
|
+
log <<EOM
|
299
|
+
This will also delete externally hosted #{display_name} data.
|
300
|
+
This means that any service you have configured as 'external'
|
301
|
+
will have any #{display_name} permanently deleted.
|
302
|
+
EOM
|
303
|
+
elsif not external_services.empty?
|
304
|
+
log <<EOM
|
305
|
+
|
306
|
+
Important note: If you also wish to delete externally hosted #{display_name}
|
307
|
+
data, please hit CTRL+C now and run '#{exe_name} cleanse --with-external'
|
308
|
+
EOM
|
309
|
+
end
|
310
|
+
|
311
|
+
unless just_do_it
|
312
|
+
data = with_external ? "local, and remote data" : "and local data"
|
313
|
+
log <<EOM
|
314
|
+
|
315
|
+
You have 60 seconds to hit CTRL-C before configuration,
|
316
|
+
logs, #{data} for this application are permanently
|
317
|
+
deleted.
|
318
|
+
*******************************************************************
|
319
|
+
|
320
|
+
EOM
|
321
|
+
begin
|
322
|
+
sleep 60
|
323
|
+
rescue Interrupt
|
324
|
+
log ""
|
325
|
+
exit 0
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def cleanse(*args)
|
331
|
+
scary_cleanse_warning(*args)
|
332
|
+
cleanup_procs_and_nuke("#{service_path}/* /tmp/opt #{data_path} #{etc_path} #{log_path}", "cleanse")
|
333
|
+
end
|
334
|
+
|
335
|
+
def get_all_services_files
|
336
|
+
Dir[File.join(sv_path, "*")]
|
337
|
+
end
|
338
|
+
|
339
|
+
def get_all_services
|
340
|
+
get_all_services_files.map { |f| File.basename(f) }.sort
|
341
|
+
end
|
342
|
+
|
343
|
+
def service_enabled?(service_name)
|
344
|
+
File.symlink?("#{service_path}/#{service_name}")
|
345
|
+
end
|
346
|
+
|
347
|
+
def run_sv_command(sv_cmd, service = nil)
|
348
|
+
exit_status = 0
|
349
|
+
sv_cmd = "1" if sv_cmd == "usr1"
|
350
|
+
sv_cmd = "2" if sv_cmd == "usr2"
|
351
|
+
if service
|
352
|
+
exit_status += run_sv_command_for_service(sv_cmd, service)
|
353
|
+
else
|
354
|
+
get_all_services.each do |service_name|
|
355
|
+
exit_status += run_sv_command_for_service(sv_cmd, service_name) if global_service_command_permitted(sv_cmd, service_name)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
exit! exit_status
|
359
|
+
end
|
360
|
+
|
361
|
+
# run an sv command for a specific service name
|
362
|
+
def run_sv_command_for_service(sv_cmd, service_name)
|
363
|
+
if service_enabled?(service_name)
|
364
|
+
status = run_command("#{base_path}/init/#{service_name} #{sv_cmd}")
|
365
|
+
status.exitstatus
|
366
|
+
else
|
367
|
+
log "#{service_name} disabled" if sv_cmd == "status" && verbose
|
368
|
+
0
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# if we're running a global service command (like p-c-c status)
|
373
|
+
# across all of the services, there are certain cases where we
|
374
|
+
# want to prevent services files that exist in the service
|
375
|
+
# directory from being activated. This method is the logic that
|
376
|
+
# blocks those services
|
377
|
+
def global_service_command_permitted(sv_cmd, service_name)
|
378
|
+
# For services that have been removed, we only want to
|
379
|
+
# them to respond to the stop command. They should not show
|
380
|
+
# up in status, and they should not be started.
|
381
|
+
if removed_services.include?(service_name)
|
382
|
+
return sv_cmd == "stop"
|
383
|
+
end
|
384
|
+
|
385
|
+
# For keepalived, we only want it to respond to the status
|
386
|
+
# command when running global service commands like p-c-c start
|
387
|
+
# and p-c-c stop
|
388
|
+
if service_name == "keepalived"
|
389
|
+
return sv_cmd == "status"
|
390
|
+
end
|
391
|
+
|
392
|
+
# If c-s-c status is called, check to see if the service
|
393
|
+
# is hidden supposed to be hidden from the status results
|
394
|
+
# (mover for example should be hidden).
|
395
|
+
if sv_cmd == "status"
|
396
|
+
return !(hidden_services.include?(service_name))
|
397
|
+
end
|
398
|
+
|
399
|
+
# All other services respond normally to p-c-c * commands
|
400
|
+
true
|
401
|
+
end
|
402
|
+
|
403
|
+
# removed services are configured via the attributes file in
|
404
|
+
# the main omnibus cookbook
|
405
|
+
def removed_services
|
406
|
+
# in the case that there is no running_config (the config file does
|
407
|
+
# not exist), we know that this will be a new server, and we don't
|
408
|
+
# have to worry about pre-upgrade services hanging around. We can safely
|
409
|
+
# return an empty array when running_config is nil
|
410
|
+
running_package_config["removed_services"] || []
|
411
|
+
end
|
412
|
+
|
413
|
+
# hidden services are configured via the attributes file in
|
414
|
+
# the main omnibus cookbook
|
415
|
+
#
|
416
|
+
# hidden services are services that we do not want to show up in
|
417
|
+
# c-s-c status.
|
418
|
+
def hidden_services
|
419
|
+
# in the case that there is no running_config (the config file does
|
420
|
+
# not exist), we don't want to return nil, just return an empty array.
|
421
|
+
# worse result with doing that is services that we don't want to show up in
|
422
|
+
# c-s-c status will show up.
|
423
|
+
running_package_config["hidden_services"] || []
|
424
|
+
end
|
425
|
+
|
426
|
+
# translate the name from the config to the package name.
|
427
|
+
# this is a special case for the private-chef package because
|
428
|
+
# it is configured to use the name and directory structure of
|
429
|
+
# 'opscode', not 'private-chef'
|
430
|
+
def package_name
|
431
|
+
case @name
|
432
|
+
# The "opscode" in /opt/opscode
|
433
|
+
when ::ChefUtils::Dist::Org::LEGACY_CONF_DIR
|
434
|
+
"private-chef"
|
435
|
+
else
|
436
|
+
@name
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
# returns nil when chef-server-running.json does not exist
|
441
|
+
def running_config
|
442
|
+
fname = "#{etc_path}/#{::ChefUtils::Dist::Server::SERVER}-running.json"
|
443
|
+
@running_config ||= if File.exist?(fname)
|
444
|
+
JSON.parse(File.read(fname))
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
# Helper function that returns the hash of config hashes that have the key 'external' : true
|
449
|
+
# in the running config. If none exist it will return an empty hash.
|
450
|
+
def external_services
|
451
|
+
@external_services ||= running_package_config.select { |k, v| v.class == Hash and v["external"] == true }
|
452
|
+
end
|
453
|
+
|
454
|
+
# Helper function that returns true if an external service entry exists for
|
455
|
+
# the named service
|
456
|
+
def service_external?(service)
|
457
|
+
return false if service.nil?
|
458
|
+
|
459
|
+
external_services.key? service
|
460
|
+
end
|
461
|
+
|
462
|
+
# Gives package config from the running_config.
|
463
|
+
# If there is no running config or if package_name doens't
|
464
|
+
# reference a valid key, this will return an empty hash
|
465
|
+
def running_package_config
|
466
|
+
if (cfg = running_config)
|
467
|
+
cfg[package_name.gsub(/-/, "_")] || {}
|
468
|
+
else
|
469
|
+
{}
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# This returns running_config[package][service].
|
474
|
+
#
|
475
|
+
# If there is no running_config or is no matching key
|
476
|
+
# it will return nil.
|
477
|
+
def running_service_config(service)
|
478
|
+
running_package_config[service]
|
479
|
+
end
|
480
|
+
|
481
|
+
def remove_old_node_state
|
482
|
+
node_cache_path = "#{base_path}/embedded/nodes/"
|
483
|
+
status = run_command("rm -rf #{node_cache_path}")
|
484
|
+
unless status.success?
|
485
|
+
log "Could not remove cached node state!"
|
486
|
+
exit 1
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
def run_chef(attr_location, args = "")
|
491
|
+
if @verbose
|
492
|
+
log_level = "-l debug"
|
493
|
+
elsif @quiet
|
494
|
+
# null formatter is awfully quiet, so let them know we're doing something.
|
495
|
+
log "Reconfiguring #{display_name}."
|
496
|
+
log_level = "-l fatal -F null"
|
497
|
+
else
|
498
|
+
log_level = ""
|
499
|
+
end
|
500
|
+
remove_old_node_state
|
501
|
+
cmd = "#{base_path}/embedded/bin/cinc-client #{log_level} -z -c #{base_path}/embedded/cookbooks/solo.rb -j #{attr_location}"
|
502
|
+
cmd += " #{args}" unless args.empty?
|
503
|
+
run_command(cmd)
|
504
|
+
end
|
505
|
+
|
506
|
+
def show_config(*args)
|
507
|
+
status = run_chef("#{base_path}/embedded/cookbooks/show-config.json", "-l fatal -F null")
|
508
|
+
exit! status.success? ? 0 : 1
|
509
|
+
end
|
510
|
+
|
511
|
+
def reconfigure(*args)
|
512
|
+
# args being passed to this command does not include the ones that are
|
513
|
+
# starting with "-". See #is_option? method. If it is starting with "-"
|
514
|
+
# then it is treated as a option and we need to look for them in ARGV.
|
515
|
+
check_license_acceptance(ARGV.include?("--accept-license"))
|
516
|
+
|
517
|
+
status = run_chef("#{base_path}/embedded/cookbooks/dna.json")
|
518
|
+
if status.success?
|
519
|
+
log "#{display_name} Reconfigured!"
|
520
|
+
exit! 0
|
521
|
+
else
|
522
|
+
exit! 1
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
def check_license_acceptance(override_accept = false)
|
527
|
+
license_guard_file_path = File.join(data_path, ".license.accepted")
|
528
|
+
|
529
|
+
# If the project does not have a license we do not have
|
530
|
+
# any license to accept.
|
531
|
+
return unless File.exist?(project_license_path)
|
532
|
+
|
533
|
+
unless File.exist?(license_guard_file_path)
|
534
|
+
if override_accept || ask_license_acceptance
|
535
|
+
FileUtils.mkdir_p(data_path)
|
536
|
+
FileUtils.touch(license_guard_file_path)
|
537
|
+
else
|
538
|
+
log "Please accept the software license agreement to continue."
|
539
|
+
exit(1)
|
540
|
+
end
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
def ask_license_acceptance
|
545
|
+
log "To use this software, you must agree to the terms of the software license agreement."
|
546
|
+
|
547
|
+
unless STDIN.tty?
|
548
|
+
log "Please view and accept the software license agreement, or pass --accept-license."
|
549
|
+
exit(1)
|
550
|
+
end
|
551
|
+
|
552
|
+
log "Press any key to continue."
|
553
|
+
user_input = STDIN.getch
|
554
|
+
user_input << STDIN.getch while STDIN.ready?
|
555
|
+
# No need to check for user input
|
556
|
+
|
557
|
+
system("less #{project_license_path}")
|
558
|
+
|
559
|
+
loop do
|
560
|
+
log "Type 'yes' to accept the software license agreement, or anything else to cancel."
|
561
|
+
|
562
|
+
user_input = STDIN.gets.chomp.downcase
|
563
|
+
case user_input
|
564
|
+
when "yes"
|
565
|
+
return true
|
566
|
+
else
|
567
|
+
log "You have not accepted the software license agreement."
|
568
|
+
return false
|
569
|
+
end
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
def project_license_path
|
574
|
+
File.join(base_path, "LICENSE")
|
575
|
+
end
|
576
|
+
|
577
|
+
def tail(*args)
|
578
|
+
# find /var/log -type f -not -path '*/sasl/*' | grep -E -v '(lock|@|tgz|gzip)' | xargs tail --follow=name --retry
|
579
|
+
command = "find -L #{log_path}"
|
580
|
+
command << "/#{args[1]}" if args[1]
|
581
|
+
command << " -type f"
|
582
|
+
command << log_path_exclude.map { |path| " -not -path '#{path}'" }.join(" ")
|
583
|
+
command << " | grep -E -v '#{log_exclude}' | xargs tail --follow=name --retry"
|
584
|
+
|
585
|
+
system(command)
|
586
|
+
end
|
587
|
+
|
588
|
+
def is_integer?(string)
|
589
|
+
return true if Integer(string) rescue false
|
590
|
+
end
|
591
|
+
|
592
|
+
def graceful_kill(*args)
|
593
|
+
service = args[1]
|
594
|
+
exit_status = 0
|
595
|
+
get_all_services.each do |service_name|
|
596
|
+
next if !service.nil? && service_name != service
|
597
|
+
|
598
|
+
if service_enabled?(service_name)
|
599
|
+
pidfile = "#{sv_path}/#{service_name}/supervise/pid"
|
600
|
+
pid = File.read(pidfile).chomp if File.exist?(pidfile)
|
601
|
+
if pid.nil? || !is_integer?(pid)
|
602
|
+
log "could not find #{service_name} runit pidfile (service already stopped?), cannot attempt SIGKILL..."
|
603
|
+
status = run_command("#{base_path}/init/#{service_name} stop")
|
604
|
+
exit_status = status.exitstatus if exit_status == 0 && !status.success?
|
605
|
+
next
|
606
|
+
end
|
607
|
+
pgrp = get_pgrp_from_pid(pid)
|
608
|
+
if pgrp.nil? || !is_integer?(pgrp)
|
609
|
+
log "could not find pgrp of pid #{pid} (not running?), cannot attempt SIGKILL..."
|
610
|
+
status = run_command("#{base_path}/init/#{service_name} stop")
|
611
|
+
exit_status = status.exitstatus if exit_status == 0 && !status.success?
|
612
|
+
next
|
613
|
+
end
|
614
|
+
run_command("#{base_path}/init/#{service_name} stop")
|
615
|
+
pids = get_pids_from_pgrp(pgrp)
|
616
|
+
unless pids.empty?
|
617
|
+
log "found stuck pids still running in process group: #{pids}, sending SIGKILL" unless pids.empty?
|
618
|
+
sigkill_pgrp(pgrp)
|
619
|
+
end
|
620
|
+
else
|
621
|
+
log "#{service_name} disabled, not stopping"
|
622
|
+
exit_status = 1
|
623
|
+
end
|
624
|
+
end
|
625
|
+
exit! exit_status
|
626
|
+
end
|
627
|
+
|
628
|
+
def help(*args)
|
629
|
+
log "#{exe_name}: command (subcommand)\n"
|
630
|
+
command_map.keys.sort.each do |command|
|
631
|
+
log command
|
632
|
+
log " #{command_map[command][:desc]}"
|
633
|
+
end
|
634
|
+
category_command_map.each do |category, commands|
|
635
|
+
# Remove "-" and replace with spaces in category and capalize for output
|
636
|
+
category_string = category.gsub("-", " ").split.map(&:capitalize).join(" ")
|
637
|
+
log "#{category_string} Commands:\n"
|
638
|
+
|
639
|
+
# Print each command in this category
|
640
|
+
commands.keys.sort.each do |command|
|
641
|
+
log " #{command}"
|
642
|
+
log " #{commands[command][:desc]}"
|
643
|
+
end
|
644
|
+
end
|
645
|
+
# Help is not an error so exit with 0. In cases where we display help as a result of an error
|
646
|
+
# the framework will handle setting proper exit code.
|
647
|
+
exit! 0
|
648
|
+
end
|
649
|
+
|
650
|
+
# Set global options and remove them from the args list we pass
|
651
|
+
# into commands.
|
652
|
+
def parse_options(args)
|
653
|
+
args.select do |option|
|
654
|
+
case option
|
655
|
+
when "--quiet", "-q"
|
656
|
+
@quiet = true
|
657
|
+
false
|
658
|
+
when "--verbose", "-v"
|
659
|
+
@verbose = true
|
660
|
+
false
|
661
|
+
end
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
# If it begins with a '-', it is an option.
|
666
|
+
def is_option?(arg)
|
667
|
+
arg && arg[0] == "-"
|
668
|
+
end
|
669
|
+
|
670
|
+
# retrieves the commmand from either the command_map
|
671
|
+
# or the category_command_map, if the command is not found
|
672
|
+
# return nil
|
673
|
+
def retrieve_command(command_to_run)
|
674
|
+
if command_map.key?(command_to_run)
|
675
|
+
command_map[command_to_run]
|
676
|
+
else
|
677
|
+
command = nil
|
678
|
+
category_command_map.each do |category, commands|
|
679
|
+
command = commands[command_to_run] if commands.key?(command_to_run)
|
680
|
+
end
|
681
|
+
# return the command, or nil if it wasn't found
|
682
|
+
command
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
# Previously this would exit immediately with the provided
|
687
|
+
# exit code; however this would prevent post-run hooks from continuing
|
688
|
+
# Instead, we'll just track whether a an exit was requested and use that
|
689
|
+
# to determine how we exit from 'run'
|
690
|
+
def run(args)
|
691
|
+
# Ensure Omnibus related binaries are in the PATH
|
692
|
+
ENV["PATH"] = [File.join(base_path, "bin"),
|
693
|
+
File.join(base_path, "embedded", "bin"),
|
694
|
+
ENV["PATH"]].join(":")
|
695
|
+
|
696
|
+
command_to_run = args[0]
|
697
|
+
|
698
|
+
## when --help is run as the command itself, we need to strip off the
|
699
|
+
## `--` to ensure the command maps correctly.
|
700
|
+
if command_to_run == "--help"
|
701
|
+
command_to_run = "help"
|
702
|
+
end
|
703
|
+
|
704
|
+
# This piece of code checks if the argument is an option. If it is,
|
705
|
+
# then it sets service to nil and adds the argument into the options
|
706
|
+
# argument. This is ugly. A better solution is having a proper parser.
|
707
|
+
# But if we are going to implement a proper parser, we might as well
|
708
|
+
# port this to Thor rather than reinventing Thor. For now, this preserves
|
709
|
+
# the behavior to complain and exit with an error if one attempts to invoke
|
710
|
+
# a pcc command that does not accept an argument. Like "help".
|
711
|
+
options = args[2..-1] || []
|
712
|
+
if is_option?(args[1])
|
713
|
+
options.unshift(args[1])
|
714
|
+
service = nil
|
715
|
+
else
|
716
|
+
service = args[1]
|
717
|
+
end
|
718
|
+
|
719
|
+
# returns either hash content of command or nil
|
720
|
+
command = retrieve_command(command_to_run)
|
721
|
+
if command.nil?
|
722
|
+
log "I don't know that command."
|
723
|
+
if args.length == 2
|
724
|
+
log "Did you mean: #{exe_name} #{service} #{command_to_run}?"
|
725
|
+
end
|
726
|
+
help
|
727
|
+
Kernel.exit 1
|
728
|
+
end
|
729
|
+
|
730
|
+
if args.length > 1 && command[:arity] != 2
|
731
|
+
log "The command #{command_to_run} does not accept any arguments"
|
732
|
+
Kernel.exit 2
|
733
|
+
end
|
734
|
+
|
735
|
+
parse_options options
|
736
|
+
@force_exit = false
|
737
|
+
exit_code = 0
|
738
|
+
|
739
|
+
run_global_pre_hooks
|
740
|
+
|
741
|
+
# Filter args to just command and service. If you are loading
|
742
|
+
# custom commands and need access to the command line argument,
|
743
|
+
# use ARGV directly.
|
744
|
+
actual_args = [command_to_run, service].reject(&:nil?)
|
745
|
+
if command_pre_hook(*actual_args)
|
746
|
+
method_to_call = to_method_name(command_to_run)
|
747
|
+
begin
|
748
|
+
ret = send(method_to_call, *actual_args)
|
749
|
+
rescue SystemExit => e
|
750
|
+
@force_exit = true
|
751
|
+
ret = e.status
|
752
|
+
end
|
753
|
+
command_post_hook(*actual_args)
|
754
|
+
exit_code = ret unless ret.nil?
|
755
|
+
else
|
756
|
+
exit_code = 8
|
757
|
+
@force_exit = true
|
758
|
+
end
|
759
|
+
|
760
|
+
if @force_exit
|
761
|
+
Kernel.exit exit_code
|
762
|
+
else
|
763
|
+
exit_code
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
def run_global_pre_hooks
|
768
|
+
@global_pre_hooks.each do |hook_name, method_name|
|
769
|
+
|
770
|
+
send(method_name)
|
771
|
+
rescue => e
|
772
|
+
$stderr.puts("Global pre-hook '#{hook_name}' failed with: '#{e.message}'")
|
773
|
+
exit(1)
|
774
|
+
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
# Below are some basic command hooks that do the right thing
|
779
|
+
# when a service is configured as external via [package][service
|
780
|
+
|
781
|
+
# If a command has a pre-hook defined we will run it.
|
782
|
+
# Otherwise, if it is a run-sv command and the service it refers to
|
783
|
+
# is an external service, we will show an error since we
|
784
|
+
# can't control external services from here.
|
785
|
+
#
|
786
|
+
# If any pre-hook returns false, it will prevent execution of the command
|
787
|
+
# and exit the command with exit code 8.
|
788
|
+
def command_pre_hook(*args)
|
789
|
+
command = args.shift
|
790
|
+
method = to_method_name("#{command}_pre_hook")
|
791
|
+
if respond_to?(method)
|
792
|
+
send(method, *args)
|
793
|
+
else
|
794
|
+
return true if args.empty?
|
795
|
+
|
796
|
+
if SV_COMMAND_NAMES.include? command
|
797
|
+
if service_external? args[0]
|
798
|
+
log error_external_service(command, args[0])
|
799
|
+
return false
|
800
|
+
end
|
801
|
+
end
|
802
|
+
true
|
803
|
+
end
|
804
|
+
end
|
805
|
+
|
806
|
+
# Executes after successful completion of a command
|
807
|
+
# If a post-hook provides a numeric return code, it will
|
808
|
+
# replace the return/exit of the original command
|
809
|
+
def command_post_hook(*args)
|
810
|
+
command = args.shift
|
811
|
+
method = to_method_name("#{command}_post_hook")
|
812
|
+
if respond_to?(method)
|
813
|
+
send(method, *args)
|
814
|
+
end
|
815
|
+
end
|
816
|
+
|
817
|
+
# If we're listing status for all services and have external
|
818
|
+
# services to show, we'll include an output header to show that
|
819
|
+
# we're reporting internal services
|
820
|
+
def status_pre_hook(service = nil)
|
821
|
+
log_internal_service_header if service.nil?
|
822
|
+
true
|
823
|
+
end
|
824
|
+
|
825
|
+
# Status gets its own hook because each externalized service will
|
826
|
+
# have its own things to do in order to report status.
|
827
|
+
# As above, we may also include an output header to show that we're
|
828
|
+
# reporting on external services.
|
829
|
+
#
|
830
|
+
# Your callback for this function should be in the form
|
831
|
+
# 'external_status_#{service_name}(detail_level)
|
832
|
+
# where detail_level is :sparse|:verbose
|
833
|
+
# :sparse is used when it's a summary service status list, eg
|
834
|
+
# "$appname-ctl status"
|
835
|
+
# :verbose is used when the specific service has been named, eg
|
836
|
+
# "$appname-ctl status postgresql"
|
837
|
+
def status_post_hook(service = nil)
|
838
|
+
if service.nil?
|
839
|
+
log_external_service_header
|
840
|
+
external_services.each_key do |service_name|
|
841
|
+
status = send(to_method_name("external_status_#{service_name}"), :sparse)
|
842
|
+
log status
|
843
|
+
end
|
844
|
+
else
|
845
|
+
# Request verbose status if the service is asked for by name.
|
846
|
+
if service_external?(service)
|
847
|
+
status = send(to_method_name("external_status_#{service}"), :verbose)
|
848
|
+
log status
|
849
|
+
end
|
850
|
+
end
|
851
|
+
end
|
852
|
+
|
853
|
+
# Data cleanup requirements for external services aren't met by the standard
|
854
|
+
# 'nuke /var/opt' behavior - this hook allows each service to perform its own
|
855
|
+
# 'cleanse' operations.
|
856
|
+
#
|
857
|
+
# Your callback for this function should be in the
|
858
|
+
# form 'external_cleanup_#{service_name}(do_clean)
|
859
|
+
# where do_cliean is true if the delete should actually be
|
860
|
+
# performed, and false if it's expected to inform the user how to
|
861
|
+
# perform the data cleanup without doing any cleanup itself.
|
862
|
+
def cleanse_post_hook(*args)
|
863
|
+
external_services.each_key do |service_name|
|
864
|
+
perform_delete = ARGV.include?("--with-external")
|
865
|
+
if perform_delete
|
866
|
+
log "Deleting data from external service: #{service_name}"
|
867
|
+
end
|
868
|
+
send(to_method_name("external_cleanse_#{service_name}"), perform_delete)
|
869
|
+
end
|
870
|
+
end
|
871
|
+
|
872
|
+
# Add some output headers if we have external services enabled
|
873
|
+
def service_list_pre_hook
|
874
|
+
log_internal_service_header
|
875
|
+
true
|
876
|
+
end
|
877
|
+
|
878
|
+
# Capture external services in the output list as well.
|
879
|
+
def service_list_post_hook
|
880
|
+
log_external_service_header
|
881
|
+
external_services.each do |name, settings|
|
882
|
+
log " > #{name} on #{settings["vip"]}"
|
883
|
+
end
|
884
|
+
end
|
885
|
+
|
886
|
+
def error_external_service(command, service)
|
887
|
+
<<EOM
|
888
|
+
-------------------------------------------------------------------
|
889
|
+
The service #{service} is running externally and cannot be managed
|
890
|
+
vi chef-server-ctl. Please log into #{external_services[service]["vip"]}
|
891
|
+
to manage it directly.
|
892
|
+
-------------------------------------------------------------------
|
893
|
+
EOM
|
894
|
+
end
|
895
|
+
|
896
|
+
def format_multiline_message(indent, message)
|
897
|
+
if message.class == String
|
898
|
+
message = message.split("\n")
|
899
|
+
end
|
900
|
+
spaces = " " * indent
|
901
|
+
message.map! { |line| "#{spaces}#{line.strip}" }
|
902
|
+
message.join("\n")
|
903
|
+
end
|
904
|
+
|
905
|
+
def log_internal_service_header
|
906
|
+
# Don't decorate output unless we have
|
907
|
+
# external services to report on.
|
908
|
+
return if external_services.empty?
|
909
|
+
|
910
|
+
log "-------------------"
|
911
|
+
log " Internal Services "
|
912
|
+
log "-------------------"
|
913
|
+
end
|
914
|
+
|
915
|
+
def log_external_service_header
|
916
|
+
return if external_services.empty?
|
917
|
+
|
918
|
+
log "-------------------"
|
919
|
+
log " External Services "
|
920
|
+
log "-------------------"
|
921
|
+
end
|
922
|
+
end
|
923
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gitlab-omnibus-ctl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.13
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chef Software, Inc.
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-07-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: chef-utils
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 16.5.54
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 16.5.54
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: chefstyle
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.2.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.2.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.2'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec_junit_formatter
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Provides command line control for omnibus pakcages, rarely used as a
|
84
|
+
gem
|
85
|
+
email:
|
86
|
+
- legal@chef.io
|
87
|
+
executables:
|
88
|
+
- omnibus-ctl
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- LICENSE
|
93
|
+
- bin/omnibus-ctl
|
94
|
+
- lib/omnibus-ctl.rb
|
95
|
+
- lib/omnibus-ctl/version.rb
|
96
|
+
homepage: https://gitlab.com/gitlab-org/build/omnibus-mirror/omnibus-ctl
|
97
|
+
licenses:
|
98
|
+
- Apache-2.0
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '2.6'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubygems_version: 3.5.15
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: Provides command line control for omnibus packages
|
119
|
+
test_files: []
|