orchestrator 1.0.2 → 1.0.3

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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/orchestrator/api/dependencies_controller.rb +2 -1
  3. data/app/controllers/orchestrator/api/logs_controller.rb +37 -0
  4. data/app/controllers/orchestrator/api/systems_controller.rb +14 -12
  5. data/app/controllers/orchestrator/api/users_controller.rb +76 -0
  6. data/app/controllers/orchestrator/api/zones_controller.rb +2 -1
  7. data/app/controllers/orchestrator/api_controller.rb +2 -1
  8. data/app/controllers/orchestrator/base.rb +24 -7
  9. data/app/models/orchestrator/access_log.rb +10 -4
  10. data/app/models/orchestrator/edge_control.rb +25 -0
  11. data/app/models/user.rb +8 -0
  12. data/config/routes.rb +4 -0
  13. data/lib/orchestrator/control.rb +24 -3
  14. data/lib/orchestrator/core/mixin.rb +10 -4
  15. data/lib/orchestrator/core/module_manager.rb +3 -3
  16. data/lib/orchestrator/core/request_proxy.rb +24 -2
  17. data/lib/orchestrator/core/requests_proxy.rb +57 -3
  18. data/lib/orchestrator/core/system_proxy.rb +12 -6
  19. data/lib/orchestrator/device/processor.rb +7 -0
  20. data/lib/orchestrator/engine.rb +1 -0
  21. data/lib/orchestrator/logger.rb +2 -1
  22. data/lib/orchestrator/logic/manager.rb +2 -2
  23. data/lib/orchestrator/logic/mixin.rb +1 -1
  24. data/lib/orchestrator/remote/edge.rb +30 -0
  25. data/lib/orchestrator/remote/master.rb +150 -0
  26. data/lib/orchestrator/remote/proxy.rb +24 -0
  27. data/lib/orchestrator/status.rb +39 -4
  28. data/lib/orchestrator/system.rb +8 -0
  29. data/lib/orchestrator/utilities/constants.rb +4 -2
  30. data/lib/orchestrator/utilities/transcoder.rb +6 -2
  31. data/lib/orchestrator/version.rb +1 -1
  32. data/lib/orchestrator/websocket_manager.rb +121 -31
  33. metadata +10 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 515ea3da1b17f0247c30e837c789ef827c9b5f47
4
- data.tar.gz: b49edf4d8158eefbb656aad8400db3281f6f60ff
3
+ metadata.gz: 0ef8d250243fa583df69dd09efc6e68d126afa10
4
+ data.tar.gz: 4e2c7b6e3072bb9ba4568a80bc4382cdae5ac075
5
5
  SHA512:
6
- metadata.gz: 93ff28f254174a0aa2867eaf230fc53865d47cab028927745def54ace1e40870067f6b94e9f9e4b4c730226225270c76275d54949e288d2904c551fbb42d8ef6
7
- data.tar.gz: 91df975e0a46c5514fcc705e2c701fbf0d4ba9f9e084538b27531efd1a7b172cbed43b0c8f0c8fb6934f51fac38b44cfe00b314a7d320834543aba37686cf5e7
6
+ metadata.gz: 87a92954d832fab00b5668f536cd5a5d578df4a2cb3b7c9f7e6416734ff7888890adeb059a063980b3e67a02f1ea24fc5324dc8c910bfe3aabdf11bed09a0c82
7
+ data.tar.gz: 5022a425b4574947646acf024c04853a3482d7c8ff6ab6111c7bd0ede5229199cb806d95676ab44e3e0fc6942455591c61d872cb4dcbabdd7efbd2df35715932
@@ -3,7 +3,8 @@ module Orchestrator
3
3
  module Api
4
4
  class DependenciesController < ApiController
5
5
  respond_to :json
6
- before_action :check_admin
6
+ before_action :check_admin, except: [:index, :show]
7
+ before_action :check_support, only: [:index, :show]
7
8
  before_action :find_dependency, only: [:show, :update, :destroy, :reload]
8
9
 
9
10
 
