Skip to content
Open
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
3 changes: 0 additions & 3 deletions apis/v1alpha1/config_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@ type ConfigSpec struct {

// DaemonSpec defines the desired state of the bpfman daemon.
type DaemonSpec struct {
// BpffsInitImage holds the image for the init container that mounts bpffs.
// +optional
BpffsInitImage string `json:"bpffsInitImage,omitempty"`
// CsiRegistrarImage holds the image for the CSI node driver registrar
// sidecar container.
// +optional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,7 @@ metadata:
capabilities: Basic Install
categories: OpenShift Optional
containerImage: quay.io/bpfman/bpfman-operator:latest
createdAt: "2025-12-23T10:13:33Z"
createdAt: "2026-01-12T09:20:12Z"
description: The bpfman Operator is designed to manage eBPF programs for applications.
features.operators.openshift.io/cnf: "false"
features.operators.openshift.io/cni: "false"
Expand Down
4 changes: 0 additions & 4 deletions bundle/manifests/bpfman.io_configs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ spec:
daemon:
description: Daemon holds the configuration for the bpfman daemon.
properties:
bpffsInitImage:
description: BpffsInitImage holds the image for the init container
that mounts bpffs.
type: string
csiRegistrarImage:
description: |-
CsiRegistrarImage holds the image for the CSI node driver registrar
Expand Down
73 changes: 73 additions & 0 deletions cmd/bpfman-agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ import (
"net"
"net/http"
"os"
"strings"
"sync"
"time"

bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1"
bpfmanagent "github.com/bpfman/bpfman-operator/controllers/bpfman-agent"
"github.com/bpfman/bpfman-operator/internal/bpffs"
"github.com/bpfman/bpfman-operator/internal/conn"
gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1"

Expand Down Expand Up @@ -69,6 +71,59 @@ func init() {
//+kubebuilder:scaffold:scheme
}

// handleMountBPFFS performs the --mount-bpffs logic. It returns
// (exitCode, message, err).
func handleMountBPFFS(mountInfoPath, mountPoint string, remount bool) (int, string, error) {
if !remount {
mounted, err := bpffs.IsMounted(mountInfoPath, mountPoint)
if err != nil {
return 1, "", fmt.Errorf("failed to check mount status at %s: %w", mountPoint, err)
}
if mounted {
return 0, fmt.Sprintf("bpffs already mounted at %s\n", mountPoint), nil
}

if err := bpffs.Mount(mountPoint); err != nil {
// Handle race: another process may have mounted
// between our check and mount attempt.
mounted, mErr := bpffs.IsMounted(mountInfoPath, mountPoint)
if mErr == nil && mounted {
return 0, fmt.Sprintf("bpffs already mounted at %s\n", mountPoint), nil
}
return 1, "", fmt.Errorf("failed to mount bpffs at %s: %w", mountPoint, err)
}
return 0, fmt.Sprintf("bpffs mounted at %s\n", mountPoint), nil
}

// Remount mode: exercise Mount() code path.
mounted, err := bpffs.IsMounted(mountInfoPath, mountPoint)
if err != nil {
return 1, "", fmt.Errorf("failed to check mount status at %s: %w", mountPoint, err)
}

var msg strings.Builder
if mounted {
if err := bpffs.Unmount(mountPoint); err != nil {
return 1, "", fmt.Errorf("failed to unmount bpffs at %s: %w", mountPoint, err)
}
fmt.Fprintf(&msg, "bpffs unmounted at %s\n", mountPoint)
}

if err := bpffs.Mount(mountPoint); err != nil {
// Handle race: another process may have mounted
// between our check and mount attempt.
mounted, mErr := bpffs.IsMounted(mountInfoPath, mountPoint)
if mErr == nil && mounted {
fmt.Fprintf(&msg, "bpffs already mounted at %s\n", mountPoint)
return 0, msg.String(), nil
}
return 1, "", fmt.Errorf("failed to mount bpffs at %s: %w", mountPoint, err)
}

fmt.Fprintf(&msg, "bpffs mounted at %s\n", mountPoint)
return 0, msg.String(), nil
}

// agentMetricsServer provides an HTTP server for exposing Prometheus
// metrics on a Unix domain socket.
//
Expand Down Expand Up @@ -317,9 +372,27 @@ func main() {
flag.StringVar(&pprofAddr, "profiling-bind-address", "", "The address the profiling endpoint binds to, such as ':6060'. Leave unset to disable profiling.")
flag.BoolVar(&enableInterfacesDiscovery, "enable-interfaces-discovery", true, "Enable ebpfman agent process to auto detect interfaces creation and deletion")
flag.StringVar(&certDir, "cert-dir", "/tmp/k8s-webhook-server/serving-certs", "The directory containing TLS certificates for HTTPS servers.")
mountBPFFS := flag.Bool("mount-bpffs", false, "Ensure bpffs is mounted at the given path, then exit (init-container mode).")
mountBPFFSPath := flag.String("mount-bpffs-path", bpffs.DefaultMountPoint, "Path where bpffs should be mounted.")
remountBPFFS := flag.Bool("mount-bpffs-remount", false, "Unmount bpffs if mounted, then mount it (testing only).")

flag.Parse()

// Handle --mount-bpffs mode: mount bpffs if needed and exit.
// This is used by init containers to ensure bpffs is mounted
// before the main containers start, without requiring
// external utilities.
if *mountBPFFS {
code, out, err := handleMountBPFFS(bpffs.DefaultMountInfoPath, *mountBPFFSPath, *remountBPFFS)
if out != "" {
fmt.Print(out)
}
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
}
os.Exit(code)
}

// Get the Log level for bpfman deployment where this pod is running.
logLevel := os.Getenv("GO_LOG")
switch logLevel {
Expand Down
11 changes: 2 additions & 9 deletions config/bpfman-deployment/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,8 @@ spec:
## in kind so mount it if needed.
initContainers:
- name: mount-bpffs
image: quay.io/fedora/fedora-minimal:39
command:
- /bin/sh
- -xc
- |
#!/bin/sh
if ! /usr/bin/findmnt --noheadings --types bpf /sys/fs/bpf >/dev/null 2>&1; then
/bin/mount bpffs /sys/fs/bpf -t bpf
fi
image: quay.io/bpfman/bpfman-agent:latest
command: ["/bpfman-agent", "--mount-bpffs"]
securityContext:
privileged: true
runAsUser: 0
Expand Down
4 changes: 0 additions & 4 deletions config/crd/bases/bpfman.io_configs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,6 @@ spec:
daemon:
description: Daemon holds the configuration for the bpfman daemon.
properties:
bpffsInitImage:
description: BpffsInitImage holds the image for the init container
that mounts bpffs.
type: string
csiRegistrarImage:
description: |-
CsiRegistrarImage holds the image for the CSI node driver registrar
Expand Down
15 changes: 8 additions & 7 deletions controllers/bpfman-operator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,14 @@ func configureBpfmanDs(staticBpfmanDS *appsv1.DaemonSet, config *v1alpha1.Config
staticBpfmanDS.Name = internal.BpfmanDsName
staticBpfmanDS.Namespace = config.Spec.Namespace
staticBpfmanDS.Spec.Template.Spec.AutomountServiceAccountToken = ptr.To(true)

// Update init container images
for cindex, container := range staticBpfmanDS.Spec.Template.Spec.InitContainers {
if container.Name == internal.BpfmanInitContainerName {
staticBpfmanDS.Spec.Template.Spec.InitContainers[cindex].Image = config.Spec.Agent.Image
}
}

for cindex, container := range staticBpfmanDS.Spec.Template.Spec.Containers {
switch container.Name {
case internal.BpfmanContainerName:
Expand All @@ -288,13 +296,6 @@ func configureBpfmanDs(staticBpfmanDS *appsv1.DaemonSet, config *v1alpha1.Config
// Do nothing
}
}

// Configure init containers
for cindex, container := range staticBpfmanDS.Spec.Template.Spec.InitContainers {
if container.Name == internal.BpfmanInitContainerName && config.Spec.Daemon.BpffsInitImage != "" {
staticBpfmanDS.Spec.Template.Spec.InitContainers[cindex].Image = config.Spec.Daemon.BpffsInitImage
}
}
}

// configureMetricsProxyDs configures the metrics-proxy DaemonSet with runtime values.
Expand Down
42 changes: 5 additions & 37 deletions controllers/bpfman-operator/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,45 +581,25 @@ func verifyCM(cm *corev1.ConfigMap, requiredFields map[string]*string) error {
return nil
}

// TestConfigureBpfmanDsImageOverrides verifies that optional image
// fields in the Config CR correctly override container images in the
// DaemonSet.
func TestConfigureBpfmanDsImageOverrides(t *testing.T) {
// TestConfigureBpfmanDsCsiRegistrarImageOverride verifies that the optional
// CsiRegistrarImage field in the Config CR correctly overrides the CSI node
// driver registrar container image in the DaemonSet.
func TestConfigureBpfmanDsCsiRegistrarImageOverride(t *testing.T) {
tests := []struct {
name string
bpffsInitImage string
csiRegistrarImage string
expectedInitImage string
expectedCsiRegistrarImage string
}{
{
name: "defaults preserved when fields empty",
bpffsInitImage: "",
name: "default preserved when field empty",
csiRegistrarImage: "",
expectedInitImage: "quay.io/fedora/fedora-minimal:39",
expectedCsiRegistrarImage: "quay.io/bpfman/csi-node-driver-registrar:v2.13.0",
},
{
name: "init image overridden when set",
bpffsInitImage: "registry.example.com/custom-init:v1",
csiRegistrarImage: "",
expectedInitImage: "registry.example.com/custom-init:v1",
expectedCsiRegistrarImage: "quay.io/bpfman/csi-node-driver-registrar:v2.13.0",
},
{
name: "csi registrar image overridden when set",
bpffsInitImage: "",
csiRegistrarImage: "registry.example.com/custom-csi:v1",
expectedInitImage: "quay.io/fedora/fedora-minimal:39",
expectedCsiRegistrarImage: "registry.example.com/custom-csi:v1",
},
{
name: "both images overridden when set",
bpffsInitImage: "registry.example.com/custom-init:v2",
csiRegistrarImage: "registry.example.com/custom-csi:v2",
expectedInitImage: "registry.example.com/custom-init:v2",
expectedCsiRegistrarImage: "registry.example.com/custom-csi:v2",
},
}

for _, tc := range tests {
Expand All @@ -645,7 +625,6 @@ func TestConfigureBpfmanDsImageOverrides(t *testing.T) {
Daemon: v1alpha1.DaemonSpec{
Image: "quay.io/bpfman/bpfman:latest",
LogLevel: "info",
BpffsInitImage: tc.bpffsInitImage,
CsiRegistrarImage: tc.csiRegistrarImage,
},
},
Expand All @@ -654,17 +633,6 @@ func TestConfigureBpfmanDsImageOverrides(t *testing.T) {
// Apply configuration to the DaemonSet.
configureBpfmanDs(ds, config)

// Verify init container image.
var initImage string
for _, c := range ds.Spec.Template.Spec.InitContainers {
if c.Name == internal.BpfmanInitContainerName {
initImage = c.Image
break
}
}
require.Equal(t, tc.expectedInitImage, initImage,
"init container image mismatch")

// Verify CSI registrar container image.
var csiImage string
for _, c := range ds.Spec.Template.Spec.Containers {
Expand Down
Loading
Loading