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
3 changes: 2 additions & 1 deletion docs/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ HyperFleet API uses PostgreSQL with GORM ORM. The schema follows a simple relati
## Core Tables

### resources
Unified resource table for all entity types (Cluster, NodePool, Channel, Version, WifConfig, etc.). Stores `kind`, `name`, `spec` (JSONB), and optional owner references (`owner_id`, `owner_kind`, `owner_href`) for parent-child relationships. Labels are stored in the separate `resource_labels` table; conditions in `resource_conditions`. Uses `deleted_time`/`deleted_by` for soft delete. Unique name constraints are scoped by `kind` and `owner_id`.
Unified resource table for all entity types (Cluster, NodePool, Channel, Version, WifConfig, etc.). Stores `kind`, `name`, `spec` (JSONB), `tenancy` (JSONB), and optional owner references (`owner_id`, `owner_kind`, `owner_href`) for parent-child relationships. Labels are stored in the separate `resource_labels` table; conditions in `resource_conditions`. Uses `deleted_time`/`deleted_by` for soft delete. Unique name constraints are scoped by `kind` and `owner_id`.

### adapter_statuses
Status records for resources. Stores adapter-reported conditions in JSONB format. No soft delete — rows are hard-deleted or replaced.
Expand All @@ -34,6 +34,7 @@ resources (self-referencing parent-child via owner_id)

Flexible schema storage for:
- **spec** - Provider-specific resource configurations
- **tenancy** - Tenancy dimensions carried by the resource (not null, defaults to `{}`); indexed with a GIN index using `jsonb_path_ops` to serve containment queries
- **conditions** - Adapter status condition arrays
- **data** - Adapter metadata

Expand Down
1 change: 1 addition & 0 deletions pkg/api/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Resource struct {
UpdatedBy string `json:"updated_by" gorm:"size:255;not null"`
Labels []ResourceLabel `json:"-" gorm:"foreignKey:ResourceID;references:ID"`
Spec datatypes.JSON `json:"spec" gorm:"type:jsonb;not null"`
Tenancy datatypes.JSON `json:"tenancy" gorm:"type:jsonb;not null;default:'{}'"`
Conditions []ResourceCondition `json:"-" gorm:"foreignKey:ResourceID;references:ID"`
References []ResourceReference `json:"-" gorm:"foreignKey:SourceID;references:ID"`
Generation int32 `json:"generation" gorm:"default:1;not null"`
Expand Down
35 changes: 35 additions & 0 deletions pkg/db/migrations/202608111200_add_resource_tenancy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package migrations

import (
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)

func addResourceTenancy() *gormigrate.Migration {
return &gormigrate.Migration{
ID: "202608111200",
Migrate: func(tx *gorm.DB) error {
if err := tx.Exec(
"ALTER TABLE resources ADD COLUMN IF NOT EXISTS tenancy JSONB NOT NULL DEFAULT '{}'::jsonb;",
).Error; err != nil {
return err
}

return tx.Exec(
"CREATE INDEX IF NOT EXISTS idx_resources_tenancy " +
"ON resources USING GIN (tenancy jsonb_path_ops);",
).Error
},
Rollback: func(tx *gorm.DB) error {
if err := tx.Exec(
"DROP INDEX IF EXISTS idx_resources_tenancy;",
).Error; err != nil {
return err
}

return tx.Exec(
"ALTER TABLE resources DROP COLUMN IF EXISTS tenancy;",
).Error
Comment on lines +30 to +32

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not drop resources.tenancy in the automatic rollback.

DROP COLUMN permanently deletes all stored tenancy JSONB values. This conflicts with docs/database.md (Line 64-66), which states that migrations never drop columns or tables. Remove the destructive column drop from the production rollback. Use a separate, audited procedure if data destruction is ever required.

As per path instructions, migrations must be backward compatible and must not create data-loss scenarios.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/db/migrations/202608111200_add_resource_tenancy.go` around lines 30 - 32,
Remove the ALTER TABLE resources DROP COLUMN IF EXISTS tenancy operation from
the migration rollback function. Keep the rollback backward-compatible without
deleting stored tenancy data, and leave any destructive cleanup to a separate
audited procedure.

Source: Path instructions

},
}
}
1 change: 1 addition & 0 deletions pkg/db/migrations/migration_structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var MigrationList = []*gormigrate.Migration{
addAdapterStatus(),
addResources(),
addConditionStatusIndex(),
addResourceTenancy(),
}

// Model represents the base model struct. All entities will have this struct embedded.
Expand Down