Skip to content

feat: wire image field through API for custom image pinning per database or node#401

Merged
tsivaprasad merged 3 commits into
mainfrom
PLAT-599-allow-custom-image-override-per-database-or-node
Jul 1, 2026
Merged

feat: wire image field through API for custom image pinning per database or node#401
tsivaprasad merged 3 commits into
mainfrom
PLAT-599-allow-custom-image-override-per-database-or-node

Conversation

@tsivaprasad

@tsivaprasad tsivaprasad commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR wires the image field through the API, allowing users to pin any container image at either the database or node level via orchestrator_opts.swarm.image. The Control Plane deploys the specified image without requiring manifest validation, while returning a warning in the API response when the image is not present in the manifest.

Changes

  • Added the image field to the SwarmOpts Goa type and regenerated the API layer.
  • Added Warnings []string to ValidationResult and ValidateSpecOutput, allowing non-fatal validation warnings to be propagated through the validation workflow and returned to API callers.
  • Updated ValidateInstanceSpecs to emit a warning when SwarmOpts.Image is explicitly set and differs from the manifest-resolved image for the selected version. The specification remains valid and is still accepted.
  • Node-level image overrides continue to take precedence over database-level settings through the existing overridableValue() merge logic in NodeInstances(), requiring no additional changes.
  • When Image is cleared, the Control Plane automatically falls back to ResolvedImage during the next reconcile cycle using the existing resolveInstanceImages() precedence logic.

Testing

Verification:

  1. Created cluster
  2. Create DB with no image specified, CP auto-resolves from manifest
cp1-req create-database < ../demo/Images/create_db_with_no_image.json
HTTP/1.1 200 OK
Content-Length: 590
Content-Type: application/json
Date: Fri, 05 Jun 2026 13:04:44 GMT

{
  database: {
    created_at: "2026-06-05T13:04:44Z"
    id: "storefront-no-image"
    spec: {
      database_name: "storefront"
      database_users: [
        {
          attributes: ["SUPERUSER", "LOGIN"]
          db_owner: true
          username: "admin"
        }
      ]
      nodes: [
        {
          host_ids: ["host-1", "host-2", "host-3"]
          name: "n1"
        }
      ]
      postgres_version: "17.9"
      spock_version: "5"
    }
    state: "creating"
    updated_at: "2026-06-05T13:04:44Z"
  }
  task: {
    created_at: "2026-06-05T13:04:44Z"
    database_id: "storefront-no-image"
    entity_id: "storefront-no-image"
    scope: "database"
    status: "pending"
    task_id: "019e97e2-cf1a-7b49-9bf2-568a6c6a85e3"
    type: "create"
  }
}
 docker service ls | grep storefront-no-image
yizq2dw5kqqq   postgres-storefront-no-image-n1-9ptayhma         replicated   1/1        ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2   
qgt248y975zw   postgres-storefront-no-image-n1-689qacsi         replicated   1/1        ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2   
h3uni81wmir0   postgres-storefront-no-image-n1-ant97dj4         replicated   1/1        ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2 
  1. Created DB passing non-existing image
    Custom image at database level → warning returned, image deployed, Docker surfaces pull failure for non-existent image
cp1-req create-database < ../demo/Images/create_db.json 
HTTP/1.1 200 OK
Content-Length: 1412
Content-Type: application/json
Date: Fri, 05 Jun 2026 04:28:15 GMT

