omnibase_infra 0.2.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omnibase_infra/__init__.py +101 -0
- omnibase_infra/adapters/adapter_onex_tool_execution.py +451 -0
- omnibase_infra/capabilities/__init__.py +15 -0
- omnibase_infra/capabilities/capability_inference_rules.py +211 -0
- omnibase_infra/capabilities/contract_capability_extractor.py +221 -0
- omnibase_infra/capabilities/intent_type_extractor.py +160 -0
- omnibase_infra/cli/__init__.py +1 -0
- omnibase_infra/cli/commands.py +216 -0
- omnibase_infra/clients/__init__.py +0 -0
- omnibase_infra/configs/widget_mapping.yaml +176 -0
- omnibase_infra/constants_topic_patterns.py +26 -0
- omnibase_infra/contracts/handlers/filesystem/handler_contract.yaml +264 -0
- omnibase_infra/contracts/handlers/mcp/handler_contract.yaml +141 -0
- omnibase_infra/decorators/__init__.py +29 -0
- omnibase_infra/decorators/allow_any.py +109 -0
- omnibase_infra/dlq/__init__.py +90 -0
- omnibase_infra/dlq/constants_dlq.py +57 -0
- omnibase_infra/dlq/models/__init__.py +26 -0
- omnibase_infra/dlq/models/enum_replay_status.py +37 -0
- omnibase_infra/dlq/models/model_dlq_replay_record.py +135 -0
- omnibase_infra/dlq/models/model_dlq_tracking_config.py +184 -0
- omnibase_infra/dlq/service_dlq_tracking.py +611 -0
- omnibase_infra/enums/__init__.py +132 -0
- omnibase_infra/enums/enum_any_type_violation.py +104 -0
- omnibase_infra/enums/enum_backend_type.py +27 -0
- omnibase_infra/enums/enum_capture_outcome.py +42 -0
- omnibase_infra/enums/enum_capture_state.py +88 -0
- omnibase_infra/enums/enum_chain_violation_type.py +119 -0
- omnibase_infra/enums/enum_circuit_state.py +51 -0
- omnibase_infra/enums/enum_confirmation_event_type.py +27 -0
- omnibase_infra/enums/enum_consumer_group_purpose.py +92 -0
- omnibase_infra/enums/enum_contract_type.py +84 -0
- omnibase_infra/enums/enum_dedupe_strategy.py +46 -0
- omnibase_infra/enums/enum_dispatch_status.py +191 -0
- omnibase_infra/enums/enum_environment.py +46 -0
- omnibase_infra/enums/enum_execution_shape_violation.py +103 -0
- omnibase_infra/enums/enum_handler_error_type.py +111 -0
- omnibase_infra/enums/enum_handler_loader_error.py +178 -0
- omnibase_infra/enums/enum_handler_source_mode.py +86 -0
- omnibase_infra/enums/enum_handler_source_type.py +87 -0
- omnibase_infra/enums/enum_handler_type.py +77 -0
- omnibase_infra/enums/enum_handler_type_category.py +61 -0
- omnibase_infra/enums/enum_infra_transport_type.py +73 -0
- omnibase_infra/enums/enum_introspection_reason.py +154 -0
- omnibase_infra/enums/enum_kafka_acks.py +99 -0
- omnibase_infra/enums/enum_message_category.py +213 -0
- omnibase_infra/enums/enum_node_archetype.py +74 -0
- omnibase_infra/enums/enum_node_output_type.py +185 -0
- omnibase_infra/enums/enum_non_retryable_error_category.py +224 -0
- omnibase_infra/enums/enum_policy_type.py +32 -0
- omnibase_infra/enums/enum_registration_state.py +261 -0
- omnibase_infra/enums/enum_registration_status.py +33 -0
- omnibase_infra/enums/enum_registry_response_status.py +28 -0
- omnibase_infra/enums/enum_response_status.py +26 -0
- omnibase_infra/enums/enum_retry_error_category.py +98 -0
- omnibase_infra/enums/enum_security_rule_id.py +103 -0
- omnibase_infra/enums/enum_selection_strategy.py +91 -0
- omnibase_infra/enums/enum_topic_standard.py +42 -0
- omnibase_infra/enums/enum_validation_severity.py +78 -0
- omnibase_infra/errors/__init__.py +160 -0
- omnibase_infra/errors/error_architecture_violation.py +152 -0
- omnibase_infra/errors/error_binding_resolution.py +128 -0
- omnibase_infra/errors/error_chain_propagation.py +188 -0
- omnibase_infra/errors/error_compute_registry.py +95 -0
- omnibase_infra/errors/error_consul.py +132 -0
- omnibase_infra/errors/error_container_wiring.py +243 -0
- omnibase_infra/errors/error_event_bus_registry.py +105 -0
- omnibase_infra/errors/error_infra.py +610 -0
- omnibase_infra/errors/error_message_type_registry.py +101 -0
- omnibase_infra/errors/error_policy_registry.py +115 -0
- omnibase_infra/errors/error_vault.py +123 -0
- omnibase_infra/event_bus/__init__.py +72 -0
- omnibase_infra/event_bus/configs/kafka_event_bus_config.yaml +84 -0
- omnibase_infra/event_bus/event_bus_inmemory.py +797 -0
- omnibase_infra/event_bus/event_bus_kafka.py +1716 -0
- omnibase_infra/event_bus/mixin_kafka_broadcast.py +180 -0
- omnibase_infra/event_bus/mixin_kafka_dlq.py +771 -0
- omnibase_infra/event_bus/models/__init__.py +29 -0
- omnibase_infra/event_bus/models/config/__init__.py +20 -0
- omnibase_infra/event_bus/models/config/model_kafka_event_bus_config.py +693 -0
- omnibase_infra/event_bus/models/model_dlq_event.py +206 -0
- omnibase_infra/event_bus/models/model_dlq_metrics.py +304 -0
- omnibase_infra/event_bus/models/model_event_headers.py +115 -0
- omnibase_infra/event_bus/models/model_event_message.py +60 -0
- omnibase_infra/event_bus/testing/__init__.py +26 -0
- omnibase_infra/event_bus/testing/adapter_protocol_event_publisher_inmemory.py +418 -0
- omnibase_infra/event_bus/testing/model_publisher_metrics.py +64 -0
- omnibase_infra/event_bus/topic_constants.py +376 -0
- omnibase_infra/handlers/__init__.py +82 -0
- omnibase_infra/handlers/filesystem/__init__.py +48 -0
- omnibase_infra/handlers/filesystem/enum_file_system_operation.py +35 -0
- omnibase_infra/handlers/filesystem/model_file_system_request.py +298 -0
- omnibase_infra/handlers/filesystem/model_file_system_result.py +166 -0
- omnibase_infra/handlers/handler_consul.py +795 -0
- omnibase_infra/handlers/handler_db.py +1046 -0
- omnibase_infra/handlers/handler_filesystem.py +1478 -0
- omnibase_infra/handlers/handler_graph.py +2015 -0
- omnibase_infra/handlers/handler_http.py +926 -0
- omnibase_infra/handlers/handler_intent.py +387 -0
- omnibase_infra/handlers/handler_manifest_persistence.contract.yaml +184 -0
- omnibase_infra/handlers/handler_manifest_persistence.py +1539 -0
- omnibase_infra/handlers/handler_mcp.py +1430 -0
- omnibase_infra/handlers/handler_qdrant.py +1076 -0
- omnibase_infra/handlers/handler_vault.py +428 -0
- omnibase_infra/handlers/mcp/__init__.py +19 -0
- omnibase_infra/handlers/mcp/adapter_onex_to_mcp.py +446 -0
- omnibase_infra/handlers/mcp/protocols.py +178 -0
- omnibase_infra/handlers/mcp/transport_streamable_http.py +352 -0
- omnibase_infra/handlers/mixins/__init__.py +47 -0
- omnibase_infra/handlers/mixins/mixin_consul_initialization.py +349 -0
- omnibase_infra/handlers/mixins/mixin_consul_kv.py +338 -0
- omnibase_infra/handlers/mixins/mixin_consul_service.py +542 -0
- omnibase_infra/handlers/mixins/mixin_consul_topic_index.py +585 -0
- omnibase_infra/handlers/mixins/mixin_vault_initialization.py +338 -0
- omnibase_infra/handlers/mixins/mixin_vault_retry.py +412 -0
- omnibase_infra/handlers/mixins/mixin_vault_secrets.py +450 -0
- omnibase_infra/handlers/mixins/mixin_vault_token.py +365 -0
- omnibase_infra/handlers/models/__init__.py +286 -0
- omnibase_infra/handlers/models/consul/__init__.py +81 -0
- omnibase_infra/handlers/models/consul/enum_consul_operation_type.py +57 -0
- omnibase_infra/handlers/models/consul/model_consul_deregister_payload.py +51 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_config.py +153 -0
- omnibase_infra/handlers/models/consul/model_consul_handler_payload.py +89 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_found_payload.py +55 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_not_found_payload.py +49 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_get_recurse_payload.py +50 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_item.py +33 -0
- omnibase_infra/handlers/models/consul/model_consul_kv_put_payload.py +41 -0
- omnibase_infra/handlers/models/consul/model_consul_register_payload.py +53 -0
- omnibase_infra/handlers/models/consul/model_consul_retry_config.py +66 -0
- omnibase_infra/handlers/models/consul/model_payload_consul.py +66 -0
- omnibase_infra/handlers/models/consul/registry_payload_consul.py +214 -0
- omnibase_infra/handlers/models/graph/__init__.py +35 -0
- omnibase_infra/handlers/models/graph/enum_graph_operation_type.py +20 -0
- omnibase_infra/handlers/models/graph/model_graph_execute_payload.py +38 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_config.py +54 -0
- omnibase_infra/handlers/models/graph/model_graph_handler_payload.py +44 -0
- omnibase_infra/handlers/models/graph/model_graph_query_payload.py +40 -0
- omnibase_infra/handlers/models/graph/model_graph_record.py +22 -0
- omnibase_infra/handlers/models/http/__init__.py +50 -0
- omnibase_infra/handlers/models/http/enum_http_operation_type.py +29 -0
- omnibase_infra/handlers/models/http/model_http_body_content.py +45 -0
- omnibase_infra/handlers/models/http/model_http_get_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_http_handler_payload.py +90 -0
- omnibase_infra/handlers/models/http/model_http_post_payload.py +88 -0
- omnibase_infra/handlers/models/http/model_payload_http.py +66 -0
- omnibase_infra/handlers/models/http/registry_payload_http.py +212 -0
- omnibase_infra/handlers/models/mcp/__init__.py +23 -0
- omnibase_infra/handlers/models/mcp/enum_mcp_operation_type.py +24 -0
- omnibase_infra/handlers/models/mcp/model_mcp_handler_config.py +40 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_call.py +32 -0
- omnibase_infra/handlers/models/mcp/model_mcp_tool_result.py +45 -0
- omnibase_infra/handlers/models/model_consul_handler_response.py +96 -0
- omnibase_infra/handlers/models/model_db_describe_response.py +83 -0
- omnibase_infra/handlers/models/model_db_query_payload.py +95 -0
- omnibase_infra/handlers/models/model_db_query_response.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_config.py +98 -0
- omnibase_infra/handlers/models/model_filesystem_delete_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_delete_result.py +77 -0
- omnibase_infra/handlers/models/model_filesystem_directory_entry.py +75 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_payload.py +54 -0
- omnibase_infra/handlers/models/model_filesystem_ensure_directory_result.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_payload.py +60 -0
- omnibase_infra/handlers/models/model_filesystem_list_directory_result.py +68 -0
- omnibase_infra/handlers/models/model_filesystem_read_payload.py +62 -0
- omnibase_infra/handlers/models/model_filesystem_read_result.py +61 -0
- omnibase_infra/handlers/models/model_filesystem_write_payload.py +70 -0
- omnibase_infra/handlers/models/model_filesystem_write_result.py +55 -0
- omnibase_infra/handlers/models/model_graph_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_handler_response.py +103 -0
- omnibase_infra/handlers/models/model_http_handler_response.py +101 -0
- omnibase_infra/handlers/models/model_manifest_metadata.py +75 -0
- omnibase_infra/handlers/models/model_manifest_persistence_config.py +62 -0
- omnibase_infra/handlers/models/model_manifest_query_payload.py +90 -0
- omnibase_infra/handlers/models/model_manifest_query_result.py +97 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_payload.py +44 -0
- omnibase_infra/handlers/models/model_manifest_retrieve_result.py +98 -0
- omnibase_infra/handlers/models/model_manifest_store_payload.py +47 -0
- omnibase_infra/handlers/models/model_manifest_store_result.py +67 -0
- omnibase_infra/handlers/models/model_operation_context.py +187 -0
- omnibase_infra/handlers/models/model_qdrant_handler_response.py +98 -0
- omnibase_infra/handlers/models/model_retry_state.py +162 -0
- omnibase_infra/handlers/models/model_vault_handler_response.py +98 -0
- omnibase_infra/handlers/models/qdrant/__init__.py +44 -0
- omnibase_infra/handlers/models/qdrant/enum_qdrant_operation_type.py +26 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_collection_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_delete_payload.py +36 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_config.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_handler_payload.py +54 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_payload.py +42 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_search_result.py +30 -0
- omnibase_infra/handlers/models/qdrant/model_qdrant_upsert_payload.py +36 -0
- omnibase_infra/handlers/models/vault/__init__.py +69 -0
- omnibase_infra/handlers/models/vault/enum_vault_operation_type.py +35 -0
- omnibase_infra/handlers/models/vault/model_payload_vault.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_delete_payload.py +57 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_config.py +148 -0
- omnibase_infra/handlers/models/vault/model_vault_handler_payload.py +101 -0
- omnibase_infra/handlers/models/vault/model_vault_list_payload.py +58 -0
- omnibase_infra/handlers/models/vault/model_vault_renew_token_payload.py +67 -0
- omnibase_infra/handlers/models/vault/model_vault_retry_config.py +66 -0
- omnibase_infra/handlers/models/vault/model_vault_secret_payload.py +106 -0
- omnibase_infra/handlers/models/vault/model_vault_write_payload.py +66 -0
- omnibase_infra/handlers/models/vault/registry_payload_vault.py +213 -0
- omnibase_infra/handlers/registration_storage/__init__.py +43 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_mock.py +392 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +922 -0
- omnibase_infra/handlers/registration_storage/models/__init__.py +23 -0
- omnibase_infra/handlers/registration_storage/models/model_delete_registration_request.py +58 -0
- omnibase_infra/handlers/registration_storage/models/model_update_registration_request.py +73 -0
- omnibase_infra/handlers/registration_storage/protocol_registration_persistence.py +191 -0
- omnibase_infra/handlers/service_discovery/__init__.py +43 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_consul.py +1051 -0
- omnibase_infra/handlers/service_discovery/handler_service_discovery_mock.py +258 -0
- omnibase_infra/handlers/service_discovery/models/__init__.py +22 -0
- omnibase_infra/handlers/service_discovery/models/model_discovery_result.py +64 -0
- omnibase_infra/handlers/service_discovery/models/model_registration_result.py +138 -0
- omnibase_infra/handlers/service_discovery/models/model_service_info.py +109 -0
- omnibase_infra/handlers/service_discovery/protocol_discovery_operations.py +170 -0
- omnibase_infra/idempotency/__init__.py +94 -0
- omnibase_infra/idempotency/models/__init__.py +43 -0
- omnibase_infra/idempotency/models/model_idempotency_check_result.py +85 -0
- omnibase_infra/idempotency/models/model_idempotency_guard_config.py +130 -0
- omnibase_infra/idempotency/models/model_idempotency_record.py +86 -0
- omnibase_infra/idempotency/models/model_idempotency_store_health_check_result.py +81 -0
- omnibase_infra/idempotency/models/model_idempotency_store_metrics.py +140 -0
- omnibase_infra/idempotency/models/model_postgres_idempotency_store_config.py +299 -0
- omnibase_infra/idempotency/protocol_idempotency_store.py +184 -0
- omnibase_infra/idempotency/store_inmemory.py +265 -0
- omnibase_infra/idempotency/store_postgres.py +923 -0
- omnibase_infra/infrastructure/__init__.py +0 -0
- omnibase_infra/migrations/001_create_event_ledger.sql +166 -0
- omnibase_infra/migrations/001_drop_event_ledger.sql +18 -0
- omnibase_infra/mixins/__init__.py +71 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +656 -0
- omnibase_infra/mixins/mixin_dict_like_accessors.py +146 -0
- omnibase_infra/mixins/mixin_envelope_extraction.py +119 -0
- omnibase_infra/mixins/mixin_node_introspection.py +2670 -0
- omnibase_infra/mixins/mixin_retry_execution.py +386 -0
- omnibase_infra/mixins/protocol_circuit_breaker_aware.py +133 -0
- omnibase_infra/models/__init__.py +144 -0
- omnibase_infra/models/bindings/__init__.py +59 -0
- omnibase_infra/models/bindings/constants.py +144 -0
- omnibase_infra/models/bindings/model_binding_resolution_result.py +103 -0
- omnibase_infra/models/bindings/model_operation_binding.py +44 -0
- omnibase_infra/models/bindings/model_operation_bindings_subcontract.py +152 -0
- omnibase_infra/models/bindings/model_parsed_binding.py +52 -0
- omnibase_infra/models/corpus/__init__.py +17 -0
- omnibase_infra/models/corpus/model_capture_config.py +133 -0
- omnibase_infra/models/corpus/model_capture_result.py +86 -0
- omnibase_infra/models/discovery/__init__.py +42 -0
- omnibase_infra/models/discovery/model_dependency_spec.py +319 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +50 -0
- omnibase_infra/models/discovery/model_introspection_config.py +330 -0
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +169 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +116 -0
- omnibase_infra/models/dispatch/__init__.py +155 -0
- omnibase_infra/models/dispatch/model_debug_trace_snapshot.py +114 -0
- omnibase_infra/models/dispatch/model_dispatch_context.py +439 -0
- omnibase_infra/models/dispatch/model_dispatch_error.py +336 -0
- omnibase_infra/models/dispatch/model_dispatch_log_context.py +400 -0
- omnibase_infra/models/dispatch/model_dispatch_metadata.py +228 -0
- omnibase_infra/models/dispatch/model_dispatch_metrics.py +496 -0
- omnibase_infra/models/dispatch/model_dispatch_outcome.py +317 -0
- omnibase_infra/models/dispatch/model_dispatch_outputs.py +231 -0
- omnibase_infra/models/dispatch/model_dispatch_result.py +436 -0
- omnibase_infra/models/dispatch/model_dispatch_route.py +279 -0
- omnibase_infra/models/dispatch/model_dispatcher_metrics.py +275 -0
- omnibase_infra/models/dispatch/model_dispatcher_registration.py +352 -0
- omnibase_infra/models/dispatch/model_materialized_dispatch.py +141 -0
- omnibase_infra/models/dispatch/model_parsed_topic.py +135 -0
- omnibase_infra/models/dispatch/model_topic_parser.py +725 -0
- omnibase_infra/models/dispatch/model_tracing_context.py +285 -0
- omnibase_infra/models/errors/__init__.py +45 -0
- omnibase_infra/models/errors/model_handler_validation_error.py +594 -0
- omnibase_infra/models/errors/model_infra_error_context.py +99 -0
- omnibase_infra/models/errors/model_message_type_registry_error_context.py +71 -0
- omnibase_infra/models/errors/model_timeout_error_context.py +110 -0
- omnibase_infra/models/handlers/__init__.py +80 -0
- omnibase_infra/models/handlers/model_bootstrap_handler_descriptor.py +162 -0
- omnibase_infra/models/handlers/model_contract_discovery_result.py +82 -0
- omnibase_infra/models/handlers/model_handler_descriptor.py +200 -0
- omnibase_infra/models/handlers/model_handler_identifier.py +215 -0
- omnibase_infra/models/handlers/model_handler_source_config.py +220 -0
- omnibase_infra/models/health/__init__.py +9 -0
- omnibase_infra/models/health/model_health_check_result.py +40 -0
- omnibase_infra/models/lifecycle/__init__.py +39 -0
- omnibase_infra/models/logging/__init__.py +51 -0
- omnibase_infra/models/logging/model_log_context.py +756 -0
- omnibase_infra/models/mcp/__init__.py +15 -0
- omnibase_infra/models/mcp/model_mcp_contract_config.py +80 -0
- omnibase_infra/models/mcp/model_mcp_server_config.py +67 -0
- omnibase_infra/models/mcp/model_mcp_tool_definition.py +73 -0
- omnibase_infra/models/mcp/model_mcp_tool_parameter.py +35 -0
- omnibase_infra/models/model_node_identity.py +126 -0
- omnibase_infra/models/model_retry_error_classification.py +78 -0
- omnibase_infra/models/projection/__init__.py +43 -0
- omnibase_infra/models/projection/model_capability_fields.py +112 -0
- omnibase_infra/models/projection/model_registration_projection.py +434 -0
- omnibase_infra/models/projection/model_registration_snapshot.py +322 -0
- omnibase_infra/models/projection/model_sequence_info.py +182 -0
- omnibase_infra/models/projection/model_snapshot_topic_config.py +591 -0
- omnibase_infra/models/projectors/__init__.py +41 -0
- omnibase_infra/models/projectors/model_projector_column.py +289 -0
- omnibase_infra/models/projectors/model_projector_discovery_result.py +65 -0
- omnibase_infra/models/projectors/model_projector_index.py +270 -0
- omnibase_infra/models/projectors/model_projector_schema.py +415 -0
- omnibase_infra/models/projectors/model_projector_validation_error.py +63 -0
- omnibase_infra/models/projectors/util_sql_identifiers.py +115 -0
- omnibase_infra/models/registration/__init__.py +68 -0
- omnibase_infra/models/registration/commands/__init__.py +15 -0
- omnibase_infra/models/registration/commands/model_node_registration_acked.py +108 -0
- omnibase_infra/models/registration/events/__init__.py +56 -0
- omnibase_infra/models/registration/events/model_node_became_active.py +103 -0
- omnibase_infra/models/registration/events/model_node_liveness_expired.py +103 -0
- omnibase_infra/models/registration/events/model_node_registration_accepted.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_received.py +98 -0
- omnibase_infra/models/registration/events/model_node_registration_ack_timed_out.py +112 -0
- omnibase_infra/models/registration/events/model_node_registration_initiated.py +107 -0
- omnibase_infra/models/registration/events/model_node_registration_rejected.py +104 -0
- omnibase_infra/models/registration/model_event_bus_topic_entry.py +59 -0
- omnibase_infra/models/registration/model_introspection_metrics.py +253 -0
- omnibase_infra/models/registration/model_node_capabilities.py +190 -0
- omnibase_infra/models/registration/model_node_event_bus_config.py +99 -0
- omnibase_infra/models/registration/model_node_heartbeat_event.py +126 -0
- omnibase_infra/models/registration/model_node_introspection_event.py +195 -0
- omnibase_infra/models/registration/model_node_metadata.py +79 -0
- omnibase_infra/models/registration/model_node_registration.py +162 -0
- omnibase_infra/models/registration/model_node_registration_record.py +162 -0
- omnibase_infra/models/registry/__init__.py +29 -0
- omnibase_infra/models/registry/model_domain_constraint.py +202 -0
- omnibase_infra/models/registry/model_message_type_entry.py +271 -0
- omnibase_infra/models/resilience/__init__.py +9 -0
- omnibase_infra/models/resilience/model_circuit_breaker_config.py +227 -0
- omnibase_infra/models/routing/__init__.py +25 -0
- omnibase_infra/models/routing/model_routing_entry.py +52 -0
- omnibase_infra/models/routing/model_routing_subcontract.py +70 -0
- omnibase_infra/models/runtime/__init__.py +49 -0
- omnibase_infra/models/runtime/model_contract_security_config.py +41 -0
- omnibase_infra/models/runtime/model_discovery_error.py +81 -0
- omnibase_infra/models/runtime/model_discovery_result.py +162 -0
- omnibase_infra/models/runtime/model_discovery_warning.py +74 -0
- omnibase_infra/models/runtime/model_failed_plugin_load.py +63 -0
- omnibase_infra/models/runtime/model_handler_contract.py +296 -0
- omnibase_infra/models/runtime/model_loaded_handler.py +129 -0
- omnibase_infra/models/runtime/model_plugin_load_context.py +93 -0
- omnibase_infra/models/runtime/model_plugin_load_summary.py +124 -0
- omnibase_infra/models/security/__init__.py +50 -0
- omnibase_infra/models/security/classification_levels.py +99 -0
- omnibase_infra/models/security/model_environment_policy.py +145 -0
- omnibase_infra/models/security/model_handler_security_policy.py +107 -0
- omnibase_infra/models/security/model_security_error.py +81 -0
- omnibase_infra/models/security/model_security_validation_result.py +328 -0
- omnibase_infra/models/security/model_security_warning.py +67 -0
- omnibase_infra/models/snapshot/__init__.py +27 -0
- omnibase_infra/models/snapshot/model_field_change.py +65 -0
- omnibase_infra/models/snapshot/model_snapshot.py +270 -0
- omnibase_infra/models/snapshot/model_snapshot_diff.py +203 -0
- omnibase_infra/models/snapshot/model_subject_ref.py +81 -0
- omnibase_infra/models/types/__init__.py +71 -0
- omnibase_infra/models/validation/__init__.py +89 -0
- omnibase_infra/models/validation/model_any_type_validation_result.py +118 -0
- omnibase_infra/models/validation/model_any_type_violation.py +141 -0
- omnibase_infra/models/validation/model_category_match_result.py +345 -0
- omnibase_infra/models/validation/model_chain_violation.py +166 -0
- omnibase_infra/models/validation/model_coverage_metrics.py +316 -0
- omnibase_infra/models/validation/model_execution_shape_rule.py +159 -0
- omnibase_infra/models/validation/model_execution_shape_validation.py +208 -0
- omnibase_infra/models/validation/model_execution_shape_validation_result.py +294 -0
- omnibase_infra/models/validation/model_execution_shape_violation.py +122 -0
- omnibase_infra/models/validation/model_localhandler_validation_result.py +139 -0
- omnibase_infra/models/validation/model_localhandler_violation.py +100 -0
- omnibase_infra/models/validation/model_output_validation_params.py +74 -0
- omnibase_infra/models/validation/model_validate_and_raise_params.py +84 -0
- omnibase_infra/models/validation/model_validation_error_params.py +84 -0
- omnibase_infra/models/validation/model_validation_outcome.py +287 -0
- omnibase_infra/nodes/__init__.py +57 -0
- omnibase_infra/nodes/architecture_validator/__init__.py +79 -0
- omnibase_infra/nodes/architecture_validator/contract.yaml +252 -0
- omnibase_infra/nodes/architecture_validator/contract_architecture_validator.yaml +203 -0
- omnibase_infra/nodes/architecture_validator/mixins/__init__.py +16 -0
- omnibase_infra/nodes/architecture_validator/mixins/mixin_file_path_rule.py +92 -0
- omnibase_infra/nodes/architecture_validator/models/__init__.py +36 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_request.py +56 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_validation_result.py +311 -0
- omnibase_infra/nodes/architecture_validator/models/model_architecture_violation.py +163 -0
- omnibase_infra/nodes/architecture_validator/models/model_rule_check_result.py +265 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_request.py +105 -0
- omnibase_infra/nodes/architecture_validator/models/model_validation_result.py +314 -0
- omnibase_infra/nodes/architecture_validator/node.py +262 -0
- omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +383 -0
- omnibase_infra/nodes/architecture_validator/protocols/__init__.py +9 -0
- omnibase_infra/nodes/architecture_validator/protocols/protocol_architecture_rule.py +225 -0
- omnibase_infra/nodes/architecture_validator/registry/__init__.py +28 -0
- omnibase_infra/nodes/architecture_validator/registry/registry_infra_architecture_validator.py +106 -0
- omnibase_infra/nodes/architecture_validator/validators/__init__.py +104 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_direct_dispatch.py +422 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_handler_publishing.py +481 -0
- omnibase_infra/nodes/architecture_validator/validators/validator_no_orchestrator_fsm.py +491 -0
- omnibase_infra/nodes/contract_registry_reducer/__init__.py +29 -0
- omnibase_infra/nodes/contract_registry_reducer/contract.yaml +255 -0
- omnibase_infra/nodes/contract_registry_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_contract_registry_state.py +266 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_cleanup_topic_references.py +55 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_deactivate_contract.py +58 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_mark_stale.py +49 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_heartbeat.py +71 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_update_topic.py +66 -0
- omnibase_infra/nodes/contract_registry_reducer/models/model_payload_upsert_contract.py +92 -0
- omnibase_infra/nodes/contract_registry_reducer/node.py +121 -0
- omnibase_infra/nodes/contract_registry_reducer/reducer.py +784 -0
- omnibase_infra/nodes/contract_registry_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/contract_registry_reducer/registry/registry_infra_contract_registry_reducer.py +101 -0
- omnibase_infra/nodes/effects/README.md +358 -0
- omnibase_infra/nodes/effects/__init__.py +26 -0
- omnibase_infra/nodes/effects/contract.yaml +167 -0
- omnibase_infra/nodes/effects/models/__init__.py +32 -0
- omnibase_infra/nodes/effects/models/model_backend_result.py +190 -0
- omnibase_infra/nodes/effects/models/model_effect_idempotency_config.py +92 -0
- omnibase_infra/nodes/effects/models/model_registry_request.py +132 -0
- omnibase_infra/nodes/effects/models/model_registry_response.py +263 -0
- omnibase_infra/nodes/effects/protocol_consul_client.py +89 -0
- omnibase_infra/nodes/effects/protocol_effect_idempotency_store.py +143 -0
- omnibase_infra/nodes/effects/protocol_postgres_adapter.py +96 -0
- omnibase_infra/nodes/effects/registry_effect.py +525 -0
- omnibase_infra/nodes/effects/store_effect_idempotency_inmemory.py +425 -0
- omnibase_infra/nodes/handlers/consul/contract.yaml +85 -0
- omnibase_infra/nodes/handlers/db/contract.yaml +72 -0
- omnibase_infra/nodes/handlers/graph/contract.yaml +127 -0
- omnibase_infra/nodes/handlers/http/contract.yaml +74 -0
- omnibase_infra/nodes/handlers/intent/contract.yaml +66 -0
- omnibase_infra/nodes/handlers/mcp/contract.yaml +69 -0
- omnibase_infra/nodes/handlers/vault/contract.yaml +91 -0
- omnibase_infra/nodes/node_intent_storage_effect/__init__.py +50 -0
- omnibase_infra/nodes/node_intent_storage_effect/contract.yaml +194 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/__init__.py +24 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_input.py +141 -0
- omnibase_infra/nodes/node_intent_storage_effect/models/model_intent_storage_output.py +130 -0
- omnibase_infra/nodes/node_intent_storage_effect/node.py +94 -0
- omnibase_infra/nodes/node_intent_storage_effect/registry/__init__.py +35 -0
- omnibase_infra/nodes/node_intent_storage_effect/registry/registry_infra_intent_storage.py +294 -0
- omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +50 -0
- omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +104 -0
- omnibase_infra/nodes/node_ledger_projection_compute/node.py +284 -0
- omnibase_infra/nodes/node_ledger_projection_compute/registry/__init__.py +29 -0
- omnibase_infra/nodes/node_ledger_projection_compute/registry/registry_infra_ledger_projection.py +118 -0
- omnibase_infra/nodes/node_ledger_write_effect/__init__.py +82 -0
- omnibase_infra/nodes/node_ledger_write_effect/contract.yaml +200 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/__init__.py +22 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_append.py +372 -0
- omnibase_infra/nodes/node_ledger_write_effect/handlers/handler_ledger_query.py +597 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/__init__.py +31 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_append_result.py +54 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_entry.py +92 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query.py +53 -0
- omnibase_infra/nodes/node_ledger_write_effect/models/model_ledger_query_result.py +41 -0
- omnibase_infra/nodes/node_ledger_write_effect/node.py +89 -0
- omnibase_infra/nodes/node_ledger_write_effect/protocols/__init__.py +13 -0
- omnibase_infra/nodes/node_ledger_write_effect/protocols/protocol_ledger_persistence.py +127 -0
- omnibase_infra/nodes/node_ledger_write_effect/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_ledger_write_effect/registry/registry_infra_ledger_write.py +121 -0
- omnibase_infra/nodes/node_registration_orchestrator/README.md +542 -0
- omnibase_infra/nodes/node_registration_orchestrator/__init__.py +120 -0
- omnibase_infra/nodes/node_registration_orchestrator/contract.yaml +482 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/__init__.py +53 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_introspected.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_node_registration_acked.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/dispatchers/dispatcher_runtime_tick.py +373 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/__init__.py +62 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_heartbeat.py +376 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_introspected.py +694 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_node_registration_acked.py +458 -0
- omnibase_infra/nodes/node_registration_orchestrator/handlers/handler_runtime_tick.py +364 -0
- omnibase_infra/nodes/node_registration_orchestrator/introspection_event_router.py +544 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/__init__.py +75 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_intent_payload.py +194 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_consul_registration_intent.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_intent_execution_result.py +50 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_node_liveness_expired.py +107 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_config.py +67 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_input.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_orchestrator_output.py +166 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +235 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_upsert_intent.py +68 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_execution_result.py +384 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_reducer_state.py +60 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registration_intent.py +177 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_registry_intent.py +247 -0
- omnibase_infra/nodes/node_registration_orchestrator/node.py +195 -0
- omnibase_infra/nodes/node_registration_orchestrator/plugin.py +909 -0
- omnibase_infra/nodes/node_registration_orchestrator/protocols.py +439 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_orchestrator/registry/registry_infra_node_registration_orchestrator.py +528 -0
- omnibase_infra/nodes/node_registration_orchestrator/timeout_coordinator.py +393 -0
- omnibase_infra/nodes/node_registration_orchestrator/wiring.py +743 -0
- omnibase_infra/nodes/node_registration_reducer/__init__.py +15 -0
- omnibase_infra/nodes/node_registration_reducer/contract.yaml +301 -0
- omnibase_infra/nodes/node_registration_reducer/models/__init__.py +38 -0
- omnibase_infra/nodes/node_registration_reducer/models/model_validation_result.py +113 -0
- omnibase_infra/nodes/node_registration_reducer/node.py +139 -0
- omnibase_infra/nodes/node_registration_reducer/registry/__init__.py +9 -0
- omnibase_infra/nodes/node_registration_reducer/registry/registry_infra_node_registration_reducer.py +79 -0
- omnibase_infra/nodes/node_registration_storage_effect/__init__.py +41 -0
- omnibase_infra/nodes/node_registration_storage_effect/contract.yaml +220 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/__init__.py +44 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_delete_result.py +132 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_record.py +199 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_registration_update.py +155 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_details.py +123 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_health_check_result.py +117 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_query.py +100 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_storage_result.py +136 -0
- omnibase_infra/nodes/node_registration_storage_effect/models/model_upsert_result.py +127 -0
- omnibase_infra/nodes/node_registration_storage_effect/node.py +112 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/__init__.py +22 -0
- omnibase_infra/nodes/node_registration_storage_effect/protocols/protocol_registration_persistence.py +333 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/__init__.py +23 -0
- omnibase_infra/nodes/node_registration_storage_effect/registry/registry_infra_registration_storage.py +215 -0
- omnibase_infra/nodes/node_registry_effect/__init__.py +85 -0
- omnibase_infra/nodes/node_registry_effect/contract.yaml +677 -0
- omnibase_infra/nodes/node_registry_effect/handlers/__init__.py +70 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_deregister.py +211 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_consul_register.py +212 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_partial_retry.py +417 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_deactivate.py +215 -0
- omnibase_infra/nodes/node_registry_effect/handlers/handler_postgres_upsert.py +208 -0
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +43 -0
- omnibase_infra/nodes/node_registry_effect/models/model_partial_retry_request.py +92 -0
- omnibase_infra/nodes/node_registry_effect/node.py +165 -0
- omnibase_infra/nodes/node_registry_effect/registry/__init__.py +27 -0
- omnibase_infra/nodes/node_registry_effect/registry/registry_infra_registry_effect.py +196 -0
- omnibase_infra/nodes/node_service_discovery_effect/__init__.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/contract.yaml +246 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/__init__.py +67 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_health_status.py +72 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/enum_service_discovery_operation.py +58 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_query.py +99 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_discovery_result.py +98 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_health_check_config.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_query_metadata.py +63 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_registration_result.py +130 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_details.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_discovery_health_check_result.py +119 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_info.py +106 -0
- omnibase_infra/nodes/node_service_discovery_effect/models/model_service_registration.py +121 -0
- omnibase_infra/nodes/node_service_discovery_effect/node.py +111 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/__init__.py +14 -0
- omnibase_infra/nodes/node_service_discovery_effect/protocols/protocol_discovery_operations.py +279 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/__init__.py +13 -0
- omnibase_infra/nodes/node_service_discovery_effect/registry/registry_infra_service_discovery.py +222 -0
- omnibase_infra/nodes/reducers/__init__.py +30 -0
- omnibase_infra/nodes/reducers/models/__init__.py +37 -0
- omnibase_infra/nodes/reducers/models/model_payload_consul_register.py +87 -0
- omnibase_infra/nodes/reducers/models/model_payload_ledger_append.py +133 -0
- omnibase_infra/nodes/reducers/models/model_payload_postgres_upsert_registration.py +60 -0
- omnibase_infra/nodes/reducers/models/model_registration_confirmation.py +166 -0
- omnibase_infra/nodes/reducers/models/model_registration_state.py +433 -0
- omnibase_infra/nodes/reducers/registration_reducer.py +1138 -0
- omnibase_infra/observability/__init__.py +143 -0
- omnibase_infra/observability/constants_metrics.py +91 -0
- omnibase_infra/observability/factory_observability_sink.py +525 -0
- omnibase_infra/observability/handlers/__init__.py +118 -0
- omnibase_infra/observability/handlers/handler_logging_structured.py +967 -0
- omnibase_infra/observability/handlers/handler_metrics_prometheus.py +1120 -0
- omnibase_infra/observability/handlers/model_logging_handler_config.py +71 -0
- omnibase_infra/observability/handlers/model_logging_handler_response.py +77 -0
- omnibase_infra/observability/handlers/model_metrics_handler_config.py +172 -0
- omnibase_infra/observability/handlers/model_metrics_handler_payload.py +135 -0
- omnibase_infra/observability/handlers/model_metrics_handler_response.py +101 -0
- omnibase_infra/observability/hooks/__init__.py +74 -0
- omnibase_infra/observability/hooks/hook_observability.py +1223 -0
- omnibase_infra/observability/models/__init__.py +30 -0
- omnibase_infra/observability/models/enum_required_log_context_key.py +77 -0
- omnibase_infra/observability/models/model_buffered_log_entry.py +117 -0
- omnibase_infra/observability/models/model_logging_sink_config.py +73 -0
- omnibase_infra/observability/models/model_metrics_sink_config.py +156 -0
- omnibase_infra/observability/sinks/__init__.py +69 -0
- omnibase_infra/observability/sinks/sink_logging_structured.py +809 -0
- omnibase_infra/observability/sinks/sink_metrics_prometheus.py +710 -0
- omnibase_infra/plugins/__init__.py +27 -0
- omnibase_infra/plugins/examples/__init__.py +28 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer.py +271 -0
- omnibase_infra/plugins/examples/plugin_json_normalizer_error_handling.py +210 -0
- omnibase_infra/plugins/models/__init__.py +21 -0
- omnibase_infra/plugins/models/model_plugin_context.py +76 -0
- omnibase_infra/plugins/models/model_plugin_input_data.py +58 -0
- omnibase_infra/plugins/models/model_plugin_output_data.py +62 -0
- omnibase_infra/plugins/plugin_compute_base.py +449 -0
- omnibase_infra/projectors/__init__.py +30 -0
- omnibase_infra/projectors/contracts/__init__.py +63 -0
- omnibase_infra/projectors/contracts/registration_projector.yaml +370 -0
- omnibase_infra/projectors/projection_reader_registration.py +1559 -0
- omnibase_infra/projectors/snapshot_publisher_registration.py +1329 -0
- omnibase_infra/protocols/__init__.py +104 -0
- omnibase_infra/protocols/protocol_capability_projection.py +253 -0
- omnibase_infra/protocols/protocol_capability_query.py +251 -0
- omnibase_infra/protocols/protocol_container_aware.py +200 -0
- omnibase_infra/protocols/protocol_dispatch_engine.py +152 -0
- omnibase_infra/protocols/protocol_event_bus_like.py +127 -0
- omnibase_infra/protocols/protocol_event_projector.py +96 -0
- omnibase_infra/protocols/protocol_idempotency_store.py +142 -0
- omnibase_infra/protocols/protocol_message_dispatcher.py +247 -0
- omnibase_infra/protocols/protocol_message_type_registry.py +306 -0
- omnibase_infra/protocols/protocol_plugin_compute.py +368 -0
- omnibase_infra/protocols/protocol_projector_schema_validator.py +82 -0
- omnibase_infra/protocols/protocol_registry_metrics.py +215 -0
- omnibase_infra/protocols/protocol_snapshot_publisher.py +396 -0
- omnibase_infra/protocols/protocol_snapshot_store.py +567 -0
- omnibase_infra/runtime/__init__.py +445 -0
- omnibase_infra/runtime/binding_config_resolver.py +2771 -0
- omnibase_infra/runtime/binding_resolver.py +753 -0
- omnibase_infra/runtime/chain_aware_dispatch.py +467 -0
- omnibase_infra/runtime/constants_notification.py +75 -0
- omnibase_infra/runtime/constants_security.py +70 -0
- omnibase_infra/runtime/contract_handler_discovery.py +587 -0
- omnibase_infra/runtime/contract_loaders/__init__.py +51 -0
- omnibase_infra/runtime/contract_loaders/handler_routing_loader.py +464 -0
- omnibase_infra/runtime/contract_loaders/operation_bindings_loader.py +789 -0
- omnibase_infra/runtime/dispatch_context_enforcer.py +427 -0
- omnibase_infra/runtime/emit_daemon/__init__.py +97 -0
- omnibase_infra/runtime/emit_daemon/cli.py +844 -0
- omnibase_infra/runtime/emit_daemon/client.py +811 -0
- omnibase_infra/runtime/emit_daemon/config.py +535 -0
- omnibase_infra/runtime/emit_daemon/daemon.py +812 -0
- omnibase_infra/runtime/emit_daemon/event_registry.py +477 -0
- omnibase_infra/runtime/emit_daemon/model_daemon_request.py +139 -0
- omnibase_infra/runtime/emit_daemon/model_daemon_response.py +191 -0
- omnibase_infra/runtime/emit_daemon/queue.py +618 -0
- omnibase_infra/runtime/enums/__init__.py +18 -0
- omnibase_infra/runtime/enums/enum_config_ref_scheme.py +33 -0
- omnibase_infra/runtime/enums/enum_scheduler_status.py +170 -0
- omnibase_infra/runtime/envelope_validator.py +179 -0
- omnibase_infra/runtime/event_bus_subcontract_wiring.py +466 -0
- omnibase_infra/runtime/handler_bootstrap_source.py +507 -0
- omnibase_infra/runtime/handler_contract_config_loader.py +603 -0
- omnibase_infra/runtime/handler_contract_source.py +750 -0
- omnibase_infra/runtime/handler_identity.py +81 -0
- omnibase_infra/runtime/handler_plugin_loader.py +2046 -0
- omnibase_infra/runtime/handler_registry.py +329 -0
- omnibase_infra/runtime/handler_source_resolver.py +367 -0
- omnibase_infra/runtime/invocation_security_enforcer.py +427 -0
- omnibase_infra/runtime/kafka_contract_source.py +984 -0
- omnibase_infra/runtime/kernel.py +40 -0
- omnibase_infra/runtime/mixin_policy_validation.py +522 -0
- omnibase_infra/runtime/mixin_semver_cache.py +402 -0
- omnibase_infra/runtime/mixins/__init__.py +24 -0
- omnibase_infra/runtime/mixins/mixin_projector_notification_publishing.py +566 -0
- omnibase_infra/runtime/mixins/mixin_projector_sql_operations.py +778 -0
- omnibase_infra/runtime/models/__init__.py +229 -0
- omnibase_infra/runtime/models/model_batch_lifecycle_result.py +217 -0
- omnibase_infra/runtime/models/model_binding_config.py +168 -0
- omnibase_infra/runtime/models/model_binding_config_cache_stats.py +135 -0
- omnibase_infra/runtime/models/model_binding_config_resolver_config.py +329 -0
- omnibase_infra/runtime/models/model_cached_secret.py +138 -0
- omnibase_infra/runtime/models/model_compute_key.py +138 -0
- omnibase_infra/runtime/models/model_compute_registration.py +97 -0
- omnibase_infra/runtime/models/model_config_cache_entry.py +61 -0
- omnibase_infra/runtime/models/model_config_ref.py +331 -0
- omnibase_infra/runtime/models/model_config_ref_parse_result.py +125 -0
- omnibase_infra/runtime/models/model_contract_load_result.py +224 -0
- omnibase_infra/runtime/models/model_domain_plugin_config.py +92 -0
- omnibase_infra/runtime/models/model_domain_plugin_result.py +270 -0
- omnibase_infra/runtime/models/model_duplicate_response.py +54 -0
- omnibase_infra/runtime/models/model_enabled_protocols_config.py +61 -0
- omnibase_infra/runtime/models/model_event_bus_config.py +54 -0
- omnibase_infra/runtime/models/model_failed_component.py +55 -0
- omnibase_infra/runtime/models/model_health_check_response.py +168 -0
- omnibase_infra/runtime/models/model_health_check_result.py +229 -0
- omnibase_infra/runtime/models/model_lifecycle_result.py +245 -0
- omnibase_infra/runtime/models/model_logging_config.py +42 -0
- omnibase_infra/runtime/models/model_optional_correlation_id.py +167 -0
- omnibase_infra/runtime/models/model_optional_string.py +94 -0
- omnibase_infra/runtime/models/model_optional_uuid.py +110 -0
- omnibase_infra/runtime/models/model_policy_context.py +100 -0
- omnibase_infra/runtime/models/model_policy_key.py +138 -0
- omnibase_infra/runtime/models/model_policy_registration.py +139 -0
- omnibase_infra/runtime/models/model_policy_result.py +103 -0
- omnibase_infra/runtime/models/model_policy_type_filter.py +157 -0
- omnibase_infra/runtime/models/model_projector_notification_config.py +171 -0
- omnibase_infra/runtime/models/model_projector_plugin_loader_config.py +47 -0
- omnibase_infra/runtime/models/model_protocol_registration_config.py +65 -0
- omnibase_infra/runtime/models/model_retry_policy.py +105 -0
- omnibase_infra/runtime/models/model_runtime_config.py +150 -0
- omnibase_infra/runtime/models/model_runtime_contract_config.py +268 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_config.py +625 -0
- omnibase_infra/runtime/models/model_runtime_scheduler_metrics.py +233 -0
- omnibase_infra/runtime/models/model_runtime_tick.py +193 -0
- omnibase_infra/runtime/models/model_secret_cache_stats.py +82 -0
- omnibase_infra/runtime/models/model_secret_mapping.py +63 -0
- omnibase_infra/runtime/models/model_secret_resolver_config.py +107 -0
- omnibase_infra/runtime/models/model_secret_resolver_metrics.py +111 -0
- omnibase_infra/runtime/models/model_secret_source_info.py +72 -0
- omnibase_infra/runtime/models/model_secret_source_spec.py +66 -0
- omnibase_infra/runtime/models/model_security_config.py +109 -0
- omnibase_infra/runtime/models/model_shutdown_batch_result.py +75 -0
- omnibase_infra/runtime/models/model_shutdown_config.py +94 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_config.py +112 -0
- omnibase_infra/runtime/models/model_transition_notification_outbox_metrics.py +140 -0
- omnibase_infra/runtime/models/model_transition_notification_publisher_metrics.py +357 -0
- omnibase_infra/runtime/projector_plugin_loader.py +1462 -0
- omnibase_infra/runtime/projector_schema_manager.py +565 -0
- omnibase_infra/runtime/projector_shell.py +1330 -0
- omnibase_infra/runtime/protocol_contract_descriptor.py +92 -0
- omnibase_infra/runtime/protocol_contract_source.py +92 -0
- omnibase_infra/runtime/protocol_domain_plugin.py +474 -0
- omnibase_infra/runtime/protocol_handler_discovery.py +221 -0
- omnibase_infra/runtime/protocol_handler_plugin_loader.py +327 -0
- omnibase_infra/runtime/protocol_lifecycle_executor.py +435 -0
- omnibase_infra/runtime/protocol_policy.py +366 -0
- omnibase_infra/runtime/protocols/__init__.py +37 -0
- omnibase_infra/runtime/protocols/protocol_runtime_scheduler.py +468 -0
- omnibase_infra/runtime/publisher_topic_scoped.py +294 -0
- omnibase_infra/runtime/registry/__init__.py +93 -0
- omnibase_infra/runtime/registry/mixin_message_type_query.py +326 -0
- omnibase_infra/runtime/registry/mixin_message_type_registration.py +354 -0
- omnibase_infra/runtime/registry/registry_event_bus_binding.py +268 -0
- omnibase_infra/runtime/registry/registry_message_type.py +542 -0
- omnibase_infra/runtime/registry/registry_protocol_binding.py +445 -0
- omnibase_infra/runtime/registry_compute.py +1143 -0
- omnibase_infra/runtime/registry_contract_source.py +693 -0
- omnibase_infra/runtime/registry_dispatcher.py +678 -0
- omnibase_infra/runtime/registry_policy.py +1185 -0
- omnibase_infra/runtime/runtime_contract_config_loader.py +406 -0
- omnibase_infra/runtime/runtime_scheduler.py +1070 -0
- omnibase_infra/runtime/secret_resolver.py +2112 -0
- omnibase_infra/runtime/security_metadata_validator.py +776 -0
- omnibase_infra/runtime/service_kernel.py +1651 -0
- omnibase_infra/runtime/service_message_dispatch_engine.py +2350 -0
- omnibase_infra/runtime/service_runtime_host_process.py +3493 -0
- omnibase_infra/runtime/transition_notification_outbox.py +1190 -0
- omnibase_infra/runtime/transition_notification_publisher.py +765 -0
- omnibase_infra/runtime/util_container_wiring.py +1124 -0
- omnibase_infra/runtime/util_validation.py +314 -0
- omnibase_infra/runtime/util_version.py +98 -0
- omnibase_infra/runtime/util_wiring.py +723 -0
- omnibase_infra/schemas/schema_registration_projection.sql +320 -0
- omnibase_infra/schemas/schema_transition_notification_outbox.sql +245 -0
- omnibase_infra/services/__init__.py +89 -0
- omnibase_infra/services/corpus_capture.py +684 -0
- omnibase_infra/services/mcp/__init__.py +31 -0
- omnibase_infra/services/mcp/mcp_server_lifecycle.py +449 -0
- omnibase_infra/services/mcp/service_mcp_tool_discovery.py +411 -0
- omnibase_infra/services/mcp/service_mcp_tool_registry.py +329 -0
- omnibase_infra/services/mcp/service_mcp_tool_sync.py +565 -0
- omnibase_infra/services/registry_api/__init__.py +40 -0
- omnibase_infra/services/registry_api/main.py +261 -0
- omnibase_infra/services/registry_api/models/__init__.py +66 -0
- omnibase_infra/services/registry_api/models/model_capability_widget_mapping.py +38 -0
- omnibase_infra/services/registry_api/models/model_pagination_info.py +48 -0
- omnibase_infra/services/registry_api/models/model_registry_discovery_response.py +73 -0
- omnibase_infra/services/registry_api/models/model_registry_health_response.py +49 -0
- omnibase_infra/services/registry_api/models/model_registry_instance_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_node_view.py +88 -0
- omnibase_infra/services/registry_api/models/model_registry_summary.py +60 -0
- omnibase_infra/services/registry_api/models/model_response_list_instances.py +43 -0
- omnibase_infra/services/registry_api/models/model_response_list_nodes.py +51 -0
- omnibase_infra/services/registry_api/models/model_warning.py +49 -0
- omnibase_infra/services/registry_api/models/model_widget_defaults.py +28 -0
- omnibase_infra/services/registry_api/models/model_widget_mapping.py +51 -0
- omnibase_infra/services/registry_api/routes.py +371 -0
- omnibase_infra/services/registry_api/service.py +837 -0
- omnibase_infra/services/service_capability_query.py +945 -0
- omnibase_infra/services/service_health.py +898 -0
- omnibase_infra/services/service_node_selector.py +530 -0
- omnibase_infra/services/service_timeout_emitter.py +699 -0
- omnibase_infra/services/service_timeout_scanner.py +394 -0
- omnibase_infra/services/session/__init__.py +56 -0
- omnibase_infra/services/session/config_consumer.py +137 -0
- omnibase_infra/services/session/config_store.py +139 -0
- omnibase_infra/services/session/consumer.py +1007 -0
- omnibase_infra/services/session/protocol_session_aggregator.py +117 -0
- omnibase_infra/services/session/store.py +997 -0
- omnibase_infra/services/snapshot/__init__.py +31 -0
- omnibase_infra/services/snapshot/service_snapshot.py +647 -0
- omnibase_infra/services/snapshot/store_inmemory.py +637 -0
- omnibase_infra/services/snapshot/store_postgres.py +1279 -0
- omnibase_infra/shared/__init__.py +8 -0
- omnibase_infra/testing/__init__.py +10 -0
- omnibase_infra/testing/utils.py +23 -0
- omnibase_infra/topics/__init__.py +45 -0
- omnibase_infra/topics/platform_topic_suffixes.py +140 -0
- omnibase_infra/topics/util_topic_composition.py +95 -0
- omnibase_infra/types/__init__.py +48 -0
- omnibase_infra/types/type_cache_info.py +49 -0
- omnibase_infra/types/type_dsn.py +173 -0
- omnibase_infra/types/type_infra_aliases.py +60 -0
- omnibase_infra/types/typed_dict/__init__.py +29 -0
- omnibase_infra/types/typed_dict/typed_dict_envelope_build_params.py +115 -0
- omnibase_infra/types/typed_dict/typed_dict_introspection_cache.py +128 -0
- omnibase_infra/types/typed_dict/typed_dict_performance_metrics_cache.py +140 -0
- omnibase_infra/types/typed_dict_capabilities.py +64 -0
- omnibase_infra/utils/__init__.py +117 -0
- omnibase_infra/utils/correlation.py +208 -0
- omnibase_infra/utils/util_atomic_file.py +261 -0
- omnibase_infra/utils/util_consumer_group.py +232 -0
- omnibase_infra/utils/util_datetime.py +372 -0
- omnibase_infra/utils/util_db_transaction.py +239 -0
- omnibase_infra/utils/util_dsn_validation.py +333 -0
- omnibase_infra/utils/util_env_parsing.py +264 -0
- omnibase_infra/utils/util_error_sanitization.py +457 -0
- omnibase_infra/utils/util_pydantic_validators.py +477 -0
- omnibase_infra/utils/util_retry_optimistic.py +281 -0
- omnibase_infra/utils/util_semver.py +233 -0
- omnibase_infra/validation/__init__.py +307 -0
- omnibase_infra/validation/contracts/security.validation.yaml +114 -0
- omnibase_infra/validation/enums/__init__.py +11 -0
- omnibase_infra/validation/enums/enum_contract_violation_severity.py +13 -0
- omnibase_infra/validation/infra_validators.py +1514 -0
- omnibase_infra/validation/linter_contract.py +907 -0
- omnibase_infra/validation/mixin_any_type_classification.py +120 -0
- omnibase_infra/validation/mixin_any_type_exemption.py +580 -0
- omnibase_infra/validation/mixin_any_type_reporting.py +106 -0
- omnibase_infra/validation/mixin_execution_shape_violation_checks.py +596 -0
- omnibase_infra/validation/mixin_node_archetype_detection.py +254 -0
- omnibase_infra/validation/models/__init__.py +15 -0
- omnibase_infra/validation/models/model_contract_lint_result.py +101 -0
- omnibase_infra/validation/models/model_contract_violation.py +41 -0
- omnibase_infra/validation/service_validation_aggregator.py +395 -0
- omnibase_infra/validation/validation_exemptions.yaml +2033 -0
- omnibase_infra/validation/validator_any_type.py +715 -0
- omnibase_infra/validation/validator_chain_propagation.py +839 -0
- omnibase_infra/validation/validator_execution_shape.py +465 -0
- omnibase_infra/validation/validator_localhandler.py +261 -0
- omnibase_infra/validation/validator_registration_security.py +410 -0
- omnibase_infra/validation/validator_routing_coverage.py +1020 -0
- omnibase_infra/validation/validator_runtime_shape.py +915 -0
- omnibase_infra/validation/validator_security.py +513 -0
- omnibase_infra/validation/validator_topic_category.py +1152 -0
- omnibase_infra-0.2.6.dist-info/METADATA +197 -0
- omnibase_infra-0.2.6.dist-info/RECORD +833 -0
- omnibase_infra-0.2.6.dist-info/WHEEL +4 -0
- omnibase_infra-0.2.6.dist-info/entry_points.txt +5 -0
- omnibase_infra-0.2.6.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1051 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Consul Service Discovery Handler.
|
|
4
|
+
|
|
5
|
+
This module provides a Consul-backed implementation of the service discovery
|
|
6
|
+
handler protocol, wrapping existing Consul functionality with circuit breaker
|
|
7
|
+
resilience.
|
|
8
|
+
|
|
9
|
+
Thread Pool Management:
|
|
10
|
+
- All synchronous consul operations run in a dedicated thread pool
|
|
11
|
+
- Configurable max workers (default: 10)
|
|
12
|
+
- Thread pool is lazily initialized on first use
|
|
13
|
+
- Thread pool gracefully shutdown on handler shutdown
|
|
14
|
+
|
|
15
|
+
Circuit Breaker:
|
|
16
|
+
- Uses MixinAsyncCircuitBreaker for consistent resilience
|
|
17
|
+
- Three states: CLOSED (normal), OPEN (blocking), HALF_OPEN (testing)
|
|
18
|
+
- Configurable via ModelCircuitBreakerConfig model
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import asyncio
|
|
24
|
+
import logging
|
|
25
|
+
import time
|
|
26
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
27
|
+
from datetime import UTC, datetime
|
|
28
|
+
from typing import TYPE_CHECKING, cast
|
|
29
|
+
from uuid import NAMESPACE_DNS, UUID, uuid4, uuid5
|
|
30
|
+
|
|
31
|
+
import consul
|
|
32
|
+
|
|
33
|
+
from omnibase_core.container import ModelONEXContainer
|
|
34
|
+
from omnibase_infra.enums import EnumInfraTransportType
|
|
35
|
+
from omnibase_infra.errors import (
|
|
36
|
+
InfraConnectionError,
|
|
37
|
+
InfraTimeoutError,
|
|
38
|
+
ModelInfraErrorContext,
|
|
39
|
+
ModelTimeoutErrorContext,
|
|
40
|
+
)
|
|
41
|
+
from omnibase_infra.handlers.service_discovery.models import (
|
|
42
|
+
ModelDiscoveryResult,
|
|
43
|
+
ModelHandlerRegistrationResult,
|
|
44
|
+
ModelServiceInfo,
|
|
45
|
+
)
|
|
46
|
+
from omnibase_infra.mixins import MixinAsyncCircuitBreaker
|
|
47
|
+
from omnibase_infra.models.resilience import ModelCircuitBreakerConfig
|
|
48
|
+
from omnibase_infra.nodes.node_service_discovery_effect.models import (
|
|
49
|
+
ModelDiscoveryQuery,
|
|
50
|
+
ModelServiceDiscoveryHealthCheckDetails,
|
|
51
|
+
ModelServiceDiscoveryHealthCheckResult,
|
|
52
|
+
)
|
|
53
|
+
from omnibase_infra.nodes.node_service_discovery_effect.models.enum_health_status import (
|
|
54
|
+
EnumHealthStatus,
|
|
55
|
+
)
|
|
56
|
+
from omnibase_infra.nodes.node_service_discovery_effect.models.enum_service_discovery_operation import (
|
|
57
|
+
EnumServiceDiscoveryOperation,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if TYPE_CHECKING:
|
|
61
|
+
from omnibase_infra.nodes.effects.protocol_consul_client import ProtocolConsulClient
|
|
62
|
+
|
|
63
|
+
logger = logging.getLogger(__name__)
|
|
64
|
+
|
|
65
|
+
# Default configuration values
|
|
66
|
+
DEFAULT_MAX_WORKERS = 10
|
|
67
|
+
DEFAULT_TIMEOUT_SECONDS = 30.0
|
|
68
|
+
|
|
69
|
+
# Custom namespace for ONEX service IDs (deterministic but distinct from DNS namespace)
|
|
70
|
+
# This ensures UUID5 generation for non-UUID service IDs is specific to ONEX
|
|
71
|
+
# and won't collide with DNS-based UUID5 values from other systems.
|
|
72
|
+
NAMESPACE_ONEX_SERVICE = uuid5(NAMESPACE_DNS, "omninode.service.discovery")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class HandlerServiceDiscoveryConsul(MixinAsyncCircuitBreaker):
|
|
76
|
+
"""Consul implementation of ProtocolServiceDiscoveryHandler.
|
|
77
|
+
|
|
78
|
+
Wraps existing Consul client functionality with circuit breaker resilience
|
|
79
|
+
and proper error handling.
|
|
80
|
+
|
|
81
|
+
Thread Safety:
|
|
82
|
+
This handler is coroutine-safe. All Consul operations are executed
|
|
83
|
+
in a dedicated thread pool, and circuit breaker state is protected
|
|
84
|
+
by asyncio.Lock.
|
|
85
|
+
|
|
86
|
+
Thread Pool Initialization:
|
|
87
|
+
The thread pool executor is lazily initialized on first use via
|
|
88
|
+
``_ensure_executor()``. This avoids resource allocation until the
|
|
89
|
+
handler is actually used.
|
|
90
|
+
|
|
91
|
+
Attributes:
|
|
92
|
+
handler_type: Returns "consul" identifier.
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
>>> from unittest.mock import MagicMock
|
|
96
|
+
>>> container = MagicMock(spec=ModelONEXContainer)
|
|
97
|
+
>>> handler = HandlerServiceDiscoveryConsul(
|
|
98
|
+
... container=container,
|
|
99
|
+
... consul_client=consul_client,
|
|
100
|
+
... circuit_breaker_config=ModelCircuitBreakerConfig(threshold=5),
|
|
101
|
+
... )
|
|
102
|
+
>>> result = await handler.register_service(service_info)
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def __init__(
|
|
106
|
+
self,
|
|
107
|
+
container: ModelONEXContainer,
|
|
108
|
+
consul_client: ProtocolConsulClient | None = None,
|
|
109
|
+
consul_host: str = "localhost",
|
|
110
|
+
consul_port: int = 8500,
|
|
111
|
+
consul_scheme: str = "http",
|
|
112
|
+
consul_token: str | None = None,
|
|
113
|
+
circuit_breaker_config: ModelCircuitBreakerConfig
|
|
114
|
+
| dict[str, object]
|
|
115
|
+
| None = None,
|
|
116
|
+
max_workers: int = DEFAULT_MAX_WORKERS,
|
|
117
|
+
timeout_seconds: float = DEFAULT_TIMEOUT_SECONDS,
|
|
118
|
+
) -> None:
|
|
119
|
+
"""Initialize HandlerServiceDiscoveryConsul.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
container: ONEX container for dependency injection and service resolution.
|
|
123
|
+
consul_client: Optional existing Consul client (ProtocolConsulClient).
|
|
124
|
+
If not provided, a new python-consul client will be created.
|
|
125
|
+
consul_host: Consul server hostname (default: "localhost").
|
|
126
|
+
consul_port: Consul server port (default: 8500).
|
|
127
|
+
consul_scheme: HTTP scheme "http" or "https" (default: "http").
|
|
128
|
+
consul_token: Optional Consul ACL token.
|
|
129
|
+
circuit_breaker_config: Optional circuit breaker configuration.
|
|
130
|
+
Can be a ModelCircuitBreakerConfig instance or a dict with keys:
|
|
131
|
+
- threshold: Max failures before opening (default: 5)
|
|
132
|
+
- reset_timeout_seconds: Seconds before reset (default: 60.0)
|
|
133
|
+
- service_name: Service identifier (default: "consul.discovery")
|
|
134
|
+
If not provided, uses ModelCircuitBreakerConfig defaults.
|
|
135
|
+
max_workers: Thread pool max workers (default: 10).
|
|
136
|
+
timeout_seconds: Operation timeout in seconds (default: 30.0).
|
|
137
|
+
"""
|
|
138
|
+
self._container = container
|
|
139
|
+
# Parse circuit breaker configuration using ModelCircuitBreakerConfig
|
|
140
|
+
if isinstance(circuit_breaker_config, ModelCircuitBreakerConfig):
|
|
141
|
+
cb_config = circuit_breaker_config
|
|
142
|
+
elif circuit_breaker_config is not None:
|
|
143
|
+
# Handle legacy dict format with key mapping
|
|
144
|
+
config_dict = dict(circuit_breaker_config)
|
|
145
|
+
# Map legacy 'reset_timeout' key to 'reset_timeout_seconds'
|
|
146
|
+
if (
|
|
147
|
+
"reset_timeout" in config_dict
|
|
148
|
+
and "reset_timeout_seconds" not in config_dict
|
|
149
|
+
):
|
|
150
|
+
config_dict["reset_timeout_seconds"] = config_dict.pop("reset_timeout")
|
|
151
|
+
# Set defaults for service_name and transport_type if not provided
|
|
152
|
+
config_dict.setdefault("service_name", "consul.discovery")
|
|
153
|
+
config_dict.setdefault("transport_type", EnumInfraTransportType.CONSUL)
|
|
154
|
+
cb_config = ModelCircuitBreakerConfig(**config_dict)
|
|
155
|
+
else:
|
|
156
|
+
cb_config = ModelCircuitBreakerConfig(
|
|
157
|
+
service_name="consul.discovery",
|
|
158
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
self._init_circuit_breaker(
|
|
162
|
+
threshold=cb_config.threshold,
|
|
163
|
+
reset_timeout=cb_config.reset_timeout_seconds,
|
|
164
|
+
service_name=cb_config.service_name,
|
|
165
|
+
transport_type=cb_config.transport_type,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Store configuration
|
|
169
|
+
self._consul_host = consul_host
|
|
170
|
+
self._consul_port = consul_port
|
|
171
|
+
self._consul_scheme = consul_scheme
|
|
172
|
+
self._consul_token = consul_token
|
|
173
|
+
self._timeout_seconds = timeout_seconds
|
|
174
|
+
|
|
175
|
+
# Initialize Consul client
|
|
176
|
+
# Note: We use consul.Consul type since that's what we create internally.
|
|
177
|
+
# External clients are expected to duck-type as consul.Consul.
|
|
178
|
+
self._consul_client: consul.Consul | None
|
|
179
|
+
if consul_client is not None:
|
|
180
|
+
# Use provided client (duck-typed ProtocolConsulClient)
|
|
181
|
+
self._consul_client = consul_client
|
|
182
|
+
self._owns_client = False
|
|
183
|
+
else:
|
|
184
|
+
# Create python-consul client
|
|
185
|
+
self._consul_client = consul.Consul(
|
|
186
|
+
host=consul_host,
|
|
187
|
+
port=consul_port,
|
|
188
|
+
scheme=consul_scheme,
|
|
189
|
+
token=consul_token,
|
|
190
|
+
)
|
|
191
|
+
self._owns_client = True
|
|
192
|
+
|
|
193
|
+
# Lazy thread pool initialization
|
|
194
|
+
self._executor: ThreadPoolExecutor | None = None
|
|
195
|
+
self._executor_lock = asyncio.Lock()
|
|
196
|
+
self._max_workers = max_workers
|
|
197
|
+
|
|
198
|
+
logger.info(
|
|
199
|
+
"HandlerServiceDiscoveryConsul initialized",
|
|
200
|
+
extra={
|
|
201
|
+
"consul_host": consul_host,
|
|
202
|
+
"consul_port": consul_port,
|
|
203
|
+
"max_workers": max_workers,
|
|
204
|
+
},
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def handler_type(self) -> str:
|
|
209
|
+
"""Return the handler type identifier.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
"consul" identifier string.
|
|
213
|
+
"""
|
|
214
|
+
return "consul"
|
|
215
|
+
|
|
216
|
+
async def _ensure_executor(self) -> ThreadPoolExecutor:
|
|
217
|
+
"""Ensure thread pool executor is initialized.
|
|
218
|
+
|
|
219
|
+
Uses double-checked locking for thread-safe lazy initialization.
|
|
220
|
+
The executor is created on first use rather than at handler
|
|
221
|
+
construction time to avoid allocating resources for handlers
|
|
222
|
+
that may never be used.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
The ThreadPoolExecutor instance.
|
|
226
|
+
"""
|
|
227
|
+
if self._executor is not None:
|
|
228
|
+
return self._executor
|
|
229
|
+
|
|
230
|
+
async with self._executor_lock:
|
|
231
|
+
if self._executor is None:
|
|
232
|
+
self._executor = ThreadPoolExecutor(max_workers=self._max_workers)
|
|
233
|
+
logger.debug(
|
|
234
|
+
"Thread pool executor initialized",
|
|
235
|
+
extra={"max_workers": self._max_workers},
|
|
236
|
+
)
|
|
237
|
+
return self._executor
|
|
238
|
+
|
|
239
|
+
def _check_not_shutdown(
|
|
240
|
+
self,
|
|
241
|
+
operation: str,
|
|
242
|
+
correlation_id: UUID,
|
|
243
|
+
) -> None:
|
|
244
|
+
"""Check that handler has not been shut down.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
operation: Name of the operation being attempted.
|
|
248
|
+
correlation_id: Correlation ID for tracing.
|
|
249
|
+
|
|
250
|
+
Raises:
|
|
251
|
+
InfraConnectionError: If handler has been shut down.
|
|
252
|
+
"""
|
|
253
|
+
if self._consul_client is None:
|
|
254
|
+
context = ModelInfraErrorContext(
|
|
255
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
256
|
+
operation=operation,
|
|
257
|
+
target_name="consul.discovery",
|
|
258
|
+
correlation_id=correlation_id,
|
|
259
|
+
)
|
|
260
|
+
raise InfraConnectionError(
|
|
261
|
+
"Handler has been shut down, cannot perform operation",
|
|
262
|
+
context=context,
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
async def register_service(
|
|
266
|
+
self,
|
|
267
|
+
service_info: ModelServiceInfo,
|
|
268
|
+
correlation_id: UUID | None = None,
|
|
269
|
+
) -> ModelHandlerRegistrationResult:
|
|
270
|
+
"""Register a service with Consul.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
service_info: Service information to register.
|
|
274
|
+
correlation_id: Optional correlation ID for tracing.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
ModelHandlerRegistrationResult with registration outcome.
|
|
278
|
+
|
|
279
|
+
Raises:
|
|
280
|
+
InfraConnectionError: If connection to Consul fails.
|
|
281
|
+
InfraTimeoutError: If operation times out.
|
|
282
|
+
InfraUnavailableError: If circuit breaker is open.
|
|
283
|
+
"""
|
|
284
|
+
correlation_id = correlation_id or uuid4()
|
|
285
|
+
start_time = time.monotonic()
|
|
286
|
+
|
|
287
|
+
# Guard against use-after-shutdown
|
|
288
|
+
self._check_not_shutdown("register_service", correlation_id)
|
|
289
|
+
|
|
290
|
+
# Check circuit breaker
|
|
291
|
+
async with self._circuit_breaker_lock:
|
|
292
|
+
await self._check_circuit_breaker(
|
|
293
|
+
operation="register_service",
|
|
294
|
+
correlation_id=correlation_id,
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
# Build health check config if URL provided
|
|
299
|
+
check_config: dict[str, str] | None = None
|
|
300
|
+
if service_info.health_check_url:
|
|
301
|
+
check_config = {
|
|
302
|
+
"http": service_info.health_check_url,
|
|
303
|
+
"interval": "10s",
|
|
304
|
+
"timeout": "5s",
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
# Execute registration in thread pool
|
|
308
|
+
# Client is typed as consul.Consul (duck-typed for injected clients)
|
|
309
|
+
client = self._consul_client
|
|
310
|
+
executor = await self._ensure_executor()
|
|
311
|
+
loop = asyncio.get_running_loop()
|
|
312
|
+
# Convert UUID to string for Consul API compatibility
|
|
313
|
+
service_id_str = str(service_info.service_id)
|
|
314
|
+
await asyncio.wait_for(
|
|
315
|
+
loop.run_in_executor(
|
|
316
|
+
executor,
|
|
317
|
+
# NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
|
|
318
|
+
# agent.service.register exists on Optional[Consul] union type.
|
|
319
|
+
lambda: client.agent.service.register( # type: ignore[union-attr] # NOTE: duck-typed client
|
|
320
|
+
name=service_info.service_name,
|
|
321
|
+
service_id=service_id_str,
|
|
322
|
+
address=service_info.address,
|
|
323
|
+
port=service_info.port,
|
|
324
|
+
tags=list(service_info.tags),
|
|
325
|
+
meta=service_info.metadata,
|
|
326
|
+
check=check_config,
|
|
327
|
+
),
|
|
328
|
+
),
|
|
329
|
+
timeout=self._timeout_seconds,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
# Reset circuit breaker on success
|
|
333
|
+
async with self._circuit_breaker_lock:
|
|
334
|
+
await self._reset_circuit_breaker()
|
|
335
|
+
|
|
336
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
337
|
+
|
|
338
|
+
logger.info(
|
|
339
|
+
"Service registered with Consul",
|
|
340
|
+
extra={
|
|
341
|
+
"service_id": service_id_str,
|
|
342
|
+
"service_name": service_info.service_name,
|
|
343
|
+
"duration_ms": duration_ms,
|
|
344
|
+
"correlation_id": str(correlation_id),
|
|
345
|
+
},
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
return ModelHandlerRegistrationResult(
|
|
349
|
+
success=True,
|
|
350
|
+
service_id=service_info.service_id,
|
|
351
|
+
operation=EnumServiceDiscoveryOperation.REGISTER,
|
|
352
|
+
duration_ms=duration_ms,
|
|
353
|
+
backend_type=self.handler_type,
|
|
354
|
+
correlation_id=correlation_id,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
except TimeoutError as e:
|
|
358
|
+
async with self._circuit_breaker_lock:
|
|
359
|
+
await self._record_circuit_failure(
|
|
360
|
+
operation="register_service",
|
|
361
|
+
correlation_id=correlation_id,
|
|
362
|
+
)
|
|
363
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
364
|
+
raise InfraTimeoutError(
|
|
365
|
+
f"Consul registration timed out after {self._timeout_seconds}s",
|
|
366
|
+
context=ModelTimeoutErrorContext(
|
|
367
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
368
|
+
operation="register_service",
|
|
369
|
+
target_name="consul.discovery",
|
|
370
|
+
correlation_id=correlation_id,
|
|
371
|
+
timeout_seconds=self._timeout_seconds,
|
|
372
|
+
),
|
|
373
|
+
) from e
|
|
374
|
+
|
|
375
|
+
except consul.ConsulException as e:
|
|
376
|
+
async with self._circuit_breaker_lock:
|
|
377
|
+
await self._record_circuit_failure(
|
|
378
|
+
operation="register_service",
|
|
379
|
+
correlation_id=correlation_id,
|
|
380
|
+
)
|
|
381
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
382
|
+
context = ModelInfraErrorContext(
|
|
383
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
384
|
+
operation="register_service",
|
|
385
|
+
target_name="consul.discovery",
|
|
386
|
+
correlation_id=correlation_id,
|
|
387
|
+
)
|
|
388
|
+
raise InfraConnectionError(
|
|
389
|
+
"Consul registration failed",
|
|
390
|
+
context=context,
|
|
391
|
+
) from e
|
|
392
|
+
|
|
393
|
+
except Exception as e:
|
|
394
|
+
async with self._circuit_breaker_lock:
|
|
395
|
+
await self._record_circuit_failure(
|
|
396
|
+
operation="register_service",
|
|
397
|
+
correlation_id=correlation_id,
|
|
398
|
+
)
|
|
399
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
400
|
+
context = ModelInfraErrorContext(
|
|
401
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
402
|
+
operation="register_service",
|
|
403
|
+
target_name="consul.discovery",
|
|
404
|
+
correlation_id=correlation_id,
|
|
405
|
+
)
|
|
406
|
+
raise InfraConnectionError(
|
|
407
|
+
f"Consul registration failed: {type(e).__name__}",
|
|
408
|
+
context=context,
|
|
409
|
+
) from e
|
|
410
|
+
|
|
411
|
+
async def deregister_service(
|
|
412
|
+
self,
|
|
413
|
+
service_id: UUID,
|
|
414
|
+
correlation_id: UUID | None = None,
|
|
415
|
+
) -> None:
|
|
416
|
+
"""Deregister a service from Consul.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
service_id: UUID of the service to deregister.
|
|
420
|
+
correlation_id: Optional correlation ID for tracing.
|
|
421
|
+
|
|
422
|
+
Raises:
|
|
423
|
+
InfraConnectionError: If connection to Consul fails.
|
|
424
|
+
InfraTimeoutError: If operation times out.
|
|
425
|
+
InfraUnavailableError: If circuit breaker is open.
|
|
426
|
+
"""
|
|
427
|
+
correlation_id = correlation_id or uuid4()
|
|
428
|
+
# Convert UUID to string for Consul API
|
|
429
|
+
service_id_str = str(service_id)
|
|
430
|
+
|
|
431
|
+
# Guard against use-after-shutdown
|
|
432
|
+
self._check_not_shutdown("deregister_service", correlation_id)
|
|
433
|
+
|
|
434
|
+
# Check circuit breaker
|
|
435
|
+
async with self._circuit_breaker_lock:
|
|
436
|
+
await self._check_circuit_breaker(
|
|
437
|
+
operation="deregister_service",
|
|
438
|
+
correlation_id=correlation_id,
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
try:
|
|
442
|
+
# Client is typed as consul.Consul (duck-typed for injected clients)
|
|
443
|
+
client = self._consul_client
|
|
444
|
+
executor = await self._ensure_executor()
|
|
445
|
+
loop = asyncio.get_running_loop()
|
|
446
|
+
await asyncio.wait_for(
|
|
447
|
+
loop.run_in_executor(
|
|
448
|
+
executor,
|
|
449
|
+
# NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
|
|
450
|
+
# agent.service.deregister exists on Optional[Consul] union type.
|
|
451
|
+
lambda: client.agent.service.deregister(service_id_str), # type: ignore[union-attr] # NOTE: duck-typed client
|
|
452
|
+
),
|
|
453
|
+
timeout=self._timeout_seconds,
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
# Reset circuit breaker on success
|
|
457
|
+
async with self._circuit_breaker_lock:
|
|
458
|
+
await self._reset_circuit_breaker()
|
|
459
|
+
|
|
460
|
+
logger.info(
|
|
461
|
+
"Service deregistered from Consul",
|
|
462
|
+
extra={
|
|
463
|
+
"service_id": service_id_str,
|
|
464
|
+
"correlation_id": str(correlation_id),
|
|
465
|
+
},
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
except TimeoutError as e:
|
|
469
|
+
async with self._circuit_breaker_lock:
|
|
470
|
+
await self._record_circuit_failure(
|
|
471
|
+
operation="deregister_service",
|
|
472
|
+
correlation_id=correlation_id,
|
|
473
|
+
)
|
|
474
|
+
raise InfraTimeoutError(
|
|
475
|
+
f"Consul deregistration timed out after {self._timeout_seconds}s",
|
|
476
|
+
context=ModelTimeoutErrorContext(
|
|
477
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
478
|
+
operation="deregister_service",
|
|
479
|
+
target_name="consul.discovery",
|
|
480
|
+
correlation_id=correlation_id,
|
|
481
|
+
timeout_seconds=self._timeout_seconds,
|
|
482
|
+
),
|
|
483
|
+
) from e
|
|
484
|
+
|
|
485
|
+
except consul.ConsulException as e:
|
|
486
|
+
async with self._circuit_breaker_lock:
|
|
487
|
+
await self._record_circuit_failure(
|
|
488
|
+
operation="deregister_service",
|
|
489
|
+
correlation_id=correlation_id,
|
|
490
|
+
)
|
|
491
|
+
context = ModelInfraErrorContext(
|
|
492
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
493
|
+
operation="deregister_service",
|
|
494
|
+
target_name="consul.discovery",
|
|
495
|
+
correlation_id=correlation_id,
|
|
496
|
+
)
|
|
497
|
+
raise InfraConnectionError(
|
|
498
|
+
"Consul deregistration failed",
|
|
499
|
+
context=context,
|
|
500
|
+
) from e
|
|
501
|
+
|
|
502
|
+
async def discover_services(
|
|
503
|
+
self,
|
|
504
|
+
query: ModelDiscoveryQuery,
|
|
505
|
+
correlation_id: UUID | None = None,
|
|
506
|
+
) -> ModelDiscoveryResult:
|
|
507
|
+
"""Discover services matching the query criteria.
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
query: Query parameters including service_name, tags,
|
|
511
|
+
and health_filter for filtering services.
|
|
512
|
+
correlation_id: Optional correlation ID for tracing.
|
|
513
|
+
If not provided, uses query.correlation_id, then generates new.
|
|
514
|
+
|
|
515
|
+
Returns:
|
|
516
|
+
ModelDiscoveryResult with list of matching services.
|
|
517
|
+
|
|
518
|
+
Raises:
|
|
519
|
+
InfraConnectionError: If connection to Consul fails.
|
|
520
|
+
InfraTimeoutError: If operation times out.
|
|
521
|
+
InfraUnavailableError: If circuit breaker is open.
|
|
522
|
+
|
|
523
|
+
Note:
|
|
524
|
+
Service IDs from Consul are converted to UUIDs. If the Consul service
|
|
525
|
+
ID is not a valid UUID string, a deterministic UUID5 is generated
|
|
526
|
+
using the ONEX service namespace. This ensures consistent IDs across
|
|
527
|
+
discovery calls while maintaining UUID format compatibility.
|
|
528
|
+
"""
|
|
529
|
+
# Standardized correlation ID handling: explicit > query > generate
|
|
530
|
+
correlation_id = correlation_id or query.correlation_id or uuid4()
|
|
531
|
+
service_name = query.service_name or ""
|
|
532
|
+
tags = query.tags
|
|
533
|
+
start_time = time.monotonic()
|
|
534
|
+
|
|
535
|
+
# Guard against use-after-shutdown
|
|
536
|
+
self._check_not_shutdown("discover_services", correlation_id)
|
|
537
|
+
|
|
538
|
+
# Check circuit breaker
|
|
539
|
+
async with self._circuit_breaker_lock:
|
|
540
|
+
await self._check_circuit_breaker(
|
|
541
|
+
operation="discover_services",
|
|
542
|
+
correlation_id=correlation_id,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
try:
|
|
546
|
+
# Query Consul catalog
|
|
547
|
+
# Client is typed as consul.Consul (duck-typed for injected clients)
|
|
548
|
+
client = self._consul_client
|
|
549
|
+
executor = await self._ensure_executor()
|
|
550
|
+
loop = asyncio.get_running_loop()
|
|
551
|
+
|
|
552
|
+
def _query_services() -> tuple[int, list[dict[str, object]]]:
|
|
553
|
+
# Use health endpoint for service discovery (includes health status)
|
|
554
|
+
tag = tags[0] if tags else None
|
|
555
|
+
# NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
|
|
556
|
+
# health.service exists on Optional[Consul] union type.
|
|
557
|
+
result: tuple[int, list[dict[str, object]]] = client.health.service( # type: ignore[union-attr] # NOTE: duck-typed client
|
|
558
|
+
service_name,
|
|
559
|
+
tag=tag,
|
|
560
|
+
passing=True, # Only healthy services
|
|
561
|
+
)
|
|
562
|
+
return result
|
|
563
|
+
|
|
564
|
+
_, services = await asyncio.wait_for(
|
|
565
|
+
loop.run_in_executor(executor, _query_services),
|
|
566
|
+
timeout=self._timeout_seconds,
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
# Reset circuit breaker on success
|
|
570
|
+
async with self._circuit_breaker_lock:
|
|
571
|
+
await self._reset_circuit_breaker()
|
|
572
|
+
|
|
573
|
+
# Convert to ModelServiceInfo list
|
|
574
|
+
service_infos: list[ModelServiceInfo] = []
|
|
575
|
+
for svc in services:
|
|
576
|
+
# Cast nested dicts for type safety
|
|
577
|
+
svc_data = cast("dict[str, object]", svc.get("Service", {}))
|
|
578
|
+
node_data = cast("dict[str, object]", svc.get("Node", {}))
|
|
579
|
+
svc_id_str = str(svc_data.get("ID", ""))
|
|
580
|
+
svc_name = svc_data.get("Service", "")
|
|
581
|
+
address = svc_data.get("Address", "") or node_data.get("Address", "")
|
|
582
|
+
port_raw = svc_data.get("Port", 0)
|
|
583
|
+
port = int(port_raw) if isinstance(port_raw, int | float | str) else 0
|
|
584
|
+
svc_tags = cast("list[str]", svc_data.get("Tags", []))
|
|
585
|
+
svc_meta = cast("dict[str, str]", svc_data.get("Meta", {}))
|
|
586
|
+
|
|
587
|
+
if svc_id_str and svc_name and address and port:
|
|
588
|
+
# Convert Consul service ID string to UUID
|
|
589
|
+
# Try to parse as UUID, otherwise generate deterministic UUID from string
|
|
590
|
+
try:
|
|
591
|
+
svc_id = UUID(svc_id_str)
|
|
592
|
+
except ValueError:
|
|
593
|
+
# Non-UUID service ID from Consul - generate deterministic UUID5
|
|
594
|
+
# using ONEX-specific namespace to avoid collisions
|
|
595
|
+
logger.warning(
|
|
596
|
+
"Non-UUID service ID from Consul, using deterministic UUID5 conversion",
|
|
597
|
+
extra={
|
|
598
|
+
"original_id": svc_id_str,
|
|
599
|
+
"correlation_id": str(correlation_id),
|
|
600
|
+
},
|
|
601
|
+
)
|
|
602
|
+
svc_id = uuid5(NAMESPACE_ONEX_SERVICE, svc_id_str)
|
|
603
|
+
|
|
604
|
+
service_infos.append(
|
|
605
|
+
ModelServiceInfo(
|
|
606
|
+
service_id=svc_id,
|
|
607
|
+
service_name=str(svc_name),
|
|
608
|
+
address=str(address),
|
|
609
|
+
port=port,
|
|
610
|
+
tags=tuple(svc_tags or []),
|
|
611
|
+
health_status=EnumHealthStatus.HEALTHY,
|
|
612
|
+
metadata=svc_meta or {},
|
|
613
|
+
registered_at=datetime.now(UTC),
|
|
614
|
+
correlation_id=correlation_id,
|
|
615
|
+
)
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
619
|
+
|
|
620
|
+
logger.info(
|
|
621
|
+
"Service discovery completed",
|
|
622
|
+
extra={
|
|
623
|
+
"service_name": service_name,
|
|
624
|
+
"found_count": len(service_infos),
|
|
625
|
+
"duration_ms": duration_ms,
|
|
626
|
+
"correlation_id": str(correlation_id),
|
|
627
|
+
},
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
return ModelDiscoveryResult(
|
|
631
|
+
success=True,
|
|
632
|
+
services=tuple(service_infos),
|
|
633
|
+
duration_ms=duration_ms,
|
|
634
|
+
backend_type=self.handler_type,
|
|
635
|
+
correlation_id=correlation_id,
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
except TimeoutError as e:
|
|
639
|
+
async with self._circuit_breaker_lock:
|
|
640
|
+
await self._record_circuit_failure(
|
|
641
|
+
operation="discover_services",
|
|
642
|
+
correlation_id=correlation_id,
|
|
643
|
+
)
|
|
644
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
645
|
+
raise InfraTimeoutError(
|
|
646
|
+
f"Consul discovery timed out after {self._timeout_seconds}s",
|
|
647
|
+
context=ModelTimeoutErrorContext(
|
|
648
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
649
|
+
operation="discover_services",
|
|
650
|
+
target_name="consul.discovery",
|
|
651
|
+
correlation_id=correlation_id,
|
|
652
|
+
timeout_seconds=self._timeout_seconds,
|
|
653
|
+
),
|
|
654
|
+
) from e
|
|
655
|
+
|
|
656
|
+
except consul.ConsulException as e:
|
|
657
|
+
async with self._circuit_breaker_lock:
|
|
658
|
+
await self._record_circuit_failure(
|
|
659
|
+
operation="discover_services",
|
|
660
|
+
correlation_id=correlation_id,
|
|
661
|
+
)
|
|
662
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
663
|
+
context = ModelInfraErrorContext(
|
|
664
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
665
|
+
operation="discover_services",
|
|
666
|
+
target_name="consul.discovery",
|
|
667
|
+
correlation_id=correlation_id,
|
|
668
|
+
)
|
|
669
|
+
raise InfraConnectionError(
|
|
670
|
+
"Consul discovery failed",
|
|
671
|
+
context=context,
|
|
672
|
+
) from e
|
|
673
|
+
|
|
674
|
+
async def list_all_services(
|
|
675
|
+
self,
|
|
676
|
+
tag_filter: str | None = None,
|
|
677
|
+
correlation_id: UUID | None = None,
|
|
678
|
+
) -> dict[str, list[str]]:
|
|
679
|
+
"""List all registered service names with their tags.
|
|
680
|
+
|
|
681
|
+
Uses Consul catalog API (/v1/catalog/services) to retrieve all
|
|
682
|
+
service names registered in the Consul catalog, along with their tags.
|
|
683
|
+
|
|
684
|
+
Args:
|
|
685
|
+
tag_filter: Optional tag to filter services by. If provided,
|
|
686
|
+
only services with this tag will be returned.
|
|
687
|
+
correlation_id: Optional correlation ID for tracing.
|
|
688
|
+
|
|
689
|
+
Returns:
|
|
690
|
+
Dictionary mapping service names to their list of tags.
|
|
691
|
+
Example: {"my-service": ["web", "api"], "other-service": ["db"]}
|
|
692
|
+
|
|
693
|
+
Raises:
|
|
694
|
+
InfraConnectionError: If connection to Consul fails.
|
|
695
|
+
InfraTimeoutError: If operation times out.
|
|
696
|
+
InfraUnavailableError: If circuit breaker is open.
|
|
697
|
+
"""
|
|
698
|
+
correlation_id = correlation_id or uuid4()
|
|
699
|
+
start_time = time.monotonic()
|
|
700
|
+
|
|
701
|
+
# Guard against use-after-shutdown
|
|
702
|
+
self._check_not_shutdown("list_all_services", correlation_id)
|
|
703
|
+
|
|
704
|
+
# Check circuit breaker
|
|
705
|
+
async with self._circuit_breaker_lock:
|
|
706
|
+
await self._check_circuit_breaker(
|
|
707
|
+
operation="list_all_services",
|
|
708
|
+
correlation_id=correlation_id,
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
try:
|
|
712
|
+
# Query Consul catalog for all services
|
|
713
|
+
client = self._consul_client
|
|
714
|
+
executor = await self._ensure_executor()
|
|
715
|
+
loop = asyncio.get_running_loop()
|
|
716
|
+
|
|
717
|
+
def _query_catalog() -> tuple[int, dict[str, list[str]]]:
|
|
718
|
+
# NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
|
|
719
|
+
# catalog.services exists on Optional[Consul] union type.
|
|
720
|
+
result: tuple[int, dict[str, list[str]]] = client.catalog.services() # type: ignore[union-attr] # NOTE: duck-typed client
|
|
721
|
+
return result
|
|
722
|
+
|
|
723
|
+
_, services = await asyncio.wait_for(
|
|
724
|
+
loop.run_in_executor(executor, _query_catalog),
|
|
725
|
+
timeout=self._timeout_seconds,
|
|
726
|
+
)
|
|
727
|
+
|
|
728
|
+
# Reset circuit breaker on success
|
|
729
|
+
async with self._circuit_breaker_lock:
|
|
730
|
+
await self._reset_circuit_breaker()
|
|
731
|
+
|
|
732
|
+
# Apply tag filter if provided
|
|
733
|
+
if tag_filter is not None:
|
|
734
|
+
services = {
|
|
735
|
+
name: tags for name, tags in services.items() if tag_filter in tags
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
739
|
+
|
|
740
|
+
logger.info(
|
|
741
|
+
"Listed all services from Consul catalog",
|
|
742
|
+
extra={
|
|
743
|
+
"service_count": len(services),
|
|
744
|
+
"tag_filter": tag_filter,
|
|
745
|
+
"duration_ms": duration_ms,
|
|
746
|
+
"correlation_id": str(correlation_id),
|
|
747
|
+
},
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
return services
|
|
751
|
+
|
|
752
|
+
except TimeoutError as e:
|
|
753
|
+
async with self._circuit_breaker_lock:
|
|
754
|
+
await self._record_circuit_failure(
|
|
755
|
+
operation="list_all_services",
|
|
756
|
+
correlation_id=correlation_id,
|
|
757
|
+
)
|
|
758
|
+
raise InfraTimeoutError(
|
|
759
|
+
f"Consul catalog query timed out after {self._timeout_seconds}s",
|
|
760
|
+
context=ModelTimeoutErrorContext(
|
|
761
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
762
|
+
operation="list_all_services",
|
|
763
|
+
target_name="consul.discovery",
|
|
764
|
+
correlation_id=correlation_id,
|
|
765
|
+
timeout_seconds=self._timeout_seconds,
|
|
766
|
+
),
|
|
767
|
+
) from e
|
|
768
|
+
|
|
769
|
+
except consul.ConsulException as e:
|
|
770
|
+
async with self._circuit_breaker_lock:
|
|
771
|
+
await self._record_circuit_failure(
|
|
772
|
+
operation="list_all_services",
|
|
773
|
+
correlation_id=correlation_id,
|
|
774
|
+
)
|
|
775
|
+
context = ModelInfraErrorContext(
|
|
776
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
777
|
+
operation="list_all_services",
|
|
778
|
+
target_name="consul.discovery",
|
|
779
|
+
correlation_id=correlation_id,
|
|
780
|
+
)
|
|
781
|
+
raise InfraConnectionError(
|
|
782
|
+
"Consul catalog query failed",
|
|
783
|
+
context=context,
|
|
784
|
+
) from e
|
|
785
|
+
|
|
786
|
+
async def get_all_service_instances(
|
|
787
|
+
self,
|
|
788
|
+
service_name: str,
|
|
789
|
+
include_unhealthy: bool = True,
|
|
790
|
+
correlation_id: UUID | None = None,
|
|
791
|
+
) -> list[ModelServiceInfo]:
|
|
792
|
+
"""Get all instances of a service, optionally including unhealthy ones.
|
|
793
|
+
|
|
794
|
+
Uses Consul health API (/v1/health/service/<name>) to retrieve all
|
|
795
|
+
instances of a service with their health status information.
|
|
796
|
+
|
|
797
|
+
Args:
|
|
798
|
+
service_name: Name of the service to query.
|
|
799
|
+
include_unhealthy: If True, return all instances regardless of health.
|
|
800
|
+
If False, only return healthy (passing) instances.
|
|
801
|
+
correlation_id: Optional correlation ID for tracing.
|
|
802
|
+
|
|
803
|
+
Returns:
|
|
804
|
+
List of ModelServiceInfo objects representing service instances,
|
|
805
|
+
including health status, health output, and last check timestamp.
|
|
806
|
+
|
|
807
|
+
Raises:
|
|
808
|
+
InfraConnectionError: If connection to Consul fails.
|
|
809
|
+
InfraTimeoutError: If operation times out.
|
|
810
|
+
InfraUnavailableError: If circuit breaker is open.
|
|
811
|
+
"""
|
|
812
|
+
correlation_id = correlation_id or uuid4()
|
|
813
|
+
start_time = time.monotonic()
|
|
814
|
+
|
|
815
|
+
# Guard against use-after-shutdown
|
|
816
|
+
self._check_not_shutdown("get_all_service_instances", correlation_id)
|
|
817
|
+
|
|
818
|
+
# Check circuit breaker
|
|
819
|
+
async with self._circuit_breaker_lock:
|
|
820
|
+
await self._check_circuit_breaker(
|
|
821
|
+
operation="get_all_service_instances",
|
|
822
|
+
correlation_id=correlation_id,
|
|
823
|
+
)
|
|
824
|
+
|
|
825
|
+
try:
|
|
826
|
+
# Query Consul health API for service instances
|
|
827
|
+
client = self._consul_client
|
|
828
|
+
executor = await self._ensure_executor()
|
|
829
|
+
loop = asyncio.get_running_loop()
|
|
830
|
+
|
|
831
|
+
def _query_health() -> tuple[int, list[dict[str, object]]]:
|
|
832
|
+
# NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
|
|
833
|
+
# health.service exists on Optional[Consul] union type.
|
|
834
|
+
# When passing=False, we get ALL instances
|
|
835
|
+
# When passing=True, we only get healthy instances
|
|
836
|
+
result: tuple[int, list[dict[str, object]]] = client.health.service( # type: ignore[union-attr] # NOTE: duck-typed client
|
|
837
|
+
service_name,
|
|
838
|
+
passing=not include_unhealthy,
|
|
839
|
+
)
|
|
840
|
+
return result
|
|
841
|
+
|
|
842
|
+
_, services = await asyncio.wait_for(
|
|
843
|
+
loop.run_in_executor(executor, _query_health),
|
|
844
|
+
timeout=self._timeout_seconds,
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
# Reset circuit breaker on success
|
|
848
|
+
async with self._circuit_breaker_lock:
|
|
849
|
+
await self._reset_circuit_breaker()
|
|
850
|
+
|
|
851
|
+
# Convert to ModelServiceInfo list with full health details
|
|
852
|
+
service_infos: list[ModelServiceInfo] = []
|
|
853
|
+
for svc in services:
|
|
854
|
+
# Cast nested dicts for type safety
|
|
855
|
+
svc_data = cast("dict[str, object]", svc.get("Service", {}))
|
|
856
|
+
node_data = cast("dict[str, object]", svc.get("Node", {}))
|
|
857
|
+
checks = cast("list[dict[str, object]]", svc.get("Checks", []))
|
|
858
|
+
|
|
859
|
+
svc_id_str = str(svc_data.get("ID", ""))
|
|
860
|
+
svc_name = svc_data.get("Service", "")
|
|
861
|
+
address = svc_data.get("Address", "") or node_data.get("Address", "")
|
|
862
|
+
port_raw = svc_data.get("Port", 0)
|
|
863
|
+
port = int(port_raw) if isinstance(port_raw, int | float | str) else 0
|
|
864
|
+
svc_tags = cast("list[str]", svc_data.get("Tags", []))
|
|
865
|
+
svc_meta = cast("dict[str, str]", svc_data.get("Meta", {}))
|
|
866
|
+
|
|
867
|
+
if svc_id_str and svc_name and address and port:
|
|
868
|
+
# Convert Consul service ID string to UUID
|
|
869
|
+
try:
|
|
870
|
+
svc_id = UUID(svc_id_str)
|
|
871
|
+
except ValueError:
|
|
872
|
+
# Non-UUID service ID from Consul - generate deterministic UUID5
|
|
873
|
+
logger.warning(
|
|
874
|
+
"Non-UUID service ID from Consul, using deterministic UUID5 conversion",
|
|
875
|
+
extra={
|
|
876
|
+
"original_id": svc_id_str,
|
|
877
|
+
"correlation_id": str(correlation_id),
|
|
878
|
+
},
|
|
879
|
+
)
|
|
880
|
+
svc_id = uuid5(NAMESPACE_ONEX_SERVICE, svc_id_str)
|
|
881
|
+
|
|
882
|
+
# Determine health status from checks
|
|
883
|
+
health_status = EnumHealthStatus.UNKNOWN
|
|
884
|
+
health_output: str | None = None
|
|
885
|
+
last_check_at: datetime | None = None
|
|
886
|
+
|
|
887
|
+
# Find the service-specific health check
|
|
888
|
+
for check in checks:
|
|
889
|
+
check_service_id = check.get("ServiceID", "")
|
|
890
|
+
if check_service_id == svc_id_str or not check_service_id:
|
|
891
|
+
status_str = str(check.get("Status", "")).lower()
|
|
892
|
+
if status_str == "passing":
|
|
893
|
+
health_status = EnumHealthStatus.HEALTHY
|
|
894
|
+
elif status_str in ("critical", "warning"):
|
|
895
|
+
health_status = EnumHealthStatus.UNHEALTHY
|
|
896
|
+
else:
|
|
897
|
+
health_status = EnumHealthStatus.UNKNOWN
|
|
898
|
+
|
|
899
|
+
health_output = str(check.get("Output", "")) or None
|
|
900
|
+
|
|
901
|
+
# Parse CreateIndex as a proxy for last check time
|
|
902
|
+
# (Consul doesn't directly expose last check time in this API)
|
|
903
|
+
# In production, you might use the check's CreateIndex or ModifyIndex
|
|
904
|
+
break
|
|
905
|
+
|
|
906
|
+
service_infos.append(
|
|
907
|
+
ModelServiceInfo(
|
|
908
|
+
service_id=svc_id,
|
|
909
|
+
service_name=str(svc_name),
|
|
910
|
+
address=str(address),
|
|
911
|
+
port=port,
|
|
912
|
+
tags=tuple(svc_tags or []),
|
|
913
|
+
health_status=health_status,
|
|
914
|
+
health_output=health_output,
|
|
915
|
+
last_check_at=last_check_at,
|
|
916
|
+
metadata=svc_meta or {},
|
|
917
|
+
registered_at=datetime.now(UTC),
|
|
918
|
+
correlation_id=correlation_id,
|
|
919
|
+
)
|
|
920
|
+
)
|
|
921
|
+
|
|
922
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
923
|
+
|
|
924
|
+
logger.info(
|
|
925
|
+
"Retrieved service instances from Consul",
|
|
926
|
+
extra={
|
|
927
|
+
"service_name": service_name,
|
|
928
|
+
"instance_count": len(service_infos),
|
|
929
|
+
"include_unhealthy": include_unhealthy,
|
|
930
|
+
"duration_ms": duration_ms,
|
|
931
|
+
"correlation_id": str(correlation_id),
|
|
932
|
+
},
|
|
933
|
+
)
|
|
934
|
+
|
|
935
|
+
return service_infos
|
|
936
|
+
|
|
937
|
+
except TimeoutError as e:
|
|
938
|
+
async with self._circuit_breaker_lock:
|
|
939
|
+
await self._record_circuit_failure(
|
|
940
|
+
operation="get_all_service_instances",
|
|
941
|
+
correlation_id=correlation_id,
|
|
942
|
+
)
|
|
943
|
+
raise InfraTimeoutError(
|
|
944
|
+
f"Consul health query timed out after {self._timeout_seconds}s",
|
|
945
|
+
context=ModelTimeoutErrorContext(
|
|
946
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
947
|
+
operation="get_all_service_instances",
|
|
948
|
+
target_name="consul.discovery",
|
|
949
|
+
correlation_id=correlation_id,
|
|
950
|
+
timeout_seconds=self._timeout_seconds,
|
|
951
|
+
),
|
|
952
|
+
) from e
|
|
953
|
+
|
|
954
|
+
except consul.ConsulException as e:
|
|
955
|
+
async with self._circuit_breaker_lock:
|
|
956
|
+
await self._record_circuit_failure(
|
|
957
|
+
operation="get_all_service_instances",
|
|
958
|
+
correlation_id=correlation_id,
|
|
959
|
+
)
|
|
960
|
+
context = ModelInfraErrorContext(
|
|
961
|
+
transport_type=EnumInfraTransportType.CONSUL,
|
|
962
|
+
operation="get_all_service_instances",
|
|
963
|
+
target_name="consul.discovery",
|
|
964
|
+
correlation_id=correlation_id,
|
|
965
|
+
)
|
|
966
|
+
raise InfraConnectionError(
|
|
967
|
+
"Consul health query failed",
|
|
968
|
+
context=context,
|
|
969
|
+
) from e
|
|
970
|
+
|
|
971
|
+
async def health_check(
|
|
972
|
+
self,
|
|
973
|
+
correlation_id: UUID | None = None,
|
|
974
|
+
) -> ModelServiceDiscoveryHealthCheckResult:
|
|
975
|
+
"""Perform a health check on the Consul connection.
|
|
976
|
+
|
|
977
|
+
Args:
|
|
978
|
+
correlation_id: Optional correlation ID for tracing.
|
|
979
|
+
|
|
980
|
+
Returns:
|
|
981
|
+
ModelServiceDiscoveryHealthCheckResult with health status information.
|
|
982
|
+
"""
|
|
983
|
+
correlation_id = correlation_id or uuid4()
|
|
984
|
+
start_time = time.monotonic()
|
|
985
|
+
|
|
986
|
+
# Guard against use-after-shutdown
|
|
987
|
+
self._check_not_shutdown("health_check", correlation_id)
|
|
988
|
+
|
|
989
|
+
try:
|
|
990
|
+
# Client is typed as consul.Consul (duck-typed for injected clients)
|
|
991
|
+
client = self._consul_client
|
|
992
|
+
executor = await self._ensure_executor()
|
|
993
|
+
loop = asyncio.get_running_loop()
|
|
994
|
+
leader = await asyncio.wait_for(
|
|
995
|
+
loop.run_in_executor(
|
|
996
|
+
executor,
|
|
997
|
+
# NOTE: client is duck-typed ProtocolConsulClient; mypy cannot verify
|
|
998
|
+
# status.leader exists on Optional[Consul] union type.
|
|
999
|
+
lambda: client.status.leader(), # type: ignore[union-attr] # NOTE: duck-typed client
|
|
1000
|
+
),
|
|
1001
|
+
timeout=5.0, # Short timeout for health check
|
|
1002
|
+
)
|
|
1003
|
+
|
|
1004
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
1005
|
+
|
|
1006
|
+
return ModelServiceDiscoveryHealthCheckResult(
|
|
1007
|
+
healthy=True,
|
|
1008
|
+
backend_type=self.handler_type,
|
|
1009
|
+
latency_ms=duration_ms,
|
|
1010
|
+
reason="ok",
|
|
1011
|
+
details=ModelServiceDiscoveryHealthCheckDetails(
|
|
1012
|
+
agent_address=f"{self._consul_host}:{self._consul_port}",
|
|
1013
|
+
leader=str(leader) if leader else None,
|
|
1014
|
+
),
|
|
1015
|
+
correlation_id=correlation_id,
|
|
1016
|
+
)
|
|
1017
|
+
|
|
1018
|
+
except Exception as e:
|
|
1019
|
+
duration_ms = (time.monotonic() - start_time) * 1000
|
|
1020
|
+
return ModelServiceDiscoveryHealthCheckResult(
|
|
1021
|
+
healthy=False,
|
|
1022
|
+
backend_type=self.handler_type,
|
|
1023
|
+
latency_ms=duration_ms,
|
|
1024
|
+
reason=f"Health check failed: {type(e).__name__}",
|
|
1025
|
+
error_type=type(e).__name__,
|
|
1026
|
+
details=ModelServiceDiscoveryHealthCheckDetails(
|
|
1027
|
+
agent_address=f"{self._consul_host}:{self._consul_port}",
|
|
1028
|
+
),
|
|
1029
|
+
correlation_id=correlation_id,
|
|
1030
|
+
)
|
|
1031
|
+
|
|
1032
|
+
async def shutdown(self) -> None:
|
|
1033
|
+
"""Shutdown the handler and release resources.
|
|
1034
|
+
|
|
1035
|
+
Cleans up:
|
|
1036
|
+
- Thread pool executor (always)
|
|
1037
|
+
- Consul client (only if handler owns it)
|
|
1038
|
+
"""
|
|
1039
|
+
if self._executor is not None:
|
|
1040
|
+
self._executor.shutdown(wait=True)
|
|
1041
|
+
self._executor = None
|
|
1042
|
+
|
|
1043
|
+
# Clean up owned Consul client
|
|
1044
|
+
if self._owns_client:
|
|
1045
|
+
self._consul_client = None
|
|
1046
|
+
self._owns_client = False
|
|
1047
|
+
|
|
1048
|
+
logger.info("HandlerServiceDiscoveryConsul shutdown complete")
|
|
1049
|
+
|
|
1050
|
+
|
|
1051
|
+
__all__ = ["HandlerServiceDiscoveryConsul"]
|