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
@@ -0,0 +1,55 @@
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
+ require 'cosmos/models/model'
21
+
22
+ module Cosmos
23
+ class SettingsModel < Model
24
+ PRIMARY_KEY = 'cosmos__settings'
25
+
26
+ # NOTE: The following three class methods are used by the ModelController
27
+ # and are reimplemented to enable various Model class methods to work
28
+ def self.get(name:, scope: nil)
29
+ super(PRIMARY_KEY, name: name)
30
+ end
31
+
32
+ def self.names(scope: nil)
33
+ super(PRIMARY_KEY)
34
+ end
35
+
36
+ def self.all(scope: nil)
37
+ super(PRIMARY_KEY)
38
+ end
39
+ # END NOTE
40
+
41
+ def initialize(name:, scope: nil, data:)
42
+ super(PRIMARY_KEY, name: name, scope: scope)
43
+ @data = data
44
+ end
45
+
46
+ # @return [Hash] JSON encoding of this model
47
+ def as_json
48
+ {
49
+ 'name' => @name,
50
+ 'data' => @data,
51
+ 'updated_at' => @updated_at
52
+ }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,165 @@
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
+ # https://redis.io/docs/manual/data-types/data-types-tutorial/#sorted-sets
22
+
23
+ require 'cosmos/models/model'
24
+ require 'cosmos/topics/calendar_topic'
25
+
26
+ module Cosmos
27
+ # Put these under the Cosmos module so they are easily accessed in the controller as
28
+ # Cosmos::SortedError vs Cosmos::SortedModel::Error
29
+ class SortedError < StandardError; end
30
+ class SortedInputError < SortedError; end
31
+ class SortedOverlapError < SortedError; end
32
+
33
+ class SortedModel < Model
34
+ SORTED_TYPE = 'sorted'.freeze # To be overriden by base class
35
+ PRIMARY_KEY = '__SORTED'.freeze # To be overriden by base class
36
+
37
+ # MUST be overriden by any subclasses
38
+ def self.pk(scope)
39
+ "#{scope}#{PRIMARY_KEY}"
40
+ end
41
+
42
+ # @return [String|nil] String of the saved json or nil if start not found
43
+ def self.get(start:, scope:)
44
+ result = Store.zrangebyscore(self.pk(scope), start, start)
45
+ return JSON.parse(result[0]) unless result.empty?
46
+ nil
47
+ end
48
+
49
+ # @return [Array<Hash>] Array up to the limit of the models (as Hash objects) stored under the primary key
50
+ def self.all(scope:, limit: 100)
51
+ result = Store.zrevrangebyscore(self.pk(scope), '+inf', '-inf', limit: [0, limit])
52
+ result.map { |item| JSON.parse(item) }
53
+ end
54
+
55
+ # @return [String|nil] json or nil if metadata empty
56
+ def self.get_current_value(scope:)
57
+ start = Time.now.to_i
58
+ array = Store.zrevrangebyscore(self.pk(scope), start, '-inf', limit: [0, 1])
59
+ return nil if array.empty?
60
+ return array[0]
61
+ end
62
+
63
+ # @param start [Integer] Start time to return values (inclusive)
64
+ # @param stop [Integer] Stop time to return values (inclusive)
65
+ # @return [Array|nil] Array up to 100 of this model or empty array
66
+ def self.range(start:, stop:, scope:, limit: 100)
67
+ if start > stop
68
+ raise SortedInputError.new "start: #{start} must be before stop: #{stop}"
69
+ end
70
+ result = Store.zrangebyscore(self.pk(scope), start, stop, limit: [0, limit])
71
+ result.map { |item| JSON.parse(item) }
72
+ end
73
+
74
+ # @return [Integer] count of the members stored under the primary key
75
+ def self.count(scope:)
76
+ Store.zcard(self.pk(scope))
77
+ end
78
+
79
+ # Remove member from a sorted set
80
+ # @return [Integer] count of the members removed, 0 if not found
81
+ def self.destroy(scope:, start:)
82
+ Store.zremrangebyscore(self.pk(scope), start, start)
83
+ end
84
+
85
+ # Remove members from min to max of the sorted set.
86
+ # @return [Integer] count of the members removed
87
+ def self.range_destroy(scope:, start:, stop:)
88
+ Store.zremrangebyscore(self.pk(scope), start, stop)
89
+ end
90
+
91
+ attr_reader :start
92
+
93
+ # @param [Integer] start - start used to store data
94
+ # @param [String] scope - Cosmos scope to track event to
95
+ # @param [Anything] kwargs - Any kwargs to store in the JSON
96
+ def initialize(start:, scope:, type: SORTED_TYPE, **kwargs)
97
+ # Name becomes the start in the base class
98
+ super(self.class.pk(scope), name: start.to_s, scope: scope, **kwargs)
99
+ @type = type # For the as_json, from_json round trip
100
+ @start = start
101
+ end
102
+
103
+ # start MUST be a positive integer
104
+ def validate_start(update: false)
105
+ unless @start.is_a?(Integer)
106
+ raise SortedInputError.new "start must be integer: #{@start}"
107
+ end
108
+ if @start.to_i < 0
109
+ raise SortedInputError.new "start must be positive: #{@start}"
110
+ end
111
+ if !update and self.class.get(start: @start, scope: @scope)
112
+ raise SortedOverlapError.new "duplicate, existing data at #{@start}"
113
+ end
114
+ @start = @start.to_i
115
+ end
116
+
117
+ # Update the Redis hash at primary_key based on the initial passed start
118
+ # The member is set to the JSON generated via calling as_json
119
+ def create(update: false)
120
+ validate_start(update: update)
121
+ @updated_at = Time.now.to_nsec_from_epoch
122
+ Store.zadd(@primary_key, @start, JSON.generate(as_json()))
123
+ if update
124
+ notify(kind: 'updated')
125
+ else
126
+ notify(kind: 'created')
127
+ end
128
+ end
129
+
130
+ # Update the Redis hash at primary_key
131
+ def update(start:)
132
+ @start = start
133
+ create(update: true)
134
+ end
135
+
136
+ # destroy the activity from the redis database
137
+ def destroy
138
+ self.class.destroy(scope: @scope, start: @start)
139
+ notify(kind: 'deleted')
140
+ end
141
+
142
+ # @return [] update the redis stream / timeline topic that something has changed
143
+ def notify(kind:, extra: nil)
144
+ notification = {
145
+ 'data' => JSON.generate(as_json()),
146
+ 'kind' => kind,
147
+ 'type' => 'calendar',
148
+ }
149
+ notification['extra'] = extra unless extra.nil?
150
+ begin
151
+ CalendarTopic.write_entry(notification, scope: @scope)
152
+ rescue StandardError => e
153
+ raise SortedError.new "Failed to write to stream: #{notification}, #{e}"
154
+ end
155
+ end
156
+
157
+ # @return [Hash] JSON encoding of this model
158
+ def as_json
159
+ { **super(),
160
+ 'start' => @start,
161
+ 'type' => SORTED_TYPE,
162
+ }
163
+ end
164
+ end
165
+ end
@@ -208,7 +208,7 @@ module Cosmos
208
208
  tlm_log_cycle_time: tlm_log_cycle_time, tlm_log_cycle_size: tlm_log_cycle_size,
209
209
  tlm_log_retain_time: tlm_log_retain_time,
210
210
  tlm_decom_log_cycle_time: tlm_decom_log_cycle_time, tlm_decom_log_cycle_size: tlm_decom_log_cycle_size,
211
- tlm_decom_log_retain_time: tlm_decom_log_retain_time,
211
+ tlm_decom_log_retain_time: tlm_decom_log_retain_time,
212
212
  reduced_minute_log_retain_time: reduced_minute_log_retain_time,
213
213
  reduced_hour_log_retain_time: reduced_hour_log_retain_time, reduced_day_log_retain_time: reduced_day_log_retain_time,
214
214
  cleanup_poll_time: cleanup_poll_time, needs_dependencies: needs_dependencies,
@@ -269,7 +269,7 @@ module Cosmos
269
269
  'tlm_decom_log_retain_time' => @tlm_decom_log_retain_time,
270
270
  'reduced_minute_log_retain_time' => @reduced_minute_log_retain_time,
271
271
  'reduced_hour_log_retain_time' => @reduced_hour_log_retain_time,
272
- 'reduced_day_log_retain_time' => @reduced_day_log_retain_time,
272
+ 'reduced_day_log_retain_time' => @reduced_day_log_retain_time,
273
273
  'cleanup_poll_time' => @cleanup_poll_time,
274
274
  'needs_dependencies' => @needs_dependencies,
275
275
  }
@@ -301,7 +301,7 @@ module Cosmos
301
301
  when 'CMD_DECOM_LOG_RETAIN_TIME'
302
302
  parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for cmd decom log files in seconds - nil = Forever>")
303
303
  @cmd_decom_log_retain_time = ConfigParser.handle_nil(parameters[0])
304
- @cmd_decom_log_retain_time = @cmd_decom_log_retain_time.to_i if @cmd_decom_log_retain_time
304
+ @cmd_decom_log_retain_time = @cmd_decom_log_retain_time.to_i if @cmd_decom_log_retain_time
305
305
  when 'TLM_LOG_CYCLE_TIME'
306
306
  parser.verify_num_parameters(1, 1, "#{keyword} <Maximum time between files in seconds>")
307
307
  @tlm_log_cycle_time = parameters[0].to_i
@@ -311,7 +311,7 @@ module Cosmos
311
311
  when 'TLM_LOG_RETAIN_TIME'
312
312
  parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for tlm log files in seconds - nil = Forever>")
313
313
  @tlm_log_retain_time = ConfigParser.handle_nil(parameters[0])
314
- @tlm_log_retain_time = @tlm_log_retain_time.to_i if @tlm_log_retain_time
314
+ @tlm_log_retain_time = @tlm_log_retain_time.to_i if @tlm_log_retain_time
315
315
  when 'TLM_DECOM_LOG_CYCLE_TIME'
316
316
  parser.verify_num_parameters(1, 1, "#{keyword} <Maximum time between files in seconds>")
317
317
  @tlm_decom_log_cycle_time = parameters[0].to_i
@@ -333,22 +333,22 @@ module Cosmos
333
333
  when 'REDUCED_DAY_LOG_RETAIN_TIME'
334
334
  parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for reduced day log files in seconds - nil = Forever>")
335
335
  @reduced_day_log_retain_time = ConfigParser.handle_nil(parameters[0])
336
- @reduced_day_log_retain_time = @reduced_day_log_retain_time.to_i if @reduced_day_log_retain_time
336
+ @reduced_day_log_retain_time = @reduced_day_log_retain_time.to_i if @reduced_day_log_retain_time
337
337
  when 'LOG_RETAIN_TIME'
338
338
  parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for all log files in seconds - nil = Forever>")
339
339
  log_retain_time = ConfigParser.handle_nil(parameters[0])
340
- if log_retain_time
340
+ if log_retain_time
341
341
  @cmd_log_retain_time = log_retain_time.to_i
342
- @cmd_decom_log_retain_time = log_retain_time.to_i
342
+ @cmd_decom_log_retain_time = log_retain_time.to_i
343
343
  @tlm_log_retain_time = log_retain_time.to_i
344
344
  @tlm_decom_log_retain_time = log_retain_time.to_i
345
- end
345
+ end
346
346
  when 'REDUCED_LOG_RETAIN_TIME'
347
347
  parser.verify_num_parameters(1, 1, "#{keyword} <Retention time for all reduced log files in seconds - nil = Forever>")
348
348
  reduced_log_retain_time = ConfigParser.handle_nil(parameters[0])
349
- if reduced_log_retain_time
349
+ if reduced_log_retain_time
350
350
  @reduced_minute_log_retain_time = reduced_log_retain_time.to_i
351
- @reduced_hour_log_retain_time = reduced_log_retain_time.to_i
351
+ @reduced_hour_log_retain_time = reduced_log_retain_time.to_i
352
352
  @reduced_day_log_retain_time = reduced_log_retain_time.to_i
