Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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