MuranoCLI 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/Gemfile +3 -2
- data/MuranoCLI.gemspec +2 -2
- data/README.markdown +107 -10
- data/lib/MrMurano/Product-Resources.rb +10 -0
- data/lib/MrMurano/ProjectFile.rb +7 -0
- data/lib/MrMurano/Solution-Endpoint.rb +24 -12
- data/lib/MrMurano/Solution-File.rb +17 -5
- data/lib/MrMurano/Solution-ServiceConfig.rb +7 -5
- data/lib/MrMurano/Solution-Services.rb +61 -31
- data/lib/MrMurano/SyncUpDown.rb +200 -49
- data/lib/MrMurano/commands/gb.rb +4 -3
- data/lib/MrMurano/commands/init.rb +55 -3
- data/lib/MrMurano/commands/login.rb +20 -0
- data/lib/MrMurano/commands/password.rb +12 -9
- data/lib/MrMurano/commands/status.rb +5 -1
- data/lib/MrMurano/commands.rb +1 -1
- data/lib/MrMurano/version.rb +1 -1
- data/spec/Account_spec.rb +8 -0
- data/spec/ConfigMigrate_spec.rb +3 -0
- data/spec/Config_spec.rb +16 -1
- data/spec/Solution-Endpoint_spec.rb +28 -20
- data/spec/Solution-ServiceEventHandler_spec.rb +27 -21
- data/spec/Solution-ServiceModules_spec.rb +42 -43
- data/spec/SyncUpDown_spec.rb +75 -68
- data/spec/cmd_init_spec.rb +303 -9
- data/spec/cmd_status_spec.rb +14 -12
- data/spec/spec_helper.rb +4 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06a751fa4b7c0b5cd5ee4c3ec8e5075d915b31bf
|
4
|
+
data.tar.gz: d3d7f35806143bdea0849b71b8117626dcf58727
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26f18f7661d62fda5df06fad381f0c1a81286027bf91dcd1da991973cb43489947a257dcffd9d69a4454247ac6a095390147cc993c51d180255d9376c724a152
|
7
|
+
data.tar.gz: 03829cb88d7211ddab47e1c0dc9a9ceed6a8858cf0d320e409a97a1acda999c08548c4f8615768f30a24246532f6bc94d9ef232a4f3e1ba98df71d25fcfc2b84
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -2,7 +2,7 @@ source 'http://rubygems.org'
|
|
2
2
|
|
3
3
|
#gemspec
|
4
4
|
|
5
|
-
gem 'commander', '~> 4.4.
|
5
|
+
gem 'commander', '~> 4.4.3'
|
6
6
|
gem 'certified', '1.0.0'
|
7
7
|
gem 'dotenv', '~> 2.1.1'
|
8
8
|
gem 'highline', '~> 1.7.8'
|
@@ -11,7 +11,7 @@ gem 'inifile', '~> 3.0'
|
|
11
11
|
gem 'json-schema', '~> 2.7.0'
|
12
12
|
gem 'mime-types', '~> 3.1'
|
13
13
|
gem 'mime-types-data', '~> 3.2016.0521'
|
14
|
-
gem 'terminal-table', '~> 1.
|
14
|
+
gem 'terminal-table', '~> 1.7.3'
|
15
15
|
|
16
16
|
group :test do
|
17
17
|
gem 'coderay', :require => false
|
@@ -19,6 +19,7 @@ group :test do
|
|
19
19
|
gem 'rspec', '~> 3.5'
|
20
20
|
gem 'simplecov', :require => false
|
21
21
|
gem 'webmock', '~> 2.3.0'
|
22
|
+
gem 'yard'
|
22
23
|
end
|
23
24
|
|
24
25
|
group :windows do
|
data/MuranoCLI.gemspec
CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
28
28
|
s.require_paths = ['lib']
|
29
29
|
|
30
|
-
s.add_runtime_dependency('commander', '~> 4.4.
|
30
|
+
s.add_runtime_dependency('commander', '~> 4.4.3')
|
31
31
|
s.add_runtime_dependency('certified', '1.0.0')
|
32
32
|
s.add_runtime_dependency('dotenv', '~> 2.1.1')
|
33
33
|
s.add_runtime_dependency('highline', '~> 1.7.8')
|
@@ -36,7 +36,7 @@ Gem::Specification.new do |s|
|
|
36
36
|
s.add_runtime_dependency('json-schema', '~> 2.7.0')
|
37
37
|
s.add_runtime_dependency('mime-types', '~> 3.1')
|
38
38
|
s.add_runtime_dependency('mime-types-data', '~> 3.2016.0521')
|
39
|
-
s.add_runtime_dependency('terminal-table', '~> 1.
|
39
|
+
s.add_runtime_dependency('terminal-table', '~> 1.7.3')
|
40
40
|
|
41
41
|
s.add_development_dependency('bundler', '~> 1.7.6')
|
42
42
|
s.add_development_dependency('ocra', '~> 1.3.8')
|
data/README.markdown
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
#
|
1
|
+
# Murano Command Line Interface (CLI)
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/MuranoCLI)
|
5
|
-
[](https://travis-ci.org/exosite/MuranoCLI)
|
6
6
|
[](http://inch-ci.org/github/exosite/MuranoCLI)
|
7
7
|
|
8
|
-
Do more from the command line with [Murano](https://exosite.com/platform/)
|
8
|
+
Do more from the command line with [Murano](https://exosite.com/platform/).
|
9
9
|
|
10
10
|
MuranoCLI is the command-line tool that interacts with Murano and makes different
|
11
11
|
tasks easier. MuranoCLI makes it easy to deploy code to a solution, import many
|
@@ -17,18 +17,24 @@ directory are synced up (or down) from Murano.
|
|
17
17
|
|
18
18
|
## Usage
|
19
19
|
|
20
|
-
### To start from an existing project in Murano
|
21
20
|
```
|
22
21
|
mkdir myproject
|
23
22
|
cd myproject
|
24
|
-
murano
|
25
|
-
murano syncdown -V
|
23
|
+
murano init
|
26
24
|
```
|
27
25
|
|
28
|
-
|
26
|
+
Update `myproject.murano` with the info about your project.
|
27
|
+
|
28
|
+
If this is a new project, you will also need to run `murano assign set` to connect
|
29
|
+
the product and solution.
|
30
|
+
|
31
|
+
If this is an existing project, you want to run `murano syncdown -V` after
|
32
|
+
running `murano init`.
|
33
|
+
|
34
|
+
Now do stuff, see what changed: `murano status` or `murano diff`.
|
29
35
|
Then deploy with `murano syncup`
|
30
36
|
|
31
|
-
### To start a brand new project
|
37
|
+
### To start a brand new project the hard way.
|
32
38
|
There are a few steps and pieces to getting a solution with a product up and
|
33
39
|
running in Murano. Here is the list.
|
34
40
|
|
@@ -53,7 +59,7 @@ Then deploy with `murano syncup`
|
|
53
59
|
When upgrading from a 1.\* version to a 2.0, you should uninstall the old versions
|
54
60
|
first.
|
55
61
|
```
|
56
|
-
> gem uninstall MuranoCLI
|
62
|
+
> gem uninstall MuranoCLI
|
57
63
|
```
|
58
64
|
|
59
65
|
And then install:
|
@@ -91,6 +97,8 @@ installing the new version.
|
|
91
97
|
|
92
98
|
## Features
|
93
99
|
|
100
|
+
### Project File
|
101
|
+
|
94
102
|
### Logs
|
95
103
|
|
96
104
|
You can monitor the log messages from your solution with the `murano logs --follow`.
|
@@ -103,6 +111,95 @@ MuranoCLI does a few things to make your log output easier to follow.
|
|
103
111
|
|
104
112
|
All of these can be toggled with command line options.
|
105
113
|
|
114
|
+
### CORS
|
115
|
+
|
116
|
+
If you are developing you UI on seperate services and you need cross-origin
|
117
|
+
resource sharing, you will need to set the
|
118
|
+
[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) options.
|
119
|
+
|
120
|
+
The current CORS options can be fetched with `murano cors`
|
121
|
+
|
122
|
+
There are three opitons for setting, the first and preferred way is to put your CORS
|
123
|
+
opitons into a file named `cors.yaml`.
|
124
|
+
|
125
|
+
Second and third are to put the CORS opitons in your project file. In the `routes`
|
126
|
+
section, add a `cors` sub-section with either the name of the file to read, or the
|
127
|
+
CORS options inline.
|
128
|
+
|
129
|
+
```yaml
|
130
|
+
routes:
|
131
|
+
cors: my_cors_file.json
|
132
|
+
```
|
133
|
+
OR:
|
134
|
+
```yaml
|
135
|
+
routes:
|
136
|
+
cors: {"origin": true}
|
137
|
+
```
|
138
|
+
|
139
|
+
Then use `murano cors set` to push these options up to your solution.
|
140
|
+
|
141
|
+
### Writing Routes (or endpoints)
|
142
|
+
|
143
|
+
All of the routes that you create in your solution are identified by their method
|
144
|
+
and path. You set this with the following line:
|
145
|
+
|
146
|
+
```lua
|
147
|
+
--#ENDPOINT METHOD PATH
|
148
|
+
```
|
149
|
+
|
150
|
+
Optionally, you can set what the expected content type is too. (If you don't set
|
151
|
+
this, the value is application/json)
|
152
|
+
|
153
|
+
```lua
|
154
|
+
--#ENDPOINT METHOD PATH CONTENT_TYPE
|
155
|
+
```
|
156
|
+
|
157
|
+
An example of a route that puts csv data:
|
158
|
+
```lua
|
159
|
+
--#ENDPOINT PUT /api/upload text/csv
|
160
|
+
```
|
161
|
+
|
162
|
+
After this header line, the script to handle the route follows. Since many routes
|
163
|
+
end up being a couple of lines or less, you can put multiple routes into a single
|
164
|
+
file.
|
165
|
+
|
166
|
+
Which looks like this:
|
167
|
+
```lua
|
168
|
+
--#ENDPOINT GET /api/somedata
|
169
|
+
return Tsdb.query(…)
|
170
|
+
|
171
|
+
--#ENDPOINT PUT /api/somedata text/csv
|
172
|
+
return myimport_module.import(request)
|
173
|
+
|
174
|
+
--#ENDPOINT DELETE /api/startover
|
175
|
+
return Tsdb.deleteAll()
|
176
|
+
```
|
177
|
+
|
178
|
+
### Writing Service Event Handlers
|
179
|
+
|
180
|
+
All of the event handlers you add to your solution are identified by which service
|
181
|
+
they are watching and which event in that service triggers the script.
|
182
|
+
|
183
|
+
This is set with the following line:
|
184
|
+
```lua
|
185
|
+
--#EVENT SERVICE EVENT
|
186
|
+
```
|
187
|
+
|
188
|
+
For example, the event handler that processes all data coming from your devices
|
189
|
+
could be:
|
190
|
+
```lua
|
191
|
+
--#EVENT device datapoint
|
192
|
+
local stamped = nil
|
193
|
+
if data.api == "record" then
|
194
|
+
stamped = tostring(data.value[1]) .. 's'
|
195
|
+
end
|
196
|
+
Tsdb.write{
|
197
|
+
tags = {sn=data.device_sn},
|
198
|
+
metrics = {[data.alias] = tonumber(data.value[2])},
|
199
|
+
ts = stamped
|
200
|
+
}
|
201
|
+
```
|
202
|
+
|
106
203
|
### MURANO_CONFIGFILE environment and Dotenv
|
107
204
|
|
108
205
|
The environment variable `MURANO_CONFIGFILE` is checked for an additional config to
|
@@ -199,7 +296,7 @@ tests work with the live Murano servers (`--tag needs_password`).
|
|
199
296
|
|
200
297
|
To use the live tests, the following environment variables need to be set:
|
201
298
|
- `MURANO_USER` : User name to log into Murano with
|
202
|
-
- `
|
299
|
+
- `MURANO_PASSWORD` : Password for that user
|
203
300
|
- `MURANO_BUSINESS` : Business id to run tests within.
|
204
301
|
|
205
302
|
A free account on Murano is sufficient for these tests.
|
@@ -11,6 +11,16 @@ module MrMurano
|
|
11
11
|
include SyncUpDown
|
12
12
|
include ProductOnePlatformRpcShim
|
13
13
|
|
14
|
+
# Resource Specific details on an Item
|
15
|
+
class ResourceItem < Item
|
16
|
+
# @return [String] Reasource Identifier, internal use only.
|
17
|
+
attr_accessor :rid
|
18
|
+
# @return [String] The name of this resource
|
19
|
+
attr_accessor :alias
|
20
|
+
# @return [String] The format of thie resource.
|
21
|
+
attr_accessor :format
|
22
|
+
end
|
23
|
+
|
14
24
|
def initialize
|
15
25
|
super
|
16
26
|
@uriparts << :proxy
|
data/lib/MrMurano/ProjectFile.rb
CHANGED
@@ -12,6 +12,8 @@ module MrMurano
|
|
12
12
|
class ProjectFile
|
13
13
|
include Verbose
|
14
14
|
|
15
|
+
attr_reader :usingProjectfile, :usingSolutionfile
|
16
|
+
|
15
17
|
# Methods that are common to various internal structs.
|
16
18
|
module PrjStructCommonMethods
|
17
19
|
## Load data from Hash into self
|
@@ -85,6 +87,8 @@ module MrMurano
|
|
85
87
|
end
|
86
88
|
|
87
89
|
def initialize()
|
90
|
+
@usingProjectfile = false
|
91
|
+
@usingSolutionfile = false
|
88
92
|
@prjFile = nil
|
89
93
|
tname = $cfg['location.base'].basename.to_s.gsub(/[^A-Za-z0-9]/, '')
|
90
94
|
@data = PrfFile.new(
|
@@ -259,6 +263,7 @@ module MrMurano
|
|
259
263
|
schema = YAML.load_file(schemaPath.to_s)
|
260
264
|
v = JSON::Validator.fully_validate(schema, data)
|
261
265
|
return v unless v.empty?
|
266
|
+
@usingProjectfile = true
|
262
267
|
|
263
268
|
@data.each_pair do |key, str|
|
264
269
|
str.load(data[key]) if data.has_key? key
|
@@ -277,6 +282,7 @@ module MrMurano
|
|
277
282
|
schema = YAML.load_file(schemaPath.to_s)
|
278
283
|
v = JSON::Validator.fully_validate(schema, data)
|
279
284
|
return v unless v.empty?
|
285
|
+
@usingSolutionfile = true
|
280
286
|
|
281
287
|
ifset(data, :default_page, @data[:assets], :default_page)
|
282
288
|
ifset(data, :file_dir, @data[:assets], :location)
|
@@ -318,6 +324,7 @@ module MrMurano
|
|
318
324
|
schema = YAML.load_file(schemaPath.to_s)
|
319
325
|
v = JSON::Validator.fully_validate(schema, data)
|
320
326
|
return v unless v.empty?
|
327
|
+
@usingSolutionfile = true
|
321
328
|
|
322
329
|
ifset(data, :default_page, @data[:assets], :default_page)
|
323
330
|
ifset(data, :assets, @data[:assets], :location)
|
@@ -7,6 +7,18 @@ require 'MrMurano/Solution'
|
|
7
7
|
module MrMurano
|
8
8
|
# …/endpoint
|
9
9
|
class Endpoint < SolutionBase
|
10
|
+
# Route Specific details on an Item
|
11
|
+
class RouteItem < Item
|
12
|
+
# @return [String] HTTP method for this endpoint
|
13
|
+
attr_accessor :method
|
14
|
+
# @return [String] path for URL maps to this endpoint
|
15
|
+
attr_accessor :path
|
16
|
+
# @return [String] Acceptable Content-Type for this endpoint
|
17
|
+
attr_accessor :content_type
|
18
|
+
# ???? What is this?
|
19
|
+
attr_accessor :use_basic_auth
|
20
|
+
end
|
21
|
+
|
10
22
|
def initialize
|
11
23
|
super
|
12
24
|
@uriparts << 'endpoint'
|
@@ -23,7 +35,7 @@ module MrMurano
|
|
23
35
|
item[:content_type] = 'application/json'
|
24
36
|
end
|
25
37
|
# XXX should this update the script header?
|
26
|
-
item
|
38
|
+
RouteItem.new(item)
|
27
39
|
end
|
28
40
|
end
|
29
41
|
|
@@ -64,20 +76,20 @@ module MrMurano
|
|
64
76
|
|
65
77
|
##
|
66
78
|
# Upload endpoint
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# @param modify
|
79
|
+
# @param local [Pathname] path to file to push
|
80
|
+
# @param remote [RouteItem] of method and endpoint path
|
81
|
+
# @param modify [Boolean] True if item exists already and this is changing it
|
70
82
|
def upload(local, remote, modify)
|
71
83
|
local = Pathname.new(local) unless local.kind_of? Pathname
|
72
84
|
raise "no file" unless local.exist?
|
73
85
|
|
74
86
|
# we assume these are small enough to slurp.
|
75
|
-
|
87
|
+
if remote.script.nil? then
|
76
88
|
script = local.read
|
77
89
|
remote[:script] = script
|
78
90
|
end
|
79
91
|
limitkeys = [:method, :path, :script, :content_type, @itemkey]
|
80
|
-
remote = remote.select{|k,v| limitkeys.include? k }
|
92
|
+
remote = remote.to_h.select{|k,v| limitkeys.include? k }
|
81
93
|
# post('', remote)
|
82
94
|
if remote.has_key? @itemkey then
|
83
95
|
put('/' + remote[@itemkey], remote) do |request, http|
|
@@ -124,12 +136,12 @@ module MrMurano
|
|
124
136
|
# header line.
|
125
137
|
cur[:line_end] = lineno unless cur.nil?
|
126
138
|
items << cur unless cur.nil?
|
127
|
-
cur =
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
139
|
+
cur = RouteItem.new(:method=>md[:method],
|
140
|
+
:path=>md[:path],
|
141
|
+
:content_type=> (md[:ctype] or 'application/json'),
|
142
|
+
:local_path=>path,
|
143
|
+
:line=>lineno,
|
144
|
+
:script=>line)
|
133
145
|
elsif not cur.nil? and not cur[:script].nil? then
|
134
146
|
cur[:script] << line
|
135
147
|
end
|
@@ -9,6 +9,15 @@ require 'MrMurano/Solution'
|
|
9
9
|
module MrMurano
|
10
10
|
# …/file
|
11
11
|
class File < SolutionBase
|
12
|
+
# File Specific details on an Item
|
13
|
+
class FileItem < Item
|
14
|
+
# @return [String] path for URL maps to this static file
|
15
|
+
attr_accessor :path
|
16
|
+
# @return [String] The MIME-Type for this content
|
17
|
+
attr_accessor :mime_type
|
18
|
+
# @return [String] Checksum for the content.
|
19
|
+
attr_accessor :checksum
|
20
|
+
end
|
12
21
|
def initialize
|
13
22
|
super
|
14
23
|
@uriparts << 'file'
|
@@ -19,7 +28,7 @@ module MrMurano
|
|
19
28
|
##
|
20
29
|
# Get a list of all of the static content
|
21
30
|
def list
|
22
|
-
get()
|
31
|
+
get().map{|i| FileItem.new(i)}
|
23
32
|
end
|
24
33
|
|
25
34
|
##
|
@@ -47,8 +56,8 @@ module MrMurano
|
|
47
56
|
##
|
48
57
|
# Delete a file
|
49
58
|
def remove(path)
|
50
|
-
|
51
|
-
delete('/'+path)
|
59
|
+
path = path[1..-1] if path[0] == '/'
|
60
|
+
delete('/'+ URI.encode_www_form_component(path))
|
52
61
|
end
|
53
62
|
|
54
63
|
def curldebug(request)
|
@@ -65,7 +74,10 @@ module MrMurano
|
|
65
74
|
def upload(local, remote, modify)
|
66
75
|
local = Pathname.new(local) unless local.kind_of? Pathname
|
67
76
|
|
68
|
-
|
77
|
+
path = remote[:path]
|
78
|
+
path = path[1..-1] if path[0] == '/'
|
79
|
+
uri = endPoint('upload/' + URI.encode_www_form_component(path))
|
80
|
+
|
69
81
|
# kludge past for a bit.
|
70
82
|
#`curl -s -H 'Authorization: token #{@token}' '#{uri.to_s}' -F file=@#{local.to_s}`
|
71
83
|
|
@@ -132,7 +144,7 @@ module MrMurano
|
|
132
144
|
end
|
133
145
|
debug "Checking #{name} (#{mime.simplified} #{sha1.hexdigest})"
|
134
146
|
|
135
|
-
|
147
|
+
FileItem.new(:path=>name, :mime_type=>mime.simplified, :checksum=>sha1.hexdigest)
|
136
148
|
end
|
137
149
|
|
138
150
|
def synckey(item)
|
@@ -94,15 +94,17 @@ module MrMurano
|
|
94
94
|
end
|
95
95
|
|
96
96
|
## Get list of call operations from a schema
|
97
|
-
def callable(id=sid)
|
97
|
+
def callable(id=sid, all=false)
|
98
98
|
scm = schema(id)
|
99
99
|
calls = []
|
100
100
|
scm[:paths].each do |path, methods|
|
101
101
|
methods.each do |method, params|
|
102
|
-
if params.kind_of?(Hash)
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
if params.kind_of?(Hash) then
|
103
|
+
call = [method]
|
104
|
+
call << path.to_s if all
|
105
|
+
call << params[:operationId]
|
106
|
+
call << (params['x-internal-use'.to_sym] or false) if all
|
107
|
+
calls << call
|
106
108
|
end
|
107
109
|
end
|
108
110
|
end
|