cosmos 5.0.2 → 5.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cosmos +183 -42
  3. data/data/config/microservice.yaml +47 -35
  4. data/data/config/plugins.yaml +10 -147
  5. data/data/config/target.yaml +70 -0
  6. data/data/config/tool.yaml +37 -31
  7. data/ext/cosmos/ext/cosmos_io/cosmos_io.c +14 -14
  8. data/ext/cosmos/ext/packet/packet.c +3 -3
  9. data/ext/cosmos/ext/structure/structure.c +31 -31
  10. data/lib/cosmos/api/api.rb +1 -25
  11. data/lib/cosmos/api/cmd_api.rb +17 -6
  12. data/lib/cosmos/api/config_api.rb +10 -4
  13. data/lib/cosmos/api/limits_api.rb +1 -1
  14. data/lib/cosmos/api/settings_api.rb +19 -7
  15. data/lib/cosmos/api/target_api.rb +2 -2
  16. data/lib/cosmos/api/tlm_api.rb +65 -41
  17. data/lib/cosmos/config/config_parser.rb +19 -22
  18. data/lib/cosmos/config/meta_config_parser.rb +1 -1
  19. data/lib/cosmos/conversions/generic_conversion.rb +2 -2
  20. data/lib/cosmos/conversions/polynomial_conversion.rb +5 -8
  21. data/lib/cosmos/conversions/segmented_polynomial_conversion.rb +26 -9
  22. data/lib/cosmos/io/json_drb.rb +5 -1
  23. data/lib/cosmos/logs/log_writer.rb +78 -29
  24. data/lib/cosmos/microservices/cleanup_microservice.rb +28 -29
  25. data/lib/cosmos/microservices/decom_microservice.rb +1 -1
  26. data/lib/cosmos/microservices/interface_microservice.rb +0 -16
  27. data/lib/cosmos/microservices/microservice.rb +3 -3
  28. data/lib/cosmos/microservices/reducer_microservice.rb +12 -10
  29. data/lib/cosmos/models/cvt_model.rb +6 -6
  30. data/lib/cosmos/models/gem_model.rb +9 -3
  31. data/lib/cosmos/models/info_model.rb +1 -1
  32. data/lib/cosmos/models/interface_model.rb +16 -7
  33. data/lib/cosmos/models/interface_status_model.rb +1 -1
  34. data/lib/cosmos/models/metadata_model.rb +69 -219
  35. data/lib/cosmos/models/metric_model.rb +2 -2
  36. data/lib/cosmos/models/microservice_model.rb +7 -4
  37. data/lib/cosmos/models/microservice_status_model.rb +1 -1
  38. data/lib/cosmos/models/model.rb +23 -16
  39. data/lib/cosmos/models/note_model.rb +122 -0
  40. data/lib/cosmos/models/ping_model.rb +2 -1
  41. data/lib/cosmos/models/plugin_model.rb +108 -48
  42. data/lib/cosmos/models/process_status_model.rb +1 -1
  43. data/lib/cosmos/models/scope_model.rb +10 -25
  44. data/lib/cosmos/models/settings_model.rb +55 -0
  45. data/lib/cosmos/models/sorted_model.rb +167 -0
  46. data/lib/cosmos/models/target_model.rb +143 -27
  47. data/lib/cosmos/models/tool_config_model.rb +38 -0
  48. data/lib/cosmos/models/tool_model.rb +9 -9
  49. data/lib/cosmos/models/widget_model.rb +11 -11
  50. data/lib/cosmos/operators/microservice_operator.rb +2 -1
  51. data/lib/cosmos/packets/packet.rb +24 -1
  52. data/lib/cosmos/packets/packet_config.rb +2 -2
  53. data/lib/cosmos/packets/packet_item.rb +57 -0
  54. data/lib/cosmos/packets/packet_item_limits.rb +14 -2
  55. data/lib/cosmos/packets/parsers/packet_item_parser.rb +1 -1
  56. data/lib/cosmos/packets/parsers/packet_parser.rb +1 -1
  57. data/lib/cosmos/packets/parsers/xtce_parser.rb +1 -1
  58. data/lib/cosmos/packets/structure.rb +30 -33
  59. data/lib/cosmos/packets/structure_item.rb +10 -1
  60. data/lib/cosmos/script/api_shared.rb +30 -25
  61. data/lib/cosmos/script/calendar.rb +37 -15
  62. data/lib/cosmos/script/commands.rb +5 -7
  63. data/lib/cosmos/script/script.rb +19 -39
  64. data/lib/cosmos/script/storage.rb +92 -105
  65. data/lib/cosmos/system/system.rb +2 -1
  66. data/lib/cosmos/tools/table_manager/table_config.rb +16 -1
  67. data/lib/cosmos/tools/table_manager/table_item.rb +1 -1
  68. data/lib/cosmos/tools/table_manager/table_manager_core.rb +213 -309
  69. data/lib/cosmos/top_level.rb +5 -1
  70. data/lib/cosmos/topics/autonomic_topic.rb +2 -2
  71. data/lib/cosmos/topics/calendar_topic.rb +1 -1
  72. data/lib/cosmos/topics/command_decom_topic.rb +35 -1
  73. data/lib/cosmos/topics/command_topic.rb +6 -4
  74. data/lib/cosmos/topics/config_topic.rb +68 -0
  75. data/lib/cosmos/topics/interface_topic.rb +8 -8
  76. data/lib/cosmos/topics/limits_event_topic.rb +5 -3
  77. data/lib/cosmos/topics/notifications_topic.rb +1 -1
  78. data/lib/cosmos/topics/router_topic.rb +9 -9
  79. data/lib/cosmos/topics/telemetry_decom_topic.rb +5 -1
  80. data/lib/cosmos/topics/telemetry_topic.rb +1 -1
  81. data/lib/cosmos/topics/timeline_topic.rb +1 -1
  82. data/lib/cosmos/topics/topic.rb +23 -8
  83. data/lib/cosmos/utilities/logger.rb +4 -3
  84. data/lib/cosmos/utilities/metric.rb +32 -26
  85. data/lib/cosmos/utilities/s3.rb +61 -0
  86. data/lib/cosmos/utilities/s3_file_cache.rb +12 -6
  87. data/lib/cosmos/utilities/store.rb +1 -0
  88. data/lib/cosmos/utilities/store_autoload.rb +25 -134
  89. data/lib/cosmos/version.rb +5 -4
  90. data/templates/plugin-template/plugin.gemspec +0 -2
  91. metadata +12 -10
  92. data/bin/xtce_converter +0 -92
  93. data/lib/cosmos/models/narrative_model.rb +0 -280