{
  database: {
    created_at: "2026-06-05T04:28:15Z"
    id: "storefront"
    spec: {
      database_name: "storefront"
      database_users: [
        {
          attributes: ["SUPERUSER", "LOGIN"]
          db_owner: true
          username: "admin"
        }
      ]
      nodes: [
        {
          host_ids: ["host-1", "host-2", "host-3"]
          name: "n1"
        }
      ]
      orchestrator_opts: {
        swarm: {
          image: "ghcr.io/pgedge/pgedge-postgres:my-custom-image"
        }
      }
      postgres_version: "17.9"
      spock_version: "5"
    }
    state: "creating"
    updated_at: "2026-06-05T04:28:15Z"
  }
  task: {
    created_at: "2026-06-05T04:28:15Z"
    database_id: "storefront"
    entity_id: "storefront"
    scope: "database"
    status: "pending"
    task_id: "019e9609-f4bc-7d81-9770-5bed8ac8e503"
    type: "create"
  }
  warnings: [
    "warning for node n1, host host-3: image \"ghcr.io/pgedge/pgedge-postgres:my-custom-image\" is not the manifest image for version 17.9_5 (manifest image: ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2); ensure it is a valid pgEdge image"
    "warning for node n1, host host-1: image \"ghcr.io/pgedge/pgedge-postgres:my-custom-image\" is not the manifest image for version 17.9_5 (manifest image: ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2); ensure it is a valid pgEdge image"
    "warning for node n1, host host-2: image \"ghcr.io/pgedge/pgedge-postgres:my-custom-image\" is not the manifest image for version 17.9_5 (manifest image: ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2); ensure it is a valid pgEdge image"
  ]
}
docker service ls | grep storefront
z4qu0xkqcnt0   postgres-storefront-n1-689qacsi   replicated   0/1        ghcr.io/pgedge/pgedge-postgres:my-custom-image 
  1. Create DB node-level image overrides database-level → node-level-image used, not db-level-image
cp1-req create-database < ../demo/Images/create_db_node_override.json 
HTTP/1.1 200 OK
Content-Length: 1546
Content-Type: application/json
Date: Fri, 05 Jun 2026 04:35:47 GMT

{
  database: {
    created_at: "2026-06-05T04:35:47Z"
    id: "storefront-node-override"
    spec: {
      database_name: "storefront"
      database_users: [
        {
          attributes: ["SUPERUSER", "LOGIN"]
          db_owner: true
          username: "admin"
        }
      ]
      nodes: [
        {
          host_ids: ["host-1", "host-2", "host-3"]
          name: "n1"
          orchestrator_opts: {
            swarm: {
              image: "ghcr.io/pgedge/pgedge-postgres:node-level-image"
            }
          }
        }
      ]
      orchestrator_opts: {
        swarm: {
          image: "ghcr.io/pgedge/pgedge-postgres:db-level-image"
        }
      }
      postgres_version: "17.9"
      spock_version: "5"
    }
    state: "creating"
    updated_at: "2026-06-05T04:35:47Z"
  }
  task: {
    created_at: "2026-06-05T04:35:47Z"
    database_id: "storefront-node-override"
    entity_id: "storefront-node-override"
    scope: "database"
    status: "pending"
    task_id: "019e9610-dc8e-7592-a475-e9fe3f61a5ed"
    type: "create"
  }
  warnings: [
    "warning for node n1, host host-3: image \"ghcr.io/pgedge/pgedge-postgres:node-level-image\" is not the manifest image for version 17.9_5 (manifest image: ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2); ensure it is a valid pgEdge image"
    "warning for node n1, host host-1: image \"ghcr.io/pgedge/pgedge-postgres:node-level-image\" is not the manifest image for version 17.9_5 (manifest image: ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2); ensure it is a valid pgEdge image"
    "warning for node n1, host host-2: image \"ghcr.io/pgedge/pgedge-postgres:node-level-image\" is not the manifest image for version 17.9_5 (manifest image: ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2); ensure it is a valid pgEdge image"
  ]
}
docker service ls | grep storefront-node-override
jo0rccekfw3f   postgres-storefront-node-override-n1-689qacsi   replicated   0/1        ghcr.io/pgedge/pgedge-postgres:node-level-image 
  1. Create DB using manifest image set explicitly → no warning returned
