MrMurano 1.6.3 → 1.7.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 +4 -4
- data/.gitignore +1 -2
- data/Gemfile +1 -1
- data/MrMurano.gemspec +1 -1
- data/README.markdown +4 -0
- data/TODO.taskpaper +17 -4
- data/lib/MrMurano/Account.rb +5 -1
- data/lib/MrMurano/Config.rb +3 -1
- data/lib/MrMurano/Product-Resources.rb +239 -0
- data/lib/MrMurano/Product.rb +51 -2
- data/lib/MrMurano/Solution-Cors.rb +81 -0
- data/lib/MrMurano/Solution-Endpoint.rb +8 -4
- data/lib/MrMurano/Solution-File.rb +4 -2
- data/lib/MrMurano/Solution-ServiceConfig.rb +74 -1
- data/lib/MrMurano/Solution-Services.rb +4 -2
- data/lib/MrMurano/Solution-Users.rb +6 -3
- data/lib/MrMurano/Solution.rb +6 -274
- data/lib/MrMurano/SyncUpDown.rb +433 -0
- data/lib/MrMurano/commands/completion.rb +152 -0
- data/lib/MrMurano/commands/content.rb +15 -14
- data/lib/MrMurano/commands/cors.rb +11 -38
- data/lib/MrMurano/commands/exportImport.rb +4 -3
- data/lib/MrMurano/commands/keystore.rb +15 -16
- data/lib/MrMurano/commands/logs.rb +2 -2
- data/lib/MrMurano/commands/productCreate.rb +4 -4
- data/lib/MrMurano/commands/productDelete.rb +6 -8
- data/lib/MrMurano/commands/productSpec.rb +31 -36
- data/lib/MrMurano/commands/productWrite.rb +9 -7
- data/lib/MrMurano/commands/serialNumberCmds.rb +4 -4
- data/lib/MrMurano/commands/solutionCreate.rb +4 -4
- data/lib/MrMurano/commands/solutionDelete.rb +5 -5
- data/lib/MrMurano/commands/status.rb +9 -42
- data/lib/MrMurano/commands/sync.rb +15 -71
- data/lib/MrMurano/commands/timeseries.rb +23 -29
- data/lib/MrMurano/commands/tsdb.rb +202 -0
- data/lib/MrMurano/commands/zshcomplete.erb +112 -0
- data/lib/MrMurano/commands.rb +4 -1
- data/lib/MrMurano/http.rb +4 -3
- data/lib/MrMurano/makePretty.rb +15 -6
- data/lib/MrMurano/verbosing.rb +71 -0
- data/lib/MrMurano/version.rb +1 -1
- data/lib/MrMurano.rb +1 -0
- data/spec/ConfigFile_spec.rb +3 -3
- data/spec/ProductContent_spec.rb +2 -2
- data/spec/ProductResources_spec.rb +152 -0
- data/spec/Product_spec.rb +38 -1
- data/spec/Solution-Cors_spec.rb +134 -0
- data/spec/Solution-ServiceConfig_spec.rb +198 -0
- data/spec/SyncRoot_spec.rb +74 -0
- data/spec/cmd_config_spec.rb +51 -0
- data/spec/fixtures/.mrmuranorc +9 -0
- data/spec/{testfiles → fixtures}/configfile +0 -0
- data/spec/fixtures/mrmuranorc_deleted_bob +8 -0
- data/spec/fixtures/product_spec_files/example.exoline.spec.yaml +116 -0
- data/spec/fixtures/product_spec_files/example.murano.spec.yaml +14 -0
- data/spec/fixtures/product_spec_files/gwe.exoline.spec.yaml +21 -0
- data/spec/fixtures/product_spec_files/gwe.murano.spec.yaml +16 -0
- data/spec/{lightbulb.yaml → fixtures/product_spec_files/lightbulb.yaml} +0 -0
- data/spec/spec_helper.rb +4 -4
- metadata +47 -19
@@ -18,15 +18,88 @@ module MrMurano
|
|
18
18
|
def scid_for_name(name)
|
19
19
|
name = name.to_s unless name.kind_of? String
|
20
20
|
scr = list().select{|i| i[:service] == name}.first
|
21
|
-
|
21
|
+
return nil if scr.nil?
|
22
|
+
scr[:id]
|
22
23
|
end
|
23
24
|
|
24
25
|
def scid
|
25
26
|
return @scid unless @scid.nil?
|
26
27
|
@scid = scid_for_name(@serviceName)
|
27
28
|
end
|
29
|
+
|
30
|
+
def info(id=scid)
|
31
|
+
get("/#{id}/info")
|
32
|
+
end
|
33
|
+
|
34
|
+
def logs(id=scid)
|
35
|
+
get("/#{id}/logs")
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(opid, meth=:get, data=nil, id=scid, &block)
|
39
|
+
call = "/#{id.to_s}/call/#{opid.to_s}"
|
40
|
+
case meth
|
41
|
+
when :get
|
42
|
+
get(call, data, &block)
|
43
|
+
when :post
|
44
|
+
data = {} if data.nil?
|
45
|
+
post(call, data, &block)
|
46
|
+
when :put
|
47
|
+
data = {} if data.nil?
|
48
|
+
put(call, data, &block)
|
49
|
+
when :delete
|
50
|
+
delete(call, &block)
|
51
|
+
else
|
52
|
+
raise "Unknown method: #{meth}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
28
56
|
end
|
29
57
|
|
58
|
+
class Services < SolutionBase
|
59
|
+
def initialize
|
60
|
+
super
|
61
|
+
@uriparts << 'service'
|
62
|
+
end
|
63
|
+
|
64
|
+
def sid_for_name(name)
|
65
|
+
name = name.to_s unless name.kind_of? String
|
66
|
+
scr = list().select{|i| i[:alias] == name}.first
|
67
|
+
scr[:id]
|
68
|
+
end
|
69
|
+
|
70
|
+
def sid
|
71
|
+
return @sid unless @sid.nil?
|
72
|
+
@sid = sid_for_name(@serviceName)
|
73
|
+
end
|
74
|
+
|
75
|
+
def list
|
76
|
+
ret = get()
|
77
|
+
ret[:items]
|
78
|
+
end
|
79
|
+
|
80
|
+
def schema(id=sid)
|
81
|
+
# TODO: cache schema in user dir?
|
82
|
+
get("/#{id}/schema")
|
83
|
+
end
|
84
|
+
|
85
|
+
## Get list of call operations from a schema
|
86
|
+
def callable(id=sid)
|
87
|
+
scm = schema(id)
|
88
|
+
calls = []
|
89
|
+
scm[:paths].each do |path, methods|
|
90
|
+
methods.each do |method, params|
|
91
|
+
if params.kind_of?(Hash) and
|
92
|
+
not params['x-internal-use'.to_sym] and
|
93
|
+
params.has_key?(:operationId) then
|
94
|
+
calls << [method, params[:operationId]]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
calls
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
30
103
|
class SC_Device < ServiceConfig
|
31
104
|
def initialize
|
32
105
|
super
|
@@ -47,7 +47,8 @@ module MrMurano
|
|
47
47
|
delete('/'+name)
|
48
48
|
end
|
49
49
|
|
50
|
-
|
50
|
+
# @param modify Bool: True if item exists already and this is changing it
|
51
|
+
def upload(local, remote, modify=false)
|
51
52
|
local = Pathname.new(local) unless local.kind_of? Pathname
|
52
53
|
raise "no file" unless local.exist?
|
53
54
|
|
@@ -164,6 +165,7 @@ module MrMurano
|
|
164
165
|
item[:name]
|
165
166
|
end
|
166
167
|
end
|
168
|
+
SyncRoot.add('modules', Library, 'M', %{Modules}, true)
|
167
169
|
|
168
170
|
# …/eventhandler
|
169
171
|
class EventHandler < ServiceBase
|
@@ -222,7 +224,7 @@ module MrMurano
|
|
222
224
|
"#{item[:service]}_#{item[:event]}"
|
223
225
|
end
|
224
226
|
end
|
227
|
+
SyncRoot.add('eventhandlers', EventHandler, 'E', %{Event Handlers}, true)
|
225
228
|
|
226
|
-
# How do we enable product.id to flow into the eventhandler?
|
227
229
|
end
|
228
230
|
# vim: set ai et sw=2 ts=2 :
|
@@ -21,7 +21,8 @@ module MrMurano
|
|
21
21
|
delete('/' + id.to_s)
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
# @param modify Bool: True if item exists already and this is changing it
|
25
|
+
def upload(local, remote, modify)
|
25
26
|
# Roles cannot be modified, so must delete and post.
|
26
27
|
delete('/' + remote[@itemkey]) do |request, http|
|
27
28
|
response = http.request(request)
|
@@ -84,7 +85,6 @@ module MrMurano
|
|
84
85
|
say_warning "Cannot read from #{from.to_s}"
|
85
86
|
return []
|
86
87
|
end
|
87
|
-
key = @itemkey.to_sym
|
88
88
|
|
89
89
|
here = []
|
90
90
|
from.open {|io| here = YAML.load(io) }
|
@@ -103,6 +103,7 @@ module MrMurano
|
|
103
103
|
@location = $cfg['location.roles']
|
104
104
|
end
|
105
105
|
end
|
106
|
+
SyncRoot.add('roles', Role, 'R', %{Roles})
|
106
107
|
|
107
108
|
# …/user
|
108
109
|
class User < UserBase
|
@@ -112,7 +113,8 @@ module MrMurano
|
|
112
113
|
@location = $cfg['location.users']
|
113
114
|
end
|
114
115
|
|
115
|
-
|
116
|
+
# @param modify Bool: True if item exists already and this is changing it
|
117
|
+
def upload(local, remote, modify)
|
116
118
|
# TODO figure out APIs for updating users.
|
117
119
|
say_warning "Updating Users isn't working currently."
|
118
120
|
# post does work if the :password field is set.
|
@@ -122,5 +124,6 @@ module MrMurano
|
|
122
124
|
item[:email]
|
123
125
|
end
|
124
126
|
end
|
127
|
+
SyncRoot.add('users', User, 'U', %{Users})
|
125
128
|
end
|
126
129
|
# vim: set ai et sw=2 ts=2 :
|
data/lib/MrMurano/Solution.rb
CHANGED
@@ -1,16 +1,11 @@
|
|
1
1
|
require 'uri'
|
2
|
-
require 'net/http'
|
3
|
-
require 'json'
|
4
|
-
require 'tempfile'
|
5
|
-
require 'shellwords'
|
6
|
-
require 'pp'
|
7
2
|
require 'MrMurano/Config'
|
8
3
|
require 'MrMurano/http'
|
9
4
|
require 'MrMurano/verbosing'
|
5
|
+
require 'MrMurano/SyncUpDown'
|
10
6
|
|
11
7
|
module MrMurano
|
12
8
|
class SolutionBase
|
13
|
-
# This might also be a valid ProductBase.
|
14
9
|
def initialize
|
15
10
|
@sid = $cfg['solution.id']
|
16
11
|
raise "No solution!" if @sid.nil?
|
@@ -23,6 +18,10 @@ module MrMurano
|
|
23
18
|
include Http
|
24
19
|
include Verbose
|
25
20
|
|
21
|
+
## Generate an endpoint in Murano
|
22
|
+
# Uses the uriparts and path
|
23
|
+
# @param path String: any additional parts for the URI
|
24
|
+
# @return URI: The full URI for this enpoint.
|
26
25
|
def endPoint(path='')
|
27
26
|
parts = ['https:/', $cfg['net.host'], 'api:1'] + @uriparts
|
28
27
|
s = parts.map{|v| v.to_s}.join('/')
|
@@ -30,274 +29,7 @@ module MrMurano
|
|
30
29
|
end
|
31
30
|
# …
|
32
31
|
|
33
|
-
|
34
|
-
# Compute a remote item hash from the local path
|
35
|
-
# @param root Pathname: Root path for this resource type from config files
|
36
|
-
# @param path Pathname: Path to local item
|
37
|
-
# @return Hash: hash of the details for the remote item for this path
|
38
|
-
def toRemoteItem(root, path)
|
39
|
-
path = Pathname.new(path) unless path.kind_of? Pathname
|
40
|
-
root = Pathname.new(root) unless root.kind_of? Pathname
|
41
|
-
{:name => path.relative_path_from(root).to_s}
|
42
|
-
end
|
43
|
-
|
44
|
-
##
|
45
|
-
# Compute the local name from remote item details
|
46
|
-
# @param item Hash: listing details for the item.
|
47
|
-
# @param itemkey Symbol: Key for look up.
|
48
|
-
def tolocalname(item, itemkey)
|
49
|
-
item[itemkey]
|
50
|
-
end
|
51
|
-
|
52
|
-
##
|
53
|
-
# Compute the local path from the listing details
|
54
|
-
#
|
55
|
-
# If there is already a matching local item, some of its details are also in
|
56
|
-
# the item hash.
|
57
|
-
#
|
58
|
-
# @param into Pathname: Root path for this resource type from config files
|
59
|
-
# @param item Hash: listing details for the item.
|
60
|
-
# @return Pathname: path to save (or merge) remote item into
|
61
|
-
def tolocalpath(into, item)
|
62
|
-
return item[:local_path] if item.has_key? :local_path
|
63
|
-
itemkey = @itemkey.to_sym
|
64
|
-
name = tolocalname(item, itemkey)
|
65
|
-
raise "Bad key(#{itemkey}) for #{item}" if name.nil?
|
66
|
-
name = Pathname.new(name) unless name.kind_of? Pathname
|
67
|
-
name = name.relative_path_from(Pathname.new('/')) if name.absolute?
|
68
|
-
dest = into + name
|
69
|
-
end
|
70
|
-
|
71
|
-
##
|
72
|
-
# So, for bundles this needs to look at all the places and build up the mered
|
73
|
-
# stack of local items.
|
74
|
-
#
|
75
|
-
# Which means it needs the from to be split into the base and the sub so we can
|
76
|
-
# inject bundle directories.
|
77
|
-
|
78
|
-
##
|
79
|
-
# Get a list of local items.
|
80
|
-
#
|
81
|
-
# This collects items in the project and all bundles.
|
82
|
-
def locallist()
|
83
|
-
# so. if @locationbase/bundles exists
|
84
|
-
# gather and merge: @locationbase/bundles/*/@location
|
85
|
-
# then merge @locationbase/@location
|
86
|
-
#
|
87
|
-
|
88
|
-
bundleDir = $cfg['location.bundles'] or 'bundles'
|
89
|
-
bundleDir = 'bundles' if bundleDir.nil?
|
90
|
-
items = {}
|
91
|
-
if (@locationbase + bundleDir).directory? then
|
92
|
-
(@locationbase + bundleDir).children.sort.each do |bndl|
|
93
|
-
if (bndl + @location).exist? then
|
94
|
-
verbose("Loading from bundle #{bndl.basename}")
|
95
|
-
bitems = localitems(bndl + @location)
|
96
|
-
bitems.map!{|b| b[:bundled] = true; b} # mark items from bundles.
|
97
|
-
|
98
|
-
|
99
|
-
# use synckey for quicker merging.
|
100
|
-
bitems.each { |b| items[synckey(b)] = b }
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
if (@locationbase + @location).exist? then
|
105
|
-
bitems = localitems(@locationbase + @location)
|
106
|
-
# use synckey for quicker merging.
|
107
|
-
bitems.each { |b| items[synckey(b)] = b }
|
108
|
-
end
|
109
|
-
|
110
|
-
items.values
|
111
|
-
end
|
112
|
-
|
113
|
-
##
|
114
|
-
# Get a list of local items rooted at #from
|
115
|
-
def localitems(from)
|
116
|
-
from.children.map do |path|
|
117
|
-
if path.directory? then
|
118
|
-
# TODO: look for definition. ( ?.rockspec? ?mr.modules? ?mr.manifest? )
|
119
|
-
# Lacking definition, find all *.lua but not *_test.lua
|
120
|
-
# This specifically and intentionally only goes one level deep.
|
121
|
-
path.children
|
122
|
-
else
|
123
|
-
path
|
124
|
-
end
|
125
|
-
end.flatten.compact.reject do |path|
|
126
|
-
path.fnmatch('*_test.lua') or path.basename.fnmatch('.*')
|
127
|
-
end.select do |path|
|
128
|
-
path.extname == '.lua'
|
129
|
-
end.map do |path|
|
130
|
-
# sometimes this is a name, sometimes it is an item.
|
131
|
-
# do I want to keep that? NO.
|
132
|
-
name = toRemoteItem(from, path)
|
133
|
-
unless name.nil? then
|
134
|
-
name[:local_path] = path
|
135
|
-
name
|
136
|
-
end
|
137
|
-
end.flatten.compact
|
138
|
-
end
|
139
|
-
|
140
|
-
def synckey(item)
|
141
|
-
key = @itemkey.to_sym
|
142
|
-
item[key]
|
143
|
-
end
|
144
|
-
|
145
|
-
def download(local, item)
|
146
|
-
if item[:bundled] then
|
147
|
-
say_warning "Not downloading into bundled item #{synckey(item)}"
|
148
|
-
return
|
149
|
-
end
|
150
|
-
local.dirname.mkpath
|
151
|
-
id = item[@itemkey.to_sym]
|
152
|
-
local.open('wb') do |io|
|
153
|
-
fetch(id) do |chunk|
|
154
|
-
io.write chunk
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def removelocal(dest, item)
|
160
|
-
dest.unlink
|
161
|
-
end
|
162
|
-
|
163
|
-
def syncup(options=Commander::Command::Options.new)
|
164
|
-
itemkey = @itemkey.to_sym
|
165
|
-
options.asdown=false
|
166
|
-
dt = status(options)
|
167
|
-
toadd = dt[:toadd]
|
168
|
-
todel = dt[:todel]
|
169
|
-
tomod = dt[:tomod]
|
170
|
-
|
171
|
-
if options.delete then
|
172
|
-
todel.each do |item|
|
173
|
-
verbose "Removing item #{item[:synckey]}"
|
174
|
-
unless $cfg['tool.dry'] then
|
175
|
-
remove(item[itemkey])
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
if options.create then
|
180
|
-
toadd.each do |item|
|
181
|
-
verbose "Adding item #{item[:synckey]}"
|
182
|
-
unless $cfg['tool.dry'] then
|
183
|
-
upload(item[:local_path], item.reject{|k,v| k==:local_path})
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
if options.update then
|
188
|
-
tomod.each do |item|
|
189
|
-
verbose "Updating item #{item[:synckey]}"
|
190
|
-
unless $cfg['tool.dry'] then
|
191
|
-
upload(item[:local_path], item.reject{|k,v| k==:local_path})
|
192
|
-
end
|
193
|
-
end
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
# FIXME this still needs the path passed in.
|
198
|
-
# Need to think some more on how syncdown works with bundles.
|
199
|
-
def syncdown(options=Commander::Command::Options.new)
|
200
|
-
options.asdown = true
|
201
|
-
dt = status(options)
|
202
|
-
into = @locationbase + @location ###
|
203
|
-
toadd = dt[:toadd]
|
204
|
-
todel = dt[:todel]
|
205
|
-
tomod = dt[:tomod]
|
206
|
-
|
207
|
-
if options.delete then
|
208
|
-
todel.each do |item|
|
209
|
-
verbose "Removing item #{item[:synckey]}"
|
210
|
-
unless $cfg['tool.dry'] then
|
211
|
-
dest = tolocalpath(into, item)
|
212
|
-
removelocal(dest, item)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
if options.create then
|
217
|
-
toadd.each do |item|
|
218
|
-
verbose "Adding item #{item[:synckey]}"
|
219
|
-
unless $cfg['tool.dry'] then
|
220
|
-
dest = tolocalpath(into, item)
|
221
|
-
download(dest, item)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
if options.update then
|
226
|
-
tomod.each do |item|
|
227
|
-
verbose "Updating item #{item[:synckey]}"
|
228
|
-
unless $cfg['tool.dry'] then
|
229
|
-
dest = tolocalpath(into, item)
|
230
|
-
download(dest, item)
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
##
|
237
|
-
# True if itemA and itemB are different
|
238
|
-
def docmp(itemA, itemB)
|
239
|
-
true
|
240
|
-
end
|
241
|
-
|
242
|
-
def dodiff(item)
|
243
|
-
tfp = Tempfile.new([tolocalname(item, @itemkey), '.lua'])
|
244
|
-
df = ""
|
245
|
-
begin
|
246
|
-
download(Pathname.new(tfp.path), item)
|
247
|
-
|
248
|
-
cmd = $cfg['diff.cmd'].shellsplit
|
249
|
-
cmd << tfp.path
|
250
|
-
cmd << item[:local_path].to_s
|
251
|
-
|
252
|
-
IO.popen(cmd) {|io| df = io.read }
|
253
|
-
ensure
|
254
|
-
tfp.close
|
255
|
-
tfp.unlink
|
256
|
-
end
|
257
|
-
df
|
258
|
-
end
|
259
|
-
|
260
|
-
def status(options=Commander::Command::Options.new)
|
261
|
-
there = list()
|
262
|
-
here = locallist()
|
263
|
-
itemkey = @itemkey.to_sym
|
264
|
-
|
265
|
-
therebox = {}
|
266
|
-
there.each do |item|
|
267
|
-
item = Hash.transform_keys_to_symbols(item)
|
268
|
-
item[:synckey] = synckey(item)
|
269
|
-
therebox[ item[:synckey] ] = item
|
270
|
-
end
|
271
|
-
herebox = {}
|
272
|
-
here.each do |item|
|
273
|
-
item = Hash.transform_keys_to_symbols(item)
|
274
|
-
item[:synckey] = synckey(item)
|
275
|
-
herebox[ item[:synckey] ] = item
|
276
|
-
end
|
277
|
-
toadd = []
|
278
|
-
todel = []
|
279
|
-
tomod = []
|
280
|
-
unchg = []
|
281
|
-
if options.asdown then
|
282
|
-
todel = (herebox.keys - therebox.keys).map{|key| herebox[key] }
|
283
|
-
toadd = (therebox.keys - herebox.keys).map{|key| therebox[key] }
|
284
|
-
else
|
285
|
-
toadd = (herebox.keys - therebox.keys).map{|key| herebox[key] }
|
286
|
-
todel = (therebox.keys - herebox.keys).map{|key| therebox[key] }
|
287
|
-
end
|
288
|
-
(herebox.keys & therebox.keys).each do |key|
|
289
|
-
# Want here to override there except for itemkey.
|
290
|
-
mrg = herebox[key].reject{|k,v| k==itemkey}
|
291
|
-
mrg = therebox[key].merge(mrg)
|
292
|
-
if docmp(herebox[key], therebox[key]) then
|
293
|
-
mrg[:diff] = dodiff(mrg) if options.diff
|
294
|
-
tomod << mrg
|
295
|
-
else
|
296
|
-
unchg << mrg
|
297
|
-
end
|
298
|
-
end
|
299
|
-
{ :toadd=>toadd, :todel=>todel, :tomod=>tomod, :unchg=>unchg }
|
300
|
-
end
|
32
|
+
include SyncUpDown
|
301
33
|
end
|
302
34
|
|
303
35
|
class Solution < SolutionBase
|