Remove duplicate RHEL kernel in os_version (#39746)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- resolved issue where duplicate kernels were reported in the OS versions API for RHEL-family distributions (RHEL, AlmaLinux, CentOS, Rocky, Fedora)
|
||||
@@ -0,0 +1,43 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
MigrationClient.AddMigration(Up_20260211200153, Down_20260211200153)
|
||||
}
|
||||
|
||||
func Up_20260211200153(tx *sql.Tx) error {
|
||||
// Previously, both "kernel" and "kernel-core" were marked as kernels for RPM-based distributions.
|
||||
// However, on RHEL-family systems (RHEL, AlmaLinux, CentOS, Rocky, Fedora), both packages exist:
|
||||
// - "kernel-core"
|
||||
// - "kernel"
|
||||
//
|
||||
// To avoid showing duplicate kernels in the OS versions API, we now only mark "kernel" as the
|
||||
// kernel package for all RPM-based distributions (including Amazon Linux and RHEL-family).
|
||||
// This unmarks "kernel-core" as a kernel.
|
||||
if _, err := tx.Exec(`
|
||||
UPDATE software_titles
|
||||
SET is_kernel = 0
|
||||
WHERE name = 'kernel-core' AND source = 'rpm_packages'
|
||||
`); err != nil {
|
||||
return fmt.Errorf("failed to unmark kernel-core as kernel: %w", err)
|
||||
}
|
||||
|
||||
// Clean up kernel_host_counts entries for kernel-core titles that are no longer kernels
|
||||
if _, err := tx.Exec(`
|
||||
DELETE khc FROM kernel_host_counts khc
|
||||
JOIN software_titles st ON st.id = khc.software_title_id
|
||||
WHERE st.name = 'kernel-core' AND st.source = 'rpm_packages'
|
||||
`); err != nil {
|
||||
return fmt.Errorf("failed to clean up kernel_host_counts for kernel-core: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Down_20260211200153(tx *sql.Tx) error {
|
||||
return nil
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUp_20260211200153(t *testing.T) {
|
||||
db := applyUpToPrev(t)
|
||||
|
||||
// Insert software titles with is_kernel = 1 for various kernel packages
|
||||
|
||||
// kernel-core with rpm_packages source - should be unmarked as kernel
|
||||
kernelCoreTitleID := execNoErrLastID(t, db, `
|
||||
INSERT INTO software_titles (name, source, is_kernel)
|
||||
VALUES ('kernel-core', 'rpm_packages', 1)
|
||||
`)
|
||||
|
||||
// kernel with rpm_packages source - should remain as kernel
|
||||
kernelTitleID := execNoErrLastID(t, db, `
|
||||
INSERT INTO software_titles (name, source, is_kernel)
|
||||
VALUES ('kernel', 'rpm_packages', 1)
|
||||
`)
|
||||
|
||||
// Insert kernel_host_counts for each software title
|
||||
_, err := db.Exec(`
|
||||
INSERT INTO kernel_host_counts (software_title_id, software_id, os_version_id, hosts_count, team_id)
|
||||
VALUES (?, 1, 100, 5, 0)
|
||||
`, kernelCoreTitleID)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = db.Exec(`
|
||||
INSERT INTO kernel_host_counts (software_title_id, software_id, os_version_id, hosts_count, team_id)
|
||||
VALUES (?, 2, 100, 3, 0)
|
||||
`, kernelTitleID)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify initial state
|
||||
var initialCount int
|
||||
err = db.Get(&initialCount, `SELECT COUNT(*) FROM kernel_host_counts`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, initialCount, "Should have 2 kernel_host_counts entries before migration")
|
||||
|
||||
// Apply migration
|
||||
applyNext(t, db)
|
||||
|
||||
// Verify software_titles is_kernel values after migration
|
||||
t.Run("kernel-core with rpm_packages is unmarked", func(t *testing.T) {
|
||||
var isKernel int
|
||||
err := db.Get(&isKernel, `SELECT is_kernel FROM software_titles WHERE id = ?`, kernelCoreTitleID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, isKernel, "kernel-core with rpm_packages should have is_kernel = 0")
|
||||
})
|
||||
|
||||
t.Run("kernel with rpm_packages remains marked", func(t *testing.T) {
|
||||
var isKernel int
|
||||
err := db.Get(&isKernel, `SELECT is_kernel FROM software_titles WHERE id = ?`, kernelTitleID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, isKernel, "kernel with rpm_packages should still have is_kernel = 1")
|
||||
})
|
||||
|
||||
// Verify kernel_host_counts cleanup
|
||||
t.Run("kernel_host_counts for kernel-core rpm_packages deleted", func(t *testing.T) {
|
||||
var count int
|
||||
err := db.Get(&count, `
|
||||
SELECT COUNT(*) FROM kernel_host_counts khc
|
||||
JOIN software_titles st ON st.id = khc.software_title_id
|
||||
WHERE st.name = 'kernel-core' AND st.source = 'rpm_packages'
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 0, count, "kernel_host_counts for kernel-core/rpm_packages should be deleted")
|
||||
})
|
||||
|
||||
t.Run("kernel_host_counts for kernel rpm_packages preserved", func(t *testing.T) {
|
||||
var count int
|
||||
err := db.Get(&count, `
|
||||
SELECT COUNT(*) FROM kernel_host_counts khc
|
||||
JOIN software_titles st ON st.id = khc.software_title_id
|
||||
WHERE st.name = 'kernel' AND st.source = 'rpm_packages'
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, count, "kernel_host_counts for kernel/rpm_packages should be preserved")
|
||||
})
|
||||
|
||||
t.Run("total kernel_host_counts reduced by one", func(t *testing.T) {
|
||||
var finalCount int
|
||||
err := db.Get(&finalCount, `SELECT COUNT(*) FROM kernel_host_counts`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, finalCount, "Should have 1 kernel_host_counts entries after migration (one deleted)")
|
||||
})
|
||||
}
|
||||
@@ -425,7 +425,7 @@ func testListKernelsByOS(t *testing.T, ds *Datastore) {
|
||||
name: "RHEL with team",
|
||||
team: true,
|
||||
host: test.NewHost(t, ds, "host_fedora41", "", "hostkey_fedora41", "hostuuid_fedora41", time.Now(), test.WithPlatform("rhel")),
|
||||
software: []fleet.Software{{Name: "kernel-core", Version: "6.11.4", Arch: "aarch64", Source: "rpm_packages", IsKernel: true}},
|
||||
software: []fleet.Software{{Name: "kernel", Version: "6.11.4", Arch: "aarch64", Source: "rpm_packages", IsKernel: true}},
|
||||
vulns: []fleet.SoftwareVulnerability{{CVE: "CVE-2025-0007"}},
|
||||
vulnsByKernelVersion: map[string][]string{
|
||||
"6.11.4": {"CVE-2025-0007"},
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -65,7 +65,7 @@ func (s *integrationEnterpriseTestSuite) TestLinuxOSVulns() {
|
||||
{
|
||||
name: "RHEL",
|
||||
host: test.NewHost(t, s.ds, "host_fedora41", "", "hostkey_fedora41", "hostuuid_fedora41", time.Now(), test.WithPlatform("rhel")),
|
||||
software: []fleet.Software{{Name: "kernel-core", Version: "6.11.4", Arch: "aarch64", Source: "rpm_packages", IsKernel: true}},
|
||||
software: []fleet.Software{{Name: "kernel", Version: "6.11.4", Arch: "aarch64", Source: "rpm_packages", IsKernel: true}},
|
||||
vulns: []fleet.SoftwareVulnerability{{CVE: "CVE-2025-0007"}},
|
||||
vulnsByKernelVersion: map[string][]string{
|
||||
"6.11.4": {"CVE-2025-0007"},
|
||||
|
||||
@@ -2015,10 +2015,9 @@ func directIngestScheduledQueryStats(ctx context.Context, logger log.Logger, hos
|
||||
}
|
||||
|
||||
const (
|
||||
linuxImageRegex = `^linux-image-[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+-[[:digit:]]+-[[:alnum:]]+`
|
||||
amazonLinuxKernelName = "kernel"
|
||||
rhelKernelName = "kernel-core"
|
||||
archKernelName = `^linux(?:-(?:lts|zen|hardened))?$`
|
||||
linuxImageRegex = `^linux-image-[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+-[[:digit:]]+-[[:alnum:]]+`
|
||||
rpmKernelName = "kernel"
|
||||
archKernelName = `^linux(?:-(?:lts|zen|hardened))?$`
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -2064,7 +2063,7 @@ func directIngestSoftware(ctx context.Context, logger log.Logger, host *fleet.Ho
|
||||
continue
|
||||
}
|
||||
|
||||
if fleet.IsLinux(host.Platform) && (kernelRegex.MatchString(s.Name) || s.Name == amazonLinuxKernelName || s.Name == rhelKernelName || archKernelRegex.MatchString(s.Name)) {
|
||||
if fleet.IsLinux(host.Platform) && (kernelRegex.MatchString(s.Name) || s.Name == rpmKernelName || archKernelRegex.MatchString(s.Name)) {
|
||||
s.IsKernel = true
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user