Add postgres_snapshot_schedules bundle resource - #6449
Conversation
## Changes Bump `github.com/databricks/databricks-sdk-go` from v0.175.0 to v0.177.0 and the pinned OpenAPI spec SHA to `0f2c9a3d`. Regenerated all downstream artifacts from the new spec: CLI command stubs, bundle JSON schema, validation, direct-engine resources, pydabs models, and the DABs<->TF field map. User-visible additions include the jobs continuous `maintenance_window` field and the pipelines RabbitMQ connector options; the postgres branch `source_snapshot` field is now handled as immutable/input-only by the direct engine. The v0.177.0 `ml.*` field removals (feature-engineering) are not referenced by hand-written CLI code, so they required no source changes. The SDK now marks the pipelines `continuous` field deprecated; it remains a supported bundle config field, so its four usages carry `//nolint:staticcheck` (SA1019). ## Why Keep the CLI current with the latest SDK and provide a clean base for upcoming feature work. ## Tests Acceptance goldens regenerated via `-update` (help text + refschema). The non-update verify pass is green apart from load-induced terraform-engine timeout flakes, each confirmed passing in isolation. Root-module unit suite passes (9532 tests). `./task fmt`, `./task lint`, and `./task checks` clean. Co-authored-by: Isaac <no-reply@databricks.com>
a34c52d to
f1196c2
Compare
## Changes
Add the `postgres_snapshot_schedules` bundle resource, which manages the
automatic-snapshot schedule of a Lakebase Postgres branch.
The snapshot schedule is a per-branch singleton with no create or delete API —
only `GetSnapshotSchedule` and `UpdateSnapshotSchedule`. The resource maps the
lifecycle onto that single write call:
- create/update set the branch's schedule via `UpdateSnapshotSchedule`
(update_mask `schedule`, awaiting the long-running operation);
- delete disables automatic snapshots by setting an empty cadence set, since
DoDelete also fires when the resource is removed from config, not only on
`bundle destroy`;
- `branch` composes the schedule's hierarchical name and is a provided id field
(a change recreates).
Modeled on the existing `postgres_*` resources. Direct engine only: the schedule
was added in databricks-sdk-go v0.177.0 and the pinned Terraform provider has no
equivalent resource yet, so the acceptance and bind tests are pinned to the
direct engine.
## Tests
- direct-engine unit CRUD (`bundle/direct/dresources`);
- acceptance (direct engine), four end-to-end scenarios, each reading the
schedule back with `postgres get-snapshot-schedule`:
- `basic`: create with a cadence, in-place cadence update, then remove the
resource from config, which disables the schedule (reads back
`schedule: null`);
- `update`: add the schedule to an already-deployed branch, then remove it;
- `out-of-band`: an out-of-band `update-snapshot-schedule` is detected as
drift by `bundle plan` (`update postgres_snapshot_schedules.main_schedule`)
and reconciled back by deploy;
- `orphaned`: removing the branch from config while keeping the schedule that
references it — the reference still resolves from the deployed state, so
validate passes and plan sequences a branch delete alongside a schedule
recreate (documented, not a validation error);
- the invariant and bind/unbind suites;
- resource-enumeration unit tests (bind support, run_as, permissions,
target-mode) extended for the new type.
Acceptance goldens regenerated via `-update`; the verify pass is green apart
from pre-existing load-induced terraform-engine timeout flakes, each confirmed
passing in isolation.
Co-authored-by: Isaac <no-reply@databricks.com>
4c0b285 to
686d31b
Compare
Drop the `update` subtest (create/update/remove is already covered by `basic`) and the `orphaned` subtest (deleting a branch while keeping a schedule that references it is user error — it plans but fails at apply, which we don't need to pin in a golden). Co-authored-by: Isaac <no-reply@databricks.com>
DoCreate built the schedule resource name as `config.Branch + "/snapshot-schedule"`, which doubles the separator if a user writes `branch:` with a trailing slash. Route it through snapshotScheduleName, which trims trailing slashes first, and cover it with a unit test. Co-authored-by: Isaac <no-reply@databricks.com>
Add a `bundle plan` after the removal step in the basic acceptance test; it reports "0 to add, 0 to change, 0 to delete" (project and branch unchanged), confirming the disable settled cleanly and leaves no lingering drift. Co-authored-by: Isaac <no-reply@databricks.com>
| deleteIsNoop := strings.HasSuffix(group, "permissions") || strings.HasSuffix(group, "grants") | ||
| // postgres_snapshot_schedules has no delete endpoint: DoDelete disables the | ||
| // schedule by setting an empty cadence set, and the schedule remains readable | ||
| // (it is intrinsic to the branch), so DoRead still succeeds afterwards. | ||
| deleteIsNoop := strings.HasSuffix(group, "permissions") || strings.HasSuffix(group, "grants") || group == "postgres_snapshot_schedules" |
There was a problem hiding this comment.
maybe we can rename deleteIsNoop? It implies that DoDelete does nothing, but it does (at least to the best of its abilities, updating it to the default empty object)
222eaa4 to
1b02de4
Compare
…s-snapshot-schedules # Conflicts: # .nextchanges/dependency-updates/bump-sdk-0.177.0.md # bundle/terraform_dabs_map/generated.go # go.mod
Completes wiring for the postgres_snapshot_schedules resource so it satisfies the repo-wide "for all resources" guard tests: - Add MarshalJSON/UnmarshalJSON to resources.PostgresSnapshotSchedule so the outer wrapper's id/url/lifecycle/modified_status survive a JSON round-trip instead of being dropped by the embedded config marshaler (mirrors resources.PostgresBranch). - Pin the resource to Beta via launchStageOverrides and cover it in the launch-stage override test. - Populate PostgresSnapshotSchedules in the StateToBundle fixtures. - Mark it direct-engine only in the Terraform lifecycle test; the pinned provider has no resource that maps to a snapshot schedule. Regenerate the resource's out.test.toml snapshots to pick up the DMS EnvMatrix inherited from a parent test.toml. Co-authored-by: Isaac <no-reply@databricks.com>
Add the required bullet marker and trailing PR link so the fragment passes check-changelog. Co-authored-by: Isaac <no-reply@databricks.com>
| // The Terraform provider has no resource that maps to a snapshot schedule, | ||
| // so it is deployed through the direct engine only (see the resource's | ||
| // acceptance test.toml). | ||
| "postgres_snapshot_schedules", |
There was a problem hiding this comment.
this is being added in a future TF release
Integration test reportCommit: ea32dbc
Top 6 slowest tests (at least 2 minutes):
|
The snapshot schedule has no web UI page of its own. Per agreement with
the Postgres team, point its URL at the parent branch's restore view:
lakebase/projects/{project_id}/branches/{branch_id}/restore, resolved
from the resource's branch. Covered in TestInitializeURLs and reflected
in the bind acceptance golden.
Co-authored-by: Isaac <no-reply@databricks.com>
Register a postgres_snapshot_schedules pattern (lakebase/%s/restore, %s = parent branch) and use workspaceurls.ResourceURL in InitializeURL instead of hand-assembling the path, matching every other resource. Co-authored-by: Isaac <no-reply@databricks.com>
Adding postgres_snapshot_schedules to workspaceurls broke tests that
assert the exact ResourceTypes() list (cmd/experimental open command and
its acceptance golden); update them.
Also derive the branch from the schedule's resolved ID rather than the
Branch field, which may hold an unresolved "${...}" reference at summary
time. This matches the other parent-referencing postgres resources and
correctly shows "(not deployed)" until the schedule has an ID.
Co-authored-by: Isaac <no-reply@databricks.com>
It now has a workspace URL, so TestBundleResourcePluralNamesResolveInWorkspaceURLs requires it to resolve rather than be empty. Co-authored-by: Isaac <no-reply@databricks.com>
…s-snapshot-schedules
Main's new TestMarshalerValueReceiver requires marshalling by value and by pointer to agree; match the sibling postgres configs. Co-authored-by: Isaac <no-reply@databricks.com>
The Lakebase web UI addresses resources by uid, not by the resource name in bundle state, so the name-based URL was wrong. Revert to no URL (back in the noURL allowlist) until a uid-based scheme is designed. Co-authored-by: Isaac <no-reply@databricks.com>
The backend rejects a snapshot schedule on any non-root branch ("not
allowed to snapshot non-root branch"), which surfaced only on cloud. Teach
the testserver the same rule, and point the acceptance and CRUD fixtures at
the project's implicit root "production" branch instead of a child branch.
Co-authored-by: Isaac <no-reply@databricks.com>
| Postgres snapshot schedules: | ||
| schedule1: | ||
| Name: | ||
| URL: (not deployed) |
There was a problem hiding this comment.
Is it expected that it shows not deployed here?
There was a problem hiding this comment.
there's no URL - we show the same for other resources that don't have a URL, so out of scope here but I'll take a look if we can return something else (like "not supported")
|
|
||
| func (b *PostgresSnapshotSchedule) GetName() string { | ||
| // Snapshot schedules don't have a user-visible name field. | ||
| return "" |
There was a problem hiding this comment.
In out.fields.txt we seem to have both id and name field, couldn't we use these?
There was a problem hiding this comment.
the resource doesn't have a name. the id/name values are simply "${parent-branch}/snapshot-schedule"
There was a problem hiding this comment.
But why not to use it as a name though?
There was a problem hiding this comment.
Fair point, it's still its name
The schedule's resource name is its ID ("{branch}/snapshot-schedule");
return it from GetName so bundle summary shows it instead of a blank,
matching the SecretScope precedent (per review feedback).
Co-authored-by: Isaac <no-reply@databricks.com>
…s-snapshot-schedules # Conflicts: # bundle/config/resources.go # bundle/config/resources_test.go # bundle/direct/dresources/all_test.go
The Python codegen had no mapping for duration.Duration (the schedule's retention type), failing pydabs-codegen with a KeyError. Treat it as a str-serialized primitive, matching time.Time (PRIMITIVES + RENAMES + the test-case synthesizer), and regenerate the PyDABs module. Add the required acceptance/bundle/python support fixture and refresh the core public-API snapshot. Co-authored-by: Isaac <no-reply@databricks.com>
## Release v1.16.0 ### CLI * `aitools install` now registers the official Claude marketplace if it is missing before installing the Databricks Claude plugin. ([#6485](#6485)) * `databricks aitools install --output json` now reports an `error_category` for a failed or skipped install (per agent, and at the top level for a failure with no per-agent entry), giving coding agents and CI a stable classification of why an install did not complete. ([#6482](#6482)) * `databricks aitools install` honors `--output json`, emitting a structured `{scope, agents[...]}` document that reports each agent's delivery and install status so coding agents and CI can consume the result without scraping the text output. JSON mode requires `--scope` and `--agents` so the command runs without interactive prompts. ([#6481](#6481)) * `databricks bundle sync` now prints sync progress (`Action: PUT`, `Uploaded ...`) by default, matching `databricks sync`. Previously it was silent unless `--output` was passed. Use `--output json` for machine-readable output. ([#6568](#6568)) * Support major-only DBR runtime versions such as `19.x-scala2.13` in the cluster picker used by `databricks auth login --configure-cluster` and `databricks labs`. ([#6574](#6574)) * Deprecated the `databricks environments setup-local --constraints-only` flag in favour of the orthogonal `--no-dbconnect`; the flag still works as a hidden alias but is hidden from `--help` and prints a one-line deprecation notice, and will be removed in a later release. ([#6470](#6470)) * Add orthogonal `--no-constraints` and `--no-dbconnect` flags to `databricks environments setup-local`: `--no-constraints` skips writing the remote Python-version and dependency pins, and `--no-dbconnect` skips the databricks-connect dependency. ([#6464](#6464)) * `databricks environments setup-local` now reports a distinct `E_PROVISION_CONFLICT` error code in `--output json` when the project's dependencies conflict with the pins written for the target environment, making the requirements unsatisfiable (the same conflict surfaced as a `W_USER_CONSTRAINT_CONFLICT` warning); it is reported after the project files are written, without attempting the doomed provisioning, while other provisioning failures continue to report `E_PROVISION`. ([#6479](#6479)) * `databricks ssh connect` and `ssh setup` now verify the tunnel's SSH host key against the key the workspace published for the connection, recorded in `~/.databricks/ssh-tunnel-known-hosts/<name>` instead of `~/.ssh/known_hosts`. Reconnecting with a name used before no longer fails with `Host key verification failed` when the compute behind that name changed, and no longer needs a manual `ssh-keygen -R`; host blocks written by an earlier `databricks ssh setup` pick this up once you re-run it. ([#6557](#6557)) * Stop `databricks ssh connect --ide` from adding a duplicate entry to the IDE's Remote Explorer on every connect: the remote authority is now the SSH host alias alone, instead of embedding the per-instance remote OS user. ([#6550](#6550)) * Add `--max-clients` and `--server-timeout` flags to `databricks ssh setup`, and `--server-timeout` to `databricks ssh connect`. Both are fixed when the SSH tunnel server job is submitted, so `ssh setup` now serializes them into the generated `ProxyCommand` instead of falling back to the built-in defaults. ([#6547](#6547)) * `ssh connect` sessions no longer end when the tunnel's websocket connection is lost. The CLI reattaches to the running session and replays the bytes that were missed, so the shell and everything running in it stay intact, and a transient failure to open a replacement connection for the periodic auth refresh is retried rather than ending the session. Reattaching requires an SSH server started by a CLI that supports it; against an older server the connection behaves as before. ([#6558](#6558)) ### Bundles * Added PyDABs (Python) support for secrets: `Resources.add_secret` and the `secret_mutator` decorator. ([#6553](#6553)) * Fix job and pipeline environment dependencies with a `*` version wildcard (e.g. `numpy==2.5.*`) being treated as local file paths. ([#6555](#6555)) * Add the `postgres_snapshot_schedules` bundle resource for managing a Lakebase Postgres branch's automatic-snapshot schedule (direct deployment engine only). ([#6449](#6449)) ### Dependency Updates * Bump `github.com/databricks/databricks-sdk-go` from v0.175.0 to v0.177.0. ([#6448](#6448)) * Bump Terraform provider from v1.128.0 to v1.131.0. ([#6544](#6544))
… bundle summary (#6663) Implement `GetName()` for postgres resources. They don't have a display name, but their resource path (e.g. `/projects/my-project/branches/my-branch`) is their effective name. Below a list of how `GetName()` is currently used. ### Resources with a dedicated name / display field | Resource | `GetName()` returns | |---|---| | Job | `Name` | | Pipeline | `Name` | | Cluster | `ClusterName` | | ClusterPolicy | `Name` | | InstancePool | `InstancePoolName` | | SqlWarehouse | `Name` | | Volume | `Name` | | Schema | `Name` | | Catalog | `Name` | | RegisteredModel | `Name` | | MlflowModel | `Name` | | MlflowExperiment | `Name` | | ModelServingEndpoint | `Name` | | VectorSearchIndex | `Name` | | VectorSearchEndpoint | `Name` | | ExternalLocation | `Name` | | DatabaseInstance | `Name` | | DatabaseCatalog | `Name` | | SyncedDatabaseTable | `Name` | | Alert | `DisplayName` | | Dashboard | `DisplayName` | | PostgresProject | `DisplayName` | | GenieSpace | `Title` | | QualityMonitor | `TableName` | ### Resources with no name field — identified by ID / hierarchical name | Resource | `GetName()` returns | | |---|---|---| | **PostgresBranch** | `ID` → `projects/{p}/branches/{b}` | ⭐ this PR (was `""`) | | **PostgresEndpoint** | `ID` → `.../endpoints/{e}` | ⭐ this PR (was `""`) | | **PostgresDatabase** | `ID` → `.../databases/{d}` | ⭐ this PR (was `""`) | | **PostgresRole** | `ID` → `.../roles/{r}` | ⭐ this PR (was `""`) | | PostgresSnapshotSchedule | `ID` → `{branch}/snapshot-schedule` | precedent (#6449) | | PostgresSyncedTable | `ID` (strips `synced_tables/` prefix) | | | PostgresCatalog | `CatalogId` | | | Snapshot | `RelativePath()` | | | App | `ID` if set, else `Name` | | | Secret | `ID` if set, else `{catalog}.{schema}.{name}` | | | SecretScope | `ID` if set, else `Name` | | ### Resources with no name at all | Resource | `GetName()` returns | | |---|---|---| | JobRun | `""` | a run has no name (and does have a URL) | This pull request and its description were written by Isaac. --------- Co-authored-by: Isaac <no-reply@databricks.com>
Changes
Add the
postgres_snapshot_schedulesbundle resource, which manages the automatic-snapshot schedule of a Lakebase Postgres branch.The snapshot schedule is a per-branch singleton with no create or delete API — only
GetSnapshotScheduleandUpdateSnapshotSchedule. The resource maps the lifecycle onto that single write call:UpdateSnapshotSchedule(update_maskschedule, awaiting the long-running operation);bundle destroy;branchcomposes the schedule's hierarchical name and is a provided id field (a change recreates).Modeled on the existing
postgres_*resources. Direct engine only: the schedule was added in databricks-sdk-go v0.177.0 and the pinned Terraform provider has no equivalent resource yet, so the acceptance and bind tests are pinned to the direct engine.Tests
bundle/direct/dresources);Acceptance goldens regenerated via
-update; the verify pass is green apart from pre-existing load-induced terraform-engine timeout flakes, each confirmed passing in isolation.