@@ -0,0 +1,37 @@
1
+
2
+ module Orchestrator
3
+ module Api
4
+ class LogsController < ApiController
5
+ respond_to :json
6
+ before_action :doorkeeper_authorize!
7
+ before_action :check_admin
8
+
9
+
10
+ # deal with live reload filter
11
+ @@elastic ||= Elastic.new(::Orchestrator::AccessLog)
12
+
13
+
14
+ def index
15
+ query = @@elastic.query(params)
16
+ query.sort = [{
17
+ created_at: "desc"
18
+ }]
19
+
20
+ # Filter systems via user_id
21
+ if params.has_key? :user_id
22
+ user_id = params.permit(:user_id)[:user_id]
23
+ query.filter({
24
+ user_id: [user_id]
25
+ })
26
+ end
27
+
28
+ results = @@elastic.search(query) do |entry|
29
+ entry.as_json.tap do |json|
30
+ json[:systems] = ControlSystem.find_by_id(json[:systems]).as_json(only: [:id, :name]) || []
31
+ end
32
+ end
33
+ respond_with results
34
+ end
35
+ end
36
+ end
37
+ end
@@ -5,7 +5,7 @@ module Orchestrator
5
5
  respond_to :json
6
6
  # state, funcs, count and types are available to authenticated users
7
7
  before_action :check_admin, only: [:create, :update, :destroy, :remove, :start, :stop]
8
- before_action :check_support, only: [:index, :show, :exec]
8
+ before_action :check_support, only: [:index, :exec]
9
9
  before_action :find_system, only: [:show, :update, :destroy, :remove, :start, :stop]
10
10
 
11
11
 
@@ -122,8 +122,9 @@ module Orchestrator
122
122
  index = para[:index]
123
123
  mod = sys.get(para[:module].to_sym, index.nil? ? 0 : (index.to_i - 1))
124
124
  if mod
125
+ user = current_user
125
126
  mod.thread.schedule do
126
- perform_exec(mod, para)
127
+ perform_exec(mod, para, user)
127
128
  end
128
129
  throw :async
129
130
  else
@@ -251,10 +252,10 @@ module Orchestrator
251
252
  end
252
253
 
253
254
  # Called on the module thread
254
- def perform_exec(mod, para)
255
+ def perform_exec(mod, para, user)
255
256
  defer = mod.thread.defer
256
257
 
257
- req = Core::RequestProxy.new(mod.thread, mod)
258
+ req = Core::RequestProxy.new(mod.thread, mod, user)
258
259
  args = para[:args] || []
259
260
  result = req.send(para[:method].to_sym, *args)
260
261
 
@@ -268,6 +269,9 @@ module Orchestrator
268
269
  defer.resolve(result)
269
270
  end
270
271
 
272
+ respHeaders = {}
273
+ allow_cors(respHeaders)
274
+
271
275
  defer.promise.then(proc { |res|
272
276
  output = ''
273
277
  begin
@@ -277,16 +281,14 @@ module Orchestrator
277
281
  # TODO:: need a better way of dealing with this
278
282
  # ALSO in websocket manager
279
283
  end
280
- env['async.callback'].call([200, {
281
- 'Content-Length' => output.bytesize,
282
- 'Content-Type' => 'application/json'
283
- }, [output]])
284
+ respHeaders['Content-Length'] = output.bytesize
285
+ respHeaders['Content-Type'] = 'application/json'
286
+ env['async.callback'].call([200, respHeaders, [output]])
284
287
  }, proc { |err|
285
288
  output = err.message
286
- env['async.callback'].call([500, {
287
- 'Content-Length' => output.bytesize,
288
- 'Content-Type' => 'text/plain'
289
- }, [output]])
289
+ respHeaders['Content-Length'] = output.bytesize
290
+ respHeaders['Content-Type'] = 'text/plain'
291
+ env['async.callback'].call([500, respHeaders, [output]])
290
292
  })
291
293
  end
292
294
  end
