Skip to content

[feature:gsoc26] Expose persistent, retry_count and next_retry_at on the REST API and add cancel endpoint for pending operations #428

Description

@Eeshu-Yadav

Is your feature request related to a problem? Please describe.

Sub-issue 07 wires up the admin; this one mirrors all of it for the REST API. The split matters because single-device persistent upgrades are an API/scripting use case in this iteration (the admin UI for single-device persistence is deferred to a future iteration), so the API surface has to land now.

Today the API surface has none of the persistence fields: BatchUpgradeSerializer exposes only upgrade_all, group, location; UpgradeOperationSerializer exposes id, device, image, status, log, progress, modified, created; and BuildBatchUpgradeView.post() accepts no is_persistent kwarg to pass through. So an API consumer can't opt a single-device upgrade into persistence, can't filter pending ops via ?status=, and can't read retry_count or next_retry_at.

Describe the solution I would implement

I would like to add full API parity with the admin surface from sub-issue 07.

  1. Add a writable is_persistent boolean to BatchUpgradeSerializer:

    • default=True so omitting the field in the request body produces the same "checked by default" outcome as the admin form.
    • Add to Meta.fields.
  2. Update UpgradeOperationSerializer and DeviceUpgradeOperationSerializer:

    • Writable is_persistent field so single-device callers can opt in.
    • Read-only retry_count and next_retry_at fields.
  3. BatchUpgradeOperationListSerializer and BatchUpgradeOperationSerializer both use fields = "__all__", so they inherit is_persistent automatically once sub-issue 01 adds it to the model. I'd add an explicit unit test asserting the field appears in the serialized output, so a future serializer refactor doesn't silently drop it.

  4. Update BuildBatchUpgradeView.post():

    • Read is_persistent from serializer.validated_data.
    • Pass it alongside the existing firmwareless, group, location kwargs to Build.batch_upgrade(). The method signature update lives in sub-issue 07.
  5. Enforce post-launch immutability at the serializer level to mirror sub-issue 01's model-level clean() guard. Override update() on UpgradeOperationSerializer and BatchUpgradeOperationSerializer to raise serializers.ValidationError (400) when is_persistent is in validated_data: a PATCH/PUT always targets a saved row, which is post-launch for UpgradeOperation by definition. For BatchUpgradeOperation the status != "idle" check from sub-issue 01's clean() would fire via is_valid() anyway, but raising explicitly gives a clearer error response.

  6. UpgradeOperationFilter (api/filters.py:11–35) and DeviceUpgradeOperationFilter (api/filters.py:38–41) already include status in their Meta.fields, so ?status=pending becomes available automatically once sub-issue 01 adds the new choice. Add explicit test coverage.

  7. The dedicated cancel endpoint already exists: POST /api/v1/firmware-upgrader/upgrade-operation/<uuid:pk>/cancel/ routed to UpgradeOperationCancelView at api/views.py:381–451 (URL declaration at api/urls.py:60–64, decorated with @swagger_auto_schema for drf_yasg). The view calls instance.cancel(); once sub-issue 03 extends _CANCELLABLE_STATUS to include pending, the same endpoint cancels pending ops with no view-side change. Add a regression test asserting POST /cancel/ returns 200 for a pending op and surfaces the ValueError from cancel() (mapped to 409) on a terminal-status op.

  8. API tests covering: POST with is_is_persistent=true/false/omitted produces correctly-flagged batches and propagates to children; single-device POST with is_is_persistent=true creates a persistent standalone op; GET ?status=pending returns only pending operations; detail response includes retry_count and next_retry_at; PATCH to mutate is_persistent post-launch is rejected with 400; cancel on a pending op returns 200 with the op transitioned to cancelled.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestgsoc-ideaIssues part of Google Summer of Code project

Type

No fields configured for Task.

Projects

Status
ToDo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions