xolo-admin 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +177 -0
- data/README.md +5 -0
- data/bin/xadm +114 -0
- data/lib/xolo/admin/command_line.rb +432 -0
- data/lib/xolo/admin/configuration.rb +196 -0
- data/lib/xolo/admin/connection.rb +191 -0
- data/lib/xolo/admin/cookie_jar.rb +81 -0
- data/lib/xolo/admin/credentials.rb +212 -0
- data/lib/xolo/admin/highline_terminal.rb +81 -0
- data/lib/xolo/admin/interactive.rb +762 -0
- data/lib/xolo/admin/jamf_pro.rb +75 -0
- data/lib/xolo/admin/options.rb +1139 -0
- data/lib/xolo/admin/processing.rb +1329 -0
- data/lib/xolo/admin/progress_history.rb +117 -0
- data/lib/xolo/admin/title.rb +285 -0
- data/lib/xolo/admin/title_editor.rb +57 -0
- data/lib/xolo/admin/validate.rb +1032 -0
- data/lib/xolo/admin/version.rb +221 -0
- data/lib/xolo/admin.rb +139 -0
- data/lib/xolo-admin.rb +8 -0
- metadata +139 -0
|
@@ -0,0 +1,1032 @@
|
|
|
1
|
+
# Copyright 2025 Pixar
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the terms set forth in the LICENSE.txt file available at
|
|
4
|
+
# at the root of this project.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
# frozen_string_literal: true
|
|
8
|
+
|
|
9
|
+
module Xolo
|
|
10
|
+
|
|
11
|
+
module Admin
|
|
12
|
+
|
|
13
|
+
# A collection of methods to convert and validate values used in Xolo.
|
|
14
|
+
#
|
|
15
|
+
# These methods will convert target values if possible, e.g. if a value
|
|
16
|
+
# should be an Integer, a String containing an integer will be converted
|
|
17
|
+
# before validation.
|
|
18
|
+
#
|
|
19
|
+
# If the converted value is valid, it will be returned, otherwise a
|
|
20
|
+
# Xolo::InvalidDataError will be raised.
|
|
21
|
+
#
|
|
22
|
+
module Validate
|
|
23
|
+
|
|
24
|
+
# Constants
|
|
25
|
+
#########################
|
|
26
|
+
#########################
|
|
27
|
+
|
|
28
|
+
# True can be specified as 'true' or 'y' or 'yes'
|
|
29
|
+
# case insensitively. Anything else is false.
|
|
30
|
+
TRUE_RE = /(\Atrue\z)|(\Ay(es)?\z)/i.freeze
|
|
31
|
+
|
|
32
|
+
# Convenience - constants from classes
|
|
33
|
+
|
|
34
|
+
TITLE_ATTRS = Xolo::Admin::Title::ATTRIBUTES
|
|
35
|
+
VERSION_ATTRS = Xolo::Admin::Version::ATTRIBUTES
|
|
36
|
+
|
|
37
|
+
# Self Service icons must be one of these mime types,
|
|
38
|
+
# as determined by the output of `file -b -I`
|
|
39
|
+
SSVC_ICON_MIME_TYPES = %w[
|
|
40
|
+
image/jpeg
|
|
41
|
+
image/png
|
|
42
|
+
image/gif
|
|
43
|
+
].freeze
|
|
44
|
+
|
|
45
|
+
# The minimum length of a Titles Description.
|
|
46
|
+
MIN_TITLE_DESC_LENGTH = 25
|
|
47
|
+
|
|
48
|
+
# OS versions (min/max) must match this regex.
|
|
49
|
+
# - Start of string
|
|
50
|
+
# - two digits
|
|
51
|
+
# - optionally
|
|
52
|
+
# - a dot
|
|
53
|
+
# - one or more digits
|
|
54
|
+
# - optionally
|
|
55
|
+
# - a dot
|
|
56
|
+
# - one or more digits
|
|
57
|
+
OS_VERSION_RE = /\A\d\d(\.\d+){0,2}\z/.freeze
|
|
58
|
+
|
|
59
|
+
# Module methods
|
|
60
|
+
##############################
|
|
61
|
+
##############################
|
|
62
|
+
|
|
63
|
+
# when this module is included
|
|
64
|
+
def self.included(includer)
|
|
65
|
+
Xolo.verbose_include includer, self
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Instance Methods
|
|
69
|
+
##########################
|
|
70
|
+
##########################
|
|
71
|
+
|
|
72
|
+
# Thes methods all raise this error
|
|
73
|
+
def raise_invalid_data_error(val, msg)
|
|
74
|
+
raise Xolo::InvalidDataError, "'#{val}': #{msg}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# is the given command valid?
|
|
78
|
+
#########
|
|
79
|
+
def validate_cli_command
|
|
80
|
+
return if Xolo::Admin::Options::COMMANDS.key? cli_cmd.command
|
|
81
|
+
|
|
82
|
+
msg = cli_cmd.command.pix_empty? ? "Usage: #{usage}" : "Unknown command: '#{cli_cmd.command}'"
|
|
83
|
+
raise ArgumentError, msg
|
|
84
|
+
end # validate command
|
|
85
|
+
|
|
86
|
+
# were we given a title?
|
|
87
|
+
#########
|
|
88
|
+
def validate_cli_title
|
|
89
|
+
# this command doesn't need a title arg
|
|
90
|
+
return if Xolo::Admin::Options::COMMANDS[cli_cmd.command][:target] == :none
|
|
91
|
+
|
|
92
|
+
raise ArgumentError, "No title provided!\nUsage: #{usage}" unless cli_cmd.title
|
|
93
|
+
|
|
94
|
+
# this validates the format
|
|
95
|
+
validate_title cli_cmd.title
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# were we given a version?
|
|
99
|
+
#########
|
|
100
|
+
def validate_cli_version
|
|
101
|
+
# this command doesn't need a version arg
|
|
102
|
+
return unless version_command? || title_or_version_command?
|
|
103
|
+
|
|
104
|
+
# TODO:
|
|
105
|
+
# If this is an 'add-' command, ensure the version
|
|
106
|
+
# doesn't already exist.
|
|
107
|
+
# Otherwise, make sure it does already exist
|
|
108
|
+
#
|
|
109
|
+
|
|
110
|
+
vers = cli_cmd.version
|
|
111
|
+
return unless vers.to_s.empty? || vers.start_with?(Xolo::DASH)
|
|
112
|
+
|
|
113
|
+
raise ArgumentError, "No version provided with '#{cli_cmd.command}' command!\nUsage: #{usage}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Validate the command options acquired from the command line.
|
|
117
|
+
# Walkthru will validate them individually as they are entered.
|
|
118
|
+
#
|
|
119
|
+
##########################
|
|
120
|
+
def validate_cli_cmd_opts
|
|
121
|
+
cmd = cli_cmd.command
|
|
122
|
+
opts_defs = Xolo::Admin::Options::COMMANDS[cmd][:opts]
|
|
123
|
+
return if opts_defs.empty?
|
|
124
|
+
|
|
125
|
+
opts_defs.each do |key, deets|
|
|
126
|
+
# skip things not given on the command line
|
|
127
|
+
next unless cli_cmd_opts["#{key}_given"]
|
|
128
|
+
|
|
129
|
+
# skip things that shouldn't be validated
|
|
130
|
+
next unless deets[:validate]
|
|
131
|
+
|
|
132
|
+
# if the item is not required, and 'none' is given, set the
|
|
133
|
+
# value to nil and we're done
|
|
134
|
+
unless deets[:required]
|
|
135
|
+
if cli_cmd_opts[key].is_a?(Array) && cli_cmd_opts[key].include?(Xolo::NONE)
|
|
136
|
+
cli_cmd_opts[key] = []
|
|
137
|
+
next
|
|
138
|
+
elsif cli_cmd_opts[key] == Xolo::NONE
|
|
139
|
+
cli_cmd_opts[key] = nil
|
|
140
|
+
next
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# If an item is :multi, it is an array.
|
|
145
|
+
# If it has only one item, split it on commas
|
|
146
|
+
# This handles multi items being given multiple times as CLI opts, or
|
|
147
|
+
# as comma-sep values in CLI opts or walkthru.
|
|
148
|
+
#
|
|
149
|
+
if deets[:multi] && cli_cmd_opts[key].size == 1
|
|
150
|
+
cli_cmd_opts[key] = cli_cmd_opts[key].first.split(Xolo::COMMA_SEP_RE)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
validation_method =
|
|
154
|
+
case deets[:validate]
|
|
155
|
+
when Symbol
|
|
156
|
+
deets[:validate]
|
|
157
|
+
when TrueClass
|
|
158
|
+
"validate_#{key}"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# nothing to do if no validation method
|
|
162
|
+
next unless validation_method
|
|
163
|
+
|
|
164
|
+
# run the validation, which raises an error if invalid, or returns
|
|
165
|
+
# the converted value if OK - the converted value replaces the original in the
|
|
166
|
+
# cmd_opts
|
|
167
|
+
# puts "Validating #{key}, value '#{cli_cmd_opts[key]}', with #{validation_method}" if debug?
|
|
168
|
+
cli_cmd_opts[key] = send validation_method, cli_cmd_opts[key]
|
|
169
|
+
# puts "Valid Value for #{key} is now '#{cli_cmd_opts[key]}'" if debug?
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# if we are here, eveything on the commandline checked out, so now
|
|
173
|
+
# go through the opts_defs keys, and for any that were not given on the command line,
|
|
174
|
+
# add the value for that key from current_opt_values to Xolo::Admin::Options.cli_cmd_opts
|
|
175
|
+
# which will then be passed for internal consistency validation.
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Title Attributes
|
|
179
|
+
#
|
|
180
|
+
##################################################
|
|
181
|
+
|
|
182
|
+
# validate a Xolo title. Must be 2+ chars long, only lowercase
|
|
183
|
+
# alpha-numerics & dashes
|
|
184
|
+
#
|
|
185
|
+
# Must also exist or not exist, depending on the command we are running
|
|
186
|
+
# @param val [Object] The value to validate
|
|
187
|
+
#
|
|
188
|
+
# @return [String] The valid value
|
|
189
|
+
##########################
|
|
190
|
+
def validate_title(val)
|
|
191
|
+
val = val.to_s.strip
|
|
192
|
+
|
|
193
|
+
title_exists = Xolo::Admin::Title.exist?(val, server_cnx)
|
|
194
|
+
|
|
195
|
+
# adding... cant already exist, and name must be OK
|
|
196
|
+
if cli_cmd.command == Xolo::Admin::Options::ADD_TITLE_CMD
|
|
197
|
+
err =
|
|
198
|
+
if title_exists
|
|
199
|
+
'already exists in Xolo'
|
|
200
|
+
elsif val !~ /\A[a-z0-9-][a-z0-9-]+\z/
|
|
201
|
+
TITLE_ATTRS[:title][:invalid_msg]
|
|
202
|
+
else
|
|
203
|
+
return val
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# a command where it must exist
|
|
207
|
+
elsif Xolo::Admin::Options::MUST_EXIST_COMMANDS.include?(cli_cmd.command)
|
|
208
|
+
return val if title_exists
|
|
209
|
+
|
|
210
|
+
err = "doesn't exist in Xolo"
|
|
211
|
+
|
|
212
|
+
# any other command
|
|
213
|
+
else
|
|
214
|
+
return val
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
raise_invalid_data_error val, err
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# validate a title display-name. Must be 3+ chars long
|
|
221
|
+
#
|
|
222
|
+
# @param val [Object] The value to validate
|
|
223
|
+
#
|
|
224
|
+
#
|
|
225
|
+
# @return [String] The valid value
|
|
226
|
+
##########################
|
|
227
|
+
def validate_title_display_name(val)
|
|
228
|
+
val = val.to_s.strip
|
|
229
|
+
return val if val =~ /\A\S.+\S\z/
|
|
230
|
+
|
|
231
|
+
raise_invalid_data_error val, TITLE_ATTRS[:display_name][:invalid_msg]
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# validate a title description. Must be 20+ chars long
|
|
235
|
+
#
|
|
236
|
+
# @param val [Object] The value to validate
|
|
237
|
+
#
|
|
238
|
+
# @return [Boolean, String] The validity, or the valid value
|
|
239
|
+
##########################
|
|
240
|
+
def validate_title_desc(val)
|
|
241
|
+
val = val.to_s.strip
|
|
242
|
+
return val if val.length >= Xolo::Core::BaseClasses::Title::MIN_TITLE_DESC_LENGTH
|
|
243
|
+
|
|
244
|
+
raise_invalid_data_error val, TITLE_ATTRS[:description][:invalid_msg]
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# validate a title publisher. Must be 3+ chars long
|
|
248
|
+
#
|
|
249
|
+
# @param val [Object] The value to validate
|
|
250
|
+
#
|
|
251
|
+
# @return [String] The valid value
|
|
252
|
+
##########################
|
|
253
|
+
def validate_publisher(val)
|
|
254
|
+
val = val.to_s.strip
|
|
255
|
+
return val if val.length >= 3
|
|
256
|
+
|
|
257
|
+
raise_invalid_data_error val, TITLE_ATTRS[:publisher][:invalid_msg]
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# validate a title app_name. Must end with .app
|
|
261
|
+
#
|
|
262
|
+
# @param val [Object] The value to validate
|
|
263
|
+
#
|
|
264
|
+
# @return [String] The valid value
|
|
265
|
+
##########################
|
|
266
|
+
def validate_app_name(val)
|
|
267
|
+
val = val.to_s.strip
|
|
268
|
+
return val if val.end_with? Xolo::DOTAPP
|
|
269
|
+
|
|
270
|
+
raise_invalid_data_error val, TITLE_ATTRS[:app_bundle_id][:invalid_msg]
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# validate a title app_bundle_id. Must include at least one dot
|
|
274
|
+
#
|
|
275
|
+
# @param val [Object] The value to validate
|
|
276
|
+
#
|
|
277
|
+
# @return [String] The valid value
|
|
278
|
+
##########################
|
|
279
|
+
def validate_app_bundle_id(val)
|
|
280
|
+
val = val.to_s.strip
|
|
281
|
+
return val if val.include? Xolo::DOT
|
|
282
|
+
|
|
283
|
+
raise_invalid_data_error val, TITLE_ATTRS[:app_bundle_id][:invalid_msg]
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# validate a title version script. Must start with '#!'
|
|
287
|
+
#
|
|
288
|
+
# @param val [Object] The value to validate
|
|
289
|
+
#
|
|
290
|
+
# @return [Pathname] The valid value
|
|
291
|
+
##########################
|
|
292
|
+
def validate_version_script(val)
|
|
293
|
+
val = Pathname.new val.to_s.strip
|
|
294
|
+
return val if val.file? && val.readable? && val.read.start_with?('#!')
|
|
295
|
+
|
|
296
|
+
raise_invalid_data_error val, TITLE_ATTRS[:version_script][:invalid_msg]
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# validate a title uninstall script:
|
|
300
|
+
# - a path to a script which must start with '#!'
|
|
301
|
+
# OR
|
|
302
|
+
# - 'none' to unset the value
|
|
303
|
+
#
|
|
304
|
+
# TODO: Consistency with expiration.
|
|
305
|
+
#
|
|
306
|
+
# @param val [Object] The value to validate
|
|
307
|
+
#
|
|
308
|
+
# @return [String] The valid value
|
|
309
|
+
##########################
|
|
310
|
+
def validate_uninstall_script(val)
|
|
311
|
+
val = val.to_s.strip
|
|
312
|
+
return if val == Xolo::NONE
|
|
313
|
+
|
|
314
|
+
script_file = Pathname.new(val).expand_path
|
|
315
|
+
|
|
316
|
+
if script_file.readable?
|
|
317
|
+
script = script_file.read
|
|
318
|
+
return script_file.to_s if script.start_with? '#!'
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
raise_invalid_data_error val, TITLE_ATTRS[:uninstall_script][:invalid_msg]
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# validate a title uninstall ids:
|
|
325
|
+
# - an array of package identifiers
|
|
326
|
+
# OR
|
|
327
|
+
# - 'none' to unset the value
|
|
328
|
+
#
|
|
329
|
+
# TODO: Consistency with expiration.
|
|
330
|
+
#
|
|
331
|
+
# @param val [Object] The value to validate
|
|
332
|
+
#
|
|
333
|
+
# @return [Array<String>] The valid value
|
|
334
|
+
##########################
|
|
335
|
+
def validate_uninstall_ids(val)
|
|
336
|
+
val = [val] unless val.is_a? Array
|
|
337
|
+
return Xolo::X if val == [Xolo::X]
|
|
338
|
+
return [] if val.include? Xolo::NONE
|
|
339
|
+
|
|
340
|
+
return val
|
|
341
|
+
|
|
342
|
+
raise_invalid_data_error val, TITLE_ATTRS[:uninstall_ids][:invalid_msg]
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# validate an array of jamf group names to use as targets when released.
|
|
346
|
+
# 'all', or 'none' are also acceptable
|
|
347
|
+
#
|
|
348
|
+
# These groups cannot be in the excluded_group - but that validation happens
|
|
349
|
+
# later in the consistency checks via validate_scope_targets_and_exclusions
|
|
350
|
+
#
|
|
351
|
+
# @param val [Array<String>] The value to validate: names of jamf comp.
|
|
352
|
+
# groups, or 'all', or 'none'
|
|
353
|
+
#
|
|
354
|
+
# @return [Array<String>] The valid value
|
|
355
|
+
##########################
|
|
356
|
+
def validate_release_groups(val)
|
|
357
|
+
val = [val] unless val.is_a? Array
|
|
358
|
+
if val.include? Xolo::NONE
|
|
359
|
+
return []
|
|
360
|
+
elsif val.include? Xolo::TARGET_ALL
|
|
361
|
+
validate_release_to_all_allowed
|
|
362
|
+
|
|
363
|
+
return [Xolo::TARGET_ALL]
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# non-existant jamf groups
|
|
367
|
+
bad_grps = bad_jamf_groups(val)
|
|
368
|
+
return val if bad_grps.empty?
|
|
369
|
+
|
|
370
|
+
bad_grps = "No Such Groups: #{bad_grps.join(Xolo::COMMA_JOIN)}"
|
|
371
|
+
raise_invalid_data_error bad_grps, TITLE_ATTRS[:release_groups][:invalid_msg]
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# check if the current admin is allowed to set a title's release groups to 'all'
|
|
375
|
+
#
|
|
376
|
+
# @return [void]
|
|
377
|
+
##########################
|
|
378
|
+
def validate_release_to_all_allowed
|
|
379
|
+
svr_resp = Xolo::Admin::Title.release_to_all_allowed?(server_cnx)
|
|
380
|
+
return if svr_resp[:allowed]
|
|
381
|
+
|
|
382
|
+
raise Xolo::InvalidDataError, svr_resp[:msg]
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# validate an array of jamf groups to use as exclusions.
|
|
386
|
+
# 'none' is also acceptable
|
|
387
|
+
#
|
|
388
|
+
# excluded groups cannot be in the release groups or pilot groups
|
|
389
|
+
#
|
|
390
|
+
# @param val [Array<String>] The value to validate: names of jamf comp.
|
|
391
|
+
# groups, or 'none'
|
|
392
|
+
#
|
|
393
|
+
# @return [Array<String>] The valid value
|
|
394
|
+
##########################
|
|
395
|
+
def validate_excluded_groups(val)
|
|
396
|
+
val = [val] unless val.is_a? Array
|
|
397
|
+
return [] if val.include? Xolo::NONE
|
|
398
|
+
|
|
399
|
+
bad_grps = bad_jamf_groups(val)
|
|
400
|
+
return val if bad_grps.empty?
|
|
401
|
+
|
|
402
|
+
bad_grps = "No Such Groups: #{bad_grps.join(Xolo::COMMA_JOIN)}"
|
|
403
|
+
|
|
404
|
+
raise_invalid_data_error bad_grps, TITLE_ATTRS[:excluded_groups][:invalid_msg]
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# validate an array of jamf groups to use as MDM deployment targets.
|
|
408
|
+
# 'none' is also acceptable
|
|
409
|
+
#
|
|
410
|
+
# @param val [Array<String>] The value to validate: names of jamf comp.
|
|
411
|
+
# groups, or 'none'
|
|
412
|
+
#
|
|
413
|
+
# @return [Array<String>] The valid value
|
|
414
|
+
##########################
|
|
415
|
+
def validate_deploy_groups(val)
|
|
416
|
+
val = [val] unless val.is_a? Array
|
|
417
|
+
return [] if val.include? Xolo::NONE
|
|
418
|
+
|
|
419
|
+
bad_grps = bad_jamf_groups(val)
|
|
420
|
+
return val if bad_grps.empty?
|
|
421
|
+
|
|
422
|
+
bad_grps = "No Such Groups: #{bad_grps.join(Xolo::COMMA_JOIN)}"
|
|
423
|
+
|
|
424
|
+
raise_invalid_data_error bad_grps, 'Invalid group(s). Must exist in Jamf Pro.'
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
# @param grp_ary [Array<String>] Jamf groups to validate
|
|
428
|
+
# @return [Array<String>] Jamf groups that do not exist.
|
|
429
|
+
##########################
|
|
430
|
+
def bad_jamf_groups(group_ary)
|
|
431
|
+
group_ary = [group_ary] unless group_ary.is_a? Array
|
|
432
|
+
|
|
433
|
+
group_ary - jamf_computer_group_names
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# validate a titles expiration. Must be a non-negative integer
|
|
437
|
+
#
|
|
438
|
+
# @param val [Object] The value to validate
|
|
439
|
+
#
|
|
440
|
+
# @return [Integer] The valid value
|
|
441
|
+
##########################
|
|
442
|
+
def validate_expiration(val)
|
|
443
|
+
val = val.to_s
|
|
444
|
+
if val.pix_integer?
|
|
445
|
+
val = val.to_i
|
|
446
|
+
return Xolo::NONE if val.zero?
|
|
447
|
+
return val if val.positive?
|
|
448
|
+
elsif val == Xolo::NONE
|
|
449
|
+
return val
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
raise_invalid_data_error val, TITLE_ATTRS[:expiration][:invalid_msg]
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# validate a title expiration paths. Must one or more full paths
|
|
456
|
+
# starting with a / and containing at least one more.
|
|
457
|
+
#
|
|
458
|
+
# @param val [Object] The value to validate
|
|
459
|
+
#
|
|
460
|
+
# @return [Array<String>] The valid array
|
|
461
|
+
##########################
|
|
462
|
+
def validate_expire_paths(val)
|
|
463
|
+
val = [val] unless val.is_a? Array
|
|
464
|
+
return [] if val.include? Xolo::NONE
|
|
465
|
+
|
|
466
|
+
val.map!(&:to_s)
|
|
467
|
+
bad_paths = []
|
|
468
|
+
|
|
469
|
+
val.each { |path| bad_paths << path unless path =~ %r{\A/\w.*/.*\z} }
|
|
470
|
+
|
|
471
|
+
return val if bad_paths.empty?
|
|
472
|
+
|
|
473
|
+
raise_invalid_data_error bad_paths.join(Xolo::COMMA_JOIN), TITLE_ATTRS[:expire_paths][:invalid_msg]
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# validate boolean options
|
|
477
|
+
#
|
|
478
|
+
# Never raises an error, just returns true of false based on the string value
|
|
479
|
+
#
|
|
480
|
+
# @param val [Object] The value to validate
|
|
481
|
+
#
|
|
482
|
+
# @return [Boolean] The valid value
|
|
483
|
+
##########################
|
|
484
|
+
def validate_boolean(val)
|
|
485
|
+
val.to_s =~ TRUE_RE ? true : false
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# validate a self_service_category. Must exist in Jamf Pro
|
|
489
|
+
#
|
|
490
|
+
# @param val [Object] The value to validate
|
|
491
|
+
#
|
|
492
|
+
# @return [Boolean, String] The validity, or the valid value
|
|
493
|
+
##########################
|
|
494
|
+
def validate_self_service_category(val)
|
|
495
|
+
val = val.to_s
|
|
496
|
+
|
|
497
|
+
# TODO: implement the check via the xolo server
|
|
498
|
+
return val # if category exists
|
|
499
|
+
|
|
500
|
+
raise_invalid_data_error val, TITLE_ATTRS[:self_service_category][:invalid_msg]
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
# validate a path to a self_service_icon. Must exist locally and be readable
|
|
504
|
+
#
|
|
505
|
+
# @param val [Object] The value to validate
|
|
506
|
+
#
|
|
507
|
+
# @return [Pathname] The valid value
|
|
508
|
+
##########################
|
|
509
|
+
def validate_self_service_icon(val)
|
|
510
|
+
val = Pathname.new val.to_s.strip
|
|
511
|
+
if val.file? && val.readable?
|
|
512
|
+
mimetype = `/usr/bin/file -b -I #{Shellwords.escape val.to_s}`.split(';').first
|
|
513
|
+
return val if SSVC_ICON_MIME_TYPES.include? mimetype
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
raise_invalid_data_error val, TITLE_ATTRS[:self_service_icon][:invalid_msg]
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# validate a path to a jamf_pkg_file. Must exist locally and be readable
|
|
520
|
+
# and have the correct ext.
|
|
521
|
+
#
|
|
522
|
+
# @param val [Object] The value to validate
|
|
523
|
+
#
|
|
524
|
+
# @return [Pathname] The valid value
|
|
525
|
+
##########################
|
|
526
|
+
def validate_pkg_to_upload(val)
|
|
527
|
+
val = Pathname.new val.to_s.strip
|
|
528
|
+
return val if val.file? && val.readable? && (Xolo::OK_PKG_EXTS.include? val.extname)
|
|
529
|
+
|
|
530
|
+
raise_invalid_data_error val, VERSION_ATTRS[:jamf_pkg_file][:invalid_msg]
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
# Version Attributes
|
|
534
|
+
#
|
|
535
|
+
##################################################
|
|
536
|
+
|
|
537
|
+
# TODO: validate a Xolo Version. Must be 2+ chars long, only lowercase
|
|
538
|
+
# alpha-numerics & dashes
|
|
539
|
+
#
|
|
540
|
+
# @param val [Object] The value to validate
|
|
541
|
+
#
|
|
542
|
+
# @return [String] The valid value
|
|
543
|
+
##########################
|
|
544
|
+
def validate_version(val)
|
|
545
|
+
val
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
# @param val [Object] The value to validate
|
|
549
|
+
#
|
|
550
|
+
# @return [Date] The valid value
|
|
551
|
+
##########################
|
|
552
|
+
def validate_publish_date(val)
|
|
553
|
+
val = Time.now.to_s if val.pix_empty?
|
|
554
|
+
val = Time.parse val.to_s
|
|
555
|
+
# TODO: ? Ensure this date is >= the prev. version and <= the next
|
|
556
|
+
return val
|
|
557
|
+
|
|
558
|
+
raise VERSION_ATTRS[:publish_date][:invalid_msg]
|
|
559
|
+
rescue StandardError => e
|
|
560
|
+
raise_invalid_data_error val, e.to_s
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
# @param val [String] The value to validate
|
|
564
|
+
#
|
|
565
|
+
# @return [String] The valid value
|
|
566
|
+
##########################
|
|
567
|
+
def validate_min_os(val)
|
|
568
|
+
# inherit if needed
|
|
569
|
+
val = current_opt_values[:min_os] if val.pix_empty?
|
|
570
|
+
|
|
571
|
+
# use the default if still empty or 'none' - we get it from the server
|
|
572
|
+
val = Xolo::Admin::Options.default_min_os if val.pix_empty? || val == Xolo::NONE
|
|
573
|
+
|
|
574
|
+
raise VERSION_ATTRS[:min_os][:invalid_msg] unless val =~ OS_VERSION_RE
|
|
575
|
+
|
|
576
|
+
val
|
|
577
|
+
rescue StandardError => e
|
|
578
|
+
raise_invalid_data_error val, e.to_s
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# @param val [Object] The value to validate
|
|
582
|
+
#
|
|
583
|
+
# @return [Gem::Version] The valid value
|
|
584
|
+
##########################
|
|
585
|
+
def validate_max_os(val)
|
|
586
|
+
return if val.pix_empty? || val == Xolo::NONE
|
|
587
|
+
|
|
588
|
+
raise VERSION_ATTRS[:max_os][:invalid_msg] unless val =~ OS_VERSION_RE
|
|
589
|
+
|
|
590
|
+
val
|
|
591
|
+
rescue StandardError => e
|
|
592
|
+
raise_invalid_data_error val, e.to_s
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
# @param val [Object] The value to validate
|
|
596
|
+
#
|
|
597
|
+
# @return [Array<Array<String>>] The valid value
|
|
598
|
+
##########################
|
|
599
|
+
def validate_killapps(val)
|
|
600
|
+
val = [val] unless val.is_a? Array
|
|
601
|
+
return Xolo::X if val == [Xolo::X]
|
|
602
|
+
return Xolo::NONE if val.include? Xolo::NONE
|
|
603
|
+
|
|
604
|
+
val.map!(&:to_s)
|
|
605
|
+
|
|
606
|
+
# if one of the killapps is Xolo::Admin::Version::USE_TITLE_FOR_KILLAPP
|
|
607
|
+
# convert it into the appropriate string
|
|
608
|
+
val.map! { |ka| expand_use_title(ka) }
|
|
609
|
+
|
|
610
|
+
val.each { |ka| validate_single_killapp(ka) }
|
|
611
|
+
|
|
612
|
+
val
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
# @param val [Object] The value to validate
|
|
616
|
+
#
|
|
617
|
+
# @return [String] The valid value
|
|
618
|
+
##########################
|
|
619
|
+
def validate_contact_email(val)
|
|
620
|
+
val = val.to_s.strip
|
|
621
|
+
return val if val =~ /\A\S+@\S+\.\S+\z/
|
|
622
|
+
|
|
623
|
+
raise_invalid_data_error val, VERSION_ATTRS[:contact_email][:invalid_msg]
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
# expand Xolo::Admin::Version::USE_TITLE_FOR_KILLAPP into the proper killall string
|
|
627
|
+
#
|
|
628
|
+
# @param ka [String] the string to expand if needed
|
|
629
|
+
# @return [String] the original string, or the expanded version
|
|
630
|
+
##########################
|
|
631
|
+
def expand_use_title(ka)
|
|
632
|
+
return ka unless ka == Xolo::Admin::Version::USE_TITLE_FOR_KILLAPP
|
|
633
|
+
|
|
634
|
+
title = Xolo::Admin::Title.fetch cli_cmd.title, server_cnx
|
|
635
|
+
unless title.app_name
|
|
636
|
+
raise_invalid_data_error Xolo::Admin::Version::USE_TITLE_FOR_KILLAPP,
|
|
637
|
+
'Title does not use app_name'
|
|
638
|
+
end
|
|
639
|
+
unless title.app_bundle_id
|
|
640
|
+
raise_invalid_data_error Xolo::Admin::Version::USE_TITLE_FOR_KILLAPP,
|
|
641
|
+
'Title does not use app_bundle_id'
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
"#{title.app_name};#{title.app_bundle_id}"
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
# Validate an individual killapp string
|
|
648
|
+
# @param ka [String] the string to expand if needed
|
|
649
|
+
# @return [String] the validated value
|
|
650
|
+
##########################
|
|
651
|
+
def validate_single_killapp(ka)
|
|
652
|
+
name, bundle_id = ka.split(Xolo::SEMICOLON_SEP_RE)
|
|
653
|
+
raise_invalid_data_error name, 'App name required before semicolon' unless name
|
|
654
|
+
raise_invalid_data_error name, 'App Bundle ID required after semicolon' unless bundle_id
|
|
655
|
+
|
|
656
|
+
# app name must end with .app. Use the err messge from Title/app_name
|
|
657
|
+
raise_invalid_data_error name, TITLE_ATTRS[:app_name][:invalid_msg] unless name.end_with?(Xolo::DOTAPP)
|
|
658
|
+
|
|
659
|
+
# app bundle id must contain a dot. Use the err messge from Title/app_bundle_id
|
|
660
|
+
unless bundle_id&.include?(Xolo::DOT)
|
|
661
|
+
raise_invalid_data_error bundle_id,
|
|
662
|
+
TITLE_ATTRS[:app_bundle_id][:invalid_msg]
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
ka
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
# validate an array of jamf groups to use as pilots for testing a version
|
|
669
|
+
# before releasing it.
|
|
670
|
+
# 'none' is also acceptable
|
|
671
|
+
#
|
|
672
|
+
# NOTE: we will not compare targets to exclusions - we'll just verify
|
|
673
|
+
# that the jamf groups exist. If a group (or an individual mac) is both a
|
|
674
|
+
# pilot and an exclusion, the exclusion wins.
|
|
675
|
+
#
|
|
676
|
+
# @param val [Array<String>] The value to validate: names of jamf comp.
|
|
677
|
+
# groups, or 'none'
|
|
678
|
+
#
|
|
679
|
+
# @return [Array<String>] The valid value
|
|
680
|
+
##########################
|
|
681
|
+
def validate_pilot_groups(val)
|
|
682
|
+
val = [val] unless val.is_a? Array
|
|
683
|
+
return [] if val.include? Xolo::NONE
|
|
684
|
+
|
|
685
|
+
bad_grps = bad_jamf_groups(val)
|
|
686
|
+
return val if bad_grps.empty?
|
|
687
|
+
|
|
688
|
+
bad_grps = "No Such Groups: #{bad_grps.join(Xolo::COMMA_JOIN)}"
|
|
689
|
+
|
|
690
|
+
raise_invalid_data_error bad_grps, VERSION_ATTRS[:pilot_groups][:invalid_msg]
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
# Try to fetch a known route from the given xolo server
|
|
694
|
+
#
|
|
695
|
+
# @param val [String] The hostname to validate.
|
|
696
|
+
#
|
|
697
|
+
# @return [void]
|
|
698
|
+
#######
|
|
699
|
+
def validate_hostname(val)
|
|
700
|
+
if val.downcase == 'x'
|
|
701
|
+
val = nil
|
|
702
|
+
return
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
response = server_cnx(host: val).get Xolo::Admin::Connection::PING_ROUTE
|
|
706
|
+
return val if response.body == Xolo::Admin::Connection::PING_RESPONSE
|
|
707
|
+
|
|
708
|
+
raise_invalid_data_error val, Xolo::Admin::Configuration::KEYS[:hostname][:invalid_msg]
|
|
709
|
+
rescue Faraday::ConnectionFailed
|
|
710
|
+
raise_invalid_data_error val, Xolo::Admin::Configuration::KEYS[:hostname][:invalid_msg]
|
|
711
|
+
rescue Faraday::SSLError
|
|
712
|
+
raise_invalid_data_error val, 'SSL Error. Be sure to use the fully qualified hostname.'
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
# Password (and username) will be validated via the server
|
|
716
|
+
#
|
|
717
|
+
# @param val [String] The passwd to be validated with the stored or given username
|
|
718
|
+
#
|
|
719
|
+
# @return [void]
|
|
720
|
+
#######
|
|
721
|
+
def validate_pw(val)
|
|
722
|
+
if val.downcase == 'x'
|
|
723
|
+
val = Xolo::BLANK
|
|
724
|
+
return
|
|
725
|
+
end
|
|
726
|
+
pw = val
|
|
727
|
+
hostname = walkthru? ? walkthru_cmd_opts[:hostname] : cli_cmd_opts[:hostname]
|
|
728
|
+
admin = walkthru? ? walkthru_cmd_opts[:admin] : cli_cmd_opts[:admin]
|
|
729
|
+
no_gui = walkthru? ? walkthru_cmd_opts[:no_gui] : cli_cmd_opts[:no_gui]
|
|
730
|
+
|
|
731
|
+
raise Xolo::MissingDataError, 'hostname must be set before password' if hostname.pix_empty?
|
|
732
|
+
raise Xolo::MissingDataError, 'admin username must be set before password' if admin.pix_empty?
|
|
733
|
+
|
|
734
|
+
pw = config.data_from_command_file_or_string(pw, enforce_secure_mode: true) if no_gui
|
|
735
|
+
|
|
736
|
+
payload = { admin: admin, password: pw }.to_json
|
|
737
|
+
begin
|
|
738
|
+
server_cnx(host: hostname).post Xolo::Admin::Connection::LOGIN_ROUTE, payload
|
|
739
|
+
rescue Faraday::UnauthorizedError
|
|
740
|
+
raise_invalid_data_error 'User/Password', 'Username or Password is incorrect'
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
# return the value for storage in the config file if no_gui is true
|
|
744
|
+
return val if no_gui
|
|
745
|
+
|
|
746
|
+
store_pw admin, val
|
|
747
|
+
|
|
748
|
+
# return a placeholder value saying that the password is stored in the keychain
|
|
749
|
+
Xolo::Admin::Configuration::CREDENTIALS_IN_KEYCHAIN
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
# Does the chosen editor exist and is it executable?
|
|
753
|
+
#
|
|
754
|
+
# @aram val [String] The path to the editor executable, possibly followed by
|
|
755
|
+
# a space and any command line arguments, e.g. '/usr/bin/vim -c "set ft=markdown"'
|
|
756
|
+
#
|
|
757
|
+
# @return [void]
|
|
758
|
+
#######
|
|
759
|
+
def validate_editor(val)
|
|
760
|
+
tool = val.split(' -')[0].strip
|
|
761
|
+
tool = Pathname.new tool
|
|
762
|
+
raise_invalid_data_error val, Xolo::Admin::Configuration::KEYS[:editor][:invalid_msg] unless tool.executable?
|
|
763
|
+
|
|
764
|
+
val
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
# Internal Consistency Checks!
|
|
768
|
+
#
|
|
769
|
+
##################################################
|
|
770
|
+
|
|
771
|
+
# These methods all raise this error
|
|
772
|
+
#
|
|
773
|
+
# @param msg [String] an error message
|
|
774
|
+
#
|
|
775
|
+
# @return [void]
|
|
776
|
+
########
|
|
777
|
+
def raise_consistency_error(msg)
|
|
778
|
+
raise Xolo::InvalidDataError, msg
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
# Given an ostruct of options that have been individually validated, and combined
|
|
782
|
+
# with any current_opt_values as needed, check the data for internal consistency.
|
|
783
|
+
# The unset values in the ostruct should be nil. 'none' is used for unsetting values,
|
|
784
|
+
# in the CLI and walkthru, and is converted to nil during validation if appropriate.
|
|
785
|
+
#
|
|
786
|
+
# @param opts [OpenStruct] the current options
|
|
787
|
+
#
|
|
788
|
+
# @return [void]
|
|
789
|
+
#######
|
|
790
|
+
def validate_internal_consistency(opts)
|
|
791
|
+
return unless add_command? || edit_command?
|
|
792
|
+
|
|
793
|
+
if version_command?
|
|
794
|
+
validate_version_consistency opts
|
|
795
|
+
elsif title_command?
|
|
796
|
+
validate_title_consistency opts
|
|
797
|
+
end
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
# @param opts [OpenStruct] the current options
|
|
801
|
+
#
|
|
802
|
+
# @return [void]
|
|
803
|
+
#######
|
|
804
|
+
def validate_version_consistency(opts)
|
|
805
|
+
validate_scope_targets_and_exclusions(opts)
|
|
806
|
+
validate_min_os_and_max_os(opts)
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
# @param opts [OpenStruct] the current options
|
|
810
|
+
#
|
|
811
|
+
# @return [void]
|
|
812
|
+
#######
|
|
813
|
+
def validate_title_consistency(opts)
|
|
814
|
+
# if we are just listing the versions, nothing to do
|
|
815
|
+
return if cli_cmd.command == Xolo::Admin::Options::LIST_VERSIONS_CMD
|
|
816
|
+
|
|
817
|
+
# order of these matters
|
|
818
|
+
validate_scope_targets_and_exclusions(opts)
|
|
819
|
+
validate_title_consistency_app_and_script(opts)
|
|
820
|
+
validate_title_consistency_app_or_script(opts)
|
|
821
|
+
validate_title_consistency_app_name_and_id(opts)
|
|
822
|
+
validate_title_consistency_uninstall(opts)
|
|
823
|
+
validate_title_consistency_expire_paths(opts)
|
|
824
|
+
validate_title_consistency_no_all_in_ssvc(opts)
|
|
825
|
+
validate_title_consistency_ssvc_needs_category(opts)
|
|
826
|
+
end # title_consistency(opts)
|
|
827
|
+
|
|
828
|
+
# groups that will be scope targets (pilot or release) cannot
|
|
829
|
+
# also be in the exclusions.
|
|
830
|
+
#
|
|
831
|
+
# @param opts [OpenStruct] the current options
|
|
832
|
+
#
|
|
833
|
+
# @return [void]
|
|
834
|
+
#######
|
|
835
|
+
def validate_scope_targets_and_exclusions(opts)
|
|
836
|
+
# require 'pp'
|
|
837
|
+
# puts 'Opts Are:'
|
|
838
|
+
# pp opts.to_h
|
|
839
|
+
# pp caller
|
|
840
|
+
|
|
841
|
+
if title_command?
|
|
842
|
+
excls = opts.excluded_groups
|
|
843
|
+
tgts = opts.release_groups
|
|
844
|
+
tgt_type = :release
|
|
845
|
+
elsif version_command?
|
|
846
|
+
@title_for_version_validation ||= Xolo::Admin::Title.fetch cli_cmd.title, server_cnx
|
|
847
|
+
excls = @title_for_version_validation.excluded_groups
|
|
848
|
+
tgts = opts.pilot_groups
|
|
849
|
+
tgt_type = :pilot
|
|
850
|
+
else
|
|
851
|
+
excls = nil
|
|
852
|
+
tgts = nil
|
|
853
|
+
end
|
|
854
|
+
return unless excls && tgts
|
|
855
|
+
|
|
856
|
+
in_both = excls & tgts
|
|
857
|
+
return if in_both.empty?
|
|
858
|
+
|
|
859
|
+
raise_consistency_error "These groups are in both #{tgt_type}_groups and the title's excluded_groups: '#{in_both.join "', '"}'"
|
|
860
|
+
end
|
|
861
|
+
|
|
862
|
+
# if app_name or app_bundle_id is given, can't use --version-script
|
|
863
|
+
#
|
|
864
|
+
# @param opts [OpenStruct] the current options
|
|
865
|
+
#
|
|
866
|
+
# @return [void]
|
|
867
|
+
#######
|
|
868
|
+
def validate_title_consistency_app_and_script(opts)
|
|
869
|
+
return unless opts[:version_script] && (opts[:app_bundle_id] || opts[:app_name])
|
|
870
|
+
|
|
871
|
+
msg =
|
|
872
|
+
if walkthru?
|
|
873
|
+
'Version Script cannot be used with App Name & App Bundle ID'
|
|
874
|
+
else
|
|
875
|
+
'--version-script cannot be used with --app-name & --app-bundle-id'
|
|
876
|
+
end
|
|
877
|
+
|
|
878
|
+
raise_consistency_error msg
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
# but either version_script or appname and bundle id must be given
|
|
882
|
+
#
|
|
883
|
+
# @param opts [OpenStruct] the current options
|
|
884
|
+
#
|
|
885
|
+
# @return [void]
|
|
886
|
+
#######
|
|
887
|
+
def validate_title_consistency_app_or_script(opts)
|
|
888
|
+
return if opts[:version_script]
|
|
889
|
+
return if opts[:app_name] || opts[:app_bundle_id]
|
|
890
|
+
|
|
891
|
+
msg =
|
|
892
|
+
if walkthru?
|
|
893
|
+
'Either App Name & App Bundle ID. or Version Script must be given.'
|
|
894
|
+
else
|
|
895
|
+
'Must provide either --app-name & --app-bundle-id OR --version-script'
|
|
896
|
+
end
|
|
897
|
+
raise_consistency_error msg
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
# If using app_name and bundle id, both must be given
|
|
901
|
+
#
|
|
902
|
+
# @param opts [OpenStruct] the current options
|
|
903
|
+
#
|
|
904
|
+
# @return [void]
|
|
905
|
+
#######
|
|
906
|
+
def validate_title_consistency_app_name_and_id(opts)
|
|
907
|
+
return if opts[:version_script]
|
|
908
|
+
return if opts[:app_name] && opts[:app_bundle_id]
|
|
909
|
+
|
|
910
|
+
msg =
|
|
911
|
+
if walkthru?
|
|
912
|
+
'App Name & App Bundle ID must both be given if either is.'
|
|
913
|
+
else
|
|
914
|
+
'--app-name & --app-bundle-id must both be given if either is.'
|
|
915
|
+
end
|
|
916
|
+
raise_consistency_error msg
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
# if expiration is > 0, there must be at least one expiration path
|
|
920
|
+
#
|
|
921
|
+
# @param opts [OpenStruct] the current options
|
|
922
|
+
#
|
|
923
|
+
# @return [void]
|
|
924
|
+
#######
|
|
925
|
+
def validate_title_consistency_expire_paths(opts)
|
|
926
|
+
return unless opts.expiration.to_i.positive?
|
|
927
|
+
return unless opts.expire_paths.pix_empty?
|
|
928
|
+
|
|
929
|
+
msg =
|
|
930
|
+
if walkthru?
|
|
931
|
+
'At least one Expiration Path must be given if Expiration is > 0.'
|
|
932
|
+
else
|
|
933
|
+
'At least one --expiration-path must be given if --expiration is > 0'
|
|
934
|
+
end
|
|
935
|
+
raise_consistency_error msg
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
# if uninstall script, cant have uninstall ids
|
|
939
|
+
# and vice versa
|
|
940
|
+
#
|
|
941
|
+
# @param opts [OpenStruct] the current options
|
|
942
|
+
#
|
|
943
|
+
# @return [void]
|
|
944
|
+
#######
|
|
945
|
+
def validate_title_consistency_uninstall(opts)
|
|
946
|
+
# walktrhu will have already validated this
|
|
947
|
+
return if walkthru?
|
|
948
|
+
|
|
949
|
+
if opts.uninstall_script_given && opts.uninstall_ids_given
|
|
950
|
+
|
|
951
|
+
# if uninstall_script is given, uninstall_ids must be unset
|
|
952
|
+
# and vice versa
|
|
953
|
+
# raise an error if both are given
|
|
954
|
+
|
|
955
|
+
raise_consistency_error '--uninstall-script cannot be used with --uninstall-ids'
|
|
956
|
+
|
|
957
|
+
elsif opts.uninstall_script_given
|
|
958
|
+
opts.uninstall_ids = nil
|
|
959
|
+
|
|
960
|
+
elsif opts.uninstall_ids_given
|
|
961
|
+
opts.uninstall_script_given = nil
|
|
962
|
+
end
|
|
963
|
+
end
|
|
964
|
+
|
|
965
|
+
# if target_group is all, can't be in self service
|
|
966
|
+
#
|
|
967
|
+
# @param opts [OpenStruct] the current options
|
|
968
|
+
#
|
|
969
|
+
# @return [void]
|
|
970
|
+
#######
|
|
971
|
+
def validate_title_consistency_no_all_in_ssvc(opts)
|
|
972
|
+
return unless opts[:release_groups].to_a.include?(Xolo::TARGET_ALL) && opts[:self_service]
|
|
973
|
+
|
|
974
|
+
msg =
|
|
975
|
+
if walkthru?
|
|
976
|
+
"Cannot be in Self Service when Target Group is '#{Xolo::TARGET_ALL}'"
|
|
977
|
+
else
|
|
978
|
+
"--self-service cannot be used when --target-groups contains '#{Xolo::TARGET_ALL}'"
|
|
979
|
+
end
|
|
980
|
+
raise_consistency_error msg
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
# if in self service, a category must be assigned
|
|
984
|
+
#
|
|
985
|
+
# @param opts [OpenStruct] the current options
|
|
986
|
+
#
|
|
987
|
+
# @return [void]
|
|
988
|
+
#######
|
|
989
|
+
def validate_title_consistency_ssvc_needs_category(opts)
|
|
990
|
+
return unless opts[:self_service]
|
|
991
|
+
return if opts[:self_service_category]
|
|
992
|
+
|
|
993
|
+
msg =
|
|
994
|
+
if walkthru?
|
|
995
|
+
'A Self Service Category must be given if Self Service is true.'
|
|
996
|
+
else
|
|
997
|
+
'A --self-service-category must be provided when using --self-service'
|
|
998
|
+
end
|
|
999
|
+
raise_consistency_error msg
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
# min_os must be <= max_os
|
|
1003
|
+
# max_os must be >= min_os
|
|
1004
|
+
#
|
|
1005
|
+
# @param opts [OpenStruct] the current options
|
|
1006
|
+
#
|
|
1007
|
+
# @return [void]
|
|
1008
|
+
#######
|
|
1009
|
+
def validate_min_os_and_max_os(opts)
|
|
1010
|
+
# if no max_os, nothing to do
|
|
1011
|
+
return if opts[:max_os].pix_empty?
|
|
1012
|
+
|
|
1013
|
+
min_os = Gem::Version.new opts[:min_os]
|
|
1014
|
+
max_os = Gem::Version.new opts[:max_os]
|
|
1015
|
+
|
|
1016
|
+
# if things look OK, we're done
|
|
1017
|
+
return if min_os <= max_os && max_os >= min_os
|
|
1018
|
+
|
|
1019
|
+
msg =
|
|
1020
|
+
if walkthru?
|
|
1021
|
+
'Minimum OS must be less than or equal to Maximum OS'
|
|
1022
|
+
else
|
|
1023
|
+
'--max-os must be greater than or equal to --min-os'
|
|
1024
|
+
end
|
|
1025
|
+
raise_consistency_error msg
|
|
1026
|
+
end
|
|
1027
|
+
|
|
1028
|
+
end # module validate
|
|
1029
|
+
|
|
1030
|
+
end # module Core
|
|
1031
|
+
|
|
1032
|
+
end # module Xolo
|