cosmos 5.0.2.pre.beta2 → 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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cosmos +1 -1
  3. data/data/config/microservice.yaml +47 -35
  4. data/data/config/plugins.yaml +3 -150
  5. data/data/config/target.yaml +70 -0
  6. data/data/config/tool.yaml +37 -31
  7. data/lib/cosmos/api/api.rb +1 -25
  8. data/lib/cosmos/api/cmd_api.rb +17 -6
  9. data/lib/cosmos/api/config_api.rb +10 -4
  10. data/lib/cosmos/api/limits_api.rb +1 -1
  11. data/lib/cosmos/api/settings_api.rb +19 -7
  12. data/lib/cosmos/api/target_api.rb +2 -2
  13. data/lib/cosmos/api/tlm_api.rb +69 -41
  14. data/lib/cosmos/config/config_parser.rb +19 -22
  15. data/lib/cosmos/config/meta_config_parser.rb +1 -1
  16. data/lib/cosmos/conversions/generic_conversion.rb +2 -2
  17. data/lib/cosmos/conversions/polynomial_conversion.rb +5 -8
  18. data/lib/cosmos/conversions/segmented_polynomial_conversion.rb +26 -9
  19. data/lib/cosmos/io/json_drb.rb +5 -1
  20. data/lib/cosmos/logs/log_writer.rb +2 -2
  21. data/lib/cosmos/microservices/cleanup_microservice.rb +28 -29
  22. data/lib/cosmos/microservices/decom_microservice.rb +1 -1
  23. data/lib/cosmos/microservices/interface_microservice.rb +0 -1
  24. data/lib/cosmos/microservices/microservice.rb +3 -3
  25. data/lib/cosmos/microservices/reducer_microservice.rb +12 -10
  26. data/lib/cosmos/models/cvt_model.rb +6 -6
  27. data/lib/cosmos/models/gem_model.rb +3 -3
  28. data/lib/cosmos/models/info_model.rb +1 -1
  29. data/lib/cosmos/models/interface_status_model.rb +1 -1
  30. data/lib/cosmos/models/metadata_model.rb +42 -216
  31. data/lib/cosmos/models/metric_model.rb +2 -2
  32. data/lib/cosmos/models/microservice_model.rb +1 -1
  33. data/lib/cosmos/models/microservice_status_model.rb +1 -1
  34. data/lib/cosmos/models/model.rb +16 -16
  35. data/lib/cosmos/models/note_model.rb +124 -0
  36. data/lib/cosmos/models/ping_model.rb +2 -1
  37. data/lib/cosmos/models/plugin_model.rb +1 -1
  38. data/lib/cosmos/models/process_status_model.rb +1 -1
  39. data/lib/cosmos/models/scope_model.rb +9 -26
  40. data/lib/cosmos/models/settings_model.rb +55 -0
  41. data/lib/cosmos/models/sorted_model.rb +165 -0
  42. data/lib/cosmos/models/target_model.rb +120 -13
  43. data/lib/cosmos/models/tool_config_model.rb +38 -0
  44. data/lib/cosmos/models/tool_model.rb +1 -1
  45. data/lib/cosmos/models/widget_model.rb +1 -1
  46. data/lib/cosmos/operators/microservice_operator.rb +2 -1
  47. data/lib/cosmos/packets/packet.rb +23 -0
  48. data/lib/cosmos/packets/packet_config.rb +2 -2
  49. data/lib/cosmos/packets/packet_item.rb +57 -0
  50. data/lib/cosmos/packets/packet_item_limits.rb +14 -2
  51. data/lib/cosmos/packets/parsers/packet_item_parser.rb +1 -1
  52. data/lib/cosmos/packets/parsers/packet_parser.rb +1 -1
  53. data/lib/cosmos/packets/parsers/xtce_parser.rb +1 -1
  54. data/lib/cosmos/packets/structure_item.rb +10 -1
  55. data/lib/cosmos/script/api_shared.rb +30 -25
  56. data/lib/cosmos/script/calendar.rb +26 -15
  57. data/lib/cosmos/script/commands.rb +5 -7
  58. data/lib/cosmos/script/script.rb +19 -39
  59. data/lib/cosmos/script/storage.rb +92 -105
  60. data/lib/cosmos/system/system.rb +2 -1
  61. data/lib/cosmos/tools/table_manager/table_item.rb +1 -1
  62. data/lib/cosmos/top_level.rb +5 -1
  63. data/lib/cosmos/topics/autonomic_topic.rb +2 -2
  64. data/lib/cosmos/topics/calendar_topic.rb +1 -1
  65. data/lib/cosmos/topics/command_decom_topic.rb +35 -1
  66. data/lib/cosmos/topics/command_topic.rb +6 -4
  67. data/lib/cosmos/topics/interface_topic.rb +8 -8
  68. data/lib/cosmos/topics/limits_event_topic.rb +5 -3
  69. data/lib/cosmos/topics/notifications_topic.rb +1 -1
  70. data/lib/cosmos/topics/router_topic.rb +9 -9
  71. data/lib/cosmos/topics/telemetry_decom_topic.rb +5 -1
  72. data/lib/cosmos/topics/telemetry_topic.rb +1 -1
  73. data/lib/cosmos/topics/timeline_topic.rb +1 -1
  74. data/lib/cosmos/topics/topic.rb +23 -8
  75. data/lib/cosmos/utilities/logger.rb +4 -3
  76. data/lib/cosmos/utilities/metric.rb +32 -26
  77. data/lib/cosmos/utilities/s3.rb +61 -0
  78. data/lib/cosmos/utilities/s3_file_cache.rb +12 -6
  79. data/lib/cosmos/utilities/store.rb +1 -0
  80. data/lib/cosmos/utilities/store_autoload.rb +25 -134
  81. data/lib/cosmos/version.rb +6 -5
  82. data/templates/plugin-template/plugin.gemspec +0 -2
  83. metadata +9 -6
  84. data/lib/cosmos/models/narrative_model.rb +0 -280
