cosmos 5.0.3 → 5.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cosmos +1 -1
  3. data/lib/cosmos/api/api.rb +1 -25
  4. data/lib/cosmos/api/cmd_api.rb +6 -6
  5. data/lib/cosmos/api/config_api.rb +10 -4
  6. data/lib/cosmos/api/limits_api.rb +1 -1
  7. data/lib/cosmos/api/settings_api.rb +19 -7
  8. data/lib/cosmos/api/target_api.rb +2 -2
  9. data/lib/cosmos/api/tlm_api.rb +8 -8
  10. data/lib/cosmos/config/config_parser.rb +2 -2
  11. data/lib/cosmos/config/meta_config_parser.rb +1 -1
  12. data/lib/cosmos/logs/log_writer.rb +2 -2
  13. data/lib/cosmos/microservices/decom_microservice.rb +1 -1
  14. data/lib/cosmos/microservices/interface_microservice.rb +0 -1
  15. data/lib/cosmos/microservices/microservice.rb +2 -2
  16. data/lib/cosmos/microservices/reducer_microservice.rb +12 -10
  17. data/lib/cosmos/models/cvt_model.rb +6 -6
  18. data/lib/cosmos/models/gem_model.rb +2 -2
  19. data/lib/cosmos/models/info_model.rb +1 -1
  20. data/lib/cosmos/models/interface_status_model.rb +1 -1
  21. data/lib/cosmos/models/metadata_model.rb +42 -216
  22. data/lib/cosmos/models/metric_model.rb +2 -2
  23. data/lib/cosmos/models/microservice_model.rb +1 -1
  24. data/lib/cosmos/models/microservice_status_model.rb +1 -1
  25. data/lib/cosmos/models/model.rb +16 -16
  26. data/lib/cosmos/models/note_model.rb +124 -0
  27. data/lib/cosmos/models/ping_model.rb +2 -1
  28. data/lib/cosmos/models/plugin_model.rb +1 -1
  29. data/lib/cosmos/models/process_status_model.rb +1 -1
  30. data/lib/cosmos/models/scope_model.rb +9 -6
  31. data/lib/cosmos/models/settings_model.rb +55 -0
  32. data/lib/cosmos/models/sorted_model.rb +165 -0
  33. data/lib/cosmos/models/target_model.rb +20 -20
  34. data/lib/cosmos/models/tool_config_model.rb +38 -0
  35. data/lib/cosmos/models/tool_model.rb +1 -1
  36. data/lib/cosmos/models/widget_model.rb +1 -1
  37. data/lib/cosmos/operators/microservice_operator.rb +2 -1
  38. data/lib/cosmos/script/calendar.rb +26 -15
  39. data/lib/cosmos/system/system.rb +2 -1
  40. data/lib/cosmos/top_level.rb +5 -1
  41. data/lib/cosmos/topics/autonomic_topic.rb +2 -2
  42. data/lib/cosmos/topics/calendar_topic.rb +1 -1
  43. data/lib/cosmos/topics/command_decom_topic.rb +31 -1
  44. data/lib/cosmos/topics/command_topic.rb +6 -4
  45. data/lib/cosmos/topics/interface_topic.rb +8 -8
  46. data/lib/cosmos/topics/limits_event_topic.rb +5 -3
  47. data/lib/cosmos/topics/notifications_topic.rb +1 -1
  48. data/lib/cosmos/topics/router_topic.rb +9 -9
  49. data/lib/cosmos/topics/telemetry_decom_topic.rb +1 -1
  50. data/lib/cosmos/topics/telemetry_topic.rb +1 -1
  51. data/lib/cosmos/topics/timeline_topic.rb +1 -1
  52. data/lib/cosmos/topics/topic.rb +21 -16
  53. data/lib/cosmos/utilities/logger.rb +3 -3
  54. data/lib/cosmos/utilities/metric.rb +32 -26
  55. data/lib/cosmos/utilities/s3.rb +1 -1
  56. data/lib/cosmos/utilities/s3_file_cache.rb +12 -6
  57. data/lib/cosmos/utilities/store.rb +1 -0
  58. data/lib/cosmos/utilities/store_autoload.rb +27 -126
  59. data/lib/cosmos/version.rb +5 -5
  60. metadata +7 -4
  61. data/lib/cosmos/models/narrative_model.rb +0 -280
