Skip to content

allow docker specs on service restart#1429

Merged
alexcos20 merged 1 commit into
next-4from
feature/allow_dockerspecs_on_service_restart
Jul 24, 2026
Merged

allow docker specs on service restart#1429
alexcos20 merged 1 commit into
next-4from
feature/allow_dockerspecs_on_service_restart

Conversation

@alexcos20

@alexcos20 alexcos20 commented Jul 23, 2026

Copy link
Copy Markdown
Member

Allow serviceRestart to change the Docker image / tag

Motivation

Service-on-Demand let a consumer start a long-running container from their own image, but
serviceRestart could only recreate the container on the exact same image. The realistic
recovery flow was impossible on-node:

Start a service on your own image → discover a bug → fix it → publish the corrected image
under a new tag → restart the service on the new tag.

Previously the only option was to stop the service and start a brand-new one, losing the paid
window, the reserved resources and the allocated host ports. This PR lets a restart carry a new
image spec while keeping payment, resources, duration and endpoints intact.

Behaviour — restart is now atomic (all-old or all-new)

A restart never mixes new request params over the stored job. There are exactly two modes,
chosen by whether the request carries any container param:

Request contains… Mode Container rebuilt from
none of image/tag/checksum/dockerfile/additionalDockerFiles/userData/dockerCmd/dockerEntrypoint REUSE the stored job, unchanged
any of the above (⇒ image becomes required) RESPEC the request only — omitted fields are empty, never inherited

Making image mandatory as soon as any container param is present is the discriminator that
makes a partial change impossible: you cannot ride a new userData/dockerCmd on top of the
stored image. In RESPEC mode the image spec is validated exactly like serviceStart (exactly one
of tag/checksum/dockerfile; dockerfile requires allowImageBuild on the environment).

Always preserved: identity (serviceId, owner, environment), payment/claimTx, resources,
duration/expiresAt, and the allocated host ports/endpoints. Only the container spec can change —
there is no extra charge, consistent with the pre-existing restart semantics.

This is not a breaking change: a restart with no container params behaves exactly as before.

Error responses

  • 400 — a container param was sent without image (a rejected partial change), or more than
    one of tag/checksum/dockerfile was supplied.
  • 403dockerfile supplied but the environment has allowImageBuild=false (fast-fail in the
    handler; the engine re-checks as the authoritative backstop).

Changes

Core

  • ServiceRestartCommand (src/@types/commands.ts) — added image, tag, checksum,
    dockerfile, additionalDockerFiles; documented the two-mode contract.
  • HTTP route (src/components/httpRoutes/compute.ts) — maps the new body fields.
  • ServiceRestartHandler (src/components/core/service/restartService.ts) — RESPEC validation
    (image required + mutual-exclusion) and the allowImageBuild fast-fail gate; forwards the full
    spec to the engine.
  • Engine (compute_engine_base.ts, compute_engine_docker.ts) — extended restartService /
    doRestartService signatures; doRestartService now computes the effective spec as a single
    atomic choice (RESPEC ⇒ request values + recomputed containerImage; REUSE ⇒ stored values),
    drives the pull-vs-build branch off the effective dockerfile, re-checks allowImageBuild, and
    persists the new image spec (image/tag/checksum/dockerfile/additionalDockerFiles/
    containerImage) onto the job so subsequent restarts and orphan-recovery see what is actually
    running. Vulnerability scanning of the new image is inherited from pullImageRef/
    buildImageFromSpec.

Tests

  • src/test/unit/service/serviceHandlers.test.ts — RESPEC forwarding, REUSE forwards-undefined,
    400 for partial changes (lone tag/userData/dockerCmd), 400 for multiple image modes, 403 for a
    build-disabled dockerfile respec, and 200 for a build-enabled one.
  • src/test/unit/compute.test.ts — engine-level: RESPEC applies + persists a new tag; RESPEC
    overrides cmd/entrypoint; REUSE reuses the whole stored spec; RESPEC clears cmd/entrypoint with
    empty arrays. (Adjusted the pre-existing tests to the new signature/semantics.)
  • src/test/integration/services.test.ts(l4) restart on a freshly-published tag applies and
    persists the new image (endpoint still reachable, same hostPort + expiresAt); (l5) a container
    param without image is rejected 400.

Docs

  • docs/services.md, docs/API.md (request table + 400/403 responses), and the Postman
    collection updated for the two-mode contract and the bug-fix flow.

Summary by CodeRabbit

  • New Features

    • Service restarts now support applying a complete new container image specification.
    • Restarts can reuse the existing specification or rebuild from supplied image settings.
    • Updated image tags can redeploy services while preserving expiration times and host ports.
    • Dockerfile-based restarts are supported where image building is enabled.
  • Bug Fixes

    • Invalid or incomplete restart specifications are now rejected with clearer validation errors.
    • Restart updates consistently persist the newly applied container configuration.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e3fa0b8-fa44-4016-b034-8630af5a7452

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/allow_dockerspecs_on_service_restart

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@alexcos20 alexcos20 linked an issue Jul 23, 2026 that may be closed by this pull request
@alexcos20

