MrMurano 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dbf6540edd42b5a6cac68bb92b3f18f69cf0e90e
4
- data.tar.gz: 311ff88459a51b3af06604d89d5c9b58f32b849f
3
+ metadata.gz: 64252dab4d9f4551bfffef5723875d8aab4c2964
4
+ data.tar.gz: a24ed29a6a1726b9b54d75e9b34cdd6e5ef596f1
5
5
  SHA512:
6
- metadata.gz: d01eaf47965c58e51cd502e5a6268d799a5cc96d4eca6bc4930a4dbe555519114ecae0c8483c1876bfbf3bc29cde1ead1cb5a91ccf307d5580c86f0fec7df5ba
7
- data.tar.gz: 8b2b715ba8955ad9ea54c02ad9a5acbc8781f7f70129929f7a295d6b17a5bd53893934f190ef79d1803f633e679a2ccfafbd3d201bde83174ff183ec7af60dcf
6
+ metadata.gz: 85dfd850d6f21f917350de746cb94e57674346fa3d06ac21ef96243491fd7e59cec4e789809a2b8834c0b4549cd71a1802a8d0d143f12fa9b366fad9944fcd20
7
+ data.tar.gz: 5021da7e66050a1bed213c504bf7405632e5b2a3f5238b1810fb43104c66b7f658d81c42eafcfd3a52fd459dd9bc7a40bd77f9486a72c37469b7d3e67429f56a
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0
4
+ - 2.2
5
+
data/Gemfile CHANGED
@@ -10,3 +10,7 @@ gem 'inifile', '~> 3.0'
10
10
  gem 'http-form_data', '~> 1.0.1'
11
11
  gem 'rainbow', '~> 2.1.0'
12
12
 
13
+ group :test do
14
+ gem 'rake'
15
+ end
16
+
data/MrMurano.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
- require 'mrmurano/version'
3
+ require_relative 'lib/MrMurano/version.rb'
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'MrMurano'
@@ -32,7 +32,7 @@ Gem::Specification.new do |s|
32
32
 
33
33
  s.add_development_dependency('bundler', '~> 1.7.6')
34
34
  s.add_development_dependency('rspec', '~> 3.2')
35
- s.add_development_dependency('rake')
35
+ s.add_development_dependency('rake', '~> 10.1.1')
36
36
  end
37
37
 
38
38
 