353
353
  end
354
354
  when 'CLEANUP_POLL_TIME'
@@ -380,7 +380,7 @@ module Cosmos
380
380
  data = File.read(filename, mode: "rb")
381
381
  begin
382
382
  Cosmos.set_working_dir(File.dirname(filename)) do
383
- data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
383
+ data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable? and File.basename(filename)[0] != '_'
384
384
  end
385
385
  rescue => error
386
386
  raise "ERB error parsing: #{filename}: #{error.formatted}"
@@ -413,12 +413,12 @@ module Cosmos
413
413
  Store.hdel("#{@scope}__limits_groups", group)
414
414
  end
415
415
  self.class.packets(@name, type: :CMD, scope: @scope).each do |packet|
416
- Store.del("#{@scope}__COMMAND__{#{@name}}__#{packet['packet_name']}")
417
- Store.del("#{@scope}__DECOMCMD__{#{@name}}__#{packet['packet_name']}")
416
+ Topic.del("#{@scope}__COMMAND__{#{@name}}__#{packet['packet_name']}")
417
+ Topic.del("#{@scope}__DECOMCMD__{#{@name}}__#{packet['packet_name']}")
418
418
  end
419
419
  self.class.packets(@name, scope: @scope).each do |packet|
420
- Store.del("#{@scope}__TELEMETRY__{#{@name}}__#{packet['packet_name']}")
421
- Store.del("#{@scope}__DECOM__{#{@name}}__#{packet['packet_name']}")
420
+ Topic.del("#{@scope}__TELEMETRY__{#{@name}}__#{packet['packet_name']}")
421
+ Topic.del("#{@scope}__DECOM__{#{@name}}__#{packet['packet_name']}")
422
422
  CvtModel.del(target_name: @name, packet_name: packet['packet_name'], scope: @scope)
423
423
  LimitsEventTopic.delete(@name, packet['packet_name'], scope: @scope)
424
424
  end
@@ -455,7 +455,7 @@ module Cosmos
455
455
 
456
456
  begin
457
457
  Cosmos.set_working_dir(File.dirname(path)) do
458
- return ERB.new(File.read(path)).result(b)
458
+ return ERB.new(File.read(path), trim_mode: "-").result(b)
459
459
  end
460
460
  rescue => error
461
461
  raise "ERB error parsing: #{path}: #{error.formatted}"
@@ -585,10 +585,10 @@ module Cosmos
585
585
  # No telemetry packets for this target
586
586
  end
587
587
  # It's ok to call initialize_streams with an empty array
588
- Store.initialize_streams(command_topic_list)
589
- Store.initialize_streams(decom_command_topic_list)
590
- Store.initialize_streams(packet_topic_list)
591
- Store.initialize_streams(decom_topic_list)
588
+ Topic.initialize_streams(command_topic_list)
589
+ Topic.initialize_streams(decom_command_topic_list)
590
+ Topic.initialize_streams(packet_topic_list)
591
+ Topic.initialize_streams(decom_topic_list)
592
592
 
593
593
  unless command_topic_list.empty?
594
594
  # CommandLog Microservice
@@ -0,0 +1,38 @@
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
+ module Cosmos
21
+ class ToolConfigModel
22
+ def self.list_configs(tool, scope: $cosmos_scope)
23
+ Store.hkeys("#{scope}__config__#{tool}")
24
+ end
25
+
26
+ def self.load_config(tool, name, scope: $cosmos_scope)
27
+ Store.hget("#{scope}__config__#{tool}", name)
28
+ end
29
+
30
+ def self.save_config(tool, name, data, scope: $cosmos_scope)
31
+ Store.hset("#{scope}__config__#{tool}", name, data)
32
+ end
33
+
34
+ def self.delete_config(tool, name, scope: $cosmos_scope)
35
+ Store.hdel("#{scope}__config__#{tool}", name)
36
+ end
37
+ end
38
+ end
@@ -231,7 +231,7 @@ module Cosmos
231
231
 
