Skip to content

feat(backup): validate extra_volumes paths during database creation and update - #44

Merged
tsivaprasad merged 6 commits into
mainfrom
PLAT-94-validate-host-path-mount-with-pgedge-docker-image
Jun 9, 2025
Merged

feat(backup): validate extra_volumes paths during database creation and update#44
tsivaprasad merged 6 commits into
mainfrom
PLAT-94-validate-host-path-mount-with-pgedge-docker-image

Conversation

@tsivaprasad

@tsivaprasad tsivaprasad commented Jun 6, 2025

Copy link
Copy Markdown
Contributor

This PR adds validation of extra_volumes paths during database creation and update to ensure all specified mount points exist and are accessible on the host system. It improves reliability by preventing invalid volume configurations early in the workflow.

Validation
Cluster Setup:

  • Created a 3-node cluster.

  • Initialized nodes to join the same cluster environment using:

restish control-plane-local-1 init-cluster
restish control-plane-local-2 join-cluster "$(restish control-plane-local-1 get-join-token)"
restish control-plane-local-3 join-cluster "$(restish control-plane-local-1 get-join-token)"

Create Database Validation:

  • Invalid Input Example:
"external_volumes": [
  {
    "host_path": "/Users/sivat/backups/host1",
    "destination_path": "/backups/container"
  }
],
"repositories": [
  {
    "id": "e45dfd13-b318-4ce2-acbe-dd07a7c1c006",
    "type": "posix",
    "base_path": "/backups/container"
  }
]
  • restish control-plane-local-1 create-database < createdatabase.json
HTTP/1.1 400 Bad Request
Content-Length: 496
Content-Type: application/json
Date: Fri, 06 Jun 2025 12:49:15 GMT
Goa-Error: invalid_input

{
  fault: false
  id: "Ea05WNX_"
  message: "volume validation failed. Please ensure that the paths provided in 'extra_volumes' exist and are accessible on the host system.
    Details: failed to get workflow result: volume validation activity error: failed to start container: failed to create container: Error response from daemon: invalid mount config for type \"bind\": bind source path does not exist: /host_mnt/Users/sivat/backups/host1"
  name: "invalid_input"
  temporary: false
  timeout: false
}
  • Valid Input Example:
"external_volumes": [
  {
    "host_path": "/Users/sivat/backups/host",
    "destination_path": "/backups/container"
  }
],
"repositories": [
  {
    "id": "e45dfd13-b318-4ce2-acbe-dd07a7c1c006",
    "type": "posix",
    "base_path": "/backups/container"
  }
]
  • restish control-plane-local-1 list-databases
HTTP/1.1 200 OK
Content-Length: 550
Content-Type: application/json
Date: Fri, 06 Jun 2025 12:53:56 GMT

[
  {
    created_at: "2025-06-06T12:50:46Z"
    id: "f2f1cae9-6f37-4609-aa98-d0991bff3493"
    instances: [
      {
        host_id: "8f6e5455-e228-4e2e-9129-a86cba1437c8"
        id: "60cff14d-066d-579c-b139-522d35f69d08"
        node_name: "n1"
        state: "available"
      }
      {
        host_id: "36dcd7ff-9f04-476e-ac6f-5495d075607d"
        id: "1de81ed2-aac1-5074-a221-681fdff0735d"
        node_name: "n2"
        state: "available"
      }
      {
        host_id: "fa461a39-5867-4a72-9923-dd7ce91e1eab"
        id: "73e3ab53-65b4-5a12-a73c-01c9df80af73"
        node_name: "n3"
        state: "available"
      }
    ]
    state: "available"
    updated_at: "2025-06-06T12:50:46Z"
  }
]
  • Backup database:

  • restish control-plane-local-1 backup-database-node f2f1cae9-6f37-4609-aa98-d0991bff3493 n1 '{ "type": "full" }'

HTTP/1.1 200 OK
Content-Length: 189
Content-Type: application/json
Date: Fri, 06 Jun 2025 12:55:56 GMT

{
  task: {
    created_at: "2025-06-06T12:55:56Z"
    database_id: "f2f1cae9-6f37-4609-aa98-d0991bff3493"
    status: "pending"
    task_id: "0197454f-ef78-75dd-a454-3dfd01c04482"
    type: "node_backup"
  }
}

Create New Database with Backup Restore:

  • Invalid Input (non-existent host path):

Similar error as create database invalid input with bind mount failure.

  • Valid Input (existing host path):

Database created successfully with restore configuration using mounted volumes.

Update Database Validation:

  • Invalid Input Example:
"external_volumes": [
  {
    "host_path": "/Users/sivat/backups/host1",
    "destination_path": "/backups/container"
  }
]

restish control-plane-local-1 update-database f2f1cae9-6f37-4609-aa98-d0991bff3494 <update.json

HTTP/1.1 400 Bad Request
Content-Length: 496
Content-Type: application/json
Date: Fri, 06 Jun 2025 13:08:18 GMT

{
  fault: false
  id: "OGyeXJth"
  message: "volume validation failed. Please ensure that the paths provided in 'extra_volumes' exist and are accessible on the host system.
    Details: failed to get workflow result: volume validation activity error: failed to start container: failed to create container: Error response from daemon: invalid mount config for type \"bind\": bind source path does not exist: /host_mnt/Users/sivat/backups/host1"
  name: "invalid_input"
  temporary: false
  timeout: false
}

Valid Input Example:

"external_volumes": [
  {
    "host_path": "/Users/sivat/backups/host",
    "destination_path": "/backups/container"
  }
]

restish control-plane-local-1 list-databases

HTTP/1.1 200 OK
Content-Length: 1098
Content-Type: application/json
Date: Fri, 06 Jun 2025 13:10:34 GMT

[
  {
    created_at: "2025-06-06T12:50:46Z"
    id: "f2f1cae9-6f37-4609-aa98-d0991bff3493"
    instances: [
      {
        host_id: "8f6e5455-e228-4e2e-9129-a86cba1437c8"
        id: "60cff14d-066d-579c-b139-522d35f69d08"
        node_name: "n1"
        state: "available"
      }
      {
        host_id: "36dcd7ff-9f04-476e-ac6f-5495d075607d"
        id: "1de81ed2-aac1-5074-a221-681fdff0735d"
        node_name: "n2"
        state: "available"
      }
      {
        host_id: "fa461a39-5867-4a72-9923-dd7ce91e1eab"
        id: "73e3ab53-65b4-5a12-a73c-01c9df80af73"
        node_name: "n3"
        state: "available"
      }
    ]
    state: "available"
    updated_at: "2025-06-06T12:50:46Z"
  }
  {
    created_at: "2025-06-06T13:02:39Z"
    id: "f2f1cae9-6f37-4609-aa98-d0991bff3494"
    instances: [
      {
        host_id: "8f6e5455-e228-4e2e-9129-a86cba1437c8"
        id: "c54a95f2-ca6d-5c32-9626-79d14a6122c3"
        node_name: "n1"
        state: "available"
      }
      {
        host_id: "36dcd7ff-9f04-476e-ac6f-5495d075607d"
        id: "8fc00bbd-d25b-5a1a-9823-c0c83c362d42"
        node_name: "n2"
        state: "available"
      }
      {
        host_id: "fa461a39-5867-4a72-9923-dd7ce91e1eab"
        id: "29708a75-a756-5e17-b32e-9ba0fc46cd8c"
        node_name: "n3"
        state: "available"
      }
    ]
    state: "available"
    updated_at: "2025-06-06T13:09:05Z"
  }
]

PLAT-94

…fy that specified extra_volumes paths exist and are accessible
@tsivaprasad
tsivaprasad requested a review from jason-lynch June 6, 2025 13:35
Comment thread server/internal/docker/docker.go Outdated
Comment thread server/internal/orchestrator/swarm/orchestrator.go Outdated
Comment thread server/internal/docker/docker.go Outdated
Comment thread server/internal/utils/utils.go Outdated
Comment thread server/internal/workflows/activities/validate_volumes.go Outdated
Comment thread server/internal/workflows/activities/validate_volumes.go Outdated
Comment thread server/internal/workflows/validate_spec.go Outdated
Comment thread server/internal/database/spec.go Outdated
Comment thread server/internal/workflows/validate_spec.go Outdated
Comment thread server/internal/workflows/service.go Outdated
Comment thread server/internal/api/service.go Outdated
Comment thread server/internal/api/service.go Outdated
Comment thread server/internal/api/service.go Outdated

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

Functionality looks good! Just left two minor suggestions for the output types. It's important to have separate types because these get written to/read from storage, and workflows can be interrupted and resumed (and maybe retried in the future). Once this is in production, there are some extra edge cases to consider when we modify the inputs and outputs. Having separate types doesn't remove those edge cases, but it limits the impact and makes it easier to understand the scope of a change if we need to change the inputs and outputs later on.

Comment thread server/internal/workflows/activities/validate_volumes.go Outdated
Comment thread server/internal/workflows/validate_spec.go Outdated

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

LGTM!

@tsivaprasad
tsivaprasad merged commit 0baa91d into main Jun 9, 2025
@tsivaprasad
tsivaprasad deleted the PLAT-94-validate-host-path-mount-with-pgedge-docker-image branch June 11, 2025 10:23
AntTheLimey added a commit that referenced this pull request Jul 20, 2026
The coldfront extension was created with `CREATE EXTENSION coldfront
CASCADE`, relying on coldfront.control declaring `requires = pg_duckdb`
so CASCADE pulls it in. That holds on the coldfront `main` branch but
the native-packages build (PR #44 — the future packaged image) dropped
the `requires` line, so CASCADE no longer installs pg_duckdb there and
CREATE EXTENSION coldfront would fail. The single-node trial masked this
because its image was built from `main`.

Create `pg_duckdb` explicitly (IF NOT EXISTS) before coldfront. Robust
regardless of the control file; idempotent on reconcile. CASCADE is kept
on the coldfront statement as belt-and-braces for any future dependency.

Build follow-up (flagged in the Phase-0 image plan): restoring
`requires = pg_duckdb` in coldfront.control for native-packages is the
cleaner semantic home; this fix stays regardless.
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