Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions cmd/bpfcompat/kernel_sweep.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ func runKernelSweep(args []string) int {
}
derived := base
derived.ID = sweepProfileID(base.ID, release)
derived.KernelFamily = sweepKernelFamily(release, base.KernelFamily)
derived.InstallKernel = release
derived.KernelPackages = debs

Expand Down Expand Up @@ -199,6 +200,30 @@ func sweepProfileID(baseID, release string) string {
return baseID + "-k" + short
}

// sweepKernelFamily keeps generated profile metadata aligned with the kernel
// that will actually boot. This matters when a vendor repository moves to a
// new kernel family before its cloud-image profile is renamed (for example
// Oracle Linux 9 moving from UEK7 5.15 to UEK8 6.12).
func sweepKernelFamily(release, fallback string) string {
parts := strings.SplitN(release, ".", 3)
if len(parts) < 2 || !decimalDigits(parts[0]) || !decimalDigits(parts[1]) {
return fallback
}
return parts[0] + "." + parts[1]
}

func decimalDigits(value string) bool {
if value == "" {
return false
}
for _, r := range value {
if r < '0' || r > '9' {
return false
}
}
return true
}

// defaultCrawlerRef supplies the kernel-crawler mapping for profiles that are
// not yet listed in the baselines file. The crawler groups RHEL rebuilds
// under their own distro key with the same value as the target flavor.
Expand All @@ -216,6 +241,8 @@ func defaultCrawlerRef(distro, kernelFamily string) freshness.CrawlerRef {
return freshness.CrawlerRef{Distro: "rocky", Target: "rocky", ReleasePrefix: prefix}
case "centos-stream":
return freshness.CrawlerRef{Distro: "centos", Target: "centos", ReleasePrefix: prefix}
case "oracle":
return freshness.CrawlerRef{Distro: "oracle", Target: "ol", ReleasePrefix: prefix}
default:
return freshness.CrawlerRef{}
}
Expand Down
39 changes: 39 additions & 0 deletions cmd/bpfcompat/kernel_sweep_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package main

import "testing"

func TestSweepKernelFamily(t *testing.T) {
tests := []struct {
name string
release string
fallback string
want string
}{
{
name: "Oracle UEK8",
release: "6.12.0-204.92.4.4.el9uek.x86_64",
fallback: "5.15",
want: "6.12",
},
{
name: "Ubuntu",
release: "5.15.0-186-generic",
fallback: "5.15",
want: "5.15",
},
{
name: "malformed",
release: "custom-kernel",
fallback: "custom",
want: "custom",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := sweepKernelFamily(tt.release, tt.fallback); got != tt.want {
t.Fatalf("sweepKernelFamily(%q, %q) = %q, want %q", tt.release, tt.fallback, got, tt.want)
}
})
}
}
3 changes: 2 additions & 1 deletion docs/support-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pinned see [image-pipeline.md](image-pipeline.md).

| Category | Count | Meaning |
|---|---|---|
| Runnable (auto-download) | 47 | Supported transport and a public vendor image URL — runs anywhere with KVM, no manual setup. |
| Runnable (auto-download) | 48 | Supported transport and a public vendor image URL — runs anywhere with KVM, no manual setup. |
| Manual image required | 8 | Supported transport, but the image is licensed or has no public URL; the operator imports it (see `make import-required-images`). |
| Generated lane | 0 | No vendor image at all — the kernel is built/booted at run time (virtme-ng upstream, Firecracker). |
| Cataloged, not runnable here | 4 | Present in the catalog but not bootable on the current SSH/cloud-init executor (immutable images, executor limits). Marked non-blocking in matrices. |
Expand Down Expand Up @@ -43,6 +43,7 @@ image is fetched on first use and its sha256 is recorded.
| opensuse-leap-15.6-6.4 | opensuse | 15.6 | 6.4 | x86_64 | ssh | auto-downloads vendor cloud image on first run |
| oracle-linux-10-uek8-6.12 | oracle | 10 | 6.12 | x86_64 | ssh | auto-downloads vendor cloud image on first run |
| oracle-linux-9-uek7-5.15 | oracle | 9 | 5.15 | x86_64 | ssh | auto-downloads vendor cloud image on first run |
| oracle-linux-9-uek7-5.15-k6.12.0-204.92.4.4.el9uek | oracle | 9 | 6.12 | x86_64 | ssh | auto-downloads vendor cloud image on first run |
| rocky-10-6.12 | rocky | 10 | 6.12 | x86_64 | ssh | auto-downloads vendor cloud image on first run |
| rocky-8-4.18 | rocky | 8 | 4.18 | x86_64 | ssh | auto-downloads vendor cloud image on first run |
| rocky-9-5.14 | rocky | 9 | 5.14 | x86_64 | ssh | auto-downloads vendor cloud image on first run |
Expand Down
26 changes: 19 additions & 7 deletions internal/freshness/crawler.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,11 @@ func httpsURL(raw string) string {
}

