diff --git a/base/database/testing.go b/base/database/testing.go index cfeefc3a4..a720ccfe4 100644 --- a/base/database/testing.go +++ b/base/database/testing.go @@ -495,6 +495,7 @@ func DeleteSystem(t *testing.T, inventoryID string) { func CreateTemplate(t *testing.T, account int, uuid string, inventoryIDs []string) { template := &models.Template{ RhAccountID: account, UUID: uuid, Name: uuid, EnvironmentID: strings.ReplaceAll(uuid, "-", ""), + Arch: "x86_64", Version: "8", } tx := DB.Begin() diff --git a/base/models/models.go b/base/models/models.go index 88f53d75d..b87949515 100644 --- a/base/models/models.go +++ b/base/models/models.go @@ -46,6 +46,8 @@ type Template struct { UUID string EnvironmentID string Name string + Arch string + Version string // Config pgtype.JSONB // currently unused Description *string Creator *string // pointer for compatibility with previous API versions @@ -92,6 +94,7 @@ type SystemPlatform struct { YumChecksum *string `gorm:"column:yum_checksum"` SatelliteManaged bool `gorm:"column:satellite_managed"` BuiltPkgcache bool `gorm:"column:built_pkgcache"` + Arch *string } func (SystemPlatform) TableName() string { diff --git a/database_admin/migrations/127_template_arch_version.down.sql b/database_admin/migrations/127_template_arch_version.down.sql new file mode 100644 index 000000000..291d5e6cf --- /dev/null +++ b/database_admin/migrations/127_template_arch_version.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE template DROP COLUMN IF EXISTS version; +ALTER TABLE template DROP COLUMN IF EXISTS arch; diff --git a/database_admin/migrations/127_template_arch_version.up.sql b/database_admin/migrations/127_template_arch_version.up.sql new file mode 100644 index 000000000..ab2483632 --- /dev/null +++ b/database_admin/migrations/127_template_arch_version.up.sql @@ -0,0 +1,3 @@ +ALTER TABLE template ADD COLUMN IF NOT EXISTS arch TEXT CHECK (NOT empty(arch)); +ALTER TABLE template ADD COLUMN IF NOT EXISTS version TEXT CHECK (NOT empty(version)); +ALTER TABLE system_platform ADD COLUMN IF NOT EXISTS arch TEXT CHECK (NOT empty(arch)); diff --git a/database_admin/schema/create_schema.sql b/database_admin/schema/create_schema.sql index 5616553ee..a8471b4ad 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 (126, false); +VALUES (127, false); -- --------------------------------------------------------------------------- -- Functions @@ -693,6 +693,8 @@ CREATE TABLE IF NOT EXISTS template published TIMESTAMP WITH TIME ZONE, last_edited TIMESTAMP WITH TIME ZONE, environment_id TEXT NOT NULL CHECK (not empty(environment_id)), + arch TEXT CHECK (not empty(arch)), + version TEXT CHECK (not empty(version)), PRIMARY KEY (rh_account_id, id), UNIQUE(rh_account_id, uuid) ) PARTITION BY HASH (rh_account_id); @@ -742,6 +744,7 @@ CREATE TABLE IF NOT EXISTS system_platform packages_applicable INT NOT NULL DEFAULT 0, template_id BIGINT, yum_checksum TEXT CHECK (NOT empty(yum_checksum)), + arch TEXT CHECK (NOT empty(arch)), PRIMARY KEY (rh_account_id, id), UNIQUE (rh_account_id, inventory_id), CONSTRAINT reporter_id FOREIGN KEY (reporter_id) REFERENCES reporter (id), diff --git a/listener/template_test.go b/listener/template_test.go index d21c2c949..1a91bcf43 100644 --- a/listener/template_test.go +++ b/listener/template_test.go @@ -33,6 +33,8 @@ func createTempateMsg(t *testing.T, eventName, orgID string, nTemplates int) mqu OrgID: orgID, Description: &description, Date: time.Now(), + Arch: "x86_64", + Version: "8", } } diff --git a/listener/templates.go b/listener/templates.go index 6c30f61df..78ddd80d8 100644 --- a/listener/templates.go +++ b/listener/templates.go @@ -93,6 +93,8 @@ func TemplateUpdate(template mqueue.TemplateResponse) error { UUID: template.UUID, EnvironmentID: template.EnvironmentID, Name: template.Name, + Arch: template.Arch, + Version: template.Version, //Config: nil, Description: template.Description, Creator: nil, diff --git a/listener/upload.go b/listener/upload.go index 8c888a05a..875d18430 100644 --- a/listener/upload.go +++ b/listener/upload.go @@ -319,6 +319,7 @@ func updateSystemPlatform(tx *gorm.DB, inventoryID string, accountID int, host * "culled_timestamp", "satellite_managed", "built_pkgcache", + "arch", } now := time.Now() @@ -345,6 +346,7 @@ func updateSystemPlatform(tx *gorm.DB, inventoryID string, accountID int, host * YumChecksum: utils.EmptyToNil(&yumChecksum), SatelliteManaged: host.SystemProfile.SatelliteManaged, BuiltPkgcache: yumUpdates.GetBuiltPkgcache(), + Arch: host.SystemProfile.Arch, } type OldChecksums struct { diff --git a/manager/controllers/template_systems.go b/manager/controllers/template_systems.go index 223527a24..d1b004f96 100644 --- a/manager/controllers/template_systems.go +++ b/manager/controllers/template_systems.go @@ -53,42 +53,41 @@ type TemplateSystemsResponse struct { Meta ListMeta `json:"meta"` } -func getTemplateID(c *gin.Context, tx *gorm.DB, account int, uuid string) (int64, error) { - var id int64 +func getTemplate(c *gin.Context, tx *gorm.DB, account int, uuid string) (*models.Template, error) { + var template models.Template if !utils.IsValidUUID(uuid) { err := errors.Errorf("Invalid template uuid: %s", uuid) LogAndRespNotFound(c, err, err.Error()) - return 0, err + return &template, err } err := tx.Model(&models.Template{}). - Select("id"). Where("rh_account_id = ? AND uuid = ?::uuid ", account, uuid). // use Find() not First() otherwise it returns error "no rows found" if uuid is not present - Find(&id).Error + Find(&template).Error if err != nil { LogAndRespError(c, err, "database error") - return 0, err + return &template, err } - if id == 0 { + if template.ID == 0 { err := errors.New("Template not found") LogAndRespNotFound(c, err, err.Error()) - return 0, err + return &template, err } - return id, nil + return &template, nil } func templateSystemsQuery(c *gin.Context, account int, groups map[string]string) (*gorm.DB, Filters, error) { templateUUID := c.Param("template_id") db := middlewares.DBFromContext(c) - templateID, err := getTemplateID(c, db, account, templateUUID) + template, err := getTemplate(c, db, account, templateUUID) if err != nil { // respose set in getTemplateID() return nil, nil, err } query := database.Systems(db, account, groups). - Where("sp.template_id = ?", templateID). + Where("sp.template_id = ?", template.ID). Select(templateSystemSelect) filters, err := ParseAllFilters(c, TemplateSystemOpts) diff --git a/manager/controllers/template_systems_update.go b/manager/controllers/template_systems_update.go index 02618cdb7..0af223e21 100644 --- a/manager/controllers/template_systems_update.go +++ b/manager/controllers/template_systems_update.go @@ -1,6 +1,7 @@ package controllers import ( + "app/base/database" "app/base/models" "app/base/utils" "app/manager/config" @@ -8,6 +9,8 @@ import ( "fmt" "net/http" + errors2 "errors" + "github.com/pkg/errors" "gorm.io/gorm" @@ -44,13 +47,13 @@ func TemplateSystemsUpdateHandler(c *gin.Context) { } db := middlewares.DBFromContext(c) - templateID, err := getTemplateID(c, db, account, templateUUID) + template, err := getTemplate(c, db, account, templateUUID) if err != nil { // respose set in getTemplateID() return } - err = assignTemplateSystems(c, db, account, &templateID, req.Systems, groups) + err = assignTemplateSystems(c, db, account, template, req.Systems, groups) if err != nil { return } @@ -62,7 +65,7 @@ func TemplateSystemsUpdateHandler(c *gin.Context) { c.Status(http.StatusOK) } -func assignTemplateSystems(c *gin.Context, db *gorm.DB, accountID int, templateID *int64, +func assignTemplateSystems(c *gin.Context, db *gorm.DB, accountID int, template *models.Template, inventoryIDs []string, groups map[string]string) error { if len(inventoryIDs) == 0 { err := errors.New(InvalidInventoryIDsErr) @@ -88,6 +91,18 @@ func assignTemplateSystems(c *gin.Context, db *gorm.DB, accountID int, templateI return err } + if err := templateArchVersionMatch(db, inventoryIDs, template, accountID, groups); err != nil { + msg := fmt.Sprintf("Incompatible template and system version or architecture: %s", err.Error()) + LogAndRespBadRequest(c, err, msg) + return err + } + + // if we want to unassign system from template, we need to set template_id=null + var templateID *int64 + if template != nil && template.ID != 0 { + templateID = &template.ID + } + tx = tx.Model(models.SystemPlatform{}). Where("rh_account_id = ? AND inventory_id IN (?::uuid)", accountID, inventoryIDs). @@ -109,3 +124,28 @@ func assignTemplateSystems(c *gin.Context, db *gorm.DB, accountID int, templateI } return nil } + +func templateArchVersionMatch( + db *gorm.DB, inventoryIDs []string, template *models.Template, acc int, groups map[string]string, +) error { + var sysArchVersions = []struct { + InventoryID string + Arch string + Version string + }{} + var err error + database.Systems(db, acc, groups). + Select("ih.id as inventory_id, ih.system_profile->'operating_system'->>'major' as version, sp.arch as arch"). + Where("id in (?)", inventoryIDs).Find(&sysArchVersions) + + for _, sys := range sysArchVersions { + if sys.Version != template.Version && sys.Arch != template.Arch { + if err == nil { + err = fmt.Errorf("template arch: %s, version: %s", template.Arch, template.Version) + } + systemErr := fmt.Errorf("system uuid: %s, arch: %s, version: %s", sys.InventoryID, sys.Arch, sys.Version) + err = errors2.Join(err, systemErr) + } + } + return err +} diff --git a/platform/platform.go b/platform/platform.go index 97fb61244..4b01bb903 100644 --- a/platform/platform.go +++ b/platform/platform.go @@ -161,7 +161,7 @@ var templates = []mqueue.TemplateResponse{ OrgID: "org_1", Description: &desc1, Arch: "x86_64", - Version: "1", + Version: "8", Date: time.Now(), RepositoryUUIDS: []string{"20000000-0000-0000-0000-000000000001"}, EnvironmentID: "10000000000000000000000000000001", @@ -172,7 +172,7 @@ var templates = []mqueue.TemplateResponse{ OrgID: "org_1", Description: &desc2, Arch: "x86_64", - Version: "1", + Version: "8", Date: time.Now(), RepositoryUUIDS: []string{"20000000-0000-0000-0000-000000000001", "20000000-0000-0000-0000-000000000002"}, EnvironmentID: "10000000000000000000000000000002", @@ -334,8 +334,8 @@ func mockCreatedTemplates(c *gin.Context) { func mockUpdatedTemplates(c *gin.Context) { updated := templates - updated[0].Version = "2" - updated[1].Version = "2" + updated[0].Version = "9" + updated[1].Version = "9" err := sendTemplateMsg("template-updated", templates) if err != nil { c.JSON(http.StatusInternalServerError, err.Error())