Skip to content
Merged
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
10 changes: 0 additions & 10 deletions base/core/gintesting.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,6 @@ type ContextKV struct {

var V3APICtx = ContextKV{Key: utils.KeyApiver, Value: 3}

var WorkspacesTestCtx = ContextKV{
Key: utils.KeyInventoryWorkspaces,
Value: []string{
"00000000-0000-0000-0000-000000000001",
"00000000-0000-0000-0000-000000000002",
"00000000-0000-0000-0000-999999999999",
},
}

func InitRouter(handler gin.HandlerFunc, contextKVs ...ContextKV) *gin.Engine {
return InitRouterWithPath(handler, "/", contextKVs...)
}
Expand All @@ -41,7 +32,6 @@ func InitRouterWithParams(handler gin.HandlerFunc, account int, method, path str
router.Use(func(c *gin.Context) {
// set default api version for tests to latest
c.Set(utils.KeyApiver, LatestAPIVersion)
c.Set(utils.KeyInventoryWorkspaces, WorkspacesTestCtx.Value)
for _, kv := range contextKVs {
c.Set(kv.Key, kv.Value)
}
Expand Down
34 changes: 22 additions & 12 deletions base/database/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ func (j joinsT) apply(tx *gorm.DB) *gorm.DB {
return tx
}

func Systems(tx *gorm.DB, accountID int, workspaceIDs []string, joins ...join) *gorm.DB {
func Systems(tx *gorm.DB, accountID int, groups map[string]string, joins ...join) *gorm.DB {
tx = tx.Table("system_inventory si").
Joins("JOIN system_patch spatch ON si.id = spatch.system_id AND si.rh_account_id = spatch.rh_account_id").
Where("si.rh_account_id = ?", accountID)
tx = (joinsT)(joins).apply(tx)
return ApplyInventoryWorkspaceFilter(tx, workspaceIDs)
return ApplyInventoryWorkspaceFilter(tx, groups)
}

func SystemAdvisories(tx *gorm.DB, accountID int, workspaceIDs []string, joins ...join) *gorm.DB {
tx = Systems(tx, accountID, workspaceIDs).
func SystemAdvisories(tx *gorm.DB, accountID int, groups map[string]string, joins ...join) *gorm.DB {
tx = Systems(tx, accountID, groups).
Joins("JOIN system_advisories sa on sa.system_id = si.id AND sa.rh_account_id = ?", accountID)
return (joinsT)(joins).apply(tx)
}
Expand All @@ -46,8 +46,8 @@ func SystemPackagesShort(tx *gorm.DB, accountID int, joins ...join) *gorm.DB {
return (joinsT)(joins).apply(tx)
}

func SystemPackages(tx *gorm.DB, accountID int, workspaceIDs []string, joins ...join) *gorm.DB {
tx = Systems(tx, accountID, workspaceIDs).
func SystemPackages(tx *gorm.DB, accountID int, groups map[string]string, joins ...join) *gorm.DB {
tx = Systems(tx, accountID, groups).
Joins("JOIN system_package2 spkg on spkg.system_id = si.id AND spkg.rh_account_id = ?", accountID).
Joins("JOIN package p on p.id = spkg.package_id").
Joins("JOIN package_name pn on pn.id = spkg.name_id")
Expand All @@ -65,9 +65,9 @@ func PackageByName(tx *gorm.DB, pkgName string, joins ...join) *gorm.DB {
return (joinsT)(joins).apply(tx)
}

func SystemAdvisoriesByInventoryID(tx *gorm.DB, accountID int, workspaceIDs []string, inventoryID string,
func SystemAdvisoriesByInventoryID(tx *gorm.DB, accountID int, groups map[string]string, inventoryID string,
joins ...join) *gorm.DB {
tx = SystemAdvisories(tx, accountID, workspaceIDs).Where("si.inventory_id = ?::uuid", inventoryID)
tx = SystemAdvisories(tx, accountID, groups).Where("si.inventory_id = ?::uuid", inventoryID)
return (joinsT)(joins).apply(tx)
}

Expand Down Expand Up @@ -240,11 +240,21 @@ func ReadReplicaConfigured() bool {
return len(utils.CoreCfg.DBReadReplicaHost) > 0 && utils.CoreCfg.DBReadReplicaPort != 0
}

func ApplyInventoryWorkspaceFilter(tx *gorm.DB, workspaceIDs []string) *gorm.DB {
if len(workspaceIDs) == 0 {
utils.LogWarn("there should always be some workspaces, at least root workspace")
func ApplyInventoryWorkspaceFilter(tx *gorm.DB, groups map[string]string) *gorm.DB {
Comment thread
MichaelMraka marked this conversation as resolved.
if _, ok := groups[utils.KeyGrouped]; !ok {
if _, ok := groups[utils.KeyUngrouped]; ok {
// show only systems with '[]' group
return tx.Where("si.workspaces = '[]'")
}
// return query without WHERE if there are no groups
return tx
}

db := DB.Where("si.workspaces @> ANY (?::jsonb[])", groups[utils.KeyGrouped])
if _, ok := groups[utils.KeyUngrouped]; ok {
db = db.Or("si.workspaces = '[]'")
}
return tx.Where("si.workspace_id IN (?)", workspaceIDs)
return tx.Where(db)
}

// LEFT JOIN templates to spatch (system_patch)
Expand Down
33 changes: 19 additions & 14 deletions base/database/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,38 @@ var (
// counts of systems from system_inventory (+ system_patch join in Systems())
nGroup1 int64 = 7
nGroup2 int64 = 2
nUngrouped int64 = 9
nUngrouped int64 = 7
nAll int64 = 18
)
var nonExisting = "00000000-0000-0000-3333-000000000000"

var testCases = []map[int64][]string{
{nGroup1: {"00000000-0000-0000-0000-000000000001"}},
{nGroup2: {"00000000-0000-0000-0000-000000000002"}},
{nGroup1 + nGroup2: {"00000000-0000-0000-0000-000000000001", "00000000-0000-0000-0000-000000000002"}},
{nGroup1 + nUngrouped: {"00000000-0000-0000-0000-000000000001", "00000000-0000-0000-0000-999999999999"}},
{nUngrouped: {nonExisting, "00000000-0000-0000-0000-999999999999"}},
{0: {nonExisting}},
{nUngrouped: {"00000000-0000-0000-0000-999999999999"}},
{nAll: {"00000000-0000-0000-0000-000000000001", "00000000-0000-0000-0000-000000000002",
"00000000-0000-0000-0000-999999999999"}},
var testCases = []map[int64]map[string]string{
{nGroup1: {utils.KeyGrouped: `{"[{\"id\":\"inventory-group-1\"}]"}`}},
{nGroup2: {utils.KeyGrouped: `{"[{\"id\":\"inventory-group-2\"}]"}`}},
{nGroup1 + nGroup2: {utils.KeyGrouped: `{"[{\"id\":\"inventory-group-1\"}]","[{\"id\":\"inventory-group-2\"}]"}`}},
{nGroup1 + nUngrouped: {
utils.KeyGrouped: `{"[{\"id\":\"inventory-group-1\"}]"}`,
utils.KeyUngrouped: "[]",
}},
{nUngrouped: {
utils.KeyGrouped: `{"[{\"id\":\"non-existing-group\"}]"}`,
utils.KeyUngrouped: "[]",
}},
{0: {utils.KeyGrouped: `{"[{\"id\":\"non-existing-group\"}]"}`}},
{nUngrouped: {utils.KeyUngrouped: "[]"}},
{nAll: {}},
{nAll: nil},
}

func TestApplyInventoryWorkspaceFilter(t *testing.T) {
utils.SkipWithoutDB(t)
Configure()

for _, tc := range testCases {
for expectedCount, workspaceIDs := range tc {
for expectedCount, groups := range tc {
var count int64
ApplyInventoryWorkspaceFilter(DB.Table("system_inventory si").
Joins("JOIN system_patch spatch ON si.id = spatch.system_id AND si.rh_account_id = spatch.rh_account_id"),
workspaceIDs).Count(&count)
groups).Count(&count)
assert.Equal(t, expectedCount, count)
}
}
Expand Down
32 changes: 32 additions & 0 deletions base/inventory/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package inventory

import (
"app/base/types"
"database/sql/driver"
"encoding/json"
"errors"

"github.com/google/uuid"
)
Expand Down Expand Up @@ -83,6 +86,35 @@ type Group struct {
Name string `json:"name"`
}

// Groups is a slice of Group that implements driver.Valuer and sql.Scanner
// for storing/loading as JSONB in the database (e.g. system_inventory.workspaces).
type Groups []Group

// Value implements driver.Valuer for GORM: marshal to JSON for DB write.
func (g *Groups) Value() (driver.Value, error) {
if g == nil {
return nil, nil
}
return json.Marshal(g)
}

// Scan implements sql.Scanner for GORM: unmarshal from JSON on DB read.
func (g *Groups) Scan(value interface{}) error {
if value == nil {
*g = nil
return nil
}
b, ok := value.([]byte)
if !ok {
return errors.New("inventory.Groups: type assertion to []byte failed")
}
if len(b) == 0 {
*g = nil
return nil
}
return json.Unmarshal(b, g)
}

type Workloads struct {
Sap SapWorkload `json:"sap,omitempty"`
Ansible AnsibleWorkload `json:"ansible,omitempty"`
Expand Down
8 changes: 4 additions & 4 deletions base/models/models.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package models

import (
"app/base/inventory"
"time"

"github.com/google/uuid"
Expand Down Expand Up @@ -70,10 +71,9 @@ type SystemInventory struct {
BuiltPkgcache bool `gorm:"column:built_pkgcache"`
Arch *string
Bootc bool
Tags []byte `gorm:"column:tags"`
Created time.Time // set by trigger system_platform_insert_trigger
WorkspaceID *uuid.UUID `gorm:"column:workspace_id"`
WorkspaceName *string `gorm:"column:workspace_name"`
Tags []byte `gorm:"column:tags"`
Created time.Time // set by trigger system_platform_insert_trigger
Workspaces *inventory.Groups `gorm:"column:workspaces"`
StaleTimestamp *time.Time
StaleWarningTimestamp *time.Time
CulledTimestamp *time.Time
Expand Down
14 changes: 8 additions & 6 deletions base/utils/gin.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import (
)

const (
KeyApiver = "apiver"
KeyAccount = "account"
KeyOrgID = "org_id"
KeyUser = "user"
KeySystem = "system_cn"
KeyInventoryWorkspaces = "workspaceIDs"
KeyApiver = "apiver"
KeyAccount = "account"
KeyOrgID = "org_id"
KeyUser = "user"
KeySystem = "system_cn"
KeyInventoryGroups = "inventoryGroups"
KeyGrouped = "grouped"
KeyUngrouped = "ungrouped"
// ReadHeaderTimeout same as nginx default
ReadHeaderTimeout = 60 * time.Second
)
Expand Down
14 changes: 1 addition & 13 deletions database_admin/migrations/153_simplify_workspaces.up.sql
Original file line number Diff line number Diff line change
@@ -1,13 +1 @@
ALTER TABLE system_inventory
ADD COLUMN workspace_id UUID,
ADD COLUMN workspace_name TEXT CHECK (NOT empty(workspace_name));

UPDATE system_inventory
SET workspace_id = (workspaces->0->>'id')::UUID,
workspace_name = workspaces->0->>'name';

CREATE INDEX IF NOT EXISTS system_inventory_workspace_id_index ON system_inventory (workspace_id);
CREATE INDEX IF NOT EXISTS system_inventory_workspace_name_index ON system_inventory (workspace_name);

ALTER TABLE system_inventory
DROP COLUMN workspaces;
-- noop
22 changes: 22 additions & 0 deletions database_admin/migrations/154_simplify_workspaces.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM pg_attribute a
JOIN pg_class c ON c.oid = a.attrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relname = 'system_inventory' AND a.attname = 'workspace_id'
AND a.attnum > 0 AND NOT a.attisdropped)
THEN
ALTER TABLE system_inventory
ADD COLUMN workspaces JSONB;

UPDATE system_inventory
SET workspaces = JSONB_BUILD_ARRAY(JSONB_BUILD_OBJECT('id', workspace_id, 'name', workspace_name));

CREATE INDEX IF NOT EXISTS system_inventory_workspaces_index ON system_inventory USING GIN (workspaces);

ALTER TABLE system_inventory
DROP COLUMN workspace_id,
DROP COLUMN workspace_name;
END IF;
END $$;

8 changes: 3 additions & 5 deletions database_admin/schema/create_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS schema_migrations


INSERT INTO schema_migrations
VALUES (153, false);
VALUES (154, false);

-- ---------------------------------------------------------------------------
-- Functions
Expand Down Expand Up @@ -592,8 +592,7 @@ CREATE TABLE IF NOT EXISTS system_inventory
ansible_workload_controller_version TEXT CHECK (NOT empty(ansible_workload_controller_version)),
mssql_workload BOOLEAN NOT NULL DEFAULT false,
mssql_workload_version TEXT CHECK (NOT empty(mssql_workload_version)),
workspace_id UUID,
workspace_name TEXT CHECK (NOT empty(workspace_name)),
workspaces JSONB,
PRIMARY KEY (rh_account_id, id),
UNIQUE (rh_account_id, inventory_id)
) PARTITION BY HASH (rh_account_id);
Expand Down Expand Up @@ -625,8 +624,7 @@ SELECT create_table_partition_triggers('system_inventory_on_update',
CREATE INDEX IF NOT EXISTS system_inventory_inventory_id_idx ON system_inventory (inventory_id);
CREATE INDEX IF NOT EXISTS system_inventory_tags_index ON system_inventory USING GIN (tags JSONB_PATH_OPS);
CREATE INDEX IF NOT EXISTS system_inventory_stale_timestamp_index ON system_inventory (stale_timestamp);
CREATE INDEX IF NOT EXISTS system_inventory_workspace_id_index ON system_inventory (workspace_id);
CREATE INDEX IF NOT EXISTS system_inventory_workspace_name_index ON system_inventory (workspace_name);
CREATE INDEX IF NOT EXISTS system_inventory_workspaces_index ON system_inventory USING GIN (workspaces);

CREATE TABLE IF NOT EXISTS deleted_system
(
Expand Down
Loading
Loading