// RHELKernelRPMs derives the bootable kernel RPM URLs for a RHEL-family
// release from its crawler entry. The crawler publishes only the
// kernel-devel RPM, which lives in AppStream; the packages that actually
// provide a bootable kernel (vmlinuz plus modules) sit in BaseOS under the
// same repository layout, so the AppStream URL pins the mirror, repository
// version and architecture, and swapping the component and package name
// yields the rest. The per-letter subdirectory some mirrors use (Rocky's
// Packages/k/) is preserved because only the file name is replaced.
// release from its crawler entry. Generic RHEL rebuilds publish kernel-devel
// in AppStream and their bootable packages in the corresponding BaseOS path.
// Oracle UEK instead publishes kernel-uek-devel and all bootable
// kernel-uek-* packages together in its UEK repository. Both layouts remain
// pinned to the exact vendor URL and release recorded by kernel-crawler.
func RHELKernelRPMs(entry Entry) ([]string, error) {
release := entry.KernelRelease
for _, headerURL := range entry.Headers {
Expand All @@ -139,6 +137,20 @@ func RHELKernelRPMs(entry Entry) ([]string, error) {
continue
}
base := headerURL[slash+1:]
uekWant := "kernel-uek-devel-" + release + ".rpm"
if base == uekWant {
dir := headerURL[:slash+1]
if !strings.Contains(dir, "/OracleLinux/") || !strings.Contains(dir, "/UEKR") {
return nil, fmt.Errorf("UEK headers URL for %s is not under an OracleLinux UEKR repository: %s", release, headerURL)
}
return []string{
httpsURL(dir + "kernel-uek-core-" + release + ".rpm"),
httpsURL(dir + "kernel-uek-modules-core-" + release + ".rpm"),
httpsURL(dir + "kernel-uek-modules-" + release + ".rpm"),
httpsURL(dir + "kernel-uek-" + release + ".rpm"),
}, nil
}

want := "kernel-devel-" + release + ".rpm"
if base != want {
continue
Expand Down
38 changes: 38 additions & 0 deletions internal/freshness/freshness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,49 @@ func TestRHELKernelRPMs(t *testing.T) {
t.Errorf("rocky url = %s, want %s", got[0], wantFirst)
}

// Oracle UEK keeps its devel, core, and module packages together and uses
// the kernel-uek-* naming family instead of the generic RHEL names.
oracle := Entry{
KernelRelease: "6.12.0-204.92.4.4.el9uek.x86_64",
Target: "ol",
Headers: []string{
"http://yum.oracle.com/repo/OracleLinux/OL9/UEKR8/x86_64/getPackage/kernel-uek-devel-6.12.0-204.92.4.4.el9uek.x86_64.rpm",
},
}
got, err = RHELKernelRPMs(oracle)
if err != nil {
t.Fatalf("RHELKernelRPMs(oracle): %v", err)
}
want = []string{
"https://yum.oracle.com/repo/OracleLinux/OL9/UEKR8/x86_64/getPackage/kernel-uek-core-6.12.0-204.92.4.4.el9uek.x86_64.rpm",
"https://yum.oracle.com/repo/OracleLinux/OL9/UEKR8/x86_64/getPackage/kernel-uek-modules-core-6.12.0-204.92.4.4.el9uek.x86_64.rpm",
"https://yum.oracle.com/repo/OracleLinux/OL9/UEKR8/x86_64/getPackage/kernel-uek-modules-6.12.0-204.92.4.4.el9uek.x86_64.rpm",
"https://yum.oracle.com/repo/OracleLinux/OL9/UEKR8/x86_64/getPackage/kernel-uek-6.12.0-204.92.4.4.el9uek.x86_64.rpm",
}
if len(got) != len(want) {
t.Fatalf("oracle: got %d urls, want %d: %v", len(got), len(want), got)
}
for i := range want {
if got[i] != want[i] {
t.Errorf("oracle url %d = %s, want %s", i, got[i], want[i])
}
}

// A headers URL for a different release must not be used.
if _, err := RHELKernelRPMs(Entry{
KernelRelease: "5.14.0-999.el9_8.x86_64",
Headers: []string{alma.Headers[0]},
}); err == nil {
t.Error("expected an error when no headers URL matches the release")
}

// A lookalike UEK URL outside Oracle's vendor repository is rejected.
if _, err := RHELKernelRPMs(Entry{
KernelRelease: oracle.KernelRelease,
Headers: []string{
"https://example.com/UEKR8/kernel-uek-devel-6.12.0-204.92.4.4.el9uek.x86_64.rpm",
},
}); err == nil {
t.Error("expected an error for a non-Oracle UEK repository")
}
}
11 changes: 6 additions & 5 deletions internal/vm/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,16 @@ const (
)

// KernelInstallFamily maps a profile distro to its kernel install family, or
// "" when kernel-sweep does not support the distro. Deliberately excluded:
// oracle (UEK ships from its own repositories, not BaseOS), immutable or
// image-based systems (rhcos, fedora-coreos, flatcar, bottlerocket, talos),
// and the mainline/CI kernel profiles, which are built rather than packaged.
// "" when kernel-sweep does not support the distro. Oracle UEK uses the RHEL
// install/reboot mechanics with package URLs derived from its UEKR repository.
// Deliberately excluded: immutable or image-based systems (rhcos,
// fedora-coreos, flatcar, bottlerocket, talos), and the mainline/CI kernel
// profiles, which are built rather than packaged.
func KernelInstallFamily(distro string) string {
switch strings.ToLower(strings.TrimSpace(distro)) {
case "ubuntu":
return KernelFamilyDebian
case "almalinux", "rocky", "centos-stream", "rhel":
case "almalinux", "rocky", "centos-stream", "rhel", "oracle":
return KernelFamilyRHEL
default:
return ""
Expand Down
10 changes: 10 additions & 0 deletions internal/vm/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ func TestValidateProfileInstallKernel(t *testing.T) {
t.Fatalf("valid install_kernel rejected: %v", err)
}

oracle := ubuntuVMProfile()
oracle.Distro = "oracle"
oracle.InstallKernel = "6.12.0-204.92.4.4.el9uek.x86_64"
oracle.KernelPackages = []string{
"https://yum.oracle.com/repo/OracleLinux/OL9/UEKR8/x86_64/getPackage/kernel-uek-core-6.12.0-204.92.4.4.el9uek.x86_64.rpm",
}
if err := ValidateProfile(oracle); err != nil {
t.Fatalf("valid Oracle UEK install_kernel rejected: %v", err)
}

nonUbuntu := ubuntuVMProfile()
nonUbuntu.Distro = "debian"
nonUbuntu.InstallKernel = "6.1.0-30-cloud-amd64"
Expand Down
6 changes: 6 additions & 0 deletions matrices/kernel-sweep-oracle-linux-9-uek7-5.15.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Generated by `bpfcompat kernel-sweep --profile oracle-linux-9-uek7-5.15`; regenerate rather than edit.
name: kernel-sweep-oracle-linux-9-uek7-5.15
profiles:
- id: oracle-linux-9-uek7-5.15-k6.12.0-204.92.4.4.el9uek
required: true
timeout: 20m
8 changes: 8 additions & 0 deletions matrices/pre-campaign-oracle-uek-refresh.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: pre-campaign-oracle-uek-refresh
profiles:
- id: ubuntu-24.04-6.8
required: true
timeout: 20m
- id: oracle-linux-9-uek7-5.15-k6.12.0-204.92.4.4.el9uek
required: true
timeout: 20m
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by `bpfcompat kernel-sweep --profile oracle-linux-9-uek7-5.15`; regenerate rather than edit.
id: oracle-linux-9-uek7-5.15-k6.12.0-204.92.4.4.el9uek
distro: oracle
version: "9"
kernel_family: "6.12"
arch: x86_64
install_kernel: 6.12.0-204.92.4.4.el9uek.x86_64
kernel_packages:
- https://yum.oracle.com/repo/OracleLinux/OL9/UEKR8/x86_64/getPackage/kernel-uek-core-6.12.0-204.92.4.4.el9uek.x86_64.rpm
- https://yum.oracle.com/repo/OracleLinux/OL9/UEKR8/x86_64/getPackage/kernel-uek-modules-core-6.12.0-204.92.4.4.el9uek.x86_64.rpm
- https://yum.oracle.com/repo/OracleLinux/OL9/UEKR8/x86_64/getPackage/kernel-uek-modules-6.12.0-204.92.4.4.el9uek.x86_64.rpm
- https://yum.oracle.com/repo/OracleLinux/OL9/UEKR8/x86_64/getPackage/kernel-uek-6.12.0-204.92.4.4.el9uek.x86_64.rpm
image:
source_url: https://yum.oracle.com/templates/OracleLinux/OL9/u7/x86_64/OL9U7_x86_64-kvm-b269.qcow2
local_path: vm/cache/oracle-linux-9-uek7-5.15.qcow2
boot:
memory_mb: 1024
cpus: 1
validator:
path: /usr/local/bin/bpfcompat-validator
capabilities:
expected_btf: true
Loading