MuranoCLI 3.2.0.beta.5 → 3.2.0.beta.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ignore +22 -11
- data/dockers/Dockerfile.2.2.9 +2 -2
- data/dockers/Dockerfile.2.3.6 +2 -2
- data/dockers/Dockerfile.2.4.3 +2 -2
- data/dockers/Dockerfile.2.5.0 +2 -2
- data/dockers/Dockerfile.GemRelease +6 -6
- data/dockers/Dockerfile.m4 +4 -4
- data/dockers/README.rst +87 -19
- data/dockers/RELEASE.rst +1 -1
- data/dockers/docker-test.sh +4 -1
- data/{.trustme.plugin → docs/ci/.trustme.plugin} +5 -2
- data/{.trustme.sh → docs/ci/.trustme.sh} +64 -16
- data/lib/MrMurano/Business.rb +73 -0
- data/lib/MrMurano/Config.rb +4 -3
- data/lib/MrMurano/Keystore.rb +4 -0
- data/lib/MrMurano/ReCommander.rb +23 -0
- data/lib/MrMurano/Solution.rb +10 -0
- data/lib/MrMurano/SyncUpDown-Core.rb +94 -56
- data/lib/MrMurano/SyncUpDown-Item.rb +2 -0
- data/lib/MrMurano/Webservice-Endpoint.rb +8 -7
- data/lib/MrMurano/commands/business.rb +60 -0
- data/lib/MrMurano/commands/content.rb +7 -1
- data/lib/MrMurano/commands/cors.rb +1 -0
- data/lib/MrMurano/commands/devices.rb +1 -1
- data/lib/MrMurano/commands/element.rb +40 -14
- data/lib/MrMurano/commands/keystore.rb +8 -0
- data/lib/MrMurano/commands/logs.rb +1 -0
- data/lib/MrMurano/commands/network.rb +120 -0
- data/lib/MrMurano/commands/postgresql.rb +2 -0
- data/lib/MrMurano/commands/service.rb +5 -0
- data/lib/MrMurano/commands/settings.rb +3 -0
- data/lib/MrMurano/commands/show.rb +1 -0
- data/lib/MrMurano/commands/status.rb +1 -0
- data/lib/MrMurano/commands/sync.rb +2 -0
- data/lib/MrMurano/commands/timeseries.rb +9 -0
- data/lib/MrMurano/commands/tsdb.rb +4 -0
- data/lib/MrMurano/commands.rb +1 -0
- data/lib/MrMurano/variegated/ruby_dig.rb +11 -0
- data/lib/MrMurano/version.rb +1 -1
- data/spec/cmd_element_spec.rb +2 -2
- data/spec/fixtures/dumped_config +1 -0
- metadata +6 -5
- /data/{.trustme.vim → docs/ci/.trustme.vim} +0 -0
data/lib/MrMurano/ReCommander.rb
CHANGED
@@ -8,6 +8,7 @@
|
|
8
8
|
require 'optparse'
|
9
9
|
require 'MrMurano/optparse'
|
10
10
|
require 'MrMurano/verbosing'
|
11
|
+
require 'MrMurano/Business'
|
11
12
|
|
12
13
|
module MrMurano
|
13
14
|
class Hooked
|
@@ -51,6 +52,9 @@ module Commander
|
|
51
52
|
attr_accessor :prompt_if_logged_off
|
52
53
|
# A command sets subcmdgrouphelp if it's help.
|
53
54
|
attr_accessor :subcmdgrouphelp
|
55
|
+
# A command sets must_not_be_managed if its
|
56
|
+
# use is restricted to unmanaged solutions.
|
57
|
+
attr_accessor :must_not_be_managed
|
54
58
|
|
55
59
|
def verify_arg_count!(args, max_args=0, mandatory=[])
|
56
60
|
if !max_args.nil? && max_args.zero?
|
@@ -84,6 +88,8 @@ module Commander
|
|
84
88
|
hooked = MrMurano::Hooked.new(section)
|
85
89
|
hooked.check_run_pre_hook
|
86
90
|
|
91
|
+
verify_solutions_unmanaged!
|
92
|
+
|
87
93
|
begin
|
88
94
|
old_run_active_command
|
89
95
|
rescue LocalJumpError => _err
|
@@ -357,6 +363,23 @@ module Commander
|
|
357
363
|
puts mur_msg
|
358
364
|
exit 0
|
359
365
|
end
|
366
|
+
|
367
|
+
# (lb): I'm not a huge fan of mingling business logic with our
|
368
|
+
# Commander monkey patch, but it's a lot more readable that
|
369
|
+
# having every command make the call!
|
370
|
+
def verify_solutions_unmanaged!
|
371
|
+
return if $cfg['tool.skip-managed']
|
372
|
+
# (lb): All @exosite.com employees are welcome behind the curtain.
|
373
|
+
if $cfg['user.name'].end_with? "@exosite.com"
|
374
|
+
MrMurano::Verbose.verbose(
|
375
|
+
"Welcome behind the curtain, #{$cfg['user.name']}!"
|
376
|
+
)
|
377
|
+
return
|
378
|
+
end
|
379
|
+
# FIXME/LATER: (lb): The Element Author should also be allowed in.
|
380
|
+
return unless active_command.must_not_be_managed
|
381
|
+
MrMurano::Business.must_not_be_managed!
|
382
|
+
end
|
360
383
|
end
|
361
384
|
end
|
362
385
|
|
data/lib/MrMurano/Solution.rb
CHANGED
@@ -220,6 +220,10 @@ module MrMurano
|
|
220
220
|
@meta[:domain]
|
221
221
|
end
|
222
222
|
|
223
|
+
def managed?
|
224
|
+
@meta[:managed]
|
225
|
+
end
|
226
|
+
|
223
227
|
def pretty_desc(add_type: false, raw_url: false)
|
224
228
|
# [lb] would normally put presentation code elsewhere (i.e., model
|
225
229
|
# classes should not be formatting output), but this seems okay.
|
@@ -297,6 +301,12 @@ module MrMurano
|
|
297
301
|
def name_validate_help
|
298
302
|
''
|
299
303
|
end
|
304
|
+
|
305
|
+
def must_not_be_managed!
|
306
|
+
return unless managed?
|
307
|
+
error 'Specified command cannot be run against managed solution.'
|
308
|
+
exit 1
|
309
|
+
end
|
300
310
|
end
|
301
311
|
|
302
312
|
class Application < Solution
|
@@ -161,26 +161,89 @@ module MrMurano
|
|
161
161
|
# @param asdown [Boolean] Direction/prespective of diff
|
162
162
|
# @return [String] The diff output
|
163
163
|
def dodiff(merged, local, _there=nil, options={})
|
164
|
+
localname = dodiff_resolve_localname(merged)
|
165
|
+
trmt, tlcl = dodiff_tempfile_paths(localname)
|
166
|
+
dodiff_header_aware(merged, trmt, tlcl, options)
|
167
|
+
end
|
168
|
+
|
169
|
+
def dodiff_resolve_localname(merged)
|
164
170
|
localname = tolocalname(merged, @itemkey)
|
165
171
|
# MUR-6488: Beware nested files, e.g., /tmp/js/script.js will
|
166
172
|
# not work, because /tmp/js does not exist!
|
167
173
|
# First, remove the leading path delimiter.
|
168
174
|
localname = localname.gsub(/^#{::File::SEPARATOR}*/, '')
|
169
175
|
# Next, replace remaining delimiters with a dot.
|
170
|
-
localname
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
176
|
+
localname.tr(::File::SEPARATOR, '.')
|
177
|
+
end
|
178
|
+
|
179
|
+
def dodiff_tempfile_paths(localname)
|
180
|
+
# (lb): Not always Lua, e.g., might be PNG. Postfix doesn't matter, though.
|
181
|
+
trmt = Tempfile.new([localname + '_remote_', '.lua'])
|
182
|
+
tlcl = Tempfile.new([localname + '_local_', '.lua'])
|
183
|
+
[trmt, tlcl]
|
184
|
+
rescue StandardError => err
|
185
|
+
MrMurano::Verbose.error("Failed to create temporary file: #{err}")
|
186
|
+
raise
|
187
|
+
end
|
188
|
+
|
189
|
+
def dodiff_header_aware(merged, trmt, tlcl, options)
|
190
|
+
dodiff_download_remote(merged, trmt, options)
|
191
|
+
MrMurano::Verbose.whirly_stop
|
192
|
+
dodiff_prepare_local_and_diff(merged, trmt, tlcl, options)
|
193
|
+
ensure
|
194
|
+
trmt.close
|
195
|
+
trmt.unlink
|
196
|
+
tlcl.close
|
197
|
+
tlcl.unlink
|
198
|
+
end
|
199
|
+
|
200
|
+
def dodiff_download_remote(merged, trmt, options)
|
201
|
+
tmp_path = Pathname.new(trmt.path)
|
202
|
+
diff_download(tmp_path, merged, options)
|
203
|
+
end
|
204
|
+
|
205
|
+
def dodiff_prepare_local_and_diff(merged, trmt, tlcl, options)
|
206
|
+
cmd = dodiff_build_cmd(trmt, tlcl, options)
|
207
|
+
merged[:exclude_header] = true
|
208
|
+
outp = dodiff_flexible(merged, tlcl, local, cmd, use_header=false)
|
209
|
+
return outp if outp.to_s.empty? || !merged.key?(:script)
|
210
|
+
merged[:exclude_header] = false
|
211
|
+
dodiff_diff_dynamic(merged, tlcl, local, cmd, use_header=true)
|
212
|
+
end
|
213
|
+
|
214
|
+
def dodiff_build_cmd(trmt, tlcl, options)
|
215
|
+
# 2017-07-03: No worries, Ruby 3.0 frozen string literals, cmd is a list.
|
216
|
+
cmd = $cfg['diff.cmd'].shellsplit
|
217
|
+
# ALT_SEPARATOR is the platform specific alternative separator,
|
218
|
+
# for Windows support.
|
219
|
+
remote_path = trmt.path.gsub(
|
220
|
+
::File::SEPARATOR, ::File::ALT_SEPARATOR || ::File::SEPARATOR
|
221
|
+
)
|
222
|
+
local_path = tlcl.path.gsub(
|
223
|
+
::File::SEPARATOR, ::File::ALT_SEPARATOR || ::File::SEPARATOR
|
224
|
+
)
|
225
|
+
if options[:asdown]
|
226
|
+
cmd << local_path
|
227
|
+
cmd << remote_path
|
228
|
+
else
|
229
|
+
cmd << remote_path
|
230
|
+
cmd << local_path
|
178
231
|
end
|
232
|
+
cmd
|
233
|
+
end
|
234
|
+
|
235
|
+
def dodiff_flexible(merged, tlcl, local, cmd, use_header)
|
236
|
+
dodiff_local_to_tempfile(merged, tlcl, local, use_header)
|
237
|
+
dodiff_do_diff(cmd)
|
238
|
+
end
|
239
|
+
|
240
|
+
def dodiff_local_to_tempfile(merged, tlcl, local, use_header)
|
179
241
|
Pathname.new(tlcl.path).open('wb') do |io|
|
180
242
|
# Copy the local file to a temp file, for the diff command.
|
181
243
|
# And for resources, remove the local-only :selected key, as
|
182
244
|
# it's not part of the remote item that gets downloaded next.
|
183
245
|
if merged.key?(:script)
|
246
|
+
io << config_vars_decode(merged[:header]) if use_header
|
184
247
|
io << config_vars_decode(merged[:script])
|
185
248
|
else
|
186
249
|
# For most items, read the local file.
|
@@ -191,59 +254,34 @@ module MrMurano
|
|
191
254
|
diff_item_write(io, merged, local, nil)
|
192
255
|
end
|
193
256
|
end
|
257
|
+
end
|
194
258
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
MrMurano::Verbose.whirly_stop
|
259
|
+
def dodiff_do_diff(cmd)
|
260
|
+
stdout_and_stderr, _status = Open3.capture2e(*cmd)
|
261
|
+
dodiff_cull_tempfile_paths(stdout_and_stderr)
|
262
|
+
end
|
201
263
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
264
|
+
def dodiff_cull_tempfile_paths(stdout_and_stderr)
|
265
|
+
# How important are the first two lines of the diff? E.g.,
|
266
|
+
# --- /tmp/raw_data_remote_20170718-20183-gdyeg9.lua 2017-07-18 ...
|
267
|
+
# +++ /tmp/raw_data_local_20170718-20183-71o4me.lua 2017-07-18 ...
|
268
|
+
# Seems like printing the path to a since-deleted temporary file is
|
269
|
+
# misleading, so cull these lines.
|
270
|
+
unless $cfg['diff.cmd'] == 'diff' || $cfg['diff.cmd'].start_with?('diff ')
|
271
|
+
return stdout_and_stderr
|
272
|
+
end
|
273
|
+
lineno = 0
|
274
|
+
consise = stdout_and_stderr.lines.reject do |line|
|
275
|
+
lineno += 1
|
276
|
+
if lineno == 1 && line.start_with?('--- ')
|
277
|
+
true
|
278
|
+
elsif lineno == 2 && line.start_with?('+++ ')
|
279
|
+
true
|
215
280
|
else
|
216
|
-
|
217
|
-
cmd << local_path
|
218
|
-
end
|
219
|
-
|
220
|
-
stdout_and_stderr, _status = Open3.capture2e(*cmd)
|
221
|
-
# How important are the first two lines of the diff? E.g.,
|
222
|
-
# --- /tmp/raw_data_remote_20170718-20183-gdyeg9.lua 2017-07-18 ...
|
223
|
-
# +++ /tmp/raw_data_local_20170718-20183-71o4me.lua 2017-07-18 ...
|
224
|
-
# Seems like printing the path to a since-deleted temporary file is
|
225
|
-
# misleading, so cull these lines.
|
226
|
-
if $cfg['diff.cmd'] == 'diff' || $cfg['diff.cmd'].start_with?('diff ')
|
227
|
-
lineno = 0
|
228
|
-
consise = stdout_and_stderr.lines.reject do |line|
|
229
|
-
lineno += 1
|
230
|
-
if lineno == 1 && line.start_with?('--- ')
|
231
|
-
true
|
232
|
-
elsif lineno == 2 && line.start_with?('+++ ')
|
233
|
-
true
|
234
|
-
else
|
235
|
-
false
|
236
|
-
end
|
237
|
-
end
|
238
|
-
stdout_and_stderr = consise.join
|
281
|
+
false
|
239
282
|
end
|
240
|
-
ensure
|
241
|
-
trmt.close
|
242
|
-
trmt.unlink
|
243
|
-
tlcl.close
|
244
|
-
tlcl.unlink
|
245
283
|
end
|
246
|
-
|
284
|
+
consise.join
|
247
285
|
end
|
248
286
|
|
249
287
|
##
|
@@ -28,6 +28,8 @@ module MrMurano
|
|
28
28
|
attr_accessor :line_beg
|
29
29
|
# @return [Integer] The line in #local_path where this #script ends.
|
30
30
|
attr_accessor :line_end
|
31
|
+
# @return [String] The (optional) #ITEM <method> <path> :script header.
|
32
|
+
attr_accessor :header
|
31
33
|
# @return [String] If requested, the diff output.
|
32
34
|
attr_accessor :diff
|
33
35
|
# @return [Boolean] When filtering, did this item pass.
|
@@ -191,19 +191,20 @@ module MrMurano
|
|
191
191
|
# But on murano diff, MurCLI makes two local temp files
|
192
192
|
# to execute the diff, and it also upcases the method in
|
193
193
|
# both files, so the diff runs clean!
|
194
|
-
#
|
195
|
-
#
|
196
|
-
|
197
|
-
|
198
|
-
|
194
|
+
# 2017-07-03: (lb): Adding upcase; and recreating header.
|
195
|
+
# 2018-06-11: (lb): Added :exclude_header to deal with
|
196
|
+
# service side missing header. Which isn't an error, per se.
|
197
|
+
script_header = "--#ENDPOINT #{md[:method].upcase} #{md[:path]}"
|
198
|
+
script_header += " #{md[:ctype]}" unless md[:ctype].to_s.empty?
|
199
|
+
script_header += "\n"
|
199
200
|
cur = RouteItem.new(
|
200
|
-
#method: md[:method],
|
201
201
|
method: md[:method].upcase,
|
202
202
|
path: md[:path],
|
203
203
|
content_type: (md[:ctype] || 'application/json'),
|
204
204
|
local_path: path,
|
205
205
|
line_beg: lineno,
|
206
|
-
|
206
|
+
header: script_header,
|
207
|
+
script: '',
|
207
208
|
)
|
208
209
|
elsif !cur.nil? && !cur[:script].nil?
|
209
210
|
# 2017-07-02: Frozen string literal: change << to +=
|
@@ -99,6 +99,66 @@ command 'business list' do |c|
|
|
99
99
|
c.summary = %(List businesses)
|
100
100
|
c.description = %(
|
101
101
|
List businesses.
|
102
|
+
|
103
|
+
E.g.,
|
104
|
+
|
105
|
+
$ murano business find my
|
106
|
+
+-------------+-------+--------------------+
|
107
|
+
| bizid | role | name |
|
108
|
+
+-------------+-------+--------------------+
|
109
|
+
| a1b2c3d4e5f | owner | My Murano Business |
|
110
|
+
+-------------+-------+--------------------+
|
111
|
+
|
112
|
+
NOTE: To match businesses that use special characters in
|
113
|
+
their name, you must either delimit or quote the string.
|
114
|
+
|
115
|
+
For example, the Bash shell interprets the pound sign as
|
116
|
+
the start of a comment, so if you were to search, e.g.,
|
117
|
+
|
118
|
+
$ murano business find #
|
119
|
+
What would you like to find?
|
120
|
+
|
121
|
+
The CLI will not receive the '#' argument, and it'll tell
|
122
|
+
you as much.
|
123
|
+
|
124
|
+
The proper way to search for such a business is to either
|
125
|
+
delimit the query, or quote it. For example,
|
126
|
+
|
127
|
+
$ murano business find "#Hashtag Business"
|
128
|
+
+-------------+-------+-------------------+
|
129
|
+
| bizid | role | name |
|
130
|
+
+-------------+-------+-------------------+
|
131
|
+
| a1b2c3d4e5f | owner | #Hashtag Business |
|
132
|
+
+-------------+-------+-------------------+
|
133
|
+
|
134
|
+
The same applies for other special characters, such as:
|
135
|
+
|
136
|
+
'&' (start process in background);
|
137
|
+
'(' and ')' (subshell invocation);
|
138
|
+
'*' (file glob);
|
139
|
+
'~' (home directory expansion);
|
140
|
+
'!' (history expansion);
|
141
|
+
'-' (ignored as an empty option).
|
142
|
+
|
143
|
+
For example, to search for a name with a parenthesis, try:
|
144
|
+
|
145
|
+
$ murano business find \\\(
|
146
|
+
+-------------+-------+---------+
|
147
|
+
| bizid | role | name |
|
148
|
+
+-------------+-------+---------+
|
149
|
+
| a1b2c3d4e5f | owner | ( ͝° ͜ʖ͡°) |
|
150
|
+
+-------------+-------+---------+
|
151
|
+
|
152
|
+
Finally to search for a business name containing a dash,
|
153
|
+
use a double-dash to indicate the end of positional
|
154
|
+
arguments. For example,
|
155
|
+
|
156
|
+
$ murano business find -- -
|
157
|
+
+-------------+-------+----------------+
|
158
|
+
| bizid | role | name |
|
159
|
+
+-------------+-------+----------------+
|
160
|
+
| a1b2c3d4e5f | owner | Loch-Busi Ness |
|
161
|
+
+-------------+-------+----------------+
|
102
162
|
).strip
|
103
163
|
c.project_not_required = true
|
104
164
|
|
@@ -19,6 +19,7 @@ This is where OTA data can be stored so that devices can easily download it.
|
|
19
19
|
).strip
|
20
20
|
c.project_not_required = true
|
21
21
|
c.subcmdgrouphelp = true
|
22
|
+
c.must_not_be_managed = true
|
22
23
|
|
23
24
|
c.action do |_args, _options|
|
24
25
|
::Commander::UI.enable_paging unless $cfg['tool.no-page']
|
@@ -35,6 +36,7 @@ List downloadable content for a product.
|
|
35
36
|
Data uploaded to a product's content area can be downloaded by devices using
|
36
37
|
the HTTP Device API.
|
37
38
|
).strip
|
39
|
+
c.must_not_be_managed = true
|
38
40
|
|
39
41
|
c.option '-l', '--long', %(Include more info for each file)
|
40
42
|
|
@@ -50,7 +52,7 @@ the HTTP Device API.
|
|
50
52
|
if !items.empty?
|
51
53
|
prd.outf(items) do |dd, ios|
|
52
54
|
if options.long
|
53
|
-
headers = %i[Name Size
|
55
|
+
headers = %i[Name Size Last\ Modified MIME]
|
54
56
|
rows = dd.map { |d| [d[:id], d[:length], d[:last_modified], d[:type]] }
|
55
57
|
else
|
56
58
|
headers = %i[Name Size]
|
@@ -73,6 +75,7 @@ Show more info for a content item.
|
|
73
75
|
Data uploaded to a product's content area can be downloaded by devices using
|
74
76
|
the HTTP Device API.
|
75
77
|
).strip
|
78
|
+
c.must_not_be_managed = true
|
76
79
|
|
77
80
|
c.action do |args, _options|
|
78
81
|
c.verify_arg_count!(args, 1, ['Missing <content name>'])
|
@@ -93,6 +96,7 @@ Delete a content item.
|
|
93
96
|
Data uploaded to a product's content area can be downloaded by devices using
|
94
97
|
the HTTP Device API.
|
95
98
|
).strip
|
99
|
+
c.must_not_be_managed = true
|
96
100
|
|
97
101
|
c.option('-y', '--[no-]yes', %(Answer "yes" to all prompts and run non-interactively))
|
98
102
|
|
@@ -115,6 +119,7 @@ Upload a content item.
|
|
115
119
|
Data uploaded to a product's content area can be downloaded by devices using
|
116
120
|
the HTTP Device API.
|
117
121
|
).strip
|
122
|
+
c.must_not_be_managed = true
|
118
123
|
|
119
124
|
c.option('--tags KEY=VALUE', %(Add extra meta info to the content item)) do |ec|
|
120
125
|
key, value = ec.split('=', 2)
|
@@ -154,6 +159,7 @@ Download a content item.
|
|
154
159
|
Data uploaded to a product's content area can be downloaded by devices using
|
155
160
|
the HTTP Device API.
|
156
161
|
).strip
|
162
|
+
c.must_not_be_managed = true
|
157
163
|
|
158
164
|
c.option '-o', '--output FILE', %(Save content to a file)
|
159
165
|
|
@@ -284,7 +284,7 @@ end
|
|
284
284
|
|
285
285
|
command 'device activate' do |c|
|
286
286
|
c.syntax = %(murano device activate <identifier>)
|
287
|
-
c.summary = %(Activate a serial number,
|
287
|
+
c.summary = %(Activate a serial number, retrieving its CIK)
|
288
288
|
c.description = %(
|
289
289
|
Activate an Identifier.
|
290
290
|
|
@@ -30,9 +30,10 @@ class ElementCmd
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def reset_state
|
33
|
+
@drop_fields = []
|
33
34
|
@edit_fields = {}
|
35
|
+
@open_fields = []
|
34
36
|
@updated_obj = {}
|
35
|
-
@use_editor = false
|
36
37
|
@input_path = nil
|
37
38
|
@input_data = nil
|
38
39
|
end
|
@@ -325,7 +326,11 @@ class ElementCmd
|
|
325
326
|
cmd.option(
|
326
327
|
"--#{switch} [VALUE]", detail
|
327
328
|
) do |param|
|
328
|
-
|
329
|
+
if param.nil?
|
330
|
+
@open_fields.push(key)
|
331
|
+
else
|
332
|
+
@edit_fields[field.to_s] = param
|
333
|
+
end
|
329
334
|
end
|
330
335
|
end
|
331
336
|
end
|
@@ -336,11 +341,19 @@ class ElementCmd
|
|
336
341
|
) do |param|
|
337
342
|
if param.nil?
|
338
343
|
# param.nil? means user did not supply key[=val].
|
339
|
-
@
|
344
|
+
@open_fields.push('')
|
340
345
|
else
|
341
346
|
# Otherwise: a=b :> ['a', 'b'] / a= :> ['a', ''] / a :> ['a', nil]
|
342
347
|
key, value = param.split('=', 2)
|
343
|
-
|
348
|
+
if value.nil?
|
349
|
+
if param =~ /(?<!-)-$/
|
350
|
+
@drop_fields.push(key.chomp('-'))
|
351
|
+
else
|
352
|
+
@open_fields.push(key)
|
353
|
+
end
|
354
|
+
else
|
355
|
+
@edit_fields[key] = value
|
356
|
+
end
|
344
357
|
end
|
345
358
|
end
|
346
359
|
end
|
@@ -379,17 +392,16 @@ class ElementCmd
|
|
379
392
|
if @input_path.nil?
|
380
393
|
# See if all values are specified, or if we should bring up the EDITOR.
|
381
394
|
if n_kvals[:n_keys] > 1
|
382
|
-
if n_kvals[:n_keys]
|
395
|
+
if (n_kvals[:n_keys] + 1) < n_kvals[:n_vals]
|
396
|
+
# EDITOR path, i.e., one key specified without a value.
|
383
397
|
error %(
|
384
|
-
Please specify at most one field
|
398
|
+
Please specify at most one field without a value.
|
385
399
|
).strip
|
386
400
|
exit 2
|
387
401
|
end
|
388
|
-
elsif n_kvals[:n_keys] == 0
|
402
|
+
elsif n_kvals[:n_keys] == 0 && @open_fields.empty?
|
389
403
|
error('Please specify one or more -e/--edit options, or an input file.')
|
390
404
|
exit 2
|
391
|
-
else
|
392
|
-
@use_editor = n_kvals[:n_vals].zero?
|
393
405
|
end
|
394
406
|
elsif n_kvals[:n_keys] > 1
|
395
407
|
error('Please specify at most a single field when specifing an input file.')
|
@@ -423,13 +435,17 @@ class ElementCmd
|
|
423
435
|
n_edits = 0
|
424
436
|
# When @edit_fields == { '' => '' }, BizAPI replies:
|
425
437
|
# Request Failed: 400: [400] child "type" fails because ["type" is required]
|
426
|
-
if
|
438
|
+
if !@open_fields.empty?
|
439
|
+
if (@open_fields.length > 1) && $cfg['tool.developer']
|
440
|
+
warning %(Unexpected: more than one open field specified)
|
441
|
+
end
|
427
442
|
n_edits += update_elem_fields_from_editor
|
428
443
|
else
|
429
444
|
# Verify that at least one option value differs from what's currently set.
|
430
445
|
n_edits += update_elem_fields_from_options
|
431
446
|
n_edits += update_elem_fields_from_input_f
|
432
447
|
end
|
448
|
+
n_edits += update_elem_fields_drop_fields
|
433
449
|
if n_edits.zero?
|
434
450
|
warning('No new field values specified to update.')
|
435
451
|
exit 0
|
@@ -447,7 +463,7 @@ class ElementCmd
|
|
447
463
|
end
|
448
464
|
|
449
465
|
def prepare_field_keys_and_value
|
450
|
-
field = @
|
466
|
+
field = @open_fields[0]
|
451
467
|
keys = []
|
452
468
|
keys = field.split('.').map(&:to_sym) unless field.to_s.empty?
|
453
469
|
if !keys.nil? && !keys.empty?
|
@@ -535,10 +551,10 @@ class ElementCmd
|
|
535
551
|
def update_elem_fields_from_input_f
|
536
552
|
n_edits = 0
|
537
553
|
return n_edits if @input_path.nil?
|
538
|
-
if (@
|
539
|
-
warning %(Unexpected: more than one field specified)
|
554
|
+
if (@open_fields.length > 1) && $cfg['tool.developer']
|
555
|
+
warning %(Unexpected: more than one open field specified)
|
540
556
|
end
|
541
|
-
keys = @
|
557
|
+
keys = @open_fields[0].split('.').map(&:to_sym) unless @open_fields.empty?
|
542
558
|
if !keys.nil? && !keys.empty?
|
543
559
|
old_val = @updated_obj.dig_safe(*keys)
|
544
560
|
if old_val != @input_data
|
@@ -552,6 +568,16 @@ class ElementCmd
|
|
552
568
|
n_edits
|
553
569
|
end
|
554
570
|
|
571
|
+
def update_elem_fields_drop_fields
|
572
|
+
n_edits = 0
|
573
|
+
@drop_fields.each do |field|
|
574
|
+
keys = field.split('.').map(&:to_sym)
|
575
|
+
@updated_obj.deep_delete(*keys)
|
576
|
+
n_edits += 1
|
577
|
+
end
|
578
|
+
n_edits
|
579
|
+
end
|
580
|
+
|
555
581
|
def value_specifies_file_input?(field_value)
|
556
582
|
field_value =~ /^@(?!@)/
|
557
583
|
end
|
@@ -32,6 +32,7 @@ command 'keystore clearAll' do |c|
|
|
32
32
|
c.description = %(
|
33
33
|
Delete all keys in the keystore.
|
34
34
|
).strip
|
35
|
+
c.must_not_be_managed = true
|
35
36
|
|
36
37
|
c.action do |args, _options|
|
37
38
|
c.verify_arg_count!(args)
|
@@ -46,6 +47,7 @@ command 'keystore info' do |c|
|
|
46
47
|
c.description = %(
|
47
48
|
Show info about the Keystore.
|
48
49
|
).strip
|
50
|
+
c.must_not_be_managed = true
|
49
51
|
|
50
52
|
c.action do |args, _options|
|
51
53
|
c.verify_arg_count!(args)
|
@@ -60,6 +62,7 @@ command 'keystore list' do |c|
|
|
60
62
|
c.description = %(
|
61
63
|
List all of the keys in the Keystore.
|
62
64
|
).strip
|
65
|
+
c.must_not_be_managed = true
|
63
66
|
|
64
67
|
c.action do |args, _options|
|
65
68
|
c.verify_arg_count!(args)
|
@@ -76,6 +79,7 @@ command 'keystore get' do |c|
|
|
76
79
|
c.description = %(
|
77
80
|
Get the value of a key in the Keystore.
|
78
81
|
).strip
|
82
|
+
c.must_not_be_managed = true
|
79
83
|
|
80
84
|
c.action do |args, _options|
|
81
85
|
c.verify_arg_count!(args, 1, ['Missing key'])
|
@@ -91,6 +95,7 @@ command 'keystore set' do |c|
|
|
91
95
|
c.description = %(
|
92
96
|
Set the value of a key in the Keystore.
|
93
97
|
).strip
|
98
|
+
c.must_not_be_managed = true
|
94
99
|
|
95
100
|
c.action do |args, _options|
|
96
101
|
c.verify_arg_count!(args, nil, ['Missing key', 'Missing value(s)'])
|
@@ -105,6 +110,7 @@ command 'keystore delete' do |c|
|
|
105
110
|
c.description = %(
|
106
111
|
Delete a key from the Keystore.
|
107
112
|
).strip
|
113
|
+
c.must_not_be_managed = true
|
108
114
|
|
109
115
|
# MAYBE?/2017-08-16: Verify on delete.
|
110
116
|
#c.option('-y', '--[no-]yes', %(Answer "yes" to all prompts and run non-interactively))
|
@@ -130,6 +136,8 @@ For current list, see:
|
|
130
136
|
|
131
137
|
http://docs.exosite.com/murano/services/keystore/#command
|
132
138
|
).strip
|
139
|
+
c.must_not_be_managed = true
|
140
|
+
|
133
141
|
c.example %(murano keystore command lpush mykey myvalue), %(Push a value onto list)
|
134
142
|
c.example %(murano keystore command lpush mykey A B C), %(Push three values onto list)
|
135
143
|
c.example %(murano keystore command lrem mykey 0 B), %(Remove all B values from list)
|