232
232
  # Load tool files
233
233
  data = File.read(filename, mode: "rb")
234
- data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable?
234
+ data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
235
235
  rubys3_client.put_object(bucket: 'tools', content_type: content_type, cache_control: cache_control, key: key, body: data)
236
236
  end
237
237
  end
@@ -121,7 +121,7 @@ module Cosmos
121
121
  # Load widget file
122
122
  data = File.read(filename, mode: "rb")
123
123
  Cosmos.set_working_dir(File.dirname(filename)) do
124
- data = ERB.new(data).result(binding.set_variables(variables)) if data.is_printable?
124
+ data = ERB.new(data, trim_mode: "-").result(binding.set_variables(variables)) if data.is_printable?
125
125
  end
126
126
  # TODO: support widgets that aren't just a single js file (and its associated map file)
127
127
  rubys3_client.put_object(bucket: 'tools', content_type: 'application/javascript', cache_control: cache_control, key: @s3_key, body: data)
@@ -39,7 +39,7 @@ module Cosmos
39
39
  end
40
40
 
41
41
  def convert_microservice_to_process_definition(microservice_name, microservice_config)
42
- process_definition = ["ruby", "plugin_microservice.rb", microservice_name]
42
+ process_definition = ["ruby", "plugin_microservice.rb"]
43
43
  work_dir = "/cosmos/lib/cosmos/microservices"
44
44
  env = microservice_config["env"].dup
45
45
  if microservice_config["needs_dependencies"]
@@ -47,6 +47,7 @@ module Cosmos
47
47
  else
48
48
  env['GEM_HOME'] = nil
49
49
  end
50
+ env['COSMOS_MICROSERVICE_NAME'] = microservice_name
50
51
  container = microservice_config["container"]
51
52
  scope = microservice_name.split("__")[0]
52
53
  return process_definition, work_dir, env, scope, container
@@ -18,6 +18,7 @@
18
18
  # copyright holder
19
19
 
20
20
  require 'cosmos/script/extract'
21
+ require 'time'
21
22
 
22
23
  module Cosmos
23
24
  module Script
@@ -25,38 +26,48 @@ module Cosmos
25
26
 
26
27
  private
27
28
 
28
- # Sets the metadata value for a target
29
+ # Gets the current metadata
29
30
  #
30
- # @param target [String] Target to set metadata on
31
31
  # @return The result of the method call.
32
- def get_metadata(target)
33
- endpoint = "/cosmos-api/metadata/_get/#{target}"
34
- response = $api_server.request('get', endpoint)
32
+ def get_metadata()
33
+ response = $api_server.request('get', "/cosmos-api/metadata/latest")
35
34
  return nil if response.nil? || response.code != 200
36
35
  return JSON.parse(response.body)
37
36
  end
38
37
 
39
- # Sets the metadata value for a target
38
+ # Sets the metadata
40
39
  #
41
- # @param target [String] Target to set metadata on
42
40
  # @param metadata [Hash<Symbol, Variable>] A hash of metadata
43
- # @param color [String] Events color to show on Calendar tool
44
- # @param start (optional) [String] Metadata time value if nil will default to current time
41
+ # @param color [String] Events color to show on Calendar tool, if nil will be blue
42
+ # @param start [Time] Metadata time value, if nil will be current time
45
43
  # @return The result of the method call.
46
- def set_metadata(target, metadata, color: nil, start: nil)
44
+ def set_metadata(metadata, color: nil, start: nil)
47
45
  color = color.nil? ? '#003784' : color
48
- data = {:color => color, :metadata => metadata, :target => target}
49
- data[:start] = start unless start.nil?
46
+ data = { color: color, metadata: metadata }
47
+ data[:start] = start.iso8601 unless start.nil?
50
48
  response = $api_server.request('post', '/cosmos-api/metadata', data: data, json: true)
51
49
  return nil if response.nil? || response.code != 201
52
50
  return JSON.parse(response.body)
53
51
  end
54
52
 
55
- # Requests the metadata from the user for a target
53
+ # Updates the metadata
56
54
  #
