Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ API lifecycle:

Key workflows in `server/internal/workflows/`:
- **UpdateDatabase**: Creates/updates database (handles node additions, configuration updates)
- **MajorVersionUpgrade**: Dedicated rolling upgrade of a database's Spock major version, one node at a time; independent of UpdateDatabase's patch/minor upgrade path
- **DeleteDatabase**: Removes database and cleanup
- **PgBackRestBackup**: Initiates backup operations
- **PgBackRestRestore**: Performs in-place restores
Expand Down
27 changes: 27 additions & 0 deletions api/apiv1/design/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,33 @@ var _ = g.Service("control-plane", func() {
})
})

g.Method("apply-major-upgrade", func() {
g.Description("Applies a dedicated Spock major-version upgrade (e.g. 5.x to 6.x) to a database, rolling through each node one at a time rather than redeploying uniformly. Independent of apply-upgrade, which is restricted to same-major minor/patch bumps and cannot reach a different Spock major.")
g.Meta("openapi:summary", "Apply Spock major-version upgrade")
g.Payload(func() {
g.Attribute("database_id", Identifier, func() {
g.Description("ID of the database to upgrade.")
g.Example("my-app")
})
g.Attribute("request", ApplyMajorUpgradeRequest)

g.Required("database_id", "request")
})
g.Result(ApplyMajorUpgradeResponse)
g.Error("cluster_not_initialized")
g.Error("database_not_modifiable")
g.Error("invalid_input")
g.Error("not_found")
g.Error("operation_already_in_progress")

g.HTTP(func() {
g.POST("/v1/databases/{database_id}/upgrade-major")
g.Body("request")

g.Meta("openapi:tag:Database")
})
})

g.Method("delete-database", func() {
g.Description("Deletes a database from the cluster.")
g.Meta("openapi:summary", "Delete database")
Expand Down
42 changes: 42 additions & 0 deletions api/apiv1/design/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ var DatabaseNodeSpec = g.Type("DatabaseNodeSpec", func() {
g.Example("17.6")
g.Meta("struct:tag:json", "postgres_version,omitempty")
})
g.Attribute("spock_version", g.String, func() {
g.Description("The major version of the Spock extension for this node. Overrides the Spock version set in the DatabaseSpec. Only settable through the dedicated major-version upgrade action — rejected on create-database and on the general update-database path, since a per-node major mismatch is only ever a deliberate, transient state during a rolling major-version upgrade, never a steady-state topology choice.")
g.Pattern(spockVersionPattern)
g.Example("6")
g.Meta("struct:tag:json", "spock_version,omitempty")
})
g.Attribute("port", g.Int, func() {
g.Description("The port used by the Postgres database for this node. Overrides the Postgres port set in the DatabaseSpec.")
g.Minimum(0)
Expand Down Expand Up @@ -1366,6 +1372,42 @@ var ApplyUpgradeResponse = g.Type("ApplyUpgradeResponse", func() {
})
})

var ApplyMajorUpgradeRequest = g.Type("ApplyMajorUpgradeRequest", func() {
g.Description("Request to upgrade a database across a Spock major version boundary (e.g. 5.x to 6.x). Deliberately separate from ApplyUpgradeRequest: a major-version bump changes replication protocol behavior and is executed one node at a time (rolling), not redeployed uniformly like a minor/patch bump.")
g.Attribute("target_spock_version", g.String, func() {
g.Description("The Spock major version to upgrade to.")
g.Pattern(spockVersionPattern)
g.Example("6")
g.Meta("struct:tag:json", "target_spock_version")
})
g.Attribute("image", g.String, func() {
g.Description("Full container image reference of the upgrade target. Must match the image field of a manifest entry whose spock_version is target_spock_version and whose postgres_version matches the database's current postgres_version. Unlike ApplyUpgradeRequest, the target entry is not required to have stability \"stable\" — a Spock major version is expected to still be in a \"dev\" stability window while this action is in use for it.")
g.Example("ghcr.io/pgedge/pgedge-postgres:18.4-spock6.0.0-standard-1")
g.MinLength(1)
g.Meta("struct:tag:json", "image")
})
g.Attribute("node_order", g.ArrayOf(g.String), func() {
g.Description("Explicit order (by node name) in which nodes are upgraded, one at a time. Defaults to the order nodes appear in the database spec if omitted. Every node in the database must appear exactly once.")
g.Example([]string{"n2", "n1", "n3"})
g.Meta("struct:tag:json", "node_order,omitempty")
})

g.Required("target_spock_version", "image")
})

var ApplyMajorUpgradeResponse = g.Type("ApplyMajorUpgradeResponse", func() {
g.Attribute("task", Task, func() {
g.Description("The task tracking the major-version upgrade operation.")
g.Meta("struct:tag:json", "task")
})
g.Attribute("database", Database, func() {
g.Description("The database being upgraded.")
g.Meta("struct:tag:json", "database")
})

g.Required("task", "database")
})

var BackupDatabaseNodeResponse = g.Type("BackupDatabaseNodeResponse", func() {
g.Attribute("task", Task, func() {
g.Description("The task that will backup this database node.")
Expand Down
23 changes: 22 additions & 1 deletion api/apiv1/gen/control_plane/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions api/apiv1/gen/control_plane/endpoints.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 50 additions & 1 deletion api/apiv1/gen/control_plane/service.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading