MuranoCLI 3.2.0.beta.1 → 3.2.0.beta.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -1
- data/.trustme.plugin +137 -0
- data/.trustme.sh +217 -117
- data/.trustme.vim +9 -3
- data/Gemfile +9 -3
- data/MuranoCLI.gemspec +8 -5
- data/Rakefile +1 -0
- data/dockers/Dockerfile.2.2.9 +6 -3
- data/dockers/Dockerfile.2.3.6 +6 -3
- data/dockers/Dockerfile.2.4.3 +6 -3
- data/dockers/Dockerfile.2.5.0 +6 -3
- data/dockers/Dockerfile.GemRelease +10 -8
- data/dockers/Dockerfile.m4 +23 -5
- data/dockers/docker-test.sh +65 -28
- data/docs/completions/murano_completion-bash +751 -57
- data/docs/develop.rst +10 -9
- data/lib/MrMurano/AccountBase.rb +95 -6
- data/lib/MrMurano/Commander-Entry.rb +9 -4
- data/lib/MrMurano/Config-Migrate.rb +2 -0
- data/lib/MrMurano/Config.rb +94 -26
- data/lib/MrMurano/Content.rb +1 -1
- data/lib/MrMurano/Exchange.rb +77 -42
- data/lib/MrMurano/Gateway.rb +1 -1
- data/lib/MrMurano/HttpAuthed.rb +20 -7
- data/lib/MrMurano/Logs.rb +10 -1
- data/lib/MrMurano/ProjectFile.rb +1 -1
- data/lib/MrMurano/ReCommander.rb +129 -73
- data/lib/MrMurano/Solution-ServiceConfig.rb +18 -11
- data/lib/MrMurano/Solution-Services.rb +78 -50
- data/lib/MrMurano/Solution-Users.rb +1 -1
- data/lib/MrMurano/Solution.rb +13 -63
- data/lib/MrMurano/SyncUpDown-Core.rb +185 -77
- data/lib/MrMurano/SyncUpDown-Item.rb +29 -4
- data/lib/MrMurano/SyncUpDown.rb +11 -11
- data/lib/MrMurano/Webservice-Cors.rb +1 -1
- data/lib/MrMurano/Webservice-Endpoint.rb +28 -17
- data/lib/MrMurano/Webservice-File.rb +103 -43
- data/lib/MrMurano/commands/domain.rb +1 -0
- data/lib/MrMurano/commands/element.rb +585 -0
- data/lib/MrMurano/commands/exchange.rb +211 -204
- data/lib/MrMurano/commands/gb.rb +1 -0
- data/lib/MrMurano/commands/globals.rb +17 -7
- data/lib/MrMurano/commands/init.rb +115 -101
- data/lib/MrMurano/commands/keystore.rb +1 -1
- data/lib/MrMurano/commands/logs.rb +2 -1
- data/lib/MrMurano/commands/postgresql.rb +17 -7
- data/lib/MrMurano/commands/service.rb +572 -0
- data/lib/MrMurano/commands/show.rb +7 -3
- data/lib/MrMurano/commands/solution.rb +2 -1
- data/lib/MrMurano/commands/solution_picker.rb +31 -15
- data/lib/MrMurano/commands/status.rb +205 -169
- data/lib/MrMurano/commands/sync.rb +70 -38
- data/lib/MrMurano/commands/token.rb +59 -14
- data/lib/MrMurano/commands/usage.rb +1 -0
- data/lib/MrMurano/commands.rb +2 -0
- data/lib/MrMurano/hash.rb +91 -0
- data/lib/MrMurano/http.rb +55 -6
- data/lib/MrMurano/makePretty.rb +47 -0
- data/lib/MrMurano/optparse.rb +60 -45
- data/lib/MrMurano/variegated/TruthyFalsey.rb +48 -0
- data/lib/MrMurano/variegated/ruby_dig.rb +64 -0
- data/lib/MrMurano/verbosing.rb +113 -3
- data/lib/MrMurano/version.rb +1 -1
- data/spec/Account_spec.rb +34 -20
- data/spec/Business_spec.rb +12 -9
- data/spec/Config_spec.rb +7 -1
- data/spec/Content_spec.rb +17 -1
- data/spec/GatewayBase_spec.rb +5 -2
- data/spec/GatewayDevice_spec.rb +4 -2
- data/spec/GatewayResource_spec.rb +4 -1
- data/spec/GatewaySettings_spec.rb +4 -1
- data/spec/HttpAuthed_spec.rb +73 -0
- data/spec/Http_spec.rb +32 -35
- data/spec/ProjectFile_spec.rb +1 -1
- data/spec/Solution-ServiceConfig_spec.rb +4 -1
- data/spec/Solution-ServiceEventHandler_spec.rb +6 -3
- data/spec/Solution-ServiceModules_spec.rb +4 -1
- data/spec/Solution-UsersRoles_spec.rb +4 -1
- data/spec/Solution_spec.rb +4 -1
- data/spec/SyncUpDown_spec.rb +1 -1
- data/spec/Webservice-Cors_spec.rb +4 -1
- data/spec/Webservice-Endpoint_spec.rb +9 -6
- data/spec/Webservice-File_spec.rb +17 -4
- data/spec/Webservice-Setting_spec.rb +6 -2
- data/spec/_workspace.rb +2 -0
- data/spec/cmd_common.rb +42 -13
- data/spec/cmd_content_spec.rb +17 -7
- data/spec/cmd_device_spec.rb +1 -1
- data/spec/cmd_domain_spec.rb +2 -2
- data/spec/cmd_element_spec.rb +400 -0
- data/spec/cmd_exchange_spec.rb +2 -2
- data/spec/cmd_init_spec.rb +59 -25
- data/spec/cmd_keystore_spec.rb +6 -3
- data/spec/cmd_link_spec.rb +10 -5
- data/spec/cmd_logs_spec.rb +1 -1
- data/spec/cmd_setting_application_spec.rb +18 -15
- data/spec/cmd_setting_product_spec.rb +7 -7
- data/spec/cmd_status_spec.rb +27 -17
- data/spec/cmd_syncdown_application_spec.rb +30 -3
- data/spec/cmd_syncdown_both_spec.rb +72 -18
- data/spec/cmd_syncup_spec.rb +71 -5
- data/spec/cmd_token_spec.rb +2 -2
- data/spec/cmd_usage_spec.rb +2 -2
- data/spec/dry_run_formatter.rb +27 -0
- data/spec/fixtures/dumped_config +8 -0
- data/spec/fixtures/exchange_element/element-show.json +1 -0
- data/spec/fixtures/exchange_element/swagger-mur-6407__10k.yaml +282 -0
- data/spec/fixtures/exchange_element/swagger-mur-6407__20k.yaml +588 -0
- data/spec/variegated_TruthyFalsey_spec.rb +29 -0
- metadata +51 -25
@@ -38,7 +38,7 @@ module MrMurano
|
|
38
38
|
# :nocov:
|
39
39
|
end
|
40
40
|
|
41
|
-
def fetch(name)
|
41
|
+
def fetch(name, _untainted=false)
|
42
42
|
raise 'Missing name!' if name.nil?
|
43
43
|
raise 'Empty name!' if name.empty?
|
44
44
|
ret = get('/' + CGI.escape(name))
|
@@ -102,6 +102,9 @@ module MrMurano
|
|
102
102
|
|
103
103
|
return unless upload_item_allowed(therealias)
|
104
104
|
|
105
|
+
get_again = false
|
106
|
+
create_it = false
|
107
|
+
|
105
108
|
put('/' + therealias, pst) do |request, http|
|
106
109
|
response = http.request(request)
|
107
110
|
isj, jsn = isJSON(response.body)
|
@@ -109,16 +112,14 @@ module MrMurano
|
|
109
112
|
# EXPLAIN: How come `case response ... when Net:HTTPNoContent` works?
|
110
113
|
# It seems magical, since response is a class and here we use is_a?.
|
111
114
|
if response.is_a?(Net::HTTPNoContent)
|
112
|
-
# 2017-08-07: When did Murano start returning 204?
|
115
|
+
# 2017-08-07: (lb): When did Murano start returning 204?
|
113
116
|
# This seems to happen when updating an existing service.
|
114
117
|
# Unfortunately, we don't get the latest updated_at, so
|
115
118
|
# a subsequent status will show this module as dirty.
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
warning "Failed to verify updated_at: #{ret}"
|
121
|
-
end
|
119
|
+
# (lb): Don't call get() from the put() handler, it just seems
|
120
|
+
# like bad form to start a new call before the existing one is
|
121
|
+
# complete!
|
122
|
+
get_again = true
|
122
123
|
elsif response.is_a?(Net::HTTPSuccess)
|
123
124
|
# A first upload will see a 200 response and a JSON body.
|
124
125
|
# A subsequent upload of the same item sees 204 and no body.
|
@@ -127,18 +128,29 @@ module MrMurano
|
|
127
128
|
updated_at = jsn[:updated_at] unless jsn.nil?
|
128
129
|
elsif response == Net::HTTPNotFound
|
129
130
|
verbose "Doesn't exist, creating"
|
130
|
-
|
131
|
+
create_it = true
|
131
132
|
else
|
132
133
|
relpath = localpath.sub(File.join(Dir.pwd, ''), '')
|
133
134
|
if response.is_a?(Net::HTTPBadRequest) && isj && jsn[:message] == 'Validation errors'
|
134
135
|
warning "Validation errors detected in #{relpath}:"
|
135
|
-
puts MrMurano::Pretties.makeJsonPretty(
|
136
|
+
puts MrMurano::Pretties.makeJsonPretty(
|
137
|
+
jsn[:errors], Struct.new(:pretty).new(true),
|
138
|
+
)
|
136
139
|
else
|
137
140
|
showHttpError(request, response)
|
138
141
|
end
|
139
142
|
warning "Failed to upload: #{relpath}"
|
140
143
|
end
|
141
144
|
end
|
145
|
+
if get_again
|
146
|
+
ret = get('/' + CGI.escape(name))
|
147
|
+
if ret.is_a?(Hash) && ret.key?(:updated_at)
|
148
|
+
updated_at = ret[:updated_at]
|
149
|
+
else
|
150
|
+
warning "Failed to verify updated_at: #{ret}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
post('/', pst) if create_it
|
142
154
|
cache_update_time_for(localpath, updated_at)
|
143
155
|
end
|
144
156
|
|
@@ -228,13 +240,23 @@ module MrMurano
|
|
228
240
|
return nil unless cache_file.file?
|
229
241
|
ret = nil
|
230
242
|
cache_file.open('r') do |io|
|
231
|
-
|
243
|
+
begin
|
244
|
+
cache = YAML.load(io)
|
245
|
+
rescue StandardError => err
|
246
|
+
# This could be solely Psych::SyntaxError but let's catch 'em all.
|
247
|
+
# (lb): We could suggest to the user that they delete the
|
248
|
+
# cache file, but I'd rather understand what's going on.
|
249
|
+
# If anything, the user seeing this error should send us
|
250
|
+
# a copy of their cache files.
|
251
|
+
warning("ERROR: Could not load the cache file at: #{cache_file}")
|
252
|
+
warning(err.to_s)
|
253
|
+
end
|
232
254
|
return nil unless cache
|
233
255
|
if cache.key?(local_path.to_s)
|
234
256
|
entry = cache[local_path.to_s]
|
235
|
-
debug("For #{local_path}:")
|
236
|
-
debug("
|
237
|
-
debug("
|
257
|
+
debug("For path #{local_path}:")
|
258
|
+
debug(" cached: #{entry}")
|
259
|
+
debug(" cm: #{cksm}")
|
238
260
|
if entry.is_a?(Hash)
|
239
261
|
if entry[:sha1] == cksm && entry.key?(:updated_at)
|
240
262
|
ret = Time.parse(entry[:updated_at])
|
@@ -472,7 +494,7 @@ module MrMurano
|
|
472
494
|
end
|
473
495
|
end
|
474
496
|
|
475
|
-
def fetch(name)
|
497
|
+
def fetch(name, _untainted=false)
|
476
498
|
ret = get('/' + CGI.escape(name))
|
477
499
|
unless ret.is_a?(Hash) && !ret.key?(:error)
|
478
500
|
error "Fetch for #{name} returned nil or error; skipping"
|
@@ -480,7 +502,11 @@ module MrMurano
|
|
480
502
|
end
|
481
503
|
aheader = (ret[:script].lines.first || '').chomp
|
482
504
|
dheader = "--#EVENT #{ret[:service]} #{ret[:event]}"
|
505
|
+
# FIXME/2018-04-24: (lb): What about untainted ?? And this weird double-yield?
|
506
|
+
# We may want to yield/return unadulterated ret[:script] if just diffing.
|
483
507
|
if block_given?
|
508
|
+
# ANSWER/2018-04-24: (lb): If header's do not match, what about ret[:script]?
|
509
|
+
# Or do both yields get called?? Smells very strange.
|
484
510
|
yield dheader + "\n" if aheader != dheader
|
485
511
|
yield ret[:script]
|
486
512
|
else
|
@@ -515,52 +541,53 @@ module MrMurano
|
|
515
541
|
# This only finds the last event in a file.
|
516
542
|
# :legacy support doesn't allow for that. But that's ok.
|
517
543
|
path = Pathname.new(path) unless path.is_a?(Pathname)
|
544
|
+
items = []
|
518
545
|
cur = nil
|
519
546
|
lineno = 0
|
520
547
|
path.readlines.each do |line|
|
548
|
+
lineno += 1
|
521
549
|
# @match_header finds a service and an event string, e.g., "--EVENT svc evt\n"
|
522
550
|
md = @match_header.match(line)
|
523
551
|
if !md.nil?
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
svc_alias = config_var if service != md[:service]
|
528
|
-
cur = EventHandlerItem.new(
|
529
|
-
#name
|
530
|
-
local_path: path,
|
531
|
-
#id
|
532
|
-
script: line,
|
533
|
-
line: lineno,
|
534
|
-
#line_end
|
535
|
-
#diff
|
536
|
-
#selected
|
537
|
-
#synckey
|
538
|
-
#synctype
|
539
|
-
#type
|
540
|
-
#updated_at
|
541
|
-
#dup_count
|
542
|
-
#alias
|
543
|
-
#updated_at
|
544
|
-
#created_at
|
545
|
-
#solution_id
|
546
|
-
service: service,
|
547
|
-
event: event_event,
|
548
|
-
type: event_type,
|
549
|
-
#phantom
|
550
|
-
svc_alias: svc_alias,
|
551
|
-
)
|
552
|
+
cur[:line_end] = lineno - 1 unless cur.nil?
|
553
|
+
cur = to_remote_item_create(md, path, line, lineno)
|
554
|
+
items << cur
|
552
555
|
elsif !cur.nil? && !cur[:script].nil?
|
553
556
|
cur[:script] += line
|
554
557
|
end
|
555
|
-
|
558
|
+
# else, cur.nil?, or no :script (not used by item), so skip this line.
|
559
|
+
end
|
560
|
+
|
561
|
+
if !cur.nil?
|
562
|
+
cur[:line_end] = lineno
|
563
|
+
else
|
564
|
+
# If cur is nil here, then we need to do a :legacy check.
|
565
|
+
cur = to_remote_item_legacy_check(cur, path, from, lineno)
|
566
|
+
items << cur
|
556
567
|
end
|
557
|
-
cur[:line_end] = lineno unless cur.nil?
|
558
568
|
|
559
|
-
|
560
|
-
|
569
|
+
items
|
570
|
+
end
|
571
|
+
|
572
|
+
def to_remote_item_create(md, path, line, lineno)
|
573
|
+
service, config_var = decode_config_var(md[:service])
|
574
|
+
event_event, event_type = resolve_event_type(service, md[:event])
|
575
|
+
# Header line.
|
576
|
+
svc_alias = config_var if service != md[:service]
|
577
|
+
EventHandlerItem.new(
|
578
|
+
# Skip: name, id, line_end, diff, selected, synckey, synctype, type,
|
579
|
+
# updated_at, dup_count, alias, updted_at, created_at, solution_id, phantom.
|
580
|
+
local_path: path,
|
581
|
+
script: line,
|
582
|
+
line_beg: lineno,
|
583
|
+
service: service,
|
584
|
+
event: event_event,
|
585
|
+
type: event_type,
|
586
|
+
svc_alias: svc_alias,
|
587
|
+
)
|
561
588
|
end
|
562
589
|
|
563
|
-
def to_remote_item_legacy_check(cur, path, from,
|
590
|
+
def to_remote_item_legacy_check(cur, path, from, line_end)
|
564
591
|
return cur unless cur.nil? && $project['services.legacy'].is_a?(Hash)
|
565
592
|
spath = path.relative_path_from(from)
|
566
593
|
debug "No headers: #{spath}"
|
@@ -574,8 +601,8 @@ module MrMurano
|
|
574
601
|
event: event,
|
575
602
|
type: nil,
|
576
603
|
local_path: path,
|
577
|
-
|
578
|
-
line_end:
|
604
|
+
line_beg: 1,
|
605
|
+
line_end: line_end,
|
579
606
|
script: path.read, # FIXME: ick, fix this.
|
580
607
|
)
|
581
608
|
end
|
@@ -805,6 +832,7 @@ module MrMurano
|
|
805
832
|
SyncRoot.instance.add(
|
806
833
|
'services', EventHandlerSolnApp, 'S', true, %w[eventhandlers]
|
807
834
|
)
|
835
|
+
|
808
836
|
# 2017-08-08: device2 and interface are now part of the skiplist, so no
|
809
837
|
# product event handlers will be found, unless the user modifies the skiplist.
|
810
838
|
SyncRoot.instance.add(
|
data/lib/MrMurano/Solution.rb
CHANGED
@@ -62,59 +62,6 @@ module MrMurano
|
|
62
62
|
end
|
63
63
|
# …
|
64
64
|
|
65
|
-
def get(path='', query=nil, &block)
|
66
|
-
aggregate = nil
|
67
|
-
total = nil
|
68
|
-
remaining = -1
|
69
|
-
orig_query = (query || []).dup
|
70
|
-
while remaining != 0
|
71
|
-
ret = super
|
72
|
-
if ret.nil? && !@suppress_error
|
73
|
-
warning "No solution with ID: #{@api_id}"
|
74
|
-
whirly_interject { say 'Run `murano show` to see the business and list of solutions.' }
|
75
|
-
MrMurano::SolutionBase.warn_configfile_env_maybe
|
76
|
-
exit 1
|
77
|
-
end
|
78
|
-
return nil if ret.nil?
|
79
|
-
# Pagination: Check if more data.
|
80
|
-
if ret.is_a?(Hash) && ret.key?(:total) && ret.key?(:items)
|
81
|
-
query = orig_query.dup
|
82
|
-
if total.nil?
|
83
|
-
total = ret[:total]
|
84
|
-
remaining = total - ret[:items].length
|
85
|
-
# The response also includes a hint of how to get the next page.
|
86
|
-
# ret[:next] == "/api/v1/eventhandler?query={\
|
87
|
-
# \"solution_id\":\"XXXXXXXXXXXXXXXX\"}&limit=20&offset=20"
|
88
|
-
# But note that the URL we use is a little different
|
89
|
-
# https://bizapi.hosted.exosite.io/api:1/solution/XXXXXXXXXXXXXXXXX/eventhandler
|
90
|
-
else
|
91
|
-
if total != ret[:total]
|
92
|
-
warning "Unexpected: subsequence :total not total: #{ret[:total]} != #{total}"
|
93
|
-
end
|
94
|
-
remaining -= ret[:items].length
|
95
|
-
end
|
96
|
-
if remaining > 0
|
97
|
-
#query.push ['limit', 20]
|
98
|
-
query.push ['offset', total - remaining]
|
99
|
-
elsif remaining != 0
|
100
|
-
warning "Unexpected: negative remaining: #{fancy_ticks(total)}"
|
101
|
-
remaining = 0
|
102
|
-
end
|
103
|
-
if aggregate.nil?
|
104
|
-
aggregate = ret
|
105
|
-
else
|
106
|
-
aggregate[:items].concat ret[:items]
|
107
|
-
end
|
108
|
-
else
|
109
|
-
# ret is not a hash, or it's missing :total or :items.
|
110
|
-
warning "Unexpected: aggregate set: #{aggregate} / ret: #{ret}" unless aggregate.nil?
|
111
|
-
aggregate = ret
|
112
|
-
remaining = 0
|
113
|
-
end
|
114
|
-
end
|
115
|
-
aggregate
|
116
|
-
end
|
117
|
-
|
118
65
|
# This at least works for EventHandler and ServiceConfig.
|
119
66
|
# - ServiceConfig overrides to fetch also 'script_key'.
|
120
67
|
def search(svc_name, path=nil)
|
@@ -133,16 +80,6 @@ module MrMurano
|
|
133
80
|
matches.select { |match| match[:service] == svc_name }
|
134
81
|
end
|
135
82
|
|
136
|
-
def self.warn_configfile_env_maybe
|
137
|
-
if !$cfg.get('business.id', :env).to_s.empty? &&
|
138
|
-
!$cfg.get('business.id', :project).to_s.empty? &&
|
139
|
-
$cfg.get('business.id', :env) != $cfg.get('business.id', :project)
|
140
|
-
MrMurano::Verbose.warning(
|
141
|
-
'NOTE: MURANO_CONFIGFILE specifies a different business.id than the local project file'
|
142
|
-
)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
83
|
include SyncUpDown
|
147
84
|
end
|
148
85
|
|
@@ -155,6 +92,8 @@ module MrMurano
|
|
155
92
|
@meta = {}
|
156
93
|
@valid = false
|
157
94
|
self.meta = meta unless meta.nil?
|
95
|
+
@soln_services = nil
|
96
|
+
@soln_svc_cfgs = {}
|
158
97
|
end
|
159
98
|
|
160
99
|
# The Solution @name.
|
@@ -213,6 +152,17 @@ module MrMurano
|
|
213
152
|
get('/logs')
|
214
153
|
end
|
215
154
|
|
155
|
+
def service
|
156
|
+
return @soln_services unless @soln_services.nil?
|
157
|
+
@soln_services = get('/service')
|
158
|
+
end
|
159
|
+
|
160
|
+
# See also Solution-ServiceConfig.rb's ServiceBase, which calls same endpoint.
|
161
|
+
def serviceconfig(query=nil)
|
162
|
+
return @soln_svc_cfgs[query] unless @soln_svc_cfgs[query].nil?
|
163
|
+
@soln_svc_cfgs[query] = get('/serviceconfig', query)
|
164
|
+
end
|
165
|
+
|
216
166
|
# *** Solution utils
|
217
167
|
|
218
168
|
def cfg_key_id
|