openc3 7.3.0 → 7.4.1
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Guardfile +0 -0
- data/LICENSE.md +0 -0
- data/README.md +0 -0
- data/Rakefile +0 -0
- data/bin/openc3cli +139 -0
- data/bin/pipuninstall +0 -0
- data/bin/uvinstall +10 -3
- data/data/config/_array_params.yaml +0 -0
- data/data/config/_canvas_values.yaml +0 -0
- data/data/config/_graph_params.yaml +0 -0
- data/data/config/_id_items.yaml +0 -0
- data/data/config/_id_params.yaml +0 -0
- data/data/config/_interfaces.yaml +0 -0
- data/data/config/_items.yaml +0 -0
- data/data/config/_params.yaml +0 -0
- data/data/config/command.yaml +0 -0
- data/data/config/command_modifiers.yaml +2 -0
- data/data/config/command_telemetry.yaml +0 -0
- data/data/config/conversions.yaml +0 -0
- data/data/config/graph_settings.yaml +0 -0
- data/data/config/interface_modifiers.yaml +103 -1
- data/data/config/item_modifiers.yaml +0 -0
- data/data/config/microservice.yaml +5 -2
- data/data/config/param_item_modifiers.yaml +0 -0
- data/data/config/parameter_modifiers.yaml +0 -0
- data/data/config/plugins.yaml +0 -0
- data/data/config/processors.yaml +0 -0
- data/data/config/protocols.yaml +0 -0
- data/data/config/screen.yaml +0 -0
- data/data/config/settings.yaml +0 -0
- data/data/config/table_manager.yaml +0 -0
- data/data/config/table_parameter_modifiers.yaml +0 -0
- data/data/config/target.yaml +0 -0
- data/data/config/target_config.yaml +0 -0
- data/data/config/telemetry.yaml +0 -0
- data/data/config/telemetry_modifiers.yaml +2 -0
- data/data/config/tool.yaml +0 -0
- data/data/config/unknown.yaml +0 -0
- data/data/config/widgets.yaml +6 -0
- data/data/xtce_schemas/SpaceSystem_20180204.xsd +0 -0
- data/data/xtce_schemas/xml.xsd +0 -0
- data/ext/openc3/ext/array/array.c +0 -0
- data/ext/openc3/ext/array/extconf.rb +0 -0
- data/ext/openc3/ext/buffered_file/buffered_file.c +0 -0
- data/ext/openc3/ext/buffered_file/extconf.rb +0 -0
- data/ext/openc3/ext/burst_protocol/burst_protocol.c +6 -0
- data/ext/openc3/ext/burst_protocol/extconf.rb +0 -0
- data/ext/openc3/ext/config_parser/config_parser.c +0 -0
- data/ext/openc3/ext/config_parser/extconf.rb +0 -0
- data/ext/openc3/ext/crc/crc.c +0 -0
- data/ext/openc3/ext/crc/extconf.rb +0 -0
- data/ext/openc3/ext/openc3_io/extconf.rb +0 -0
- data/ext/openc3/ext/openc3_io/openc3_io.c +0 -0
- data/ext/openc3/ext/packet/extconf.rb +0 -0
- data/ext/openc3/ext/packet/packet.c +0 -0
- data/ext/openc3/ext/platform/extconf.rb +0 -0
- data/ext/openc3/ext/platform/platform.c +4 -1
- data/ext/openc3/ext/polynomial_conversion/extconf.rb +0 -0
- data/ext/openc3/ext/polynomial_conversion/polynomial_conversion.c +0 -0
- data/ext/openc3/ext/string/extconf.rb +0 -0
- data/ext/openc3/ext/string/string.c +0 -0
- data/ext/openc3/ext/structure/structure.c +49 -17
- data/ext/openc3/ext/tabbed_plots_config/extconf.rb +0 -0
- data/ext/openc3/ext/tabbed_plots_config/tabbed_plots_config.c +0 -0
- data/ext/openc3/ext/telemetry/extconf.rb +0 -0
- data/ext/openc3/ext/telemetry/telemetry.c +0 -0
- data/lib/cosmos.rb +0 -0
- data/lib/cosmosc2.rb +0 -0
- data/lib/openc3/accessors/accessor.rb +0 -0
- data/lib/openc3/accessors/binary_accessor.rb +63 -9
- data/lib/openc3/accessors/cbor_accessor.rb +0 -0
- data/lib/openc3/accessors/form_accessor.rb +0 -0
- data/lib/openc3/accessors/html_accessor.rb +0 -0
- data/lib/openc3/accessors/http_accessor.rb +0 -0
- data/lib/openc3/accessors/json_accessor.rb +0 -0
- data/lib/openc3/accessors/template_accessor.rb +0 -0
- data/lib/openc3/accessors/xml_accessor.rb +0 -0
- data/lib/openc3/accessors.rb +0 -0
- data/lib/openc3/api/README.md +0 -0
- data/lib/openc3/api/api.rb +0 -0
- data/lib/openc3/api/authorized_api.rb +0 -0
- data/lib/openc3/api/calendar_api.rb +0 -0
- data/lib/openc3/api/cmd_api.rb +1 -1
- data/lib/openc3/api/config_api.rb +0 -0
- data/lib/openc3/api/interface_api.rb +0 -0
- data/lib/openc3/api/limits_api.rb +0 -0
- data/lib/openc3/api/metrics_api.rb +1 -1
- data/lib/openc3/api/offline_access_api.rb +1 -1
- data/lib/openc3/api/router_api.rb +0 -0
- data/lib/openc3/api/settings_api.rb +22 -18
- data/lib/openc3/api/stash_api.rb +1 -1
- data/lib/openc3/api/target_api.rb +0 -0
- data/lib/openc3/api/tlm_api.rb +19 -14
- data/lib/openc3/bridge/bridge.rb +0 -0
- data/lib/openc3/bridge/bridge_config.rb +0 -0
- data/lib/openc3/bridge/bridge_interface_thread.rb +0 -0
- data/lib/openc3/bridge/bridge_router_thread.rb +0 -0
- data/lib/openc3/ccsds/ccsds_packet.rb +0 -0
- data/lib/openc3/ccsds/ccsds_parser.rb +0 -0
- data/lib/openc3/config/config_parser.rb +78 -17
- data/lib/openc3/config/meta_config_parser.rb +0 -0
- data/lib/openc3/conversions/bit_reverse_conversion.rb +0 -0
- data/lib/openc3/conversions/conversion.rb +0 -0
- data/lib/openc3/conversions/generic_conversion.rb +0 -0
- data/lib/openc3/conversions/ip_read_conversion.rb +0 -0
- data/lib/openc3/conversions/ip_write_conversion.rb +0 -0
- data/lib/openc3/conversions/object_read_conversion.rb +0 -0
- data/lib/openc3/conversions/object_write_conversion.rb +0 -0
- data/lib/openc3/conversions/packet_time_formatted_conversion.rb +0 -0
- data/lib/openc3/conversions/packet_time_seconds_conversion.rb +0 -0
- data/lib/openc3/conversions/polynomial_conversion.rb +0 -0
- data/lib/openc3/conversions/processor_conversion.rb +0 -0
- data/lib/openc3/conversions/received_count_conversion.rb +0 -0
- data/lib/openc3/conversions/received_time_formatted_conversion.rb +0 -0
- data/lib/openc3/conversions/received_time_seconds_conversion.rb +0 -0
- data/lib/openc3/conversions/segmented_polynomial_conversion.rb +0 -0
- data/lib/openc3/conversions/unix_time_conversion.rb +0 -0
- data/lib/openc3/conversions/unix_time_formatted_conversion.rb +0 -0
- data/lib/openc3/conversions/unix_time_seconds_conversion.rb +0 -0
- data/lib/openc3/conversions.rb +0 -0
- data/lib/openc3/core_ext/array.rb +0 -0
- data/lib/openc3/core_ext/binding.rb +0 -0
- data/lib/openc3/core_ext/class.rb +0 -0
- data/lib/openc3/core_ext/exception.rb +0 -0
- data/lib/openc3/core_ext/faraday.rb +0 -0
- data/lib/openc3/core_ext/file.rb +0 -0
- data/lib/openc3/core_ext/io.rb +0 -0
- data/lib/openc3/core_ext/kernel.rb +0 -0
- data/lib/openc3/core_ext/math.rb +0 -0
- data/lib/openc3/core_ext/matrix.rb +0 -0
- data/lib/openc3/core_ext/objectspace.rb +0 -0
- data/lib/openc3/core_ext/openc3_io.rb +0 -0
- data/lib/openc3/core_ext/range.rb +0 -0
- data/lib/openc3/core_ext/socket.rb +0 -0
- data/lib/openc3/core_ext/string.rb +0 -0
- data/lib/openc3/core_ext/stringio.rb +0 -0
- data/lib/openc3/core_ext/tempfile.rb +0 -0
- data/lib/openc3/core_ext/time.rb +0 -0
- data/lib/openc3/core_ext.rb +0 -0
- data/lib/openc3/ext/.keep +0 -0
- data/lib/openc3/interfaces/file_interface.rb +0 -0
- data/lib/openc3/interfaces/http_client_interface.rb +0 -0
- data/lib/openc3/interfaces/http_server_interface.rb +0 -0
- data/lib/openc3/interfaces/interface.rb +5 -4
- data/lib/openc3/interfaces/mqtt_interface.rb +0 -0
- data/lib/openc3/interfaces/mqtt_stream_interface.rb +2 -0
- data/lib/openc3/interfaces/protocols/burst_protocol.rb +27 -0
- data/lib/openc3/interfaces/protocols/cmd_response_protocol.rb +0 -0
- data/lib/openc3/interfaces/protocols/cobs_protocol.rb +0 -0
- data/lib/openc3/interfaces/protocols/crc_protocol.rb +0 -0
- data/lib/openc3/interfaces/protocols/fixed_protocol.rb +0 -0
- data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +0 -0
- data/lib/openc3/interfaces/protocols/length_protocol.rb +7 -0
- data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +7 -0
- data/lib/openc3/interfaces/protocols/protocol.rb +12 -4
- data/lib/openc3/interfaces/protocols/slip_protocol.rb +0 -0
- data/lib/openc3/interfaces/protocols/template_protocol.rb +2 -2
- data/lib/openc3/interfaces/protocols/terminated_protocol.rb +0 -0
- data/lib/openc3/interfaces/serial_interface.rb +2 -0
- data/lib/openc3/interfaces/simulated_target_interface.rb +0 -0
- data/lib/openc3/interfaces/stream_interface.rb +0 -0
- data/lib/openc3/interfaces/tcpip_client_interface.rb +2 -0
- data/lib/openc3/interfaces/tcpip_server_interface.rb +0 -0
- data/lib/openc3/interfaces/udp_interface.rb +16 -5
- data/lib/openc3/interfaces.rb +0 -0
- data/lib/openc3/io/buffered_file.rb +0 -0
- data/lib/openc3/io/io_multiplexer.rb +0 -0
- data/lib/openc3/io/json_api.rb +3 -2
- data/lib/openc3/io/json_api_object.rb +24 -3
- data/lib/openc3/io/json_drb.rb +20 -4
- data/lib/openc3/io/json_drb_object.rb +3 -2
- data/lib/openc3/io/json_drb_rack.rb +0 -0
- data/lib/openc3/io/json_rpc.rb +0 -0
- data/lib/openc3/io/posix_serial_driver.rb +0 -0
- data/lib/openc3/io/serial_driver.rb +0 -0
- data/lib/openc3/io/stderr.rb +0 -0
- data/lib/openc3/io/stdout.rb +0 -0
- data/lib/openc3/io/udp_sockets.rb +38 -8
- data/lib/openc3/io/win32_serial_driver.rb +1 -0
- data/lib/openc3/logs/buffered_packet_log_reader.rb +0 -0
- data/lib/openc3/logs/buffered_packet_log_writer.rb +0 -0
- data/lib/openc3/logs/log_writer.rb +0 -0
- data/lib/openc3/logs/packet_log_constants.rb +0 -0
- data/lib/openc3/logs/packet_log_reader.rb +23 -2
- data/lib/openc3/logs/packet_log_writer.rb +5 -0
- data/lib/openc3/logs/stream_log.rb +1 -1
- data/lib/openc3/logs/stream_log_pair.rb +0 -0
- data/lib/openc3/logs/text_log_writer.rb +0 -0
- data/lib/openc3/logs.rb +0 -0
- data/lib/openc3/microservices/cleanup_microservice.rb +0 -0
- data/lib/openc3/microservices/decom_common.rb +0 -0
- data/lib/openc3/microservices/decom_microservice.rb +0 -0
- data/lib/openc3/microservices/interface_decom_common.rb +2 -1
- data/lib/openc3/microservices/interface_microservice.rb +72 -25
- data/lib/openc3/microservices/log_microservice.rb +0 -0
- data/lib/openc3/microservices/microservice.rb +3 -3
- data/lib/openc3/microservices/multi_microservice.rb +1 -1
- data/lib/openc3/microservices/periodic_microservice.rb +2 -2
- data/lib/openc3/microservices/plugin_microservice.rb +0 -0
- data/lib/openc3/microservices/queue_microservice.rb +0 -0
- data/lib/openc3/microservices/router_microservice.rb +0 -0
- data/lib/openc3/microservices/scope_cleanup_microservice.rb +0 -0
- data/lib/openc3/microservices/text_log_microservice.rb +0 -0
- data/lib/openc3/migrations/20220420190000_log_stuff.rb +0 -0
- data/lib/openc3/migrations/20221202214600_add_target_names.rb +0 -0
- data/lib/openc3/migrations/20221210174900_convert_to_multi.rb +0 -0
- data/lib/openc3/migrations/20230915000002_no_scope_log_messages.rb +0 -0
- data/lib/openc3/migrations/20231022000000_tlm_viewer_config.rb +0 -0
- data/lib/openc3/migrations/20241208080000_no_critical_cmd.rb +0 -0
- data/lib/openc3/migrations/20241208080001_no_trigger_group.rb +0 -0
- data/lib/openc3/migrations/20250108060000_news_feed.rb +0 -0
- data/lib/openc3/migrations/20250402000000_periodic_only_default.rb +0 -0
- data/lib/openc3/migrations/20260203000000_remove_store_id.rb +0 -0
- data/lib/openc3/migrations/20260204000000_remove_decom_reducer.rb +0 -0
- data/lib/openc3/migrations/20260701000000_updated_at_to_int.rb +0 -0
- data/lib/openc3/migrations/20260704000000_default_bridge.rb +38 -0
- data/lib/openc3/migrations/20260731000000_system_health_json.rb +11 -0
- data/lib/openc3/models/activity_model.rb +0 -0
- data/lib/openc3/models/auth_model.rb +35 -7
- data/lib/openc3/models/bridge_model.rb +133 -0
- data/lib/openc3/models/config_keyword_model.rb +63 -0
- data/lib/openc3/models/cvt_model.rb +0 -0
- data/lib/openc3/models/db_sharded_model.rb +0 -0
- data/lib/openc3/models/environment_model.rb +0 -0
- data/lib/openc3/models/gem_model.rb +12 -9
- data/lib/openc3/models/host_interface_microservice_model.rb +114 -0
- data/lib/openc3/models/info_model.rb +0 -0
- data/lib/openc3/models/interface_model.rb +178 -19
- data/lib/openc3/models/interface_status_model.rb +0 -0
- data/lib/openc3/models/metadata_model.rb +0 -0
- data/lib/openc3/models/metric_model.rb +0 -0
- data/lib/openc3/models/microservice_model.rb +9 -22
- data/lib/openc3/models/microservice_status_model.rb +0 -0
- data/lib/openc3/models/migration_model.rb +0 -0
- data/lib/openc3/models/model.rb +0 -0
- data/lib/openc3/models/news_model.rb +0 -0
- data/lib/openc3/models/note_model.rb +0 -0
- data/lib/openc3/models/offline_access_model.rb +0 -0
- data/lib/openc3/models/ping_model.rb +0 -0
- data/lib/openc3/models/plugin_model.rb +9 -2
- data/lib/openc3/models/plugin_store_model.rb +0 -0
- data/lib/openc3/models/process_status_model.rb +0 -0
- data/lib/openc3/models/python_package_model.rb +4 -4
- data/lib/openc3/models/queue_model.rb +0 -0
- data/lib/openc3/models/reaction_model.rb +6 -3
- data/lib/openc3/models/reingest_job_model.rb +0 -0
- data/lib/openc3/models/router_model.rb +0 -0
- data/lib/openc3/models/router_status_model.rb +0 -0
- data/lib/openc3/models/scope_model.rb +51 -26
- data/lib/openc3/models/script_engine_model.rb +0 -0
- data/lib/openc3/models/script_status_model.rb +0 -0
- data/lib/openc3/models/secret_model.rb +0 -0
- data/lib/openc3/models/setting_model.rb +548 -3
- data/lib/openc3/models/sorted_model.rb +0 -0
- data/lib/openc3/models/stash_model.rb +0 -0
- data/lib/openc3/models/target_model.rb +30 -25
- data/lib/openc3/models/timeline_model.rb +0 -0
- data/lib/openc3/models/tool_config_model.rb +0 -0
- data/lib/openc3/models/tool_model.rb +1 -0
- data/lib/openc3/models/trigger_group_model.rb +0 -0
- data/lib/openc3/models/trigger_model.rb +20 -0
- data/lib/openc3/models/widget_model.rb +0 -0
- data/lib/openc3/operators/microservice_operator.rb +64 -22
- data/lib/openc3/operators/operator.rb +4 -4
- data/lib/openc3/packets/binary_accessor.rb +0 -0
- data/lib/openc3/packets/command_validator.rb +0 -0
- data/lib/openc3/packets/commands.rb +0 -0
- data/lib/openc3/packets/json_packet.rb +0 -0
- data/lib/openc3/packets/limits.rb +0 -0
- data/lib/openc3/packets/limits_response.rb +0 -0
- data/lib/openc3/packets/packet.rb +4 -0
- data/lib/openc3/packets/packet_config.rb +36 -1
- data/lib/openc3/packets/packet_item.rb +0 -0
- data/lib/openc3/packets/packet_item_limits.rb +0 -0
- data/lib/openc3/packets/parsers/format_string_parser.rb +0 -0
- data/lib/openc3/packets/parsers/limits_parser.rb +0 -0
- data/lib/openc3/packets/parsers/limits_response_parser.rb +0 -0
- data/lib/openc3/packets/parsers/packet_item_parser.rb +0 -0
- data/lib/openc3/packets/parsers/packet_parser.rb +0 -0
- data/lib/openc3/packets/parsers/processor_parser.rb +0 -0
- data/lib/openc3/packets/parsers/state_parser.rb +0 -0
- data/lib/openc3/packets/parsers/xtce_converter.rb +0 -0
- data/lib/openc3/packets/parsers/xtce_parser.rb +8 -1
- data/lib/openc3/packets/structure.rb +7 -1
- data/lib/openc3/packets/structure_item.rb +0 -0
- data/lib/openc3/packets/telemetry.rb +0 -0
- data/lib/openc3/processors/processor.rb +2 -0
- data/lib/openc3/processors/statistics_processor.rb +0 -0
- data/lib/openc3/processors/watermark_processor.rb +0 -0
- data/lib/openc3/processors.rb +0 -0
- data/lib/openc3/script/api_shared.rb +0 -0
- data/lib/openc3/script/autonomic.rb +12 -12
- data/lib/openc3/script/calendar.rb +0 -0
- data/lib/openc3/script/commands.rb +0 -0
- data/lib/openc3/script/critical_cmd.rb +0 -0
- data/lib/openc3/script/exceptions.rb +0 -0
- data/lib/openc3/script/extract.rb +0 -0
- data/lib/openc3/script/limits.rb +0 -0
- data/lib/openc3/script/metadata.rb +0 -0
- data/lib/openc3/script/packages.rb +0 -0
- data/lib/openc3/script/plugins.rb +0 -0
- data/lib/openc3/script/queue.rb +0 -0
- data/lib/openc3/script/screen.rb +0 -0
- data/lib/openc3/script/script.rb +2 -2
- data/lib/openc3/script/script_runner.rb +0 -0
- data/lib/openc3/script/storage.rb +4 -4
- data/lib/openc3/script/suite.rb +0 -0
- data/lib/openc3/script/suite_results.rb +0 -0
- data/lib/openc3/script/suite_runner.rb +26 -0
- data/lib/openc3/script/tables.rb +5 -1
- data/lib/openc3/script/telemetry.rb +9 -3
- data/lib/openc3/script/web_socket_api.rb +218 -145
- data/lib/openc3/script.rb +0 -0
- data/lib/openc3/script_engines/script_engine.rb +0 -0
- data/lib/openc3/streams/mqtt_stream.rb +0 -0
- data/lib/openc3/streams/serial_stream.rb +0 -0
- data/lib/openc3/streams/stream.rb +0 -0
- data/lib/openc3/streams/tcpip_client_stream.rb +0 -0
- data/lib/openc3/streams/tcpip_socket_stream.rb +0 -0
- data/lib/openc3/streams/web_socket_client_stream.rb +0 -0
- data/lib/openc3/subpacketizers/subpacketizer.rb +0 -0
- data/lib/openc3/system/system.rb +0 -0
- data/lib/openc3/system/target.rb +0 -0
- data/lib/openc3/system.rb +0 -0
- data/lib/openc3/tools/cmd_tlm_server/api.rb +0 -0
- data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +7 -0
- data/lib/openc3/tools/table_manager/table.rb +0 -0
- data/lib/openc3/tools/table_manager/table_config.rb +0 -0
- data/lib/openc3/tools/table_manager/table_item.rb +0 -0
- data/lib/openc3/tools/table_manager/table_item_parser.rb +0 -0
- data/lib/openc3/tools/table_manager/table_manager_core.rb +0 -0
- data/lib/openc3/tools/table_manager/table_parser.rb +0 -0
- data/lib/openc3/tools/test_runner/test.rb +0 -0
- data/lib/openc3/top_level.rb +1 -1
- data/lib/openc3/topics/autonomic_topic.rb +0 -0
- data/lib/openc3/topics/calendar_topic.rb +0 -0
- data/lib/openc3/topics/command_decom_topic.rb +0 -0
- data/lib/openc3/topics/command_topic.rb +0 -0
- data/lib/openc3/topics/config_topic.rb +0 -0
- data/lib/openc3/topics/decom_interface_topic.rb +2 -1
- data/lib/openc3/topics/interface_topic.rb +8 -2
- data/lib/openc3/topics/limits_event_topic.rb +0 -0
- data/lib/openc3/topics/notebook_topic.rb +0 -0
- data/lib/openc3/topics/queue_topic.rb +0 -0
- data/lib/openc3/topics/router_topic.rb +6 -1
- data/lib/openc3/topics/system_events_topic.rb +0 -0
- data/lib/openc3/topics/telemetry_decom_topic.rb +0 -0
- data/lib/openc3/topics/telemetry_topic.rb +0 -0
- data/lib/openc3/topics/timeline_topic.rb +0 -0
- data/lib/openc3/topics/topic.rb +0 -0
- data/lib/openc3/utilities/authentication.rb +4 -4
- data/lib/openc3/utilities/authorization.rb +0 -0
- data/lib/openc3/utilities/aws_bucket.rb +2 -2
- data/lib/openc3/utilities/bucket.rb +1 -1
- data/lib/openc3/utilities/bucket_file_cache.rb +55 -22
- data/lib/openc3/utilities/bucket_require.rb +0 -0
- data/lib/openc3/utilities/bucket_utilities.rb +3 -4
- data/lib/openc3/utilities/cli_generator.rb +0 -0
- data/lib/openc3/utilities/cmd_log.rb +0 -0
- data/lib/openc3/utilities/config_overlay.rb +75 -0
- data/lib/openc3/utilities/cosmos_rails_formatter.rb +0 -0
- data/lib/openc3/utilities/crc.rb +59 -32
- data/lib/openc3/utilities/csv.rb +0 -0
- data/lib/openc3/utilities/ctrf.rb +0 -0
- data/lib/openc3/utilities/env_helper.rb +0 -0
- data/lib/openc3/utilities/local_bucket.rb +0 -0
- data/lib/openc3/utilities/local_mode.rb +32 -6
- data/lib/openc3/utilities/logger.rb +30 -8
- data/lib/openc3/utilities/message_log.rb +0 -0
- data/lib/openc3/utilities/metric.rb +0 -0
- data/lib/openc3/utilities/migration.rb +0 -0
- data/lib/openc3/utilities/open_telemetry.rb +0 -0
- data/lib/openc3/utilities/process_manager.rb +0 -0
- data/lib/openc3/utilities/pypi_url.rb +0 -0
- data/lib/openc3/utilities/python_proxy.rb +0 -0
- data/lib/openc3/utilities/python_venv.rb +135 -0
- data/lib/openc3/utilities/quaternion.rb +0 -0
- data/lib/openc3/utilities/questdb_client.rb +29 -46
- data/lib/openc3/utilities/redis_secrets.rb +0 -0
- data/lib/openc3/utilities/reingest_job.rb +0 -0
- data/lib/openc3/utilities/ruby_lex_utils.rb +0 -0
- data/lib/openc3/utilities/rubygems_url.rb +39 -0
- data/lib/openc3/utilities/running_script.rb +24 -55
- data/lib/openc3/utilities/s3_autoload.rb +5 -5
- data/lib/openc3/utilities/script.rb +12 -8
- data/lib/openc3/utilities/secrets.rb +48 -3
- data/lib/openc3/utilities/simulated_target.rb +0 -0
- data/lib/openc3/utilities/sleeper.rb +0 -0
- data/lib/openc3/utilities/store.rb +0 -0
- data/lib/openc3/utilities/store_autoload.rb +2 -2
- data/lib/openc3/utilities/store_queued.rb +0 -0
- data/lib/openc3/utilities/target_file.rb +18 -1
- data/lib/openc3/utilities/thread_manager.rb +0 -0
- data/lib/openc3/utilities/throttle.rb +0 -0
- data/lib/openc3/utilities/zip.rb +0 -0
- data/lib/openc3/utilities.rb +0 -0
- data/lib/openc3/version.rb +6 -6
- data/lib/openc3/win32/excel.rb +0 -0
- data/lib/openc3/win32/win32.rb +0 -0
- data/lib/openc3/win32/win32_main.rb +0 -0
- data/lib/openc3.rb +2 -2
- data/tasks/gemfile_stats.rake +0 -0
- data/tasks/spec.rake +0 -0
- data/templates/command_validator/command_validator.py +0 -0
- data/templates/command_validator/command_validator.rb +0 -0
- data/templates/conversion/conversion.py +0 -0
- data/templates/conversion/conversion.rb +0 -0
- data/templates/limits_response/response.py +0 -0
- data/templates/limits_response/response.rb +0 -0
- data/templates/microservice/microservices/TEMPLATE/microservice.py +0 -0
- data/templates/microservice/microservices/TEMPLATE/microservice.rb +0 -0
- data/templates/plugin/.gitignore +0 -0
- data/templates/plugin/LICENSE.md +0 -0
- data/templates/plugin/README.md +0 -0
- data/templates/plugin/Rakefile +0 -0
- data/templates/plugin/plugin.gemspec +0 -0
- data/templates/plugin/plugin.txt +0 -0
- data/templates/processor/processor.py +1 -1
- data/templates/processor/processor.rb +0 -0
- data/templates/target/targets/TARGET/cmd_tlm/cmd.txt +0 -0
- data/templates/target/targets/TARGET/cmd_tlm/tlm.txt +0 -0
- data/templates/target/targets/TARGET/lib/target.py +0 -0
- data/templates/target/targets/TARGET/lib/target.rb +0 -0
- data/templates/target/targets/TARGET/procedures/procedure.py +0 -0
- data/templates/target/targets/TARGET/procedures/procedure.rb +0 -0
- data/templates/target/targets/TARGET/public/README.txt +0 -0
- data/templates/target/targets/TARGET/screens/status.txt +0 -0
- data/templates/target/targets/TARGET/target.txt +0 -0
- data/templates/tool_angular/.editorconfig +0 -0
- data/templates/tool_angular/.gitignore +0 -0
- data/templates/tool_angular/.vscode/extensions.json +0 -0
- data/templates/tool_angular/.vscode/launch.json +0 -0
- data/templates/tool_angular/.vscode/tasks.json +0 -0
- data/templates/tool_angular/angular.json +0 -0
- data/templates/tool_angular/extra-webpack.config.js +0 -0
- data/templates/tool_angular/package.json +2 -2
- data/templates/tool_angular/src/app/app-routing.module.ts +0 -0
- data/templates/tool_angular/src/app/app.component.html +0 -0
- data/templates/tool_angular/src/app/app.component.scss +0 -0
- data/templates/tool_angular/src/app/app.component.spec.ts +0 -0
- data/templates/tool_angular/src/app/app.component.ts +0 -0
- data/templates/tool_angular/src/app/app.module.ts +0 -0
- data/templates/tool_angular/src/app/custom-overlay-container.ts +0 -0
- data/templates/tool_angular/src/app/empty-route/empty-route.component.ts +0 -0
- data/templates/tool_angular/src/app/openc3-api.d.ts +0 -0
- data/templates/tool_angular/src/assets/.gitkeep +0 -0
- data/templates/tool_angular/src/environments/environment.prod.ts +0 -0
- data/templates/tool_angular/src/environments/environment.ts +0 -0
- data/templates/tool_angular/src/favicon.ico +0 -0
- data/templates/tool_angular/src/index.html +0 -0
- data/templates/tool_angular/src/main.single-spa.ts +0 -0
- data/templates/tool_angular/src/single-spa/asset-url.ts +0 -0
- data/templates/tool_angular/src/single-spa/single-spa-props.ts +0 -0
- data/templates/tool_angular/src/styles.scss +0 -0
- data/templates/tool_angular/tsconfig.app.json +0 -0
- data/templates/tool_angular/tsconfig.json +0 -0
- data/templates/tool_angular/tsconfig.spec.json +0 -0
- data/templates/tool_react/.eslintrc +0 -0
- data/templates/tool_react/.gitignore +0 -0
- data/templates/tool_react/.prettierignore +0 -0
- data/templates/tool_react/babel.config.json +0 -0
- data/templates/tool_react/jest.config.js +0 -0
- data/templates/tool_react/package.json +1 -1
- data/templates/tool_react/src/openc3-tool_name.js +0 -0
- data/templates/tool_react/src/root.component.js +0 -0
- data/templates/tool_react/src/root.component.test.js +0 -0
- data/templates/tool_react/webpack.config.js +0 -0
- data/templates/tool_svelte/.gitignore +0 -0
- data/templates/tool_svelte/.prettierignore +0 -0
- data/templates/tool_svelte/.prettierrc.js +0 -0
- data/templates/tool_svelte/babel.config.js +0 -0
- data/templates/tool_svelte/build/smui.css +0 -0
- data/templates/tool_svelte/jest.config.js +0 -0
- data/templates/tool_svelte/package.json +1 -1
- data/templates/tool_svelte/rollup.config.js +0 -0
- data/templates/tool_svelte/src/App.svelte +0 -0
- data/templates/tool_svelte/src/App.test.js +0 -0
- data/templates/tool_svelte/src/theme/_smui-theme.scss +0 -0
- data/templates/tool_svelte/src/tool_name.js +0 -0
- data/templates/tool_vue/.env.standalone +0 -0
- data/templates/tool_vue/.gitignore +0 -0
- data/templates/tool_vue/.prettierrc.cjs +0 -0
- data/templates/tool_vue/eslint.config.mjs +0 -0
- data/templates/tool_vue/jsconfig.json +0 -0
- data/templates/tool_vue/package.json +3 -3
- data/templates/tool_vue/src/App.vue +0 -0
- data/templates/tool_vue/src/main.js +0 -0
- data/templates/tool_vue/src/router.js +0 -0
- data/templates/tool_vue/src/tools/tool_name/tool_name.vue +0 -0
- data/templates/tool_vue/vite.config.js +1 -1
- data/templates/widget/.prettierrc.cjs +0 -0
- data/templates/widget/LICENSE.md +0 -0
- data/templates/widget/Rakefile +0 -0
- data/templates/widget/package.json +2 -2
- data/templates/widget/src/Widget.vue +0 -0
- data/templates/widget/vite.config.js +0 -0
- metadata +56 -9
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
3
|
+
# Copyright 2026 OpenC3, Inc.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This program is distributed in the hope that it will be useful,
|
|
7
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
8
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
9
|
+
# See LICENSE.md for more details.
|
|
10
|
+
#
|
|
11
|
+
# This file may also be used under the terms of a commercial license
|
|
12
|
+
# if purchased from OpenC3, Inc.
|
|
13
|
+
|
|
14
|
+
require 'open3'
|
|
15
|
+
require 'openc3/models/target_model'
|
|
16
|
+
require 'openc3/utilities/target_file'
|
|
17
|
+
|
|
18
|
+
module OpenC3
|
|
19
|
+
module PythonVenv
|
|
20
|
+
PLUGIN_VENVS_DIR = '/gems/plugin_venvs'
|
|
21
|
+
|
|
22
|
+
# Shared user site-packages directory used by plugins that predate the
|
|
23
|
+
# per-plugin UV venvs. Every caller that falls back off a plugin venv points
|
|
24
|
+
# PYTHONUSERBASE here, so keep it in one place rather than repeating the
|
|
25
|
+
# literal in script.rb, running_script.rb, microservice_operator.rb and
|
|
26
|
+
# microservice_model.rb.
|
|
27
|
+
DEFAULT_PYTHONUSERBASE = '/gems/python_packages'
|
|
28
|
+
|
|
29
|
+
# Packages a plugin venv must never supply itself. The scripts and
|
|
30
|
+
# microservices that use a plugin venv still run the base venv's Python
|
|
31
|
+
# binary, and CPython searches every PYTHONPATH entry ahead of the running
|
|
32
|
+
# venv's own site-packages. A plugin that declares openc3 as a dependency
|
|
33
|
+
# would therefore shadow the system library with its own copy, silently
|
|
34
|
+
# swapping the API client the script talks to COSMOS with. Reordering
|
|
35
|
+
# PYTHONPATH cannot fix this - the whole variable outranks the base venv -
|
|
36
|
+
# so the copy is removed at install time instead.
|
|
37
|
+
RESERVED_PACKAGES = ['openc3'].freeze
|
|
38
|
+
|
|
39
|
+
# Resolve a script's plugin venv and expose its packages to a child process.
|
|
40
|
+
# Returns the venv path when configured, or nil when the system environment
|
|
41
|
+
# should be used.
|
|
42
|
+
def self.configure_for_script(environment, name:, scope:, python_venv: nil)
|
|
43
|
+
venv_dir = resolve_script_venv(name: name, scope: scope, python_venv: python_venv)
|
|
44
|
+
return nil unless venv_dir
|
|
45
|
+
|
|
46
|
+
configure_environment(environment, venv_dir)
|
|
47
|
+
venv_dir
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.configure_environment(environment, venv_dir)
|
|
51
|
+
environment['VIRTUAL_ENV'] = venv_dir
|
|
52
|
+
environment['PATH'] = "#{venv_dir}/bin:#{ENV.fetch('PATH', '')}"
|
|
53
|
+
environment['PYTHONUSERBASE'] = venv_dir
|
|
54
|
+
|
|
55
|
+
site_packages = Dir.glob("#{venv_dir}/lib/python*/site-packages").first
|
|
56
|
+
# Prefer a PYTHONPATH the caller already seeded on `environment` over the
|
|
57
|
+
# ambient one. Script.process_suite sets it to ENV['PYTHONPATH'] || '.'
|
|
58
|
+
# before calling here, and reading ENV directly would drop that value.
|
|
59
|
+
existing_pythonpath = environment['PYTHONPATH'] || ENV.fetch('PYTHONPATH', '')
|
|
60
|
+
if site_packages
|
|
61
|
+
environment['PYTHONPATH'] = existing_pythonpath.empty? ? site_packages : "#{site_packages}:#{existing_pythonpath}"
|
|
62
|
+
else
|
|
63
|
+
environment['PYTHONPATH'] = existing_pythonpath.empty? ? nil : existing_pythonpath
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
environment
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Remove any RESERVED_PACKAGES a plugin installed into its own venv.
|
|
70
|
+
# Returns the names that were removed so callers can report them.
|
|
71
|
+
def self.purge_reserved_packages(venv_dir)
|
|
72
|
+
return [] if venv_dir.nil? || venv_dir.empty?
|
|
73
|
+
|
|
74
|
+
purged = []
|
|
75
|
+
RESERVED_PACKAGES.each do |package|
|
|
76
|
+
next unless package_installed?(venv_dir, package)
|
|
77
|
+
|
|
78
|
+
Logger.warn("Plugin venv #{venv_dir} declares '#{package}', which would shadow the " \
|
|
79
|
+
"system library. Removing it; the plugin will use the system #{package}.")
|
|
80
|
+
_output, status = Open3.capture2e('uv', 'pip', 'uninstall', '--python', venv_dir, package)
|
|
81
|
+
if status.success?
|
|
82
|
+
purged << package
|
|
83
|
+
else
|
|
84
|
+
Logger.error("Failed to remove '#{package}' from plugin venv #{venv_dir}")
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
purged
|
|
88
|
+
rescue => e
|
|
89
|
+
Logger.error("Could not purge reserved packages from #{venv_dir}: #{e.message}")
|
|
90
|
+
[]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.package_installed?(venv_dir, package)
|
|
94
|
+
_output, status = Open3.capture2e('uv', 'pip', 'show', '--python', venv_dir, package)
|
|
95
|
+
status.success?
|
|
96
|
+
end
|
|
97
|
+
private_class_method :package_installed?
|
|
98
|
+
|
|
99
|
+
def self.plugin_venv_path(scope:, plugin_name:)
|
|
100
|
+
sanitized_name = "#{scope}__#{plugin_name}".tr('^a-zA-Z0-9_-', '_')
|
|
101
|
+
candidate = File.join(PLUGIN_VENVS_DIR, sanitized_name, '.venv')
|
|
102
|
+
candidate if File.directory?(candidate)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def self.resolve_script_venv(name:, scope:, python_venv: nil)
|
|
106
|
+
target_name = name.split('/')[0].to_s.upcase
|
|
107
|
+
if target_name == TargetFile::TEMP_FOLDER && python_venv
|
|
108
|
+
# File.basename strips directory components; the tr() then whitelists the
|
|
109
|
+
# name down to the character set PluginModel.plugin_venv_name builds it
|
|
110
|
+
# from. That leaves no path separators and no glob metacharacters, so
|
|
111
|
+
# the value cannot escape PLUGIN_VENVS_DIR or widen the Dir.glob in
|
|
112
|
+
# configure_environment.
|
|
113
|
+
safe_name = File.basename(python_venv.to_s).tr('^a-zA-Z0-9_-', '_')
|
|
114
|
+
# The venv name is client-supplied, so it must also be confined to the
|
|
115
|
+
# caller's scope. Venv directories are named "<scope>__<plugin>" by
|
|
116
|
+
# PluginModel.plugin_venv_name; without this check a user in one scope
|
|
117
|
+
# could name another scope's venv and run against its packages.
|
|
118
|
+
return nil unless safe_name.start_with?("#{scope}__".tr('^a-zA-Z0-9_-', '_'))
|
|
119
|
+
|
|
120
|
+
candidate = File.join(PLUGIN_VENVS_DIR, safe_name, '.venv')
|
|
121
|
+
return candidate if File.directory?(candidate)
|
|
122
|
+
else
|
|
123
|
+
target_info = TargetModel.get(name: target_name, scope: scope)
|
|
124
|
+
if target_info && target_info['plugin']
|
|
125
|
+
return plugin_venv_path(scope: scope, plugin_name: target_info['plugin'])
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
nil
|
|
129
|
+
rescue => e
|
|
130
|
+
Logger.debug("Could not resolve plugin venv for script '#{name}': #{e.message}")
|
|
131
|
+
nil
|
|
132
|
+
end
|
|
133
|
+
private_class_method :resolve_script_venv
|
|
134
|
+
end
|
|
135
|
+
end
|
|
File without changes
|
|
@@ -928,6 +928,10 @@ module OpenC3
|
|
|
928
928
|
# @return [Array, Hash] Array of [value, limits_state] pairs per row, or {} if no results.
|
|
929
929
|
# Single-row results return a flat array; multi-row results return array of arrays.
|
|
930
930
|
def self.tsdb_lookup(items, start_time:, end_time: nil, scope: "DEFAULT")
|
|
931
|
+
# Every item is a placeholder for an item which doesn't exist, so there's
|
|
932
|
+
# nothing to query. Return a single row of nil values, one per item.
|
|
933
|
+
return Array.new(items.length) { [nil, nil] } if items.all? { |item| item[2].nil? }
|
|
934
|
+
|
|
931
935
|
# Group items by db_shard number while preserving their original positions
|
|
932
936
|
db_shard_groups = {} # db_shard => { positions: [], items: [] }
|
|
933
937
|
items.each_with_index do |item, pos|
|
|
@@ -944,62 +948,36 @@ module OpenC3
|
|
|
944
948
|
return tsdb_lookup_single_db_shard(group[:items], start_time: start_time, end_time: end_time, scope: scope, db_shard: db_shard)
|
|
945
949
|
end
|
|
946
950
|
|
|
947
|
-
# Cross-db_shard: execute per-db_shard queries and merge results
|
|
948
|
-
|
|
951
|
+
# Cross-db_shard: execute per-db_shard queries (as arrays of rows) and merge results
|
|
952
|
+
db_shard_rows = {} # db_shard => rows
|
|
949
953
|
db_shard_groups.each do |db_shard, group|
|
|
950
|
-
result = tsdb_lookup_single_db_shard(group[:items], start_time: start_time, end_time: end_time, scope: scope, db_shard: db_shard)
|
|
951
|
-
|
|
954
|
+
result = tsdb_lookup_single_db_shard(group[:items], start_time: start_time, end_time: end_time, scope: scope, db_shard: db_shard, flatten: false)
|
|
955
|
+
db_shard_rows[db_shard] = result.is_a?(Array) ? result : []
|
|
952
956
|
end
|
|
953
957
|
|
|
954
|
-
#
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
# Merge them into the original item order.
|
|
964
|
-
merged = Array.new(items.length) { [nil, nil] }
|
|
965
|
-
db_shard_groups.each do |db_shard, group|
|
|
966
|
-
result = db_shard_results[db_shard]
|
|
967
|
-
next if result == {} || !result.is_a?(Array)
|
|
958
|
+
# Merge results positionally back into the original item order. Each db_shard
|
|
959
|
+
# may have different row counts so use the maximum row count and fill missing
|
|
960
|
+
# positions with [nil, nil]. If all db_shards returned empty, return empty.
|
|
961
|
+
max_rows = db_shard_rows.values.map(&:length).max
|
|
962
|
+
return {} if max_rows == 0
|
|
963
|
+
|
|
964
|
+
merged = Array.new(max_rows) { Array.new(items.length) { [nil, nil] } }
|
|
965
|
+
db_shard_groups.each do |db_shard, group|
|
|
966
|
+
db_shard_rows[db_shard].each_with_index do |row, row_num|
|
|
968
967
|
group[:positions].each_with_index do |orig_pos, db_shard_idx|
|
|
969
|
-
merged[orig_pos] =
|
|
968
|
+
merged[row_num][orig_pos] = row[db_shard_idx] if row[db_shard_idx]
|
|
970
969
|
end
|
|
971
970
|
end
|
|
972
|
-
merged
|
|
973
|
-
else
|
|
974
|
-
# Multi-row mode: find max row count across db_shards
|
|
975
|
-
max_rows = 0
|
|
976
|
-
db_shard_groups.each do |db_shard, _group|
|
|
977
|
-
result = db_shard_results[db_shard]
|
|
978
|
-
next if result == {}
|
|
979
|
-
count = result.is_a?(Array) ? result.length : 0
|
|
980
|
-
max_rows = count if count > max_rows
|
|
981
|
-
end
|
|
982
|
-
return {} if max_rows == 0
|
|
983
|
-
|
|
984
|
-
merged = Array.new(max_rows) { Array.new(items.length) { [nil, nil] } }
|
|
985
|
-
db_shard_groups.each do |db_shard, group|
|
|
986
|
-
result = db_shard_results[db_shard]
|
|
987
|
-
next if result == {}
|
|
988
|
-
rows = result.is_a?(Array) ? result : []
|
|
989
|
-
rows.each_with_index do |row, row_num|
|
|
990
|
-
next unless row.is_a?(Array)
|
|
991
|
-
group[:positions].each_with_index do |orig_pos, db_shard_idx|
|
|
992
|
-
merged[row_num][orig_pos] = row[db_shard_idx] if row[db_shard_idx]
|
|
993
|
-
end
|
|
994
|
-
end
|
|
995
|
-
end
|
|
996
|
-
merged
|
|
997
971
|
end
|
|
972
|
+
# Match the single db_shard behavior of returning a flat array for a single row
|
|
973
|
+
return merged[0] if max_rows == 1
|
|
974
|
+
merged
|
|
998
975
|
end
|
|
999
976
|
|
|
1000
977
|
# Execute a tsdb_lookup query against a single db_shard.
|
|
1001
978
|
# This contains the original ASOF JOIN logic for items all on the same QuestDB instance.
|
|
1002
|
-
|
|
979
|
+
# When flatten is true a single row result is returned as a flat array rather than an array of rows.
|
|
980
|
+
def self.tsdb_lookup_single_db_shard(items, start_time:, end_time: nil, scope: "DEFAULT", db_shard: 0, flatten: true)
|
|
1003
981
|
tables = {}
|
|
1004
982
|
names = []
|
|
1005
983
|
nil_count = 0
|
|
@@ -1060,6 +1038,11 @@ module OpenC3
|
|
|
1060
1038
|
end
|
|
1061
1039
|
end
|
|
1062
1040
|
|
|
1041
|
+
# Every item in this db_shard is a placeholder so there's no table to query.
|
|
1042
|
+
# Return no results and let tsdb_lookup fill these positions with [nil, nil]
|
|
1043
|
+
# when it merges the db_shards (an all placeholder lookup returns before this)
|
|
1044
|
+
return {} if tables.empty?
|
|
1045
|
+
|
|
1063
1046
|
# Add needed timestamp columns to the SELECT for calculated items
|
|
1064
1047
|
needed_timestamps.each do |table_index, ts_columns|
|
|
1065
1048
|
ts_columns.each do |ts_col|
|
|
@@ -1147,7 +1130,7 @@ module OpenC3
|
|
|
1147
1130
|
data[row_num].insert(position, [calculated_value, nil])
|
|
1148
1131
|
end
|
|
1149
1132
|
end
|
|
1150
|
-
if result.ntuples == 1
|
|
1133
|
+
if flatten and result.ntuples == 1
|
|
1151
1134
|
data = data[0]
|
|
1152
1135
|
end
|
|
1153
1136
|
data
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# encoding: ascii-8bit
|
|
2
|
+
|
|
3
|
+
# Copyright 2026 OpenC3, Inc.
|
|
4
|
+
# All Rights Reserved.
|
|
5
|
+
#
|
|
6
|
+
# This program is distributed in the hope that it will be useful,
|
|
7
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
8
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
9
|
+
# See LICENSE.md for more details.
|
|
10
|
+
#
|
|
11
|
+
# This file may also be used under the terms of a commercial license
|
|
12
|
+
# if purchased from OpenC3, Inc.
|
|
13
|
+
|
|
14
|
+
require 'uri'
|
|
15
|
+
require 'openc3/utilities/logger'
|
|
16
|
+
|
|
17
|
+
module OpenC3
|
|
18
|
+
class RubygemsUrl
|
|
19
|
+
DEFAULT = 'https://rubygems.org'
|
|
20
|
+
|
|
21
|
+
# Validate that a resolved rubygems_url is an http(s) URL before it is used
|
|
22
|
+
# as a Gem source. The value can come from a user-writable setting or ENV, so
|
|
23
|
+
# a malformed or non-http value is rejected and replaced with the default
|
|
24
|
+
# rather than passed through to RubyGems.
|
|
25
|
+
#
|
|
26
|
+
# @param rubygems_url [String] the resolved rubygems source url to validate
|
|
27
|
+
# @return [String] the original url if valid, otherwise DEFAULT
|
|
28
|
+
def self.validate(rubygems_url)
|
|
29
|
+
uri = URI.parse(rubygems_url)
|
|
30
|
+
unless uri.is_a?(URI::HTTP) && !uri.host.to_s.empty?
|
|
31
|
+
raise URI::InvalidURIError, "not an http(s) URL"
|
|
32
|
+
end
|
|
33
|
+
rubygems_url
|
|
34
|
+
rescue URI::InvalidURIError => e
|
|
35
|
+
Logger.error("Invalid rubygems_url '#{rubygems_url}' (#{e.message}); falling back to #{DEFAULT}")
|
|
36
|
+
DEFAULT
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -28,6 +28,7 @@ require 'openc3/utilities/script'
|
|
|
28
28
|
require 'openc3/utilities/store'
|
|
29
29
|
require 'openc3/utilities/store_queued'
|
|
30
30
|
require 'openc3/utilities/bucket_require'
|
|
31
|
+
require 'openc3/utilities/python_venv'
|
|
31
32
|
require 'openc3/models/offline_access_model'
|
|
32
33
|
require 'openc3/models/environment_model'
|
|
33
34
|
require 'openc3/models/script_engine_model'
|
|
@@ -36,7 +37,7 @@ require 'fileutils'
|
|
|
36
37
|
|
|
37
38
|
if not defined? RAILS_ROOT
|
|
38
39
|
if ENV['RAILS_ROOT']
|
|
39
|
-
RAILS_ROOT = ENV
|
|
40
|
+
RAILS_ROOT = ENV.fetch('RAILS_ROOT')
|
|
40
41
|
elsif defined? Rails
|
|
41
42
|
RAILS_ROOT = Rails.root
|
|
42
43
|
else
|
|
@@ -436,7 +437,7 @@ class RunningScript
|
|
|
436
437
|
extension = File.extname(name).to_s.downcase
|
|
437
438
|
script_engine = nil
|
|
438
439
|
if extension == '.py'
|
|
439
|
-
process_name = ENV
|
|
440
|
+
process_name = ENV.fetch('OPENC3_PYTHON_BIN', '/openc3/python/.venv/bin/python')
|
|
440
441
|
runner_path = File.join(RAILS_ROOT, 'scripts', 'run_script.py')
|
|
441
442
|
elsif extension == '.rb'
|
|
442
443
|
process_name = 'ruby'
|
|
@@ -449,7 +450,7 @@ class RunningScript
|
|
|
449
450
|
if script_engine_model
|
|
450
451
|
script_engine = script_engine_model.filename
|
|
451
452
|
if File.extname(script_engine).to_s.downcase == '.py'
|
|
452
|
-
process_name = ENV
|
|
453
|
+
process_name = ENV.fetch('OPENC3_PYTHON_BIN', '/openc3/python/.venv/bin/python')
|
|
453
454
|
runner_path = File.join(RAILS_ROOT, 'scripts', 'run_script.py')
|
|
454
455
|
else
|
|
455
456
|
process_name = 'ruby'
|
|
@@ -521,17 +522,17 @@ class RunningScript
|
|
|
521
522
|
|
|
522
523
|
# Set proper secrets for running script
|
|
523
524
|
process.environment['SECRET_KEY_BASE'] = nil
|
|
524
|
-
process.environment['OPENC3_REDIS_USERNAME'] = ENV
|
|
525
|
-
process.environment['OPENC3_REDIS_PASSWORD'] = ENV
|
|
526
|
-
process.environment['OPENC3_BUCKET_USERNAME'] = ENV
|
|
527
|
-
process.environment['OPENC3_BUCKET_PASSWORD'] = ENV
|
|
525
|
+
process.environment['OPENC3_REDIS_USERNAME'] = ENV.fetch('OPENC3_SR_REDIS_USERNAME', nil)
|
|
526
|
+
process.environment['OPENC3_REDIS_PASSWORD'] = ENV.fetch('OPENC3_SR_REDIS_PASSWORD', nil)
|
|
527
|
+
process.environment['OPENC3_BUCKET_USERNAME'] = ENV.fetch('OPENC3_SR_BUCKET_USERNAME', nil)
|
|
528
|
+
process.environment['OPENC3_BUCKET_PASSWORD'] = ENV.fetch('OPENC3_SR_BUCKET_PASSWORD', nil)
|
|
528
529
|
process.environment['OPENC3_SR_REDIS_USERNAME'] = nil
|
|
529
530
|
process.environment['OPENC3_SR_REDIS_PASSWORD'] = nil
|
|
530
531
|
process.environment['OPENC3_SR_BUCKET_USERNAME'] = nil
|
|
531
532
|
process.environment['OPENC3_SR_BUCKET_PASSWORD'] = nil
|
|
532
|
-
process.environment['OPENC3_API_CLIENT'] = ENV
|
|
533
|
+
process.environment['OPENC3_API_CLIENT'] = ENV.fetch('OPENC3_API_CLIENT', nil)
|
|
533
534
|
if model and model.offline_access_token
|
|
534
|
-
auth = OpenC3::OpenC3KeycloakAuthentication.new(ENV
|
|
535
|
+
auth = OpenC3::OpenC3KeycloakAuthentication.new(ENV.fetch('OPENC3_KEYCLOAK_URL', nil))
|
|
535
536
|
valid_token = auth.get_token_from_refresh_token(model.offline_access_token)
|
|
536
537
|
if valid_token
|
|
537
538
|
process.environment['OPENC3_API_TOKEN'] = model.offline_access_token
|
|
@@ -541,58 +542,26 @@ class RunningScript
|
|
|
541
542
|
raise "offline_access token invalid for script"
|
|
542
543
|
end
|
|
543
544
|
else
|
|
544
|
-
process.environment['OPENC3_API_USER'] = ENV
|
|
545
|
+
process.environment['OPENC3_API_USER'] = ENV.fetch('OPENC3_API_USER', nil)
|
|
545
546
|
if ENV['OPENC3_SERVICE_PASSWORD']
|
|
546
|
-
process.environment['OPENC3_API_PASSWORD'] = ENV
|
|
547
|
+
process.environment['OPENC3_API_PASSWORD'] = ENV.fetch('OPENC3_SERVICE_PASSWORD')
|
|
547
548
|
else
|
|
548
549
|
raise "No authentication available for script"
|
|
549
550
|
end
|
|
550
551
|
end
|
|
551
|
-
process.environment['GEM_HOME'] = ENV
|
|
552
|
+
process.environment['GEM_HOME'] = ENV.fetch('GEM_HOME', nil)
|
|
552
553
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
begin
|
|
559
|
-
target_name = name.split('/')[0].to_s.upcase
|
|
560
|
-
if target_name == '__TEMP__' && python_venv
|
|
561
|
-
# File.basename prevents path traversal (strips directory components)
|
|
562
|
-
safe_name = File.basename(python_venv.to_s)
|
|
563
|
-
candidate = "/gems/plugin_venvs/#{safe_name}/.venv"
|
|
564
|
-
python_venv_dir = candidate if File.directory?(candidate)
|
|
565
|
-
else
|
|
566
|
-
target_info = OpenC3::TargetModel.get(name: target_name, scope: scope)
|
|
567
|
-
if target_info && target_info['plugin']
|
|
568
|
-
sanitized_name = "#{scope}__#{target_info['plugin']}".tr('^a-zA-Z0-9_-', '_')
|
|
569
|
-
candidate = "/gems/plugin_venvs/#{sanitized_name}/.venv"
|
|
570
|
-
python_venv_dir = candidate if File.directory?(candidate)
|
|
571
|
-
end
|
|
572
|
-
end
|
|
573
|
-
rescue => e
|
|
574
|
-
OpenC3::Logger.debug("Could not resolve plugin venv for script '#{name}': #{e.message}")
|
|
575
|
-
end
|
|
576
|
-
|
|
577
|
-
if python_venv_dir
|
|
578
|
-
process.environment['VIRTUAL_ENV'] = python_venv_dir
|
|
579
|
-
process.environment['PATH'] = "#{python_venv_dir}/bin:#{ENV.fetch('PATH', '')}"
|
|
580
|
-
process.environment['PYTHONUSERBASE'] = python_venv_dir
|
|
581
|
-
# Add plugin venv site-packages to PYTHONPATH so the base venv's Python
|
|
582
|
-
# binary can find plugin-specific packages. PYTHONPATH is always respected
|
|
583
|
-
# by CPython regardless of venv activation state.
|
|
584
|
-
site_packages = Dir.glob("#{python_venv_dir}/lib/python*/site-packages").first
|
|
585
|
-
existing_pythonpath = ENV.fetch('PYTHONPATH', '')
|
|
586
|
-
if site_packages
|
|
587
|
-
process.environment['PYTHONPATH'] = existing_pythonpath.empty? ? site_packages : "#{site_packages}:#{existing_pythonpath}"
|
|
588
|
-
else
|
|
589
|
-
process.environment['PYTHONPATH'] = existing_pythonpath.empty? ? nil : existing_pythonpath
|
|
590
|
-
end
|
|
591
|
-
else
|
|
592
|
-
system_venv = File.dirname(ENV['OPENC3_PYTHON_BIN'] || '/openc3/python/.venv/bin/python').chomp('/bin')
|
|
554
|
+
python_venv_dir = OpenC3::PythonVenv.configure_for_script(
|
|
555
|
+
process.environment, name: name, scope: scope, python_venv: python_venv
|
|
556
|
+
)
|
|
557
|
+
unless python_venv_dir
|
|
558
|
+
system_venv = File.dirname(ENV.fetch('OPENC3_PYTHON_BIN', '/openc3/python/.venv/bin/python')).chomp('/bin')
|
|
593
559
|
process.environment['VIRTUAL_ENV'] = system_venv
|
|
594
|
-
process.environment['PYTHONUSERBASE'] = ENV
|
|
595
|
-
|
|
560
|
+
process.environment['PYTHONUSERBASE'] = ENV.fetch('PYTHONUSERBASE', OpenC3::PythonVenv::DEFAULT_PYTHONUSERBASE)
|
|
561
|
+
# Deliberately nil when PYTHONPATH is unset rather than defaulting to
|
|
562
|
+
# something like '.', which would put the script's own working directory
|
|
563
|
+
# on the import path and let a local file shadow a real module.
|
|
564
|
+
process.environment['PYTHONPATH'] = ENV.fetch('PYTHONPATH', nil)
|
|
596
565
|
end
|
|
597
566
|
|
|
598
567
|
# Spawned process should not be controlled by same Bundler constraints as spawning process
|
|
@@ -1556,7 +1525,7 @@ class RunningScript
|
|
|
1556
1525
|
$stdout = OpenC3::Stdout.instance
|
|
1557
1526
|
$stderr = OpenC3::Stderr.instance
|
|
1558
1527
|
OpenC3::Logger.stdout = true
|
|
1559
|
-
OpenC3::Logger.level = OpenC3::Logger
|
|
1528
|
+
OpenC3::Logger.level = OpenC3::Logger.default_level
|
|
1560
1529
|
end
|
|
1561
1530
|
|
|
1562
1531
|
def output_thread
|
|
@@ -3,9 +3,9 @@ require 'aws-sdk-s3'
|
|
|
3
3
|
if ENV['OPENC3_CLOUD'] == 'local'
|
|
4
4
|
Aws.config.update(
|
|
5
5
|
s3: {
|
|
6
|
-
endpoint: ENV
|
|
7
|
-
access_key_id: ENV
|
|
8
|
-
secret_access_key: ENV
|
|
6
|
+
endpoint: ENV.fetch('OPENC3_BUCKET_URL', nil) || (ENV['OPENC3_DEVEL'] ? 'http://127.0.0.1:9000' : 'http://openc3-buckets:9000'),
|
|
7
|
+
access_key_id: ENV.fetch('OPENC3_BUCKET_USERNAME', nil),
|
|
8
|
+
secret_access_key: ENV.fetch('OPENC3_BUCKET_PASSWORD', nil),
|
|
9
9
|
force_path_style: true,
|
|
10
10
|
region: 'us-east-1',
|
|
11
11
|
# Disable automatic checksum calculation for S3-compatible backends (versitygw)
|
|
@@ -20,9 +20,9 @@ if ENV['OPENC3_CLOUD'] == 'local'
|
|
|
20
20
|
else # AWS
|
|
21
21
|
Aws.config.update(
|
|
22
22
|
s3: {
|
|
23
|
-
endpoint: "https://s3.#{ENV
|
|
23
|
+
endpoint: "https://s3.#{ENV.fetch('AWS_REGION', nil)}.amazonaws.com",
|
|
24
24
|
force_path_style: true,
|
|
25
|
-
region: ENV
|
|
25
|
+
region: ENV.fetch('AWS_REGION', nil)
|
|
26
26
|
}
|
|
27
27
|
)
|
|
28
28
|
end
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
require 'tempfile'
|
|
19
19
|
require 'openc3/utilities/target_file'
|
|
20
|
+
require 'openc3/utilities/python_venv'
|
|
20
21
|
require 'openc3/utilities/running_script'
|
|
21
22
|
require 'openc3/script/suite'
|
|
22
23
|
require 'openc3/script/suite_runner'
|
|
@@ -37,12 +38,6 @@ class Script < OpenC3::TargetFile
|
|
|
37
38
|
super(scope, nil, target: target) # No path matchers
|
|
38
39
|
end
|
|
39
40
|
|
|
40
|
-
# Split off the '*' that indicates a file is modified on the server
|
|
41
|
-
def self.strip_modified(name)
|
|
42
|
-
name.split('*')[0]
|
|
43
|
-
end
|
|
44
|
-
private_class_method :strip_modified
|
|
45
|
-
|
|
46
41
|
def self.lock(scope, name, username)
|
|
47
42
|
OpenC3::Store.hset("#{scope}__script-locks", strip_modified(name), username)
|
|
48
43
|
end
|
|
@@ -88,7 +83,7 @@ class Script < OpenC3::TargetFile
|
|
|
88
83
|
username: username, comment: comment)
|
|
89
84
|
end
|
|
90
85
|
|
|
91
|
-
def self.process_suite(name, contents, new_process: true, username: nil, scope:)
|
|
86
|
+
def self.process_suite(name, contents, new_process: true, username: nil, scope:, python_venv: nil)
|
|
92
87
|
python = false
|
|
93
88
|
python = true if File.extname(name) == '.py'
|
|
94
89
|
|
|
@@ -155,10 +150,19 @@ class Script < OpenC3::TargetFile
|
|
|
155
150
|
end
|
|
156
151
|
end
|
|
157
152
|
process.environment['GEM_HOME'] = ENV['GEM_HOME'] || '/gems'
|
|
158
|
-
process.environment['PYTHONUSERBASE'] = ENV['PYTHONUSERBASE'] ||
|
|
153
|
+
process.environment['PYTHONUSERBASE'] = ENV['PYTHONUSERBASE'] || OpenC3::PythonVenv::DEFAULT_PYTHONUSERBASE
|
|
159
154
|
# Preserve PYTHONPATH to ensure Python can find both UV venv and user packages
|
|
160
155
|
process.environment['PYTHONPATH'] = ENV['PYTHONPATH'] || '.'
|
|
161
156
|
|
|
157
|
+
# Suite analysis executes Python in a separate process before the script
|
|
158
|
+
# itself is started. Give that process the same plugin venv visibility as
|
|
159
|
+
# RunningScript so imports used while defining a suite can be resolved.
|
|
160
|
+
if python
|
|
161
|
+
OpenC3::PythonVenv.configure_for_script(
|
|
162
|
+
process.environment, name: name, scope: scope, python_venv: python_venv
|
|
163
|
+
)
|
|
164
|
+
end
|
|
165
|
+
|
|
162
166
|
# Spawned process should not be controlled by same Bundler constraints as spawning process
|
|
163
167
|
ENV.each do |key, _value|
|
|
164
168
|
if key =~ /^BUNDLE/
|
|
@@ -15,13 +15,58 @@ ENV['OPENC3_SECRET_BACKEND'] ||= 'redis'
|
|
|
15
15
|
|
|
16
16
|
module OpenC3
|
|
17
17
|
class Secrets
|
|
18
|
+
# Default base directory that FILE type secret paths must reside under.
|
|
19
|
+
# FILE type secrets are written by the operator (or mounted by Kubernetes)
|
|
20
|
+
# and then read back by the microservice, so the path is a destination we
|
|
21
|
+
# control, not an arbitrary file on the host.
|
|
22
|
+
DEFAULT_SECRET_FILE_DIR = '/tmp'
|
|
23
|
+
|
|
18
24
|
def initialize
|
|
19
25
|
@local_secrets = {}
|
|
20
26
|
end
|
|
21
27
|
|
|
28
|
+
# Base directory that FILE type secret paths must reside under
|
|
29
|
+
def self.secret_file_dir
|
|
30
|
+
dir = ENV.fetch('OPENC3_SECRET_FILE_DIR', nil)
|
|
31
|
+
dir = DEFAULT_SECRET_FILE_DIR if dir.nil? or dir.empty?
|
|
32
|
+
dir
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Validates a FILE type secret path. The path must expand to a location
|
|
36
|
+
# strictly inside Secrets.secret_file_dir. This prevents a plugin
|
|
37
|
+
# configuration such as 'SECRET FILE KEY /root/.ssh/id_rsa' or
|
|
38
|
+
# 'SECRET FILE KEY /tmp/../etc/shadow' from reading or overwriting arbitrary
|
|
39
|
+
# files as the OpenC3 process user.
|
|
40
|
+
#
|
|
41
|
+
# @param path [String] Path from a SECRET FILE definition
|
|
42
|
+
# @return [String] The validated absolute path
|
|
43
|
+
def self.validate_file_path(path)
|
|
44
|
+
raise ArgumentError, "Secret file path must be a String but is a #{path.class}" unless path.is_a?(String)
|
|
45
|
+
raise ArgumentError, "Secret file path must not be blank" if path.strip.empty?
|
|
46
|
+
raise ArgumentError, "Secret file path must not contain a null byte" if path.include?("\x00")
|
|
47
|
+
|
|
48
|
+
# expand_path collapses '..' and '.', expands '~', and makes a relative path
|
|
49
|
+
# absolute, so the comparison below can't be walked out of. This is
|
|
50
|
+
# deliberately a purely lexical check: it does not touch the filesystem, so
|
|
51
|
+
# a path validates identically at plugin install time (cmd-tlm-api) and at
|
|
52
|
+
# write time (openc3-operator), which are separate containers. Symlinks are
|
|
53
|
+
# rejected by the operator when it writes the secret.
|
|
54
|
+
base = File.expand_path(secret_file_dir)
|
|
55
|
+
absolute = File.expand_path(path)
|
|
56
|
+
unless absolute.start_with?(base + File::SEPARATOR)
|
|
57
|
+
raise ArgumentError, "Secret file path '#{path}' must be under '#{base}'"
|
|
58
|
+
end
|
|
59
|
+
absolute
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return true if path is inside base, or is base itself
|
|
63
|
+
def self.path_contained?(base, path)
|
|
64
|
+
path == base or path.start_with?(base.end_with?(File::SEPARATOR) ? base : base + File::SEPARATOR)
|
|
65
|
+
end
|
|
66
|
+
|
|
22
67
|
def self.getClient
|
|
23
68
|
raise 'OPENC3_SECRET_BACKEND environment variable is required' unless ENV['OPENC3_SECRET_BACKEND']
|
|
24
|
-
secrets_class = ENV
|
|
69
|
+
secrets_class = ENV.fetch('OPENC3_SECRET_BACKEND').capitalize + 'Secrets'
|
|
25
70
|
klass = OpenC3.require_class('openc3/utilities/' + secrets_class.class_name_to_filename)
|
|
26
71
|
klass.new
|
|
27
72
|
end
|
|
@@ -50,9 +95,9 @@ module OpenC3
|
|
|
50
95
|
type, key, data, _secret_store = secret
|
|
51
96
|
case type
|
|
52
97
|
when 'ENV'
|
|
53
|
-
@local_secrets[key] = ENV
|
|
98
|
+
@local_secrets[key] = ENV.fetch(data, nil)
|
|
54
99
|
when 'FILE'
|
|
55
|
-
@local_secrets[key] = File.read(data)
|
|
100
|
+
@local_secrets[key] = File.read(Secrets.validate_file_path(data))
|
|
56
101
|
else
|
|
57
102
|
raise "Unknown secret type: #{type}"
|
|
58
103
|
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -116,8 +116,8 @@ module OpenC3
|
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
def initialize(pool_size = 10, db_shard: 0)
|
|
119
|
-
@redis_username = ENV
|
|
120
|
-
@redis_key = ENV
|
|
119
|
+
@redis_username = ENV.fetch('OPENC3_REDIS_USERNAME', nil)
|
|
120
|
+
@redis_key = ENV.fetch('OPENC3_REDIS_PASSWORD', nil)
|
|
121
121
|
hostname = ENV['OPENC3_REDIS_HOSTNAME'].to_s.gsub("SHARDNUM", db_shard.to_s)
|
|
122
122
|
@redis_url = "redis://#{hostname}:#{ENV.fetch('OPENC3_REDIS_PORT', 6379)}"
|
|
123
123
|
@redis_pool = StoreConnectionPool.new(size: pool_size) { build_redis() }
|
|
File without changes
|