openc3 5.2.0 → 5.3.0

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.
@@ -58,8 +58,6 @@ module OpenC3
58
58
  attr_accessor :raw_logger_pair
59
59
  # @return [String] The ip address to bind to. Default to ANY (0.0.0.0)
60
60
  attr_accessor :listen_address
61
- # @return [boolean] Automatically send SYSTEM META on connect - Default false - Can be CMD/TLM
62
- attr_accessor :auto_system_meta
63
61
 
64
62
  # @param write_port [Integer] The server write port. Clients should connect
65
63
  # and expect to receive data from this port.
@@ -114,7 +112,6 @@ module OpenC3
114
112
  @raw_logging_enabled = false
115
113
  @connection_mutex = Mutex.new
116
114
  @listen_address = "0.0.0.0"
117
- @auto_system_meta = false
118
115
 
119
116
  @read_allowed = false unless ConfigParser.handle_nil(read_port)
120
117
  @write_allowed = false unless ConfigParser.handle_nil(write_port)
@@ -282,15 +279,12 @@ module OpenC3
282
279
 
283
280
  # Supported Options
284
281
  # LISTEN_ADDRESS - Ip address of the interface to accept connections on - Default: 0.0.0.0
285
- # AUTO_SYSTEM_META - Automatically send SYSTEM META on connect - Default false
286
282
  # (see Interface#set_option)
287
283
  def set_option(option_name, option_values)
288
284
  super(option_name, option_values)
289
285
  case option_name.upcase
290
286
  when 'LISTEN_ADDRESS'
291
287
  @listen_address = option_values[0]
292
- when 'AUTO_SYSTEM_META'
293
- @auto_system_meta = ConfigParser.handle_true_false(option_values[0])
294
288
  end
295
289
  end
296
290
 
@@ -411,11 +405,6 @@ module OpenC3
411
405
  interface.connect
412
406
 
413
407
  if listen_write
414
- if @auto_system_meta
415
- meta_packet = System.telemetry.packet('SYSTEM', 'META').clone
416
- interface.write(meta_packet)
417
- end
418
-
419
408
  @write_connection_callback.call(interface) if @write_connection_callback
420
409
  @connection_mutex.synchronize do
421
410
  @write_interface_infos << InterfaceInfo.new(interface, hostname, host_ip, port)
@@ -17,11 +17,12 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  module OpenC3
24
24
  autoload(:Interface, 'openc3/interfaces/interface.rb')
25
+ autoload(:MqttInterface, 'openc3/interfaces/mqtt_interface.rb')
25
26
  autoload(:StreamInterface, 'openc3/interfaces/stream_interface.rb')
26
27
  autoload(:SerialInterface, 'openc3/interfaces/serial_interface.rb')
27
28
  autoload(:SimulatedTargetInterface, 'openc3/interfaces/simulated_target_interface.rb')
@@ -31,7 +32,6 @@ module OpenC3
31
32
  autoload(:LincInterface, 'openc3/interfaces/linc_interface.rb')
32
33
  autoload(:LincHandshakeCommand, 'openc3/interfaces/linc_interface.rb')
33
34
  autoload(:LincHandshake, 'openc3/interfaces/linc_interface.rb')
34
- autoload(:DartStatusInterface, 'openc3/interfaces/dart_status_interface.rb')
35
35
 
36
36
  autoload(:Protocol, 'openc3/interfaces/protocols/protocol.rb')
37
37
  autoload(:BurstProtocol, 'openc3/interfaces/protocols/burst_protocol.rb')
@@ -41,7 +41,6 @@ module OpenC3
41
41
  autoload(:TemplateProtocol, 'openc3/interfaces/protocols/template_protocol.rb')
42
42
  autoload(:TerminatedProtocol, 'openc3/interfaces/protocols/terminated_protocol.rb')
43
43
 
44
- autoload(:OverrideProtocol, 'openc3/interfaces/protocols/override_protocol.rb')
45
44
  autoload(:CrcProtocol, 'openc3/interfaces/protocols/crc_protocol.rb')
46
45
  autoload(:IgnorePacketProtocol, 'openc3/interfaces/protocols/ignore_packet_protocol.rb')
47
46
  end
@@ -31,8 +31,8 @@ module OpenC3
31
31
  @buffer_depth = buffer_depth
32
32
  end
33
33
 
