diff --git a/pkg/controller/performanceprofile/performanceprofile_controller_suite_test.go b/pkg/controller/performanceprofile/performanceprofile_controller_suite_test.go new file mode 100644 index 000000000..dcb0019c8 --- /dev/null +++ b/pkg/controller/performanceprofile/performanceprofile_controller_suite_test.go @@ -0,0 +1,26 @@ +package performanceprofile + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/openshift-kni/performance-addon-operators/pkg/apis" + configv1 "github.com/openshift/api/config/v1" + tunedv1 "github.com/openshift/cluster-node-tuning-operator/pkg/apis/tuned/v1" + mcov1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" + + "k8s.io/client-go/kubernetes/scheme" +) + +func TestFeatureGate(t *testing.T) { + RegisterFailHandler(Fail) + + // add resources API to default scheme + apis.AddToScheme(scheme.Scheme) + configv1.AddToScheme(scheme.Scheme) + mcov1.AddToScheme(scheme.Scheme) + tunedv1.AddToScheme(scheme.Scheme) + + RunSpecs(t, "Performance Profile Suite") +} diff --git a/pkg/controller/performanceprofile/performanceprofile_controller_test.go b/pkg/controller/performanceprofile/performanceprofile_controller_test.go new file mode 100644 index 000000000..ac39f4140 --- /dev/null +++ b/pkg/controller/performanceprofile/performanceprofile_controller_test.go @@ -0,0 +1,224 @@ +package performanceprofile + +import ( + "context" + "time" + + "github.com/openshift-kni/performance-addon-operators/pkg/controller/performanceprofile/components/featuregate" + "github.com/openshift-kni/performance-addon-operators/pkg/controller/performanceprofile/components/tuned" + + "github.com/openshift-kni/performance-addon-operators/pkg/controller/performanceprofile/components/kubeletconfig" + + "github.com/openshift-kni/performance-addon-operators/pkg/controller/performanceprofile/components/machineconfig" + + "github.com/openshift-kni/performance-addon-operators/pkg/controller/performanceprofile/components/machineconfigpool" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + performancev1alpha1 "github.com/openshift-kni/performance-addon-operators/pkg/apis/performance/v1alpha1" + "github.com/openshift-kni/performance-addon-operators/pkg/controller/performanceprofile/components" + testutils "github.com/openshift-kni/performance-addon-operators/pkg/utils/testing" + configv1 "github.com/openshift/api/config/v1" + tunedv1 "github.com/openshift/cluster-node-tuning-operator/pkg/apis/tuned/v1" + mcov1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" + + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes/scheme" + + "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +const assetsDir = "../../../build/assets" + +var _ = Describe("Controller", func() { + var request reconcile.Request + var profile *performancev1alpha1.PerformanceProfile + + BeforeEach(func() { + profile = testutils.NewPerformanceProfile("test") + request = reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: metav1.NamespaceNone, + Name: profile.Name, + }, + } + }) + + It("should add finalizer to the performance profile", func() { + r := newFakeReconciler(profile) + + result, err := r.Reconcile(request) + Expect(err).ToNot(HaveOccurred()) + Expect(result).To(Equal(reconcile.Result{})) + + updatedProfile := &performancev1alpha1.PerformanceProfile{} + key := types.NamespacedName{ + Name: profile.Name, + Namespace: metav1.NamespaceNone, + } + Expect(r.client.Get(context.TODO(), key, updatedProfile)).ToNot(HaveOccurred()) + Expect(hasFinalizer(updatedProfile, finalizer)).To(Equal(true)) + }) + + It("should verify scripts required parameters", func() { + profile.Finalizers = append(profile.Finalizers, finalizer) + profile.Spec.CPU.Isolated = nil + r := newFakeReconciler(profile) + + // we do not return error, because we do not want to reconcile again, and just print error under the log, + // once we will have validation webhook, this test will not be relevant anymore + result, err := r.Reconcile(request) + Expect(err).ToNot(HaveOccurred()) + Expect(result).To(Equal(reconcile.Result{})) + + // verify that no components created by the controller + mcp := &mcov1.MachineConfigPool{} + key := types.NamespacedName{ + Name: components.GetComponentName(profile.Name, components.RoleWorkerPerformance), + Namespace: metav1.NamespaceNone, + } + err = r.client.Get(context.TODO(), key, mcp) + Expect(errors.IsNotFound(err)).To(Equal(true)) + }) + + It("should create all needed components", func() { + profile.Finalizers = append(profile.Finalizers, finalizer) + profile.Spec.NodeSelector = map[string]string{"test": "test"} + r := newFakeReconciler(profile) + + result, err := r.Reconcile(request) + Expect(err).ToNot(HaveOccurred()) + Expect(result).To(Equal(reconcile.Result{})) + + // verify that controller created all components + name := components.GetComponentName(profile.Name, components.RoleWorkerPerformance) + key := types.NamespacedName{ + Name: name, + Namespace: metav1.NamespaceNone, + } + + // verify MachineConfigPool creation + mcp := &mcov1.MachineConfigPool{} + err = r.client.Get(context.TODO(), key, mcp) + Expect(err).ToNot(HaveOccurred()) + + // verify MachineConfig creation + mc := &mcov1.MachineConfig{} + err = r.client.Get(context.TODO(), key, mc) + Expect(err).ToNot(HaveOccurred()) + + // verify KubeletConfig creation + kc := &mcov1.KubeletConfig{} + err = r.client.Get(context.TODO(), key, kc) + Expect(err).ToNot(HaveOccurred()) + + // verify FeatureGate creation + fg := &configv1.FeatureGate{} + key.Name = components.FeatureGateLatencySensetiveName + err = r.client.Get(context.TODO(), key, fg) + Expect(err).ToNot(HaveOccurred()) + + // verify tuned LatencySensitive creation + tunedLatency := &tunedv1.Tuned{} + key.Name = components.ProfileNameNetworkLatency + key.Namespace = components.NamespaceNodeTuningOperator + err = r.client.Get(context.TODO(), key, tunedLatency) + Expect(err).ToNot(HaveOccurred()) + + // verify tuned tuned real-time kernel creation + tunedRTKernel := &tunedv1.Tuned{} + key.Name = components.GetComponentName(profile.Name, components.ProfileNameWorkerRT) + err = r.client.Get(context.TODO(), key, tunedRTKernel) + Expect(err).ToNot(HaveOccurred()) + }) + + Context("with profile with deletion timestamp", func() { + BeforeEach(func() { + profile.DeletionTimestamp = &metav1.Time{ + Time: time.Now(), + } + }) + + It("should remove all components and remove the finalizer", func() { + profile.Finalizers = append(profile.Finalizers, finalizer) + profile.Spec.NodeSelector = map[string]string{"test": "test"} + + mcp := machineconfigpool.NewPerformance(profile) + + mc, err := machineconfig.NewPerformance(assetsDir, profile) + Expect(err).ToNot(HaveOccurred()) + + kc := kubeletconfig.NewPerformance(profile) + + fg := featuregate.NewLatencySensitive() + + tunedLatency, err := tuned.NewNetworkLatency(assetsDir) + Expect(err).ToNot(HaveOccurred()) + + tunedRTKernel, err := tuned.NewWorkerRealTimeKernel(assetsDir, profile) + Expect(err).ToNot(HaveOccurred()) + + r := newFakeReconciler(profile, mcp, mc, kc, fg, tunedLatency, tunedRTKernel) + result, err := r.Reconcile(request) + Expect(err).ToNot(HaveOccurred()) + Expect(result).To(Equal(reconcile.Result{})) + + // verify that controller deleted all components + name := components.GetComponentName(profile.Name, components.RoleWorkerPerformance) + key := types.NamespacedName{ + Name: name, + Namespace: metav1.NamespaceNone, + } + + // verify MachineConfigPool deletion + err = r.client.Get(context.TODO(), key, mcp) + Expect(errors.IsNotFound(err)).To(Equal(true)) + + // verify MachineConfig deletion + err = r.client.Get(context.TODO(), key, mc) + Expect(errors.IsNotFound(err)).To(Equal(true)) + + // verify KubeletConfig deletion + err = r.client.Get(context.TODO(), key, kc) + Expect(errors.IsNotFound(err)).To(Equal(true)) + + // verify feature gate deletion + key.Name = components.FeatureGateLatencySensetiveName + err = r.client.Get(context.TODO(), key, fg) + Expect(errors.IsNotFound(err)).To(Equal(true)) + + // verify tuned latency deletion + key.Name = components.ProfileNameNetworkLatency + key.Namespace = components.NamespaceNodeTuningOperator + err = r.client.Get(context.TODO(), key, tunedLatency) + Expect(errors.IsNotFound(err)).To(Equal(true)) + + // verify tuned real-time kernel deletion + key.Name = components.GetComponentName(profile.Name, components.ProfileNameWorkerRT) + key.Namespace = components.NamespaceNodeTuningOperator + err = r.client.Get(context.TODO(), key, tunedRTKernel) + Expect(errors.IsNotFound(err)).To(Equal(true)) + + // verify finalizer deletion + key.Name = profile.Name + key.Namespace = metav1.NamespaceNone + updatedProfile := &performancev1alpha1.PerformanceProfile{} + Expect(r.client.Get(context.TODO(), key, updatedProfile)).ToNot(HaveOccurred()) + Expect(hasFinalizer(updatedProfile, finalizer)).To(Equal(false)) + }) + }) +}) + +// newFakeReconciler returns a new reconcile.Reconciler with a fake client +func newFakeReconciler(initObjects ...runtime.Object) *ReconcilePerformanceProfile { + fakeClient := fake.NewFakeClient(initObjects...) + return &ReconcilePerformanceProfile{ + client: fakeClient, + scheme: scheme.Scheme, + assetsDir: assetsDir, + } +} diff --git a/pkg/utils/testing/testing.go b/pkg/utils/testing/testing.go index 93969bbbb..f7fdf3a39 100644 --- a/pkg/utils/testing/testing.go +++ b/pkg/utils/testing/testing.go @@ -4,6 +4,7 @@ import ( performancev1alpha1 "github.com/openshift-kni/performance-addon-operators/pkg/apis/performance/v1alpha1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/utils/pointer" ) @@ -31,6 +32,7 @@ func NewPerformanceProfile(name string) *performancev1alpha1.PerformanceProfile TypeMeta: metav1.TypeMeta{Kind: "PerformanceProfile"}, ObjectMeta: metav1.ObjectMeta{ Name: name, + UID: types.UID("11111111-1111-1111-1111-1111111111111"), }, Spec: performancev1alpha1.PerformanceProfileSpec{ CPU: &performancev1alpha1.CPU{