@@ -96,9 +96,8 @@ module Cosmos
96
96
  ReducerModel
97
97
  .all_files(type: :DECOM, target: @target_name, scope: @scope)
98
98
  .each do |file|
99
- if process_file(file, 'minute', MINUTE_ENTRY_SECS, MINUTE_FILE_SECS)
100
- ReducerModel.rm_file(file)
101
- end
99
+ process_file(file, 'minute', MINUTE_ENTRY_SECS, MINUTE_FILE_SECS)
100
+ ReducerModel.rm_file(file)
102
101
  end
103
102
  end
104
103
  end
@@ -108,9 +107,8 @@ module Cosmos
108
107
  ReducerModel
109
108
  .all_files(type: :MINUTE, target: @target_name, scope: @scope)
110
109
  .each do |file|
111
- if process_file(file, 'hour', HOUR_ENTRY_SECS, HOUR_FILE_SECS)
112
- ReducerModel.rm_file(file)
113
- end
110
+ process_file(file, 'hour', HOUR_ENTRY_SECS, HOUR_FILE_SECS)
111
+ ReducerModel.rm_file(file)
114
112
  end
115
113
  end
116
114
  end
@@ -120,9 +118,8 @@ module Cosmos
120
118
  ReducerModel
121
119
  .all_files(type: :HOUR, target: @target_name, scope: @scope)
122
120
  .each do |file|
123
- if process_file(file, 'day', DAY_ENTRY_SECS, DAY_FILE_SECS)
124
- ReducerModel.rm_file(file)
125
- end
121
+ process_file(file, 'day', DAY_ENTRY_SECS, DAY_FILE_SECS)
122
+ ReducerModel.rm_file(file)
126
123
  end
127
124
  end
128
125
  end
@@ -250,7 +247,12 @@ module Cosmos
250
247
  )
251
248
  true
252
249
  rescue => e
253
- Logger.error("Reducer Error #{e}\n#{e.backtrace}")
250
+ if file.local_path and File.exist?(file.local_path)
251
+ Logger.error("Reducer Error: #{filename}:#{File.size(file.local_path)} bytes: \n#{e.formatted}")
252
+ else
253
+ Logger.error("Reducer Error: #{filename}:(Not Retrieved): \n#{e.formatted}")
254
+ end
255
+ false
254
256
  end
255
257
 