@@ -0,0 +1,76 @@
1
+
2
+ module Orchestrator
3
+ module Api
4
+ class UsersController < ApiController
5
+ respond_to :json
6
+ before_action :check_authorization, only: [:update]
7
+ before_action :check_admin, only: [:index, :destroy]
8
+
9
+
10
+ before_action :doorkeeper_authorize!
11
+
12
+
13
+ # deal with live reload filter
14
+ @@elastic ||= Elastic.new(User)
15
+
16
+ # Admins can see a little more of the users data
17
+ ADMIN_DATA = User::PUBLIC_DATA.dup
18
+ ADMIN_DATA[:only] += [:support, :sys_admin, :email]
19
+
20
+
21
+ def index
22
+ query = @@elastic.query(params)
23
+ results = @@elastic.search(query) do |user|
24
+ user.as_json(ADMIN_DATA)
25
+ end
26
+ respond_with results
27
+ end
28
+
29
+ def show
30
+ user = User.find(id)
31
+
32
+ # We only want to provide limited 'public' information
33
+ respond_with user, User::PUBLIC_DATA
34
+ end
35
+
36
+ def current
37
+ respond_with current_user
38
+ end
39
+
40
+
41
+ ##
42
+ # Requests requiring authorization have already loaded the model
43
+ def update
44
+ @user.update_attributes(safe_params)
45
+ @user.save
46
+ respond_with @user
47
+ end
48
+
49
+ # TODO:: We should only ever disable users... Need to add this flag
50
+ #def destroy
51
+ # respond_with @user.delete
52
+ #end
53
+
54
+
55
+ protected
56
+
57
+
58
+ def safe_params
59
+ if current_user.sys_admin
60
+ params.require(:user).permit(:name, :email, :nickname, :sys_admin, :support)
61
+ else
62
+ params.require(:user).permit(:name, :email, :nickname)
63
+ end
64
+ end
65
+
66
+ def check_authorization
67
+ # Find will raise a 404 (not found) if there is an error
68
+ @user = User.find(id)
69
+ user = current_user
70
+
71
+ # Does the current user have permission to perform the current action
72
+ head(:forbidden) unless @user.id == user.id || user.sys_admin
73
+ end
74
+ end
75
+ end
76
+ end
@@ -3,7 +3,8 @@ module Orchestrator
3
3
  module Api
4
4
  class ZonesController < ApiController
5
5
  respond_to :json
6
- before_action :check_admin
6
+ before_action :check_admin, except: [:index, :show]
7
+ before_action :check_support, only: [:index, :show]
7
8
  before_action :find_zone, only: [:show, :update, :destroy]
8
9
 
9
10
 
@@ -1,6 +1,7 @@
1
1
 
2
2
  module Orchestrator
3
- class ApiController < ::AcaEngineBase
3
+ class ApiController < ::Orchestrator::Base
4
+
4
5
 
5
6
  protected
6
7
 
@@ -5,8 +5,8 @@ module Orchestrator
5
5
  rescue_from Couchbase::Error::NotFound, with: :entry_not_found
6
6
 
7
7
 
8
- # Add headers to allow for CORS requests to the API
9
- before_filter :allow_cors
8
+ before_action :doorkeeper_authorize!, except: :options
9
+ before_filter :allow_cors # Add headers to allow for CORS requests to the API
10
10
 
11
11
 
12
12
  # This is a preflight OPTIONS request
@@ -28,11 +28,11 @@ module Orchestrator
28
28
  COMMON_HEADERS = 'Origin, Accept, Content-Type, X-Requested-With, Authorization, X-Frame-Options'.freeze
29
29
  ONE_DAY = '1728000'.freeze
30
30
 
31
- def allow_cors
32
- headers[ALLOW_ORIGIN] = ANY_ORIGIN
33
- headers[ALLOW_METHODS] = ANY_METHOD
34
- headers[ALLOW_HEADERS] = COMMON_HEADERS
35
- headers[MAX_AGE] = ONE_DAY
31
+ def allow_cors(headerHash = headers)
32
+ headerHash[ALLOW_ORIGIN] = ANY_ORIGIN
33
+ headerHash[ALLOW_METHODS] = ANY_METHOD
34
+ headerHash[ALLOW_HEADERS] = COMMON_HEADERS
35
+ headerHash[MAX_AGE] = ONE_DAY
36
36
  end
