MrMurano 1.3.2 → 1.4.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/.rspec +2 -0
- data/Gemfile +3 -1
- data/MrMurano.gemspec +2 -1
- data/README.markdown +19 -1
- data/Rakefile +5 -4
- data/bin/mr +1 -1
- data/lib/MrMurano/Account.rb +2 -2
- data/lib/MrMurano/Product.rb +255 -0
- data/lib/MrMurano/Solution-File.rb +2 -0
- data/lib/MrMurano/Solution-Services.rb +8 -1
- data/lib/MrMurano/Solution.rb +1 -6
- data/lib/MrMurano/configCommand.rb +68 -0
- data/lib/MrMurano/configFile.rb +34 -81
- data/lib/MrMurano/contentCommand.rb +113 -0
- data/lib/MrMurano/exportImport.rb +123 -0
- data/lib/MrMurano/http.rb +23 -7
- data/lib/MrMurano/keystore.rb +1 -1
- data/lib/MrMurano/verbosing.rb +17 -0
- data/lib/MrMurano/version.rb +1 -1
- data/lib/MrMurano.rb +5 -0
- data/spec/Account-Passwords_spec.rb +121 -0
- data/spec/ProductBase_spec.rb +112 -0
- data/spec/ProductContent_spec.rb +98 -0
- data/spec/Product_spec.rb +109 -0
- data/spec/lightbulb.yaml +12 -0
- data/spec/spec_helper.rb +105 -0
- metadata +40 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5af905319869d15b61caf042c9ef16ce355013c7
|
4
|
+
data.tar.gz: 5859d5041f72f72143e1bde1412857b66ad817a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92b19376b2470b30da3196aa702958f837002bac6873f093be78dcf3c60d105c3472bdd9e9b27def0a5f1e539cced568120719dc9338ef33e7d28007371e0aff
|
7
|
+
data.tar.gz: a5d950dc9e1089851a0bdd6b3c20a27c308f8b7d7a0a9ef85d47de0037e3109fceaa97b68bfe722c04f010115085532f98c9db5d5492a0afff05614ff71ef346
|
data/.rspec
ADDED
data/Gemfile
CHANGED
data/MrMurano.gemspec
CHANGED
@@ -31,8 +31,9 @@ Gem::Specification.new do |s|
|
|
31
31
|
s.add_runtime_dependency('rainbow', '~> 2.1.0')
|
32
32
|
|
33
33
|
s.add_development_dependency('bundler', '~> 1.7.6')
|
34
|
-
s.add_development_dependency('rspec', '~> 3.2')
|
35
34
|
s.add_development_dependency('rake', '~> 10.1.1')
|
35
|
+
s.add_development_dependency('rspec', '~> 3.5')
|
36
|
+
s.add_development_dependency('webmock', '~> 2.1.0')
|
36
37
|
end
|
37
38
|
|
38
39
|
|
data/README.markdown
CHANGED
@@ -47,12 +47,30 @@ All of these can be toggled with command line options.
|
|
47
47
|
|
48
48
|
To aid with debugging, MrMurano has direct access to a solution's Keystore service.
|
49
49
|
|
50
|
-
To see all of the keys in the current solution: `mr keystore`
|
50
|
+
To see all of the keys in the current solution: `mr keystore`
|
51
51
|
|
52
52
|
### Timeseries
|
53
53
|
|
54
54
|
To aid with debugging, MrMurano has direct access to a solution's Timeseries service.
|
55
55
|
|
56
|
+
### Product Content Area
|
57
|
+
|
58
|
+
MrMurano can manage the content area for a product. This area is a place to store
|
59
|
+
files for use by devices. Typically holding firmware images for Over-The-Air
|
60
|
+
updating. Although any kind of fleet wide data that devices may need to download
|
61
|
+
can be stored here.
|
62
|
+
|
63
|
+
Once the `product.id` is set, the content for that product can be accessed with the
|
64
|
+
following commands:
|
65
|
+
```
|
66
|
+
> mr content list
|
67
|
+
> mr content upload
|
68
|
+
> mr content info
|
69
|
+
> mr content delete
|
70
|
+
> mr content download
|
71
|
+
```
|
72
|
+
|
73
|
+
Call them with `--help` for details.
|
56
74
|
|
57
75
|
### Sub-directories
|
58
76
|
|
data/Rakefile
CHANGED
@@ -5,24 +5,25 @@ task :default => [:test]
|
|
5
5
|
# TODO: figure out better way to test.
|
6
6
|
desc "Install gem in user dir"
|
7
7
|
task :bob do
|
8
|
-
|
8
|
+
sh %{gem install --user-install pkg/MrMurano-#{Bundler::GemHelper.gemspec.version}.gem}
|
9
9
|
end
|
10
10
|
|
11
11
|
desc "Uninstall from user dir"
|
12
12
|
task :unbob do
|
13
|
-
|
13
|
+
sh %{gem uninstall --user-install pkg/MrMurano-#{Bundler::GemHelper.gemspec.version}.gem}
|
14
14
|
end
|
15
15
|
|
16
16
|
task :echo do
|
17
|
-
|
17
|
+
puts "= #{Bundler::GemHelper.gemspec.version} ="
|
18
18
|
end
|
19
19
|
|
20
20
|
task :run do
|
21
|
-
|
21
|
+
sh %{ruby -Ilib bin/mr }
|
22
22
|
end
|
23
23
|
|
24
24
|
|
25
25
|
task :test do
|
26
|
+
sh %{rspec}
|
26
27
|
end
|
27
28
|
|
28
29
|
# vim: set sw=4 ts=4 :
|
data/bin/mr
CHANGED
@@ -7,7 +7,7 @@ require 'MrMurano'
|
|
7
7
|
require 'pp'
|
8
8
|
|
9
9
|
program :version, MrMurano::VERSION
|
10
|
-
program :description, %{Manage a Solution in Exosite's Murano}
|
10
|
+
program :description, %{Manage a Solution and Product in Exosite's Murano}
|
11
11
|
|
12
12
|
global_option('-V', '--verbose', 'Be chatty') {
|
13
13
|
$cfg['tool.verbose'] = true
|
data/lib/MrMurano/Account.rb
CHANGED
@@ -87,9 +87,9 @@ module MrMurano
|
|
87
87
|
# Cannot have token call token, so cannot use workit.
|
88
88
|
uri = endPoint('token/')
|
89
89
|
request = Net::HTTP::Post.new(uri)
|
90
|
-
|
91
|
-
|
90
|
+
request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
|
92
91
|
request.content_type = 'application/json'
|
92
|
+
curldebug(request)
|
93
93
|
#request.basic_auth(username(), password())
|
94
94
|
request.body = JSON.generate(_loginInfo)
|
95
95
|
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'mime/types'
|
3
|
+
require 'csv'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
module MrMurano
|
7
|
+
class ProductBase
|
8
|
+
def initialize
|
9
|
+
@pid = $cfg['product.id']
|
10
|
+
raise "No Product ID!" if @pid.nil?
|
11
|
+
@uriparts = [:product, @pid]
|
12
|
+
end
|
13
|
+
|
14
|
+
include Http
|
15
|
+
include Verbose
|
16
|
+
|
17
|
+
def endPoint(path='')
|
18
|
+
parts = ['https:/', $cfg['net.host'], 'api:1'] + @uriparts
|
19
|
+
s = parts.map{|v| v.to_s}.join('/')
|
20
|
+
URI(s + path.to_s)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Product < ProductBase
|
25
|
+
def info
|
26
|
+
get('/info')
|
27
|
+
end
|
28
|
+
|
29
|
+
def list(offset=0, limit=50)
|
30
|
+
get("/device/?offset=#{offset}&limit=#{limit}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def enable(sn)
|
34
|
+
post("/device/#{sn.to_s}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def update(specFile)
|
38
|
+
specFile = Pathname.new(specFile) unless specFile.kind_of? Pathname
|
39
|
+
|
40
|
+
uri = endPoint('/definition')
|
41
|
+
request = Net::HTTP::Post.new(uri)
|
42
|
+
ret = nil
|
43
|
+
|
44
|
+
specFile.open do |io|
|
45
|
+
request.body_stream = io
|
46
|
+
request.content_length = specFile.size
|
47
|
+
set_def_headers(request)
|
48
|
+
request.content_type = 'text/yaml'
|
49
|
+
ret = workit(request)
|
50
|
+
end
|
51
|
+
ret
|
52
|
+
end
|
53
|
+
|
54
|
+
def write(sn, values)
|
55
|
+
post("/write/#{sn}", values)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Manage the uploadable content for products.
|
62
|
+
class ProductContent < ProductBase
|
63
|
+
def initialize
|
64
|
+
super
|
65
|
+
@uriparts << :proxy
|
66
|
+
@uriparts << :provision
|
67
|
+
@uriparts << :manage
|
68
|
+
@uriparts << :content
|
69
|
+
@uriparts << @pid
|
70
|
+
end
|
71
|
+
|
72
|
+
## List all things in content area
|
73
|
+
def list
|
74
|
+
ret = get('/')
|
75
|
+
return [] if ret.kind_of?(Hash)
|
76
|
+
ret.lines.map{|i|i.chomp}
|
77
|
+
end
|
78
|
+
|
79
|
+
## List all contents allowed for sn
|
80
|
+
def list_for(sn)
|
81
|
+
ret = get("/?sn=#{sn}")
|
82
|
+
return [] if ret.kind_of?(Hash)
|
83
|
+
ret.lines.map{|i|i.chomp}
|
84
|
+
end
|
85
|
+
|
86
|
+
## Create a new content item
|
87
|
+
def create(id, meta='', protect=false)
|
88
|
+
http_reset
|
89
|
+
data = {:id=>id, :meta=>meta}
|
90
|
+
data[:protected] = true if protect
|
91
|
+
postf('/', data)
|
92
|
+
end
|
93
|
+
|
94
|
+
## Remove Content item
|
95
|
+
def remove(id)
|
96
|
+
postf('/', {:id=>id, :delete=>true})
|
97
|
+
end
|
98
|
+
|
99
|
+
## Get info for content item
|
100
|
+
def info(id)
|
101
|
+
get("/#{id}") do |request, http|
|
102
|
+
http.request(request) do |resp|
|
103
|
+
case resp
|
104
|
+
when Net::HTTPSuccess
|
105
|
+
return CSV.parse(resp.body)
|
106
|
+
else
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
## Download data for content item
|
114
|
+
def download(id, &block)
|
115
|
+
get("/#{id}?download=true") do |request, http|
|
116
|
+
http.request(request) do |resp|
|
117
|
+
case resp
|
118
|
+
when Net::HTTPSuccess
|
119
|
+
if block_given? then
|
120
|
+
resp.read_body &block
|
121
|
+
else
|
122
|
+
resp.read_body do |chunk|
|
123
|
+
$stdout.write chunk
|
124
|
+
end
|
125
|
+
end
|
126
|
+
else
|
127
|
+
say_error "got #{resp.to_s} from #{request} #{request.uri.to_s}"
|
128
|
+
raise resp
|
129
|
+
end
|
130
|
+
end
|
131
|
+
nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
## Upload data for content item
|
136
|
+
# TODO: add support for passing in IOStream
|
137
|
+
def upload(id, path)
|
138
|
+
path = Pathname.new(path) unless path.kind_of? Pathname
|
139
|
+
|
140
|
+
mime = MIME::Types.type_for(path.to_s)[0] || MIME::Types["application/octet-stream"][0]
|
141
|
+
uri = endPoint("/#{id}")
|
142
|
+
request = Net::HTTP::Post.new(uri)
|
143
|
+
ret = nil
|
144
|
+
|
145
|
+
path.open do |io|
|
146
|
+
request.body_stream = io
|
147
|
+
set_def_headers(request)
|
148
|
+
request.content_length = path.size
|
149
|
+
request.content_type = mime.simplified
|
150
|
+
ret = workit(request)
|
151
|
+
end
|
152
|
+
ret
|
153
|
+
end
|
154
|
+
|
155
|
+
## Delete data for content item
|
156
|
+
# Note that the content item is still present and listed.
|
157
|
+
def remove_content(id)
|
158
|
+
delete("/#{id}")
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# This is not applicable to Murano. Remove?
|
165
|
+
class ProductModel < ProductBase
|
166
|
+
def initialize
|
167
|
+
super
|
168
|
+
@uriparts << :proxy
|
169
|
+
@uriparts << :provision
|
170
|
+
@uriparts << :manage
|
171
|
+
@uriparts << :model
|
172
|
+
end
|
173
|
+
|
174
|
+
# In Murano, there should only ever be one.
|
175
|
+
# AND it should be @pid
|
176
|
+
def list
|
177
|
+
get('/')
|
178
|
+
end
|
179
|
+
|
180
|
+
def info(modelID=@pid)
|
181
|
+
get("/#{modelID}")
|
182
|
+
end
|
183
|
+
|
184
|
+
def list_sn(modelID=@pid)
|
185
|
+
get("/#{modelID}/")
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
## TODO: Determine which of these are expected to be used.
|
190
|
+
class ProductSerialNumber < ProductBase
|
191
|
+
def initialize
|
192
|
+
super
|
193
|
+
@uriparts << :proxy
|
194
|
+
@uriparts << :provision
|
195
|
+
@uriparts << :manage
|
196
|
+
@uriparts << :model
|
197
|
+
@uriparts << @pid
|
198
|
+
end
|
199
|
+
|
200
|
+
def list(offset=0, limit=1000)
|
201
|
+
ret = get("/?offset=#{offset}&limit=#{limit}&status=true")
|
202
|
+
return [] if ret.kind_of?(Hash)
|
203
|
+
CSV.parse(ret)
|
204
|
+
end
|
205
|
+
|
206
|
+
def logs(sn)
|
207
|
+
get("/#{sn}?show=log") # results are empty
|
208
|
+
end
|
209
|
+
|
210
|
+
def regen(sn)
|
211
|
+
postf("/#{sn}", {:enable=>true})
|
212
|
+
end
|
213
|
+
|
214
|
+
def disable(sn)
|
215
|
+
postf("/#{sn}", {:disable=>true})
|
216
|
+
end
|
217
|
+
|
218
|
+
def activate(sn)
|
219
|
+
# TODO: Need to create a new @http for the different host. Fails otherwise
|
220
|
+
uri = URI("https://#{@pid}.m2.exosite.com/provision/activate")
|
221
|
+
request = Net::HTTP::Post.new(uri)
|
222
|
+
request.form_data = {
|
223
|
+
:vendor => @pid,
|
224
|
+
:model => @pid,
|
225
|
+
:sn => sn
|
226
|
+
}
|
227
|
+
request['User-Agent'] = "MrMurano/#{MrMurano::VERSION}"
|
228
|
+
request['authorization'] = nil
|
229
|
+
request.content_type = 'application/x-www-form-urlencoded; charset=utf-8'
|
230
|
+
workit(request)
|
231
|
+
end
|
232
|
+
|
233
|
+
def add_sn(sn, extra='')
|
234
|
+
# this does add, but what does that mean?
|
235
|
+
# Still need to call …/device/<sn> to enable.
|
236
|
+
# How long is the activation window?
|
237
|
+
postf('/', {:sn=>sn,:extra=>extra})
|
238
|
+
end
|
239
|
+
|
240
|
+
def remove_sn(sn)
|
241
|
+
postf('/', {:sn=>sn, :delete=>true})
|
242
|
+
end
|
243
|
+
|
244
|
+
def ranges
|
245
|
+
get('/?show=ranges')
|
246
|
+
end
|
247
|
+
|
248
|
+
def add_range()
|
249
|
+
post('/', {:ranges=>[ ]})
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
|
254
|
+
end
|
255
|
+
# vim: set ai et sw=2 ts=2 :
|
@@ -81,6 +81,7 @@ module MrMurano
|
|
81
81
|
file = HTTP::FormData::File.new(local.to_s, {:mime_type=>remote[:mime_type]})
|
82
82
|
form = HTTP::FormData.create(:file=>file)
|
83
83
|
req = Net::HTTP::Put.new(uri)
|
84
|
+
set_def_headers(req)
|
84
85
|
workit(req) do |request,http|
|
85
86
|
request.content_type = form.content_type
|
86
87
|
request.content_length = form.content_length
|
@@ -135,6 +136,7 @@ module MrMurano
|
|
135
136
|
sha1 << hexit(chunk)
|
136
137
|
end
|
137
138
|
end
|
139
|
+
debug "Checking #{name} (#{mime.simplified} #{sha1.hexdigest})"
|
138
140
|
|
139
141
|
{:path=>name, :mime_type=>mime.simplified, :checksum=>sha1.hexdigest}
|
140
142
|
end
|
@@ -126,8 +126,15 @@ module MrMurano
|
|
126
126
|
|
127
127
|
def list
|
128
128
|
ret = get()
|
129
|
+
# eventhandler.skiplist is a list of whitespace seperated dot-paired values.
|
130
|
+
# fe: service.event service service service.event
|
129
131
|
skiplist = ($cfg['eventhandler.skiplist'] or '').split
|
130
|
-
ret[:items].reject{|i|
|
132
|
+
ret[:items].reject { |i|
|
133
|
+
i.has_key?(:service) and i.has_key?(:event) and
|
134
|
+
( skiplist.include? i[:service] or
|
135
|
+
skiplist.include? "#{i[:service]}.#{i[:event]}"
|
136
|
+
)
|
137
|
+
}
|
131
138
|
end
|
132
139
|
|
133
140
|
def fetch(name)
|
data/lib/MrMurano/Solution.rb
CHANGED
@@ -0,0 +1,68 @@
|
|
1
|
+
|
2
|
+
command :config do |c|
|
3
|
+
c.syntax = %{mr config [options] <key> [<new value>]}
|
4
|
+
c.summary = %{Get and set options}
|
5
|
+
c.description = %{
|
6
|
+
You can get, set, or query config options with this command. All config
|
7
|
+
options are in a 'section.key' format. There is also a layer of scopes
|
8
|
+
that the keys can be saved in.
|
9
|
+
}
|
10
|
+
|
11
|
+
c.example %{See what the current combined config is}, 'mr config --dump'
|
12
|
+
c.example %{Query a value}, 'mr config solution.id'
|
13
|
+
c.example %{Set a new value; writing to the project config file}, 'mr config solution.id XXXXXXXX'
|
14
|
+
c.example %{Set a new value; writing to the private config file}, 'mr config --private solution.id XXXXXXXX'
|
15
|
+
c.example %{Set a new value; writing to the user config file}, 'mr config --user user.name my@email.address'
|
16
|
+
c.example %{Unset a value in a configfile. (lower scopes will become visible if set)},
|
17
|
+
'mr config diff.cmd --unset'
|
18
|
+
|
19
|
+
|
20
|
+
c.option '--system', 'Use only the system config file. (/etc/mrmuranorc)'
|
21
|
+
c.option '--user', 'Use only the config file in $HOME (.mrmuranorc)'
|
22
|
+
c.option '--project', 'Use only the config file in the project (.mrmuranorc)'
|
23
|
+
c.option '--private', 'Use only the private config file in the project (.mrmuranorc.private)'
|
24
|
+
c.option '--specified', 'Use only the config file from the --config option.'
|
25
|
+
|
26
|
+
c.option '--unset', 'Remove key from config file.'
|
27
|
+
c.option '--dump', 'Dump the current combined view of the config'
|
28
|
+
|
29
|
+
c.action do |args, options|
|
30
|
+
|
31
|
+
if options.dump then
|
32
|
+
puts $cfg.dump()
|
33
|
+
elsif args.count == 0 then
|
34
|
+
say_error "Need a config key"
|
35
|
+
elsif args.count == 1 and not options.unset then
|
36
|
+
options.defaults :system=>false, :user=>false, :project=>false,
|
37
|
+
:specified=>false, :private=>false
|
38
|
+
|
39
|
+
# For read, if no scopes, than all. Otherwise just those specified
|
40
|
+
scopes = []
|
41
|
+
scopes << :system if options.system
|
42
|
+
scopes << :user if options.user
|
43
|
+
scopes << :project if options.project
|
44
|
+
scopes << :private if options.private
|
45
|
+
scopes << :specified if options.specified
|
46
|
+
scopes = MrMurano::Config::CFG_SCOPES if scopes.empty?
|
47
|
+
|
48
|
+
say $cfg.get(args[0], scopes)
|
49
|
+
else
|
50
|
+
|
51
|
+
options.defaults :system=>false, :user=>false, :project=>true,
|
52
|
+
:specified=>false, :private=>false
|
53
|
+
# For write, if scope is specified, only write to that scope.
|
54
|
+
scope = :project
|
55
|
+
scope = :system if options.system
|
56
|
+
scope = :user if options.user
|
57
|
+
scope = :project if options.project
|
58
|
+
scope = :private if options.private
|
59
|
+
scope = :specified if options.specified
|
60
|
+
|
61
|
+
args[1] = nil if options.unset
|
62
|
+
$cfg.set(args[0], args[1], scope)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
# vim: set ai et sw=2 ts=2 :
|
data/lib/MrMurano/configFile.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'inifile'
|
3
|
-
require 'pp'
|
4
3
|
|
5
4
|
module MrMurano
|
6
5
|
class Config
|
@@ -37,27 +36,31 @@ module MrMurano
|
|
37
36
|
CFG_SCOPES=%w{internal specified project private user system defaults}.map{|i| i.to_sym}.freeze
|
38
37
|
CFG_FILE_NAME = '.mrmuranorc'.freeze
|
39
38
|
CFG_PRVT_NAME = '.mrmuranorc.private'.freeze
|
39
|
+
CFG_DIR_NAME = '.mrmurano'.freeze
|
40
|
+
CFG_ALTRC_NAME = '.mrmurano/config'.freeze
|
41
|
+
CFG_SYS_NAME = '/etc/mrmuranorc'.freeze
|
40
42
|
|
41
43
|
def initialize
|
42
44
|
@paths = []
|
43
45
|
@paths << ConfigFile.new(:internal, nil, IniFile.new())
|
44
46
|
# :specified --configfile FILE goes here. (see load_specific)
|
45
|
-
prjfile =
|
47
|
+
prjfile = findProjectDir()
|
46
48
|
unless prjfile.nil? then
|
47
|
-
@paths << ConfigFile.new(:private, prjfile
|
48
|
-
@paths << ConfigFile.new(:project, prjfile)
|
49
|
+
@paths << ConfigFile.new(:private, prjfile + CFG_PRVT_NAME)
|
50
|
+
@paths << ConfigFile.new(:project, prjfile + CFG_FILE_NAME)
|
49
51
|
end
|
50
52
|
@paths << ConfigFile.new(:user, Pathname.new(Dir.home) + CFG_FILE_NAME)
|
51
|
-
@paths << ConfigFile.new(:system, Pathname.new(
|
53
|
+
@paths << ConfigFile.new(:system, Pathname.new(CFG_SYS_NAME))
|
52
54
|
@paths << ConfigFile.new(:defaults, nil, IniFile.new())
|
53
55
|
|
54
56
|
|
55
57
|
set('tool.verbose', false, :defaults)
|
58
|
+
set('tool.debug', false, :defaults)
|
56
59
|
set('tool.dry', false, :defaults)
|
57
60
|
|
58
61
|
set('net.host', 'bizapi.hosted.exosite.io', :defaults)
|
59
62
|
|
60
|
-
set('location.base', prjfile
|
63
|
+
set('location.base', prjfile, :defaults) unless prjfile.nil?
|
61
64
|
set('location.files', 'files', :defaults)
|
62
65
|
set('location.endpoints', 'endpoints', :defaults)
|
63
66
|
set('location.modules', 'modules', :defaults)
|
@@ -67,27 +70,43 @@ module MrMurano
|
|
67
70
|
|
68
71
|
set('files.default_page', 'index.html', :defaults)
|
69
72
|
|
70
|
-
set('eventhandler.skiplist', 'websocket webservice', :defaults)
|
73
|
+
set('eventhandler.skiplist', 'websocket webservice device.service_call', :defaults)
|
71
74
|
|
72
75
|
set('diff.cmd', 'diff -u', :defaults)
|
73
76
|
end
|
74
77
|
|
75
|
-
|
76
|
-
#
|
77
|
-
|
78
|
+
## Find the root of this project Directory.
|
79
|
+
#
|
80
|
+
# The Project dir is the directory between PWD and HOME that has one of (in
|
81
|
+
# order of preference):
|
82
|
+
# - .mrmuranorc
|
83
|
+
# - .mrmuranorc.private
|
84
|
+
# - .mrmurano/config
|
85
|
+
# - .mrmurano/
|
86
|
+
# - .git/
|
87
|
+
def findProjectDir()
|
78
88
|
result=nil
|
89
|
+
fileNames=[CFG_FILE_NAME, CFG_PRVT_NAME, CFG_ALTRC_NAME]
|
90
|
+
dirNames=[CFG_DIR_NAME, '.git']
|
79
91
|
home = Pathname.new(Dir.home)
|
80
92
|
pwd = Pathname.new(Dir.pwd)
|
81
93
|
return nil if home == pwd
|
82
|
-
pwd.dirname.ascend do |i|
|
94
|
+
pwd.dirname.ascend do |i|
|
95
|
+
break unless result.nil?
|
83
96
|
break if i == home
|
84
|
-
|
85
|
-
|
86
|
-
|
97
|
+
fileNames.each do |f|
|
98
|
+
if (i + f).exist? then
|
99
|
+
result = i
|
100
|
+
end
|
101
|
+
end
|
102
|
+
dirNames.each do |f|
|
103
|
+
if (i + f).directory? then
|
104
|
+
result = i
|
105
|
+
end
|
87
106
|
end
|
88
107
|
end
|
89
108
|
# if nothing found, assume it will live in pwd.
|
90
|
-
result = Pathname.new(Dir.pwd)
|
109
|
+
result = Pathname.new(Dir.pwd) if result.nil?
|
91
110
|
return result
|
92
111
|
end
|
93
112
|
|
@@ -182,70 +201,4 @@ module MrMurano
|
|
182
201
|
end
|
183
202
|
end
|
184
203
|
|
185
|
-
command :config do |c|
|
186
|
-
c.syntax = %{mr config [options] <key> [<new value>]}
|
187
|
-
c.summary = %{Get and set options}
|
188
|
-
c.description = %{
|
189
|
-
You can get, set, or query config options with this command. All config
|
190
|
-
options are in a 'section.key' format. There is also a layer of scopes
|
191
|
-
that the keys can be saved in.
|
192
|
-
}
|
193
|
-
|
194
|
-
c.example %{See what the current combined config is}, 'mr config --dump'
|
195
|
-
c.example %{Query a value}, 'mr config solution.id'
|
196
|
-
c.example %{Set a new value; writing to the project config file}, 'mr config solution.id XXXXXXXX'
|
197
|
-
c.example %{Set a new value; writing to the private config file}, 'mr config --private solution.id XXXXXXXX'
|
198
|
-
c.example %{Set a new value; writing to the user config file}, 'mr config --user user.name my@email.address'
|
199
|
-
c.example %{Unset a value in a configfile. (lower scopes will become visible if set)},
|
200
|
-
'mr config diff.cmd --unset'
|
201
|
-
|
202
|
-
|
203
|
-
c.option '--system', 'Use only the system config file. (/etc/mrmuranorc)'
|
204
|
-
c.option '--user', 'Use only the config file in $HOME (.mrmuranorc)'
|
205
|
-
c.option '--project', 'Use only the config file in the project (.mrmuranorc)'
|
206
|
-
c.option '--private', 'Use only the private config file in the project (.mrmuranorc.private)'
|
207
|
-
c.option '--specified', 'Use only the config file from the --config option.'
|
208
|
-
|
209
|
-
c.option '--unset', 'Remove key from config file.'
|
210
|
-
c.option '--dump', 'Dump the current combined view of the config'
|
211
|
-
|
212
|
-
c.action do |args, options|
|
213
|
-
|
214
|
-
if options.dump then
|
215
|
-
puts $cfg.dump()
|
216
|
-
elsif args.count == 0 then
|
217
|
-
say_error "Need a config key"
|
218
|
-
elsif args.count == 1 and not options.unset then
|
219
|
-
options.defaults :system=>false, :user=>false, :project=>false,
|
220
|
-
:specified=>false, :private=>false
|
221
|
-
|
222
|
-
# For read, if no scopes, than all. Otherwise just those specified
|
223
|
-
scopes = []
|
224
|
-
scopes << :system if options.system
|
225
|
-
scopes << :user if options.user
|
226
|
-
scopes << :project if options.project
|
227
|
-
scopes << :private if options.private
|
228
|
-
scopes << :specified if options.specified
|
229
|
-
scopes = MrMurano::Config::CFG_SCOPES if scopes.empty?
|
230
|
-
|
231
|
-
say $cfg.get(args[0], scopes)
|
232
|
-
else
|
233
|
-
|
234
|
-
options.defaults :system=>false, :user=>false, :project=>true,
|
235
|
-
:specified=>false, :private=>false
|
236
|
-
# For write, if scope is specified, only write to that scope.
|
237
|
-
scope = :project
|
238
|
-
scope = :system if options.system
|
239
|
-
scope = :user if options.user
|
240
|
-
scope = :project if options.project
|
241
|
-
scope = :private if options.private
|
242
|
-
scope = :specified if options.specified
|
243
|
-
|
244
|
-
args[1] = nil if options.unset
|
245
|
-
$cfg.set(args[0], args[1], scope)
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
end
|
250
|
-
|
251
204
|
# vim: set ai et sw=2 ts=2 :
|