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
4 changes: 2 additions & 2 deletions .forbidden-terms-whitelist.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@
},
{
"filename": "specs/platform/global-architecture.spec.md",
"line": 1164,
"line": 1190,
"rationale": "Example directory names 'vteam-stage'/'vteam-uat' in a GitOps directory-tree illustration reference real external cluster environments; they are example paths, not the discouraged term."
},
{
"filename": "CLAUDE.md",
"line": 125,
"rationale": "The em dash appears inside a convention rule that documents the character itself as a forbidden term; it must be shown literally to be unambiguous."
"rationale": "The em dash appears inside the convention rule that documents the character itself as a forbidden term; it must be shown literally to be unambiguous."
}
]
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ jobs:
strategy:
fail-fast: false
matrix:
database-provider: [deployment, cnpg]
database-provider: [deployment, cnpg, external]
# This job now also absorbs the Konflux build wait that plan-images used to
# hold: kind-up runs first and overlaps the remote build, the
# wait-on-check-action steps then block for whatever build time is left (up
Expand Down
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ help:
@echo ""
@echo " Kind Specific"
@echo " kind-env Print environment variables for local setup"
@echo " kind-up Create cluster + deploy all components (OIDC enabled)"
@echo " LOCAL_IMAGES=true: build from working tree (default)"
@echo " LOCAL_IMAGES=true BUILD_SOURCE=baseline: build from origin/main"
@echo " LOCAL_IMAGES=true KIND_SKIP_BUILD=true: reuse existing local images"
@echo " KIND_SKIP_SEED=true: defer seeding (run kind-seed later)"
@echo " kind-seed Seed platform resources into a running cluster"
@echo " SKIP_SEED=true: defer seeding during kind-up / openshift-up"
@echo " SEED_STRICT=true: fail the command if seeding is incomplete"
Expand Down
12 changes: 12 additions & 0 deletions components/api-server/openapi/openapi.managedDatabases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,12 @@ components:
type: string
connection_secret:
type: string
description: >-
For provider "external": the NAMESPACE holding the admin credentials
Secret, not a Secret name. Must be a bare namespace name (no "/")
prefixed with "hypershell-managed-db-" and a valid DNS-1123 label.
The Secret inside it always has the fixed name
"hypershell-managed-db-credentials". Ignored by other providers.
status:
type: string
# NEW SCHEMA START
Expand Down Expand Up @@ -276,6 +282,12 @@ components:
type: string
connection_secret:
type: string
description: >-
For provider "external": the NAMESPACE holding the admin credentials
Secret, not a Secret name. Must be a bare namespace name (no "/")
prefixed with "hypershell-managed-db-" and a valid DNS-1123 label.
The Secret inside it always has the fixed name
"hypershell-managed-db-credentials". Ignored by other providers.
status:
type: string
parameters:
Expand Down

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

10 changes: 10 additions & 0 deletions components/api-server/pkg/api/openapi/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2289,6 +2289,11 @@ components:
instance_class:
type: string
connection_secret:
description: "For provider \"external\": the NAMESPACE holding the admin\
\ credentials Secret, not a Secret name. Must be a bare namespace name\
\ (no \"/\") prefixed with \"hypershell-managed-db-\" and a valid DNS-1123\
\ label. The Secret inside it always has the fixed name \"hypershell-managed-db-credentials\"\
. Ignored by other providers."
type: string
status:
type: string
Expand Down Expand Up @@ -2382,6 +2387,11 @@ components:
instance_class:
type: string
connection_secret:
description: "For provider \"external\": the NAMESPACE holding the admin\
\ credentials Secret, not a Secret name. Must be a bare namespace name\
\ (no \"/\") prefixed with \"hypershell-managed-db-\" and a valid DNS-1123\
\ label. The Secret inside it always has the fixed name \"hypershell-managed-db-credentials\"\
. Ignored by other providers."
type: string
status:
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Name | Type | Description | Notes
**Engine** | Pointer to **string** | | [optional]
**EngineVersion** | Pointer to **string** | | [optional]
**InstanceClass** | Pointer to **string** | | [optional]
**ConnectionSecret** | Pointer to **string** | | [optional]
**ConnectionSecret** | Pointer to **string** | For provider \"external\": the NAMESPACE holding the admin credentials Secret, not a Secret name. Must be a bare namespace name (no \"/\") prefixed with \"hypershell-managed-db-\" and a valid DNS-1123 label. The Secret inside it always has the fixed name \"hypershell-managed-db-credentials\". Ignored by other providers. | [optional]
**Status** | Pointer to **string** | | [optional]

## Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Name | Type | Description | Notes
**Engine** | Pointer to **string** | | [optional]
**EngineVersion** | Pointer to **string** | | [optional]
**InstanceClass** | Pointer to **string** | | [optional]
**ConnectionSecret** | Pointer to **string** | | [optional]
**ConnectionSecret** | Pointer to **string** | For provider \"external\": the NAMESPACE holding the admin credentials Secret, not a Secret name. Must be a bare namespace name (no \"/\") prefixed with \"hypershell-managed-db-\" and a valid DNS-1123 label. The Secret inside it always has the fixed name \"hypershell-managed-db-credentials\". Ignored by other providers. | [optional]
**Status** | Pointer to **string** | | [optional]

## Methods
Expand Down
29 changes: 15 additions & 14 deletions components/api-server/pkg/api/openapi/model_managed_database.go

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

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

37 changes: 37 additions & 0 deletions components/api-server/plugins/gateways/placement.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ type DatabaseLookup interface {
FindSole(ctx context.Context) (databaseID string, err error)
}

// DatabaseSelector resolves placement when more than one candidate
// ManagedDatabase is allowed to exist. FindOldest returns the earliest-created
// candidate, or "" when none exist.
type DatabaseSelector interface {
FindOldest(ctx context.Context) (databaseID string, err error)
}

type DatabaseCreator interface {
CreateForGateway(ctx context.Context, gatewayName string) (databaseID string, err error)
}
Expand Down Expand Up @@ -94,6 +101,36 @@ func NewDeploymentPlacement(dbs DatabaseCreator) PlacementResolver {
return &deploymentPlacement{dbs: dbs}
}

// externalPlacement assigns every new gateway to the first-created external
// ManagedDatabase. More than one registration is not an error: an operator may
// register a second external server ahead of a migration without intending to
// move where new gateways land. Only the empty result is rejected.
//
// Selection happens at gateway creation only. Once assigned, database_id is
// fixed for the gateway's lifetime, so a later registration never relocates an
// existing gateway.
type externalPlacement struct {
dbs DatabaseSelector
}

func NewExternalPlacement(dbs DatabaseSelector) PlacementResolver {
return &externalPlacement{dbs: dbs}
}

func (p *externalPlacement) Resolve(ctx context.Context, gw *Gateway) error {
gw.DatabaseId = ""

dbID, err := p.dbs.FindOldest(ctx)
if err != nil {
return newPlacementDependencyError("resolve external database", err)
}
if dbID == "" {
return newPlacementValidationError("no external ManagedDatabase is registered; register one before creating gateways")
}
gw.DatabaseId = dbID
return nil
}

func (p *deploymentPlacement) Resolve(ctx context.Context, gw *Gateway) error {
// A deployment database is dedicated to exactly one gateway. Ignore any
// client-provided database_id and always create the server-owned resource.
Expand Down
Loading
Loading