lablup / lablup/backend.ai

Add adminScanImage GraphQL mutation for rescanning an image absent from the DB

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

Description

The manager CLI (mgr image rescan ) can scan an image that has no row in the images table: it picks the target registry by prefix-matching the canonical against the registered container registries, then queries the registry manifest directly. The REST endpoint POST /image/rescan cannot: it resolves the image row from the images table first and derives the registry from image_row.registry_id, so an image absent from the DB fails with UnknownImageReference.

The admin catalog UI already has a full-registry rescan. Add the per-image counterpart as a superadmin GraphQL mutation with the CLI semantics, without changing any existing endpoint.

Schema (v2, Strawberry):

input ScanImageInput {
canonical: String!
}

type ScanImagePayload {
images: [ImageV2\!]\!
errors: [String\!]\!
}

type Mutation {
adminScanImage(input: ScanImageInput!): ScanImagePayload
}

Behaviour:

- superadmin only; check_admin_only() on the first line of the resolver.
- global scope; no domain or project scope argument.
- No architecture argument: every architecture in the manifest list is scanned, which is why the payload returns a list rather than a single image.
- A canonical without a tag resolves to latest, same as the CLI. Document this in the input field description.
- Synchronous, not a background task. A single tag is one manifest request plus one config blob per architecture.
- No V2 suffix on the mutation name: there is no v1 scanImage to collide with. The payload references the existing ImageV2 type.

Scope, repository and service:

- repositories/image/db_source/db_source.py: add scan_image_by_canonical(canonical). Reuse the existing pieces unchanged: _load_configured_registries, _filter_by_img_canonical, scan_single_image, and ContainerRegistry.scan_single_ref. Do NOT read the images table.
- Do NOT include the registry-name fallback that rescan_images() has. A value matching no registry prefix must be rejected, never escalated into a full-registry scan. This is the one place where the new path deliberately differs from the CLI: the admin catalog UI already covers full rescans, so the fallback here would only turn a typo into a full scan.
- Raise RegistryNotFoundForImage when no registry prefix matches, and InternalServerError when more than one registry row shares the same registry_name and project.
- Tag parsing stays with the existing parser.
- repositories/image/repository.py: thin delegating method with the image_repository_resilience decorator.
- services/image/actions/scan_image_by_canonical.py (new): action carrying only canonical, operation type CREATE.
- services/image/service.py: handler returning the scanned images plus per-architecture error messages. Raise ImageNotFound when the scan yields no images and no errors.
- services/image/processors.py: register with group.global_scope(...).
- Leave the existing ScanImageAction and its REST caller untouched.

Scope, API:

- common/dto/manager/v2/image/request.py and response.py: the existing RescanImagesInput / RescanImagesPayload pair is currently unreferenced by any code and mirrors the REST shape (architecture required, single item). Repurpose that pair as ScanImageInput / ScanImagePayload with the shape above rather than adding a new one.
- api/adapters/image/adapter.py: admin_scan_image(input) calling the action and converting to the DTO.
- api/gql/image/mutations.py (new): input and payload GQL types, following api/gql/container_registry/mutations.py.
- api/gql/image/resolver.py: the resolver, calling the adapter only, never the processor directly.
- api/gql/image/__init__.py and api/gql/schema.py: re-export and register on the Mutation type.
- Use the NEXT_RELEASE_VERSION constant for added_version; do not hardcode a version string.
- Regenerate the v2 SDL with scripts/generate-graphql-schema.sh and add a news fragment under changes/.

Do not touch POST /image/rescan, POST /container-registries/rescan, the v1 rescanImages mutation, or mgr image rescan.

One adjacent fix, same file: the existing scan_image handler in services/image/service.py reads result.images[0], which raises IndexError (HTTP 500) when the image exists in the DB but its tag was removed from the registry. Guard it to raise ImageNotFound instead. The rest of the REST behaviour (architecture required, DB row required) stays as is.

Success Criteria

- [ ] superadmin calls adminScanImage with a canonical absent from the images table -> a new image row is created and returned in the payload
- [ ] scan a canonical that already has a row -> the existing row is updated, no duplicate row
- [ ] a multi-architecture manifest list -> payload images contains one entry per architecture
- [ ] a non-superadmin caller -> 403 Forbidden
- [ ] a canonical whose host is not a registered container registry -> RegistryNotFoundForImage (404)
- [ ] passing a bare registry name as canonical -> error, and no full-registry scan is triggered
- [ ] a canonical without a tag -> the latest tag is scanned
- [ ] partial per-architecture failures -> mutation still succeeds and the messages appear in errors
- [ ] the action is recorded in the audit log as a global-scope action
- [ ] scripts/generate-graphql-schema.sh output contains adminScanImage and the regenerated SDL is committed
- [ ] existing REST POST /image/rescan behaviour is unchanged for an image present in the DB
- [ ] REST POST /image/rescan for a DB row whose registry tag was deleted -> ImageNotFound (404), not IndexError (500)
- [ ] pants test passes for affected packages

JIRA Issue: BA-7904

Contributor guide

Open the contributing guide

Research direction

Start with repositories/image/db_source/db_source.py and the existing scan_image flow, then trace the repository, service, processor, adapter, and GraphQL mutation patterns named in the issue. Compare api/gql/container_registry/mutations.py and run scripts/generate-graphql-schema.sh. Done means the new superadmin mutation handles the listed scan cases, the adjacent REST error becomes ImageNotFound, the SDL and news fragment are committed, and affected Pants tests pass.

Written by the indexing model from the issue text.

Assessment

Tech stack
graphql, python
Domain
api, backend, database
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.