diff --git a/packages/orchestrator/README.md b/packages/orchestrator/README.md index 5a56bbb571..3dd0ed6474 100644 --- a/packages/orchestrator/README.md +++ b/packages/orchestrator/README.md @@ -148,5 +148,5 @@ Automatically set in local mode. Set before running to override: ## Limitations -- Custom template builds support base images from the declared distro families: **Debian/Ubuntu** (apt), the **RHEL family** — Fedora, CentOS Stream, Rocky, Alma — (dnf/microdnf/yum), **TencentOS Server** 3+ and **OpenCloudOS** 8+ (dnf/microdnf/yum; TencentOS Server 2.x repos lack socat, fuse3, nftables and jq, so its builds fail at package install), **Arch** (pacman), and **Alpine** (apk, OpenRC). The distro is resolved from the image's `/etc/os-release` `ID` — never by probing for package managers. Images without an os-release identity (distroless, scratch) or from an undeclared family are rejected fast, with a build-log error naming the reason and the supported families (see `pkg/template/build/phases/base/distro`). Minimal/restricted-repo images may fail provisioning if their repos don't carry the required packages; the failure is surfaced in the build log. +- Custom template builds support base images from the declared distro families: **Debian/Ubuntu** (apt), the **RHEL family** — Fedora, CentOS Stream, Rocky, Alma — (dnf/microdnf/yum), **TencentOS Server** 3+ and **OpenCloudOS** 8+ (dnf/microdnf/yum; TencentOS Server 2.x repos lack socat, fuse3, nftables and jq, so its builds fail at package install), **Kylin** Linux Advanced Server V10 (dnf/microdnf/yum; its repos ship `shadow` rather than `shadow-utils`), **Arch** (pacman), and **Alpine** (apk, OpenRC). The distro is resolved from the image's `/etc/os-release` `ID` — never by probing for package managers. Images without an os-release identity (distroless, scratch) or from an undeclared family are rejected fast, with a build-log error naming the reason and the supported families (see `pkg/template/build/phases/base/distro`). Minimal/restricted-repo images may fail provisioning if their repos don't carry the required packages; the failure is surfaced in the build log. - Sandboxes always boot the kernel E2B supplies, never one from the base image: `/lib/modules` is empty, no kernel modules can be loaded, and SELinux is disabled on the kernel command line (`selinux=0`), so a base image that ships an SELinux policy boots without it. `kernel-devel` from a distro's repos resolves against a kernel that isn't running. **RHEL** (`rhel`, incl. UBI), **Oracle Linux** (`ol`) and **Amazon Linux** (`amzn`) are therefore not accepted even though they are RPM/dnf images — they are chosen for kABI, signed kernel modules and UEK, which this cannot honour. The community rebuilds above are supported because they are chosen for the userland. diff --git a/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go b/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go index 8eb3e1ec19..1a5a0fb8ca 100644 --- a/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go +++ b/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go @@ -124,6 +124,31 @@ var Profiles = []Profile{ CABundle: "/etc/ssl/certs/ca-certificates.crt", CARefresh: `update-ca-trust extract && ln -sf /etc/pki/tls/certs/ca-bundle.crt "$E2B_CA_BUNDLE"`, }, + { + // Kylin Linux Advanced Server (银河麒麟高级服务器) V10 is an RPM/dnf, + // systemd distro widely used across domestic infrastructure. Its + // /etc/os-release declares ID=kylin and no ID_LIKE, so without this + // profile a Kylin base falls through to the unsupported-distro error. + // Verified against macrosan/kylin:v10-sp3-2403: RHEL-shaped, with the + // same one difference as openEuler — it ships shadow, not shadow-utils. + Key: "kylin", + Init: InitSystemd, + IDs: []string{"kylin"}, + Packages: []string{ + "systemd", "shadow", "passwd", "openssh-server", "sudo", "chrony", + "socat", "curl", "ca-certificates", "fuse3", "iptables", "git", + "nfs-utils", "less", "nftables", "iputils", "jq", "bash", "tar", + "iproute", "findutils", + }, + PkgQueryBody: `rpm -q "$1" >/dev/null 2>&1`, + PkgInstall: `dnf -y install --allowerasing "$@" || microdnf -y install "$@" || yum -y install "$@"`, + InitBinary: "/usr/lib/systemd/systemd", + TimeSyncUnit: "chronyd", + SSHUnit: "sshd", + AdminGroup: "wheel", + CABundle: "/etc/ssl/certs/ca-certificates.crt", + CARefresh: `update-ca-trust extract && ln -sf /etc/pki/tls/certs/ca-bundle.crt "$E2B_CA_BUNDLE"`, + }, { Key: "arch", Init: InitSystemd, diff --git a/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go b/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go index e2b6a7da97..e18e3dfe48 100644 --- a/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go +++ b/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go @@ -1,6 +1,7 @@ package distro import ( + "slices" "crypto/sha256" "encoding/hex" "strings" @@ -339,3 +340,33 @@ func TestRejectedIDsAreNotReachableViaIDLike(t *testing.T) { t.Error("the rejected-id guard must precede the ID_LIKE fallback") } } + +// Kylin Advanced Server V10 declares ID=kylin and no ID_LIKE, so it needs its +// own profile. Verified against macrosan/kylin:v10-sp3-2403: RHEL-shaped, with +// the same package-name difference as openEuler — its repos ship shadow, not +// shadow-utils. These assertions pin both facts. +func TestKylinProfile(t *testing.T) { + t.Parallel() + k := profileByKey(t, "kylin") + + if !slices.Contains(k.IDs, "kylin") { + t.Errorf("kylin id must be matched, got IDs=%v", k.IDs) + } + if slices.Contains(k.Packages, "shadow-utils") { + t.Error("Kylin repos have no shadow-utils; it must install shadow") + } + if !slices.Contains(k.Packages, "shadow") { + t.Error("kylin must install shadow for the shadow-tools capability") + } + if k.InitBinary != "/usr/lib/systemd/systemd" { + t.Errorf("kylin init path wrong: %s", k.InitBinary) + } + for _, want := range []string{"dnf -y install --allowerasing", "|| microdnf -y install", "|| yum -y install"} { + if !strings.Contains(k.PkgInstall, want) { + t.Errorf("kylin install missing %q: %s", want, k.PkgInstall) + } + } + if !strings.Contains(ShellSelector(), "kylin)") { + t.Error("selector must have a kylin arm") + } +} diff --git a/packages/orchestrator/pkg/template/build/phases/base/distro/parity_test.go b/packages/orchestrator/pkg/template/build/phases/base/distro/parity_test.go index 42238e65ae..688216253d 100644 --- a/packages/orchestrator/pkg/template/build/phases/base/distro/parity_test.go +++ b/packages/orchestrator/pkg/template/build/phases/base/distro/parity_test.go @@ -22,6 +22,7 @@ var capabilities = []struct { "debian": {"systemd", "systemd-sysv"}, "rhel": {"systemd"}, "tencentos": {"systemd"}, + "kylin": {"systemd"}, "arch": {"systemd"}, "alpine": {"openrc"}, }}, @@ -30,6 +31,7 @@ var capabilities = []struct { "debian": {}, "rhel": {"bash"}, "tencentos": {"bash"}, + "kylin": {"bash"}, "arch": {"bash"}, "alpine": {"bash"}, }}, @@ -38,6 +40,7 @@ var capabilities = []struct { "debian": {}, "rhel": {"shadow-utils", "passwd"}, "tencentos": {"shadow-utils", "passwd"}, + "kylin": {"shadow", "passwd"}, "arch": {"shadow"}, "alpine": {"shadow"}, }}, @@ -45,6 +48,7 @@ var capabilities = []struct { "debian": {"openssh-server"}, "rhel": {"openssh-server"}, "tencentos": {"openssh-server"}, + "kylin": {"openssh-server"}, "arch": {"openssh"}, "alpine": {"openssh"}, }}, @@ -52,6 +56,7 @@ var capabilities = []struct { "debian": {"sudo"}, "rhel": {"sudo"}, "tencentos": {"sudo"}, + "kylin": {"sudo"}, "arch": {"sudo"}, "alpine": {"sudo"}, }}, @@ -59,6 +64,7 @@ var capabilities = []struct { "debian": {"chrony"}, "rhel": {"chrony"}, "tencentos": {"chrony"}, + "kylin": {"chrony"}, "arch": {"chrony"}, "alpine": {"chrony"}, }}, @@ -66,6 +72,7 @@ var capabilities = []struct { "debian": {"ca-certificates"}, "rhel": {"ca-certificates"}, "tencentos": {"ca-certificates"}, + "kylin": {"ca-certificates"}, "arch": {"ca-certificates"}, "alpine": {"ca-certificates"}, }}, @@ -73,6 +80,7 @@ var capabilities = []struct { "debian": {"fuse3"}, "rhel": {"fuse3"}, "tencentos": {"fuse3"}, + "kylin": {"fuse3"}, "arch": {"fuse3"}, "alpine": {"fuse3"}, }}, @@ -80,6 +88,7 @@ var capabilities = []struct { "debian": {"nfs-common"}, "rhel": {"nfs-utils"}, "tencentos": {"nfs-utils"}, + "kylin": {"nfs-utils"}, "arch": {"nfs-utils"}, "alpine": {"nfs-utils"}, }}, @@ -87,6 +96,7 @@ var capabilities = []struct { "debian": {"iptables", "nftables"}, "rhel": {"iptables", "nftables"}, "tencentos": {"iptables", "nftables"}, + "kylin": {"iptables", "nftables"}, "arch": {"iptables", "nftables"}, "alpine": {"iptables", "nftables"}, }}, @@ -94,6 +104,7 @@ var capabilities = []struct { "debian": {"git"}, "rhel": {"git"}, "tencentos": {"git"}, + "kylin": {"git"}, "arch": {"git"}, "alpine": {"git"}, }}, @@ -101,6 +112,7 @@ var capabilities = []struct { "debian": {"jq"}, "rhel": {"jq"}, "tencentos": {"jq"}, + "kylin": {"jq"}, "arch": {"jq"}, "alpine": {"jq"}, }}, @@ -108,6 +120,7 @@ var capabilities = []struct { "debian": {"socat"}, "rhel": {"socat"}, "tencentos": {"socat"}, + "kylin": {"socat"}, "arch": {"socat"}, "alpine": {"socat"}, }}, @@ -115,6 +128,7 @@ var capabilities = []struct { "debian": {"curl"}, "rhel": {"curl"}, "tencentos": {"curl"}, + "kylin": {"curl"}, "arch": {"curl"}, "alpine": {"curl"}, }}, @@ -122,6 +136,7 @@ var capabilities = []struct { "debian": {"less"}, "rhel": {"less"}, "tencentos": {"less"}, + "kylin": {"less"}, "arch": {"less"}, "alpine": {"less"}, }}, @@ -129,6 +144,7 @@ var capabilities = []struct { "debian": {"iputils-ping"}, "rhel": {"iputils"}, "tencentos": {"iputils"}, + "kylin": {"iputils"}, "arch": {"iputils"}, "alpine": {"iputils"}, }}, @@ -138,6 +154,7 @@ var capabilities = []struct { "debian": {"iproute2"}, "rhel": {"iproute"}, "tencentos": {"iproute"}, + "kylin": {"iproute"}, "arch": {}, "alpine": {"iproute2"}, }}, @@ -151,6 +168,7 @@ var capabilities = []struct { "debian": {}, "rhel": {"findutils"}, "tencentos": {"findutils"}, + "kylin": {"findutils"}, "arch": {}, "alpine": {}, }}, @@ -160,6 +178,7 @@ var capabilities = []struct { "debian": {}, "rhel": {"tar"}, "tencentos": {"tar"}, + "kylin": {"tar"}, "arch": {}, "alpine": {}, }}, @@ -169,6 +188,7 @@ var capabilities = []struct { "debian": {}, "rhel": {}, "tencentos": {}, + "kylin": {}, "arch": {}, "alpine": {"util-linux-misc"}, }},