@@ -19,271 +19,97 @@
19
19
 
20
20
  # https://www.rubydoc.info/gems/redis/Redis/Commands/SortedSets
21
21
 
22
- require 'cosmos/topics/calendar_topic'
22
+ require 'cosmos/models/sorted_model'
23
23
 
24
24
  module Cosmos
25
-
26
- class MetadataError < StandardError; end
27
-
28
- class MetadataInputError < MetadataError; end
29
-
30
- class MetadataOverlapError < MetadataError; end
31
-
32
- class MetadataModel < Model
33
-
34
- CHRONICLE_TYPE = 'metadata'.freeze
35
- CURRENT_VALUE = '__current.metadata.value'.freeze
25
+ class MetadataModel < SortedModel
26
+ METADATA_TYPE = 'metadata'.freeze
36
27
  PRIMARY_KEY = '__METADATA'.freeze
37
28
 
38
29
  def self.pk(scope)
39
- return "#{scope}#{PRIMARY_KEY}"
40
- end
41
-
42
- # @return [String|nil] String of the saved json or nil if score not found under current value
43
- def self.get_current_value(target:, scope:)
44
- json = Store.hget("#{scope}#{CURRENT_VALUE}", target)
45
- return nil unless json
46
- return self.from_json(JSON.parse(json), scope: scope)
47
- end
48
-
49
- # @return [Array|nil] Array up to 100 of this model or empty array
50
- def self.get(start:, stop:, scope:, limit: 100)
51
- if start > stop
52
- raise MetadataInputError.new "start: #{start} must be before stop: #{stop}"
53
- end
54
- pk = self.pk(scope)
55
- array = Store.zrangebyscore(pk, start, stop, :limit => [0, limit])
56
- ret_array = Array.new
57
- array.each do |value|
58
- ret_array << JSON.parse(value)
59
- end
60
- return ret_array
61
- end
62
-
63
- # @return [Array<Hash>] Array up to the limit of the models (as Hash objects) stored under the primary key
64
- def self.all(scope:, limit: 100)
65
- pk = self.pk(scope)
66
- array = Store.zrange(pk, 0, -1, :limit => [0, limit])
67
- ret_array = Array.new
68
- array.each do |value|
69
- ret_array << JSON.parse(value)
70
- end
71
- return ret_array
72
- end
73
-
74
- # @return [Integer] count of the members stored under the primary key
75
- def self.count(scope:)
76
- return Store.zcard(self.pk(scope))
30
+ "#{scope}#{PRIMARY_KEY}"
77
31
  end
78
32
 
79
- # @return [String|nil] String of the saved json or nil if score not found under primary_key
80
- def self.score(score:, scope:)
81
- pk = self.pk(scope)
82
- array = Store.zrangebyscore(pk, score, score, :limit => [0, 1])
83
- array.each do |value|
84
- return JSON.parse(value)
85
- end
86
- return nil
87
- end
88
-
89
- # Remove member from a sorted set based on the score.
90
- # @return [Integer] count of the members removed
91
- def self.destroy(scope:, score:)
92
- pk = self.pk(scope)
93
- Store.zremrangebyscore(pk, score, score)
94
- end
95
-
96
- # Remove members from min to max of the sorted set.
97
- # @return [Integer] count of the members removed
98
- def self.range_destroy(scope:, min:, max:)
99
- pk = self.pk(scope)
100
- Store.zremrangebyscore(pk, min, max)
101
- end
102
-
103
- # @return [MetadataModel] Model generated from the passed JSON
104
- def self.from_json(json, scope:)
105
- json = JSON.parse(json) if String === json
106
- raise "json data is nil" if json.nil?
107
-
108
- json.transform_keys!(&:to_sym)
109
- self.new(**json, scope: scope)
110
- end
111
-
112
- attr_reader :target, :start, :color, :metadata, :type
33
+ attr_reader :color, :metadata, :type
113
34
 
114
- # @param [String] target - should be the target but can be anything
115
35
  # @param [Integer] start - time metadata is active in seconds from Epoch
116
36
  # @param [String] color - The event color
117
37
  # @param [String] metadata - Key value pair object to link to name