@@ -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
@@ -134,13 +128,14 @@ module Cosmos
134
128
  @updated_at = kw_args[:updated_at]
135
129
  @plugin = kw_args[:plugin]
136
130
  @scope = kw_args[:scope]
131
+ @destroyed = false
137
132
  end
138
133
 
139
134
  # Update the Redis hash at primary_key and set the field "name"
140
135
  # to the JSON generated via calling as_json
141
136
  def create(update: false, force: false)
142
137
  unless force
143
- existing = Store.hget(@primary_key, @name)
138
+ existing = self.class.store.hget(@primary_key, @name)
144
139
  if existing
145
140
  raise "#{@primary_key}:#{@name} already exists at create" unless update
146
141
  else
@@ -148,7 +143,7 @@ module Cosmos
148
143
  end
149
144
  end
150
145
  @updated_at = Time.now.to_nsec_from_epoch
151
- Store.hset(@primary_key, @name, JSON.generate(self.as_json))
146
+ self.class.store.hset(@primary_key, @name, JSON.generate(self.as_json))
152
147
  end
153
148
 
154
149
  # Alias for create(update: true)
@@ -169,8 +164,14 @@ module Cosmos
169
164
 
170
165
  # Delete the model from the Store
171
166
  def destroy
167
+ @destroyed = true
172
168
  undeploy()
173
- Store.hdel(@primary_key, @name)
169
+ self.class.store.hdel(@primary_key, @name)
170
+ end
171
+
172
+ # Indicate if destroy has been called
173
+ def destroyed?
174
+ @destroyed
174
175
  end
175
176
 
176
177
  # @return [Hash] JSON encoding of this model
@@ -186,4 +187,10 @@ module Cosmos
186
187
  ""
187
188
  end
188
189
  end
190
+
191
+ class EphemeralModel < Model
192
+ def self.store
193
+ EphemeralStore
194
+ end
195
+ end
189
196
  end