34
- def next_packet_time
35
- fill_buffer()
34
+ def next_packet_time(identify_and_define = true)
35
+ fill_buffer(identify_and_define)
36
36
  packet = @buffer[0]
37
37
  return packet.packet_time if packet
38
38
  return nil
@@ -107,6 +107,28 @@ module OpenC3
107
107
  end
108
108
  next 'SUCCESS'
109
109
  end
110
+ if msg_hash.key?('interface_cmd')
111
+ params = JSON.parse(msg_hash['interface_cmd'], allow_nan: true, create_additions: true)
112
+ begin
113
+ @logger.info "#{@interface.name}: interface_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')}"
114
+ @interface.interface_cmd(params['cmd_name'], params['cmd_params'])
115
+ rescue => e
116
+ @logger.error "#{@interface.name}: interface_cmd: #{e.formatted}"
117
+ next e.message
118
+ end
119
+ next 'SUCCESS'
120
+ end
121
+ if msg_hash.key?('protocol_cmd')
122
+ params = JSON.parse(msg_hash['protocol_cmd'], allow_nan: true, create_additions: true)
123
+ begin
124
+ @logger.info "#{@interface.name}: protocol_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')} read_write: #{params['read_write']} index: #{params['index']}"
125
+ @interface.protocol_cmd(params['cmd_name'], params['cmd_params'], read_write: params['read_write'], index: params['index'])
126
+ rescue => e
127
+ @logger.error "#{@interface.name}: protocol_cmd: #{e.formatted}"
128
+ next e.message
129
+ end
130
+ next 'SUCCESS'
131
+ end
110
132
  end
111
133
 
112
134
  target_name = msg_hash['target_name']
@@ -234,6 +256,28 @@ module OpenC3
234
256
  @router.stop_raw_logging
235
257
  end
236
258
  end
259
+ if msg_hash.key?('router_cmd')
260
+ params = JSON.parse(msg_hash['router_cmd'], allow_nan: true, create_additions: true)
261
+ begin
262
+ @logger.info "#{@router.name}: router_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')}"
263
+ @router.interface_cmd(params['cmd_name'], params['cmd_params'])
264
+ rescue => e
265
+ @logger.error "#{@router.name}: router_cmd: #{e.formatted}"
266
+ next e.message
267
+ end
268
+ next 'SUCCESS'
269
+ end
270
+ if msg_hash.key?('protocol_cmd')
271
+ params = JSON.parse(msg_hash['protocol_cmd'], allow_nan: true, create_additions: true)
272
+ begin
273
+ @logger.info "#{@router.name}: protocol_cmd: #{params['cmd_name']} #{params['cmd_params'].join(' ')} read_write: #{params['read_write']} index: #{params['index']}"
274
+ @router.protocol_cmd(params['cmd_name'], params['cmd_params'], read_write: params['read_write'], index: params['index'])
275
+ rescue => e
276
+ @logger.error "#{@router.name}: protoco_cmd: #{e.formatted}"
277
+ next e.message
278
+ end
279
+ next 'SUCCESS'
280
+ end
237
281
  next 'SUCCESS'
238
282
  end
239
283
 
@@ -264,6 +308,7 @@ module OpenC3
264
308
  UNKNOWN_BYTES_TO_PRINT = 16
265
309
 
266
310
  def initialize(name)
311
+ @mutex = Mutex.new
267
312
  super(name)
268
313
  @interface_or_router = self.class.name.to_s.split("Microservice")[0].upcase.split("::")[-1]
269
314
  @scope = name.split("__")[0]
@@ -307,7 +352,6 @@ module OpenC3
307
352
  @cancel_thread = false
308
353
  @connection_failed_messages = []
309
354
  @connection_lost_messages = []
310
- @mutex = Mutex.new
311
355
  if @interface_or_router == 'INTERFACE'
312
356
  @handler_thread = InterfaceCmdHandlerThread.new(@interface, self, logger: @logger, scope: @scope)
313
357
  else
@@ -560,7 +604,7 @@ module OpenC3
560
604
 
561
605
  # Disconnect from the interface and stop the thread
562
606
  def stop
563
- @logger.info "#{@interface.name}: stop requested"
607
+ @logger.info "#{@interface ? @interface.name : @name}: stop requested"
564
608
  @mutex.synchronize do
565
609
  # Need to make sure that @cancel_thread is set and the interface disconnected within
