Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup os dependent machine config extensions. #266

Merged
merged 2 commits into from
Mar 10, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 17 additions & 1 deletion controllers/openshift_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type KataConfigOpenShiftReconciler struct {

clientset kubernetes.Interface
kataConfig *kataconfigurationv1.KataConfig
Os OperatingSystem
}

const (
Expand Down Expand Up @@ -473,6 +474,21 @@ func (r *KataConfigOpenShiftReconciler) newMCForCR(machinePool string) (*mcfgv1.
return nil, err
}

// RHCOS uses "sandboxed-containers" as thats resolved/translated in the machine-config-operator to "kata-containers"
// FCOS however does not get any translation in the machine-config-operator so we need to
// send in "kata-containers".
// Both are later send to rpm-ostree for installation.
//
// As RHCOS is rather special variant, use "kata-containers" by default.
var extensions = []string{"kata-containers"}

if r.Os.IsEL() {
extensions = []string{"sandboxed-containers"}
}
if r.Os.IsFCOS() {
bpradipt marked this conversation as resolved.
Show resolved Hide resolved
extensions = []string{"kata-containers"}
}

mc := mcfgv1.MachineConfig{
TypeMeta: metav1.TypeMeta{
APIVersion: "machineconfiguration.openshift.io/v1",
Expand All @@ -487,7 +503,7 @@ func (r *KataConfigOpenShiftReconciler) newMCForCR(machinePool string) (*mcfgv1.
Namespace: "openshift-sandboxed-containers-operator",
},
Spec: mcfgv1.MachineConfigSpec{
Extensions: []string{"sandboxed-containers"},
Extensions: extensions,
Config: runtime.RawExtension{
Raw: icb,
},
Expand Down
81 changes: 81 additions & 0 deletions controllers/osrelease.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package controllers

import (
"github.com/ashcrow/osrelease"
"strings"
)

// OS Release Paths
const (
EtcOSReleasePath string = "/etc/os-release"
LibOSReleasePath string = "/usr/lib/os-release"
)

// OS IDs
const (
coreos string = "coreos"
fedora string = "fedora"
rhcos string = "rhcos"
scos string = "scos"
)

// OperatingSystem is a wrapper around a subset of the os-release fields
// and also tracks whether ostree is in use.
type OperatingSystem struct {
// id is the ID field from the os-release
id string
// variantID is the VARIANT_ID field from the os-release
variantID string
// version is the VERSION, RHEL_VERSION, or VERSION_ID field from the os-release
version string
// osrelease is the underlying struct from github.com/ashcrow/osrelease
bpradipt marked this conversation as resolved.
Show resolved Hide resolved
osrelease osrelease.OSRelease
}

func NewOperatingSystem(etcPath, libPath string) (OperatingSystem, error) {
ret := OperatingSystem{}

or, err := osrelease.NewWithOverrides(etcPath, libPath)
if err != nil {
return ret, err
}

ret.id = or.ID
ret.variantID = or.VARIANT_ID
ret.version = getOSVersion(or)
ret.osrelease = or

return ret, nil
}

// IsEL is true if the OS is an Enterprise Linux variant,
// i.e. RHEL CoreOS (RHCOS) or CentOS Stream CoreOS (SCOS)
func (os OperatingSystem) IsEL() bool {
return os.id == rhcos || os.id == scos
}

// IsFCOS is true if the OS is Fedora CoreOS
func (os OperatingSystem) IsFCOS() bool {
return os.id == fedora && os.variantID == coreos
}

// Determines the OS version based upon the contents of the RHEL_VERSION, VERSION or VERSION_ID fields.
func getOSVersion(or osrelease.OSRelease) string {
// If we have the RHEL_VERSION field, we should use that value instead.
if rhelVersion, ok := or.ADDITIONAL_FIELDS["RHEL_VERSION"]; ok {
return rhelVersion
}

// If we have the OPENSHIFT_VERSION field, we can compute the OS version.
if openshiftVersion, ok := or.ADDITIONAL_FIELDS["OPENSHIFT_VERSION"]; ok {
// Move the "." from the middle of the OpenShift version to the end; e.g., 4.12 becomes 412.
openshiftVersion := strings.ReplaceAll(openshiftVersion, ".", "") + "."
if strings.HasPrefix(or.VERSION, openshiftVersion) {
// Strip the OpenShift Version prefix from the VERSION field, if it is found.
return strings.ReplaceAll(or.VERSION, openshiftVersion, "")
}
}

// Fallback to the VERSION_ID field
return or.VERSION_ID
}
117 changes: 117 additions & 0 deletions controllers/osrelease_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package controllers

import (
"fmt"
"os"
"path/filepath"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func TestOSRelease(t *testing.T) {
rhcos90OSReleaseContents := `NAME="Red Hat Enterprise Linux CoreOS"
ID="rhcos"
ID_LIKE="rhel fedora"
VERSION="413.90.202212151724-0"
VERSION_ID="4.13"
VARIANT="CoreOS"
VARIANT_ID=coreos
PLATFORM_ID="platform:el9"
PRETTY_NAME="Red Hat Enterprise Linux CoreOS 413.90.202212151724-0 (Plow)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:redhat:enterprise_linux:9::coreos"
HOME_URL="https://www.redhat.com/"
DOCUMENTATION_URL="https://docs.openshift.com/container-platform/4.13/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="OpenShift Container Platform"
REDHAT_BUGZILLA_PRODUCT_VERSION="4.13"
REDHAT_SUPPORT_PRODUCT="OpenShift Container Platform"
REDHAT_SUPPORT_PRODUCT_VERSION="4.13"
OPENSHIFT_VERSION="4.13"
RHEL_VERSION="9.0"
OSTREE_VERSION="413.90.202212151724-0"`

fcosOSReleaseContents := `NAME="Fedora Linux"
VERSION="37.20230110.3.1 (CoreOS)"
ID=fedora
VERSION_ID=37
VERSION_CODENAME=""
PLATFORM_ID="platform:f37"
PRETTY_NAME="Fedora CoreOS 37.20230110.3.1"
ANSI_COLOR="0;38;2;60;110;180"
LOGO=fedora-logo-icon
CPE_NAME="cpe:/o:fedoraproject:fedora:37"
HOME_URL="https://getfedora.org/coreos/"
DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora-coreos/"
SUPPORT_URL="https://github.com/coreos/fedora-coreos-tracker/"
BUG_REPORT_URL="https://github.com/coreos/fedora-coreos-tracker/"
REDHAT_BUGZILLA_PRODUCT="Fedora"
REDHAT_BUGZILLA_PRODUCT_VERSION=37
REDHAT_SUPPORT_PRODUCT="Fedora"
REDHAT_SUPPORT_PRODUCT_VERSION=37
SUPPORT_END=2023-11-14
VARIANT="CoreOS"
VARIANT_ID=coreos
OSTREE_VERSION='37.20230110.3.1'`

testCases := []struct {
Name string
OSReleaseContents string
IsEL bool
IsFCOS bool
}{
{
Name: "FCOS",
OSReleaseContents: fcosOSReleaseContents,
IsFCOS: true,
},
{
Name: "RHCOS 9.0",
OSReleaseContents: rhcos90OSReleaseContents,
IsEL: true,
IsFCOS: false,
},
}

for _, testCase := range testCases {
testCase := testCase
t.Run(testCase.Name, func(t *testing.T) {
It(fmt.Sprintf("Should handle OS %s", testCase.Name), func() {

os, err := LoadOSRelease(testCase.OSReleaseContents, testCase.OSReleaseContents)
if err != nil {
t.Errorf("Failed to load OS data for test case {%s}: {%s}", testCase.Name, err.Error())
}

Expect(os.IsEL()).Should(Equal(testCase.IsEL))
Expect(os.IsFCOS()).Should(Equal(testCase.IsFCOS))
})
})
}
}

// Generates the OperatingSystem data from strings which contain the desired
// content. Mostly useful for testing purposes.
func LoadOSRelease(etcOSReleaseContent, libOSReleaseContent string) (OperatingSystem, error) {
tempDir, err := os.MkdirTemp("", "")
if err != nil {
return OperatingSystem{}, err
}

defer os.RemoveAll(tempDir)

etcOSReleasePath := filepath.Join(tempDir, "etc-os-release")
libOSReleasePath := filepath.Join(tempDir, "lib-os-release")

if err := os.WriteFile(etcOSReleasePath, []byte(etcOSReleaseContent), 0o644); err != nil {
return OperatingSystem{}, err
}

if err := os.WriteFile(libOSReleasePath, []byte(libOSReleaseContent), 0o644); err != nil {
return OperatingSystem{}, err
}

return NewOperatingSystem(etcOSReleasePath, libOSReleasePath)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/openshift/sandboxed-containers-operator
go 1.18

require (
github.com/ashcrow/osrelease v0.0.0-20180626175927-9b292693c55c
github.com/coreos/ignition/v2 v2.9.0
github.com/go-logr/logr v1.2.3
github.com/onsi/ginkgo/v2 v2.1.6
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/ashcrow/osrelease v0.0.0-20180626175927-9b292693c55c h1:icme0QhxrgZOxTBnT6K8dfGLwbKWSOVwPB95XTbo8Ws=
github.com/ashcrow/osrelease v0.0.0-20180626175927-9b292693c55c/go.mod h1:BRljTyotlu+6N+Qlu5MhjxpdmccCnp9lDvZjNNV8qr4=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.19.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
Expand Down
8 changes: 8 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,18 @@ func main() {

setupLog.Info("added labels")

// Create the OS detection struct
var operatingSystem, err = controllers.NewOperatingSystem(controllers.EtcOSReleasePath, controllers.LibOSReleasePath)
if err != nil {
setupLog.Error(err, "unable to detect operating system")
os.Exit(1)
}

if err = (&controllers.KataConfigOpenShiftReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("KataConfig"),
Scheme: mgr.GetScheme(),
Os: operatingSystem,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create KataConfig controller for OpenShift cluster", "controller", "KataConfig")
os.Exit(1)
Expand Down