@@ -0,0 +1,122 @@
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
+ NoteModel.destroy(scope: @scope, start: update) if update
90
+ Store.zadd(@primary_key, @start, JSON.generate(as_json()))
91
+ if update
92
+ notify(kind: 'updated')
93
+ else
94
+ notify(kind: 'created')
95
+ end
96
+ end
97
+
98
+ # Update the Redis hash at primary_key
99
+ def update(start:, stop:, color:, description:)
100
+ orig_start = @start
101
+ @start = start
102
+ @stop = stop
103
+ @color = color
104
+ @description = description
105
+ create(update: orig_start)
106
+ end
107
+
108
+ # @return [Hash] generated from the NoteModel
109
+ def as_json
110
+ return {
111
+ 'scope' => @scope,
112
+ 'start' => @start,
113
+ 'stop' => @stop,
114
+ 'color' => @color,
115
+ 'description' => @description,
116
+ 'type' => NOTE_TYPE,
117
+ 'updated_at' => @updated_at,
118
+ }
119
+ end
120
+ alias to_s as_json
121
+ end
122
+ 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'
@@ -32,6 +32,7 @@ require 'cosmos/models/tool_model'
32
32
  require 'cosmos/models/widget_model'
33
33
  require 'cosmos/models/microservice_model'
34
34
  require 'tmpdir'
35
+ require 'tempfile'
35
36
 
36
37
  module Cosmos
37
38
  # Represents a COSMOS plugin that can consist of targets, interfaces, routers
@@ -41,6 +42,9 @@ module Cosmos
41
42
  PRIMARY_KEY = 'cosmos_plugins'
42
43
 
43
44
  attr_accessor :variables
45
+ attr_accessor :plugin_txt_lines
46
+ attr_accessor :needs_dependencies
47
+
44
48
 
45
49
  # NOTE: The following three class methods are used by the ModelController
46
50
  # and are reimplemented to enable various Model class methods to work
@@ -58,74 +62,103 @@ module Cosmos
58
62
 
59
63
  # Called by the PluginsController to parse the plugin variables
60
64
  # Doesn't actaully create the plugin during the phase
61
- def self.install_phase1(gem_file_path, existing_variables = nil, scope:)
62
- gem_filename = File.basename(gem_file_path)
63
-
64
- # Load gem to internal gem server
65
- Cosmos::GemModel.put(gem_file_path, gem_install: false, scope: scope)
66
-
67
- # Extract gem and process plugin.txt to determine what VARIABLEs need to be filled in
68
- pkg = Gem::Package.new(gem_file_path)
65
+ def self.install_phase1(gem_file_path, existing_variables: nil, existing_plugin_txt_lines: nil, process_existing: false, scope:, validate_only: false)
66
+ gem_name = File.basename(gem_file_path).split("__")[0]
69
67
 
70
68
  temp_dir = Dir.mktmpdir
69
+ tf = nil
71
70
  begin
72
- pkg.extract_files(temp_dir)
73
- plugin_txt_path = File.join(temp_dir, 'plugin.txt')
74
- if File.exist?(plugin_txt_path)
75
- parser = Cosmos::ConfigParser.new("http://cosmosc2.com")
76
-
77
- # Phase 1 Gather Variables
78
- variables = {}
79
- parser.parse_file(plugin_txt_path,
80
- false,
81
- true,
82
- false) do |keyword, params|
83
- case keyword
84
- when 'VARIABLE'
85
- usage = "#{keyword} <Variable Name> <Default Value>"
86
- parser.verify_num_parameters(2, nil, usage)
87
- variable_name = params[0]
88
- value = params[1..-1].join(" ")
89
- variables[variable_name] = value
90
- if existing_variables && existing_variables.key?(variable_name)
91
- variables[variable_name] = existing_variables[variable_name]
92
- end
93
- # Ignore everything else during phase 1
94
- end
71
+ if File.exists?(gem_file_path)
72
+ # Load gem to internal gem server
73
+ Cosmos::GemModel.put(gem_file_path, gem_install: false, scope: scope) unless validate_only
74
+ else
75
+ gem_file_path = Cosmos::GemModel.get(temp_dir, gem_name)
76
+ end
77
+
78
+ # Extract gem and process plugin.txt to determine what VARIABLEs need to be filled in
79
+ pkg = Gem::Package.new(gem_file_path)
80
+
81
+ if existing_plugin_txt_lines and process_existing
82
+ # This is only used in cosmos load when everything is known
83
+ plugin_txt_lines = existing_plugin_txt_lines
84
+ file_data = existing_plugin_txt_lines.join("\n")
85
+ tf = Tempfile.new("plugin.txt")
86
+ tf.write(file_data)
87
+ tf.close
88
+ plugin_txt_path = tf.path
89
+ else
90
+ # Otherwise we always process the new and return both
91
+ pkg.extract_files(temp_dir)
92
+ plugin_txt_path = File.join(temp_dir, 'plugin.txt')
93
+ plugin_text = File.read(plugin_txt_path)
94
+ plugin_txt_lines = []
95
+ plugin_text.each_line do |line|
96
+ plugin_txt_lines << line.chomp
95
97
  end
