feat(cloud): persist device-flow Postgres credentials into DVC prefs#17
feat(cloud): persist device-flow Postgres credentials into DVC prefs#17jirhiker wants to merge 1 commit into
Conversation
Closes the last gap in the device-flow onboarding so a technician can
go from "fresh workstation" to "DVC connected" without typing,
pasting, or running any CLI other than the Pychron Preferences UI:
- pychron/cloud/api_client.py — DeviceCodePollSuccess gains
database_url and database_role slots. poll_device_code() parses
them out of the response and strips database_url from the safe_raw
debug-friendly dict so the embedded password cannot leak into
caller logs.
- pychron/cloud/dvc_credentials.py (NEW) — pure helpers wrapping the
parse → CSV → favorites round-trip:
* parse_database_url() decomposes a postgresql:// URL into the
fields a DVCConnectionItem needs, percent-decoding the password.
* build_dvc_connection_csv() serializes the parsed result as a
positional CSV that DVCConnectionItem(attrs=...) will rehydrate
on next DVC startup. The host field carries host:port so non-
default-port Cloud SQL connections work — the underlying CSV
schema has no separate port attribute.
* merge_dvc_connection_favorites() either replaces an existing
cloud-<lab> favorite or appends a new one, and demotes any
other row that previously held default=True so only one
default favorite is active.
* apply_db_credentials_to_prefs() ties it together and pushes the
list back to pychron.dvc.connection.favorites via the standard
apptools preferences adapter.
- pychron/cloud/workstation_setup.py — WorkstationSetup gets
database_url, database_role, and default_metadata_repo
attributes; from_device_code() populates them from the poll-
success body. None on either field is a legitimate state — the
workstation runs HTTP-only when the bridge has no staged
credential.
- pychron/cloud/tasks/preferences.py:
* New _registration_status field (Registered / Partial /
Unregistered) bound to a CustomLabel so the technician sees the
workstation's onboarding state at a glance on pane open.
* The Start-device-code-enrollment button now refuses to proceed
when the workstation is already onboarded (registration.json +
keyring token both present) without an explicit confirmation
dialog — protects against an accidental tap rotating the SSH
keypair and burning a fresh device-code slot.
* After a successful enrollment, _persist_db_credentials_from_setup
writes the staged Postgres credentials into the DVC connection
favorites and the same whoami probe the manual Test Connection
button uses runs automatically so the technician sees an
immediate end-to-end pass / fail without an extra click. A
parse failure is non-fatal — surfaced via the status badge so
the rest of the enrollment isn't rolled back.
* Re-onboard / revoke / switch-lab buttons all refresh the
registration status indicator on completion.
- test/cloud/test_dvc_credentials.py (NEW) — 28 unit tests covering
parse_database_url, build_dvc_connection_csv, merge_dvc_connection_favorites,
_row_set_field (caveman regression for the silent no-op bug), and
apply_db_credentials_to_prefs end-to-end against a fake prefs
adapter.
- test/cloud/test_device_code_setup.py — 3 new tests: db credential
fields propagate to the returned WorkstationSetup, missing fields
leave attrs at None, and the plaintext password is stripped from
DeviceCodePollSuccess.raw the same way api_token already is.
Total: 171 cloud tests passing (was 140, +31 new).
The companion server-side staging endpoint and admin CLI live in
pychronAPI feat/workstation-db-credentials (PR NMGRL#42).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
HOLD — server pivoting to Cloud SQL IAM auth. This PR persists a static-password `database_url` into `pychron.dvc.connection.favorites`. The latest BDD spec requires Cloud SQL IAM auth (per-workstation GCP service account, IAM DB user, no static password). pychron's `DVCConnectionItem` already supports `connection_method=cloudsql_iam`, so the client work needed is roughly:
Held until pychronAPI ships the IAM-staging endpoint. |
|
Superseded by #18 (Cloud SQL IAM client path), which has merged to main. The static-password parsing + persistence this PR carries is no longer the target architecture per the latest BDD spec, and conflicts with the IAM `apply_iam_credentials_to_prefs` path that already shipped. Closing + deleting the branch. Kept in repo history at commit `abf5e81f7` if anyone needs to read the static-password CSV / favorites round-trip patterns (which carried forward almost unchanged into #18 with the cloudsql_* fields swapped in). |
Summary
Closes the last gap in the device-flow onboarding so a technician can go from "fresh workstation" to "DVC connected" without typing, pasting, or running any CLI other than the Pychron Preferences UI.
The poll-success response now optionally carries
database_url+database_role(server-side: pychronAPI PR NMGRL#42). This PR wires those through the client:pychron.cloud.dvc_credentialsmodule — pure helpers forparse_database_url→build_dvc_connection_csv→merge_dvc_connection_favorites→apply_db_credentials_to_prefs.WorkstationSetupcarriesdatabase_url/database_role/default_metadata_repoattributes afterfrom_device_codereturns.pychron.dvc.connection.favoritesafter successwhoamiprobe the Test Connection button usesDeviceCodePollSuccess.raw(same defensive treatment asapi_token).Tests
test/cloud/test_dvc_credentials.py(parse, build, merge, apply round-trip against a fake apptools preferences adapter).test/cloud/test_device_code_setup.py(db credential propagation throughfrom_device_code, raw-field redaction).Test plan
pytest test/cloud/— 171 passedpychron.dvc.connection.favoritesis populated and DVC startup picks it up🤖 Generated with Claude Code