From 29a9b34ef965449f94478df07fd1b19322cff60f Mon Sep 17 00:00:00 2001 From: mliptak0 Date: Tue, 11 Aug 2026 16:31:17 +0200 Subject: [PATCH] HYPERFLEET-1468 - feat: Add the resources tenancy column and GIN index --- docs/database.md | 3 +- pkg/api/resource.go | 1 + .../202608111200_add_resource_tenancy.go | 35 +++++++++++++++++++ pkg/db/migrations/migration_structs.go | 1 + 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 pkg/db/migrations/202608111200_add_resource_tenancy.go diff --git a/docs/database.md b/docs/database.md index a507f771..5a16ffde 100644 --- a/docs/database.md +++ b/docs/database.md @@ -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. @@ -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 diff --git a/pkg/api/resource.go b/pkg/api/resource.go index 694bc5a5..7a33a827 100644 --- a/pkg/api/resource.go +++ b/pkg/api/resource.go @@ -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"` diff --git a/pkg/db/migrations/202608111200_add_resource_tenancy.go b/pkg/db/migrations/202608111200_add_resource_tenancy.go new file mode 100644 index 00000000..1586a518 --- /dev/null +++ b/pkg/db/migrations/202608111200_add_resource_tenancy.go @@ -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 + }, + } +} diff --git a/pkg/db/migrations/migration_structs.go b/pkg/db/migrations/migration_structs.go index 87ebd305..bf695e16 100755 --- a/pkg/db/migrations/migration_structs.go +++ b/pkg/db/migrations/migration_structs.go @@ -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.