openc3 5.5.0.pre.beta0 → 5.5.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of openc3 might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bin/openc3cli +58 -74
- data/data/config/_canvas_values.yaml +41 -0
- data/data/config/_graph_params.yaml +25 -0
- data/data/config/graph_settings.yaml +52 -0
- data/data/config/interface_modifiers.yaml +4 -0
- data/data/config/item_modifiers.yaml +1 -1
- data/data/config/microservice.yaml +5 -1
- data/data/config/plugins.yaml +5 -0
- data/data/config/screen.yaml +44 -50
- data/data/config/settings.yaml +144 -0
- data/data/config/widgets.yaml +1744 -1491
- data/lib/openc3/api/cmd_api.rb +43 -17
- data/lib/openc3/api/tlm_api.rb +37 -4
- data/lib/openc3/bridge/bridge.rb +3 -3
- data/lib/openc3/bridge/bridge_config.rb +68 -20
- data/lib/openc3/interfaces/interface.rb +8 -0
- data/lib/openc3/microservices/decom_microservice.rb +0 -3
- data/lib/openc3/microservices/interface_microservice.rb +2 -0
- data/lib/openc3/microservices/reaction_microservice.rb +0 -1
- data/lib/openc3/models/cvt_model.rb +1 -2
- data/lib/openc3/models/interface_model.rb +5 -2
- data/lib/openc3/models/metadata_model.rb +1 -1
- data/lib/openc3/models/microservice_model.rb +7 -6
- data/lib/openc3/models/note_model.rb +1 -1
- data/lib/openc3/models/plugin_model.rb +17 -8
- data/lib/openc3/models/timeline_model.rb +1 -1
- data/lib/openc3/models/trigger_group_model.rb +1 -1
- data/lib/openc3/operators/microservice_operator.rb +2 -2
- data/lib/openc3/packets/packet_item.rb +1 -1
- data/lib/openc3/script/storage.rb +5 -5
- data/lib/openc3/streams/web_socket_client_stream.rb +0 -1
- data/lib/openc3/tools/table_manager/table_manager_core.rb +1 -2
- data/lib/openc3/utilities/aws_bucket.rb +22 -4
- data/lib/openc3/utilities/bucket_file_cache.rb +3 -2
- data/lib/openc3/utilities/bucket_utilities.rb +1 -1
- data/lib/openc3/utilities/cli_generator.rb +210 -0
- data/lib/openc3/utilities/local_mode.rb +2 -2
- data/lib/openc3/utilities/redis_secrets.rb +4 -4
- data/lib/openc3/utilities/secrets.rb +5 -5
- data/lib/openc3/utilities/target_file.rb +43 -32
- data/lib/openc3/version.rb +6 -6
- data/templates/conversion/conversion.rb +43 -0
- data/templates/limits_response/response.rb +51 -0
- data/templates/microservice/microservices/TEMPLATE/microservice.rb +62 -0
- data/templates/plugin/plugin.txt +1 -0
- data/templates/{plugin-template → target}/targets/TARGET/screens/status.txt +1 -2
- metadata +23 -16
- data/lib/openc3/models/traefik_model.rb +0 -47
- data/templates/plugin-template/plugin.txt +0 -9
- /data/templates/{plugin-template → plugin}/LICENSE.txt +0 -0
- /data/templates/{plugin-template → plugin}/README.md +0 -0
- /data/templates/{plugin-template → plugin}/Rakefile +0 -0
- /data/templates/{plugin-template → plugin}/plugin.gemspec +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/cmd_tlm/cmd.txt +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/cmd_tlm/tlm.txt +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/lib/target.rb +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/procedures/procedure.rb +0 -0
- /data/templates/{plugin-template → target}/targets/TARGET/target.txt +0 -0
@@ -44,7 +44,7 @@ module OpenC3
|
|
44
44
|
class PluginModel < Model
|
45
45
|
PRIMARY_KEY = 'openc3_plugins'
|
46
46
|
# Reserved VARIABLE names. See local_mode.rb: update_local_plugin()
|
47
|
-
RESERVED_VARIABLE_NAMES = ['target_name', 'microservice_name']
|
47
|
+
RESERVED_VARIABLE_NAMES = ['target_name', 'microservice_name', 'scope']
|
48
48
|
|
49
49
|
attr_accessor :variables
|
50
50
|
attr_accessor :plugin_txt_lines
|
@@ -72,7 +72,7 @@ module OpenC3
|
|
72
72
|
temp_dir = Dir.mktmpdir
|
73
73
|
tf = nil
|
74
74
|
begin
|
75
|
-
if File.
|
75
|
+
if File.exist?(gem_file_path)
|
76
76
|
# Load gem to internal gem server
|
77
77
|
OpenC3::GemModel.put(gem_file_path, gem_install: false, scope: scope) unless validate_only
|
78
78
|
else
|
@@ -122,7 +122,6 @@ module OpenC3
|
|
122
122
|
if existing_variables && existing_variables.key?(variable_name)
|
123
123
|
variables[variable_name] = existing_variables[variable_name]
|
124
124
|
end
|
125
|
-
# Ignore everything else during phase 1
|
126
125
|
end
|
127
126
|
end
|
128
127
|
|
@@ -162,14 +161,21 @@ module OpenC3
|
|
162
161
|
gem_path = File.join(temp_dir, "gem")
|
163
162
|
FileUtils.mkdir_p(gem_path)
|
164
163
|
pkg = Gem::Package.new(gem_file_path)
|
165
|
-
needs_dependencies = pkg.spec.runtime_dependencies.length > 0
|
166
164
|
pkg.extract_files(gem_path)
|
167
165
|
Dir[File.join(gem_path, '**/screens/*.txt')].each do |filename|
|
168
166
|
if File.basename(filename) != File.basename(filename).downcase
|
169
|
-
raise "Invalid screen
|
167
|
+
raise "Invalid screen filename: #{filename}. Screen filenames must be lowercase."
|
170
168
|
end
|
171
169
|
end
|
170
|
+
needs_dependencies = pkg.spec.runtime_dependencies.length > 0
|
172
171
|
needs_dependencies = true if Dir.exist?(File.join(gem_path, 'lib'))
|
172
|
+
# If needs_dependencies hasn't already been set we need to scan the plugin.txt
|
173
|
+
# to see if they've explicitly set the NEEDS_DEPENDENCIES keyword
|
174
|
+
unless needs_dependencies
|
175
|
+
if plugin_hash['plugin_txt_lines'].join("\n").include?('NEEDS_DEPENDENCIES')
|
176
|
+
needs_dependencies = true
|
177
|
+
end
|
178
|
+
end
|
173
179
|
if needs_dependencies
|
174
180
|
plugin_model.needs_dependencies = true
|
175
181
|
plugin_model.update unless validate_only
|
@@ -192,13 +198,15 @@ module OpenC3
|
|
192
198
|
tf.close
|
193
199
|
plugin_txt_path = tf.path
|
194
200
|
variables = plugin_hash['variables']
|
201
|
+
variables ||= {}
|
202
|
+
variables['scope'] = scope
|
195
203
|
if File.exist?(plugin_txt_path)
|
196
204
|
parser = OpenC3::ConfigParser.new("https://openc3.com")
|
197
205
|
|
198
206
|
current_model = nil
|
199
207
|
parser.parse_file(plugin_txt_path, false, true, true, variables) do |keyword, params|
|
200
208
|
case keyword
|
201
|
-
when 'VARIABLE'
|
209
|
+
when 'VARIABLE', 'NEEDS_DEPENDENCIES'
|
202
210
|
# Ignore during phase 2
|
203
211
|
when 'TARGET', 'INTERFACE', 'ROUTER', 'MICROSERVICE', 'TOOL', 'WIDGET'
|
204
212
|
if current_model
|
@@ -206,12 +214,13 @@ module OpenC3
|
|
206
214
|
current_model.deploy(gem_path, variables, validate_only: validate_only)
|
207
215
|
current_model = nil
|
208
216
|
end
|
209
|
-
current_model = OpenC3.const_get((keyword.capitalize + 'Model').intern).handle_config(parser,
|
217
|
+
current_model = OpenC3.const_get((keyword.capitalize + 'Model').intern).handle_config(parser,
|
218
|
+
keyword, params, plugin: plugin_model.name, needs_dependencies: needs_dependencies, scope: scope)
|
210
219
|
else
|
211
220
|
if current_model
|
212
221
|
current_model.handle_config(parser, keyword, params)
|
213
222
|
else
|
214
|
-
raise "Invalid keyword #{keyword} in plugin.txt"
|
223
|
+
raise "Invalid keyword '#{keyword}' in plugin.txt"
|
215
224
|
end
|
216
225
|
end
|
217
226
|
end
|
@@ -91,7 +91,7 @@ module OpenC3
|
|
91
91
|
if color.nil?
|
92
92
|
color = '#%06x' % (rand * 0xffffff)
|
93
93
|
end
|
94
|
-
valid_color = color =~ /[0-
|
94
|
+
valid_color = color =~ /[0-9a-fA-F]{6}/
|
95
95
|
if valid_color.nil?
|
96
96
|
raise RuntimeError.new "invalid color but in hex format. #FF0000"
|
97
97
|
end
|
@@ -92,7 +92,7 @@ module OpenC3
|
|
92
92
|
if color.nil?
|
93
93
|
color = '#%06x' % (rand * 0xffffff)
|
94
94
|
end
|
95
|
-
valid_color = color =~ /[0-
|
95
|
+
valid_color = color =~ /[0-9a-fA-F]{6}/
|
96
96
|
if valid_color.nil?
|
97
97
|
raise TriggerGroupInputError.new "invalid color must be in hex format. #FF0000"
|
98
98
|
end
|
@@ -60,8 +60,8 @@ module OpenC3
|
|
60
60
|
# Setup secrets for microservice
|
61
61
|
secrets = microservice_config["secrets"]
|
62
62
|
if secrets
|
63
|
-
secrets.each do |type, secret_name, env_name_or_path|
|
64
|
-
secret_value = @secrets.get(secret_name, scope: scope)
|
63
|
+
secrets.each do |type, secret_name, env_name_or_path, secret_store|
|
64
|
+
secret_value = @secrets.get(secret_name, secret_store: secret_store, scope: scope)
|
65
65
|
if secret_value
|
66
66
|
case type
|
67
67
|
when 'ENV'
|
@@ -80,7 +80,7 @@ module OpenC3
|
|
80
80
|
|
81
81
|
# @return [Hash] Whether or not messages should be printed for this state.
|
82
82
|
# Given as STATE_NAME => true / false.
|
83
|
-
|
83
|
+
attr_reader :messages_disabled
|
84
84
|
|
85
85
|
# Colors associated with states
|
86
86
|
# @return [Hash] State colors given as STATE_NAME => COLOR
|
@@ -77,7 +77,7 @@ module OpenC3
|
|
77
77
|
else
|
78
78
|
request.body_stream = io_or_string
|
79
79
|
end
|
80
|
-
|
80
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
81
81
|
http.request(request) do |response|
|
82
82
|
response.value() # Raises an HTTP error if the response is not 2xx (success)
|
83
83
|
return response
|
@@ -157,17 +157,17 @@ module OpenC3
|
|
157
157
|
if $openc3_in_cluster
|
158
158
|
case ENV['OPENC3_CLOUD']
|
159
159
|
when 'local'
|
160
|
-
|
160
|
+
URI.parse("http://openc3-minio:9000" + url)
|
161
161
|
when 'aws'
|
162
|
-
|
162
|
+
URI.parse("https://s3.#{ENV['AWS_REGION']}.amazonaws.com" + url)
|
163
163
|
when 'gcp'
|
164
|
-
|
164
|
+
URI.parse("https://storage.googleapis.com" + url)
|
165
165
|
# when 'azure'
|
166
166
|
else
|
167
167
|
raise "Unknown cloud #{ENV['OPENC3_CLOUD']}"
|
168
168
|
end
|
169
169
|
else
|
170
|
-
|
170
|
+
URI.parse($api_server.generate_url + url)
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
@@ -17,7 +17,7 @@
|
|
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
|
require 'openc3'
|
@@ -148,7 +148,6 @@ module OpenC3
|
|
148
148
|
}
|
149
149
|
col = 0
|
150
150
|
row = 0
|
151
|
-
num_cols = table.num_columns
|
152
151
|
table.sorted_items.each_with_index do |item, index|
|
153
152
|
next if item.hidden
|
154
153
|
if table.num_columns == 1
|
@@ -111,7 +111,12 @@ module OpenC3
|
|
111
111
|
token = nil
|
112
112
|
result = []
|
113
113
|
while true
|
114
|
-
resp = @client.list_objects_v2(
|
114
|
+
resp = @client.list_objects_v2({
|
115
|
+
bucket: bucket,
|
116
|
+
max_keys: max_request,
|
117
|
+
prefix: prefix,
|
118
|
+
continuation_token: token
|
119
|
+
})
|
115
120
|
result.concat(resp.contents)
|
116
121
|
break if result.length >= max_total
|
117
122
|
break unless resp.is_truncated
|
@@ -119,12 +124,12 @@ module OpenC3
|
|
119
124
|
end
|
120
125
|
# Array of objects with key and size methods
|
121
126
|
result
|
122
|
-
rescue Aws::S3::Errors::NoSuchBucket
|
127
|
+
rescue Aws::S3::Errors::NoSuchBucket
|
123
128
|
raise NotFound, "Bucket '#{bucket}' does not exist."
|
124
129
|
end
|
125
130
|
|
126
131
|
# Lists the files under a specified path
|
127
|
-
def list_files(bucket:, path:, only_directories: false)
|
132
|
+
def list_files(bucket:, path:, only_directories: false, include_head: false)
|
128
133
|
# Trailing slash is important in AWS S3 when listing files
|
129
134
|
# See https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Types/ListObjectsV2Output.html#common_prefixes-instance_method
|
130
135
|
if path[-1] != '/'
|
@@ -158,6 +163,9 @@ module OpenC3
|
|
158
163
|
item['name'] = aws_item.key.split('/')[-1]
|
159
164
|
item['modified'] = aws_item.last_modified
|
160
165
|
item['size'] = aws_item.size
|
166
|
+
if include_head
|
167
|
+
item['head'] = head_object(bucket: bucket, key: aws_item.key)
|
168
|
+
end
|
161
169
|
files << item
|
162
170
|
end
|
163
171
|
result = [dirs, files]
|
@@ -166,10 +174,20 @@ module OpenC3
|
|
166
174
|
token = resp.next_continuation_token
|
167
175
|
end
|
168
176
|
result
|
169
|
-
rescue Aws::S3::Errors::NoSuchBucket
|
177
|
+
rescue Aws::S3::Errors::NoSuchBucket
|
170
178
|
raise NotFound, "Bucket '#{bucket}' does not exist."
|
171
179
|
end
|
172
180
|
|
181
|
+
# get metadata for a specific object
|
182
|
+
def head_object(bucket:, key:)
|
183
|
+
head = @client.head_object({
|
184
|
+
bucket: bucket,
|
185
|
+
key: key
|
186
|
+
})
|
187
|
+
rescue Aws::S3::Errors::NotFound => error
|
188
|
+
raise NotFound, "Object: #{bucket}/#{key}"
|
189
|
+
end
|
190
|
+
|
173
191
|
# put_object fires off the request to store but does not confirm
|
174
192
|
def put_object(bucket:, key:, body:, content_type: nil, cache_control: nil, metadata: nil)
|
175
193
|
@client.put_object(bucket: bucket, key: key, body: body,
|
@@ -57,10 +57,10 @@ class BucketFile
|
|
57
57
|
cmd_or_tlm = path_split[2].to_s.upcase
|
58
58
|
target_name = path_split[3].to_s.upcase
|
59
59
|
if stream_mode == 'RAW'
|
60
|
-
type = (
|
60
|
+
type = (cmd_or_tlm == 'CMD') ? 'COMMAND' : 'TELEMETRY'
|
61
61
|
else
|
62
62
|
if stream_mode == 'DECOM'
|
63
|
-
type = (
|
63
|
+
type = (cmd_or_tlm == 'CMD') ? 'DECOMCMD' : 'DECOM'
|
64
64
|
else
|
65
65
|
type = stream_mode # REDUCED_MINUTE, REDUCED_HOUR, or REDUCED_DAY
|
66
66
|
end
|
@@ -156,6 +156,7 @@ class BucketFileCache
|
|
156
156
|
@current_disk_usage = 0
|
157
157
|
@queued_bucket_files = []
|
158
158
|
@bucket_file_hash = {}
|
159
|
+
bucket_file = nil
|
159
160
|
|
160
161
|
@thread = Thread.new do
|
161
162
|
client = OpenC3::Bucket.getClient()
|
@@ -162,7 +162,7 @@ module OpenC3
|
|
162
162
|
|
163
163
|
def self.get_file_times(bucket_path)
|
164
164
|
basename = File.basename(bucket_path)
|
165
|
-
file_start_timestamp, file_end_timestamp,
|
165
|
+
file_start_timestamp, file_end_timestamp, _ = basename.split("__")
|
166
166
|
file_start_time = DateTime.strptime(file_start_timestamp, FILE_TIMESTAMP_FORMAT).to_time
|
167
167
|
file_end_time = DateTime.strptime(file_end_timestamp, FILE_TIMESTAMP_FORMAT).to_time
|
168
168
|
return file_start_time, file_end_time
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2023 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
|
+
module OpenC3
|
20
|
+
class CliGenerator
|
21
|
+
GENERATORS = %w(plugin target microservice conversion limits_response)
|
22
|
+
TEMPLATES_DIR = "#{File.dirname(__FILE__)}/../../../templates"
|
23
|
+
|
24
|
+
# Called by openc3cli with ARGV[1..-1]
|
25
|
+
def self.generate(args)
|
26
|
+
unless GENERATORS.include?(args[0])
|
27
|
+
abort("Unknown generator '#{args[0]}'. Valid generators: #{GENERATORS.join(', ')}")
|
28
|
+
end
|
29
|
+
check_args(args)
|
30
|
+
send("generate_#{args[0]}", args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.check_args(args)
|
34
|
+
args.each do |arg|
|
35
|
+
if arg =~ /\s/
|
36
|
+
abort("#{args[0].capitalize} arguments can not have spaces!")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
# All generators except 'plugin' must be within an existing plugin
|
40
|
+
if args[0] != 'plugin' and Dir.glob("*.gemspec").empty?
|
41
|
+
abort("No gemspec file detected. #{args[0].capitalize} generator should be run within an existing plugin.")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.process_template(template_dir, the_binding)
|
46
|
+
Dir.glob("#{template_dir}/**/*").each do |file|
|
47
|
+
base_name = file.sub("#{template_dir}/", '')
|
48
|
+
yield base_name
|
49
|
+
if File.directory?(file)
|
50
|
+
FileUtils.mkdir(base_name)
|
51
|
+
next
|
52
|
+
end
|
53
|
+
output = ERB.new(File.read(file), trim_mode: "-").result(the_binding)
|
54
|
+
File.open(base_name, 'w') do |file|
|
55
|
+
file.write output
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.generate_plugin(args)
|
61
|
+
if args.length != 2
|
62
|
+
abort("Usage: cli generate #{args[0]} <NAME>")
|
63
|
+
end
|
64
|
+
|
65
|
+
# Create the local variables
|
66
|
+
plugin = args[1].downcase.gsub(/_+|-+/, '-')
|
67
|
+
plugin_name = "openc3-cosmos-#{plugin}"
|
68
|
+
if File.exist?(plugin_name)
|
69
|
+
abort("Plugin #{plugin_name} already exists!")
|
70
|
+
end
|
71
|
+
FileUtils.mkdir(plugin_name)
|
72
|
+
Dir.chdir(plugin_name) # Change to the plugin path to make copying easier
|
73
|
+
|
74
|
+
process_template("#{TEMPLATES_DIR}/plugin", binding) do |filename|
|
75
|
+
filename.sub!("plugin.gemspec", "#{plugin_name}.gemspec")
|
76
|
+
end
|
77
|
+
|
78
|
+
puts "Plugin #{plugin_name} successfully generated!"
|
79
|
+
return plugin_name
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.generate_target(args)
|
83
|
+
if args.length != 2
|
84
|
+
abort("Usage: cli generate #{args[0]} <NAME>")
|
85
|
+
end
|
86
|
+
|
87
|
+
# Create the local variables
|
88
|
+
target_name = args[1].upcase.gsub(/_+|-+/, '_')
|
89
|
+
target_path = "targets/#{target_name}"
|
90
|
+
if File.exist?(target_path)
|
91
|
+
abort("Target #{target_path} already exists!")
|
92
|
+
end
|
93
|
+
target_lib_filename = "#{target_name.downcase}.rb"
|
94
|
+
target_class = target_lib_filename.filename_to_class_name
|
95
|
+
target_object = target_name.downcase
|
96
|
+
|
97
|
+
process_template("#{TEMPLATES_DIR}/target", binding) do |filename|
|
98
|
+
# Rename the template TARGET to our actual target named after the plugin
|
99
|
+
filename.sub!("targets/TARGET", "targets/#{target_name}")
|
100
|
+
filename.sub!("target.rb", target_lib_filename)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Add this target to plugin.txt
|
104
|
+
File.open("plugin.txt", 'a') do |file|
|
105
|
+
file.puts <<~DOC
|
106
|
+
|
107
|
+
VARIABLE #{target_name.downcase}_target_name #{target_name}
|
108
|
+
|
109
|
+
TARGET #{target_name} <%= #{target_name.downcase}_target_name %>
|
110
|
+
INTERFACE <%= #{target_name.downcase}_target_name %>_INT tcpip_client_interface.rb host.docker.internal 8080 8081 10.0 nil BURST
|
111
|
+
MAP_TARGET <%= #{target_name.downcase}_target_name %>
|
112
|
+
DOC
|
113
|
+
end
|
114
|
+
|
115
|
+
puts "Target #{target_name} successfully generated!"
|
116
|
+
return target_name
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.generate_microservice(args)
|
120
|
+
if args.length != 2
|
121
|
+
abort("Usage: cli generate #{args[0]} <NAME>")
|
122
|
+
end
|
123
|
+
|
124
|
+
# Create the local variables
|
125
|
+
microservice_name = args[1].upcase.gsub(/_+|-+/, '_')
|
126
|
+
microservice_path = "microservices/#{microservice_name}"
|
127
|
+
if File.exist?(microservice_path)
|
128
|
+
abort("Microservice #{microservice_path} already exists!")
|
129
|
+
end
|
130
|
+
microservice_filename = "#{microservice_name.downcase}.rb"
|
131
|
+
microservice_class = microservice_filename.filename_to_class_name
|
132
|
+
|
133
|
+
process_template("#{TEMPLATES_DIR}/microservice", binding) do |filename|
|
134
|
+
# Rename the template MICROSERVICE to our actual microservice name
|
135
|
+
filename.sub!("microservices/TEMPLATE", "microservices/#{microservice_name}")
|
136
|
+
filename.sub!("microservice.rb", microservice_filename)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Add this microservice to plugin.txt
|
140
|
+
File.open("plugin.txt", 'a') do |file|
|
141
|
+
file.puts <<~DOC
|
142
|
+
|
143
|
+
MICROSERVICE #{microservice_name} #{microservice_name.downcase.gsub('_','-')}-microservice
|
144
|
+
CMD ruby #{microservice_name.downcase}.rb
|
145
|
+
DOC
|
146
|
+
end
|
147
|
+
|
148
|
+
puts "Microservice #{microservice_name} successfully generated!"
|
149
|
+
return microservice_name
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.generate_conversion(args)
|
153
|
+
if args.length != 3
|
154
|
+
abort("Usage: cli generate conversion <TARGET> <NAME>")
|
155
|
+
end
|
156
|
+
|
157
|
+
# Create the local variables
|
158
|
+
target_name = args[1].upcase
|
159
|
+
unless File.exist?("targets/#{target_name}")
|
160
|
+
abort("Target '#{target_name}' does not exist! Conversions must be created for existing targets.")
|
161
|
+
end
|
162
|
+
conversion_name = "#{args[2].upcase.gsub(/_+|-+/, '_')}_CONVERSION"
|
163
|
+
conversion_path = "targets/#{target_name}/lib/"
|
164
|
+
conversion_basename = "#{conversion_name.downcase}.rb"
|
165
|
+
conversion_class = conversion_basename.filename_to_class_name
|
166
|
+
conversion_filename = "targets/#{target_name}/lib/#{conversion_basename}"
|
167
|
+
if File.exist?(conversion_filename)
|
168
|
+
abort("Conversion #{conversion_filename} already exists!")
|
169
|
+
end
|
170
|
+
|
171
|
+
process_template("#{TEMPLATES_DIR}/conversion", binding) do |filename|
|
172
|
+
filename.sub!("conversion.rb", conversion_filename)
|
173
|
+
end
|
174
|
+
|
175
|
+
puts "Conversion #{conversion_filename} successfully generated!"
|
176
|
+
puts "To use the conversion add the following to a telemetry item:"
|
177
|
+
puts " READ_CONVERSION #{conversion_basename}"
|
178
|
+
return conversion_name
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.generate_limits_response(args)
|
182
|
+
if args.length != 3
|
183
|
+
abort("Usage: cli generate limits_response <TARGET> <NAME>")
|
184
|
+
end
|
185
|
+
|
186
|
+
# Create the local variables
|
187
|
+
target_name = args[1].upcase
|
188
|
+
unless File.exist?("targets/#{target_name}")
|
189
|
+
abort("Target '#{target_name}' does not exist! Limits responses must be created for existing targets.")
|
190
|
+
end
|
191
|
+
response_name = "#{args[2].upcase.gsub(/_+|-+/, '_')}_LIMITS_RESPONSE"
|
192
|
+
response_path = "targets/#{target_name}/lib/"
|
193
|
+
response_basename = "#{response_name.downcase}.rb"
|
194
|
+
response_class = response_basename.filename_to_class_name
|
195
|
+
response_filename = "targets/#{target_name}/lib/#{response_basename}"
|
196
|
+
if File.exist?(response_filename)
|
197
|
+
abort("response #{response_filename} already exists!")
|
198
|
+
end
|
199
|
+
|
200
|
+
process_template("#{TEMPLATES_DIR}/limits_response", binding) do |filename|
|
201
|
+
filename.sub!("response.rb", response_filename)
|
202
|
+
end
|
203
|
+
|
204
|
+
puts "Limits response #{response_filename} successfully generated!"
|
205
|
+
puts "To use the limits response add the following to a telemetry item:"
|
206
|
+
puts " LIMITS_RESPONSE #{response_basename}"
|
207
|
+
return response_name
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -275,13 +275,13 @@ module OpenC3
|
|
275
275
|
return if DEFAULT_PLUGINS.include?(gem_name)
|
276
276
|
puts "Updating local plugin files: #{full_folder_path}"
|
277
277
|
FileUtils.mkdir_p(full_folder_path)
|
278
|
-
gems,
|
278
|
+
gems, _ = scan_plugin_dir(full_folder_path)
|
279
279
|
gems.each do |gem|
|
280
280
|
File.delete(gem)
|
281
281
|
end
|
282
282
|
temp_dir = Dir.mktmpdir
|
283
283
|
begin
|
284
|
-
unless File.
|
284
|
+
unless File.exist?(plugin_file_path)
|
285
285
|
plugin_file_path = OpenC3::GemModel.get(plugin_file_path)
|
286
286
|
end
|
287
287
|
File.open(File.join(full_folder_path, File.basename(plugin_file_path)), 'wb') do |file|
|
@@ -21,11 +21,11 @@ require 'openc3/utilities/secrets'
|
|
21
21
|
|
22
22
|
module OpenC3
|
23
23
|
class RedisSecrets < Secrets
|
24
|
-
def keys(scope:)
|
24
|
+
def keys(secret_store: nil, scope:)
|
25
25
|
SecretModel.names(scope: scope)
|
26
26
|
end
|
27
27
|
|
28
|
-
def get(key, scope:)
|
28
|
+
def get(key, secret_store: nil, scope:)
|
29
29
|
data = SecretModel.get(name: key, scope: scope)
|
30
30
|
if data
|
31
31
|
return data['value']
|
@@ -34,11 +34,11 @@ module OpenC3
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
def set(key, value, scope:)
|
37
|
+
def set(key, value, secret_store: nil, scope:)
|
38
38
|
SecretModel.set( {name: key, value: value.to_s }, scope: scope)
|
39
39
|
end
|
40
40
|
|
41
|
-
def delete(key, scope:)
|
41
|
+
def delete(key, secret_store: nil, scope:)
|
42
42
|
model = SecretModel.get_model(name: key, scope: scope)
|
43
43
|
model.destroy if model
|
44
44
|
end
|
@@ -31,24 +31,24 @@ module OpenC3
|
|
31
31
|
klass.new
|
32
32
|
end
|
33
33
|
|
34
|
-
def keys(scope:)
|
34
|
+
def keys(secret_store: nil, scope:)
|
35
35
|
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
36
36
|
end
|
37
37
|
|
38
|
-
def get(key, scope:)
|
38
|
+
def get(key, secret_store: nil, scope:)
|
39
39
|
return @local_secrets[key]
|
40
40
|
end
|
41
41
|
|
42
|
-
def set(key, value, scope:)
|
42
|
+
def set(key, value, secret_store: nil, scope:)
|
43
43
|
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
44
44
|
end
|
45
45
|
|
46
|
-
def delete(key, scope:)
|
46
|
+
def delete(key, secret_store: nil, scope:)
|
47
47
|
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
48
48
|
end
|
49
49
|
|
50
50
|
def setup(secrets)
|
51
|
-
secrets.each do |type, key, data|
|
51
|
+
secrets.each do |type, key, data, secret_store|
|
52
52
|
case type
|
53
53
|
when 'ENV'
|
54
54
|
@local_secrets[key] = ENV[data]
|
@@ -26,41 +26,19 @@ module OpenC3
|
|
26
26
|
# Matches ScriptRunner.vue const TEMP_FOLDER
|
27
27
|
TEMP_FOLDER = '__TEMP__'
|
28
28
|
|
29
|
-
def self.all(scope, path_matchers, include_temp: false)
|
30
|
-
|
31
|
-
modified = []
|
32
|
-
temp = []
|
29
|
+
def self.all(scope, path_matchers, target: nil, include_temp: false)
|
30
|
+
target = target.upcase if target
|
33
31
|
|
34
32
|
bucket = Bucket.getClient()
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
# DEFAULT/targets_modified/__TEMP__/YYYY_MM_DD_HH_MM_SS_mmm_temp.rb
|
42
|
-
if split_key[2] == TEMP_FOLDER
|
43
|
-
temp << split_key[2..-1].join('/') if include_temp
|
44
|
-
next
|
45
|
-
end
|
46
|
-
|
47
|
-
if path_matchers
|
48
|
-
found = false
|
49
|
-
path_matchers.each do |path|
|
50
|
-
if split_key.include?(path)
|
51
|
-
found = true
|
52
|
-
break
|
53
|
-
end
|
54
|
-
end
|
55
|
-
next unless found
|
56
|
-
end
|
57
|
-
result_no_scope_or_target_folder = split_key[2..-1].join('/')
|
58
|
-
if object.key.include?("#{scope}/targets_modified")
|
59
|
-
modified << result_no_scope_or_target_folder
|
60
|
-
else
|
61
|
-
result << result_no_scope_or_target_folder
|
62
|
-
end
|
33
|
+
if target
|
34
|
+
prefix = "#{scope}/targets/#{target}/"
|
35
|
+
modified_prefix = "#{scope}/targets_modified/#{target}/"
|
36
|
+
else
|
37
|
+
prefix = "#{scope}/targets/"
|
38
|
+
modified_prefix = "#{scope}/targets_modified/"
|
63
39
|
end
|
40
|
+
result, _ = remote_target_files(bucket_client: bucket, prefix: prefix, include_temp: false, path_matchers: path_matchers)
|
41
|
+
modified, temp = remote_target_files(bucket_client: bucket, prefix: modified_prefix, include_temp: include_temp, path_matchers: path_matchers)
|
64
42
|
|
65
43
|
# Add in local targets_modified if present
|
66
44
|
if ENV['OPENC3_LOCAL_MODE']
|
@@ -172,5 +150,38 @@ module OpenC3
|
|
172
150
|
)
|
173
151
|
true
|
174
152
|
end
|
153
|
+
|
154
|
+
# protected
|
155
|
+
|
156
|
+
def self.remote_target_files(bucket_client:, prefix:, include_temp: false, path_matchers: nil)
|
157
|
+
result = []
|
158
|
+
temp = []
|
159
|
+
resp = bucket_client.list_objects(
|
160
|
+
bucket: ENV['OPENC3_CONFIG_BUCKET'],
|
161
|
+
prefix: prefix,
|
162
|
+
)
|
163
|
+
resp.each do |object|
|
164
|
+
split_key = object.key.split('/')
|
165
|
+
# DEFAULT/targets_modified/__TEMP__/YYYY_MM_DD_HH_MM_SS_mmm_temp.rb
|
166
|
+
if split_key[2] == TEMP_FOLDER
|
167
|
+
temp << split_key[2..-1].join('/') if include_temp
|
168
|
+
next
|
169
|
+
end
|
170
|
+
|
171
|
+
if path_matchers
|
172
|
+
found = false
|
173
|
+
path_matchers.each do |path|
|
174
|
+
if split_key.include?(path)
|
175
|
+
found = true
|
176
|
+
break
|
177
|
+
end
|
178
|
+
end
|
179
|
+
next unless found
|
180
|
+
end
|
181
|
+
result_no_scope_or_target_folder = split_key[2..-1].join('/')
|
182
|
+
result << result_no_scope_or_target_folder
|
183
|
+
end
|
184
|
+
return result, temp
|
185
|
+
end
|
175
186
|
end
|
176
187
|
end
|
data/lib/openc3/version.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# encoding: ascii-8bit
|
2
2
|
|
3
|
-
OPENC3_VERSION = '5.5.
|
3
|
+
OPENC3_VERSION = '5.5.1'
|
4
4
|
module OpenC3
|
5
5
|
module Version
|
6
6
|
MAJOR = '5'
|
7
7
|
MINOR = '5'
|
8
|
-
PATCH = '
|
9
|
-
OTHER = '
|
10
|
-
BUILD = '
|
8
|
+
PATCH = '1'
|
9
|
+
OTHER = ''
|
10
|
+
BUILD = 'a1e84e1850e06f2c01726e03372e2872137b3852'
|
11
11
|
end
|
12
|
-
VERSION = '5.5.
|
13
|
-
GEM_VERSION = '5.5.
|
12
|
+
VERSION = '5.5.1'
|
13
|
+
GEM_VERSION = '5.5.1'
|
14
14
|
end
|