37
37
 
38
38
 
@@ -55,5 +55,22 @@ module Orchestrator
55
55
  yield if model.save && block_given?
56
56
  respond_with :api, model
57
57
  end
58
+
59
+ # Checking if the user is an administrator
60
+ def check_admin
61
+ user = current_user
62
+ head(:forbidden) unless user && user.sys_admin
63
+ end
64
+
65
+ # Checking if the user is support personnel
66
+ def check_support
67
+ user = current_user
68
+ head(:forbidden) unless user && (user.support || user.sys_admin)
69
+ end
70
+
71
+ # current user using doorkeeper
72
+ def current_user
73
+ @current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
74
+ end
58
75
  end
59
76
  end
@@ -16,15 +16,21 @@ module Orchestrator
16
16
  attribute :notes
17
17
 
18
18
  attribute :created_at
19
- attribute :ended_at, default: lambda { Time.now.to_i }
19
+ attribute :ended_at
20
+ attribute :last_checked_at, default: 0
20
21
 
21
22
 
22
- def initialize
23
- super
24
- self.created_at = Time.now.to_i
23
+ def initialize(*args)
24
+ super(*args)
25
+
26
+ if self.created_at.nil?
27
+ self.created_at = Time.now.to_i
28
+ end
25
29
  end
26
30
 
27
31
  def save
32
+ self.last_checked_at = Time.now.to_i
33
+
28
34
  if self.persisted
29
35
  super
30
36
  else
@@ -0,0 +1,25 @@
1
+
2
+ module Orchestrator
3
+ class EdgeControl < Couchbase::Model
4
+ design_document :edge
5
+ include ::CouchbaseId::Generator
6
+
7
+
8
+ attribute :name
9
+ attribute :description
10
+ attribute :failover
11
+ attribute :timeout, default: 30
12
+ attribute :window_start # CRON string
13
+ attribute :window_length # Time in seconds
14
+ attribute :settings, default: lambda { {} }
15
+ attribute :admins, default: lambda { [] }
16
+ attribute :commit # Current commit
17
+
18
+ attribute :created_at, default: lambda { Time.now.to_i }
19
+
20
+
21
+ def online?(id)
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,8 @@
1
+
2
+ class User < Couchbase::Model
3
+ # Mostly defined in coauth
4
+
5
+ # Protected attributes
6
+ attribute :sys_admin, default: false
7
+ attribute :support, default: false
8
+ end
@@ -33,6 +33,10 @@ Orchestrator::Engine.routes.draw do
33
33
  end
34
34
  resources :groups # users define the groups they are in
35
35
  resources :zones # zones define what groups can access them
36
+ resources :users do
37
+ get 'current', on: :collection
38
+ end
39
+ resources :logs
36
40
 
37
41
  concerns :mods
38
42
  end
@@ -30,6 +30,11 @@ module Orchestrator
30
30
  @exceptions = method(:log_unhandled_exception)
31
31
 
32
32
  @ready = false
33
+ @ready_defer = @loop.defer
34
+ @ready_promise = @ready_defer.promise
35
+
36
+ # We keep track of unloaded modules so we can optimise loading them again
37
+ @unloaded = Set.new
33
38
 
34
39
  if Rails.env.production?
35
40
  logger = ::Logger.new(::Rails.root.join('log/control.log').to_s, 10, 4194304)
@@ -43,7 +48,7 @@ module Orchestrator
43
48
  end
44
49
 
45
50
 
46
- attr_reader :logger, :loop, :ready, :zones
51
+ attr_reader :logger, :loop, :ready, :ready_promise, :zones, :threads
47
52
 
48
53
 
49
54
  # Start the control reactor
@@ -109,6 +114,19 @@ module Orchestrator
109
114
  # update the module cache