data/README.markdown CHANGED
@@ -1,6 +1,7 @@
1
1
  # MrMurano
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/MrMurano.svg)](https://badge.fury.io/rb/MrMurano)
4
+ [![Build Status](https://travis-ci.org/tadpol/MrMurano.svg?branch=master)](https://travis-ci.org/tadpol/MrMurano)
4
5
 
5
6
  Do more from the command line with [Murano](https://exosite.com/platform/)
6
7
 
@@ -11,15 +12,13 @@ To start from an existing project in Murano
11
12
  mkdir myproject
12
13
  cd myproject
13
14
  mr config solution.id XXXXXX
14
- mr syncdown
15
+ mr syncdown -V
15
16
  ```
16
17
 
17
18
  Do stuff, see what changed: `mr status` or `mr diff`.
18
19
  Then deploy with `mr syncup`
19
20
 
20
21
 
21
-
22
-
23
22
  ## Install
24
23
 
25
24
  ```
@@ -32,6 +31,29 @@ Or
32
31
 
33
32
  ## Features
34
33
 
34
+ ### Logs
35
+
36
+ You can monitor the log messages from your solution with the `mr logs --follow`.
37
+ Or quickly get the last few with `mr logs`
38
+
39
+ MrMurano does a few things to make your log output easier to follow.
40
+ - Adds color to easily see where each log message starts.
41
+ - Reformats the timestamps to be in local time.
42
+ - Finds JSON blobs and pretty prints them.
43
+
44
+ All of these can be toggled with command line options.
45
+
46
+ ### Keystore
47
+
48
+ To aid with debugging, MrMurano has direct access to a solution's Keystore service.
49
+
50
+ To see all of the keys in the current solution: `mr keystore`
51
+
52
+ ### Timeseries
53
+
54
+ To aid with debugging, MrMurano has direct access to a solution's Timeseries service.
55
+
56
+
35
57
  ### Sub-directories
36
58
 
37
59
  For the endpoints, modules, and eventhandlers directories. The can contain both
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
- #task :default => []
3
+ task :default => [:test]
4
4
 
5
5
  # TODO: figure out better way to test.
6
6
  desc "Install gem in user dir"
@@ -21,5 +21,9 @@ task :run do
21
21
  sh %{ruby -Ilib bin/mr }
22
22
  end
23
23
 
24
+
25
+ task :test do
26
+ end
27
+
24
28
  # vim: set sw=4 ts=4 :
25
29
 
data/TODO.taskpaper CHANGED
@@ -1,3 +1,5 @@
1
+ Readme:
2
+ - Add more walk-thrus of common actions. (create new solution, …)
1
3
 
2
4
  Commands:
3
5
  - Status will show things have changes when they don’t.
@@ -11,28 +13,38 @@ Account:
11
13
  Endpoints:
12
14
  - Add support for multiple endpoints in one file (maybe)
13
15
  - Add directory support like in modules @done(2016-07-26)
14
-
16
+
15
17
  Files:
18
+ - Add ignore patterns to config
16
19
  - Switch to mime-types v3
17
20
  - Fix upload. @done(2016-08-01)
18
21
  - Files won't update, they always delete then add. @done(2016-07-28)
19
22
 
20
- Users:
23
+ Users and Roles:
21
24
  Much of this is stuck until we get more docs on the User/Role management
22
25
  - Figure out how to upload (create and update) user info.
23
26
  - Figure out how to add Roles to Users in the local data and upload it.
24
27
  - Fix diff for Users and Roles.
25
28
  - Have hash keys in the yaml be strings not symbols. (don't start with colon) @done(2016-07-27)
26
29
 
30
+ CORS:
31
+ - Get working with sync up/down.
32
+ - GET&PUT /cors data @done(2016-09-08)
33
+
27
34
  Product:
28
35
  - Need to add way to set the product ID on a device eventhandler. @done(2016-08-01)
29
36
 
30
37
  Config:
31
38
  - Think about adding dev,staging,prod system; how would that work?
32
39
  Would it work ok to just use the --configfile option?
40
+ I'm thinking so. To the point of dropping the :private scope.
41
+ - Add ENV support for some (or all?) options.
42
+ At least the --configfile option. (and --config option)
43
+ - Maybe add dotenv support.
33
44
 
34
45
  SolutionBase:
35
46
  - All network traffic is serialized. Make some parallel.
47
+ - JSON parse should use symbols for keys. @done(2016-09-01)
36
48
  - Add the --curl verbose option. @done(2016-08-12)
37
49
  - Rebuild how local names and paths are computed from remote items. @done(2016-07-27)
38
50
 
@@ -41,7 +53,7 @@ Bundles:
41
53
  - Work on design @done(2016-08-09)
42
54
  Thinking of something like VIM bundles. A directory of directories. Each with a
43
55
  manafest file? (maybe) A Bundle is a group of modules, endpoints, static files
44
- and the other things.
56
+ and the other things.
45
57
 
46
58
  There needs to be some layering logic added, where the bundles are stacked and
47
59
  then the top-level files are stack on top of that. This builds the final map of
data/bin/mr CHANGED
@@ -40,7 +40,7 @@ $cfg.load
40
40
  # Basic command support is:
41
41
  # - read/write config file in [Project, User, System] (all are optional)
42
42
  # - Introspection for tab completion.
43
- # - Look for tools in PATH that are +x and "mr-foo..."
43
+ # - Look for tools in PATH that are +x and "mr-foo..."
44
44
 
45
45
 
46
46
  # Look for plug-ins
data/lib/MrMurano.rb CHANGED
@@ -8,7 +8,11 @@ require 'MrMurano/Solution-File.rb'
8
8
  require 'MrMurano/Solution-Services.rb'
9
9
  require 'MrMurano/Solution-Users.rb'
10
10
  require 'MrMurano/Solution-ServiceConfig.rb'
11
+
12
+ require 'MrMurano/cors'
13
+ require 'MrMurano/keystore.rb'
11
14
  require 'MrMurano/logs.rb'
12
15
  require 'MrMurano/sync.rb'
13
16
  require 'MrMurano/status.rb'
17
+ require 'MrMurano/timeseries.rb'
14
18
  #require 'MrMurano/shelledCommand'
@@ -51,6 +51,13 @@ module MrMurano
51
51
  end
52
52
 
53
53
  class Account
54
+ def initialize
55
+ @json_opts = {
56
+ :allow_nan => true,
57
+ :symbolize_names => true,
58
+ :create_additions => false
59
+ }
60
+ end
54
61
 
55
62
 
56
63
  def endPoint(path)
@@ -91,8 +98,8 @@ module MrMurano
91
98
  response = http.request(request)
92
99
  case response
93
100
  when Net::HTTPSuccess
94
- token = JSON.parse(response.body)
95
- @token = token['token']
101
+ token = JSON.parse(response.body, @json_opts)
102
+ @token = token[:token]
96
103
  else
97
104
  say_error "No token! because: #{response}"
98
105
  @token = nil
@@ -113,7 +120,7 @@ module MrMurano
113
120
  response = http.request(request)
114
121
  case response
115
122
  when Net::HTTPSuccess
116
- busy = JSON.parse(response.body)
123
+ busy = JSON.parse(response.body, @json_opts)
117
124
  return busy
118
125
  else
119
126
  raise response
@@ -132,7 +139,7 @@ module MrMurano
132
139
  response = http.request(request)
133
140
  case response
134
141
  when Net::HTTPSuccess
135
- busy = JSON.parse(response.body)
142
+ busy = JSON.parse(response.body, @json_opts)
136
143
  return busy
137
144
  else
138
145
  raise response
@@ -151,7 +158,7 @@ module MrMurano
151
158
  response = http.request(request)
152
159
  case response
153
160
  when Net::HTTPSuccess
154
- busy = JSON.parse(response.body)
161
+ busy = JSON.parse(response.body, @json_opts)
155
162
  return busy
156
163
  else
157
164
  raise response
@@ -180,9 +187,9 @@ command :account do |c|
180
187
  if options.businesses then
181
188
  data = acc.businesses
182
189
  if options.idonly then
183
- say data.map{|row| row['bizid']}.join(' ')
190
+ say data.map{|row| row[:bizid]}.join(' ')
184
191
  else
185
- busy = data.map{|row| [row['bizid'], row['role'], row['name']]}
192
+ busy = data.map{|row| [row[:bizid], row[:role], row[:name]]}
186
193
  table = Terminal::Table.new :rows => busy, :headings => ['Biz ID', 'Role', 'Name']
187
194
  say table
188
195
  end
@@ -190,9 +197,9 @@ command :account do |c|
190
197
  elsif options.products then
191
198
  data = acc.products
192
199
  if options.idonly then
193
- say data.map{|row| row['pid']}.join(' ')
200
+ say data.map{|row| row[:pid]}.join(' ')
194
201
  else
195
- busy = data.map{|r| [r['label'], r['type'], r['pid'], r['modelId']]}
202
+ busy = data.map{|r| [r[:label], r[:type], r[:pid], r[:modelId]]}
196
203
  table = Terminal::Table.new :rows => busy, :headings => ['Label', 'Type', 'PID', 'ModelID']
197
204
  say table
198
205
  end
@@ -200,9 +207,9 @@ command :account do |c|
200
207
  elsif options.solutions then
201
208
  data = acc.solutions
202
209
  if options.idonly then
203
- say data.map{|row| row['apiId']}.join(' ')
210
+ say data.map{|row| row[:apiId]}.join(' ')
204
211
  else
205
- busy = data.map{|r| [r['apiId'], r['domain'], r['type'], r['sid']]}
212
+ busy = data.map{|r| [r[:apiId], r[:domain], r[:type], r[:sid]]}
206
213
  table = Terminal::Table.new :rows => busy, :headings => ['API ID', 'Domain', 'Type', 'SID']
207
214
  say table
208
215
  end
@@ -20,16 +20,16 @@ module MrMurano
20
20
 
21
21
  def fetch(id)
22
22
  ret = get('/' + id.to_s)
23
- aheader = ret['script'].lines.first.chomp
24
- dheader = /^--#ENDPOINT (?i:#{ret['method']}) #{ret['path']}$/
25
- rheader = %{--#ENDPOINT #{ret['method']} #{ret['path']}\n}
23
+ aheader = ret[:script].lines.first.chomp
24
+ dheader = /^--#ENDPOINT (?i:#{ret[:method]}) #{ret[:path]}$/
25
+ rheader = %{--#ENDPOINT #{ret[:method]} #{ret[:path]}\n}
26
26
  if block_given? then
27
27
  yield rheader unless dheader =~ aheader
28
- yield ret['script']
28
+ yield ret[:script]
29
29
  else
30
30
  res = ''
31
31
  res << rheader unless dheader =~ aheader
32
- res << ret['script']
32
+ res << ret[:script]
33
33
  res
34
34
  end
35
35
  end
@@ -9,30 +9,40 @@ module MrMurano
9
9
  end
10
10
 
11
11
  def list
12
- get()['items']
12
+ get()[:items]
13
13
  end
14
14
  def fetch(id)
15
15
  get('/' + id.to_s)
16
16
  end
17
17
 
18
+ def scid_for_name(name)
19
+ name = name.to_s unless name.kind_of? String
20
+ scr = list().select{|i| i[:service] == name}.first
21
+ scid = scr[:id]
22
+ end
23
+
24
+ def scid
25
+ return @scid unless @scid.nil?
26
+ @scid = scid_for_name(@serviceName)
27
+ end
28
+
29
+ # Below is Device ServiceConfig Specific. Should it be in its own class?
18
30
 
19
31
  def assignTriggers(products)
20
- scr = list().select{|i| i['service'] == 'device' or i[:service] == 'device'}.first
21
- scid = scr['id'] or scr[:id]
32
+ scid = scid_for_name('device')
22
33
 
23
- details = Hash.transform_keys_to_symbols(fetch(scid))
34
+ details = fetch(scid)
24
35
  products = [products] unless products.kind_of? Array
25
36
  details[:triggers] = {:pid=>products}
26
37
 
27
38
  put('/'+scid, details)
28
-
39
+
29
40
  end
30
41
 
31
42
  def showTriggers
32
- scr = list().select{|i| i['service'] == 'device' or i[:service] == 'device'}.first
33
- scid = scr['id'] or scr[:id]
43
+ scid = scid_for_name('device')
34
44
 
35
- details = Hash.transform_keys_to_symbols(fetch(scid))
45
+ details = fetch(scid)
36
46
 
37
47
  return [] if details[:triggers].nil?
38
48
  details[:triggers][:pid]
@@ -57,7 +67,7 @@ command :assign do |c|
57
67
  say trigs.join(' ')
58
68
  else
59
69
  acc = MrMurano::Account.new
60
- products = acc.products.map{|p| Hash.transform_keys_to_symbols(p)}
70
+ products = acc.products
61
71
  products.select!{|p| trigs.include? p[:pid] }
62
72
  busy = products.map{|r| [r[:label], r[:type], r[:pid], r[:modelId]]}
63
73
  table = Terminal::Table.new :rows => busy, :headings => ['Label', 'Type', 'PID', 'ModelID']
@@ -70,7 +80,7 @@ command :assign do |c|
70
80
  prid = $cfg['product.id']
71
81
  else
72
82
  acc = MrMurano::Account.new
73
- products = acc.products.map{|p| Hash.transform_keys_to_symbols(p)}
83
+ products = acc.products
74
84
  products.select!{|p| p[:label] == prname or p[:pid] == prname }
75
85
  prid = products.map{|p| p[:pid]}
76
86
  end
@@ -27,15 +27,15 @@ module MrMurano
27
27
 
28
28
  def list
29
29
  ret = get()
30
- ret['items']
30
+ ret[:items]
31
31
  end
32
32
 
33
33
  def fetch(name)
34
34
  ret = get('/'+name)
35
35
  if block_given? then
36
- yield ret['script']
36
+ yield ret[:script]
37
37
  else
38
- ret['script']
38
+ ret[:script]
39
39
  end
40
40
  end
41
41
 
@@ -127,20 +127,20 @@ module MrMurano
127
127
  def list
128
128
  ret = get()
129
129
  skiplist = ($cfg['eventhandler.skiplist'] or '').split
130
- ret['items'].reject{|i| i.has_key?('service') and skiplist.include? i['service'] }
130
+ ret[:items].reject{|i| i.has_key?(:service) and skiplist.include? i[:service] }
131
131
  end
132
132
 
133
133
  def fetch(name)
134
134
  ret = get('/'+name)
135
- aheader = (ret['script'].lines.first or "").chomp
136
- dheader = "--#EVENT #{ret['service']} #{ret['event']}"
135
+ aheader = (ret[:script].lines.first or "").chomp
136
+ dheader = "--#EVENT #{ret[:service]} #{ret[:event]}"
137
137
  if block_given? then
138
138
  yield dheader + "\n" if aheader != dheader
139
- yield ret['script']
139
+ yield ret[:script]
140
140
  else
141
141
  res = ''
142
142
  res << dheader + "\n" if aheader != dheader
143
- res << ret['script']
143
+ res << ret[:script]
144
144
  res
145
145
  end
146
146
  end
@@ -10,12 +10,18 @@ module MrMurano
10
10
  # This might also be a valid ProductBase.
11
11
  def initialize
12
12
  @token = Account.new.token
13
+ raise "Not logged in!" if @token.nil?
13
14
  @sid = $cfg['solution.id']
14
15
  raise "No solution!" if @sid.nil?
15
16
  @uriparts = [:solution, @sid]
16
17
  @itemkey = :id
17
18
  @locationbase = $cfg['location.base']
18
19
  @location = nil
20
+ @json_opts = {
21
+ :allow_nan => true,
22
+ :symbolize_names => true,
23
+ :create_additions => false
24
+ }
19
25
  end
20
26
 
21
27
  def verbose(msg)
@@ -70,7 +76,7 @@ module MrMurano
70
76
  when Net::HTTPSuccess
71
77
  return {} if response.body.nil?
72
78
  begin
73
- return JSON.parse(response.body)
79
+ return JSON.parse(response.body, @json_opts)
74
80
  rescue
75
81
  return response.body
76
82
  end
@@ -276,7 +282,7 @@ module MrMurano
276
282
  def syncdown(options=Commander::Command::Options.new)
277
283
  options.asdown = true
278
284
  dt = status(options)
279
- into = @locationbase + @location
285
+ into = @locationbase + @location ###
280
286
  toadd = dt[:toadd]
281
287
  todel = dt[:todel]
282
288
  tomod = dt[:tomod]
@@ -396,21 +402,6 @@ module MrMurano
396
402
 
397
403
  end
398
404
 
399
- # …/serviceconfig
400
- class ServiceConfig < SolutionBase
401
- def initialize
402
- super
403
- @uriparts << 'serviceconfig'
404
- end
405
-
406
- def list
407
- get()['items']
408
- end
409
- def fetch(id)
410
- get('/' + id.to_s)
411
- end
412
- end
413
-
414
405
  end
415
406
 
416
407
  # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,57 @@
1
+
2
+ module MrMurano
3
+ class Cors < SolutionBase
4
+ def initialize
5
+ super
6
+ @uriparts << 'cors'
7
+ @location = $cfg['location.cors']
8
+ end
9
+
10
+ def fetch()
11
+ ret = get()
12
+ ret[:cors]
13
+ end
14
+
15
+ # TODO: fill out other metheds so this could be part of sync up/down.
16
+
17
+ ##
18
+ # Upload CORS
19
+ # :local path to file to push
20
+ # :remote hash of method and endpoint path (ignored for now)
21
+ def upload(local, remote)
22
+ local = Pathname.new(local) unless local.kind_of? Pathname
23
+ raise "no file" unless local.exist?
24
+
25
+ local.open do |io|
26
+ data = YAML.load(io)
27
+ put('', data)
28
+ end
29
+ end
30
+
31
+ def tolocalpath(into, item)
32
+ into
33
+ end
34
+ end
35
+ end
36
+
37
+ command :cors do |c|
38
+ c.syntax = %{mr cors [options]}
39
+ c.description = %{Get or set the CORS for the solution.}
40
+ c.option '-f','--file FILE', String, %{File to set CORS from}
41
+
42
+ c.action do |args,options|
43
+ sol = MrMurano::Cors.new
44
+
45
+ if options.file then
46
+ #set
47
+ pp sol.upload(options.file, {})
48
+ else
49
+ # get
50
+ ret = sol.fetch()
51
+ puts ret
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,105 @@
1
+
2
+ module MrMurano
3
+ class Keystore < ServiceConfig
4
+ def initialize
5
+ super
6
+ @serviceName = 'keystore'
7
+ end
8
+
9
+ def keyinfo
10
+ ret = get("/#{scid}/call/info")
11
+ end
12
+
13
+ def listkeys
14
+ ret = get("/#{scid}/call/list")
15
+ ret[:keys]
16
+ end
17
+
18
+ def getkey(key)
19
+ ret = post("/#{scid}/call/get", {:key=>key})
20
+ ret[:value]
21
+ end
22
+
23
+ def setkey(key, value)
24
+ post("/#{scid}/call/set", { :key=>key, :value=>value })
25
+ end
26
+
27
+ def delkey(key)
28
+ post("/#{scid}/call/delete", { :key=>key})
29
+ end
30
+
31
+ def command(key, cmd, args)
32
+ post("/#{scid}/call/command", {:key=>key, :command=>cmd, :args=>args})
33
+ end
34
+
35
+ end
36
+ end
37
+
38
+ command 'keystore info' do |c|
39
+ c.syntax = %{mr keystore info}
40
+ c.description = %{Show info about the Keystore}
41
+ c.action do |args,options|
42
+ sol = MrMurano::Keystore.new
43
+ pp sol.keyinfo
44
+ end
45
+ end
46
+
47
+ command 'keystore list' do |c|
48
+ c.syntax = %{mr keystore list}
49
+ c.description = %{List all of the keys in the Keystore}
50
+ c.action do |args,options|
51
+ sol = MrMurano::Keystore.new
52
+ sol.listkeys.each do |key|
53
+ puts key
54
+ end
55
+ end
56
+ end
57
+ alias_command :keystore, 'keystore list'
58
+
59
+ command 'keystore get' do |c|
60
+ c.syntax = %{mr keystore get <key>}
61
+ c.description = %{Get the value of a key in the Keystore}
62
+ c.action do |args,options|
63
+ sol = MrMurano::Keystore.new
64
+ ret = sol.getkey(args[0])
65
+ puts ret
66
+ end
67
+ end
68
+
69
+ command 'keystore set' do |c|
70
+ c.syntax = %{mr keystore set <key> <value...>}
71
+ c.description = %{Set teh value of a key in the Keystore}
72
+ c.action do |args,options|
73
+ sol = MrMurano::Keystore.new
74
+ sol.setkey(args[0], args[1..-1].join(' '))
75
+ end
76
+ end
77
+
78
+ command 'keystore delete' do |c|
79
+ c.syntax = %{mr keystore delete <key>}
80
+ c.description = %{Delete a key from the Keystore}
81
+ c.action do |args,options|
82
+ sol = MrMurano::Keystore.new
83
+ sol.delkey(args[0])
84
+ end
85
+ end
86
+ alias_command 'keystore rm', 'keystore delete'
87
+
88
+ command 'keystore command' do |c|
89
+ c.syntax = %{mr keystore command <key> <command> <args...>}
90
+ c.description = %{Call some Redis commands in the Keystore.
91
+
92
+ Only a subset of all Redis commands is supported.
93
+ See http://docs.exosite.com/murano/services/keystore/#command for current list.
94
+ }
95
+ c.example %{mr keystore command mykey lpush myvalue}, %{Push a value onto list}
96
+ c.example %{mr keystore command mykey lpush A B C}, %{Push three values onto list}
97
+ c.example %{mr keystore command mykey lrem 0 B}, %{Remove all B values from list}
98
+ c.action do |args,options|
99
+ sol = MrMurano::Keystore.new
100
+ pp sol.command(args[0], args[1], args[2..-1])
101
+ end
102
+ end
103
+ alias_command 'keystore cmd', 'keystore command'
104
+
105
+ # vim: set ai et sw=2 ts=2 :
data/lib/MrMurano/logs.rb CHANGED
@@ -22,8 +22,8 @@ command :logs do |c|
22
22
  begin
23
23
  ret = sol.get('/logs') # TODO: ('/logs?polling=true') Currently ignored.
24
24
 
25
- if ret.kind_of?(Hash) and ret.has_key?('items') then
26
- ret['items'].reverse.each do |line|
25
+ if ret.kind_of?(Hash) and ret.has_key?(:items) then
26
+ ret[:items].reverse.each do |line|
27
27
  curtime = ""
28
28
  if line.kind_of?(String) then
29
29
 
@@ -53,7 +53,6 @@ command :logs do |c|
53
53
  out = line
54
54
 
55
55
  elsif line.kind_of?(Hash) then
56
- line = Hash.transform_keys_to_symbols(line)
57
56
  out=""
58
57
 
59
58
  out << "[#{line[:subject]}]".color(:red).background(:aliceblue)
@@ -0,0 +1,84 @@
1
+
2
+ module MrMurano
3
+ class Timeseries < ServiceConfig
4
+ def initialize
5
+ super
6
+ @serviceName = 'timeseries'
7
+ end
8
+
9
+ def query(query)
10
+ post("/#{scid}/call/query", {:q=>query})
11
+ end
12
+
13
+ def write(writestr)
14
+ post("/#{scid}/call/write", { :q=>writestr })
15
+ end
16
+
17
+ def command(cmd)
18
+ post("/#{scid}/call/command", { :q=>cmd})
19
+ end
20
+
21
+ end
22
+ end
23
+
24
+ command 'timeseries query' do |c|
25
+ c.syntax = %{mr timeseries query <query string>}
26
+ c.description = %{Query the timeseries database}
27
+ c.option '--[no-]json', %{Display results as raw json}
28
+ #c.option '--csv', %{Display results as csv}
29
+ # Should get a CSV formatter to make sure cells are properly escapped.
30
+ c.action do |args,options|
31
+ options.defalts :json=>false
32
+ sol = MrMurano::Timeseries.new
33
+ ret = sol.query args.join(' ')
34
+ if options.json then
35
+ puts ret.to_json
36
+ else
37
+ (ret[:results] or []).each do |res|
38
+ (res[:series] or []).each do |ser|
39
+ cols = ser[:columns]
40
+ table = Terminal::Table.new :title=>ser[:name], :headings=>cols, :rows=>ser[:values]
41
+ puts table
42
+ end
43
+ end
44
+ # If nothing displayed, Format wasn't what we expected, so do what?
45
+ end
46
+ end
47
+ end
48
+ alias_command :tsq, 'timeseries query'
49
+
50
+ command 'timeseries write' do |c|
51
+ c.syntax = %{mr timeseries <write string>}
52
+ c.description = %{Write data into the timeseries database}
53
+ c.option '--[no-]json', %{Display results as raw json}
54
+ c.action do |args,options|
55
+ options.defalts :json=>false
56
+ sol = MrMurano::Timeseries.new
57
+ ret = sol.write args.join(' ')
58
+ if options.json then
59
+ puts ret.to_json
60
+ else
61
+ pp ret
62
+ end
63
+ end
64
+ end
65
+ alias_command :tsw, 'timeseries write'
66
+
67
+ command 'timeseries command' do |c|
68
+ c.syntax = %{mr timeseries command <db command>}
69
+ c.description = %{Execute a non-query command in the database}
70
+ c.option '--[no-]json', %{Display results as raw json}
71
+ c.action do |args,options|
72
+ options.defalts :json=>false
73
+ sol = MrMurano::Timeseries.new
74
+ ret = sol.command args.join(' ')
75
+ if options.json then
76
+ puts ret.to_json
77
+ else
78
+ pp ret
79
+ end
80
+ end
81
+ end
82
+
83
+
84
+ # vim: set ai et sw=2 ts=2 :
@@ -1,4 +1,4 @@
1
1
  module MrMurano
2
- VERSION = '1.2.1'.freeze
2
+ VERSION = '1.3.0'.freeze
3
3
  end
4
4
 
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.2.1
4
+ version: 1.3.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-09-07 00:00:00.000000000 Z
11
+ date: 2016-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -140,16 +140,16 @@ dependencies:
140
140
  name: rake
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - '>='
143
+ - - ~>
144
144
  - !ruby/object:Gem::Version
145
- version: '0'
145
+ version: 10.1.1
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - '>='
150
+ - - ~>
151
151
  - !ruby/object:Gem::Version
152
- version: '0'
152
+ version: 10.1.1
153
153
  description: "Do more from the command line with Murano\n\n Push and pull data from
154
154
  Murano.\n Get status on what things have changed.\n See a diff of the changes
155
155
  before you push.\n "
@@ -161,6 +161,7 @@ extensions: []
161
161
  extra_rdoc_files: []
162
162
  files:
163
163
  - .gitignore
164
+ - .travis.yml
164
165
  - Gemfile
165
166
  - MrMurano.gemspec
166
167
  - README.markdown
@@ -176,11 +177,14 @@ files:
176
177
  - lib/MrMurano/Solution-Users.rb
177
178
  - lib/MrMurano/Solution.rb
178
179
  - lib/MrMurano/configFile.rb
180
+ - lib/MrMurano/cors.rb
179
181
  - lib/MrMurano/hash.rb
182
+ - lib/MrMurano/keystore.rb
180
183
  - lib/MrMurano/logs.rb
181
184
  - lib/MrMurano/shelledCommand.rb
182
185
  - lib/MrMurano/status.rb
183
186
  - lib/MrMurano/sync.rb
187
+ - lib/MrMurano/timeseries.rb
184
188
  - lib/MrMurano/version.rb
185
189
  homepage: https://github.com/tadpol/MrMurano
186
190
  licenses: