From 23154e56d8d485fc1eda108812054dc6b5739707 Mon Sep 17 00:00:00 2001 From: manavrajvanshi-nx Date: Mon, 3 Oct 2022 17:31:36 -0700 Subject: [PATCH] 15 fix code scanning alert disabled tls certificate check (#25) * Updated functions to use secrets * Added ca cert mechanism * Removed redundant error log * Updated README * Bumped version to 0.0.2 * Updated sample CR yaml * Returned from reconcile function if credentials not found in secrets * Consolidated secret data fetching into one function call * Updated README for secrets to be using stringData. Updated unit tests. Optimized database instance credentials fetching while provisioning. Added RBAC rules for services, endpoints, and secrets for the controller-manager. --- Makefile | 2 +- README.md | 56 ++-- api/v1alpha1/constants.go | 9 + api/v1alpha1/database_types.go | 28 +- api/v1alpha1/ndb_api_helpers.go | 31 ++- api/v1alpha1/zz_generated.deepcopy.go | 16 -- .../crd/bases/ndb.nutanix.com_databases.yaml | 35 +-- config/rbac/role.yaml | 21 ++ config/samples/ndb_v1alpha1_database.yaml | 9 +- controllers/database_controller.go | 23 +- controllers/database_controller_test.go | 74 +++-- controllers/database_reconciler_helpers.go | 54 +++- ndbclient/ndbclient.go | 15 +- test/ndb_api_helpers_test.go | 255 +++++++++++++++--- test/ndb_api_test.go | 8 +- test/secret_test.go | 132 +++++++++ test/suite_test.go | 86 ++++++ util/secret.go | 46 ++++ 18 files changed, 763 insertions(+), 137 deletions(-) create mode 100644 test/secret_test.go create mode 100644 test/suite_test.go create mode 100644 util/secret.go diff --git a/Makefile b/Makefile index 82103c54..0de102ef 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # To re-generate a bundle for another specific version without changing the standard setup, you can: # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) -VERSION ?= 0.0.1 +VERSION ?= 0.0.2 # CHANNELS define the bundle channels used in the bundle. # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") diff --git a/README.md b/README.md index 54f1c64a..dc64c0ea 100644 --- a/README.md +++ b/README.md @@ -29,12 +29,38 @@ make deploy ### Using the Operator -1. To create instances of custom resources (provision databases), edit [ndb_v1alpha1_database.yaml](config/samples/ndb_v1alpha1_database.yaml) file with the NDB installation and database instance details and run: +1. Create the secrets that are to be used by the custom resource: +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: your-ndb-secret +type: Opaque +stringData: + username: username-for-ndb-server + password: password-for-ndb-server + ca_certificate: | + -----BEGIN CERTIFICATE----- + CA CERTIFICATE (ca_certificate is optional) + -----END CERTIFICATE----- +--- +apiVersion: v1 +kind: Secret +metadata: + name: your-db-secret +type: Opaque +stringData: + password: password-for-the-database-instance + ssh_public_key: SSH-PUBLIC-KEY + +``` + +2. To create instances of custom resources (provision databases), edit [ndb_v1alpha1_database.yaml](config/samples/ndb_v1alpha1_database.yaml) file with the NDB installation and database instance details and run: ```sh kubectl apply -f config/samples/ndb_v1alpha1_database.yaml ``` -2. To delete instances of custom resources (deprovision databases) run: +3. To delete instances of custom resources (deprovision databases) run: ```sh kubectl delete -f config/samples/ndb_v1alpha1_database.yaml @@ -47,36 +73,36 @@ metadata: # This name that will be used within the kubernetes cluster name: db spec: + # NDB server specific details ndb: # Cluster id of the cluster where the Database has to be provisioned # Can be fetched from the GET /clusters endpoint clusterId: "Nutanix Cluster Id" - # Credentials for NDB installation - credentials: - loginUser: admin - password: "NDB Password" - sshPublicKey: "SSH Key" + # Credentials secret name for NDB installation + # data: username, password, + # stringData: ca_certificate + credentialSecret: your-ndb-secret # The NDB Server server: https://[NDB IP]:8443/era/v0.9 - + # Set to true to skip SSL verification, default: false. + skipCertificateVerification: true + # Database instance specific details (that is to be provisioned) databaseInstance: # The database instance name on NDB - databaseInstanceName: "Database Instance Name" + databaseInstanceName: "Database-Instance-Name" # Names of the databases on that instance databaseNames: - database_one - database_two - database_three - # Password for the database - password: qwertyuiop + # Credentials secret name for NDB installation + # data: password, ssh_public_key + credentialSecret: your-db-secret size: 10 timezone: "UTC" type: postgres ``` - - - ## Developement ### Installation and Running the controller locally @@ -107,7 +133,7 @@ More information can be found via the [Kubebuilder Documentation](https://book.k ### Building and pushing to an image registry Build and push your image to the location specified by `IMG`: - + ```sh make docker-build docker-push IMG=/ndb-operator:tag ``` diff --git a/api/v1alpha1/constants.go b/api/v1alpha1/constants.go index ac49947a..2a3e2331 100644 --- a/api/v1alpha1/constants.go +++ b/api/v1alpha1/constants.go @@ -39,6 +39,10 @@ const ( FINALIZER_DATABASE_INSTANCE = "ndb.nutanix.com/finalizerdatabaseinstance" FINALIZER_DATABASE_SERVER = "ndb.nutanix.com/finalizerdatabaseserver" + NDB_PARAM_USERNAME = "username" + NDB_PARAM_PASSWORD = "password" + NDB_PARAM_SSH_PUBLIC_KEY = "ssh_public_key" + PROFILE_TYPE_COMPUTE = "Compute" PROFILE_TYPE_DATABASE_PARAMETER = "Database_Parameter" PROFILE_TYPE_NETWORK = "Network" @@ -47,6 +51,11 @@ const ( PROPERTY_NAME_VM_IP = "vm_ip" + SECRET_DATA_KEY_CA_CERTIFICATE = "ca_certificate" + SECRET_DATA_KEY_PASSWORD = "password" + SECRET_DATA_KEY_SSH_PUBLIC_KEY = "ssh_public_key" + SECRET_DATA_KEY_USERNAME = "username" + SLA_NAME_NONE = "NONE" TOPOLOGY_ALL = "ALL" diff --git a/api/v1alpha1/database_types.go b/api/v1alpha1/database_types.go index 833f7918..1edcb9fa 100644 --- a/api/v1alpha1/database_types.go +++ b/api/v1alpha1/database_types.go @@ -72,18 +72,17 @@ func init() { // Details of the NDB installation type NDB struct { - ClusterId string `json:"clusterId"` - Credentials Credentials `json:"credentials"` - Server string `json:"server"` -} - -type Credentials struct { - // Username for NDB - LoginUser string `json:"loginUser"` - // Password for NDB - Password string `json:"password"` - // SSH public key for the database vm - SSHPublicKey string `json:"sshPublicKey"` + // +kubebuilder:validation:Required + ClusterId string `json:"clusterId"` + // +kubebuilder:validation:Required + // Name of the secret holding the credentials for NDB (username and password) + CredentialSecret string `json:"credentialSecret"` + // +kubebuilder:validation:Required + Server string `json:"server"` + // +kubebuilder:default:=false + // +optional + // Skip server's certificate and hostname verification + SkipCertificateVerification bool `json:"skipCertificateVerification"` } // Database instance specific details @@ -95,8 +94,9 @@ type Instance struct { // +kubebuilder:validation:MinItems:=1 // Name of the database to be provisiond in the database instance DatabaseNames []string `json:"databaseNames"` - // Password of the database instance - Password string `json:"password"` + // +kubebuilder:validation:Required + // Name of the secret holding the credentials for the database instance (password and ssh key) + CredentialSecret string `json:"credentialSecret"` // +kubebuilder:validation:Minimum:=10 // +kubebuilder:default:=10 // +optional diff --git a/api/v1alpha1/ndb_api_helpers.go b/api/v1alpha1/ndb_api_helpers.go index 9d10775b..97981e42 100644 --- a/api/v1alpha1/ndb_api_helpers.go +++ b/api/v1alpha1/ndb_api_helpers.go @@ -30,7 +30,7 @@ import ( // This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB // The database provisioned has a NONE time machine SLA attached to it, and uses the default OOB profiles -func GenerateProvisioningRequest(ctx context.Context, ndbclient *ndbclient.NDBClient, dbSpec DatabaseSpec) (req *DatabaseProvisionRequest, err error) { +func GenerateProvisioningRequest(ctx context.Context, ndbclient *ndbclient.NDBClient, dbSpec DatabaseSpec, reqData map[string]interface{}) (req *DatabaseProvisionRequest, err error) { log := ctrllog.FromContext(ctx) log.Info("Entered ndb_api_helpers.GenerateProvisioningRequest", "database name", dbSpec.Instance.DatabaseInstanceName, "database type", dbSpec.Instance.Type) @@ -50,6 +50,31 @@ func GenerateProvisioningRequest(ctx context.Context, ndbclient *ndbclient.NDBCl database_names := strings.Join(dbSpec.Instance.DatabaseNames, ",") + // Type assertion + dbPassword, ok := reqData[NDB_PARAM_PASSWORD].(string) + if !ok || dbPassword == "" { + err = errors.New("invalid database password") + var errStatement string + if !ok { + errStatement = "Type assertion failed for database password. Expected a string value" + } else { + errStatement = "Empty database password" + } + log.Error(err, errStatement) + } + // Type assertion + SSHPublicKey, ok := reqData[NDB_PARAM_SSH_PUBLIC_KEY].(string) + if !ok || SSHPublicKey == "" { + err = errors.New("invalid ssh public key") + var errStatement string + if !ok { + errStatement = "Type assertion failed for SSHPublicKey. Expected a string value" + } else { + errStatement = "Empty SSHPublicKey" + } + log.Error(err, errStatement) + } + // Creating a provisioning request based on the database type req = &DatabaseProvisionRequest{ DatabaseType: GetDatabaseEngineName(dbSpec.Instance.Type), @@ -64,7 +89,7 @@ func GenerateProvisioningRequest(ctx context.Context, ndbclient *ndbclient.NDBCl CreateDbServer: true, NodeCount: 1, NxClusterId: dbSpec.NDB.ClusterId, - SSHPublicKey: dbSpec.NDB.Credentials.SSHPublicKey, + SSHPublicKey: SSHPublicKey, Clustered: false, AutoTuneStagingDrive: true, TimeMachineInfo: TimeMachineInfo{ @@ -114,7 +139,7 @@ func GenerateProvisioningRequest(ctx context.Context, ndbclient *ndbclient.NDBCl }, { Name: "db_password", - Value: dbSpec.Instance.Password, + Value: dbPassword, }, }, Nodes: []Node{ diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 1a88dee2..d1ad9113 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -25,21 +25,6 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Credentials) DeepCopyInto(out *Credentials) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Credentials. -func (in *Credentials) DeepCopy() *Credentials { - if in == nil { - return nil - } - out := new(Credentials) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Database) DeepCopyInto(out *Database) { *out = *in @@ -155,7 +140,6 @@ func (in *Instance) DeepCopy() *Instance { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NDB) DeepCopyInto(out *NDB) { *out = *in - out.Credentials = in.Credentials } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NDB. diff --git a/config/crd/bases/ndb.nutanix.com_databases.yaml b/config/crd/bases/ndb.nutanix.com_databases.yaml index 95f14b44..1f98f8c4 100644 --- a/config/crd/bases/ndb.nutanix.com_databases.yaml +++ b/config/crd/bases/ndb.nutanix.com_databases.yaml @@ -54,6 +54,10 @@ spec: databaseInstance: description: Database instance specific details properties: + credentialSecret: + description: Name of the secret holding the credentials for the + database instance (password and ssh key) + type: string databaseInstanceName: default: database_instance_name description: Name of the database instance @@ -69,9 +73,6 @@ spec: type: string minItems: 1 type: array - password: - description: Password of the database instance - type: string profiles: properties: compute: @@ -119,9 +120,9 @@ spec: - mongodb type: string required: + - credentialSecret - databaseInstanceName - databaseNames - - password - type type: object ndb: @@ -129,27 +130,19 @@ spec: properties: clusterId: type: string - credentials: - properties: - loginUser: - description: Username for NDB - type: string - password: - description: Password for NDB - type: string - sshPublicKey: - description: SSH public key for the database vm - type: string - required: - - loginUser - - password - - sshPublicKey - type: object + credentialSecret: + description: Name of the secret holding the credentials for NDB + (username and password) + type: string server: type: string + skipCertificateVerification: + default: false + description: Skip server's certificate and hostname verification + type: boolean required: - clusterId - - credentials + - credentialSecret - server type: object required: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 54b64ffa..049ed1b9 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -31,3 +31,24 @@ rules: - get - patch - update +- apiGroups: + - "" + resources: + - services + - endpoints + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch diff --git a/config/samples/ndb_v1alpha1_database.yaml b/config/samples/ndb_v1alpha1_database.yaml index 01b48a85..1e17ef2d 100644 --- a/config/samples/ndb_v1alpha1_database.yaml +++ b/config/samples/ndb_v1alpha1_database.yaml @@ -5,19 +5,16 @@ metadata: spec: ndb: clusterId: "Nutanix Cluster Id" - credentials: - loginUser: admin - password: "NDB Password" - sshPublicKey: "SSH Key" + credentialSecret : ndb-secret-name server: https://[NDB IP]:8443/era/v0.9 - + skipCertificateVerification: true databaseInstance: databaseInstanceName: "Database Instance Name" databaseNames: - database_one - database_two - database_three - password: + credentialSecret: db-instance-secret-name size: 10 timezone: "UTC" type: postgres diff --git a/controllers/database_controller.go b/controllers/database_controller.go index 52779a85..2e941f44 100644 --- a/controllers/database_controller.go +++ b/controllers/database_controller.go @@ -23,6 +23,7 @@ package controllers import ( "context" + "fmt" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -68,11 +69,23 @@ func (r *DatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c log.Info("Database CR Status: " + util.ToString(database.Status)) - spec := database.Spec - server := spec.NDB - ndbClient := ndbclient.NewNDBClient(server.Credentials.LoginUser, server.Credentials.Password, server.Server) - - // log.Info(fmt.Sprintf("Finalizers: %v", database.Finalizers)) + NDBInfo := database.Spec.NDB + username, password, caCert, err := r.getNDBCredentials(ctx, NDBInfo.CredentialSecret, req.Namespace) + if err != nil || username == "" || password == "" { + var errStatement string + if err == nil { + errStatement = "NDB username or password cannot be empty" + err = fmt.Errorf("empty NDB credentials") + } else { + errStatement = "An error occured while fetching the NDB Secrets" + } + log.Error(err, errStatement) + return r.requeueOnErr(err) + } + if caCert == "" { + log.Info("Ca-cert not found, falling back to host's HTTPs certs.") + } + ndbClient := ndbclient.NewNDBClient(username, password, NDBInfo.Server, caCert, NDBInfo.SkipCertificateVerification) // Examine DeletionTimestamp to determine if object is under deletion if database.ObjectMeta.DeletionTimestamp.IsZero() { diff --git a/controllers/database_controller_test.go b/controllers/database_controller_test.go index 5f191fb8..ee02acaa 100644 --- a/controllers/database_controller_test.go +++ b/controllers/database_controller_test.go @@ -25,6 +25,8 @@ import ( "context" "time" + b64 "encoding/base64" + ndbv1alpha1 "github.com/nutanix-cloud-native/ndb-operator/api/v1alpha1" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -45,20 +47,30 @@ var _ = Describe("Database controller", func() { ctx := context.Background() - database := &ndbv1alpha1.Database{} + const ndbSecretName = "test-ndb-secret-name" + const instanceSecretName = "test-instance-secret-name" + const username = "test-username" + const password = "test-password" + const sshPublicKey = "test-ssh-key" - namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: namespaceName, - Namespace: namespaceName, - }, - } + var namespace *corev1.Namespace + var ndbSecret *corev1.Secret + var instanceSecret *corev1.Secret + database := &ndbv1alpha1.Database{} typeNamespaceName := types.NamespacedName{Name: DatabaseName, Namespace: namespaceName} typeNamespaceNameForService := types.NamespacedName{Name: DatabaseName + "-svc", Namespace: namespaceName} + ndbSecretTypeNamespaceName := types.NamespacedName{Name: ndbSecretName, Namespace: namespaceName} + instanceSecretTypeNamespaceName := types.NamespacedName{Name: instanceSecretName, Namespace: namespaceName} BeforeEach(func() { By("Creating the Namespace to perform the tests") + namespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespaceName, + Namespace: namespaceName, + }, + } err := k8sClient.Create(ctx, namespace) Expect(err).To(Not(HaveOccurred())) @@ -69,24 +81,56 @@ var _ = Describe("Database controller", func() { }, Spec: ndbv1alpha1.DatabaseSpec{ NDB: ndbv1alpha1.NDB{ - ClusterId: "abcd", - Credentials: ndbv1alpha1.Credentials{ - LoginUser: "username", - Password: "password", - SSHPublicKey: "abcd", - }, - Server: testNDBServer, + ClusterId: "abcd", + CredentialSecret: ndbSecretName, + Server: testNDBServer, }, Instance: ndbv1alpha1.Instance{ DatabaseInstanceName: DatabaseName, DatabaseNames: []string{"database_1"}, - Password: "abcd", + CredentialSecret: instanceSecretName, Size: 10, TimeZone: "UTC", Type: "postgres", }, }, } + + ndbSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: ndbSecretName, + Namespace: namespaceName, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + ndbv1alpha1.NDB_PARAM_USERNAME: []byte(b64.StdEncoding.EncodeToString([]byte(username))), + ndbv1alpha1.NDB_PARAM_PASSWORD: []byte(b64.StdEncoding.EncodeToString([]byte(password))), + }, + } + instanceSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: instanceSecretName, + Namespace: namespaceName, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + ndbv1alpha1.NDB_PARAM_PASSWORD: []byte(b64.StdEncoding.EncodeToString([]byte(password))), + ndbv1alpha1.NDB_PARAM_SSH_PUBLIC_KEY: []byte(b64.StdEncoding.EncodeToString([]byte(sshPublicKey))), + }, + } + + By("Creating the secrets") + err = k8sClient.Get(ctx, ndbSecretTypeNamespaceName, ndbSecret) + if err != nil && errors.IsNotFound(err) { + err = k8sClient.Create(ctx, ndbSecret) + Expect(err).To(Not(HaveOccurred())) + } + err = k8sClient.Get(ctx, instanceSecretTypeNamespaceName, instanceSecret) + if err != nil && errors.IsNotFound(err) { + err = k8sClient.Create(ctx, instanceSecret) + Expect(err).To(Not(HaveOccurred())) + } + }) AfterEach(func() { diff --git a/controllers/database_reconciler_helpers.go b/controllers/database_reconciler_helpers.go index 8c36e706..1183f3ba 100644 --- a/controllers/database_reconciler_helpers.go +++ b/controllers/database_reconciler_helpers.go @@ -18,6 +18,7 @@ package controllers import ( "context" + "fmt" "math" "time" @@ -198,7 +199,25 @@ func (r *DatabaseReconciler) handleSync(ctx context.Context, database *ndbv1alph // DB Status.Status is empty => Provision a DB log.Info("Provisioning a database instance with NDB.") - generatedReq, err := ndbv1alpha1.GenerateProvisioningRequest(ctx, ndbClient, database.Spec) + dbPassword, sshPublicKey, err := r.getDatabaseInstanceCredentials(ctx, database.Spec.Instance.CredentialSecret, req.Namespace) + if err != nil || dbPassword == "" || sshPublicKey == "" { + var errStatement string + if err == nil { + errStatement = "Database instance password and ssh key cannot be empty" + err = fmt.Errorf("empty DB instance credentials") + } else { + errStatement = "An error occured while fetching the DB Instance Secrets" + } + log.Error(err, errStatement) + return r.requeueOnErr(err) + } + + reqData := map[string]interface{}{ + ndbv1alpha1.NDB_PARAM_PASSWORD: dbPassword, + ndbv1alpha1.NDB_PARAM_SSH_PUBLIC_KEY: sshPublicKey, + } + + generatedReq, err := ndbv1alpha1.GenerateProvisioningRequest(ctx, ndbClient, database.Spec, reqData) if err != nil { log.Error(err, "Could not generate provisioning request, requeuing.") return r.requeueOnErr(err) @@ -379,3 +398,36 @@ func (r *DatabaseReconciler) setupEndpoints(ctx context.Context, database *ndbv1 log.Info("Returning from database_reconciler_helpers.setupEndpoints") return } + +// Returns the credentials(username, password and caCertificate) for NDB +// Returns an error if reading the secret containing credentials fails +func (r *DatabaseReconciler) getNDBCredentials(ctx context.Context, name, namespace string) (username, password, caCert string, err error) { + log := ctrllog.FromContext(ctx) + log.Info("Entered database_reconciler_helpers.getNDBCredentials") + secretDataMap, err := util.GetAllDataFromSecret(ctx, r.Client, name, namespace) + if err != nil { + log.Error(err, "Error occured in util.GetAllDataFromSecret while fetching all NDB secrets", "Secret Name", name, "Namespace", namespace) + return + } + username = string(secretDataMap[ndbv1alpha1.SECRET_DATA_KEY_USERNAME]) + password = string(secretDataMap[ndbv1alpha1.SECRET_DATA_KEY_PASSWORD]) + caCert = string(secretDataMap[ndbv1alpha1.SECRET_DATA_KEY_CA_CERTIFICATE]) + log.Info("Returning from database_reconciler_helpers.getNDBCredentials") + return +} + +// Returns the credentials(password and ssh public key) for NDB +// Returns an error if reading the secret containing credentials fails +func (r *DatabaseReconciler) getDatabaseInstanceCredentials(ctx context.Context, name, namespace string) (password, sshPublicKey string, err error) { + log := ctrllog.FromContext(ctx) + log.Info("Entered database_reconciler_helpers.getDatabaseInstanceCredentials") + secretDataMap, err := util.GetAllDataFromSecret(ctx, r.Client, name, namespace) + if err != nil { + log.Error(err, "Error occured in util.GetAllDataFromSecret while fetching all database instance secrets", "Secret Name", name, "Namespace", namespace) + return + } + password = string(secretDataMap[ndbv1alpha1.SECRET_DATA_KEY_PASSWORD]) + sshPublicKey = string(secretDataMap[ndbv1alpha1.SECRET_DATA_KEY_SSH_PUBLIC_KEY]) + log.Info("Returning from database_reconciler_helpers.getDatabaseInstanceCredentials") + return +} diff --git a/ndbclient/ndbclient.go b/ndbclient/ndbclient.go index 5bfd15fb..884cc86e 100644 --- a/ndbclient/ndbclient.go +++ b/ndbclient/ndbclient.go @@ -19,6 +19,7 @@ package ndbclient import ( "bytes" "crypto/tls" + "crypto/x509" "encoding/json" "net/http" ) @@ -30,11 +31,17 @@ type NDBClient struct { client *http.Client } -func NewNDBClient(username, password, url string) *NDBClient { - transport := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, +func NewNDBClient(username, password, url, caCert string, skipVerify bool) *NDBClient { + + TLSClientConfig := &tls.Config{InsecureSkipVerify: skipVerify} + if caCert != "" { + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM([]byte(caCert)) + TLSClientConfig.RootCAs = caCertPool + } + client := &http.Client{ + Transport: &http.Transport{TLSClientConfig: TLSClientConfig}, } - client := &http.Client{Transport: transport} return &NDBClient{username, password, url, client} } diff --git a/test/ndb_api_helpers_test.go b/test/ndb_api_helpers_test.go index cf066ee9..45b98b6a 100644 --- a/test/ndb_api_helpers_test.go +++ b/test/ndb_api_helpers_test.go @@ -33,7 +33,7 @@ func TestGetNoneTimeMachineSLA(t *testing.T) { //Set server := GetServerTestHelper(t) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test sla, err := v1alpha1.GetNoneTimeMachineSLA(context.Background(), ndbclient) @@ -83,7 +83,7 @@ func TestGetNoneTimeMachineSLAReturnsErrorWhenNoneTimeMachineNotFound(t *testing } })) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test sla, err := v1alpha1.GetNoneTimeMachineSLA(context.Background(), ndbclient) @@ -101,7 +101,7 @@ func TestGetOOBProfiles(t *testing.T) { //Set server := GetServerTestHelper(t) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test dbTypes := []string{"postgres", "mysql", "mongodb"} @@ -135,7 +135,7 @@ func TestGetOOBProfilesOnlyGetsTheSmallOOBComputeProfile(t *testing.T) { //Set server := GetServerTestHelper(t) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test dbTypes := []string{"postgres", "mysql", "mongodb"} @@ -172,7 +172,7 @@ func TestGetOOBProfilesReturnsErrorWhenSomeProfileIsNotFound(t *testing.T) { } })) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test dbTypes := []string{"postgres", "mysql", "mongodb"} @@ -238,20 +238,16 @@ func TestGenerateProvisioningRequestReturnsErrorIfNoneTMNotFound(t *testing.T) { } })) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test dbTypes := []string{"postgres", "mysql", "mongodb"} for _, dbType := range dbTypes { dbSpec := v1alpha1.DatabaseSpec{ NDB: v1alpha1.NDB{ - Server: "abc.def.ghi.jkl/v99/api", - ClusterId: "test-cluster-id", - Credentials: v1alpha1.Credentials{ - LoginUser: "test-username", - Password: "test-password", - SSHPublicKey: "==qwertyuiopasdfghjklzxcvbnm==", - }, + Server: "abc.def.ghi.jkl/v99/api", + ClusterId: "test-cluster-id", + CredentialSecret: "qwertyuiop", }, Instance: v1alpha1.Instance{ DatabaseNames: []string{"a", "b", "c", "d"}, @@ -261,7 +257,12 @@ func TestGenerateProvisioningRequestReturnsErrorIfNoneTMNotFound(t *testing.T) { }, } - _, err := v1alpha1.GenerateProvisioningRequest(context.Background(), ndbclient, dbSpec) + reqData := map[string]interface{}{ + v1alpha1.NDB_PARAM_PASSWORD: "qwerty", + v1alpha1.NDB_PARAM_SSH_PUBLIC_KEY: "qwertyuiop", + } + + _, err := v1alpha1.GenerateProvisioningRequest(context.Background(), ndbclient, dbSpec, reqData) t.Log(err) if err == nil { t.Errorf("GenerateProvisioningRequest should return an error when NONE time machine is not found") @@ -331,20 +332,16 @@ func TestGenerateProvisioningRequestReturnsErrorIfProfilesNotFound(t *testing.T) } })) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test dbTypes := []string{"postgres", "mysql", "mongodb"} for _, dbType := range dbTypes { dbSpec := v1alpha1.DatabaseSpec{ NDB: v1alpha1.NDB{ - Server: "abc.def.ghi.jkl/v99/api", - ClusterId: "test-cluster-id", - Credentials: v1alpha1.Credentials{ - LoginUser: "test-username", - Password: "test-password", - SSHPublicKey: "==qwertyuiopasdfghjklzxcvbnm==", - }, + Server: "abc.def.ghi.jkl/v99/api", + ClusterId: "test-cluster-id", + CredentialSecret: "test-credential-secret-name", }, Instance: v1alpha1.Instance{ DatabaseNames: []string{"a", "b", "c", "d"}, @@ -354,7 +351,12 @@ func TestGenerateProvisioningRequestReturnsErrorIfProfilesNotFound(t *testing.T) }, } - _, err := v1alpha1.GenerateProvisioningRequest(context.Background(), ndbclient, dbSpec) + reqData := map[string]interface{}{ + v1alpha1.NDB_PARAM_PASSWORD: "qwerty", + v1alpha1.NDB_PARAM_SSH_PUBLIC_KEY: "qwertyuiop", + } + + _, err := v1alpha1.GenerateProvisioningRequest(context.Background(), ndbclient, dbSpec, reqData) t.Log(err) if err == nil { t.Errorf("GenerateProvisioningRequest should return an error when profiles are not found") @@ -366,20 +368,16 @@ func TestGenerateProvisioningRequest(t *testing.T) { //Set server := GetServerTestHelper(t) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test dbTypes := []string{"postgres", "mysql", "mongodb"} for _, dbType := range dbTypes { dbSpec := v1alpha1.DatabaseSpec{ NDB: v1alpha1.NDB{ - Server: "abc.def.ghi.jkl/v99/api", - ClusterId: "test-cluster-id", - Credentials: v1alpha1.Credentials{ - LoginUser: "test-username", - Password: "test-password", - SSHPublicKey: "==qwertyuiopasdfghjklzxcvbnm==", - }, + Server: "abc.def.ghi.jkl/v99/api", + ClusterId: "test-cluster-id", + CredentialSecret: "test-credential-secret-name", }, Instance: v1alpha1.Instance{ DatabaseNames: []string{"a", "b", "c", "d"}, @@ -389,7 +387,12 @@ func TestGenerateProvisioningRequest(t *testing.T) { }, } - request, _ := v1alpha1.GenerateProvisioningRequest(context.Background(), ndbclient, dbSpec) + reqData := map[string]interface{}{ + v1alpha1.NDB_PARAM_PASSWORD: "qwerty", + v1alpha1.NDB_PARAM_SSH_PUBLIC_KEY: "qwertyuiop", + } + + request, _ := v1alpha1.GenerateProvisioningRequest(context.Background(), ndbclient, dbSpec, reqData) //Assert if request.DatabaseType != v1alpha1.GetDatabaseEngineName(dbType) { @@ -413,3 +416,191 @@ func TestGenerateProvisioningRequest(t *testing.T) { } } } + +func TestGenerateProvisioningRequestReturnsErrorIfDBPasswordIsEmpty(t *testing.T) { + + //Set + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !checkAuthTestHelper(r) { + t.Errorf("Invalid Authentication Credentials") + } else { + var response interface{} + if r.URL.Path == "/profiles" { + response = []v1alpha1.ProfileResponse{ + { + Id: "1", + Name: "a", + Type: "test type", + EngineType: "test engine", + LatestVersionId: "v-id-1", + Topology: "test topology", + }, + } + } else if r.URL.Path == "/slas" { + response = []v1alpha1.SLAResponse{ + { + Id: "sla-1-id", + Name: "SLA 1", + UniqueName: "SLA 1 Unique Name", + Description: "SLA 1 Description", + DailyRetention: 1, + WeeklyRetention: 2, + MonthlyRetention: 3, + QuarterlyRetention: 4, + YearlyRetention: 5, + }, + { + Id: "sla-2-id", + Name: "SLA 2", + UniqueName: "SLA 2 Unique Name", + Description: "SLA 2 Description", + DailyRetention: 1, + WeeklyRetention: 2, + MonthlyRetention: 3, + QuarterlyRetention: 4, + YearlyRetention: 5, + }, + { + Id: NONE_SLA_ID, + Name: v1alpha1.SLA_NAME_NONE, + UniqueName: "SLA 3 Unique Name", + Description: "SLA 3 Description", + DailyRetention: 1, + WeeklyRetention: 2, + MonthlyRetention: 3, + QuarterlyRetention: 4, + YearlyRetention: 5, + }, + } + } + resp, _ := json.Marshal(response) + w.WriteHeader(http.StatusOK) + w.Write(resp) + } + })) + defer server.Close() + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) + + //Test + dbTypes := []string{"postgres", "mysql", "mongodb"} + for _, dbType := range dbTypes { + dbSpec := v1alpha1.DatabaseSpec{ + NDB: v1alpha1.NDB{ + Server: "abc.def.ghi.jkl/v99/api", + ClusterId: "test-cluster-id", + CredentialSecret: "test-credential-secret-name", + }, + Instance: v1alpha1.Instance{ + DatabaseNames: []string{"a", "b", "c", "d"}, + Type: dbType, + DatabaseInstanceName: dbType + "-instance-test", + TimeZone: "UTC", + }, + } + + reqData := map[string]interface{}{ + v1alpha1.NDB_PARAM_PASSWORD: "", + v1alpha1.NDB_PARAM_SSH_PUBLIC_KEY: "qwertyuiop", + } + + _, err := v1alpha1.GenerateProvisioningRequest(context.Background(), ndbclient, dbSpec, reqData) + t.Log(err) + if err == nil { + t.Errorf("GenerateProvisioningRequest should return an error when db password is empty") + } + } +} + +func TestGenerateProvisioningRequestReturnsErrorIfSSHKeyIsEmpty(t *testing.T) { + + //Set + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !checkAuthTestHelper(r) { + t.Errorf("Invalid Authentication Credentials") + } else { + var response interface{} + if r.URL.Path == "/profiles" { + response = []v1alpha1.ProfileResponse{ + { + Id: "1", + Name: "a", + Type: "test type", + EngineType: "test engine", + LatestVersionId: "v-id-1", + Topology: "test topology", + }, + } + } else if r.URL.Path == "/slas" { + response = []v1alpha1.SLAResponse{ + { + Id: "sla-1-id", + Name: "SLA 1", + UniqueName: "SLA 1 Unique Name", + Description: "SLA 1 Description", + DailyRetention: 1, + WeeklyRetention: 2, + MonthlyRetention: 3, + QuarterlyRetention: 4, + YearlyRetention: 5, + }, + { + Id: "sla-2-id", + Name: "SLA 2", + UniqueName: "SLA 2 Unique Name", + Description: "SLA 2 Description", + DailyRetention: 1, + WeeklyRetention: 2, + MonthlyRetention: 3, + QuarterlyRetention: 4, + YearlyRetention: 5, + }, + { + Id: NONE_SLA_ID, + Name: v1alpha1.SLA_NAME_NONE, + UniqueName: "SLA 3 Unique Name", + Description: "SLA 3 Description", + DailyRetention: 1, + WeeklyRetention: 2, + MonthlyRetention: 3, + QuarterlyRetention: 4, + YearlyRetention: 5, + }, + } + } + resp, _ := json.Marshal(response) + w.WriteHeader(http.StatusOK) + w.Write(resp) + } + })) + defer server.Close() + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) + + //Test + dbTypes := []string{"postgres", "mysql", "mongodb"} + for _, dbType := range dbTypes { + dbSpec := v1alpha1.DatabaseSpec{ + NDB: v1alpha1.NDB{ + Server: "abc.def.ghi.jkl/v99/api", + ClusterId: "test-cluster-id", + CredentialSecret: "test-credential-secret-name", + }, + Instance: v1alpha1.Instance{ + DatabaseNames: []string{"a", "b", "c", "d"}, + Type: dbType, + DatabaseInstanceName: dbType + "-instance-test", + TimeZone: "UTC", + }, + } + + reqData := map[string]interface{}{ + v1alpha1.NDB_PARAM_PASSWORD: "qwertyuiop", + v1alpha1.NDB_PARAM_SSH_PUBLIC_KEY: "", + } + + _, err := v1alpha1.GenerateProvisioningRequest(context.Background(), ndbclient, dbSpec, reqData) + t.Log(err) + if err == nil { + t.Errorf("GenerateProvisioningRequest should return an error when ssh key is empty") + } + } +} diff --git a/test/ndb_api_test.go b/test/ndb_api_test.go index 8ab0ee18..669b062f 100644 --- a/test/ndb_api_test.go +++ b/test/ndb_api_test.go @@ -39,7 +39,7 @@ func TestGetAllSLAs(t *testing.T) { //Set server := GetServerTestHelper(t) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test value, _ := v1alpha1.GetAllSLAs(context.Background(), ndbclient) @@ -58,7 +58,7 @@ func TestGetAllSLAsThrowsErrorWhenClientReturnsNon200(t *testing.T) { } })) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test _, err := v1alpha1.GetAllSLAs(context.Background(), ndbclient) @@ -71,7 +71,7 @@ func TestGetAllProfiles(t *testing.T) { //Set server := GetServerTestHelper(t) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test value, _ := v1alpha1.GetAllProfiles(context.Background(), ndbclient) @@ -91,7 +91,7 @@ func TestGetAllProfileThrowsErrorWhenClientReturnsNon200(t *testing.T) { } })) defer server.Close() - ndbclient := ndbclient.NewNDBClient("username", "password", server.URL) + ndbclient := ndbclient.NewNDBClient("username", "password", server.URL, "", true) //Test _, err := v1alpha1.GetAllProfiles(context.Background(), ndbclient) diff --git a/test/secret_test.go b/test/secret_test.go new file mode 100644 index 00000000..875f8af4 --- /dev/null +++ b/test/secret_test.go @@ -0,0 +1,132 @@ +package test + +import ( + "context" + + ndbv1alpha1 "github.com/nutanix-cloud-native/ndb-operator/api/v1alpha1" + "github.com/nutanix-cloud-native/ndb-operator/util" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +var _ = Describe("Test Secret Utility", func() { + Context("util.get_data_from_secret test", func() { + const ndbSecretName = "test-ndb-secret-name" + const instanceSecretName = "test-instance-secret-name" + const namespaceName = "test-namespace" + + const username = "test-username" + const password = "test-password" + const sshPublicKey = "test-ssh-key" + + ctx := context.Background() + + var namespace *corev1.Namespace + var ndbSecret *corev1.Secret + var instanceSecret *corev1.Secret + + ndbSecretTypeNamespaceName := types.NamespacedName{Name: ndbSecretName, Namespace: namespaceName} + instanceSecretTypeNamespaceName := types.NamespacedName{Name: instanceSecretName, Namespace: namespaceName} + + BeforeEach(func() { + namespace = &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespaceName, + Namespace: namespaceName, + }, + } + ndbSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: ndbSecretName, + Namespace: namespaceName, + }, + Type: corev1.SecretTypeOpaque, + StringData: map[string]string{ + ndbv1alpha1.NDB_PARAM_USERNAME: username, + ndbv1alpha1.NDB_PARAM_PASSWORD: password, + }, + } + instanceSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: instanceSecretName, + Namespace: namespaceName, + }, + Type: corev1.SecretTypeOpaque, + StringData: map[string]string{ + ndbv1alpha1.NDB_PARAM_PASSWORD: password, + ndbv1alpha1.NDB_PARAM_SSH_PUBLIC_KEY: sshPublicKey, + }, + } + By("Creating the Namespace to perform the tests") + err := k8sClient.Create(ctx, namespace) + Expect(err).To(Not(HaveOccurred())) + + By("Creating the secrets") + err = k8sClient.Get(ctx, ndbSecretTypeNamespaceName, ndbSecret) + if err != nil && errors.IsNotFound(err) { + err = k8sClient.Create(ctx, ndbSecret) + Expect(err).To(Not(HaveOccurred())) + } + err = k8sClient.Get(ctx, instanceSecretTypeNamespaceName, instanceSecret) + if err != nil && errors.IsNotFound(err) { + err = k8sClient.Create(ctx, instanceSecret) + Expect(err).To(Not(HaveOccurred())) + } + + }) + + AfterEach(func() { + + By("Deleting the Namespace to perform the tests") + _ = k8sClient.Delete(ctx, namespace) + + }) + + It("should get the data", func() { + By("fetching and checking the NDB secrets") + foundNDBSecret := &corev1.Secret{} + err := k8sClient.Get(ctx, ndbSecretTypeNamespaceName, foundNDBSecret) + Expect(err).To(Not(HaveOccurred())) + //Checking data from ndb secret + // username + data, err := util.GetDataFromSecret(ctx, k8sClient, ndbSecretName, namespaceName, ndbv1alpha1.SECRET_DATA_KEY_USERNAME) + Expect(err).To(Not(HaveOccurred())) + Expect(data).To(Equal(username)) + // password + data, err = util.GetDataFromSecret(ctx, k8sClient, ndbSecretName, namespaceName, ndbv1alpha1.SECRET_DATA_KEY_PASSWORD) + Expect(err).To(Not(HaveOccurred())) + Expect(data).To(Equal(password)) + + By("fetching and checking the instance secrets") + foundInstanceSecret := &corev1.Secret{} + err = k8sClient.Get(ctx, instanceSecretTypeNamespaceName, foundInstanceSecret) + Expect(err).To(Not(HaveOccurred())) + //Checking data from instance secret + // password + data, err = util.GetDataFromSecret(ctx, k8sClient, instanceSecretName, namespaceName, ndbv1alpha1.SECRET_DATA_KEY_PASSWORD) + Expect(err).To(Not(HaveOccurred())) + Expect(data).To(Equal(password)) + // ssh key + data, err = util.GetDataFromSecret(ctx, k8sClient, instanceSecretName, namespaceName, ndbv1alpha1.SECRET_DATA_KEY_SSH_PUBLIC_KEY) + Expect(err).To(Not(HaveOccurred())) + Expect(data).To(Equal(sshPublicKey)) + + By("returning error when secrets are not present") + // To simulate the situation when no secrets are present + _ = k8sClient.Delete(ctx, ndbSecret) + _ = k8sClient.Delete(ctx, instanceSecret) + _, err = util.GetDataFromSecret(ctx, k8sClient, ndbSecretName, namespaceName, ndbv1alpha1.SECRET_DATA_KEY_USERNAME) + Expect(err).To(HaveOccurred()) + _, err = util.GetDataFromSecret(ctx, k8sClient, ndbSecretName, namespaceName, ndbv1alpha1.SECRET_DATA_KEY_PASSWORD) + Expect(err).To(HaveOccurred()) + _, err = util.GetDataFromSecret(ctx, k8sClient, instanceSecretName, namespaceName, ndbv1alpha1.SECRET_DATA_KEY_PASSWORD) + Expect(err).To(HaveOccurred()) + _, err = util.GetDataFromSecret(ctx, k8sClient, instanceSecretName, namespaceName, ndbv1alpha1.SECRET_DATA_KEY_SSH_PUBLIC_KEY) + Expect(err).To(HaveOccurred()) + }) + }) +}) diff --git a/test/suite_test.go b/test/suite_test.go new file mode 100644 index 00000000..f8160718 --- /dev/null +++ b/test/suite_test.go @@ -0,0 +1,86 @@ +/* +Copyright 2021-2022 Nutanix, Inc. + +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. +*/ + +/* +GENERATED by operator-sdk +*/ + +package test + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + "sigs.k8s.io/controller-runtime/pkg/envtest/printer" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + ndbv1alpha1 "github.com/nutanix-cloud-native/ndb-operator/api/v1alpha1" + //+kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecsWithDefaultAndCustomReporters(t, + "Controller Suite", + []Reporter{printer.NewlineReporter{}}) +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = ndbv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + +}, 60) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/util/secret.go b/util/secret.go new file mode 100644 index 00000000..e8f37997 --- /dev/null +++ b/util/secret.go @@ -0,0 +1,46 @@ +package util + +import ( + "context" + "fmt" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" +) + +// Returns the data for a key present in the given secret and namespace combination +// returns an error if either the secret or the data for the given key is not found +func GetDataFromSecret(ctx context.Context, client client.Client, name, namespace, key string) (data string, err error) { + log := ctrllog.FromContext(ctx) + log.Info("Entered util.GetDataFromSecret", "key", key, "Secret Name", name, "Secret Namespace", namespace) + secretData, err := GetAllDataFromSecret(ctx, client, name, namespace) + if err != nil { + log.Error(err, "Error occured while trying to read secret", "Secret Name", name, "Secret Namespace", namespace) + } else { + if val, ok := secretData[key]; ok { + data = string(val) + log.Info("Returning from util.GetDataFromSecret") + } else { + err = fmt.Errorf(fmt.Sprintf("Key '%s' not present in the secret '%s' in namespace '%s'", key, name, namespace)) + } + } + return +} + +// Returns all the data in the given secret and namespace combination +// returns an error if the secret is not found +func GetAllDataFromSecret(ctx context.Context, client client.Client, name, namespace string) (data map[string][]byte, err error) { + log := ctrllog.FromContext(ctx) + log.Info("Entered util.GetAllDataFromSecret", "Secret Name", name, "Secret Namespace", namespace) + secret := &v1.Secret{} + err = client.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, secret) + if err != nil { + log.Error(err, "Error occured while trying to read secret", "Secret Name", name, "Secret Namespace", namespace) + } else { + data = secret.Data + log.Info("Returning from util.GetAllDataFromSecret") + } + return +}