From c719d9d0457b2a9019c28b00860e7398a4c95e34 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Sat, 8 Oct 2022 00:34:21 -0400 Subject: [PATCH] OADP-823 unit test for oadp-1.0 csi plugin cluster version validation (#850) --- controllers/dpa_controller.go | 2 + controllers/validator.go | 14 +--- controllers/validator_test.go | 143 +++++++++++++++++++++++++++++++++- main.go | 47 ++--------- pkg/scheme/scheme.go | 59 ++++++++++++++ 5 files changed, 212 insertions(+), 53 deletions(-) create mode 100644 pkg/scheme/scheme.go diff --git a/controllers/dpa_controller.go b/controllers/dpa_controller.go index 01aea9a2a9..8c7176dbe8 100644 --- a/controllers/dpa_controller.go +++ b/controllers/dpa_controller.go @@ -32,6 +32,7 @@ import ( oadpv1alpha1 "github.com/openshift/oadp-operator/api/v1alpha1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/discovery" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" ctrl "sigs.k8s.io/controller-runtime" @@ -45,6 +46,7 @@ import ( // DPAReconciler reconciles a Velero object type DPAReconciler struct { client.Client + discovery.DiscoveryInterface Scheme *runtime.Scheme Log logr.Logger Context context.Context diff --git a/controllers/validator.go b/controllers/validator.go index a7b87ad71b..aab9efa88a 100644 --- a/controllers/validator.go +++ b/controllers/validator.go @@ -3,9 +3,6 @@ package controllers import ( "errors" "fmt" - "k8s.io/apimachinery/pkg/version" - "k8s.io/client-go/kubernetes" - "sigs.k8s.io/controller-runtime/pkg/client/config" "github.com/go-logr/logr" oadpv1alpha1 "github.com/openshift/oadp-operator/api/v1alpha1" @@ -91,7 +88,7 @@ func (r *DPAReconciler) ValidateVeleroPlugins(log logr.Logger) (bool, error) { // check csi compatibility with cluster version // velero version <1.9 expects API group snapshot.storage.k8s.io/v1beta1, while OCP 4.11 (k8s 1.24) has only snapshot.storage.k8s.io/v1 if plugin == oadpv1alpha1.DefaultPluginCSI { - clusterVersion, err := getClusterVersion() + clusterVersion, err := r.DiscoveryInterface.ServerVersion() if err != nil { return false, err } @@ -119,12 +116,3 @@ func (r *DPAReconciler) ValidateVeleroPlugins(log logr.Logger) (bool, error) { } return true, nil } - -func getClusterVersion() (*version.Info, error) { - kubeConf := config.GetConfigOrDie() - clientset, err := kubernetes.NewForConfig(kubeConf) - if err != nil { - return nil, err - } - return clientset.Discovery().ServerVersion() -} diff --git a/controllers/validator_test.go b/controllers/validator_test.go index 3347340858..afc822fd96 100644 --- a/controllers/validator_test.go +++ b/controllers/validator_test.go @@ -1,18 +1,26 @@ package controllers import ( - "k8s.io/apimachinery/pkg/api/resource" + "context" "testing" "github.com/go-logr/logr" + logrTesting "github.com/go-logr/logr/testing" oadpv1alpha1 "github.com/openshift/oadp-operator/api/v1alpha1" + oadpScheme "github.com/openshift/oadp-operator/pkg/scheme" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/version" + discoverFake "k8s.io/client-go/discovery/fake" + clienttesting "k8s.io/client-go/testing" "k8s.io/client-go/tools/record" "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" + clientFake "sigs.k8s.io/controller-runtime/pkg/client/fake" ) func TestDPAReconciler_ValidateDataProtectionCR(t *testing.T) { @@ -707,3 +715,136 @@ func TestDPAReconciler_ValidateDataProtectionCR(t *testing.T) { }) } } + +var namespacedName = types.NamespacedName{ + Namespace: "test-ns", + Name: "test-DPA-CR", +} +func TestDPAReconciler_ValidateVeleroPlugins(t *testing.T) { + type fields struct { + objects []runtime.Object + FakedServerVersion *version.Info + Scheme *runtime.Scheme + NamespacedName types.NamespacedName + EventRecorder record.EventRecorder + } + tests := []struct { + name string + fields fields + want bool + wantErr bool + }{ + { + name: "given valid DPA CR with AWS, CSI Default Plugin and secret", + fields: fields{ + objects: []runtime.Object{ + &oadpv1alpha1.DataProtectionApplication{ + TypeMeta: metav1.TypeMeta{ + Kind: "DataProtectionApplication", + APIVersion: "oadp.openshift.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-DPA-CR", + Namespace: "test-ns", + }, + Spec: oadpv1alpha1.DataProtectionApplicationSpec{ + Configuration: &oadpv1alpha1.ApplicationConfig{ + Velero: &oadpv1alpha1.VeleroConfig{ + DefaultPlugins: []oadpv1alpha1.DefaultPlugin{ + oadpv1alpha1.DefaultPluginAWS, + oadpv1alpha1.DefaultPluginCSI, + }, + }, + }, + }, + }, + // secret + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cloud-credentials", + Namespace: "test-ns", + }, + Data: map[string][]byte{ + "cloud": []byte("test"), + }, + }, + }, + }, + want: true, + wantErr: false, + }, + { + name: "Cluster version too high for CSI plugin", + fields: fields{ + objects: []runtime.Object{ + &oadpv1alpha1.DataProtectionApplication{ + TypeMeta: metav1.TypeMeta{ + Kind: "DataProtectionApplication", + APIVersion: "oadp.openshift.io/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "test-DPA-CR", + Namespace: "test-ns", + }, + Spec: oadpv1alpha1.DataProtectionApplicationSpec{ + Configuration: &oadpv1alpha1.ApplicationConfig{ + Velero: &oadpv1alpha1.VeleroConfig{ + DefaultPlugins: []oadpv1alpha1.DefaultPlugin{ + oadpv1alpha1.DefaultPluginAWS, + oadpv1alpha1.DefaultPluginCSI, + }, + }, + }, + }, + }, + // secret + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cloud-credentials", + Namespace: "test-ns", + }, + Data: map[string][]byte{ + "cloud": []byte("test"), + }, + }, + }, + FakedServerVersion: &version.Info{ + Major: "1", + Minor: "24", + }, + }, + want: false, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.fields.FakedServerVersion == nil { + tt.fields.FakedServerVersion = &version.Info{ + Major: "1", + Minor: "23", + } + } + log := logrTesting.TestLogger{T: t} + scheme := runtime.NewScheme() + oadpScheme.AddToScheme(scheme, log) + r := &DPAReconciler{ + Client: clientFake.NewFakeClientWithScheme(scheme, tt.fields.objects...), + DiscoveryInterface: &discoverFake.FakeDiscovery{ + Fake: &clienttesting.Fake{}, + FakedServerVersion: tt.fields.FakedServerVersion}, + Log: log, + NamespacedName: namespacedName, + Context: context.Background(), + } + got, err := r.ValidateVeleroPlugins(r.Log) + if (err != nil) != tt.wantErr { + t.Errorf("DPAReconciler.ValidateVeleroPlugins() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("DPAReconciler.ValidateVeleroPlugins() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/main.go b/main.go index 401dcb1307..37b180a442 100644 --- a/main.go +++ b/main.go @@ -19,19 +19,16 @@ package main import ( "flag" "fmt" - monitor "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" "os" + oadpScheme "github.com/openshift/oadp-operator/pkg/scheme" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. + "k8s.io/client-go/discovery" _ "k8s.io/client-go/plugin/pkg/client/auth" - routev1 "github.com/openshift/api/route/v1" - security "github.com/openshift/api/security/v1" "github.com/openshift/oadp-operator/controllers" - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" @@ -92,41 +89,13 @@ func main() { os.Exit(1) } - // Setup scheme for OCP resources - if err := monitor.AddToScheme(mgr.GetScheme()); err != nil { - setupLog.Error(err, "unable to add OpenShift monitoring APIs to scheme") - os.Exit(1) - } - - if err := security.AddToScheme(mgr.GetScheme()); err != nil { - setupLog.Error(err, "unable to add OpenShift security APIs to scheme") - os.Exit(1) - } - - if err := routev1.AddToScheme(mgr.GetScheme()); err != nil { - setupLog.Error(err, "unable to add OpenShift route API to scheme") - os.Exit(1) - } - - if err := velerov1.AddToScheme(mgr.GetScheme()); err != nil { - setupLog.Error(err, "unable to add Velero APIs to scheme") - os.Exit(1) - } - - if err := appsv1.AddToScheme(mgr.GetScheme()); err != nil { - setupLog.Error(err, "unable to add Kubernetes APIs to scheme") - os.Exit(1) - } - - if err := v1.AddToScheme(mgr.GetScheme()); err != nil { - setupLog.Error(err, "unable to add Kubernetes API extensions to scheme") - os.Exit(1) - } + oadpScheme.AddToScheme(mgr.GetScheme(), setupLog) if err = (&controllers.DPAReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - EventRecorder: mgr.GetEventRecorderFor("DPA-controller"), + Client: mgr.GetClient(), + DiscoveryInterface: discovery.NewDiscoveryClientForConfigOrDie(mgr.GetConfig()), + Scheme: mgr.GetScheme(), + EventRecorder: mgr.GetEventRecorderFor("DPA-controller"), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "DataProtectionApplication") os.Exit(1) diff --git a/pkg/scheme/scheme.go b/pkg/scheme/scheme.go new file mode 100644 index 0000000000..710b72e86c --- /dev/null +++ b/pkg/scheme/scheme.go @@ -0,0 +1,59 @@ +package scheme + +import ( + "os" + + "k8s.io/apimachinery/pkg/runtime" + + "github.com/go-logr/logr" + monitor "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + + _ "k8s.io/client-go/plugin/pkg/client/auth" + + routev1 "github.com/openshift/api/route/v1" + security "github.com/openshift/api/security/v1" + "github.com/openshift/oadp-operator/api/v1alpha1" + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) +func AddToScheme(scheme *runtime.Scheme, setupLog logr.Logger) { + // Setup scheme for OCP resources + if err := monitor.AddToScheme(scheme); err != nil { + setupLog.Error(err, "unable to add OpenShift monitoring APIs to scheme") + os.Exit(1) + } + + if err := security.AddToScheme(scheme); err != nil { + setupLog.Error(err, "unable to add OpenShift security APIs to scheme") + os.Exit(1) + } + + if err := routev1.AddToScheme(scheme); err != nil { + setupLog.Error(err, "unable to add OpenShift route API to scheme") + os.Exit(1) + } + + if err := velerov1.AddToScheme(scheme); err != nil { + setupLog.Error(err, "unable to add Velero APIs to scheme") + os.Exit(1) + } + + if err := appsv1.AddToScheme(scheme); err != nil { + setupLog.Error(err, "unable to add Kubernetes APIs to scheme") + os.Exit(1) + } + + if err := v1.AddToScheme(scheme); err != nil { + setupLog.Error(err, "unable to add Kubernetes API extensions to scheme") + os.Exit(1) + } + + if err := v1alpha1.AddToScheme(scheme); err != nil { + setupLog.Error(err, "unable to add Kubernetes API extensions to scheme") + os.Exit(1) + } +} \ No newline at end of file