<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ajoshi24</id>
	<title>Expertiza_Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Ajoshi24"/>
	<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=Special:Contributions/Ajoshi24"/>
	<updated>2026-06-04T13:46:29Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.41.0</generator>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=150356</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=150356"/>
		<updated>2023-05-02T21:28:26Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
Employing Clean Code Practices + Design Patterns:&lt;br /&gt;
&lt;br /&gt;
DRY (Do Not Repeat Yourself):&lt;br /&gt;
The initial draft of using 4 if &amp;amp; else statements for each of the profiles (namely: software, compute, network, and storage) and performing the same checks and code again proved to be in direct opposition to the DRY approach whose repetitiveness can be seen from the below snippet:&lt;br /&gt;
::::&lt;br /&gt;
:::::://Custom Software Profile Check and overriding the default values&lt;br /&gt;
::::::softwareProfile := dbSpec.Instance.Profiles.Software&lt;br /&gt;
::::::if softwareProfile == (Profile{}) {&lt;br /&gt;
::::::::log.Info(&amp;quot;No enrichment for software profiles as no custom profile received for Software. Hence, proceeding with default OOB software profile&amp;quot;)&lt;br /&gt;
::::::} else {&lt;br /&gt;
::::::::isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, softwareProfile, PROFILE_TYPE_SOFTWARE)&lt;br /&gt;
::::::::if errorThroughChecks != nil {&lt;br /&gt;
:::::::::://log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
::::::::::return errorThroughChecks&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::::if isValidProfile {&lt;br /&gt;
::::::::::profilesMap[PROFILE_TYPE_SOFTWARE] = matchedProfile&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::}&lt;br /&gt;
&lt;br /&gt;
:::::://Custom Compute Profile Check and overriding the default values&lt;br /&gt;
::::::computeProfile := dbSpec.Instance.Profiles.Compute&lt;br /&gt;
::::::if computeProfile == (Profile{}) {&lt;br /&gt;
::::::::log.Info(&amp;quot;No enrichment for compute profiles as no custom profile received for Compute. Hence, proceeding with default OOB compute profile&amp;quot;)&lt;br /&gt;
::::::} else {&lt;br /&gt;
::::::::isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, genericProfiles, computeProfile, PROFILE_TYPE_COMPUTE)&lt;br /&gt;
::::::::if errorThroughChecks != nil {&lt;br /&gt;
:::::::::://log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
::::::::::return errorThroughChecks&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::::if isValidProfile {&lt;br /&gt;
::::::::::profilesMap[PROFILE_TYPE_COMPUTE] = matchedProfile&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::}&lt;br /&gt;
&lt;br /&gt;
:::::://Custom Network Profile Check and overriding the default values&lt;br /&gt;
::::::networkProfile := dbSpec.Instance.Profiles.Network&lt;br /&gt;
::::::if networkProfile == (Profile{}) {&lt;br /&gt;
::::::::log.Info(&amp;quot;No enrichment for network profiles as no custom profile received for Network. Hence, proceeding with default OOB network profile&amp;quot;)&lt;br /&gt;
::::::} else {&lt;br /&gt;
::::::::isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, networkProfile, PROFILE_TYPE_NETWORK)&lt;br /&gt;
::::::::if errorThroughChecks != nil {&lt;br /&gt;
:::::::::://log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
::::::::::return errorThroughChecks&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::::if isValidProfile {&lt;br /&gt;
::::::::::profilesMap[PROFILE_TYPE_NETWORK] = matchedProfile&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::}&lt;br /&gt;
&lt;br /&gt;
:::::://Custom DbParam Profile Check and overriding the default values&lt;br /&gt;
::::::dbParamProfile := dbSpec.Instance.Profiles.DbParam&lt;br /&gt;
::::::if dbParamProfile == (Profile{}) {&lt;br /&gt;
::::::::log.Info(&amp;quot;No enrichment for database parameter profiles as no custom profile received for it. Hence, proceeding with default OOB dbParam profile&amp;quot;)&lt;br /&gt;
::::::} else {&lt;br /&gt;
::::::::isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, dbParamProfile, PROFILE_TYPE_DATABASE_PARAMETER)&lt;br /&gt;
::::::::if err != nil {&lt;br /&gt;
:::::::::://log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
::::::::::return errorThroughChecks&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::::if isValidProfile {&lt;br /&gt;
::::::::::profilesMap[PROFILE_TYPE_DATABASE_PARAMETER] = matchedProfile&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::}&lt;br /&gt;
::::}&lt;br /&gt;
::::return&lt;br /&gt;
::}&lt;br /&gt;
&lt;br /&gt;
Hence, we create an array of profiles, and iterating over those profiles and calling a clean modular function by identifying the common key points from the above snippet helps in eliminating code duplication and makes the code more, readable, and open for extension by just adding new profile name to the list of profiles and easily maintainable! This approach can be viewed in EnrichProfilesMap() and delegate functions.&lt;br /&gt;
&lt;br /&gt;
Refactoring + Delegation using Facade Design Pattern:&lt;br /&gt;
&lt;br /&gt;
By default, for populating profiles, GetOOBProfiles() was called which did the task of fetching all profiles and populating default profile values. However, with the advent of custom profiles being provided from YAML, we suggest refactoring GetOOBProfiles() to EnrichProfilesMap() which will perform the same task as GetOOBProfiles did but in addition override the default values to input profiles provided after performing checks such as:&lt;br /&gt;
(1) Emptiness / Null checks for profiles&lt;br /&gt;
(2) Performing matching of the Id/VersionId for the custom profile provided &amp;amp; failing the database provisioning request in case of a match is not found.&lt;br /&gt;
Thus, for each of the checkers, we create modular functions and delegate the task of performing the above-mentioned checks.&lt;br /&gt;
&lt;br /&gt;
The flow proceeds as:&lt;br /&gt;
&lt;br /&gt;
EnrichProfilesMap() &lt;br /&gt;
Performs the task to set default values and override the default values by calling the below functions.  &lt;br /&gt;
&lt;br /&gt;
PerformProfileMatchingAndEnrichProfiles() uses&lt;br /&gt;
[isEmptyProfile() + GetAppropriateProfileForType() + GetTopologyForProfileType() in filtering profiles]&lt;br /&gt;
Once, the user input is received, the input is delegated for checks and performing matching if the profile exists. Additionally, other factory methods are also used for performing the matching of profiles&lt;br /&gt;
&lt;br /&gt;
EnrichProfileMapForProfileType()&lt;br /&gt;
Performs the final overriding of the default profile with the matched profile. Additionally, it cancels the database provisioning request for unmatched profiles.&lt;br /&gt;
&lt;br /&gt;
Moreover, in Tests, new tests have been added which indicate their functionality through their names adhering to the Clean Coding Naming principle:&lt;br /&gt;
TestEnrichAndGetProfilesWhenCustomProfilesMatch()&lt;br /&gt;
TestEnrichAndGetProfilesWhenInvalidCustomProfilesProvided()&lt;br /&gt;
&lt;br /&gt;
Additionally, we can also view the creation of specific functions as the alignment with the Facade Design Pattern that delegates the task of performing specific actions to respective functions. &lt;br /&gt;
&lt;br /&gt;
Alternatively, a closer look at the initial draft and performing Cyclomatic Complexity checks indicate that our approach has breached the threshold value of permissible complexity. Hence, to tackle this problem, we will change the filtering profile functions to perform matching based on Id and further based on versionId and eliminating factory methods like getTopology(), getProfileForType(), and a few checker functions that will aid in reducing the cyclomatic complexity automatically.&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== db-operator\api\v1alpha1db_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
&lt;br /&gt;
=====GenerateProvisioningRequest =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;db-operator\config\samplesdb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :'''&lt;br /&gt;
:// Fetch the OOB profiles for the database&lt;br /&gt;
:profilesMap, err := GetOOBProfiles(ctx, ndbclient, dbSpec.Instance.Type)&lt;br /&gt;
:if err != nil {&lt;br /&gt;
::log.Error(err, &amp;quot;Error occurred while getting OOB profiles&amp;quot;, &amp;quot;database name&amp;quot;, dbSpec.Instance.DatabaseInstanceName, &amp;quot;database type&amp;quot;, dbSpec.Instance.Type)&lt;br /&gt;
::return&lt;br /&gt;
:}&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
:// Fetch upto date profiles for the database&lt;br /&gt;
::profilesMap, err := MatchAndGetProfiles(ctx, ndbclient, dbSpec.Instance.Type, dbSpec.Instance.Profiles)&lt;br /&gt;
::if err != nil {&lt;br /&gt;
:::log.Error(err, &amp;quot;Error occurred while enriching and getting profiles&amp;quot;, &amp;quot;database name&amp;quot;, dbSpec.Instance.DatabaseInstanceName, &amp;quot;database type&amp;quot;, dbSpec.Instance.Type)&lt;br /&gt;
:::return&lt;br /&gt;
::}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;=====Explanation for the change :=====&lt;br /&gt;
Replaced the call for GetOOBProfiles function with MatchAndGetProfiles due to added functionality of populating the profileMap with all the values for the profileType available&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====MatchAndGetProfiles =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' now this function first fetches all the profiles from NDB API. Then for each profile type it checks if a profile is provided in the YAML file or not. If it is provided in the YAML file, then this function calls GetProfileByType and matchProfiles functions to do the further work. If it is not provided in the YAML file, then this function calls PopulateDefaultProfiles function to assign default profiles.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
:func MatchAndGetProfiles(ctx context.Context, ndbclient *ndbclient.NDBClient, dbType string, profiles Profiles) (profileMap map[string]ProfileResponse, err error) {&lt;br /&gt;
&lt;br /&gt;
::log := ctrllog.FromContext(ctx)&lt;br /&gt;
&lt;br /&gt;
::// Map of profile type to profiles&lt;br /&gt;
::profileMap = make(map[string]ProfileResponse)&lt;br /&gt;
&lt;br /&gt;
::allProfiles, err := GetAllProfiles(ctx, ndbclient)&lt;br /&gt;
&lt;br /&gt;
::if err != nil {&lt;br /&gt;
:::return&lt;br /&gt;
::}&lt;br /&gt;
&lt;br /&gt;
::log.Info(&amp;quot;Received Input Profiles = &amp;quot;, &amp;quot;Received Input Profiles&amp;quot;, profiles)&lt;br /&gt;
::profileOptions := [...]string{PROFILE_TYPE_COMPUTE, PROFILE_TYPE_SOFTWARE, PROFILE_TYPE_NETWORK, PROFILE_TYPE_DATABASE_PARAMETER, PROFILE_TYPE_STORAGE}&lt;br /&gt;
::for _, profileType := range profileOptions {&lt;br /&gt;
:::if profiles == (Profiles{}) {&lt;br /&gt;
::::err = PopulateDefaultProfile(ctx, profileMap, profileType, allProfiles, dbType)&lt;br /&gt;
:::} else {&lt;br /&gt;
::::profile := GetProfileByType(profileType, profiles)&lt;br /&gt;
::::err = matchProfiles(ctx, profileType, profile, allProfiles, profileMap, dbType)&lt;br /&gt;
:::}&lt;br /&gt;
:::if err != nil {&lt;br /&gt;
::::return&lt;br /&gt;
:::}&lt;br /&gt;
::}&lt;br /&gt;
&lt;br /&gt;
::return&lt;br /&gt;
&lt;br /&gt;
:}&lt;br /&gt;
&amp;lt;br&amp;gt;=====Explanation for the change :===== We need a function that checks if profiles are there in YAML file for or not and depending on that we need to make the further decisions. This function helps us to check that and delegate the next tasks.&lt;br /&gt;
&lt;br /&gt;
=====matchProfiles =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function does:&lt;br /&gt;
:(a) Id level matching with profiles in allProfiles&lt;br /&gt;
:(b) If Id level match is successful, flow proceeds to match based on versionId&lt;br /&gt;
::When matched, the latestVersionId is overridden with the versionId as it is this attribute while dbProvisioning which is used for&lt;br /&gt;
::profileType versionId.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
:func matchProfiles(ctx context.Context, profileType string, profile Profile, allProfiles []ProfileResponse, profilesMap map[string]ProfileResponse, dbType string) (err error) {&lt;br /&gt;
::log := ctrllog.FromContext(ctx)&lt;br /&gt;
::var idMatchedProfile []ProfileResponse&lt;br /&gt;
::var matchedVersion []Version&lt;br /&gt;
::if isEmptyProfile(profile) {&lt;br /&gt;
:::err = PopulateDefaultProfile(ctx, profilesMap, profileType, allProfiles, dbType)&lt;br /&gt;
:::return&lt;br /&gt;
::}&lt;br /&gt;
::log.Info(&amp;quot;Performing profile matching for profileType =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
::// match based on ID&lt;br /&gt;
::idMatchedProfile = util.Filter(allProfiles, func(p ProfileResponse) bool { return p.Id == profile.Id &amp;amp;&amp;amp; p.Type == profileType })&lt;br /&gt;
::// matching based on versionID&lt;br /&gt;
::if len(idMatchedProfile) &amp;gt; 0 {&lt;br /&gt;
:::matchedVersion = util.Filter(idMatchedProfile[0].Versions, func(versions Version) bool { return versions.Id == profile.VersionId })&lt;br /&gt;
:::// when versionID level match found, override latestVersionId as it is used in the database provisioning request&lt;br /&gt;
:::if len(matchedVersion) &amp;gt; 0 {&lt;br /&gt;
::::log.Info(&amp;quot;Id and VersionId matched for profileType&amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
::::idMatchedProfile[0].LatestVersionId = profile.VersionId&lt;br /&gt;
:::}&lt;br /&gt;
::}&lt;br /&gt;
::err = PopulateProfileOfType(ctx, profilesMap, profileType, allProfiles, dbType, idMatchedProfile)&lt;br /&gt;
::return&lt;br /&gt;
:}&lt;br /&gt;
&amp;lt;br&amp;gt;=====Explanation for the change :===== We use profiles in YAML file only when they have correct Id. To perform this essential task of checking the correctness of Id and after that populating the versionId, we have created this function. After checking the correctness, this function delegates the further tasks to PopulateProfileOfType function.&lt;br /&gt;
&lt;br /&gt;
=====PopulateProfileOfType =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function performs the task of populating profileMap with response (matching result) received for the profileType.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
:func PopulateProfileOfType(ctx context.Context, profileMap map[string]ProfileResponse, profileType string, allProfiles []ProfileResponse, dbType string, response []ProfileResponse) (err error) {&lt;br /&gt;
::log := ctrllog.FromContext(ctx)&lt;br /&gt;
::// if response is empty, it indicates no matching profile found; hence set the default OOB profile for that type&lt;br /&gt;
::if len(response) == 0 {&lt;br /&gt;
:::err = fmt.Errorf(&amp;quot;No matching profile found for profileType = %s&amp;quot;, profileType)&lt;br /&gt;
:::log.Info(&amp;quot;Error Occurred. No enrichment performed for profile = &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
:::return&lt;br /&gt;
::}&lt;br /&gt;
::log.Info(&amp;quot;Going to populate profile value in profilesMap for profileType = &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
::profileMap[profileType] = response[0]&lt;br /&gt;
::return&lt;br /&gt;
:}&lt;br /&gt;
&amp;lt;br&amp;gt;=====Explanation for the change :===== Since this function is called only after the sanity check of Id and versionId, this function just has to populate the profiles by their respective types.&lt;br /&gt;
&lt;br /&gt;
=====PopulateDefaultProfile =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This method populates profileMap with the default value for the profileType.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
:func PopulateDefaultProfile(ctx context.Context, profileMap map[string]ProfileResponse, profileType string, allProfiles []ProfileResponse, dbType string) (err error) {&lt;br /&gt;
::log := ctrllog.FromContext(ctx)&lt;br /&gt;
::log.Info(&amp;quot;Going to set default profile value for profileType = &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
::genericProfiles := util.Filter(allProfiles, func(p ProfileResponse) bool { return p.EngineType == DATABASE_ENGINE_TYPE_GENERIC })&lt;br /&gt;
::dbEngineSpecificProfiles := util.Filter(allProfiles, func(p ProfileResponse) bool { return p.EngineType == GetDatabaseEngineName(dbType) })&lt;br /&gt;
::response, err := GetDefaultProfileForType(genericProfiles, dbEngineSpecificProfiles, profileType)&lt;br /&gt;
::if err != nil {&lt;br /&gt;
:::return&lt;br /&gt;
::}&lt;br /&gt;
::profileMap[profileType] = response[0]&lt;br /&gt;
::return&lt;br /&gt;
:}&lt;br /&gt;
&amp;lt;br&amp;gt;=====Explanation for the change :===== Since this function is called only if profiles are missing in the YAML file or have wrong profile Id, this function just has to populate the default out of the box profiles for each profile type. This function calls GetDefaultProfileForType function to get profiles for each profile type.&lt;br /&gt;
&lt;br /&gt;
=====GetDefaultProfileForType =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function gets default profile for each profile type from the result of GET API call to the NDB API.&lt;br /&gt;
&lt;br /&gt;
'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
:func GetDefaultProfileForType(genericProfiles []ProfileResponse, dbEngineSpecificProfiles []ProfileResponse, profileType string) (profile []ProfileResponse, err error) {&lt;br /&gt;
::switch profileType {&lt;br /&gt;
::case PROFILE_TYPE_COMPUTE:&lt;br /&gt;
:::profile = util.Filter(genericProfiles, func(p ProfileResponse) bool {&lt;br /&gt;
::::return p.Type == PROFILE_TYPE_COMPUTE &amp;amp;&amp;amp; strings.Contains(strings.ToLower(p.Name), &amp;quot;small&amp;quot;)&lt;br /&gt;
:::})&lt;br /&gt;
:::break&lt;br /&gt;
::case PROFILE_TYPE_SOFTWARE:&lt;br /&gt;
:::profile = util.Filter(dbEngineSpecificProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_SOFTWARE &amp;amp;&amp;amp; p.Topology == TOPOLOGY_SINGLE })&lt;br /&gt;
:::break&lt;br /&gt;
::case PROFILE_TYPE_NETWORK:&lt;br /&gt;
:::profile = util.Filter(dbEngineSpecificProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_NETWORK })&lt;br /&gt;
:::break&lt;br /&gt;
::case PROFILE_TYPE_DATABASE_PARAMETER:&lt;br /&gt;
:::profile = util.Filter(dbEngineSpecificProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_DATABASE_PARAMETER })&lt;br /&gt;
:::break&lt;br /&gt;
::case PROFILE_TYPE_STORAGE:&lt;br /&gt;
:::profile = util.Filter(genericProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_STORAGE })&lt;br /&gt;
:::break&lt;br /&gt;
::default:&lt;br /&gt;
:::return&lt;br /&gt;
::}&lt;br /&gt;
::if len(profile) == 0 {&lt;br /&gt;
:::err = errors.New(&amp;quot;oob profile: one or more OOB profile(s) were not found&amp;quot;)&lt;br /&gt;
:::return&lt;br /&gt;
::}&lt;br /&gt;
::return&lt;br /&gt;
:}&lt;br /&gt;
&amp;lt;br&amp;gt;=====Explanation for the change :===== We have to find appropriate default profile from the API call result. With the help of some util functions, this function performs this filtering task.&lt;br /&gt;
&lt;br /&gt;
=====GetProfileByType =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function just returns the appropriate costant value from the pre-defined object structures.&lt;br /&gt;
&lt;br /&gt;
'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
:func GetProfileByType(profileType string, profiles Profiles) Profile {&lt;br /&gt;
::defaultEmptyProfile := Profile{}&lt;br /&gt;
::switch profileType {&lt;br /&gt;
::case PROFILE_TYPE_COMPUTE:&lt;br /&gt;
:::return profiles.Compute&lt;br /&gt;
::case PROFILE_TYPE_SOFTWARE:&lt;br /&gt;
:::return profiles.Software&lt;br /&gt;
::case PROFILE_TYPE_NETWORK:&lt;br /&gt;
:::return profiles.Network&lt;br /&gt;
::case PROFILE_TYPE_DATABASE_PARAMETER:&lt;br /&gt;
:::return profiles.DbParam&lt;br /&gt;
::default:&lt;br /&gt;
:::return defaultEmptyProfile&lt;br /&gt;
::}&lt;br /&gt;
:}&lt;br /&gt;
&amp;lt;br&amp;gt;=====Explanation for the change :===== We use pre-defined object structures for reference everywhere. This function helps us to make that references correctly.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on provided software/compute/network/dbParams profiles&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the provided software/compute/network/dbParams profiles as input through YAML file, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;db-operator\config\samplesdb_v1alpha1_database.yaml&amp;quot; and &amp;quot;db-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;db-operator\config\samplesdb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samplesdb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;db-operator\config\samplesdb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Throwing error if invalid software/compute/network/dbParams profiles are given as input&lt;br /&gt;
** Description: This test case verifies that error is thrown if invalid software/compute/network/dbParams profiles are provided as input through YAML file.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;db-operator\config\samplesdb_v1alpha1_database.yaml&amp;quot; and &amp;quot;db-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;db-operator\config\samplesdb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samplesdb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the database has not been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the error is thrown on the command prompt&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system does not provision the database&lt;br /&gt;
*** The error is thrown saying that the id/version id of software/compute/network/dbParams profiles is invalid&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 3 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default software/compute/network/dbParams profiles for database provisioning when software/compute/network/dbParams profiles are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default software/compute/network/dbParams profiles for configuration when software/compute/network/dbParams profiles are not present in the profiles section of &amp;quot;db-operator\config\samplesdb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;db-operator\config\samplesdb_v1alpha1_database.yaml&amp;quot; and &amp;quot;db-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are not available for input in a profiles section inside &amp;quot;db-operator\config\samplesdb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samplesdb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the default profiles&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;db-operator\config\samplesdb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the default profile values&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
Testcases were written in &amp;quot;db-operator\testdb_api_helpers_test.go&amp;quot;&lt;br /&gt;
&amp;lt;br&amp;gt;Dummy Objects required for these testcases were created in &amp;quot;db-operator\test\testutility.go&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 1 and Test Scenario 3 ===&lt;br /&gt;
::func TestEnrichAndGetProfilesWhenCustomProfilesMatch(t *testing.T) {&lt;br /&gt;
:::://Set&lt;br /&gt;
::::server := GetServerTestHelper(t)&lt;br /&gt;
::::defer server.Close()&lt;br /&gt;
::::ndbclient := ndbclient.NewNDBClient(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, server.URL, &amp;quot;&amp;quot;, true)&lt;br /&gt;
:::://Test&lt;br /&gt;
::::dbTypes := []string{&amp;quot;postgres&amp;quot;, &amp;quot;mysql&amp;quot;, &amp;quot;mongodb&amp;quot;}&lt;br /&gt;
::::for _, dbType := range dbTypes {&lt;br /&gt;
::::::// get custom profile based upon the database type&lt;br /&gt;
::::::customProfile := GetCustomProfileForDBType(dbType)&lt;br /&gt;
::::::profileMap, _ := v1alpha1.EnrichAndGetProfiles(context.Background(), ndbclient, dbType, customProfile)&lt;br /&gt;
:::::://Assert&lt;br /&gt;
::::::profileTypes := []string{&lt;br /&gt;
::::::::v1alpha1.PROFILE_TYPE_COMPUTE,&lt;br /&gt;
::::::::v1alpha1.PROFILE_TYPE_STORAGE,&lt;br /&gt;
::::::::v1alpha1.PROFILE_TYPE_SOFTWARE,&lt;br /&gt;
::::::::v1alpha1.PROFILE_TYPE_NETWORK,&lt;br /&gt;
::::::::v1alpha1.PROFILE_TYPE_DATABASE_PARAMETER,&lt;br /&gt;
::::::}&lt;br /&gt;
::::::for _, profileType := range profileTypes {&lt;br /&gt;
::::::::profile := profileMap[profileType]&lt;br /&gt;
:::::::://Assert that no profileType is empty&lt;br /&gt;
::::::::if profile == (v1alpha1.ProfileResponse{}) {&lt;br /&gt;
::::::::::t.Errorf(&amp;quot;Empty profile type %s for dbType %s&amp;quot;, profileType, dbType)&lt;br /&gt;
::::::::}&lt;br /&gt;
:::::::://Assert that profile EngineType matches the database engine or the generic type&lt;br /&gt;
::::::::if profile.EngineType != v1alpha1.GetDatabaseEngineName(dbType) &amp;amp;&amp;amp; profile.EngineType != v1alpha1.DATABASE_ENGINE_TYPE_GENERIC {&lt;br /&gt;
::::::::::t.Errorf(&amp;quot;Profile engine type %s for dbType %s does not match&amp;quot;, profile.EngineType, dbType)&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::::obtainedProfile := v1alpha1.GetProfileForType(profileType, customProfile)&lt;br /&gt;
::::::::// Ignoring Storage Profile Type as the Profile struct currently only supports compute, software, network and dbParam&lt;br /&gt;
::::::::if profileType != v1alpha1.PROFILE_TYPE_STORAGE &amp;amp;&amp;amp; profile.Id != obtainedProfile.Id &amp;amp;&amp;amp; profile.LatestVersionId != obtainedProfile.VersionId {&lt;br /&gt;
::::::::::t.Errorf(&amp;quot;Custom Profile Enrichment failed for profileType = %s and dbType = %s&amp;quot;, profileType, dbType)&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::}&lt;br /&gt;
::::}&lt;br /&gt;
::}&lt;br /&gt;
&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
::func GetCustomProfileForDBType(dbType string) (profiles v1alpha1.Profiles) {&lt;br /&gt;
::::switch dbType {&lt;br /&gt;
::::case v1alpha1.DATABASE_TYPE_POSTGRES:&lt;br /&gt;
::::::profiles = v1alpha1.Profiles{&lt;br /&gt;
::::::::// Custom Software Profile Name = &amp;quot;custom postgres software profile&amp;quot;&lt;br /&gt;
::::::::Software: v1alpha1.Profile{&lt;br /&gt;
::::::::::Id: &amp;quot;12&amp;quot;,&lt;br /&gt;
::::::::::VersionId: &amp;quot;v-id-12&amp;quot;,&lt;br /&gt;
::::::::},&lt;br /&gt;
::::::::// Custom ompute Name = &amp;quot;a&amp;quot;&lt;br /&gt;
::::::::Compute: v1alpha1.Profile{&lt;br /&gt;
::::::::::Id: &amp;quot;1&amp;quot;,&lt;br /&gt;
::::::::::VersionId: &amp;quot;v-id-1&amp;quot;,&lt;br /&gt;
::::::::},&lt;br /&gt;
::::::::Network: v1alpha1.Profile{&lt;br /&gt;
::::::::::Id: &amp;quot;15&amp;quot;,&lt;br /&gt;
::::::::::VersionId: &amp;quot;v-id-15&amp;quot;,&lt;br /&gt;
::::::::},&lt;br /&gt;
::::::::DbParam: v1alpha1.Profile{&lt;br /&gt;
::::::::::Id: &amp;quot;18&amp;quot;,&lt;br /&gt;
::::::::::VersionId: &amp;quot;v-id-18&amp;quot;,&lt;br /&gt;
::::::::},&lt;br /&gt;
::::::}&lt;br /&gt;
::::::return profiles&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 2 ===&lt;br /&gt;
::func TestEnrichAndGetProfilesWhenInvalidCustomProfilesProvided(t *testing.T) {&lt;br /&gt;
:::://Set&lt;br /&gt;
::::server := GetServerTestHelper(t)&lt;br /&gt;
::::defer server.Close()&lt;br /&gt;
::::ndbclient := ndbclient.NewNDBClient(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, server.URL, &amp;quot;&amp;quot;, true)&lt;br /&gt;
:::://Test&lt;br /&gt;
::::dbTypes := []string{&amp;quot;postgres_invalid_profiles&amp;quot;, &amp;quot;mysql_invalid_profiles&amp;quot;, &amp;quot;mongodb_invalid_profiles&amp;quot;}&lt;br /&gt;
::::for _, dbType := range dbTypes {&lt;br /&gt;
::::::// get custom profile based upon the database type&lt;br /&gt;
::::::customProfile := GetCustomProfileForDBType(dbType)&lt;br /&gt;
::::::profileMap, _ := v1alpha1.EnrichAndGetProfiles(context.Background(), ndbclient, dbType, customProfile)&lt;br /&gt;
:::::://Assert&lt;br /&gt;
::::::profileTypes := []string{&lt;br /&gt;
::::::::v1alpha1.PROFILE_TYPE_COMPUTE,&lt;br /&gt;
::::::::v1alpha1.PROFILE_TYPE_STORAGE,&lt;br /&gt;
::::::::v1alpha1.PROFILE_TYPE_SOFTWARE,&lt;br /&gt;
::::::::v1alpha1.PROFILE_TYPE_NETWORK,&lt;br /&gt;
::::::::v1alpha1.PROFILE_TYPE_DATABASE_PARAMETER,&lt;br /&gt;
::::::}&lt;br /&gt;
::::::for _, profileType := range profileTypes {&lt;br /&gt;
::::::::profile := profileMap[profileType]&lt;br /&gt;
:::::::://Assert that profile EngineType matches the database engine or the generic type&lt;br /&gt;
::::::::if profile.EngineType != v1alpha1.GetDatabaseEngineName(dbType) &amp;amp;&amp;amp; profile.EngineType != v1alpha1.DATABASE_ENGINE_TYPE_GENERIC {&lt;br /&gt;
::::::::::t.Errorf(&amp;quot;Profile engine type %s for dbType %s does not match&amp;quot;, profile.EngineType, dbType)&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::::/* since custom profile is passed it should not default to OOB, and err should be raised stating the custom profile passed does not exist,&lt;br /&gt;
::::::::and thus database provisioning does not occur&lt;br /&gt;
::::::::*/&lt;br /&gt;
::::::::if profile != (v1alpha1.ProfileResponse{}) {&lt;br /&gt;
::::::::::t.Errorf(&amp;quot;Incorrect Profile Match found for profile type = %s and dbType = %s&amp;quot;, profileType, dbType)&lt;br /&gt;
::::::::}&lt;br /&gt;
::::::}&lt;br /&gt;
::::}&lt;br /&gt;
::}&lt;br /&gt;
&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
::case v1alpha1.DATABASE_TYPE_MONGODB_INVALID_PROFILE, v1alpha1.DATABASE_TYPE_MYSQL_INVALID_PROFILE, v1alpha1.DATABASE_TYPE_POSTGRES_INVALID_PROFILE:&lt;br /&gt;
::::::// below custom profiles do not exist and will be used for the negative scenario&lt;br /&gt;
::::::profiles = v1alpha1.Profiles{&lt;br /&gt;
::::::::Software: v1alpha1.Profile{&lt;br /&gt;
::::::::::Id: &amp;quot;140&amp;quot;,&lt;br /&gt;
::::::::::VersionId: &amp;quot;v-id-140&amp;quot;,&lt;br /&gt;
::::::::},&lt;br /&gt;
::::::::Compute: v1alpha1.Profile{&lt;br /&gt;
::::::::::Id: &amp;quot;100&amp;quot;,&lt;br /&gt;
::::::::::VersionId: &amp;quot;v-id-100&amp;quot;,&lt;br /&gt;
::::::::},&lt;br /&gt;
::::::::Network: v1alpha1.Profile{&lt;br /&gt;
::::::::::Id: &amp;quot;170&amp;quot;,&lt;br /&gt;
::::::::::VersionId: &amp;quot;v-id-170&amp;quot;,&lt;br /&gt;
::::::::},&lt;br /&gt;
::::::::DbParam: v1alpha1.Profile{&lt;br /&gt;
::::::::::Id: &amp;quot;200&amp;quot;,&lt;br /&gt;
::::::::::VersionId: &amp;quot;v-id-200&amp;quot;,&lt;br /&gt;
::::::::},&lt;br /&gt;
::::::}&lt;br /&gt;
::::::return profiles&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=150352</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=150352"/>
		<updated>2023-05-02T20:45:29Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
Employing Clean Code Practices + Design Patterns:&lt;br /&gt;
&lt;br /&gt;
DRY (Do Not Repeat Yourself):&lt;br /&gt;
The initial draft of using 4 if &amp;amp; else statements for each of the profiles (namely: software, compute, network, and storage) and performing the same checks and code again proved to be in direct opposition to the DRY approach whose repetitiveness can be seen from the below snippet:&lt;br /&gt;
        &lt;br /&gt;
            //Custom Software Profile Check and overriding the default values&lt;br /&gt;
            softwareProfile := dbSpec.Instance.Profiles.Software&lt;br /&gt;
            if softwareProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for software profiles as no custom profile received for Software. Hence, proceeding with default OOB software profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, softwareProfile, PROFILE_TYPE_SOFTWARE)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_SOFTWARE] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom Compute Profile Check and overriding the default values&lt;br /&gt;
            computeProfile := dbSpec.Instance.Profiles.Compute&lt;br /&gt;
            if computeProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for compute profiles as no custom profile received for Compute. Hence, proceeding with default OOB compute profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, genericProfiles, computeProfile, PROFILE_TYPE_COMPUTE)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_COMPUTE] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom Network Profile Check and overriding the default values&lt;br /&gt;
            networkProfile := dbSpec.Instance.Profiles.Network&lt;br /&gt;
            if networkProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for network profiles as no custom profile received for Network. Hence, proceeding with default OOB network profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, networkProfile, PROFILE_TYPE_NETWORK)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_NETWORK] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom DbParam Profile Check and overriding the default values&lt;br /&gt;
            dbParamProfile := dbSpec.Instance.Profiles.DbParam&lt;br /&gt;
            if dbParamProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for database parameter profiles as no custom profile received for it. Hence, proceeding with default OOB dbParam profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, dbParamProfile, PROFILE_TYPE_DATABASE_PARAMETER)&lt;br /&gt;
                if err != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_DATABASE_PARAMETER] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Hence, we create an array of profiles, and iterating over those profiles and calling a clean modular function by identifying the common key points from the above snippet helps in eliminating code duplication and makes the code more, readable, and open for extension by just adding new profile name to the list of profiles and easily maintainable! This approach can be viewed in EnrichProfilesMap() and delegate functions.&lt;br /&gt;
&lt;br /&gt;
Refactoring + Delegation using Facade Design Pattern:&lt;br /&gt;
&lt;br /&gt;
By default, for populating profiles, GetOOBProfiles() was called which did the task of fetching all profiles and populating default profile values. However, with the advent of custom profiles being provided from YAML, we suggest refactoring GetOOBProfiles() to EnrichProfilesMap() which will perform the same task as GetOOBProfiles did but in addition override the default values to input profiles provided after performing checks such as:&lt;br /&gt;
(1) Emptiness / Null checks for profiles&lt;br /&gt;
(2) Performing matching of the Id/VersionId for the custom profile provided &amp;amp; failing the database provisioning request in case of a match is not found.&lt;br /&gt;
Thus, for each of the checkers, we create modular functions and delegate the task of performing the above-mentioned checks.&lt;br /&gt;
&lt;br /&gt;
The flow proceeds as:&lt;br /&gt;
&lt;br /&gt;
EnrichProfilesMap() &lt;br /&gt;
Performs the task to set default values and override the default values by calling the below functions.  &lt;br /&gt;
&lt;br /&gt;
PerformProfileMatchingAndEnrichProfiles() uses&lt;br /&gt;
[isEmptyProfile() + GetAppropriateProfileForType() + GetTopologyForProfileType() in filtering profiles]&lt;br /&gt;
Once, the user input is received, the input is delegated for checks and performing matching if the profile exists. Additionally, other factory methods are also used for performing the matching of profiles&lt;br /&gt;
&lt;br /&gt;
EnrichProfileMapForProfileType()&lt;br /&gt;
Performs the final overriding of the default profile with the matched profile. Additionally, it cancels the database provisioning request for unmatched profiles.&lt;br /&gt;
&lt;br /&gt;
Moreover, in Tests, new tests have been added which indicate their functionality through their names adhering to the Clean Coding Naming principle:&lt;br /&gt;
TestEnrichAndGetProfilesWhenCustomProfilesMatch()&lt;br /&gt;
TestEnrichAndGetProfilesWhenInvalidCustomProfilesProvided()&lt;br /&gt;
&lt;br /&gt;
Additionally, we can also view the creation of specific functions as the alignment with the Facade Design Pattern that delegates the task of performing specific actions to respective functions. &lt;br /&gt;
&lt;br /&gt;
Alternatively, a closer look at the initial draft and performing Cyclomatic Complexity checks indicate that our approach has breached the threshold value of permissible complexity. Hence, to tackle this problem, we will change the filtering profile functions to perform matching based on Id and further based on versionId and eliminating factory methods like getTopology(), getProfileForType(), and a few checker functions that will aid in reducing the cyclomatic complexity automatically.&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
&lt;br /&gt;
=====GenerateProvisioningRequest =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :'''&lt;br /&gt;
&lt;br /&gt;
		// Fetch the OOB profiles for the database&lt;br /&gt;
		profilesMap, err := GetOOBProfiles(ctx, ndbclient, dbSpec.Instance.Type)&lt;br /&gt;
		if err != nil {&lt;br /&gt;
			log.Error(err, &amp;quot;Error occurred while getting OOB profiles&amp;quot;, &amp;quot;database name&amp;quot;, dbSpec.Instance.DatabaseInstanceName, &amp;quot;database type&amp;quot;, dbSpec.Instance.Type)&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
		// Fetch upto date profiles for the database&lt;br /&gt;
			profilesMap, err := MatchAndGetProfiles(ctx, ndbclient, dbSpec.Instance.Type, dbSpec.Instance.Profiles)&lt;br /&gt;
			if err != nil {&lt;br /&gt;
				log.Error(err, &amp;quot;Error occurred while enriching and getting profiles&amp;quot;, &amp;quot;database name&amp;quot;, dbSpec.Instance.DatabaseInstanceName, &amp;quot;database type&amp;quot;, dbSpec.Instance.Type)&lt;br /&gt;
				return&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :'''&lt;br /&gt;
Replaced the call for GetOOBProfiles function with MatchAndGetProfiles due to added functionality of populating the profileMap with all the values for the profileType available&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====MatchAndGetProfiles =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' now this function first fetches all the profiles from NDB API. Then for each profile type it checks if a profile is provided in the YAML file or not. If it is provided in the YAML file, then this function calls GetProfileByType and matchProfiles functions to do the further work. If it is not provided in the YAML file, then this function calls PopulateDefaultProfiles function to assign default profiles.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
		func MatchAndGetProfiles(ctx context.Context, ndbclient *ndbclient.NDBClient, dbType string, profiles Profiles) (profileMap map[string]ProfileResponse, err error) {&lt;br /&gt;
&lt;br /&gt;
		log := ctrllog.FromContext(ctx)&lt;br /&gt;
&lt;br /&gt;
		// Map of profile type to profiles&lt;br /&gt;
		profileMap = make(map[string]ProfileResponse)&lt;br /&gt;
&lt;br /&gt;
		allProfiles, err := GetAllProfiles(ctx, ndbclient)&lt;br /&gt;
&lt;br /&gt;
		if err != nil {&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		log.Info(&amp;quot;Received Input Profiles = &amp;quot;, &amp;quot;Received Input Profiles&amp;quot;, profiles)&lt;br /&gt;
		profileOptions := [...]string{PROFILE_TYPE_COMPUTE, PROFILE_TYPE_SOFTWARE, PROFILE_TYPE_NETWORK, PROFILE_TYPE_DATABASE_PARAMETER, PROFILE_TYPE_STORAGE}&lt;br /&gt;
		for _, profileType := range profileOptions {&lt;br /&gt;
			if profiles == (Profiles{}) {&lt;br /&gt;
				err = PopulateDefaultProfile(ctx, profileMap, profileType, allProfiles, dbType)&lt;br /&gt;
			} else {&lt;br /&gt;
				profile := GetProfileByType(profileType, profiles)&lt;br /&gt;
				err = matchProfiles(ctx, profileType, profile, allProfiles, profileMap, dbType)&lt;br /&gt;
			}&lt;br /&gt;
			if err != nil {&lt;br /&gt;
				return&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		return&lt;br /&gt;
&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' We need a function that checks if profiles are there in YAML file for or not and depending on that we need to make the further decisions. This function helps us to check that and delegate the next tasks.&lt;br /&gt;
&lt;br /&gt;
=====matchProfiles =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function does:&lt;br /&gt;
	(a) Id level matching with profiles in allProfiles&lt;br /&gt;
	(b) If Id level match is successful, flow proceeds to match based on versionId&lt;br /&gt;
		When matched, the latestVersionId is overridden with the versionId as it is this attribute while dbProvisioning which is used for&lt;br /&gt;
		profileType versionId.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
		func matchProfiles(ctx context.Context, profileType string, profile Profile, allProfiles []ProfileResponse, profilesMap map[string]ProfileResponse, dbType string) (err error) {&lt;br /&gt;
			log := ctrllog.FromContext(ctx)&lt;br /&gt;
			var idMatchedProfile []ProfileResponse&lt;br /&gt;
			var matchedVersion []Version&lt;br /&gt;
			if isEmptyProfile(profile) {&lt;br /&gt;
				err = PopulateDefaultProfile(ctx, profilesMap, profileType, allProfiles, dbType)&lt;br /&gt;
				return&lt;br /&gt;
			}&lt;br /&gt;
			log.Info(&amp;quot;Performing profile matching for profileType =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
			// match based on ID&lt;br /&gt;
			idMatchedProfile = util.Filter(allProfiles, func(p ProfileResponse) bool { return p.Id == profile.Id &amp;amp;&amp;amp; p.Type == profileType })&lt;br /&gt;
			// matching based on versionID&lt;br /&gt;
			if len(idMatchedProfile) &amp;gt; 0 {&lt;br /&gt;
				matchedVersion = util.Filter(idMatchedProfile[0].Versions, func(versions Version) bool { return versions.Id == profile.VersionId })&lt;br /&gt;
				// when versionID level match found, override latestVersionId as it is used in the database provisioning request&lt;br /&gt;
				if len(matchedVersion) &amp;gt; 0 {&lt;br /&gt;
					log.Info(&amp;quot;Id and VersionId matched for profileType&amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
					idMatchedProfile[0].LatestVersionId = profile.VersionId&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
			err = PopulateProfileOfType(ctx, profilesMap, profileType, allProfiles, dbType, idMatchedProfile)&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' We use profiles in YAML file only when they have correct Id. To perform this essential task of checking the correctness of Id and after that populating the versionId, we have created this function. After checking the correctness, this function delegates the further tasks to PopulateProfileOfType function.&lt;br /&gt;
&lt;br /&gt;
=====PopulateProfileOfType =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function performs the task of populating profileMap with response (matching result) received for the profileType.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
		func PopulateProfileOfType(ctx context.Context, profileMap map[string]ProfileResponse, profileType string, allProfiles []ProfileResponse, dbType string, response []ProfileResponse) (err error) {&lt;br /&gt;
			log := ctrllog.FromContext(ctx)&lt;br /&gt;
			// if response is empty, it indicates no matching profile found; hence set the default OOB profile for that type&lt;br /&gt;
			if len(response) == 0 {&lt;br /&gt;
				err = fmt.Errorf(&amp;quot;No matching profile found for profileType = %s&amp;quot;, profileType)&lt;br /&gt;
				log.Info(&amp;quot;Error Occurred. No enrichment performed for profile = &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
				return&lt;br /&gt;
			}&lt;br /&gt;
			log.Info(&amp;quot;Going to populate profile value in profilesMap for profileType = &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
			profileMap[profileType] = response[0]&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' Since this function is called only after the sanity check of Id and versionId, this function just has to populate the profiles by their respective types.&lt;br /&gt;
&lt;br /&gt;
=====PopulateDefaultProfile =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This method populates profileMap with the default value for the profileType.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
		func PopulateDefaultProfile(ctx context.Context, profileMap map[string]ProfileResponse, profileType string, allProfiles []ProfileResponse, dbType string) (err error) {&lt;br /&gt;
			log := ctrllog.FromContext(ctx)&lt;br /&gt;
			log.Info(&amp;quot;Going to set default profile value for profileType = &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
			genericProfiles := util.Filter(allProfiles, func(p ProfileResponse) bool { return p.EngineType == DATABASE_ENGINE_TYPE_GENERIC })&lt;br /&gt;
			dbEngineSpecificProfiles := util.Filter(allProfiles, func(p ProfileResponse) bool { return p.EngineType == GetDatabaseEngineName(dbType) })&lt;br /&gt;
			response, err := GetDefaultProfileForType(genericProfiles, dbEngineSpecificProfiles, profileType)&lt;br /&gt;
			if err != nil {&lt;br /&gt;
				return&lt;br /&gt;
			}&lt;br /&gt;
			profileMap[profileType] = response[0]&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' Since this function is called only if profiles are missing in the YAML file or have wrong profile Id, this function just has to populate the default out of the box profiles for each profile type. This function calls GetDefaultProfileForType function to get profiles for each profile type.&lt;br /&gt;
&lt;br /&gt;
=====GetDefaultProfileForType =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function gets default profile for each profile type from the result of GET API call to the NDB API.&lt;br /&gt;
&lt;br /&gt;
'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
		func GetDefaultProfileForType(genericProfiles []ProfileResponse, dbEngineSpecificProfiles []ProfileResponse, profileType string) (profile []ProfileResponse, err error) {&lt;br /&gt;
			switch profileType {&lt;br /&gt;
			case PROFILE_TYPE_COMPUTE:&lt;br /&gt;
				profile = util.Filter(genericProfiles, func(p ProfileResponse) bool {&lt;br /&gt;
					return p.Type == PROFILE_TYPE_COMPUTE &amp;amp;&amp;amp; strings.Contains(strings.ToLower(p.Name), &amp;quot;small&amp;quot;)&lt;br /&gt;
				})&lt;br /&gt;
				break&lt;br /&gt;
			case PROFILE_TYPE_SOFTWARE:&lt;br /&gt;
				profile = util.Filter(dbEngineSpecificProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_SOFTWARE &amp;amp;&amp;amp; p.Topology == TOPOLOGY_SINGLE })&lt;br /&gt;
				break&lt;br /&gt;
			case PROFILE_TYPE_NETWORK:&lt;br /&gt;
				profile = util.Filter(dbEngineSpecificProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_NETWORK })&lt;br /&gt;
				break&lt;br /&gt;
			case PROFILE_TYPE_DATABASE_PARAMETER:&lt;br /&gt;
				profile = util.Filter(dbEngineSpecificProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_DATABASE_PARAMETER })&lt;br /&gt;
				break&lt;br /&gt;
			case PROFILE_TYPE_STORAGE:&lt;br /&gt;
				profile = util.Filter(genericProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_STORAGE })&lt;br /&gt;
				break&lt;br /&gt;
			default:&lt;br /&gt;
				return&lt;br /&gt;
			}&lt;br /&gt;
			if len(profile) == 0 {&lt;br /&gt;
				err = errors.New(&amp;quot;oob profile: one or more OOB profile(s) were not found&amp;quot;)&lt;br /&gt;
				return&lt;br /&gt;
			}&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' We have to find appropriate default profile from the API call result. With the help of some util functions, this function performs this filtering task.&lt;br /&gt;
&lt;br /&gt;
=====GetProfileByType =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function just returns the appropriate costant value from the pre-defined object structures.&lt;br /&gt;
&lt;br /&gt;
'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
		func GetProfileByType(profileType string, profiles Profiles) Profile {&lt;br /&gt;
			defaultEmptyProfile := Profile{}&lt;br /&gt;
			switch profileType {&lt;br /&gt;
			case PROFILE_TYPE_COMPUTE:&lt;br /&gt;
				return profiles.Compute&lt;br /&gt;
			case PROFILE_TYPE_SOFTWARE:&lt;br /&gt;
				return profiles.Software&lt;br /&gt;
			case PROFILE_TYPE_NETWORK:&lt;br /&gt;
				return profiles.Network&lt;br /&gt;
			case PROFILE_TYPE_DATABASE_PARAMETER:&lt;br /&gt;
				return profiles.DbParam&lt;br /&gt;
			default:&lt;br /&gt;
				return defaultEmptyProfile&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' We use pre-defined object structures for reference everywhere. This function helps us to make that references correctly.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on provided software/compute/network/dbParams profiles&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the provided software/compute/network/dbParams profiles as input through YAML file, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Throwing error if invalid software/compute/network/dbParams profiles are given as input&lt;br /&gt;
** Description: This test case verifies that error is thrown if invalid software/compute/network/dbParams profiles are provided as input through YAML file.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the database has not been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the error is thrown on the command prompt&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system does not provision the database&lt;br /&gt;
*** The error is thrown saying that the id/version id of software/compute/network/dbParams profiles is invalid&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 3 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default software/compute/network/dbParams profiles for database provisioning when software/compute/network/dbParams profiles are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default software/compute/network/dbParams profiles for configuration when software/compute/network/dbParams profiles are not present in the profiles section of &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are not available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the default profiles&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the default profile values&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
Testcases were written in &amp;quot;\ndb-operator\test\ndb_api_helpers_test.go&amp;quot;&lt;br /&gt;
&amp;lt;br&amp;gt;Dummy Objects required for these testcases were created in &amp;quot;\ndb-operator\test\testutility.go&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 1 and Test Scenario 3 ===&lt;br /&gt;
    func TestEnrichAndGetProfilesWhenCustomProfilesMatch(t *testing.T) {&lt;br /&gt;
        //Set&lt;br /&gt;
        server := GetServerTestHelper(t)&lt;br /&gt;
        defer server.Close()&lt;br /&gt;
        ndbclient := ndbclient.NewNDBClient(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, server.URL, &amp;quot;&amp;quot;, true)&lt;br /&gt;
        //Test&lt;br /&gt;
        dbTypes := []string{&amp;quot;postgres&amp;quot;, &amp;quot;mysql&amp;quot;, &amp;quot;mongodb&amp;quot;}&lt;br /&gt;
        for _, dbType := range dbTypes {&lt;br /&gt;
            // get custom profile based upon the database type&lt;br /&gt;
            customProfile := GetCustomProfileForDBType(dbType)&lt;br /&gt;
            profileMap, _ := v1alpha1.EnrichAndGetProfiles(context.Background(), ndbclient, dbType, customProfile)&lt;br /&gt;
            //Assert&lt;br /&gt;
            profileTypes := []string{&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_COMPUTE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_STORAGE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_SOFTWARE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_NETWORK,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_DATABASE_PARAMETER,&lt;br /&gt;
            }&lt;br /&gt;
            for _, profileType := range profileTypes {&lt;br /&gt;
                profile := profileMap[profileType]&lt;br /&gt;
                //Assert that no profileType is empty&lt;br /&gt;
                if profile == (v1alpha1.ProfileResponse{}) {&lt;br /&gt;
                    t.Errorf(&amp;quot;Empty profile type %s for dbType %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                //Assert that profile EngineType matches the database engine or the generic type&lt;br /&gt;
                if profile.EngineType != v1alpha1.GetDatabaseEngineName(dbType) &amp;amp;&amp;amp; profile.EngineType != v1alpha1.DATABASE_ENGINE_TYPE_GENERIC {&lt;br /&gt;
                    t.Errorf(&amp;quot;Profile engine type %s for dbType %s does not match&amp;quot;, profile.EngineType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                obtainedProfile := v1alpha1.GetProfileForType(profileType, customProfile)&lt;br /&gt;
                // Ignoring Storage Profile Type as the Profile struct currently only supports compute, software, network and dbParam&lt;br /&gt;
                if profileType != v1alpha1.PROFILE_TYPE_STORAGE &amp;amp;&amp;amp; profile.Id != obtainedProfile.Id &amp;amp;&amp;amp; profile.LatestVersionId != obtainedProfile.VersionId {&lt;br /&gt;
                    t.Errorf(&amp;quot;Custom Profile Enrichment failed for profileType = %s and dbType = %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
    func GetCustomProfileForDBType(dbType string) (profiles v1alpha1.Profiles) {&lt;br /&gt;
        switch dbType {&lt;br /&gt;
        case v1alpha1.DATABASE_TYPE_POSTGRES:&lt;br /&gt;
            profiles = v1alpha1.Profiles{&lt;br /&gt;
                // Custom Software Profile Name = &amp;quot;custom postgres software profile&amp;quot;&lt;br /&gt;
                Software: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;12&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-12&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                // Custom ompute Name = &amp;quot;a&amp;quot;&lt;br /&gt;
                Compute: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;1&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-1&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Network: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;15&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-15&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                DbParam: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;18&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-18&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
            }&lt;br /&gt;
            return profiles&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 2 ===&lt;br /&gt;
    func TestEnrichAndGetProfilesWhenInvalidCustomProfilesProvided(t *testing.T) {&lt;br /&gt;
        //Set&lt;br /&gt;
        server := GetServerTestHelper(t)&lt;br /&gt;
        defer server.Close()&lt;br /&gt;
        ndbclient := ndbclient.NewNDBClient(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, server.URL, &amp;quot;&amp;quot;, true)&lt;br /&gt;
        //Test&lt;br /&gt;
        dbTypes := []string{&amp;quot;postgres_invalid_profiles&amp;quot;, &amp;quot;mysql_invalid_profiles&amp;quot;, &amp;quot;mongodb_invalid_profiles&amp;quot;}&lt;br /&gt;
        for _, dbType := range dbTypes {&lt;br /&gt;
            // get custom profile based upon the database type&lt;br /&gt;
            customProfile := GetCustomProfileForDBType(dbType)&lt;br /&gt;
            profileMap, _ := v1alpha1.EnrichAndGetProfiles(context.Background(), ndbclient, dbType, customProfile)&lt;br /&gt;
            //Assert&lt;br /&gt;
            profileTypes := []string{&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_COMPUTE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_STORAGE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_SOFTWARE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_NETWORK,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_DATABASE_PARAMETER,&lt;br /&gt;
            }&lt;br /&gt;
            for _, profileType := range profileTypes {&lt;br /&gt;
                profile := profileMap[profileType]&lt;br /&gt;
                //Assert that profile EngineType matches the database engine or the generic type&lt;br /&gt;
                if profile.EngineType != v1alpha1.GetDatabaseEngineName(dbType) &amp;amp;&amp;amp; profile.EngineType != v1alpha1.DATABASE_ENGINE_TYPE_GENERIC {&lt;br /&gt;
                    t.Errorf(&amp;quot;Profile engine type %s for dbType %s does not match&amp;quot;, profile.EngineType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                /* since custom profile is passed it should not default to OOB, and err should be raised stating the custom profile passed does not exist,&lt;br /&gt;
                and thus database provisioning does not occur&lt;br /&gt;
                */&lt;br /&gt;
                if profile != (v1alpha1.ProfileResponse{}) {&lt;br /&gt;
                    t.Errorf(&amp;quot;Incorrect Profile Match found for profile type = %s and dbType = %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
    case v1alpha1.DATABASE_TYPE_MONGODB_INVALID_PROFILE, v1alpha1.DATABASE_TYPE_MYSQL_INVALID_PROFILE, v1alpha1.DATABASE_TYPE_POSTGRES_INVALID_PROFILE:&lt;br /&gt;
            // below custom profiles do not exist and will be used for the negative scenario&lt;br /&gt;
            profiles = v1alpha1.Profiles{&lt;br /&gt;
                Software: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;140&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-140&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Compute: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;100&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-100&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Network: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;170&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-170&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                DbParam: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;200&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-200&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
            }&lt;br /&gt;
            return profiles&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=150351</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=150351"/>
		<updated>2023-05-02T20:40:03Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
Employing Clean Code Practices + Design Patterns:&lt;br /&gt;
&lt;br /&gt;
DRY (Do Not Repeat Yourself):&lt;br /&gt;
The initial draft of using 4 if &amp;amp; else statements for each of the profiles (namely: software, compute, network, and storage) and performing the same checks and code again proved to be in direct opposition to the DRY approach whose repetitiveness can be seen from the below snippet:&lt;br /&gt;
        &lt;br /&gt;
            //Custom Software Profile Check and overriding the default values&lt;br /&gt;
            softwareProfile := dbSpec.Instance.Profiles.Software&lt;br /&gt;
            if softwareProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for software profiles as no custom profile received for Software. Hence, proceeding with default OOB software profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, softwareProfile, PROFILE_TYPE_SOFTWARE)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_SOFTWARE] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom Compute Profile Check and overriding the default values&lt;br /&gt;
            computeProfile := dbSpec.Instance.Profiles.Compute&lt;br /&gt;
            if computeProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for compute profiles as no custom profile received for Compute. Hence, proceeding with default OOB compute profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, genericProfiles, computeProfile, PROFILE_TYPE_COMPUTE)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_COMPUTE] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom Network Profile Check and overriding the default values&lt;br /&gt;
            networkProfile := dbSpec.Instance.Profiles.Network&lt;br /&gt;
            if networkProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for network profiles as no custom profile received for Network. Hence, proceeding with default OOB network profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, networkProfile, PROFILE_TYPE_NETWORK)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_NETWORK] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom DbParam Profile Check and overriding the default values&lt;br /&gt;
            dbParamProfile := dbSpec.Instance.Profiles.DbParam&lt;br /&gt;
            if dbParamProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for database parameter profiles as no custom profile received for it. Hence, proceeding with default OOB dbParam profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, dbParamProfile, PROFILE_TYPE_DATABASE_PARAMETER)&lt;br /&gt;
                if err != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_DATABASE_PARAMETER] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Hence, we create an array of profiles, and iterating over those profiles and calling a clean modular function by identifying the common key points from the above snippet helps in eliminating code duplication and makes the code more, readable, and open for extension by just adding new profile name to the list of profiles and easily maintainable! This approach can be viewed in EnrichProfilesMap() and delegate functions.&lt;br /&gt;
&lt;br /&gt;
Refactoring + Delegation using Facade Design Pattern:&lt;br /&gt;
&lt;br /&gt;
By default, for populating profiles, GetOOBProfiles() was called which did the task of fetching all profiles and populating default profile values. However, with the advent of custom profiles being provided from YAML, we suggest refactoring GetOOBProfiles() to EnrichProfilesMap() which will perform the same task as GetOOBProfiles did but in addition override the default values to input profiles provided after performing checks such as:&lt;br /&gt;
(1) Emptiness / Null checks for profiles&lt;br /&gt;
(2) Performing matching of the Id/VersionId for the custom profile provided &amp;amp; failing the database provisioning request in case of a match is not found.&lt;br /&gt;
Thus, for each of the checkers, we create modular functions and delegate the task of performing the above-mentioned checks.&lt;br /&gt;
&lt;br /&gt;
The flow proceeds as:&lt;br /&gt;
&lt;br /&gt;
EnrichProfilesMap() &lt;br /&gt;
Performs the task to set default values and override the default values by calling the below functions.  &lt;br /&gt;
&lt;br /&gt;
PerformProfileMatchingAndEnrichProfiles() uses&lt;br /&gt;
[isEmptyProfile() + GetAppropriateProfileForType() + GetTopologyForProfileType() in filtering profiles]&lt;br /&gt;
Once, the user input is received, the input is delegated for checks and performing matching if the profile exists. Additionally, other factory methods are also used for performing the matching of profiles&lt;br /&gt;
&lt;br /&gt;
EnrichProfileMapForProfileType()&lt;br /&gt;
Performs the final overriding of the default profile with the matched profile. Additionally, it cancels the database provisioning request for unmatched profiles.&lt;br /&gt;
&lt;br /&gt;
Moreover, in Tests, new tests have been added which indicate their functionality through their names adhering to the Clean Coding Naming principle:&lt;br /&gt;
TestEnrichAndGetProfilesWhenCustomProfilesMatch()&lt;br /&gt;
TestEnrichAndGetProfilesWhenInvalidCustomProfilesProvided()&lt;br /&gt;
&lt;br /&gt;
Additionally, we can also view the creation of specific functions as the alignment with the Facade Design Pattern that delegates the task of performing specific actions to respective functions. &lt;br /&gt;
&lt;br /&gt;
Alternatively, a closer look at the initial draft and performing Cyclomatic Complexity checks indicate that our approach has breached the threshold value of permissible complexity. Hence, to tackle this problem, we will change the filtering profile functions to perform matching based on Id and further based on versionId and eliminating factory methods like getTopology(), getProfileForType(), and a few checker functions that will aid in reducing the cyclomatic complexity automatically.&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
&lt;br /&gt;
=====GenerateProvisioningRequest =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :'''&lt;br /&gt;
&lt;br /&gt;
	// Fetch the OOB profiles for the database&lt;br /&gt;
	profilesMap, err := GetOOBProfiles(ctx, ndbclient, dbSpec.Instance.Type)&lt;br /&gt;
	if err != nil {&lt;br /&gt;
		log.Error(err, &amp;quot;Error occurred while getting OOB profiles&amp;quot;, &amp;quot;database name&amp;quot;, dbSpec.Instance.DatabaseInstanceName, &amp;quot;database type&amp;quot;, dbSpec.Instance.Type)&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
	// Fetch upto date profiles for the database&lt;br /&gt;
		profilesMap, err := MatchAndGetProfiles(ctx, ndbclient, dbSpec.Instance.Type, dbSpec.Instance.Profiles)&lt;br /&gt;
		if err != nil {&lt;br /&gt;
			log.Error(err, &amp;quot;Error occurred while enriching and getting profiles&amp;quot;, &amp;quot;database name&amp;quot;, dbSpec.Instance.DatabaseInstanceName, &amp;quot;database type&amp;quot;, dbSpec.Instance.Type)&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :'''&lt;br /&gt;
Replaced the call for GetOOBProfiles function with MatchAndGetProfiles due to added functionality of populating the profileMap with all the values for the profileType available&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====MatchAndGetProfiles =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' now this function first fetches all the profiles from NDB API. Then for each profile type it checks if a profile is provided in the YAML file or not. If it is provided in the YAML file, then this function calls GetProfileByType and matchProfiles functions to do the further work. If it is not provided in the YAML file, then this function calls PopulateDefaultProfiles function to assign default profiles.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
	func MatchAndGetProfiles(ctx context.Context, ndbclient *ndbclient.NDBClient, dbType string, profiles Profiles) (profileMap map[string]ProfileResponse, err error) {&lt;br /&gt;
&lt;br /&gt;
	log := ctrllog.FromContext(ctx)&lt;br /&gt;
&lt;br /&gt;
	// Map of profile type to profiles&lt;br /&gt;
	profileMap = make(map[string]ProfileResponse)&lt;br /&gt;
&lt;br /&gt;
	allProfiles, err := GetAllProfiles(ctx, ndbclient)&lt;br /&gt;
&lt;br /&gt;
	if err != nil {&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	log.Info(&amp;quot;Received Input Profiles = &amp;quot;, &amp;quot;Received Input Profiles&amp;quot;, profiles)&lt;br /&gt;
	profileOptions := [...]string{PROFILE_TYPE_COMPUTE, PROFILE_TYPE_SOFTWARE, PROFILE_TYPE_NETWORK, PROFILE_TYPE_DATABASE_PARAMETER, PROFILE_TYPE_STORAGE}&lt;br /&gt;
	for _, profileType := range profileOptions {&lt;br /&gt;
		if profiles == (Profiles{}) {&lt;br /&gt;
			err = PopulateDefaultProfile(ctx, profileMap, profileType, allProfiles, dbType)&lt;br /&gt;
		} else {&lt;br /&gt;
			profile := GetProfileByType(profileType, profiles)&lt;br /&gt;
			err = matchProfiles(ctx, profileType, profile, allProfiles, profileMap, dbType)&lt;br /&gt;
		}&lt;br /&gt;
		if err != nil {&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' We need a function that checks if profiles are there in YAML file for or not and depending on that we need to make the further decisions. This function helps us to check that and delegate the next tasks.&lt;br /&gt;
&lt;br /&gt;
=====matchProfiles =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function does:&lt;br /&gt;
	(a) Id level matching with profiles in allProfiles&lt;br /&gt;
	(b) If Id level match is successful, flow proceeds to match based on versionId&lt;br /&gt;
		When matched, the latestVersionId is overridden with the versionId as it is this attribute while dbProvisioning which is used for&lt;br /&gt;
		profileType versionId.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
	func matchProfiles(ctx context.Context, profileType string, profile Profile, allProfiles []ProfileResponse, profilesMap map[string]ProfileResponse, dbType string) (err error) {&lt;br /&gt;
		log := ctrllog.FromContext(ctx)&lt;br /&gt;
		var idMatchedProfile []ProfileResponse&lt;br /&gt;
		var matchedVersion []Version&lt;br /&gt;
		if isEmptyProfile(profile) {&lt;br /&gt;
			err = PopulateDefaultProfile(ctx, profilesMap, profileType, allProfiles, dbType)&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
		log.Info(&amp;quot;Performing profile matching for profileType =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
		// match based on ID&lt;br /&gt;
		idMatchedProfile = util.Filter(allProfiles, func(p ProfileResponse) bool { return p.Id == profile.Id &amp;amp;&amp;amp; p.Type == profileType })&lt;br /&gt;
		// matching based on versionID&lt;br /&gt;
		if len(idMatchedProfile) &amp;gt; 0 {&lt;br /&gt;
			matchedVersion = util.Filter(idMatchedProfile[0].Versions, func(versions Version) bool { return versions.Id == profile.VersionId })&lt;br /&gt;
			// when versionID level match found, override latestVersionId as it is used in the database provisioning request&lt;br /&gt;
			if len(matchedVersion) &amp;gt; 0 {&lt;br /&gt;
				log.Info(&amp;quot;Id and VersionId matched for profileType&amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
				idMatchedProfile[0].LatestVersionId = profile.VersionId&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		err = PopulateProfileOfType(ctx, profilesMap, profileType, allProfiles, dbType, idMatchedProfile)&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' We use profiles in YAML file only when they have correct Id. To perform this essential task of checking the correctness of Id and after that populating the versionId, we have created this function. After checking the correctness, this function delegates the further tasks to PopulateProfileOfType function.&lt;br /&gt;
&lt;br /&gt;
=====PopulateProfileOfType =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function performs the task of populating profileMap with response (matching result) received for the profileType.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
	func PopulateProfileOfType(ctx context.Context, profileMap map[string]ProfileResponse, profileType string, allProfiles []ProfileResponse, dbType string, response []ProfileResponse) (err error) {&lt;br /&gt;
		log := ctrllog.FromContext(ctx)&lt;br /&gt;
		// if response is empty, it indicates no matching profile found; hence set the default OOB profile for that type&lt;br /&gt;
		if len(response) == 0 {&lt;br /&gt;
			err = fmt.Errorf(&amp;quot;No matching profile found for profileType = %s&amp;quot;, profileType)&lt;br /&gt;
			log.Info(&amp;quot;Error Occurred. No enrichment performed for profile = &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
		log.Info(&amp;quot;Going to populate profile value in profilesMap for profileType = &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
		profileMap[profileType] = response[0]&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' Since this function is called only after the sanity check of Id and versionId, this function just has to populate the profiles by their respective types.&lt;br /&gt;
&lt;br /&gt;
=====PopulateDefaultProfile =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This method populates profileMap with the default value for the profileType.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
	func PopulateDefaultProfile(ctx context.Context, profileMap map[string]ProfileResponse, profileType string, allProfiles []ProfileResponse, dbType string) (err error) {&lt;br /&gt;
		log := ctrllog.FromContext(ctx)&lt;br /&gt;
		log.Info(&amp;quot;Going to set default profile value for profileType = &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
		genericProfiles := util.Filter(allProfiles, func(p ProfileResponse) bool { return p.EngineType == DATABASE_ENGINE_TYPE_GENERIC })&lt;br /&gt;
		dbEngineSpecificProfiles := util.Filter(allProfiles, func(p ProfileResponse) bool { return p.EngineType == GetDatabaseEngineName(dbType) })&lt;br /&gt;
		response, err := GetDefaultProfileForType(genericProfiles, dbEngineSpecificProfiles, profileType)&lt;br /&gt;
		if err != nil {&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
		profileMap[profileType] = response[0]&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' Since this function is called only if profiles are missing in the YAML file or have wrong profile Id, this function just has to populate the default out of the box profiles for each profile type. This function calls GetDefaultProfileForType function to get profiles for each profile type.&lt;br /&gt;
&lt;br /&gt;
=====GetDefaultProfileForType =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function gets default profile for each profile type from the result of GET API call to the NDB API.&lt;br /&gt;
&lt;br /&gt;
'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
	func GetDefaultProfileForType(genericProfiles []ProfileResponse, dbEngineSpecificProfiles []ProfileResponse, profileType string) (profile []ProfileResponse, err error) {&lt;br /&gt;
		switch profileType {&lt;br /&gt;
		case PROFILE_TYPE_COMPUTE:&lt;br /&gt;
			profile = util.Filter(genericProfiles, func(p ProfileResponse) bool {&lt;br /&gt;
				return p.Type == PROFILE_TYPE_COMPUTE &amp;amp;&amp;amp; strings.Contains(strings.ToLower(p.Name), &amp;quot;small&amp;quot;)&lt;br /&gt;
			})&lt;br /&gt;
			break&lt;br /&gt;
		case PROFILE_TYPE_SOFTWARE:&lt;br /&gt;
			profile = util.Filter(dbEngineSpecificProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_SOFTWARE &amp;amp;&amp;amp; p.Topology == TOPOLOGY_SINGLE })&lt;br /&gt;
			break&lt;br /&gt;
		case PROFILE_TYPE_NETWORK:&lt;br /&gt;
			profile = util.Filter(dbEngineSpecificProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_NETWORK })&lt;br /&gt;
			break&lt;br /&gt;
		case PROFILE_TYPE_DATABASE_PARAMETER:&lt;br /&gt;
			profile = util.Filter(dbEngineSpecificProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_DATABASE_PARAMETER })&lt;br /&gt;
			break&lt;br /&gt;
		case PROFILE_TYPE_STORAGE:&lt;br /&gt;
			profile = util.Filter(genericProfiles, func(p ProfileResponse) bool { return p.Type == PROFILE_TYPE_STORAGE })&lt;br /&gt;
			break&lt;br /&gt;
		default:&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
		if len(profile) == 0 {&lt;br /&gt;
			err = errors.New(&amp;quot;oob profile: one or more OOB profile(s) were not found&amp;quot;)&lt;br /&gt;
			return&lt;br /&gt;
		}&lt;br /&gt;
		return&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' We have to find appropriate default profile from the API call result. With the help of some util functions, this function performs this filtering task.&lt;br /&gt;
&lt;br /&gt;
=====GetProfileByType =====&lt;br /&gt;
&lt;br /&gt;
'''Previous Working :''' This function was not there previously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working :''' This function just returns the appropriate costant value from the pre-defined object structures.&lt;br /&gt;
&lt;br /&gt;
'''Previous Code :''' N/A&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code :'''&lt;br /&gt;
&lt;br /&gt;
	func GetProfileByType(profileType string, profiles Profiles) Profile {&lt;br /&gt;
		defaultEmptyProfile := Profile{}&lt;br /&gt;
		switch profileType {&lt;br /&gt;
		case PROFILE_TYPE_COMPUTE:&lt;br /&gt;
			return profiles.Compute&lt;br /&gt;
		case PROFILE_TYPE_SOFTWARE:&lt;br /&gt;
			return profiles.Software&lt;br /&gt;
		case PROFILE_TYPE_NETWORK:&lt;br /&gt;
			return profiles.Network&lt;br /&gt;
		case PROFILE_TYPE_DATABASE_PARAMETER:&lt;br /&gt;
			return profiles.DbParam&lt;br /&gt;
		default:&lt;br /&gt;
			return defaultEmptyProfile&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change :''' We use pre-defined object structures for reference everywhere. This function helps us to make that references correctly.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on provided software/compute/network/dbParams profiles&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the provided software/compute/network/dbParams profiles as input through YAML file, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Throwing error if invalid software/compute/network/dbParams profiles are given as input&lt;br /&gt;
** Description: This test case verifies that error is thrown if invalid software/compute/network/dbParams profiles are provided as input through YAML file.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the database has not been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the error is thrown on the command prompt&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system does not provision the database&lt;br /&gt;
*** The error is thrown saying that the id/version id of software/compute/network/dbParams profiles is invalid&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 3 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default software/compute/network/dbParams profiles for database provisioning when software/compute/network/dbParams profiles are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default software/compute/network/dbParams profiles for configuration when software/compute/network/dbParams profiles are not present in the profiles section of &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are not available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the default profiles&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the default profile values&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
Testcases were written in &amp;quot;\ndb-operator\test\ndb_api_helpers_test.go&amp;quot;&lt;br /&gt;
&amp;lt;br&amp;gt;Dummy Objects required for these testcases were created in &amp;quot;\ndb-operator\test\testutility.go&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 1 and Test Scenario 3 ===&lt;br /&gt;
    func TestEnrichAndGetProfilesWhenCustomProfilesMatch(t *testing.T) {&lt;br /&gt;
        //Set&lt;br /&gt;
        server := GetServerTestHelper(t)&lt;br /&gt;
        defer server.Close()&lt;br /&gt;
        ndbclient := ndbclient.NewNDBClient(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, server.URL, &amp;quot;&amp;quot;, true)&lt;br /&gt;
        //Test&lt;br /&gt;
        dbTypes := []string{&amp;quot;postgres&amp;quot;, &amp;quot;mysql&amp;quot;, &amp;quot;mongodb&amp;quot;}&lt;br /&gt;
        for _, dbType := range dbTypes {&lt;br /&gt;
            // get custom profile based upon the database type&lt;br /&gt;
            customProfile := GetCustomProfileForDBType(dbType)&lt;br /&gt;
            profileMap, _ := v1alpha1.EnrichAndGetProfiles(context.Background(), ndbclient, dbType, customProfile)&lt;br /&gt;
            //Assert&lt;br /&gt;
            profileTypes := []string{&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_COMPUTE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_STORAGE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_SOFTWARE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_NETWORK,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_DATABASE_PARAMETER,&lt;br /&gt;
            }&lt;br /&gt;
            for _, profileType := range profileTypes {&lt;br /&gt;
                profile := profileMap[profileType]&lt;br /&gt;
                //Assert that no profileType is empty&lt;br /&gt;
                if profile == (v1alpha1.ProfileResponse{}) {&lt;br /&gt;
                    t.Errorf(&amp;quot;Empty profile type %s for dbType %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                //Assert that profile EngineType matches the database engine or the generic type&lt;br /&gt;
                if profile.EngineType != v1alpha1.GetDatabaseEngineName(dbType) &amp;amp;&amp;amp; profile.EngineType != v1alpha1.DATABASE_ENGINE_TYPE_GENERIC {&lt;br /&gt;
                    t.Errorf(&amp;quot;Profile engine type %s for dbType %s does not match&amp;quot;, profile.EngineType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                obtainedProfile := v1alpha1.GetProfileForType(profileType, customProfile)&lt;br /&gt;
                // Ignoring Storage Profile Type as the Profile struct currently only supports compute, software, network and dbParam&lt;br /&gt;
                if profileType != v1alpha1.PROFILE_TYPE_STORAGE &amp;amp;&amp;amp; profile.Id != obtainedProfile.Id &amp;amp;&amp;amp; profile.LatestVersionId != obtainedProfile.VersionId {&lt;br /&gt;
                    t.Errorf(&amp;quot;Custom Profile Enrichment failed for profileType = %s and dbType = %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
    func GetCustomProfileForDBType(dbType string) (profiles v1alpha1.Profiles) {&lt;br /&gt;
        switch dbType {&lt;br /&gt;
        case v1alpha1.DATABASE_TYPE_POSTGRES:&lt;br /&gt;
            profiles = v1alpha1.Profiles{&lt;br /&gt;
                // Custom Software Profile Name = &amp;quot;custom postgres software profile&amp;quot;&lt;br /&gt;
                Software: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;12&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-12&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                // Custom ompute Name = &amp;quot;a&amp;quot;&lt;br /&gt;
                Compute: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;1&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-1&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Network: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;15&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-15&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                DbParam: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;18&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-18&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
            }&lt;br /&gt;
            return profiles&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 2 ===&lt;br /&gt;
    func TestEnrichAndGetProfilesWhenInvalidCustomProfilesProvided(t *testing.T) {&lt;br /&gt;
        //Set&lt;br /&gt;
        server := GetServerTestHelper(t)&lt;br /&gt;
        defer server.Close()&lt;br /&gt;
        ndbclient := ndbclient.NewNDBClient(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, server.URL, &amp;quot;&amp;quot;, true)&lt;br /&gt;
        //Test&lt;br /&gt;
        dbTypes := []string{&amp;quot;postgres_invalid_profiles&amp;quot;, &amp;quot;mysql_invalid_profiles&amp;quot;, &amp;quot;mongodb_invalid_profiles&amp;quot;}&lt;br /&gt;
        for _, dbType := range dbTypes {&lt;br /&gt;
            // get custom profile based upon the database type&lt;br /&gt;
            customProfile := GetCustomProfileForDBType(dbType)&lt;br /&gt;
            profileMap, _ := v1alpha1.EnrichAndGetProfiles(context.Background(), ndbclient, dbType, customProfile)&lt;br /&gt;
            //Assert&lt;br /&gt;
            profileTypes := []string{&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_COMPUTE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_STORAGE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_SOFTWARE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_NETWORK,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_DATABASE_PARAMETER,&lt;br /&gt;
            }&lt;br /&gt;
            for _, profileType := range profileTypes {&lt;br /&gt;
                profile := profileMap[profileType]&lt;br /&gt;
                //Assert that profile EngineType matches the database engine or the generic type&lt;br /&gt;
                if profile.EngineType != v1alpha1.GetDatabaseEngineName(dbType) &amp;amp;&amp;amp; profile.EngineType != v1alpha1.DATABASE_ENGINE_TYPE_GENERIC {&lt;br /&gt;
                    t.Errorf(&amp;quot;Profile engine type %s for dbType %s does not match&amp;quot;, profile.EngineType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                /* since custom profile is passed it should not default to OOB, and err should be raised stating the custom profile passed does not exist,&lt;br /&gt;
                and thus database provisioning does not occur&lt;br /&gt;
                */&lt;br /&gt;
                if profile != (v1alpha1.ProfileResponse{}) {&lt;br /&gt;
                    t.Errorf(&amp;quot;Incorrect Profile Match found for profile type = %s and dbType = %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
    case v1alpha1.DATABASE_TYPE_MONGODB_INVALID_PROFILE, v1alpha1.DATABASE_TYPE_MYSQL_INVALID_PROFILE, v1alpha1.DATABASE_TYPE_POSTGRES_INVALID_PROFILE:&lt;br /&gt;
            // below custom profiles do not exist and will be used for the negative scenario&lt;br /&gt;
            profiles = v1alpha1.Profiles{&lt;br /&gt;
                Software: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;140&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-140&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Compute: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;100&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-100&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Network: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;170&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-170&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                DbParam: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;200&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-200&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
            }&lt;br /&gt;
            return profiles&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=149228</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=149228"/>
		<updated>2023-04-08T01:00:15Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
Employing Clean Code Practices + Design Patterns:&lt;br /&gt;
&lt;br /&gt;
DRY (Do Not Repeat Yourself):&lt;br /&gt;
The initial draft of using 4 if &amp;amp; else statements for each of the profiles (namely: software, compute, network, and storage) and performing the same checks and code again proved to be in direct opposition to the DRY approach whose repetitiveness can be seen from the below snippet:&lt;br /&gt;
        &lt;br /&gt;
            //Custom Software Profile Check and overriding the default values&lt;br /&gt;
            softwareProfile := dbSpec.Instance.Profiles.Software&lt;br /&gt;
            if softwareProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for software profiles as no custom profile received for Software. Hence, proceeding with default OOB software profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, softwareProfile, PROFILE_TYPE_SOFTWARE)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_SOFTWARE] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom Compute Profile Check and overriding the default values&lt;br /&gt;
            computeProfile := dbSpec.Instance.Profiles.Compute&lt;br /&gt;
            if computeProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for compute profiles as no custom profile received for Compute. Hence, proceeding with default OOB compute profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, genericProfiles, computeProfile, PROFILE_TYPE_COMPUTE)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_COMPUTE] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom Network Profile Check and overriding the default values&lt;br /&gt;
            networkProfile := dbSpec.Instance.Profiles.Network&lt;br /&gt;
            if networkProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for network profiles as no custom profile received for Network. Hence, proceeding with default OOB network profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, networkProfile, PROFILE_TYPE_NETWORK)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_NETWORK] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom DbParam Profile Check and overriding the default values&lt;br /&gt;
            dbParamProfile := dbSpec.Instance.Profiles.DbParam&lt;br /&gt;
            if dbParamProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for database parameter profiles as no custom profile received for it. Hence, proceeding with default OOB dbParam profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, dbParamProfile, PROFILE_TYPE_DATABASE_PARAMETER)&lt;br /&gt;
                if err != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_DATABASE_PARAMETER] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Hence, we create an array of profiles, and iterating over those profiles and calling a clean modular function by identifying the common key points from the above snippet helps in eliminating code duplication and makes the code more, readable, and open for extension by just adding new profile name to the list of profiles and easily maintainable! This approach can be viewed in EnrichProfilesMap() and delegate functions.&lt;br /&gt;
&lt;br /&gt;
Refactoring + Delegation using Facade Design Pattern:&lt;br /&gt;
&lt;br /&gt;
By default, for populating profiles, GetOOBProfiles() was called which did the task of fetching all profiles and populating default profile values. However, with the advent of custom profiles being provided from YAML, we suggest refactoring GetOOBProfiles() to EnrichProfilesMap() which will perform the same task as GetOOBProfiles did but in addition override the default values to input profiles provided after performing checks such as:&lt;br /&gt;
(1) Emptiness / Null checks for profiles&lt;br /&gt;
(2) Performing matching of the Id/VersionId for the custom profile provided &amp;amp; failing the database provisioning request in case of a match is not found.&lt;br /&gt;
Thus, for each of the checkers, we create modular functions and delegate the task of performing the above-mentioned checks.&lt;br /&gt;
&lt;br /&gt;
The flow proceeds as:&lt;br /&gt;
&lt;br /&gt;
EnrichProfilesMap() &lt;br /&gt;
Performs the task to set default values and override the default values by calling the below functions.  &lt;br /&gt;
&lt;br /&gt;
PerformProfileMatchingAndEnrichProfiles() uses&lt;br /&gt;
[isEmptyProfile() + GetAppropriateProfileForType() + GetTopologyForProfileType() in filtering profiles]&lt;br /&gt;
Once, the user input is received, the input is delegated for checks and performing matching if the profile exists. Additionally, other factory methods are also used for performing the matching of profiles&lt;br /&gt;
&lt;br /&gt;
EnrichProfileMapForProfileType()&lt;br /&gt;
Performs the final overriding of the default profile with the matched profile. Additionally, it cancels the database provisioning request for unmatched profiles.&lt;br /&gt;
&lt;br /&gt;
Moreover, in Tests, new tests have been added which indicate their functionality through their names adhering to the Clean Coding Naming principle:&lt;br /&gt;
TestEnrichAndGetProfilesWhenCustomProfilesMatch()&lt;br /&gt;
TestEnrichAndGetProfilesWhenInvalidCustomProfilesProvided()&lt;br /&gt;
&lt;br /&gt;
Additionally, we can also view the creation of specific functions as the alignment with the Facade Design Pattern that delegates the task of performing specific actions to respective functions. &lt;br /&gt;
&lt;br /&gt;
Alternatively, a closer look at the initial draft and performing Cyclomatic Complexity checks indicate that our approach has breached the threshold value of permissible complexity. Hence, to tackle this problem, we will change the filtering profile functions to perform matching based on Id and further based on versionId and eliminating factory methods like getTopology(), getProfileForType(), and a few checker functions that will aid in reducing the cyclomatic complexity automatically.&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
'''Previous Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&lt;br /&gt;
    // Fetch the OOB profiles for the database&lt;br /&gt;
    profilesMap, err := GetOOBProfiles(ctx, ndbclient, dbSpec.Instance.Type)&lt;br /&gt;
    if err != nil {&lt;br /&gt;
        log.Error(err, &amp;quot;Error occurred while getting OOB profiles&amp;quot;, &amp;quot;database name&amp;quot;, dbSpec.Instance.DatabaseInstanceName, &amp;quot;database type&amp;quot;, dbSpec.Instance.Type)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    // Fetch upto date profiles for the database&lt;br /&gt;
    profilesMap, err := EnrichAndGetProfiles(ctx, ndbclient, dbSpec.Instance.Type, dbSpec.Instance.Profiles)&lt;br /&gt;
    if err != nil {&lt;br /&gt;
        log.Error(err, &amp;quot;Error occurred while enriching and getting profiles&amp;quot;, &amp;quot;database name&amp;quot;, dbSpec.Instance.DatabaseInstanceName, &amp;quot;database type&amp;quot;, dbSpec.Instance.Type)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
'''Previous Working''' : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
        profileMap[PROFILE_TYPE_COMPUTE] = computeProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_STORAGE] = storageProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_SOFTWARE] = softwareProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_NETWORK] = networkProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_DATABASE_PARAMETER] = dbParamProfiles[0]&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
        profileMap[PROFILE_TYPE_COMPUTE] = computeProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_STORAGE] = storageProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_SOFTWARE] = softwareProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_NETWORK] = networkProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_DATABASE_PARAMETER] = dbParamProfiles[0]&lt;br /&gt;
        // performs overriding of default OOB profiles based on the customProfiles obtained through YAML&lt;br /&gt;
        err = EnrichProfilesMap(ctx, customProfiles, genericProfiles, dbEngineSpecificProfiles, profileMap)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    func EnrichProfilesMap(ctx context.Context, customProfiles Profiles, genericProfiles []ProfileResponse, dbEngineSpecificProfiles []ProfileResponse, profilesMap map[string]ProfileResponse) (err error) {&lt;br /&gt;
        log := ctrllog.FromContext(ctx)&lt;br /&gt;
        if customProfiles == (Profiles{}) {&lt;br /&gt;
            log.Info(&amp;quot;Defaulting to using OOB Profiles as no custom profiles received. Returning from enrichingProfilesMap&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
        }&lt;br /&gt;
        log.Info(&amp;quot;Received Custom Profiles =&amp;gt; &amp;quot;, &amp;quot;Received Custom Profiles&amp;quot;, customProfiles)&lt;br /&gt;
        customProfileOptions := [...]string{PROFILE_TYPE_COMPUTE, PROFILE_TYPE_SOFTWARE, PROFILE_TYPE_NETWORK, PROFILE_TYPE_DATABASE_PARAMETER}&lt;br /&gt;
        for _, profileValue := range customProfileOptions {&lt;br /&gt;
            err = PerformProfileMatchingAndEnrichProfiles(ctx, profileValue, customProfiles, genericProfiles, dbEngineSpecificProfiles, profilesMap)&lt;br /&gt;
            if err != nil {&lt;br /&gt;
                return&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    func PerformProfileMatchingAndEnrichProfiles(ctx context.Context, profileType string, customProfiles Profiles, genericProfiles []ProfileResponse, dbEngineSpecificProfiles []ProfileResponse, profilesMap map[string]ProfileResponse) (err error) {&lt;br /&gt;
        log := ctrllog.FromContext(ctx)&lt;br /&gt;
        customProfile := GetProfileForType(profileType, customProfiles)&lt;br /&gt;
        if !isEmptyProfile(customProfile) {&lt;br /&gt;
            profileToUseForMatching := GetAppropriateProfileForType(profileType, genericProfiles, dbEngineSpecificProfiles)&lt;br /&gt;
            log.Info(&amp;quot;Performing profile matching for profileType =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
            matchedProfile := util.Filter(profileToUseForMatching, func(p ProfileResponse) bool {&lt;br /&gt;
                return p.Type == profileType &amp;amp;&amp;amp;&lt;br /&gt;
                    p.Id == customProfile.Id &amp;amp;&amp;amp;&lt;br /&gt;
                    p.LatestVersionId == customProfile.VersionId &amp;amp;&amp;amp;&lt;br /&gt;
                    p.Topology == GetTopologyForProfileType(profileType)&lt;br /&gt;
            })&lt;br /&gt;
            err = EnrichProfileMapForProfileType(ctx, profilesMap, profileType, matchedProfile)&lt;br /&gt;
        }&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    func GetAppropriateProfileForType(profileType string, genericProfiles []ProfileResponse, dbEngineSpecificProfiles []ProfileResponse) (profiles []ProfileResponse) {&lt;br /&gt;
        if profileType == PROFILE_TYPE_COMPUTE {&lt;br /&gt;
            return genericProfiles&lt;br /&gt;
        } else {&lt;br /&gt;
            return dbEngineSpecificProfiles&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    func EnrichProfileMapForProfileType(ctx context.Context, profileMap map[string]ProfileResponse, profileType string, response []ProfileResponse) (err error) {&lt;br /&gt;
        log := ctrllog.FromContext(ctx)&lt;br /&gt;
        if len(response) == 0 {&lt;br /&gt;
            err = fmt.Errorf(&amp;quot;No matching profile found for profileType = %s&amp;quot;, profileType)&lt;br /&gt;
            log.Info(&amp;quot;Error Occurred. No enrichment performed for profile =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
            return&lt;br /&gt;
        }&lt;br /&gt;
        log.Info(&amp;quot;Profile Matching succeeded for profileType =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
        log.Info(&amp;quot;Going to perform custom profile enrichment performed for =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
        profileMap[profileType] = response[0]&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Providing the least costly topology based on each profile type.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    func GetTopologyForProfileType(profileType string) string {&lt;br /&gt;
        switch profileType {&lt;br /&gt;
        case PROFILE_TYPE_COMPUTE:&lt;br /&gt;
            return TOPOLOGY_ALL&lt;br /&gt;
        case PROFILE_TYPE_SOFTWARE:&lt;br /&gt;
            return TOPOLOGY_SINGLE&lt;br /&gt;
        case PROFILE_TYPE_NETWORK:&lt;br /&gt;
            return TOPOLOGY_ALL&lt;br /&gt;
        case PROFILE_TYPE_DATABASE_PARAMETER:&lt;br /&gt;
            return TOPOLOGY_INSTANCE&lt;br /&gt;
        default:&lt;br /&gt;
            return &amp;quot;&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on provided software/compute/network/dbParams profiles&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the provided software/compute/network/dbParams profiles as input through YAML file, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Throwing error if invalid software/compute/network/dbParams profiles are given as input&lt;br /&gt;
** Description: This test case verifies that error is thrown if invalid software/compute/network/dbParams profiles are provided as input through YAML file.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the database has not been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the error is thrown on the command prompt&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system does not provision the database&lt;br /&gt;
*** The error is thrown saying that the id/version id of software/compute/network/dbParams profiles is invalid&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 3 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default software/compute/network/dbParams profiles for database provisioning when software/compute/network/dbParams profiles are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default software/compute/network/dbParams profiles for configuration when software/compute/network/dbParams profiles are not present in the profiles section of &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are not available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the default profiles&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the default profile values&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
Testcases were written in &amp;quot;\ndb-operator\test\ndb_api_helpers_test.go&amp;quot;&lt;br /&gt;
&amp;lt;br&amp;gt;Dummy Objects required for these testcases were created in &amp;quot;\ndb-operator\test\testutility.go&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 1 and Test Scenario 3 ===&lt;br /&gt;
    func TestEnrichAndGetProfilesWhenCustomProfilesMatch(t *testing.T) {&lt;br /&gt;
        //Set&lt;br /&gt;
        server := GetServerTestHelper(t)&lt;br /&gt;
        defer server.Close()&lt;br /&gt;
        ndbclient := ndbclient.NewNDBClient(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, server.URL, &amp;quot;&amp;quot;, true)&lt;br /&gt;
        //Test&lt;br /&gt;
        dbTypes := []string{&amp;quot;postgres&amp;quot;, &amp;quot;mysql&amp;quot;, &amp;quot;mongodb&amp;quot;}&lt;br /&gt;
        for _, dbType := range dbTypes {&lt;br /&gt;
            // get custom profile based upon the database type&lt;br /&gt;
            customProfile := GetCustomProfileForDBType(dbType)&lt;br /&gt;
            profileMap, _ := v1alpha1.EnrichAndGetProfiles(context.Background(), ndbclient, dbType, customProfile)&lt;br /&gt;
            //Assert&lt;br /&gt;
            profileTypes := []string{&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_COMPUTE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_STORAGE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_SOFTWARE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_NETWORK,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_DATABASE_PARAMETER,&lt;br /&gt;
            }&lt;br /&gt;
            for _, profileType := range profileTypes {&lt;br /&gt;
                profile := profileMap[profileType]&lt;br /&gt;
                //Assert that no profileType is empty&lt;br /&gt;
                if profile == (v1alpha1.ProfileResponse{}) {&lt;br /&gt;
                    t.Errorf(&amp;quot;Empty profile type %s for dbType %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                //Assert that profile EngineType matches the database engine or the generic type&lt;br /&gt;
                if profile.EngineType != v1alpha1.GetDatabaseEngineName(dbType) &amp;amp;&amp;amp; profile.EngineType != v1alpha1.DATABASE_ENGINE_TYPE_GENERIC {&lt;br /&gt;
                    t.Errorf(&amp;quot;Profile engine type %s for dbType %s does not match&amp;quot;, profile.EngineType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                obtainedProfile := v1alpha1.GetProfileForType(profileType, customProfile)&lt;br /&gt;
                // Ignoring Storage Profile Type as the Profile struct currently only supports compute, software, network and dbParam&lt;br /&gt;
                if profileType != v1alpha1.PROFILE_TYPE_STORAGE &amp;amp;&amp;amp; profile.Id != obtainedProfile.Id &amp;amp;&amp;amp; profile.LatestVersionId != obtainedProfile.VersionId {&lt;br /&gt;
                    t.Errorf(&amp;quot;Custom Profile Enrichment failed for profileType = %s and dbType = %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
    func GetCustomProfileForDBType(dbType string) (profiles v1alpha1.Profiles) {&lt;br /&gt;
        switch dbType {&lt;br /&gt;
        case v1alpha1.DATABASE_TYPE_POSTGRES:&lt;br /&gt;
            profiles = v1alpha1.Profiles{&lt;br /&gt;
                // Custom Software Profile Name = &amp;quot;custom postgres software profile&amp;quot;&lt;br /&gt;
                Software: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;12&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-12&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                // Custom ompute Name = &amp;quot;a&amp;quot;&lt;br /&gt;
                Compute: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;1&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-1&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Network: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;15&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-15&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                DbParam: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;18&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-18&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
            }&lt;br /&gt;
            return profiles&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 2 ===&lt;br /&gt;
    func TestEnrichAndGetProfilesWhenInvalidCustomProfilesProvided(t *testing.T) {&lt;br /&gt;
        //Set&lt;br /&gt;
        server := GetServerTestHelper(t)&lt;br /&gt;
        defer server.Close()&lt;br /&gt;
        ndbclient := ndbclient.NewNDBClient(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, server.URL, &amp;quot;&amp;quot;, true)&lt;br /&gt;
        //Test&lt;br /&gt;
        dbTypes := []string{&amp;quot;postgres_invalid_profiles&amp;quot;, &amp;quot;mysql_invalid_profiles&amp;quot;, &amp;quot;mongodb_invalid_profiles&amp;quot;}&lt;br /&gt;
        for _, dbType := range dbTypes {&lt;br /&gt;
            // get custom profile based upon the database type&lt;br /&gt;
            customProfile := GetCustomProfileForDBType(dbType)&lt;br /&gt;
            profileMap, _ := v1alpha1.EnrichAndGetProfiles(context.Background(), ndbclient, dbType, customProfile)&lt;br /&gt;
            //Assert&lt;br /&gt;
            profileTypes := []string{&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_COMPUTE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_STORAGE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_SOFTWARE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_NETWORK,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_DATABASE_PARAMETER,&lt;br /&gt;
            }&lt;br /&gt;
            for _, profileType := range profileTypes {&lt;br /&gt;
                profile := profileMap[profileType]&lt;br /&gt;
                //Assert that profile EngineType matches the database engine or the generic type&lt;br /&gt;
                if profile.EngineType != v1alpha1.GetDatabaseEngineName(dbType) &amp;amp;&amp;amp; profile.EngineType != v1alpha1.DATABASE_ENGINE_TYPE_GENERIC {&lt;br /&gt;
                    t.Errorf(&amp;quot;Profile engine type %s for dbType %s does not match&amp;quot;, profile.EngineType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                /* since custom profile is passed it should not default to OOB, and err should be raised stating the custom profile passed does not exist,&lt;br /&gt;
                and thus database provisioning does not occur&lt;br /&gt;
                */&lt;br /&gt;
                if profile != (v1alpha1.ProfileResponse{}) {&lt;br /&gt;
                    t.Errorf(&amp;quot;Incorrect Profile Match found for profile type = %s and dbType = %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
    case v1alpha1.DATABASE_TYPE_MONGODB_INVALID_PROFILE, v1alpha1.DATABASE_TYPE_MYSQL_INVALID_PROFILE, v1alpha1.DATABASE_TYPE_POSTGRES_INVALID_PROFILE:&lt;br /&gt;
            // below custom profiles do not exist and will be used for the negative scenario&lt;br /&gt;
            profiles = v1alpha1.Profiles{&lt;br /&gt;
                Software: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;140&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-140&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Compute: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;100&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-100&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Network: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;170&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-170&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                DbParam: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;200&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-200&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
            }&lt;br /&gt;
            return profiles&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=149217</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=149217"/>
		<updated>2023-04-08T00:52:28Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
Employing Clean Code Practices + Design Patterns:&lt;br /&gt;
&lt;br /&gt;
DRY (Do Not Repeat Yourself):&lt;br /&gt;
The initial draft of using 4 if &amp;amp; else statements for each of the profiles (namely: software, compute, network, and storage) and performing the same checks and code again proved to be in direct opposition to the DRY approach whose repetitiveness can be seen from the below snippet:&lt;br /&gt;
        &lt;br /&gt;
            //Custom Software Profile Check and overriding the default values&lt;br /&gt;
            softwareProfile := dbSpec.Instance.Profiles.Software&lt;br /&gt;
            if softwareProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for software profiles as no custom profile received for Software. Hence, proceeding with default OOB software profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, softwareProfile, PROFILE_TYPE_SOFTWARE)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_SOFTWARE] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom Compute Profile Check and overriding the default values&lt;br /&gt;
            computeProfile := dbSpec.Instance.Profiles.Compute&lt;br /&gt;
            if computeProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for compute profiles as no custom profile received for Compute. Hence, proceeding with default OOB compute profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, genericProfiles, computeProfile, PROFILE_TYPE_COMPUTE)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_COMPUTE] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom Network Profile Check and overriding the default values&lt;br /&gt;
            networkProfile := dbSpec.Instance.Profiles.Network&lt;br /&gt;
            if networkProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for network profiles as no custom profile received for Network. Hence, proceeding with default OOB network profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, networkProfile, PROFILE_TYPE_NETWORK)&lt;br /&gt;
                if errorThroughChecks != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_NETWORK] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            //Custom DbParam Profile Check and overriding the default values&lt;br /&gt;
            dbParamProfile := dbSpec.Instance.Profiles.DbParam&lt;br /&gt;
            if dbParamProfile == (Profile{}) {&lt;br /&gt;
                log.Info(&amp;quot;No enrichment for database parameter profiles as no custom profile received for it. Hence, proceeding with default OOB dbParam profile&amp;quot;)&lt;br /&gt;
            } else {&lt;br /&gt;
                isValidProfile, matchedProfile, errorThroughChecks := performProfileAvailabilityCheck(ctx, dbEngineSpecificProfiles, dbParamProfile, PROFILE_TYPE_DATABASE_PARAMETER)&lt;br /&gt;
                if err != nil {&lt;br /&gt;
                    //log.Error(err, &amp;quot;&amp;quot;)&lt;br /&gt;
                    return errorThroughChecks&lt;br /&gt;
                }&lt;br /&gt;
                if isValidProfile {&lt;br /&gt;
                    profilesMap[PROFILE_TYPE_DATABASE_PARAMETER] = matchedProfile&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Hence, we create an array of profiles, and iterating over those profiles and calling a clean modular function by identifying the common key points from the above snippet helps in eliminating code duplication and makes the code more, readable, and open for extension by just adding new profile name to the list of profiles and easily maintainable! This approach can be viewed in EnrichProfilesMap() and delegate functions.&lt;br /&gt;
&lt;br /&gt;
Refactoring + Delegation using Facade Design Pattern:&lt;br /&gt;
&lt;br /&gt;
By default, for populating profiles, GetOOBProfiles() was called which did the task of fetching all profiles and populating default profile values. However, with the advent of custom profiles being provided from YAML, we suggest refactoring GetOOBProfiles() to EnrichProfilesMap() which will perform the same task as GetOOBProfiles did but in addition override the default values to input profiles provided after performing checks such as:&lt;br /&gt;
(1) Emptiness / Null checks for profiles&lt;br /&gt;
(2) Performing matching of the Id/VersionId for the custom profile provided &amp;amp; failing the database provisioning request in case of a match is not found.&lt;br /&gt;
Thus, for each of the checkers, we create modular functions and delegate the task of performing the above-mentioned checks.&lt;br /&gt;
&lt;br /&gt;
The flow proceeds as:&lt;br /&gt;
&lt;br /&gt;
EnrichProfilesMap() &lt;br /&gt;
Performs the task to set default values and override the default values by calling the below functions.  &lt;br /&gt;
&lt;br /&gt;
PerformProfileMatchingAndEnrichProfiles() uses&lt;br /&gt;
[isEmptyProfile() + GetAppropriateProfileForType() + GetTopologyForProfileType() in filtering profiles]&lt;br /&gt;
Once, the user input is received, the input is delegated for checks and performing matching if the profile exists. Additionally, other factory methods are also used for performing the matching of profiles&lt;br /&gt;
&lt;br /&gt;
EnrichProfileMapForProfileType()&lt;br /&gt;
Performs the final overriding of the default profile with the matched profile. Additionally, it cancels the database provisioning request for unmatched profiles.&lt;br /&gt;
&lt;br /&gt;
Moreover, in Tests, new tests have been added which indicate their functionality through their names adhering to the Clean Coding Naming principle:&lt;br /&gt;
TestEnrichAndGetProfilesWhenCustomProfilesMatch()&lt;br /&gt;
TestEnrichAndGetProfilesWhenInvalidCustomProfilesProvided()&lt;br /&gt;
&lt;br /&gt;
Additionally, we can also view the creation of specific functions as the alignment with the Facade Design Pattern that delegates the task of performing specific actions to respective functions. &lt;br /&gt;
&lt;br /&gt;
Alternatively, a closer look at the initial draft and performing Cyclomatic Complexity checks indicate that our approach has breached the threshold value of permissible complexity. Hence, to tackle this problem, we will change the filtering profile functions to perform matching based on Id and further based on versionId and eliminating factory methods like getTopology(), getProfileForType(), and a few checker functions that will aid in reducing the cyclomatic complexity automatically.&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
'''Previous Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&lt;br /&gt;
    // Fetch the OOB profiles for the database&lt;br /&gt;
    profilesMap, err := GetOOBProfiles(ctx, ndbclient, dbSpec.Instance.Type)&lt;br /&gt;
    if err != nil {&lt;br /&gt;
        log.Error(err, &amp;quot;Error occurred while getting OOB profiles&amp;quot;, &amp;quot;database name&amp;quot;, dbSpec.Instance.DatabaseInstanceName, &amp;quot;database type&amp;quot;, dbSpec.Instance.Type)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    // Fetch upto date profiles for the database&lt;br /&gt;
    profilesMap, err := EnrichAndGetProfiles(ctx, ndbclient, dbSpec.Instance.Type, dbSpec.Instance.Profiles)&lt;br /&gt;
    if err != nil {&lt;br /&gt;
        log.Error(err, &amp;quot;Error occurred while enriching and getting profiles&amp;quot;, &amp;quot;database name&amp;quot;, dbSpec.Instance.DatabaseInstanceName, &amp;quot;database type&amp;quot;, dbSpec.Instance.Type)&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
'''Previous Working''' : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
        profileMap[PROFILE_TYPE_COMPUTE] = computeProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_STORAGE] = storageProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_SOFTWARE] = softwareProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_NETWORK] = networkProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_DATABASE_PARAMETER] = dbParamProfiles[0]&lt;br /&gt;
&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
        profileMap[PROFILE_TYPE_COMPUTE] = computeProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_STORAGE] = storageProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_SOFTWARE] = softwareProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_NETWORK] = networkProfiles[0]&lt;br /&gt;
        profileMap[PROFILE_TYPE_DATABASE_PARAMETER] = dbParamProfiles[0]&lt;br /&gt;
&lt;br /&gt;
        // performs overriding of default OOB profiles based on the customProfiles obtained through YAML&lt;br /&gt;
        err = EnrichProfilesMap(ctx, customProfiles, genericProfiles, dbEngineSpecificProfiles, profileMap)&lt;br /&gt;
&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    func EnrichProfilesMap(ctx context.Context, customProfiles Profiles, genericProfiles []ProfileResponse, dbEngineSpecificProfiles []ProfileResponse, profilesMap map[string]ProfileResponse) (err error) {&lt;br /&gt;
        log := ctrllog.FromContext(ctx)&lt;br /&gt;
        if customProfiles == (Profiles{}) {&lt;br /&gt;
            log.Info(&amp;quot;Defaulting to using OOB Profiles as no custom profiles received. Returning from enrichingProfilesMap&amp;quot;)&lt;br /&gt;
            return&lt;br /&gt;
        }&lt;br /&gt;
        log.Info(&amp;quot;Received Custom Profiles =&amp;gt; &amp;quot;, &amp;quot;Received Custom Profiles&amp;quot;, customProfiles)&lt;br /&gt;
        customProfileOptions := [...]string{PROFILE_TYPE_COMPUTE, PROFILE_TYPE_SOFTWARE, PROFILE_TYPE_NETWORK, PROFILE_TYPE_DATABASE_PARAMETER}&lt;br /&gt;
        for _, profileValue := range customProfileOptions {&lt;br /&gt;
            err = PerformProfileMatchingAndEnrichProfiles(ctx, profileValue, customProfiles, genericProfiles, dbEngineSpecificProfiles, profilesMap)&lt;br /&gt;
            if err != nil {&lt;br /&gt;
                return&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    func PerformProfileMatchingAndEnrichProfiles(ctx context.Context, profileType string, customProfiles Profiles, genericProfiles []ProfileResponse, dbEngineSpecificProfiles []ProfileResponse, profilesMap map[string]ProfileResponse) (err error) {&lt;br /&gt;
        log := ctrllog.FromContext(ctx)&lt;br /&gt;
        customProfile := GetProfileForType(profileType, customProfiles)&lt;br /&gt;
        if !isEmptyProfile(customProfile) {&lt;br /&gt;
            profileToUseForMatching := GetAppropriateProfileForType(profileType, genericProfiles, dbEngineSpecificProfiles)&lt;br /&gt;
            log.Info(&amp;quot;Performing profile matching for profileType =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
            matchedProfile := util.Filter(profileToUseForMatching, func(p ProfileResponse) bool {&lt;br /&gt;
                return p.Type == profileType &amp;amp;&amp;amp;&lt;br /&gt;
                    p.Id == customProfile.Id &amp;amp;&amp;amp;&lt;br /&gt;
                    p.LatestVersionId == customProfile.VersionId &amp;amp;&amp;amp;&lt;br /&gt;
                    p.Topology == GetTopologyForProfileType(profileType)&lt;br /&gt;
            })&lt;br /&gt;
            err = EnrichProfileMapForProfileType(ctx, profilesMap, profileType, matchedProfile)&lt;br /&gt;
        }&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    func GetAppropriateProfileForType(profileType string, genericProfiles []ProfileResponse, dbEngineSpecificProfiles []ProfileResponse) (profiles []ProfileResponse) {&lt;br /&gt;
        if profileType == PROFILE_TYPE_COMPUTE {&lt;br /&gt;
            return genericProfiles&lt;br /&gt;
        } else {&lt;br /&gt;
            return dbEngineSpecificProfiles&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    func EnrichProfileMapForProfileType(ctx context.Context, profileMap map[string]ProfileResponse, profileType string, response []ProfileResponse) (err error) {&lt;br /&gt;
        log := ctrllog.FromContext(ctx)&lt;br /&gt;
        if len(response) == 0 {&lt;br /&gt;
            err = fmt.Errorf(&amp;quot;No matching profile found for profileType = %s&amp;quot;, profileType)&lt;br /&gt;
            log.Info(&amp;quot;Error Occurred. No enrichment performed for profile =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
            return&lt;br /&gt;
        }&lt;br /&gt;
        log.Info(&amp;quot;Profile Matching succeeded for profileType =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
        log.Info(&amp;quot;Going to perform custom profile enrichment performed for =&amp;gt; &amp;quot;, &amp;quot;profileType&amp;quot;, profileType)&lt;br /&gt;
        profileMap[profileType] = response[0]&lt;br /&gt;
        return&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Providing the least costly topology based on each profile type.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
    func GetTopologyForProfileType(profileType string) string {&lt;br /&gt;
        switch profileType {&lt;br /&gt;
        case PROFILE_TYPE_COMPUTE:&lt;br /&gt;
            return TOPOLOGY_ALL&lt;br /&gt;
        case PROFILE_TYPE_SOFTWARE:&lt;br /&gt;
            return TOPOLOGY_SINGLE&lt;br /&gt;
        case PROFILE_TYPE_NETWORK:&lt;br /&gt;
            return TOPOLOGY_ALL&lt;br /&gt;
        case PROFILE_TYPE_DATABASE_PARAMETER:&lt;br /&gt;
            return TOPOLOGY_INSTANCE&lt;br /&gt;
        default:&lt;br /&gt;
            return &amp;quot;&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on provided software/compute/network/dbParams profiles&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the provided software/compute/network/dbParams profiles as input through YAML file, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Throwing error if invalid software/compute/network/dbParams profiles are given as input&lt;br /&gt;
** Description: This test case verifies that error is thrown if invalid software/compute/network/dbParams profiles are provided as input through YAML file.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the database has not been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the error is thrown on the command prompt&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system does not provision the database&lt;br /&gt;
*** The error is thrown saying that the id/version id of software/compute/network/dbParams profiles is invalid&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 3 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default software/compute/network/dbParams profiles for database provisioning when software/compute/network/dbParams profiles are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default software/compute/network/dbParams profiles for configuration when software/compute/network/dbParams profiles are not present in the profiles section of &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are not available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the default profiles&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the default profile values&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
Testcases were written in &amp;quot;\ndb-operator\test\ndb_api_helpers_test.go&amp;quot;&lt;br /&gt;
&amp;lt;br&amp;gt;Dummy Objects required for these testcases were created in &amp;quot;\ndb-operator\test\testutility.go&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 1 and Test Scenario 3 ===&lt;br /&gt;
    func TestEnrichAndGetProfilesWhenCustomProfilesMatch(t *testing.T) {&lt;br /&gt;
&lt;br /&gt;
        //Set&lt;br /&gt;
        server := GetServerTestHelper(t)&lt;br /&gt;
        defer server.Close()&lt;br /&gt;
        ndbclient := ndbclient.NewNDBClient(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, server.URL, &amp;quot;&amp;quot;, true)&lt;br /&gt;
&lt;br /&gt;
        //Test&lt;br /&gt;
        dbTypes := []string{&amp;quot;postgres&amp;quot;, &amp;quot;mysql&amp;quot;, &amp;quot;mongodb&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
        for _, dbType := range dbTypes {&lt;br /&gt;
&lt;br /&gt;
            // get custom profile based upon the database type&lt;br /&gt;
            customProfile := GetCustomProfileForDBType(dbType)&lt;br /&gt;
&lt;br /&gt;
            profileMap, _ := v1alpha1.EnrichAndGetProfiles(context.Background(), ndbclient, dbType, customProfile)&lt;br /&gt;
&lt;br /&gt;
            //Assert&lt;br /&gt;
            profileTypes := []string{&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_COMPUTE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_STORAGE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_SOFTWARE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_NETWORK,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_DATABASE_PARAMETER,&lt;br /&gt;
            }&lt;br /&gt;
            for _, profileType := range profileTypes {&lt;br /&gt;
                profile := profileMap[profileType]&lt;br /&gt;
                //Assert that no profileType is empty&lt;br /&gt;
                if profile == (v1alpha1.ProfileResponse{}) {&lt;br /&gt;
                    t.Errorf(&amp;quot;Empty profile type %s for dbType %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                //Assert that profile EngineType matches the database engine or the generic type&lt;br /&gt;
                if profile.EngineType != v1alpha1.GetDatabaseEngineName(dbType) &amp;amp;&amp;amp; profile.EngineType != v1alpha1.DATABASE_ENGINE_TYPE_GENERIC {&lt;br /&gt;
                    t.Errorf(&amp;quot;Profile engine type %s for dbType %s does not match&amp;quot;, profile.EngineType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                obtainedProfile := v1alpha1.GetProfileForType(profileType, customProfile)&lt;br /&gt;
                // Ignoring Storage Profile Type as the Profile struct currently only supports compute, software, network and dbParam&lt;br /&gt;
                if profileType != v1alpha1.PROFILE_TYPE_STORAGE &amp;amp;&amp;amp; profile.Id != obtainedProfile.Id &amp;amp;&amp;amp; profile.LatestVersionId != obtainedProfile.VersionId {&lt;br /&gt;
                    t.Errorf(&amp;quot;Custom Profile Enrichment failed for profileType = %s and dbType = %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
    func GetCustomProfileForDBType(dbType string) (profiles v1alpha1.Profiles) {&lt;br /&gt;
        switch dbType {&lt;br /&gt;
        case v1alpha1.DATABASE_TYPE_POSTGRES:&lt;br /&gt;
            profiles = v1alpha1.Profiles{&lt;br /&gt;
                // Custom Software Profile Name = &amp;quot;custom postgres software profile&amp;quot;&lt;br /&gt;
                Software: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;12&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-12&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                // Custom ompute Name = &amp;quot;a&amp;quot;&lt;br /&gt;
                Compute: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;1&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-1&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Network: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;15&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-15&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                DbParam: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;18&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-18&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
            }&lt;br /&gt;
            return profiles&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 2 ===&lt;br /&gt;
    func TestEnrichAndGetProfilesWhenInvalidCustomProfilesProvided(t *testing.T) {&lt;br /&gt;
&lt;br /&gt;
        //Set&lt;br /&gt;
        server := GetServerTestHelper(t)&lt;br /&gt;
        defer server.Close()&lt;br /&gt;
        ndbclient := ndbclient.NewNDBClient(&amp;quot;username&amp;quot;, &amp;quot;password&amp;quot;, server.URL, &amp;quot;&amp;quot;, true)&lt;br /&gt;
&lt;br /&gt;
        //Test&lt;br /&gt;
        dbTypes := []string{&amp;quot;postgres_invalid_profiles&amp;quot;, &amp;quot;mysql_invalid_profiles&amp;quot;, &amp;quot;mongodb_invalid_profiles&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
        for _, dbType := range dbTypes {&lt;br /&gt;
&lt;br /&gt;
            // get custom profile based upon the database type&lt;br /&gt;
            customProfile := GetCustomProfileForDBType(dbType)&lt;br /&gt;
&lt;br /&gt;
            profileMap, _ := v1alpha1.EnrichAndGetProfiles(context.Background(), ndbclient, dbType, customProfile)&lt;br /&gt;
&lt;br /&gt;
            //Assert&lt;br /&gt;
            profileTypes := []string{&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_COMPUTE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_STORAGE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_SOFTWARE,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_NETWORK,&lt;br /&gt;
                v1alpha1.PROFILE_TYPE_DATABASE_PARAMETER,&lt;br /&gt;
            }&lt;br /&gt;
            for _, profileType := range profileTypes {&lt;br /&gt;
                profile := profileMap[profileType]&lt;br /&gt;
                //Assert that profile EngineType matches the database engine or the generic type&lt;br /&gt;
                if profile.EngineType != v1alpha1.GetDatabaseEngineName(dbType) &amp;amp;&amp;amp; profile.EngineType != v1alpha1.DATABASE_ENGINE_TYPE_GENERIC {&lt;br /&gt;
                    t.Errorf(&amp;quot;Profile engine type %s for dbType %s does not match&amp;quot;, profile.EngineType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
                /* since custom profile is passed it should not default to OOB, and err should be raised stating the custom profile passed does not exist,&lt;br /&gt;
                and thus database provisioning does not occur&lt;br /&gt;
                */&lt;br /&gt;
                if profile != (v1alpha1.ProfileResponse{}) {&lt;br /&gt;
                    t.Errorf(&amp;quot;Incorrect Profile Match found for profile type = %s and dbType = %s&amp;quot;, profileType, dbType)&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
    case v1alpha1.DATABASE_TYPE_MONGODB_INVALID_PROFILE, v1alpha1.DATABASE_TYPE_MYSQL_INVALID_PROFILE, v1alpha1.DATABASE_TYPE_POSTGRES_INVALID_PROFILE:&lt;br /&gt;
            // below custom profiles do not exist and will be used for the negative scenario&lt;br /&gt;
            profiles = v1alpha1.Profiles{&lt;br /&gt;
                Software: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;140&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-140&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Compute: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;100&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-100&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                Network: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;170&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-170&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
                DbParam: v1alpha1.Profile{&lt;br /&gt;
                    Id:        &amp;quot;200&amp;quot;,&lt;br /&gt;
                    VersionId: &amp;quot;v-id-200&amp;quot;,&lt;br /&gt;
                },&lt;br /&gt;
            }&lt;br /&gt;
            return profiles&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=149175</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=149175"/>
		<updated>2023-04-07T23:17:37Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
'''Previous Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:GetOOBProfiles.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
'''Previous Working''' : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file18png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Providing the least costly topology based on each profile type.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on provided software/compute/network/dbParams profiles&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the provided software/compute/network/dbParams profiles as input through YAML file, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Throwing error if invalid software/compute/network/dbParams profiles are given as input&lt;br /&gt;
** Description: This test case verifies that error is thrown if invalid software/compute/network/dbParams profiles are provided as input through YAML file.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the database has not been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the error is thrown on the command prompt&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system does not provision the database&lt;br /&gt;
*** The error is thrown saying that the id/version id of software/compute/network/dbParams profiles is invalid&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 3 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default software/compute/network/dbParams profiles for database provisioning when software/compute/network/dbParams profiles are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default software/compute/network/dbParams profiles for configuration when software/compute/network/dbParams profiles are not present in the profiles section of &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are not available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the default profiles&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the default profile values&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
Testcases were written in &amp;quot;\ndb-operator\test\ndb_api_helpers_test.go&amp;quot;&lt;br /&gt;
&amp;lt;br&amp;gt;Dummy Objects required for these testcases were created in &amp;quot;\ndb-operator\test\testutility.go&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 1 and Test Scenario 3 ===&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_1_3.png|1000px]]&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_1_3_dummy_object.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 2 ===&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_2.png|1000px]]&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_2_dummy_object.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148855</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148855"/>
		<updated>2023-04-06T01:53:59Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
'''Previous Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:GetOOBProfiles.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
'''Previous Working''' : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file18png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Providing the least costly topology based on each profile type.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on provided software/compute/network/dbParams profiles&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the provided software/compute/network/dbParams profiles as input through YAML file, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Throwing error if invalid software/compute/network/dbParams profiles are given as input&lt;br /&gt;
** Description: This test case verifies that error is thrown if invalid software/compute/network/dbParams profiles are provided as input through YAML file.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the database has not been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the error is thrown on the command prompt&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system does not provision the database&lt;br /&gt;
*** The error is thrown saying that the id/version id of software/compute/network/dbParams profiles is invalid&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 3 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default software/compute/network/dbParams profiles for database provisioning when software/compute/network/dbParams profiles are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default software/compute/network/dbParams profiles for configuration when software/compute/network/dbParams profiles are not present in the profiles section of &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are not available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the default profiles&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the default profile values&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
Testcases were written in &amp;quot;\ndb-operator\test\ndb_api_helpers_test.go&amp;quot;&lt;br /&gt;
&amp;lt;br&amp;gt;Dummy Objects required for these testcases were created in &amp;quot;\ndb-operator\test\testutility.go&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 1 and Test Scenario 3 ===&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_1_3.png|1000px]]&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_1_3_dummy_object.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 2 ===&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_2.png|1000px]]&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_2_dummy_object.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_scenario_2_dummy_object.png&amp;diff=148854</id>
		<title>File:Test scenario 2 dummy object.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_scenario_2_dummy_object.png&amp;diff=148854"/>
		<updated>2023-04-06T01:50:10Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_scenario_2.png&amp;diff=148853</id>
		<title>File:Test scenario 2.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_scenario_2.png&amp;diff=148853"/>
		<updated>2023-04-06T01:49:33Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_scenario_1_3_dummy_object.png&amp;diff=148852</id>
		<title>File:Test scenario 1 3 dummy object.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_scenario_1_3_dummy_object.png&amp;diff=148852"/>
		<updated>2023-04-06T01:48:58Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_scenario_1_3.png&amp;diff=148851</id>
		<title>File:Test scenario 1 3.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Test_scenario_1_3.png&amp;diff=148851"/>
		<updated>2023-04-06T01:48:03Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148850</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148850"/>
		<updated>2023-04-06T01:46:38Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
'''Previous Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:GetOOBProfiles.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
'''Previous Working''' : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file18png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Providing the least costly topology based on each profile type.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on provided software/compute/network/dbParams profiles&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the provided software/compute/network/dbParams profiles as input through YAML file, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Throwing error if invalid software/compute/network/dbParams profiles are given as input&lt;br /&gt;
** Description: This test case verifies that error is thrown if invalid software/compute/network/dbParams profiles are provided as input through YAML file.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the database has not been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the error is thrown on the command prompt&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system does not provision the database&lt;br /&gt;
*** The error is thrown saying that the id/version id of software/compute/network/dbParams profiles is invalid&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 3 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default software/compute/network/dbParams profiles for database provisioning when software/compute/network/dbParams profiles are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default software/compute/network/dbParams profiles for configuration when software/compute/network/dbParams profiles are not present in the profiles section of &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are not available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the default profiles&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the default profile values&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
Testcases were written in &amp;quot;\ndb-operator\test\ndb_api_helpers_test.go&amp;quot;&lt;br /&gt;
Dummy Objects required for these testcases were created in &amp;quot;\ndb-operator\test\testutility.go&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 1 and Test Scenario 3 ===&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_1_3.png|1000px]]&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_1_3_dummy_object.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
=== Testcase to check Test Scenario 2 ===&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_2.png|1000px]]&lt;br /&gt;
==== Code for creating Dummy Objects required for this testcase ====&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:test_scenario_2_dummy_object.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148848</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148848"/>
		<updated>2023-04-06T01:31:28Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:file6.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
[[File:file7.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
'''Previous Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:GetOOBProfiles.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
'''Previous Working''' : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file18png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Providing the least costly topology based on each profile type.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on provided software/compute/network/dbParams profiles&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the provided software/compute/network/dbParams profiles as input through YAML file, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Throwing error if invalid software/compute/network/dbParams profiles are given as input&lt;br /&gt;
** Description: This test case verifies that error is thrown if invalid software/compute/network/dbParams profiles are provided as input through YAML file.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the database has not been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the error is thrown on the command prompt&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system does not provision the database&lt;br /&gt;
*** The error is thrown saying that the id/version id of software/compute/network/dbParams profiles is invalid&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 3 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default software/compute/network/dbParams profiles for database provisioning when software/compute/network/dbParams profiles are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default software/compute/network/dbParams profiles for configuration when software/compute/network/dbParams profiles are not present in the profiles section of &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** Pre-requisites are installed&lt;br /&gt;
*** Docker Desktop Application is running&lt;br /&gt;
*** Kubernetes cluster is up&lt;br /&gt;
*** Nutanix Test Drive is active and the cluster id and other credentials are present inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; and &amp;quot;\ndb-operator\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** The software/compute/network/dbParams profiles are not available for input in a profiles section inside &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Run command &amp;quot;make install run&amp;quot; in the root directory of the project&lt;br /&gt;
*** Create secrets with command &amp;quot;kubectl apply -f .\config\samples\secret.yaml&amp;quot;&lt;br /&gt;
*** Provision the database with command &amp;quot;kubectl apply -f .\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** Check if the appropriate database has been provisioned on the Nutanix test drive&lt;br /&gt;
*** Verify that the compute/software/network/dbParams profiles of the database match the default profiles&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the configurations specified in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;&lt;br /&gt;
*** The the compute/software/network/dbParams profiles match the default profile values&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:File18png.png&amp;diff=148742</id>
		<title>File:File18png.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:File18png.png&amp;diff=148742"/>
		<updated>2023-04-05T02:11:24Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148741</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148741"/>
		<updated>2023-04-05T02:10:19Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: /* EnrichProfileMapForProfileType */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:file6.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
[[File:file7.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
'''Previous Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:GetOOBProfiles.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
'''Previous Working''' : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file18png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Providing the least costly topology based on each profile type.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on compute parameters&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the compute parameters passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** The compute parameters are available for input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Pass the compute parameters as input to the system&lt;br /&gt;
*** Check if the appropriate database has been provisioned based on the compute parameters&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the compute parameters&lt;br /&gt;
*** The database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default setup for configuration when optional parameters are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default setup for configuration when optional parameters are not passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** No optional parameters are passed as input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Do not pass any optional parameters as input to the system&lt;br /&gt;
*** Check if the database is configured using the default setup for configuration&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the default setup&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system configures the database using the default setup when no optional parameters are passed&lt;br /&gt;
*** The database configuration and settings match the expected values based on the default setup&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148740</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148740"/>
		<updated>2023-04-05T02:04:16Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:file6.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
[[File:file7.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
'''Previous Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:GetOOBProfiles.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
'''Previous Working''' : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
'''Previous Working''' : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;'''Enhanced Working''' : Providing the least costly topology based on each profile type.&lt;br /&gt;
'''Previous Code''' : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;'''New Code''' :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;'''Explanation for the change''' : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on compute parameters&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the compute parameters passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** The compute parameters are available for input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Pass the compute parameters as input to the system&lt;br /&gt;
*** Check if the appropriate database has been provisioned based on the compute parameters&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the compute parameters&lt;br /&gt;
*** The database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default setup for configuration when optional parameters are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default setup for configuration when optional parameters are not passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** No optional parameters are passed as input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Do not pass any optional parameters as input to the system&lt;br /&gt;
*** Check if the database is configured using the default setup for configuration&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the default setup&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system configures the database using the default setup when no optional parameters are passed&lt;br /&gt;
*** The database configuration and settings match the expected values based on the default setup&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148739</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148739"/>
		<updated>2023-04-05T01:59:50Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:file6.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
[[File:file7.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ===&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
==== Previous Working ==== : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;==== Enhanced Working ==== : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;==== Previous Code ==== :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:GetOOBProfiles.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;==== New Code ==== :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;==== Explanation for the change ==== :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
==== Previous Working ==== : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;==== Enhanced Working ==== : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;==== Previous Code ==== :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;==== New Code ==== :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;==== Explanation for the change ==== : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
==== Previous Working ==== : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;==== Enhanced Working ==== : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;==== Previous Code ==== : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;==== New Code ==== :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;==== Explanation for the change ==== : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
==== Previous Working ==== : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;==== Enhanced Working ==== : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;==== Previous Code ==== : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;==== New Code ==== :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;==== Explanation for the change ==== : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
==== Previous Working ==== : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;==== Enhanced Working ==== : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;==== Previous Code ==== : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;==== New Code ==== :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;==== Explanation for the change ==== : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
==== Previous Working ==== : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;==== Enhanced Working ==== : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
==== Previous Code ==== : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;==== New Code ==== :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;==== Explanation for the change ==== : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
==== Previous Working ==== : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;==== Enhanced Working ==== : Providing the least costly topology based on each profile type.&lt;br /&gt;
==== Previous Code ==== : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;==== New Code ==== :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;==== Explanation for the change ==== : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on compute parameters&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the compute parameters passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** The compute parameters are available for input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Pass the compute parameters as input to the system&lt;br /&gt;
*** Check if the appropriate database has been provisioned based on the compute parameters&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the compute parameters&lt;br /&gt;
*** The database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default setup for configuration when optional parameters are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default setup for configuration when optional parameters are not passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** No optional parameters are passed as input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Do not pass any optional parameters as input to the system&lt;br /&gt;
*** Check if the database is configured using the default setup for configuration&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the default setup&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system configures the database using the default setup when no optional parameters are passed&lt;br /&gt;
*** The database configuration and settings match the expected values based on the default setup&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148738</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148738"/>
		<updated>2023-04-05T01:53:01Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:file6.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
[[File:file7.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ====&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
previous working : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;previous code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:GetOOBProfiles.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
previous working : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : Providing the least costly topology based on each profile type.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on compute parameters&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the compute parameters passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** The compute parameters are available for input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Pass the compute parameters as input to the system&lt;br /&gt;
*** Check if the appropriate database has been provisioned based on the compute parameters&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the compute parameters&lt;br /&gt;
*** The database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default setup for configuration when optional parameters are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default setup for configuration when optional parameters are not passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** No optional parameters are passed as input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Do not pass any optional parameters as input to the system&lt;br /&gt;
*** Check if the database is configured using the default setup for configuration&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the default setup&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system configures the database using the default setup when no optional parameters are passed&lt;br /&gt;
*** The database configuration and settings match the expected values based on the default setup&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:File11png.png&amp;diff=148737</id>
		<title>File:File11png.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:File11png.png&amp;diff=148737"/>
		<updated>2023-04-05T01:51:25Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148736</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148736"/>
		<updated>2023-04-05T01:50:21Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:file6.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
[[File:file7.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ====&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
previous working : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;previous code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:GetOOBProfiles.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
previous working : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : Providing the least costly topology based on each profile type.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on compute parameters&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the compute parameters passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** The compute parameters are available for input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Pass the compute parameters as input to the system&lt;br /&gt;
*** Check if the appropriate database has been provisioned based on the compute parameters&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the compute parameters&lt;br /&gt;
*** The database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default setup for configuration when optional parameters are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default setup for configuration when optional parameters are not passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** No optional parameters are passed as input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Do not pass any optional parameters as input to the system&lt;br /&gt;
*** Check if the database is configured using the default setup for configuration&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the default setup&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system configures the database using the default setup when no optional parameters are passed&lt;br /&gt;
*** The database configuration and settings match the expected values based on the default setup&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:File17png.png&amp;diff=148735</id>
		<title>File:File17png.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:File17png.png&amp;diff=148735"/>
		<updated>2023-04-05T01:45:30Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: Ajoshi24 uploaded a new version of File:File17png.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:File17png.png&amp;diff=148734</id>
		<title>File:File17png.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:File17png.png&amp;diff=148734"/>
		<updated>2023-04-05T01:45:00Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:File16png.png&amp;diff=148733</id>
		<title>File:File16png.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:File16png.png&amp;diff=148733"/>
		<updated>2023-04-05T01:44:23Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:File15png.png&amp;diff=148732</id>
		<title>File:File15png.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:File15png.png&amp;diff=148732"/>
		<updated>2023-04-05T01:43:50Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:File13png.png&amp;diff=148731</id>
		<title>File:File13png.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:File13png.png&amp;diff=148731"/>
		<updated>2023-04-05T01:42:58Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:File10.png&amp;diff=148730</id>
		<title>File:File10.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:File10.png&amp;diff=148730"/>
		<updated>2023-04-05T01:33:35Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148729</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148729"/>
		<updated>2023-04-05T01:33:07Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:file6.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
[[File:file7.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ====&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
previous working : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;previous code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:GetOOBProfiles.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
previous working : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : Providing the least costly topology based on each profile type.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on compute parameters&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the compute parameters passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** The compute parameters are available for input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Pass the compute parameters as input to the system&lt;br /&gt;
*** Check if the appropriate database has been provisioned based on the compute parameters&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the compute parameters&lt;br /&gt;
*** The database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default setup for configuration when optional parameters are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default setup for configuration when optional parameters are not passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** No optional parameters are passed as input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Do not pass any optional parameters as input to the system&lt;br /&gt;
*** Check if the database is configured using the default setup for configuration&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the default setup&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system configures the database using the default setup when no optional parameters are passed&lt;br /&gt;
*** The database configuration and settings match the expected values based on the default setup&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:File8.png&amp;diff=148728</id>
		<title>File:File8.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:File8.png&amp;diff=148728"/>
		<updated>2023-04-05T01:31:16Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: GetOOBProfiles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;GetOOBProfiles&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:File9.png&amp;diff=148727</id>
		<title>File:File9.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:File9.png&amp;diff=148727"/>
		<updated>2023-04-05T01:28:33Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:GetOOBProfiles.png&amp;diff=148726</id>
		<title>File:GetOOBProfiles.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:GetOOBProfiles.png&amp;diff=148726"/>
		<updated>2023-04-05T01:27:49Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148725</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148725"/>
		<updated>2023-04-05T01:25:18Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:file6.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
[[File:file7.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
=== \ndb-operator\api\v1alpha1\ndb_api_helpers.go ====&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
previous working : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
&amp;lt;br&amp;gt;previous code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file8.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file9.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
previous working : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file10.png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file11png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file13png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file15png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
&amp;lt;br&amp;gt;previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file16png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type &amp;lt;br&amp;gt;specified if the custom profile provided passes the checks.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
===== GetTopologyForProfileType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
&amp;lt;br&amp;gt;enhanced working : Providing the least costly topology based on each profile type.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
&amp;lt;br&amp;gt;new code :&lt;br /&gt;
&amp;lt;br&amp;gt;[[File:file17png|1000px]]&lt;br /&gt;
&amp;lt;br&amp;gt;Explanation of the change : Costlier topologies need more space on NDB Test Drive which results in &amp;quot;NoHostResources&amp;quot; error. So this function chooses the least costly topology to avoid this error.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on compute parameters&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the compute parameters passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** The compute parameters are available for input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Pass the compute parameters as input to the system&lt;br /&gt;
*** Check if the appropriate database has been provisioned based on the compute parameters&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the compute parameters&lt;br /&gt;
*** The database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default setup for configuration when optional parameters are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default setup for configuration when optional parameters are not passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** No optional parameters are passed as input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Do not pass any optional parameters as input to the system&lt;br /&gt;
*** Check if the database is configured using the default setup for configuration&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the default setup&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system configures the database using the default setup when no optional parameters are passed&lt;br /&gt;
*** The database configuration and settings match the expected values based on the default setup&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148724</id>
		<title>CSC/ECE 517 Spring 2023 - NTNX-3. Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2023_-_NTNX-3._Refactor_models_to_keep_profiles_(software,_compute,_network,_etc)_as_optional_and_use_default_if_not_specified&amp;diff=148724"/>
		<updated>2023-04-05T00:57:36Z</updated>

		<summary type="html">&lt;p&gt;Ajoshi24: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Background==&lt;br /&gt;
Kubernetes&lt;br /&gt;
An open-source container orchestration technology called Kubernetes is used to automatically deploy, scale, and manage containerized applications. Developers can use Kubernetes to distribute and control containerized applications across a dispersed network of servers or PCs. To ensure that the actual state of an application matches the desired state, it uses a declarative model to express the desired state and automatically manages the containerized components. Kubernetes can be operated on public or private cloud infrastructure as well as in-house data centers and offers a wide range of functionality for managing containerized applications, such as autonomous scaling, rolling updates, self-healing, service discovery, and load balancing.&lt;br /&gt;
&lt;br /&gt;
===Nutanix Database Service===&lt;br /&gt;
&lt;br /&gt;
A hybrid multi-cloud database-as-a-service for Microsoft SQL Server, Oracle Database, PostgreSQL, MongoDB, and MySQL, among other databases, is called Nutanix Database Service. It allows for the efficient management of hundreds to thousands of databases, the quick creation of new ones, and the automation of time-consuming administration activities like patching and backups. Users can also choose certain operating systems, database versions, and extensions to satisfy application and compliance requirements. Customers from all around the world have optimized their databases across numerous locations and sped up software development using Nutanix Database Service.&lt;br /&gt;
&lt;br /&gt;
===Features offered by NDB Service:===&lt;br /&gt;
[[File:f4.png|1000px]]&lt;br /&gt;
#Nutanix NDB is a distributed NoSQL database service that is part of the Nutanix platform. Some of the key features of NDB include highly scalable architecture, distributed data storage, support for multiple data models, consistent data, fast data access, automatic sharding, real-time analytics, high availability and fault tolerance, and strong security features.&lt;br /&gt;
#With its ability to scale up or down the number of nodes in a cluster, Nutanix NDB provides highly scalable architecture without any downtime. Its distributed architecture ensures high availability and fault tolerance, while its support for multiple data models makes it a versatile database service for a wide range of use cases. Additionally, NDB supports strong consistency and fast data access by caching frequently accessed data in memory, which helps reduce the number of disk reads and improves query performance.&lt;br /&gt;
#NDB also provides automatic sharding, which helps ensure that your database can handle large amounts of data. You can use graph queries to analyze relationships between data in real-time, which can help you make more informed decisions. Furthermore, NDB offers high availability and fault tolerance through its distributed architecture and replication features. Lastly, NDB provides strong security features, including role-based access control, data encryption at rest, and network security features.&lt;br /&gt;
&lt;br /&gt;
[[File:f1.jpg|1200px]]&lt;br /&gt;
&lt;br /&gt;
===NDB Kubernetes Operator===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes Operator is an innovative tool created by Nutanix to streamline the management and operation of the Nutanix NDB (NoSQL database) on Kubernetes clusters.&lt;br /&gt;
&lt;br /&gt;
With the NDB Kubernetes Operator, deploying and managing NDB clusters on Kubernetes has never been easier, as it eliminates the need to manually configure and manage the underlying infrastructure. Built on the Kubernetes operator framework, it offers a declarative way to manage the lifecycle of NDB clusters and other related resources.&lt;br /&gt;
&lt;br /&gt;
One of the key benefits of the operator is that it simplifies the management of NDB clusters by automating common tasks, such as cluster creation, scaling, upgrading, backup, and recovery. It also offers a high degree of flexibility and customization, allowing you to configure various aspects of the cluster, such as storage, networking, and security.&lt;br /&gt;
&lt;br /&gt;
Another advantage of the NDB Kubernetes Operator is its seamless integration with other Kubernetes tools and resources, such as Helm charts, Kubernetes secrets, and Kubernetes ConfigMaps. This integration makes it easy to integrate NDB into your existing Kubernetes-based infrastructure and workflows, providing a hassle-free solution for managing your database clusters.&lt;br /&gt;
&lt;br /&gt;
Overall, the NDB Kubernetes Operator is a powerful and flexible tool for managing NDB clusters on Kubernetes, freeing you up to focus on your application logic rather than infrastructure management. Its automation capabilities and integration with other Kubernetes tools make it a must-have tool for developers and administrators looking to simplify and streamline their database management on Kubernetes.&lt;br /&gt;
&lt;br /&gt;
==Existing Architecture and Problem Statement==&lt;br /&gt;
===Problem Statement: Refactor models to keep profiles (software, compute, network, etc) as optional and use default if not specified===&lt;br /&gt;
&lt;br /&gt;
The NDB Kubernetes operator currently uses default compute, network and OS software profiles while provisioning the database. Refactor this module to include optional fields and only if absent, fall back to default.&lt;br /&gt;
&lt;br /&gt;
===NDB Architecture===&lt;br /&gt;
&lt;br /&gt;
[[File:file2.png|1300px]]&lt;br /&gt;
&lt;br /&gt;
Microsoft SQL Server, Oracle Database, PostgreSQL, MySQL, and MongoDB are just a few of the databases that can have high availability, scalability, and speed thanks to the distributed architecture of the Nutanix Database Service. The hyper-converged infrastructure from Nutanix, which offers a scalable and adaptable platform for handling enterprise workloads, is the foundation around which the architecture is built.&lt;br /&gt;
&lt;br /&gt;
There are various layers in the architecture of the Nutanix Database Service. The Nutanix hyperconverged infrastructure is the basic layer that provides the storage, computing, and networking resources needed to run the databases. The Nutanix Acropolis operating system, which offers the essential virtualization and administration features, sits on top of this layer.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era layer, which is located above the Nutanix Acropolis layer, offers the Nutanix Database Service the ability to manage databases throughout their existence. The Nutanix Era Manager, a centralized management console that offers a single point of access for controlling the databases across several clouds and data centers, is included in this tier.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Orchestrator, which is in charge of automating the provisioning, scaling, patching, and backup of the databases, is another component of the Nutanix Era layer. The Orchestrator offers a declarative approach for specifying the desired state of the databases and is built to work with a variety of databases.&lt;br /&gt;
&lt;br /&gt;
The Nutanix Era Application, a web-based interface that enables database administrators and developers to quickly provision and administer the databases, is the final component of the top layer. A self-service interface for installing databases as well as a number of tools for tracking and troubleshooting database performance are offered by the Era Application.&lt;br /&gt;
&lt;br /&gt;
==Design &amp;amp; Workflow==&lt;br /&gt;
Large amounts of data may be handled by the highly scalable, fault-tolerant, and consistent Nutanix NDB NoSQL database. It is a distributed database created to be installed over several cluster nodes. A portion of the data is stored on each node in the cluster, and the data is replicated across several nodes to guarantee high availability.&lt;br /&gt;
&lt;br /&gt;
Configure your Nutanix cluster: We need to configure your Nutanix cluster to support NDB. This includes setting up the storage and network configurations, configuring the NDB nodes, and defining the replication factor.&lt;br /&gt;
&lt;br /&gt;
Create a table: We need to create a table in NDB to store your data. This includes defining the schema, specifying the replication factor, and configuring any other options you need.&lt;br /&gt;
&lt;br /&gt;
Write your code: We need to write your code to interact with the NDB cluster. This includes inserting and retrieving data, as well as performing more complex operations such as querying, indexing, and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Test your code: We need to test your code to ensure that it works as expected. This includes testing basic operations such as creating and retrieving data, as well as testing more complex operations such as queries and data aggregation.&lt;br /&gt;
&lt;br /&gt;
Monitor your cluster: We need to monitor your NDB cluster to ensure that it is performing as expected. This includes monitoring resource usage, handling errors and exceptions, and optimizing performance.&lt;br /&gt;
&lt;br /&gt;
Optimize your cluster: We need to optimize your NDB cluster over time to ensure that it continues to meet your needs. This includes tuning the configuration, optimizing queries, and scaling the cluster as needed.&lt;br /&gt;
&lt;br /&gt;
Backup and recovery: We need to establish backup and recovery procedures to ensure that your data is protected against data loss or corruption. This includes regularly backing up your data, testing your backups, and establishing procedures for recovering data in case of a disaster.&lt;br /&gt;
&lt;br /&gt;
[[File:file5.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
==Potential Design Patterns, Principles, and Code Refactoring strategies==&lt;br /&gt;
&lt;br /&gt;
The codebase could be converted into an Object Oriented fashion with classes. Further, here are some of the design patterns we could use:&lt;br /&gt;
&lt;br /&gt;
'''Builder:''' This pattern could be used to create the provisioning request for a database instance in a more modular and flexible way. Rather than creating the request directly in one function, a builder class could be used to set individual properties of the request. This would make the code more maintainable and extensible, and would allow for easier testing of different combinations of request properties.&lt;br /&gt;
&lt;br /&gt;
'''Factory:''' Since there are different types of database instances that can be provisioned (e.g. MySQL, Postgres, etc.), a factory pattern could be used to create the appropriate request object based on the specified database type. This would help to decouple the creation of the request object from the calling code, and would make it easier to add support for new database types in the future.&lt;br /&gt;
&lt;br /&gt;
'''Dependency Injection:''' To allow for better testability, dependency injection can be used to decouple the code from its dependencies. For example, in the provided code snippet, the NDBClient is being passed into the GenerateProvisioningRequest() function. However, if the NDBClient had additional dependencies or if it were difficult to create a testable version of the NDBClient, dependency injection could be used to allow for easier testing and swapping of dependencies.&lt;br /&gt;
&lt;br /&gt;
''' Code Refactoring:'''&lt;br /&gt;
After reviewing the code base, it was discovered that the ndb_api_helpers.go file contains the code for provisioning the database. The main task of generating the request payload for provisioning the database is handled by the GenerateProvisioningRequest function. To retrieve all the profiles, this function utilizes the GetOOBProfiles function which returns a map of all the profiles. However, the current implementation of GenerateProvisioningRequest only retrieves the first element of the values within the map, which is assumed to be the default value. This means that the function doesn't verify if the user has provided a specific profile or not before assigning a default value.&lt;br /&gt;
&lt;br /&gt;
To improve this behavior, we plan to iterate over all the profiles in the arrays that are inside the values of the profiles map. If the user has provided input for a specific profile, we will assign that input to the profiles variable. If not, we will use the first element of the array as the default value.&lt;br /&gt;
&lt;br /&gt;
This change will allow us to properly check whether the user has provided input for a specific profile or not, and avoid the incorrect assumption that the first element of the values array is always the default value. By iterating over all the profiles, we can ensure that the correct profile is selected and assigned to the profiles variable. This will lead to more accurate and reliable database provisioning.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:file6.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
[[File:file7.png|1000px]]&lt;br /&gt;
&lt;br /&gt;
== Modifications ==&lt;br /&gt;
&lt;br /&gt;
===\ndb-operator\api\v1alpha1\ndb_api_helpers.go ====&lt;br /&gt;
====Functions Changed====&lt;br /&gt;
===== GenerateProvisioningRequest =====&lt;br /&gt;
previous working : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and uses default compute, software, network, databaseParams profiles&lt;br /&gt;
enhanced working : This function generates and returns a request for provisioning a database (and a dbserver vm) on NDB and if user has provided custom profiles in &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot;, it will use those profiles to create the provisioning request or it will fall back to default profiles&lt;br /&gt;
previous code :&lt;br /&gt;
[[File:file8.png|1000px]]&lt;br /&gt;
new code :&lt;br /&gt;
[[File:file9.png|1000px]]&lt;br /&gt;
Explanation of the change :&lt;br /&gt;
changed the name of GetOOBProfiles to EnrichAndGetProfiles due to added functionality of overriding default profile values with custom profiles read from YAML file after performing applicability checks&lt;br /&gt;
&lt;br /&gt;
===== EnrichAndGetProfiles =====&lt;br /&gt;
previous working : previously this function was named GetOOBProfiles. This function used to fetch all the profiles from NDB API and return ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles.&lt;br /&gt;
enhanced working : now this function fetches all the profiles from NDB API and populates ProfilesMap with default profiles for each of the compute, software, network and dbParams profiles. Then it calls function EnrichProfilesMap function which will populate ProfilesMap with custom profiles if there are any in the YAML file.&lt;br /&gt;
previous code :&lt;br /&gt;
[[File:file10.png|1000px]]&lt;br /&gt;
new code :&lt;br /&gt;
[[File:file11png|1000px]]&lt;br /&gt;
Explanation of the change : since we only want to fall back to default profiles if there are no custom profiles mentioned in the YAML file, we are calling a new function EnrichProfilesMap which will populate ProfilesMap with the custom profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfilesMap =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
enhanced working : This function checks if there are any custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file. If there any custom profiles, this function will call function PerformProfileMatchingAndEnrichProfiles to fetch them for each category (Compute, Software, Network, dbParams) and populate ProfilesMap with it.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
new code :&lt;br /&gt;
[[File:file13png|1000px]]&lt;br /&gt;
Explanation of the change : Since we have added new section for custom profiles in the &amp;quot;\ndb-operator\config\samples\ndb_v1alpha1_database.yaml&amp;quot; file, we needed a function that will check if there is a section for custom profiles and delegate the task to fetch the custom profiles from the YAML file. This function fulfills that need.&lt;br /&gt;
&lt;br /&gt;
===== PerformProfileMatchingAndEnrichProfiles =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
enhanced working : Based on compute or (software, network &amp;amp; dbParam), generic or dbEngineSpecific profiles are used for matching the input customProfile. Furthermore, based on whether matched or not matched, delegation is performed to override the default profile values.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
new code :&lt;br /&gt;
[[File:file15png|1000px]]&lt;br /&gt;
Explanation of the change : We want to check if custom profiles mentioned in the YAML file are valid or not. If the profile type is compute, this function calls another function to validate the custom profile with generic profiles. If the profile type is network/software/dbParams, this function calls another function to validate the custom profile with dbEngineSpecific profiles.&lt;br /&gt;
&lt;br /&gt;
===== GetAppropriateProfileForType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
enhanced working : This functions gives either generic or dbEngine specific profiles based upon the profile type to be filtered upon.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
new code :&lt;br /&gt;
[[File:file16png|1000px]]&lt;br /&gt;
Explanation of the change : This function is used by PerformProfileMatchingAndEnrichProfiles function to make the decision of what kind of profiles are to be matched with what type of profiles.&lt;br /&gt;
&lt;br /&gt;
===== EnrichProfileMapForProfileType =====&lt;br /&gt;
previous working : This function was not there previously.&lt;br /&gt;
enhanced working : This function checks the correctness of the profile (response) passed as the parameter and overrides the profilesMap for the custom profile type specified if the custom profile provided passes the checks.&lt;br /&gt;
previous code : N/A&lt;br /&gt;
new code :&lt;br /&gt;
[[File:file17png|1000px]]&lt;br /&gt;
Explanation of the change : The custom profile mentioned in the YAML file is only valid if it exists in the list of all profiles provided by the NDB API. This function performs the task to check if the given custom profile exists in the all profiles list.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
&lt;br /&gt;
=== Test Case Scenario 1 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Provisioning of appropriate database based on compute parameters&lt;br /&gt;
** Description: This test case verifies that the appropriate database is provisioned based on the compute parameters passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** The compute parameters are available for input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Pass the compute parameters as input to the system&lt;br /&gt;
*** Check if the appropriate database has been provisioned based on the compute parameters&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system provisions the appropriate database based on the compute parameters&lt;br /&gt;
*** The database configuration and settings match the expected values based on the input parameters&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
=== Test Case Scenario 2 ===&lt;br /&gt;
&lt;br /&gt;
Test case name: Use of default setup for configuration when optional parameters are not passed&lt;br /&gt;
** Description: This test case verifies that the database configured uses the default setup for configuration when optional parameters are not passed as input, as expected.&lt;br /&gt;
** Pre-conditions:&lt;br /&gt;
*** The system is set up and running&lt;br /&gt;
*** No optional parameters are passed as input&lt;br /&gt;
** Test steps:&lt;br /&gt;
*** Do not pass any optional parameters as input to the system&lt;br /&gt;
*** Check if the database is configured using the default setup for configuration&lt;br /&gt;
*** Verify that the database configuration and settings match the expected values based on the default setup&lt;br /&gt;
** Expected results:&lt;br /&gt;
*** The system configures the database using the default setup when no optional parameters are passed&lt;br /&gt;
*** The database configuration and settings match the expected values based on the default setup&lt;br /&gt;
*** The test case passes successfully&lt;br /&gt;
&lt;br /&gt;
==Github==&lt;br /&gt;
* Repo: https://github.com/karan-47/ndb-operator/tree/feature/ntnx_3&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Mentors==&lt;br /&gt;
* Prof. Edward F. Gehringer&lt;br /&gt;
* Krunal Jhaveri&lt;br /&gt;
* Manav Rajvanshi&lt;br /&gt;
* Krishna Saurabh Vankadaru&lt;br /&gt;
* Kartiki Bhandakkar&lt;br /&gt;
&lt;br /&gt;
==Contributors==&lt;br /&gt;
* Karan Pradeep Gala (kgala2)&lt;br /&gt;
* Ashish Joshi (ajoshi24)&lt;br /&gt;
* Tilak Satra (trsatra)&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
[1] Nutanix. (n.d.). Nutanix Database Service. Retrieved from https://www.nutanix.com/products/database-service&lt;br /&gt;
&lt;br /&gt;
[2] Kubernetes Operator Pattern https://kubernetes.io/docs/concepts/extend-kubernetes/operator&lt;br /&gt;
&lt;br /&gt;
[3] NDB Operator Document - https://docs.google.com/document/d/1-VykKyIeky3n4JciIIrNgirk-Cn4pDT1behc9Yl8Nxk/&lt;br /&gt;
&lt;br /&gt;
[4] Go Operator SDK - https://sdk.operatorframework.io/docs/buildingoperators/golang/tutorial/&lt;/div&gt;</summary>
		<author><name>Ajoshi24</name></author>
	</entry>
</feed>