Skip to content

Commit

Permalink
feat: support for project limits in project resource and project/proj…
Browse files Browse the repository at this point in the history
…ects data sources (#1347)

* INTMDB-554: support for project limits adjusting project resource and project/projects data sources

* fix linting errors

* favor sdk method for generating random names in project tests

* include examples for project defining limits

* refactor: extract getStateTeams/getStateAPIKeys/getStateLimits to common method improving naming and adding documentation to method

* refactor: extract method for setting an api limit

* refactor print statements to generate warning diagnostics in project data source

* fix formatting in terraform example file

* inlcude limits in projects data source example usage section
  • Loading branch information
AgustinBettati authored Jul 31, 2023
1 parent 50961d7 commit 9c13dc0
Show file tree
Hide file tree
Showing 15 changed files with 604 additions and 92 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ website/node_modules
*.tfvars
service-account.json
log.*
test.env
__debug_*

website/vendor

Expand Down
7 changes: 7 additions & 0 deletions examples/atlas-project-limits/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# MongoDB Atlas Provider -- Atlas Project with custom limits
This example creates a Project and defines custom values for certain limits.

Variables Required to be set:
- `public_key`: Atlas public key
- `private_key`: Atlas private key
- `org_id`: Organization ID where project will be created
14 changes: 14 additions & 0 deletions examples/atlas-project-limits/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
resource "mongodbatlas_project" "test" {
name = "project-name"
org_id = var.org_id

limits {
name = "atlas.project.deployment.clusters"
value = 2
}

limits {
name = "atlas.project.deployment.nodesPerPrivateLinkRegion"
value = 3
}
}
4 changes: 4 additions & 0 deletions examples/atlas-project-limits/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
provider "mongodbatlas" {
public_key = var.public_key
private_key = var.private_key
}
12 changes: 12 additions & 0 deletions examples/atlas-project-limits/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
variable "public_key" {
description = "Public API key to authenticate to Atlas"
type = string
}
variable "private_key" {
description = "Private API key to authenticate to Atlas"
type = string
}
variable "org_id" {
description = "Atlas Organization ID"
type = string
}
9 changes: 9 additions & 0 deletions examples/atlas-project-limits/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
terraform {
required_providers {
mongodbatlas = {
source = "mongodb/mongodbatlas"
version = "~> 1.11"
}
}
required_version = ">= 0.13"
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func main() {
flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve")
flag.Parse()

opts := &plugin.ServeOpts{ProviderFunc: mongodbatlas.Provider}
opts := &plugin.ServeOpts{Debug: debugMode, ProviderFunc: mongodbatlas.Provider}

if debugMode {
err := plugin.Debug(context.Background(), "registry.terraform.io/mongodb/mongodbatlas", opts)
Expand Down
40 changes: 40 additions & 0 deletions mongodbatlas/data_source_mongodbatlas_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,34 @@ func dataSourceMongoDBAtlasProject() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"limits": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
},
"value": {
Type: schema.TypeInt,
Computed: true,
},
"current_usage": {
Type: schema.TypeInt,
Computed: true,
},
"default_limit": {
Type: schema.TypeInt,
Computed: true,
},
"maximum_limit": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
},
}
}
Expand Down Expand Up @@ -139,6 +167,7 @@ func getProjectAPIKeys(ctx context.Context, conn *matlas.Client, orgID, projectI
func dataSourceMongoDBAtlasProjectRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
// Get client connection.
conn := meta.(*MongoDBClient).Atlas
connV2 := meta.(*MongoDBClient).AtlasV2

projectID, projectIDOk := d.GetOk("project_id")
name, nameOk := d.GetOk("name")
Expand Down Expand Up @@ -175,6 +204,12 @@ func dataSourceMongoDBAtlasProjectRead(ctx context.Context, d *schema.ResourceDa
}
log.Println("[WARN] `api_keys` will be empty because the user has no permissions to read the api keys endpoint")
}

limits, _, err := connV2.ProjectsApi.ListProjectLimits(ctx, project.ID).Execute()
if err != nil {
return diag.Errorf("error getting project's limits (%s): %s", projectID, err)
}

projectSettings, _, err := conn.Projects.GetProjectSettings(ctx, project.ID)
if err != nil {
return diag.Errorf("error getting project's settings assigned (%s): %s", projectID, err)
Expand Down Expand Up @@ -203,6 +238,11 @@ func dataSourceMongoDBAtlasProjectRead(ctx context.Context, d *schema.ResourceDa
if err := d.Set("api_keys", flattenAPIKeys(apiKeys)); err != nil {
return diag.Errorf(errorProjectSetting, `api_keys`, project.ID, err)
}

if err := d.Set("limits", flattenLimits(limits)); err != nil {
return diag.Errorf(errorProjectSetting, `limits`, projectID, err)
}

if err := d.Set("is_collect_database_specifics_statistics_enabled", projectSettings.IsCollectDatabaseSpecificsStatisticsEnabled); err != nil {
return diag.Errorf(errorProjectSetting, `is_collect_database_specifics_statistics_enabled`, project.ID, err)
}
Expand Down
40 changes: 25 additions & 15 deletions mongodbatlas/data_source_mongodbatlas_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestAccProjectDSProject_byID(t *testing.T) {
ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccMongoDBAtlasProjectConfigWithDSByID(projectName, orgID,
Config: testAccMongoDBAtlasProjectDSUsingRS(testAccMongoDBAtlasProjectConfig(projectName, orgID,
[]*matlas.ProjectTeam{
{
TeamID: teamsIds[0],
Expand All @@ -49,7 +49,7 @@ func TestAccProjectDSProject_byID(t *testing.T) {
roles: []string{"GROUP_OWNER"},
},
},
),
)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "name"),
resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "org_id"),
Expand All @@ -76,7 +76,7 @@ func TestAccProjectDSProject_byName(t *testing.T) {
ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccMongoDBAtlasProjectConfigWithDSByName(projectName, orgID,
Config: testAccMongoDBAtlasProjectDSUsingRS(testAccMongoDBAtlasProjectConfig(projectName, orgID,
[]*matlas.ProjectTeam{
{
TeamID: teamsIds[0],
Expand All @@ -98,7 +98,7 @@ func TestAccProjectDSProject_byName(t *testing.T) {
roles: []string{"GROUP_OWNER"},
},
},
),
)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "name"),
resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "org_id"),
Expand All @@ -125,7 +125,7 @@ func TestAccProjectDSProject_defaultFlags(t *testing.T) {
ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccMongoDBAtlasProjectConfigWithDSByName(projectName, orgID,
Config: testAccMongoDBAtlasProjectDSUsingRS(testAccMongoDBAtlasProjectConfig(projectName, orgID,
[]*matlas.ProjectTeam{
{
TeamID: teamsIds[0],
Expand All @@ -147,7 +147,7 @@ func TestAccProjectDSProject_defaultFlags(t *testing.T) {
roles: []string{"GROUP_OWNER"},
},
},
),
)),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "name"),
resource.TestCheckResourceAttrSet("mongodbatlas_project.test", "org_id"),
Expand All @@ -163,22 +163,32 @@ func TestAccProjectDSProject_defaultFlags(t *testing.T) {
})
}

func testAccMongoDBAtlasProjectConfigWithDSByID(projectName, orgID string, teams []*matlas.ProjectTeam, apiKeys []*apiKey) string {
return fmt.Sprintf(`
%s
func TestAccProjectDSProject_limits(t *testing.T) {
projectName := acctest.RandomWithPrefix("test-acc")
orgID := os.Getenv("MONGODB_ATLAS_ORG_ID")

data "mongodbatlas_project" "test" {
project_id = "${mongodbatlas_project.test.id}"
}
`, testAccMongoDBAtlasProjectConfig(projectName, orgID, teams, apiKeys))
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheckBasic(t) },
ProviderFactories: testAccProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccMongoDBAtlasProjectDSUsingRS(testAccMongoDBAtlasProjectConfigWithLimits(projectName, orgID, []*projectLimit{})),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("data.mongodbatlas_project.test", "name"),
resource.TestCheckResourceAttrSet("data.mongodbatlas_project.test", "org_id"),
resource.TestCheckResourceAttrSet("data.mongodbatlas_project.test", "limits.0.name"),
),
},
},
})
}

