diff --git a/Changelog.md b/Changelog.md index 7300e570c30194f361316b08700133eefa8354fd..b613678bb97b126c573cee17bf94d1f53be693ab 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ # Changelog +# [3.5] - 2025-05-12 +- Add a new CLI command for interacting with the task scheduler. +- Fix a bug in the subscription validation task. + # [3.4] - 2025-05-08 - Fix some bugs in the nightly validation schedule. diff --git a/gso/__init__.py b/gso/__init__.py index 8ed08f8e1716e9f6e04d6cd244253b43f01875d8..99f1d386081a807a5c7b34527ba890cafe7aab1e 100644 --- a/gso/__init__.py +++ b/gso/__init__.py @@ -65,10 +65,11 @@ def init_gso_app() -> OrchestratorCore: def init_cli_app() -> typer.Typer: """Initialise GSO as a CLI application.""" - from gso.cli import imports, netbox # noqa: PLC0415 + from gso.cli import imports, netbox, schedule # noqa: PLC0415 cli_app.add_typer(imports.app, name="import-cli") cli_app.add_typer(netbox.app, name="netbox-cli") + cli_app.add_typer(schedule.app, name="schedule-cli") return cli_app() diff --git a/gso/cli/schedule.py b/gso/cli/schedule.py new file mode 100644 index 0000000000000000000000000000000000000000..b0a29c0560948cb75bb5a00572751c32d667f265 --- /dev/null +++ b/gso/cli/schedule.py @@ -0,0 +1,46 @@ +"""CLI for interacting with the task scheduler. Only supports running a single task.""" + +# <!-- vale off --> +# Copyright 2019-2020 SURF. +# Copyright 2025 GÉANT Vereniging. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# <!-- vale on --> + +import logging +from collections.abc import Callable + +import typer + +from gso.schedules.clean_old_tasks import clean_old_tasks +from gso.schedules.send_email_notifications import send_email_notifications +from gso.schedules.validate_products import validate_products +from gso.schedules.validate_subscriptions import validate_subscriptions + +logger = logging.getLogger(__name__) + +app: typer.Typer = typer.Typer() + +ALL_SCHEDULES: list[Callable] = [ + clean_old_tasks, + send_email_notifications, + validate_products, + validate_subscriptions, +] + + +@app.command() +def run_task(task_name: str) -> None: + """Force the execution of a task by name.""" + for s in ALL_SCHEDULES: + if task_name == s.__name__: + s() diff --git a/gso/schedules/validate_subscriptions.py b/gso/schedules/validate_subscriptions.py index 7323b6086b948201da4a0f5b0ee1574030646076..db3573802cd6d9fb29438068fa3737a57f0b4bf9 100644 --- a/gso/schedules/validate_subscriptions.py +++ b/gso/schedules/validate_subscriptions.py @@ -37,26 +37,43 @@ def validate_subscriptions() -> None: return for subscription in subscriptions: - validation_workflow = None - + found_a_validation_workflow = False for workflow in subscription.product.workflows: if workflow.target == Target.SYSTEM and workflow.name.startswith("validate_"): validation_workflow = workflow.name - - if validation_workflow: + found_a_validation_workflow = True validation_workflow_usable = (subscription.status in TARGET_DEFAULT_USABLE_MAP[Target.SYSTEM]) and ( - subscription.insync or (workflow in WF_USABLE_WHILE_OUT_OF_SYNC) + subscription.insync or (validation_workflow in WF_USABLE_WHILE_OUT_OF_SYNC) ) if validation_workflow_usable: - json = [{"subscription_id": str(subscription.subscription_id)}] + logger.info( + "Found a usable validation workflow, scheduling task.", + product=subscription.product.name, + subscription_id=subscription.subscription_id, + subscription_description=subscription.description, + workflow=validation_workflow, + ) + json = [{"subscription_id": str(subscription.subscription_id)}] validate_func = get_execution_context()["validate"] validate_func(validation_workflow, json=json) - if not validation_workflow: + else: + logger.info( + "Validation workflow is not usable on this subscription instance", + product=subscription.product.name, + subscription_id=subscription.subscription_id, + subscription_description=subscription.description, + status=subscription.status, + insync=subscription.insync, + workflow=validation_workflow, + ) + + if not found_a_validation_workflow: logger.warning( "SubscriptionTable has no validation workflow", - subscription=subscription, product=subscription.product.name, + subscription_id=subscription.subscription_id, + subscription_description=subscription.description, ) diff --git a/setup.py b/setup.py index 58344ee060832df1d14ea56f7d86e56d1ab824a0..9cdc16edc8f47b2b6890bb3cd488a566c5315529 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import find_packages, setup setup( name="geant-service-orchestrator", - version="3.4", + version="3.5", author="GÉANT Orchestration and Automation Team", author_email="goat@geant.org", description="GÉANT Service Orchestrator",