Add SSH source support with dialstdio and UI components
- Implement SSH dial via stdio for remote connections - Add sources API and storage layer for managing connection sources - Add SourcePanel and SshFields web components for SSH configuration - Update app structure to support source-based connections - Update handlers and server for new sources endpoint Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,11 +15,16 @@ Two ways to move things:
|
||||
|
||||
The target needs **nothing installed**: no agent, no Python, no Go — just `sshd`, `docker`, `bash` and `gzip`.
|
||||
|
||||
The **source** is picked in the UI: the daemon DockMV runs next to, another daemon by address, or a
|
||||
remote host over SSH — which needs nothing installed either. One DockMV can therefore move containers
|
||||
between any two of your hosts.
|
||||
|
||||
---
|
||||
|
||||
## Install
|
||||
|
||||
Run DockMV on the **source** host (the one holding the containers to move).
|
||||
Run DockMV on the **source** host (the one holding the containers to move), or anywhere that can
|
||||
reach it — see [sources](#sources).
|
||||
|
||||
### Docker Compose — recommended
|
||||
|
||||
@@ -66,7 +71,7 @@ make build # rebuilds the UI, then the binary
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Source host — where DockMV runs
|
||||
### Source host — where the containers are
|
||||
|
||||
| Requirement | Notes |
|
||||
| --- | --- |
|
||||
@@ -75,6 +80,9 @@ make build # rebuilds the UI, then the binary
|
||||
|
||||
Nothing else. The binary is static: no libc, no runtime, no Python.
|
||||
|
||||
A **remote** source needs the same as a target — `sshd` and a `docker` CLI of 18.09 or newer, since
|
||||
the Engine API is tunnelled through `docker system dial-stdio`. Nothing is installed there either.
|
||||
|
||||
### Target host — where containers land
|
||||
|
||||
| Requirement | Why |
|
||||
@@ -104,17 +112,37 @@ on the Go side; React 19, Vite 7 and TypeScript 5.9 on the UI side. That is the
|
||||
|
||||
## Using it
|
||||
|
||||
1. **Containers tab** — everything on the source host, grouped by compose project. Tick what to move.
|
||||
2. **Expand a row** (`▸`) for per-container details: target name, image pulled or transferred,
|
||||
1. **Source host** — top of the right panel. Defaults to the daemon DockMV runs next to; pick another
|
||||
one to read a different host. See [sources](#sources).
|
||||
2. **Containers tab** — everything on the source host, grouped by compose project. Tick what to move.
|
||||
3. **Expand a row** (`▸`) for per-container details: target name, image pulled or transferred,
|
||||
networks and ports, and — per mount — **copy the data**, **create it empty**, or **do not mount it**.
|
||||
Bind mounts can be relocated; named volumes renamed.
|
||||
3. **Apply to selected** does the same thing to every selected container at once.
|
||||
4. **Right panel** — add the target host, *connect*, then **migrate over SSH** or **build a package**.
|
||||
5. **Jobs tab** — live progress per container and per mount, with the full command log.
|
||||
4. **Apply to selected** does the same thing to every selected container at once.
|
||||
5. **Right panel** — add the target host, *connect*, then **migrate over SSH** or **build a package**.
|
||||
6. **Jobs tab** — live progress per container and per mount, with the full command log.
|
||||
|
||||
Start with **dry run** ticked: it runs every check and prints every command without touching the target.
|
||||
**Preview the commands** shows the exact `docker` invocations that will run. Nothing is hidden.
|
||||
|
||||
### Sources
|
||||
|
||||
Three kinds, all interchangeable once selected — the container list, the preview, the migration and
|
||||
the package build all read from whichever source is active:
|
||||
|
||||
| Kind | How it is reached | Notes |
|
||||
| --- | --- | --- |
|
||||
| **this host** | the socket in `DOCKER_HOST`, or `--docker-host` | always present; cannot be edited or removed |
|
||||
| **docker address** | `tcp://host:2375`, or another `unix://` socket | TLS uses the certificates from `DOCKER_CERT_PATH` in DockMV's own environment. A plain `tcp://` daemon is unauthenticated — anyone who reaches that port is root on that host |
|
||||
| **ssh** | the remote host's own docker CLI, through `docker system dial-stdio` | host keys are verified and credentials handled exactly like a target's |
|
||||
|
||||
The selected source is remembered in `<data-dir>/sources.json` and reselected on the next start; an
|
||||
explicit `--docker-host` on the command line overrides it for that run. Saved sources whose
|
||||
credentials you chose not to remember ask for them again after a restart.
|
||||
|
||||
With a remote source the data relays through DockMV — source → this host → target — so it crosses the
|
||||
network twice. Running DockMV on the source host keeps it to one hop.
|
||||
|
||||
<details>
|
||||
<summary><b>How the data is actually moved</b></summary>
|
||||
|
||||
@@ -163,10 +191,11 @@ The tool can stop containers and read every volume on the host, so it is treated
|
||||
admin tool:
|
||||
|
||||
- Binds to **`127.0.0.1` by default**. Binding elsewhere auto-generates an access token and prints it.
|
||||
- **SSH host keys are verified** like OpenSSH. An unknown key is refused until you approve the
|
||||
fingerprint in the UI; a *changed* key is refused outright. Trusted keys go to `<data-dir>/known_hosts`.
|
||||
- **SSH host keys are verified** like OpenSSH, for sources as well as targets. An unknown key is
|
||||
refused until you approve the fingerprint in the UI; a *changed* key is refused outright. Trusted
|
||||
keys go to `<data-dir>/known_hosts`.
|
||||
- **Credentials are not persisted unless you ask.** *Remember* writes them to
|
||||
`<data-dir>/connections.json`, mode `0600`.
|
||||
`<data-dir>/connections.json` for targets and `<data-dir>/sources.json` for sources, mode `0600`.
|
||||
- **Nothing on the target is overwritten by default.** An existing container name fails the item;
|
||||
you pick *skip*, *rename* or *replace*. An existing volume is reused and merged into, never
|
||||
silently deleted, unless you pick *replace*.
|
||||
@@ -227,9 +256,10 @@ dockmv version
|
||||
```
|
||||
--addr string address to listen on (default "127.0.0.1:8080")
|
||||
--token string require this token on every request; "auto" generates one
|
||||
--data-dir string connections and trusted host keys (default: OS config dir)
|
||||
--data-dir string sources, connections and trusted host keys (default: OS config dir)
|
||||
--package-dir string where migration packages are written (default <data-dir>/packages)
|
||||
--docker-host string source docker daemon (default: the DOCKER_HOST environment)
|
||||
--docker-host string local source docker daemon (default: the DOCKER_HOST environment);
|
||||
given explicitly, it overrides the remembered source
|
||||
-v verbose logging
|
||||
```
|
||||
|
||||
@@ -245,6 +275,12 @@ Everything the UI does is available over HTTP. Pass the token as `X-Auth-Token`
|
||||
GET /api/health
|
||||
GET /api/source inventory + default selections
|
||||
GET /api/source/sizes volume sizes (slow)
|
||||
GET /api/sources known sources + which one is selected
|
||||
POST /api/sources
|
||||
DELETE /api/sources/{id}
|
||||
POST /api/sources/{id}/select switch the source everything reads from
|
||||
POST /api/sources/{id}/probe read the SSH host key fingerprint
|
||||
POST /api/sources/{id}/trust approve that fingerprint
|
||||
GET /api/connections
|
||||
POST /api/connections
|
||||
DELETE /api/connections/{id}
|
||||
@@ -270,7 +306,9 @@ GET /api/packages, /api/packages/{name}/download
|
||||
web/ React + TypeScript UI (vite)
|
||||
internal/spec/ the transport model: a container, and how to render it back into docker flags
|
||||
internal/dkr/ source Docker daemon: inventory, archive streams, image save
|
||||
internal/sshx/ SSH transport, host key trust, driving the target's docker CLI
|
||||
internal/sshx/ SSH transport, host key trust, driving the target's docker CLI, and
|
||||
tunnelling a remote source's API through `docker system dial-stdio`
|
||||
internal/store/ saved sources and target connections, and which source is selected
|
||||
internal/migrate/ the two engines: SSH streaming, and package + installer generation
|
||||
internal/job/ progress tracking for long-running work
|
||||
internal/api/ HTTP handlers and SSE
|
||||
|
||||
Reference in New Issue
Block a user