57
- def input_metadata(*args, **kwargs)
58
- rasie StandardError "can only be used in script-runner"
55
+ # @param start [Integer] Metadata time value as integer seconds from epoch
56
+ # @param metadata [Hash<Symbol, Variable>] A hash of metadata
57
+ # @param color [String] Events color to show on Calendar tool, if nil will be blue
58
+ # @return The result of the method call.
59
+ def update_metadata(start, metadata, color: nil)
60
+ color = color.nil? ? '#003784' : color
61
+ data = { :color => color, :metadata => metadata }
62
+ data[:start] = Time.at(start).iso8601
63
+ response = $api_server.request('put', "/cosmos-api/metadata/#{start}", data: data, json: true)
64
+ return nil if response.nil? || response.code != 201
65
+ return JSON.parse(response.body)
59
66
  end
60
67
 
68
+ # Requests the metadata from the user for a target
69
+ def input_metadata(*args, **kwargs)
70
+ raise StandardError "can only be used in Script Runner"
71
+ end
61
72
  end
62
73
  end
@@ -26,6 +26,7 @@ require 'cosmos/packets/limits'
26
26
  require 'cosmos/system/target'
27
27
  require 'cosmos/utilities/s3'
28
28
  require 'cosmos/utilities/zip'
29
+ require 'cosmos/models/scope_model'
29
30
  require 'thread'
30
31
  require 'fileutils'
31
32
 
@@ -54,7 +55,7 @@ module Cosmos
54
55
 
55
56
  # @return [Symbol] The current limits_set of the system returned from Redis
56
57
  def self.limits_set
57
- Store.hget("#{$cosmos_scope}__cosmos_system", 'limits_set').intern
58
+ ScopeModel.limits_set(scope: $cosmos_scope)
58
59
  end
59
60
 
60
61
  def self.setup_targets(target_names, base_dir, scope:)
@@ -37,9 +37,13 @@ class HazardousError < StandardError
37
37
  attr_accessor :cmd_name
38
38
  attr_accessor :cmd_params
39
39
  attr_accessor :hazardous_description
40
+ attr_accessor :formatted # formatted command for use in resending original
40
41
 
41
42
  def to_s
42
- "#{target_name} #{cmd_name} with #{cmd_params} is Hazardous due to #{hazardous_description}"
43
+ string = "#{target_name} #{cmd_name} with #{cmd_params} is Hazardous"
44
+ string << "due to '#{hazardous_description}'" if hazardous_description
45
+ # Pass along the original formatted command so it can be resent
46
+ string << ".\n#{formatted}"
43
47
  end
44
48
  end
45
49
 
@@ -26,7 +26,7 @@ module Cosmos
26
26
  # Notify to the topic
27
27
  #
28
28
  # ```json
29
- # {
29
+ # {
30
30
  # "kind" => "created",
31
31
  # "type" => "trigger",
32
32
  # "data" => {
@@ -46,7 +46,7 @@ module Cosmos
46
46
  # }
47
47
  # ```
48
48
  def self.write_notification(notification, scope:)
49
- Store.write_topic("#{scope}#{PRIMARY_KEY}", notification, '*', 1000)
49
+ Topic.write_topic("#{scope}#{PRIMARY_KEY}", notification, '*', 1000)
50
50
  end
51
51
  end
52
52
  end
@@ -38,7 +38,7 @@ module Cosmos
38
38
  # }
39
39
  # ```
40
40
  def self.write_entry(entry, scope:)
41
- Store.write_topic("#{scope}#{PRIMARY_KEY}", entry, '*', 1000)
41
+ Topic.write_topic("#{scope}#{PRIMARY_KEY}", entry, '*', 1000)
42
42
  end
43
43
  end
44
44
  end
@@ -40,7 +40,37 @@ module Cosmos
40
40
  json_hash[item.name + "__U"] = packet.read_item(item, :WITH_UNITS) if item.units
41
41
  end
42
42
  msg_hash['json_data'] = JSON.generate(json_hash.as_json)
