MrMurano 1.7.4 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.markdown +33 -19
- data/TODO.taskpaper +22 -2
- data/lib/MrMurano/Config.rb +6 -2
- data/lib/MrMurano/Product-Resources.rb +3 -1
- data/lib/MrMurano/Solution-Endpoint.rb +33 -12
- data/lib/MrMurano/Solution-ServiceConfig.rb +1 -0
- data/lib/MrMurano/SubCmdGroupContext.rb +45 -0
- data/lib/MrMurano/SyncUpDown.rb +27 -12
- data/lib/MrMurano/commands.rb +2 -0
- data/lib/MrMurano/commands/assign.rb +6 -3
- data/lib/MrMurano/commands/content.rb +14 -1
- data/lib/MrMurano/commands/keystore.rb +12 -1
- data/lib/MrMurano/commands/product.rb +14 -0
- data/lib/MrMurano/commands/productList.rb +28 -5
- data/lib/MrMurano/commands/productSpec.rb +14 -4
- data/lib/MrMurano/commands/serialNumberCmds.rb +12 -1
- data/lib/MrMurano/commands/solution.rb +14 -0
- data/lib/MrMurano/commands/solutionList.rb +27 -5
- data/lib/MrMurano/commands/status.rb +5 -1
- data/lib/MrMurano/commands/timeseries.rb +13 -0
- data/lib/MrMurano/commands/tsdb.rb +35 -6
- data/lib/MrMurano/version.rb +1 -1
- data/spec/ProductResources_spec.rb +32 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18a4696a79038b6f17c3a43cf1253edf00945062
|
4
|
+
data.tar.gz: aab88cf46593211db7207e8759e31f52a407a7c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e88eb9c42ddfda7599c5be3f53cfec180f1faadb5fdc83a0d2f784401ba11eab1f54b5db69ac4d517f07a9284f1706502bbc7701989bcf8b52df5d39d4ecdff
|
7
|
+
data.tar.gz: 20751f37d098dbc3f547518c80a9a465eaa62b9850b60ce12089f1b661c7ded530f68391e537994513cd0762d292b45c6277b11e078069d0bcce92b10b5f4c59
|
data/README.markdown
CHANGED
@@ -5,6 +5,13 @@
|
|
5
5
|
|
6
6
|
Do more from the command line with [Murano](https://exosite.com/platform/)
|
7
7
|
|
8
|
+
MrMurano is the command-line tool that interacts with Murano and makes different
|
9
|
+
tasks easier. MrMurano makes it easy to deploy code to a solution, import many
|
10
|
+
product definitions at once, set up endpoints and APIs, and more.
|
11
|
+
|
12
|
+
MrMurano works around the idea of syncing, much like rsync. Files from your working
|
13
|
+
directory are synced up (or down) from Murano.
|
14
|
+
|
8
15
|
## Usage
|
9
16
|
|
10
17
|
### To start from an existing project in Murano
|
@@ -26,11 +33,13 @@ running in Murano. Here is the list.
|
|
26
33
|
- Set it: `mr config business.id ZZZZZZZZZ`
|
27
34
|
- Create a product: `mr product create myawesomeproduct`
|
28
35
|
- Save the result: `mr config product.id YYYYYYYYY`
|
29
|
-
-
|
36
|
+
- Set the product definition: `mr config product.spec prd.spec`
|
37
|
+
- Add resource aliases to specs/prd.spec
|
38
|
+
- Sync the product definition up: `mr syncup -V --specs`
|
30
39
|
- Create a solution: `mr solution create myawesomesolution`
|
31
40
|
- Save the result: `mr config solution.id XXXXXX`
|
32
41
|
- Sync solution code up: `mr syncup -V`
|
33
|
-
- Assign the
|
42
|
+
- Assign the product to the solution: `mr assign set`
|
34
43
|
|
35
44
|
Do stuff, see what changed: `mr status` or `mr diff`.
|
36
45
|
Then deploy with `mr syncup`
|
@@ -105,15 +114,29 @@ EOF
|
|
105
114
|
This also allows for keeping private things in a seperate config file and having
|
106
115
|
the shared things checked into source control.
|
107
116
|
|
108
|
-
###
|
117
|
+
### Direct Service Access
|
118
|
+
|
119
|
+
To aid with debugging, MrMurano has direct access to some of the services in a
|
120
|
+
solution.
|
109
121
|
|
110
|
-
|
122
|
+
Currently these are:
|
123
|
+
- Keystore: `mr keystore`
|
124
|
+
- Timeseries: `mr timeseries`
|
125
|
+
- TSDB: `mr tsdb`
|
111
126
|
|
112
|
-
|
127
|
+
### Output Format
|
113
128
|
|
114
|
-
|
129
|
+
Many sub-commands respect the `outformat` setting. This lets you switch the output
|
130
|
+
between YAML, JSON, Ruby, CSV, and pretty tables. Not all formats work with all
|
131
|
+
commands.
|
115
132
|
|
116
|
-
|
133
|
+
```
|
134
|
+
mr tsdb product list
|
135
|
+
mr tsdb product list -c outformat=csv
|
136
|
+
mr tsdb product list -c outformat=json
|
137
|
+
mr tsdb product list -c outformat=yaml
|
138
|
+
mr tsdb product list -c outformat=pp
|
139
|
+
```
|
117
140
|
|
118
141
|
### Product Content Area
|
119
142
|
|
@@ -183,18 +206,9 @@ spec
|
|
183
206
|
spec/cico.murano.spec
|
184
207
|
```
|
185
208
|
|
209
|
+
## Developing
|
186
210
|
|
187
|
-
|
188
|
-
|
189
|
-
MrMuanro allows adding bundles of resources to your project.
|
190
|
-
|
191
|
-
A Bundle is a group of modules, endpoints, and static files.
|
192
|
-
|
193
|
-
Bundles live in the 'bundle' directory. Each bundle is a directory that matches
|
194
|
-
the layout of a project. (with directories for endpoints, modules, files, etc)
|
195
|
-
|
196
|
-
The items in bundles are layered by sorting the bundle names. Then your project's
|
197
|
-
items are layered on top. This builds the list of what is synced. It also allows
|
198
|
-
you to override things that are in a bundle from you project.
|
211
|
+
MrMurano uses git flow for managing branches.
|
199
212
|
|
213
|
+
MrMurano also uses [bunder](http://bundler.io).
|
200
214
|
|
data/TODO.taskpaper
CHANGED
@@ -3,6 +3,12 @@ Readme:
|
|
3
3
|
- Look into using VCR for testing. @pri(low)
|
4
4
|
|
5
5
|
Commands:
|
6
|
+
- Init command.
|
7
|
+
- Empty sub-commands should return help. @done(2016-11-21)
|
8
|
+
There are a bunch of empty sub-commands that prefix another layer. Such as
|
9
|
+
assign, content, product, and others. Those should be impemented as a ‘help’
|
10
|
+
only command. That is they should return help like the plain `mr` command, but
|
11
|
+
just for their sub-section of things
|
6
12
|
- Errors and Warnings should get sent to STDERR @done(2016-11-03)
|
7
13
|
- Need a more consistent output format. 'pp' is still used in many places. @done(2016-11-03)
|
8
14
|
Maybe have a tool setting for output format? json, yaml, pp, csv, table ?
|
@@ -17,7 +23,7 @@ Account:
|
|
17
23
|
- Netrc library (or the netrc format) doesn't allow '#' in passwords. @done(2016-08-10)
|
18
24
|
|
19
25
|
Endpoints:
|
20
|
-
- Add support for multiple endpoints in one file (
|
26
|
+
- Add support for multiple endpoints in one file @pri(high) @done(2016-11-18)
|
21
27
|
- Add directory support like in modules @done(2016-07-26)
|
22
28
|
|
23
29
|
Files:
|
@@ -39,12 +45,18 @@ CORS:
|
|
39
45
|
- GET&PUT /cors data @done(2016-09-08)
|
40
46
|
|
41
47
|
TSDB:
|
42
|
-
-
|
48
|
+
- For query, if no metrics on cmdline, then do listMetrics and use all. @done(2016-11-21)
|
49
|
+
well, the first 1000 or whatever we get from a single call to listMetrics
|
50
|
+
- Query should handle tags prefixed with @ to match write. @done(2016-11-21)
|
51
|
+
- Add support for new TSDB service. @pri(high) @done(2016-11-03)
|
43
52
|
|
44
53
|
Timeseries:
|
45
54
|
- Add CSV output option. @done(2016-09-09)
|
46
55
|
|
47
56
|
Product:
|
57
|
+
- Support multiple products.
|
58
|
+
Think about how this would work. There is the syncing of the resoruces, and then
|
59
|
+
some of the commands that use product.id.
|
48
60
|
- Auto convert exoline spec files into murano spec files on upload? @done(2016-10-27)
|
49
61
|
Not doing this. Convert is there if you need it.
|
50
62
|
- write alias command @done(2016-09-26)
|
@@ -54,12 +66,20 @@ Service Device:
|
|
54
66
|
- When listing and bussiness.id is missing, gracefully fall back to --idonly @done(2016-09-12)
|
55
67
|
|
56
68
|
Config:
|
69
|
+
- Add config tool.default_sync to set which things sync{up,down} by default
|
70
|
+
It is internally hardcoded to be -s, -a, -m, -e right now.
|
57
71
|
- Add ENV['MR_CONFIGFILE'] path to file to load like --configfile @done(2016-09-22)
|
58
72
|
- Maybe add dotenv support. @done(2016-09-22)
|
59
73
|
- Think about adding dev,staging,prod system; how would that work? @done(2016-09-16)
|
60
74
|
|
75
|
+
SyncUpDown:
|
76
|
+
- Document the hash keys for an item. @pri(high)
|
77
|
+
Also consider turning that hash into a Struct
|
78
|
+
- Allow specifying local files to limit actions to.
|
79
|
+
|
61
80
|
SolutionBase:
|
62
81
|
- All network traffic is serialized. Make some parallel.
|
82
|
+
This might break some things.
|
63
83
|
- Errors from the server should be displayed prettier. @done(2016-09-26)
|
64
84
|
- JSON parse should use symbols for keys. @done(2016-09-01)
|
65
85
|
- Add the --curl verbose option. @done(2016-08-12)
|
data/lib/MrMurano/Config.rb
CHANGED
@@ -39,7 +39,7 @@ module MrMurano
|
|
39
39
|
CFG_PRVT_NAME = '.mrmuranorc.private'.freeze # Going away.
|
40
40
|
CFG_DIR_NAME = '.mrmurano'.freeze
|
41
41
|
CFG_ALTRC_NAME = '.mrmurano/config'.freeze
|
42
|
-
CFG_SYS_NAME = '/etc/mrmuranorc'.freeze
|
42
|
+
CFG_SYS_NAME = '/etc/mrmuranorc'.freeze # Going away.
|
43
43
|
|
44
44
|
def initialize
|
45
45
|
@paths = []
|
@@ -64,7 +64,10 @@ module MrMurano
|
|
64
64
|
end
|
65
65
|
@paths << ConfigFile.new(:user, Pathname.new(Dir.home) + CFG_FILE_NAME)
|
66
66
|
fixModes(Pathname.new(Dir.home) + CFG_DIR_NAME)
|
67
|
-
|
67
|
+
if Pathname.new(CFG_SYS_NAME).exist? then
|
68
|
+
say_warning "!!! Using #{CFG_SYS_NAME} is deprecated"
|
69
|
+
@paths << ConfigFile.new(:system, Pathname.new(CFG_SYS_NAME))
|
70
|
+
end
|
68
71
|
@paths << ConfigFile.new(:defaults, nil, IniFile.new())
|
69
72
|
|
70
73
|
|
@@ -83,6 +86,7 @@ module MrMurano
|
|
83
86
|
set('location.roles', 'roles.yaml', :defaults)
|
84
87
|
set('location.users', 'users.yaml', :defaults)
|
85
88
|
set('location.cors', 'cors.yaml', :defaults)
|
89
|
+
set('location.specs', 'specs', :defaults)
|
86
90
|
|
87
91
|
set('files.default_page', 'index.html', :defaults)
|
88
92
|
|
@@ -33,10 +33,12 @@ module MrMurano
|
|
33
33
|
name = $cfg['product.spec']
|
34
34
|
prid = $cfg['product.id']
|
35
35
|
name = $cfg["p-#{prid}.spec"] unless prid.nil? or $cfg["p-#{prid}.spec"].nil?
|
36
|
+
raise "No spec file named; run `mr config prodcut.spec <specfile>`" if name.nil?
|
36
37
|
|
37
38
|
unless $cfg['location.specs'].nil? then
|
38
|
-
name = File.join($cfg['location.specs'], name)
|
39
|
+
name = ::File.join($cfg['location.specs'], name)
|
39
40
|
end
|
41
|
+
debug " spec file name => #{name}"
|
40
42
|
name
|
41
43
|
end
|
42
44
|
|
@@ -16,11 +16,15 @@ module MrMurano
|
|
16
16
|
##
|
17
17
|
# This gets all data about all endpoints
|
18
18
|
def list
|
19
|
-
get()
|
19
|
+
get().map do |item|
|
20
|
+
item[:content_type] = 'application/json' if item[:content_type].empty?
|
21
|
+
item
|
22
|
+
end
|
20
23
|
end
|
21
24
|
|
22
25
|
def fetch(id)
|
23
26
|
ret = get('/' + id.to_s)
|
27
|
+
ret[:content_type] = 'application/json' if ret[:content_type].empty?
|
24
28
|
aheader = (ret[:script].lines.first or "").chomp
|
25
29
|
dheader = /^--#ENDPOINT (?i:#{ret[:method]}) #{ret[:path]}$/
|
26
30
|
rheader = %{--#ENDPOINT #{ret[:method]} #{ret[:path]}\n}
|
@@ -45,10 +49,12 @@ module MrMurano
|
|
45
49
|
raise "no file" unless local.exist?
|
46
50
|
|
47
51
|
# we assume these are small enough to slurp.
|
48
|
-
script
|
52
|
+
unless remote.has_key? :script then
|
53
|
+
script = local.read
|
54
|
+
remote[:script] = script
|
55
|
+
end
|
49
56
|
limitkeys = [:method, :path, :script, @itemkey]
|
50
57
|
remote = remote.select{|k,v| limitkeys.include? k }
|
51
|
-
remote[:script] = script
|
52
58
|
# post('', remote)
|
53
59
|
if remote.has_key? @itemkey then
|
54
60
|
put('/' + remote[@itemkey], remote) do |request, http|
|
@@ -84,16 +90,31 @@ module MrMurano
|
|
84
90
|
end
|
85
91
|
|
86
92
|
def toRemoteItem(from, path)
|
87
|
-
#
|
93
|
+
# Path could be have multiple endpoints in side, so a loop.
|
94
|
+
items = []
|
88
95
|
path = Pathname.new(path) unless path.kind_of? Pathname
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
96
|
+
cur = nil
|
97
|
+
lineno=0
|
98
|
+
path.readlines().each do |line|
|
99
|
+
md = /--#ENDPOINT (?<method>\S+) (?<path>\S+)( (?<ctype>.*))?/.match(line)
|
100
|
+
if not md.nil? then
|
101
|
+
# header line.
|
102
|
+
cur[:line_end] = lineno unless cur.nil?
|
103
|
+
items << cur unless cur.nil?
|
104
|
+
cur = {:method=>md[:method],
|
105
|
+
:path=>md[:path],
|
106
|
+
:content_type=> (md[:ctype] or 'application/json'),
|
107
|
+
:local_path=>path,
|
108
|
+
:line=>lineno,
|
109
|
+
:script=>line}
|
110
|
+
elsif not cur.nil? and not cur[:script].nil? then
|
111
|
+
cur[:script] << line
|
112
|
+
end
|
113
|
+
lineno += 1
|
95
114
|
end
|
96
|
-
|
115
|
+
cur[:line_end] = lineno unless cur.nil?
|
116
|
+
items << cur unless cur.nil?
|
117
|
+
items
|
97
118
|
end
|
98
119
|
|
99
120
|
def synckey(item)
|
@@ -107,7 +128,7 @@ module MrMurano
|
|
107
128
|
if itemB[:script].nil? and itemB[:local_path] then
|
108
129
|
itemB[:script] = itemB[:local_path].read
|
109
130
|
end
|
110
|
-
return itemA[:script] != itemB[:script]
|
131
|
+
return (itemA[:script] != itemB[:script] or itemA[:content_type] != itemB[:content_type])
|
111
132
|
end
|
112
133
|
|
113
134
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
module MrMurano
|
3
|
+
class SubCmdGroupHelp
|
4
|
+
attr :name, :description
|
5
|
+
|
6
|
+
def initialize(command)
|
7
|
+
@name = command.syntax.to_s
|
8
|
+
@description = command.description.to_s
|
9
|
+
@runner = ::Commander::Runner.instance
|
10
|
+
prefix = /^#{command.name.to_s} /
|
11
|
+
cmds = @runner.instance_variable_get(:@commands).select{|n,_| n.to_s =~ prefix}
|
12
|
+
@commands = cmds
|
13
|
+
als = @runner.instance_variable_get(:@aliases).select{|n,_| n.to_s =~ prefix}
|
14
|
+
@aliases = als
|
15
|
+
|
16
|
+
@options = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def program(key)
|
20
|
+
case key
|
21
|
+
when :name
|
22
|
+
@name
|
23
|
+
when :description
|
24
|
+
@description
|
25
|
+
when :help
|
26
|
+
nil
|
27
|
+
else
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def alias?(name)
|
33
|
+
@aliases.include? name.to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_help
|
37
|
+
hf = @runner.program(:help_formatter).new(self)
|
38
|
+
pc = Commander::HelpFormatter::ProgramContext.new(self).get_binding
|
39
|
+
hf.template(:help).result(pc)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# vim: set ai et sw=2 ts=2 :
|
data/lib/MrMurano/SyncUpDown.rb
CHANGED
@@ -5,6 +5,7 @@ require 'MrMurano/Config'
|
|
5
5
|
require 'MrMurano/hash'
|
6
6
|
|
7
7
|
module MrMurano
|
8
|
+
## Track what things are syncable.
|
8
9
|
class SyncRoot
|
9
10
|
Syncable = Struct.new(:name, :class, :type, :desc, :bydefault) do
|
10
11
|
end
|
@@ -258,16 +259,18 @@ module MrMurano
|
|
258
259
|
path
|
259
260
|
end
|
260
261
|
end.flatten.compact.reject do |path|
|
262
|
+
# TODO: These globs should be in $cfg.
|
261
263
|
path.fnmatch('*_test.lua') or path.basename.fnmatch('.*')
|
262
264
|
end.select do |path|
|
265
|
+
# TODO: These globs should be in $cfg.
|
263
266
|
path.extname == '.lua'
|
264
267
|
end.map do |path|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
268
|
+
item = toRemoteItem(from, path)
|
269
|
+
if item.kind_of?(Array) then
|
270
|
+
item.compact.map{|i| i[:local_path] = path; i}
|
271
|
+
elsif not item.nil? then
|
272
|
+
item[:local_path] = path
|
273
|
+
item
|
271
274
|
end
|
272
275
|
end.flatten.compact
|
273
276
|
end
|
@@ -368,19 +371,31 @@ module MrMurano
|
|
368
371
|
# @param item Hash: The item to get a diff of
|
369
372
|
# @return String: The diff output
|
370
373
|
def dodiff(item)
|
371
|
-
|
374
|
+
trmt = Tempfile.new([tolocalname(item, @itemkey)+'_remote_', '.lua'])
|
375
|
+
tlcl = Tempfile.new([tolocalname(item, @itemkey)+'_local_', '.lua'])
|
376
|
+
if item.has_key? :script then
|
377
|
+
Pathname.new(tlcl.path).open('wb') do |io|
|
378
|
+
io << item[:script]
|
379
|
+
end
|
380
|
+
else
|
381
|
+
Pathname.new(tlcl.path).open('wb') do |io|
|
382
|
+
io << item[:local_path].read
|
383
|
+
end
|
384
|
+
end
|
372
385
|
df = ""
|
373
386
|
begin
|
374
|
-
download(Pathname.new(
|
387
|
+
download(Pathname.new(trmt.path), item)
|
375
388
|
|
376
389
|
cmd = $cfg['diff.cmd'].shellsplit
|
377
|
-
cmd <<
|
378
|
-
cmd <<
|
390
|
+
cmd << trmt.path
|
391
|
+
cmd << tlcl.path
|
379
392
|
|
380
393
|
IO.popen(cmd) {|io| df = io.read }
|
381
394
|
ensure
|
382
|
-
|
383
|
-
|
395
|
+
trmt.close
|
396
|
+
trmt.unlink
|
397
|
+
tlcl.close
|
398
|
+
tlcl.unlink
|
384
399
|
end
|
385
400
|
df
|
386
401
|
end
|
data/lib/MrMurano/commands.rb
CHANGED
@@ -7,12 +7,14 @@ require 'MrMurano/commands/domain'
|
|
7
7
|
require 'MrMurano/commands/exportImport'
|
8
8
|
require 'MrMurano/commands/keystore'
|
9
9
|
require 'MrMurano/commands/logs'
|
10
|
+
require 'MrMurano/commands/product'
|
10
11
|
require 'MrMurano/commands/productCreate'
|
11
12
|
require 'MrMurano/commands/productDelete'
|
12
13
|
require 'MrMurano/commands/productList'
|
13
14
|
require 'MrMurano/commands/productSpec'
|
14
15
|
require 'MrMurano/commands/productWrite'
|
15
16
|
require 'MrMurano/commands/serialNumberCmds'
|
17
|
+
require 'MrMurano/commands/solution'
|
16
18
|
require 'MrMurano/commands/solutionCreate'
|
17
19
|
require 'MrMurano/commands/solutionDelete'
|
18
20
|
require 'MrMurano/commands/solutionList'
|
@@ -4,11 +4,14 @@ command 'assign list' do |c|
|
|
4
4
|
c.syntax = 'mr assign list [options]'
|
5
5
|
c.description = 'List the products that are assigned'
|
6
6
|
c.option '--idonly', 'Only return the ids'
|
7
|
+
|
7
8
|
c.action do |args, options|
|
8
9
|
sol = MrMurano::SC_Device.new
|
9
10
|
|
10
11
|
trigs = sol.showTriggers()
|
11
|
-
|
12
|
+
options.idonly = true if $cfg['business.id'].nil?
|
13
|
+
|
14
|
+
if options.idonly then
|
12
15
|
say trigs.join(' ')
|
13
16
|
else
|
14
17
|
acc = MrMurano::Account.new
|
@@ -17,8 +20,8 @@ command 'assign list' do |c|
|
|
17
20
|
if products.empty? then
|
18
21
|
say trigs.join(' ')
|
19
22
|
else
|
20
|
-
busy = products.map{|r| [r[:label], r[:
|
21
|
-
table = Terminal::Table.new :rows => busy, :headings => ['Label', '
|
23
|
+
busy = products.map{|r| [r[:label], r[:modelId]]}
|
24
|
+
table = Terminal::Table.new :rows => busy, :headings => ['Label', 'ModelID']
|
22
25
|
say table
|
23
26
|
end
|
24
27
|
end
|
@@ -1,5 +1,19 @@
|
|
1
1
|
require 'MrMurano/Product'
|
2
2
|
|
3
|
+
command :content do |c|
|
4
|
+
c.syntax = %{mr content}
|
5
|
+
c.summary = %{About Content Area}
|
6
|
+
c.description = %{This set of commands let you interact with the content area for a product.
|
7
|
+
|
8
|
+
This is where OTA data can be stored so that devices can easily download it.
|
9
|
+
}
|
10
|
+
|
11
|
+
c.action do |args, options|
|
12
|
+
::Commander::UI.enable_paging
|
13
|
+
say MrMurano::SubCmdGroupHelp.new(c).get_help
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
3
17
|
command 'content list' do |c|
|
4
18
|
c.syntax = %{mr content list}
|
5
19
|
c.summary = %{List downloadable content for a product}
|
@@ -13,7 +27,6 @@ command 'content list' do |c|
|
|
13
27
|
prd.outf prd.list
|
14
28
|
end
|
15
29
|
end
|
16
|
-
alias_command :content, 'content list'
|
17
30
|
|
18
31
|
command 'content info' do |c|
|
19
32
|
c.syntax = %{mr content info <content id>}
|
@@ -36,6 +36,18 @@ module MrMurano
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
command :keystore do |c|
|
40
|
+
c.syntax = %{mr keystore}
|
41
|
+
c.summary = %{About Keystore}
|
42
|
+
c.description = %{The Keystore sub-commands let you interact directly with the Keystore instance
|
43
|
+
in a solution. This allows for easier debugging, being able to quickly get and
|
44
|
+
set data. As well as calling any of the other supported REDIS commands.}
|
45
|
+
c.action do |args, options|
|
46
|
+
::Commander::UI.enable_paging
|
47
|
+
say MrMurano::SubCmdGroupHelp.new(c).get_help
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
39
51
|
command 'keystore info' do |c|
|
40
52
|
c.syntax = %{mr keystore info}
|
41
53
|
c.description = %{Show info about the Keystore}
|
@@ -53,7 +65,6 @@ command 'keystore list' do |c|
|
|
53
65
|
sol.outf sol.listkeys
|
54
66
|
end
|
55
67
|
end
|
56
|
-
alias_command :keystore, 'keystore list'
|
57
68
|
|
58
69
|
command 'keystore get' do |c|
|
59
70
|
c.syntax = %{mr keystore get <key>}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'MrMurano/SubCmdGroupContext'
|
2
|
+
|
3
|
+
command :product do |c|
|
4
|
+
c.syntax = %{mr product}
|
5
|
+
c.summary = %{About Product}
|
6
|
+
c.description = %{Sub-commands for working with Products}
|
7
|
+
|
8
|
+
c.action do |args, options|
|
9
|
+
::Commander::UI.enable_paging
|
10
|
+
say MrMurano::SubCmdGroupHelp.new(c).get_help
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# vim: set ai et sw=2 ts=2 :
|
@@ -5,19 +5,42 @@ command 'product list' do |c|
|
|
5
5
|
c.syntax = %{mr product list [options]}
|
6
6
|
c.description = %{List products}
|
7
7
|
c.option '--idonly', 'Only return the ids'
|
8
|
+
c.option '--[no-]all', 'Show all fields'
|
9
|
+
c.option '-o', '--output FILE', %{Download to file instead of STDOUT}
|
8
10
|
|
9
11
|
c.action do |args, options|
|
10
12
|
acc = MrMurano::Account.new
|
11
13
|
data = acc.products
|
14
|
+
|
15
|
+
io=nil
|
16
|
+
if options.output then
|
17
|
+
io = File.open(options.output, 'w')
|
18
|
+
end
|
19
|
+
|
12
20
|
if options.idonly then
|
13
|
-
|
21
|
+
headers = [:modelId]
|
22
|
+
data = data.map{|row| [row[:modelId]]}
|
23
|
+
elsif not options.all then
|
24
|
+
headers = [:label, :modelId]
|
25
|
+
data = data.map{|r| [r[:label], r[:modelId]]}
|
14
26
|
else
|
15
|
-
|
16
|
-
|
17
|
-
say table
|
27
|
+
headers = data[0].keys
|
28
|
+
data = data.map{|r| headers.map{|h| r[h]}}
|
18
29
|
end
|
30
|
+
|
31
|
+
acc.outf(data, io) do |dd, ios|
|
32
|
+
if options.idonly then
|
33
|
+
ios.puts dd.join(' ')
|
34
|
+
else
|
35
|
+
acc.tabularize({
|
36
|
+
:headers=>headers.map{|h| h.to_s},
|
37
|
+
:rows=>dd
|
38
|
+
}, ios)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
io.close unless io.nil?
|
42
|
+
|
19
43
|
end
|
20
44
|
end
|
21
|
-
alias_command :product, 'product list'
|
22
45
|
|
23
46
|
# vim: set ai et sw=2 ts=2 :
|
@@ -4,6 +4,17 @@ require 'MrMurano/hash'
|
|
4
4
|
require 'yaml'
|
5
5
|
require 'terminal-table'
|
6
6
|
|
7
|
+
command 'product spec' do |c|
|
8
|
+
c.syntax = %{mr product spec}
|
9
|
+
c.summary = %{About Product Specs}
|
10
|
+
c.description = %{Some utility for working with prodcut specification files.}
|
11
|
+
|
12
|
+
c.action do |args, options|
|
13
|
+
::Commander::UI.enable_paging
|
14
|
+
say MrMurano::SubCmdGroupHelp.new(c).get_help
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
7
18
|
command 'product spec convert' do |c|
|
8
19
|
c.syntax = %{mr product spec convert FILE}
|
9
20
|
c.summary = %{Convert exoline spec file into Murano format}
|
@@ -72,11 +83,11 @@ command 'product spec pull' do |c|
|
|
72
83
|
end
|
73
84
|
|
74
85
|
prd.outf(ret, io) do |dd, ios|
|
75
|
-
if
|
76
|
-
ios.puts
|
86
|
+
if dd.kind_of? Array then
|
87
|
+
ios.puts dd.join(' ')
|
77
88
|
else
|
78
89
|
prd.tabularize({
|
79
|
-
:rows =>
|
90
|
+
:rows => dd[:resources].map{|r| [r[:alias], r[:format], r[:rid]]},
|
80
91
|
:headers => ['Alias', 'Format', 'RID']
|
81
92
|
}, ios)
|
82
93
|
end
|
@@ -84,7 +95,6 @@ command 'product spec pull' do |c|
|
|
84
95
|
io.close unless io.nil?
|
85
96
|
end
|
86
97
|
end
|
87
|
-
alias_command 'product spec', 'product spec pull'
|
88
98
|
alias_command 'product spec list', 'product spec pull', '--astable'
|
89
99
|
|
90
100
|
# vim: set ai et sw=2 ts=2 :
|
@@ -1,6 +1,18 @@
|
|
1
1
|
require 'MrMurano/Product'
|
2
2
|
require 'terminal-table'
|
3
3
|
|
4
|
+
command :sn do |c|
|
5
|
+
c.syntax = %{mr sn}
|
6
|
+
c.summary = %{About Serial Numbers}
|
7
|
+
c.description = %{The sn sub-commands allow for managing the identifiers (or Serial Numbers) on
|
8
|
+
a product.}
|
9
|
+
|
10
|
+
c.action do |args, options|
|
11
|
+
::Commander::UI.enable_paging
|
12
|
+
say MrMurano::SubCmdGroupHelp.new(c).get_help
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
4
16
|
command 'sn list' do |c|
|
5
17
|
c.syntax = %{mr sn list [options]}
|
6
18
|
c.summary = %{List serial numbers for a product}
|
@@ -18,7 +30,6 @@ command 'sn list' do |c|
|
|
18
30
|
say table
|
19
31
|
end
|
20
32
|
end
|
21
|
-
alias_command :sn, 'sn list'
|
22
33
|
|
23
34
|
command 'sn enable' do |c|
|
24
35
|
c.syntax = %{mr sn enable [<sn>|--file <sns>]}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'MrMurano/SubCmdGroupContext'
|
2
|
+
|
3
|
+
command :solution do |c|
|
4
|
+
c.syntax = %{mr solution}
|
5
|
+
c.summary = %{About Solution}
|
6
|
+
c.description = %{Sub-commands for working with solutions}
|
7
|
+
|
8
|
+
c.action do |args, options|
|
9
|
+
::Commander::UI.enable_paging
|
10
|
+
say MrMurano::SubCmdGroupHelp.new(c).get_help
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# vim: set ai et sw=2 ts=2 :
|
@@ -5,19 +5,41 @@ command 'solution list' do |c|
|
|
5
5
|
c.syntax = %{mr solution list [options]}
|
6
6
|
c.description = %{List solutions}
|
7
7
|
c.option '--idonly', 'Only return the ids'
|
8
|
+
c.option '--[no-]all', 'Show all fields'
|
9
|
+
c.option '-o', '--output FILE', %{Download to file instead of STDOUT}
|
8
10
|
|
9
11
|
c.action do |args, options|
|
10
12
|
acc = MrMurano::Account.new
|
11
13
|
data = acc.solutions
|
14
|
+
|
15
|
+
io=nil
|
16
|
+
if options.output then
|
17
|
+
io = File.open(options.output, 'w')
|
18
|
+
end
|
19
|
+
|
12
20
|
if options.idonly then
|
13
|
-
|
21
|
+
headers = [:apiId]
|
22
|
+
data = data.map{|row| [row[:apiId]]}
|
23
|
+
elsif not options.all then
|
24
|
+
headers = [:apiId, :domain]
|
25
|
+
data = data.map{|r| [r[:apiId], r[:domain]]}
|
14
26
|
else
|
15
|
-
|
16
|
-
|
17
|
-
|
27
|
+
headers = data[0].keys
|
28
|
+
data = data.map{|r| headers.map{|h| r[h]}}
|
29
|
+
end
|
30
|
+
|
31
|
+
acc.outf(data, io) do |dd, ios|
|
32
|
+
if options.idonly then
|
33
|
+
ios.puts dd.join(' ')
|
34
|
+
else
|
35
|
+
acc.tabularize({
|
36
|
+
:headers=>headers.map{|h| h.to_s},
|
37
|
+
:rows=>dd
|
38
|
+
}, ios)
|
39
|
+
end
|
18
40
|
end
|
41
|
+
io.close unless io.nil?
|
19
42
|
end
|
20
43
|
end
|
21
|
-
alias_command :solution, 'solution list'
|
22
44
|
|
23
45
|
# vim: set ai et sw=2 ts=2 :
|
@@ -20,7 +20,11 @@ command :status do |c|
|
|
20
20
|
|
21
21
|
def fmtr(item)
|
22
22
|
if item.has_key? :local_path then
|
23
|
-
item[:local_path].relative_path_from(Pathname.pwd()).to_s
|
23
|
+
lp = item[:local_path].relative_path_from(Pathname.pwd()).to_s
|
24
|
+
if item.has_key?(:line) and item[:line] > 0 then
|
25
|
+
return "#{lp}:#{item[:line]}"
|
26
|
+
end
|
27
|
+
lp
|
24
28
|
else
|
25
29
|
item[:synckey]
|
26
30
|
end
|
@@ -22,6 +22,19 @@ module MrMurano
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
command :timeseries do |c|
|
26
|
+
c.syntax = %{mr timeseries}
|
27
|
+
c.summary = %{About Timeseries}
|
28
|
+
c.description = %{The timeseries sub-commands let you interact directly with the Timeseries
|
29
|
+
instance in a solution. This allows for easier debugging, being able to
|
30
|
+
quickly try out different queries or write test data.}
|
31
|
+
|
32
|
+
c.action do |args, options|
|
33
|
+
::Commander::UI.enable_paging
|
34
|
+
say MrMurano::SubCmdGroupHelp.new(c).get_help
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
25
38
|
command 'timeseries query' do |c|
|
26
39
|
c.syntax = %{mr timeseries query <query string>}
|
27
40
|
c.description = %{Query the timeseries database}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'date'
|
2
2
|
require 'MrMurano/Solution-ServiceConfig'
|
3
|
+
require 'MrMurano/SubCmdGroupContext'
|
3
4
|
|
4
5
|
module MrMurano
|
5
6
|
module ServiceConfigs
|
@@ -50,6 +51,7 @@ Also, many date-time formats can be parsed and will be converted to microseconds
|
|
50
51
|
}
|
51
52
|
c.option '--when TIMESTAMP', %{When this data happened. (defaults to now)}
|
52
53
|
# TODO: add option to take data from STDIN.
|
54
|
+
c.example 'mr tsdb write hum=45 lux=12765 @sn=44', %{Write two metrics (hum and lux) with a tag (sn)}
|
53
55
|
|
54
56
|
c.action do |args, options|
|
55
57
|
sol = MrMurano::ServiceConfigs::Tsdb.new
|
@@ -80,15 +82,13 @@ Also, many date-time formats can be parsed and will be converted to microseconds
|
|
80
82
|
end
|
81
83
|
|
82
84
|
command 'tsdb query' do |c|
|
83
|
-
c.syntax = %{mr tsdb query [options] }
|
85
|
+
c.syntax = %{mr tsdb query [options] <metric>|@<tag=value> …}
|
84
86
|
c.summary = %{query data}
|
85
|
-
c.description =%{
|
86
|
-
|
87
|
-
list metrics to return
|
88
|
-
list tag=value to match
|
89
|
-
|
87
|
+
c.description =%{Query data from the TSDB.
|
90
88
|
|
91
89
|
FUNCS is a comma seperated list of the aggregate functions.
|
90
|
+
Currently: avg, min, max, count, sum. For string metrics, only count.
|
91
|
+
|
92
92
|
FILL is null, none, any integer, previous
|
93
93
|
|
94
94
|
DURATION is an integer with time unit to indicate relative time before now.
|
@@ -114,6 +114,16 @@ Also, many date-time formats can be parsed and will be converted to microseconds
|
|
114
114
|
|
115
115
|
c.option '-o', '--output FILE', %{Download to file instead of STDOUT}
|
116
116
|
|
117
|
+
c.example 'mr tsdb query hum', 'Get all hum metric entries'
|
118
|
+
c.example 'mr tsdb query hum @sn=45', 'Get all hum metric entries for tag sn=45'
|
119
|
+
c.example 'mr tsdb query hum --limit 1', 'Get just the most recent entry'
|
120
|
+
c.example 'mr tsdb query hum --relative_start 1h', 'Get last hour of hum entries'
|
121
|
+
c.example 'mr tsdb query hum --relative_start -1h', 'Get last hour of hum entries'
|
122
|
+
c.example 'mr tsdb query hum --relative_start 2h --relative_end 1h', 'Get hum entries of two hours ago, but not the last hours'
|
123
|
+
c.example 'mr tsdb query hum --sampling_size 30m', 'Get one hum entry from each 30 minute chunk of time'
|
124
|
+
c.example 'mr tsdb query hum --sampling_size 30m --aggregate avg', 'Get average hum entry from each 30 minute chunk of time'
|
125
|
+
|
126
|
+
|
117
127
|
c.action do |args, options|
|
118
128
|
sol = MrMurano::ServiceConfigs::Tsdb.new
|
119
129
|
|
@@ -124,6 +134,7 @@ Also, many date-time formats can be parsed and will be converted to microseconds
|
|
124
134
|
if arg =~ /=/ then
|
125
135
|
# a tag.
|
126
136
|
k,v = arg.split('=', 2)
|
137
|
+
k = k[1..-1] if k[0] == '@'
|
127
138
|
tags[k] = v
|
128
139
|
else
|
129
140
|
metrics << arg
|
@@ -132,6 +143,11 @@ Also, many date-time formats can be parsed and will be converted to microseconds
|
|
132
143
|
query[:tags] = tags unless tags.empty?
|
133
144
|
query[:metrics] = metrics unless metrics.empty?
|
134
145
|
|
146
|
+
# A query without any metrics is invalid. So if the user didn't provide any,
|
147
|
+
# look up all of them (well, frist however many) and use that list.
|
148
|
+
ret = sol.listMetrics
|
149
|
+
query[:metrics] = ret[:metrics]
|
150
|
+
|
135
151
|
unless options.start_time.nil? then
|
136
152
|
query[:start_time] = sol.str_to_timestamp(options.start_time)
|
137
153
|
end
|
@@ -199,4 +215,17 @@ command 'tsdb list metrics' do |c|
|
|
199
215
|
end
|
200
216
|
end
|
201
217
|
|
218
|
+
command :tsdb do |c|
|
219
|
+
c.syntax = %{mr tsdb}
|
220
|
+
c.summary = %{About TSDB}
|
221
|
+
c.description = %{The tsdb sub-commands let you interact directly with the TSDB instance in a
|
222
|
+
solution. This allows for easier debugging, being able to quickly try out
|
223
|
+
different queries or write test data.}
|
224
|
+
|
225
|
+
c.action do |args, options|
|
226
|
+
::Commander::UI.enable_paging
|
227
|
+
say MrMurano::SubCmdGroupHelp.new(c).get_help
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
202
231
|
# vim: set ai et sw=2 ts=2 :
|
data/lib/MrMurano/version.rb
CHANGED
@@ -8,6 +8,7 @@ RSpec.describe MrMurano::ProductResources do
|
|
8
8
|
$cfg.load
|
9
9
|
$cfg['net.host'] = 'bizapi.hosted.exosite.io'
|
10
10
|
$cfg['product.id'] = 'XYZ'
|
11
|
+
$cfg['product.spec'] = 'XYZ.yaml'
|
11
12
|
|
12
13
|
@prd = MrMurano::ProductResources.new
|
13
14
|
allow(@prd).to receive(:token).and_return("TTTTTTTTTT")
|
@@ -19,6 +20,37 @@ RSpec.describe MrMurano::ProductResources do
|
|
19
20
|
expect(uri.to_s).to eq("https://bizapi.hosted.exosite.io/api:1/product/XYZ/proxy/onep:v1/rpc/process")
|
20
21
|
end
|
21
22
|
|
23
|
+
context "location" do
|
24
|
+
it "Gets a product.spec, with location.specs" do
|
25
|
+
loc = @prd.location
|
26
|
+
expect(loc).to eq("specs/XYZ.yaml")
|
27
|
+
end
|
28
|
+
it "Gets a product.spec, without location.specs" do
|
29
|
+
$cfg.set('location.specs', nil, :defaults)
|
30
|
+
loc = @prd.location
|
31
|
+
expect(loc).to eq("XYZ.yaml")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "Gets a p-FOO.spec, with location.specs" do
|
35
|
+
$cfg['p-XYZ.spec'] = 'magical.file'
|
36
|
+
loc = @prd.location
|
37
|
+
expect(loc).to eq("specs/magical.file")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "Gets a p-FOO.spec, without location.specs" do
|
41
|
+
$cfg['p-XYZ.spec'] = 'magical.file'
|
42
|
+
$cfg.set('location.specs', nil, :defaults)
|
43
|
+
loc = @prd.location
|
44
|
+
expect(loc).to eq("magical.file")
|
45
|
+
end
|
46
|
+
|
47
|
+
it "raises when no spec name" do
|
48
|
+
$cfg['product.spec'] = nil
|
49
|
+
$cfg['product.id'] = nil
|
50
|
+
expect { @prd.location }.to raise_error("No spec file named; run `mr config prodcut.spec <specfile>`")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
22
54
|
context "do_rpc" do
|
23
55
|
# Note, do_rpc is private.
|
24
56
|
it "Accepts an object" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: MrMurano
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Conrad Tadpol Tilstra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-11-
|
11
|
+
date: 2016-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: commander
|
@@ -209,6 +209,7 @@ files:
|
|
209
209
|
- lib/MrMurano/Solution-Services.rb
|
210
210
|
- lib/MrMurano/Solution-Users.rb
|
211
211
|
- lib/MrMurano/Solution.rb
|
212
|
+
- lib/MrMurano/SubCmdGroupContext.rb
|
212
213
|
- lib/MrMurano/SyncUpDown.rb
|
213
214
|
- lib/MrMurano/commands.rb
|
214
215
|
- lib/MrMurano/commands/account.rb
|
@@ -221,12 +222,14 @@ files:
|
|
221
222
|
- lib/MrMurano/commands/exportImport.rb
|
222
223
|
- lib/MrMurano/commands/keystore.rb
|
223
224
|
- lib/MrMurano/commands/logs.rb
|
225
|
+
- lib/MrMurano/commands/product.rb
|
224
226
|
- lib/MrMurano/commands/productCreate.rb
|
225
227
|
- lib/MrMurano/commands/productDelete.rb
|
226
228
|
- lib/MrMurano/commands/productList.rb
|
227
229
|
- lib/MrMurano/commands/productSpec.rb
|
228
230
|
- lib/MrMurano/commands/productWrite.rb
|
229
231
|
- lib/MrMurano/commands/serialNumberCmds.rb
|
232
|
+
- lib/MrMurano/commands/solution.rb
|
230
233
|
- lib/MrMurano/commands/solutionCreate.rb
|
231
234
|
- lib/MrMurano/commands/solutionDelete.rb
|
232
235
|
- lib/MrMurano/commands/solutionList.rb
|