orchestrator 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
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