From f7762925d8ce525609a1942cf00cca0b76907808 Mon Sep 17 00:00:00 2001 From: Billy McFall <22157057+Billy99@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:22:51 -0500 Subject: [PATCH] Add core code to support namespace based CRDs Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com> --- apis/v1alpha1/bpfprogram_ns_types.go | 79 ++++ apis/v1alpha1/bpfprogram_types.go | 30 ++ apis/v1alpha1/xdpProgram_ns_types.go | 52 +++ apis/v1alpha1/zz_generated.deepcopy.go | 118 ++++++ apis/v1alpha1/zz_generated.register.go | 4 + ...c.authorization.k8s.io_v1_clusterrole.yaml | 34 ++ ...ole_rbac.authorization.k8s.io_v1_role.yaml | 20 + ...ole_rbac.authorization.k8s.io_v1_role.yaml | 31 ++ ...ole_rbac.authorization.k8s.io_v1_role.yaml | 27 ++ ...ole_rbac.authorization.k8s.io_v1_role.yaml | 32 ++ ...bpfman-operator.clusterserviceversion.yaml | 80 +++- bundle/manifests/bpfman.io_bpfnsprograms.yaml | 150 ++++++++ bundle/manifests/bpfman.io_xdpnsprograms.yaml | 362 ++++++++++++++++++ cmd/bpfman-agent/main.go | 43 ++- cmd/bpfman-operator/main.go | 44 ++- config/crd/bases/bpfman.io_bpfnsprograms.yaml | 144 +++++++ config/crd/bases/bpfman.io_xdpnsprograms.yaml | 356 +++++++++++++++++ config/crd/kustomization.yaml | 3 + .../patches/cainjection_in_bpfnsprograms.yaml | 7 + .../patches/cainjection_in_xdpnsprograms.yaml | 7 + .../crd/patches/webhook_in_bpfnsprograms.yaml | 16 + .../crd/patches/webhook_in_xdpnsprograms.yaml | 16 + ...bpfman-operator.clusterserviceversion.yaml | 10 + config/rbac/bpfman-agent/role.yaml | 55 +++ config/rbac/bpfman-operator/role.yaml | 75 ++++ config/rbac/bpfnsprogram_editor_role.yaml | 31 ++ config/rbac/bpfnsprogram_viewer_role.yaml | 27 ++ config/rbac/kustomization.yaml | 2 + config/rbac/role_binding.yaml | 20 + .../bpfman-agent/application-program.go | 57 +-- .../bpfman-agent/application-program_test.go | 26 +- controllers/bpfman-agent/common.go | 296 +++++++------- controllers/bpfman-agent/common_cluster.go | 149 +++++++ controllers/bpfman-agent/common_namespace.go | 151 ++++++++ controllers/bpfman-agent/containers.go | 5 +- controllers/bpfman-agent/fentry-program.go | 8 +- .../bpfman-agent/fentry-program_test.go | 17 +- controllers/bpfman-agent/fexit-program.go | 8 +- .../bpfman-agent/fexit-program_test.go | 16 +- .../bpfman-agent/internal/bpfman-core.go | 8 +- controllers/bpfman-agent/kprobe-program.go | 8 +- .../bpfman-agent/kprobe-program_test.go | 16 +- controllers/bpfman-agent/tc-program.go | 11 +- controllers/bpfman-agent/tc-program_test.go | 43 ++- controllers/bpfman-agent/tcx-program.go | 9 +- controllers/bpfman-agent/tcx-program_test.go | 43 ++- .../bpfman-agent/tracepoint-program.go | 8 +- .../bpfman-agent/tracepoint-program_test.go | 18 +- controllers/bpfman-agent/uprobe-program.go | 8 +- .../bpfman-agent/uprobe-program_test.go | 16 +- controllers/bpfman-agent/xdp-ns-program.go | 230 +++++++++++ controllers/bpfman-agent/xdp-program.go | 8 +- controllers/bpfman-agent/xdp-program_test.go | 24 +- .../application-program_test.go | 8 +- .../bpfman-operator/application-programs.go | 13 +- controllers/bpfman-operator/common.go | 106 ++--- controllers/bpfman-operator/common_cluster.go | 83 ++++ .../bpfman-operator/common_namespace.go | 84 ++++ controllers/bpfman-operator/configmap.go | 2 +- controllers/bpfman-operator/configmap_test.go | 12 +- controllers/bpfman-operator/fentry-program.go | 12 +- .../bpfman-operator/fentry-program_test.go | 7 +- controllers/bpfman-operator/fexit-program.go | 12 +- .../bpfman-operator/fexit-program_test.go | 8 +- controllers/bpfman-operator/kprobe-program.go | 12 +- .../bpfman-operator/kprobe-program_test.go | 8 +- controllers/bpfman-operator/tc-program.go | 12 +- .../bpfman-operator/tc-program_test.go | 8 +- controllers/bpfman-operator/tcx-program.go | 12 +- .../bpfman-operator/tcx-program_test.go | 8 +- .../bpfman-operator/tracepoint-program.go | 12 +- .../tracepoint-program_test.go | 8 +- controllers/bpfman-operator/uprobe-program.go | 12 +- .../bpfman-operator/uprobe-program_test.go | 8 +- controllers/bpfman-operator/xdp-ns-program.go | 125 ++++++ controllers/bpfman-operator/xdp-program.go | 12 +- .../bpfman-operator/xdp-program_test.go | 8 +- internal/constants.go | 5 + internal/k8s.go | 18 + pkg/client/apis/v1alpha1/bpfnsprogram.go | 99 +++++ .../apis/v1alpha1/expansion_generated.go | 16 + pkg/client/apis/v1alpha1/xdpnsprogram.go | 99 +++++ .../typed/apis/v1alpha1/apis_client.go | 10 + .../typed/apis/v1alpha1/bpfnsprogram.go | 195 ++++++++++ .../apis/v1alpha1/fake/fake_apis_client.go | 8 + .../apis/v1alpha1/fake/fake_bpfnsprogram.go | 141 +++++++ .../apis/v1alpha1/fake/fake_xdpnsprogram.go | 141 +++++++ .../apis/v1alpha1/generated_expansion.go | 4 + .../typed/apis/v1alpha1/xdpnsprogram.go | 195 ++++++++++ .../apis/v1alpha1/bpfnsprogram.go | 90 +++++ .../apis/v1alpha1/interface.go | 14 + .../apis/v1alpha1/xdpnsprogram.go | 90 +++++ pkg/client/externalversions/generic.go | 4 + 93 files changed, 4378 insertions(+), 401 deletions(-) create mode 100644 apis/v1alpha1/bpfprogram_ns_types.go create mode 100644 apis/v1alpha1/xdpProgram_ns_types.go create mode 100644 bundle/manifests/bpfman-agent-role_rbac.authorization.k8s.io_v1_role.yaml create mode 100644 bundle/manifests/bpfman-bpfnsprogram-editor-role_rbac.authorization.k8s.io_v1_role.yaml create mode 100644 bundle/manifests/bpfman-bpfnsprogram-viewer-role_rbac.authorization.k8s.io_v1_role.yaml create mode 100644 bundle/manifests/bpfman-operator-role_rbac.authorization.k8s.io_v1_role.yaml create mode 100644 bundle/manifests/bpfman.io_bpfnsprograms.yaml create mode 100644 bundle/manifests/bpfman.io_xdpnsprograms.yaml create mode 100644 config/crd/bases/bpfman.io_bpfnsprograms.yaml create mode 100644 config/crd/bases/bpfman.io_xdpnsprograms.yaml create mode 100644 config/crd/patches/cainjection_in_bpfnsprograms.yaml create mode 100644 config/crd/patches/cainjection_in_xdpnsprograms.yaml create mode 100644 config/crd/patches/webhook_in_bpfnsprograms.yaml create mode 100644 config/crd/patches/webhook_in_xdpnsprograms.yaml create mode 100644 config/rbac/bpfnsprogram_editor_role.yaml create mode 100644 config/rbac/bpfnsprogram_viewer_role.yaml create mode 100644 controllers/bpfman-agent/common_cluster.go create mode 100644 controllers/bpfman-agent/common_namespace.go create mode 100644 controllers/bpfman-agent/xdp-ns-program.go create mode 100644 controllers/bpfman-operator/common_cluster.go create mode 100644 controllers/bpfman-operator/common_namespace.go create mode 100644 controllers/bpfman-operator/xdp-ns-program.go create mode 100644 pkg/client/apis/v1alpha1/bpfnsprogram.go create mode 100644 pkg/client/apis/v1alpha1/xdpnsprogram.go create mode 100644 pkg/client/clientset/typed/apis/v1alpha1/bpfnsprogram.go create mode 100644 pkg/client/clientset/typed/apis/v1alpha1/fake/fake_bpfnsprogram.go create mode 100644 pkg/client/clientset/typed/apis/v1alpha1/fake/fake_xdpnsprogram.go create mode 100644 pkg/client/clientset/typed/apis/v1alpha1/xdpnsprogram.go create mode 100644 pkg/client/externalversions/apis/v1alpha1/bpfnsprogram.go create mode 100644 pkg/client/externalversions/apis/v1alpha1/xdpnsprogram.go diff --git a/apis/v1alpha1/bpfprogram_ns_types.go b/apis/v1alpha1/bpfprogram_ns_types.go new file mode 100644 index 000000000..58688fa3d --- /dev/null +++ b/apis/v1alpha1/bpfprogram_ns_types.go @@ -0,0 +1,79 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// All fields are required unless explicitly marked optional +// +kubebuilder:validation:Required +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1types "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +// +genclient +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// BpfNsProgram is the Schema for the Bpfnsprograms API +// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[0].reason` +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +type BpfNsProgram struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BpfProgramSpec `json:"spec"` + // +optional + Status BpfProgramStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// BpfNsProgramList contains a list of BpfProgram +type BpfNsProgramList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []BpfNsProgram `json:"items"` +} + +func (bp BpfNsProgram) GetName() string { + return bp.Name +} + +func (bp BpfNsProgram) GetUID() metav1types.UID { + return bp.UID +} + +func (bp BpfNsProgram) GetAnnotations() map[string]string { + return bp.Annotations +} + +func (bp BpfNsProgram) GetLabels() map[string]string { + return bp.Labels +} + +func (bp BpfNsProgram) GetStatus() *BpfProgramStatus { + return &bp.Status +} + +func (bp BpfNsProgram) GetClientObject() client.Object { + return &bp +} + +func (bpl BpfNsProgramList) GetItems() []BpfNsProgram { + return bpl.Items +} diff --git a/apis/v1alpha1/bpfprogram_types.go b/apis/v1alpha1/bpfprogram_types.go index 9fa52c644..9e841bd44 100644 --- a/apis/v1alpha1/bpfprogram_types.go +++ b/apis/v1alpha1/bpfprogram_types.go @@ -20,6 +20,8 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1types "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" ) // +genclient @@ -69,3 +71,31 @@ type BpfProgramList struct { metav1.ListMeta `json:"metadata,omitempty"` Items []BpfProgram `json:"items"` } + +func (bp BpfProgram) GetName() string { + return bp.Name +} + +func (bp BpfProgram) GetUID() metav1types.UID { + return bp.UID +} + +func (bp BpfProgram) GetAnnotations() map[string]string { + return bp.Annotations +} + +func (bp BpfProgram) GetLabels() map[string]string { + return bp.Labels +} + +func (bp BpfProgram) GetStatus() *BpfProgramStatus { + return &bp.Status +} + +func (bp BpfProgram) GetClientObject() client.Object { + return &bp +} + +func (bpl BpfProgramList) GetItems() []BpfProgram { + return bpl.Items +} diff --git a/apis/v1alpha1/xdpProgram_ns_types.go b/apis/v1alpha1/xdpProgram_ns_types.go new file mode 100644 index 000000000..0df7a3721 --- /dev/null +++ b/apis/v1alpha1/xdpProgram_ns_types.go @@ -0,0 +1,52 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// All fields are required unless explicitly marked optional +// +kubebuilder:validation:Required +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:scope=Namespaced + +// XdpNsProgram is the Schema for the XdpNsPrograms API +// +kubebuilder:printcolumn:name="BpfFunctionName",type=string,JSONPath=`.spec.bpffunctionname` +// +kubebuilder:printcolumn:name="NodeSelector",type=string,JSONPath=`.spec.nodeselector` +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[0].reason` +// +kubebuilder:printcolumn:name="Priority",type=string,JSONPath=`.spec.priority`,priority=1 +// +kubebuilder:printcolumn:name="InterfaceSelector",type=string,JSONPath=`.spec.interfaceselector`,priority=1 +// +kubebuilder:printcolumn:name="ProceedOn",type=string,JSONPath=`.spec.proceedon`,priority=1 +type XdpNsProgram struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec XdpProgramSpec `json:"spec"` + // +optional + Status XdpProgramStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true +// XdpProgramList contains a list of XdpPrograms +type XdpNsProgramList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []XdpNsProgram `json:"items"` +} diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 80f3b52cd..1d5e7c964 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -221,6 +221,65 @@ func (in *BpfApplicationStatus) DeepCopy() *BpfApplicationStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BpfNsProgram) DeepCopyInto(out *BpfNsProgram) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BpfNsProgram. +func (in *BpfNsProgram) DeepCopy() *BpfNsProgram { + if in == nil { + return nil + } + out := new(BpfNsProgram) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BpfNsProgram) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BpfNsProgramList) DeepCopyInto(out *BpfNsProgramList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BpfNsProgram, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BpfNsProgramList. +func (in *BpfNsProgramList) DeepCopy() *BpfNsProgramList { + if in == nil { + return nil + } + out := new(BpfNsProgramList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BpfNsProgramList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BpfProgram) DeepCopyInto(out *BpfProgram) { *out = *in @@ -1242,6 +1301,65 @@ func (in *UprobeProgramStatus) DeepCopy() *UprobeProgramStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *XdpNsProgram) DeepCopyInto(out *XdpNsProgram) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new XdpNsProgram. +func (in *XdpNsProgram) DeepCopy() *XdpNsProgram { + if in == nil { + return nil + } + out := new(XdpNsProgram) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *XdpNsProgram) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *XdpNsProgramList) DeepCopyInto(out *XdpNsProgramList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]XdpNsProgram, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new XdpNsProgramList. +func (in *XdpNsProgramList) DeepCopy() *XdpNsProgramList { + if in == nil { + return nil + } + out := new(XdpNsProgramList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *XdpNsProgramList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *XdpProgram) DeepCopyInto(out *XdpProgram) { *out = *in diff --git a/apis/v1alpha1/zz_generated.register.go b/apis/v1alpha1/zz_generated.register.go index 5b55645af..64d6bbf74 100644 --- a/apis/v1alpha1/zz_generated.register.go +++ b/apis/v1alpha1/zz_generated.register.go @@ -63,6 +63,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &BpfApplication{}, &BpfApplicationList{}, + &BpfNsProgram{}, + &BpfNsProgramList{}, &BpfProgram{}, &BpfProgramList{}, &FentryProgram{}, @@ -79,6 +81,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &TracepointProgramList{}, &UprobeProgram{}, &UprobeProgramList{}, + &XdpNsProgram{}, + &XdpNsProgramList{}, &XdpProgram{}, &XdpProgramList{}, ) diff --git a/bundle/manifests/bpfman-agent-role_rbac.authorization.k8s.io_v1_clusterrole.yaml b/bundle/manifests/bpfman-agent-role_rbac.authorization.k8s.io_v1_clusterrole.yaml index feb710832..e02b216ad 100644 --- a/bundle/manifests/bpfman-agent-role_rbac.authorization.k8s.io_v1_clusterrole.yaml +++ b/bundle/manifests/bpfman-agent-role_rbac.authorization.k8s.io_v1_clusterrole.yaml @@ -18,6 +18,32 @@ rules: - bpfapplications/finalizers verbs: - update +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms/finalizers + verbs: + - update +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms/status + verbs: + - get + - patch + - update - apiGroups: - bpfman.io resources: @@ -142,6 +168,14 @@ rules: - uprobeprograms/finalizers verbs: - update +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms + verbs: + - get + - list + - watch - apiGroups: - bpfman.io resources: diff --git a/bundle/manifests/bpfman-agent-role_rbac.authorization.k8s.io_v1_role.yaml b/bundle/manifests/bpfman-agent-role_rbac.authorization.k8s.io_v1_role.yaml new file mode 100644 index 000000000..4ff9d8af2 --- /dev/null +++ b/bundle/manifests/bpfman-agent-role_rbac.authorization.k8s.io_v1_role.yaml @@ -0,0 +1,20 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + creationTimestamp: null + name: bpfman-agent-role +rules: +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms + verbs: + - get + - list + - watch +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms/finalizers + verbs: + - update diff --git a/bundle/manifests/bpfman-bpfnsprogram-editor-role_rbac.authorization.k8s.io_v1_role.yaml b/bundle/manifests/bpfman-bpfnsprogram-editor-role_rbac.authorization.k8s.io_v1_role.yaml new file mode 100644 index 000000000..6bd7cf7cd --- /dev/null +++ b/bundle/manifests/bpfman-bpfnsprogram-editor-role_rbac.authorization.k8s.io_v1_role.yaml @@ -0,0 +1,31 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: bpfman-operator + app.kubernetes.io/instance: bpfnsprogram-editor-role + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: role + app.kubernetes.io/part-of: bpfman-operator + name: bpfman-bpfnsprogram-editor-role +rules: +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms/status + verbs: + - get diff --git a/bundle/manifests/bpfman-bpfnsprogram-viewer-role_rbac.authorization.k8s.io_v1_role.yaml b/bundle/manifests/bpfman-bpfnsprogram-viewer-role_rbac.authorization.k8s.io_v1_role.yaml new file mode 100644 index 000000000..9d83ad2e5 --- /dev/null +++ b/bundle/manifests/bpfman-bpfnsprogram-viewer-role_rbac.authorization.k8s.io_v1_role.yaml @@ -0,0 +1,27 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + creationTimestamp: null + labels: + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: bpfman-operator + app.kubernetes.io/instance: bpfnsprogram-viewer-role + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/name: role + app.kubernetes.io/part-of: bpfman-operator + name: bpfman-bpfnsprogram-viewer-role +rules: +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms + verbs: + - get + - list + - watch +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms/status + verbs: + - get diff --git a/bundle/manifests/bpfman-operator-role_rbac.authorization.k8s.io_v1_role.yaml b/bundle/manifests/bpfman-operator-role_rbac.authorization.k8s.io_v1_role.yaml new file mode 100644 index 000000000..4dcbda1fc --- /dev/null +++ b/bundle/manifests/bpfman-operator-role_rbac.authorization.k8s.io_v1_role.yaml @@ -0,0 +1,32 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + creationTimestamp: null + name: bpfman-operator-role +rules: +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms/finalizers + verbs: + - update +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms/status + verbs: + - get + - patch + - update diff --git a/bundle/manifests/bpfman-operator.clusterserviceversion.yaml b/bundle/manifests/bpfman-operator.clusterserviceversion.yaml index 59339fa32..6c258e004 100644 --- a/bundle/manifests/bpfman-operator.clusterserviceversion.yaml +++ b/bundle/manifests/bpfman-operator.clusterserviceversion.yaml @@ -307,7 +307,7 @@ metadata: capabilities: Basic Install categories: OpenShift Optional containerImage: quay.io/bpfman/bpfman-operator:latest - createdAt: "2024-10-21T11:51:40Z" + createdAt: "2024-12-03T17:08:30Z" features.operators.openshift.io/cnf: "false" features.operators.openshift.io/cni: "false" features.operators.openshift.io/csi: "true" @@ -357,6 +357,11 @@ spec: kind: BpfApplication name: bpfapplications.bpfman.io version: v1alpha1 + - description: BpfNsProgram is the Schema for the BpfNsProgram API + displayName: Bpf NS Program + kind: BpfNsProgram + name: bpfnsprograms.bpfman.io + version: v1alpha1 - description: BpfProgram is the Schema for the BpfProgram API displayName: Bpf Program kind: BpfProgram @@ -395,6 +400,11 @@ spec: kind: UprobeProgram name: uprobeprograms.bpfman.io version: v1alpha1 + - description: XdpNsProgram is the Schema for the Xdpnsprograms API + displayName: Xdp NS Program + kind: XdpNsProgram + name: xdpnsprograms.bpfman.io + version: v1alpha1 - description: XdpProgram is the Schema for the Xdpprograms API displayName: Xdp Program kind: XdpProgram @@ -759,6 +769,14 @@ spec: - get - patch - update + - apiGroups: + - bpfman.io + resources: + - bpfnsprograms + verbs: + - get + - list + - watch - apiGroups: - bpfman.io resources: @@ -955,6 +973,32 @@ spec: - get - patch - update + - apiGroups: + - bpfman.io + resources: + - xdpnsprograms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - bpfman.io + resources: + - xdpnsprograms/finalizers + verbs: + - update + - apiGroups: + - bpfman.io + resources: + - xdpnsprograms/status + verbs: + - get + - patch + - update - apiGroups: - bpfman.io resources: @@ -1167,6 +1211,40 @@ spec: verbs: - create - patch + - apiGroups: + - bpfman.io + resources: + - bpfnsprograms + verbs: + - get + - list + - watch + - apiGroups: + - bpfman.io + resources: + - xdpnsprograms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - bpfman.io + resources: + - xdpnsprograms/finalizers + verbs: + - update + - apiGroups: + - bpfman.io + resources: + - xdpnsprograms/status + verbs: + - get + - patch + - update serviceAccountName: bpfman-operator strategy: deployment installModes: diff --git a/bundle/manifests/bpfman.io_bpfnsprograms.yaml b/bundle/manifests/bpfman.io_bpfnsprograms.yaml new file mode 100644 index 000000000..606780fdd --- /dev/null +++ b/bundle/manifests/bpfman.io_bpfnsprograms.yaml @@ -0,0 +1,150 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + creationTimestamp: null + name: bpfnsprograms.bpfman.io +spec: + group: bpfman.io + names: + kind: BpfNsProgram + listKind: BpfNsProgramList + plural: bpfnsprograms + singular: bpfnsprogram + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.type + name: Type + type: string + - jsonPath: .status.conditions[0].reason + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: BpfNsProgram is the Schema for the Bpfnsprograms API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BpfProgramSpec defines the desired state of BpfProgram + properties: + type: + description: Type specifies the bpf program type + type: string + type: object + status: + description: |- + BpfProgramStatus defines the observed state of BpfProgram + TODO Make these a fixed set of metav1.Condition.types and metav1.Condition.reasons + properties: + conditions: + description: |- + Conditions houses the updates regarding the actual implementation of + the bpf program on the node + Known .status.conditions.type are: "Available", "Progressing", and "Degraded" + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/bundle/manifests/bpfman.io_xdpnsprograms.yaml b/bundle/manifests/bpfman.io_xdpnsprograms.yaml new file mode 100644 index 000000000..e7e933893 --- /dev/null +++ b/bundle/manifests/bpfman.io_xdpnsprograms.yaml @@ -0,0 +1,362 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + creationTimestamp: null + name: xdpnsprograms.bpfman.io +spec: + group: bpfman.io + names: + kind: XdpNsProgram + listKind: XdpNsProgramList + plural: xdpnsprograms + singular: xdpnsprogram + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.bpffunctionname + name: BpfFunctionName + type: string + - jsonPath: .spec.nodeselector + name: NodeSelector + type: string + - jsonPath: .status.conditions[0].reason + name: Status + type: string + - jsonPath: .spec.priority + name: Priority + priority: 1 + type: string + - jsonPath: .spec.interfaceselector + name: InterfaceSelector + priority: 1 + type: string + - jsonPath: .spec.proceedon + name: ProceedOn + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: XdpNsProgram is the Schema for the XdpNsPrograms API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: XdpProgramSpec defines the desired state of XdpProgram + properties: + bpffunctionname: + description: |- + BpfFunctionName is the name of the function that is the entry point for the BPF + program + type: string + bytecode: + description: |- + Bytecode configures where the bpf program's bytecode should be loaded + from. + properties: + image: + description: Image used to specify a bytecode container image. + properties: + imagepullpolicy: + default: IfNotPresent + description: PullPolicy describes a policy for if/when to + pull a bytecode image. Defaults to IfNotPresent. + enum: + - Always + - Never + - IfNotPresent + type: string + imagepullsecret: + description: |- + ImagePullSecret is the name of the secret bpfman should use to get remote image + repository secrets. + properties: + name: + description: Name of the secret which contains the credentials + to access the image repository. + type: string + namespace: + description: Namespace of the secret which contains the + credentials to access the image repository. + type: string + required: + - name + - namespace + type: object + url: + description: Valid container image URL used to reference a + remote bytecode image. + type: string + required: + - url + type: object + path: + description: Path is used to specify a bytecode object via filepath. + type: string + type: object + globaldata: + additionalProperties: + format: byte + type: string + description: |- + GlobalData allows the user to set global variables when the program is loaded + with an array of raw bytes. This is a very low level primitive. The caller + is responsible for formatting the byte string appropriately considering + such things as size, endianness, alignment and packing of data structures. + type: object + interfaceselector: + description: Selector to determine the network interface (or interfaces) + maxProperties: 1 + minProperties: 1 + properties: + interfaces: + description: |- + Interfaces refers to a list of network interfaces to attach the BPF + program to. + items: + type: string + type: array + primarynodeinterface: + description: Attach BPF program to the primary interface on the + node. Only 'true' accepted. + type: boolean + type: object + mapownerselector: + description: |- + MapOwnerSelector is used to select the loaded eBPF program this eBPF program + will share a map with. The value is a label applied to the BpfProgram to select. + The selector must resolve to exactly one instance of a BpfProgram on a given node + or the eBPF program will not load. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + nodeselector: + description: |- + NodeSelector allows the user to specify which nodes to deploy the + bpf program to. This field must be specified, to select all nodes + use standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + priority: + description: |- + Priority specifies the priority of the bpf program in relation to + other programs of the same type with the same attach point. It is a value + from 0 to 1000 where lower values have higher precedence. + format: int32 + maximum: 1000 + minimum: 0 + type: integer + proceedon: + default: + - pass + - dispatcher_return + items: + enum: + - aborted + - drop + - pass + - tx + - redirect + - dispatcher_return + type: string + maxItems: 6 + type: array + required: + - bpffunctionname + - bytecode + - interfaceselector + - nodeselector + - priority + type: object + status: + description: XdpProgramStatus defines the observed state of XdpProgram + properties: + conditions: + description: |- + Conditions houses the global cluster state for the eBPFProgram. The explicit + condition types are defined internally. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/cmd/bpfman-agent/main.go b/cmd/bpfman-agent/main.go index 02f03e2c1..f0b548130 100644 --- a/cmd/bpfman-agent/main.go +++ b/cmd/bpfman-agent/main.go @@ -139,7 +139,7 @@ func main() { os.Exit(1) } - common := bpfmanagent.ReconcilerCommon{ + common := bpfmanagent.ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), GrpcConn: conn, @@ -147,64 +147,87 @@ func main() { NodeName: nodeName, } - if err = (&bpfmanagent.XdpProgramReconciler{ + commonCluster := bpfmanagent.ClusterProgramReconciler{ ReconcilerCommon: common, + } + + commonNs := bpfmanagent.ReconcilerCommon[bpfmaniov1alpha1.BpfNsProgram, bpfmaniov1alpha1.BpfNsProgramList]{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + GrpcConn: conn, + BpfmanClient: gobpfman.NewBpfmanClient(conn), + NodeName: nodeName, + } + + commonNamespace := bpfmanagent.NamespaceProgramReconciler{ + ReconcilerCommon: commonNs, + } + + if err = (&bpfmanagent.XdpProgramReconciler{ + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create xdpProgram controller", "controller", "BpfProgram") os.Exit(1) } + if err = (&bpfmanagent.XdpNsProgramReconciler{ + NamespaceProgramReconciler: commonNamespace, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create xdpsProgram controller", "controller", "BpfProgram") + os.Exit(1) + } + if err = (&bpfmanagent.TcProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create tcProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanagent.TcxProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create tcxProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanagent.TracepointProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create tracepointProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanagent.KprobeProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create kprobeProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanagent.UprobeProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create uprobeProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanagent.FentryProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create fentryProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanagent.FexitProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create fexitProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanagent.BpfApplicationReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create BpfApplicationProgram controller", "controller", "BpfProgram") os.Exit(1) diff --git a/cmd/bpfman-operator/main.go b/cmd/bpfman-operator/main.go index b6f2e86a2..af8b8f411 100644 --- a/cmd/bpfman-operator/main.go +++ b/cmd/bpfman-operator/main.go @@ -58,7 +58,7 @@ func init() { } // Returns true if the current platform is Openshift. -func isOpenshift(client discovery.DiscoveryInterface, cfg *rest.Config) (bool, error) { +func isOpenshift(client discovery.DiscoveryInterface, _ *rest.Config) (bool, error) { k8sVersion, err := client.ServerVersion() if err != nil { setupLog.Info("issue occurred while fetching ServerVersion") @@ -163,11 +163,24 @@ func main() { os.Exit(1) } - common := bpfmanoperator.ReconcilerCommon{ + common := bpfmanoperator.ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), } + commonCluster := bpfmanoperator.ClusterProgramReconciler{ + ReconcilerCommon: common, + } + + commonNs := bpfmanoperator.ReconcilerCommon[bpfmaniov1alpha1.BpfNsProgram, bpfmaniov1alpha1.BpfNsProgramList]{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + } + + commonNamespace := bpfmanoperator.NamespaceProgramReconciler{ + ReconcilerCommon: commonNs, + } + setupLog.Info("Discovering APIs") dc, err := discovery.NewDiscoveryClientForConfig(mgr.GetConfig()) if err != nil { @@ -183,7 +196,7 @@ func main() { } if err = (&bpfmanoperator.BpfmanConfigReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, BpfmanStandardDeployment: internal.BpfmanDaemonManifestPath, CsiDriverDeployment: internal.BpfmanCsiDriverPath, RestrictedSCC: internal.BpfmanRestrictedSCCPath, @@ -194,63 +207,70 @@ func main() { } if err = (&bpfmanoperator.XdpProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create xdpProgram controller", "controller", "BpfProgram") os.Exit(1) } + if err = (&bpfmanoperator.XdpNsProgramReconciler{ + NamespaceProgramReconciler: commonNamespace, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create xdpNsProgram controller", "controller", "BpfProgram") + os.Exit(1) + } + if err = (&bpfmanoperator.TcProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create tcProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanoperator.TcxProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create tcxProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanoperator.TracepointProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create tracepointProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanoperator.KprobeProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create kprobeProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanoperator.UprobeProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create uprobeProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanoperator.FentryProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create fentryProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanoperator.FexitProgramReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create fexitProgram controller", "controller", "BpfProgram") os.Exit(1) } if err = (&bpfmanoperator.BpfApplicationReconciler{ - ReconcilerCommon: common, + ClusterProgramReconciler: commonCluster, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "BpfApplication") os.Exit(1) diff --git a/config/crd/bases/bpfman.io_bpfnsprograms.yaml b/config/crd/bases/bpfman.io_bpfnsprograms.yaml new file mode 100644 index 000000000..3cd7faabb --- /dev/null +++ b/config/crd/bases/bpfman.io_bpfnsprograms.yaml @@ -0,0 +1,144 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: bpfnsprograms.bpfman.io +spec: + group: bpfman.io + names: + kind: BpfNsProgram + listKind: BpfNsProgramList + plural: bpfnsprograms + singular: bpfnsprogram + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.type + name: Type + type: string + - jsonPath: .status.conditions[0].reason + name: Status + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: BpfNsProgram is the Schema for the Bpfnsprograms API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BpfProgramSpec defines the desired state of BpfProgram + properties: + type: + description: Type specifies the bpf program type + type: string + type: object + status: + description: |- + BpfProgramStatus defines the observed state of BpfProgram + TODO Make these a fixed set of metav1.Condition.types and metav1.Condition.reasons + properties: + conditions: + description: |- + Conditions houses the updates regarding the actual implementation of + the bpf program on the node + Known .status.conditions.type are: "Available", "Progressing", and "Degraded" + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/bases/bpfman.io_xdpnsprograms.yaml b/config/crd/bases/bpfman.io_xdpnsprograms.yaml new file mode 100644 index 000000000..39ff9a283 --- /dev/null +++ b/config/crd/bases/bpfman.io_xdpnsprograms.yaml @@ -0,0 +1,356 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: xdpnsprograms.bpfman.io +spec: + group: bpfman.io + names: + kind: XdpNsProgram + listKind: XdpNsProgramList + plural: xdpnsprograms + singular: xdpnsprogram + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.bpffunctionname + name: BpfFunctionName + type: string + - jsonPath: .spec.nodeselector + name: NodeSelector + type: string + - jsonPath: .status.conditions[0].reason + name: Status + type: string + - jsonPath: .spec.priority + name: Priority + priority: 1 + type: string + - jsonPath: .spec.interfaceselector + name: InterfaceSelector + priority: 1 + type: string + - jsonPath: .spec.proceedon + name: ProceedOn + priority: 1 + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: XdpNsProgram is the Schema for the XdpNsPrograms API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: XdpProgramSpec defines the desired state of XdpProgram + properties: + bpffunctionname: + description: |- + BpfFunctionName is the name of the function that is the entry point for the BPF + program + type: string + bytecode: + description: |- + Bytecode configures where the bpf program's bytecode should be loaded + from. + properties: + image: + description: Image used to specify a bytecode container image. + properties: + imagepullpolicy: + default: IfNotPresent + description: PullPolicy describes a policy for if/when to + pull a bytecode image. Defaults to IfNotPresent. + enum: + - Always + - Never + - IfNotPresent + type: string + imagepullsecret: + description: |- + ImagePullSecret is the name of the secret bpfman should use to get remote image + repository secrets. + properties: + name: + description: Name of the secret which contains the credentials + to access the image repository. + type: string + namespace: + description: Namespace of the secret which contains the + credentials to access the image repository. + type: string + required: + - name + - namespace + type: object + url: + description: Valid container image URL used to reference a + remote bytecode image. + type: string + required: + - url + type: object + path: + description: Path is used to specify a bytecode object via filepath. + type: string + type: object + globaldata: + additionalProperties: + format: byte + type: string + description: |- + GlobalData allows the user to set global variables when the program is loaded + with an array of raw bytes. This is a very low level primitive. The caller + is responsible for formatting the byte string appropriately considering + such things as size, endianness, alignment and packing of data structures. + type: object + interfaceselector: + description: Selector to determine the network interface (or interfaces) + maxProperties: 1 + minProperties: 1 + properties: + interfaces: + description: |- + Interfaces refers to a list of network interfaces to attach the BPF + program to. + items: + type: string + type: array + primarynodeinterface: + description: Attach BPF program to the primary interface on the + node. Only 'true' accepted. + type: boolean + type: object + mapownerselector: + description: |- + MapOwnerSelector is used to select the loaded eBPF program this eBPF program + will share a map with. The value is a label applied to the BpfProgram to select. + The selector must resolve to exactly one instance of a BpfProgram on a given node + or the eBPF program will not load. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + nodeselector: + description: |- + NodeSelector allows the user to specify which nodes to deploy the + bpf program to. This field must be specified, to select all nodes + use standard metav1.LabelSelector semantics and make it empty. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. + The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector applies + to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + priority: + description: |- + Priority specifies the priority of the bpf program in relation to + other programs of the same type with the same attach point. It is a value + from 0 to 1000 where lower values have higher precedence. + format: int32 + maximum: 1000 + minimum: 0 + type: integer + proceedon: + default: + - pass + - dispatcher_return + items: + enum: + - aborted + - drop + - pass + - tx + - redirect + - dispatcher_return + type: string + maxItems: 6 + type: array + required: + - bpffunctionname + - bytecode + - interfaceselector + - nodeselector + - priority + type: object + status: + description: XdpProgramStatus defines the observed state of XdpProgram + properties: + conditions: + description: |- + Conditions houses the global cluster state for the eBPFProgram. The explicit + condition types are defined internally. + items: + description: "Condition contains details for one aspect of the current + state of this API Resource.\n---\nThis struct is intended for + direct use as an array at the field path .status.conditions. For + example,\n\n\n\ttype FooStatus struct{\n\t // Represents the + observations of a foo's current state.\n\t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\"\n\t // + +patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t + \ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\" + patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t + \ // other fields\n\t}" + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: |- + type of condition in CamelCase or in foo.example.com/CamelCase. + --- + Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be + useful (see .node.status.conditions), the ability to deconflict is important. + The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 41da2bb50..f778e827f 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -12,6 +12,8 @@ resources: - bases/bpfman.io_fentryprograms.yaml - bases/bpfman.io_fexitprograms.yaml - bases/bpfman.io_bpfapplications.yaml + - bases/bpfman.io_bpfnsprograms.yaml + - bases/bpfman.io_xdpnsprograms.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -20,6 +22,7 @@ patchesStrategicMerge: #- patches/webhook_in_bpfprograms.yaml #- patches/webhook_in_tcprograms.yaml #- patches/webhook_in_xdpprograms.yaml +#- patches/webhook_in_xdpnsprograms.yaml #- patches/webhook_in_tracepointprograms.yaml #- patches/webhook_in_kprobeprograms.yaml #- patches/webhook_in_uprobeprograms.yaml diff --git a/config/crd/patches/cainjection_in_bpfnsprograms.yaml b/config/crd/patches/cainjection_in_bpfnsprograms.yaml new file mode 100644 index 000000000..dddcc8ab4 --- /dev/null +++ b/config/crd/patches/cainjection_in_bpfnsprograms.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: bpfnsprograms.bpfman.io diff --git a/config/crd/patches/cainjection_in_xdpnsprograms.yaml b/config/crd/patches/cainjection_in_xdpnsprograms.yaml new file mode 100644 index 000000000..9a44e43d8 --- /dev/null +++ b/config/crd/patches/cainjection_in_xdpnsprograms.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: xdpnsprograms.bpfman.io diff --git a/config/crd/patches/webhook_in_bpfnsprograms.yaml b/config/crd/patches/webhook_in_bpfnsprograms.yaml new file mode 100644 index 000000000..264ad53a6 --- /dev/null +++ b/config/crd/patches/webhook_in_bpfnsprograms.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: bpfnsprograms.bpfman.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/crd/patches/webhook_in_xdpnsprograms.yaml b/config/crd/patches/webhook_in_xdpnsprograms.yaml new file mode 100644 index 000000000..c4d2aba26 --- /dev/null +++ b/config/crd/patches/webhook_in_xdpnsprograms.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: xdpnsprograms.bpfman.io +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/config/manifests/bases/bpfman-operator.clusterserviceversion.yaml b/config/manifests/bases/bpfman-operator.clusterserviceversion.yaml index 255546b5c..3fdbee45b 100644 --- a/config/manifests/bases/bpfman-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/bpfman-operator.clusterserviceversion.yaml @@ -57,11 +57,21 @@ spec: kind: BpfProgram name: bpfprograms.bpfman.io version: v1alpha1 + - description: BpfNsProgram is the Schema for the BpfNsProgram API + displayName: Bpf NS Program + kind: BpfNsProgram + name: bpfnsprograms.bpfman.io + version: v1alpha1 - description: XdpProgram is the Schema for the Xdpprograms API displayName: Xdp Program kind: XdpProgram name: xdpprograms.bpfman.io version: v1alpha1 + - description: XdpNsProgram is the Schema for the Xdpnsprograms API + displayName: Xdp NS Program + kind: XdpNsProgram + name: xdpnsprograms.bpfman.io + version: v1alpha1 - description: TcProgram is the Schema for the Tcprograms API displayName: Tc Program kind: TcProgram diff --git a/config/rbac/bpfman-agent/role.yaml b/config/rbac/bpfman-agent/role.yaml index da51037c7..0e78821d8 100644 --- a/config/rbac/bpfman-agent/role.yaml +++ b/config/rbac/bpfman-agent/role.yaml @@ -18,6 +18,32 @@ rules: - bpfapplications/finalizers verbs: - update +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms/finalizers + verbs: + - update +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms/status + verbs: + - get + - patch + - update - apiGroups: - bpfman.io resources: @@ -142,6 +168,14 @@ rules: - uprobeprograms/finalizers verbs: - update +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms + verbs: + - get + - list + - watch - apiGroups: - bpfman.io resources: @@ -178,3 +212,24 @@ rules: - secrets verbs: - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: agent-role + namespace: bpfman +rules: +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms + verbs: + - get + - list + - watch +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms/finalizers + verbs: + - update diff --git a/config/rbac/bpfman-operator/role.yaml b/config/rbac/bpfman-operator/role.yaml index 5e0913a4c..e7dfebaca 100644 --- a/config/rbac/bpfman-operator/role.yaml +++ b/config/rbac/bpfman-operator/role.yaml @@ -42,6 +42,14 @@ rules: - get - patch - update +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms + verbs: + - get + - list + - watch - apiGroups: - bpfman.io resources: @@ -238,6 +246,32 @@ rules: - get - patch - update +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms/finalizers + verbs: + - update +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms/status + verbs: + - get + - patch + - update - apiGroups: - bpfman.io resources: @@ -301,3 +335,44 @@ rules: - get - list - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: operator-role + namespace: bpfman +rules: +- apiGroups: + - bpfman.io + resources: + - bpfnsprograms + verbs: + - get + - list + - watch +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms/finalizers + verbs: + - update +- apiGroups: + - bpfman.io + resources: + - xdpnsprograms/status + verbs: + - get + - patch + - update diff --git a/config/rbac/bpfnsprogram_editor_role.yaml b/config/rbac/bpfnsprogram_editor_role.yaml new file mode 100644 index 000000000..973a04b0d --- /dev/null +++ b/config/rbac/bpfnsprogram_editor_role.yaml @@ -0,0 +1,31 @@ +# permissions for end users to edit bpfprograms. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/name: role + app.kubernetes.io/instance: bpfnsprogram-editor-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: bpfman-operator + app.kubernetes.io/part-of: bpfman-operator + app.kubernetes.io/managed-by: kustomize + name: bpfnsprogram-editor-role +rules: + - apiGroups: + - bpfman.io + resources: + - bpfnsprograms + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - bpfman.io + resources: + - bpfnsprograms/status + verbs: + - get diff --git a/config/rbac/bpfnsprogram_viewer_role.yaml b/config/rbac/bpfnsprogram_viewer_role.yaml new file mode 100644 index 000000000..1cd2ba154 --- /dev/null +++ b/config/rbac/bpfnsprogram_viewer_role.yaml @@ -0,0 +1,27 @@ +# permissions for end users to view bpfprograms. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/name: role + app.kubernetes.io/instance: bpfnsprogram-viewer-role + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: bpfman-operator + app.kubernetes.io/part-of: bpfman-operator + app.kubernetes.io/managed-by: kustomize + name: bpfnsprogram-viewer-role +rules: + - apiGroups: + - bpfman.io + resources: + - bpfnsprograms + verbs: + - get + - list + - watch + - apiGroups: + - bpfman.io + resources: + - bpfnsprograms/status + verbs: + - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index ad78ce73a..2d6028e1d 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -12,6 +12,8 @@ resources: - leader_election_role_binding.yaml - bpfprogram_editor_role.yaml - bpfprogram_viewer_role.yaml + - bpfnsprogram_editor_role.yaml + - bpfnsprogram_viewer_role.yaml # Comment the following 4 lines if you want to disable # the auth proxy (https://github.com/brancz/kube-rbac-proxy) # which protects your /metrics endpoint. diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index a1791f6f9..874a081e9 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -38,6 +38,26 @@ subjects: --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding +metadata: + labels: + app.kubernetes.io/name: rolebinding + app.kubernetes.io/component: rbac + app.kubernetes.io/created-by: bpfman-operator + app.kubernetes.io/part-of: bpfman-operator + app.kubernetes.io/managed-by: kustomize + name: operator-rolebinding + namespace: bpfman +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: bpfman-operator-role +subjects: + - kind: ServiceAccount + name: operator + namespace: system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding metadata: labels: app.kubernetes.io/name: rolebinding diff --git a/controllers/bpfman-agent/application-program.go b/controllers/bpfman-agent/application-program.go index 08d2dea64..0f3093bd5 100644 --- a/controllers/bpfman-agent/application-program.go +++ b/controllers/bpfman-agent/application-program.go @@ -22,7 +22,7 @@ import ( //+kubebuilder:rbac:groups=bpfman.io,resources=bpfapplications,verbs=get;list;watch type BpfApplicationReconciler struct { - ReconcilerCommon + ClusterProgramReconciler currentApp *bpfmaniov1alpha1.BpfApplication ourNode *v1.Node } @@ -66,6 +66,7 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque var res ctrl.Result var err error var complete bool + var lastRec bpfmanReconciler[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] buildProgramName := func( app bpfmaniov1alpha1.BpfApplication, @@ -89,15 +90,16 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque }, } rec := &FentryProgramReconciler{ - ReconcilerCommon: r.ReconcilerCommon, - currentFentryProgram: &fentryProgram, - ourNode: r.ourNode, + ClusterProgramReconciler: r.ClusterProgramReconciler, + currentFentryProgram: &fentryProgram, + ourNode: r.ourNode, } rec.appOwner = &a fentryObjects := []client.Object{&fentryProgram} appProgramMap[appProgramId] = true // Reconcile FentryProgram. complete, res, err = r.reconcileCommon(ctx, rec, fentryObjects) + lastRec = rec case bpfmaniov1alpha1.ProgTypeFexit: appProgramId := fmt.Sprintf("%s-%s-%s", strings.ToLower(string(p.Type)), sanitize(p.Fexit.FunctionName), p.Fexit.BpfFunctionName) @@ -111,15 +113,16 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque }, } rec := &FexitProgramReconciler{ - ReconcilerCommon: r.ReconcilerCommon, - currentFexitProgram: &fexitProgram, - ourNode: r.ourNode, + ClusterProgramReconciler: r.ClusterProgramReconciler, + currentFexitProgram: &fexitProgram, + ourNode: r.ourNode, } rec.appOwner = &a fexitObjects := []client.Object{&fexitProgram} appProgramMap[appProgramId] = true // Reconcile FexitProgram. complete, res, err = r.reconcileCommon(ctx, rec, fexitObjects) + lastRec = rec case bpfmaniov1alpha1.ProgTypeKprobe, bpfmaniov1alpha1.ProgTypeKretprobe: @@ -134,15 +137,16 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque }, } rec := &KprobeProgramReconciler{ - ReconcilerCommon: r.ReconcilerCommon, - currentKprobeProgram: &kprobeProgram, - ourNode: r.ourNode, + ClusterProgramReconciler: r.ClusterProgramReconciler, + currentKprobeProgram: &kprobeProgram, + ourNode: r.ourNode, } rec.appOwner = &a kprobeObjects := []client.Object{&kprobeProgram} appProgramMap[appProgramId] = true // Reconcile KprobeProgram or KpretprobeProgram. complete, res, err = r.reconcileCommon(ctx, rec, kprobeObjects) + lastRec = rec case bpfmaniov1alpha1.ProgTypeUprobe, bpfmaniov1alpha1.ProgTypeUretprobe: @@ -157,15 +161,16 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque }, } rec := &UprobeProgramReconciler{ - ReconcilerCommon: r.ReconcilerCommon, - currentUprobeProgram: &uprobeProgram, - ourNode: r.ourNode, + ClusterProgramReconciler: r.ClusterProgramReconciler, + currentUprobeProgram: &uprobeProgram, + ourNode: r.ourNode, } rec.appOwner = &a uprobeObjects := []client.Object{&uprobeProgram} appProgramMap[appProgramId] = true // Reconcile UprobeProgram or UpretprobeProgram. complete, res, err = r.reconcileCommon(ctx, rec, uprobeObjects) + lastRec = rec case bpfmaniov1alpha1.ProgTypeTracepoint: appProgramId := fmt.Sprintf("%s-%s", strings.ToLower(string(p.Type)), p.Tracepoint.BpfFunctionName) @@ -179,7 +184,7 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque }, } rec := &TracepointProgramReconciler{ - ReconcilerCommon: r.ReconcilerCommon, + ClusterProgramReconciler: r.ClusterProgramReconciler, currentTracepointProgram: &tracepointProgram, ourNode: r.ourNode, } @@ -188,6 +193,7 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque appProgramMap[appProgramId] = true // Reconcile TracepointProgram. complete, res, err = r.reconcileCommon(ctx, rec, tracepointObjects) + lastRec = rec case bpfmaniov1alpha1.ProgTypeTC: _, ifErr := getInterfaces(&p.TC.InterfaceSelector, r.ourNode) @@ -207,15 +213,16 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque }, } rec := &TcProgramReconciler{ - ReconcilerCommon: r.ReconcilerCommon, - currentTcProgram: &tcProgram, - ourNode: r.ourNode, + ClusterProgramReconciler: r.ClusterProgramReconciler, + currentTcProgram: &tcProgram, + ourNode: r.ourNode, } rec.appOwner = &a tcObjects := []client.Object{&tcProgram} appProgramMap[appProgramId] = true // Reconcile TcProgram. complete, res, err = r.reconcileCommon(ctx, rec, tcObjects) + lastRec = rec case bpfmaniov1alpha1.ProgTypeTCX: _, ifErr := getInterfaces(&p.TCX.InterfaceSelector, r.ourNode) @@ -235,15 +242,16 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque }, } rec := &TcxProgramReconciler{ - ReconcilerCommon: r.ReconcilerCommon, - currentTcxProgram: &tcxProgram, - ourNode: r.ourNode, + ClusterProgramReconciler: r.ClusterProgramReconciler, + currentTcxProgram: &tcxProgram, + ourNode: r.ourNode, } rec.appOwner = &a tcxObjects := []client.Object{&tcxProgram} appProgramMap[appProgramId] = true // Reconcile TcxProgram. complete, res, err = r.reconcileCommon(ctx, rec, tcxObjects) + lastRec = rec case bpfmaniov1alpha1.ProgTypeXDP: _, ifErr := getInterfaces(&p.XDP.InterfaceSelector, r.ourNode) @@ -263,15 +271,16 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque }, } rec := &XdpProgramReconciler{ - ReconcilerCommon: r.ReconcilerCommon, - currentXdpProgram: &xdpProgram, - ourNode: r.ourNode, + ClusterProgramReconciler: r.ClusterProgramReconciler, + currentXdpProgram: &xdpProgram, + ourNode: r.ourNode, } rec.appOwner = &a xdpObjects := []client.Object{&xdpProgram} appProgramMap[appProgramId] = true // Reconcile XdpProgram. complete, res, err = r.reconcileCommon(ctx, rec, xdpObjects) + lastRec = rec default: ctxLogger.Error(fmt.Errorf("unsupported bpf program type"), "unsupported bpf program type", "ProgType", p.Type) @@ -307,7 +316,7 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque } } // Delete BpfPrograms that are no longer needed - res, err = r.unLoadAndDeleteBpfProgramsList(ctx, bpfDeletedPrograms, internal.BpfApplicationControllerFinalizer) + res, err = r.unLoadAndDeleteBpfProgramsList(ctx, lastRec, bpfDeletedPrograms, internal.BpfApplicationControllerFinalizer) if err != nil { ctxLogger.Error(err, "failed to delete programs") return ctrl.Result{}, err diff --git a/controllers/bpfman-agent/application-program_test.go b/controllers/bpfman-agent/application-program_test.go index d3c0195c7..896b3012e 100644 --- a/controllers/bpfman-agent/application-program_test.go +++ b/controllers/bpfman-agent/application-program_test.go @@ -6,7 +6,6 @@ import ( "testing" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" - bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" agenttestutils "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal/test-utils" "github.com/bpfman/bpfman-operator/internal" testutils "github.com/bpfman/bpfman-operator/internal/test-utils" @@ -105,19 +104,22 @@ func TestBpfApplicationControllerCreate(t *testing.T) { cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, appOwner: App, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger, so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &BpfApplicationReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &BpfApplicationReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -135,7 +137,8 @@ func TestBpfApplicationControllerCreate(t *testing.T) { } // Check the BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, fentryAppProgramId, fentryAttachPoint, fentryBpfProg) + rfentry := &FentryProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} + err = rc.getBpfProgram(ctx, rfentry, name, fentryAppProgramId, fentryAttachPoint, fentryBpfProg) require.NoError(t, err) require.NotEmpty(t, fentryBpfProg) @@ -186,11 +189,11 @@ func TestBpfApplicationControllerCreate(t *testing.T) { } // Check that the bpfProgram's programs was correctly updated - err = rc.getBpfProgram(ctx, name, fentryAppProgramId, fentryAttachPoint, fentryBpfProg) + err = rc.getBpfProgram(ctx, rfentry, name, fentryAppProgramId, fentryAttachPoint, fentryBpfProg) require.NoError(t, err) // prog ID should already have been set - id, err := bpfmanagentinternal.GetID(fentryBpfProg) + id, err := GetID(fentryBpfProg) require.NoError(t, err) // Check the bpfLoadRequest was correctly Built @@ -210,7 +213,7 @@ func TestBpfApplicationControllerCreate(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, fentryAppProgramId, fentryAttachPoint, fentryBpfProg) + err = rc.getBpfProgram(ctx, rfentry, name, fentryAppProgramId, fentryAttachPoint, fentryBpfProg) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), fentryBpfProg.Status.Conditions[0].Type) @@ -222,7 +225,8 @@ func TestBpfApplicationControllerCreate(t *testing.T) { t.Fatalf("reconcile: (%v)", err) } - err = rc.getBpfProgram(ctx, name, kprobeAppProgramId, kprobeAttachPoint, kprobeBpfProg) + rkprobe := &KprobeProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} + err = rc.getBpfProgram(ctx, rkprobe, name, kprobeAppProgramId, kprobeAttachPoint, kprobeBpfProg) require.NoError(t, err) require.NotEmpty(t, kprobeBpfProg) @@ -275,11 +279,11 @@ func TestBpfApplicationControllerCreate(t *testing.T) { } // Check that the bpfProgram's programs was correctly updated - err = rc.getBpfProgram(ctx, name, kprobeAppProgramId, kprobeAttachPoint, kprobeBpfProg) + err = rc.getBpfProgram(ctx, rkprobe, name, kprobeAppProgramId, kprobeAttachPoint, kprobeBpfProg) require.NoError(t, err) // prog ID should already have been set - id, err = bpfmanagentinternal.GetID(kprobeBpfProg) + id, err = GetID(kprobeBpfProg) require.NoError(t, err) // Check the bpfLoadRequest was correctly Built @@ -299,7 +303,7 @@ func TestBpfApplicationControllerCreate(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, kprobeAppProgramId, kprobeAttachPoint, kprobeBpfProg) + err = rc.getBpfProgram(ctx, rkprobe, name, kprobeAppProgramId, kprobeAttachPoint, kprobeBpfProg) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), kprobeBpfProg.Status.Conditions[0].Type) diff --git a/controllers/bpfman-agent/common.go b/controllers/bpfman-agent/common.go index bd1d69ed9..6792b4fdf 100644 --- a/controllers/bpfman-agent/common.go +++ b/controllers/bpfman-agent/common.go @@ -25,10 +25,10 @@ import ( "time" v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" @@ -59,6 +59,10 @@ import ( //+kubebuilder:rbac:groups=bpfman.io,resources=fentryprograms/finalizers,verbs=update //+kubebuilder:rbac:groups=bpfman.io,resources=fexityprograms/finalizers,verbs=update //+kubebuilder:rbac:groups=bpfman.io,resources=bpfapplications/finalizers,verbs=update +//+kubebuilder:rbac:groups=bpfman.io,resources=bpfnsprograms,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=bpfman.io,resources=bpfnsprograms/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=bpfman.io,resources=bpfnsprograms/finalizers,verbs=update +//+kubebuilder:rbac:groups=bpfman.io,namespace=bpfman,resources=xdpnsprograms/finalizers,verbs=update //+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch //+kubebuilder:rbac:groups=core,resources=nodes,verbs=get;list;watch //+kubebuilder:rbac:groups=core,resources=secrets,verbs=get @@ -68,8 +72,27 @@ const ( programDoesNotExistErr = "does not exist" ) +type BpfProg interface { + + // GetName returns the name of the current program. + GetName() string + + // GetUID returns the UID of the current program. + GetUID() types.UID + GetAnnotations() map[string]string + GetLabels() map[string]string + GetStatus() *bpfmaniov1alpha1.BpfProgramStatus + GetClientObject() client.Object +} + +type BpfProgList[T any] interface { + // bpfmaniov1alpha1.BpfProgramList | bpfmaniov1alpha1.BpfNsProgramList + + GetItems() []T +} + // ReconcilerCommon provides a skeleton for all *Program Reconcilers. -type ReconcilerCommon struct { +type ReconcilerCommon[T BpfProg, TL BpfProgList[T]] struct { client.Client Scheme *runtime.Scheme GrpcConn *grpc.ClientConn @@ -84,7 +107,17 @@ type ReconcilerCommon struct { // bpfmanReconciler defines a generic bpfProgram K8s object reconciler which can // program bpfman from user intent in the K8s CRDs. -type bpfmanReconciler interface { +type bpfmanReconciler[T BpfProg, TL BpfProgList[T]] interface { + // BPF Cluster of Namespaced Reconciler + getBpfList(ctx context.Context, opts []client.ListOption) (*TL, error) + updateBpfStatus(ctx context.Context, bpfProgram *T, condition metav1.Condition) error + createBpfProgram( + attachPoint string, + rec bpfmanReconciler[T, TL], + annotations map[string]string, + ) (*T, error) + + // *Program Reconciler // SetupWithManager registers the reconciler with the manager and defines // which kubernetes events will trigger a reconcile. SetupWithManager(mgr ctrl.Manager) error @@ -110,12 +143,13 @@ type bpfmanReconciler interface { getProgType() internal.ProgramType // getName returns the name of the current program being reconciled. getName() string + getNamespace() string // getExpectedBpfPrograms returns the list of BpfPrograms that are expected // to be loaded on the current node. - getExpectedBpfPrograms(ctx context.Context) (*bpfmaniov1alpha1.BpfProgramList, error) + getExpectedBpfPrograms(ctx context.Context) (*TL, error) // getLoadRequest returns the LoadRequest that should be sent to bpfman to // load the given BpfProgram. - getLoadRequest(bpfProgram *bpfmaniov1alpha1.BpfProgram, mapOwnerId *uint32) (*gobpfman.LoadRequest, error) + getLoadRequest(bpfProgram *T, mapOwnerId *uint32) (*gobpfman.LoadRequest, error) // getNode returns node object for the current node. getNode() *v1.Node // getBpfProgramCommon returns the BpfProgramCommon object for the current @@ -142,7 +176,7 @@ type bpfmanReconciler interface { // user and retry if specified. For some errors the controller may decide not to // retry. Note: This only results in calls to bpfman if we need to change // something -func (r *ReconcilerCommon) reconcileCommon(ctx context.Context, rec bpfmanReconciler, +func (r *ReconcilerCommon[T, TL]) reconcileCommon(ctx context.Context, rec bpfmanReconciler[T, TL], programs []client.Object) (bool, ctrl.Result, error) { r.Logger.V(1).Info("Start reconcileCommon()") @@ -194,17 +228,17 @@ func (r *ReconcilerCommon) reconcileCommon(ctx context.Context, rec bpfmanReconc // reconcileBpfmanPrograms ONLY reconciles the bpfman state for a single BpfProgram. // It does not interact with the k8s API in any way. -func (r *ReconcilerCommon) reconcileBpfProgram(ctx context.Context, - rec bpfmanReconciler, +func (r *ReconcilerCommon[T, TL]) reconcileBpfProgram(ctx context.Context, + rec bpfmanReconciler[T, TL], loadedBpfPrograms map[string]*gobpfman.ListResponse_ListResult, - bpfProgram *bpfmaniov1alpha1.BpfProgram, + bpfProgram *T, isNodeSelected bool, isBeingDeleted bool, mapOwnerStatus *MapOwnerParamStatus) (bpfmaniov1alpha1.BpfProgramConditionType, error) { - r.Logger.V(1).Info("enter reconcileBpfmanProgram()", "bpfProgram", bpfProgram.Name, "CurrentProgram", rec.getName()) + r.Logger.V(1).Info("enter reconcileBpfmanProgram()", "bpfProgram", (*bpfProgram).GetName(), "CurrentProgram", rec.getName()) - uuid := bpfProgram.UID + uuid := (*bpfProgram).GetUID() noContainersOnNode := noContainersOnNode(bpfProgram) loadedBpfProgram, isLoaded := loadedBpfPrograms[string(uuid)] shouldBeLoaded := bpfProgramShouldBeLoaded(isNodeSelected, isBeingDeleted, noContainersOnNode, mapOwnerStatus) @@ -214,7 +248,7 @@ func (r *ReconcilerCommon) reconcileBpfProgram(ctx context.Context, switch isLoaded { case true: // prog ID should already have been set if program is loaded - id, err := bpfmanagentinternal.GetID(bpfProgram) + id, err := GetID(bpfProgram) if err != nil { r.Logger.Error(err, "Failed to get bpf program ID") return bpfmaniov1alpha1.BpfProgCondNotLoaded, err @@ -229,13 +263,13 @@ func (r *ReconcilerCommon) reconcileBpfProgram(ctx context.Context, } isSame, reasons := bpfmanagentinternal.DoesProgExist(loadedBpfProgram, loadRequest) if !isSame { - r.Logger.V(1).Info("bpf program is in wrong state, unloading and reloading", "reason", reasons, "bpfProgram Name", bpfProgram.Name, "bpf program ID", id) + r.Logger.V(1).Info("bpf program is in wrong state, unloading and reloading", "reason", reasons, "bpfProgram Name", (*bpfProgram).GetName(), "bpf program ID", id) if err := bpfmanagentinternal.UnloadBpfmanProgram(ctx, r.BpfmanClient, *id); err != nil { r.Logger.Error(err, "Failed to unload BPF Program") return bpfmaniov1alpha1.BpfProgCondNotUnloaded, err } - r.Logger.Info("Calling bpfman to load bpf program on Node", "bpfProgram Name", bpfProgram.Name) + r.Logger.Info("Calling bpfman to load bpf program on Node", "bpfProgram Name", (*bpfProgram).GetName()) r.progId, err = bpfmanagentinternal.LoadBpfmanProgram(ctx, r.BpfmanClient, loadRequest) if err != nil { r.Logger.Error(err, "Failed to load bpf program") @@ -248,7 +282,7 @@ func (r *ReconcilerCommon) reconcileBpfProgram(ctx context.Context, } case false: // The program is loaded but it shouldn't be loaded. - r.Logger.Info("Calling bpfman to unload program on node", "bpfProgram Name", bpfProgram.Name, "Program ID", id) + r.Logger.Info("Calling bpfman to unload program on node", "bpfProgram Name", (*bpfProgram).GetName(), "Program ID", id) if err := bpfmanagentinternal.UnloadBpfmanProgram(ctx, r.BpfmanClient, *id); err != nil { r.Logger.Error(err, "Failed to unload Program") return bpfmaniov1alpha1.BpfProgCondNotUnloaded, err @@ -263,7 +297,7 @@ func (r *ReconcilerCommon) reconcileBpfProgram(ctx context.Context, return bpfmaniov1alpha1.BpfProgCondBytecodeSelectorError, err } - r.Logger.Info("Calling bpfman to load program on node", "bpfProgram name", bpfProgram.Name) + r.Logger.Info("Calling bpfman to load program on node", "bpfProgram name", (*bpfProgram).GetName()) r.progId, err = bpfmanagentinternal.LoadBpfmanProgram(ctx, r.BpfmanClient, loadRequest) if err != nil { r.Logger.Error(err, "Failed to load Program") @@ -286,7 +320,7 @@ func (r *ReconcilerCommon) reconcileBpfProgram(ctx context.Context, // reconcileBpfProgramSuccessCondition returns the proper condition for a // successful reconcile of a bpfProgram based on the given parameters. -func (r *ReconcilerCommon) reconcileBpfProgramSuccessCondition( +func (r *ReconcilerCommon[T, TL]) reconcileBpfProgramSuccessCondition( isLoaded bool, shouldBeLoaded bool, isNodeSelected bool, @@ -451,7 +485,7 @@ func getInterfaces(interfaceSelector *bpfmaniov1alpha1.InterfaceSelector, ourNod // removeFinalizer removes the finalizer from the BpfProgram object if is applied, // returning if the action resulted in a kube API update or not along with any // errors. -func (r *ReconcilerCommon) removeFinalizer(ctx context.Context, o client.Object, finalizer string) bool { +func (r *ReconcilerCommon[T, TL]) removeFinalizer(ctx context.Context, o client.Object, finalizer string) bool { changed := controllerutil.RemoveFinalizer(o, finalizer) if changed { r.Logger.Info("Removing finalizer from bpfProgram", "object name", o.GetName()) @@ -468,45 +502,57 @@ func (r *ReconcilerCommon) removeFinalizer(ctx context.Context, o client.Object, // updateStatus updates the status of a BpfProgram object if needed, returning // false if the status was already set for the given bpfProgram, meaning reconciliation // may continue. -func (r *ReconcilerCommon) updateStatus(ctx context.Context, bpfProgram *bpfmaniov1alpha1.BpfProgram, cond bpfmaniov1alpha1.BpfProgramConditionType) bool { - - r.Logger.V(1).Info("updateStatus()", "existing conds", bpfProgram.Status.Conditions, "new cond", cond) +func (r *ReconcilerCommon[T, TL]) updateStatus( + ctx context.Context, + rec bpfmanReconciler[T, TL], + bpfProgram *T, + cond bpfmaniov1alpha1.BpfProgramConditionType, +) bool { + status := (*bpfProgram).GetStatus() + r.Logger.V(1).Info("updateStatus()", "existing conds", status.Conditions, "new cond", cond) - if bpfProgram.Status.Conditions != nil { - numConditions := len(bpfProgram.Status.Conditions) + if status.Conditions != nil { + numConditions := len(status.Conditions) if numConditions == 1 { - if bpfProgram.Status.Conditions[0].Type == string(cond) { + if status.Conditions[0].Type == string(cond) { // No change, so just return false -- not updated return false } else { // We're changing the condition, so delete this one. The // new condition will be added below. - bpfProgram.Status.Conditions = nil + status.Conditions = nil } } else if numConditions > 1 { // We should only ever have one condition, so we shouldn't hit this // case. However, if we do, log a message, delete the existing // conditions, and add the new one below. r.Logger.Info("more than one BpfProgramCondition", "numConditions", numConditions) - bpfProgram.Status.Conditions = nil + status.Conditions = nil } // if numConditions == 0, just add the new condition below. } - meta.SetStatusCondition(&bpfProgram.Status.Conditions, cond.Condition()) + //meta.SetStatusCondition(&status.Conditions, cond.Condition()) - r.Logger.V(1).Info("Updating bpfProgram condition", "bpfProgram", bpfProgram.Name, "condition", cond.Condition().Type) - if err := r.Status().Update(ctx, bpfProgram); err != nil { + r.Logger.V(1).Info("Updating bpfProgram condition", "bpfProgram", (*bpfProgram).GetName(), "condition", cond.Condition().Type) + r.Logger.Info("BILLY: Updating bpfProgram condition", "bpfProgram", (*bpfProgram).GetName(), "condition", cond.Condition().Type) + //if err := r.Status().Update(ctx, (*bpfProgram).GetClientObject()); err != nil { + if err := rec.updateBpfStatus(ctx, bpfProgram, cond.Condition()); err != nil { r.Logger.Error(err, "failed to set bpfProgram object status") } + newStatus := (*bpfProgram).GetStatus() + r.Logger.Info("BILLY: Updating bpfProgram condition", "status", newStatus) + r.Logger.V(1).Info("condition updated", "new condition", cond) return true } -func statusContains(bpfProgram *bpfmaniov1alpha1.BpfProgram, cond bpfmaniov1alpha1.BpfProgramConditionType) bool { - for _, c := range bpfProgram.Status.Conditions { +//lint:ignore U1000 Linter claims function unused, but generics confusing linter +func statusContains[T BpfProg](bpfProgram *T, cond bpfmaniov1alpha1.BpfProgramConditionType) bool { + status := (*bpfProgram).GetStatus() + for _, c := range status.Conditions { if c.Type == string(cond) { return true } @@ -519,10 +565,8 @@ type bpfProgKey struct { attachPoint string } -func (r *ReconcilerCommon) getExistingBpfPrograms(ctx context.Context, - rec bpfmanReconciler) (map[bpfProgKey]bpfmaniov1alpha1.BpfProgram, error) { - - bpfProgramList := &bpfmaniov1alpha1.BpfProgramList{} +func (r *ReconcilerCommon[T, TL]) getExistingBpfPrograms(ctx context.Context, + rec bpfmanReconciler[T, TL]) (map[bpfProgKey]T, error) { // Only list bpfPrograms for this *Program and the controller's node opts := []client.ListOption{ @@ -533,13 +577,13 @@ func (r *ReconcilerCommon) getExistingBpfPrograms(ctx context.Context, }, } - err := r.List(ctx, bpfProgramList, opts...) + bpfProgramList, err := rec.getBpfList(ctx, opts) if err != nil { return nil, err } - existingBpfPrograms := map[bpfProgKey]bpfmaniov1alpha1.BpfProgram{} - for _, bpfProg := range bpfProgramList.Items { + existingBpfPrograms := map[bpfProgKey]T{} + for _, bpfProg := range (*bpfProgramList).GetItems() { key := bpfProgKey{ appProgId: bpfProg.GetLabels()[internal.AppProgramId], attachPoint: bpfProg.GetAnnotations()[internal.BpfProgramAttachPoint], @@ -555,46 +599,6 @@ func generateUniqueName(baseName string) string { return fmt.Sprintf("%s-%s", baseName, uuid[:8]) } -// createBpfProgram moves some shared logic for building bpfProgram objects -// into a central location. -func (r *ReconcilerCommon) createBpfProgram( - attachPoint string, - rec bpfmanReconciler, - annotations map[string]string) (*bpfmaniov1alpha1.BpfProgram, error) { - - r.Logger.V(1).Info("createBpfProgram()", "Name", attachPoint, - "Owner", rec.getOwner().GetName(), "OwnerType", rec.getRecType(), "Name", rec.getName()) - - if annotations == nil { - annotations = make(map[string]string) - } - annotations[internal.BpfProgramAttachPoint] = attachPoint - - bpfProg := &bpfmaniov1alpha1.BpfProgram{ - ObjectMeta: metav1.ObjectMeta{ - Name: generateUniqueName(rec.getName()), - Finalizers: []string{rec.getFinalizer()}, - Labels: map[string]string{ - internal.BpfProgramOwner: rec.getOwner().GetName(), - internal.AppProgramId: rec.getAppProgramId(), - internal.K8sHostLabel: r.NodeName, - }, - Annotations: annotations, - }, - Spec: bpfmaniov1alpha1.BpfProgramSpec{ - Type: rec.getRecType(), - }, - Status: bpfmaniov1alpha1.BpfProgramStatus{Conditions: []metav1.Condition{}}, - } - - // Make the corresponding BpfProgramConfig the owner - if err := ctrl.SetControllerReference(rec.getOwner(), bpfProg, r.Scheme); err != nil { - return nil, fmt.Errorf("failed to bpfProgram object owner reference: %v", err) - } - - return bpfProg, nil -} - // Programs may be deleted for one of two reasons. The first is that the global // *Program object is being deleted. The second is that the something has // changed on the node that is causing the need to remove individual @@ -612,10 +616,10 @@ func (r *ReconcilerCommon) createBpfProgram( // // For the second case, we need to do the first 2 steps, and then explicitly // delete the bpfPrograms that are no longer needed. -func (r *ReconcilerCommon) handleProgDelete( +func (r *ReconcilerCommon[T, TL]) handleProgDelete( ctx context.Context, - rec bpfmanReconciler, - existingBpfPrograms map[bpfProgKey]bpfmaniov1alpha1.BpfProgram, + rec bpfmanReconciler[T, TL], + existingBpfPrograms map[bpfProgKey]T, loadedBpfPrograms map[string]*gobpfman.ListResponse_ListResult, isNodeSelected bool, isBeingDeleted bool, @@ -624,7 +628,7 @@ func (r *ReconcilerCommon) handleProgDelete( r.Logger.V(1).Info("handleProgDelete()", "isBeingDeleted", isBeingDeleted, "isNodeSelected", isNodeSelected, "mapOwnerStatus", mapOwnerStatus) for _, bpfProgram := range existingBpfPrograms { - r.Logger.V(1).Info("Deleting bpfProgram", "Name", bpfProgram.Name) + r.Logger.V(1).Info("Deleting bpfProgram", "Name", bpfProgram.GetName()) // Reconcile the bpfProgram if error write condition and exit with // retry. cond, err := r.reconcileBpfProgram(ctx, @@ -636,11 +640,11 @@ func (r *ReconcilerCommon) handleProgDelete( mapOwnerStatus, ) if err != nil { - r.updateStatus(ctx, &bpfProgram, cond) + r.updateStatus(ctx, rec, &bpfProgram, cond) return internal.Requeue, fmt.Errorf("failed to delete bpfman program: %v", err) } - if r.removeFinalizer(ctx, &bpfProgram, rec.getFinalizer()) { + if r.removeFinalizer(ctx, bpfProgram.GetClientObject(), rec.getFinalizer()) { return internal.Updated, nil } @@ -648,7 +652,7 @@ func (r *ReconcilerCommon) handleProgDelete( // We're deleting these programs because the *Program is being // deleted, so update the status and the program will be deleted // when the owner is deleted. - if r.updateStatus(ctx, &bpfProgram, cond) { + if r.updateStatus(ctx, rec, &bpfProgram, cond) { return internal.Updated, nil } } else { @@ -656,8 +660,8 @@ func (r *ReconcilerCommon) handleProgDelete( // to changes that caused the containers to not be selected anymore. // So, explicitly delete them. opts := client.DeleteOptions{} - r.Logger.Info("Deleting bpfProgram", "Name", bpfProgram.Name, "Owner", bpfProgram.GetName()) - if err := r.Delete(ctx, &bpfProgram, &opts); err != nil { + r.Logger.Info("Deleting bpfProgram", "Name", bpfProgram.GetName(), "Owner", bpfProgram.GetName()) + if err := r.Delete(ctx, bpfProgram.GetClientObject(), &opts); err != nil { return internal.Requeue, fmt.Errorf("failed to delete bpfProgram object: %v", err) } return internal.Updated, nil @@ -672,41 +676,46 @@ func (r *ReconcilerCommon) handleProgDelete( // unLoadAndDeleteProgramsList unloads and deletes BbpPrograms when the owning // *Program or BpfApplication is not being deleted itself, but something // has changed such that the BpfPrograms are no longer needed. -func (r *ReconcilerCommon) unLoadAndDeleteBpfProgramsList(ctx context.Context, bpfProgramsList *bpfmaniov1alpha1.BpfProgramList, finalizerString string) (reconcile.Result, error) { - for _, bpfProgram := range bpfProgramsList.Items { - r.Logger.V(1).Info("Deleting bpfProgram", "Name", bpfProgram.Name) - id, err := bpfmanagentinternal.GetID(&bpfProgram) +func (r *ReconcilerCommon[T, TL]) unLoadAndDeleteBpfProgramsList( + ctx context.Context, + rec bpfmanReconciler[T, TL], + bpfProgramsList *TL, + finalizerString string, +) (reconcile.Result, error) { + for _, bpfProgram := range (*bpfProgramsList).GetItems() { + r.Logger.V(1).Info("Deleting bpfProgram", "Name", bpfProgram.GetName()) + id, err := GetID(&bpfProgram) if err != nil { r.Logger.Error(err, "Failed to get bpf program ID") return ctrl.Result{}, nil } if id != nil && !statusContains(&bpfProgram, bpfmaniov1alpha1.BpfProgCondUnloaded) { - r.Logger.Info("Calling bpfman to unload program on node", "bpfProgram Name", bpfProgram.Name, "Program ID", id) + r.Logger.Info("Calling bpfman to unload program on node", "bpfProgram Name", bpfProgram.GetName(), "Program ID", id) if err := bpfmanagentinternal.UnloadBpfmanProgram(ctx, r.BpfmanClient, *id); err != nil { if strings.Contains(err.Error(), programDoesNotExistErr) { - r.Logger.Info("Program not found on node", "bpfProgram Name", bpfProgram.Name, "Program ID", id) + r.Logger.Info("Program not found on node", "bpfProgram Name", bpfProgram.GetName(), "Program ID", id) } else { r.Logger.Error(err, "Failed to unload Program") return ctrl.Result{RequeueAfter: retryDurationAgent}, nil } - if r.updateStatus(ctx, &bpfProgram, bpfmaniov1alpha1.BpfProgCondUnloaded) { + if r.updateStatus(ctx, rec, &bpfProgram, bpfmaniov1alpha1.BpfProgCondUnloaded) { return ctrl.Result{}, nil } } } - if r.updateStatus(ctx, &bpfProgram, bpfmaniov1alpha1.BpfProgCondUnloaded) { + if r.updateStatus(ctx, rec, &bpfProgram, bpfmaniov1alpha1.BpfProgCondUnloaded) { return ctrl.Result{}, nil } - if r.removeFinalizer(ctx, &bpfProgram, finalizerString) { + if r.removeFinalizer(ctx, bpfProgram.GetClientObject(), finalizerString) { return ctrl.Result{}, nil } opts := client.DeleteOptions{} - r.Logger.Info("Deleting bpfProgram", "Name", bpfProgram.Name, "Owner", bpfProgram.GetName()) - if err := r.Delete(ctx, &bpfProgram, &opts); err != nil { + r.Logger.Info("Deleting bpfProgram", "Name", bpfProgram.GetName(), "Owner", bpfProgram.GetName()) + if err := r.Delete(ctx, bpfProgram.GetClientObject(), &opts); err != nil { return ctrl.Result{RequeueAfter: retryDurationAgent}, fmt.Errorf("failed to delete bpfProgram object: %v", err) } else { // we will deal one program at a time, so we can break out of the loop @@ -720,11 +729,11 @@ func (r *ReconcilerCommon) unLoadAndDeleteBpfProgramsList(ctx context.Context, b // bpfPrograms. If a bpfProgram is expected but doesn't exist, it is created. // If an expected bpfProgram exists, it is reconciled. If a bpfProgram exists // but is not expected, it is deleted. -func (r *ReconcilerCommon) handleProgCreateOrUpdate( +func (r *ReconcilerCommon[T, TL]) handleProgCreateOrUpdate( ctx context.Context, - rec bpfmanReconciler, - existingBpfPrograms map[bpfProgKey]bpfmaniov1alpha1.BpfProgram, - expectedBpfPrograms *bpfmaniov1alpha1.BpfProgramList, + rec bpfmanReconciler[T, TL], + existingBpfPrograms map[bpfProgKey]T, + expectedBpfPrograms *TL, loadedBpfPrograms map[string]*gobpfman.ListResponse_ListResult, isNodeSelected bool, isBeingDeleted bool, @@ -734,13 +743,18 @@ func (r *ReconcilerCommon) handleProgCreateOrUpdate( isNodeSelected, "mapOwnerStatus", mapOwnerStatus) // If the *Program isn't being deleted ALWAYS create the bpfPrograms // even if the node isn't selected - for _, expectedBpfProgram := range expectedBpfPrograms.Items { - r.Logger.V(1).Info("Creating or Updating", "Name", expectedBpfProgram.Name) + for _, expectedBpfProgram := range (*expectedBpfPrograms).GetItems() { + r.Logger.V(1).Info("Creating or Updating", "Name", expectedBpfProgram.GetName()) key := bpfProgKey{ appProgId: expectedBpfProgram.GetLabels()[internal.AppProgramId], attachPoint: expectedBpfProgram.GetAnnotations()[internal.BpfProgramAttachPoint], } existingBpfProgram, exists := existingBpfPrograms[key] + r.Logger.Info("BILLY: Test for Exists", + "exists", exists, + "appProgId", expectedBpfProgram.GetLabels()[internal.AppProgramId], + "attachPoint", expectedBpfProgram.GetAnnotations()[internal.BpfProgramAttachPoint], + ) if exists { // Remove the bpfProgram from the existingPrograms map so we know // not to delete it below. @@ -748,8 +762,8 @@ func (r *ReconcilerCommon) handleProgCreateOrUpdate( } else { // Create a new bpfProgram Object for this program. opts := client.CreateOptions{} - r.Logger.Info("Creating bpfProgram", "Name", expectedBpfProgram.Name, "Owner", rec.getOwner().GetName()) - if err := r.Create(ctx, &expectedBpfProgram, &opts); err != nil { + r.Logger.Info("Creating bpfProgram", "Name", expectedBpfProgram.GetName(), "Owner", rec.getOwner().GetName()) + if err := r.Create(ctx, expectedBpfProgram.GetClientObject(), &opts); err != nil { return internal.Requeue, fmt.Errorf("failed to create bpfProgram object: %v", err) } return internal.Updated, nil @@ -766,7 +780,7 @@ func (r *ReconcilerCommon) handleProgCreateOrUpdate( mapOwnerStatus, ) if err != nil { - if r.updateStatus(ctx, &existingBpfProgram, cond) { + if r.updateStatus(ctx, rec, &existingBpfProgram, cond) { // Return an error the first time. return internal.Updated, fmt.Errorf("failed to reconcile bpfman program: %v", err) } @@ -777,7 +791,7 @@ func (r *ReconcilerCommon) handleProgCreateOrUpdate( cond == bpfmaniov1alpha1.BpfProgCondMapOwnerNotLoaded || cond == bpfmaniov1alpha1.BpfProgCondNoContainersOnNode { // Write NodeNodeSelected status - if r.updateStatus(ctx, &existingBpfProgram, cond) { + if r.updateStatus(ctx, rec, &existingBpfProgram, cond) { r.Logger.V(1).Info("Update condition from bpfman reconcile", "condition", cond) return internal.Updated, nil } else { @@ -788,20 +802,22 @@ func (r *ReconcilerCommon) handleProgCreateOrUpdate( // GetID() will fail if ProgramId is not in the annotations, which is expected on a // create. In this case existingId will be nil and DeepEqual() will fail and cause // annotation to be set. - existingId, _ := bpfmanagentinternal.GetID(&existingBpfProgram) + existingId, _ := GetID(&existingBpfProgram) // If bpfProgram Maps OR the program ID annotation isn't up to date just update it and return if !reflect.DeepEqual(existingId, r.progId) { - r.Logger.Info("Updating bpfProgram Object", "Id", r.progId, "bpfProgram", existingBpfProgram.Name) + r.Logger.Info("Updating bpfProgram Object", "Id", r.progId, "bpfProgram", existingBpfProgram.GetName()) // annotations should be populated on create - existingBpfProgram.Annotations[internal.IdAnnotation] = strconv.FormatUint(uint64(*r.progId), 10) - if err := r.Update(ctx, &existingBpfProgram, &client.UpdateOptions{}); err != nil { + annotations := existingBpfProgram.GetAnnotations() + annotations[internal.IdAnnotation] = strconv.FormatUint(uint64(*r.progId), 10) + + if err := r.Update(ctx, existingBpfProgram.GetClientObject(), &client.UpdateOptions{}); err != nil { return internal.Requeue, fmt.Errorf("failed to update bpfProgram's Programs: %v", err) } return internal.Updated, nil } - if r.updateStatus(ctx, &existingBpfProgram, cond) { + if r.updateStatus(ctx, rec, &existingBpfProgram, cond) { return internal.Updated, nil } } @@ -826,8 +842,8 @@ func (r *ReconcilerCommon) handleProgCreateOrUpdate( // against the K8s API. If the function returns a retry boolean and error, the // reconcile will be retried based on a default 5 second interval if the retry // boolean is set to `true`. -func (r *ReconcilerCommon) reconcileProgram(ctx context.Context, - rec bpfmanReconciler, +func (r *ReconcilerCommon[T, TL]) reconcileProgram(ctx context.Context, + rec bpfmanReconciler[T, TL], program client.Object, loadedBpfPrograms map[string]*gobpfman.ListResponse_ListResult) (internal.ReconcileResult, error) { @@ -851,7 +867,7 @@ func (r *ReconcilerCommon) reconcileProgram(ctx context.Context, // Determine if the MapOwnerSelector was set, and if so, see if the MapOwner // ID can be found. - mapOwnerStatus, err := r.processMapOwnerParam(ctx, &rec.getBpfProgramCommon().MapOwnerSelector) + mapOwnerStatus, err := r.processMapOwnerParam(ctx, rec, &rec.getBpfProgramCommon().MapOwnerSelector) if err != nil { return internal.Requeue, fmt.Errorf("failed to determine map owner: %v", err) } @@ -896,8 +912,9 @@ type MapOwnerParamStatus struct { // function returns the ID of the BpfProgram that owns the map on this node. // Found or not, this function also returns some flags (isSet, isFound, isLoaded) // to help with the processing and setting of the proper condition on the BpfProgram Object. -func (r *ReconcilerCommon) processMapOwnerParam( +func (r *ReconcilerCommon[T, TL]) processMapOwnerParam( ctx context.Context, + rec bpfmanReconciler[T, TL], selector *metav1.LabelSelector) (*MapOwnerParamStatus, error) { mapOwnerStatus := &MapOwnerParamStatus{} @@ -922,38 +939,38 @@ func (r *ReconcilerCommon) processMapOwnerParam( labelMap[key] = value } opts := []client.ListOption{labelMap} - bpfProgramList := &bpfmaniov1alpha1.BpfProgramList{} r.Logger.V(1).Info("MapOwner Labels:", "opts", opts) - err := r.List(ctx, bpfProgramList, opts...) + bpfProgramList, err := rec.getBpfList(ctx, opts) if err != nil { return mapOwnerStatus, err } // If no BpfProgram Objects were found, or more than one, then return. - if len(bpfProgramList.Items) == 0 { + items := (*bpfProgramList).GetItems() + if len(items) == 0 { return mapOwnerStatus, nil - } else if len(bpfProgramList.Items) > 1 { + } else if len(items) > 1 { return mapOwnerStatus, fmt.Errorf("MapOwnerSelector resolved to multiple bpfProgram Objects") } else { mapOwnerStatus.isFound = true // Get bpfProgram based on UID meta - prog, err := bpfmanagentinternal.GetBpfmanProgram(ctx, r.BpfmanClient, bpfProgramList.Items[0].GetUID()) + prog, err := bpfmanagentinternal.GetBpfmanProgram(ctx, r.BpfmanClient, items[0].GetUID()) if err != nil { - return nil, fmt.Errorf("failed to get bpfman program for BpfProgram with UID %s: %v", bpfProgramList.Items[0].GetUID(), err) + return nil, fmt.Errorf("failed to get bpfman program for BpfProgram with UID %s: %v", items[0].GetUID(), err) } kernelInfo := prog.GetKernelInfo() if kernelInfo == nil { - return nil, fmt.Errorf("failed to process bpfman program for BpfProgram with UID %s: %v", bpfProgramList.Items[0].GetUID(), err) + return nil, fmt.Errorf("failed to process bpfman program for BpfProgram with UID %s: %v", items[0].GetUID(), err) } mapOwnerStatus.mapOwnerId = &kernelInfo.Id // Get most recent condition from the one eBPF Program and determine // if the BpfProgram is loaded or not. - conLen := len(bpfProgramList.Items[0].Status.Conditions) + conLen := len(items[0].GetStatus().Conditions) if conLen > 0 && - bpfProgramList.Items[0].Status.Conditions[conLen-1].Type == + items[0].GetStatus().Conditions[conLen-1].Type == string(bpfmaniov1alpha1.BpfProgCondLoaded) { mapOwnerStatus.isLoaded = true } @@ -998,14 +1015,13 @@ func appProgramId(labels map[string]string) string { // getBpfProgram returns a BpfProgram object in the bpfProgram parameter based // on the given owner, appProgId, and attachPoint. If the BpfProgram is not // found, an error is returned. -func (r *ReconcilerCommon) getBpfProgram( +func (r *ReconcilerCommon[T, TL]) getBpfProgram( ctx context.Context, + rec bpfmanReconciler[T, TL], owner string, appProgId string, attachPoint string, - bpfProgram *bpfmaniov1alpha1.BpfProgram) error { - - bpfProgramList := &bpfmaniov1alpha1.BpfProgramList{} + bpfProgram *T) error { // Only list bpfPrograms for this *Program and the controller's node opts := []client.ListOption{ @@ -1016,12 +1032,12 @@ func (r *ReconcilerCommon) getBpfProgram( }, } - err := r.List(ctx, bpfProgramList, opts...) + bpfProgramList, err := rec.getBpfList(ctx, opts) if err != nil { return err } - for _, bpfProg := range bpfProgramList.Items { + for _, bpfProg := range (*bpfProgramList).GetItems() { if appProgId == bpfProg.GetLabels()[internal.AppProgramId] && attachPoint == bpfProg.GetAnnotations()[internal.BpfProgramAttachPoint] { *bpfProgram = bpfProg @@ -1031,3 +1047,19 @@ func (r *ReconcilerCommon) getBpfProgram( return fmt.Errorf("bpfProgram not found") } + +// get the program ID from a bpfProgram +func GetID[T BpfProg](p *T) (*uint32, error) { + annotations := (*p).GetAnnotations() + idString, ok := annotations[internal.IdAnnotation] + if !ok { + return nil, fmt.Errorf("failed to get program ID because no annotations") + } + + id, err := strconv.ParseUint(idString, 10, 32) + if err != nil { + return nil, fmt.Errorf("failed to parse program ID: %v", err) + } + uid := uint32(id) + return &uid, nil +} diff --git a/controllers/bpfman-agent/common_cluster.go b/controllers/bpfman-agent/common_cluster.go new file mode 100644 index 000000000..6e56649bb --- /dev/null +++ b/controllers/bpfman-agent/common_cluster.go @@ -0,0 +1,149 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + +package bpfmanagent + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + "github.com/bpfman/bpfman-operator/internal" +) + +type ClusterProgramReconciler struct { + ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] +} + +// createBpfProgram moves some shared logic for building bpfProgram objects +// into a central location. +func (r *ClusterProgramReconciler) createBpfProgram( + attachPoint string, + rec bpfmanReconciler[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList], + annotations map[string]string, +) (*bpfmaniov1alpha1.BpfProgram, error) { + + r.Logger.V(1).Info("createBpfProgram()", "Name", attachPoint, + "Owner", rec.getOwner().GetName(), "OwnerType", rec.getRecType(), "Name", rec.getName()) + + if annotations == nil { + annotations = make(map[string]string) + } + annotations[internal.BpfProgramAttachPoint] = attachPoint + + bpfProg := &bpfmaniov1alpha1.BpfProgram{ + ObjectMeta: metav1.ObjectMeta{ + Name: generateUniqueName(rec.getName()), + Finalizers: []string{rec.getFinalizer()}, + Labels: map[string]string{ + internal.BpfProgramOwner: rec.getOwner().GetName(), + internal.AppProgramId: rec.getAppProgramId(), + internal.K8sHostLabel: r.NodeName, + }, + Annotations: annotations, + }, + Spec: bpfmaniov1alpha1.BpfProgramSpec{ + Type: rec.getRecType(), + }, + Status: bpfmaniov1alpha1.BpfProgramStatus{Conditions: []metav1.Condition{}}, + } + + // Make the corresponding BpfProgramConfig the owner + if err := ctrl.SetControllerReference(rec.getOwner(), bpfProg, r.Scheme); err != nil { + return nil, fmt.Errorf("failed to bpfProgram object owner reference: %v", err) + } + + return bpfProg, nil +} + +/* +func (r *ClusterProgramReconciler) createBpfProgram( + owner metav1.Object, + attachPoint string, + recType string, + name string, + appProgramId string, + nodeName string, + finalizer string, + annotations map[string]string, + scheme *runtime.Scheme, + _namespace string, +) (*bpfmaniov1alpha1.BpfProgram, error) { + + r.Logger.V(1).Info("createBpfProgram()", "Name", attachPoint, + "Owner", owner.GetName(), "OwnerType", recType, "Name", name) + + if annotations == nil { + annotations = make(map[string]string) + } + annotations[internal.BpfProgramAttachPoint] = attachPoint + + bpfProg := &bpfmaniov1alpha1.BpfProgram{ + ObjectMeta: metav1.ObjectMeta{ + Name: generateUniqueName(name), + Finalizers: []string{finalizer}, + Labels: map[string]string{ + internal.BpfProgramOwner: owner.GetName(), + internal.AppProgramId: appProgramId, + internal.K8sHostLabel: nodeName, + }, + Annotations: annotations, + }, + Spec: bpfmaniov1alpha1.BpfProgramSpec{ + Type: recType, + }, + Status: bpfmaniov1alpha1.BpfProgramStatus{Conditions: []metav1.Condition{}}, + } + + // Make the corresponding BpfProgramConfig the owner + if err := ctrl.SetControllerReference(owner, bpfProg, scheme); err != nil { + return nil, fmt.Errorf("failed to bpfProgram object owner reference: %v", err) + } + + return bpfProg, nil +} +*/ + +func (r *ClusterProgramReconciler) getBpfList( + ctx context.Context, + opts []client.ListOption, +) (*bpfmaniov1alpha1.BpfProgramList, error) { + + bpfProgramList := &bpfmaniov1alpha1.BpfProgramList{} + + err := r.List(ctx, bpfProgramList, opts...) + if err != nil { + return nil, err + } + + return bpfProgramList, nil +} + +func (r *ClusterProgramReconciler) updateBpfStatus( + ctx context.Context, + bpfProgram *bpfmaniov1alpha1.BpfProgram, + condition metav1.Condition, +) error { + meta.SetStatusCondition(&bpfProgram.Status.Conditions, condition) + return r.Status().Update(ctx, bpfProgram) +} diff --git a/controllers/bpfman-agent/common_namespace.go b/controllers/bpfman-agent/common_namespace.go new file mode 100644 index 000000000..a3418d195 --- /dev/null +++ b/controllers/bpfman-agent/common_namespace.go @@ -0,0 +1,151 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + +package bpfmanagent + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + "github.com/bpfman/bpfman-operator/internal" +) + +type NamespaceProgramReconciler struct { + ReconcilerCommon[bpfmaniov1alpha1.BpfNsProgram, bpfmaniov1alpha1.BpfNsProgramList] +} + +// createBpfProgram moves some shared logic for building bpfProgram objects +// into a central location. +func (r *NamespaceProgramReconciler) createBpfProgram( + attachPoint string, + rec bpfmanReconciler[bpfmaniov1alpha1.BpfNsProgram, bpfmaniov1alpha1.BpfNsProgramList], + annotations map[string]string, +) (*bpfmaniov1alpha1.BpfNsProgram, error) { + + r.Logger.V(1).Info("createBpfNsProgram()", "Name", attachPoint, + "Owner", rec.getOwner().GetName(), "OwnerType", rec.getRecType(), "Name", rec.getName(), "Namespace", rec.getNamespace()) + + if annotations == nil { + annotations = make(map[string]string) + } + annotations[internal.BpfProgramAttachPoint] = attachPoint + + bpfProg := &bpfmaniov1alpha1.BpfNsProgram{ + ObjectMeta: metav1.ObjectMeta{ + Name: generateUniqueName(rec.getName()), + Namespace: rec.getNamespace(), + Finalizers: []string{rec.getFinalizer()}, + Labels: map[string]string{ + internal.BpfProgramOwner: rec.getOwner().GetName(), + internal.AppProgramId: rec.getAppProgramId(), + internal.K8sHostLabel: r.NodeName, + }, + Annotations: annotations, + }, + Spec: bpfmaniov1alpha1.BpfProgramSpec{ + Type: rec.getRecType(), + }, + Status: bpfmaniov1alpha1.BpfProgramStatus{Conditions: []metav1.Condition{}}, + } + + // Make the corresponding BpfProgramConfig the owner + if err := ctrl.SetControllerReference(rec.getOwner(), bpfProg, r.Scheme); err != nil { + return nil, fmt.Errorf("failed to bpfProgram object owner reference: %v", err) + } + + return bpfProg, nil +} + +/* +func (r *NamespaceProgramReconciler) createBpfProgram( + owner metav1.Object, + attachPoint string, + recType string, + name string, + appProgramId string, + nodeName string, + finalizer string, + annotations map[string]string, + scheme *runtime.Scheme, + namespace string, +) (*bpfmaniov1alpha1.BpfNsProgram, error) { + + r.Logger.V(1).Info("createBpfNsProgram()", "Name", attachPoint, + "Owner", owner.GetName(), "OwnerType", recType, "Name", name) + + if annotations == nil { + annotations = make(map[string]string) + } + annotations[internal.BpfProgramAttachPoint] = attachPoint + + bpfProg := &bpfmaniov1alpha1.BpfNsProgram{ + ObjectMeta: metav1.ObjectMeta{ + Name: generateUniqueName(name), + Namespace: namespace, + Finalizers: []string{finalizer}, + Labels: map[string]string{ + internal.BpfProgramOwner: owner.GetName(), + internal.AppProgramId: appProgramId, + internal.K8sHostLabel: nodeName, + }, + Annotations: annotations, + }, + Spec: bpfmaniov1alpha1.BpfProgramSpec{ + Type: recType, + }, + Status: bpfmaniov1alpha1.BpfProgramStatus{Conditions: []metav1.Condition{}}, + } + + // Make the corresponding BpfProgramConfig the owner + if err := ctrl.SetControllerReference(owner, bpfProg, scheme); err != nil { + return nil, fmt.Errorf("failed to bpfProgram object owner reference: %v", err) + } + + return bpfProg, nil +} +*/ + +func (r *NamespaceProgramReconciler) getBpfList( + ctx context.Context, + opts []client.ListOption, +) (*bpfmaniov1alpha1.BpfNsProgramList, error) { + + bpfProgramList := &bpfmaniov1alpha1.BpfNsProgramList{} + + err := r.List(ctx, bpfProgramList, opts...) + if err != nil { + return nil, err + } + + return bpfProgramList, nil +} + +func (r *NamespaceProgramReconciler) updateBpfStatus( + ctx context.Context, + bpfProgram *bpfmaniov1alpha1.BpfNsProgram, + condition metav1.Condition, +) error { + meta.SetStatusCondition(&bpfProgram.Status.Conditions, condition) + return r.Status().Update(ctx, bpfProgram) +} diff --git a/controllers/bpfman-agent/containers.go b/controllers/bpfman-agent/containers.go index 4edec2df1..5c2c24533 100644 --- a/controllers/bpfman-agent/containers.go +++ b/controllers/bpfman-agent/containers.go @@ -182,12 +182,13 @@ func getContainerInfo(podList *v1.PodList, containerNames *[]string, logger logr // Check if the annotation is set to indicate that no containers on this node // matched the container selector. -func noContainersOnNode(bpfProgram *bpfmaniov1alpha1.BpfProgram) bool { +func noContainersOnNode[T BpfProg](bpfProgram *T) bool { if bpfProgram == nil { return false } - noContainersOnNode, ok := bpfProgram.Annotations[internal.UprobeNoContainersOnNode] + annotations := (*bpfProgram).GetAnnotations() + noContainersOnNode, ok := annotations[internal.UprobeNoContainersOnNode] if ok && noContainersOnNode == "true" { return true } diff --git a/controllers/bpfman-agent/fentry-program.go b/controllers/bpfman-agent/fentry-program.go index 680693e1e..ed2cdae2e 100644 --- a/controllers/bpfman-agent/fentry-program.go +++ b/controllers/bpfman-agent/fentry-program.go @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanagent import ( @@ -40,7 +42,7 @@ import ( // BpfProgramReconciler reconciles a BpfProgram object type FentryProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler currentFentryProgram *bpfmaniov1alpha1.FentryProgram ourNode *v1.Node } @@ -69,6 +71,10 @@ func (r *FentryProgramReconciler) getName() string { return r.currentFentryProgram.Name } +func (r *FentryProgramReconciler) getNamespace() string { + return r.currentFentryProgram.Namespace +} + func (r *FentryProgramReconciler) getNode() *v1.Node { return r.ourNode } diff --git a/controllers/bpfman-agent/fentry-program_test.go b/controllers/bpfman-agent/fentry-program_test.go index 1a5384162..77c359add 100644 --- a/controllers/bpfman-agent/fentry-program_test.go +++ b/controllers/bpfman-agent/fentry-program_test.go @@ -24,7 +24,6 @@ import ( "google.golang.org/protobuf/testing/protocmp" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" - bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" agenttestutils "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal/test-utils" internal "github.com/bpfman/bpfman-operator/internal" testutils "github.com/bpfman/bpfman-operator/internal/test-utils" @@ -92,18 +91,22 @@ func TestFentryProgramControllerCreate(t *testing.T) { cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } + // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &FentryProgramReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &FentryProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -121,7 +124,7 @@ func TestFentryProgramControllerCreate(t *testing.T) { } // Check the BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.NotEmpty(t, bpfProg) @@ -170,11 +173,11 @@ func TestFentryProgramControllerCreate(t *testing.T) { } // Check that the bpfProgram's programs was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) // prog ID should already have been set - id, err := bpfmanagentinternal.GetID(bpfProg) + id, err := GetID(bpfProg) require.NoError(t, err) // Check the bpfLoadRequest was correctly Built @@ -194,7 +197,7 @@ func TestFentryProgramControllerCreate(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProg.Status.Conditions[0].Type) diff --git a/controllers/bpfman-agent/fexit-program.go b/controllers/bpfman-agent/fexit-program.go index b52171e80..a6709f568 100644 --- a/controllers/bpfman-agent/fexit-program.go +++ b/controllers/bpfman-agent/fexit-program.go @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanagent import ( @@ -40,7 +42,7 @@ import ( // BpfProgramReconciler reconciles a BpfProgram object type FexitProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler currentFexitProgram *bpfmaniov1alpha1.FexitProgram ourNode *v1.Node } @@ -69,6 +71,10 @@ func (r *FexitProgramReconciler) getName() string { return r.currentFexitProgram.Name } +func (r *FexitProgramReconciler) getNamespace() string { + return r.currentFexitProgram.Namespace +} + func (r *FexitProgramReconciler) getNode() *v1.Node { return r.ourNode } diff --git a/controllers/bpfman-agent/fexit-program_test.go b/controllers/bpfman-agent/fexit-program_test.go index a424384d5..976075bf0 100644 --- a/controllers/bpfman-agent/fexit-program_test.go +++ b/controllers/bpfman-agent/fexit-program_test.go @@ -24,7 +24,6 @@ import ( "google.golang.org/protobuf/testing/protocmp" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" - bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" agenttestutils "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal/test-utils" internal "github.com/bpfman/bpfman-operator/internal" testutils "github.com/bpfman/bpfman-operator/internal/test-utils" @@ -92,18 +91,21 @@ func TestFexitProgramControllerCreate(t *testing.T) { cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &FexitProgramReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &FexitProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -121,7 +123,7 @@ func TestFexitProgramControllerCreate(t *testing.T) { } // Check the BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.NotEmpty(t, bpfProg) @@ -170,11 +172,11 @@ func TestFexitProgramControllerCreate(t *testing.T) { } // Check that the bpfProgram's programs was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) // prog ID should already have been set - id, err := bpfmanagentinternal.GetID(bpfProg) + id, err := GetID(bpfProg) require.NoError(t, err) // Check the bpfLoadRequest was correctly Built @@ -194,7 +196,7 @@ func TestFexitProgramControllerCreate(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProg.Status.Conditions[0].Type) diff --git a/controllers/bpfman-agent/internal/bpfman-core.go b/controllers/bpfman-agent/internal/bpfman-core.go index 864e453b3..dba971845 100644 --- a/controllers/bpfman-agent/internal/bpfman-core.go +++ b/controllers/bpfman-agent/internal/bpfman-core.go @@ -19,7 +19,6 @@ package internal import ( "context" "fmt" - "strconv" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" "github.com/bpfman/bpfman-operator/internal" @@ -206,9 +205,11 @@ func Build_kernel_info_annotations(p *gobpfman.ListResponse_ListResult) map[stri return nil } +/* // get the program ID from a bpfProgram -func GetID(p *bpfmaniov1alpha1.BpfProgram) (*uint32, error) { - idString, ok := p.Annotations[internal.IdAnnotation] +func GetID[T bpfmanagent.BpfProg](p *T) (*uint32, error) { + annotations := (*p).GetAnnotations() + idString, ok := annotations[internal.IdAnnotation] if !ok { return nil, fmt.Errorf("failed to get program ID because no annotations") } @@ -220,3 +221,4 @@ func GetID(p *bpfmaniov1alpha1.BpfProgram) (*uint32, error) { uid := uint32(id) return &uid, nil } +*/ diff --git a/controllers/bpfman-agent/kprobe-program.go b/controllers/bpfman-agent/kprobe-program.go index 89e912b70..92f60a729 100644 --- a/controllers/bpfman-agent/kprobe-program.go +++ b/controllers/bpfman-agent/kprobe-program.go @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanagent import ( @@ -40,7 +42,7 @@ import ( // BpfProgramReconciler reconciles a BpfProgram object type KprobeProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler currentKprobeProgram *bpfmaniov1alpha1.KprobeProgram ourNode *v1.Node } @@ -69,6 +71,10 @@ func (r *KprobeProgramReconciler) getName() string { return r.currentKprobeProgram.Name } +func (r *KprobeProgramReconciler) getNamespace() string { + return r.currentKprobeProgram.Namespace +} + func (r *KprobeProgramReconciler) getNode() *v1.Node { return r.ourNode } diff --git a/controllers/bpfman-agent/kprobe-program_test.go b/controllers/bpfman-agent/kprobe-program_test.go index 96f1a3744..a1b5fdb86 100644 --- a/controllers/bpfman-agent/kprobe-program_test.go +++ b/controllers/bpfman-agent/kprobe-program_test.go @@ -24,7 +24,6 @@ import ( "google.golang.org/protobuf/testing/protocmp" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" - bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" agenttestutils "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal/test-utils" internal "github.com/bpfman/bpfman-operator/internal" testutils "github.com/bpfman/bpfman-operator/internal/test-utils" @@ -97,18 +96,21 @@ func TestKprobeProgramControllerCreate(t *testing.T) { cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &KprobeProgramReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &KprobeProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -126,7 +128,7 @@ func TestKprobeProgramControllerCreate(t *testing.T) { } // Check the BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.NotEmpty(t, bpfProg) @@ -178,11 +180,11 @@ func TestKprobeProgramControllerCreate(t *testing.T) { } // Check that the bpfProgram's programs was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) // prog ID should already have been set - id, err := bpfmanagentinternal.GetID(bpfProg) + id, err := GetID(bpfProg) require.NoError(t, err) // Check the bpfLoadRequest was correctly Built @@ -202,7 +204,7 @@ func TestKprobeProgramControllerCreate(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProg.Status.Conditions[0].Type) diff --git a/controllers/bpfman-agent/tc-program.go b/controllers/bpfman-agent/tc-program.go index 03515dd05..2a2244a19 100644 --- a/controllers/bpfman-agent/tc-program.go +++ b/controllers/bpfman-agent/tc-program.go @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanagent import ( @@ -41,7 +43,7 @@ import ( // TcProgramReconciler reconciles a tcProgram object by creating multiple // bpfProgram objects and managing bpfman for each one. type TcProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler currentTcProgram *bpfmaniov1alpha1.TcProgram interfaces []string ourNode *v1.Node @@ -71,6 +73,10 @@ func (r *TcProgramReconciler) getName() string { return r.currentTcProgram.Name } +func (r *TcProgramReconciler) getNamespace() string { + return r.currentTcProgram.Namespace +} + func (r *TcProgramReconciler) getNode() *v1.Node { return r.ourNode } @@ -180,6 +186,9 @@ func (r *TcProgramReconciler) getExpectedBpfPrograms(ctx context.Context) (*bpfm if err != nil { return nil, fmt.Errorf("failed to create BpfProgram %s: %v", attachPoint, err) } + r.Logger.Info("BILLY-TC: getExpectedBpfPrograms()", + "prog", prog, + ) progs.Items = append(progs.Items, *prog) } diff --git a/controllers/bpfman-agent/tc-program_test.go b/controllers/bpfman-agent/tc-program_test.go index a1b711843..c2d2590e2 100644 --- a/controllers/bpfman-agent/tc-program_test.go +++ b/controllers/bpfman-agent/tc-program_test.go @@ -24,7 +24,6 @@ import ( "google.golang.org/protobuf/testing/protocmp" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" - bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" agenttestutils "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal/test-utils" testutils "github.com/bpfman/bpfman-operator/internal/test-utils" @@ -102,18 +101,21 @@ func TestTcProgramControllerCreate(t *testing.T) { cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &TcProgramReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &TcProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -131,7 +133,7 @@ func TestTcProgramControllerCreate(t *testing.T) { } // Check the BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.NotEmpty(t, bpfProg) @@ -183,11 +185,11 @@ func TestTcProgramControllerCreate(t *testing.T) { } // Check that the bpfProgram's programs was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) // prog ID should already have been set - id, err := bpfmanagentinternal.GetID(bpfProg) + id, err := GetID(bpfProg) require.NoError(t, err) // Check the bpfLoadRequest was correctly built @@ -206,7 +208,7 @@ func TestTcProgramControllerCreate(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProg.Status.Conditions[0].Type) @@ -274,18 +276,21 @@ func TestTcProgramControllerCreateMultiIntf(t *testing.T) { cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &TcProgramReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &TcProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -303,7 +308,7 @@ func TestTcProgramControllerCreateMultiIntf(t *testing.T) { } // Check the first BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) require.NotEmpty(t, bpfProgEth0) @@ -351,7 +356,7 @@ func TestTcProgramControllerCreateMultiIntf(t *testing.T) { require.False(t, res.Requeue) // Check the Second BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) require.NotEmpty(t, bpfProgEth1) @@ -429,19 +434,19 @@ func TestTcProgramControllerCreateMultiIntf(t *testing.T) { } // Check that the bpfProgram's maps was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) // prog ID should already have been set - id0, err := bpfmanagentinternal.GetID(bpfProgEth0) + id0, err := GetID(bpfProgEth0) require.NoError(t, err) // Check that the bpfProgram's maps was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) // prog ID should already have been set - id1, err := bpfmanagentinternal.GetID(bpfProgEth1) + id1, err := GetID(bpfProgEth1) require.NoError(t, err) // Check the bpfLoadRequest was correctly built @@ -457,11 +462,11 @@ func TestTcProgramControllerCreateMultiIntf(t *testing.T) { } // Check that the bpfProgram's maps was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) // Check that the bpfProgram's maps was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) // Third reconcile should update the bpfPrograms status to loaded @@ -474,13 +479,13 @@ func TestTcProgramControllerCreateMultiIntf(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProgEth0.Status.Conditions[0].Type) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProgEth1.Status.Conditions[0].Type) diff --git a/controllers/bpfman-agent/tcx-program.go b/controllers/bpfman-agent/tcx-program.go index 788db969e..a6317f9e5 100644 --- a/controllers/bpfman-agent/tcx-program.go +++ b/controllers/bpfman-agent/tcx-program.go @@ -14,11 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanagent import ( "context" "fmt" + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" "github.com/bpfman/bpfman-operator/internal" @@ -40,7 +43,7 @@ import ( // TcxProgramReconciler reconciles a tcxProgram object by creating multiple // bpfProgram objects and managing bpfman for each one. type TcxProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler currentTcxProgram *bpfmaniov1alpha1.TcxProgram interfaces []string ourNode *v1.Node @@ -70,6 +73,10 @@ func (r *TcxProgramReconciler) getName() string { return r.currentTcxProgram.Name } +func (r *TcxProgramReconciler) getNamespace() string { + return r.currentTcxProgram.Namespace +} + func (r *TcxProgramReconciler) getNode() *v1.Node { return r.ourNode } diff --git a/controllers/bpfman-agent/tcx-program_test.go b/controllers/bpfman-agent/tcx-program_test.go index 090d640de..a5ebee61c 100644 --- a/controllers/bpfman-agent/tcx-program_test.go +++ b/controllers/bpfman-agent/tcx-program_test.go @@ -24,7 +24,6 @@ import ( "google.golang.org/protobuf/testing/protocmp" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" - bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" agenttestutils "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal/test-utils" testutils "github.com/bpfman/bpfman-operator/internal/test-utils" @@ -98,18 +97,21 @@ func TestTcxProgramControllerCreate(t *testing.T) { cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &TcxProgramReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &TcxProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -127,7 +129,7 @@ func TestTcxProgramControllerCreate(t *testing.T) { } // Check the BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.NotEmpty(t, bpfProg) @@ -178,11 +180,11 @@ func TestTcxProgramControllerCreate(t *testing.T) { } // Check that the bpfProgram's programs was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) // prog ID should already have been set - id, err := bpfmanagentinternal.GetID(bpfProg) + id, err := GetID(bpfProg) require.NoError(t, err) // Check the bpfLoadRequest was correctly built @@ -201,7 +203,7 @@ func TestTcxProgramControllerCreate(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProg.Status.Conditions[0].Type) @@ -265,18 +267,21 @@ func TestTcxProgramControllerCreateMultiIntf(t *testing.T) { cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &TcxProgramReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &TcxProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -294,7 +299,7 @@ func TestTcxProgramControllerCreateMultiIntf(t *testing.T) { } // Check the first BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) require.NotEmpty(t, bpfProgEth0) @@ -342,7 +347,7 @@ func TestTcxProgramControllerCreateMultiIntf(t *testing.T) { require.False(t, res.Requeue) // Check the Second BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) require.NotEmpty(t, bpfProgEth1) @@ -418,19 +423,19 @@ func TestTcxProgramControllerCreateMultiIntf(t *testing.T) { } // Check that the bpfProgram's maps was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) // prog ID should already have been set - id0, err := bpfmanagentinternal.GetID(bpfProgEth0) + id0, err := GetID(bpfProgEth0) require.NoError(t, err) // Check that the bpfProgram's maps was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) // prog ID should already have been set - id1, err := bpfmanagentinternal.GetID(bpfProgEth1) + id1, err := GetID(bpfProgEth1) require.NoError(t, err) // Check the bpfLoadRequest was correctly built @@ -446,11 +451,11 @@ func TestTcxProgramControllerCreateMultiIntf(t *testing.T) { } // Check that the bpfProgram's maps was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) // Check that the bpfProgram's maps was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) // Third reconcile should update the bpfPrograms status to loaded @@ -463,13 +468,13 @@ func TestTcxProgramControllerCreateMultiIntf(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProgEth0.Status.Conditions[0].Type) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProgEth1.Status.Conditions[0].Type) diff --git a/controllers/bpfman-agent/tracepoint-program.go b/controllers/bpfman-agent/tracepoint-program.go index dfafff528..7bd384136 100644 --- a/controllers/bpfman-agent/tracepoint-program.go +++ b/controllers/bpfman-agent/tracepoint-program.go @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanagent import ( @@ -40,7 +42,7 @@ import ( // BpfProgramReconciler reconciles a BpfProgram object type TracepointProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler ourNode *v1.Node currentTracepointProgram *bpfmaniov1alpha1.TracepointProgram } @@ -69,6 +71,10 @@ func (r *TracepointProgramReconciler) getName() string { return r.currentTracepointProgram.Name } +func (r *TracepointProgramReconciler) getNamespace() string { + return r.currentTracepointProgram.Namespace +} + func (r *TracepointProgramReconciler) getNode() *v1.Node { return r.ourNode } diff --git a/controllers/bpfman-agent/tracepoint-program_test.go b/controllers/bpfman-agent/tracepoint-program_test.go index 3455f72a4..e12a5329e 100644 --- a/controllers/bpfman-agent/tracepoint-program_test.go +++ b/controllers/bpfman-agent/tracepoint-program_test.go @@ -24,7 +24,6 @@ import ( "google.golang.org/protobuf/testing/protocmp" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" - bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" agenttestutils "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal/test-utils" internal "github.com/bpfman/bpfman-operator/internal" testutils "github.com/bpfman/bpfman-operator/internal/test-utils" @@ -92,18 +91,21 @@ func TestTracepointProgramControllerCreate(t *testing.T) { cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &TracepointProgramReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &TracepointProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -121,7 +123,7 @@ func TestTracepointProgramControllerCreate(t *testing.T) { } // Check the BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.NotEmpty(t, bpfProg) @@ -170,11 +172,11 @@ func TestTracepointProgramControllerCreate(t *testing.T) { } // Check that the bpfProgram's programs was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) // prog ID should already have been set - id, err := bpfmanagentinternal.GetID(bpfProg) + id, err := GetID(bpfProg) require.NoError(t, err) // Check the bpfLoadRequest was correctly Built @@ -184,7 +186,7 @@ func TestTracepointProgramControllerCreate(t *testing.T) { } // Check that the bpfProgram's programs was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) // Third reconcile should update the bpfPrograms status to loaded @@ -197,7 +199,7 @@ func TestTracepointProgramControllerCreate(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProg.Status.Conditions[0].Type) diff --git a/controllers/bpfman-agent/uprobe-program.go b/controllers/bpfman-agent/uprobe-program.go index 7e03b3fd7..e9dfdcebd 100644 --- a/controllers/bpfman-agent/uprobe-program.go +++ b/controllers/bpfman-agent/uprobe-program.go @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanagent import ( @@ -41,7 +43,7 @@ import ( // BpfProgramReconciler reconciles a BpfProgram object type UprobeProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler currentUprobeProgram *bpfmaniov1alpha1.UprobeProgram ourNode *v1.Node } @@ -70,6 +72,10 @@ func (r *UprobeProgramReconciler) getName() string { return r.currentUprobeProgram.Name } +func (r *UprobeProgramReconciler) getNamespace() string { + return r.currentUprobeProgram.Namespace +} + func (r *UprobeProgramReconciler) getNode() *v1.Node { return r.ourNode } diff --git a/controllers/bpfman-agent/uprobe-program_test.go b/controllers/bpfman-agent/uprobe-program_test.go index fbcd48a98..facfa8cb0 100644 --- a/controllers/bpfman-agent/uprobe-program_test.go +++ b/controllers/bpfman-agent/uprobe-program_test.go @@ -24,7 +24,6 @@ import ( "google.golang.org/protobuf/testing/protocmp" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" - bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" agenttestutils "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal/test-utils" internal "github.com/bpfman/bpfman-operator/internal" testutils "github.com/bpfman/bpfman-operator/internal/test-utils" @@ -100,18 +99,21 @@ func TestUprobeProgramControllerCreate(t *testing.T) { cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &UprobeProgramReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &UprobeProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -129,7 +131,7 @@ func TestUprobeProgramControllerCreate(t *testing.T) { } // Check the BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.NotEmpty(t, bpfProg) @@ -181,11 +183,11 @@ func TestUprobeProgramControllerCreate(t *testing.T) { } // Check that the bpfProgram's programs was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) // prog ID should already have been set - id, err := bpfmanagentinternal.GetID(bpfProg) + id, err := GetID(bpfProg) require.NoError(t, err) // Check the bpfLoadRequest was correctly Built @@ -205,7 +207,7 @@ func TestUprobeProgramControllerCreate(t *testing.T) { require.False(t, res.Requeue) // Check that the bpfProgram's status was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId, attachPoint, bpfProg) + err = rc.getBpfProgram(ctx, r, name, appProgramId, attachPoint, bpfProg) require.NoError(t, err) require.Equal(t, string(bpfmaniov1alpha1.BpfProgCondLoaded), bpfProg.Status.Conditions[0].Type) diff --git a/controllers/bpfman-agent/xdp-ns-program.go b/controllers/bpfman-agent/xdp-ns-program.go new file mode 100644 index 000000000..58a8a95b0 --- /dev/null +++ b/controllers/bpfman-agent/xdp-ns-program.go @@ -0,0 +1,230 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + +package bpfmanagent + +import ( + "context" + "fmt" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" + internal "github.com/bpfman/bpfman-operator/internal" + gobpfman "github.com/bpfman/bpfman/clients/gobpfman/v1" + + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +//+kubebuilder:rbac:groups=bpfman.io,resources=xdpnsprograms,verbs=get;list;watch +//+kubebuilder:rbac:groups=bpfman.io,namespace=bpfman,resources=xdpnsprograms,verbs=get;list;watch + +// BpfProgramReconciler reconciles a BpfNsProgram object +type XdpNsProgramReconciler struct { + NamespaceProgramReconciler + currentXdpNsProgram *bpfmaniov1alpha1.XdpNsProgram + interfaces []string + ourNode *v1.Node +} + +func (r *XdpNsProgramReconciler) getFinalizer() string { + return r.finalizer +} + +func (r *XdpNsProgramReconciler) getOwner() metav1.Object { + if r.appOwner == nil { + return r.currentXdpNsProgram + } else { + return r.appOwner + } +} + +func (r *XdpNsProgramReconciler) getRecType() string { + return r.recType +} + +func (r *XdpNsProgramReconciler) getProgType() internal.ProgramType { + return internal.Xdp +} + +func (r *XdpNsProgramReconciler) getName() string { + return r.currentXdpNsProgram.Name +} + +func (r *XdpNsProgramReconciler) getNamespace() string { + return r.currentXdpNsProgram.Namespace +} + +func (r *XdpNsProgramReconciler) getNode() *v1.Node { + return r.ourNode +} + +func (r *XdpNsProgramReconciler) getBpfProgramCommon() *bpfmaniov1alpha1.BpfProgramCommon { + return &r.currentXdpNsProgram.Spec.BpfProgramCommon +} + +func (r *XdpNsProgramReconciler) getNodeSelector() *metav1.LabelSelector { + return &r.currentXdpNsProgram.Spec.NodeSelector +} + +func (r *XdpNsProgramReconciler) getBpfGlobalData() map[string][]byte { + return r.currentXdpNsProgram.Spec.GlobalData +} + +func (r *XdpNsProgramReconciler) getAppProgramId() string { + return appProgramId(r.currentXdpNsProgram.GetLabels()) +} + +func (r *XdpNsProgramReconciler) setCurrentProgram(program client.Object) error { + var err error + var ok bool + + r.currentXdpNsProgram, ok = program.(*bpfmaniov1alpha1.XdpNsProgram) + if !ok { + return fmt.Errorf("failed to cast program to XdpNsProgram") + } + + r.interfaces, err = getInterfaces(&r.currentXdpNsProgram.Spec.InterfaceSelector, r.ourNode) + if err != nil { + return fmt.Errorf("failed to get interfaces for XdpNsProgram: %v", err) + } + + return nil +} + +// SetupWithManager sets up the controller with the Manager. +// The Bpfman-Agent should reconcile whenever a XdpNsProgram is updated, +// load the program to the node via bpfman, and then create a bpfProgram object +// to reflect per node state information. +func (r *XdpNsProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&bpfmaniov1alpha1.XdpNsProgram{}, + builder.WithPredicates(predicate.And( + predicate.GenerationChangedPredicate{}, + predicate.ResourceVersionChangedPredicate{}), + ), + ). + Owns(&bpfmaniov1alpha1.BpfNsProgram{}, + builder.WithPredicates(predicate.And( + internal.BpfNsProgramTypePredicate(internal.XdpNsString), + internal.BpfProgramNodePredicate(r.NodeName)), + ), + ). + // Only trigger reconciliation if node labels change since that could + // make the XdpNsProgram no longer select the Node. Additionally only + // care about node events specific to our node + Watches( + &v1.Node{}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(predicate.And(predicate.LabelChangedPredicate{}, nodePredicate(r.NodeName))), + ). + Complete(r) +} + +func (r *XdpNsProgramReconciler) getExpectedBpfPrograms(ctx context.Context) (*bpfmaniov1alpha1.BpfNsProgramList, error) { + progs := &bpfmaniov1alpha1.BpfNsProgramList{} + + for _, iface := range r.interfaces { + attachPoint := iface + annotations := map[string]string{internal.XdpNsProgramInterface: iface} + + prog, err := r.createBpfProgram(attachPoint, r, annotations) + if err != nil { + return nil, fmt.Errorf("failed to create BpfNsProgram %s: %v", attachPoint, err) + } + + progs.Items = append(progs.Items, *prog) + } + + return progs, nil +} + +func (r *XdpNsProgramReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + // Initialize node and current program + r.currentXdpNsProgram = &bpfmaniov1alpha1.XdpNsProgram{} + r.finalizer = internal.XdpNsProgramControllerFinalizer + r.recType = internal.Xdp.String() + r.ourNode = &v1.Node{} + r.Logger = ctrl.Log.WithName("xdp-ns") + + ctxLogger := log.FromContext(ctx) + ctxLogger.Info("Reconcile XDP-NS: Enter", "ReconcileKey", req) + + // Lookup K8s node object for this bpfman-agent This should always succeed + if err := r.Get(ctx, types.NamespacedName{Namespace: v1.NamespaceAll, Name: r.NodeName}, r.ourNode); err != nil { + return ctrl.Result{Requeue: false}, fmt.Errorf("failed getting bpfman-agent node %s : %v", + req.NamespacedName, err) + } + + xdpPrograms := &bpfmaniov1alpha1.XdpNsProgramList{} + opts := []client.ListOption{} + + if err := r.List(ctx, xdpPrograms, opts...); err != nil { + return ctrl.Result{Requeue: false}, fmt.Errorf("failed getting XdpNsPrograms for full reconcile %s : %v", + req.NamespacedName, err) + } + + if len(xdpPrograms.Items) == 0 { + r.Logger.Info("XdpNsProgramController found no XDP Programs") + return ctrl.Result{Requeue: false}, nil + } + + // Create a list of Xdp programs to pass into reconcileCommon() + var xdpObjects []client.Object = make([]client.Object, len(xdpPrograms.Items)) + for i := range xdpPrograms.Items { + xdpObjects[i] = &xdpPrograms.Items[i] + } + + // Reconcile each TcProgram. + _, result, err := r.reconcileCommon(ctx, r, xdpObjects) + return result, err +} + +func (r *XdpNsProgramReconciler) getLoadRequest(bpfProgram *bpfmaniov1alpha1.BpfNsProgram, mapOwnerId *uint32) (*gobpfman.LoadRequest, error) { + bytecode, err := bpfmanagentinternal.GetBytecode(r.Client, &r.currentXdpNsProgram.Spec.ByteCode) + if err != nil { + return nil, fmt.Errorf("failed to process bytecode selector: %v", err) + } + + loadRequest := gobpfman.LoadRequest{ + Bytecode: bytecode, + Name: r.currentXdpNsProgram.Spec.BpfFunctionName, + ProgramType: uint32(internal.Xdp), + Attach: &gobpfman.AttachInfo{ + Info: &gobpfman.AttachInfo_XdpAttachInfo{ + XdpAttachInfo: &gobpfman.XDPAttachInfo{ + Priority: r.currentXdpNsProgram.Spec.Priority, + Iface: bpfProgram.Annotations[internal.XdpNsProgramInterface], + ProceedOn: xdpProceedOnToInt(r.currentXdpNsProgram.Spec.ProceedOn), + }, + }, + }, + Metadata: map[string]string{internal.UuidMetadataKey: string(bpfProgram.UID), internal.ProgramNameKey: r.getOwner().GetName()}, + GlobalData: r.currentXdpNsProgram.Spec.GlobalData, + MapOwnerId: mapOwnerId, + } + + return &loadRequest, nil +} diff --git a/controllers/bpfman-agent/xdp-program.go b/controllers/bpfman-agent/xdp-program.go index 72859a4b2..ff2072493 100644 --- a/controllers/bpfman-agent/xdp-program.go +++ b/controllers/bpfman-agent/xdp-program.go @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanagent import ( @@ -40,7 +42,7 @@ import ( // BpfProgramReconciler reconciles a BpfProgram object type XdpProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler currentXdpProgram *bpfmaniov1alpha1.XdpProgram interfaces []string ourNode *v1.Node @@ -70,6 +72,10 @@ func (r *XdpProgramReconciler) getName() string { return r.currentXdpProgram.Name } +func (r *XdpProgramReconciler) getNamespace() string { + return r.currentXdpProgram.Namespace +} + func (r *XdpProgramReconciler) getNode() *v1.Node { return r.ourNode } diff --git a/controllers/bpfman-agent/xdp-program_test.go b/controllers/bpfman-agent/xdp-program_test.go index 922cd28e4..dc5449e07 100644 --- a/controllers/bpfman-agent/xdp-program_test.go +++ b/controllers/bpfman-agent/xdp-program_test.go @@ -24,7 +24,6 @@ import ( "google.golang.org/protobuf/testing/protocmp" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" - bpfmanagentinternal "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal" agenttestutils "github.com/bpfman/bpfman-operator/controllers/bpfman-agent/internal/test-utils" internal "github.com/bpfman/bpfman-operator/internal" testutils "github.com/bpfman/bpfman-operator/internal/test-utils" @@ -115,18 +114,21 @@ func xdpProgramControllerCreate(t *testing.T, multiInterface bool, multiConditio cli := agenttestutils.NewBpfmanClientFake() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, BpfmanClient: cli, NodeName: fakeNode.Name, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &XdpProgramReconciler{ReconcilerCommon: rc, ourNode: fakeNode} + r := &XdpProgramReconciler{ClusterProgramReconciler: cpr, ourNode: fakeNode} // Mock request to simulate Reconcile() being called on an event for a // watched resource . @@ -144,7 +146,7 @@ func xdpProgramControllerCreate(t *testing.T, multiInterface bool, multiConditio } // Check the first BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) require.NotEmpty(t, bpfProgEth0) @@ -195,11 +197,11 @@ func xdpProgramControllerCreate(t *testing.T, multiInterface bool, multiConditio } // Check that the bpfProgram's maps was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) // prog ID should already have been set - id0, err := bpfmanagentinternal.GetID(bpfProgEth0) + id0, err := GetID(bpfProgEth0) require.NoError(t, err) // Check the bpfLoadRequest was correctly built @@ -235,7 +237,7 @@ func xdpProgramControllerCreate(t *testing.T, multiInterface bool, multiConditio require.False(t, res.Requeue) // Get program object - err = rc.getBpfProgram(ctx, name, appProgramId0, attachPoint0, bpfProgEth0) + err = rc.getBpfProgram(ctx, r, name, appProgramId0, attachPoint0, bpfProgEth0) require.NoError(t, err) // Check that the bpfProgram's status was correctly updated @@ -255,7 +257,7 @@ func xdpProgramControllerCreate(t *testing.T, multiInterface bool, multiConditio require.False(t, res.Requeue) // Check the Second BpfProgram Object was created successfully - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) require.NotEmpty(t, bpfProgEth1) @@ -315,11 +317,11 @@ func xdpProgramControllerCreate(t *testing.T, multiInterface bool, multiConditio } // Check that the bpfProgram's maps was correctly updated - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) // prog ID should already have been set - id1, err := bpfmanagentinternal.GetID(bpfProgEth1) + id1, err := GetID(bpfProgEth1) require.NoError(t, err) // Check the bpfLoadRequest was correctly built @@ -329,7 +331,7 @@ func xdpProgramControllerCreate(t *testing.T, multiInterface bool, multiConditio } // Get program object - err = rc.getBpfProgram(ctx, name, appProgramId1, attachPoint1, bpfProgEth1) + err = rc.getBpfProgram(ctx, r, name, appProgramId1, attachPoint1, bpfProgEth1) require.NoError(t, err) // Check that the bpfProgram's status was correctly updated diff --git a/controllers/bpfman-operator/application-program_test.go b/controllers/bpfman-operator/application-program_test.go index 76fc0770f..a2f3d2a25 100644 --- a/controllers/bpfman-operator/application-program_test.go +++ b/controllers/bpfman-operator/application-program_test.go @@ -135,16 +135,20 @@ func appProgramReconcile(t *testing.T, multiCondition bool) { // Create a fake client to mock API calls. cl := fake.NewClientBuilder().WithStatusSubresource(App).WithRuntimeObjects(objs...).Build() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } + // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ApplicationProgram object with the scheme and fake client. - r := &BpfApplicationReconciler{ReconcilerCommon: rc} + r := &BpfApplicationReconciler{ClusterProgramReconciler: cpr} // Mock request to simulate Reconcile() being called on an event for a // watched resource . diff --git a/controllers/bpfman-operator/application-programs.go b/controllers/bpfman-operator/application-programs.go index 0001c3430..8af4af4a7 100644 --- a/controllers/bpfman-operator/application-programs.go +++ b/controllers/bpfman-operator/application-programs.go @@ -41,13 +41,15 @@ import ( // BpfApplicationReconciler reconciles a BpfApplication object type BpfApplicationReconciler struct { - ReconcilerCommon + ClusterProgramReconciler } -func (r *BpfApplicationReconciler) getRecCommon() *ReconcilerCommon { - return &r.ReconcilerCommon +//lint:ignore U1000 Linter claims function unused, but generics confusing linter +func (r *BpfApplicationReconciler) getRecCommon() *ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] { + return &r.ClusterProgramReconciler.ReconcilerCommon } +//lint:ignore U1000 Linter claims function unused, but generics confusing linter func (r *BpfApplicationReconciler) getFinalizer() string { return internal.BpfApplicationControllerFinalizer } @@ -60,7 +62,7 @@ func (r *BpfApplicationReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &bpfmaniov1alpha1.BpfProgram{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(predicate.And(statusChangedPredicate(), internal.BpfProgramTypePredicate(internal.ApplicationString))), + builder.WithPredicates(predicate.And(statusChangedPredicateCluster(), internal.BpfProgramTypePredicate(internal.ApplicationString))), ). Complete(r) } @@ -106,7 +108,8 @@ func (r *BpfApplicationReconciler) Reconcile(ctx context.Context, req ctrl.Reque return reconcileBpfProgram(ctx, r, appProgram) } -func (r *BpfApplicationReconciler) updateStatus(ctx context.Context, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { +//lint:ignore U1000 Linter claims function unused, but generics confusing linter +func (r *BpfApplicationReconciler) updateStatus(ctx context.Context, _namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { // Sometimes we end up with a stale FentryProgram due to races, do this // get to ensure we're up to date before attempting a status update. app := &bpfmaniov1alpha1.BpfApplication{} diff --git a/controllers/bpfman-operator/common.go b/controllers/bpfman-operator/common.go index 69e89efa1..6c81e9e87 100644 --- a/controllers/bpfman-operator/common.go +++ b/controllers/bpfman-operator/common.go @@ -19,7 +19,6 @@ package bpfmanoperator import ( "context" "fmt" - "reflect" "time" corev1 "k8s.io/api/core/v1" @@ -29,8 +28,6 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" internal "github.com/bpfman/bpfman-operator/internal" @@ -39,47 +36,73 @@ import ( ) //+kubebuilder:rbac:groups=bpfman.io,resources=bpfprograms,verbs=get;list;watch +//+kubebuilder:rbac:groups=bpfman.io,resources=bpfnsprograms,verbs=get;list;watch +//+kubebuilder:rbac:groups=bpfman.io,namespace=bpfman,resources=bpfnsprograms,verbs=get;list;watch //+kubebuilder:rbac:groups=core,resources=nodes,verbs=get;list;watch const ( retryDurationOperator = 5 * time.Second ) +type BpfProgOper interface { + GetName() string + + GetLabels() map[string]string + GetStatus() *bpfmaniov1alpha1.BpfProgramStatus +} + +type BpfProgListOper[T any] interface { + // bpfmniov1alpha1.BpfProgramList | bpfmaniov1alpha1.BpfNsProgramList + + GetItems() []T +} + // ReconcilerCommon reconciles a BpfProgram object -type ReconcilerCommon struct { +type ReconcilerCommon[T BpfProgOper, TL BpfProgListOper[T]] struct { client.Client Scheme *runtime.Scheme Logger logr.Logger } // bpfmanReconciler defines a k8s reconciler which can program bpfman. -type ProgramReconciler interface { - getRecCommon() *ReconcilerCommon +type ProgramReconciler[T BpfProgOper, TL BpfProgListOper[T]] interface { + // BPF Cluster of Namespaced Reconciler + getBpfList(ctx context.Context, + progName string, + progNamespace string, + ) (*TL, error) + containsFinalizer(bpfProgram *T, finalizer string) bool + + // *Program Reconciler + getRecCommon() *ReconcilerCommon[T, TL] updateStatus(ctx context.Context, + namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) getFinalizer() string } -func reconcileBpfProgram(ctx context.Context, rec ProgramReconciler, prog client.Object) (ctrl.Result, error) { +func reconcileBpfProgram[T BpfProgOper, TL BpfProgListOper[T]]( + ctx context.Context, + rec ProgramReconciler[T, TL], + prog client.Object, +) (ctrl.Result, error) { r := rec.getRecCommon() progName := prog.GetName() + progNamespace := prog.GetNamespace() - r.Logger.V(1).Info("Reconciling Program", "ProgramName", progName) + r.Logger.V(1).Info("Reconciling Program", "Namespace", progNamespace, "ProgramName", progName) if !controllerutil.ContainsFinalizer(prog, internal.BpfmanOperatorFinalizer) { + r.Logger.V(1).Info("Add Finalizer", "Namespace", progNamespace, "ProgramName", progName) return r.addFinalizer(ctx, prog, internal.BpfmanOperatorFinalizer) } // reconcile Program Object on all other events // list all existing bpfProgram state for the given Program - bpfPrograms := &bpfmaniov1alpha1.BpfProgramList{} - - // Only list bpfPrograms for this Program - opts := []client.ListOption{client.MatchingLabels{internal.BpfProgramOwner: progName}} - - if err := r.List(ctx, bpfPrograms, opts...); err != nil { + bpfPrograms, err := rec.getBpfList(ctx, progName, progNamespace) + if err != nil { r.Logger.Error(err, "failed to get freshPrograms for full reconcile") return ctrl.Result{}, nil } @@ -96,7 +119,7 @@ func reconcileBpfProgram(ctx context.Context, rec ProgramReconciler, prog client if prog.GetDeletionTimestamp().IsZero() { for _, node := range nodes.Items { nodeFound := false - for _, program := range bpfPrograms.Items { + for _, program := range (*bpfPrograms).GetItems() { bpfProgramNode := program.GetLabels()[internal.K8sHostLabel] if node.Name == bpfProgramNode { nodeFound = true @@ -104,7 +127,7 @@ func reconcileBpfProgram(ctx context.Context, rec ProgramReconciler, prog client } } if !nodeFound { - return rec.updateStatus(ctx, progName, bpfmaniov1alpha1.ProgramNotYetLoaded, "") + return rec.updateStatus(ctx, progNamespace, progName, bpfmaniov1alpha1.ProgramNotYetLoaded, "") } } } @@ -112,14 +135,15 @@ func reconcileBpfProgram(ctx context.Context, rec ProgramReconciler, prog client failedBpfPrograms := []string{} finalApplied := []string{} // Make sure no bpfPrograms had any issues in the loading or unloading process - for _, bpfProgram := range bpfPrograms.Items { + for _, bpfProgram := range (*bpfPrograms).GetItems() { - if controllerutil.ContainsFinalizer(&bpfProgram, rec.getFinalizer()) { - finalApplied = append(finalApplied, bpfProgram.Name) + if rec.containsFinalizer(&bpfProgram, rec.getFinalizer()) { + finalApplied = append(finalApplied, bpfProgram.GetName()) } - if bpfmanHelpers.IsBpfProgramConditionFailure(&bpfProgram.Status.Conditions) { - failedBpfPrograms = append(failedBpfPrograms, bpfProgram.Name) + status := bpfProgram.GetStatus() + if bpfmanHelpers.IsBpfProgramConditionFailure(&status.Conditions) { + failedBpfPrograms = append(failedBpfPrograms, bpfProgram.GetName()) } } @@ -132,21 +156,21 @@ func reconcileBpfProgram(ctx context.Context, rec ProgramReconciler, prog client } // Causes Requeue - return rec.updateStatus(ctx, progName, bpfmaniov1alpha1.ProgramDeleteError, fmt.Sprintf("Program Deletion failed on the following bpfProgram Objects: %v", - finalApplied)) + return rec.updateStatus(ctx, progNamespace, progName, bpfmaniov1alpha1.ProgramDeleteError, + fmt.Sprintf("Program Deletion failed on the following bpfProgram Objects: %v", finalApplied)) } if len(failedBpfPrograms) != 0 { // Causes Requeue - return rec.updateStatus(ctx, progName, bpfmaniov1alpha1.ProgramReconcileError, + return rec.updateStatus(ctx, progNamespace, progName, bpfmaniov1alpha1.ProgramReconcileError, fmt.Sprintf("bpfProgramReconciliation failed on the following bpfProgram Objects: %v", failedBpfPrograms)) } // Causes Requeue - return rec.updateStatus(ctx, progName, bpfmaniov1alpha1.ProgramReconcileSuccess, "") + return rec.updateStatus(ctx, progNamespace, progName, bpfmaniov1alpha1.ProgramReconcileSuccess, "") } -func (r *ReconcilerCommon) removeFinalizer(ctx context.Context, prog client.Object, finalizer string) (ctrl.Result, error) { +func (r *ReconcilerCommon[T, TL]) removeFinalizer(ctx context.Context, prog client.Object, finalizer string) (ctrl.Result, error) { r.Logger.V(1).Info("Program is deleted remove finalizer", "ProgramName", prog.GetName()) if changed := controllerutil.RemoveFinalizer(prog, finalizer); changed { @@ -160,7 +184,7 @@ func (r *ReconcilerCommon) removeFinalizer(ctx context.Context, prog client.Obje return ctrl.Result{}, nil } -func (r *ReconcilerCommon) addFinalizer(ctx context.Context, prog client.Object, finalizer string) (ctrl.Result, error) { +func (r *ReconcilerCommon[T, TL]) addFinalizer(ctx context.Context, prog client.Object, finalizer string) (ctrl.Result, error) { controllerutil.AddFinalizer(prog, finalizer) err := r.Update(ctx, prog) @@ -172,27 +196,13 @@ func (r *ReconcilerCommon) addFinalizer(ctx context.Context, prog client.Object, return ctrl.Result{}, nil } -// Only reconcile if a bpfprogram object's status has been updated. -func statusChangedPredicate() predicate.Funcs { - return predicate.Funcs{ - GenericFunc: func(e event.GenericEvent) bool { - return false - }, - CreateFunc: func(e event.CreateEvent) bool { - return false - }, - UpdateFunc: func(e event.UpdateEvent) bool { - oldObject := e.ObjectOld.(*bpfmaniov1alpha1.BpfProgram) - newObject := e.ObjectNew.(*bpfmaniov1alpha1.BpfProgram) - return !reflect.DeepEqual(oldObject.Status, newObject.Status) - }, - DeleteFunc: func(e event.DeleteEvent) bool { - return false - }, - } -} - -func (r *ReconcilerCommon) updateCondition(ctx context.Context, obj client.Object, conditions *[]metav1.Condition, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { +func (r *ReconcilerCommon[T, TL]) updateCondition( + ctx context.Context, + obj client.Object, + conditions *[]metav1.Condition, + cond bpfmaniov1alpha1.ProgramConditionType, + message string, +) (ctrl.Result, error) { r.Logger.V(1).Info("updateCondition()", "existing conds", conditions, "new cond", cond) diff --git a/controllers/bpfman-operator/common_cluster.go b/controllers/bpfman-operator/common_cluster.go new file mode 100644 index 000000000..1e1a6cb06 --- /dev/null +++ b/controllers/bpfman-operator/common_cluster.go @@ -0,0 +1,83 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bpfmanoperator + +import ( + "context" + "reflect" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + internal "github.com/bpfman/bpfman-operator/internal" +) + +type ClusterProgramReconciler struct { + ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] +} + +//lint:ignore U1000 Linter claims function unused, but generics confusing linter +func (r *ClusterProgramReconciler) getBpfList( + ctx context.Context, + progName string, + _progNamespace string, +) (*bpfmaniov1alpha1.BpfProgramList, error) { + + bpfProgramList := &bpfmaniov1alpha1.BpfProgramList{} + + // Only list bpfPrograms for this Program + opts := []client.ListOption{ + client.MatchingLabels{internal.BpfProgramOwner: progName}, + } + + err := r.List(ctx, bpfProgramList, opts...) + if err != nil { + return nil, err + } + + return bpfProgramList, nil +} + +//lint:ignore U1000 Linter claims function unused, but generics confusing linter +func (r *ClusterProgramReconciler) containsFinalizer( + bpfProgram *bpfmaniov1alpha1.BpfProgram, + finalizer string, +) bool { + return controllerutil.ContainsFinalizer(bpfProgram, finalizer) +} + +func statusChangedPredicateCluster() predicate.Funcs { + return predicate.Funcs{ + GenericFunc: func(e event.GenericEvent) bool { + return false + }, + CreateFunc: func(e event.CreateEvent) bool { + return false + }, + UpdateFunc: func(e event.UpdateEvent) bool { + oldObject := e.ObjectOld.(*bpfmaniov1alpha1.BpfProgram) + newObject := e.ObjectNew.(*bpfmaniov1alpha1.BpfProgram) + return !reflect.DeepEqual(oldObject.GetStatus(), newObject.Status) + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return false + }, + } +} diff --git a/controllers/bpfman-operator/common_namespace.go b/controllers/bpfman-operator/common_namespace.go new file mode 100644 index 000000000..a3dc65677 --- /dev/null +++ b/controllers/bpfman-operator/common_namespace.go @@ -0,0 +1,84 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bpfmanoperator + +import ( + "context" + "reflect" + + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + internal "github.com/bpfman/bpfman-operator/internal" +) + +type NamespaceProgramReconciler struct { + ReconcilerCommon[bpfmaniov1alpha1.BpfNsProgram, bpfmaniov1alpha1.BpfNsProgramList] +} + +//lint:ignore U1000 Linter claims function unused, but generics confusing linter +func (r *NamespaceProgramReconciler) getBpfList( + ctx context.Context, + progName string, + progNamespace string, +) (*bpfmaniov1alpha1.BpfNsProgramList, error) { + + bpfProgramList := &bpfmaniov1alpha1.BpfNsProgramList{} + + // Only list bpfPrograms for this Program + opts := []client.ListOption{ + client.MatchingLabels{internal.BpfProgramOwner: progName}, + client.InNamespace(progNamespace), + } + + err := r.List(ctx, bpfProgramList, opts...) + if err != nil { + return nil, err + } + + return bpfProgramList, nil +} + +//lint:ignore U1000 Linter claims function unused, but generics confusing linter +func (r *NamespaceProgramReconciler) containsFinalizer( + bpfProgram *bpfmaniov1alpha1.BpfNsProgram, + finalizer string, +) bool { + return controllerutil.ContainsFinalizer(bpfProgram, finalizer) +} + +func statusChangedPredicateNamespace() predicate.Funcs { + return predicate.Funcs{ + GenericFunc: func(e event.GenericEvent) bool { + return false + }, + CreateFunc: func(e event.CreateEvent) bool { + return false + }, + UpdateFunc: func(e event.UpdateEvent) bool { + oldObject := e.ObjectOld.(*bpfmaniov1alpha1.BpfNsProgram) + newObject := e.ObjectNew.(*bpfmaniov1alpha1.BpfNsProgram) + return !reflect.DeepEqual(oldObject.GetStatus(), newObject.Status) + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return false + }, + } +} diff --git a/controllers/bpfman-operator/configmap.go b/controllers/bpfman-operator/configmap.go index 4049f3814..c55b8791b 100644 --- a/controllers/bpfman-operator/configmap.go +++ b/controllers/bpfman-operator/configmap.go @@ -49,7 +49,7 @@ import ( // +kubebuilder:rbac:groups=bpfman.io,resources=configmaps/finalizers,verbs=update type BpfmanConfigReconciler struct { - ReconcilerCommon + ClusterProgramReconciler BpfmanStandardDeployment string CsiDriverDeployment string RestrictedSCC string diff --git a/controllers/bpfman-operator/configmap_test.go b/controllers/bpfman-operator/configmap_test.go index 8bb153eae..9b1bbb7c0 100644 --- a/controllers/bpfman-operator/configmap_test.go +++ b/controllers/bpfman-operator/configmap_test.go @@ -40,6 +40,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/reconcile" + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" "github.com/bpfman/bpfman-operator/internal" ) @@ -92,10 +93,19 @@ func setupTestEnvironment(isOpenShift bool) (*BpfmanConfigReconciler, *corev1.Co // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ + Client: cl, + Scheme: s, + } + + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } + // Create a BpfmanConfigReconciler object with the scheme and // fake client. r := &BpfmanConfigReconciler{ - ReconcilerCommon: ReconcilerCommon{Client: cl, Scheme: s}, + ClusterProgramReconciler: cpr, BpfmanStandardDeployment: resolveConfigPath(internal.BpfmanDaemonManifestPath), CsiDriverDeployment: resolveConfigPath(internal.BpfmanCsiDriverPath), IsOpenshift: isOpenShift, diff --git a/controllers/bpfman-operator/fentry-program.go b/controllers/bpfman-operator/fentry-program.go index 68f7d3c3b..00430f1ee 100644 --- a/controllers/bpfman-operator/fentry-program.go +++ b/controllers/bpfman-operator/fentry-program.go @@ -11,6 +11,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanoperator import ( @@ -36,11 +38,11 @@ import ( //+kubebuilder:rbac:groups=bpfman.io,resources=fentryprograms/finalizers,verbs=update type FentryProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler } -func (r *FentryProgramReconciler) getRecCommon() *ReconcilerCommon { - return &r.ReconcilerCommon +func (r *FentryProgramReconciler) getRecCommon() *ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] { + return &r.ClusterProgramReconciler.ReconcilerCommon } func (r *FentryProgramReconciler) getFinalizer() string { @@ -55,7 +57,7 @@ func (r *FentryProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &bpfmaniov1alpha1.BpfProgram{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(predicate.And(statusChangedPredicate(), internal.BpfProgramTypePredicate(internal.FentryString))), + builder.WithPredicates(predicate.And(statusChangedPredicateCluster(), internal.BpfProgramTypePredicate(internal.FentryString))), ). Complete(r) } @@ -101,7 +103,7 @@ func (r *FentryProgramReconciler) Reconcile(ctx context.Context, req ctrl.Reques return reconcileBpfProgram(ctx, r, fentryProgram) } -func (r *FentryProgramReconciler) updateStatus(ctx context.Context, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { +func (r *FentryProgramReconciler) updateStatus(ctx context.Context, _namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { // Sometimes we end up with a stale FentryProgram due to races, do this // get to ensure we're up to date before attempting a status update. prog := &bpfmaniov1alpha1.FentryProgram{} diff --git a/controllers/bpfman-operator/fentry-program_test.go b/controllers/bpfman-operator/fentry-program_test.go index 1ffd18af7..335894de0 100644 --- a/controllers/bpfman-operator/fentry-program_test.go +++ b/controllers/bpfman-operator/fentry-program_test.go @@ -105,16 +105,19 @@ func fentryProgramReconcile(t *testing.T, multiCondition bool) { // Create a fake client to mock API calls. cl := fake.NewClientBuilder().WithStatusSubresource(Fentry).WithRuntimeObjects(objs...).Build() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a FentryProgram object with the scheme and fake client. - r := &FentryProgramReconciler{ReconcilerCommon: rc} + r := &FentryProgramReconciler{ClusterProgramReconciler: cpr} // Mock request to simulate Reconcile() being called on an event for a // watched resource . diff --git a/controllers/bpfman-operator/fexit-program.go b/controllers/bpfman-operator/fexit-program.go index 4578bd0d6..3d3cecc46 100644 --- a/controllers/bpfman-operator/fexit-program.go +++ b/controllers/bpfman-operator/fexit-program.go @@ -11,6 +11,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanoperator import ( @@ -36,11 +38,11 @@ import ( //+kubebuilder:rbac:groups=bpfman.io,resources=fexitprograms/finalizers,verbs=update type FexitProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler } -func (r *FexitProgramReconciler) getRecCommon() *ReconcilerCommon { - return &r.ReconcilerCommon +func (r *FexitProgramReconciler) getRecCommon() *ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] { + return &r.ClusterProgramReconciler.ReconcilerCommon } func (r *FexitProgramReconciler) getFinalizer() string { @@ -55,7 +57,7 @@ func (r *FexitProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &bpfmaniov1alpha1.BpfProgram{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(predicate.And(statusChangedPredicate(), internal.BpfProgramTypePredicate(internal.FexitString))), + builder.WithPredicates(predicate.And(statusChangedPredicateCluster(), internal.BpfProgramTypePredicate(internal.FexitString))), ). Complete(r) } @@ -101,7 +103,7 @@ func (r *FexitProgramReconciler) Reconcile(ctx context.Context, req ctrl.Request return reconcileBpfProgram(ctx, r, fexitProgram) } -func (r *FexitProgramReconciler) updateStatus(ctx context.Context, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { +func (r *FexitProgramReconciler) updateStatus(ctx context.Context, _namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { // Sometimes we end up with a stale FexitProgram due to races, do this // get to ensure we're up to date before attempting a status update. prog := &bpfmaniov1alpha1.FexitProgram{} diff --git a/controllers/bpfman-operator/fexit-program_test.go b/controllers/bpfman-operator/fexit-program_test.go index 086e30d9d..d2077ece4 100644 --- a/controllers/bpfman-operator/fexit-program_test.go +++ b/controllers/bpfman-operator/fexit-program_test.go @@ -105,16 +105,20 @@ func fexitProgramReconcile(t *testing.T, multiCondition bool) { // Create a fake client to mock API calls. cl := fake.NewClientBuilder().WithStatusSubresource(Fexit).WithRuntimeObjects(objs...).Build() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } + // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a FexitProgram object with the scheme and fake client. - r := &FexitProgramReconciler{ReconcilerCommon: rc} + r := &FexitProgramReconciler{ClusterProgramReconciler: cpr} // Mock request to simulate Reconcile() being called on an event for a // watched resource . diff --git a/controllers/bpfman-operator/kprobe-program.go b/controllers/bpfman-operator/kprobe-program.go index f36af3c1c..ad30f9079 100644 --- a/controllers/bpfman-operator/kprobe-program.go +++ b/controllers/bpfman-operator/kprobe-program.go @@ -11,6 +11,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanoperator import ( @@ -36,11 +38,11 @@ import ( //+kubebuilder:rbac:groups=bpfman.io,resources=kprobeprograms/finalizers,verbs=update type KprobeProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler } -func (r *KprobeProgramReconciler) getRecCommon() *ReconcilerCommon { - return &r.ReconcilerCommon +func (r *KprobeProgramReconciler) getRecCommon() *ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] { + return &r.ClusterProgramReconciler.ReconcilerCommon } func (r *KprobeProgramReconciler) getFinalizer() string { @@ -55,7 +57,7 @@ func (r *KprobeProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &bpfmaniov1alpha1.BpfProgram{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(predicate.And(statusChangedPredicate(), internal.BpfProgramTypePredicate(internal.Kprobe.String()))), + builder.WithPredicates(predicate.And(statusChangedPredicateCluster(), internal.BpfProgramTypePredicate(internal.Kprobe.String()))), ). Complete(r) } @@ -101,7 +103,7 @@ func (r *KprobeProgramReconciler) Reconcile(ctx context.Context, req ctrl.Reques return reconcileBpfProgram(ctx, r, kprobeProgram) } -func (r *KprobeProgramReconciler) updateStatus(ctx context.Context, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { +func (r *KprobeProgramReconciler) updateStatus(ctx context.Context, _namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { // Sometimes we end up with a stale KprobeProgram due to races, do this // get to ensure we're up to date before attempting a status update. prog := &bpfmaniov1alpha1.KprobeProgram{} diff --git a/controllers/bpfman-operator/kprobe-program_test.go b/controllers/bpfman-operator/kprobe-program_test.go index 5216fbd77..c0864dc58 100644 --- a/controllers/bpfman-operator/kprobe-program_test.go +++ b/controllers/bpfman-operator/kprobe-program_test.go @@ -109,16 +109,20 @@ func kprobeProgramReconcile(t *testing.T, multiCondition bool) { // Create a fake client to mock API calls. cl := fake.NewClientBuilder().WithStatusSubresource(Kprobe).WithRuntimeObjects(objs...).Build() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } + // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a KprobeProgram object with the scheme and fake client. - r := &KprobeProgramReconciler{ReconcilerCommon: rc} + r := &KprobeProgramReconciler{ClusterProgramReconciler: cpr} // Mock request to simulate Reconcile() being called on an event for a // watched resource . diff --git a/controllers/bpfman-operator/tc-program.go b/controllers/bpfman-operator/tc-program.go index f3bc673c7..d7a99b278 100644 --- a/controllers/bpfman-operator/tc-program.go +++ b/controllers/bpfman-operator/tc-program.go @@ -11,6 +11,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanoperator import ( @@ -32,11 +34,11 @@ import ( ) type TcProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler } -func (r *TcProgramReconciler) getRecCommon() *ReconcilerCommon { - return &r.ReconcilerCommon +func (r *TcProgramReconciler) getRecCommon() *ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] { + return &r.ClusterProgramReconciler.ReconcilerCommon } func (r *TcProgramReconciler) getFinalizer() string { @@ -51,7 +53,7 @@ func (r *TcProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &bpfmaniov1alpha1.BpfProgram{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(predicate.And(statusChangedPredicate(), internal.BpfProgramTypePredicate(internal.Tc.String()))), + builder.WithPredicates(predicate.And(statusChangedPredicateCluster(), internal.BpfProgramTypePredicate(internal.Tc.String()))), ). Complete(r) } @@ -101,7 +103,7 @@ func (r *TcProgramReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return reconcileBpfProgram(ctx, r, tcProgram) } -func (r *TcProgramReconciler) updateStatus(ctx context.Context, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { +func (r *TcProgramReconciler) updateStatus(ctx context.Context, _namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { // Sometimes we end up with a stale TcProgram due to races, do this // get to ensure we're up to date before attempting a finalizer removal. prog := &bpfmaniov1alpha1.TcProgram{} diff --git a/controllers/bpfman-operator/tc-program_test.go b/controllers/bpfman-operator/tc-program_test.go index 032a4d7f8..8226e95a5 100644 --- a/controllers/bpfman-operator/tc-program_test.go +++ b/controllers/bpfman-operator/tc-program_test.go @@ -112,16 +112,20 @@ func TestTcProgramReconcile(t *testing.T) { // Create a fake client to mock API calls. cl := fake.NewClientBuilder().WithStatusSubresource(tc).WithRuntimeObjects(objs...).Build() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } + // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &TcProgramReconciler{ReconcilerCommon: rc} + r := &TcProgramReconciler{ClusterProgramReconciler: cpr} // Mock request to simulate Reconcile() being called on an event for a // watched resource . diff --git a/controllers/bpfman-operator/tcx-program.go b/controllers/bpfman-operator/tcx-program.go index 05a9925fd..fcbac1f0f 100644 --- a/controllers/bpfman-operator/tcx-program.go +++ b/controllers/bpfman-operator/tcx-program.go @@ -11,6 +11,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanoperator import ( @@ -32,11 +34,11 @@ import ( ) type TcxProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler } -func (r *TcxProgramReconciler) getRecCommon() *ReconcilerCommon { - return &r.ReconcilerCommon +func (r *TcxProgramReconciler) getRecCommon() *ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] { + return &r.ClusterProgramReconciler.ReconcilerCommon } func (r *TcxProgramReconciler) getFinalizer() string { @@ -51,7 +53,7 @@ func (r *TcxProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &bpfmaniov1alpha1.BpfProgram{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(predicate.And(statusChangedPredicate(), internal.BpfProgramTypePredicate(internal.TcxString))), + builder.WithPredicates(predicate.And(statusChangedPredicateCluster(), internal.BpfProgramTypePredicate(internal.TcxString))), ). Complete(r) } @@ -101,7 +103,7 @@ func (r *TcxProgramReconciler) Reconcile(ctx context.Context, req ctrl.Request) return reconcileBpfProgram(ctx, r, tcxProgram) } -func (r *TcxProgramReconciler) updateStatus(ctx context.Context, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { +func (r *TcxProgramReconciler) updateStatus(ctx context.Context, _namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { // Sometimes we end up with a stale TcxProgram due to races, do this // get to ensure we're up to date before attempting a finalizer removal. prog := &bpfmaniov1alpha1.TcxProgram{} diff --git a/controllers/bpfman-operator/tcx-program_test.go b/controllers/bpfman-operator/tcx-program_test.go index 5623e995f..1a042b320 100644 --- a/controllers/bpfman-operator/tcx-program_test.go +++ b/controllers/bpfman-operator/tcx-program_test.go @@ -108,16 +108,20 @@ func TestTcxProgramReconcile(t *testing.T) { // Create a fake client to mock API calls. cl := fake.NewClientBuilder().WithStatusSubresource(tcx).WithRuntimeObjects(objs...).Build() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } + // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &TcxProgramReconciler{ReconcilerCommon: rc} + r := &TcxProgramReconciler{ClusterProgramReconciler: cpr} // Mock request to simulate Reconcile() being called on an event for a // watched resource . diff --git a/controllers/bpfman-operator/tracepoint-program.go b/controllers/bpfman-operator/tracepoint-program.go index 84025f074..5869bbbd7 100644 --- a/controllers/bpfman-operator/tracepoint-program.go +++ b/controllers/bpfman-operator/tracepoint-program.go @@ -11,6 +11,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanoperator import ( @@ -36,11 +38,11 @@ import ( //+kubebuilder:rbac:groups=bpfman.io,resources=tracepointprograms/finalizers,verbs=update type TracepointProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler } -func (r *TracepointProgramReconciler) getRecCommon() *ReconcilerCommon { - return &r.ReconcilerCommon +func (r *TracepointProgramReconciler) getRecCommon() *ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] { + return &r.ClusterProgramReconciler.ReconcilerCommon } func (r *TracepointProgramReconciler) getFinalizer() string { @@ -55,7 +57,7 @@ func (r *TracepointProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &bpfmaniov1alpha1.BpfProgram{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(predicate.And(statusChangedPredicate(), internal.BpfProgramTypePredicate(internal.Tracepoint.String()))), + builder.WithPredicates(predicate.And(statusChangedPredicateCluster(), internal.BpfProgramTypePredicate(internal.Tracepoint.String()))), ). Complete(r) } @@ -101,7 +103,7 @@ func (r *TracepointProgramReconciler) Reconcile(ctx context.Context, req ctrl.Re return reconcileBpfProgram(ctx, r, tracepointProgram) } -func (r *TracepointProgramReconciler) updateStatus(ctx context.Context, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { +func (r *TracepointProgramReconciler) updateStatus(ctx context.Context, _namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { // Sometimes we end up with a stale TracepointProgram due to races, do this // get to ensure we're up to date before attempting a finalizer removal. prog := &bpfmaniov1alpha1.TracepointProgram{} diff --git a/controllers/bpfman-operator/tracepoint-program_test.go b/controllers/bpfman-operator/tracepoint-program_test.go index 8cac0a367..7b3e2756d 100644 --- a/controllers/bpfman-operator/tracepoint-program_test.go +++ b/controllers/bpfman-operator/tracepoint-program_test.go @@ -102,16 +102,20 @@ func TestTracepointProgramReconcile(t *testing.T) { // Create a fake client to mock API calls. cl := fake.NewClientBuilder().WithStatusSubresource(Tracepoint).WithRuntimeObjects(objs...).Build() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } + // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a TracepointProgram object with the scheme and fake client. - r := &TracepointProgramReconciler{ReconcilerCommon: rc} + r := &TracepointProgramReconciler{ClusterProgramReconciler: cpr} // Mock request to simulate Reconcile() being called on an event for a // watched resource . diff --git a/controllers/bpfman-operator/uprobe-program.go b/controllers/bpfman-operator/uprobe-program.go index 16616202d..f6bbab412 100644 --- a/controllers/bpfman-operator/uprobe-program.go +++ b/controllers/bpfman-operator/uprobe-program.go @@ -11,6 +11,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanoperator import ( @@ -36,11 +38,11 @@ import ( //+kubebuilder:rbac:groups=bpfman.io,resources=uprobeprograms/finalizers,verbs=update type UprobeProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler } -func (r *UprobeProgramReconciler) getRecCommon() *ReconcilerCommon { - return &r.ReconcilerCommon +func (r *UprobeProgramReconciler) getRecCommon() *ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] { + return &r.ClusterProgramReconciler.ReconcilerCommon } func (r *UprobeProgramReconciler) getFinalizer() string { @@ -55,7 +57,7 @@ func (r *UprobeProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &bpfmaniov1alpha1.BpfProgram{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(predicate.And(statusChangedPredicate(), internal.BpfProgramTypePredicate(internal.UprobeString))), + builder.WithPredicates(predicate.And(statusChangedPredicateCluster(), internal.BpfProgramTypePredicate(internal.UprobeString))), ). Complete(r) } @@ -101,7 +103,7 @@ func (r *UprobeProgramReconciler) Reconcile(ctx context.Context, req ctrl.Reques return reconcileBpfProgram(ctx, r, uprobeProgram) } -func (r *UprobeProgramReconciler) updateStatus(ctx context.Context, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { +func (r *UprobeProgramReconciler) updateStatus(ctx context.Context, _namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { // Sometimes we end up with a stale UprobeProgram due to races, do this // get to ensure we're up to date before attempting a status update. prog := &bpfmaniov1alpha1.UprobeProgram{} diff --git a/controllers/bpfman-operator/uprobe-program_test.go b/controllers/bpfman-operator/uprobe-program_test.go index d591165fd..cb13972e7 100644 --- a/controllers/bpfman-operator/uprobe-program_test.go +++ b/controllers/bpfman-operator/uprobe-program_test.go @@ -108,16 +108,20 @@ func TestUprobeProgramReconcile(t *testing.T) { // Create a fake client to mock API calls. cl := fake.NewClientBuilder().WithStatusSubresource(Uprobe).WithRuntimeObjects(objs...).Build() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } + // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a UprobeProgram object with the scheme and fake client. - r := &UprobeProgramReconciler{ReconcilerCommon: rc} + r := &UprobeProgramReconciler{ClusterProgramReconciler: cpr} // Mock request to simulate Reconcile() being called on an event for a // watched resource . diff --git a/controllers/bpfman-operator/xdp-ns-program.go b/controllers/bpfman-operator/xdp-ns-program.go new file mode 100644 index 000000000..aed1b9dcc --- /dev/null +++ b/controllers/bpfman-operator/xdp-ns-program.go @@ -0,0 +1,125 @@ +/* +Copyright 2024. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + +package bpfmanoperator + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + + bpfmaniov1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + "github.com/bpfman/bpfman-operator/internal" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +//+kubebuilder:rbac:groups=bpfman.io,resources=xdpnsprograms,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=bpfman.io,resources=xdpnsprograms/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=bpfman.io,resources=xdpnsprograms/finalizers,verbs=update +//+kubebuilder:rbac:groups=bpfman.io,namespace=bpfman,resources=xdpnsprograms,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=bpfman.io,namespace=bpfman,resources=xdpnsprograms/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=bpfman.io,namespace=bpfman,resources=xdpnsprograms/finalizers,verbs=update + +type XdpNsProgramReconciler struct { + NamespaceProgramReconciler +} + +func (r *XdpNsProgramReconciler) getRecCommon() *ReconcilerCommon[bpfmaniov1alpha1.BpfNsProgram, bpfmaniov1alpha1.BpfNsProgramList] { + return &r.NamespaceProgramReconciler.ReconcilerCommon +} + +func (r *XdpNsProgramReconciler) getFinalizer() string { + return internal.XdpNsProgramControllerFinalizer +} + +// SetupWithManager sets up the controller with the Manager. +func (r *XdpNsProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&bpfmaniov1alpha1.XdpNsProgram{}). + // Watch bpfPrograms which are owned by XdpNsPrograms + Watches( + &bpfmaniov1alpha1.BpfNsProgram{}, + &handler.EnqueueRequestForObject{}, + builder.WithPredicates(predicate.And( + statusChangedPredicateNamespace(), + internal.BpfNsProgramTypePredicate(internal.XdpNsString)), + ), + ). + Complete(r) +} + +func (r *XdpNsProgramReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + r.Logger = log.FromContext(ctx) + + r.Logger.Info("BILLY: XDP-NS Reconcile", "Namespace", req.NamespacedName) + + xdpNsProgram := &bpfmaniov1alpha1.XdpNsProgram{} + if err := r.Get(ctx, req.NamespacedName, xdpNsProgram); err != nil { + // list all XdpNsProgram objects with + if errors.IsNotFound(err) { + // TODO(astoycos) we could simplify this logic by making the name of the + // generated bpfProgram object a bit more deterministic + bpfProgram := &bpfmaniov1alpha1.BpfNsProgram{} + if err := r.Get(ctx, req.NamespacedName, bpfProgram); err != nil { + if errors.IsNotFound(err) { + r.Logger.V(1).Info("bpfProgram not found stale reconcile, exiting", "Name", req.NamespacedName) + } else { + r.Logger.Error(err, "failed getting bpfProgram Object", "Name", req.NamespacedName) + } + return ctrl.Result{}, nil + } + + // Get owning XdpNsProgram object from ownerRef + ownerRef := metav1.GetControllerOf(bpfProgram) + if ownerRef == nil { + return ctrl.Result{Requeue: false}, fmt.Errorf("failed getting bpfProgram Object owner") + } + + if err := r.Get(ctx, types.NamespacedName{Namespace: req.NamespacedName.Namespace, Name: ownerRef.Name}, xdpNsProgram); err != nil { + if errors.IsNotFound(err) { + r.Logger.Info("xdpNsProgram from ownerRef not found stale reconcile exiting", "Name", req.NamespacedName) + } else { + r.Logger.Error(err, "failed getting XdpNsProgram Object from ownerRef", "NAme", req.NamespacedName) + } + return ctrl.Result{}, nil + } + + } else { + r.Logger.Error(err, "failed getting XdpNsProgram Object", "Name", req.NamespacedName) + return ctrl.Result{}, nil + } + } + + return reconcileBpfProgram(ctx, r, xdpNsProgram) +} + +func (r *XdpNsProgramReconciler) updateStatus(ctx context.Context, namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { + // Sometimes we end up with a stale XdpNsProgram due to races, do this + // get to ensure we're up to date before attempting a finalizer removal. + prog := &bpfmaniov1alpha1.XdpNsProgram{} + if err := r.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, prog); err != nil { + r.Logger.V(1).Error(err, "failed to get fresh XdpNsProgram object...requeuing") + return ctrl.Result{Requeue: true, RequeueAfter: retryDurationOperator}, nil + } + + return r.updateCondition(ctx, prog, &prog.Status.Conditions, cond, message) +} diff --git a/controllers/bpfman-operator/xdp-program.go b/controllers/bpfman-operator/xdp-program.go index ea9bd7e9b..033f35897 100644 --- a/controllers/bpfman-operator/xdp-program.go +++ b/controllers/bpfman-operator/xdp-program.go @@ -11,6 +11,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +//lint:file-ignore U1000 Linter claims functions unused, but are required for generic + package bpfmanoperator import ( @@ -36,11 +38,11 @@ import ( //+kubebuilder:rbac:groups=bpfman.io,resources=xdpprograms/finalizers,verbs=update type XdpProgramReconciler struct { - ReconcilerCommon + ClusterProgramReconciler } -func (r *XdpProgramReconciler) getRecCommon() *ReconcilerCommon { - return &r.ReconcilerCommon +func (r *XdpProgramReconciler) getRecCommon() *ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList] { + return &r.ClusterProgramReconciler.ReconcilerCommon } func (r *XdpProgramReconciler) getFinalizer() string { @@ -55,7 +57,7 @@ func (r *XdpProgramReconciler) SetupWithManager(mgr ctrl.Manager) error { Watches( &bpfmaniov1alpha1.BpfProgram{}, &handler.EnqueueRequestForObject{}, - builder.WithPredicates(predicate.And(statusChangedPredicate(), internal.BpfProgramTypePredicate(internal.Xdp.String()))), + builder.WithPredicates(predicate.And(statusChangedPredicateCluster(), internal.BpfProgramTypePredicate(internal.Xdp.String()))), ). Complete(r) } @@ -103,7 +105,7 @@ func (r *XdpProgramReconciler) Reconcile(ctx context.Context, req ctrl.Request) return reconcileBpfProgram(ctx, r, xdpProgram) } -func (r *XdpProgramReconciler) updateStatus(ctx context.Context, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { +func (r *XdpProgramReconciler) updateStatus(ctx context.Context, _namespace string, name string, cond bpfmaniov1alpha1.ProgramConditionType, message string) (ctrl.Result, error) { // Sometimes we end up with a stale XdpProgram due to races, do this // get to ensure we're up to date before attempting a finalizer removal. prog := &bpfmaniov1alpha1.XdpProgram{} diff --git a/controllers/bpfman-operator/xdp-program_test.go b/controllers/bpfman-operator/xdp-program_test.go index 92223a008..b4ae491f7 100644 --- a/controllers/bpfman-operator/xdp-program_test.go +++ b/controllers/bpfman-operator/xdp-program_test.go @@ -108,16 +108,20 @@ func TestXdpProgramReconcile(t *testing.T) { // Create a fake client to mock API calls. cl := fake.NewClientBuilder().WithStatusSubresource(Xdp).WithRuntimeObjects(objs...).Build() - rc := ReconcilerCommon{ + rc := ReconcilerCommon[bpfmaniov1alpha1.BpfProgram, bpfmaniov1alpha1.BpfProgramList]{ Client: cl, Scheme: s, } + cpr := ClusterProgramReconciler{ + ReconcilerCommon: rc, + } + // Set development Logger so we can see all logs in tests. logf.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) // Create a ReconcileMemcached object with the scheme and fake client. - r := &XdpProgramReconciler{ReconcilerCommon: rc} + r := &XdpProgramReconciler{ClusterProgramReconciler: cpr} // Mock request to simulate Reconcile() being called on an event for a // watched resource . diff --git a/internal/constants.go b/internal/constants.go index 2fc58f303..1e6394fd8 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -20,6 +20,7 @@ import "fmt" const ( XdpProgramInterface = "bpfman.io.xdpprogramcontroller/interface" + XdpNsProgramInterface = "bpfman.io.xdpnsprogramcontroller/interface" TcProgramInterface = "bpfman.io.tcprogramcontroller/interface" TcxProgramInterface = "bpfman.io.tcxprogramcontroller/interface" TracepointProgramTracepoint = "bpfman.io.tracepointprogramcontroller/tracepoint" @@ -74,6 +75,9 @@ const ( // XdpProgramControllerFinalizer is the finalizer that holds an Xdp BpfProgram // object from deletion until cleanup can be performed. XdpProgramControllerFinalizer = "bpfman.io.xdpprogramcontroller/finalizer" + // XdpNsProgramControllerFinalizer is the finalizer that holds an Xdp BpfProgram + // object from deletion until cleanup can be performed. + XdpNsProgramControllerFinalizer = "bpfman.io.xdpnsprogramcontroller/finalizer" // TcProgramControllerFinalizer is the finalizer that holds an Tc BpfProgram // object from deletion until cleanup can be performed. TcProgramControllerFinalizer = "bpfman.io.tcprogramcontroller/finalizer" @@ -246,6 +250,7 @@ const FentryString = "fentry" const FexitString = "fexit" const ApplicationString = "application" const TcxString = "tcx" +const XdpNsString = "xdp" type ReconcileResult uint8 diff --git a/internal/k8s.go b/internal/k8s.go index 2945b14f9..174178448 100644 --- a/internal/k8s.go +++ b/internal/k8s.go @@ -43,6 +43,24 @@ func BpfProgramTypePredicate(kind string) predicate.Funcs { } } +// Only reconcile if a bpfnsprogram has been created for the controller's program type. +func BpfNsProgramTypePredicate(kind string) predicate.Funcs { + return predicate.Funcs{ + GenericFunc: func(e event.GenericEvent) bool { + return e.Object.(*bpfmaniov1alpha1.BpfNsProgram).Spec.Type == kind + }, + CreateFunc: func(e event.CreateEvent) bool { + return e.Object.(*bpfmaniov1alpha1.BpfNsProgram).Spec.Type == kind + }, + UpdateFunc: func(e event.UpdateEvent) bool { + return e.ObjectNew.(*bpfmaniov1alpha1.BpfNsProgram).Spec.Type == kind + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return e.Object.(*bpfmaniov1alpha1.BpfNsProgram).Spec.Type == kind + }, + } +} + // Only reconcile if a bpfprogram has been created for a controller's node. func BpfProgramNodePredicate(nodeName string) predicate.Funcs { return predicate.Funcs{ diff --git a/pkg/client/apis/v1alpha1/bpfnsprogram.go b/pkg/client/apis/v1alpha1/bpfnsprogram.go new file mode 100644 index 000000000..4846a24fb --- /dev/null +++ b/pkg/client/apis/v1alpha1/bpfnsprogram.go @@ -0,0 +1,99 @@ +/* +Copyright 2023 The bpfman Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// BpfNsProgramLister helps list BpfNsPrograms. +// All objects returned here must be treated as read-only. +type BpfNsProgramLister interface { + // List lists all BpfNsPrograms in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.BpfNsProgram, err error) + // BpfNsPrograms returns an object that can list and get BpfNsPrograms. + BpfNsPrograms(namespace string) BpfNsProgramNamespaceLister + BpfNsProgramListerExpansion +} + +// bpfNsProgramLister implements the BpfNsProgramLister interface. +type bpfNsProgramLister struct { + indexer cache.Indexer +} + +// NewBpfNsProgramLister returns a new BpfNsProgramLister. +func NewBpfNsProgramLister(indexer cache.Indexer) BpfNsProgramLister { + return &bpfNsProgramLister{indexer: indexer} +} + +// List lists all BpfNsPrograms in the indexer. +func (s *bpfNsProgramLister) List(selector labels.Selector) (ret []*v1alpha1.BpfNsProgram, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.BpfNsProgram)) + }) + return ret, err +} + +// BpfNsPrograms returns an object that can list and get BpfNsPrograms. +func (s *bpfNsProgramLister) BpfNsPrograms(namespace string) BpfNsProgramNamespaceLister { + return bpfNsProgramNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// BpfNsProgramNamespaceLister helps list and get BpfNsPrograms. +// All objects returned here must be treated as read-only. +type BpfNsProgramNamespaceLister interface { + // List lists all BpfNsPrograms in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.BpfNsProgram, err error) + // Get retrieves the BpfNsProgram from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.BpfNsProgram, error) + BpfNsProgramNamespaceListerExpansion +} + +// bpfNsProgramNamespaceLister implements the BpfNsProgramNamespaceLister +// interface. +type bpfNsProgramNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all BpfNsPrograms in the indexer for a given namespace. +func (s bpfNsProgramNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.BpfNsProgram, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.BpfNsProgram)) + }) + return ret, err +} + +// Get retrieves the BpfNsProgram from the indexer for a given namespace and name. +func (s bpfNsProgramNamespaceLister) Get(name string) (*v1alpha1.BpfNsProgram, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("bpfnsprogram"), name) + } + return obj.(*v1alpha1.BpfNsProgram), nil +} diff --git a/pkg/client/apis/v1alpha1/expansion_generated.go b/pkg/client/apis/v1alpha1/expansion_generated.go index df6e451bd..f3b404006 100644 --- a/pkg/client/apis/v1alpha1/expansion_generated.go +++ b/pkg/client/apis/v1alpha1/expansion_generated.go @@ -22,6 +22,14 @@ package v1alpha1 // BpfApplicationLister. type BpfApplicationListerExpansion interface{} +// BpfNsProgramListerExpansion allows custom methods to be added to +// BpfNsProgramLister. +type BpfNsProgramListerExpansion interface{} + +// BpfNsProgramNamespaceListerExpansion allows custom methods to be added to +// BpfNsProgramNamespaceLister. +type BpfNsProgramNamespaceListerExpansion interface{} + // BpfProgramListerExpansion allows custom methods to be added to // BpfProgramLister. type BpfProgramListerExpansion interface{} @@ -54,6 +62,14 @@ type TracepointProgramListerExpansion interface{} // UprobeProgramLister. type UprobeProgramListerExpansion interface{} +// XdpNsProgramListerExpansion allows custom methods to be added to +// XdpNsProgramLister. +type XdpNsProgramListerExpansion interface{} + +// XdpNsProgramNamespaceListerExpansion allows custom methods to be added to +// XdpNsProgramNamespaceLister. +type XdpNsProgramNamespaceListerExpansion interface{} + // XdpProgramListerExpansion allows custom methods to be added to // XdpProgramLister. type XdpProgramListerExpansion interface{} diff --git a/pkg/client/apis/v1alpha1/xdpnsprogram.go b/pkg/client/apis/v1alpha1/xdpnsprogram.go new file mode 100644 index 000000000..ba698cd14 --- /dev/null +++ b/pkg/client/apis/v1alpha1/xdpnsprogram.go @@ -0,0 +1,99 @@ +/* +Copyright 2023 The bpfman Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// XdpNsProgramLister helps list XdpNsPrograms. +// All objects returned here must be treated as read-only. +type XdpNsProgramLister interface { + // List lists all XdpNsPrograms in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.XdpNsProgram, err error) + // XdpNsPrograms returns an object that can list and get XdpNsPrograms. + XdpNsPrograms(namespace string) XdpNsProgramNamespaceLister + XdpNsProgramListerExpansion +} + +// xdpNsProgramLister implements the XdpNsProgramLister interface. +type xdpNsProgramLister struct { + indexer cache.Indexer +} + +// NewXdpNsProgramLister returns a new XdpNsProgramLister. +func NewXdpNsProgramLister(indexer cache.Indexer) XdpNsProgramLister { + return &xdpNsProgramLister{indexer: indexer} +} + +// List lists all XdpNsPrograms in the indexer. +func (s *xdpNsProgramLister) List(selector labels.Selector) (ret []*v1alpha1.XdpNsProgram, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.XdpNsProgram)) + }) + return ret, err +} + +// XdpNsPrograms returns an object that can list and get XdpNsPrograms. +func (s *xdpNsProgramLister) XdpNsPrograms(namespace string) XdpNsProgramNamespaceLister { + return xdpNsProgramNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// XdpNsProgramNamespaceLister helps list and get XdpNsPrograms. +// All objects returned here must be treated as read-only. +type XdpNsProgramNamespaceLister interface { + // List lists all XdpNsPrograms in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.XdpNsProgram, err error) + // Get retrieves the XdpNsProgram from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.XdpNsProgram, error) + XdpNsProgramNamespaceListerExpansion +} + +// xdpNsProgramNamespaceLister implements the XdpNsProgramNamespaceLister +// interface. +type xdpNsProgramNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all XdpNsPrograms in the indexer for a given namespace. +func (s xdpNsProgramNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.XdpNsProgram, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.XdpNsProgram)) + }) + return ret, err +} + +// Get retrieves the XdpNsProgram from the indexer for a given namespace and name. +func (s xdpNsProgramNamespaceLister) Get(name string) (*v1alpha1.XdpNsProgram, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("xdpnsprogram"), name) + } + return obj.(*v1alpha1.XdpNsProgram), nil +} diff --git a/pkg/client/clientset/typed/apis/v1alpha1/apis_client.go b/pkg/client/clientset/typed/apis/v1alpha1/apis_client.go index ac2e4d3a1..c23b344c8 100644 --- a/pkg/client/clientset/typed/apis/v1alpha1/apis_client.go +++ b/pkg/client/clientset/typed/apis/v1alpha1/apis_client.go @@ -29,6 +29,7 @@ import ( type BpfmanV1alpha1Interface interface { RESTClient() rest.Interface BpfApplicationsGetter + BpfNsProgramsGetter BpfProgramsGetter FentryProgramsGetter FexitProgramsGetter @@ -37,6 +38,7 @@ type BpfmanV1alpha1Interface interface { TcxProgramsGetter TracepointProgramsGetter UprobeProgramsGetter + XdpNsProgramsGetter XdpProgramsGetter } @@ -49,6 +51,10 @@ func (c *BpfmanV1alpha1Client) BpfApplications() BpfApplicationInterface { return newBpfApplications(c) } +func (c *BpfmanV1alpha1Client) BpfNsPrograms(namespace string) BpfNsProgramInterface { + return newBpfNsPrograms(c, namespace) +} + func (c *BpfmanV1alpha1Client) BpfPrograms() BpfProgramInterface { return newBpfPrograms(c) } @@ -81,6 +87,10 @@ func (c *BpfmanV1alpha1Client) UprobePrograms() UprobeProgramInterface { return newUprobePrograms(c) } +func (c *BpfmanV1alpha1Client) XdpNsPrograms(namespace string) XdpNsProgramInterface { + return newXdpNsPrograms(c, namespace) +} + func (c *BpfmanV1alpha1Client) XdpPrograms() XdpProgramInterface { return newXdpPrograms(c) } diff --git a/pkg/client/clientset/typed/apis/v1alpha1/bpfnsprogram.go b/pkg/client/clientset/typed/apis/v1alpha1/bpfnsprogram.go new file mode 100644 index 000000000..b71f972a0 --- /dev/null +++ b/pkg/client/clientset/typed/apis/v1alpha1/bpfnsprogram.go @@ -0,0 +1,195 @@ +/* +Copyright 2023 The bpfman Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + scheme "github.com/bpfman/bpfman-operator/pkg/client/clientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// BpfNsProgramsGetter has a method to return a BpfNsProgramInterface. +// A group's client should implement this interface. +type BpfNsProgramsGetter interface { + BpfNsPrograms(namespace string) BpfNsProgramInterface +} + +// BpfNsProgramInterface has methods to work with BpfNsProgram resources. +type BpfNsProgramInterface interface { + Create(ctx context.Context, bpfNsProgram *v1alpha1.BpfNsProgram, opts v1.CreateOptions) (*v1alpha1.BpfNsProgram, error) + Update(ctx context.Context, bpfNsProgram *v1alpha1.BpfNsProgram, opts v1.UpdateOptions) (*v1alpha1.BpfNsProgram, error) + UpdateStatus(ctx context.Context, bpfNsProgram *v1alpha1.BpfNsProgram, opts v1.UpdateOptions) (*v1alpha1.BpfNsProgram, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.BpfNsProgram, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.BpfNsProgramList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.BpfNsProgram, err error) + BpfNsProgramExpansion +} + +// bpfNsPrograms implements BpfNsProgramInterface +type bpfNsPrograms struct { + client rest.Interface + ns string +} + +// newBpfNsPrograms returns a BpfNsPrograms +func newBpfNsPrograms(c *BpfmanV1alpha1Client, namespace string) *bpfNsPrograms { + return &bpfNsPrograms{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the bpfNsProgram, and returns the corresponding bpfNsProgram object, and an error if there is any. +func (c *bpfNsPrograms) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.BpfNsProgram, err error) { + result = &v1alpha1.BpfNsProgram{} + err = c.client.Get(). + Namespace(c.ns). + Resource("bpfnsprograms"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of BpfNsPrograms that match those selectors. +func (c *bpfNsPrograms) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.BpfNsProgramList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.BpfNsProgramList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("bpfnsprograms"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested bpfNsPrograms. +func (c *bpfNsPrograms) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("bpfnsprograms"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a bpfNsProgram and creates it. Returns the server's representation of the bpfNsProgram, and an error, if there is any. +func (c *bpfNsPrograms) Create(ctx context.Context, bpfNsProgram *v1alpha1.BpfNsProgram, opts v1.CreateOptions) (result *v1alpha1.BpfNsProgram, err error) { + result = &v1alpha1.BpfNsProgram{} + err = c.client.Post(). + Namespace(c.ns). + Resource("bpfnsprograms"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(bpfNsProgram). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a bpfNsProgram and updates it. Returns the server's representation of the bpfNsProgram, and an error, if there is any. +func (c *bpfNsPrograms) Update(ctx context.Context, bpfNsProgram *v1alpha1.BpfNsProgram, opts v1.UpdateOptions) (result *v1alpha1.BpfNsProgram, err error) { + result = &v1alpha1.BpfNsProgram{} + err = c.client.Put(). + Namespace(c.ns). + Resource("bpfnsprograms"). + Name(bpfNsProgram.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(bpfNsProgram). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *bpfNsPrograms) UpdateStatus(ctx context.Context, bpfNsProgram *v1alpha1.BpfNsProgram, opts v1.UpdateOptions) (result *v1alpha1.BpfNsProgram, err error) { + result = &v1alpha1.BpfNsProgram{} + err = c.client.Put(). + Namespace(c.ns). + Resource("bpfnsprograms"). + Name(bpfNsProgram.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(bpfNsProgram). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the bpfNsProgram and deletes it. Returns an error if one occurs. +func (c *bpfNsPrograms) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("bpfnsprograms"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *bpfNsPrograms) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("bpfnsprograms"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched bpfNsProgram. +func (c *bpfNsPrograms) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.BpfNsProgram, err error) { + result = &v1alpha1.BpfNsProgram{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("bpfnsprograms"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/clientset/typed/apis/v1alpha1/fake/fake_apis_client.go b/pkg/client/clientset/typed/apis/v1alpha1/fake/fake_apis_client.go index b83fbca87..37b5dddc9 100644 --- a/pkg/client/clientset/typed/apis/v1alpha1/fake/fake_apis_client.go +++ b/pkg/client/clientset/typed/apis/v1alpha1/fake/fake_apis_client.go @@ -32,6 +32,10 @@ func (c *FakeBpfmanV1alpha1) BpfApplications() v1alpha1.BpfApplicationInterface return &FakeBpfApplications{c} } +func (c *FakeBpfmanV1alpha1) BpfNsPrograms(namespace string) v1alpha1.BpfNsProgramInterface { + return &FakeBpfNsPrograms{c, namespace} +} + func (c *FakeBpfmanV1alpha1) BpfPrograms() v1alpha1.BpfProgramInterface { return &FakeBpfPrograms{c} } @@ -64,6 +68,10 @@ func (c *FakeBpfmanV1alpha1) UprobePrograms() v1alpha1.UprobeProgramInterface { return &FakeUprobePrograms{c} } +func (c *FakeBpfmanV1alpha1) XdpNsPrograms(namespace string) v1alpha1.XdpNsProgramInterface { + return &FakeXdpNsPrograms{c, namespace} +} + func (c *FakeBpfmanV1alpha1) XdpPrograms() v1alpha1.XdpProgramInterface { return &FakeXdpPrograms{c} } diff --git a/pkg/client/clientset/typed/apis/v1alpha1/fake/fake_bpfnsprogram.go b/pkg/client/clientset/typed/apis/v1alpha1/fake/fake_bpfnsprogram.go new file mode 100644 index 000000000..2c029a7a0 --- /dev/null +++ b/pkg/client/clientset/typed/apis/v1alpha1/fake/fake_bpfnsprogram.go @@ -0,0 +1,141 @@ +/* +Copyright 2023 The bpfman Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeBpfNsPrograms implements BpfNsProgramInterface +type FakeBpfNsPrograms struct { + Fake *FakeBpfmanV1alpha1 + ns string +} + +var bpfnsprogramsResource = v1alpha1.SchemeGroupVersion.WithResource("bpfnsprograms") + +var bpfnsprogramsKind = v1alpha1.SchemeGroupVersion.WithKind("BpfNsProgram") + +// Get takes name of the bpfNsProgram, and returns the corresponding bpfNsProgram object, and an error if there is any. +func (c *FakeBpfNsPrograms) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.BpfNsProgram, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(bpfnsprogramsResource, c.ns, name), &v1alpha1.BpfNsProgram{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.BpfNsProgram), err +} + +// List takes label and field selectors, and returns the list of BpfNsPrograms that match those selectors. +func (c *FakeBpfNsPrograms) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.BpfNsProgramList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(bpfnsprogramsResource, bpfnsprogramsKind, c.ns, opts), &v1alpha1.BpfNsProgramList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.BpfNsProgramList{ListMeta: obj.(*v1alpha1.BpfNsProgramList).ListMeta} + for _, item := range obj.(*v1alpha1.BpfNsProgramList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested bpfNsPrograms. +func (c *FakeBpfNsPrograms) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(bpfnsprogramsResource, c.ns, opts)) + +} + +// Create takes the representation of a bpfNsProgram and creates it. Returns the server's representation of the bpfNsProgram, and an error, if there is any. +func (c *FakeBpfNsPrograms) Create(ctx context.Context, bpfNsProgram *v1alpha1.BpfNsProgram, opts v1.CreateOptions) (result *v1alpha1.BpfNsProgram, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(bpfnsprogramsResource, c.ns, bpfNsProgram), &v1alpha1.BpfNsProgram{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.BpfNsProgram), err +} + +// Update takes the representation of a bpfNsProgram and updates it. Returns the server's representation of the bpfNsProgram, and an error, if there is any. +func (c *FakeBpfNsPrograms) Update(ctx context.Context, bpfNsProgram *v1alpha1.BpfNsProgram, opts v1.UpdateOptions) (result *v1alpha1.BpfNsProgram, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(bpfnsprogramsResource, c.ns, bpfNsProgram), &v1alpha1.BpfNsProgram{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.BpfNsProgram), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeBpfNsPrograms) UpdateStatus(ctx context.Context, bpfNsProgram *v1alpha1.BpfNsProgram, opts v1.UpdateOptions) (*v1alpha1.BpfNsProgram, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(bpfnsprogramsResource, "status", c.ns, bpfNsProgram), &v1alpha1.BpfNsProgram{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.BpfNsProgram), err +} + +// Delete takes name of the bpfNsProgram and deletes it. Returns an error if one occurs. +func (c *FakeBpfNsPrograms) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(bpfnsprogramsResource, c.ns, name, opts), &v1alpha1.BpfNsProgram{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeBpfNsPrograms) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(bpfnsprogramsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.BpfNsProgramList{}) + return err +} + +// Patch applies the patch and returns the patched bpfNsProgram. +func (c *FakeBpfNsPrograms) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.BpfNsProgram, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(bpfnsprogramsResource, c.ns, name, pt, data, subresources...), &v1alpha1.BpfNsProgram{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.BpfNsProgram), err +} diff --git a/pkg/client/clientset/typed/apis/v1alpha1/fake/fake_xdpnsprogram.go b/pkg/client/clientset/typed/apis/v1alpha1/fake/fake_xdpnsprogram.go new file mode 100644 index 000000000..ef9ee7dc8 --- /dev/null +++ b/pkg/client/clientset/typed/apis/v1alpha1/fake/fake_xdpnsprogram.go @@ -0,0 +1,141 @@ +/* +Copyright 2023 The bpfman Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeXdpNsPrograms implements XdpNsProgramInterface +type FakeXdpNsPrograms struct { + Fake *FakeBpfmanV1alpha1 + ns string +} + +var xdpnsprogramsResource = v1alpha1.SchemeGroupVersion.WithResource("xdpnsprograms") + +var xdpnsprogramsKind = v1alpha1.SchemeGroupVersion.WithKind("XdpNsProgram") + +// Get takes name of the xdpNsProgram, and returns the corresponding xdpNsProgram object, and an error if there is any. +func (c *FakeXdpNsPrograms) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.XdpNsProgram, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(xdpnsprogramsResource, c.ns, name), &v1alpha1.XdpNsProgram{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.XdpNsProgram), err +} + +// List takes label and field selectors, and returns the list of XdpNsPrograms that match those selectors. +func (c *FakeXdpNsPrograms) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.XdpNsProgramList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(xdpnsprogramsResource, xdpnsprogramsKind, c.ns, opts), &v1alpha1.XdpNsProgramList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.XdpNsProgramList{ListMeta: obj.(*v1alpha1.XdpNsProgramList).ListMeta} + for _, item := range obj.(*v1alpha1.XdpNsProgramList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested xdpNsPrograms. +func (c *FakeXdpNsPrograms) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(xdpnsprogramsResource, c.ns, opts)) + +} + +// Create takes the representation of a xdpNsProgram and creates it. Returns the server's representation of the xdpNsProgram, and an error, if there is any. +func (c *FakeXdpNsPrograms) Create(ctx context.Context, xdpNsProgram *v1alpha1.XdpNsProgram, opts v1.CreateOptions) (result *v1alpha1.XdpNsProgram, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(xdpnsprogramsResource, c.ns, xdpNsProgram), &v1alpha1.XdpNsProgram{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.XdpNsProgram), err +} + +// Update takes the representation of a xdpNsProgram and updates it. Returns the server's representation of the xdpNsProgram, and an error, if there is any. +func (c *FakeXdpNsPrograms) Update(ctx context.Context, xdpNsProgram *v1alpha1.XdpNsProgram, opts v1.UpdateOptions) (result *v1alpha1.XdpNsProgram, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(xdpnsprogramsResource, c.ns, xdpNsProgram), &v1alpha1.XdpNsProgram{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.XdpNsProgram), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeXdpNsPrograms) UpdateStatus(ctx context.Context, xdpNsProgram *v1alpha1.XdpNsProgram, opts v1.UpdateOptions) (*v1alpha1.XdpNsProgram, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(xdpnsprogramsResource, "status", c.ns, xdpNsProgram), &v1alpha1.XdpNsProgram{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.XdpNsProgram), err +} + +// Delete takes name of the xdpNsProgram and deletes it. Returns an error if one occurs. +func (c *FakeXdpNsPrograms) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(xdpnsprogramsResource, c.ns, name, opts), &v1alpha1.XdpNsProgram{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeXdpNsPrograms) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(xdpnsprogramsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.XdpNsProgramList{}) + return err +} + +// Patch applies the patch and returns the patched xdpNsProgram. +func (c *FakeXdpNsPrograms) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.XdpNsProgram, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(xdpnsprogramsResource, c.ns, name, pt, data, subresources...), &v1alpha1.XdpNsProgram{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.XdpNsProgram), err +} diff --git a/pkg/client/clientset/typed/apis/v1alpha1/generated_expansion.go b/pkg/client/clientset/typed/apis/v1alpha1/generated_expansion.go index c514790d0..91149c255 100644 --- a/pkg/client/clientset/typed/apis/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/typed/apis/v1alpha1/generated_expansion.go @@ -20,6 +20,8 @@ package v1alpha1 type BpfApplicationExpansion interface{} +type BpfNsProgramExpansion interface{} + type BpfProgramExpansion interface{} type FentryProgramExpansion interface{} @@ -36,4 +38,6 @@ type TracepointProgramExpansion interface{} type UprobeProgramExpansion interface{} +type XdpNsProgramExpansion interface{} + type XdpProgramExpansion interface{} diff --git a/pkg/client/clientset/typed/apis/v1alpha1/xdpnsprogram.go b/pkg/client/clientset/typed/apis/v1alpha1/xdpnsprogram.go new file mode 100644 index 000000000..09742d71d --- /dev/null +++ b/pkg/client/clientset/typed/apis/v1alpha1/xdpnsprogram.go @@ -0,0 +1,195 @@ +/* +Copyright 2023 The bpfman Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + scheme "github.com/bpfman/bpfman-operator/pkg/client/clientset/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// XdpNsProgramsGetter has a method to return a XdpNsProgramInterface. +// A group's client should implement this interface. +type XdpNsProgramsGetter interface { + XdpNsPrograms(namespace string) XdpNsProgramInterface +} + +// XdpNsProgramInterface has methods to work with XdpNsProgram resources. +type XdpNsProgramInterface interface { + Create(ctx context.Context, xdpNsProgram *v1alpha1.XdpNsProgram, opts v1.CreateOptions) (*v1alpha1.XdpNsProgram, error) + Update(ctx context.Context, xdpNsProgram *v1alpha1.XdpNsProgram, opts v1.UpdateOptions) (*v1alpha1.XdpNsProgram, error) + UpdateStatus(ctx context.Context, xdpNsProgram *v1alpha1.XdpNsProgram, opts v1.UpdateOptions) (*v1alpha1.XdpNsProgram, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.XdpNsProgram, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.XdpNsProgramList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.XdpNsProgram, err error) + XdpNsProgramExpansion +} + +// xdpNsPrograms implements XdpNsProgramInterface +type xdpNsPrograms struct { + client rest.Interface + ns string +} + +// newXdpNsPrograms returns a XdpNsPrograms +func newXdpNsPrograms(c *BpfmanV1alpha1Client, namespace string) *xdpNsPrograms { + return &xdpNsPrograms{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the xdpNsProgram, and returns the corresponding xdpNsProgram object, and an error if there is any. +func (c *xdpNsPrograms) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.XdpNsProgram, err error) { + result = &v1alpha1.XdpNsProgram{} + err = c.client.Get(). + Namespace(c.ns). + Resource("xdpnsprograms"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of XdpNsPrograms that match those selectors. +func (c *xdpNsPrograms) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.XdpNsProgramList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.XdpNsProgramList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("xdpnsprograms"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested xdpNsPrograms. +func (c *xdpNsPrograms) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("xdpnsprograms"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a xdpNsProgram and creates it. Returns the server's representation of the xdpNsProgram, and an error, if there is any. +func (c *xdpNsPrograms) Create(ctx context.Context, xdpNsProgram *v1alpha1.XdpNsProgram, opts v1.CreateOptions) (result *v1alpha1.XdpNsProgram, err error) { + result = &v1alpha1.XdpNsProgram{} + err = c.client.Post(). + Namespace(c.ns). + Resource("xdpnsprograms"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(xdpNsProgram). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a xdpNsProgram and updates it. Returns the server's representation of the xdpNsProgram, and an error, if there is any. +func (c *xdpNsPrograms) Update(ctx context.Context, xdpNsProgram *v1alpha1.XdpNsProgram, opts v1.UpdateOptions) (result *v1alpha1.XdpNsProgram, err error) { + result = &v1alpha1.XdpNsProgram{} + err = c.client.Put(). + Namespace(c.ns). + Resource("xdpnsprograms"). + Name(xdpNsProgram.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(xdpNsProgram). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *xdpNsPrograms) UpdateStatus(ctx context.Context, xdpNsProgram *v1alpha1.XdpNsProgram, opts v1.UpdateOptions) (result *v1alpha1.XdpNsProgram, err error) { + result = &v1alpha1.XdpNsProgram{} + err = c.client.Put(). + Namespace(c.ns). + Resource("xdpnsprograms"). + Name(xdpNsProgram.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(xdpNsProgram). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the xdpNsProgram and deletes it. Returns an error if one occurs. +func (c *xdpNsPrograms) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("xdpnsprograms"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *xdpNsPrograms) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("xdpnsprograms"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched xdpNsProgram. +func (c *xdpNsPrograms) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.XdpNsProgram, err error) { + result = &v1alpha1.XdpNsProgram{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("xdpnsprograms"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/externalversions/apis/v1alpha1/bpfnsprogram.go b/pkg/client/externalversions/apis/v1alpha1/bpfnsprogram.go new file mode 100644 index 000000000..d03eeb308 --- /dev/null +++ b/pkg/client/externalversions/apis/v1alpha1/bpfnsprogram.go @@ -0,0 +1,90 @@ +/* +Copyright 2023 The bpfman Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + apisv1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + v1alpha1 "github.com/bpfman/bpfman-operator/pkg/client/apis/v1alpha1" + clientset "github.com/bpfman/bpfman-operator/pkg/client/clientset" + internalinterfaces "github.com/bpfman/bpfman-operator/pkg/client/externalversions/internalinterfaces" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// BpfNsProgramInformer provides access to a shared informer and lister for +// BpfNsPrograms. +type BpfNsProgramInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.BpfNsProgramLister +} + +type bpfNsProgramInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewBpfNsProgramInformer constructs a new informer for BpfNsProgram type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewBpfNsProgramInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredBpfNsProgramInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredBpfNsProgramInformer constructs a new informer for BpfNsProgram type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredBpfNsProgramInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.BpfmanV1alpha1().BpfNsPrograms(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.BpfmanV1alpha1().BpfNsPrograms(namespace).Watch(context.TODO(), options) + }, + }, + &apisv1alpha1.BpfNsProgram{}, + resyncPeriod, + indexers, + ) +} + +func (f *bpfNsProgramInformer) defaultInformer(client clientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredBpfNsProgramInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *bpfNsProgramInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&apisv1alpha1.BpfNsProgram{}, f.defaultInformer) +} + +func (f *bpfNsProgramInformer) Lister() v1alpha1.BpfNsProgramLister { + return v1alpha1.NewBpfNsProgramLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/externalversions/apis/v1alpha1/interface.go b/pkg/client/externalversions/apis/v1alpha1/interface.go index 432597f9d..b4f0ee6ad 100644 --- a/pkg/client/externalversions/apis/v1alpha1/interface.go +++ b/pkg/client/externalversions/apis/v1alpha1/interface.go @@ -26,6 +26,8 @@ import ( type Interface interface { // BpfApplications returns a BpfApplicationInformer. BpfApplications() BpfApplicationInformer + // BpfNsPrograms returns a BpfNsProgramInformer. + BpfNsPrograms() BpfNsProgramInformer // BpfPrograms returns a BpfProgramInformer. BpfPrograms() BpfProgramInformer // FentryPrograms returns a FentryProgramInformer. @@ -42,6 +44,8 @@ type Interface interface { TracepointPrograms() TracepointProgramInformer // UprobePrograms returns a UprobeProgramInformer. UprobePrograms() UprobeProgramInformer + // XdpNsPrograms returns a XdpNsProgramInformer. + XdpNsPrograms() XdpNsProgramInformer // XdpPrograms returns a XdpProgramInformer. XdpPrograms() XdpProgramInformer } @@ -62,6 +66,11 @@ func (v *version) BpfApplications() BpfApplicationInformer { return &bpfApplicationInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } +// BpfNsPrograms returns a BpfNsProgramInformer. +func (v *version) BpfNsPrograms() BpfNsProgramInformer { + return &bpfNsProgramInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // BpfPrograms returns a BpfProgramInformer. func (v *version) BpfPrograms() BpfProgramInformer { return &bpfProgramInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} @@ -102,6 +111,11 @@ func (v *version) UprobePrograms() UprobeProgramInformer { return &uprobeProgramInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } +// XdpNsPrograms returns a XdpNsProgramInformer. +func (v *version) XdpNsPrograms() XdpNsProgramInformer { + return &xdpNsProgramInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // XdpPrograms returns a XdpProgramInformer. func (v *version) XdpPrograms() XdpProgramInformer { return &xdpProgramInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/externalversions/apis/v1alpha1/xdpnsprogram.go b/pkg/client/externalversions/apis/v1alpha1/xdpnsprogram.go new file mode 100644 index 000000000..b43c10380 --- /dev/null +++ b/pkg/client/externalversions/apis/v1alpha1/xdpnsprogram.go @@ -0,0 +1,90 @@ +/* +Copyright 2023 The bpfman Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + apisv1alpha1 "github.com/bpfman/bpfman-operator/apis/v1alpha1" + v1alpha1 "github.com/bpfman/bpfman-operator/pkg/client/apis/v1alpha1" + clientset "github.com/bpfman/bpfman-operator/pkg/client/clientset" + internalinterfaces "github.com/bpfman/bpfman-operator/pkg/client/externalversions/internalinterfaces" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// XdpNsProgramInformer provides access to a shared informer and lister for +// XdpNsPrograms. +type XdpNsProgramInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.XdpNsProgramLister +} + +type xdpNsProgramInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewXdpNsProgramInformer constructs a new informer for XdpNsProgram type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewXdpNsProgramInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredXdpNsProgramInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredXdpNsProgramInformer constructs a new informer for XdpNsProgram type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredXdpNsProgramInformer(client clientset.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.BpfmanV1alpha1().XdpNsPrograms(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.BpfmanV1alpha1().XdpNsPrograms(namespace).Watch(context.TODO(), options) + }, + }, + &apisv1alpha1.XdpNsProgram{}, + resyncPeriod, + indexers, + ) +} + +func (f *xdpNsProgramInformer) defaultInformer(client clientset.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredXdpNsProgramInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *xdpNsProgramInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&apisv1alpha1.XdpNsProgram{}, f.defaultInformer) +} + +func (f *xdpNsProgramInformer) Lister() v1alpha1.XdpNsProgramLister { + return v1alpha1.NewXdpNsProgramLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/externalversions/generic.go b/pkg/client/externalversions/generic.go index 4db1e4a1e..b3e74c9d9 100644 --- a/pkg/client/externalversions/generic.go +++ b/pkg/client/externalversions/generic.go @@ -55,6 +55,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // Group=bpfman.io, Version=v1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("bpfapplications"): return &genericInformer{resource: resource.GroupResource(), informer: f.Bpfman().V1alpha1().BpfApplications().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("bpfnsprograms"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Bpfman().V1alpha1().BpfNsPrograms().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("bpfprograms"): return &genericInformer{resource: resource.GroupResource(), informer: f.Bpfman().V1alpha1().BpfPrograms().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("fentryprograms"): @@ -71,6 +73,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Bpfman().V1alpha1().TracepointPrograms().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("uprobeprograms"): return &genericInformer{resource: resource.GroupResource(), informer: f.Bpfman().V1alpha1().UprobePrograms().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("xdpnsprograms"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Bpfman().V1alpha1().XdpNsPrograms().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("xdpprograms"): return &genericInformer{resource: resource.GroupResource(), informer: f.Bpfman().V1alpha1().XdpPrograms().Informer()}, nil