566
610
  # mutex to ensure that connect() is not called when we want to stop()
@@ -578,7 +622,7 @@ module OpenC3
578
622
  end
579
623
 
580
624
  def shutdown(sig = nil)
581
- @logger.info "#{@interface.name}: shutdown requested"
625
+ @logger.info "#{@interface ? @interface.name : @name}: shutdown requested"
582
626
  stop()
583
627
  super()
584
628
  end
@@ -27,6 +27,7 @@ OpenC3.require_file 'fileutils'
27
27
  OpenC3.require_file 'openc3/utilities/zip'
28
28
  OpenC3.require_file 'openc3/utilities/store'
29
29
  OpenC3.require_file 'openc3/utilities/bucket'
30
+ OpenC3.require_file 'openc3/utilities/secrets'
30
31
  OpenC3.require_file 'openc3/utilities/sleeper'
31
32
  OpenC3.require_file 'openc3/utilities/open_telemetry'
32
33
  OpenC3.require_file 'openc3/models/microservice_model'
@@ -43,6 +44,7 @@ module OpenC3
43
44
  attr_accessor :custom
44
45
  attr_accessor :scope
45
46
  attr_accessor :logger
47
+ attr_accessor :secrets
46
48
 
47
49
  def self.run(name = nil)
48
50
  name = ENV['OPENC3_MICROSERVICE_NAME'] unless name
@@ -93,6 +95,7 @@ module OpenC3
93
95
  @logger = Logger.new
94
96
  @logger.scope = @scope
95
97
  @logger.microservice_name = @name
98
+ @secrets = Secrets.getClient
96
99
 
97
100
  OpenC3.setup_open_telemetry(@name, false)
98
101
 
@@ -104,6 +107,9 @@ module OpenC3
104
107
  if @config
105
108
  @topics = @config['topics']
106
109
  @plugin = @config['plugin']
110
+ if @config['secrets']
111
+ @secrets.setup(@config['secrets'])
112
+ end
107
113
  else
108
114
  @config = {}
109
115
  @plugin = nil
@@ -25,30 +25,29 @@ require 'openc3/utilities/store'
25
25
  module OpenC3
26
26
  class CvtModel
27
27
  VALUE_TYPES = [:RAW, :CONVERTED, :FORMATTED, :WITH_UNITS]
28
- # Stores telemetry item overrides which are returned on every request to get_item
29
- @overrides = {}
30
-
31
28
  def self.build_json_from_packet(packet)
32
29
  packet.decom
33
30
  end
34
31
 
35
32
  # Delete the current value table for a target
36
- def self.del(target_name:, packet_name:, scope:)
33
+ def self.del(target_name:, packet_name:, scope: $openc3_scope)
37
34
  Store.hdel("#{scope}__tlm__#{target_name}", packet_name)
38
35
  end
39
36
 
40
37
  # Set the current value table for a target, packet
41
- def self.set(hash, target_name:, packet_name:, scope:)
38
+ def self.set(hash, target_name:, packet_name:, scope: $openc3_scope)
42
39
  Store.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
43
40
  end
44
41
 
45
42
  # Set an item in the current value table
46
- def self.set_item(target_name, packet_name, item_name, value, type:, scope:)
43
+ def self.set_item(target_name, packet_name, item_name, value, type:, scope: $openc3_scope)
47
44
  case type
48
45
  when :WITH_UNITS
49
46
  field = "#{item_name}__U"
47
+ value = value.to_s # WITH_UNITS should always be a string
50
48
  when :FORMATTED
51
49
  field = "#{item_name}__F"
50
+ value = value.to_s # FORMATTED should always be a string
52
51
  when :CONVERTED
53
52
  field = "#{item_name}__C"
54
53
  when :RAW
@@ -62,27 +61,37 @@ module OpenC3
62
61
  end
63
62
 
64
63
  # Get an item from the current value table
65
- def self.get_item(target_name, packet_name, item_name, type:, scope:)
66
- if @overrides["#{target_name}__#{packet_name}__#{item_name}__#{type}"]
67
- return @overrides["#{target_name}__#{packet_name}__#{item_name}__#{type}"]
68
- end
69
-
64
+ def self.get_item(target_name, packet_name, item_name, type:, scope: $openc3_scope)
65
+ override_key = item_name
70
66
  types = []