118
38
  # @param [String] scope - Cosmos scope to track event to
119
39
  def initialize(
120
- target:,
40
+ scope:,
121
41
  start:,
122
42
  color: nil,
123
43
  metadata:,
124
- scope:,
125
- type: CHRONICLE_TYPE,
44
+ type: METADATA_TYPE,
126
45
  updated_at: 0
127
46
  )
128
- super(MetadataModel.pk(scope), name: start.to_s, scope: scope)
129
- set_input(start: start, color: color, metadata: metadata)
130
- @target = target
131
- @type = type
132
- @updated_at = updated_at
133
- end
134
-
135
- # validate color
136
- def validate_color(color)
137
- if color.nil?
138
- color = '#%06x' % (rand * 0xffffff)
139
- end
140
- valid_color = color =~ /(#*)([0-9,a-f,A-f]{6})/
141
- if valid_color.nil?
142
- raise MetadataInputError.new "invalid color, must be in hex format, e.g. #FF0000"
143
- end
144
-
145
- color = "##{color}" unless color.start_with?('#')
146
- return color
147
- end
148
-
149
- # validate the input to the rules we have created for timelines.
150
- # - An entry's start MUST be valid.
151
- # - An entry's start MUST NOT be in the future.
152
- # - An entry's metadata MUST a hash/object.
153
- def validate_input(start:, color:, metadata:)
154
- if start.is_a?(Integer) == false
155
- raise MetadataInputError.new "failed validation input must be integer: #{start}"
156
- end
157
- now = Time.now.strftime('%s%3N').to_i
158
- if start > now
159
- raise MetadataInputError.new "start can not be in the future: #{start} > #{now}"
160
- end
161
- validate_color(color)
162
- if metadata.is_a?(Hash) == false
163
- raise MetadataInputError.new "Metadata must be a hash/object: #{metadata}"
164
- end
165
- end
166
-
167
- # Set the values of the instance, @start, @stop, @metadata...
168
- def set_input(start:, color:, metadata:)
169
- if start.is_a?(Integer) == false
170
- raise MetadataInputError.new "start input must be integer: #{start}"
171
- end
47
+ super(start: start, scope: scope, updated_at: updated_at)
172
48
  @start = start
173
49
  @color = color
174
50
  @metadata = metadata
51
+ @type = type # For the as_json, from_json round trip
175
52
  end
176
53
 
177
- # validate_time will be called on create and update this will validate
178
- # that no other chronicle event or metadata had been saved for that time.
179
- # One event or metadata per second to ensure data can be updated.
180
- #
181
- # @param [Integer] ignore_score - should be nil unless you want to ignore
182
- # a time when doing an update
183
- def validate_time(ignore_score: nil)
184
- array = Store.zrangebyscore(@primary_key, @start, @start, :limit => [0, 1])
185
- array.each do |value|
186
- entry = JSON.parse(value)
187
- if ignore_score == entry['start']
188
- next
189
- else
190
- return entry
191
- end
192
- end
193
- return nil
54
+ # Validates the instance variables: @start, @color, @metadata
55
+ def validate(update: false)
56
+ validate_start(update: update)
57
+ validate_color()
58
+ validate_metadata()
194
59
  end
195
60
 
196
- # Update the Redis hash at primary_key and set the score equal to the start Epoch time
197
- # the member is set to the JSON generated via calling as_json
198
- def create
199
- validate_input(start: @start, color: @color, metadata: @metadata)
200
- collision = validate_time()
201
- unless collision.nil?
202
- raise MetadataOverlapError.new "no chronicle can overlap, collision: #{collision}"
61
+ def validate_color()
62
+ if @color.nil?
63
+ @color = '#%06x' % (rand * 0xffffff)
203
64
  end
204
-
205
- @updated_at = Time.now.to_nsec_from_epoch
206
- Store.zadd(@primary_key, @start, JSON.generate(as_json()))
207
- update_current_value()
208
- notify(kind: 'created')
209
- end
210
-
211
- # Update the Redis hash at primary_key and remove the current activity at the current score
212
- # and update the score to the new score equal to the start Epoch time this uses a multi
213
- # to execute both the remove and create. The member via the JSON generated via calling as_json
214
- def update(start:, color:, metadata:)
215
- validate_input(start: start, color: color, metadata: metadata)
216
- old_start = @start
217
- @updated_at = Time.now.to_nsec_from_epoch
218
- set_input(start: start, color: color, metadata: metadata)
219
- # copy of create
220
- collision = validate_time(ignore_score: old_start)
221
- unless collision.nil?
222
- raise MetadataOverlapError.new "failed to update #{old_start}, no chronicles can overlap, collision: #{collision}"
65
+ unless @color =~ /(#*)([0-9,a-f,A-f]{6})/
66
+ raise SortedInputError.new "invalid color, must be in hex format, e.g. #FF0000"
223
67
  end
68
+ @color = "##{@color}" unless @color.start_with?('#')
69
+ end
224
70
 
225
- Store.multi do |multi|
226
- multi.zremrangebyscore(@primary_key, old_start, old_start)
227
- multi.zadd(@primary_key, @start, JSON.generate(as_json()))
71
+ def validate_metadata()
72
+ unless @metadata.is_a?(Hash)
73
+ raise SortedInputError.new "Metadata must be a hash/object: #{@metadata}"
228
74
  end
229
- update_current_value(old_start: old_start)
230
- notify(kind: 'updated', extra: old_start)
231
- return @start
232
75
  end
233
76
 
234
- # Update the Redis hash at primary_key and check if this metadata instance
235
- # is newer than the current instance stored in the hash. If the hash does
236
- # NOT contain an instance or this metadata instance is newer it will update
237
- # the current hash.
238
- def update_current_value(old_start: nil)
239
- update = true
240
- json = Store.hget("#{@scope}#{CURRENT_VALUE}", @target)
241
- unless json.nil?
242
- model = MetadataModel.from_json(JSON.parse(json), scope: @scope)
243
- update = model.start <= @start || model.start == old_start
244
- end
77
+ # Update the Redis hash at primary_key based on the initial passed start
78
+ # The member is set to the JSON generated via calling as_json
79
+ def create(update: false)
80
+ validate(update: update)
81
+ @updated_at = Time.now.to_nsec_from_epoch
82
+ Store.zadd(@primary_key, @start, JSON.generate(as_json()))
245
83
  if update
246
- return Store.hset("#{@scope}#{CURRENT_VALUE}", @target, JSON.generate(as_json()))
84
+ notify(kind: 'updated')
85
+ else
86
+ notify(kind: 'created')
247
87
  end
248
88
  end
249
89
 
250
- # destroy the activity from the redis database
251
- def destroy
252
- Store.zremrangebyscore(@primary_key, @start, @start)
253
- notify(kind: 'deleted')
254
- end
255
-
256
- # @return [] update the redis stream / timeline topic that something has changed
257
- def notify(kind:, extra: nil)
258
- notification = {
259
- 'data' => JSON.generate(as_json()),
260
- 'kind' => kind,
261
- 'type' => 'calendar',
262
- }
263
- notification['extra'] = extra unless extra.nil?
264
- begin
265
- CalendarTopic.write_entry(notification, scope: @scope)
266
- rescue StandardError => e
267
- raise MetadataError.new "Failed to write to stream: #{notification}, #{e}"
268
- end
90
+ # Update the Redis hash at primary_key
91
+ def update(start:, color:, metadata:)
92
+ @start = start
93
+ @color = color
94
+ @metadata = metadata
95
+ create(update: true)
269
96
  end
270
97
 
271
98
  # @return [Hash] generated from the MetadataModel
272
99
  def as_json
273
100
  return {
274
- 'target' => @target,
275
101
  'scope' => @scope,
276
- 'updated_at' => @updated_at,
277
102
  'start' => @start,
278
103
  'color' => @color,
279
104
  'metadata' => @metadata,
280
- 'type' => CHRONICLE_TYPE,
105
+ 'type' => METADATA_TYPE,
106
+ 'updated_at' => @updated_at,
281
107
  }
282
108
  end
283
109
 
284
110
  # @return [String] string view of metadata
285
111
  def to_s
286
- return "<MetadataModel t: #{@target}, s: #{@start}, c: #{@color}, m: #{@metadata}>"
112
+ return "<MetadataModel s: #{@start}, c: #{@color}, m: #{@metadata}>"
287
113
  end
288
114
  end
289
115
  end
@@ -20,7 +20,7 @@
20
20
  require 'cosmos/models/model'
21
21
 
22
22
  module Cosmos
23
- class MetricModel < Model
23
+ class MetricModel < EphemeralModel
24
24
  PRIMARY_KEY = '__cosmos__metric'.freeze
25
25
 
26
26
  # NOTE: The following three class methods are used by the ModelController
@@ -38,7 +38,7 @@ module Cosmos
38
38
  end
39
39
 
40
40
  def self.destroy(scope:, name:)
41
- Store.hdel("#{scope}#{PRIMARY_KEY}", name)
41
+ EphemeralStore.hdel("#{scope}#{PRIMARY_KEY}", name)
42
42
  end
43
43
 
44
44
  def initialize(name:, scope:, metric_name:, label_list:)
@@ -186,7 +186,7 @@ module Cosmos
186
186
  # Load microservice files
187
187
  data = File.read(filename, mode: "rb")
188
188
  Cosmos.set_working_dir(File.dirname(filename)) do
189
- data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable?
189
+ data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
190
190
  end
191
191
  rubys3_client.put_object(bucket: 'config', key: key, body: data)
192
192
  end
@@ -20,7 +20,7 @@
20
20
  require 'cosmos/models/model'
21
21
 
22
22
  module Cosmos
23
- class MicroserviceStatusModel < Model
23
+ class MicroserviceStatusModel < EphemeralModel
24
24
  PRIMARY_KEY = 'cosmos_microservice_status'
25
25
 
26
26
  attr_accessor :state
@@ -27,12 +27,16 @@ module Cosmos
27
27
  attr_accessor :plugin
28
28
  attr_accessor :scope
29
29
 
30
+ def self.store
31
+ Store
32
+ end
33
+
30
34
  # NOTE: The following three methods must be reimplemented by Model subclasses
31
35
  # without primary_key to support other class methods.
32
36
 
33
37
  # @return [Hash|nil] Hash of this model or nil if name not found under primary_key
34
38
  def self.get(primary_key, name:)
35
- json = Store.hget(primary_key, name)
39
+ json = store.hget(primary_key, name)
36
40
  if json
37
41
  return JSON.parse(json)
38
42
  else
@@ -42,12 +46,12 @@ module Cosmos
42
46
 
43
47
  # @return [Array<String>] All the names stored under the primary key
44
48
  def self.names(primary_key)
45
- Store.hkeys(primary_key).sort
49
+ store.hkeys(primary_key).sort
46
50
  end
47
51
 
48
52
  # @return [Array<Hash>] All the models (as Hash objects) stored under the primary key
49
53
  def self.all(primary_key)
50
- hash = Store.hgetall(primary_key)
54
+ hash = store.hgetall(primary_key)
51
55
  hash.each do |key, value|
52
56
  hash[key] = JSON.parse(value)
53
57
  end
@@ -117,16 +121,6 @@ module Cosmos
117
121
  raise "must be implemented by subclass"
118
122
  end
119
123
 
120
- # TODO: Not used
121
- # def self.from_config(primary_key, filename)
122
- # model = nil
123
- # parser = ConfigParser.new
124
- # parser.parse_file(filename) do |keyword, parameters|
125
- # model = self.handle_config(primary_key, parser, model, keyword, parameters)
126
- # end
127
- # model
128
- # end
129
-
130
124
  # Store the primary key and keyword arguments
131
125
  def initialize(primary_key, **kw_args)
132
126
  @primary_key = primary_key
@@ -140,7 +134,7 @@ module Cosmos
140
134
  # to the JSON generated via calling as_json
141
135
  def create(update: false, force: false)
142
136
  unless force
143
- existing = Store.hget(@primary_key, @name)
137
+ existing = self.class.store.hget(@primary_key, @name)
144
138
  if existing
145
139
  raise "#{@primary_key}:#{@name} already exists at create" unless update
146
140
  else
@@ -148,7 +142,7 @@ module Cosmos
148
142
  end
149
143
  end
150
144
  @updated_at = Time.now.to_nsec_from_epoch
151
- Store.hset(@primary_key, @name, JSON.generate(self.as_json))
145
+ self.class.store.hset(@primary_key, @name, JSON.generate(self.as_json))
152
146
  end
153
147
 
154
148
  # Alias for create(update: true)
@@ -170,7 +164,7 @@ module Cosmos
170
164
  # Delete the model from the Store
171
165
  def destroy
172
166
  undeploy()
173
- Store.hdel(@primary_key, @name)
167
+ self.class.store.hdel(@primary_key, @name)
174
168
  end
175
169
 
176
170
  # @return [Hash] JSON encoding of this model
@@ -186,4 +180,10 @@ module Cosmos
186
180
  ""
187
181
  end
188
182
  end
183
+
184
+ class EphemeralModel < Model
185
+ def self.store
186
+ EphemeralStore
187
+ end
188
+ end
189
189
  end
@@ -0,0 +1,124 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 Ball Aerospace & Technologies Corp.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # This program may also be used under the terms of a commercial or
17
+ # enterprise edition license of COSMOS if purchased from the
18
+ # copyright holder
19
+
20
+ # https://www.rubydoc.info/gems/redis/Redis/Commands/SortedSets
21
+
22
+ require 'cosmos/models/sorted_model'
23
+
24
+ module Cosmos
25
+ class NoteModel < SortedModel
26
+ NOTE_TYPE = 'note'.freeze
27
+ PRIMARY_KEY = '__NOTE'.freeze
28
+
29
+ def self.pk(scope)
30
+ "#{scope}#{PRIMARY_KEY}"
31
+ end
32
+
33
+ attr_reader :stop, :color, :description, :type
34
+
35
+ # @param [String] scope - Cosmos scope to track event to
36
+ # @param [Integer] start - start of the event in seconds from Epoch
37
+ # @param [Integer] stop - stop of the event in seconds from Epoch
38
+ # @param [String] color - The event color
39
+ # @param [String] description - What the event is about
40
+ def initialize(
41
+ scope:,
42
+ start:,
43
+ stop:,
44
+ color: nil,
45
+ description:,
46
+ type: NOTE_TYPE,
47
+ updated_at: 0
48
+ )
49
+ super(start: start, scope: scope, updated_at: updated_at)
50
+ @start = start
51
+ @stop = stop
52
+ @color = color
53
+ @description = description
54
+ @type = type # For the as_json, from_json round trip
55
+ end
56
+
57
+ # Validates the instance variables: @start, @stop, @color, @description
58
+ def validate(update: false)
59
+ validate_start(update: update)
60
+ validate_stop()
61
+ validate_color()
62
+ end
63
+
64
+ def validate_stop()
65
+ unless @stop.is_a?(Integer)
66
+ raise SortedInputError.new "stop must be integer: #{@stop}"
67
+ end
68
+ if @stop.to_i < @start
69
+ raise SortedInputError.new "stop: #{@stop} must be >= start: #{@start}"
70
+ end
71
+ @stop = @stop.to_i
72
+ end
73
+
74
+ def validate_color()
75
+ if @color.nil?
76
+ @color = '#%06x' % (rand * 0xffffff)
77
+ end
78
+ unless @color =~ /(#*)([0-9,a-f,A-f]{6})/
79
+ raise SortedInputError.new "invalid color, must be in hex format, e.g. #FF0000"
80
+ end
81
+ @color = "##{@color}" unless @color.start_with?('#')
82
+ end
83
+
84
+ # Update the Redis hash at primary_key based on the initial passed start
85
+ # The member is set to the JSON generated via calling as_json
86
+ def create(update: false)
87
+ validate(update: update)
88
+ @updated_at = Time.now.to_nsec_from_epoch
89
+ Store.zadd(@primary_key, @start, JSON.generate(as_json()))
90
+ if update
91
+ notify(kind: 'updated')
92
+ else
93
+ notify(kind: 'created')
94
+ end
95
+ end
96
+
97
+ # Update the Redis hash at primary_key
98
+ def update(start:, stop:, color:, description:)
99
+ @start = start
100
+ @stop = stop
101
+ @color = color
102
+ @description = description
103
+ create(update: true)
104
+ end
105
+
106
+ # @return [Hash] generated from the NoteModel
107
+ def as_json
108
+ return {
109
+ 'scope' => @scope,
110
+ 'start' => @start,
111
+ 'stop' => @stop,
112
+ 'color' => @color,
113
+ 'description' => @description,
114
+ 'type' => NOTE_TYPE,
115
+ 'updated_at' => @updated_at,
116
+ }
117
+ end
118
+
119
+ # @return [String] string view of NoteModel
120
+ def to_s
121
+ return "<NoteModel s: #{@start}, x: #{@stop}, c: #{@color}, d: #{@description}>"
122
+ end
123
+ end
124
+ end
@@ -24,7 +24,8 @@ module Cosmos
24
24
  # @return String ['UP' or 'DOWN']
25
25
  def self.get()
26
26
  response = Store.ping()
27
- if response
27
+ response2 = EphemeralStore.ping()
28
+ if response and response2
28
29
  return 'UP'
29
30
  else
30
31
  return 'DOWN'
@@ -125,7 +125,7 @@ module Cosmos
125
125
  gem_file_path = Cosmos::GemModel.get(temp_dir, name)
126
126
 
127
127
  # Actually install the gem now (slow)
128
- Cosmos::GemModel.install(gem_file_path)
128
+ Cosmos::GemModel.install(gem_file_path, scope: scope)
129
129
 
130
130
  # Extract gem contents
131
131
  gem_path = File.join(temp_dir, "gem")
@@ -21,7 +21,7 @@ require 'cosmos/models/model'
21
21
 
22
22
  module Cosmos
23
23
  # Stores the status about an process.
24
- class ProcessStatusModel < Model
24
+ class ProcessStatusModel < EphemeralModel
25
25
  PRIMARY_KEY = 'cosmos_process_status'
26
26
 
27
27
  attr_accessor :state
@@ -20,6 +20,7 @@
20
20
  require 'cosmos/version'
21
21
  require 'cosmos/models/model'
22
22
  require 'cosmos/models/microservice_model'
23
+ require 'cosmos/models/settings_model'
23
24
 
24
25
  module Cosmos
25
26
  class ScopeModel < Model
@@ -87,7 +88,7 @@ module Cosmos
87
88
  Logger.info "Configured microservice #{microservice_name}"
88
89
 
89
90
  # UNKNOWN CommandLog Microservice
90
- Store.initialize_streams(["#{@scope}__COMMAND__{UNKNOWN}__UNKNOWN"])
91
+ Topic.initialize_streams(["#{@scope}__COMMAND__{UNKNOWN}__UNKNOWN"])
91
92
  microservice_name = "#{@scope}__COMMANDLOG__UNKNOWN"
92
93
  microservice = MicroserviceModel.new(
93
94
  name: microservice_name,
@@ -107,7 +108,7 @@ module Cosmos
107
108
  Logger.info "Configured microservice #{microservice_name}"
108
109
 
109
110
  # UNKNOWN PacketLog Microservice
110
- Store.initialize_streams(["#{@scope}__TELEMETRY__{UNKNOWN}__UNKNOWN"])
111
+ Topic.initialize_streams(["#{@scope}__TELEMETRY__{UNKNOWN}__UNKNOWN"])
111
112
  microservice_name = "#{@scope}__PACKETLOG__UNKNOWN"
112
113
  microservice = MicroserviceModel.new(
113
114
  name: microservice_name,
@@ -139,10 +140,12 @@ module Cosmos
139
140
  end
140
141
 
141
142
  def seed_database
142
- # Set default values for items in the db that should be set
143
- # Use the "nx" (not-exists) variant of redis calls here to not overwrite things the user has already set
144
- Cosmos::Store.hsetnx('cosmos__settings', 'source_url', 'https://github.com/BallAerospace/COSMOS')
145
- Cosmos::Store.hsetnx('cosmos__settings', 'version', ENV['COSMOS_VERSION'] || COSMOS_VERSION)
143
+ setting = SettingsModel.get(name: 'source_url')
144
+ SettingsModel.set({ name: 'source_url', data: 'https://github.com/BallAerospace/COSMOS' }, scope: @scope) unless setting
145
+ end
146
+
147
+ def self.limits_set(scope:)
148
+ Store.hget("#{scope}__cosmos_system", 'limits_set').intern
146
149
  end
147
150
  end
148
151
  end