Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new resource netapp volumes quotaRules #12665

Merged
merged 13 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions mmv1/products/netapp/VolumeQuotaRule.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Copyright 2024 Google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

---
name: 'VolumeQuotaRule'
api_resource_type_kind: QuotaRule
description: |
QuotaRule specifies the maximum capacity a user or group can use within a volume. They can be used for creating default and individual quota rules.
references:
guides:
'Documentation': https://cloud.google.com/netapp/volumes/docs/configure-and-use/volumes/overview#volume_user_and_group_quotas
api: https://cloud.google.com/netapp/volumes/docs/reference/rest/v1/projects.locations.volumes.quotaRules
docs:
base_url: 'projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/quotaRules'
self_link: 'projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/quotaRules/{{name}}'
create_url: 'projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/quotaRules?quotaRuleId={{name}}'
update_url: 'projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/quotaRules/{{name}}'
update_verb: 'PATCH'
update_mask: true
delete_url: 'projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/quotaRules/{{name}}'
timeouts:
insert_minutes: 20
update_minutes: 20
delete_minutes: 20
autogen_async: true
async:
actions: ['create', 'delete', 'update']
type: 'OpAsync'
operation:
base_url: '{{op_id}}'
result:
resource_inside_response: false
custom_code:
# Skipping the sweeper since we need to sweep multiple regions
exclude_sweeper: true
examples:
- name: 'netapp_volume_quota_rule_basic'
primary_resource_id: 'test_quota_rule'
vars:
volume_name: 'test-volume'
pool_name: 'test-pool'
network_name: 'test-network'
quota_rule_name: 'test-volume-quota-rule'
test_vars_overrides:
'network_name': 'acctest.BootstrapSharedServiceNetworkingConnection(t, "gcnv-network-config-1", acctest.ServiceNetworkWithParentService("netapp.servicenetworking.goog"))'
parameters:
- name: 'location'
type: String
description: |
Loction of the quotaRule. QuotaRules are child resources of volumes and live in the same location.
url_param_only: true
immutable: true
- name: 'volume_name'
type: String
description: |
Name of the volume to create the quotaRule in.
url_param_only: true
required: true
immutable: true
- name: 'name'
type: String
description:
The resource name of the quotaRule.
url_param_only: true
required: true
immutable: true
properties:
- name: 'target'
type: String
description: |
The quota rule applies to the specified user or group.
Valid targets for volumes with NFS protocol enabled:
- UNIX UID for individual user quota
- UNIX GID for individual group quota
Valid targets for volumes with SMB protocol enabled:
- Windows SID for individual user quota
Leave empty for default quotas
- name: 'type'
type: Enum
description: |
Types of Quota Rule.
required: true
enum_values:
- 'INDIVIDUAL_USER_QUOTA'
- 'INDIVIDUAL_GROUP_QUOTA'
- 'DEFAULT_USER_QUOTA'
- 'DEFAULT_GROUP_QUOTA'
- name: 'diskLimitMib'
type: Integer
description:
The maximum allowed capacity in MiB.
required: true
- name: 'state'
type: String
description: |
The state of the quota rule. Possible Values : [STATE_UNSPECIFIED, CREATING, UPDATING, READY, DELETING, ERROR]
output: true
- name: 'stateDetails'
type: String
description: |
State details of the quota rule
output: true
- name: 'createTime'
type: String
description: |
Create time of the quota rule. A timestamp in RFC3339 UTC "Zulu" format. Examples: "2023-06-22T09:13:01.617Z".
output: true
- name: 'description'
type: String
description: |
Description for the quota rule.
- name: 'labels'
type: KeyValueLabels
description: |
Labels as key value pairs of the quota rule. Example: `{ "owner": "Bob", "department": "finance", "purpose": "testing" }`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
resource "google_netapp_storage_pool" "default" {
name = "{{index $.Vars "pool_name"}}"
location = "us-west2"
service_level = "PREMIUM"
capacity_gib = 2048
network = data.google_compute_network.default.id
}

resource "google_netapp_volume" "default" {
location = google_netapp_storage_pool.default.location
name = "{{index $.Vars "volume_name"}}"
capacity_gib = 100
share_name = "{{index $.Vars "volume_name"}}"
storage_pool = google_netapp_storage_pool.default.name
protocols = ["NFSV3"]
}

resource "google_netapp_volume_quota_rule" "{{$.PrimaryResourceId}}" {
depends_on = [google_netapp_volume.default]
location = google_netapp_volume.default.location
volume_name = google_netapp_volume.default.name
type = "DEFAULT_USER_QUOTA"
disk_limit_mib = 50
name = "{{index $.Vars "quota_rule_name"}}"
}

data "google_compute_network" "default" {
name = "{{index $.Vars "network_name"}}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package netapp

import (
"context"
"log"
"strings"
"testing"

"github.com/hashicorp/terraform-provider-google/google/envvar"
"github.com/hashicorp/terraform-provider-google/google/sweeper"
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
)

func init() {
sweeper.AddTestSweepers("NetappVolumeQuotaRule", testSweepNetappVolumeQuotaRule)
}

// At the time of writing, the CI only passes us-central1 as the region
func testSweepNetappVolumeQuotaRule(region string) error {
resourceName := "NetappVolumeQuotaRule"
log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName)

config, err := sweeper.SharedConfigForRegion(region)
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err)
return err
}

err = config.LoadAndValidate(context.Background())
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err)
return err
}

t := &testing.T{}
billingId := envvar.GetTestBillingAccountFromEnv(t)

regions := []string{"us-central1", "us-west2", "us-east4"}
for _, r := range regions {
log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s in %s", resourceName, r)

// Setup variables to replace in list template
d := &tpgresource.ResourceDataMock{
FieldsInSchema: map[string]interface{}{
"project": config.Project,
"region": r,
"location": r,
"zone": "-",
"billing_account": billingId,
},
}

listTemplate := strings.Split("https://netapp.googleapis.com/v1/projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/quotaRules", "?")[0]
listUrl, err := tpgresource.ReplaceVars(d, config, listTemplate)
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err)
continue
}

res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
Project: config.Project,
RawURL: listUrl,
UserAgent: config.UserAgent,
})
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err)
continue
}

resourceList, ok := res["volumeQuotaRules"]
if !ok {
log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.")
continue
}

rl := resourceList.([]interface{})

log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName)
// Keep count of items that aren't sweepable for logging.
nonPrefixCount := 0
for _, ri := range rl {
obj := ri.(map[string]interface{})
if obj["name"] == nil {
log.Printf("[INFO][SWEEPER_LOG] %s resource name was nil", resourceName)
continue
}

name := tpgresource.GetResourceNameFromSelfLink(obj["name"].(string))
// Skip resources that shouldn't be sweeped
if !sweeper.IsSweepableTestResource(name) {
nonPrefixCount++
continue
}

deleteTemplate := "https://netapp.googleapis.com/v1/projects/{{project}}/locations/{{location}}/volumes/{{volume_name}}/quotaRules/{{name}}"
deleteUrl, err := tpgresource.ReplaceVars(d, config, deleteTemplate)
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err)
continue
}
deleteUrl = deleteUrl + name

// Don't wait on operations as we may have a lot to delete
_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "DELETE",
Project: config.Project,
RawURL: deleteUrl,
UserAgent: config.UserAgent,
})
if err != nil {
log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err)
} else {
log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name)
}
}

if nonPrefixCount > 0 {
log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount)
}
}

return nil
}
Loading
Loading