43
- Store.write_topic(topic, msg_hash)
43
+ Topic.write_topic(topic, msg_hash)
44
+ end
45
+
46
+ def self.get_cmd_item(target_name, packet_name, param_name, type: :WITH_UNITS, scope: $cosmos_scope)
47
+ msg_id, msg_hash = Topic.get_newest_message("#{scope}__DECOMCMD__{#{target_name}}__#{packet_name}")
48
+ if msg_id
49
+ # TODO: We now have these reserved items directly on command packets
50
+ # Do we still calculate from msg_hash['time'] or use the times directly?
51
+ #
52
+ # if param_name == 'RECEIVED_TIMESECONDS' || param_name == 'PACKET_TIMESECONDS'
53
+ # Time.from_nsec_from_epoch(msg_hash['time'].to_i).to_f
54
+ # elsif param_name == 'RECEIVED_TIMEFORMATTED' || param_name == 'PACKET_TIMEFORMATTED'
55
+ # Time.from_nsec_from_epoch(msg_hash['time'].to_i).formatted
56
+ if param_name == 'RECEIVED_COUNT'
57
+ msg_hash['received_count'].to_i
58
+ else
59
+ json = msg_hash['json_data']
60
+ hash = JSON.parse(json)
61
+ # Start from the most complex down to the basic raw value
62
+ value = hash["#{param_name}__U"]
63
+ return value if value && type == :WITH_UNITS
64
+
65
+ value = hash["#{param_name}__F"]
66
+ return value if value && (type == :WITH_UNITS || type == :FORMATTED)
67
+
68
+ value = hash["#{param_name}__C"]
69
+ return value if value && (type == :WITH_UNITS || type == :FORMATTED || type == :CONVERTED)
70
+
71
+ return hash[param_name]
72
+ end
73
+ end
44
74
  end
45
75
  end
46
76
  end
@@ -31,17 +31,17 @@ module Cosmos
31
31
  received_count: packet.received_count,
32
32
  stored: packet.stored,
33
33
  buffer: packet.buffer(false) }
34
- Store.write_topic(topic, msg_hash)
34
+ Topic.write_topic(topic, msg_hash)
35
35
  end
36
36
 
37
37
  # @param command [Hash] Command hash structure read to be written to a topic
38
38
  def self.send_command(command, scope:)
39
39
  ack_topic = "{#{scope}__ACKCMD}TARGET__#{command['target_name']}"
40
- Store.update_topic_offsets([ack_topic])
40
+ Topic.update_topic_offsets([ack_topic])
41
41
  # Save the existing cmd_params Hash and JSON generate before writing to the topic
42
42
  cmd_params = command['cmd_params']
43
43
  command['cmd_params'] = JSON.generate(command['cmd_params'].as_json)
44
- cmd_id = Store.write_topic("{#{scope}__CMD}TARGET__#{command['target_name']}", command, '*', 100)
44
+ cmd_id = Topic.write_topic("{#{scope}__CMD}TARGET__#{command['target_name']}", command, '*', 100)
45
45
  # TODO: This timeout is fine for most but can we get the write_timeout from the interface here?
46
46
  time = Time.now
47
47
  while (Time.now - time) < COMMAND_ACK_TIMEOUT_S
@@ -66,7 +66,7 @@ module Cosmos
66
66
  ###########################################################################
67
67
 
68
68
  def self.raise_hazardous_error(msg_hash, target_name, cmd_name, cmd_params)
69
- _, description, _ = msg_hash["result"].split("\n")
69
+ _, description, formatted = msg_hash["result"].split("\n")
70
70
  # Create and populate a new HazardousError and raise it up
71
71
  # The _cmd method in script/commands.rb rescues this and calls prompt_for_hazardous
72
72
  error = HazardousError.new
@@ -74,6 +74,8 @@ module Cosmos
74
74
  error.cmd_name = cmd_name
75
75
  error.cmd_params = cmd_params
76
76
  error.hazardous_description = description
77
+ error.formatted = formatted
78
+
77
79
  # No Logger.info because the error is already logged by the Logger.info "Ack Received ...
78
80
  raise error
79
81
  end