98
+ end
99
+
100
+ parser = Cosmos::ConfigParser.new("http://cosmosc2.com")
96
101
 
97
- model = PluginModel.new(name: gem_filename, variables: variables, scope: scope)
98
- return model.as_json
102
+ # Phase 1 Gather Variables
103
+ variables = {}
104
+ parser.parse_file(plugin_txt_path,
105
+ false,
106
+ true,
107
+ false) do |keyword, params|
108
+ case keyword
109
+ when 'VARIABLE'
110
+ usage = "#{keyword} <Variable Name> <Default Value>"
111
+ parser.verify_num_parameters(2, nil, usage)
112
+ variable_name = params[0]
113
+ value = params[1..-1].join(" ")
114
+ variables[variable_name] = value
115
+ if existing_variables && existing_variables.key?(variable_name)
116
+ variables[variable_name] = existing_variables[variable_name]
117
+ end
118
+ # Ignore everything else during phase 1
119
+ end
99
120
  end
121
+
122
+ model = PluginModel.new(name: gem_name, variables: variables, plugin_txt_lines: plugin_txt_lines, scope: scope)
123
+ result = model.as_json
124
+ result['existing_plugin_txt_lines'] = existing_plugin_txt_lines if existing_plugin_txt_lines and not process_existing and existing_plugin_txt_lines != result['plugin_txt_lines']
125
+ return result
100
126
  ensure
101
127
  FileUtils.remove_entry(temp_dir) if temp_dir and File.exist?(temp_dir)
128
+ tf.unlink if tf
102
129
  end
103
130
  end
104
131
 
105
132
  # Called by the PluginsController to create the plugin
106
133
  # Because this uses ERB it must be run in a seperate process from the API to
107
134
  # prevent corruption and single require problems in the current proces
108
- def self.install_phase2(name, variables, scope:)
135
+ def self.install_phase2(plugin_hash, scope:, validate_only: false)
109
136
  rubys3_client = Aws::S3::Client.new
110
137
 
111
138
  # Ensure config bucket exists
112
- begin
113
- rubys3_client.head_bucket(bucket: 'config')
114
- rescue Aws::S3::Errors::NotFound
115
- rubys3_client.create_bucket(bucket: 'config')
139
+ unless validate_only
140
+ begin
141
+ rubys3_client.head_bucket(bucket: 'config')
142
+ rescue Aws::S3::Errors::NotFound
143
+ rubys3_client.create_bucket(bucket: 'config')
144
+ end
116
145
  end
117
146
 
118
147
  # Register plugin to aid in uninstall if install fails
119
- plugin_model = PluginModel.new(name: name, variables: variables, scope: scope)
120
- plugin_model.create
148
+ plugin_hash.delete("existing_plugin_txt_lines")
149
+ plugin_model = PluginModel.new(**(plugin_hash.transform_keys(&:to_sym)), scope: scope)
150
+ plugin_model.create unless validate_only
121
151
 
122
152
  temp_dir = Dir.mktmpdir
123
153
  begin
154
+ tf = nil
155
+
124
156
  # Get the gem from local gem server
125
- gem_file_path = Cosmos::GemModel.get(temp_dir, name)
157
+ gem_name = plugin_hash['name'].split("__")[0]
158
+ gem_file_path = Cosmos::GemModel.get(temp_dir, gem_name)
126
159
 
