cosmos 5.0.3 → 5.0.4

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 (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