110
115
  defer.promise.then do |mod_manager|
111
116
  @loaded[mod_id] = mod_manager
117
+
118
+ # Transfer any existing observers over to the new thread
119
+ if @ready && @unloaded.include?(mod_id)
120
+ @unloaded.delete(mod_id)
121
+
122
+ new_thread = thread.observer
123
+ @threads.each do |thr|
124
+ thr.observer.move(mod_id, new_thread)
125
+ end
126
+ end
127
+
128
+ # Return the manager
129
+ mod_manager
112
130
  end
113
131
  defer.promise
114
132
  }, @exceptions)
@@ -163,8 +181,10 @@ module Orchestrator
163
181
  # Stop the module gracefully
164
182
  # Then remove it from @loaded
165
183
  def unload(mod_id)
166
- stop(mod_id).then(proc {
167
- @loaded.delete(mod_id.to_sym)
184
+ mod = mod_id.to_sym
185
+ stop(mod).then(proc {
186
+ @unloaded << mod
187
+ @loaded.delete(mod)
168
188
  true # promise response
169
189
  })
170
190
  end
@@ -196,6 +216,7 @@ module Orchestrator
196
216
  # Clear the system cache (in case it has been populated at all)
197
217
  System.clear_cache
198
218
  @ready = true
219
+ @ready_defer.resolve(true)
199
220
  end
200
221
 
201
222
  def log_unhandled_exception(*args)
@@ -75,8 +75,11 @@ module Orchestrator
75
75
  end
76
76
 
77
77
  def setting(name)
78
- set = name.to_sym
79
- @__config__.setting(set)
78
+ @__config__.setting(name.to_sym)
79
+ end
80
+
81
+ def thread
82
+ @__config__.thread
80
83
  end
81
84
 
82
85
  # Updates a setting that will effect the local module only
@@ -85,8 +88,7 @@ module Orchestrator
85
88
  # @param value [String|Symbol|Numeric|Array|Hash] the setting value
86
89
  # @return [::Libuv::Q::Promise] Promise that will resolve once the setting is persisted
87
90
  def define_setting(name, value)
88
- set = name.to_sym
89
- @__config__.define_setting(set, value)
91
+ @__config__.define_setting(name.to_sym, value)
90
92
  end
91
93
 
92
94
  def wake_device(mac, ip = '<broadcast>')
@@ -95,6 +97,10 @@ module Orchestrator
95
97
  end
96
98
  end
97
99
 
100
+ def current_user
101
+ @__config__.current_user
102
+ end
103
+
98
104
  # Outputs any statistics collected on the module
99
105
  def __STATS__
100
106
  stats = {}
@@ -17,6 +17,7 @@ module Orchestrator
17
17
 
18
18
  attr_reader :thread, :settings, :instance
19
19
  attr_reader :status, :stattrak, :logger
20
+ attr_accessor :current_user
20
21
 
21
22
 
22
23
  # Should always be called on the module thread
@@ -83,9 +84,8 @@ module Orchestrator
83
84
  # NOTE:: Couchbase does support non-blocking gets although I think this is simpler
84
85
  #
85
86
  # @return [::Orchestrator::Core::SystemProxy]
86
- # @raise [Couchbase::Error::NotFound] if unable to find the system in the DB
87
87
  def get_system(name)
88
- id = ::Orchestrator::ControlSystem.bucket.get("sysname-#{name}")
88
+ id = ::Orchestrator::ControlSystem.bucket.get("sysname-#{name.downcase}", {quiet: true}) || name
89
89
  ::Orchestrator::Core::SystemProxy.new(@thread, id.to_sym, self)
90
90
  end
91
91
 
@@ -144,7 +144,7 @@ module Orchestrator
144
144
  def setting(name)
145
145
  res = @settings.settings[name]
146
146
  if res.nil?
147
- if !@settings.control_system_id.nil?
147
+ if @settings.control_system_id
148
148
  sys = System.get(@settings.control_system_id)
149
149
  res = sys.settings[name]
150
150