diff --git a/packages/models-library/src/models_library/services_metadata_published.py b/packages/models-library/src/models_library/services_metadata_published.py index 51fba05b7f42..ebbc4a30a262 100644 --- a/packages/models-library/src/models_library/services_metadata_published.py +++ b/packages/models-library/src/models_library/services_metadata_published.py @@ -149,6 +149,22 @@ class ServiceMetaDataPublished(ServiceKeyVersion, ServiceBaseDisplay): alias="progress_regexp", description="regexp pattern for detecting computational service's progress", ) + strip_path: str | None = Field( + None, + description="??? Mystery Pokemon Missingo", + ) + inputs_path: str | None = Field( + None, + description="if this is present, the service is a modern style dv2 dynamic service", + ) + outputs_path: str | None = Field( + None, + description="if this is present, the service is a modern style dv2 dynamic service", + ) + state_paths: list[str] = Field( + None, + description="if this is present, the service is a modern style dv2 dynamic service", + ) # SEE https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys image_digest: str | None = Field( diff --git a/packages/postgres-database/src/simcore_postgres_database/migration/versions/354b5d921312_introduce_legacy_service_identification_.py b/packages/postgres-database/src/simcore_postgres_database/migration/versions/354b5d921312_introduce_legacy_service_identification_.py new file mode 100644 index 000000000000..5726cfd88e2e --- /dev/null +++ b/packages/postgres-database/src/simcore_postgres_database/migration/versions/354b5d921312_introduce_legacy_service_identification_.py @@ -0,0 +1,36 @@ +"""Introduce legacy service identification column + +Revision ID: 354b5d921312 +Revises: cf8f743fd0b7 +Create Date: 2025-04-15 13:53:44.404695+00:00 + +""" + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "354b5d921312" +down_revision = "cf8f743fd0b7" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "services_meta_data", + sa.Column( + "is_classic_dynamic_service", + sa.Boolean(), + server_default=sa.text("false"), + nullable=False, + ), + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column("services_meta_data", "is_classic_dynamic_service") + # ### end Alembic commands ### diff --git a/packages/postgres-database/src/simcore_postgres_database/models/services.py b/packages/postgres-database/src/simcore_postgres_database/models/services.py index ec12f0f3ca87..1759dc6e9d93 100644 --- a/packages/postgres-database/src/simcore_postgres_database/models/services.py +++ b/packages/postgres-database/src/simcore_postgres_database/models/services.py @@ -119,6 +119,13 @@ doc="Timestamp when the service is retired (editable)." "A fixed time before this date, service is marked as deprecated.", ), + sa.Column( + "is_classic_dynamic_service", + sa.Boolean, + nullable=False, + server_default=sa.false(), + doc="Is the service a legacy-style classic dynamic service? This is false for comp-services and special items like filepickers.", + ), sa.PrimaryKeyConstraint("key", "version", name="services_meta_data_pk"), ) diff --git a/services/catalog/src/simcore_service_catalog/core/background_tasks.py b/services/catalog/src/simcore_service_catalog/core/background_tasks.py index fea4c6537d8a..5cdff6b8129e 100644 --- a/services/catalog/src/simcore_service_catalog/core/background_tasks.py +++ b/services/catalog/src/simcore_service_catalog/core/background_tasks.py @@ -89,9 +89,16 @@ def _by_version(t: tuple[ServiceKey, ServiceVersion]) -> Version: ) # set the service in the DB + _is_classic_dynamic_service: bool = ( + service_metadata.inputs_path is None + and service_metadata.outputs_path is None + and service_metadata.state_paths is None + ) await services_repo.create_or_update_service( ServiceMetaDataDBCreate( - **service_metadata.model_dump(exclude_unset=True), owner=owner_gid + **service_metadata.model_dump(exclude_unset=True, exclude={""}), + owner=owner_gid, + _is_classic_dynamic_service=_is_classic_dynamic_service, ), service_access_rights, ) diff --git a/services/catalog/src/simcore_service_catalog/models/services_db.py b/services/catalog/src/simcore_service_catalog/models/services_db.py index 2ad800d2b44d..f01688ad116e 100644 --- a/services/catalog/src/simcore_service_catalog/models/services_db.py +++ b/services/catalog/src/simcore_service_catalog/models/services_db.py @@ -118,6 +118,7 @@ class ServiceMetaDataDBCreate(BaseModel): # lifecycle deprecated: datetime | None = None + _is_classic_dynamic_service: bool = False @staticmethod def _update_json_schema_extra(schema: JsonDict) -> None: diff --git a/services/director/src/simcore_service_director/api/rest/_services.py b/services/director/src/simcore_service_director/api/rest/_services.py index a82699331ce0..b5794e24af99 100644 --- a/services/director/src/simcore_service_director/api/rest/_services.py +++ b/services/director/src/simcore_service_director/api/rest/_services.py @@ -57,7 +57,7 @@ async def list_services( the_app, registry_proxy.ServiceType.DYNAMIC ) # NOTE: the validation is done in the catalog. This entrypoint IS and MUST BE only used by the catalog!! - # NOTE2: the catalog will directly talk to the registry see case #2165 [https://github.com/ITISFoundation/osparc-simcore/issues/2165] + # NOTE2: the catalog might eventually directly talk to the registry see case #2165 [https://github.com/ITISFoundation/osparc-simcore/issues/2165] # services = node_validator.validate_nodes(services) return Envelope[list[dict[str, Any]]](data=services) except RegistryConnectionError as err: diff --git a/services/director/src/simcore_service_director/registry_proxy.py b/services/director/src/simcore_service_director/registry_proxy.py index f90373bb2f18..01bf7d681c58 100644 --- a/services/director/src/simcore_service_director/registry_proxy.py +++ b/services/director/src/simcore_service_director/registry_proxy.py @@ -410,12 +410,23 @@ async def get_image_details( if not labels: return image_details for key in labels: - if not key.startswith("io.simcore."): + if not key.startswith("io.simcore.") and not key.startswith( + "simcore.service." + ): # Keeping "simcore.service." adds additonally input_paths, output_paths, state_paths continue try: label_data = json.loads(labels[key]) for label_key in label_data: + if ( + isinstance(label_key, dict) + or isinstance(label_data, list) + or isinstance(label_data, str) + ): # Dicts from "simcore.service." docker image labels are omitted. + continue image_details[label_key] = label_data[label_key] + except json.JSONDecodeError: + label_data = [] # Set to empty list if the value is not JSON + pass except json.decoder.JSONDecodeError: logging.exception( "Error while decoding json formatted data from %s:%s", diff --git a/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/modules/long_running_tasks.py b/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/modules/long_running_tasks.py index 83458fd9d1e0..a1c0dfead7ae 100644 --- a/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/modules/long_running_tasks.py +++ b/services/dynamic-sidecar/src/simcore_service_dynamic_sidecar/modules/long_running_tasks.py @@ -328,7 +328,7 @@ async def _send_resource_tracking_stop(platform_status: SimcorePlatformStatus): progress.update(message="done", percent=ProgressPercent(0.99)) -def _get_satate_folders_size(paths: list[Path]) -> int: +def _get_state_folders_size(paths: list[Path]) -> int: total_size: int = 0 for path in paths: for file in path.rglob("*"): @@ -406,7 +406,7 @@ async def task_restore_state( ) progress.update(message="state restored", percent=ProgressPercent(0.99)) - return _get_satate_folders_size(state_paths) + return _get_state_folders_size(state_paths) async def _save_state_folder( @@ -471,7 +471,7 @@ async def task_save_state( await post_sidecar_log_message(app, "Finished state saving", log_level=logging.INFO) progress.update(message="finished state saving", percent=ProgressPercent(0.99)) - return _get_satate_folders_size(state_paths) + return _get_state_folders_size(state_paths) async def task_ports_inputs_pull(