lablup / lablup/backend.ai

Let `POST /admin/images/rescan` register a single image that is not yet in the images table

Open
#14,612 0 comments 0 reactions 0 assignees View on GitHub
comp:manager type:feature
Dominant language
Python
Stars
670
Forks
183
Avg merge
15h 13m
Merged PRs (30d)
368

Description

## Motivation

Customers want to pull individual NGC catalog images (`nvcr.io/nvidia/tritonserver:26.08-vllm-python-py3`, NIMs, …) into Backend.AI without a full registry scan — NGC has no catalog API, so only per-image scans work. Today the only way to register such an image is the manager CLI (`backend.ai mgr image rescan `), which runs on the manager host. The WebUI is adding an "Add image" modal (lablup/backend.ai-webui, FR-3940) that posts each pasted canonical to `POST /admin/images/rescan`; this issue is its prerequisite.

## Current behaviour

`POST /admin/images/rescan` (`src/ai/backend/manager/api/rest/image/registry.py:24`, body `{canonical, architecture}`) goes `ScanImageAction` → `ImageService.scan_image` → `scan_image_by_identifier` → `db_source.scan_and_upsert_image` (`repositories/image/db_source/db_source.py:284`), whose first statement resolves the image **from the DB** (`ImageRow.resolve` → `ImageNotFound`, HTTP 404 `image_read_not-found`) and takes the registry from the found row's `registry_id`. So the endpoint can only refresh images that are already registered.

The CLI takes a different path: `db_source.rescan_images(registry_or_image)` (`db_source.py:477`) matches the canonical against the configured `container_registries` rows by prefix (`_filter_by_img_canonical`, `:592`) and calls `scan_single_image` → `scanner.scan_single_ref` — no DB lookup of the image, so a new image is registered as long as its `(registry_name, project)` row exists. Both `scan_and_upsert_image` and `scan_image_by_identifier` are marked `Deprecated. Use scan_images_by_ids instead.`

## Proposal

1. Make `POST /admin/images/rescan` fall back to the CLI's DB-independent path when the canonical is not yet registered — e.g. have `ImageService.scan_image` call `ImageRepository.rescan_images(registry_or_image=canonical)` (or resolve the registry row by prefix and call `scan_single_image` directly) instead of `scan_image_by_identifier`. `architecture` can stay as the manifest-selection hint.
2. Return a clean error for a missing tag/manifest. Today `_scan_tag` swallows the registry 404 (`container_registry/base.py:363-366`), `commit_rescan_result` yields `[]`, and `ScanImageActionResult(image=result.images[0], …)` (`services/image/service.py:388`) raises `IndexError` → HTTP 500 with no body.
3. Populate `RescanImagesResponse.errors` — it is `[]` on every current path (`base.py:291`), so the client has no per-image failure channel.
4. (Optional) Keep raising on ambiguous registry rows (`db_source.py:500-503`) but with a typed error code the client can classify, instead of `RuntimeError`.

## Notes

- Verified on `origin/main` 8545de4841. Route exists since 26.3.0.
- The WebUI gates its button on the manager version; once this lands, the flag will be keyed to the shipping release.
- Related WebUI issues: lablup/backend.ai-webui FR-3939 / FR-3940 (wayfinder map FR-3933).

Contributor guide

Open the contributing guide

Research direction

Start with src/ai/backend/manager/api/rest/image/registry.py:24 and trace ScanImageAction through services/image/service.py, repositories/image/db_source/db_source.py, and container_registry/base.py. Compare the endpoint path with rescan_images and scan_single_image; done means an unregistered canonical can be registered, missing manifests return a clean error, and RescanImagesResponse.errors reports failures.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, python
Domain
api, backend, databases
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.