127
160
  # Actually install the gem now (slow)
128
- Cosmos::GemModel.install(gem_file_path)
161
+ Cosmos::GemModel.install(gem_file_path, scope: scope)
129
162
 
130
163
  # Extract gem contents
131
164
  gem_path = File.join(temp_dir, "gem")
@@ -133,6 +166,11 @@ module Cosmos
133
166
  pkg = Gem::Package.new(gem_file_path)
134
167
  needs_dependencies = pkg.spec.runtime_dependencies.length > 0
135
168
  pkg.extract_files(gem_path)
169
+ needs_dependencies = true if Dir.exist?(File.join(gem_path, 'lib'))
170
+ if needs_dependencies
171
+ plugin_model.needs_dependencies = true
172
+ plugin_model.update
173
+ end
136
174
 
137
175
  # Temporarily add all lib folders from the gem to the end of the load path
138
176
  load_dirs = []
@@ -145,7 +183,12 @@ module Cosmos
145
183
  end
146
184
 
147
185
  # Process plugin.txt file
148
- plugin_txt_path = File.join(gem_path, 'plugin.txt')
186
+ file_data = plugin_hash['plugin_txt_lines'].join("\n")
187
+ tf = Tempfile.new("plugin.txt")
188
+ tf.write(file_data)
189
+ tf.close
190
+ plugin_txt_path = tf.path
191
+ variables = plugin_hash['variables']
149
192
  if File.exist?(plugin_txt_path)
150
193
  parser = Cosmos::ConfigParser.new("http://cosmosc2.com")
151
194
 
@@ -156,8 +199,8 @@ module Cosmos
156
199
  # Ignore during phase 2
157
200
  when 'TARGET', 'INTERFACE', 'ROUTER', 'MICROSERVICE', 'TOOL', 'WIDGET'
158
201
  if current_model
159
- current_model.create
160
- current_model.deploy(gem_path, variables)
202
+ current_model.create unless validate_only
203
+ current_model.deploy(gem_path, variables, validate_only: validate_only)
161
204
  current_model = nil
162
205
  end
163
206
  current_model = Cosmos.const_get((keyword.capitalize + 'Model').intern).handle_config(parser, keyword, params, plugin: plugin_model.name, needs_dependencies: needs_dependencies, scope: scope)
@@ -170,8 +213,8 @@ module Cosmos
170
213
  end
171
214
  end
172
215
  if current_model
173
- current_model.create
174
- current_model.deploy(gem_path, variables)
216
+ current_model.create unless validate_only
217
+ current_model.deploy(gem_path, variables, validate_only: validate_only)
175
218
  current_model = nil
176
219
  end
177
220
  end
@@ -182,21 +225,28 @@ module Cosmos
182
225
  end
183
226
  rescue => err
184
227
  # Install failed - need to cleanup
185
- plugin_model.destroy
228
+ plugin_model.destroy unless validate_only
186
229
  raise err
187
230
  ensure
188
231
  FileUtils.remove_entry(temp_dir) if temp_dir and File.exist?(temp_dir)
232
+ tf.unlink if tf
189
233
  end
234
+
235
+ return plugin_model.as_json
190
236
  end
191
237
 
192
238
  def initialize(
193
239
  name:,
194
240
  variables: {},
241
+ plugin_txt_lines: [],
242
+ needs_dependencies: false,
195
243
  updated_at: nil,
196
244
  scope:
197
245
  )
198
246
  super("#{scope}__#{PRIMARY_KEY}", name: name, updated_at: updated_at, scope: scope)
199
247
  @variables = variables
248
+ @plugin_txt_lines = plugin_txt_lines
249
+ @needs_dependencies = ConfigParser.handle_true_false(needs_dependencies)
200
250
  end
201
251
 
202
252
  def create(update: false, force: false)
@@ -208,6 +258,8 @@ module Cosmos
208
258
  {
209
259
  'name' => @name,
210
260
  'variables' => @variables,
261
+ 'plugin_txt_lines' => @plugin_txt_lines,
262
+ 'needs_dependencies' => @needs_dependencies,
211
263
  'updated_at' => @updated_at
212
264
  }
213
265
  end
@@ -220,5 +272,13 @@ module Cosmos
220
272
  end
221
273
  end
222
274
  end