256
258
  def reduce(type, data_keys, reduced)
@@ -40,12 +40,12 @@ module Cosmos
40
40
 
41
41
  # Delete the current value table for a target
42
42
  def self.del(target_name:, packet_name:, scope:)
43
- Store.hdel("#{scope}__tlm__#{target_name}", packet_name)
43
+ EphemeralStore.hdel("#{scope}__tlm__#{target_name}", packet_name)
44
44
  end
45
45
 
46
46
  # Set the current value table for a target, packet
47
47
  def self.set(hash, target_name:, packet_name:, scope:)
48
- Store.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json))
48
+ EphemeralStore.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json))
49
49
  end
50
50
 
51
51
  # Set an item in the current value table
@@ -62,9 +62,9 @@ module Cosmos
62
62
  else
63
63
  raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
64
64
  end
65
- hash = JSON.parse(Store.hget("#{scope}__tlm__#{target_name}", packet_name))
65
+ hash = JSON.parse(EphemeralStore.hget("#{scope}__tlm__#{target_name}", packet_name))
66
66
  hash[field] = value
67
- Store.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json))
67
+ EphemeralStore.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json))
68
68
  end
69
69
 
70
70
  # Get an item from the current value table
@@ -86,7 +86,7 @@ module Cosmos
86
86
  else
87
87
  raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
88
88
  end
89
- hash = JSON.parse(Store.hget("#{scope}__tlm__#{target_name}", packet_name))
89
+ hash = JSON.parse(EphemeralStore.hget("#{scope}__tlm__#{target_name}", packet_name))
90
90
  hash.values_at(*types).each do |result|
91
91
  return result if result
92
92
  end
@@ -106,7 +106,7 @@ module Cosmos
106
106
 
107
107
  lookups.each do |target_packet_key, target_name, packet_name, packet_values|
108
108
  unless packet_lookup[target_packet_key]
109
- packet = Store.hget("#{scope}__tlm__#{target_name}", packet_name)
109
+ packet = EphemeralStore.hget("#{scope}__tlm__#{target_name}", packet_name)
110
110
  raise "Packet '#{target_name} #{packet_name}' does not exist" unless packet
111
111
  packet_lookup[target_packet_key] = JSON.parse(packet)
112
112
  end
@@ -73,7 +73,7 @@ module Cosmos
73
73
  return nil
74
74
  end
75
75
 
76
- def self.install(name_or_path)
76
+ def self.install(name_or_path, scope:)
77
77
  temp_dir = Dir.mktmpdir
78
78
  begin
79
79
  if File.exist?(name_or_path)
@@ -81,10 +81,10 @@ module Cosmos
81
81
  else
82
82
  gem_file_path = get(temp_dir, name_or_path)
83
83
  end
84
- rubygems_url = get_setting('rubygems_url')
84
+ rubygems_url = get_setting('rubygems_url', scope: scope)
85
85
  Gem.sources = [rubygems_url] if rubygems_url
86
86
  Gem.done_installing_hooks.clear
87
- Gem.install(gem_file_path, {:build_args => '--no-document'})
87
+ Gem.install(gem_file_path, "> 0.pre", :build_args => ['--no-document'], :prerelease => true)
88
88
  rescue => err
89
89
  message = "Gem file #{gem_file_path} error installing to /gems\n#{err.formatted}"
90
90
  Logger.error message
@@ -24,7 +24,7 @@ module Cosmos
24
24
 
25
25
  # @return Hash information and statistics about the redis server
26
26
  def self.get()
27
- return Store.info()
27
+ return [Store.info(), EphemeralStore.info]
28
28
  end
29
29
 
30
30
  end
@@ -23,7 +23,7 @@ module Cosmos
23
23
  # Stores the status about an interface. This class also implements logic
24
24
  # to handle status for a router since the functionality is identical
25
25
  # (only difference is the Redis key used).
26
- class InterfaceStatusModel < Model
26
+ class InterfaceStatusModel < EphemeralModel
27
27
  INTERFACES_PRIMARY_KEY = 'cosmos_interface_status'
28
28
  ROUTERS_PRIMARY_KEY = 'cosmos_router_status'
29
29
 
@@ -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