From 545e066ac23627185cb3abdef437008b3e1b08d9 Mon Sep 17 00:00:00 2001 From: Zoheb Shaikh Date: Thu, 27 Feb 2025 15:25:33 +0000 Subject: [PATCH] Add scratch endpoint --- helm/blueapi/templates/statefulset.yaml | 8 ++++---- helm/blueapi/values.yaml | 4 ---- src/blueapi/cli/scratch.py | 21 +++++++++++++++++++++ src/blueapi/client/rest.py | 3 +++ src/blueapi/service/interface.py | 10 +++++++++- src/blueapi/service/main.py | 7 +++++++ src/blueapi/service/model.py | 12 ++++++++++++ 7 files changed, 56 insertions(+), 9 deletions(-) diff --git a/helm/blueapi/templates/statefulset.yaml b/helm/blueapi/templates/statefulset.yaml index 6d35761b7..b4fb42b6e 100644 --- a/helm/blueapi/templates/statefulset.yaml +++ b/helm/blueapi/templates/statefulset.yaml @@ -50,7 +50,7 @@ spec: sizeLimit: 5Gi {{- end }} initContainers: - {{- if .Values.scratchHostPath }} + {{- if .Values.initContainer.scratch.root }} - name: setup-scratch image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} @@ -69,7 +69,7 @@ spec: mountPath: "/config" readOnly: true - name: scratch-host - mountPath: {{ .Values.worker.scratch.root }} + mountPath: {{ .Values.initContainer.scratch.root }} mountPropagation: HostToContainer - name: venv mountPath: /artefacts @@ -86,9 +86,9 @@ spec: - name: worker-config mountPath: "/config" readOnly: true - {{- if .Values.scratchHostPath }} + {{- if .Values.initContainer.scratch.root }} - name: scratch-host - mountPath: {{ .Values.worker.scratch.root }} + mountPath: {{ .Values.initContainer.scratch.root }} mountPropagation: HostToContainer - name: venv mountPath: /venv diff --git a/helm/blueapi/values.yaml b/helm/blueapi/values.yaml index 607cc4fa9..74b1d0288 100644 --- a/helm/blueapi/values.yaml +++ b/helm/blueapi/values.yaml @@ -119,7 +119,3 @@ initContainer: repositories: [] # - name: "dodal" # remote_url: https://github.com/DiamondLightSource/dodal.git - -# Mount path for scratch area from host machine, setting -# this effectively enables scratch area management -scratchHostPath: "" # example: /usr/local/blueapi-software-scratch diff --git a/src/blueapi/cli/scratch.py b/src/blueapi/cli/scratch.py index 4c155da52..f477ad881 100644 --- a/src/blueapi/cli/scratch.py +++ b/src/blueapi/cli/scratch.py @@ -8,6 +8,7 @@ from git import Repo from blueapi.config import ScratchConfig +from blueapi.service.model import ScratchResponse from blueapi.utils import get_owner_gid, is_sgid_set _DEFAULT_INSTALL_TIMEOUT: float = 300.0 @@ -128,3 +129,23 @@ def _validate_directory(path: Path) -> None: raise KeyError(f"{path}: No such file or directory") elif path.is_file(): raise KeyError(f"{path}: Is a file, not a directory") + + +def get_scratch_info(config: ScratchConfig) -> ScratchResponse: + _validate_directory(config.root) + scratch_responses = ScratchResponse(package_name=[], version=[], is_dirty=[]) + for repo in config.repositories: + local_directory = config.root / repo.name + repo = Repo(local_directory) + try: + branch = repo.active_branch.name + except TypeError: + branch = repo.head.commit.hexsha + + is_dirty = repo.is_dirty() + + scratch_responses.package_name.append(repo.remotes.origin.url) + scratch_responses.version.append(branch) + scratch_responses.is_dirty.append(is_dirty) + + return scratch_responses diff --git a/src/blueapi/client/rest.py b/src/blueapi/client/rest.py index d1805f239..15654c75f 100644 --- a/src/blueapi/client/rest.py +++ b/src/blueapi/client/rest.py @@ -135,6 +135,9 @@ def delete_environment(self) -> EnvironmentResponse: def get_oidc_config(self) -> OIDCConfig: return self._request_and_deserialize("/config/oidc", OIDCConfig) + def get_scratch_packages(self) -> dict: + return self._request_and_deserialize("/scratch/", dict) + @start_as_current_span(TRACER, "method", "data", "suffix") def _request_and_deserialize( self, diff --git a/src/blueapi/service/interface.py b/src/blueapi/service/interface.py index 803841964..b46eab341 100644 --- a/src/blueapi/service/interface.py +++ b/src/blueapi/service/interface.py @@ -6,10 +6,11 @@ from bluesky_stomp.messaging import StompClient from bluesky_stomp.models import Broker, DestinationBase, MessageTopic +from blueapi.cli.scratch import get_scratch_info from blueapi.config import ApplicationConfig, OIDCConfig, StompConfig from blueapi.core.context import BlueskyContext from blueapi.core.event import EventStream -from blueapi.service.model import DeviceModel, PlanModel, WorkerTask +from blueapi.service.model import DeviceModel, PlanModel, ScratchResponse, WorkerTask from blueapi.worker.event import TaskStatusEnum, WorkerState from blueapi.worker.task import Task from blueapi.worker.task_worker import TaskWorker, TrackableTask @@ -196,3 +197,10 @@ def get_task_by_id(task_id: str) -> TrackableTask | None: def get_oidc_config() -> OIDCConfig | None: return config().oidc + + +def get_scratch_packages() -> ScratchResponse: + if _CONFIG.scratch is None: + return ScratchResponse(package_name=[], version=[], is_dirty=[]) + else: + return get_scratch_info(config=_CONFIG.scratch) diff --git a/src/blueapi/service/main.py b/src/blueapi/service/main.py index 3e779c518..9f6e3efe7 100644 --- a/src/blueapi/service/main.py +++ b/src/blueapi/service/main.py @@ -38,6 +38,7 @@ EnvironmentResponse, PlanModel, PlanResponse, + ScratchResponse, StateChangeRequest, TaskResponse, TasksListResponse, @@ -420,6 +421,12 @@ def set_state( return runner.run(interface.get_worker_state) +@router.get("/scratch", response_model=ScratchResponse) +@start_as_current_span(TRACER) +def get_scratch_packages(runner: WorkerDispatcher = Depends(_runner)): + return runner.run(interface.get_scratch_packages) + + @start_as_current_span(TRACER, "config") def start(config: ApplicationConfig): import uvicorn diff --git a/src/blueapi/service/model.py b/src/blueapi/service/model.py index 8f0e9d60b..1f62fb95b 100644 --- a/src/blueapi/service/model.py +++ b/src/blueapi/service/model.py @@ -157,6 +157,18 @@ class EnvironmentResponse(BlueapiBaseModel): ) +class ScratchResponse(BlueapiBaseModel): + """ + State of the scratch area. + """ + + package_name: list[str] = Field(description="Name of the package") + version: list[str] = Field(description="Version of the package") + is_dirty: list[bool] = Field( + description="Does the package have uncommitted changes" + ) + + class Cache(BlueapiBaseModel): """ Represents the cached data required for managing authentication.