func testAccMongoDBAtlasProjectConfigWithDSByName(projectName, orgID string, teams []*matlas.ProjectTeam, apiKeys []*apiKey) string {
func testAccMongoDBAtlasProjectDSUsingRS(rs string) string {
return fmt.Sprintf(`
%s
data "mongodbatlas_project" "test" {
name = "${mongodbatlas_project.test.name}"
}
`, testAccMongoDBAtlasProjectConfig(projectName, orgID, teams, apiKeys))
`, rs)
}
92 changes: 81 additions & 11 deletions mongodbatlas/data_source_mongodbatlas_projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,34 @@ func dataSourceMongoDBAtlasProjects() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"limits": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
},
"value": {
Type: schema.TypeInt,
Computed: true,
},
"current_usage": {
Type: schema.TypeInt,
Computed: true,
},
"default_limit": {
Type: schema.TypeInt,
Computed: true,
},
"maximum_limit": {
Type: schema.TypeInt,
Computed: true,
},
},
},
},
},
},
},
Expand All @@ -127,31 +155,47 @@ func dataSourceMongoDBAtlasProjects() *schema.Resource {

func dataSourceMongoDBAtlasProjectsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
// Get client connection.
conn := meta.(*MongoDBClient).Atlas
client := meta.(*MongoDBClient)
conn := client.Atlas
var diags diag.Diagnostics

options := &matlas.ListOptions{
PageNum: d.Get("page_num").(int),
ItemsPerPage: d.Get("items_per_page").(int),
}

projects, _, err := conn.Projects.GetAllProjects(ctx, options)
if err != nil {
return diag.FromErr(fmt.Errorf("error getting projects information: %s", err))
diagError := diag.Errorf("error getting projects information: %s", err)
diags = append(diags, diagError...)
}

results, projectDiag := flattenProjects(ctx, client, projects.Results)
if projectDiag != nil {
diags = append(diags, projectDiag...)
}

if err := d.Set("results", flattenProjects(ctx, conn, projects.Results)); err != nil {
return diag.FromErr(fmt.Errorf("error setting `results`: %s", err))
if err := d.Set("results", results); err != nil {
diagError := diag.FromErr(fmt.Errorf("error setting `results`: %s", err))
diags = append(diags, diagError...)
}

if err := d.Set("total_count", projects.TotalCount); err != nil {
return diag.FromErr(fmt.Errorf("error setting `name`: %s", err))
diagError := diag.FromErr(fmt.Errorf("error setting `name`: %s", err))
diags = append(diags, diagError...)
}

d.SetId(id.UniqueId())

return nil
return diags
}

func flattenProjects(ctx context.Context, conn *matlas.Client, projects []*matlas.Project) []map[string]interface{} {
func flattenProjects(ctx context.Context, client *MongoDBClient, projects []*matlas.Project) ([]map[string]interface{}, diag.Diagnostics) {
conn := client.Atlas
connV2 := client.AtlasV2

var diags diag.Diagnostics

var results []map[string]interface{}

if len(projects) > 0 {
Expand All @@ -160,17 +204,42 @@ func flattenProjects(ctx context.Context, conn *matlas.Client, projects []*matla
for k, project := range projects {
teams, _, err := conn.Projects.GetProjectTeamsAssigned(ctx, project.ID)
if err != nil {
fmt.Printf("[WARN] error getting project's teams assigned (%s): %s", project.ID, err)
diagWarning := diag.Diagnostic{
Severity: diag.Warning,
Summary: "Error getting project's teams assigned",
Detail: fmt.Sprintf("Error getting project's teams assigned (%s): %s", project.ID, err),
}
diags = append(diags, diagWarning)
}

apiKeys, err := getProjectAPIKeys(ctx, conn, project.OrgID, project.ID)
if err != nil {
fmt.Printf("[WARN] error getting project's api keys (%s): %s", project.ID, err)
diagWarning := diag.Diagnostic{
Severity: diag.Warning,
Summary: "Error getting project's api keys",
Detail: fmt.Sprintf("Error getting project's api keys (%s): %s", project.ID, err),
}
diags = append(diags, diagWarning)
}

limits, _, err := connV2.ProjectsApi.ListProjectLimits(ctx, project.ID).Execute()
if err != nil {
diagWarning := diag.Diagnostic{
Severity: diag.Warning,
Summary: "Error getting project's limits",
Detail: fmt.Sprintf("Error getting project's limits (%s): %s", project.ID, err),
}
diags = append(diags, diagWarning)
}

projectSettings, _, err := conn.Projects.GetProjectSettings(ctx, project.ID)
if err != nil {
fmt.Printf("[WARN] error getting project's settings assigned (%s): %s", project.ID, err)
diagWarning := diag.Diagnostic{
Severity: diag.Warning,
Summary: "Error getting project's settings assigned",
Detail: fmt.Sprintf("Error getting project's settings assigned (%s): %s", project.ID, err),
}
diags = append(diags, diagWarning)
}

results[k] = map[string]interface{}{
Expand All @@ -188,9 +257,10 @@ func flattenProjects(ctx context.Context, conn *matlas.Client, projects []*matla
"is_realtime_performance_panel_enabled": projectSettings.IsRealtimePerformancePanelEnabled,
"is_schema_advisor_enabled": projectSettings.IsSchemaAdvisorEnabled,
"region_usage_restrictions": project.RegionUsageRestrictions,
"limits": flattenLimits(limits),
}
}
}

return results
return results, diags
}
Loading

0 comments on commit 9c13dc0

Please sign in to comment.