71
67
  case type
72
68
  when :WITH_UNITS
73
69
  types = ["#{item_name}__U", "#{item_name}__F", "#{item_name}__C", item_name]
70
+ override_key = "#{item_name}__U"
74
71
  when :FORMATTED
75
72
  types = ["#{item_name}__F", "#{item_name}__C", item_name]
73
+ override_key = "#{item_name}__F"
76
74
  when :CONVERTED
77
75
  types = ["#{item_name}__C", item_name]
76
+ override_key = "#{item_name}__C"
78
77
  when :RAW
79
78
  types = [item_name]
80
79
  else
81
80
  raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
82
81
  end
82
+ overrides = Store.hget("#{scope}__override__#{target_name}", packet_name)
83
+ if overrides
84
+ result = JSON.parse(overrides, :allow_nan => true, :create_additions => true)[override_key]
85
+ return result if result
86
+ end
83
87
  hash = JSON.parse(Store.hget("#{scope}__tlm__#{target_name}", packet_name), :allow_nan => true, :create_additions => true)
84
88
  hash.values_at(*types).each do |result|
85
- return result if result
89
+ if result
90
+ if type == :FORMATTED or type == :WITH_UNITS
91
+ return result.to_s
92
+ end
93
+ return result
94
+ end
86
95
  end
87
96
  return nil
88
97
  end
@@ -97,10 +106,11 @@ module OpenC3
97
106
  results = []
98
107
  lookups = []
99
108
  packet_lookup = {}
109
+ overrides = {}
100
110
  # First generate a lookup hash of all the items represented so we can query the CVT
101
- items.each { |item| _parse_item(lookups, item) }
111
+ items.each { |item| _parse_item(lookups, overrides, item, scope: scope) }
102
112
 
103
- lookups.each do |target_packet_key, target_name, packet_name, packet_values|
113
+ lookups.each do |target_packet_key, target_name, packet_name, value_keys|
104
114
  unless packet_lookup[target_packet_key]
105
115
  packet = Store.hget("#{scope}__tlm__#{target_name}", packet_name)
106
116
  raise "Packet '#{target_name} #{packet_name}' does not exist" unless packet
@@ -108,23 +118,26 @@ module OpenC3
108
118
  end
109
119
  hash = packet_lookup[target_packet_key]
110
120
  item_result = []
111
- packet_values.each do |value|
112
- item_result[0] = hash[value]
113
- break if item_result[0] # We want the first value
114
- end
115
- # If we were able to find a value, try to get the limits state
116
- if item_result[0]
117
- if now - hash['RECEIVED_TIMESECONDS'] > stale_time
118
- item_result[1] = :STALE
121
+ if value_keys.is_a?(Hash) # Set in _parse_item to indicate override
122
+ item_result[0] = value_keys['value']
123
+ else
124
+ value_keys.each do |key|
125
+ item_result[0] = hash[key]
126
+ break if item_result[0] # We want the first value
127
+ end
128
+ # If we were able to find a value, try to get the limits state
129
+ if item_result[0]
130
+ if now - hash['RECEIVED_TIMESECONDS'] > stale_time
131
+ item_result[1] = :STALE
132
+ else
133
+ # The last key is simply the name (RAW) so we can append __L
134
+ # If there is no limits then it returns nil which is acceptable
135
+ item_result[1] = hash["#{value_keys[-1]}__L"]
136
+ item_result[1] = item_result[1].intern if item_result[1] # Convert to symbol
137
+ end
119
138
  else
120
- # The last key is simply the name (RAW) so we can append __L
121
- # If there is no limits then it returns nil which is acceptable
122
- item_result[1] = hash["#{packet_values[-1]}__L"]
123
- item_result[1] = item_result[1].intern if item_result[1] # Convert to symbol
139
+ raise "Item '#{target_name} #{packet_name} #{value_keys[-1]}' does not exist" unless hash.key?(value_keys[-1])
124
140
  end
125
- else
126
- raise "Item '#{target_name} #{packet_name} #{packet_values[-1]}' does not exist" unless hash.key?(packet_values[-1])
127
- item_result[1] = nil
128
141
  end
129
142
  results << item_result
130
143
  end
@@ -133,35 +146,64 @@ module OpenC3
133
146
 
134
147
  # Override a current value table item such that it always returns the same value
135
148
  # for the given type