cp1-req create-database < ../demo/Images/create_db_manifest_image.json 
HTTP/1.1 200 OK
Content-Length: 708
Content-Type: application/json
Date: Fri, 05 Jun 2026 04:38:27 GMT

{
  database: {
    created_at: "2026-06-05T04:38:27Z"
    id: "storefront-manifest-image"
    spec: {
      database_name: "storefront"
      database_users: [
        {
          attributes: ["SUPERUSER", "LOGIN"]
          db_owner: true
          username: "admin"
        }
      ]
      nodes: [
        {
          host_ids: ["host-1", "host-2", "host-3"]
          name: "n1"
        }
      ]
      orchestrator_opts: {
        swarm: {
          image: "ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2"
        }
      }
      postgres_version: "17.9"
      spock_version: "5"
    }
    state: "creating"
    updated_at: "2026-06-05T04:38:27Z"
  }
  task: {
    created_at: "2026-06-05T04:38:27Z"
    database_id: "storefront-manifest-image"
    entity_id: "storefront-manifest-image"
    scope: "database"
    status: "pending"
    task_id: "019e9613-4c02-78f4-867f-0552ec69a0cd"
    type: "create"
  }
}

docker service ls | grep storefront
2lyuimxnkbc2 postgres-storefront-manifest-image-n1-9ptayhma replicated 1/1 ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2
9792vnbo0jwn postgres-storefront-manifest-image-n1-689qacsi replicated 1/1 ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2
8a3zmtz2hbzq postgres-storefront-manifest-image-n1-ant97dj4 replicated 1/1 ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2

5. Update databse to clearing image → falls back to manifest-resolved image on next update 
cp1-req update-database storefront < ../demo/Images/update_db.json
HTTP/1.1 200 OK
Content-Length: 735
Content-Type: application/json
Date: Fri, 05 Jun 2026 05:32:29 GMT

{
  database: {
    created_at: "2026-06-05T04:28:15Z"
    id: "storefront"
    instances: [
      {
        created_at: "2026-06-05T04:28:18Z"
        host_id: "host-1"
        id: "storefront-n1-689qacsi"
        node_name: "n1"
        state: "failed"
        updated_at: "2026-06-05T04:28:42Z"
      }
    ]
    spec: {
      database_name: "storefront"
      database_users: [
        {
          attributes: ["SUPERUSER", "LOGIN"]
          db_owner: true
          username: "admin"
        }
      ]
      nodes: [
        {
          host_ids: ["host-1", "host-2", "host-3"]
          name: "n1"
        }
      ]
      postgres_version: "17.9"
      spock_version: "5"
    }
    state: "modifying"
    updated_at: "2026-06-05T05:32:28Z"
  }
  task: {
    created_at: "2026-06-05T05:32:28Z"
    database_id: "storefront"
    entity_id: "storefront"
    scope: "database"
    status: "pending"
    task_id: "019e9644-c1c7-71e6-9ee0-a8cfb9db241b"
    type: "update"
  }
}
docker service ls
ID             NAME                                             MODE         REPLICAS   IMAGE                                                       PORTS
y24h2aafs0n9   postgres-storefront-n1-9ptayhma                  replicated   1/1        ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2   
z4qu0xkqcnt0   postgres-storefront-n1-689qacsi                  replicated   1/1        ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2   
e7cv5z40jqmx   postgres-storefront-n1-ant97dj4                  replicated   1/1        ghcr.io/pgedge/pgedge-postgres:17.9-spock5.0.6-standard-2   

Checklist

  • Tests added

PLAT-599

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@tsivaprasad, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a266e465-eacc-42e0-bf30-4860c171b1ed

📥 Commits

Reviewing files that changed from the base of the PR and between 7c3f46b and 3e0f3f8.

