From 5ad60d066991ca83bc292644b2bf04f58551c280 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Thu, 19 Feb 2026 13:35:16 +0000 Subject: [PATCH 01/17] New configuration schema for Launch Manager --- .../examples/example_conf.json | 175 +++++++ .../s-core_launch_manager.schema.json | 492 ++++++++++++++++++ .../schema/s-core_launch_manager.schema.json | 123 +++++ .../types/alive_supervision.schema.json | 15 + .../types/component_properties.schema.json | 99 ++++ .../types/deployment_config.schema.json | 126 +++++ .../schema/types/recovery_action.schema.json | 43 ++ .../schema/types/run_target.schema.json | 45 ++ .../schema/types/watchdog.schema.json | 27 + .../new_config_schema/scripts/bundle.py | 193 +++++++ .../new_config_schema/scripts/validate.py | 157 ++++++ 11 files changed, 1495 insertions(+) create mode 100755 src/launch_manager_daemon/config/new_config_schema/examples/example_conf.json create mode 100644 src/launch_manager_daemon/config/new_config_schema/published_schema/s-core_launch_manager.schema.json create mode 100755 src/launch_manager_daemon/config/new_config_schema/schema/s-core_launch_manager.schema.json create mode 100755 src/launch_manager_daemon/config/new_config_schema/schema/types/alive_supervision.schema.json create mode 100755 src/launch_manager_daemon/config/new_config_schema/schema/types/component_properties.schema.json create mode 100755 src/launch_manager_daemon/config/new_config_schema/schema/types/deployment_config.schema.json create mode 100755 src/launch_manager_daemon/config/new_config_schema/schema/types/recovery_action.schema.json create mode 100755 src/launch_manager_daemon/config/new_config_schema/schema/types/run_target.schema.json create mode 100755 src/launch_manager_daemon/config/new_config_schema/schema/types/watchdog.schema.json create mode 100755 src/launch_manager_daemon/config/new_config_schema/scripts/bundle.py create mode 100755 src/launch_manager_daemon/config/new_config_schema/scripts/validate.py diff --git a/src/launch_manager_daemon/config/new_config_schema/examples/example_conf.json b/src/launch_manager_daemon/config/new_config_schema/examples/example_conf.json new file mode 100755 index 00000000..fc69c5c3 --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/examples/example_conf.json @@ -0,0 +1,175 @@ +{ + "schema_version": 1, + "defaults": { + "deployment_config": { + "ready_timeout": 0.5, + "shutdown_timeout": 0.5, + "environmental_variables": { + "LD_LIBRARY_PATH": "/opt/lib" + }, + "bin_dir": "/opt", + "working_dir": "/tmp", + "ready_recovery_action": { + "restart": { + "number_of_attempts": 0, + "delay_before_restart": 0 + } + }, + "recovery_action": { + "switch_run_target": { + "run_target": "Off" + } + }, + "sandbox": { + "uid": 1000, + "gid": 1000, + "supplementary_group_ids": [], + "scheduling_policy": "SCHED_OTHER", + "scheduling_priority": 0 + } + }, + "component_properties": { + "application_profile": { + "application_type": "Reporting_And_Supervised", + "is_self_terminating": false, + "alive_supervision": { + "reporting_cycle": 0.5, + "failed_cycles_tolerance": 2, + "min_indications": 1, + "max_indications": 3 + } + }, + "depends_on": [], + "process_arguments": [], + "ready_condition": { + "process_state": "Running" + } + }, + "run_target": { + "depends_on": [], + "transition_timeout": 5, + "recovery_action": { + "switch_run_target": { + "run_target": "Off" + } + } + }, + "alive_supervision" : { + "evaluation_cycle": 0.5 + }, + "watchdog": { + "device_file_path": "/dev/watchdog", + "max_timeout": 2.0, + "deactivate_on_shutdown": true, + "require_magic_close": false + } + }, + "components": { + "setup_filesystem_sh": { + "description": "Script to mount partitions at the right directories", + "component_properties": { + "binary_name": "bin/setup_filesystem.sh", + "application_profile": { + "application_type": "Native", + "is_self_terminating": true + }, + "process_arguments": ["-a", "-b"], + "ready_condition": { + "process_state": "Terminated" + } + }, + "deployment_config": { + "bin_dir": "/opt/scripts" + } + }, + "dlt-daemon": { + "description": "Logging application", + "component_properties": { + "binary_name": "dltd", + "application_profile": { + "application_type": "Native" + }, + "depends_on": ["setup_filesystem_sh"] + }, + "deployment_config": { + "bin_dir" : "/opt/apps/dlt-daemon" + } + }, + "someip-daemon": { + "description": "SOME/IP application", + "component_properties": { + "binary_name": "someipd" + }, + "deployment_config": { + "bin_dir" : "/opt/apps/someip" + } + }, + "test_app1": { + "description": "Simple test application", + "component_properties": { + "binary_name": "test_app1", + "depends_on": ["dlt-daemon", "someip-daemon"] + }, + "deployment_config": { + "bin_dir" : "/opt/apps/test_app1" + } + }, + "state_manager": { + "description": "Application that manages life cycle of the ECU", + "component_properties": { + "binary_name": "sm", + "application_profile": { + "application_type": "State_Manager" + }, + "depends_on": ["setup_filesystem_sh"] + }, + "deployment_config": { + "bin_dir" : "/opt/apps/state_manager" + } + } + }, + "run_targets": { + "Minimal": { + "description": "Minimal functionality of the system", + "depends_on": ["state_manager"], + "recovery_action": { + "switch_run_target": { + "run_target": "Off" + } + } + }, + "Full": { + "description": "Everything running", + "depends_on": ["test_app1", "Minimal"], + "transition_timeout": 5, + "recovery_action": { + "switch_run_target": { + "run_target": "Minimal" + } + } + }, + "Off": { + "description": "Nothing is running", + "recovery_action": { + "switch_run_target": { + "run_target": "Off" + } + } + } + }, + "alive_supervision" : { + "evaluation_cycle": 0.5 + }, + "fallback_run_target": { + "description": "Switching off everything", + "depends_on": [], + "transition_timeout": 1.5 + }, + "initial_run_target": "Minimal", + "watchdog": { + "device_file_path": "/dev/watchdog", + "max_timeout": 2, + "deactivate_on_shutdown": true, + "require_magic_close": false + } +} diff --git a/src/launch_manager_daemon/config/new_config_schema/published_schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/new_config_schema/published_schema/s-core_launch_manager.schema.json new file mode 100644 index 00000000..1cf10a09 --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/published_schema/s-core_launch_manager.schema.json @@ -0,0 +1,492 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "title": "Configuration schema for the S-CORE Launch Manager", + "description": "Defines the structure and valid values for the Launch Manager configuration file, which specifies managed components, run targets, and recovery behaviors.", + "$defs": { + "component_properties": { + "type": "object", + "description": "Defines a reusable type that captures essential development-time characteristics of a software component.", + "properties": { + "binary_name": { + "type": "string", + "description": "Specifies the relative path of the executable file inside the directory defined by 'deployment_config.bin_dir'. The final executable path will be resolved as '{bin_dir}/{binary_name}'. Example values include simple filenames (e.g., 'test_app1') or subdirectory paths (e.g., 'bin/test_app1')." + }, + "application_profile": { + "type": "object", + "description": "Defines the application profile that specifies the runtime behavior and capabilities of this component.", + "properties": { + "application_type": { + "type": "string", + "enum": [ + "Native", + "Reporting", + "Reporting_And_Supervised", + "State_Manager" + ], + "description": "Specifies the level of integration between the component and the Launch Manager. 'Native': no integration with Launch Manager. 'Reporting': uses Launch Manager lifecycle APIs. 'Reporting_And_Supervised': uses lifecycle APIs and sends alive notifications. 'State_Manager': uses lifecycle APIs, sends alive notifications, and has permission to change the active Run Target." + }, + "is_self_terminating": { + "type": "boolean", + "description": "Indicates whether the component is designed to terminate automatically once its planned tasks are completed (true), or remain running until explicitly requested to terminate by the Launch Manager (false)." + }, + "alive_supervision": { + "type": "object", + "description": "Defines the configuration parameters used for alive monitoring of the component.", + "properties": { + "reporting_cycle": { + "type": "number", + "exclusiveMinimum": 0, + "description": "Specifies the duration of the time interval used to verify that the component sends alive notifications within the expected time frame." + }, + "failed_cycles_tolerance": { + "type": "integer", + "minimum": 0, + "description": "Specifies the maximum number of consecutive reporting cycle failures (see 'reporting_cycle'). Once the number of failed cycles exceeds this maximum, the Launch Manager will trigger the configured recovery action." + }, + "min_indications": { + "type": "integer", + "minimum": 0, + "description": "Specifies the minimum number of checkpoints that must be reported within each configured 'reporting_cycle'." + }, + "max_indications": { + "type": "integer", + "minimum": 0, + "description": "Specifies the maximum number of checkpoints that may be reported within each configured 'reporting_cycle'." + } + }, + "required": [], + "additionalProperties": false + } + }, + "required": [], + "additionalProperties": false + }, + "depends_on": { + "type": "array", + "description": "Specifies the names of components that this component depends on. Each dependency must be initialized and reach its ready state before this component can start.", + "items": { + "type": "string", + "description": "Specifies the name of a component on which this component depends." + } + }, + "process_arguments": { + "type": "array", + "description": "Specifies an ordered list of command-line arguments passed to the component at startup.", + "items": { + "type": "string", + "description": "Specifies a single command-line argument token as a UTF-8 string; order is preserved." + } + }, + "ready_condition": { + "type": "object", + "description": "Defines the set of conditions that determine when the component completes its initializing state and enters the ready state.", + "properties": { + "process_state": { + "type": "string", + "enum": [ + "Running", + "Terminated" + ], + "description": "Specifies the required state of the component's POSIX process. 'Running': the process has started and reached its running state. 'Terminated': the process has started, reached its running state, and then terminated successfully." + } + }, + "required": [], + "additionalProperties": false + } + }, + "required": [], + "additionalProperties": false + }, + "recovery_action": { + "type": "object", + "description": "Defines a reusable type that specifies recovery actions to execute when an error or failure occurs.", + "properties": { + "restart": { + "type": "object", + "description": "Defines a recovery action that restarts the POSIX process associated with this component.", + "properties": { + "number_of_attempts": { + "type": "integer", + "minimum": 0, + "description": "Specifies the maximum number of restart attempts before the Launch Manager concludes that recovery cannot succeed." + }, + "delay_before_restart": { + "type": "number", + "minimum": 0, + "description": "Specifies the delay duration that the Launch Manager waits before initiating a restart attempt." + } + }, + "required": [], + "additionalProperties": false + }, + "switch_run_target": { + "type": "object", + "description": "Defines a recovery action that switches to a Run Target. This can be a different Run Target or the same one to retry activation of the current Run Target.", + "properties": { + "run_target": { + "type": "string", + "description": "Specifies the name of the Run Target that the Launch Manager should switch to." + } + }, + "required": [], + "additionalProperties": false + } + }, + "oneOf": [ + { + "required": [ + "restart" + ] + }, + { + "required": [ + "switch_run_target" + ] + } + ], + "additionalProperties": false + }, + "deployment_config": { + "type": "object", + "description": "Defines a reusable type that contains configuration parameters that are specific to a particular deployment environment or system setup.", + "properties": { + "ready_timeout": { + "type": "number", + "exclusiveMinimum": 0, + "description": "Specifies the maximum time allowed for the component to reach its ready state. The timeout is measured from when the component process is created until the ready conditions specified in 'component_properties.ready_condition' are met." + }, + "shutdown_timeout": { + "type": "number", + "exclusiveMinimum": 0, + "description": "Specifies the maximum time allowed for the component to terminate after it receives a SIGTERM signal from the Launch Manager. The timeout is measured from when the Launch Manager sends the SIGTERM signal until the Operating System notifies the Launch Manager that the child process has terminated." + }, + "environmental_variables": { + "type": "object", + "description": "Defines the set of environment variables passed to the component at startup.", + "additionalProperties": { + "type": "string", + "description": "Specifies the environment variable's value as a string. An empty string is allowed and represents an intentionally empty environment variable." + } + }, + "bin_dir": { + "type": "string", + "description": "Specifies the absolute filesystem path to the directory where the component is installed." + }, + "working_dir": { + "type": "string", + "description": "Specifies the directory used as the working directory for the component during execution." + }, + "ready_recovery_action": { + "allOf": [ + { + "$ref": "#/$defs/recovery_action" + }, + { + "properties": { + "restart": true + }, + "required": [ + "restart" + ], + "not": { + "required": [ + "switch_run_target" + ] + } + } + ], + "description": "Specifies the recovery action to execute when the component fails to reach its ready state within the configured timeout." + }, + "recovery_action": { + "allOf": [ + { + "$ref": "#/$defs/recovery_action" + }, + { + "properties": { + "switch_run_target": true + }, + "required": [ + "switch_run_target" + ], + "not": { + "required": [ + "restart" + ] + } + } + ], + "description": "Specifies the recovery action to execute when the component malfunctions after reaching its ready state." + }, + "sandbox": { + "type": "object", + "description": "Defines the sandbox configuration parameters that isolate and constrain the component's runtime execution.", + "properties": { + "uid": { + "type": "integer", + "minimum": 0, + "description": "Specifies the POSIX user ID (UID) under which this component executes." + }, + "gid": { + "type": "integer", + "minimum": 0, + "description": "Specifies the primary POSIX group ID (GID) under which this component executes." + }, + "supplementary_group_ids": { + "type": "array", + "description": "Specifies the list of supplementary POSIX group IDs (GIDs) assigned to this component.", + "items": { + "type": "integer", + "minimum": 0, + "description": "Specifies a single supplementary POSIX group ID (GID)." + } + }, + "security_policy": { + "type": "string", + "description": "Specifies the security policy or confinement profile name (such as an SELinux or AppArmor profile) assigned to the component." + }, + "scheduling_policy": { + "type": "string", + "description": "Specifies the scheduling policy applied to the component's initial thread. Supported values correspond to OS-defined policies (e.g., FIFO, RR, OTHER).", + "anyOf": [ + { + "enum": [ + "SCHED_FIFO", + "SCHED_RR", + "SCHED_OTHER" + ] + }, + { + "type": "string" + } + ] + }, + "scheduling_priority": { + "type": "integer", + "description": "Specifies the scheduling priority applied to the component's initial thread." + }, + "max_memory_usage": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Specifies the maximum amount of memory, in bytes, that the component is permitted to use during runtime." + }, + "max_cpu_usage": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Specifies the maximum CPU usage limit for the component, expressed as a percentage of total CPU capacity." + } + }, + "additionalProperties": false + } + }, + "required": [], + "additionalProperties": false + }, + "run_target": { + "type": "object", + "description": "Defines a reusable type that specifies configuration parameters for a Run Target.", + "properties": { + "description": { + "type": "string", + "description": "Specifies a user-defined description of the Run Target." + }, + "depends_on": { + "type": "array", + "description": "Specifies the names of components and Run Targets that must be activated when this Run Target is activated.", + "items": { + "type": "string", + "description": "Specifies the name of a component or Run Target that this Run Target depends on." + } + }, + "transition_timeout": { + "type": "number", + "description": "Specifies the time limit for the Run Target transition. If this limit is exceeded, the transition is considered failed.", + "exclusiveMinimum": 0 + }, + "recovery_action": { + "allOf": [ + { + "$ref": "#/$defs/recovery_action" + }, + { + "properties": { + "switch_run_target": true + }, + "required": [ + "switch_run_target" + ], + "not": { + "required": [ + "restart" + ] + } + } + ], + "description": "Specifies the recovery action to execute when a component assigned to this Run Target fails." + } + }, + "required": [ + "recovery_action" + ], + "additionalProperties": false + }, + "alive_supervision": { + "type": "object", + "description": "Defines a reusable type that contains configuration parameters for alive supervision.", + "properties": { + "evaluation_cycle": { + "type": "number", + "exclusiveMinimum": 0, + "description": "Specifies the length of the time window used to assess incoming alive supervision reports." + } + }, + "required": [], + "additionalProperties": false + }, + "watchdog": { + "type": "object", + "description": "Defines a reusable type that contains configuration parameters for the external watchdog.", + "properties": { + "device_file_path": { + "type": "string", + "description": "Specifies the path to the external watchdog device file (e.g., /dev/watchdog)." + }, + "max_timeout": { + "type": "number", + "minimum": 0, + "description": "Specifies the maximum timeout value that the Launch Manager configures on the external watchdog during startup. The external watchdog uses this timeout as the deadline for receiving periodic alive reports from the Launch Manager." + }, + "deactivate_on_shutdown": { + "type": "boolean", + "description": "Specifies whether the Launch Manager disables the external watchdog during shutdown. When set to true, the watchdog is deactivated; when false, it remains active." + }, + "require_magic_close": { + "type": "boolean", + "description": "Specifies whether the Launch Manager performs a defined shutdown sequence to inform the external watchdog that the shutdown is intentional and to prevent a watchdog-initiated reset. When true, the magic close sequence is performed; when false, it is not." + } + }, + "required": [], + "additionalProperties": false + } + }, + "properties": { + "schema_version": { + "type": "integer", + "description": "Specifies the schema version number that the Launch Manager uses to determine how to parse and validate this configuration file.", + "enum": [ + 1 + ] + }, + "defaults": { + "type": "object", + "description": "Defines default configuration values that components and Run Targets inherit unless they provide their own overriding values.", + "properties": { + "component_properties": { + "description": "Defines default component property values applied to all components unless overridden in individual component definitions.", + "$ref": "#/$defs/component_properties" + }, + "deployment_config": { + "description": "Defines default deployment configuration values applied to all components unless overridden in individual component definitions.", + "$ref": "#/$defs/deployment_config" + }, + "run_target": { + "description": "Defines default Run Target configuration values applied to all Run Targets unless overridden in individual Run Target definitions.", + "$ref": "#/$defs/run_target" + }, + "alive_supervision": { + "description": "Defines default alive supervision configuration values used unless a global 'alive_supervision' configuration is specified at the root level.", + "$ref": "#/$defs/alive_supervision" + }, + "watchdog": { + "description": "Defines default watchdog configuration values applied to all watchdogs unless overridden in individual watchdog definitions.", + "$ref": "#/$defs/watchdog" + } + }, + "required": [], + "additionalProperties": false + }, + "components": { + "type": "object", + "description": "Defines software components managed by the Launch Manager, where each property name is a unique component identifier and its value contains the component's configuration.", + "patternProperties": { + "^[a-zA-Z0-9_-]+$": { + "type": "object", + "description": "Defines an individual component's configuration properties and deployment settings.", + "properties": { + "description": { + "type": "string", + "description": "Specifies a human-readable description of the component's purpose." + }, + "component_properties": { + "description": "Defines component properties for this component; any properties not specified here are inherited from 'defaults.component_properties'.", + "$ref": "#/$defs/component_properties" + }, + "deployment_config": { + "description": "Defines deployment configuration for this component; any properties not specified here are inherited from 'defaults.deployment_config'.", + "$ref": "#/$defs/deployment_config" + } + }, + "required": [], + "additionalProperties": false + } + }, + "required": [], + "additionalProperties": false + }, + "run_targets": { + "type": "object", + "description": "Defines Run Targets representing different operational modes of the system, where each property name is a unique Run Target identifier and its value contains the Run Target's configuration.", + "patternProperties": { + "^[a-zA-Z0-9_-]+$": { + "$ref": "#/$defs/run_target" + } + }, + "required": [], + "additionalProperties": false + }, + "initial_run_target": { + "type": "string", + "description": "Specifies the name of the initial Run Target that the Launch Manager activates during the startup sequence. This name must match a Run Target defined in 'run_targets'." + }, + "fallback_run_target": { + "type": "object", + "description": "Defines the fallback Run Target configuration that the Launch Manager activates when all recovery attempts have been exhausted. This Run Target does not include a recovery_action property.", + "properties": { + "description": { + "type": "string", + "description": "Specifies a human-readable description of the fallback Run Target." + }, + "depends_on": { + "type": "array", + "description": "Specifies the names of components and Run Targets that must be activated when this Run Target is activated.", + "items": { + "type": "string", + "description": "Specifies the name of a component or Run Target that this Run Target depends on." + } + }, + "transition_timeout": { + "type": "number", + "description": "Specifies the time limit for the Run Target transition. If this limit is exceeded, the transition is considered failed.", + "exclusiveMinimum": 0 + } + }, + "required": [ + "depends_on" + ], + "additionalProperties": false + }, + "alive_supervision": { + "description": "Defines the alive supervision configuration parameters used to monitor component health. This configuration overrides 'defaults.alive_supervision' if specified.", + "$ref": "#/$defs/alive_supervision" + }, + "watchdog": { + "description": "Defines the external watchdog device configuration used by the Launch Manager. This configuration overrides 'defaults.watchdog' if specified.", + "$ref": "#/$defs/watchdog" + } + }, + "required": [ + "schema_version" + ], + "additionalProperties": false +} \ No newline at end of file diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/new_config_schema/schema/s-core_launch_manager.schema.json new file mode 100755 index 00000000..d7344161 --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/schema/s-core_launch_manager.schema.json @@ -0,0 +1,123 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "title": "Configuration schema for the S-CORE Launch Manager", + "description": "Defines the structure and valid values for the Launch Manager configuration file, which specifies managed components, run targets, and recovery behaviors.", + "properties": { + "schema_version": { + "type": "integer", + "description": "Specifies the schema version number that the Launch Manager uses to determine how to parse and validate this configuration file.", + "enum": [ 1 ] + }, + "defaults": { + "type": "object", + "description": "Defines default configuration values that components and Run Targets inherit unless they provide their own overriding values.", + "properties": { + "component_properties": { + "$ref": "./types/component_properties.schema.json", + "description": "Defines default component property values applied to all components unless overridden in individual component definitions." + }, + "deployment_config": { + "$ref": "./types/deployment_config.schema.json", + "description": "Defines default deployment configuration values applied to all components unless overridden in individual component definitions." + }, + "run_target": { + "$ref": "./types/run_target.schema.json", + "description": "Defines default Run Target configuration values applied to all Run Targets unless overridden in individual Run Target definitions." + }, + "alive_supervision": { + "$ref": "./types/alive_supervision.schema.json", + "description": "Defines default alive supervision configuration values used unless a global 'alive_supervision' configuration is specified at the root level." + }, + "watchdog": { + "$ref": "./types/watchdog.schema.json", + "description": "Defines default watchdog configuration values applied to all watchdogs unless overridden in individual watchdog definitions." + } + }, + "required": [], + "additionalProperties": false + }, + "components": { + "type": "object", + "description": "Defines software components managed by the Launch Manager, where each property name is a unique component identifier and its value contains the component's configuration.", + "patternProperties": { + "^[a-zA-Z0-9_-]+$": { + "type": "object", + "description": "Defines an individual component's configuration properties and deployment settings.", + "properties": { + "description": { + "type": "string", + "description": "Specifies a human-readable description of the component's purpose." + }, + "component_properties": { + "$ref": "./types/component_properties.schema.json", + "description": "Defines component properties for this component; any properties not specified here are inherited from 'defaults.component_properties'." + }, + "deployment_config": { + "$ref": "./types/deployment_config.schema.json", + "description": "Defines deployment configuration for this component; any properties not specified here are inherited from 'defaults.deployment_config'." + } + }, + "required": [], + "additionalProperties": false + } + }, + "required": [], + "additionalProperties": false + }, + "run_targets": { + "type": "object", + "description": "Defines Run Targets representing different operational modes of the system, where each property name is a unique Run Target identifier and its value contains the Run Target's configuration.", + "patternProperties": { + "^[a-zA-Z0-9_-]+$": { + "$ref": "./types/run_target.schema.json" + } + }, + "required": [], + "additionalProperties": false + }, + "initial_run_target": { + "type": "string", + "description": "Specifies the name of the initial Run Target that the Launch Manager activates during the startup sequence. This name must match a Run Target defined in 'run_targets'." + }, + "fallback_run_target": { + "type": "object", + "description": "Defines the fallback Run Target configuration that the Launch Manager activates when all recovery attempts have been exhausted. This Run Target does not include a recovery_action property.", + "properties": { + "description": { + "type": "string", + "description": "Specifies a human-readable description of the fallback Run Target." + }, + "depends_on": { + "type": "array", + "description": "Specifies the names of components and Run Targets that must be activated when this Run Target is activated.", + "items": { + "type": "string", + "description": "Specifies the name of a component or Run Target that this Run Target depends on." + } + }, + "transition_timeout": { + "type": "number", + "description": "Specifies the time limit for the Run Target transition. If this limit is exceeded, the transition is considered failed.", + "exclusiveMinimum": 0 + } + }, + "required": [ + "depends_on" + ], + "additionalProperties": false + }, + "alive_supervision": { + "$ref": "./types/alive_supervision.schema.json", + "description": "Defines the alive supervision configuration parameters used to monitor component health. This configuration overrides 'defaults.alive_supervision' if specified." + }, + "watchdog": { + "$ref": "./types/watchdog.schema.json", + "description": "Defines the external watchdog device configuration used by the Launch Manager. This configuration overrides 'defaults.watchdog' if specified." + } + }, + "required": [ + "schema_version" + ], + "additionalProperties": false +} diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/alive_supervision.schema.json b/src/launch_manager_daemon/config/new_config_schema/schema/types/alive_supervision.schema.json new file mode 100755 index 00000000..919de721 --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/schema/types/alive_supervision.schema.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "description": "Defines a reusable type that contains configuration parameters for alive supervision.", + "properties": { + "evaluation_cycle": { + "type": "number", + "exclusiveMinimum": 0, + "description": "Specifies the length of the time window used to assess incoming alive supervision reports." + } + }, + + "required": [], + "additionalProperties": false +} diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/component_properties.schema.json b/src/launch_manager_daemon/config/new_config_schema/schema/types/component_properties.schema.json new file mode 100755 index 00000000..087b470f --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/schema/types/component_properties.schema.json @@ -0,0 +1,99 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "description": "Defines a reusable type that captures essential development-time characteristics of a software component.", + "properties": { + "binary_name": { + "type": "string", + "description": "Specifies the relative path of the executable file inside the directory defined by 'deployment_config.bin_dir'. The final executable path will be resolved as '{bin_dir}/{binary_name}'. Example values include simple filenames (e.g., 'test_app1') or subdirectory paths (e.g., 'bin/test_app1')." + }, + + "application_profile": { + "type": "object", + "description": "Defines the application profile that specifies the runtime behavior and capabilities of this component.", + "properties": { + "application_type": { + "type": "string", + "enum": [ + "Native", + "Reporting", + "Reporting_And_Supervised", + "State_Manager" + ], + "description": "Specifies the level of integration between the component and the Launch Manager. 'Native': no integration with Launch Manager. 'Reporting': uses Launch Manager lifecycle APIs. 'Reporting_And_Supervised': uses lifecycle APIs and sends alive notifications. 'State_Manager': uses lifecycle APIs, sends alive notifications, and has permission to change the active Run Target." + }, + "is_self_terminating": { + "type": "boolean", + "description": "Indicates whether the component is designed to terminate automatically once its planned tasks are completed (true), or remain running until explicitly requested to terminate by the Launch Manager (false)." + }, + "alive_supervision": { + "type": "object", + "description": "Defines the configuration parameters used for alive monitoring of the component.", + "properties": { + "reporting_cycle": { + "type": "number", + "exclusiveMinimum": 0, + "description": "Specifies the duration of the time interval used to verify that the component sends alive notifications within the expected time frame." + }, + "failed_cycles_tolerance": { + "type": "integer", + "minimum": 0, + "description": "Specifies the maximum number of consecutive reporting cycle failures (see 'reporting_cycle'). Once the number of failed cycles exceeds this maximum, the Launch Manager will trigger the configured recovery action." + }, + "min_indications": { + "type": "integer", + "minimum": 0, + "description": "Specifies the minimum number of checkpoints that must be reported within each configured 'reporting_cycle'." + }, + "max_indications": { + "type": "integer", + "minimum": 0, + "description": "Specifies the maximum number of checkpoints that may be reported within each configured 'reporting_cycle'." + } + }, + "required": [], + "additionalProperties": false + } + }, + "required": [], + "additionalProperties": false + }, + + "depends_on": { + "type": "array", + "description": "Specifies the names of components that this component depends on. Each dependency must be initialized and reach its ready state before this component can start.", + "items": { + "type": "string", + "description": "Specifies the name of a component on which this component depends." + } + }, + + "process_arguments": { + "type": "array", + "description": "Specifies an ordered list of command-line arguments passed to the component at startup.", + "items": { + "type": "string", + "description": "Specifies a single command-line argument token as a UTF-8 string; order is preserved." + } + }, + + "ready_condition": { + "type": "object", + "description": "Defines the set of conditions that determine when the component completes its initializing state and enters the ready state.", + "properties": { + "process_state": { + "type": "string", + "enum": [ + "Running", + "Terminated" + ], + "description": "Specifies the required state of the component's POSIX process. 'Running': the process has started and reached its running state. 'Terminated': the process has started, reached its running state, and then terminated successfully." + } + }, + "required": [], + "additionalProperties": false + } + }, + "required": [], + "additionalProperties": false +} diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/deployment_config.schema.json b/src/launch_manager_daemon/config/new_config_schema/schema/types/deployment_config.schema.json new file mode 100755 index 00000000..7a22b221 --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/schema/types/deployment_config.schema.json @@ -0,0 +1,126 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "description": "Defines a reusable type that contains configuration parameters that are specific to a particular deployment environment or system setup.", + + "properties": { + "ready_timeout": { + "type": "number", + "exclusiveMinimum": 0, + "description": "Specifies the maximum time allowed for the component to reach its ready state. The timeout is measured from when the component process is created until the ready conditions specified in 'component_properties.ready_condition' are met." + }, + "shutdown_timeout": { + "type": "number", + "exclusiveMinimum": 0, + "description": "Specifies the maximum time allowed for the component to terminate after it receives a SIGTERM signal from the Launch Manager. The timeout is measured from when the Launch Manager sends the SIGTERM signal until the Operating System notifies the Launch Manager that the child process has terminated." + }, + "environmental_variables": { + "type": "object", + "description": "Defines the set of environment variables passed to the component at startup.", + "additionalProperties": { + "type": "string", + "description": "Specifies the environment variable's value as a string. An empty string is allowed and represents an intentionally empty environment variable." + } + }, + "bin_dir": { + "type": "string", + "description": "Specifies the absolute filesystem path to the directory where the component is installed." + }, + "working_dir": { + "type": "string", + "description": "Specifies the directory used as the working directory for the component during execution." + }, + "ready_recovery_action": { + "allOf": [ + { "$ref": "./recovery_action.schema.json" }, + { + "properties": { + "restart": true + }, + "required": ["restart"], + "not": { + "required": ["switch_run_target"] + } + } + ], + "description": "Specifies the recovery action to execute when the component fails to reach its ready state within the configured timeout." + }, + "recovery_action": { + "allOf": [ + { "$ref": "./recovery_action.schema.json" }, + { + "properties": { + "switch_run_target": true + }, + "required": ["switch_run_target"], + "not": { + "required": ["restart"] + } + } + ], + "description": "Specifies the recovery action to execute when the component malfunctions after reaching its ready state." + }, + "sandbox": { + "type": "object", + "description": "Defines the sandbox configuration parameters that isolate and constrain the component's runtime execution.", + "properties": { + "uid": { + "type": "integer", + "minimum": 0, + "description": "Specifies the POSIX user ID (UID) under which this component executes." + }, + "gid": { + "type": "integer", + "minimum": 0, + "description": "Specifies the primary POSIX group ID (GID) under which this component executes." + }, + "supplementary_group_ids": { + "type": "array", + "description": "Specifies the list of supplementary POSIX group IDs (GIDs) assigned to this component.", + "items": { + "type": "integer", + "minimum": 0, + "description": "Specifies a single supplementary POSIX group ID (GID)." + } + }, + "security_policy": { + "type": "string", + "description": "Specifies the security policy or confinement profile name (such as an SELinux or AppArmor profile) assigned to the component." + }, + "scheduling_policy": { + "type": "string", + "description": "Specifies the scheduling policy applied to the component's initial thread. Supported values correspond to OS-defined policies (e.g., FIFO, RR, OTHER).", + "anyOf": [ + { + "enum": [ + "SCHED_FIFO", + "SCHED_RR", + "SCHED_OTHER" + ] + }, + { + "type": "string" + } + ] + }, + "scheduling_priority": { + "type": "integer", + "description": "Specifies the scheduling priority applied to the component's initial thread." + }, + "max_memory_usage": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Specifies the maximum amount of memory, in bytes, that the component is permitted to use during runtime." + }, + "max_cpu_usage": { + "type": "integer", + "exclusiveMinimum": 0, + "description": "Specifies the maximum CPU usage limit for the component, expressed as a percentage of total CPU capacity." + } + }, + "additionalProperties": false + } + }, + "required": [], + "additionalProperties": false +} diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/recovery_action.schema.json b/src/launch_manager_daemon/config/new_config_schema/schema/types/recovery_action.schema.json new file mode 100755 index 00000000..2f5dd119 --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/schema/types/recovery_action.schema.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "description": "Defines a reusable type that specifies recovery actions to execute when an error or failure occurs.", + "properties": { + "restart": { + "type": "object", + "description": "Defines a recovery action that restarts the POSIX process associated with this component.", + "properties": { + "number_of_attempts": { + "type": "integer", + "minimum": 0, + "description": "Specifies the maximum number of restart attempts before the Launch Manager concludes that recovery cannot succeed." + }, + "delay_before_restart": { + "type": "number", + "minimum": 0, + "description": "Specifies the delay duration that the Launch Manager waits before initiating a restart attempt." + } + }, + "required": [], + "additionalProperties": false + }, + "switch_run_target": { + "type": "object", + "description": "Defines a recovery action that switches to a Run Target. This can be a different Run Target or the same one to retry activation of the current Run Target.", + "properties": { + "run_target": { + "type": "string", + "description": "Specifies the name of the Run Target that the Launch Manager should switch to." + } + }, + "required": [], + "additionalProperties": false + } + }, + + "oneOf": [ + { "required": ["restart"] }, + { "required": ["switch_run_target"] } + ], + "additionalProperties": false +} diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/run_target.schema.json b/src/launch_manager_daemon/config/new_config_schema/schema/types/run_target.schema.json new file mode 100755 index 00000000..946748d9 --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/schema/types/run_target.schema.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "description": "Defines a reusable type that specifies configuration parameters for a Run Target.", + "properties": { + "description": { + "type": "string", + "description": "Specifies a user-defined description of the Run Target." + }, + + "depends_on": { + "type": "array", + "description": "Specifies the names of components and Run Targets that must be activated when this Run Target is activated.", + "items": { + "type": "string", + "description": "Specifies the name of a component or Run Target that this Run Target depends on." + } + }, + + "transition_timeout": { + "type": "number", + "description": "Specifies the time limit for the Run Target transition. If this limit is exceeded, the transition is considered failed.", + "exclusiveMinimum": 0 + }, + + "recovery_action": { + "allOf": [ + { "$ref": "./recovery_action.schema.json" }, + { + "properties": { + "switch_run_target": true + }, + "required": ["switch_run_target"], + "not": { + "required": ["restart"] + } + } + ], + "description": "Specifies the recovery action to execute when a component assigned to this Run Target fails." + } + }, + + "required": ["recovery_action"], + "additionalProperties": false +} diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/watchdog.schema.json b/src/launch_manager_daemon/config/new_config_schema/schema/types/watchdog.schema.json new file mode 100755 index 00000000..a7716025 --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/schema/types/watchdog.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "description": "Defines a reusable type that contains configuration parameters for the external watchdog.", + "properties": { + "device_file_path": { + "type": "string", + "description": "Specifies the path to the external watchdog device file (e.g., /dev/watchdog)." + }, + "max_timeout": { + "type": "number", + "minimum": 0, + "description": "Specifies the maximum timeout value that the Launch Manager configures on the external watchdog during startup. The external watchdog uses this timeout as the deadline for receiving periodic alive reports from the Launch Manager." + }, + "deactivate_on_shutdown": { + "type": "boolean", + "description": "Specifies whether the Launch Manager disables the external watchdog during shutdown. When set to true, the watchdog is deactivated; when false, it remains active." + }, + "require_magic_close": { + "type": "boolean", + "description": "Specifies whether the Launch Manager performs a defined shutdown sequence to inform the external watchdog that the shutdown is intentional and to prevent a watchdog-initiated reset. When true, the magic close sequence is performed; when false, it is not." + } + }, + + "required": [], + "additionalProperties": false +} diff --git a/src/launch_manager_daemon/config/new_config_schema/scripts/bundle.py b/src/launch_manager_daemon/config/new_config_schema/scripts/bundle.py new file mode 100755 index 00000000..20b6e452 --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/scripts/bundle.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 + +""" +Bundle a multi-file JSON Schema into a single file using $defs. + +Usage: + python scripts/bundle_defs.py \ + --input config/schema/s-core-launch-manager.schema.json \ + --output config/schema/s-core-launch-manager.defs.bundle.json + + +Notes: +- Instead of fully inlining (flattening) each $ref, this script: + 1) Collects every local file $ref (e.g. "./types/*.schema.json"). + 2) Imports each referenced schema once into the top-level "$defs" (deduplicated). + 3) Rewrites $ref values to point to "#/$defs/" (JSON Pointer fragments like "#/foo/bar" are preserved by converting) + * For example: "file.json#/foo/bar" -> "#/$defs//foo/bar". +- If the imported schema itself has local file refs, those are also pulled into $defs recursively with the same logic. +- Name derivation for $defs keys: file basename without ".schema.json" (e.g., "watchdog"). Collisions are resolved by appending a numeric suffix (e.g., watchdog_2). +- To avoid base-URI confusion inside bundled defs, $id and nested $schema are stripped from the imported defs. +""" + +from __future__ import annotations +import argparse +import json +from pathlib import Path +from typing import Any, Dict, Tuple + +Json = Any + +class DefsBundler: + def __init__(self, input_file: Path) -> None: + self.input_file = input_file.resolve() + self.defs: Dict[str, Json] = {} + self.file_to_defname: Dict[Path, str] = {} + + @staticmethod + def _deepcopy(obj: Json) -> Json: + return json.loads(json.dumps(obj)) + + @staticmethod + def _is_local_file_ref(ref: str) -> bool: + if not isinstance(ref, str): + return False + if ref.startswith('#'): + return False + if '://' in ref: + return False + return True + + @staticmethod + def _split_ref(ref: str) -> Tuple[str, str]: + # Returns (file_part, fragment_pointer) where fragment_pointer is like "/a/b" (without leading '#') or '' + if '#' in ref: + file_part, frag = ref.split('#', 1) + if frag.startswith('/'): + return file_part, frag # JSON Pointer already + if frag.startswith('#/'): + return file_part, frag[1:] + # treat unknown as JSON Pointer missing leading '/' + return file_part, '/' + frag if frag else '' + return ref, '' + + @staticmethod + def _derive_name_from_file(path: Path) -> str: + name = path.stem # e.g., "watchdog.schema" + if name.endswith('.schema'): + name = name[:-7] # remove trailing ".schema" + return name + + def _unique_def_name(self, base: str) -> str: + if base not in self.defs: + return base + i = 2 + while True: + cand = f"{base}_{i}" + if cand not in self.defs: + return cand + i += 1 + + def _strip_ids(self, schema: Json) -> Json: + # Remove $id and nested $schema fields from imported defs to avoid base-URI conflicts + if isinstance(schema, dict): + schema = {k: self._strip_ids(v) for k, v in schema.items() if k not in ('$id', '$schema')} + elif isinstance(schema, list): + schema = [self._strip_ids(v) for v in schema] + return schema + + def _register_def_from_file(self, current_file: Path, ref_path: str) -> str: + target = (current_file.parent / ref_path).resolve() + if target in self.file_to_defname: + return self.file_to_defname[target] + with open(target, 'r', encoding='utf-8') as f: + schema = json.load(f) + name_base = self._derive_name_from_file(target) + name = self._unique_def_name(name_base) + cleaned = self._strip_ids(schema) + # Before storing, rewrite refs inside this imported schema + rewritten = self._rewrite_refs(cleaned, target) + self.defs[name] = rewritten + self.file_to_defname[target] = name + return name + + def _rewrite_refs(self, node: Json, current_file: Path) -> Json: + # Traverse node; for any local file $ref, add that file into $defs and rewrite the $ref to #/$defs//fragment + if isinstance(node, dict): + if '$ref' in node and isinstance(node['$ref'], str): + ref_str = node['$ref'] + if self._is_local_file_ref(ref_str): + file_part, frag = self._split_ref(ref_str) + defname = self._register_def_from_file(current_file, file_part) if file_part else None + if defname: + # Compose new JSON Pointer: #/$defs/ + pointer = f"#/$defs/{defname}{frag}" + return {**{k: v for k, v in node.items() if k != '$ref'}, '$ref': pointer} + # Otherwise, descend + return {k: self._rewrite_refs(v, current_file) for k, v in node.items()} + elif isinstance(node, list): + return [self._rewrite_refs(v, current_file) for v in node] + else: + return node + + def bundle(self, out_path: Path) -> None: + with open(self.input_file, 'r', encoding='utf-8') as f: + root = json.load(f) + + bundled_root = self._deepcopy(root) + + # Rewrite refs in the root + bundled_root = self._rewrite_refs(bundled_root, self.input_file) + + # Merge with existing $defs if any + existing_defs = bundled_root.get('$defs', {}) if isinstance(bundled_root, dict) else {} + if not isinstance(existing_defs, dict): + existing_defs = {} + + merged_defs: Dict[str, Json] = {} + + # Copy existing defs + for k, v in existing_defs.items(): + merged_defs[k] = v + + # Add generated defs (dedupe) + for k, v in self.defs.items(): + kk = k + ctr = 2 + while kk in merged_defs: + kk = f"{k}_{ctr}" + ctr += 1 + merged_defs[kk] = v + + # ---------- force a specific order in generated schema ---------- + if isinstance(bundled_root, dict): + new_root = {} + + # The "$schema", "type", "$id", "title", and "description" elements should be at the top of the file + for key in ["$schema", "type", "$id", "title", "description"]: + if key in bundled_root: + new_root[key] = bundled_root[key] + + # Then insert "$defs" (before "properties") + new_root["$defs"] = merged_defs + + # Insert "properties" immediately after "$defs" + if "properties" in bundled_root: + new_root["properties"] = bundled_root["properties"] + + # Insert remaining keys (required, additionalProperties, etc.) + for key, value in bundled_root.items(): + if key not in new_root: + new_root[key] = value + + bundled_root = new_root + # ---------------------------------------------------------------- + + out_path.parent.mkdir(parents=True, exist_ok=True) + with open(out_path, 'w', encoding='utf-8') as f: + json.dump(bundled_root, f, indent=2, ensure_ascii=False) + + +def main() -> None: + ap = argparse.ArgumentParser(description='Bundle multi-file JSON Schema into one file using $defs (deduplicated).') + ap.add_argument('--input', required=True, help='Path to the top-level schema JSON') + ap.add_argument('--output', required=True, help='Path to write the bundled schema JSON') + args = ap.parse_args() + + bundler = DefsBundler(Path(args.input)) + bundler.bundle(Path(args.output)) + print(f"Bundled schema written to: {args.output}") + +if __name__ == '__main__': + main() + diff --git a/src/launch_manager_daemon/config/new_config_schema/scripts/validate.py b/src/launch_manager_daemon/config/new_config_schema/scripts/validate.py new file mode 100755 index 00000000..4f9cd520 --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/scripts/validate.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 + +""" +Validate JSON instance(s) against a multi-file JSON Schema with relative $ref paths. + +Usage examples: + Validate a single file: + python validate_config.py --schema ./schema/s-core_launch_manager.schema.json --instance ./examples/config.json + + Validate all JSON files in a directory (recursively): + python validate_config.py --schema ./schema/s-core_launch_manager.schema.json --instances-dir ./examples + +Exit codes: + 0 -> all instances are valid + 1 -> at least one instance failed validation or there was an error +""" + +import argparse +import json +import sys +from pathlib import Path + +try: + from jsonschema import validators, RefResolver, FormatChecker + from jsonschema.exceptions import RefResolutionError, SchemaError, ValidationError +except ImportError: + print("This script requires the 'jsonschema' package. Install with:\n pip install jsonschema", file=sys.stderr) + sys.exit(1) + + +def load_json(path: Path): + try: + with path.open("r", encoding="utf-8") as f: + return json.load(f) + except json.JSONDecodeError as e: + raise ValueError(f"Failed to parse JSON file '{path}': {e}") from e + except OSError as e: + raise ValueError(f"Failed to read file '{path}': {e}") from e + + +def json_pointer_path(parts): + """ + Convert an error path into a friendly string like: + $.topLevel.items[2].name + """ + if not parts: + return "$" + s = "$" + for p in parts: + if isinstance(p, int): + s += f"[{p}]" + else: + s += f".{p}" + return s + + +def build_validator(schema_path: Path): + schema = load_json(schema_path) + + # Choose validator based on $schema automatically (Draft-07 / 2019-09 / 2020-12, etc.) + ValidatorClass = validators.validator_for(schema) + # Validate the schema itself (optional but helpful) + try: + ValidatorClass.check_schema(schema) + except SchemaError as e: + raise SchemaError(f"Your schema appears invalid: {e.message}\nAt: {'/'.join(map(str, e.path))}") from e + + # Base URI for resolving relative $refs like "./types/*.schema.json" + base_uri = schema_path.resolve().parent.as_uri() + "/" + + # RefResolver is deprecated upstream but still widely supported and reliable for local file resolution. + resolver = RefResolver(base_uri=base_uri, referrer=schema) + + # Enable common format checks (e.g., "uri", "email", "date-time") + format_checker = FormatChecker() + + return ValidatorClass(schema, resolver=resolver, format_checker=format_checker) + + +def validate_instance(validator, instance_path: Path): + instance = load_json(instance_path) + errors = sorted(validator.iter_errors(instance), key=lambda e: list(e.path)) + return errors + + +def find_json_files(root: Path): + # Recurse and pick *.json files only + return [p for p in root.rglob("*.json") if p.is_file()] + + +def main(): + parser = argparse.ArgumentParser(description="Validate JSON instance(s) against a multi-file JSON Schema.") + parser.add_argument("--schema", required=True, type=Path, help="Path to the top-level schema (e.g., ./schema/s-core_launch_manager.schema.json)") + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("--instance", type=Path, help="Path to a single JSON instance to validate") + group.add_argument("--instances-dir", type=Path, help="Path to a directory containing JSON instances (recursively)") + parser.add_argument("--stop-on-first", action="store_true", help="Stop after the first instance with errors") + args = parser.parse_args() + + try: + validator = build_validator(args.schema) + except (ValueError, SchemaError) as e: + print(f"[Schema Error] {e}", file=sys.stderr) + sys.exit(1) + + instance_paths = [] + if args.instance: + instance_paths = [args.instance] + else: + if not args.instances_dir.exists(): + print(f"[Error] Instances directory not found: {args.instances_dir}", file=sys.stderr) + sys.exit(1) + instance_paths = find_json_files(args.instances_dir) + if not instance_paths: + print(f"[Info] No JSON files found under: {args.instances_dir}") + sys.exit(0) + + any_failed = False + for path in instance_paths: + try: + errors = validate_instance(validator, path) + except ValueError as e: + print(f"Error --> {path}: {e}", file=sys.stderr) + any_failed = True + if args.stop_on_first: + break + continue + except RefResolutionError as e: + print(f"Error --> {path}: Failed to resolve a $ref - {e}", file=sys.stderr) + print(" Tips:") + print(" * Ensure $ref paths like './types/...' are correct relative to the top-level schema file.") + print(" * Make sure referenced files exist and are valid JSON schemas.") + any_failed = True + if args.stop_on_first: + break + continue + + if not errors: + print(f"Success --> {path}: valid") + else: + any_failed = True + print(f"Error --> {path}: {len(errors)} error(s)") + for i, err in enumerate(errors, 1): + instance_loc = json_pointer_path(err.path) + schema_loc = "/".join(map(str, err.schema_path)) if err.schema_path else "(root)" + print(f" [{i}] at {instance_loc}") + print(f" --> {err.message}") + print(f" schema path: {schema_loc}") + if args.stop_on_first: + break + + sys.exit(1 if any_failed else 0) + + +if __name__ == "__main__": + main() + From 415be1f6a5f65e71da77c7bc3d22b255c1aaa0f7 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Thu, 19 Feb 2026 15:43:20 +0000 Subject: [PATCH 02/17] Adding first version of documentation for json schema --- .../config/new_config_schema/README.rst | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 src/launch_manager_daemon/config/new_config_schema/README.rst diff --git a/src/launch_manager_daemon/config/new_config_schema/README.rst b/src/launch_manager_daemon/config/new_config_schema/README.rst new file mode 100755 index 00000000..e46184cf --- /dev/null +++ b/src/launch_manager_daemon/config/new_config_schema/README.rst @@ -0,0 +1,53 @@ +################################### +Launch Manager Configuration Schema +################################### + +This folder contains the development environment for the Launch Manager configuration JSON Schema. The schema is developed across multiple files to facilitate maintenance and modifications, then compiled into a single file for publication. This approach simplifies the development process while maintaining convenience for end users. + + +******** +Examples +******** + +Configuration examples are provided in the ``examples`` folder, each accompanied by a brief description. + + +****** +Schema +****** + +The ``schema`` folder contains the primary development work. The setup uses a multi-file structure where reusable ``types`` are stored in the types folder, and the top-level schema resides in the ``s-core_launch_manager.schema.json`` file. The key feature of this setup is the use of relative ``$ref`` paths throughout the folder structure. Note that all references are constructed relative to the current file's location. + +For instance, to reference a file from a subfolder use: + +.. code-block:: json + + "$ref": "./subfolder_name/file_name" + +And to reference a file from the same folder use: + +.. code-block:: json + + "$ref": "./file_name" + + +**************** +Published Schema +**************** + +The official, end-user consumable schema is placed in the ``published_schema`` folder. Upon completion of development, the multi-file schema from the ``schema`` folder is merged into a single file and published here. + +******* +Scripts +******* + +Utility scripts for schema development are located in ``scripts`` folder: + + * bundle.py + + * Merges the multi-file schema into a single file for end-user distribution. + + * validate.py + + * Validates Launch Manager configuration instances against the schema. This script supports both single-file and multi-file schema formats. + From 1989e86246fbbdd13e3c72925d253dd5b7845438 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:24:13 +0000 Subject: [PATCH 03/17] This schema is not yet in use, new folder name reflects this better --- .../config/{new_config_schema => future_config_schema}/README.rst | 0 .../examples/example_conf.json | 0 .../published_schema/s-core_launch_manager.schema.json | 0 .../schema/s-core_launch_manager.schema.json | 0 .../schema/types/alive_supervision.schema.json | 0 .../schema/types/component_properties.schema.json | 0 .../schema/types/deployment_config.schema.json | 0 .../schema/types/recovery_action.schema.json | 0 .../schema/types/run_target.schema.json | 0 .../schema/types/watchdog.schema.json | 0 .../{new_config_schema => future_config_schema}/scripts/bundle.py | 0 .../scripts/validate.py | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/README.rst (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/examples/example_conf.json (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/published_schema/s-core_launch_manager.schema.json (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/schema/s-core_launch_manager.schema.json (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/schema/types/alive_supervision.schema.json (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/schema/types/component_properties.schema.json (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/schema/types/deployment_config.schema.json (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/schema/types/recovery_action.schema.json (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/schema/types/run_target.schema.json (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/schema/types/watchdog.schema.json (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/scripts/bundle.py (100%) rename src/launch_manager_daemon/config/{new_config_schema => future_config_schema}/scripts/validate.py (100%) diff --git a/src/launch_manager_daemon/config/new_config_schema/README.rst b/src/launch_manager_daemon/config/future_config_schema/README.rst similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/README.rst rename to src/launch_manager_daemon/config/future_config_schema/README.rst diff --git a/src/launch_manager_daemon/config/new_config_schema/examples/example_conf.json b/src/launch_manager_daemon/config/future_config_schema/examples/example_conf.json similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/examples/example_conf.json rename to src/launch_manager_daemon/config/future_config_schema/examples/example_conf.json diff --git a/src/launch_manager_daemon/config/new_config_schema/published_schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/published_schema/s-core_launch_manager.schema.json rename to src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/future_config_schema/schema/s-core_launch_manager.schema.json similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/schema/s-core_launch_manager.schema.json rename to src/launch_manager_daemon/config/future_config_schema/schema/s-core_launch_manager.schema.json diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/alive_supervision.schema.json b/src/launch_manager_daemon/config/future_config_schema/schema/types/alive_supervision.schema.json similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/schema/types/alive_supervision.schema.json rename to src/launch_manager_daemon/config/future_config_schema/schema/types/alive_supervision.schema.json diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/component_properties.schema.json b/src/launch_manager_daemon/config/future_config_schema/schema/types/component_properties.schema.json similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/schema/types/component_properties.schema.json rename to src/launch_manager_daemon/config/future_config_schema/schema/types/component_properties.schema.json diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/deployment_config.schema.json b/src/launch_manager_daemon/config/future_config_schema/schema/types/deployment_config.schema.json similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/schema/types/deployment_config.schema.json rename to src/launch_manager_daemon/config/future_config_schema/schema/types/deployment_config.schema.json diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/recovery_action.schema.json b/src/launch_manager_daemon/config/future_config_schema/schema/types/recovery_action.schema.json similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/schema/types/recovery_action.schema.json rename to src/launch_manager_daemon/config/future_config_schema/schema/types/recovery_action.schema.json diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/run_target.schema.json b/src/launch_manager_daemon/config/future_config_schema/schema/types/run_target.schema.json similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/schema/types/run_target.schema.json rename to src/launch_manager_daemon/config/future_config_schema/schema/types/run_target.schema.json diff --git a/src/launch_manager_daemon/config/new_config_schema/schema/types/watchdog.schema.json b/src/launch_manager_daemon/config/future_config_schema/schema/types/watchdog.schema.json similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/schema/types/watchdog.schema.json rename to src/launch_manager_daemon/config/future_config_schema/schema/types/watchdog.schema.json diff --git a/src/launch_manager_daemon/config/new_config_schema/scripts/bundle.py b/src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/scripts/bundle.py rename to src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py diff --git a/src/launch_manager_daemon/config/new_config_schema/scripts/validate.py b/src/launch_manager_daemon/config/future_config_schema/scripts/validate.py similarity index 100% rename from src/launch_manager_daemon/config/new_config_schema/scripts/validate.py rename to src/launch_manager_daemon/config/future_config_schema/scripts/validate.py From 63e45bda507400d217bfdf0722d7953c0ba89905 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:38:55 +0000 Subject: [PATCH 04/17] New name should better reflect what is inside this folder --- .../{schema => draft_schema}/s-core_launch_manager.schema.json | 0 .../{schema => draft_schema}/types/alive_supervision.schema.json | 0 .../types/component_properties.schema.json | 0 .../{schema => draft_schema}/types/deployment_config.schema.json | 0 .../{schema => draft_schema}/types/recovery_action.schema.json | 0 .../{schema => draft_schema}/types/run_target.schema.json | 0 .../{schema => draft_schema}/types/watchdog.schema.json | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename src/launch_manager_daemon/config/future_config_schema/{schema => draft_schema}/s-core_launch_manager.schema.json (100%) rename src/launch_manager_daemon/config/future_config_schema/{schema => draft_schema}/types/alive_supervision.schema.json (100%) rename src/launch_manager_daemon/config/future_config_schema/{schema => draft_schema}/types/component_properties.schema.json (100%) rename src/launch_manager_daemon/config/future_config_schema/{schema => draft_schema}/types/deployment_config.schema.json (100%) rename src/launch_manager_daemon/config/future_config_schema/{schema => draft_schema}/types/recovery_action.schema.json (100%) rename src/launch_manager_daemon/config/future_config_schema/{schema => draft_schema}/types/run_target.schema.json (100%) rename src/launch_manager_daemon/config/future_config_schema/{schema => draft_schema}/types/watchdog.schema.json (100%) diff --git a/src/launch_manager_daemon/config/future_config_schema/schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/schema/s-core_launch_manager.schema.json rename to src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/schema/types/alive_supervision.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/alive_supervision.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/schema/types/alive_supervision.schema.json rename to src/launch_manager_daemon/config/future_config_schema/draft_schema/types/alive_supervision.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/schema/types/component_properties.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/schema/types/component_properties.schema.json rename to src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/schema/types/deployment_config.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/deployment_config.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/schema/types/deployment_config.schema.json rename to src/launch_manager_daemon/config/future_config_schema/draft_schema/types/deployment_config.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/schema/types/recovery_action.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/recovery_action.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/schema/types/recovery_action.schema.json rename to src/launch_manager_daemon/config/future_config_schema/draft_schema/types/recovery_action.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/schema/types/run_target.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/schema/types/run_target.schema.json rename to src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/schema/types/watchdog.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/watchdog.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/schema/types/watchdog.schema.json rename to src/launch_manager_daemon/config/future_config_schema/draft_schema/types/watchdog.schema.json From 596a7327cafe4f4563ae9249a37e876aaf3862e2 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:40:52 +0000 Subject: [PATCH 05/17] Improve development setup documentation --- .../config/future_config_schema/README.rst | 164 +++++++++++++++--- 1 file changed, 142 insertions(+), 22 deletions(-) diff --git a/src/launch_manager_daemon/config/future_config_schema/README.rst b/src/launch_manager_daemon/config/future_config_schema/README.rst index e46184cf..528c4f88 100755 --- a/src/launch_manager_daemon/config/future_config_schema/README.rst +++ b/src/launch_manager_daemon/config/future_config_schema/README.rst @@ -1,53 +1,173 @@ -################################### +.. + # ******************************************************************************* + # Copyright (c) 2025 Contributors to the Eclipse Foundation + # + # See the NOTICE file(s) distributed with this work for additional + # information regarding copyright ownership. + # + # This program and the accompanying materials are made available under the + # terms of the Apache License Version 2.0 which is available at + # https://www.apache.org/licenses/LICENSE-2.0 + # + # SPDX-License-Identifier: Apache-2.0 + # ******************************************************************************* + + Launch Manager Configuration Schema ################################### -This folder contains the development environment for the Launch Manager configuration JSON Schema. The schema is developed across multiple files to facilitate maintenance and modifications, then compiled into a single file for publication. This approach simplifies the development process while maintaining convenience for end users. - +This folder contains the development environment for the Launch Manager configuration JSON Schema. The schema defines and validates the structure of Launch Manager configuration files. +Overview ******** + +This project uses a **two-folder approach** for schema management: + +- ``draft_schema/`` - Multi-file schema structure for active development +- ``published_schema/`` - Single-file schema for end-user consumption + +The multi-file structure in ``draft_schema/`` makes it easier to maintain and modify the schema by organizing reusable components into separate files. When development is complete, these files are compiled into a single file in ``published_schema/`` for convenience of end users. + +**Project Structure:** + +:: + + +-- draft_schema/ # Multi-file schema under development + +-- published_schema/ # Single-file schema ready for use + +-- examples/ # Sample configuration files + +-- scripts/ # Tools for bundling and validation + +Quick Start +*********** + +For End Users +============= + +If you just want to validate your Launch Manager configuration: + +1. Use the schema in ``published_schema/s-core_launch_manager.schema.json`` +2. Check the ``examples/`` folder for sample configurations +3. Validate your config: + + .. code-block:: bash + + validate.py --schema published_schema/s-core_launch_manager.schema.json --instance your_config.json + +For Schema Developers +====================== + +If you're modifying or extending the schema: + +1. Edit files in ``draft_schema/`` +2. Bundle your changes: + + .. code-block:: bash + + bundle.py --input draft_schema/s-core_launch_manager.schema.json --output published_schema/s-core_launch_manager.schema.json + +3. Test against examples to ensure nothing broke + + Examples ******** -Configuration examples are provided in the ``examples`` folder, each accompanied by a brief description. +Configuration examples are provided in the ``examples`` folder, each accompanied by a brief description. **Start here** if you're new to Launch Manager configurations - these show real-world usage patterns. + + +Schema Development (draft_schema) +********************************** +The ``draft_schema`` folder contains the primary development work. The setup uses a multi-file structure where: -****** -Schema -****** +- **Reusable types** are stored in the ``types/`` subfolder +- **Top-level schema** resides in ``s-core_launch_manager.schema.json`` file -The ``schema`` folder contains the primary development work. The setup uses a multi-file structure where reusable ``types`` are stored in the types folder, and the top-level schema resides in the ``s-core_launch_manager.schema.json`` file. The key feature of this setup is the use of relative ``$ref`` paths throughout the folder structure. Note that all references are constructed relative to the current file's location. +Working with $ref Paths +======================== -For instance, to reference a file from a subfolder use: +The multi-file schema uses JSON Schema's ``$ref`` keyword to reference definitions across files. Understanding how these references work is crucial when modifying the schema. + +**Key principle:** All ``$ref`` paths are relative to the location of the file containing the reference, not to any root folder. + +Reference Examples +------------------ + +**To reference a file in a subfolder** (e.g., from ``s-core_launch_manager.schema.json`` to ``types/deployment_config.schema.json``): .. code-block:: json - "$ref": "./subfolder_name/file_name" + "$ref": "./types/deployment_config.schema.json" -And to reference a file from the same folder use: +**To reference a file in the same folder:** (e.g., from ``types/deployment_config.schema.json`` to ``types/recovery_action.schema.json``): .. code-block:: json - "$ref": "./file_name" + "$ref": "./recovery_action.schema.json" +Common Pitfalls +--------------- -**************** -Published Schema -**************** +- **Always use relative paths** starting with ``./`` or ``../`` +- **Don't use absolute paths** or paths from the project root +- **Remember the current file's location** when constructing paths +- When moving files, **update all references** to and from that file + +The bundling script resolves all these relative references into a single file, so the published schema doesn't need external file references. + + +Published Schema (published_schema) +************************************ + +The official, end-user consumable schema is placed in the ``published_schema`` folder. Upon completion of development, the multi-file schema from the ``draft_schema`` folder is merged into a single file and published here. + +**This is the version end users should reference** in their validation tools and IDE configurations. -The official, end-user consumable schema is placed in the ``published_schema`` folder. Upon completion of development, the multi-file schema from the ``schema`` folder is merged into a single file and published here. -******* Scripts ******* -Utility scripts for schema development are located in ``scripts`` folder: +Utility scripts for schema development are located in the ``scripts`` folder: + +bundle.py +========= + +Merges the multi-file schema into a single file for end-user distribution. - * bundle.py +**Usage:** - * Merges the multi-file schema into a single file for end-user distribution. +.. code-block:: bash - * validate.py + bundle.py --input ../draft_schema/s-core_launch_manager.schema.json --output ../published_schema/s-core_launch_manager.schema.json + Bundled schema written to: ../published_schema/s-core_launch_manager.schema.json - * Validates Launch Manager configuration instances against the schema. This script supports both single-file and multi-file schema formats. +**When to use:** After making changes in ``draft_schema/``, run this to create the publishable version. + +validate.py +=========== + +Validates Launch Manager configuration instances against the schema. This script supports both single-file and multi-file schema formats. + +**Validate against published schema:** + +.. code-block:: bash + + validate.py --schema ../published_schema/s-core_launch_manager.schema.json --instance ../examples/example_conf.json + Success --> ../examples/example_conf.json: valid + +**Validate against draft schema (during development):** + +.. code-block:: bash + + validate.py --schema ../draft_schema/s-core_launch_manager.schema.json --instance ../examples/example_conf.json + Success --> ../examples/example_conf.json: valid + +**When to use:** Run this frequently during development to catch errors early. Always validate examples before publishing. + + +Typical Workflow +**************** +1. **Modify** schema files in ``draft_schema/`` +2. **Validate** your changes against examples using the draft schema +3. **Bundle** the multi-file schema into a single file +4. **Validate** examples again against the published schema From 7ecce4332c052b0a9e2b5605956c97bc2adc4060 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:10:33 +0000 Subject: [PATCH 06/17] Changing mandatory configuration requirements Make initial_run_target mandatory, to force users to explicitly specify system startup behavior. Remove recovery_action requirement from run_target type, as meaningful defaults are difficult to define and enforcement should occur outside the schema. --- .../draft_schema/s-core_launch_manager.schema.json | 3 ++- .../draft_schema/types/run_target.schema.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json index d7344161..f76e3591 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json @@ -117,7 +117,8 @@ } }, "required": [ - "schema_version" + "schema_version", + "initial_run_target" ], "additionalProperties": false } diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json index 946748d9..72f72a0a 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json @@ -40,6 +40,6 @@ } }, - "required": ["recovery_action"], + "required": [], "additionalProperties": false } From 346b7b5a86f97f75cf5da856332ec4ddd1c678b4 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:30:49 +0000 Subject: [PATCH 07/17] Adaptations to reflect changes in previous commit --- .../config/future_config_schema/examples/example_conf.json | 7 +------ .../published_schema/s-core_launch_manager.schema.json | 7 +++---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/launch_manager_daemon/config/future_config_schema/examples/example_conf.json b/src/launch_manager_daemon/config/future_config_schema/examples/example_conf.json index fc69c5c3..ddb3bc7a 100755 --- a/src/launch_manager_daemon/config/future_config_schema/examples/example_conf.json +++ b/src/launch_manager_daemon/config/future_config_schema/examples/example_conf.json @@ -47,12 +47,7 @@ }, "run_target": { "depends_on": [], - "transition_timeout": 5, - "recovery_action": { - "switch_run_target": { - "run_target": "Off" - } - } + "transition_timeout": 5 }, "alive_supervision" : { "evaluation_cycle": 0.5 diff --git a/src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json index 1cf10a09..7a2c547d 100644 --- a/src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json @@ -326,9 +326,7 @@ "description": "Specifies the recovery action to execute when a component assigned to this Run Target fails." } }, - "required": [ - "recovery_action" - ], + "required": [], "additionalProperties": false }, "alive_supervision": { @@ -486,7 +484,8 @@ } }, "required": [ - "schema_version" + "schema_version", + "initial_run_target" ], "additionalProperties": false } \ No newline at end of file From 75d558c76676466aa526670f9387befcda38ff05 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:48:31 +0000 Subject: [PATCH 08/17] Fixing copyright headers --- .../config/future_config_schema/README.rst | 2 +- .../config/future_config_schema/scripts/bundle.py | 12 ++++++++++++ .../config/future_config_schema/scripts/validate.py | 12 ++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/launch_manager_daemon/config/future_config_schema/README.rst b/src/launch_manager_daemon/config/future_config_schema/README.rst index 528c4f88..281467ca 100755 --- a/src/launch_manager_daemon/config/future_config_schema/README.rst +++ b/src/launch_manager_daemon/config/future_config_schema/README.rst @@ -1,6 +1,6 @@ .. # ******************************************************************************* - # Copyright (c) 2025 Contributors to the Eclipse Foundation + # Copyright (c) 2026 Contributors to the Eclipse Foundation # # See the NOTICE file(s) distributed with this work for additional # information regarding copyright ownership. diff --git a/src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py b/src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py index 20b6e452..b54ae847 100755 --- a/src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py +++ b/src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py @@ -1,4 +1,16 @@ #!/usr/bin/env python3 +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* """ Bundle a multi-file JSON Schema into a single file using $defs. diff --git a/src/launch_manager_daemon/config/future_config_schema/scripts/validate.py b/src/launch_manager_daemon/config/future_config_schema/scripts/validate.py index 4f9cd520..5521528a 100755 --- a/src/launch_manager_daemon/config/future_config_schema/scripts/validate.py +++ b/src/launch_manager_daemon/config/future_config_schema/scripts/validate.py @@ -1,4 +1,16 @@ #!/usr/bin/env python3 +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* """ Validate JSON instance(s) against a multi-file JSON Schema with relative $ref paths. From 31bb735f31d951d009abe7147cc7e9d698eab4dc Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:05:32 +0000 Subject: [PATCH 09/17] Changes introduced by 'bazel run //:format.fix' --- .../future_config_schema/scripts/bundle.py | 64 ++++++++++++------- .../future_config_schema/scripts/validate.py | 50 +++++++++++---- 2 files changed, 80 insertions(+), 34 deletions(-) diff --git a/src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py b/src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py index b54ae847..d5dc26f3 100755 --- a/src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py +++ b/src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py @@ -40,6 +40,7 @@ Json = Any + class DefsBundler: def __init__(self, input_file: Path) -> None: self.input_file = input_file.resolve() @@ -54,29 +55,29 @@ def _deepcopy(obj: Json) -> Json: def _is_local_file_ref(ref: str) -> bool: if not isinstance(ref, str): return False - if ref.startswith('#'): + if ref.startswith("#"): return False - if '://' in ref: + if "://" in ref: return False return True @staticmethod def _split_ref(ref: str) -> Tuple[str, str]: # Returns (file_part, fragment_pointer) where fragment_pointer is like "/a/b" (without leading '#') or '' - if '#' in ref: - file_part, frag = ref.split('#', 1) - if frag.startswith('/'): + if "#" in ref: + file_part, frag = ref.split("#", 1) + if frag.startswith("/"): return file_part, frag # JSON Pointer already - if frag.startswith('#/'): + if frag.startswith("#/"): return file_part, frag[1:] # treat unknown as JSON Pointer missing leading '/' - return file_part, '/' + frag if frag else '' - return ref, '' + return file_part, "/" + frag if frag else "" + return ref, "" @staticmethod def _derive_name_from_file(path: Path) -> str: name = path.stem # e.g., "watchdog.schema" - if name.endswith('.schema'): + if name.endswith(".schema"): name = name[:-7] # remove trailing ".schema" return name @@ -93,7 +94,11 @@ def _unique_def_name(self, base: str) -> str: def _strip_ids(self, schema: Json) -> Json: # Remove $id and nested $schema fields from imported defs to avoid base-URI conflicts if isinstance(schema, dict): - schema = {k: self._strip_ids(v) for k, v in schema.items() if k not in ('$id', '$schema')} + schema = { + k: self._strip_ids(v) + for k, v in schema.items() + if k not in ("$id", "$schema") + } elif isinstance(schema, list): schema = [self._strip_ids(v) for v in schema] return schema @@ -102,7 +107,7 @@ def _register_def_from_file(self, current_file: Path, ref_path: str) -> str: target = (current_file.parent / ref_path).resolve() if target in self.file_to_defname: return self.file_to_defname[target] - with open(target, 'r', encoding='utf-8') as f: + with open(target, "r", encoding="utf-8") as f: schema = json.load(f) name_base = self._derive_name_from_file(target) name = self._unique_def_name(name_base) @@ -116,15 +121,22 @@ def _register_def_from_file(self, current_file: Path, ref_path: str) -> str: def _rewrite_refs(self, node: Json, current_file: Path) -> Json: # Traverse node; for any local file $ref, add that file into $defs and rewrite the $ref to #/$defs//fragment if isinstance(node, dict): - if '$ref' in node and isinstance(node['$ref'], str): - ref_str = node['$ref'] + if "$ref" in node and isinstance(node["$ref"], str): + ref_str = node["$ref"] if self._is_local_file_ref(ref_str): file_part, frag = self._split_ref(ref_str) - defname = self._register_def_from_file(current_file, file_part) if file_part else None + defname = ( + self._register_def_from_file(current_file, file_part) + if file_part + else None + ) if defname: # Compose new JSON Pointer: #/$defs/ pointer = f"#/$defs/{defname}{frag}" - return {**{k: v for k, v in node.items() if k != '$ref'}, '$ref': pointer} + return { + **{k: v for k, v in node.items() if k != "$ref"}, + "$ref": pointer, + } # Otherwise, descend return {k: self._rewrite_refs(v, current_file) for k, v in node.items()} elif isinstance(node, list): @@ -133,7 +145,7 @@ def _rewrite_refs(self, node: Json, current_file: Path) -> Json: return node def bundle(self, out_path: Path) -> None: - with open(self.input_file, 'r', encoding='utf-8') as f: + with open(self.input_file, "r", encoding="utf-8") as f: root = json.load(f) bundled_root = self._deepcopy(root) @@ -142,7 +154,9 @@ def bundle(self, out_path: Path) -> None: bundled_root = self._rewrite_refs(bundled_root, self.input_file) # Merge with existing $defs if any - existing_defs = bundled_root.get('$defs', {}) if isinstance(bundled_root, dict) else {} + existing_defs = ( + bundled_root.get("$defs", {}) if isinstance(bundled_root, dict) else {} + ) if not isinstance(existing_defs, dict): existing_defs = {} @@ -186,20 +200,24 @@ def bundle(self, out_path: Path) -> None: # ---------------------------------------------------------------- out_path.parent.mkdir(parents=True, exist_ok=True) - with open(out_path, 'w', encoding='utf-8') as f: + with open(out_path, "w", encoding="utf-8") as f: json.dump(bundled_root, f, indent=2, ensure_ascii=False) def main() -> None: - ap = argparse.ArgumentParser(description='Bundle multi-file JSON Schema into one file using $defs (deduplicated).') - ap.add_argument('--input', required=True, help='Path to the top-level schema JSON') - ap.add_argument('--output', required=True, help='Path to write the bundled schema JSON') + ap = argparse.ArgumentParser( + description="Bundle multi-file JSON Schema into one file using $defs (deduplicated)." + ) + ap.add_argument("--input", required=True, help="Path to the top-level schema JSON") + ap.add_argument( + "--output", required=True, help="Path to write the bundled schema JSON" + ) args = ap.parse_args() bundler = DefsBundler(Path(args.input)) bundler.bundle(Path(args.output)) print(f"Bundled schema written to: {args.output}") -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() diff --git a/src/launch_manager_daemon/config/future_config_schema/scripts/validate.py b/src/launch_manager_daemon/config/future_config_schema/scripts/validate.py index 5521528a..f34418be 100755 --- a/src/launch_manager_daemon/config/future_config_schema/scripts/validate.py +++ b/src/launch_manager_daemon/config/future_config_schema/scripts/validate.py @@ -36,7 +36,10 @@ from jsonschema import validators, RefResolver, FormatChecker from jsonschema.exceptions import RefResolutionError, SchemaError, ValidationError except ImportError: - print("This script requires the 'jsonschema' package. Install with:\n pip install jsonschema", file=sys.stderr) + print( + "This script requires the 'jsonschema' package. Install with:\n pip install jsonschema", + file=sys.stderr, + ) sys.exit(1) @@ -75,7 +78,9 @@ def build_validator(schema_path: Path): try: ValidatorClass.check_schema(schema) except SchemaError as e: - raise SchemaError(f"Your schema appears invalid: {e.message}\nAt: {'/'.join(map(str, e.path))}") from e + raise SchemaError( + f"Your schema appears invalid: {e.message}\nAt: {'/'.join(map(str, e.path))}" + ) from e # Base URI for resolving relative $refs like "./types/*.schema.json" base_uri = schema_path.resolve().parent.as_uri() + "/" @@ -101,12 +106,29 @@ def find_json_files(root: Path): def main(): - parser = argparse.ArgumentParser(description="Validate JSON instance(s) against a multi-file JSON Schema.") - parser.add_argument("--schema", required=True, type=Path, help="Path to the top-level schema (e.g., ./schema/s-core_launch_manager.schema.json)") + parser = argparse.ArgumentParser( + description="Validate JSON instance(s) against a multi-file JSON Schema." + ) + parser.add_argument( + "--schema", + required=True, + type=Path, + help="Path to the top-level schema (e.g., ./schema/s-core_launch_manager.schema.json)", + ) group = parser.add_mutually_exclusive_group(required=True) - group.add_argument("--instance", type=Path, help="Path to a single JSON instance to validate") - group.add_argument("--instances-dir", type=Path, help="Path to a directory containing JSON instances (recursively)") - parser.add_argument("--stop-on-first", action="store_true", help="Stop after the first instance with errors") + group.add_argument( + "--instance", type=Path, help="Path to a single JSON instance to validate" + ) + group.add_argument( + "--instances-dir", + type=Path, + help="Path to a directory containing JSON instances (recursively)", + ) + parser.add_argument( + "--stop-on-first", + action="store_true", + help="Stop after the first instance with errors", + ) args = parser.parse_args() try: @@ -120,7 +142,10 @@ def main(): instance_paths = [args.instance] else: if not args.instances_dir.exists(): - print(f"[Error] Instances directory not found: {args.instances_dir}", file=sys.stderr) + print( + f"[Error] Instances directory not found: {args.instances_dir}", + file=sys.stderr, + ) sys.exit(1) instance_paths = find_json_files(args.instances_dir) if not instance_paths: @@ -140,7 +165,9 @@ def main(): except RefResolutionError as e: print(f"Error --> {path}: Failed to resolve a $ref - {e}", file=sys.stderr) print(" Tips:") - print(" * Ensure $ref paths like './types/...' are correct relative to the top-level schema file.") + print( + " * Ensure $ref paths like './types/...' are correct relative to the top-level schema file." + ) print(" * Make sure referenced files exist and are valid JSON schemas.") any_failed = True if args.stop_on_first: @@ -154,7 +181,9 @@ def main(): print(f"Error --> {path}: {len(errors)} error(s)") for i, err in enumerate(errors, 1): instance_loc = json_pointer_path(err.path) - schema_loc = "/".join(map(str, err.schema_path)) if err.schema_path else "(root)" + schema_loc = ( + "/".join(map(str, err.schema_path)) if err.schema_path else "(root)" + ) print(f" [{i}] at {instance_loc}") print(f" --> {err.message}") print(f" schema path: {schema_loc}") @@ -166,4 +195,3 @@ def main(): if __name__ == "__main__": main() - From 6a82242d584e07903fc7ee476865451ad68afc4b Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:57:41 +0000 Subject: [PATCH 10/17] Addressing review comment Co-authored-by: ramceb <89037993+ramceb@users.noreply.github.com> Signed-off-by: SimonKozik <244535158+SimonKozik@users.noreply.github.com> --- .../draft_schema/types/component_properties.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json index 087b470f..e78bb609 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json @@ -5,7 +5,7 @@ "properties": { "binary_name": { "type": "string", - "description": "Specifies the relative path of the executable file inside the directory defined by 'deployment_config.bin_dir'. The final executable path will be resolved as '{bin_dir}/{binary_name}'. Example values include simple filenames (e.g., 'test_app1') or subdirectory paths (e.g., 'bin/test_app1')." + "description": "Specifies the relative path of the executable file inside the directory defined by 'deployment_config.bin_dir'. The final executable path will be resolved as '{deployment_config.bin_dir}/{binary_name}'. Example values include simple filenames (e.g., 'test_app1') or subdirectory paths (e.g., 'bin/test_app1')." }, "application_profile": { From 57ef0eb7ef29b87123a86085d0ab0d9785030c48 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:58:27 +0000 Subject: [PATCH 11/17] Addressing review comments Co-authored-by: ramceb <89037993+ramceb@users.noreply.github.com> Signed-off-by: SimonKozik <244535158+SimonKozik@users.noreply.github.com> --- .../draft_schema/types/component_properties.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json index e78bb609..287033cf 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", - "description": "Defines a reusable type that captures essential development-time characteristics of a software component.", + "description": "Defines a reusable type that captures essential characteristics of a software component.", "properties": { "binary_name": { "type": "string", From 28cc1fc48663ef1b9a1412f86ee7669824e8a75c Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Tue, 24 Feb 2026 16:00:43 +0000 Subject: [PATCH 12/17] Applying review suggestion Co-authored-by: ramceb <89037993+ramceb@users.noreply.github.com> Signed-off-by: SimonKozik <244535158+SimonKozik@users.noreply.github.com> --- .../draft_schema/types/component_properties.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json index 287033cf..49818794 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json @@ -61,7 +61,7 @@ "depends_on": { "type": "array", - "description": "Specifies the names of components that this component depends on. Each dependency must be initialized and reach its ready state before this component can start.", + "description": "Specifies the names of components that this component depends on. Each dependency must be initialized and reach its ready state before the launch_manager will start this component.", "items": { "type": "string", "description": "Specifies the name of a component on which this component depends." From 6218342115ab8ef998d36c29cf34a0c85466b2a0 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Wed, 25 Feb 2026 17:31:00 +0000 Subject: [PATCH 13/17] Adding measurements units to the description strings --- .../s-core_launch_manager.schema.json | 2 +- .../types/alive_supervision.schema.json | 2 +- .../types/component_properties.schema.json | 4 ++-- .../types/deployment_config.schema.json | 6 ++--- .../types/recovery_action.schema.json | 2 +- .../draft_schema/types/run_target.schema.json | 2 +- .../draft_schema/types/watchdog.schema.json | 2 +- .../s-core_launch_manager.schema.json | 24 +++++++++---------- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json index f76e3591..70acb2ac 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json @@ -98,7 +98,7 @@ }, "transition_timeout": { "type": "number", - "description": "Specifies the time limit for the Run Target transition. If this limit is exceeded, the transition is considered failed.", + "description": "Specifies the time limit, in seconds (e.g., '1.5' for 1500 milliseconds), for the Run Target transition. If this limit is exceeded, the transition is considered failed.", "exclusiveMinimum": 0 } }, diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/alive_supervision.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/alive_supervision.schema.json index 919de721..757815ff 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/alive_supervision.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/alive_supervision.schema.json @@ -6,7 +6,7 @@ "evaluation_cycle": { "type": "number", "exclusiveMinimum": 0, - "description": "Specifies the length of the time window used to assess incoming alive supervision reports." + "description": "Specifies the length, in seconds (e.g., '0.5' for 500 milliseconds), of the time window used to assess incoming alive supervision reports." } }, diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json index 49818794..56ee58aa 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json @@ -33,7 +33,7 @@ "reporting_cycle": { "type": "number", "exclusiveMinimum": 0, - "description": "Specifies the duration of the time interval used to verify that the component sends alive notifications within the expected time frame." + "description": "Specifies the duration, in seconds (e.g., '0.5' for 500 milliseconds), of the time interval used to verify that the component sends alive notifications within the expected time frame." }, "failed_cycles_tolerance": { "type": "integer", @@ -61,7 +61,7 @@ "depends_on": { "type": "array", - "description": "Specifies the names of components that this component depends on. Each dependency must be initialized and reach its ready state before the launch_manager will start this component.", + "description": "Specifies the names of components that this component depends on. Each dependency must be initialized and reach its ready state before the Launch Manager will start this component.", "items": { "type": "string", "description": "Specifies the name of a component on which this component depends." diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/deployment_config.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/deployment_config.schema.json index 7a22b221..4d9dbc78 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/deployment_config.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/deployment_config.schema.json @@ -7,12 +7,12 @@ "ready_timeout": { "type": "number", "exclusiveMinimum": 0, - "description": "Specifies the maximum time allowed for the component to reach its ready state. The timeout is measured from when the component process is created until the ready conditions specified in 'component_properties.ready_condition' are met." + "description": "Specifies the maximum time, in seconds (e.g., '0.25' for 250 milliseconds), allowed for the component to reach its ready state. The timeout is measured from when the component process is created until the ready conditions specified in 'component_properties.ready_condition' are met." }, "shutdown_timeout": { "type": "number", "exclusiveMinimum": 0, - "description": "Specifies the maximum time allowed for the component to terminate after it receives a SIGTERM signal from the Launch Manager. The timeout is measured from when the Launch Manager sends the SIGTERM signal until the Operating System notifies the Launch Manager that the child process has terminated." + "description": "Specifies the maximum time, in seconds (e.g., '0.75' for 750 milliseconds), allowed for the component to terminate after it receives a SIGTERM signal from the Launch Manager. The timeout is measured from when the Launch Manager sends the SIGTERM signal until the Operating System notifies the Launch Manager that the child process has terminated." }, "environmental_variables": { "type": "object", @@ -115,7 +115,7 @@ "max_cpu_usage": { "type": "integer", "exclusiveMinimum": 0, - "description": "Specifies the maximum CPU usage limit for the component, expressed as a percentage of total CPU capacity." + "description": "Specifies the maximum CPU usage limit for the component, expressed as a percentage (%) of total CPU capacity." } }, "additionalProperties": false diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/recovery_action.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/recovery_action.schema.json index 2f5dd119..d152e7c3 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/recovery_action.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/recovery_action.schema.json @@ -15,7 +15,7 @@ "delay_before_restart": { "type": "number", "minimum": 0, - "description": "Specifies the delay duration that the Launch Manager waits before initiating a restart attempt." + "description": "Specifies the delay duration, in seconds (e.g., '0.25' for 250 milliseconds), that the Launch Manager waits before initiating a restart attempt." } }, "required": [], diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json index 72f72a0a..ac38e665 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json @@ -19,7 +19,7 @@ "transition_timeout": { "type": "number", - "description": "Specifies the time limit for the Run Target transition. If this limit is exceeded, the transition is considered failed.", + "description": "Specifies the time limit, in seconds (e.g., '1.5' for 1500 milliseconds), for the Run Target transition. If this limit is exceeded, the transition is considered failed.", "exclusiveMinimum": 0 }, diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/watchdog.schema.json b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/watchdog.schema.json index a7716025..ef07a5b9 100755 --- a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/watchdog.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/watchdog.schema.json @@ -10,7 +10,7 @@ "max_timeout": { "type": "number", "minimum": 0, - "description": "Specifies the maximum timeout value that the Launch Manager configures on the external watchdog during startup. The external watchdog uses this timeout as the deadline for receiving periodic alive reports from the Launch Manager." + "description": "Specifies the maximum timeout value, in seconds (e.g., '0.5' for 500 milliseconds), that the Launch Manager configures on the external watchdog during startup. The external watchdog uses this timeout as the deadline for receiving periodic alive reports from the Launch Manager." }, "deactivate_on_shutdown": { "type": "boolean", diff --git a/src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json index 7a2c547d..371630d7 100644 --- a/src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json +++ b/src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json @@ -6,11 +6,11 @@ "$defs": { "component_properties": { "type": "object", - "description": "Defines a reusable type that captures essential development-time characteristics of a software component.", + "description": "Defines a reusable type that captures essential characteristics of a software component.", "properties": { "binary_name": { "type": "string", - "description": "Specifies the relative path of the executable file inside the directory defined by 'deployment_config.bin_dir'. The final executable path will be resolved as '{bin_dir}/{binary_name}'. Example values include simple filenames (e.g., 'test_app1') or subdirectory paths (e.g., 'bin/test_app1')." + "description": "Specifies the relative path of the executable file inside the directory defined by 'deployment_config.bin_dir'. The final executable path will be resolved as '{deployment_config.bin_dir}/{binary_name}'. Example values include simple filenames (e.g., 'test_app1') or subdirectory paths (e.g., 'bin/test_app1')." }, "application_profile": { "type": "object", @@ -37,7 +37,7 @@ "reporting_cycle": { "type": "number", "exclusiveMinimum": 0, - "description": "Specifies the duration of the time interval used to verify that the component sends alive notifications within the expected time frame." + "description": "Specifies the duration, in seconds (e.g., '0.5' for 500 milliseconds), of the time interval used to verify that the component sends alive notifications within the expected time frame." }, "failed_cycles_tolerance": { "type": "integer", @@ -64,7 +64,7 @@ }, "depends_on": { "type": "array", - "description": "Specifies the names of components that this component depends on. Each dependency must be initialized and reach its ready state before this component can start.", + "description": "Specifies the names of components that this component depends on. Each dependency must be initialized and reach its ready state before the Launch Manager will start this component.", "items": { "type": "string", "description": "Specifies the name of a component on which this component depends." @@ -114,7 +114,7 @@ "delay_before_restart": { "type": "number", "minimum": 0, - "description": "Specifies the delay duration that the Launch Manager waits before initiating a restart attempt." + "description": "Specifies the delay duration, in seconds (e.g., '0.25' for 250 milliseconds), that the Launch Manager waits before initiating a restart attempt." } }, "required": [], @@ -154,12 +154,12 @@ "ready_timeout": { "type": "number", "exclusiveMinimum": 0, - "description": "Specifies the maximum time allowed for the component to reach its ready state. The timeout is measured from when the component process is created until the ready conditions specified in 'component_properties.ready_condition' are met." + "description": "Specifies the maximum time, in seconds (e.g., '0.25' for 250 milliseconds), allowed for the component to reach its ready state. The timeout is measured from when the component process is created until the ready conditions specified in 'component_properties.ready_condition' are met." }, "shutdown_timeout": { "type": "number", "exclusiveMinimum": 0, - "description": "Specifies the maximum time allowed for the component to terminate after it receives a SIGTERM signal from the Launch Manager. The timeout is measured from when the Launch Manager sends the SIGTERM signal until the Operating System notifies the Launch Manager that the child process has terminated." + "description": "Specifies the maximum time, in seconds (e.g., '0.75' for 750 milliseconds), allowed for the component to terminate after it receives a SIGTERM signal from the Launch Manager. The timeout is measured from when the Launch Manager sends the SIGTERM signal until the Operating System notifies the Launch Manager that the child process has terminated." }, "environmental_variables": { "type": "object", @@ -274,7 +274,7 @@ "max_cpu_usage": { "type": "integer", "exclusiveMinimum": 0, - "description": "Specifies the maximum CPU usage limit for the component, expressed as a percentage of total CPU capacity." + "description": "Specifies the maximum CPU usage limit for the component, expressed as a percentage (%) of total CPU capacity." } }, "additionalProperties": false @@ -301,7 +301,7 @@ }, "transition_timeout": { "type": "number", - "description": "Specifies the time limit for the Run Target transition. If this limit is exceeded, the transition is considered failed.", + "description": "Specifies the time limit, in seconds (e.g., '1.5' for 1500 milliseconds), for the Run Target transition. If this limit is exceeded, the transition is considered failed.", "exclusiveMinimum": 0 }, "recovery_action": { @@ -336,7 +336,7 @@ "evaluation_cycle": { "type": "number", "exclusiveMinimum": 0, - "description": "Specifies the length of the time window used to assess incoming alive supervision reports." + "description": "Specifies the length, in seconds (e.g., '0.5' for 500 milliseconds), of the time window used to assess incoming alive supervision reports." } }, "required": [], @@ -353,7 +353,7 @@ "max_timeout": { "type": "number", "minimum": 0, - "description": "Specifies the maximum timeout value that the Launch Manager configures on the external watchdog during startup. The external watchdog uses this timeout as the deadline for receiving periodic alive reports from the Launch Manager." + "description": "Specifies the maximum timeout value, in seconds (e.g., '0.5' for 500 milliseconds), that the Launch Manager configures on the external watchdog during startup. The external watchdog uses this timeout as the deadline for receiving periodic alive reports from the Launch Manager." }, "deactivate_on_shutdown": { "type": "boolean", @@ -465,7 +465,7 @@ }, "transition_timeout": { "type": "number", - "description": "Specifies the time limit for the Run Target transition. If this limit is exceeded, the transition is considered failed.", + "description": "Specifies the time limit, in seconds (e.g., '1.5' for 1500 milliseconds), for the Run Target transition. If this limit is exceeded, the transition is considered failed.", "exclusiveMinimum": 0 } }, From 7e332a91e9d9848082318377381565e7d5dcca21 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Mon, 2 Mar 2026 13:39:44 +0000 Subject: [PATCH 14/17] Changing folder name as suggested in review comment --- .../config/{future_config_schema => config_schema}/README.rst | 0 .../draft_schema/s-core_launch_manager.schema.json | 0 .../draft_schema/types/alive_supervision.schema.json | 0 .../draft_schema/types/component_properties.schema.json | 0 .../draft_schema/types/deployment_config.schema.json | 0 .../draft_schema/types/recovery_action.schema.json | 0 .../draft_schema/types/run_target.schema.json | 0 .../draft_schema/types/watchdog.schema.json | 0 .../examples/example_conf.json | 0 .../published_schema/s-core_launch_manager.schema.json | 0 .../{future_config_schema => config_schema}/scripts/bundle.py | 0 .../{future_config_schema => config_schema}/scripts/validate.py | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/README.rst (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/draft_schema/s-core_launch_manager.schema.json (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/draft_schema/types/alive_supervision.schema.json (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/draft_schema/types/component_properties.schema.json (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/draft_schema/types/deployment_config.schema.json (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/draft_schema/types/recovery_action.schema.json (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/draft_schema/types/run_target.schema.json (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/draft_schema/types/watchdog.schema.json (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/examples/example_conf.json (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/published_schema/s-core_launch_manager.schema.json (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/scripts/bundle.py (100%) rename src/launch_manager_daemon/config/{future_config_schema => config_schema}/scripts/validate.py (100%) diff --git a/src/launch_manager_daemon/config/future_config_schema/README.rst b/src/launch_manager_daemon/config/config_schema/README.rst similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/README.rst rename to src/launch_manager_daemon/config/config_schema/README.rst diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/s-core_launch_manager.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/draft_schema/s-core_launch_manager.schema.json rename to src/launch_manager_daemon/config/config_schema/draft_schema/s-core_launch_manager.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/alive_supervision.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/alive_supervision.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/draft_schema/types/alive_supervision.schema.json rename to src/launch_manager_daemon/config/config_schema/draft_schema/types/alive_supervision.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/component_properties.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/draft_schema/types/component_properties.schema.json rename to src/launch_manager_daemon/config/config_schema/draft_schema/types/component_properties.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/deployment_config.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/deployment_config.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/draft_schema/types/deployment_config.schema.json rename to src/launch_manager_daemon/config/config_schema/draft_schema/types/deployment_config.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/recovery_action.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/recovery_action.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/draft_schema/types/recovery_action.schema.json rename to src/launch_manager_daemon/config/config_schema/draft_schema/types/recovery_action.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/run_target.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/draft_schema/types/run_target.schema.json rename to src/launch_manager_daemon/config/config_schema/draft_schema/types/run_target.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/draft_schema/types/watchdog.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/watchdog.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/draft_schema/types/watchdog.schema.json rename to src/launch_manager_daemon/config/config_schema/draft_schema/types/watchdog.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/examples/example_conf.json b/src/launch_manager_daemon/config/config_schema/examples/example_conf.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/examples/example_conf.json rename to src/launch_manager_daemon/config/config_schema/examples/example_conf.json diff --git a/src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/config_schema/published_schema/s-core_launch_manager.schema.json similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/published_schema/s-core_launch_manager.schema.json rename to src/launch_manager_daemon/config/config_schema/published_schema/s-core_launch_manager.schema.json diff --git a/src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py b/src/launch_manager_daemon/config/config_schema/scripts/bundle.py similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/scripts/bundle.py rename to src/launch_manager_daemon/config/config_schema/scripts/bundle.py diff --git a/src/launch_manager_daemon/config/future_config_schema/scripts/validate.py b/src/launch_manager_daemon/config/config_schema/scripts/validate.py similarity index 100% rename from src/launch_manager_daemon/config/future_config_schema/scripts/validate.py rename to src/launch_manager_daemon/config/config_schema/scripts/validate.py From 1ed9d08f9785ef7ee08461a406e5aaaae5b78b6a Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Mon, 2 Mar 2026 13:48:15 +0000 Subject: [PATCH 15/17] Removing multi-file version of the schema --- .../s-core_launch_manager.schema.json | 124 ----------------- .../types/alive_supervision.schema.json | 15 --- .../types/component_properties.schema.json | 99 -------------- .../types/deployment_config.schema.json | 126 ------------------ .../types/recovery_action.schema.json | 43 ------ .../draft_schema/types/run_target.schema.json | 45 ------- .../draft_schema/types/watchdog.schema.json | 27 ---- 7 files changed, 479 deletions(-) delete mode 100755 src/launch_manager_daemon/config/config_schema/draft_schema/s-core_launch_manager.schema.json delete mode 100755 src/launch_manager_daemon/config/config_schema/draft_schema/types/alive_supervision.schema.json delete mode 100755 src/launch_manager_daemon/config/config_schema/draft_schema/types/component_properties.schema.json delete mode 100755 src/launch_manager_daemon/config/config_schema/draft_schema/types/deployment_config.schema.json delete mode 100755 src/launch_manager_daemon/config/config_schema/draft_schema/types/recovery_action.schema.json delete mode 100755 src/launch_manager_daemon/config/config_schema/draft_schema/types/run_target.schema.json delete mode 100755 src/launch_manager_daemon/config/config_schema/draft_schema/types/watchdog.schema.json diff --git a/src/launch_manager_daemon/config/config_schema/draft_schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/s-core_launch_manager.schema.json deleted file mode 100755 index 70acb2ac..00000000 --- a/src/launch_manager_daemon/config/config_schema/draft_schema/s-core_launch_manager.schema.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "title": "Configuration schema for the S-CORE Launch Manager", - "description": "Defines the structure and valid values for the Launch Manager configuration file, which specifies managed components, run targets, and recovery behaviors.", - "properties": { - "schema_version": { - "type": "integer", - "description": "Specifies the schema version number that the Launch Manager uses to determine how to parse and validate this configuration file.", - "enum": [ 1 ] - }, - "defaults": { - "type": "object", - "description": "Defines default configuration values that components and Run Targets inherit unless they provide their own overriding values.", - "properties": { - "component_properties": { - "$ref": "./types/component_properties.schema.json", - "description": "Defines default component property values applied to all components unless overridden in individual component definitions." - }, - "deployment_config": { - "$ref": "./types/deployment_config.schema.json", - "description": "Defines default deployment configuration values applied to all components unless overridden in individual component definitions." - }, - "run_target": { - "$ref": "./types/run_target.schema.json", - "description": "Defines default Run Target configuration values applied to all Run Targets unless overridden in individual Run Target definitions." - }, - "alive_supervision": { - "$ref": "./types/alive_supervision.schema.json", - "description": "Defines default alive supervision configuration values used unless a global 'alive_supervision' configuration is specified at the root level." - }, - "watchdog": { - "$ref": "./types/watchdog.schema.json", - "description": "Defines default watchdog configuration values applied to all watchdogs unless overridden in individual watchdog definitions." - } - }, - "required": [], - "additionalProperties": false - }, - "components": { - "type": "object", - "description": "Defines software components managed by the Launch Manager, where each property name is a unique component identifier and its value contains the component's configuration.", - "patternProperties": { - "^[a-zA-Z0-9_-]+$": { - "type": "object", - "description": "Defines an individual component's configuration properties and deployment settings.", - "properties": { - "description": { - "type": "string", - "description": "Specifies a human-readable description of the component's purpose." - }, - "component_properties": { - "$ref": "./types/component_properties.schema.json", - "description": "Defines component properties for this component; any properties not specified here are inherited from 'defaults.component_properties'." - }, - "deployment_config": { - "$ref": "./types/deployment_config.schema.json", - "description": "Defines deployment configuration for this component; any properties not specified here are inherited from 'defaults.deployment_config'." - } - }, - "required": [], - "additionalProperties": false - } - }, - "required": [], - "additionalProperties": false - }, - "run_targets": { - "type": "object", - "description": "Defines Run Targets representing different operational modes of the system, where each property name is a unique Run Target identifier and its value contains the Run Target's configuration.", - "patternProperties": { - "^[a-zA-Z0-9_-]+$": { - "$ref": "./types/run_target.schema.json" - } - }, - "required": [], - "additionalProperties": false - }, - "initial_run_target": { - "type": "string", - "description": "Specifies the name of the initial Run Target that the Launch Manager activates during the startup sequence. This name must match a Run Target defined in 'run_targets'." - }, - "fallback_run_target": { - "type": "object", - "description": "Defines the fallback Run Target configuration that the Launch Manager activates when all recovery attempts have been exhausted. This Run Target does not include a recovery_action property.", - "properties": { - "description": { - "type": "string", - "description": "Specifies a human-readable description of the fallback Run Target." - }, - "depends_on": { - "type": "array", - "description": "Specifies the names of components and Run Targets that must be activated when this Run Target is activated.", - "items": { - "type": "string", - "description": "Specifies the name of a component or Run Target that this Run Target depends on." - } - }, - "transition_timeout": { - "type": "number", - "description": "Specifies the time limit, in seconds (e.g., '1.5' for 1500 milliseconds), for the Run Target transition. If this limit is exceeded, the transition is considered failed.", - "exclusiveMinimum": 0 - } - }, - "required": [ - "depends_on" - ], - "additionalProperties": false - }, - "alive_supervision": { - "$ref": "./types/alive_supervision.schema.json", - "description": "Defines the alive supervision configuration parameters used to monitor component health. This configuration overrides 'defaults.alive_supervision' if specified." - }, - "watchdog": { - "$ref": "./types/watchdog.schema.json", - "description": "Defines the external watchdog device configuration used by the Launch Manager. This configuration overrides 'defaults.watchdog' if specified." - } - }, - "required": [ - "schema_version", - "initial_run_target" - ], - "additionalProperties": false -} diff --git a/src/launch_manager_daemon/config/config_schema/draft_schema/types/alive_supervision.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/alive_supervision.schema.json deleted file mode 100755 index 757815ff..00000000 --- a/src/launch_manager_daemon/config/config_schema/draft_schema/types/alive_supervision.schema.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "description": "Defines a reusable type that contains configuration parameters for alive supervision.", - "properties": { - "evaluation_cycle": { - "type": "number", - "exclusiveMinimum": 0, - "description": "Specifies the length, in seconds (e.g., '0.5' for 500 milliseconds), of the time window used to assess incoming alive supervision reports." - } - }, - - "required": [], - "additionalProperties": false -} diff --git a/src/launch_manager_daemon/config/config_schema/draft_schema/types/component_properties.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/component_properties.schema.json deleted file mode 100755 index 56ee58aa..00000000 --- a/src/launch_manager_daemon/config/config_schema/draft_schema/types/component_properties.schema.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "description": "Defines a reusable type that captures essential characteristics of a software component.", - "properties": { - "binary_name": { - "type": "string", - "description": "Specifies the relative path of the executable file inside the directory defined by 'deployment_config.bin_dir'. The final executable path will be resolved as '{deployment_config.bin_dir}/{binary_name}'. Example values include simple filenames (e.g., 'test_app1') or subdirectory paths (e.g., 'bin/test_app1')." - }, - - "application_profile": { - "type": "object", - "description": "Defines the application profile that specifies the runtime behavior and capabilities of this component.", - "properties": { - "application_type": { - "type": "string", - "enum": [ - "Native", - "Reporting", - "Reporting_And_Supervised", - "State_Manager" - ], - "description": "Specifies the level of integration between the component and the Launch Manager. 'Native': no integration with Launch Manager. 'Reporting': uses Launch Manager lifecycle APIs. 'Reporting_And_Supervised': uses lifecycle APIs and sends alive notifications. 'State_Manager': uses lifecycle APIs, sends alive notifications, and has permission to change the active Run Target." - }, - "is_self_terminating": { - "type": "boolean", - "description": "Indicates whether the component is designed to terminate automatically once its planned tasks are completed (true), or remain running until explicitly requested to terminate by the Launch Manager (false)." - }, - "alive_supervision": { - "type": "object", - "description": "Defines the configuration parameters used for alive monitoring of the component.", - "properties": { - "reporting_cycle": { - "type": "number", - "exclusiveMinimum": 0, - "description": "Specifies the duration, in seconds (e.g., '0.5' for 500 milliseconds), of the time interval used to verify that the component sends alive notifications within the expected time frame." - }, - "failed_cycles_tolerance": { - "type": "integer", - "minimum": 0, - "description": "Specifies the maximum number of consecutive reporting cycle failures (see 'reporting_cycle'). Once the number of failed cycles exceeds this maximum, the Launch Manager will trigger the configured recovery action." - }, - "min_indications": { - "type": "integer", - "minimum": 0, - "description": "Specifies the minimum number of checkpoints that must be reported within each configured 'reporting_cycle'." - }, - "max_indications": { - "type": "integer", - "minimum": 0, - "description": "Specifies the maximum number of checkpoints that may be reported within each configured 'reporting_cycle'." - } - }, - "required": [], - "additionalProperties": false - } - }, - "required": [], - "additionalProperties": false - }, - - "depends_on": { - "type": "array", - "description": "Specifies the names of components that this component depends on. Each dependency must be initialized and reach its ready state before the Launch Manager will start this component.", - "items": { - "type": "string", - "description": "Specifies the name of a component on which this component depends." - } - }, - - "process_arguments": { - "type": "array", - "description": "Specifies an ordered list of command-line arguments passed to the component at startup.", - "items": { - "type": "string", - "description": "Specifies a single command-line argument token as a UTF-8 string; order is preserved." - } - }, - - "ready_condition": { - "type": "object", - "description": "Defines the set of conditions that determine when the component completes its initializing state and enters the ready state.", - "properties": { - "process_state": { - "type": "string", - "enum": [ - "Running", - "Terminated" - ], - "description": "Specifies the required state of the component's POSIX process. 'Running': the process has started and reached its running state. 'Terminated': the process has started, reached its running state, and then terminated successfully." - } - }, - "required": [], - "additionalProperties": false - } - }, - "required": [], - "additionalProperties": false -} diff --git a/src/launch_manager_daemon/config/config_schema/draft_schema/types/deployment_config.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/deployment_config.schema.json deleted file mode 100755 index 4d9dbc78..00000000 --- a/src/launch_manager_daemon/config/config_schema/draft_schema/types/deployment_config.schema.json +++ /dev/null @@ -1,126 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "description": "Defines a reusable type that contains configuration parameters that are specific to a particular deployment environment or system setup.", - - "properties": { - "ready_timeout": { - "type": "number", - "exclusiveMinimum": 0, - "description": "Specifies the maximum time, in seconds (e.g., '0.25' for 250 milliseconds), allowed for the component to reach its ready state. The timeout is measured from when the component process is created until the ready conditions specified in 'component_properties.ready_condition' are met." - }, - "shutdown_timeout": { - "type": "number", - "exclusiveMinimum": 0, - "description": "Specifies the maximum time, in seconds (e.g., '0.75' for 750 milliseconds), allowed for the component to terminate after it receives a SIGTERM signal from the Launch Manager. The timeout is measured from when the Launch Manager sends the SIGTERM signal until the Operating System notifies the Launch Manager that the child process has terminated." - }, - "environmental_variables": { - "type": "object", - "description": "Defines the set of environment variables passed to the component at startup.", - "additionalProperties": { - "type": "string", - "description": "Specifies the environment variable's value as a string. An empty string is allowed and represents an intentionally empty environment variable." - } - }, - "bin_dir": { - "type": "string", - "description": "Specifies the absolute filesystem path to the directory where the component is installed." - }, - "working_dir": { - "type": "string", - "description": "Specifies the directory used as the working directory for the component during execution." - }, - "ready_recovery_action": { - "allOf": [ - { "$ref": "./recovery_action.schema.json" }, - { - "properties": { - "restart": true - }, - "required": ["restart"], - "not": { - "required": ["switch_run_target"] - } - } - ], - "description": "Specifies the recovery action to execute when the component fails to reach its ready state within the configured timeout." - }, - "recovery_action": { - "allOf": [ - { "$ref": "./recovery_action.schema.json" }, - { - "properties": { - "switch_run_target": true - }, - "required": ["switch_run_target"], - "not": { - "required": ["restart"] - } - } - ], - "description": "Specifies the recovery action to execute when the component malfunctions after reaching its ready state." - }, - "sandbox": { - "type": "object", - "description": "Defines the sandbox configuration parameters that isolate and constrain the component's runtime execution.", - "properties": { - "uid": { - "type": "integer", - "minimum": 0, - "description": "Specifies the POSIX user ID (UID) under which this component executes." - }, - "gid": { - "type": "integer", - "minimum": 0, - "description": "Specifies the primary POSIX group ID (GID) under which this component executes." - }, - "supplementary_group_ids": { - "type": "array", - "description": "Specifies the list of supplementary POSIX group IDs (GIDs) assigned to this component.", - "items": { - "type": "integer", - "minimum": 0, - "description": "Specifies a single supplementary POSIX group ID (GID)." - } - }, - "security_policy": { - "type": "string", - "description": "Specifies the security policy or confinement profile name (such as an SELinux or AppArmor profile) assigned to the component." - }, - "scheduling_policy": { - "type": "string", - "description": "Specifies the scheduling policy applied to the component's initial thread. Supported values correspond to OS-defined policies (e.g., FIFO, RR, OTHER).", - "anyOf": [ - { - "enum": [ - "SCHED_FIFO", - "SCHED_RR", - "SCHED_OTHER" - ] - }, - { - "type": "string" - } - ] - }, - "scheduling_priority": { - "type": "integer", - "description": "Specifies the scheduling priority applied to the component's initial thread." - }, - "max_memory_usage": { - "type": "integer", - "exclusiveMinimum": 0, - "description": "Specifies the maximum amount of memory, in bytes, that the component is permitted to use during runtime." - }, - "max_cpu_usage": { - "type": "integer", - "exclusiveMinimum": 0, - "description": "Specifies the maximum CPU usage limit for the component, expressed as a percentage (%) of total CPU capacity." - } - }, - "additionalProperties": false - } - }, - "required": [], - "additionalProperties": false -} diff --git a/src/launch_manager_daemon/config/config_schema/draft_schema/types/recovery_action.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/recovery_action.schema.json deleted file mode 100755 index d152e7c3..00000000 --- a/src/launch_manager_daemon/config/config_schema/draft_schema/types/recovery_action.schema.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "description": "Defines a reusable type that specifies recovery actions to execute when an error or failure occurs.", - "properties": { - "restart": { - "type": "object", - "description": "Defines a recovery action that restarts the POSIX process associated with this component.", - "properties": { - "number_of_attempts": { - "type": "integer", - "minimum": 0, - "description": "Specifies the maximum number of restart attempts before the Launch Manager concludes that recovery cannot succeed." - }, - "delay_before_restart": { - "type": "number", - "minimum": 0, - "description": "Specifies the delay duration, in seconds (e.g., '0.25' for 250 milliseconds), that the Launch Manager waits before initiating a restart attempt." - } - }, - "required": [], - "additionalProperties": false - }, - "switch_run_target": { - "type": "object", - "description": "Defines a recovery action that switches to a Run Target. This can be a different Run Target or the same one to retry activation of the current Run Target.", - "properties": { - "run_target": { - "type": "string", - "description": "Specifies the name of the Run Target that the Launch Manager should switch to." - } - }, - "required": [], - "additionalProperties": false - } - }, - - "oneOf": [ - { "required": ["restart"] }, - { "required": ["switch_run_target"] } - ], - "additionalProperties": false -} diff --git a/src/launch_manager_daemon/config/config_schema/draft_schema/types/run_target.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/run_target.schema.json deleted file mode 100755 index ac38e665..00000000 --- a/src/launch_manager_daemon/config/config_schema/draft_schema/types/run_target.schema.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "description": "Defines a reusable type that specifies configuration parameters for a Run Target.", - "properties": { - "description": { - "type": "string", - "description": "Specifies a user-defined description of the Run Target." - }, - - "depends_on": { - "type": "array", - "description": "Specifies the names of components and Run Targets that must be activated when this Run Target is activated.", - "items": { - "type": "string", - "description": "Specifies the name of a component or Run Target that this Run Target depends on." - } - }, - - "transition_timeout": { - "type": "number", - "description": "Specifies the time limit, in seconds (e.g., '1.5' for 1500 milliseconds), for the Run Target transition. If this limit is exceeded, the transition is considered failed.", - "exclusiveMinimum": 0 - }, - - "recovery_action": { - "allOf": [ - { "$ref": "./recovery_action.schema.json" }, - { - "properties": { - "switch_run_target": true - }, - "required": ["switch_run_target"], - "not": { - "required": ["restart"] - } - } - ], - "description": "Specifies the recovery action to execute when a component assigned to this Run Target fails." - } - }, - - "required": [], - "additionalProperties": false -} diff --git a/src/launch_manager_daemon/config/config_schema/draft_schema/types/watchdog.schema.json b/src/launch_manager_daemon/config/config_schema/draft_schema/types/watchdog.schema.json deleted file mode 100755 index ef07a5b9..00000000 --- a/src/launch_manager_daemon/config/config_schema/draft_schema/types/watchdog.schema.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "description": "Defines a reusable type that contains configuration parameters for the external watchdog.", - "properties": { - "device_file_path": { - "type": "string", - "description": "Specifies the path to the external watchdog device file (e.g., /dev/watchdog)." - }, - "max_timeout": { - "type": "number", - "minimum": 0, - "description": "Specifies the maximum timeout value, in seconds (e.g., '0.5' for 500 milliseconds), that the Launch Manager configures on the external watchdog during startup. The external watchdog uses this timeout as the deadline for receiving periodic alive reports from the Launch Manager." - }, - "deactivate_on_shutdown": { - "type": "boolean", - "description": "Specifies whether the Launch Manager disables the external watchdog during shutdown. When set to true, the watchdog is deactivated; when false, it remains active." - }, - "require_magic_close": { - "type": "boolean", - "description": "Specifies whether the Launch Manager performs a defined shutdown sequence to inform the external watchdog that the shutdown is intentional and to prevent a watchdog-initiated reset. When true, the magic close sequence is performed; when false, it is not." - } - }, - - "required": [], - "additionalProperties": false -} From 650a517144770948b4537d8f5dda67b4d30f6297 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Mon, 2 Mar 2026 13:56:38 +0000 Subject: [PATCH 16/17] Moving schema file to the correct location --- .../{published_schema => }/s-core_launch_manager.schema.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/launch_manager_daemon/config/config_schema/{published_schema => }/s-core_launch_manager.schema.json (100%) diff --git a/src/launch_manager_daemon/config/config_schema/published_schema/s-core_launch_manager.schema.json b/src/launch_manager_daemon/config/config_schema/s-core_launch_manager.schema.json similarity index 100% rename from src/launch_manager_daemon/config/config_schema/published_schema/s-core_launch_manager.schema.json rename to src/launch_manager_daemon/config/config_schema/s-core_launch_manager.schema.json From 0d32e092486c3b9b9d2c3056ea9b74ce1c2b6be7 Mon Sep 17 00:00:00 2001 From: SimonKozik <244535158+SimonKozik@users.noreply.github.com> Date: Mon, 2 Mar 2026 14:53:44 +0000 Subject: [PATCH 17/17] Adapting remaining files to the latest's changes Removing bundle.py as this is no longer needed Adapting README file to reflect the current workflow --- .../config/config_schema/README.rst | 141 +++-------- .../config/config_schema/scripts/bundle.py | 223 ------------------ 2 files changed, 27 insertions(+), 337 deletions(-) delete mode 100755 src/launch_manager_daemon/config/config_schema/scripts/bundle.py diff --git a/src/launch_manager_daemon/config/config_schema/README.rst b/src/launch_manager_daemon/config/config_schema/README.rst index 281467ca..52e1857b 100755 --- a/src/launch_manager_daemon/config/config_schema/README.rst +++ b/src/launch_manager_daemon/config/config_schema/README.rst @@ -16,158 +16,71 @@ Launch Manager Configuration Schema ################################### -This folder contains the development environment for the Launch Manager configuration JSON Schema. The schema defines and validates the structure of Launch Manager configuration files. +This folder contains the Launch Manager configuration JSON Schema. The schema defines and validates the structure of Launch Manager configuration files. Overview ******** -This project uses a **two-folder approach** for schema management: - -- ``draft_schema/`` - Multi-file schema structure for active development -- ``published_schema/`` - Single-file schema for end-user consumption - -The multi-file structure in ``draft_schema/`` makes it easier to maintain and modify the schema by organizing reusable components into separate files. When development is complete, these files are compiled into a single file in ``published_schema/`` for convenience of end users. +This project manages the Launch Manager configuration schema as a single, self-contained file. When you need to modify or extend the schema, you should directly edit `s-core_launch_manager.schema.json`. **Project Structure:** :: - +-- draft_schema/ # Multi-file schema under development - +-- published_schema/ # Single-file schema ready for use - +-- examples/ # Sample configuration files - +-- scripts/ # Tools for bundling and validation + +-- s-core_launch_manager.schema.json # The Launch Manager schema. + +-- examples/ # Illustrative example configuration files for the schema. + +-- scripts/ # Utility scripts, including a validation tool. Quick Start *********** -For End Users -============= - -If you just want to validate your Launch Manager configuration: - -1. Use the schema in ``published_schema/s-core_launch_manager.schema.json`` -2. Check the ``examples/`` folder for sample configurations -3. Validate your config: - - .. code-block:: bash - - validate.py --schema published_schema/s-core_launch_manager.schema.json --instance your_config.json - -For Schema Developers +For Users & Developers ====================== -If you're modifying or extending the schema: - -1. Edit files in ``draft_schema/`` -2. Bundle your changes: - - .. code-block:: bash +Whether you're validating a Launch Manager configuration against the schema, or actively developing and modifying the schema itself, here's how to interact with this project: - bundle.py --input draft_schema/s-core_launch_manager.schema.json --output published_schema/s-core_launch_manager.schema.json +1. **Locate the Schema:** The complete schema definition resides in ``s-core_launch_manager.schema.json``. +2. **Explore Examples:** The ``examples/`` folder provides various sample Launch Manager configuration files. These are invaluable for understanding how the schema applies in practice and how to structure your own configurations. +3. **Validate Your Configuration:** Use the provided validation script to check if your configuration file conforms to the schema: -3. Test against examples to ensure nothing broke + .. code-block:: bash + scripts/validate.py --schema s-core_launch_manager.schema.json --instance your_config.json Examples ******** -Configuration examples are provided in the ``examples`` folder, each accompanied by a brief description. **Start here** if you're new to Launch Manager configurations - these show real-world usage patterns. - - -Schema Development (draft_schema) -********************************** - -The ``draft_schema`` folder contains the primary development work. The setup uses a multi-file structure where: - -- **Reusable types** are stored in the ``types/`` subfolder -- **Top-level schema** resides in ``s-core_launch_manager.schema.json`` file - -Working with $ref Paths -======================== - -The multi-file schema uses JSON Schema's ``$ref`` keyword to reference definitions across files. Understanding how these references work is crucial when modifying the schema. - -**Key principle:** All ``$ref`` paths are relative to the location of the file containing the reference, not to any root folder. - -Reference Examples ------------------- - -**To reference a file in a subfolder** (e.g., from ``s-core_launch_manager.schema.json`` to ``types/deployment_config.schema.json``): - -.. code-block:: json - - "$ref": "./types/deployment_config.schema.json" - -**To reference a file in the same folder:** (e.g., from ``types/deployment_config.schema.json`` to ``types/recovery_action.schema.json``): - -.. code-block:: json - - "$ref": "./recovery_action.schema.json" - -Common Pitfalls ---------------- - -- **Always use relative paths** starting with ``./`` or ``../`` -- **Don't use absolute paths** or paths from the project root -- **Remember the current file's location** when constructing paths -- When moving files, **update all references** to and from that file - -The bundling script resolves all these relative references into a single file, so the published schema doesn't need external file references. - - -Published Schema (published_schema) -************************************ - -The official, end-user consumable schema is placed in the ``published_schema`` folder. Upon completion of development, the multi-file schema from the ``draft_schema`` folder is merged into a single file and published here. - -**This is the version end users should reference** in their validation tools and IDE configurations. +The ``examples`` folder contains a set of sample Launch Manager configuration files. Each example demonstrates valid configurations according to the ``s-core_launch_manager.schema.json``. +**Recommendation:** If you are new to Launch Manager configurations, **start by reviewing these examples**. They offer practical insight into the expected structure, available properties, and common use cases defined by the schema. Scripts ******* -Utility scripts for schema development are located in the ``scripts`` folder: - -bundle.py -========= - -Merges the multi-file schema into a single file for end-user distribution. - -**Usage:** - -.. code-block:: bash - - bundle.py --input ../draft_schema/s-core_launch_manager.schema.json --output ../published_schema/s-core_launch_manager.schema.json - Bundled schema written to: ../published_schema/s-core_launch_manager.schema.json - -**When to use:** After making changes in ``draft_schema/``, run this to create the publishable version. +The ``scripts`` folder houses utility scripts designed to assist with schema development. validate.py =========== -Validates Launch Manager configuration instances against the schema. This script supports both single-file and multi-file schema formats. - -**Validate against published schema:** +The ``validate.py`` script is a crucial tool for verifying that any given Launch Manager configuration instance adheres to the rules defined in `s-core_launch_manager.schema.json`. -.. code-block:: bash - - validate.py --schema ../published_schema/s-core_launch_manager.schema.json --instance ../examples/example_conf.json - Success --> ../examples/example_conf.json: valid +**Usage:** -**Validate against draft schema (during development):** +To validate a configuration file (e.g., `example_conf.json` from the `examples` folder) against the schema: .. code-block:: bash - validate.py --schema ../draft_schema/s-core_launch_manager.schema.json --instance ../examples/example_conf.json - Success --> ../examples/example_conf.json: valid - -**When to use:** Run this frequently during development to catch errors early. Always validate examples before publishing. + scripts/validate.py --schema s-core_launch_manager.schema.json --instance examples/example_conf.json + Success --> examples/example_conf.json: valid +**When to use:** +* **During Development:** Run this script frequently whenever you're creating or modifying a Launch Manager configuration file. It provides immediate feedback on whether your changes are valid according to the schema. +* **Schema Development:** If you are making changes to `s-core_launch_manager.schema.json` itself, always run `validate.py` against the examples to ensure your schema changes haven't inadvertently broken existing, valid configurations. Typical Workflow **************** -1. **Modify** schema files in ``draft_schema/`` -2. **Validate** your changes against examples using the draft schema -3. **Bundle** the multi-file schema into a single file -4. **Validate** examples again against the published schema +For schema developers or those creating new configurations: + +1. **Modify** the ``s-core_launch_manager.schema.json`` file (if you're updating the schema definition) or your Launch Manager configuration file. +2. **Validate** your changes using the `scripts/validate.py` script against relevant example files or your new configuration. This iterative process helps ensure compliance and catch errors early. diff --git a/src/launch_manager_daemon/config/config_schema/scripts/bundle.py b/src/launch_manager_daemon/config/config_schema/scripts/bundle.py deleted file mode 100755 index d5dc26f3..00000000 --- a/src/launch_manager_daemon/config/config_schema/scripts/bundle.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env python3 -# ******************************************************************************* -# Copyright (c) 2026 Contributors to the Eclipse Foundation -# -# See the NOTICE file(s) distributed with this work for additional -# information regarding copyright ownership. -# -# This program and the accompanying materials are made available under the -# terms of the Apache License Version 2.0 which is available at -# https://www.apache.org/licenses/LICENSE-2.0 -# -# SPDX-License-Identifier: Apache-2.0 -# ******************************************************************************* - -""" -Bundle a multi-file JSON Schema into a single file using $defs. - -Usage: - python scripts/bundle_defs.py \ - --input config/schema/s-core-launch-manager.schema.json \ - --output config/schema/s-core-launch-manager.defs.bundle.json - - -Notes: -- Instead of fully inlining (flattening) each $ref, this script: - 1) Collects every local file $ref (e.g. "./types/*.schema.json"). - 2) Imports each referenced schema once into the top-level "$defs" (deduplicated). - 3) Rewrites $ref values to point to "#/$defs/" (JSON Pointer fragments like "#/foo/bar" are preserved by converting) - * For example: "file.json#/foo/bar" -> "#/$defs//foo/bar". -- If the imported schema itself has local file refs, those are also pulled into $defs recursively with the same logic. -- Name derivation for $defs keys: file basename without ".schema.json" (e.g., "watchdog"). Collisions are resolved by appending a numeric suffix (e.g., watchdog_2). -- To avoid base-URI confusion inside bundled defs, $id and nested $schema are stripped from the imported defs. -""" - -from __future__ import annotations -import argparse -import json -from pathlib import Path -from typing import Any, Dict, Tuple - -Json = Any - - -class DefsBundler: - def __init__(self, input_file: Path) -> None: - self.input_file = input_file.resolve() - self.defs: Dict[str, Json] = {} - self.file_to_defname: Dict[Path, str] = {} - - @staticmethod - def _deepcopy(obj: Json) -> Json: - return json.loads(json.dumps(obj)) - - @staticmethod - def _is_local_file_ref(ref: str) -> bool: - if not isinstance(ref, str): - return False - if ref.startswith("#"): - return False - if "://" in ref: - return False - return True - - @staticmethod - def _split_ref(ref: str) -> Tuple[str, str]: - # Returns (file_part, fragment_pointer) where fragment_pointer is like "/a/b" (without leading '#') or '' - if "#" in ref: - file_part, frag = ref.split("#", 1) - if frag.startswith("/"): - return file_part, frag # JSON Pointer already - if frag.startswith("#/"): - return file_part, frag[1:] - # treat unknown as JSON Pointer missing leading '/' - return file_part, "/" + frag if frag else "" - return ref, "" - - @staticmethod - def _derive_name_from_file(path: Path) -> str: - name = path.stem # e.g., "watchdog.schema" - if name.endswith(".schema"): - name = name[:-7] # remove trailing ".schema" - return name - - def _unique_def_name(self, base: str) -> str: - if base not in self.defs: - return base - i = 2 - while True: - cand = f"{base}_{i}" - if cand not in self.defs: - return cand - i += 1 - - def _strip_ids(self, schema: Json) -> Json: - # Remove $id and nested $schema fields from imported defs to avoid base-URI conflicts - if isinstance(schema, dict): - schema = { - k: self._strip_ids(v) - for k, v in schema.items() - if k not in ("$id", "$schema") - } - elif isinstance(schema, list): - schema = [self._strip_ids(v) for v in schema] - return schema - - def _register_def_from_file(self, current_file: Path, ref_path: str) -> str: - target = (current_file.parent / ref_path).resolve() - if target in self.file_to_defname: - return self.file_to_defname[target] - with open(target, "r", encoding="utf-8") as f: - schema = json.load(f) - name_base = self._derive_name_from_file(target) - name = self._unique_def_name(name_base) - cleaned = self._strip_ids(schema) - # Before storing, rewrite refs inside this imported schema - rewritten = self._rewrite_refs(cleaned, target) - self.defs[name] = rewritten - self.file_to_defname[target] = name - return name - - def _rewrite_refs(self, node: Json, current_file: Path) -> Json: - # Traverse node; for any local file $ref, add that file into $defs and rewrite the $ref to #/$defs//fragment - if isinstance(node, dict): - if "$ref" in node and isinstance(node["$ref"], str): - ref_str = node["$ref"] - if self._is_local_file_ref(ref_str): - file_part, frag = self._split_ref(ref_str) - defname = ( - self._register_def_from_file(current_file, file_part) - if file_part - else None - ) - if defname: - # Compose new JSON Pointer: #/$defs/ - pointer = f"#/$defs/{defname}{frag}" - return { - **{k: v for k, v in node.items() if k != "$ref"}, - "$ref": pointer, - } - # Otherwise, descend - return {k: self._rewrite_refs(v, current_file) for k, v in node.items()} - elif isinstance(node, list): - return [self._rewrite_refs(v, current_file) for v in node] - else: - return node - - def bundle(self, out_path: Path) -> None: - with open(self.input_file, "r", encoding="utf-8") as f: - root = json.load(f) - - bundled_root = self._deepcopy(root) - - # Rewrite refs in the root - bundled_root = self._rewrite_refs(bundled_root, self.input_file) - - # Merge with existing $defs if any - existing_defs = ( - bundled_root.get("$defs", {}) if isinstance(bundled_root, dict) else {} - ) - if not isinstance(existing_defs, dict): - existing_defs = {} - - merged_defs: Dict[str, Json] = {} - - # Copy existing defs - for k, v in existing_defs.items(): - merged_defs[k] = v - - # Add generated defs (dedupe) - for k, v in self.defs.items(): - kk = k - ctr = 2 - while kk in merged_defs: - kk = f"{k}_{ctr}" - ctr += 1 - merged_defs[kk] = v - - # ---------- force a specific order in generated schema ---------- - if isinstance(bundled_root, dict): - new_root = {} - - # The "$schema", "type", "$id", "title", and "description" elements should be at the top of the file - for key in ["$schema", "type", "$id", "title", "description"]: - if key in bundled_root: - new_root[key] = bundled_root[key] - - # Then insert "$defs" (before "properties") - new_root["$defs"] = merged_defs - - # Insert "properties" immediately after "$defs" - if "properties" in bundled_root: - new_root["properties"] = bundled_root["properties"] - - # Insert remaining keys (required, additionalProperties, etc.) - for key, value in bundled_root.items(): - if key not in new_root: - new_root[key] = value - - bundled_root = new_root - # ---------------------------------------------------------------- - - out_path.parent.mkdir(parents=True, exist_ok=True) - with open(out_path, "w", encoding="utf-8") as f: - json.dump(bundled_root, f, indent=2, ensure_ascii=False) - - -def main() -> None: - ap = argparse.ArgumentParser( - description="Bundle multi-file JSON Schema into one file using $defs (deduplicated)." - ) - ap.add_argument("--input", required=True, help="Path to the top-level schema JSON") - ap.add_argument( - "--output", required=True, help="Path to write the bundled schema JSON" - ) - args = ap.parse_args() - - bundler = DefsBundler(Path(args.input)) - bundler.bundle(Path(args.output)) - print(f"Bundled schema written to: {args.output}") - - -if __name__ == "__main__": - main()