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

Enhance Cluster Environment Detection for OSTree-Based and Classic RHEL Systems #215

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
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
112 changes: 102 additions & 10 deletions internal/utils/cluster_environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package utils
import (
"context"
"fmt"
"os"

v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
Expand All @@ -22,32 +23,82 @@ func NewClusterEnvironment(client client.Client) *ClusterEnvironment {
}

type Flavour string
type FlavourSet map[Flavour]struct{}

// Add a flavour to the set
func (f FlavourSet) Add(flavour Flavour) {
f[flavour] = struct{}{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

struct{}{} needs more thought

}

// Remove a flavour from the set
func (f FlavourSet) Remove(flavour Flavour) {
delete(f, flavour)
}

// Check if a flavour exists in the set
func (f FlavourSet) Contains(flavour Flavour) bool {
_, exists := f[flavour]
return exists
}

// Convert the set to a slice
func (f FlavourSet) ToSlice() []Flavour {
keys := make([]Flavour, 0, len(f))
for key := range f {
keys = append(keys, key)
}
return keys
}

const (
OpenShiftFlavour Flavour = "OpenShift"
MicroShiftFlavour Flavour = "MicroShift"
UnknownFlavour Flavour = "Unknown"
OpenShiftFlavour Flavour = "OpenShift"
MicroShiftFlavour Flavour = "MicroShift"
OstreeFlavour Flavour = "Ostree"
ClassicRhelFlavour Flavour = "ClassicRHEL"
UnknownFlavour Flavour = "Unknown"
)

func (ce *ClusterEnvironment) Flavour(ctx context.Context) (Flavour, error) {
func (ce *ClusterEnvironment) Flavours(ctx context.Context) (FlavourSet, error) {
detectedFlavours := FlavourSet{}

microShift, err := ce.isMicroShift(ctx)
if err != nil {
return UnknownFlavour, err
return nil, err
}
if microShift {
return MicroShiftFlavour, nil
detectedFlavours.Add(MicroShiftFlavour)
}

openShift, err := ce.isOpenShift(ctx)
if err != nil {
return UnknownFlavour, err
return nil, err
}
if openShift {
return OpenShiftFlavour, nil
detectedFlavours.Add(OpenShiftFlavour)
}

ostree, err := ce.isOSTree()
if err != nil {
return nil, err
}
if ostree {
detectedFlavours.Add(OstreeFlavour)
}

classic, err := ce.isClassicRHEL(ctx)
if err != nil {
return nil, err
}
if classic {
detectedFlavours.Add(ClassicRhelFlavour)
}
return UnknownFlavour, nil
}

if len(detectedFlavours) == 0 {
detectedFlavours.Add(UnknownFlavour)
}

return detectedFlavours, nil
}
func (ce *ClusterEnvironment) isMicroShift(ctx context.Context) (bool, error) {
cm := v1.ConfigMap{}
cm.SetName("microshift-version")
Expand All @@ -74,3 +125,44 @@ func (ce *ClusterEnvironment) isOpenShift(ctx context.Context) (bool, error) {
}
return true, nil
}

// isClassicRHEL checks if the OS running on the current node is classic RHEL.
func (ce *ClusterEnvironment) isClassicRHEL(ctx context.Context) (bool, error) {
// Retrieve the node name from the K8S_NODE environment variable
nodeName := os.Getenv("K8S_NODE")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

downward API always available?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes downward API is always available in kubernetes.

if nodeName == "" {
return false, fmt.Errorf("K8S_NODE environment variable is not set")
}

// Fetch the Node object using the Kubernetes client
node := &v1.Node{}
if err := ce.client.Get(ctx, types.NamespacedName{Name: nodeName}, node); err != nil {
return false, fmt.Errorf("failed to retrieve node information: %v", err)
}

// Extract OSImage from the node's status
osImage := node.Status.NodeInfo.OSImage
fmt.Printf("Node OSImage: %s\n", osImage)

isOstree, err := ce.isOSTree()
if err != nil {
return false, err
}
// Determine if the OS is classic RHEL
if osImage == "Red Hat Enterprise Linux" || (len(osImage) >= 3 && osImage[:3] == "RHEL") && !isOstree {
return true, nil // Classic RHEL detected
}

return false, nil // Not classic RHEL (could be OSTree-based or something else)
}

// isOSTree checks for the presence of the /run/ostree-booted file to determine if running on OSTree
func (ce *ClusterEnvironment) isOSTree() (bool, error) {
if _, err := os.Stat("/run/ostree-booted"); err != nil {
if os.IsNotExist(err) {
return false, nil // Not an OSTree-based OS
}
return false, fmt.Errorf("Failed to check OSTree status: %v", err)
}
return true, nil // OSTree-based OS detected
}