Copy link
Copy Markdown
Member Author

/run-security-scan

@alexcos20 alexcos20 left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI automated code review (Gemini 3).

Overall risk: low

Summary:
This PR elegantly introduces RESPEC mode for serviceRestart, giving users the ability to dynamically update container specifications (e.g., swapping a new image tag to fix a bug) without losing their paid duration or host resources. The distinction between 'all-old' (REUSE) and 'all-new' (RESPEC) prevents fragmented, hard-to-debug container states. The logic is securely implemented with proper edge-case handling, including environment variable decryption validation and Dockerfile build checks.

Comments:
• [INFO][style] Good use of strict validation here. Enforcing an 'all-or-nothing' container specification when any single container parameter is supplied keeps the service state predictable and avoids messy, partial overwrites.
• [INFO][security] Excellent authoritative backstop. Even though the handler fast-fails, having the engine re-validate the daemon's allowImageBuild config prevents race conditions if the environment changes right before the engine processes the operation.
• [INFO][other] The tests clearly demonstrate the expected behavior of falling back to default image CMD and ENTRYPOINT when RESPEC mode omits the command overrides or supplies empty arrays. Very thorough testing.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/core/service/restartService.ts (1)

95-186: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Unclaimed-payment restart returns 500, not the documented 400.

Every other invalid-restart condition (not found, expired, environment missing, services disabled, access denied, undecryptable userData) gets an explicit pre-check here that returns a 4xx via buildInvalidParametersResponse/inline status. "Payment never claimed" has no such pre-check — it is only detected inside engine.restartService (compute_engine_docker.ts, if (!job.payment?.claimTx) throw new Error(...)), and that thrown Error is caught by the generic catch at line 182 and mapped to httpStatus: 500.

This is reachable in production: processServiceStart's failure path can leave a job in Error status with payment.lockTx set but no claimTx (escrow lock failed outright) — that job is not Expired, so it sails past every existing guard and only fails deep inside the engine. docs/API.md documents this exact case as a 400 response, so clients built against the docs will mishandle it as a server error.

Add an explicit pre-check mirroring the Expired check above (and the dockerfile/allowImageBuild fast-fail pattern — handler fast-fails with the correct status, engine backstops):

🛡️ Proposed fix
     // State check — cannot restart an expired service
     if (job.status === ServiceStatusNumber.Expired)
       return buildInvalidParametersResponse(
         buildInvalidRequestMessage('Cannot restart an expired service')
       )
+
+    // Payment check — mirrors the engine's authoritative recheck, but must surface as a
+    // 400 (invalid request) rather than falling through to the generic 500 catch below.
+    if (!job.payment?.claimTx)
+      return buildInvalidParametersResponse(
+        buildInvalidRequestMessage(
+          'Cannot restart a service whose payment was never claimed (unpaid or refunded) — start a new service'
+        )
+      )

No test in serviceHandlers.test.ts/services.test.ts currently exercises this path — worth adding once fixed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/core/service/restartService.ts` around lines 95 - 186, Before
the existing Expired-state check in the restart handler, add a pre-check for
jobs with payment.lockTx set but no payment.claimTx, returning
buildInvalidParametersResponse with the documented 400 invalid-request response.
Keep engine.restartService as the authoritative backstop while ensuring this
condition no longer reaches the generic 500 catch; add coverage in the relevant
service handler tests if available.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/components/core/service/restartService.ts`:
- Around line 95-186: Before the existing Expired-state check in the restart
handler, add a pre-check for jobs with payment.lockTx set but no
payment.claimTx, returning buildInvalidParametersResponse with the documented
400 invalid-request response. Keep engine.restartService as the authoritative
backstop while ensuring this condition no longer reaches the generic 500 catch;
add coverage in the relevant service handler tests if available.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0cf50d9d-a435-46e5-90ad-5cc04317e811

📥 Commits

Reviewing files that changed from the base of the PR and between 85fa61b and 172076b.

📒 Files selected for processing (11)
  • docs/API.md
  • docs/Ocean Node.postman_collection.json
  • docs/services.md
  • src/@types/commands.ts
  • src/components/c2d/compute_engine_base.ts
  • src/components/c2d/compute_engine_docker.ts
  • src/components/core/service/restartService.ts
  • src/components/httpRoutes/compute.ts
  • src/test/integration/services.test.ts
  • src/test/unit/compute.test.ts
  • src/test/unit/service/serviceHandlers.test.ts

@alexcos20
alexcos20 merged commit c483ea1 into next-4 Jul 24, 2026
10 checks passed
@alexcos20
alexcos20 deleted the feature/allow_dockerspecs_on_service_restart branch July 24, 2026 04:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ServiceRestart should allow to change docker image

1 participant