Skip to content

Slice D — Config-file migration endpoint POST /v1/admin/config/file/migrate #1784

Description

@KirylKurnosenka

Slice D — Config-file migration endpoint POST /v1/admin/config/file/migrate

Part of #1781.

Goal

Provide an explicit, admin-triggered endpoint that copies file-defined config
entities into the platform bucket, so they become API-managed. Migration is on
demand (not on startup), which avoids resurrecting an API-deleted entity on restart.
After migration, the normal rebuild materializes the blob and (per the Slice B+C
blob-shadows-file rule) it becomes authoritative while the file entry still exists;
file entries are retired afterward.

Endpoint

POST /v1/admin/config/file/migrate, under the existing /v1/admin/config/file/*
admin surface (add the route to ControllerSelector; house the handler with
FileConfigController or a sibling admin controller). Admin-only, same auth as the
rest of /v1/admin/*. Runs under LockService.underBucketLocks(ADMIN_BUCKET_LOCATIONS, …)
(it writes to platform), so it is single-writer/multi-pod safe. Triggers
rebuildNow() after a non-dry-run so the new blobs materialize.

Request

POST /v1/admin/config/file/migrate
{
  "types": ["models", "applications"],  // optional; omit or "all" => every migratable type
  "dryRun": false                        // optional; true => report only, no writes
}

Behavior (per requested type, per entity in the file-sourced config)

  1. Idempotent presence check — skip if the platform blob already exists,
    computed as an in-memory set-diff against the already-loaded canonical ids in the
    current Config (no per-entity blob reads). Re-invoking migrates only what is
    missing.
  2. Otherwise write it to platform/{type}/{shortName} using the same write path the
    config-write API uses for that type
    :
    • models / interceptors / roles / routes / keys → the raw-blob + SecretFieldProcessor
      path (reuse AdminApplyController's per-type write helpers, which target
      platform).
    • applications / toolsets → ApplicationService / ToolSetService
      (external-service / authSettings secret processing, admin-managed fields
      AUTHORITATIVE, function/schema prep). A raw SecretFieldProcessor write is
      insufficient for these types.
  3. dryRun performs steps 1–2 as a computation only and writes nothing.

Response (synchronous, per-entity result — mirrors AdminApplyController's EntityResult)

200 OK
{ "results": [
  { "id": "models/platform/gpt-4",     "status": "migrated" },
  { "id": "models/platform/gpt-4o",    "status": "skipped",  "reason": "already in blob" },
  { "id": "applications/platform/foo", "status": "failed",   "reason": "..." }
] }

Open item — schema migration naming

A file schema is keyed by its $id, but its blob needs a path name for
schemas/platform/{name}. The name source (derive from $id, or a dedicated field)
must be settled before migrating the schemas / catalog_schemas types. Resolve this
as part of this slice, or scope schema migration out of the first cut.

Testing

  • Integration — new ConfigFileMigrateApiTest (full stack, so rebuild + blob +
    lock are exercised):
    • migrates file entities into platform; after migration they resolve and list by
      short name;
    • a second call is a no-op (all skipped, no writes);
    • the types filter migrates only the requested types;
    • dryRun writes nothing but reports what would migrate;
    • migrated applications/toolsets keep their secrets (encrypted at rest, decrypted in
      Config);
    • non-admin is rejected.

Depends on

Slice A (apps/toolsets in platform) and Slice B+C (short-name resolution) — the
migrated entities must materialize and resolve by short name.

Metadata

Metadata

Labels

enhancementNew feature or request

Projects

Status
Code Review

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions