Copilot commented on code in PR #197: URL: https://github.com/apache/cloudstack-terraform-provider/pull/197#discussion_r2256282892
########## cloudstack/resource_cloudstack_limits.go: ########## @@ -0,0 +1,317 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "log" + "strconv" + "strings" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +// resourceTypeMap maps string resource types to their integer values +var resourceTypeMap = map[string]int{ + "instance": 0, + "ip": 1, + "volume": 2, + "snapshot": 3, + "template": 4, + "project": 5, + "network": 6, + "vpc": 7, + "cpu": 8, + "memory": 9, + "primarystorage": 10, + "secondarystorage": 11, Review Comment: The resourceTypeMap is missing some resource types that are documented in the data source (publicip, eip, autoscalevmgroup). This inconsistency could cause confusion and limit functionality. ```suggestion "instance": 0, "ip": 1, "volume": 2, "snapshot": 3, "template": 4, "project": 5, "network": 6, "vpc": 7, "cpu": 8, "memory": 9, "primarystorage": 10, "secondarystorage": 11, "publicip": 12, "eip": 13, "autoscalevmgroup": 14, ``` ########## cloudstack/resource_cloudstack_limits.go: ########## @@ -0,0 +1,317 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "log" + "strconv" + "strings" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +// resourceTypeMap maps string resource types to their integer values +var resourceTypeMap = map[string]int{ + "instance": 0, + "ip": 1, + "volume": 2, + "snapshot": 3, + "template": 4, + "project": 5, + "network": 6, + "vpc": 7, + "cpu": 8, + "memory": 9, + "primarystorage": 10, + "secondarystorage": 11, +} + +func resourceCloudStackLimits() *schema.Resource { + return &schema.Resource{ + Read: resourceCloudStackLimitsRead, + Update: resourceCloudStackLimitsUpdate, + Create: resourceCloudStackLimitsCreate, + Delete: resourceCloudStackLimitsDelete, + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "instance", "ip", "volume", "snapshot", "template", "project", "network", "vpc", + "cpu", "memory", "primarystorage", "secondarystorage", + }, false), // false disables case-insensitive matching + Description: "The type of resource to update the limits. Available types are: " + + "instance, ip, volume, snapshot, template, project, network, vpc, cpu, memory, " + + "primarystorage, secondarystorage", + }, + "account": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "Update resource for a specified account. Must be used with the domainid parameter.", + }, + "domainid": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "Update resource limits for all accounts in specified domain. If used with the account parameter, updates resource limits for a specified account in specified domain.", + }, + "max": { + Type: schema.TypeInt, + Optional: true, + Description: "Maximum resource limit. Use -1 for unlimited resource limit.", + }, + "projectid": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "Update resource limits for project.", + }, + }, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +// getResourceType gets the resource type from the type field +func getResourceType(d *schema.ResourceData) (int, error) { + // Check if type is set + if v, ok := d.GetOk("type"); ok { + typeStr := v.(string) + if resourcetype, ok := resourceTypeMap[typeStr]; ok { + return resourcetype, nil + } + return 0, fmt.Errorf("invalid type value: %s", typeStr) + } + + return 0, fmt.Errorf("type must be specified") +} + +func resourceCloudStackLimitsCreate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + resourcetype, err := getResourceType(d) + if err != nil { + return err + } + + account := d.Get("account").(string) + domainid := d.Get("domainid").(string) + max := d.Get("max").(int) + projectid := d.Get("projectid").(string) + + // Validate account and domain parameters + if account != "" && domainid == "" { + return fmt.Errorf("domainid is required when account is specified") + } + + // Create a new parameter struct + p := cs.Limit.NewUpdateResourceLimitParams(resourcetype) + if account != "" { + p.SetAccount(account) + } + if domainid != "" { + p.SetDomainid(domainid) + } + if max != 0 { + p.SetMax(int64(max)) Review Comment: This condition prevents setting a limit to 0, which might be a valid limit value. Consider using d.HasChange("max") or checking if the value was explicitly set instead. ```suggestion if maxVal, ok := d.GetOk("max"); ok { p.SetMax(int64(maxVal.(int))) ``` ########## cloudstack/resource_cloudstack_limits.go: ########## @@ -0,0 +1,317 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "log" + "strconv" + "strings" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +// resourceTypeMap maps string resource types to their integer values +var resourceTypeMap = map[string]int{ + "instance": 0, + "ip": 1, + "volume": 2, + "snapshot": 3, + "template": 4, + "project": 5, + "network": 6, + "vpc": 7, + "cpu": 8, + "memory": 9, + "primarystorage": 10, + "secondarystorage": 11, +} + +func resourceCloudStackLimits() *schema.Resource { + return &schema.Resource{ + Read: resourceCloudStackLimitsRead, + Update: resourceCloudStackLimitsUpdate, + Create: resourceCloudStackLimitsCreate, + Delete: resourceCloudStackLimitsDelete, + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "instance", "ip", "volume", "snapshot", "template", "project", "network", "vpc", + "cpu", "memory", "primarystorage", "secondarystorage", + }, false), // false disables case-insensitive matching + Description: "The type of resource to update the limits. Available types are: " + + "instance, ip, volume, snapshot, template, project, network, vpc, cpu, memory, " + + "primarystorage, secondarystorage", + }, + "account": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "Update resource for a specified account. Must be used with the domainid parameter.", + }, + "domainid": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "Update resource limits for all accounts in specified domain. If used with the account parameter, updates resource limits for a specified account in specified domain.", + }, + "max": { + Type: schema.TypeInt, + Optional: true, + Description: "Maximum resource limit. Use -1 for unlimited resource limit.", + }, + "projectid": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "Update resource limits for project.", + }, + }, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +// getResourceType gets the resource type from the type field +func getResourceType(d *schema.ResourceData) (int, error) { + // Check if type is set + if v, ok := d.GetOk("type"); ok { + typeStr := v.(string) + if resourcetype, ok := resourceTypeMap[typeStr]; ok { + return resourcetype, nil + } + return 0, fmt.Errorf("invalid type value: %s", typeStr) + } + + return 0, fmt.Errorf("type must be specified") +} + +func resourceCloudStackLimitsCreate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + resourcetype, err := getResourceType(d) + if err != nil { + return err + } + + account := d.Get("account").(string) + domainid := d.Get("domainid").(string) + max := d.Get("max").(int) + projectid := d.Get("projectid").(string) + + // Validate account and domain parameters + if account != "" && domainid == "" { + return fmt.Errorf("domainid is required when account is specified") + } + + // Create a new parameter struct + p := cs.Limit.NewUpdateResourceLimitParams(resourcetype) + if account != "" { + p.SetAccount(account) + } + if domainid != "" { + p.SetDomainid(domainid) + } + if max != 0 { + p.SetMax(int64(max)) + } + if projectid != "" { + p.SetProjectid(projectid) + } + + log.Printf("[DEBUG] Updating Resource Limit for type %d", resourcetype) + _, err = cs.Limit.UpdateResourceLimit(p) + + if err != nil { + return fmt.Errorf("Error creating resource limit: %s", err) + } + + // Generate a unique ID based on the parameters + id := generateResourceID(resourcetype, account, domainid, projectid) + d.SetId(id) + + return resourceCloudStackLimitsRead(d, meta) +} + +// generateResourceID creates a unique ID for the resource based on its parameters +func generateResourceID(resourcetype int, account, domainid, projectid string) string { + if projectid != "" { + return fmt.Sprintf("%d-project-%s", resourcetype, projectid) + } + + if account != "" && domainid != "" { + return fmt.Sprintf("%d-account-%s-%s", resourcetype, account, domainid) + } + + if domainid != "" { + return fmt.Sprintf("%d-domain-%s", resourcetype, domainid) + } + + return fmt.Sprintf("%d", resourcetype) +} + +func resourceCloudStackLimitsRead(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + // Get the resourcetype from the type field + resourcetype, err := getResourceType(d) + if err != nil { + // If there's an error getting the type, try to extract it from the ID + idParts := strings.Split(d.Id(), "-") + if len(idParts) > 0 { + if rt, err := strconv.Atoi(idParts[0]); err == nil { + resourcetype = rt + // Find the string representation for this numeric type + for typeStr, typeVal := range resourceTypeMap { + if typeVal == rt { + d.Set("type", typeStr) + break + } + } + } + } + } + + account := d.Get("account").(string) + domainid := d.Get("domainid").(string) + projectid := d.Get("projectid").(string) + + // Create a new parameter struct + p := cs.Limit.NewListResourceLimitsParams() + p.SetResourcetype(resourcetype) + if account != "" { + p.SetAccount(account) + } + if domainid != "" { + p.SetDomainid(domainid) + } + if projectid != "" { + p.SetProjectid(projectid) + } + + // Retrieve the resource limits + l, err := cs.Limit.ListResourceLimits(p) + if err != nil { + return fmt.Errorf("error retrieving resource limits: %s", err) + } + + if l.Count == 0 { + log.Printf("[DEBUG] Resource limit not found") + d.SetId("") + return nil + } + + // Update the config + for _, limit := range l.ResourceLimits { + if limit.Resourcetype == fmt.Sprintf("%d", resourcetype) { + d.Set("max", limit.Max) + + // Only set the type field if it was originally specified in the configuration + if v, ok := d.GetOk("type"); ok { + // Preserve the original case of the type parameter + d.Set("type", v.(string)) + } + + return nil + } + } + + return fmt.Errorf("resource limit not found") +} + +func resourceCloudStackLimitsUpdate(d *schema.ResourceData, meta interface{}) error { + cs := meta.(*cloudstack.CloudStackClient) + + resourcetype, err := getResourceType(d) + if err != nil { + return err + } + + account := d.Get("account").(string) + domainid := d.Get("domainid").(string) + max := d.Get("max").(int) + projectid := d.Get("projectid").(string) + + // Create a new parameter struct + p := cs.Limit.NewUpdateResourceLimitParams(resourcetype) + if account != "" { + p.SetAccount(account) + } + if domainid != "" { + p.SetDomainid(domainid) + } + if max != 0 { + p.SetMax(int64(max)) Review Comment: This condition prevents setting a limit to 0, which might be a valid limit value. Consider using d.HasChange("max") or checking if the value was explicitly set instead. ```suggestion if v, ok := d.GetOk("max"); ok { p.SetMax(int64(v.(int))) ``` ########## cloudstack/resource_cloudstack_limits_test.go: ########## @@ -0,0 +1,457 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +func TestAccCloudStackLimits_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"), + resource.TestCheckResourceAttr( + "cloudstack_limits.foo", "type", "instance"), + resource.TestCheckResourceAttr( + "cloudstack_limits.foo", "max", "10"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_update(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"), + resource.TestCheckResourceAttr( + "cloudstack_limits.foo", "max", "10"), + ), + }, + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_update, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"), + resource.TestCheckResourceAttr( + "cloudstack_limits.foo", "max", "20"), + ), + }, + }, + }) +} + +func testAccCheckCloudStackLimitsExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Limits ID is set") + } + + return nil + } +} + +func testAccCheckCloudStackLimitsDestroy(s *terraform.State) error { + return nil +} + +const testAccCloudStackLimits_basic = ` +resource "cloudstack_limits" "foo" { + type = "instance" + max = 10 + domainid = cloudstack_domain.test_domain.id +} +` + +const testAccCloudStackLimits_update = ` +resource "cloudstack_limits" "foo" { + type = "instance" + max = 20 + domainid = cloudstack_domain.test_domain.id +} +` + +func TestAccCloudStackLimits_domain(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_domain_limit, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.domain_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.domain_limit", "type", "volume"), + resource.TestCheckResourceAttr( + "cloudstack_limits.domain_limit", "max", "50"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_account(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_account, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.account_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.account_limit", "type", "snapshot"), + resource.TestCheckResourceAttr( + "cloudstack_limits.account_limit", "max", "100"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_project(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_project, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.project_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.project_limit", "type", "primarystorage"), + resource.TestCheckResourceAttr( + "cloudstack_limits.project_limit", "max", "1000"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_unlimited(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_unlimited, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.unlimited"), + resource.TestCheckResourceAttr( + "cloudstack_limits.unlimited", "type", "cpu"), + resource.TestCheckResourceAttr( + "cloudstack_limits.unlimited", "max", "-1"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_stringType(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_stringType, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.string_type"), + resource.TestCheckResourceAttr( + "cloudstack_limits.string_type", "type", "network"), + resource.TestCheckResourceAttr( + "cloudstack_limits.string_type", "max", "30"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_ip(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_ip, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.ip_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.ip_limit", "type", "ip"), + resource.TestCheckResourceAttr( + "cloudstack_limits.ip_limit", "max", "25"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_template(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_template, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.template_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.template_limit", "type", "template"), + resource.TestCheckResourceAttr( + "cloudstack_limits.template_limit", "max", "40"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_projectType(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_projectType, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.project_type_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.project_type_limit", "type", "project"), + resource.TestCheckResourceAttr( + "cloudstack_limits.project_type_limit", "max", "15"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_vpc(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_vpc, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.vpc_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.vpc_limit", "type", "vpc"), + resource.TestCheckResourceAttr( + "cloudstack_limits.vpc_limit", "max", "10"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_memory(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_memory, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.memory_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.memory_limit", "type", "memory"), + resource.TestCheckResourceAttr( + "cloudstack_limits.memory_limit", "max", "8192"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_secondarystorage(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_secondarystorage, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.secondarystorage_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.secondarystorage_limit", "type", "secondarystorage"), + resource.TestCheckResourceAttr( + "cloudstack_limits.secondarystorage_limit", "max", "2000"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_import(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"), + ), + }, + { + ResourceName: "cloudstack_limits.foo", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"domainid", "type", "type", "max"}, Review Comment: The "type" field is duplicated in the ImportStateVerifyIgnore slice. This should only appear once. ```suggestion ImportStateVerifyIgnore: []string{"domainid", "type", "max"}, ``` ########## cloudstack/resource_cloudstack_limits_test.go: ########## @@ -0,0 +1,457 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" +) + +func TestAccCloudStackLimits_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"), + resource.TestCheckResourceAttr( + "cloudstack_limits.foo", "type", "instance"), + resource.TestCheckResourceAttr( + "cloudstack_limits.foo", "max", "10"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_update(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"), + resource.TestCheckResourceAttr( + "cloudstack_limits.foo", "max", "10"), + ), + }, + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_update, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"), + resource.TestCheckResourceAttr( + "cloudstack_limits.foo", "max", "20"), + ), + }, + }, + }) +} + +func testAccCheckCloudStackLimitsExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Limits ID is set") + } + + return nil + } +} + +func testAccCheckCloudStackLimitsDestroy(s *terraform.State) error { + return nil +} + +const testAccCloudStackLimits_basic = ` +resource "cloudstack_limits" "foo" { + type = "instance" + max = 10 + domainid = cloudstack_domain.test_domain.id +} +` + +const testAccCloudStackLimits_update = ` +resource "cloudstack_limits" "foo" { + type = "instance" + max = 20 + domainid = cloudstack_domain.test_domain.id +} +` + +func TestAccCloudStackLimits_domain(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_domain_limit, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.domain_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.domain_limit", "type", "volume"), + resource.TestCheckResourceAttr( + "cloudstack_limits.domain_limit", "max", "50"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_account(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_account, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.account_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.account_limit", "type", "snapshot"), + resource.TestCheckResourceAttr( + "cloudstack_limits.account_limit", "max", "100"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_project(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_project, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.project_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.project_limit", "type", "primarystorage"), + resource.TestCheckResourceAttr( + "cloudstack_limits.project_limit", "max", "1000"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_unlimited(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_unlimited, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.unlimited"), + resource.TestCheckResourceAttr( + "cloudstack_limits.unlimited", "type", "cpu"), + resource.TestCheckResourceAttr( + "cloudstack_limits.unlimited", "max", "-1"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_stringType(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_stringType, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.string_type"), + resource.TestCheckResourceAttr( + "cloudstack_limits.string_type", "type", "network"), + resource.TestCheckResourceAttr( + "cloudstack_limits.string_type", "max", "30"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_ip(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_ip, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.ip_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.ip_limit", "type", "ip"), + resource.TestCheckResourceAttr( + "cloudstack_limits.ip_limit", "max", "25"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_template(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_template, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.template_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.template_limit", "type", "template"), + resource.TestCheckResourceAttr( + "cloudstack_limits.template_limit", "max", "40"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_projectType(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_projectType, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.project_type_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.project_type_limit", "type", "project"), + resource.TestCheckResourceAttr( + "cloudstack_limits.project_type_limit", "max", "15"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_vpc(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_vpc, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.vpc_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.vpc_limit", "type", "vpc"), + resource.TestCheckResourceAttr( + "cloudstack_limits.vpc_limit", "max", "10"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_memory(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_memory, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.memory_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.memory_limit", "type", "memory"), + resource.TestCheckResourceAttr( + "cloudstack_limits.memory_limit", "max", "8192"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_secondarystorage(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_secondarystorage, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.secondarystorage_limit"), + resource.TestCheckResourceAttr( + "cloudstack_limits.secondarystorage_limit", "type", "secondarystorage"), + resource.TestCheckResourceAttr( + "cloudstack_limits.secondarystorage_limit", "max", "2000"), + ), + }, + }, + }) +} + +func TestAccCloudStackLimits_import(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudStackLimitsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudStackLimits_domain + testAccCloudStackLimits_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudStackLimitsExists("cloudstack_limits.foo"), + ), + }, + { + ResourceName: "cloudstack_limits.foo", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"domainid", "type", "type", "max"}, + }, + }, + }) +} + +// Test configurations for different resource types +const testAccCloudStackLimits_domain = ` +resource "cloudstack_domain" "test_domain" { + name = "test-domain-limits" +} +` + +const testAccCloudStackLimits_domain_limit = ` +resource "cloudstack_limits" "domain_limit" { + type = "volume" Review Comment: Inconsistent indentation with excessive whitespace. Should align with the other fields using consistent spacing. ```suggestion type = "volume" ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: dev-unsubscr...@cloudstack.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org