136
- def self.override(target_name, packet_name, item_name, value, type:, scope: $openc3_scope)
137
- if VALUE_TYPES.include?(type)
138
- @overrides["#{target_name}__#{packet_name}__#{item_name}__#{type}"] = value
149
+ def self.override(target_name, packet_name, item_name, value, type: :ALL, scope: $openc3_scope)
150
+ hash = Store.hget("#{scope}__override__#{target_name}", packet_name)
151
+ hash = JSON.parse(hash, :allow_nan => true, :create_additions => true) if hash
152
+ hash ||= {} # In case the above didn't create anything
153
+ case type
154
+ when :ALL
155
+ hash[item_name] = value
156
+ hash["#{item_name}__C"] = value
157
+ hash["#{item_name}__F"] = value.to_s
158
+ hash["#{item_name}__U"] = value.to_s
159
+ when :RAW
160
+ hash[item_name] = value
161
+ when :CONVERTED
162
+ hash["#{item_name}__C"] = value
163
+ when :FORMATTED
164
+ hash["#{item_name}__F"] = value.to_s # Always a String
165
+ when :WITH_UNITS
166
+ hash["#{item_name}__U"] = value.to_s # Always a String
139
167
  else
140
168
  raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
141
169
  end
170
+ Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
142
171
  end
143
172
 
144
173
  # Normalize a current value table item such that it returns the actual value
145
174
  def self.normalize(target_name, packet_name, item_name, type: :ALL, scope: $openc3_scope)
146
- if type == :ALL
147
- VALUE_TYPES.each do |type|
148
- @overrides.delete("#{target_name}__#{packet_name}__#{item_name}__#{type}")
149
- end
175
+ hash = Store.hget("#{scope}__override__#{target_name}", packet_name)
176
+ hash = JSON.parse(hash, :allow_nan => true, :create_additions => true) if hash
177
+ hash ||= {} # In case the above didn't create anything
178
+ case type
179
+ when :ALL
180
+ hash.delete(item_name)
181
+ hash.delete("#{item_name}__C")
182
+ hash.delete("#{item_name}__F")
183
+ hash.delete("#{item_name}__U")
184
+ when :RAW
185
+ hash.delete(item_name)
186
+ when :CONVERTED
187
+ hash.delete("#{item_name}__C")
188
+ when :FORMATTED
189
+ hash.delete("#{item_name}__F")
190
+ when :WITH_UNITS
191
+ hash.delete("#{item_name}__U")
150
192
  else
151
- if VALUE_TYPES.include?(type)
152
- @overrides.delete("#{target_name}__#{packet_name}__#{item_name}__#{type}")
153
- else
154
- raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
155
- end
193
+ raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
194
+ end
195
+ if hash.empty?
196
+ Store.hdel("#{scope}__override__#{target_name}", packet_name)
197
+ else
198
+ Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
156
199
  end
157
200
  end
158
201
 
159
202
  # PRIVATE METHODS
160
203
 
161
- def self._parse_item(lookups, item)
162
- # parse item and update lookups with packet_name and target_name and keys
163
- #
164
- # return an ordered array of hash with keys
204
+ # parse item and update lookups with packet_name and target_name and keys
205
+ # return an ordered array of hash with keys
206
+ def self._parse_item(lookups, overrides, item, scope:)
165
207
  target_name, packet_name, item_name, value_type = item.split('__')
166
208
  raise ArgumentError, "items must be formatted as TGT__PKT__ITEM__TYPE" if target_name.nil? || packet_name.nil? || item_name.nil? || value_type.nil?
167
209
 
@@ -177,9 +219,23 @@ module OpenC3
177
219
  when 'WITH_UNITS'
178
220
  keys = ["#{item_name}__U", "#{item_name}__F", "#{item_name}__C", item_name]
179
221
  else
180
- raise "Unknown value type #{value_type}"
222
+ raise "Unknown value type '#{value_type}'"
223
+ end
224
+ tgt_pkt_key = "#{target_name}__#{packet_name}"
225
+ # Check the overrides cache for this target / packet
226
+ unless overrides[tgt_pkt_key]
227
+ override_data = Store.hget("#{scope}__override__#{target_name}", packet_name)
228
+ if override_data
229
+ overrides[tgt_pkt_key] = JSON.parse(override_data, :allow_nan => true, :create_additions => true)
230
+ else
231
+ overrides[tgt_pkt_key] = {}
232
+ end
233
+ end
234
+ if overrides[tgt_pkt_key][keys[0]]
235
+ # Set the result as a Hash to distingish it from the key array and from an overridden Array value
236
+ keys = {'value' => overrides[tgt_pkt_key][keys[0]]}
181
237
  end
