diff --git a/base/database/utils.go b/base/database/utils.go index 044d6b676..cb7675051 100644 --- a/base/database/utils.go +++ b/base/database/utils.go @@ -26,16 +26,16 @@ func (j joinsT) apply(tx *gorm.DB) *gorm.DB { return tx } -func Systems(tx *gorm.DB, accountID int, groups map[string]string, joins ...join) *gorm.DB { +func Systems(tx *gorm.DB, accountID int, workspaceIDs []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, groups) + return ApplyInventoryWorkspaceFilter(tx, workspaceIDs) } -func SystemAdvisories(tx *gorm.DB, accountID int, groups map[string]string, joins ...join) *gorm.DB { - tx = Systems(tx, accountID, groups). +func SystemAdvisories(tx *gorm.DB, accountID int, workspaceIDs []string, joins ...join) *gorm.DB { + tx = Systems(tx, accountID, workspaceIDs). Joins("JOIN system_advisories sa on sa.system_id = si.id AND sa.rh_account_id = ?", accountID) return (joinsT)(joins).apply(tx) } @@ -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, groups map[string]string, joins ...join) *gorm.DB { - tx = Systems(tx, accountID, groups). +func SystemPackages(tx *gorm.DB, accountID int, workspaceIDs []string, joins ...join) *gorm.DB { + tx = Systems(tx, accountID, workspaceIDs). 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") @@ -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, groups map[string]string, inventoryID string, +func SystemAdvisoriesByInventoryID(tx *gorm.DB, accountID int, workspaceIDs []string, inventoryID string, joins ...join) *gorm.DB { - tx = SystemAdvisories(tx, accountID, groups).Where("si.inventory_id = ?::uuid", inventoryID) + tx = SystemAdvisories(tx, accountID, workspaceIDs).Where("si.inventory_id = ?::uuid", inventoryID) return (joinsT)(joins).apply(tx) } @@ -240,21 +240,11 @@ func ReadReplicaConfigured() bool { return len(utils.CoreCfg.DBReadReplicaHost) > 0 && utils.CoreCfg.DBReadReplicaPort != 0 } -func ApplyInventoryWorkspaceFilter(tx *gorm.DB, groups map[string]string) *gorm.DB { - 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 = '[]'") +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") } - return tx.Where(db) + return tx.Where("si.workspace_id IN (?)", workspaceIDs) } // LEFT JOIN templates to spatch (system_patch) diff --git a/base/database/utils_test.go b/base/database/utils_test.go index 358b3e6c1..9348fce56 100644 --- a/base/database/utils_test.go +++ b/base/database/utils_test.go @@ -11,26 +11,19 @@ var ( // counts of systems from system_inventory (+ system_patch join in Systems()) nGroup1 int64 = 7 nGroup2 int64 = 2 - nUngrouped int64 = 7 + nUngrouped int64 = 9 nAll int64 = 18 ) -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}, +var testCases = []map[int64][]string{ + {nGroup1: {"inventory-group-1"}}, + {nGroup2: {"inventory-group-2"}}, + {nGroup1 + nGroup2: {"inventory-group-1", "inventory-group-2"}}, + {nGroup1 + nUngrouped: {"inventory-group-1", "root-workspace"}}, + {nUngrouped: {"non-existing-group", "root-workspace"}}, + {0: {"non-existing-group"}}, + {nUngrouped: {"root-workspace"}}, + {nAll: {"inventory-group-1", "inventory-group-2", "root-workspace"}}, } func TestApplyInventoryWorkspaceFilter(t *testing.T) { @@ -38,11 +31,11 @@ func TestApplyInventoryWorkspaceFilter(t *testing.T) { Configure() for _, tc := range testCases { - for expectedCount, groups := range tc { + for expectedCount, workspaceIDs := 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"), - groups).Count(&count) + workspaceIDs).Count(&count) assert.Equal(t, expectedCount, count) } } diff --git a/base/inventory/inventory.go b/base/inventory/inventory.go index db615358f..e2cfcdc31 100644 --- a/base/inventory/inventory.go +++ b/base/inventory/inventory.go @@ -2,9 +2,6 @@ package inventory import ( "app/base/types" - "database/sql/driver" - "encoding/json" - "errors" "github.com/google/uuid" ) @@ -86,35 +83,6 @@ 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"` diff --git a/base/models/models.go b/base/models/models.go index 25771cc6d..666a2ef4f 100644 --- a/base/models/models.go +++ b/base/models/models.go @@ -1,7 +1,6 @@ package models import ( - "app/base/inventory" "time" "github.com/google/uuid" @@ -71,9 +70,10 @@ 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 - Workspaces *inventory.Groups `gorm:"column:workspaces"` + Tags []byte `gorm:"column:tags"` + Created time.Time // set by trigger system_platform_insert_trigger + WorkspaceID *string `gorm:"column:workspace_id"` + WorkspaceName *string `gorm:"column:workspace_name"` StaleTimestamp *time.Time StaleWarningTimestamp *time.Time CulledTimestamp *time.Time diff --git a/base/utils/gin.go b/base/utils/gin.go index 73e622384..58501973e 100644 --- a/base/utils/gin.go +++ b/base/utils/gin.go @@ -13,14 +13,12 @@ import ( ) const ( - KeyApiver = "apiver" - KeyAccount = "account" - KeyOrgID = "org_id" - KeyUser = "user" - KeySystem = "system_cn" - KeyInventoryGroups = "inventoryGroups" - KeyGrouped = "grouped" - KeyUngrouped = "ungrouped" + KeyApiver = "apiver" + KeyAccount = "account" + KeyOrgID = "org_id" + KeyUser = "user" + KeySystem = "system_cn" + KeyInventoryWorkspaces = "workspaceIDs" // ReadHeaderTimeout same as nginx default ReadHeaderTimeout = 60 * time.Second ) diff --git a/database_admin/migrations/153_simplify_workspaces.up.sql b/database_admin/migrations/153_simplify_workspaces.up.sql new file mode 100644 index 000000000..fdd0ce7d3 --- /dev/null +++ b/database_admin/migrations/153_simplify_workspaces.up.sql @@ -0,0 +1,13 @@ +ALTER TABLE system_inventory + ADD COLUMN workspace_id TEXT CHECK (NOT empty(workspace_id)), + ADD COLUMN workspace_name TEXT CHECK (NOT empty(workspace_name)); + +UPDATE system_inventory + SET workspace_id = workspaces->0->>'id', + 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; diff --git a/database_admin/schema/create_schema.sql b/database_admin/schema/create_schema.sql index 28a57e442..e14be6288 100644 --- a/database_admin/schema/create_schema.sql +++ b/database_admin/schema/create_schema.sql @@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS schema_migrations INSERT INTO schema_migrations -VALUES (152, false); +VALUES (153, false); -- --------------------------------------------------------------------------- -- Functions @@ -592,7 +592,8 @@ 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)), - workspaces JSONB, + workspace_id TEXT CHECK (NOT empty(workspace_id)), + workspace_name TEXT CHECK (NOT empty(workspace_name)), PRIMARY KEY (rh_account_id, id), UNIQUE (rh_account_id, inventory_id) ) PARTITION BY HASH (rh_account_id); @@ -624,7 +625,8 @@ 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_workspaces_index ON system_inventory USING GIN (workspaces); +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 TABLE IF NOT EXISTS deleted_system ( diff --git a/dev/test_data.sql b/dev/test_data.sql index c4fb5f544..3d8c39ddb 100644 --- a/dev/test_data.sql +++ b/dev/test_data.sql @@ -24,13 +24,13 @@ INSERT INTO template (id, rh_account_id, uuid, environment_id, name, description (3, 1, '99900000-0000-0000-0000-000000000003', '99900000000000000000000000000003', 'temp3-1', NULL, '{"to_time": "2000-01-01T00:00:00+00:00"}', 'x86_64', '8', 'user3'), (4, 3, '99900000-0000-0000-0000-000000000004', '99900000000000000000000000000004', 'temp4-3', 'desc4', '{"to_time": "2000-01-01T00:00:00+00:00"}', 'x86_64', '8', 'user4'); -INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_upload, display_name, reporter_id, arch, tags, created, stale_timestamp, stale_warning_timestamp, workspaces, os_name, os_major, os_minor, rhsm_version, subscription_manager_id, sap_workload, sap_workload_sids, mssql_workload, mssql_workload_version) VALUES -(1, '00000000-0000-0000-0000-000000000001', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2020-09-22 12:00:00-04', '00000000-0000-0000-0000-000000000001', 1, 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"},{"key": "k2", "value": "val2", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[{"id": "inventory-group-1", "name": "group1"}]', 'RHEL', 8, 10, '8.10', NULL, true, ARRAY['ABC', 'DEF', 'GHI'], false, NULL), -(2, '00000000-0000-0000-0000-000000000002', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-09-22 12:00:00-04', '00000000-0000-0000-0000-000000000002', 1, 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"},{"key": "k2", "value": "val2", "namespace": "ns1"},{"key": "k3", "value": "val3", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[{"id": "inventory-group-1", "name": "group1"}]', 'RHEL', 8, 1, '8.1', NULL, true, ARRAY['ABC'], false, NULL), -(3, '00000000-0000-0000-0000-000000000003', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-09-18 12:00:00-04', '00000000-0000-0000-0000-000000000003', 1, 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}, {"key": "k3", "value": "val4", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[{"id": "inventory-group-1", "name": "group1"}]', 'RHEL', 8, 1, '8.0', NULL, true, NULL, false, NULL), -(4, '00000000-0000-0000-0000-000000000004', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-09-18 12:00:00-04', '00000000-0000-0000-0000-000000000004', 1, 'x86_64', '[{"key": "k3", "value": "val4", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[{"id": "inventory-group-1", "name": "group1"}]', 'RHEL', 8, 2, '8.3', 'cccccccc-0000-0000-0001-000000000004', true, NULL, false, NULL), -(5, '00000000-0000-0000-0000-000000000005', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-09-18 12:00:00-04', '00000000-0000-0000-0000-000000000005', 1, 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[{"id": "inventory-group-1", "name": "group1"}]', 'RHEL', 8, 3, '8.3', 'cccccccc-0000-0000-0001-000000000005', true, NULL, false, NULL), -(6, '00000000-0000-0000-0000-000000000006', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-08-26 12:00:00-04', '00000000-0000-0000-0000-000000000006', 1, 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[{"id": "inventory-group-1", "name": "group1"}]', 'RHEL', 7, 3, '7.3', NULL, true, NULL, true, '15.3.0'); +INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_upload, display_name, reporter_id, arch, tags, created, stale_timestamp, stale_warning_timestamp, workspace_id, workspace_name, os_name, os_major, os_minor, rhsm_version, subscription_manager_id, sap_workload, sap_workload_sids, mssql_workload, mssql_workload_version) VALUES +(1, '00000000-0000-0000-0000-000000000001', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2020-09-22 12:00:00-04', '00000000-0000-0000-0000-000000000001', 1, 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"},{"key": "k2", "value": "val2", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'inventory-group-1', 'group1', 'RHEL', 8, 10, '8.10', NULL, true, ARRAY['ABC', 'DEF', 'GHI'], false, NULL), +(2, '00000000-0000-0000-0000-000000000002', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-09-22 12:00:00-04', '00000000-0000-0000-0000-000000000002', 1, 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"},{"key": "k2", "value": "val2", "namespace": "ns1"},{"key": "k3", "value": "val3", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'inventory-group-1', 'group1', 'RHEL', 8, 1, '8.1', NULL, true, ARRAY['ABC'], false, NULL), +(3, '00000000-0000-0000-0000-000000000003', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-09-18 12:00:00-04', '00000000-0000-0000-0000-000000000003', 1, 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}, {"key": "k3", "value": "val4", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'inventory-group-1', 'group1', 'RHEL', 8, 1, '8.0', NULL, true, NULL, false, NULL), +(4, '00000000-0000-0000-0000-000000000004', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-09-18 12:00:00-04', '00000000-0000-0000-0000-000000000004', 1, 'x86_64', '[{"key": "k3", "value": "val4", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'inventory-group-1', 'group1', 'RHEL', 8, 2, '8.3', 'cccccccc-0000-0000-0001-000000000004', true, NULL, false, NULL), +(5, '00000000-0000-0000-0000-000000000005', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-09-18 12:00:00-04', '00000000-0000-0000-0000-000000000005', 1, 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'inventory-group-1', 'group1', 'RHEL', 8, 3, '8.3', 'cccccccc-0000-0000-0001-000000000005', true, NULL, false, NULL), +(6, '00000000-0000-0000-0000-000000000006', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-08-26 12:00:00-04', '00000000-0000-0000-0000-000000000006', 1, 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'inventory-group-1', 'group1', 'RHEL', 7, 3, '7.3', NULL, true, NULL, true, '15.3.0'); INSERT INTO system_patch (system_id, rh_account_id, last_evaluation, third_party, template_id) VALUES (1, 1, '2018-09-22 12:00:00-04', true , 1), (2, 1, '2018-09-22 12:00:00-04', false, 1), @@ -39,17 +39,17 @@ INSERT INTO system_patch (system_id, rh_account_id, last_evaluation, third_party (5, 1, '2018-09-22 12:00:00-04', false, NULL), (6, 1, '2018-09-22 12:00:00-04', false, NULL); -INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_updated, unchanged_since, last_upload, display_name, arch, tags, created, stale_timestamp, stale_warning_timestamp, workspaces, os_name, os_major, rhsm_version, subscription_manager_id, sap_workload, ansible_workload, ansible_workload_controller_version) VALUES -(7, '00000000-0000-0000-0000-000000000007', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-10-04 14:13:12-04', '2018-09-22 12:00:00-04', '2018-08-26 12:00:00-04', '00000000-0000-0000-0000-000000000007', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[{"id": "inventory-group-2", "name": "group2"}]', 'RHEL', 8, '8.x', 'cccccccc-0000-0000-0001-000000000007', true, true, '1.0'); +INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_updated, unchanged_since, last_upload, display_name, arch, tags, created, stale_timestamp, stale_warning_timestamp, workspace_id, workspace_name, os_name, os_major, rhsm_version, subscription_manager_id, sap_workload, ansible_workload, ansible_workload_controller_version) VALUES +(7, '00000000-0000-0000-0000-000000000007', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-10-04 14:13:12-04', '2018-09-22 12:00:00-04', '2018-08-26 12:00:00-04', '00000000-0000-0000-0000-000000000007', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'inventory-group-2', 'group2', 'RHEL', 8, '8.x', 'cccccccc-0000-0000-0001-000000000007', true, true, '1.0'); INSERT INTO system_patch (system_id, rh_account_id) VALUES (7, 1); -INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_upload, display_name, arch, tags, created, stale_timestamp, stale_warning_timestamp, workspaces, os_name, os_major, os_minor, rhsm_version, subscription_manager_id, sap_workload) VALUES -( 8, '00000000-0000-0000-0000-000000000008', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-08-26 12:00:00-04', '00000000-0000-0000-0000-000000000008', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[{"id": "inventory-group-2", "name": "group2"}]', 'RHEL', 8, 3, '8.3', 'cccccccc-0000-0000-0001-000000000008', true), -( 9, '00000000-0000-0000-0000-000000000009', 2, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000009', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', NULL, 'RHEL', 8, 1, '8.1', NULL, true), -(10, '00000000-0000-0000-0000-000000000010', 2, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000010', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', NULL, 'RHEL', 8, 2, '8.2', NULL, true), -(11, '00000000-0000-0000-0000-000000000011', 2, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000011', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[]', 'RHEL', 8, 3, '8.3', NULL, true), -(12, '00000000-0000-0000-0000-000000000012', 3, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000012', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[]', 'RHEL', 8, 1, '8.1', NULL, true); +INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_upload, display_name, arch, tags, created, stale_timestamp, stale_warning_timestamp, workspace_id, workspace_name, os_name, os_major, os_minor, rhsm_version, subscription_manager_id, sap_workload) VALUES +( 8, '00000000-0000-0000-0000-000000000008', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-08-26 12:00:00-04', '00000000-0000-0000-0000-000000000008', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'inventory-group-2', 'group2', 'RHEL', 8, 3, '8.3', 'cccccccc-0000-0000-0001-000000000008', true), +( 9, '00000000-0000-0000-0000-000000000009', 2, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000009', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'root-workspace', 'root-ws', 'RHEL', 8, 1, '8.1', NULL, true), +(10, '00000000-0000-0000-0000-000000000010', 2, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000010', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'root-workspace', 'root-ws', 'RHEL', 8, 2, '8.2', NULL, true), +(11, '00000000-0000-0000-0000-000000000011', 2, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000011', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'root-workspace', 'root-ws', 'RHEL', 8, 3, '8.3', NULL, true), +(12, '00000000-0000-0000-0000-000000000012', 3, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000012', 'x86_64', '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'root-workspace', 'root-ws', 'RHEL', 8, 1, '8.1', NULL, true); INSERT INTO system_patch (system_id, rh_account_id, last_evaluation, packages_installed, packages_installable, packages_applicable) VALUES ( 8, 1, '2018-09-22 12:00:00-04', 0, 0, 0), ( 9, 2, '2018-09-22 12:00:00-04', 0, 0, 0), @@ -57,24 +57,24 @@ INSERT INTO system_patch (system_id, rh_account_id, last_evaluation, packages_in (11, 2, '2018-09-22 12:00:00-04', 0, 0, 0), (12, 3, '2018-09-22 12:00:00-04', 2, 2, 2); -INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_upload, display_name, yum_updates, tags, created, stale_timestamp, stale_warning_timestamp, workspaces, os_name, os_major, os_minor, rhsm_version, sap_workload) VALUES -(13, '00000000-0000-0000-0000-000000000013', 3, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000013', NULL, '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[]', 'RHEL', 8, 2, '8.2', true), -(14, '00000000-0000-0000-0000-000000000014', 3, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000014', NULL, '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[]', 'RHEL', 8, 3, NULL, true), -(15, '00000000-0000-0000-0000-000000000015', 3, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000015', '{"update_list": {"suricata-0:6.0.3-2.fc35.i686": {"available_updates": [{"erratum": "RHSA-2021:3801", "basearch": "i686", "releasever": "ser1", "repository": "group_oisf:suricata-6.0", "package": "suricata-0:6.0.4-2.fc35.i686"}]}}, "basearch": "i686", "releasever": "ser1"}', '[{"key": "k3", "value": "val4", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[]', 'RHEL', 8, 1, '8.1', false); +INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_upload, display_name, yum_updates, tags, created, stale_timestamp, stale_warning_timestamp, workspace_id, workspace_name, os_name, os_major, os_minor, rhsm_version, sap_workload) VALUES +(13, '00000000-0000-0000-0000-000000000013', 3, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000013', NULL, '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'root-workspace', 'root-ws', 'RHEL', 8, 2, '8.2', true), +(14, '00000000-0000-0000-0000-000000000014', 3, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000014', NULL, '[{"key": "k1", "value": "val1", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'root-workspace', 'root-ws', 'RHEL', 8, 3, NULL, true), +(15, '00000000-0000-0000-0000-000000000015', 3, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000015', '{"update_list": {"suricata-0:6.0.3-2.fc35.i686": {"available_updates": [{"erratum": "RHSA-2021:3801", "basearch": "i686", "releasever": "ser1", "repository": "group_oisf:suricata-6.0", "package": "suricata-0:6.0.4-2.fc35.i686"}]}}, "basearch": "i686", "releasever": "ser1"}', '[{"key": "k3", "value": "val4", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'root-workspace', 'root-ws', 'RHEL', 8, 1, '8.1', false); INSERT INTO system_patch (system_id, rh_account_id, last_evaluation, packages_installed) VALUES (13, 3, '2018-09-22 12:00:00-04', 1), (14, 3, '2018-09-22 12:00:00-04', 0), (15, 3, '2018-09-22 12:00:00-04', 0); -INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_upload, display_name, tags, created, stale_timestamp, stale_warning_timestamp, workspaces, os_name, os_major, os_minor, rhsm_version, ansible_workload, ansible_workload_controller_version, mssql_workload, mssql_workload_version) VALUES -(16, '00000000-0000-0000-0000-000000000016', 3, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000016', '[]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[]', 'RHEL', 8, 2, '8.2', false, NULL, false, NULL), -(17, '00000000-0000-0000-0000-000000000017', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000017', '[]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[]', 'RHEL', 8, 1, '8.1', true, '1.0', true, '15.3.0'); +INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_upload, display_name, tags, created, stale_timestamp, stale_warning_timestamp, workspace_id, workspace_name, os_name, os_major, os_minor, rhsm_version, ansible_workload, ansible_workload_controller_version, mssql_workload, mssql_workload_version) VALUES +(16, '00000000-0000-0000-0000-000000000016', 3, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000016', '[]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'root-workspace', 'root-ws', 'RHEL', 8, 2, '8.2', false, NULL, false, NULL), +(17, '00000000-0000-0000-0000-000000000017', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2018-01-22 12:00:00-04', '00000000-0000-0000-0000-000000000017', '[]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'root-workspace', 'root-ws', 'RHEL', 8, 1, '8.1', true, '1.0', true, '15.3.0'); INSERT INTO system_patch (system_id, rh_account_id, last_evaluation, packages_installed, packages_installable, packages_applicable, template_id) VALUES (16, 3, '2018-09-22 12:00:00-04', 1, 1, 1, 4), (17, 1, '2018-09-22 12:00:00-04', 2, 2, 2, NULL); -INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_upload, display_name, reporter_id, arch, tags, created, stale_timestamp, stale_warning_timestamp, workspaces, os_name, os_major, os_minor, rhsm_version, subscription_manager_id, sap_workload) VALUES -(18, '00000000-0000-0000-0000-000000000018', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2020-09-22 12:00:00-04', '00000000-0000-0000-0000-000000000018', 1, 'x86_64', '[{"key": "k3", "value": "val4", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', '[{"id": "inventory-group-1", "name": "group1"}]', 'RHEL', 8, 2, '8.3', '99999999-9999-9999-9999-999999999404', true); +INSERT INTO system_inventory (id, inventory_id, rh_account_id, vmaas_json, json_checksum, last_upload, display_name, reporter_id, arch, tags, created, stale_timestamp, stale_warning_timestamp, workspace_id, workspace_name, os_name, os_major, os_minor, rhsm_version, subscription_manager_id, sap_workload) VALUES +(18, '00000000-0000-0000-0000-000000000018', 1, '{ "package_list": [ "kernel-2.6.32-696.20.1.el6.x86_64" ], "repository_list": [ "rhel-6-server-rpms" ] }', '1', '2020-09-22 12:00:00-04', '00000000-0000-0000-0000-000000000018', 1, 'x86_64', '[{"key": "k3", "value": "val4", "namespace": "ns1"}]', '2018-08-26 12:00:00-04', '2018-08-26 12:00:00-04', '2018-09-02 12:00:00-04', 'inventory-group-1', 'group1', 'RHEL', 8, 2, '8.3', '99999999-9999-9999-9999-999999999404', true); INSERT INTO system_patch (system_id, rh_account_id, last_evaluation, third_party) VALUES (18, 1, '2018-09-22 12:00:00-04', true); diff --git a/docs/v3/openapi.json b/docs/v3/openapi.json index 0c4149f8a..0015754b8 100644 --- a/docs/v3/openapi.json +++ b/docs/v3/openapi.json @@ -6333,12 +6333,6 @@ "display_name": { "type": "string" }, - "groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/controllers.SystemGroup" - } - }, "id": { "type": "string" }, @@ -6377,6 +6371,12 @@ }, "template_uuid": { "type": "string" + }, + "workspace_id": { + "type": "string" + }, + "workspace_name": { + "type": "string" } } }, @@ -6415,12 +6415,6 @@ "display_name": { "type": "string" }, - "groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/controllers.SystemGroup" - } - }, "last_upload": { "type": "string" }, @@ -6456,6 +6450,12 @@ }, "template_uuid": { "type": "string" + }, + "workspace_id": { + "type": "string" + }, + "workspace_name": { + "type": "string" } } }, @@ -6725,12 +6725,6 @@ "display_name": { "type": "string" }, - "groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/controllers.SystemGroup" - } - }, "id": { "type": "string" }, @@ -6763,6 +6757,12 @@ }, "update_status": { "type": "string" + }, + "workspace_id": { + "type": "string" + }, + "workspace_name": { + "type": "string" } } }, @@ -6974,12 +6974,6 @@ "display_name": { "type": "string" }, - "groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/controllers.SystemGroup" - } - }, "id": { "type": "string" }, @@ -7051,6 +7045,12 @@ }, "template_uuid": { "type": "string" + }, + "workspace_id": { + "type": "string" + }, + "workspace_name": { + "type": "string" } } }, @@ -7062,17 +7062,6 @@ } } }, - "controllers.SystemGroup": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - } - }, "controllers.SystemItem": { "type": "object", "properties": { @@ -7123,12 +7112,6 @@ "display_name": { "type": "string" }, - "groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/controllers.SystemGroup" - } - }, "installable_other_count": { "type": "integer" }, @@ -7197,6 +7180,12 @@ }, "template_uuid": { "type": "string" + }, + "workspace_id": { + "type": "string" + }, + "workspace_name": { + "type": "string" } } }, @@ -7239,12 +7228,6 @@ "display_name": { "type": "string" }, - "groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/controllers.SystemGroup" - } - }, "installable_other_count": { "type": "integer" }, @@ -7328,6 +7311,12 @@ }, "third_party": { "type": "boolean" + }, + "workspace_id": { + "type": "string" + }, + "workspace_name": { + "type": "string" } } }, @@ -7601,12 +7590,6 @@ "display_name": { "type": "string" }, - "groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/controllers.SystemGroup" - } - }, "installable_other_count": { "type": "integer" }, @@ -7633,6 +7616,12 @@ "items": { "$ref": "#/components/schemas/controllers.SystemTag" } + }, + "workspace_id": { + "type": "string" + }, + "workspace_name": { + "type": "string" } } }, @@ -7670,12 +7659,6 @@ "display_name": { "type": "string" }, - "groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/controllers.SystemGroup" - } - }, "id": { "type": "string" }, @@ -7705,6 +7688,12 @@ "items": { "$ref": "#/components/schemas/controllers.SystemTag" } + }, + "workspace_id": { + "type": "string" + }, + "workspace_name": { + "type": "string" } } }, diff --git a/listener/common_test.go b/listener/common_test.go index 3df43e157..2441040b1 100644 --- a/listener/common_test.go +++ b/listener/common_test.go @@ -86,7 +86,7 @@ func assertSystemInDB(t *testing.T, inventoryID string, rhAccountID *int, report // assertSystemInventoryProfileMatchesHost checks host-derived system_inventory columns written by // storeOrUpdateSysPlatform (must stay in sync on ON CONFLICT DO UPDATE, not only on first insert). -// nolint: unparam +// nolint: unparam,funlen func assertSystemInventoryProfileMatchesHost(t *testing.T, inventoryID string, host *Host) { t.Helper() var inv models.SystemInventory @@ -94,8 +94,18 @@ func assertSystemInventoryProfileMatchesHost(t *testing.T, inventoryID string, h assert.JSONEq(t, string(utils.MarshalNilToJSONB(host.Tags)), string(inv.Tags)) - require.NotNil(t, inv.Workspaces) - assert.Equal(t, host.Groups, []inventory.Group(*inv.Workspaces)) + if hostWorkspaceID := host.Groups[0].ID; hostWorkspaceID != "" { + require.NotNil(t, inv.WorkspaceID) + assert.Equal(t, hostWorkspaceID, *inv.WorkspaceID) + } else { + require.Nil(t, inv.WorkspaceID) + } + if hostWorkspaceName := host.Groups[0].Name; hostWorkspaceName != "" { + require.NotNil(t, inv.WorkspaceName) + assert.Equal(t, hostWorkspaceName, *inv.WorkspaceName) + } else { + require.Nil(t, inv.WorkspaceName) + } if host.SystemProfile.OperatingSystem.Name != "" { require.NotNil(t, inv.OSName) @@ -184,7 +194,7 @@ func createTestUploadEvent(orgID, inventoryID, reporter string, packages, yum bo reporter: {LastCheckIn: types.Rfc3339TimestampWithZ(now)}, }, Tags: []byte(`{"namespace": "insights-client","key": "env","value": "prod"}`), - Groups: []inventory.Group{{ID: "group1"}}, + Groups: []inventory.Group{{ID: "group1", Name: "group1"}}, SystemProfile: inventory.SystemProfile{ OperatingSystem: inventory.OperatingSystem{ Name: "RHEL", diff --git a/listener/upload.go b/listener/upload.go index 27189e364..30d0cce03 100644 --- a/listener/upload.go +++ b/listener/upload.go @@ -336,7 +336,8 @@ func updateSystemPlatform(tx *gorm.DB, accountID int, host *Host, "arch", "bootc", "tags", - "workspaces", + "workspace_id", + "workspace_name", "os_name", "os_major", "os_minor", @@ -358,7 +359,21 @@ func updateSystemPlatform(tx *gorm.DB, accountID int, host *Host, isBootc := len(host.SystemProfile.BootcStatus.Booted.Image) > 0 updatesReqJSONString := string(updatesReqJSON) - hostWorkspaces := inventory.Groups(host.Groups) + var workspaceID, workspaceName *string + if l := len(host.Groups); l >= 1 { + if host.Groups[0].ID != "" { + workspaceID = &host.Groups[0].ID + } + if host.Groups[0].Name != "" { + workspaceName = &host.Groups[0].Name + } + if l != 1 { + utils.LogWarn( + "host_id", host.ID, "org_id", host.OrgID, "workspaces", host.Groups, + "received a host with multiple workspaces", + ) + } + } systemPlatform := &models.SystemPlatformV2{ Inventory: models.SystemInventory{ InventoryID: inventoryID, @@ -366,7 +381,8 @@ func updateSystemPlatform(tx *gorm.DB, accountID int, host *Host, DisplayName: displayName, Created: host.Created, Tags: utils.MarshalNilToJSONB(host.Tags), - Workspaces: &hostWorkspaces, + WorkspaceID: workspaceID, + WorkspaceName: workspaceName, VmaasJSON: utils.EmptyToNil(&updatesReqJSONString), JSONChecksum: utils.EmptyToNil(&jsonChecksum), LastUpload: host.GetLastUpload(), diff --git a/listener/upload_test.go b/listener/upload_test.go index 8b75e6d43..8e7019e14 100644 --- a/listener/upload_test.go +++ b/listener/upload_test.go @@ -477,7 +477,8 @@ func TestStoreOrUpdateSysPlatform(t *testing.T) { vmaasJSON := "this_is_json" // insert new row hostEvent := createTestUploadEvent("1", id, "puptoo", false, true, "created") - hostWorkspaces := inventory.Groups(hostEvent.Host.Groups) + workspaceID := &hostEvent.Host.Groups[0].ID + workspaceName := &hostEvent.Host.Groups[0].Name inStore := &models.SystemPlatformV2{ Inventory: models.SystemInventory{ InventoryID: "99990000-0000-0000-0000-000000000001", @@ -487,7 +488,8 @@ func TestStoreOrUpdateSysPlatform(t *testing.T) { SatelliteManaged: false, Created: hostEvent.Host.Created, Tags: utils.MarshalNilToJSONB(hostEvent.Host.Tags), - Workspaces: &hostWorkspaces, + WorkspaceID: workspaceID, + WorkspaceName: workspaceName, OSName: utils.EmptyToNil(&hostEvent.Host.SystemProfile.OperatingSystem.Name), OSMajor: &hostEvent.Host.SystemProfile.OperatingSystem.Major, OSMinor: &hostEvent.Host.SystemProfile.OperatingSystem.Minor, @@ -504,7 +506,7 @@ func TestStoreOrUpdateSysPlatform(t *testing.T) { } err := storeOrUpdateSysPlatform(database.DB, inStore, colsToUpdate) - assert.Nil(t, err) + require.Nil(t, err) var outStore models.SystemInventory assert.NoError(t, database.DB.Where("id = ? AND rh_account_id = ?", inStore.Inventory.ID, inStore.Inventory.RhAccountID). // nolint:lll @@ -530,8 +532,10 @@ func TestStoreOrUpdateSysPlatform(t *testing.T) { assert.Contains(t, string(inventoryAfterInsert.Tags), `"key": "env"`) assert.Contains(t, string(inventoryAfterInsert.Tags), `"value": "prod"`) - require.NotNil(t, inventoryAfterInsert.Workspaces) - assert.Equal(t, hostEvent.Host.Groups, []inventory.Group(*inventoryAfterInsert.Workspaces)) + require.NotNil(t, inventoryAfterInsert.WorkspaceID) + assert.Equal(t, hostEvent.Host.Groups[0].ID, *inventoryAfterInsert.WorkspaceID) + require.NotNil(t, inventoryAfterInsert.WorkspaceName) + assert.Equal(t, hostEvent.Host.Groups[0].Name, *inventoryAfterInsert.WorkspaceName) assert.Equal(t, hostEvent.Host.SystemProfile.OperatingSystem.Name, *inventoryAfterInsert.OSName) assert.Equal(t, hostEvent.Host.SystemProfile.OperatingSystem.Major, *inventoryAfterInsert.OSMajor) @@ -569,7 +573,8 @@ func TestStoreOrUpdateSysPlatform(t *testing.T) { SatelliteManaged: true, Created: hostEvent.Host.Created, Tags: utils.MarshalNilToJSONB(hostEvent.Host.Tags), - Workspaces: &hostWorkspaces, + WorkspaceID: workspaceID, + WorkspaceName: workspaceName, OSName: utils.EmptyToNil(&hostEvent.Host.SystemProfile.OperatingSystem.Name), OSMajor: &hostEvent.Host.SystemProfile.OperatingSystem.Major, OSMinor: &hostEvent.Host.SystemProfile.OperatingSystem.Minor, diff --git a/manager/controllers/advisories.go b/manager/controllers/advisories.go index e06c88368..ba8e020ab 100644 --- a/manager/controllers/advisories.go +++ b/manager/controllers/advisories.go @@ -82,16 +82,18 @@ type AdvisoriesResponse struct { func advisoriesCommon(c *gin.Context) (*gorm.DB, *ListMeta, []string, error) { db := middlewares.DBFromContext(c) account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) var query *gorm.DB filters, err := ParseAllFilters(c, AdvisoriesOpts) if err != nil { return nil, nil, nil, err } - if config.DisableCachedCounts || HasInventoryFilter(filters) || len(groups) != 0 { + // TODO: fix below; the condition is always true since moving from groups to workspaces, + // there will always be at least root workspace + if config.DisableCachedCounts || HasInventoryFilter(filters) || len(workspaceIDs) != 0 { middlewares.AdvisoryAccountDataCnt.WithLabelValues("miss").Inc() - query = buildQueryAdvisoriesTagged(db, filters, account, groups) + query = buildQueryAdvisoriesTagged(db, filters, account, workspaceIDs) } else { middlewares.AdvisoryAccountDataCnt.WithLabelValues("hit").Inc() query = buildQueryAdvisories(db, account) @@ -216,8 +218,8 @@ func buildQueryAdvisories(db *gorm.DB, account int) *gorm.DB { return query } -func buildAdvisoryAccountDataQuery(db *gorm.DB, account int, groups map[string]string) *gorm.DB { - query := database.SystemAdvisories(db, account, groups). +func buildAdvisoryAccountDataQuery(db *gorm.DB, account int, workspaceIDs []string) *gorm.DB { + query := database.SystemAdvisories(db, account, workspaceIDs). Select(`sa.advisory_id, si.rh_account_id as rh_account_id, count(si.*) filter (where sa.status_id = 0) as systems_installable, count(si.*) as systems_applicable`). @@ -227,9 +229,9 @@ func buildAdvisoryAccountDataQuery(db *gorm.DB, account int, groups map[string]s return query } -func buildQueryAdvisoriesTagged(db *gorm.DB, filters map[string]FilterData, account int, groups map[string]string, +func buildQueryAdvisoriesTagged(db *gorm.DB, filters map[string]FilterData, account int, workspaceIDs []string, ) *gorm.DB { - subq := buildAdvisoryAccountDataQuery(db, account, groups) + subq := buildAdvisoryAccountDataQuery(db, account, workspaceIDs) subq, _ = ApplyInventoryFilter(filters, subq, "si.inventory_id") query := database.AdvisoryMetadata(db). diff --git a/manager/controllers/advisories_export.go b/manager/controllers/advisories_export.go index 90e34e933..a9a7af66a 100644 --- a/manager/controllers/advisories_export.go +++ b/manager/controllers/advisories_export.go @@ -31,7 +31,7 @@ import ( // @Router /export/advisories [get] func AdvisoriesExportHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) filters, err := ParseAllFilters(c, AdvisoriesOpts) if err != nil { return @@ -39,8 +39,10 @@ func AdvisoriesExportHandler(c *gin.Context) { db := middlewares.DBFromContext(c) var query *gorm.DB - if config.DisableCachedCounts || HasInventoryFilter(filters) || len(groups) != 0 { - query = buildQueryAdvisoriesTagged(db, filters, account, groups) + // TODO: fix below; the condition is always true since moving from groups to workspaces, + // there will always be at least root workspace + if config.DisableCachedCounts || HasInventoryFilter(filters) || len(workspaceIDs) != 0 { + query = buildQueryAdvisoriesTagged(db, filters, account, workspaceIDs) } else { query = buildQueryAdvisories(db, account) } diff --git a/manager/controllers/advisories_export_test.go b/manager/controllers/advisories_export_test.go index 6e2328ba2..2d0b4d377 100644 --- a/manager/controllers/advisories_export_test.go +++ b/manager/controllers/advisories_export_test.go @@ -13,7 +13,7 @@ import ( func TestAdvisoriesExportJSON(t *testing.T) { core.SetupTest(t) - w := CreateRequest("GET", "/", nil, "application/json", AdvisoriesExportHandler) + w := CreateRequest("GET", "/", nil, "application/json", AdvisoriesExportHandler, c) var output []AdvisoriesDBLookup CheckResponse(t, w, http.StatusOK, &output) @@ -32,7 +32,7 @@ func TestAdvisoriesExportJSON(t *testing.T) { func TestAdvisoriesExportCSV(t *testing.T) { core.SetupTest(t) - w := CreateRequest("GET", "/", nil, "text/csv", AdvisoriesExportHandler) + w := CreateRequest("GET", "/", nil, "text/csv", AdvisoriesExportHandler, c) assert.Equal(t, http.StatusOK, w.Code) body := w.Body.String() @@ -44,7 +44,7 @@ func TestAdvisoriesExportCSV(t *testing.T) { func TestAdvisoriesExportWrongFormat(t *testing.T) { core.SetupTest(t) - w := CreateRequest("GET", "/", nil, "test-format", AdvisoriesExportHandler) + w := CreateRequest("GET", "/", nil, "test-format", AdvisoriesExportHandler, c) assert.Equal(t, http.StatusUnsupportedMediaType, w.Code) body := w.Body.String() @@ -55,7 +55,7 @@ func TestAdvisoriesExportCSVFilter(t *testing.T) { core.SetupTest(t) for _, URL := range []string{"/?filter[id]=RH-1", "/?filter[synopsis]=adv-1-syn"} { - w := CreateRequest("GET", URL, nil, "text/csv", AdvisoriesExportHandler) + w := CreateRequest("GET", URL, nil, "text/csv", AdvisoriesExportHandler, c) assert.Equal(t, http.StatusOK, w.Code) body := w.Body.String() @@ -70,7 +70,7 @@ func TestAdvisoriesExportCSVFilter(t *testing.T) { func TestAdvisoriesExportTagsInvalid(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/", "", "?tags=ns1/k3=val4&tags=invalidTag", nil, "", - AdvisoriesExportHandler) + AdvisoriesExportHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -80,7 +80,7 @@ func TestAdvisoriesExportTagsInvalid(t *testing.T) { func TestAdvisoriesExportIncorrectFilter(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/", "", "?filter[filteriamnotexitst]=abcd", nil, "text/csv", - AdvisoriesExportHandler) + AdvisoriesExportHandler, c) assert.Equal(t, http.StatusBadRequest, w.Code) } diff --git a/manager/controllers/advisories_test.go b/manager/controllers/advisories_test.go index 4c10882b0..9e517f17d 100644 --- a/manager/controllers/advisories_test.go +++ b/manager/controllers/advisories_test.go @@ -22,7 +22,7 @@ func TestInit(_ *testing.T) { func testAdvisories(t *testing.T, url string) AdvisoriesResponse { core.SetupTest(t) - w := CreateRequest("GET", url, nil, "", AdvisoriesListHandler) + w := CreateRequest("GET", url, nil, "", AdvisoriesListHandler, c) var output AdvisoriesResponse CheckResponse(t, w, http.StatusOK, &output) @@ -31,7 +31,7 @@ func testAdvisories(t *testing.T, url string) AdvisoriesResponse { func testAdvisoriesIDs(t *testing.T, url string) IDsPlainResponse { core.SetupTest(t) - w := CreateRequest("GET", url, nil, "", AdvisoriesListIDsHandler) + w := CreateRequest("GET", url, nil, "", AdvisoriesListIDsHandler, c) var output IDsPlainResponse CheckResponse(t, w, http.StatusOK, &output) @@ -101,7 +101,7 @@ func TestAdvisoriesOffset(t *testing.T) { func TestAdvisoriesOffsetOverflow(t *testing.T) { core.SetupTest(t) - w := CreateRequest("GET", "/?offset=13&limit=4", nil, "", AdvisoriesListHandler) + w := CreateRequest("GET", "/?offset=13&limit=4", nil, "", AdvisoriesListHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -261,7 +261,7 @@ func TestAdvisoriesPossibleSorts(t *testing.T) { continue } - w := CreateRequest("GET", fmt.Sprintf("/?sort=%v", sort), nil, "", AdvisoriesListHandler) + w := CreateRequest("GET", fmt.Sprintf("/?sort=%v", sort), nil, "", AdvisoriesListHandler, c) var output AdvisoriesResponse CheckResponse(t, w, http.StatusOK, &output) @@ -272,7 +272,7 @@ func TestAdvisoriesPossibleSorts(t *testing.T) { func TestAdvisoriesWrongSort(t *testing.T) { core.SetupTest(t) - w := CreateRequest("GET", "/?sort=unknown_key", nil, "", AdvisoriesListHandler) + w := CreateRequest("GET", "/?sort=unknown_key", nil, "", AdvisoriesListHandler, c) assert.Equal(t, http.StatusBadRequest, w.Code) } @@ -328,29 +328,29 @@ func TestAdvisoriesTags(t *testing.T) { func TestListAdvisoriesTagsInvalid(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/", "", "?tags=ns1/k3=val4&tags=invalidTag", nil, "", - AdvisoriesListHandler) + AdvisoriesListHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) assert.Equal(t, fmt.Sprintf(InvalidTagMsg, "invalidTag"), errResp.Error) } -func doTestWrongOffset(t *testing.T, path, param, q string, handler gin.HandlerFunc) { +func doTestWrongOffset(t *testing.T, path, param, q string, handler gin.HandlerFunc, contextKVs ...core.ContextKV) { core.SetupTest(t) - w := CreateRequestRouterWithParams("GET", path, param, q, nil, "", handler, 3) + w := CreateRequestRouterWithParams("GET", path, param, q, nil, "", handler, 3, contextKVs...) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) assert.Equal(t, InvalidOffsetMsg, errResp.Error) } func TestAdvisoriesWrongOffset(t *testing.T) { - doTestWrongOffset(t, "/", "", "?offset=1000", AdvisoriesListHandler) + doTestWrongOffset(t, "/", "", "?offset=1000", AdvisoriesListHandler, c) } func TestAdvisoryTagsInMetadata(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "?tags=ns1/k3=val4&tags=ns1/k1=val1", nil, "", - AdvisoriesListHandler) + AdvisoriesListHandler, c) var output AdvisoriesResponse CheckResponse(t, w, http.StatusOK, &output) diff --git a/manager/controllers/advisory_systems.go b/manager/controllers/advisory_systems.go index d85a2f5c5..55868d7e9 100644 --- a/manager/controllers/advisory_systems.go +++ b/manager/controllers/advisory_systems.go @@ -40,7 +40,7 @@ type AdvisorySystemItemAttributes struct { OSAttributes SystemTimestamps SystemTags - SystemGroups + SystemWorkspace BaselineIDAttr BaselineNameAttr TemplateAttibutes @@ -70,7 +70,7 @@ var AdvisorySystemOpts = ListOpts{ func advisorySystemsCommon(c *gin.Context) (*gorm.DB, *ListMeta, []string, error) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) advisoryName := c.Param("advisory_id") if advisoryName == "" { @@ -92,7 +92,7 @@ func advisorySystemsCommon(c *gin.Context) (*gorm.DB, *ListMeta, []string, error return nil, nil, nil, err } - query := buildAdvisorySystemsQuery(db, account, groups, advisoryName) + query := buildAdvisorySystemsQuery(db, account, workspaceIDs, advisoryName) opts := AdvisorySystemOpts filters, err := ParseAllFilters(c, opts) if err != nil { @@ -269,9 +269,9 @@ func AdvisorySystemsListIDsHandler(c *gin.Context) { c.JSON(http.StatusOK, &resp) } -func buildAdvisorySystemsQuery(db *gorm.DB, account int, groups map[string]string, advisoryName string) *gorm.DB { +func buildAdvisorySystemsQuery(db *gorm.DB, account int, workspaceIDs []string, advisoryName string) *gorm.DB { selectQuery := AdvisorySystemsSelect - query := database.SystemAdvisories(db, account, groups, database.JoinTemplates, database.JoinAdvisoryMetadata). + query := database.SystemAdvisories(db, account, workspaceIDs, database.JoinTemplates, database.JoinAdvisoryMetadata). Select(selectQuery). Joins("LEFT JOIN status st ON sa.status_id = st.id"). Where("am.name = ?", advisoryName). diff --git a/manager/controllers/advisory_systems_export.go b/manager/controllers/advisory_systems_export.go index c63592dfb..30b1c1f74 100644 --- a/manager/controllers/advisory_systems_export.go +++ b/manager/controllers/advisory_systems_export.go @@ -53,7 +53,7 @@ var AdvisorySystemExportOpts = ListOpts{ // @Router /export/advisories/{advisory_id}/systems [get] func AdvisorySystemsExportHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) advisoryName := c.Param("advisory_id") if advisoryName == "" { @@ -73,7 +73,7 @@ func AdvisorySystemsExportHandler(c *gin.Context) { return } - query := buildAdvisorySystemsQuery(db, account, groups, advisoryName) + query := buildAdvisorySystemsQuery(db, account, workspaceIDs, advisoryName) filters, err := ParseAllFilters(c, AdvisorySystemExportOpts) if err != nil { return diff --git a/manager/controllers/advisory_systems_export_test.go b/manager/controllers/advisory_systems_export_test.go index 8804e8b9e..da8ba4320 100644 --- a/manager/controllers/advisory_systems_export_test.go +++ b/manager/controllers/advisory_systems_export_test.go @@ -12,7 +12,7 @@ import ( func TestAdvisorySystemsExportJSON(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "", nil, "application/json", - AdvisorySystemsExportHandler) + AdvisorySystemsExportHandler, c) var output []SystemDBLookupExtended CheckResponse(t, w, http.StatusOK, &output) @@ -23,7 +23,8 @@ func TestAdvisorySystemsExportJSON(t *testing.T) { func TestAdvisorySystemsExportCSV(t *testing.T) { core.SetupTest(t) - w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "", nil, "text/csv", AdvisorySystemsExportHandler) + w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "", nil, "text/csv", + AdvisorySystemsExportHandler, c) assert.Equal(t, http.StatusOK, w.Code) body := w.Body.String() @@ -32,12 +33,12 @@ func TestAdvisorySystemsExportCSV(t *testing.T) { assert.Equal(t, 8, len(lines)) assert.Equal(t, "display_name,last_upload,stale,os,rhsm,stale_timestamp,stale_warning_timestamp,culled_timestamp,created,tags,"+ - "groups,baseline_id,baseline_name,template_name,template_uuid,status,satellite_managed,built_pkgcache,id", lines[0]) + "workspace_id,workspace_name,baseline_id,baseline_name,template_name,template_uuid,status,"+ + "satellite_managed,built_pkgcache,id", lines[0]) assert.Equal(t, "00000000-0000-0000-0000-000000000001,2020-09-22T16:00:00Z,false,RHEL 8.10,8.10,2018-08-26T16:00:00Z,"+ "2018-09-02T16:00:00Z,,2018-08-26T16:00:00Z,\"[{'key':'k1','namespace':'ns1','value':'val1'},"+ - "{'key':'k2','namespace':'ns1','value':'val2'}]\",\"[{'id':'inventory-group-1','name':'group1'}]\","+ - "0,,temp1-1,99900000-0000-0000-0000-000000000001,Installable,false,false,"+ - "00000000-0000-0000-0000-000000000001", + "{'key':'k2','namespace':'ns1','value':'val2'}]\",inventory-group-1,group1,"+ + "0,,temp1-1,99900000-0000-0000-0000-000000000001,Installable,false,false,00000000-0000-0000-0000-000000000001", lines[1]) } diff --git a/manager/controllers/advisory_systems_test.go b/manager/controllers/advisory_systems_test.go index 1599ebd99..77e5bcd47 100644 --- a/manager/controllers/advisory_systems_test.go +++ b/manager/controllers/advisory_systems_test.go @@ -13,7 +13,7 @@ import ( func TestAdvisorySystemsDefault(t *testing.T) { core.SetupTest(t) - w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "", nil, "", AdvisorySystemsListHandler) + w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "", nil, "", AdvisorySystemsListHandler, c) var output AdvisorySystemsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -36,7 +36,7 @@ func TestAdvisorySystemsDefault(t *testing.T) { func TestAdvisorySystemsIDsDefault(t *testing.T) { core.SetupTest(t) - w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "", nil, "", AdvisorySystemsListIDsHandler) + w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "", nil, "", AdvisorySystemsListIDsHandler, c) var output IDsStatusResponse CheckResponse(t, w, http.StatusOK, &output) @@ -50,7 +50,7 @@ func TestAdvisorySystemsIDsDefault(t *testing.T) { func TestAdvisorySystemsNotFound(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "nonexistant/systems", "", nil, "", - AdvisorySystemsListHandler) + AdvisorySystemsListHandler, c) assert.Equal(t, http.StatusNotFound, w.Code) } @@ -58,7 +58,7 @@ func TestAdvisorySystemsNotFound(t *testing.T) { func TestAdvisorySystemsOffsetLimit(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "?offset=5&limit=3", nil, "", - AdvisorySystemsListHandler) + AdvisorySystemsListHandler, c) var output AdvisorySystemsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -71,7 +71,7 @@ func TestAdvisorySystemsOffsetLimit(t *testing.T) { func TestAdvisorySystemsOffsetOverflow(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "?offset=100&limit=3", nil, "", - AdvisorySystemsListHandler) + AdvisorySystemsListHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -86,7 +86,7 @@ func TestAdvisorySystemsSorts(t *testing.T) { continue // ignore obsoleted baseline attributes } w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", fmt.Sprintf("?sort=%v", sort), nil, "", - AdvisorySystemsListHandler) + AdvisorySystemsListHandler, c) var output AdvisorySystemsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -98,7 +98,7 @@ func TestAdvisorySystemsSorts(t *testing.T) { func TestAdvisorySystemsWrongSort(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "?sort=unknown_key", nil, "", - AdvisorySystemsListHandler) + AdvisorySystemsListHandler, c) assert.Equal(t, http.StatusBadRequest, w.Code) } @@ -106,7 +106,7 @@ func TestAdvisorySystemsWrongSort(t *testing.T) { func TestAdvisorySystemsTags(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "?tags=ns1/k1=val1", nil, "", - AdvisorySystemsListHandler) + AdvisorySystemsListHandler, c) var output AdvisorySystemsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -116,7 +116,7 @@ func TestAdvisorySystemsTags(t *testing.T) { func TestAdvisorySystemsTagsMultiple(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "?tags=ns1/k3=val4&tags=ns1/k1=val1", nil, "", - AdvisorySystemsListHandler) + AdvisorySystemsListHandler, c) var output AdvisorySystemsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -127,7 +127,7 @@ func TestAdvisorySystemsTagsMultiple(t *testing.T) { func TestAdvisorySystemsTagsInvalid(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "?tags=ns1/k3=val4&tags=invalidTag", nil, "", - AdvisorySystemsListHandler) + AdvisorySystemsListHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -137,7 +137,7 @@ func TestAdvisorySystemsTagsInvalid(t *testing.T) { func TestAdvisorySystemsTagsUnknown(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "?tags=ns1/k3=val4&tags=ns1/k1=unk", nil, "", - AdvisorySystemsListHandler) + AdvisorySystemsListHandler, c) var output AdvisorySystemsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -145,13 +145,13 @@ func TestAdvisorySystemsTagsUnknown(t *testing.T) { } func TestAdvisorySystemsWrongOffset(t *testing.T) { - doTestWrongOffset(t, "/:advisory_id", "RH-1", "?offset=1000", AdvisorySystemsListHandler) + doTestWrongOffset(t, "/:advisory_id", "RH-1", "?offset=1000", AdvisorySystemsListHandler, c) } func TestAdvisorySystemsTagsInMetadata(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:advisory_id", "RH-1", "?tags=ns1/k3=val4&tags=ns1/k1=val1", nil, "", - AdvisorySystemsListHandler) + AdvisorySystemsListHandler, c) var output AdvisorySystemsResponse CheckResponse(t, w, http.StatusOK, &output) diff --git a/manager/controllers/common_attributes.go b/manager/controllers/common_attributes.go index 964ead642..ff2c95ead 100644 --- a/manager/controllers/common_attributes.go +++ b/manager/controllers/common_attributes.go @@ -24,8 +24,9 @@ type SystemTags struct { Tags SystemTagsList `json:"tags" csv:"tags" query:"si.tags" gorm:"column:tags"` } -type SystemGroups struct { - Groups SystemGroupsList `json:"groups" csv:"groups" query:"si.workspaces" gorm:"column:groups" order_query:"si.workspaces->0->>'name'"` +type SystemWorkspace struct { + WorkspaceID *string `json:"workspace_id" csv:"workspace_id" query:"si.workspace_id" gorm:"column:workspace_id"` + WorkspaceName *string `json:"workspace_name" csv:"workspace_name" query:"si.workspace_name" gorm:"column:workspace_name"` } // baseline attributes are obsoleted and we keep them only for backward API compatibility diff --git a/manager/controllers/package_systems.go b/manager/controllers/package_systems.go index c948e2242..7b0a68257 100644 --- a/manager/controllers/package_systems.go +++ b/manager/controllers/package_systems.go @@ -39,7 +39,7 @@ type PackageSystemItem struct { BaselineIDAttr OSAttributes UpdateStatus string `json:"update_status" csv:"update_status" query:"CASE WHEN spkg.installable_id is not null THEN 'Installable' WHEN spkg.applicable_id is not null THEN 'Applicable' ELSE 'None' END" gorm:"column:update_status"` - SystemGroups + SystemWorkspace } type PackageSystemDBLookup struct { @@ -54,9 +54,9 @@ type PackageSystemsResponse struct { Meta ListMeta `json:"meta"` } -func packageSystemsQuery(db *gorm.DB, acc int, groups map[string]string, packageName string, packageIDs []int, +func packageSystemsQuery(db *gorm.DB, acc int, workspaceIDs []string, packageName string, packageIDs []int, ) *gorm.DB { - query := database.SystemPackages(db, acc, groups, + query := database.SystemPackages(db, acc, workspaceIDs, database.JoinTemplates, database.JoinInstallableApplicablePackages). Select(PackageSystemsSelect). Where("si.stale = false"). @@ -67,7 +67,7 @@ func packageSystemsQuery(db *gorm.DB, acc int, groups map[string]string, package func packageSystemsCommon(db *gorm.DB, c *gin.Context) (*gorm.DB, *ListMeta, []string, error) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) var filters map[string]FilterData packageName := c.Param("package_name") @@ -87,7 +87,7 @@ func packageSystemsCommon(db *gorm.DB, c *gin.Context) (*gorm.DB, *ListMeta, []s return nil, nil, nil, errors.New("package not found") } - query := packageSystemsQuery(db, account, groups, packageName, packageIDs) + query := packageSystemsQuery(db, account, workspaceIDs, packageName, packageIDs) filters, err := ParseAllFilters(c, PackageSystemsOpts) if err != nil { return nil, nil, nil, err diff --git a/manager/controllers/package_systems_export.go b/manager/controllers/package_systems_export.go index 65acc3744..5bac813c4 100644 --- a/manager/controllers/package_systems_export.go +++ b/manager/controllers/package_systems_export.go @@ -33,7 +33,7 @@ import ( // @Router /export/packages/{package_name}/systems [get] func PackageSystemsExportHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) packageName := c.Param("package_name") if packageName == "" { @@ -53,7 +53,7 @@ func PackageSystemsExportHandler(c *gin.Context) { return } - query := packageSystemsQuery(db, account, groups, packageName, packageIDs) + query := packageSystemsQuery(db, account, workspaceIDs, packageName, packageIDs) filters, err := ParseAllFilters(c, PackageSystemsOpts) if err != nil { return diff --git a/manager/controllers/package_systems_export_test.go b/manager/controllers/package_systems_export_test.go index acfc52af1..84c57066a 100644 --- a/manager/controllers/package_systems_export_test.go +++ b/manager/controllers/package_systems_export_test.go @@ -13,7 +13,7 @@ import ( func TestPackageSystemsExportHandlerJSON(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:package_name/systems", "kernel", "?sort=id", nil, "application/json", - PackageSystemsExportHandler, 3) + PackageSystemsExportHandler, 3, c) var output []PackageSystemItem CheckResponse(t, w, http.StatusOK, &output) @@ -29,7 +29,7 @@ func TestPackageSystemsExportHandlerJSON(t *testing.T) { func TestPackageSystemsExportHandlerCSV(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:package_name/systems", "kernel", "?sort=id", nil, "text/csv", - PackageSystemsExportHandler, 3) + PackageSystemsExportHandler, 3, c) assert.Equal(t, http.StatusOK, w.Code) body := w.Body.String() @@ -38,20 +38,21 @@ func TestPackageSystemsExportHandlerCSV(t *testing.T) { assert.Equal(t, 5, len(lines)) assert.Equal(t, "id,display_name,installed_evra,available_evra,updatable,tags,"+ "baseline_name,baseline_uptodate,template_name,template_uuid,satellite_managed,baseline_id,os,rhsm,"+ - "update_status,groups", lines[0]) + "update_status,workspace_id,workspace_name", lines[0]) assert.Equal(t, "00000000-0000-0000-0000-000000000012,00000000-0000-0000-0000-000000000012,"+ "5.6.13-200.fc31.x86_64,5.6.13-201.fc31.x86_64,true,"+ - "\"[{'key':'k1','namespace':'ns1','value':'val1'}]\",,,,,false,0,RHEL 8.1,8.1,Installable,[]", + "\"[{'key':'k1','namespace':'ns1','value':'val1'}]\",,,,,false,0,RHEL 8.1,8.1,Installable,"+ + "root-workspace,root-ws", lines[1]) assert.Equal(t, "00000000-0000-0000-0000-000000000013,00000000-0000-0000-0000-000000000013,"+ "5.6.13-200.fc31.x86_64,,false,\"[{'key':'k1','namespace':'ns1','value':'val1'}]\",,,,,"+ - "false,0,RHEL 8.2,8.2,None,[]", lines[2]) + "false,0,RHEL 8.2,8.2,None,root-workspace,root-ws", lines[2]) } func TestPackageSystemsExportInvalidName(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:package_name/systems", "unknown_package", "", nil, "text/csv", - PackageSystemsExportHandler, 3) + PackageSystemsExportHandler, 3, c) assert.Equal(t, http.StatusNotFound, w.Code) } diff --git a/manager/controllers/package_systems_test.go b/manager/controllers/package_systems_test.go index 056b73cb1..60681b64b 100644 --- a/manager/controllers/package_systems_test.go +++ b/manager/controllers/package_systems_test.go @@ -29,7 +29,7 @@ func TestPackageIDsSystems(t *testing.T) { } func TestPackageSystemsWrongOffset(t *testing.T) { - doTestWrongOffset(t, "/:package_name/systems", "kernel", "?offset=1000", PackageSystemsListHandler) + doTestWrongOffset(t, "/:package_name/systems", "kernel", "?offset=1000", PackageSystemsListHandler, c) } func TestPackageSystemsTagsInvalid(t *testing.T) { @@ -47,7 +47,7 @@ func TestPackageSystemsInvalidName(t *testing.T) { func testPackageSystems(t *testing.T, param, queryString string, account int) PackageSystemsResponse { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:package_name/systems", param, queryString, nil, "", - PackageSystemsListHandler, account) + PackageSystemsListHandler, account, c) var output PackageSystemsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -57,7 +57,7 @@ func testPackageSystems(t *testing.T, param, queryString string, account int) Pa func testPackageIDsSystems(t *testing.T, param, queryString string, account int) IDsStatusResponse { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:package_name/systems", param, queryString, nil, "", - PackageSystemsListIDsHandler, account) + PackageSystemsListIDsHandler, account, c) var output IDsStatusResponse CheckResponse(t, w, http.StatusOK, &output) @@ -67,7 +67,7 @@ func testPackageIDsSystems(t *testing.T, param, queryString string, account int) func testPackageSystemsError(t *testing.T, param, queryString string, account int) (int, utils.ErrorResponse) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:package_name/systems", param, queryString, nil, "", - PackageSystemsListHandler, account) + PackageSystemsListHandler, account, c) var errResp utils.ErrorResponse ParseResponseBody(t, w.Body.Bytes(), &errResp) @@ -77,7 +77,7 @@ func testPackageSystemsError(t *testing.T, param, queryString string, account in func TestPackageSystemsTagsInMetadata(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:package_name/systems", "kernel", "?tags=ns1/k3=val4&tags=ns1/k1=val1", - nil, "", PackageSystemsListHandler, 3) + nil, "", PackageSystemsListHandler, 3, c) var output PackageSystemsResponse ParseResponseBody(t, w.Body.Bytes(), &output) diff --git a/manager/controllers/package_versions.go b/manager/controllers/package_versions.go index 78bf80a8c..07df60435 100644 --- a/manager/controllers/package_versions.go +++ b/manager/controllers/package_versions.go @@ -45,8 +45,8 @@ func packagesNameID(db *gorm.DB, pkgName string) *gorm.DB { Where("pn.name = ?", pkgName) } -func packageVersionsQuery(db *gorm.DB, acc int, groups map[string]string, packageNameIDs []int) *gorm.DB { - query := database.SystemPackages(db, acc, groups). +func packageVersionsQuery(db *gorm.DB, acc int, workspaceIDs []string, packageNameIDs []int) *gorm.DB { + query := database.SystemPackages(db, acc, workspaceIDs). Distinct(PackageVersionSelect). Where("si.stale = false"). Where("spkg.name_id in (?)", packageNameIDs) @@ -69,7 +69,7 @@ func packageVersionsQuery(db *gorm.DB, acc int, groups map[string]string, packag // @Router /packages/{package_name}/versions [get] func PackageVersionsListHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) packageName := c.Param("package_name") if packageName == "" { @@ -89,7 +89,7 @@ func PackageVersionsListHandler(c *gin.Context) { return } - query := packageVersionsQuery(db, account, groups, packageNameIDs) + query := packageVersionsQuery(db, account, workspaceIDs, packageNameIDs) // we don't support tags and filters for this endpoint query, meta, params, err := ListCommon(query, c, nil, PackageVersionsOpts) if err != nil { diff --git a/manager/controllers/package_versions_test.go b/manager/controllers/package_versions_test.go index 231874411..868ed5e81 100644 --- a/manager/controllers/package_versions_test.go +++ b/manager/controllers/package_versions_test.go @@ -11,7 +11,7 @@ import ( func TestPackageVersions(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:package_name/versions", "firefox", "", nil, "", - PackageVersionsListHandler, 3) + PackageVersionsListHandler, 3, c) var output PackageVersionsResponse assert.Greater(t, len(w.Body.Bytes()), 0) @@ -23,7 +23,7 @@ func TestPackageVersions(t *testing.T) { func TestPackageVersionsInvalidName(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:package_name/versions", "not-existing", "", nil, "", - PackageVersionsListHandler, 3) + PackageVersionsListHandler, 3, c) assert.Equal(t, http.StatusNotFound, w.Code) } diff --git a/manager/controllers/packages.go b/manager/controllers/packages.go index 59b8b8eea..ea4d14de0 100644 --- a/manager/controllers/packages.go +++ b/manager/controllers/packages.go @@ -54,7 +54,7 @@ type queryItem struct { var queryItemSelect = database.MustGetSelect(&queryItem{}) -func packagesQuery(db *gorm.DB, filters map[string]FilterData, acc int, groups map[string]string, +func packagesQuery(db *gorm.DB, filters map[string]FilterData, acc int, workspaceIDs []string, useCache bool) *gorm.DB { if useCache { middlewares.PackageAccountDataCnt.WithLabelValues("hit").Inc() @@ -65,7 +65,7 @@ func packagesQuery(db *gorm.DB, filters map[string]FilterData, acc int, groups m return q } middlewares.PackageAccountDataCnt.WithLabelValues("miss").Inc() - systemsWithPkgsInstalledQ := database.Systems(db, acc, groups). + systemsWithPkgsInstalledQ := database.Systems(db, acc, workspaceIDs). Select("si.id"). Where("si.stale = false AND spatch.packages_installed > 0") @@ -114,7 +114,7 @@ func packagesQuery(db *gorm.DB, filters map[string]FilterData, acc int, groups m func PackagesListHandler(c *gin.Context) { var filters map[string]FilterData account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) filters, err := ParseAllFilters(c, PackagesOpts) if err != nil { @@ -122,13 +122,13 @@ func PackagesListHandler(c *gin.Context) { } db := middlewares.DBFromContext(c) - useCache := shouldUseCache(db, account, filters, groups) + useCache := shouldUseCache(db, account, filters, workspaceIDs) if !useCache { db.Exec("SET work_mem TO '?'", utils.CoreCfg.DBWorkMem) defer db.Exec("RESET work_mem") } - query := packagesQuery(db, filters, account, groups, useCache) + query := packagesQuery(db, filters, account, workspaceIDs, useCache) query, meta, params, err := ListCommon(query, c, filters, PackagesOpts) if err != nil { return @@ -167,11 +167,14 @@ func PackageDBLookup2Item(packages []PackageDBLookup) ([]PackageItem, int) { } // use cache only when tag filter is not used, there are no inventory groups and cache is valid -func shouldUseCache(db *gorm.DB, acc int, filters map[string]FilterData, groups map[string]string) bool { +func shouldUseCache(db *gorm.DB, acc int, filters map[string]FilterData, workspaceIDs []string) bool { + // TODO: remove this function, it does not make sense since replacing groups with workspaces, + // because there is always at least the root workspace + if !config.EnabledPackageCache { return false } - if HasInventoryFilter(filters) || len(groups) != 0 { + if HasInventoryFilter(filters) || len(workspaceIDs) != 0 { return false } diff --git a/manager/controllers/packages_export.go b/manager/controllers/packages_export.go index 43c6f607b..e027af7b0 100644 --- a/manager/controllers/packages_export.go +++ b/manager/controllers/packages_export.go @@ -27,19 +27,19 @@ import ( // @Router /export/packages [get] func PackagesExportHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) filters, err := ParseAllFilters(c, PackagesOpts) if err != nil { return } db := middlewares.DBFromContext(c) - useCache := shouldUseCache(db, account, filters, groups) + useCache := shouldUseCache(db, account, filters, workspaceIDs) if !useCache { db.Exec("SET work_mem TO '?'", utils.CoreCfg.DBWorkMem) defer db.Exec("RESET work_mem") } - query := packagesQuery(db, filters, account, groups, useCache) + query := packagesQuery(db, filters, account, workspaceIDs, useCache) query, err = ExportListCommon(query, c, PackagesOpts) var data []PackageDBLookup diff --git a/manager/controllers/packages_export_test.go b/manager/controllers/packages_export_test.go index 5b559ef2f..fb121a084 100644 --- a/manager/controllers/packages_export_test.go +++ b/manager/controllers/packages_export_test.go @@ -12,7 +12,7 @@ import ( func TestPackageExportJSON(t *testing.T) { core.SetupTest(t) - w := CreateRequestRouterWithParams("GET", "/", "", "", nil, "application/json", PackagesExportHandler, 3) + w := CreateRequestRouterWithParams("GET", "/", "", "", nil, "application/json", PackagesExportHandler, 3, c) var output []PackageItem CheckResponse(t, w, http.StatusOK, &output) @@ -30,7 +30,7 @@ func TestPackageExportJSON(t *testing.T) { func TestPackageExportCSV(t *testing.T) { core.SetupTest(t) - w := CreateRequestRouterWithParams("GET", "/", "", "", nil, "text/csv", PackagesExportHandler, 3) + w := CreateRequestRouterWithParams("GET", "/", "", "", nil, "text/csv", PackagesExportHandler, 3, c) assert.Equal(t, http.StatusOK, w.Code) body := w.Body.String() diff --git a/manager/controllers/packages_test.go b/manager/controllers/packages_test.go index cec81cf3d..ae2b80f3d 100644 --- a/manager/controllers/packages_test.go +++ b/manager/controllers/packages_test.go @@ -12,7 +12,7 @@ import ( func doTestPackagesBytes(t *testing.T, q string) (resp []byte, status int) { core.SetupTest(t) - w := CreateRequestRouterWithParams("GET", "/", "", q, nil, "", PackagesListHandler, 3) + w := CreateRequestRouterWithParams("GET", "/", "", q, nil, "", PackagesListHandler, 3, c) return w.Body.Bytes(), w.Code } @@ -75,7 +75,7 @@ func TestSearchPackages(t *testing.T) { func TestPackageTagsInvalid(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/", "", "?tags=ns1/k3=val4&tags=invalidTag", nil, "", - PackagesListHandler, 3) + PackagesListHandler, 3, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -89,7 +89,7 @@ func TestPackagesWrongOffset(t *testing.T) { func TestPackageTagsInMetadata(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/", "", "?tags=ns1/k3=val4&tags=ns1/k1=val1", nil, "", - PackagesListHandler, 3) + PackagesListHandler, 3, c) var output PackagesResponse CheckResponse(t, w, http.StatusOK, &output) diff --git a/manager/controllers/structures.go b/manager/controllers/structures.go index 5634e704f..6b70ce050 100644 --- a/manager/controllers/structures.go +++ b/manager/controllers/structures.go @@ -69,8 +69,3 @@ type IDsSatelliteManagedResponse struct { // TODO: delete later once UI is using only the new `data` field IDsResponseCommon } - -type SystemGroup struct { - ID string `json:"id"` - Name string `json:"name"` -} diff --git a/manager/controllers/system_advisories.go b/manager/controllers/system_advisories.go index 2a4e85bf0..34ae6b3bc 100644 --- a/manager/controllers/system_advisories.go +++ b/manager/controllers/system_advisories.go @@ -72,7 +72,7 @@ func (v *RelList) Scan(value interface{}) error { func systemAdvisoriesCommon(c *gin.Context) (*gorm.DB, *ListMeta, []string, error) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) inventoryID := c.Param("inventory_id") if inventoryID == "" { @@ -105,7 +105,7 @@ func systemAdvisoriesCommon(c *gin.Context) (*gorm.DB, *ListMeta, []string, erro return nil, nil, nil, err } - query := buildSystemAdvisoriesQuery(db, account, groups, inventoryID) + query := buildSystemAdvisoriesQuery(db, account, workspaceIDs, inventoryID) query, meta, params, err := ListCommon(query, c, filters, SystemAdvisoriesOpts) // Error handling and setting of result code & content is done in ListCommon return query, meta, params, err @@ -200,8 +200,8 @@ func SystemAdvisoriesIDsHandler(c *gin.Context) { c.JSON(http.StatusOK, &resp) } -func buildSystemAdvisoriesQuery(db *gorm.DB, account int, groups map[string]string, inventoryID string) *gorm.DB { - query := database.SystemAdvisoriesByInventoryID(db, account, groups, inventoryID, +func buildSystemAdvisoriesQuery(db *gorm.DB, account int, workspaceIDs []string, inventoryID string) *gorm.DB { + query := database.SystemAdvisoriesByInventoryID(db, account, workspaceIDs, inventoryID, database.JoinAdvisoryMetadata, database.JoinAdvisoryType). Joins("JOIN status ON sa.status_id = status.id"). Joins("LEFT JOIN advisory_severity sev ON am.severity_id = sev.id"). diff --git a/manager/controllers/system_advisories_export.go b/manager/controllers/system_advisories_export.go index c094841b6..62814cdfd 100644 --- a/manager/controllers/system_advisories_export.go +++ b/manager/controllers/system_advisories_export.go @@ -34,7 +34,7 @@ import ( // @Router /export/systems/{inventory_id}/advisories [get] func SystemAdvisoriesExportHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) inventoryID := c.Param("inventory_id") if inventoryID == "" { @@ -60,7 +60,7 @@ func SystemAdvisoriesExportHandler(c *gin.Context) { return } - query := buildSystemAdvisoriesQuery(db, account, groups, inventoryID) + query := buildSystemAdvisoriesQuery(db, account, workspaceIDs, inventoryID) query = query.Order("id") query, err = ExportListCommon(query, c, SystemAdvisoriesOpts) if err != nil { diff --git a/manager/controllers/system_advisories_export_test.go b/manager/controllers/system_advisories_export_test.go index 8ee3f29d6..c9f4d19d8 100644 --- a/manager/controllers/system_advisories_export_test.go +++ b/manager/controllers/system_advisories_export_test.go @@ -12,7 +12,7 @@ import ( func TestSystemAdvisoriesExportJSON(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", "", nil, - "application/json", SystemAdvisoriesExportHandler) + "application/json", SystemAdvisoriesExportHandler, c) var output []AdvisoriesDBLookup CheckResponse(t, w, http.StatusOK, &output) @@ -23,7 +23,7 @@ func TestSystemAdvisoriesExportJSON(t *testing.T) { func TestSystemAdvisoriesExportCSV(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", "", nil, - "text/csv", SystemAdvisoriesExportHandler) + "text/csv", SystemAdvisoriesExportHandler, c) assert.Equal(t, http.StatusOK, w.Code) body := w.Body.String() @@ -39,7 +39,7 @@ func TestSystemAdvisoriesExportCSV(t *testing.T) { func TestUnknownSystemAdvisoriesExport(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "unknownsystem", "", nil, "text/csv", - SystemAdvisoriesExportHandler) + SystemAdvisoriesExportHandler, c) assert.Equal(t, http.StatusBadRequest, w.Code) } diff --git a/manager/controllers/system_advisories_test.go b/manager/controllers/system_advisories_test.go index 34068131f..7859492a9 100644 --- a/manager/controllers/system_advisories_test.go +++ b/manager/controllers/system_advisories_test.go @@ -13,7 +13,7 @@ import ( func TestSystemAdvisoriesDefault(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", "", nil, "", - SystemAdvisoriesHandler) + SystemAdvisoriesHandler, c) var output SystemAdvisoriesResponse CheckResponse(t, w, http.StatusOK, &output) @@ -34,7 +34,7 @@ func TestSystemAdvisoriesDefault(t *testing.T) { func TestSystemAdvisoriesIDsDefault(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", "", nil, "", - SystemAdvisoriesIDsHandler) + SystemAdvisoriesIDsHandler, c) var output IDsStatusResponse CheckResponse(t, w, http.StatusOK, &output) @@ -48,7 +48,7 @@ func TestSystemAdvisoriesIDsDefault(t *testing.T) { func TestSystemAdvisoriesNotFound(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "nonexistant/advisories", "", nil, "", - SystemAdvisoriesHandler) + SystemAdvisoriesHandler, c) assert.Equal(t, http.StatusNotFound, w.Code) } @@ -56,7 +56,7 @@ func TestSystemAdvisoriesNotFound(t *testing.T) { func TestSystemAdvisoriesIDsNotFound(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "nonexistant/advisories", "", nil, "", - SystemAdvisoriesIDsHandler) + SystemAdvisoriesIDsHandler, c) assert.Equal(t, http.StatusNotFound, w.Code) } @@ -64,7 +64,7 @@ func TestSystemAdvisoriesIDsNotFound(t *testing.T) { func TestSystemAdvisoriesOffsetLimit(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", - "?offset=4&limit=3", nil, "", SystemAdvisoriesHandler) + "?offset=4&limit=3", nil, "", SystemAdvisoriesHandler, c) var output SystemAdvisoriesResponse CheckResponse(t, w, http.StatusOK, &output) @@ -75,7 +75,7 @@ func TestSystemAdvisoriesOffsetLimit(t *testing.T) { func TestSystemAdvisoriesIDsOffsetLimit(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", - "?offset=4&limit=3", nil, "", SystemAdvisoriesIDsHandler) + "?offset=4&limit=3", nil, "", SystemAdvisoriesIDsHandler, c) var output IDsStatusResponse CheckResponse(t, w, http.StatusOK, &output) @@ -89,7 +89,7 @@ func TestSystemAdvisoriesIDsOffsetLimit(t *testing.T) { func TestSystemAdvisoriesOffsetOverflow(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", - "?offset=100&limit=3", nil, "", SystemAdvisoriesHandler) + "?offset=100&limit=3", nil, "", SystemAdvisoriesHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -105,7 +105,7 @@ func TestSystemAdvisoriesPossibleSorts(t *testing.T) { continue } w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", - fmt.Sprintf("?sort=%v", sort), nil, "", SystemAdvisoriesHandler) + fmt.Sprintf("?sort=%v", sort), nil, "", SystemAdvisoriesHandler, c) var output SystemAdvisoriesResponse CheckResponse(t, w, http.StatusOK, &output) @@ -116,7 +116,7 @@ func TestSystemAdvisoriesPossibleSorts(t *testing.T) { func TestSystemAdvisoriesWrongSort(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", - "?sort=unknown_key", nil, "", SystemAdvisoriesHandler) + "?sort=unknown_key", nil, "", SystemAdvisoriesHandler, c) assert.Equal(t, http.StatusBadRequest, w.Code) } @@ -124,7 +124,7 @@ func TestSystemAdvisoriesWrongSort(t *testing.T) { func TestSystemAdvisoriesSearch(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", - "?search=h-3", nil, "", SystemAdvisoriesHandler) + "?search=h-3", nil, "", SystemAdvisoriesHandler, c) var output SystemAdvisoriesResponse CheckResponse(t, w, http.StatusOK, &output) @@ -141,12 +141,12 @@ func TestSystemAdvisoriesSearch(t *testing.T) { func TestSystemAdvisoriesWrongOffset(t *testing.T) { doTestWrongOffset(t, "/:inventory_id", "00000000-0000-0000-0000-000000000001", "?offset=1000", - SystemAdvisoriesHandler) + SystemAdvisoriesHandler, c) } func TestSystemAdvisoriesExportUnknown(t *testing.T) { core.SetupTest(t) - w := CreateRequestRouterWithPath("GET", "/:inventory_id", "unknownsystem", "", nil, "", SystemAdvisoriesHandler) + w := CreateRequestRouterWithPath("GET", "/:inventory_id", "unknownsystem", "", nil, "", SystemAdvisoriesHandler, c) assert.Equal(t, http.StatusBadRequest, w.Code) } diff --git a/manager/controllers/system_detail.go b/manager/controllers/system_detail.go index faa78bfb8..02d6c93a3 100644 --- a/manager/controllers/system_detail.go +++ b/manager/controllers/system_detail.go @@ -44,7 +44,7 @@ type SystemYumUpdatesResponse struct { // @Router /systems/{inventory_id} [get] func SystemDetailHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) inventoryID := c.Param("inventory_id") if inventoryID == "" { @@ -63,7 +63,7 @@ func SystemDetailHandler(c *gin.Context) { var systemDetail SystemDetailLookup db := middlewares.DBFromContext(c) - query := database.Systems(db, account, groups, database.JoinTemplates). + query := database.Systems(db, account, workspaceIDs, database.JoinTemplates). Select(database.MustGetSelect(&systemDetail)). Where("si.inventory_id = ?::uuid", inventoryID) @@ -154,7 +154,7 @@ func SystemYumUpdatesHandler(c *gin.Context) { func systemJSONsCommon(c *gin.Context, column string) *models.SystemInventory { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) inventoryID := c.Param("inventory_id") if inventoryID == "" { @@ -173,7 +173,7 @@ func systemJSONsCommon(c *gin.Context, column string) *models.SystemInventory { var system models.SystemInventory db := middlewares.DBFromContext(c) - query := database.Systems(db, account, groups). + query := database.Systems(db, account, workspaceIDs). Select(column). Where("si.inventory_id = ?::uuid", inventoryID) diff --git a/manager/controllers/system_detail_test.go b/manager/controllers/system_detail_test.go index a886a0166..de1db9514 100644 --- a/manager/controllers/system_detail_test.go +++ b/manager/controllers/system_detail_test.go @@ -12,7 +12,7 @@ import ( func TestSystemDetailDefault1(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", "", nil, "", - SystemDetailHandler) + SystemDetailHandler, c) var output SystemDetailResponse CheckResponse(t, w, http.StatusOK, &output) @@ -40,7 +40,7 @@ func TestSystemDetailDefault2(t *testing.T) { core.SetupTest(t) // get system with some installable/updatable packages w := CreateRequestRouterWithAccount("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000012", "", nil, "", - SystemDetailHandler, 3) + SystemDetailHandler, 3, c) var output SystemDetailResponse CheckResponse(t, w, http.StatusOK, &output) @@ -50,7 +50,7 @@ func TestSystemDetailDefault2(t *testing.T) { func TestSystemDetailNoIdProvided(t *testing.T) { core.SetupTest(t) - w := CreateRequest("GET", "/", nil, "", SystemDetailHandler) + w := CreateRequest("GET", "/", nil, "", SystemDetailHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -60,7 +60,7 @@ func TestSystemDetailNoIdProvided(t *testing.T) { func TestSystemDetailNotFound(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:inventory_id", "ffffffff-ffff-ffff-ffff-ffffffffffff", "", nil, "", - SystemDetailHandler) + SystemDetailHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusNotFound, &errResp) @@ -70,7 +70,7 @@ func TestSystemDetailNotFound(t *testing.T) { func TestSystemsNoRHSM(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithAccount("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000014", "", nil, "", - SystemDetailHandler, 3) + SystemDetailHandler, 3, c) var output SystemDetailResponse CheckResponse(t, w, http.StatusOK, &output) @@ -82,7 +82,7 @@ func TestSystemsNoRHSM(t *testing.T) { func TestRHSMLessThanOS(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithAccount("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000003", "", nil, "", - SystemDetailHandler, 1) + SystemDetailHandler, 1, c) var output SystemDetailResponse CheckResponse(t, w, http.StatusOK, &output) @@ -95,7 +95,7 @@ func TestRHSMLessThanOS(t *testing.T) { func TestRHSMGreaterThanOS(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithAccount("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000004", "", nil, "", - SystemDetailHandler, 1) + SystemDetailHandler, 1, c) var output SystemDetailResponse CheckResponse(t, w, http.StatusOK, &output) @@ -114,7 +114,7 @@ func TestSystemUnknown(t *testing.T) { func TestSystemDetailFiltering(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithAccount("GET", "/:inventory_id", "00000000-0000-0000-0000-000000000001", - "?filter[filter]=abcd", nil, "", SystemDetailHandler, 1) + "?filter[filter]=abcd", nil, "", SystemDetailHandler, 1, c) var errResp utils.ErrorResponse ParseResponseBody(t, w.Body.Bytes(), &errResp) diff --git a/manager/controllers/system_packages.go b/manager/controllers/system_packages.go index fecf585c4..337298a52 100644 --- a/manager/controllers/system_packages.go +++ b/manager/controllers/system_packages.go @@ -56,8 +56,8 @@ type SystemPackageDBLoad struct { MetaTotalHelper } -func systemPackageQuery(db *gorm.DB, account int, groups map[string]string, inventoryID string) *gorm.DB { - query := database.SystemPackages(db, account, groups, database.JoinInstallableApplicablePackages). +func systemPackageQuery(db *gorm.DB, account int, workspaceIDs []string, inventoryID string) *gorm.DB { + query := database.SystemPackages(db, account, workspaceIDs, database.JoinInstallableApplicablePackages). Joins("LEFT JOIN strings AS descr ON p.description_hash = descr.id"). Joins("LEFT JOIN strings AS sum ON p.summary_hash = sum.id"). Select(SystemPackagesSelect). @@ -89,7 +89,7 @@ func systemPackageQuery(db *gorm.DB, account int, groups map[string]string, inve // @Router /systems/{inventory_id}/packages [get] func SystemPackagesHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) inventoryID := c.Param("inventory_id") if inventoryID == "" { @@ -108,7 +108,7 @@ func SystemPackagesHandler(c *gin.Context) { } var loaded []SystemPackageDBLoad db := middlewares.DBFromContext(c) - q := systemPackageQuery(db, account, groups, inventoryID) + q := systemPackageQuery(db, account, workspaceIDs, inventoryID) q, meta, params, err := ListCommon(q, c, filters, SystemPackagesOpts) if err != nil { return diff --git a/manager/controllers/system_packages_export.go b/manager/controllers/system_packages_export.go index d585307b8..64ba60526 100644 --- a/manager/controllers/system_packages_export.go +++ b/manager/controllers/system_packages_export.go @@ -37,7 +37,7 @@ type SystemPackageInline struct { // @Router /export/systems/{inventory_id}/packages [get] func SystemPackagesExportHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) inventoryID := c.Param("inventory_id") if inventoryID == "" { @@ -52,7 +52,7 @@ func SystemPackagesExportHandler(c *gin.Context) { var loaded []SystemPackageDBLoad db := middlewares.DBFromContext(c) - q := systemPackageQuery(db, account, groups, inventoryID) + q := systemPackageQuery(db, account, workspaceIDs, inventoryID) q, err := ExportListCommon(q, c, SystemPackagesOpts) if err != nil { // Error handling and setting of result code & content is done in ListCommon diff --git a/manager/controllers/system_packages_export_test.go b/manager/controllers/system_packages_export_test.go index cd9add4e8..c79a68ef4 100644 --- a/manager/controllers/system_packages_export_test.go +++ b/manager/controllers/system_packages_export_test.go @@ -12,7 +12,7 @@ import ( func TestSystemPackagesExportHandlerJSON(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:inventory_id/packages", "00000000-0000-0000-0000-000000000013", "", - nil, "application/json", SystemPackagesExportHandler, 3) + nil, "application/json", SystemPackagesExportHandler, 3, c) var output []SystemPackageInline CheckResponse(t, w, http.StatusOK, &output) @@ -31,7 +31,7 @@ func TestSystemPackagesExportHandlerJSON(t *testing.T) { func TestSystemPackagesExportHandlerCSV(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:inventory_id/packages", "00000000-0000-0000-0000-000000000013", "", - nil, "text/csv", SystemPackagesExportHandler, 3) + nil, "text/csv", SystemPackagesExportHandler, 3, c) assert.Equal(t, http.StatusOK, w.Code) body := w.Body.String() @@ -56,7 +56,7 @@ func TestSystemPackagesExportHandlerCSV(t *testing.T) { func TestSystemPackagesExportUnknown(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:inventory_id/packages", "unknownsystem", "", nil, "text/csv", - SystemPackagesExportHandler, 3) + SystemPackagesExportHandler, 3, c) assert.Equal(t, http.StatusBadRequest, w.Code) } diff --git a/manager/controllers/system_packages_test.go b/manager/controllers/system_packages_test.go index 816af29e8..d0fcaa820 100644 --- a/manager/controllers/system_packages_test.go +++ b/manager/controllers/system_packages_test.go @@ -11,7 +11,7 @@ import ( func TestSystemPackages(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:inventory_id/packages", "00000000-0000-0000-0000-000000000013", "", - nil, "", SystemPackagesHandler, 3) + nil, "", SystemPackagesHandler, 3, c) var output SystemPackageResponse CheckResponse(t, w, http.StatusOK, &output) @@ -30,7 +30,7 @@ func TestSystemPackages(t *testing.T) { func TestPackagesSearch(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:inventory_id/packages", "00000000-0000-0000-0000-000000000012", - "?search=kernel", nil, "", SystemPackagesHandler, 3) + "?search=kernel", nil, "", SystemPackagesHandler, 3, c) var output SystemPackageResponse CheckResponse(t, w, http.StatusOK, &output) @@ -41,7 +41,7 @@ func TestPackagesSearch(t *testing.T) { func TestNoPackages(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:inventory_id/packages", "00000000-0000-0000-0000-000000000001", "", - nil, "", SystemPackagesHandler, 1) + nil, "", SystemPackagesHandler, 1, c) assert.Equal(t, http.StatusOK, w.Code) } @@ -49,7 +49,7 @@ func TestNoPackages(t *testing.T) { func TestSystemPackagesUpdatableOnly(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:inventory_id/packages", "00000000-0000-0000-0000-000000000013", - "?filter[updatable]=true", nil, "", SystemPackagesHandler, 3) + "?filter[updatable]=true", nil, "", SystemPackagesHandler, 3, c) var output SystemPackageResponse CheckResponse(t, w, http.StatusOK, &output) @@ -60,7 +60,7 @@ func TestSystemPackagesUpdatableOnly(t *testing.T) { func TestSystemPackagesName(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:inventory_id/packages", "00000000-0000-0000-0000-000000000013", - "?filter[name]=firefox", nil, "", SystemPackagesHandler, 3) + "?filter[name]=firefox", nil, "", SystemPackagesHandler, 3, c) var output SystemPackageResponse CheckResponse(t, w, http.StatusOK, &output) @@ -71,7 +71,7 @@ func TestSystemPackagesName(t *testing.T) { func TestSystemPackagesNonUpdatableOnly(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:inventory_id/packages", "00000000-0000-0000-0000-000000000013", - "?filter[updatable]=false", nil, "", SystemPackagesHandler, 3) + "?filter[updatable]=false", nil, "", SystemPackagesHandler, 3, c) var output SystemPackageResponse CheckResponse(t, w, http.StatusOK, &output) @@ -83,13 +83,13 @@ func TestSystemPackagesNonUpdatableOnly(t *testing.T) { func TestSystemPackagesWrongOffset(t *testing.T) { doTestWrongOffset(t, "/:inventory_id/packages", - "00000000-0000-0000-0000-000000000001", "?offset=1000", SystemPackagesHandler) + "00000000-0000-0000-0000-000000000001", "?offset=1000", SystemPackagesHandler, c) } func TestSystemPackagesUnknown(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithParams("GET", "/:inventory_id/packages", "unknownsystem", "", nil, "", - SystemPackagesHandler, 3) + SystemPackagesHandler, 3, c) assert.Equal(t, http.StatusBadRequest, w.Code) } diff --git a/manager/controllers/systems.go b/manager/controllers/systems.go index 097541022..6087919e4 100644 --- a/manager/controllers/systems.go +++ b/manager/controllers/systems.go @@ -94,7 +94,7 @@ type SystemItemAttributes struct { ApplicableOtherCount int `json:"applicable_other_count" csv:"applicable_other_count" query:"(spatch.applicable_advisory_count_cache - spatch.installable_advisory_sec_count_cache - spatch.installable_advisory_bug_count_cache - spatch.installable_advisory_enh_count_cache)" gorm:"column:applicable_other_count"` BaselineIDAttr TemplateAttibutes - SystemGroups + SystemWorkspace SystemArch } @@ -111,10 +111,9 @@ type SystemItemAttributesExtended struct { } type SystemTagsList []SystemTag -type SystemGroupsList []SystemGroup type SystemJSONBItemType interface { - SystemTagsList | SystemGroupsList + SystemTagsList } func (v SystemTagsList) String() string { @@ -129,18 +128,6 @@ func (v *SystemTagsList) Scan(value interface{}) error { return SystemJSONBItemScan(v, value) } -func (v SystemGroupsList) String() string { - return SystemJSONBItemString(v) -} - -func (v SystemGroupsList) Value() (driver.Value, error) { - return SystemJSONBItemValue(v) -} - -func (v *SystemGroupsList) Scan(value interface{}) error { - return SystemJSONBItemScan(v, value) -} - func SystemJSONBItemString[T SystemJSONBItemType](v T) string { b, err := sonic.Marshal(v) if err != nil { @@ -184,9 +171,9 @@ type SystemsResponse struct { func systemsCommon(c *gin.Context) (*gorm.DB, *ListMeta, []string, error) { var err error account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) db := middlewares.DBFromContext(c) - query := querySystems(db, account, groups) + query := querySystems(db, account, workspaceIDs) filters, err := ParseAllFilters(c, SystemOpts) if err != nil { return nil, nil, nil, err @@ -357,6 +344,6 @@ func SystemsListIDsHandler(c *gin.Context) { c.JSON(http.StatusOK, &resp) } -func querySystems(db *gorm.DB, account int, groups map[string]string) *gorm.DB { - return database.Systems(db, account, groups, database.JoinTemplates).Select(SystemsSelect) +func querySystems(db *gorm.DB, account int, workspaceIDs []string) *gorm.DB { + return database.Systems(db, account, workspaceIDs, database.JoinTemplates).Select(SystemsSelect) } diff --git a/manager/controllers/systems_advisories_view.go b/manager/controllers/systems_advisories_view.go index 7c4b4f885..f6166cc6f 100644 --- a/manager/controllers/systems_advisories_view.go +++ b/manager/controllers/systems_advisories_view.go @@ -72,14 +72,14 @@ func totalItems(tx *gorm.DB, cols string) (int, error) { return int(count), err } -func systemsAdvisoriesQuery(c *gin.Context, db *gorm.DB, acc int, groups map[string]string, +func systemsAdvisoriesQuery(c *gin.Context, db *gorm.DB, acc int, workspaceIDs []string, req SystemsAdvisoriesRequest) (*gorm.DB, *ListMeta, *Links, error) { systems := req.Systems advisories := req.Advisories sysq := database.ApplyInventoryWorkspaceFilter( db.Table("system_inventory si"). Where("si.rh_account_id = ?", acc), - groups). + workspaceIDs). Distinct("si.rh_account_id, si.id, si.inventory_id"). // we need to join system_advisories to make `limit` work properly // without this join it can happen that we display less items on some pages @@ -122,12 +122,12 @@ func systemsAdvisoriesQuery(c *gin.Context, db *gorm.DB, acc int, groups map[str return query, meta, links, err } -func advisoriesSystemsQuery(c *gin.Context, db *gorm.DB, acc int, groups map[string]string, +func advisoriesSystemsQuery(c *gin.Context, db *gorm.DB, acc int, workspaceIDs []string, req SystemsAdvisoriesRequest) (*gorm.DB, *ListMeta, *Links, error) { systems := req.Systems advisories := req.Advisories // get all advisories for all systems in the account - advq := database.SystemAdvisories(db, acc, groups, database.JoinAdvisoryMetadata). + advq := database.SystemAdvisories(db, acc, workspaceIDs, database.JoinAdvisoryMetadata). Distinct("am.id, am.name") // we need to join system_advisories to make `limit` work properly // without this join it can happen that we display less items on some pages @@ -180,7 +180,7 @@ func queryDB(c *gin.Context, endpoint string) ([]systemsAdvisoriesDBLoad, *ListM return nil, nil, nil, err } acc := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) db := middlewares.DBFromContext(c) // backward compatibility, put limit/offset from json into querystring if req.Limit != nil { @@ -191,9 +191,9 @@ func queryDB(c *gin.Context, endpoint string) ([]systemsAdvisoriesDBLoad, *ListM } switch endpoint { case "SystemsAdvisories": - q, meta, links, err = systemsAdvisoriesQuery(c, db, acc, groups, req) + q, meta, links, err = systemsAdvisoriesQuery(c, db, acc, workspaceIDs, req) case "AdvisoriesSystems": - q, meta, links, err = advisoriesSystemsQuery(c, db, acc, groups, req) + q, meta, links, err = advisoriesSystemsQuery(c, db, acc, workspaceIDs, req) default: return nil, nil, nil, fmt.Errorf("unknown endpoint '%s'", endpoint) } diff --git a/manager/controllers/systems_advisories_view_test.go b/manager/controllers/systems_advisories_view_test.go index 6ac238d04..51b314952 100644 --- a/manager/controllers/systems_advisories_view_test.go +++ b/manager/controllers/systems_advisories_view_test.go @@ -13,7 +13,13 @@ import ( "github.com/stretchr/testify/assert" ) -func doTestView(t *testing.T, handler gin.HandlerFunc, q string, limit, offset *int) *httptest.ResponseRecorder { +var c = core.ContextKV{ + Key: utils.KeyInventoryWorkspaces, + Value: []string{"root-workspace", "inventory-group-1", "inventory-group-2"}, +} + +func doTestView(t *testing.T, handler gin.HandlerFunc, q string, limit, offset *int, contextKVs ...core.ContextKV, +) *httptest.ResponseRecorder { core.SetupTest(t) body := SystemsAdvisoriesRequest{ Systems: []SystemID{"00000000-0000-0000-0000-000000000001", "00000000-0000-0000-0000-000000000002"}, @@ -26,12 +32,12 @@ func doTestView(t *testing.T, handler gin.HandlerFunc, q string, limit, offset * panic(err) } - w := CreateRequestRouterWithParams("POST", "/", "", q, bytes.NewBuffer(bodyJSON), "", handler, 1) + w := CreateRequestRouterWithParams("POST", "/", "", q, bytes.NewBuffer(bodyJSON), "", handler, 1, contextKVs...) return w } func TestSystemsAdvisoriesView(t *testing.T) { - w := doTestView(t, PostSystemsAdvisories, "", nil, nil) + w := doTestView(t, PostSystemsAdvisories, "", nil, nil, c) var output SystemsAdvisoriesResponse CheckResponse(t, w, http.StatusOK, &output) assert.Equal(t, output.Data["00000000-0000-0000-0000-000000000001"][0], AdvisoryName("RH-1")) @@ -40,7 +46,7 @@ func TestSystemsAdvisoriesView(t *testing.T) { } func TestAdvisoriesSystemsView(t *testing.T) { - w := doTestView(t, PostAdvisoriesSystems, "", nil, nil) + w := doTestView(t, PostAdvisoriesSystems, "", nil, nil, c) var output AdvisoriesSystemsResponse CheckResponse(t, w, http.StatusOK, &output) assert.Equal(t, output.Data["RH-1"][0], SystemID("00000000-0000-0000-0000-000000000001")) @@ -48,7 +54,7 @@ func TestAdvisoriesSystemsView(t *testing.T) { } func TestSystemsAdvisoriesViewTags(t *testing.T) { - w := doTestView(t, PostSystemsAdvisories, "?filter[system_profile][sap_sids]=DEF", nil, nil) + w := doTestView(t, PostSystemsAdvisories, "?filter[system_profile][sap_sids]=DEF", nil, nil, c) var output SystemsAdvisoriesResponse CheckResponse(t, w, http.StatusOK, &output) assert.Equal(t, output.Data["00000000-0000-0000-0000-000000000001"][0], AdvisoryName("RH-1")) @@ -56,7 +62,7 @@ func TestSystemsAdvisoriesViewTags(t *testing.T) { } func TestAdvisoriesSystemsViewTags(t *testing.T) { - w := doTestView(t, PostAdvisoriesSystems, "?filter[system_profile][sap_sids]=DEF", nil, nil) + w := doTestView(t, PostAdvisoriesSystems, "?filter[system_profile][sap_sids]=DEF", nil, nil, c) var output AdvisoriesSystemsResponse CheckResponse(t, w, http.StatusOK, &output) assert.Equal(t, output.Data["RH-1"][0], SystemID("00000000-0000-0000-0000-000000000001")) @@ -65,7 +71,7 @@ func TestAdvisoriesSystemsViewTags(t *testing.T) { func TestSystemAdvisoriesViewOffsetLimit(t *testing.T) { limit := 3 offset := 0 - w := doTestView(t, PostSystemsAdvisories, "", &limit, &offset) + w := doTestView(t, PostSystemsAdvisories, "", &limit, &offset, c) var output SystemsAdvisoriesResponse CheckResponse(t, w, http.StatusOK, &output) assert.Equal(t, 2, len(output.Data)) @@ -78,7 +84,7 @@ func TestSystemAdvisoriesViewOffsetLimit(t *testing.T) { func TestSystemAdvisoriesViewOffsetOverflow(t *testing.T) { limit := 1 offset := 100 - w := doTestView(t, PostSystemsAdvisories, "", &limit, &offset) + w := doTestView(t, PostSystemsAdvisories, "", &limit, &offset, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) assert.Equal(t, InvalidOffsetMsg, errResp.Error) @@ -95,7 +101,7 @@ func TestSystemAdvisoriesViewWrongOffset(t *testing.T) { func TestAvisorySystemsViewOffsetLimit(t *testing.T) { limit := 3 offset := 0 - w := doTestView(t, PostAdvisoriesSystems, "", &limit, &offset) + w := doTestView(t, PostAdvisoriesSystems, "", &limit, &offset, c) var output AdvisoriesSystemsResponse CheckResponse(t, w, http.StatusOK, &output) assert.Equal(t, 2, len(output.Data)) @@ -108,7 +114,7 @@ func TestAvisorySystemsViewOffsetLimit(t *testing.T) { func TestAvisorySystemsViewOffsetOverflow(t *testing.T) { limit := 1 offset := 100 - w := doTestView(t, PostAdvisoriesSystems, "", &limit, &offset) + w := doTestView(t, PostAdvisoriesSystems, "", &limit, &offset, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) assert.Equal(t, InvalidOffsetMsg, errResp.Error) @@ -116,7 +122,7 @@ func TestAvisorySystemsViewOffsetOverflow(t *testing.T) { func TestAvisorySystemsViewWrongOffset(t *testing.T) { offset := 1000 - w := doTestView(t, PostAdvisoriesSystems, "", nil, &offset) + w := doTestView(t, PostAdvisoriesSystems, "", nil, &offset, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) assert.Equal(t, InvalidOffsetMsg, errResp.Error) diff --git a/manager/controllers/systems_auth_test.go b/manager/controllers/systems_auth_test.go index 560510ae2..e5658d028 100644 --- a/manager/controllers/systems_auth_test.go +++ b/manager/controllers/systems_auth_test.go @@ -11,7 +11,7 @@ import ( func testAccountSystemCounts(t *testing.T, acc int, count int) { core.SetupTest(t) var output SystemsResponse - w := CreateRequestRouterWithAccount("GET", "/", "", "", nil, "", SystemsListHandler, acc) + w := CreateRequestRouterWithAccount("GET", "/", "", "", nil, "", SystemsListHandler, acc, c) CheckResponse(t, w, http.StatusOK, &output) // data assert.Equal(t, count, len(output.Data)) diff --git a/manager/controllers/systems_export.go b/manager/controllers/systems_export.go index d3ef42314..ce1d41328 100644 --- a/manager/controllers/systems_export.go +++ b/manager/controllers/systems_export.go @@ -56,9 +56,9 @@ import ( // @Router /export/systems [get] func SystemsExportHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) db := middlewares.DBFromContext(c) - query := querySystems(db, account, groups) + query := querySystems(db, account, workspaceIDs) filters, err := ParseAllFilters(c, SystemOpts) if err != nil { return diff --git a/manager/controllers/systems_export_test.go b/manager/controllers/systems_export_test.go index ae99a7cc6..77a3c3aa7 100644 --- a/manager/controllers/systems_export_test.go +++ b/manager/controllers/systems_export_test.go @@ -18,11 +18,11 @@ var SystemCsvHeader = "id,display_name,os,rhsm,tags,last_evaluation," + "satellite_managed,built_pkgcache,packages_installable,packages_applicable," + "installable_rhsa_count,installable_rhba_count,installable_rhea_count,installable_other_count," + "applicable_rhsa_count,applicable_rhba_count,applicable_rhea_count,applicable_other_count," + - "baseline_id,template_name,template_uuid,groups,arch" + "baseline_id,template_name,template_uuid,workspace_id,workspace_name,arch" func makeRequest(t *testing.T, path string, contentType string) *httptest.ResponseRecorder { core.SetupTest(t) - return CreateRequest("GET", path, nil, contentType, SystemsExportHandler) + return CreateRequest("GET", path, nil, contentType, SystemsExportHandler, c) } func TestSystemsExportJSON(t *testing.T) { @@ -60,7 +60,7 @@ func TestSystemsExportCSV(t *testing.T) { "2018-09-22T16:00:00Z,2,2,1,0,0,,"+ "2020-09-22T16:00:00Z,2018-08-26T16:00:00Z,2018-09-02T16:00:00Z,,2018-08-26T16:00:00Z,"+ "false,false,false,0,0,2,2,1,0,2,3,3,3,0,temp1-1,99900000-0000-0000-0000-000000000001,"+ - "\"[{'id':'inventory-group-1','name':'group1'}]\",x86_64", + "inventory-group-1,group1,x86_64", lines[1]) } diff --git a/manager/controllers/systems_ids_test.go b/manager/controllers/systems_ids_test.go index 0ad521c42..f6bf552dd 100644 --- a/manager/controllers/systems_ids_test.go +++ b/manager/controllers/systems_ids_test.go @@ -29,7 +29,7 @@ func TestSystemsIDsOffsetLimit(t *testing.T) { } func TestSystemsIDsWrongOffset(t *testing.T) { - doTestWrongOffset(t, "/", "", "?offset=13&limit=4", SystemsListIDsHandler) + doTestWrongOffset(t, "/", "", "?offset=13&limit=4", SystemsListIDsHandler, c) } func TestSystemsIDsWrongSort(t *testing.T) { @@ -192,7 +192,7 @@ func TestSystemsIDsFilterTemplateUUID(t *testing.T) { func testSystemsIDs(t *testing.T, queryString string, account int) IDsSatelliteManagedResponse { core.SetupTest(t) - w := CreateRequestRouterWithAccount("GET", "/", "", queryString, nil, "", SystemsListIDsHandler, account) + w := CreateRequestRouterWithAccount("GET", "/", "", queryString, nil, "", SystemsListIDsHandler, account, c) var output IDsSatelliteManagedResponse CheckResponse(t, w, http.StatusOK, &output) @@ -201,7 +201,7 @@ func testSystemsIDs(t *testing.T, queryString string, account int) IDsSatelliteM func testSystemsIDsError(t *testing.T, queryString string) (int, utils.ErrorResponse) { core.SetupTest(t) - w := CreateRequestRouterWithPath("GET", "/", "", queryString, nil, "", SystemsListIDsHandler) + w := CreateRequestRouterWithPath("GET", "/", "", queryString, nil, "", SystemsListIDsHandler, c) var errResp utils.ErrorResponse ParseResponseBody(t, w.Body.Bytes(), &errResp) diff --git a/manager/controllers/systems_test.go b/manager/controllers/systems_test.go index 4520c1826..081f04240 100644 --- a/manager/controllers/systems_test.go +++ b/manager/controllers/systems_test.go @@ -70,7 +70,7 @@ func TestSystemsOffsetLimit(t *testing.T) { } func TestSystemsWrongOffset(t *testing.T) { - doTestWrongOffset(t, "/", "", "?offset=13&limit=4", SystemsListHandler) + doTestWrongOffset(t, "/", "", "?offset=13&limit=4", SystemsListHandler, c) } func TestSystemsWrongSort(t *testing.T) { @@ -257,7 +257,7 @@ func TestSystemsFilterTemplateUUID(t *testing.T) { func testSystems(t *testing.T, queryString string, account int) SystemsResponse { core.SetupTest(t) - w := CreateRequestRouterWithAccount("GET", "/", "", queryString, nil, "", SystemsListHandler, account) + w := CreateRequestRouterWithAccount("GET", "/", "", queryString, nil, "", SystemsListHandler, account, c) var output SystemsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -266,7 +266,7 @@ func testSystems(t *testing.T, queryString string, account int) SystemsResponse func testSystemsError(t *testing.T, queryString string) (int, utils.ErrorResponse) { core.SetupTest(t) - w := CreateRequestRouterWithPath("GET", "/", "", queryString, nil, "", SystemsListHandler) + w := CreateRequestRouterWithPath("GET", "/", "", queryString, nil, "", SystemsListHandler, c) var errResp utils.ErrorResponse ParseResponseBody(t, w.Body.Bytes(), &errResp) @@ -276,7 +276,7 @@ func testSystemsError(t *testing.T, queryString string) (int, utils.ErrorRespons func TestSystemsTagsInMetadata(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithAccount("GET", "/", "", "?tags=ns1/k3=val4&tags=ns1/k1=val1", nil, "", - SystemsListHandler, 3) + SystemsListHandler, 3, c) var output SystemsResponse ParseResponseBody(t, w.Body.Bytes(), &output) diff --git a/manager/controllers/systemtags.go b/manager/controllers/systemtags.go index 5f02993d9..adec630bb 100644 --- a/manager/controllers/systemtags.go +++ b/manager/controllers/systemtags.go @@ -65,11 +65,11 @@ var SystemTagsOpts = ListOpts{ func SystemTagListHandler(c *gin.Context) { var err error account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) db := middlewares.DBFromContext(c) // https://stackoverflow.com/questions/33474778/how-to-group-result-by-array-column-in-postgres - sq := database.Systems(db, account, groups). + sq := database.Systems(db, account, workspaceIDs). Select("jsonb_array_elements(si.tags) AS tag") query := db.Table("(?) AS sq", sq). diff --git a/manager/controllers/systemtags_test.go b/manager/controllers/systemtags_test.go index ec12e5b05..14bb6ef82 100644 --- a/manager/controllers/systemtags_test.go +++ b/manager/controllers/systemtags_test.go @@ -12,7 +12,7 @@ import ( func TestSystemTagsListDefault(t *testing.T) { core.SetupTest(t) - w := CreateRequestRouterWithAccount("GET", "/", "", "", nil, "", SystemTagListHandler, 1) + w := CreateRequestRouterWithAccount("GET", "/", "", "", nil, "", SystemTagListHandler, 1, c) var output SystemTagsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -29,7 +29,7 @@ func TestSystemTagsListDefault(t *testing.T) { func TestSystemTagsListPagination(t *testing.T) { core.SetupTest(t) - w := CreateRequestRouterWithAccount("GET", "/", "", "?offset=1&limit=1", nil, "", SystemTagListHandler, 1) + w := CreateRequestRouterWithAccount("GET", "/", "", "?offset=1&limit=1", nil, "", SystemTagListHandler, 1, c) var output SystemTagsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -46,7 +46,7 @@ func TestSystemTagsListPagination(t *testing.T) { func TestSystemTagsTotalItems(t *testing.T) { core.SetupTest(t) - w := CreateRequestRouterWithAccount("GET", "/", "", "", nil, "", SystemTagListHandler, 1) + w := CreateRequestRouterWithAccount("GET", "/", "", "", nil, "", SystemTagListHandler, 1, c) var output SystemTagsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -56,7 +56,7 @@ func TestSystemTagsTotalItems(t *testing.T) { func TestSystemTagsListSort(t *testing.T) { core.SetupTest(t) - w := CreateRequestRouterWithAccount("GET", "/", "", "?sort=count", nil, "", SystemTagListHandler, 1) + w := CreateRequestRouterWithAccount("GET", "/", "", "?sort=count", nil, "", SystemTagListHandler, 1, c) var output SystemTagsResponse CheckResponse(t, w, http.StatusOK, &output) diff --git a/manager/controllers/template_subscribed_systems_update.go b/manager/controllers/template_subscribed_systems_update.go index 5657ed8da..bcf2d2af0 100644 --- a/manager/controllers/template_subscribed_systems_update.go +++ b/manager/controllers/template_subscribed_systems_update.go @@ -26,6 +26,7 @@ import ( // @Router /templates/{template_id}/subscribed-systems [PATCH] func TemplateSubscribedSystemsUpdateHandler(c *gin.Context) { templateUUID := c.Param("template_id") + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) db := middlewares.DBFromContext(c) @@ -41,12 +42,12 @@ func TemplateSubscribedSystemsUpdateHandler(c *gin.Context) { } systemList := []string{systemUUID} - err = checkTemplateSystems(c, db, account, template, systemList, nil) + err = checkTemplateSystems(c, db, account, template, systemList, workspaceIDs) if err != nil { return } - err = assignCandlepinEnvironment(c, db, account, &template.EnvironmentID, systemList, nil) + err = assignCandlepinEnvironment(c, db, account, &template.EnvironmentID, systemList, workspaceIDs) if err != nil { return } diff --git a/manager/controllers/template_subscribed_systems_update_test.go b/manager/controllers/template_subscribed_systems_update_test.go index 14a207df3..d5062d9bd 100644 --- a/manager/controllers/template_subscribed_systems_update_test.go +++ b/manager/controllers/template_subscribed_systems_update_test.go @@ -49,7 +49,7 @@ func TestUpdateTemplateSubscribedSystems(t *testing.T) { database.CreateTemplate(t, templateAccount, templateUUID, nil) w := CreateRequestRouterWithParams("PATCH", "/:template_id/subscribed-systems", templateUUID, "", nil, "", - TemplateSubscribedSystemsUpdateHandler, templateAccount, + TemplateSubscribedSystemsUpdateHandler, templateAccount, c, core.ContextKV{Key: utils.KeySystem, Value: subscriptionUUID}, core.ContextKV{Key: utils.KeyOrgID, Value: orgID}) @@ -64,7 +64,7 @@ func TestUpdateTemplateSubscribedSystemsInvalid(t *testing.T) { database.CreateTemplate(t, templateAccount, templateUUID, nil) w := CreateRequestRouterWithParams("PATCH", "/:template_id/subscribed-systems", templateUUID, "", nil, "", - TemplateSubscribedSystemsUpdateHandler, templateAccount, + TemplateSubscribedSystemsUpdateHandler, templateAccount, c, core.ContextKV{Key: utils.KeySystem, Value: subscriptionInvalidUUID}) assert.Equal(t, http.StatusNotFound, w.Code) diff --git a/manager/controllers/template_systems.go b/manager/controllers/template_systems.go index 83d243275..d42b7de29 100644 --- a/manager/controllers/template_systems.go +++ b/manager/controllers/template_systems.go @@ -35,7 +35,7 @@ type TemplateSystemAttributes struct { InstallableAdvisories ApplicableAdvisories SystemTags - SystemGroups + SystemWorkspace SystemLastUpload } @@ -75,7 +75,7 @@ func getTemplate(c *gin.Context, tx *gorm.DB, account int, uuid string) (*models return &template, nil } -func templateSystemsQuery(c *gin.Context, account int, groups map[string]string) (*gorm.DB, Filters, error) { +func templateSystemsQuery(c *gin.Context, account int, workspaceIDs []string) (*gorm.DB, Filters, error) { templateUUID := c.Param("template_id") db := middlewares.DBFromContext(c) @@ -85,7 +85,7 @@ func templateSystemsQuery(c *gin.Context, account int, groups map[string]string) return nil, nil, err } - query := database.Systems(db, account, groups). + query := database.Systems(db, account, workspaceIDs). Where("spatch.template_id = ?", template.ID). Select(templateSystemSelect) @@ -97,9 +97,9 @@ func templateSystemsQuery(c *gin.Context, account int, groups map[string]string) return query, filters, nil } -func templateSystemsCommon(c *gin.Context, account int, groups map[string]string, +func templateSystemsCommon(c *gin.Context, account int, workspaceIDs []string, ) (*gorm.DB, *ListMeta, []string, error) { - query, filters, err := templateSystemsQuery(c, account, groups) + query, filters, err := templateSystemsQuery(c, account, workspaceIDs) if err != nil { return nil, nil, nil, err } // Error handled in method itself @@ -159,9 +159,9 @@ func templateSystemData(templateSystems []TemplateSystemsDBLookup) ([]TemplateSy // @Router /templates/{template_id}/systems [get] func TemplateSystemsListHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) - query, meta, params, err := templateSystemsCommon(c, account, groups) + query, meta, params, err := templateSystemsCommon(c, account, workspaceIDs) if err != nil { return } // Error handled in method itself @@ -208,9 +208,9 @@ func TemplateSystemsListHandler(c *gin.Context) { // @Router /ids/templates/{template_id}/systems [get] func TemplateSystemsListIDsHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) - query, meta, _, err := templateSystemsCommon(c, account, groups) + query, meta, _, err := templateSystemsCommon(c, account, workspaceIDs) if err != nil { return } // Error handled in method itself diff --git a/manager/controllers/template_systems_delete.go b/manager/controllers/template_systems_delete.go index bf84c7834..284a38a26 100644 --- a/manager/controllers/template_systems_delete.go +++ b/manager/controllers/template_systems_delete.go @@ -24,7 +24,7 @@ import ( // @Router /templates/systems [DELETE] func TemplateSystemsDeleteHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) var req TemplateSystemsUpdateRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -38,12 +38,12 @@ func TemplateSystemsDeleteHandler(c *gin.Context) { db := middlewares.DBFromContext(c) - err := checkTemplateSystems(c, db, account, nil, req.Systems, groups) + err := checkTemplateSystems(c, db, account, nil, req.Systems, workspaceIDs) if err != nil { return } - err = assignCandlepinEnvironment(c, db, account, nil, req.Systems, groups) + err = assignCandlepinEnvironment(c, db, account, nil, req.Systems, workspaceIDs) if err != nil { return } diff --git a/manager/controllers/template_systems_delete_test.go b/manager/controllers/template_systems_delete_test.go index a0541b985..3d56c0f43 100644 --- a/manager/controllers/template_systems_delete_test.go +++ b/manager/controllers/template_systems_delete_test.go @@ -22,7 +22,7 @@ func testTemplateSystemsDelete(t *testing.T, body TemplateSystemsUpdateRequest, } w := CreateRequestRouterWithParams("POST", "/systems", "", "", bytes.NewBuffer(bodyJSON), "", - TemplateSystemsDeleteHandler, templateAccount) + TemplateSystemsDeleteHandler, templateAccount, c) assert.Equal(t, status, w.Code) return w @@ -89,7 +89,7 @@ func TestTemplateSystemsDeleteTooManySystems(t *testing.T) { } w := CreateRequestRouterWithParams("PUT", templatePath, templateUUID, "", bytes.NewBuffer(putBodyJSON), "", - TemplateSystemsUpdateHandler, templateAccount) + TemplateSystemsUpdateHandler, templateAccount, c) assert.Equal(t, http.StatusOK, w.Code) systems = append(systems, additionalSystem) diff --git a/manager/controllers/template_systems_export.go b/manager/controllers/template_systems_export.go index 944b9468d..caf3681c7 100644 --- a/manager/controllers/template_systems_export.go +++ b/manager/controllers/template_systems_export.go @@ -33,9 +33,9 @@ import ( // @Router /export/templates/{template_id}/systems [get] func TemplateSystemsExportHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) - query, _, err := templateSystemsQuery(c, account, groups) + query, _, err := templateSystemsQuery(c, account, workspaceIDs) if err != nil { return } // Error handled in method itself diff --git a/manager/controllers/template_systems_export_test.go b/manager/controllers/template_systems_export_test.go index 0d3a312f3..cd24ca199 100644 --- a/manager/controllers/template_systems_export_test.go +++ b/manager/controllers/template_systems_export_test.go @@ -14,13 +14,13 @@ import ( var TemplateCsvHeader = "id,display_name,os,rhsm," + "installable_rhsa_count,installable_rhba_count,installable_rhea_count,installable_other_count," + "applicable_rhsa_count,applicable_rhba_count,applicable_rhea_count,applicable_other_count," + - "tags,groups,last_upload" + "tags,workspace_id,workspace_name,last_upload" // nolint: dupl func TestTemplateSystemsExportJSON(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", "99900000-0000-0000-0000-000000000001", "", - nil, "application/json", TemplateSystemsExportHandler) + nil, "application/json", TemplateSystemsExportHandler, c) var output []TemplateSystemsDBLookup CheckResponse(t, w, http.StatusOK, &output) @@ -56,7 +56,7 @@ func TestTemplateSystemsExportJSON(t *testing.T) { func TestTemplateSystemsExportCSV(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", "99900000-0000-0000-0000-000000000001", "", - nil, "text/csv", TemplateSystemsExportHandler) + nil, "text/csv", TemplateSystemsExportHandler, c) assert.Equal(t, http.StatusOK, w.Code) body := w.Body.String() @@ -68,19 +68,18 @@ func TestTemplateSystemsExportCSV(t *testing.T) { assert.Equal(t, "00000000-0000-0000-0000-000000000002,00000000-0000-0000-0000-000000000002,RHEL 8.1,"+ "8.1,0,0,0,0,0,0,1,0,\"[{'key':'k1','namespace':'ns1','value':'val1'},"+ "{'key':'k2','namespace':'ns1','value':'val2'},{'key':'k3','namespace':'ns1','value':'val3'}]\","+ - "\"[{'id':'inventory-group-1','name':'group1'}]\",2018-09-22T16:00:00Z", + "inventory-group-1,group1,2018-09-22T16:00:00Z", lines[1]) assert.Equal(t, "00000000-0000-0000-0000-000000000001,00000000-0000-0000-0000-000000000001,RHEL 8.10,"+ "8.10,2,2,1,0,2,3,3,0,\"[{'key':'k1','namespace':'ns1','value':'val1'},"+ - "{'key':'k2','namespace':'ns1','value':'val2'}]\","+ - "\"[{'id':'inventory-group-1','name':'group1'}]\",2020-09-22T16:00:00Z", + "{'key':'k2','namespace':'ns1','value':'val2'}]\",inventory-group-1,group1,2020-09-22T16:00:00Z", lines[2]) } func TestTemplateSystemsExportWrongFormat(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", "99900000-0000-0000-0000-000000000001", "", - nil, "test-format", TemplateSystemsExportHandler) + nil, "test-format", TemplateSystemsExportHandler, c) assert.Equal(t, http.StatusUnsupportedMediaType, w.Code) body := w.Body.String() @@ -90,7 +89,7 @@ func TestTemplateSystemsExportWrongFormat(t *testing.T) { func TestTemplateSystemsExportCSVFilter(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", "99900000-0000-0000-0000-000000000001", - "?filter[display_name]=nonexistent", nil, "text/csv", TemplateSystemsExportHandler) + "?filter[display_name]=nonexistent", nil, "text/csv", TemplateSystemsExportHandler, c) assert.Equal(t, http.StatusOK, w.Code) body := w.Body.String() @@ -103,7 +102,7 @@ func TestTemplateSystemsExportCSVFilter(t *testing.T) { func TestExportTemplateSystemsTags(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", "99900000-0000-0000-0000-000000000001", - "?tags=ns1/k2=val2", nil, "application/json", TemplateSystemsExportHandler) + "?tags=ns1/k2=val2", nil, "application/json", TemplateSystemsExportHandler, c) var output []TemplateSystemsDBLookup CheckResponse(t, w, http.StatusOK, &output) @@ -115,7 +114,7 @@ func TestExportTemplateSystemsTags(t *testing.T) { func TestExportTemplateSystemsTagsInvalid(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", "99900000-0000-0000-0000-000000000001", - "?tags=ns1/k3=val4&tags=invalidTag", nil, "application/json", TemplateSystemsExportHandler) + "?tags=ns1/k3=val4&tags=invalidTag", nil, "application/json", TemplateSystemsExportHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -126,7 +125,7 @@ func TestTemplateSystemsExportWorkloads(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", "99900000-0000-0000-0000-000000000001", "?filter[system_profile][sap_system]=true&filter[system_profile][sap_sids]=ABC", nil, "application/json", - TemplateSystemsExportHandler) + TemplateSystemsExportHandler, c) var output []TemplateSystemsDBLookup CheckResponse(t, w, http.StatusOK, &output) diff --git a/manager/controllers/template_systems_test.go b/manager/controllers/template_systems_test.go index 5c2e52a61..44f5a744b 100644 --- a/manager/controllers/template_systems_test.go +++ b/manager/controllers/template_systems_test.go @@ -12,7 +12,7 @@ import ( func testTemplateSystems(t *testing.T, param, queryString string) TemplateSystemsResponse { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", param, queryString, nil, "", - TemplateSystemsListHandler) + TemplateSystemsListHandler, c) var output TemplateSystemsResponse CheckResponse(t, w, http.StatusOK, &output) @@ -78,7 +78,7 @@ func TestTemplateSystemsUnlimited(t *testing.T) { func TestTemplateSystemOffsetOverflow(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", "99900000-0000-0000-0000-000000000001", - "?offset=10&limit=4", nil, "", TemplateSystemsListHandler) + "?offset=10&limit=4", nil, "", TemplateSystemsListHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -101,7 +101,7 @@ func TestTemplatesFilterTag(t *testing.T) { func TestTemplateSystemsWrongSort(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", "99900000-0000-0000-0000-000000000001", - "?sort=unknown_key", nil, "", TemplateSystemsListHandler) + "?sort=unknown_key", nil, "", TemplateSystemsListHandler, c) assert.Equal(t, http.StatusBadRequest, w.Code) } @@ -131,7 +131,7 @@ func TestTemplateSystemsSearch(t *testing.T) { func TestTemplateSystemsInvalidUUID(t *testing.T) { core.SetupTest(t) w := CreateRequestRouterWithPath("GET", "/:template_id/systems", "InvalidTemplateUUID", - "?sort=unknown_key", nil, "", TemplateSystemsListHandler) + "?sort=unknown_key", nil, "", TemplateSystemsListHandler, c) assert.Equal(t, http.StatusNotFound, w.Code) assert.Equal(t, `{"error":"Invalid template uuid: InvalidTemplateUUID"}`, w.Body.String()) diff --git a/manager/controllers/template_systems_update.go b/manager/controllers/template_systems_update.go index 9ba90f0ee..2d4591eab 100644 --- a/manager/controllers/template_systems_update.go +++ b/manager/controllers/template_systems_update.go @@ -54,7 +54,7 @@ type SystemTemplateDBLookup struct { func TemplateSystemsUpdateHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) templateUUID := c.Param("template_id") - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) var req TemplateSystemsUpdateRequest if err := c.ShouldBindJSON(&req); err != nil { @@ -73,12 +73,12 @@ func TemplateSystemsUpdateHandler(c *gin.Context) { return } - err = checkTemplateSystems(c, db, account, template, req.Systems, groups) + err = checkTemplateSystems(c, db, account, template, req.Systems, workspaceIDs) if err != nil { return } - err = assignCandlepinEnvironment(c, db, account, &template.EnvironmentID, req.Systems, groups) + err = assignCandlepinEnvironment(c, db, account, &template.EnvironmentID, req.Systems, workspaceIDs) if err != nil { return } @@ -97,14 +97,14 @@ func TemplateSystemsUpdateHandler(c *gin.Context) { } func checkTemplateSystems(c *gin.Context, db *gorm.DB, accountID int, template *models.Template, - inventoryIDs []string, groups map[string]string) error { + inventoryIDs []string, workspaceIDs []string) error { if len(inventoryIDs) == 0 { err := errors.New(InvalidInventoryIDsErr) utils.LogAndRespBadRequest(c, err, InvalidInventoryIDsErr) return err } - err := checkInventoryIDs(db, accountID, inventoryIDs, groups) + err := checkInventoryIDs(db, accountID, inventoryIDs, workspaceIDs) if err != nil { switch { case errors.Is(err, base.ErrBadRequest): @@ -119,7 +119,7 @@ func checkTemplateSystems(c *gin.Context, db *gorm.DB, accountID int, template * } } - if err := templateArchVersionMatch(db, inventoryIDs, template, accountID, groups); err != nil { + if err := templateArchVersionMatch(db, inventoryIDs, template, accountID, workspaceIDs); err != nil { msg := fmt.Sprintf("Incompatible template and system version or architecture: %s", err.Error()) utils.LogAndRespBadRequest(c, err, msg) return err @@ -164,7 +164,7 @@ func assignTemplateSystems(c *gin.Context, db *gorm.DB, accountID int, template } func templateArchVersionMatch( - db *gorm.DB, inventoryIDs []string, template *models.Template, acc int, groups map[string]string, + db *gorm.DB, inventoryIDs []string, template *models.Template, acc int, workspaceIDs []string, ) error { if template == nil { return nil @@ -175,7 +175,7 @@ func templateArchVersionMatch( Version string }{} var err error - err = database.Systems(db, acc, groups). + err = database.Systems(db, acc, workspaceIDs). Select("si.inventory_id as inventory_id, si.os_major as version, si.arch as arch"). Where("si.inventory_id in (?)", inventoryIDs).Find(&sysArchVersions).Error if err != nil { @@ -220,13 +220,13 @@ func callCandlepin(ctx context.Context, owner string, request *candlepin.Consume } func assignCandlepinEnvironment(c *gin.Context, db *gorm.DB, accountID int, env *string, inventoryIDs []string, - groups map[string]string) error { + workspaceIDs []string) error { var hosts = []struct { InventoryID string Consumer *string }{} - err := database.Systems(db, accountID, groups). + err := database.Systems(db, accountID, workspaceIDs). Select("si.inventory_id as inventory_id, si.subscription_manager_id as consumer"). Where("si.inventory_id in (?)", inventoryIDs).Find(&hosts).Error if err != nil { @@ -270,12 +270,12 @@ func assignCandlepinEnvironment(c *gin.Context, db *gorm.DB, accountID int, env return nil } -func checkInventoryIDs(db *gorm.DB, accountID int, inventoryIDs []string, groups map[string]string) (err error) { +func checkInventoryIDs(db *gorm.DB, accountID int, inventoryIDs []string, workspaceIDs []string) (err error) { var containingSystems []SystemTemplateDBLookup var missingIDs []string var satelliteIDs []string var bootcIDs []string - err = database.Systems(db, accountID, groups). + err = database.Systems(db, accountID, workspaceIDs). Select("si.inventory_id, si.satellite_managed, si.bootc"). Where("si.inventory_id IN (?::uuid)", inventoryIDs). Scan(&containingSystems).Error diff --git a/manager/controllers/template_systems_update_test.go b/manager/controllers/template_systems_update_test.go index afec85543..ad1a2edcf 100644 --- a/manager/controllers/template_systems_update_test.go +++ b/manager/controllers/template_systems_update_test.go @@ -37,7 +37,7 @@ func TestUpdateTemplateSystems(t *testing.T) { "00000000-0000-0000-0000-000000000007", }) w := CreateRequestRouterWithParams("PUT", templatePath, templateUUID, "", bytes.NewBufferString(data), "", - TemplateSystemsUpdateHandler, templateAccount) + TemplateSystemsUpdateHandler, templateAccount, c) assert.Equal(t, http.StatusOK, w.Code) database.CheckTemplateSystems(t, templateAccount, templateUUID, templateSystems) @@ -56,7 +56,7 @@ func TestUpdateTemplateInvalidVersion(t *testing.T) { "00000000-0000-0000-0000-000000000007", }) w := CreateRequestRouterWithParams("PUT", templatePath, templateUUID, "", bytes.NewBufferString(data), "", - TemplateSystemsUpdateHandler, templateAccount) + TemplateSystemsUpdateHandler, templateAccount, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -79,7 +79,7 @@ func TestUpdateTemplateInvalidSystem(t *testing.T) { }` database.CreateTemplate(t, templateAccount, templateUUID, []string{}) w := CreateRequestRouterWithParams("PUT", templatePath, templateUUID, "", bytes.NewBufferString(data), "", - TemplateSystemsUpdateHandler, templateAccount) + TemplateSystemsUpdateHandler, templateAccount, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusNotFound, &errResp) @@ -102,7 +102,7 @@ func TestUpdateTemplateSystemNotInCandlepin(t *testing.T) { }` database.CreateTemplate(t, templateAccount, templateUUID, []string{}) w := CreateRequestRouterWithParams("PUT", templatePath, templateUUID, "", bytes.NewBufferString(data), "", - TemplateSystemsUpdateHandler, templateAccount) + TemplateSystemsUpdateHandler, templateAccount, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -118,7 +118,7 @@ func TestUpdateTemplateNullValues(t *testing.T) { database.CreateTemplate(t, templateAccount, templateUUID, templateSystems) data := "{}" w := CreateRequestRouterWithParams("PUT", templatePath, templateUUID, "", bytes.NewBufferString(data), "", - TemplateSystemsUpdateHandler, templateAccount) + TemplateSystemsUpdateHandler, templateAccount, c) var resp interface{} CheckResponse(t, w, http.StatusBadRequest, &resp) @@ -130,7 +130,7 @@ func TestUpdateTemplateNullValues(t *testing.T) { func TestUpdateTemplateInvalidTemplateID(t *testing.T) { core.SetupTestEnvironment() w := CreateRequestRouterWithParams("PUT", templatePath, "invalidTemplate", "", bytes.NewBufferString("{}"), "", - TemplateSystemsUpdateHandler, templateAccount) + TemplateSystemsUpdateHandler, templateAccount, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusNotFound, &errResp) @@ -152,7 +152,7 @@ func TestReassignTemplateSystems2(t *testing.T) { ] }` w := CreateRequestRouterWithParams("PUT", templatePath, template2, "", bytes.NewBufferString(data), "", - TemplateSystemsUpdateHandler, templateAccount) + TemplateSystemsUpdateHandler, templateAccount, c) assert.Equal(t, http.StatusOK, w.Code) @@ -202,7 +202,7 @@ func testUpdateTemplateBadRequest(t *testing.T, satelliteManaged, bootc bool) { }` w := CreateRequestRouterWithParams("PUT", templatePath, templateUUID, "", bytes.NewBufferString(data), "", - TemplateSystemsUpdateHandler, templateAccount) + TemplateSystemsUpdateHandler, templateAccount, c) var err utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &err) } @@ -238,7 +238,7 @@ func TestUpdateTemplateSystemsCandlepin404(t *testing.T) { }) defer database.DeleteTemplate(t, templateAccount, templateUUID) w := CreateRequestRouterWithParams("PUT", templatePath, templateUUID, "", bytes.NewBufferString(data), "", - TemplateSystemsUpdateHandler, templateAccount, core.ContextKV{Key: utils.KeyOrgID, Value: orgID}) + TemplateSystemsUpdateHandler, templateAccount, c, core.ContextKV{Key: utils.KeyOrgID, Value: orgID}) assert.Equal(t, http.StatusBadRequest, w.Code) database.CheckTemplateSystems(t, templateAccount, templateUUID, []string{"00000000-0000-0000-0000-000000000007"}) @@ -251,7 +251,7 @@ func TestUpdateTemplateSystemsCandlepin404(t *testing.T) { ] }` w = CreateRequestRouterWithParams("PUT", templatePath, templateUUID, "", bytes.NewBufferString(data), "", - TemplateSystemsUpdateHandler, templateAccount, core.ContextKV{Key: utils.KeyOrgID, Value: orgID}) + TemplateSystemsUpdateHandler, templateAccount, c, core.ContextKV{Key: utils.KeyOrgID, Value: orgID}) assert.Equal(t, http.StatusBadRequest, w.Code) database.CheckTemplateSystems(t, templateAccount, templateUUID, @@ -278,7 +278,7 @@ func TestUpdateTemplateTooManySystems(t *testing.T) { } w := CreateRequestRouterWithParams("PUT", templatePath, templateUUID, "", bytes.NewBuffer(bodyJSON), "", - TemplateSystemsUpdateHandler, templateAccount) + TemplateSystemsUpdateHandler, templateAccount, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) diff --git a/manager/controllers/templates.go b/manager/controllers/templates.go index 75d58c20a..d58a92306 100644 --- a/manager/controllers/templates.go +++ b/manager/controllers/templates.go @@ -77,14 +77,14 @@ type TemplatesResponse struct { // @Router /templates [get] func TemplatesListHandler(c *gin.Context) { account := c.GetInt(utils.KeyAccount) - groups := c.GetStringMapString(utils.KeyInventoryGroups) + workspaceIDs := c.GetStringSlice(utils.KeyInventoryWorkspaces) filters, err := ParseAllFilters(c, TemplateOpts) if err != nil { return } db := middlewares.DBFromContext(c) - query := templatesQuery(db, filters, account, groups) + query := templatesQuery(db, filters, account, workspaceIDs) query, meta, params, err := ListCommon(query, c, filters, TemplateOpts) if err != nil { @@ -123,8 +123,8 @@ func TemplatesListHandler(c *gin.Context) { c.JSON(http.StatusOK, &resp) } -func templatesQuery(db *gorm.DB, filters map[string]FilterData, account int, groups map[string]string) *gorm.DB { - subq := database.Systems(db, account, groups). +func templatesQuery(db *gorm.DB, filters map[string]FilterData, account int, workspaceIDs []string) *gorm.DB { + subq := database.Systems(db, account, workspaceIDs). Select("spatch.template_id, count(*) as systems"). Group("spatch.template_id") diff --git a/manager/controllers/templates_test.go b/manager/controllers/templates_test.go index 7e036e480..29b7fa64f 100644 --- a/manager/controllers/templates_test.go +++ b/manager/controllers/templates_test.go @@ -12,7 +12,7 @@ import ( func testTemplates(t *testing.T, url string) TemplatesResponse { core.SetupTest(t) - w := CreateRequest("GET", url, nil, "", TemplatesListHandler) + w := CreateRequest("GET", url, nil, "", TemplatesListHandler, c) var output TemplatesResponse CheckResponse(t, w, http.StatusOK, &output) @@ -21,7 +21,7 @@ func testTemplates(t *testing.T, url string) TemplatesResponse { func testTemplatesError(t *testing.T, url string, expectedStatus int) utils.ErrorResponse { core.SetupTest(t) - w := CreateRequest("GET", url, nil, "", TemplatesListHandler) + w := CreateRequest("GET", url, nil, "", TemplatesListHandler, c) var output utils.ErrorResponse CheckResponse(t, w, expectedStatus, &output) @@ -64,7 +64,7 @@ func TestTemplatesOffsetLimit(t *testing.T) { func TestTemplatesOffsetOverflow(t *testing.T) { core.SetupTest(t) - w := CreateRequest("GET", "/?offset=10&limit=4", nil, "", TemplatesListHandler) + w := CreateRequest("GET", "/?offset=10&limit=4", nil, "", TemplatesListHandler, c) var errResp utils.ErrorResponse CheckResponse(t, w, http.StatusBadRequest, &errResp) @@ -115,7 +115,7 @@ func TestTemplatesSort(t *testing.T) { func TestTemplatesWrongSort(t *testing.T) { core.SetupTest(t) - w := CreateRequest("GET", "/?sort=unknown_key", nil, "", TemplatesListHandler) + w := CreateRequest("GET", "/?sort=unknown_key", nil, "", TemplatesListHandler, c) assert.Equal(t, http.StatusBadRequest, w.Code) } diff --git a/manager/controllers/utils.go b/manager/controllers/utils.go index 416564a9e..a2ab02d5c 100644 --- a/manager/controllers/utils.go +++ b/manager/controllers/utils.go @@ -392,18 +392,7 @@ func ApplyInventoryWhere(filters map[string]FilterData, tx *gorm.DB) (*gorm.DB, // returns "(si.mssql_workload_version) = 1.0" func buildInventoryQuery(tx *gorm.DB, key string, values []string) *gorm.DB { if strings.Contains(key, "group_name") { - groups := []string{} - for _, v := range values { - name := v - group, err := utils.ParseInventoryGroup(nil, &name) - if err != nil { - // couldn't marshal inventory group to json - continue - } - groups = append(groups, group) - } - jsonq := fmt.Sprintf("{%s}", strings.Join(groups, ",")) - return tx.Where("si.workspaces @> ANY (?::jsonb[])", jsonq) + return tx.Where("si.workspace_name IN (?)", values) } var cmp string diff --git a/manager/controllers/utils_test.go b/manager/controllers/utils_test.go index 8be6e427d..7216b380d 100644 --- a/manager/controllers/utils_test.go +++ b/manager/controllers/utils_test.go @@ -21,17 +21,15 @@ func TestGroupNameFilter(t *testing.T) { filters, err := ParseAllFilters(c, ListOpts{}) assert.Nil(t, err) - var systems []SystemsID - groups := map[string]string{ - utils.KeyGrouped: `{"[{\"id\":\"inventory-group-1\"}]","[{\"id\":\"inventory-group-2\"}]"}`, - } - tx := database.Systems(database.DB, 1, groups) - tx, _ = ApplyInventoryFilter(filters, tx, "si.inventory_id") - tx.Scan(&systems) - - assert.Equal(t, 2, len(systems)) // 2 systems with `group2` in test_data - assert.Equal(t, "00000000-0000-0000-0000-000000000007", systems[0].ID) - assert.Equal(t, "00000000-0000-0000-0000-000000000008", systems[1].ID) + var systems2 []SystemsID + workspaceIDs := []string{"inventory-group-1", "inventory-group-2"} + ty := database.Systems(database.DB, 1, workspaceIDs) + ty, _ = ApplyInventoryFilter(filters, ty, "si.inventory_id") + ty.Scan(&systems2) + + assert.Equal(t, 2, len(systems2)) + assert.Equal(t, "00000000-0000-0000-0000-000000000007", systems2[0].ID) + assert.Equal(t, "00000000-0000-0000-0000-000000000008", systems2[1].ID) } func TestGroupNameFilter2(t *testing.T) { @@ -44,13 +42,11 @@ func TestGroupNameFilter2(t *testing.T) { filters, err := ParseAllFilters(c, ListOpts{}) assert.Nil(t, err) - var systems []SystemsID - groups := map[string]string{ - utils.KeyGrouped: `{"[{\"id\":\"inventory-group-1\"}]","[{\"id\":\"inventory-group-2\"}]"}`, - } - tx := database.Systems(database.DB, 1, groups) - tx, _ = ApplyInventoryFilter(filters, tx, "si.inventory_id") - tx.Scan(&systems) + var systems2 []SystemsID + workspaceIDs := []string{"inventory-group-1", "inventory-group-2"} + ty := database.Systems(database.DB, 1, workspaceIDs) + ty, _ = ApplyInventoryFilter(filters, ty, "si.inventory_id") + ty.Scan(&systems2) - assert.Equal(t, 9, len(systems)) // 2 systems with `group2`, 6 with `group1` in test_data + assert.Equal(t, 9, len(systems2)) } diff --git a/manager/middlewares/kessel.go b/manager/middlewares/kessel.go index 6c71ec00b..91aea0b65 100644 --- a/manager/middlewares/kessel.go +++ b/manager/middlewares/kessel.go @@ -3,7 +3,6 @@ package middlewares import ( "app/base/utils" "context" - "fmt" "net/http" "strings" "time" @@ -35,27 +34,6 @@ func setupClient() (kesselv2.KesselInventoryServiceClient, *grpc.ClientConn, err return clientBuilder.Build() } -func processWorkspaces(workspaces []*kesselv2.StreamedListObjectsResponse) (map[string]string, error) { - defer func(start time.Time) { - utils.LogDebug("durationMs", time.Since(start).Milliseconds(), "processed workspaces") - }(time.Now()) - - groups := make([]string, 0, len(workspaces)) - for _, workspace := range workspaces { - group, err := utils.ParseInventoryGroup(&workspace.Object.ResourceId, nil) - if err != nil { - // couldn't marshal inventory group to json - continue - } - groups = append(groups, group) - } - - if len(groups) == 0 { - return nil, errors.New("no workspaces found") - } - return map[string]string{utils.KeyGrouped: fmt.Sprintf("{%s}", strings.Join(groups, ","))}, nil -} - func buildPermission(c *gin.Context) string { permission := "patch_system_" nameSplit := strings.Split(c.HandlerName(), ".") @@ -142,13 +120,17 @@ func hasPermissionKessel(c *gin.Context) { return } - inventoryGroups, err := processWorkspaces(workspaces) - if err != nil { - utils.LogWarn(err.Error()) + workspaceIDs := make([]string, 0, len(workspaces)) + for _, workspace := range workspaces { + workspaceIDs = append(workspaceIDs, workspace.Object.ResourceId) + } + + if len(workspaceIDs) == 0 { + utils.LogWarn(errors.New("no workspaces found")) c.AbortWithStatusJSON(http.StatusUnauthorized, utils.ErrorResponse{Error: "Missing permission"}) return } - c.Set(utils.KeyInventoryGroups, inventoryGroups) + c.Set(utils.KeyInventoryWorkspaces, workspaceIDs) } func Kessel() gin.HandlerFunc { diff --git a/manager/middlewares/kessel_test.go b/manager/middlewares/kessel_test.go index b70da7a9e..3813c315d 100644 --- a/manager/middlewares/kessel_test.go +++ b/manager/middlewares/kessel_test.go @@ -2,9 +2,7 @@ package middlewares import ( "app/base/utils" - "fmt" "net/http" - "strconv" "testing" "google.golang.org/grpc" @@ -66,18 +64,6 @@ func TestSetupClient(t *testing.T) { utils.CoreCfg.KesselAuthClientSecret = originalKesselAuthClientSecret } -func TestProcessWorkspaces(t *testing.T) { - expected := fmt.Sprintf("{%s,%s}", strconv.Quote(`[{"id":"test-1"}]`), strconv.Quote(`[{"id":"test-2"}]`)) - workspaces := []*kesselv2.StreamedListObjectsResponse{ - {Object: &kesselv2.ResourceReference{ResourceId: "test-1"}}, - {Object: &kesselv2.ResourceReference{ResourceId: "test-2"}}, - } - processed, err := processWorkspaces(workspaces) - if assert.NoError(t, err) { - assert.Equal(t, expected, processed[utils.KeyGrouped]) - } -} - func TestBuildPermission(t *testing.T) { c := &gin.Context{Request: &http.Request{Method: http.MethodGet}} permission := buildPermission(c) @@ -109,11 +95,12 @@ func TestHasPermissionKessel(t *testing.T) { c.Request.Header.Set("x-rh-identity", "ewogICAgImVudGl0bGVtZW50cyI6IHsKICAgICAgICAiaW5zaWdodHMiOiB7CiAgICAgICAgICAgICJpc19lbnRpdGxlZCI6IHRydWUKICAgICAgICB9LAogICAgICAgICJjb3N0X21hbmFnZW1lbnQiOiB7CiAgICAgICAgICAgICJpc19lbnRpdGxlZCI6IHRydWUKICAgICAgICB9LAogICAgICAgICJhbnNpYmxlIjogewogICAgICAgICAgICAiaXNfZW50aXRsZWQiOiB0cnVlCiAgICAgICAgfSwKICAgICAgICAib3BlbnNoaWZ0IjogewogICAgICAgICAgICAiaXNfZW50aXRsZWQiOiB0cnVlCiAgICAgICAgfSwKICAgICAgICAic21hcnRfbWFuYWdlbWVudCI6IHsKICAgICAgICAgICAgImlzX2VudGl0bGVkIjogdHJ1ZQogICAgICAgIH0sCiAgICAgICAgIm1pZ3JhdGlvbnMiOiB7CiAgICAgICAgICAgICJpc19lbnRpdGxlZCI6IHRydWUKICAgICAgICB9CiAgICB9LAogICAgImlkZW50aXR5IjogewogICAgICAgICJpbnRlcm5hbCI6IHsKICAgICAgICAgICAgImF1dGhfdGltZSI6IDI5OSwKICAgICAgICAgICAgImF1dGhfdHlwZSI6ICJiYXNpYy1hdXRoIiwKICAgICAgICAgICAgIm9yZ19pZCI6ICIxMTc4OTc3MiIKICAgICAgICB9LAogICAgICAgICJhY2NvdW50X251bWJlciI6ICI2MDg5NzE5IiwKICAgICAgICAidXNlciI6IHsKICAgICAgICAgICAgImZpcnN0X25hbWUiOiAiSW5zaWdodHMiLAogICAgICAgICAgICAiaXNfYWN0aXZlIjogdHJ1ZSwKICAgICAgICAgICAgImlzX2ludGVybmFsIjogZmFsc2UsCiAgICAgICAgICAgICJsYXN0X25hbWUiOiAiUUEiLAogICAgICAgICAgICAibG9jYWxlIjogImVuX1VTIiwKICAgICAgICAgICAgImlzX29yZ19hZG1pbiI6IHRydWUsCiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJpbnNpZ2h0cy1xYSIsCiAgICAgICAgICAgICJlbWFpbCI6ICJqbmVlZGxlK3FhQHJlZGhhdC5jb20iLAogICAgICAgICAgICAidXNlcl9pZCI6ICI2MDg5NzE5IgogICAgICAgIH0sCiAgICAgICAgInR5cGUiOiAiVXNlciIKICAgIH0KfQ==") //nolint:lll hasPermissionKessel(c) - inventoryGroups, found := c.Get(utils.KeyInventoryGroups) + workspaces, found := c.Get(utils.KeyInventoryWorkspaces) require.True(t, found) - inventoryGroupMap, ok := (inventoryGroups).(map[string]string) + workspaceIDs, ok := (workspaces).([]string) require.True(t, ok) - assert.Equal(t, `{"[{\"id\":\"inventory-group-1\"}]"}`, inventoryGroupMap[utils.KeyGrouped]) + require.Greater(t, len(workspaceIDs), 0) + assert.Equal(t, "inventory-group-1", workspaceIDs[0]) } func mockXRHID(userType string) *identity.XRHID { diff --git a/manager/middlewares/rbac.go b/manager/middlewares/rbac.go index 7232aff9d..9ddf258c6 100644 --- a/manager/middlewares/rbac.go +++ b/manager/middlewares/rbac.go @@ -6,7 +6,6 @@ import ( "app/base/rbac" "app/base/utils" "app/manager/config" - "fmt" "net/http" "strconv" "strings" @@ -122,70 +121,25 @@ func isAccessGranted(c *gin.Context) bool { handlerName := nameSplitted[len(nameSplitted)-1] granted := checkPermissions(&access, handlerName, c.Request.Method) - if granted { - // collect inventory groups - groups, err := findInventoryGroups(&access) - if err != nil { - utils.LogError("err", err.Error(), "RBAC") - granted = false - } - c.Set(utils.KeyInventoryGroups, groups) + if !granted { + return granted } - return granted -} - -func findInventoryGroups(access *rbac.AccessPagination) (map[string]string, error) { - res := make(map[string]string) - if len(access.Data) == 0 { - utils.LogDebug("rbac zero access data") - return res, nil - } - inventoryHostsReadPerms := expandedPermission(inventoryHostsReadPerm) - groups := []string{} + workspaceIDs := make([]string, 0) for _, a := range access.Data { - // look for groups only on inventory:hosts:read permissions - if !slices.Contains(inventoryHostsReadPerms, a.Permission) { - continue - } - - if len(a.ResourceDefinitions) == 0 { - // access to all groups - utils.LogDebug("rbac access to all groups") - return nil, nil - } for _, rd := range a.ResourceDefinitions { - if rd.AttributeFilter.Key != "group.id" { - continue - } - - if rd.AttributeFilter.Operation != "in" && rd.AttributeFilter.Operation != "equal" { - err := fmt.Errorf( - "invalid value '%s' for attributeFilter.Operation", - rd.AttributeFilter.Operation, - ) - return nil, err - } for _, v := range rd.AttributeFilter.Value { if v == nil { - res[utils.KeyUngrouped] = "[]" - continue + // TODO: use root ws + } else { + workspaceIDs = append(workspaceIDs, *v) } - group, err := utils.ParseInventoryGroup(v, nil) - if err != nil { - // couldn't marshal inventory group to json - continue - } - groups = append(groups, group) } } } + c.Set(utils.KeyInventoryWorkspaces, workspaceIDs) - if len(groups) > 0 { - res[utils.KeyGrouped] = fmt.Sprintf("{%s}", strings.Join(groups, ",")) - } - utils.LogDebug("group_count", len(groups), "processed groups") - return res, nil + return granted } func RBAC() gin.HandlerFunc { diff --git a/manager/middlewares/rbac_test.go b/manager/middlewares/rbac_test.go index aa41641dd..f54e22a7b 100644 --- a/manager/middlewares/rbac_test.go +++ b/manager/middlewares/rbac_test.go @@ -2,8 +2,6 @@ package middlewares import ( "app/base/rbac" - "app/base/utils" - "encoding/json" "net/http" "net/http/httptest" "testing" @@ -242,148 +240,6 @@ func TestPermissionsRead(t *testing.T) { assert.False(t, checkPermissions(&access, handler, "GET")) } -func TestFindInventoryGroupsGrouped(t *testing.T) { - access := &rbac.AccessPagination{ - Data: []rbac.Access{{ - Permission: "inventory:hosts:read", - ResourceDefinitions: []rbac.ResourceDefinition{{ - AttributeFilter: rbac.AttributeFilter{ - Key: "group.id", - Value: []*string{&group1}, - Operation: "in", - }, - }}, - }}, - } - groups, err := findInventoryGroups(access) - if assert.NoError(t, err) { - assert.Equal(t, - `{"[{\"id\":\"df57820e-965c-49a6-b0bc-797b7dd60581\"}]"}`, - groups[utils.KeyGrouped], - ) - val, ok := groups[utils.KeyUngrouped] - assert.Equal(t, "", val) - assert.Equal(t, false, ok) - } -} - -func TestFindInventoryGroupsUnrouped(t *testing.T) { - access := &rbac.AccessPagination{ - Data: []rbac.Access{{ - Permission: "inventory:hosts:read", - ResourceDefinitions: []rbac.ResourceDefinition{{ - AttributeFilter: rbac.AttributeFilter{ - Key: "group.id", - Value: []*string{nil}, - Operation: "in", - }, - }}, - }}, - } - groups, err := findInventoryGroups(access) - if assert.NoError(t, err) { - val, ok := groups[utils.KeyGrouped] - assert.Equal(t, "", val) - assert.Equal(t, false, ok) - assert.Equal(t, "[]", groups[utils.KeyUngrouped]) - } -} - -func TestFindInventoryGroups(t *testing.T) { - access := &rbac.AccessPagination{ - Data: []rbac.Access{{ - Permission: "inventory:hosts:read", - ResourceDefinitions: []rbac.ResourceDefinition{{ - AttributeFilter: rbac.AttributeFilter{ - Key: "group.id", - Value: []*string{&group1, &group2, nil}, - Operation: "in", - }, - }}, - }}, - } - groups, err := findInventoryGroups(access) - if assert.NoError(t, err) { - assert.Equal(t, - `{"[{\"id\":\"df57820e-965c-49a6-b0bc-797b7dd60581\"}]","[{\"id\":\"df3f0efd-c853-41b5-80a1-86881d5343d1\"}]"}`, - groups[utils.KeyGrouped], - ) - assert.Equal(t, "[]", groups[utils.KeyUngrouped]) - } -} - -func TestFindInventoryGroupsOverwrite(t *testing.T) { - access := &rbac.AccessPagination{ - Data: []rbac.Access{ - { - Permission: "inventory:hosts:read", - ResourceDefinitions: []rbac.ResourceDefinition{{ - AttributeFilter: rbac.AttributeFilter{ - Key: "group.id", - Value: []*string{&group1, nil}, - Operation: "in", - }, - }}, - }, - { - Permission: "inventory:hosts:read", - ResourceDefinitions: []rbac.ResourceDefinition{}, - }, - }, - } - groups, err := findInventoryGroups(access) - if assert.NoError(t, err) { - // we expect access to all groups (empty map) - assert.Equal(t, 0, len(groups)) - } -} - -func TestFindInventoryGroupsOverwrite2(t *testing.T) { - access := &rbac.AccessPagination{ - Data: []rbac.Access{ - { - Permission: "inventory:hosts:read", - ResourceDefinitions: []rbac.ResourceDefinition{}, - }, - { - Permission: "inventory:hosts:read", - ResourceDefinitions: []rbac.ResourceDefinition{{ - AttributeFilter: rbac.AttributeFilter{ - Key: "group.id", - Value: []*string{&group1, nil}, - Operation: "in", - }, - }}, - }, - }, - } - groups, err := findInventoryGroups(access) - if assert.NoError(t, err) { - // we expect access to all groups (empty map) - assert.Equal(t, 0, len(groups)) - } -} - -func TestFindInventoryGroupsInvalidOp(t *testing.T) { - access := &rbac.AccessPagination{ - Data: []rbac.Access{ - { - Permission: "inventory:hosts:read", - ResourceDefinitions: []rbac.ResourceDefinition{{ - AttributeFilter: rbac.AttributeFilter{ - Key: "group.id", - Value: []*string{}, - Operation: "unsupported", - }, - }}, - }, - }, - } - groups, err := findInventoryGroups(access) - assert.Error(t, err) - assert.Nil(t, groups) -} - func TestMultiplePermissions(t *testing.T) { handler := "MultiplePermissions" access := rbac.AccessPagination{ @@ -413,75 +269,3 @@ func TestMultiplePermissions(t *testing.T) { assert.True(t, checkPermissions(&access, handler, "GET")) assert.False(t, checkPermissions(&access, handler, "DELETE")) } - -var allowedOperations = `{"data": [ - { - "resourceDefinitions": [], - "permission": "patch:*:read" - }, - { - "resourceDefinitions": [ - { - "attributeFilter": { - "key": "group.id", - "value": "00000000-f688-49d4-a8e2-87394f1ac1b1", - "operation": "equal" - } - } - ], - "permission": "inventory:hosts:read" - }, - { - "resourceDefinitions": [ - { - "attributeFilter": { - "key": "group.id", - "value": [ "00000000-f7a6-45a1-b5a8-410f20052fb1", "00000000-78e0-4cad-bf01-63cf1e4b1dca" ], - "operation": "in" - } - } - ], - "permission": "inventory:hosts:read" - }, - { - "resourceDefinitions": [ - { - "attributeFilter": { - "key": "group.id", - "value": [ "00000000-f688-49d4-a8e2-ee394f1ac1b1" ], - "operation": "in" - } - } - ], - "permission": "inventory:hosts:read" - }, - { - "resourceDefinitions": [ - { - "attributeFilter": { - "key": "group.id", - "value": null, - "operation": "equal" - } - } - ], - "permission": "inventory:hosts:read" - } - ] -} -` - -func TestPermissionsAllowedOperations(t *testing.T) { - handler := "SystemsListHandler" - access := rbac.AccessPagination{} - err := json.Unmarshal([]byte(allowedOperations), &access) - assert.NoError(t, err) - assert.True(t, checkPermissions(&access, handler, "GET")) - groups, err := findInventoryGroups(&access) - assert.NoError(t, err) - assert.Equal(t, "[]", groups["ungrouped"]) - assert.Equal(t, `{"[{\"id\":\"00000000-f688-49d4-a8e2-87394f1ac1b1\"}]",`+ - `"[{\"id\":\"00000000-f7a6-45a1-b5a8-410f20052fb1\"}]",`+ - `"[{\"id\":\"00000000-78e0-4cad-bf01-63cf1e4b1dca\"}]",`+ - `"[{\"id\":\"00000000-f688-49d4-a8e2-ee394f1ac1b1\"}]"}`, groups["grouped"]) -}