⛔ Files ignored due to path filters (7)
  • api/apiv1/gen/control_plane/service.go is excluded by !**/gen/**
  • api/apiv1/gen/http/control_plane/client/types.go is excluded by !**/gen/**
  • api/apiv1/gen/http/control_plane/server/types.go is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi.json is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi.yaml is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi3.json is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi3.yaml is excluded by !**/gen/**
📒 Files selected for processing (3)
  • api/apiv1/design/database.go
  • server/internal/docker/docker.go
  • server/internal/orchestrator/swarm/validate_instance_specs_test.go
📝 Walkthrough

Walkthrough

This PR adds an optional Swarm image override, checks image availability during validation, and propagates validation warnings through workflow aggregation into create and update database responses.

Changes

Image Override and Validation Warnings

Layer / File(s) Summary
API Contracts and Domain Warning Field
api/apiv1/design/database.go, server/internal/database/orchestrator.go
Response types gain optional warnings arrays, Swarm options gain an optional image override field, and validation results gain a warnings field.
Swarm Image Conversion
server/internal/api/apiv1/convert.go, server/internal/api/apiv1/convert_test.go
Swarm image values are mapped between API and database options, with tests covering pointer and empty-string conversion behavior.
Image Override Validation
server/internal/docker/docker.go, server/internal/orchestrator/swarm/orchestrator.go, server/internal/orchestrator/swarm/validate_instance_specs_test.go
Docker image existence checks are added and used inside instance-spec validation, with tests covering the validation paths.
Warning Aggregation
server/internal/workflows/validate_spec.go, server/internal/workflows/validate_spec_test.go
Validation warnings are collected into workflow output, with tests covering warning-only, mixed, and empty cases.
API Response Wiring
server/internal/api/apiv1/post_init_handlers.go
Create and update handlers return validation warnings in their API responses.

Poem

🐰 I hopped through images, bright and new,
With warnings flowing safely through,
From validation hops to API light,
The Swarm now speaks with clearer sight,
And carrots of code feel just right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: wiring the image field through the API for custom image pinning.
Description check ✅ Passed The description includes Summary, Changes, Testing, and Checklist sections, with only the optional Notes for Reviewers section missing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch PLAT-599-allow-custom-image-override-per-database-or-node

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.

@codacy-production

codacy-production Bot commented Jun 5, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 17 complexity · 0 duplication

Metric Results
Complexity 17
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@tsivaprasad

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@jason-lynch jason-lynch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Adding the image field to the API looks good! However, I'm not sure that the validation this PR adds is very helpful, especially since it doesn't prevent you from using an invalid image. Instead of a non-fatal validation, maybe we could check that the image exists by trying to pull it with the Docker client?

Something else to think about for a separate PR: another way that users can get into trouble is if the Postgres and Spock versions in the image don't match the ones in the spec. We should spend a little time thinking about how we can make that less dangerous, such as by enforcing a tag format that contains the Postgres and Spock versions like we do in our official images. I think @mmols had mentioned at one point that it would be reasonable for us to enforce a tag format for custom images.

@tsivaprasad

Copy link
Copy Markdown
Contributor Author

Adding the image field to the API looks good! However, I'm not sure that the validation this PR adds is very helpful, especially since it doesn't prevent you from using an invalid image. Instead of a non-fatal validation, maybe we could check that the image exists by trying to pull it with the Docker client?

Something else to think about for a separate PR: another way that users can get into trouble is if the Postgres and Spock versions in the image don't match the ones in the spec. We should spend a little time thinking about how we can make that less dangerous, such as by enforcing a tag format that contains the Postgres and Spock versions like we do in our official images. I think @mmols had mentioned at one point that it would be reasonable for us to enforce a tag format for custom images.

Thanks @jason-lynch
Good point — a non-fatal warning by itself isn't very useful if it still allows a broken deployment. We've updated the implementation accordingly.

Instead of performing a full docker pull (which downloads image layers), we now use DistributionInspect, a lightweight registry manifest lookup that verifies the image exists without transferring image data.

The validation behavior is now:

  • Hard fail if the image cannot be found or the registry cannot be reached.
  • Warning (non-fatal) if the image exists but differs from the manifest image associated with the selected version.
  • No validation warning when the specified image matches the manifest image exactly.

Created place holder ticket to Enforce version-aware tag format for custom image overrides https://pgedge.atlassian.net/browse/PLAT-641

@jason-lynch

jason-lynch commented Jun 11, 2026

Copy link
Copy Markdown
Member

@tsivaprasad That sounds good! Although I still have a question about this type of validation:

Warning (non-fatal) if the image exists but differs from the manifest image associated with the selected version.

What's the use case for this warning? To me, this looks like any valid usage of the override field will result in a warning. The only time it wouldn't return a warning is when you specify the same image that's in the manifest, in which case the override isn't doing anything. Am I understanding that right or is there a case I'm missing?

I'm wondering if we need warnings at all.

@tsivaprasad

Copy link
Copy Markdown
Contributor Author

@tsivaprasad That sounds good! Although I still have a question about this type of validation:

Warning (non-fatal) if the image exists but differs from the manifest image associated with the selected version.

What's the use case for this warning? To me, this looks like any valid usage of the override field will result in a warning. The only time it wouldn't return a warning is when you specify the same image that's in the manifest, in which case the override isn't doing anything. Am I understanding that right or is there a case I'm missing?

I'm wondering if we need warnings at all.

You're right — the warning fires on every intentional use of the override, so it was just noise. We've removed it. The only validation that remains is the hard fail when the image can't be found or reached in the registry. If the image is reachable, it's accepted with no warnings.

@jason-lynch

Copy link
Copy Markdown
Member

You're right — the warning fires on every intentional use of the override, so it was just noise. We've removed it. The only validation that remains is the hard fail when the image can't be found or reached in the registry. If the image is reachable, it's accepted with no warnings.

Thanks! Now that it's unused, could you please remove the warnings field from the API and the validation code?

@tsivaprasad tsivaprasad force-pushed the PLAT-596-add-image-version-fields-to-database-spec branch from 94d6615 to b49d1cd Compare June 30, 2026 10:45
Base automatically changed from PLAT-596-add-image-version-fields-to-database-spec to main June 30, 2026 15:19
@tsivaprasad tsivaprasad force-pushed the PLAT-599-allow-custom-image-override-per-database-or-node branch from cb8ce47 to 7c3f46b Compare June 30, 2026 18:10

@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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
server/internal/orchestrator/swarm/validate_instance_specs_test.go (1)

31-101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assertions run inside empty loops — these tests are largely vacuous.

In every subtest Previous is nil, Current has no port/volume/network diff, and o.docker is nil. As a result ValidateInstanceSpecs appends nothing to results, so the for _, r := range results loops never execute and the assert.Empty(...) calls never run. The only assertion actually exercised is require.NoError. The image-override path (CheckImageExists) is never covered because the docker client is nil.

Recommend asserting the result count explicitly and, ideally, injecting a fake docker client to cover the failure path.

♻️ Example: assert on results explicitly
 		results, err := o.ValidateInstanceSpecs(ctx, changes)
 		require.NoError(t, err)
-		for _, r := range results {
-			assert.Empty(t, r.Warnings)
-			assert.Empty(t, r.Errors)
-		}
+		assert.Empty(t, results)
🤖 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 `@server/internal/orchestrator/swarm/validate_instance_specs_test.go` around
lines 31 - 101, The subtests in ValidateInstanceSpecs are vacuous because
results is empty and the assertion loop never runs, so only require.NoError is
exercised. Update the tests to assert the expected results length explicitly in
each subtest, using ValidateInstanceSpecs as the entry point and checking for
zero or nonzero items as appropriate. To cover the image-override behavior,
inject a fake docker client into o.docker and add a test path that exercises
CheckImageExists instead of relying on the nil-client skip. Keep the existing
cases for knownVersion and unknownVersion, but make the assertions verify actual
returned warnings/errors rather than looping over an empty slice.
🤖 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.

Inline comments:
In `@server/internal/docker/docker.go`:
- Around line 591-597: Update CheckImageExists to avoid relying on anonymous
DistributionInspect, since it can reject private images and valid overrides;
resolve registry credentials first using the Docker client/auth flow before
validating the image. If the lookup may contact an external registry, wrap the
inspection in a context with a timeout, and keep the existing error wrapping in
Docker.CheckImageExists for clear failure messages.

---

Nitpick comments:
In `@server/internal/orchestrator/swarm/validate_instance_specs_test.go`:
- Around line 31-101: The subtests in ValidateInstanceSpecs are vacuous because
results is empty and the assertion loop never runs, so only require.NoError is
exercised. Update the tests to assert the expected results length explicitly in
each subtest, using ValidateInstanceSpecs as the entry point and checking for
zero or nonzero items as appropriate. To cover the image-override behavior,
inject a fake docker client into o.docker and add a test path that exercises
CheckImageExists instead of relying on the nil-client skip. Keep the existing
cases for knownVersion and unknownVersion, but make the assertions verify actual
returned warnings/errors rather than looping over an empty slice.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d9344ed9-e337-41c5-a0a4-7dbcf944da73

📥 Commits

Reviewing files that changed from the base of the PR and between 09a808a and 7c3f46b.

⛔ Files ignored due to path filters (11)
  • api/apiv1/gen/control_plane/service.go is excluded by !**/gen/**
  • api/apiv1/gen/http/cli/control_plane/cli.go is excluded by !**/gen/**
  • api/apiv1/gen/http/control_plane/client/cli.go is excluded by !**/gen/**
  • api/apiv1/gen/http/control_plane/client/encode_decode.go is excluded by !**/gen/**
  • api/apiv1/gen/http/control_plane/client/types.go is excluded by !**/gen/**
  • api/apiv1/gen/http/control_plane/server/encode_decode.go is excluded by !**/gen/**
  • api/apiv1/gen/http/control_plane/server/types.go is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi.json is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi.yaml is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi3.json is excluded by !**/gen/**
  • api/apiv1/gen/http/openapi3.yaml is excluded by !**/gen/**
📒 Files selected for processing (10)
  • api/apiv1/design/database.go
  • server/internal/api/apiv1/convert.go
  • server/internal/api/apiv1/convert_test.go
  • server/internal/api/apiv1/post_init_handlers.go
  • server/internal/database/orchestrator.go
  • server/internal/docker/docker.go
  • server/internal/orchestrator/swarm/orchestrator.go
  • server/internal/orchestrator/swarm/validate_instance_specs_test.go
  • server/internal/workflows/validate_spec.go
  • server/internal/workflows/validate_spec_test.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • server/internal/workflows/validate_spec_test.go
  • server/internal/workflows/validate_spec.go
  • server/internal/api/apiv1/convert.go
  • server/internal/api/apiv1/post_init_handlers.go
  • server/internal/database/orchestrator.go
  • api/apiv1/design/database.go
  • server/internal/api/apiv1/convert_test.go

Comment thread server/internal/docker/docker.go
@tsivaprasad

Copy link
Copy Markdown
Contributor Author

You're right — the warning fires on every intentional use of the override, so it was just noise. We've removed it. The only validation that remains is the hard fail when the image can't be found or reached in the registry. If the image is reachable, it's accepted with no warnings.

Thanks! Now that it's unused, could you please remove the warnings field from the API and the validation code?

@jason-lynch Addressed this comment, pls have a look.

@tsivaprasad tsivaprasad requested a review from jason-lynch June 30, 2026 19:29
@tsivaprasad tsivaprasad merged commit a21e6d3 into main Jul 1, 2026
3 checks passed
@tsivaprasad tsivaprasad deleted the PLAT-599-allow-custom-image-override-per-database-or-node branch July 1, 2026 03:03
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.

2 participants