182
- lookups << ["#{target_name}__#{packet_name}", target_name, packet_name, keys]
238
+ lookups << [tgt_pkt_key, target_name, packet_name, keys]
183
239
  end
184
240
  end
185
241
  end
@@ -38,11 +38,13 @@ module OpenC3
38
38
  attr_accessor :reconnect_delay
39
39
  attr_accessor :disable_disconnect
40
40
  attr_accessor :options
41
+ attr_accessor :secret_options
41
42
  attr_accessor :protocols
42
43
  attr_accessor :interfaces
43
44
  attr_accessor :log
44
45
  attr_accessor :log_raw
45
46
  attr_accessor :needs_dependencies
47
+ attr_accessor :secrets
46
48
 
47
49
  # NOTE: The following three class methods are used by the ModelController
48
50
  # and are reimplemented to enable various Model class methods to work
@@ -101,12 +103,14 @@ module OpenC3
101
103
  reconnect_delay: 5.0,
102
104
  disable_disconnect: false,
103
105
  options: [],
106
+ secret_options: [],
104
107
  protocols: [],
105
108
  log: true,
106
109
  log_raw: false,
107
110
  updated_at: nil,
108
111
  plugin: nil,
109
112
  needs_dependencies: false,
113
+ secrets: [],
110
114
  scope:
111
115
  )
112
116
  if self.class._get_type == 'INTERFACE'
@@ -123,10 +127,12 @@ module OpenC3
123
127
  @reconnect_delay = reconnect_delay
124
128
  @disable_disconnect = disable_disconnect
125
129
  @options = options
130
+ @secret_options = secret_options
126
131
  @protocols = protocols
127
132
  @log = log
128
133
  @log_raw = log_raw
129
134
  @needs_dependencies = needs_dependencies
135
+ @secrets = secrets
130
136
  end
131
137
 
132
138
  # Called by InterfaceMicroservice to instantiate the Interface defined
@@ -139,6 +145,7 @@ module OpenC3
139
145
  else
140
146
  interface_or_router = klass.new
141
147
  end
148
+ interface_or_router.secrets.setup(@secrets)
142
149
  interface_or_router.target_names = @target_names.dup
143
150
  interface_or_router.cmd_target_names = @cmd_target_names.dup
144
151
  interface_or_router.tlm_target_names = @tlm_target_names.dup
@@ -149,6 +156,11 @@ module OpenC3
149
156
  @options.each do |option|
150
157
  interface_or_router.set_option(option[0], option[1..-1])
151
158
  end
159
+ @secret_options.each do |option|
160
+ secret_name = option[1]
161
+ secret_value = interface_or_router.secrets.get(secret_name, scope: @scope)
162
+ interface_or_router.set_option(option[0], [secret_value])
163
+ end
152
164
  @protocols.each do |protocol|
153
165
  klass = OpenC3.require_class(protocol[1])
154
166
  interface_or_router.add_protocol(klass, protocol[2..-1], protocol[0].upcase.intern)
@@ -168,11 +180,13 @@ module OpenC3
168
180
  'reconnect_delay' => @reconnect_delay,
169
181
  'disable_disconnect' => @disable_disconnect,
170
182
  'options' => @options,
183
+ 'secret_options' => @secret_options,
171
184
  'protocols' => @protocols,
172
185
  'log' => @log,
173
186
  'log_raw' => @log_raw,
174
187
  'plugin' => @plugin,
175
188
  'needs_dependencies' => @needs_dependencies,
189
+ 'secrets' => @secrets.as_json(*a),
176
190
  'updated_at' => @updated_at
177
191
  }
178
192
  end
@@ -242,6 +256,14 @@ module OpenC3
242
256
  parser.verify_num_parameters(0, 0, "#{keyword}")
243
257
  @log_raw = true
244
258
 
259
+ when 'SECRET'
260
+ parser.verify_num_parameters(3, 4, "#{keyword} <Secret Type: ENV or FILE> <Secret Name> <Environment Variable Name or File Path> <Option Name (Optional)>")
261
+ @secrets << parameters[0..2]
262
+ if parameters[3]
263
+ # Option Name, Secret Name
264
+ @secret_options << [parameters[3], parameters[1]]
265
+ end
266
+
245
267
  else
246
268
  raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Interface/Router: #{keyword} #{parameters.join(" ")}")
247
269
 
@@ -261,6 +283,7 @@ module OpenC3
261
283
  target_names: @target_names,
262
284
  plugin: @plugin,
263
285
  needs_dependencies: @needs_dependencies,
286
+ secrets: @secrets,
264
287
  scope: @scope
265
288
  )
266
289
  unless validate_only
@@ -39,6 +39,7 @@ module OpenC3
39
39
  attr_accessor :work_dir
40
40
  attr_accessor :ports
41
41
  attr_accessor :parent
42
+ attr_accessor :secrets
42
43
 
43
44
  # NOTE: The following three class methods are used by the ModelController
44
45
  # and are reimplemented to enable various Model class methods to work
@@ -96,6 +97,7 @@ module OpenC3
96
97
  updated_at: nil,
97
98
  plugin: nil,
98
99
  needs_dependencies: false,
100
+ secrets: [],
99
101
  scope:
100
102
  )
101
103
  parts = name.split("__")
@@ -118,6 +120,7 @@ module OpenC3
118
120
  @parent = parent
119
121
  @container = container
120
122
  @needs_dependencies = needs_dependencies
123
+ @secrets = secrets
121
124
  @bucket = Bucket.getClient()
122
125
  end
123
126
 
@@ -137,6 +140,7 @@ module OpenC3
137
140
  'updated_at' => @updated_at,
138
141
  'plugin' => @plugin,
139
142
  'needs_dependencies' => @needs_dependencies,
143
+ 'secrets' => @secrets.as_json(*a)
140
144
  }
141
145
  end
142
146
 
@@ -182,6 +186,9 @@ module OpenC3
182
186
  when 'CONTAINER'
183
187
  parser.verify_num_parameters(1, 1, "#{keyword} <Container Image Name>")
184
188
  @container = parameters[0]
189
+ when 'SECRET'
190
+ parser.verify_num_parameters(3, 3, "#{keyword} <Secret Type: ENV or FILE> <Secret Name> <Environment Variable Name or File Path>")
191
+ @secrets << parameters.dup
185
192
  else
186
193
  raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Microservice: #{keyword} #{parameters.join(" ")}")
187
194
  end
@@ -154,7 +154,7 @@ module OpenC3
154
154
  end
155
155
  end
156
156
  @updated_at = Time.now.to_nsec_from_epoch
157
- self.class.store.hset(@primary_key, @name, JSON.generate(self.as_json(:allow_nan => true)))
157
+ self.class.store.hset(@primary_key, @name, JSON.generate(self.as_json(:allow_nan => true), :allow_nan => true))
158
158
  end
159
159
 
160
160
  # Alias for create(update: true)
@@ -0,0 +1,53 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2022 OpenC3, Inc.
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 file may also be used under the terms of a commercial license
17
+ # if purchased from OpenC3, Inc.
18
+
19
+ require 'openc3/models/model'
20
+
21
+ module OpenC3
22
+ class SecretModel < Model
23
+ PRIMARY_KEY = 'openc3__secrets'
24
+
25
+ # NOTE: The following three class methods are used by the ModelController
26
+ # and are reimplemented to enable various Model class methods to work
27
+ def self.get(name:, scope:)
28
+ super("#{scope}__#{PRIMARY_KEY}", name: name)
29
+ end
30
+
31
+ def self.names(scope:)
32
+ super("#{scope}__#{PRIMARY_KEY}")
33
+ end
34
+
35
+ def self.all(scope:)
36
+ super("#{scope}__#{PRIMARY_KEY}")
37
+ end
38
+ # END NOTE
39
+
40
+ def initialize(name:, value:, scope:)
41
+ super("#{scope}__#{PRIMARY_KEY}", name: name, scope: scope)
42
+ @value = value
43
+ end
44
+
45
+ # @return [Hash] JSON encoding of this model
46
+ def as_json(*a)
47
+ {
48
+ 'name' => @name,
49
+ 'value' => @value.as_json(*a),
50
+ }
51
+ end
52
+ end
53
+ end