275
+
276
+ # Reinstall
277
+ def restore
278
+ plugin_hash = self.as_json
279
+ plugin_hash['name'] = plugin_hash['name'].split("__")[0]
280
+ Cosmos::PluginModel.install_phase2(plugin_hash, scope: @scope)
281
+ @destroyed = false
282
+ end
223
283
  end
224
284
  end
@@ -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
@@ -51,23 +52,7 @@ module Cosmos
51
52
  def deploy(gem_path, variables)
52
53
  seed_database()
53
54
 
54
- # Cleanup Microservice
55
- microservice_name = "#{@scope}__CLEANUP__S3"
56
- microservice = MicroserviceModel.new(
57
- name: microservice_name,
58
- cmd: ["ruby", "cleanup_microservice.rb", microservice_name],
59
- work_dir: '/cosmos/lib/cosmos/microservices',
60
- options: [
61
- ["SIZE", "20_000_000_000"], # Max Size to keep in S3
62
- ["DELAY", "300"], # Delay between size checks
63
- ["BUCKET", "logs"], # Bucket to monitor
64
- ["PREFIX", @scope + "/"], # Path into bucket to monitor
65
- ],
66
- scope: @scope
67
- )
68
- microservice.create
69
- microservice.deploy(gem_path, variables)
70
- Logger.info "Configured microservice #{microservice_name}"
55
+ ConfigTopic.initialize_stream(@scope)
71
56
 
72
57
  # COSMOS Log Microservice
73
58
  microservice_name = "#{@scope}__COSMOS__LOG"
@@ -105,7 +90,7 @@ module Cosmos
105
90
  Logger.info "Configured microservice #{microservice_name}"
106
91
 
107
92
  # UNKNOWN CommandLog Microservice
108
- Store.initialize_streams(["#{@scope}__COMMAND__{UNKNOWN}__UNKNOWN"])
93
+ Topic.initialize_streams(["#{@scope}__COMMAND__{UNKNOWN}__UNKNOWN"])
109
94
  microservice_name = "#{@scope}__COMMANDLOG__UNKNOWN"
110
95
  microservice = MicroserviceModel.new(
111
96
  name: microservice_name,
@@ -125,7 +110,7 @@ module Cosmos
125
110
  Logger.info "Configured microservice #{microservice_name}"
126
111
 
127
112
  # UNKNOWN PacketLog Microservice
128
- Store.initialize_streams(["#{@scope}__TELEMETRY__{UNKNOWN}__UNKNOWN"])
113
+ Topic.initialize_streams(["#{@scope}__TELEMETRY__{UNKNOWN}__UNKNOWN"])
129
114
  microservice_name = "#{@scope}__PACKETLOG__UNKNOWN"
130
115
  microservice = MicroserviceModel.new(
131
116
  name: microservice_name,
@@ -146,8 +131,6 @@ module Cosmos
146
131
  end
147
132
 
148
133
  def undeploy
149
- model = MicroserviceModel.get_model(name: "#{@scope}__CLEANUP__S3", scope: @scope)
150
- model.destroy if model
151
134
  model = MicroserviceModel.get_model(name: "#{@scope}__COSMOS__LOG", scope: @scope)
152
135
  model.destroy if model
153
136
  model = MicroserviceModel.get_model(name: "#{@scope}__NOTIFICATION__LOG", scope: @scope)
@@ -159,10 +142,12 @@ module Cosmos
159
142
  end
160
143
 
161
144
  def seed_database
162
- # Set default values for items in the db that should be set
163
- # Use the "nx" (not-exists) variant of redis calls here to not overwrite things the user has already set
164
- Cosmos::Store.hsetnx('cosmos__settings', 'source_url', 'https://github.com/BallAerospace/COSMOS')
165
- Cosmos::Store.hsetnx('cosmos__settings', 'version', ENV['COSMOS_VERSION'] || COSMOS_VERSION)
145
+ setting = SettingsModel.get(name: 'source_url')
146
+ SettingsModel.set({ name: 'source_url', data: 'https://github.com/BallAerospace/COSMOS' }, scope: @scope) unless setting
147
+ end
148
+
149
+ def self.limits_set(scope:)
150
+ Store.hget("#{scope}__cosmos_system", 'limits_set').intern
166
151
  end
167
152
  end
168
153
  end
@@ -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