Skip to content

Commit 2751244

Browse files
committed
Merge branch 'main' into tpg-22214-set-computed-name-a-d
2 parents 68a3816 + 6ffd5b2 commit 2751244

File tree

102 files changed

+3385
-391
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+3385
-391
lines changed

.ci/magician/cmd/generate_downstream.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright 2025 Google LLC. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package cmd
217

318
import (
@@ -15,7 +30,7 @@ import (
1530
"github.com/spf13/cobra"
1631
)
1732

18-
var changelogExp = regexp.MustCompile("(?s)```release-note.*?```")
33+
var changelogExp = regexp.MustCompile("(?s)```release-note:(?P<noteType>[a-zA-Z]+).*```")
1934

2035
var gdEnvironmentVariables = [...]string{
2136
"BASE_BRANCH",
@@ -354,8 +369,11 @@ func addChangelogEntry(downstreamRepo *source.Repo, pullRequest *github.PullRequ
354369
return err
355370
}
356371
rnr.Mkdir(".changelog")
357-
if err := rnr.WriteFile(filepath.Join(".changelog", fmt.Sprintf("%d.txt", pullRequest.Number)), strings.Join(changelogExp.FindAllString(pullRequest.Body, -1), "\n")); err != nil {
358-
return err
372+
matches := changelogExp.FindStringSubmatch(pullRequest.Body)
373+
if matches != nil && matches[1] != "none" {
374+
if err := rnr.WriteFile(filepath.Join(".changelog", fmt.Sprintf("%d.txt", pullRequest.Number)), strings.Join(changelogExp.FindAllString(pullRequest.Body, -1), "\n")); err != nil {
375+
return err
376+
}
359377
}
360378
return rnr.PopDir()
361379
}

.ci/magician/github/membership_data.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,18 @@ var (
6464
"BBBmau": {
6565
vacations: []Vacation{
6666
{
67-
startDate: newDate(2025, 3, 1),
68-
endDate: newDate(2025, 3, 10),
67+
startDate: newDate(2025, 4, 7),
68+
endDate: newDate(2025, 4, 11),
6969
},
7070
},
7171
},
7272
"c2thorn": {
73-
vacations: []Vacation{},
73+
vacations: []Vacation{
74+
{
75+
startDate: newDate(2025, 4, 9),
76+
endDate: newDate(2025, 4, 15),
77+
},
78+
},
7479
},
7580
"hao-nan-li": {
7681
vacations: []Vacation{},

docs/content/develop/add-fields.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ Replace `String` in the field type with one of the following options:
167167
key_name: 'KEY_NAME'
168168
key_description: |
169169
MULTILINE_KEY_FIELD_DESCRIPTION
170+
# Map of primitive values
171+
value_type:
172+
name: mapIntegerName
173+
type: Integer
174+
175+
# Map of complex values
170176
value_type:
171177
name: mapObjectName
172178
type: NestedObject
@@ -177,7 +183,7 @@ Replace `String` in the field type with one of the following options:
177183
MULTI_LINE_FIELD_DESCRIPTION
178184
```
179185
180-
This type is only used for string -> complex type mappings, use "KeyValuePairs" for simple mappings. Complex maps can't be represented natively in Terraform, and this type is transformed into an associative array (TypeSet) with the key merged into the object alongside other top-level fields.
186+
This type is used for general-case string -> non-string type mappings, use "KeyValuePairs" for string -> string mappings. Complex maps can't be represented natively in Terraform, and this type is transformed into an associative array (TypeSet) with the key merged into the object alongside other top-level fields.
181187
182188
For `key_name` and `key_description`, provide a domain-appropriate name and description. For example, a map that references a specific type of resource would generally use the singular resource kind as the key name (such as "topic" for PubSub Topic) and a descriptor of the expected format depending on the context (such as resourceId vs full resource name).
183189

docs/content/reference/resource.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,140 @@ Example:
325325
mutex: 'alloydb/instance/{{name}}'
326326
```
327327

328+
## Sweeper
329+
330+
Sweepers are a testing infrastructure mechanism that automatically clean up resources created during tests. They run before tests start and can be run manually to clean up dangling resources. Sweepers help prevent test failures due to resource quota limits and reduce cloud infrastructure costs by removing test resources that were not properly cleaned up.
331+
332+
Sweeper generation is enabled by default, except in the following conditions which require customization here:
333+
334+
- Resources with custom deletion code
335+
- Resources with parent-child relationships (unless the parent relationship is configured)
336+
- Resources with complex URL parameters that aren't simple region/project parameters
337+
338+
Define the sweeper block in a resource to override these exclusions and enable sweeper generation for that resource.
339+
340+
### `exclude_sweeper`
341+
342+
If set to `true`, no sweeper will be generated for this resource. This is useful for resources that cannot or should not be automatically cleaned up.
343+
344+
Default: `false`
345+
346+
Example:
347+
348+
```yaml
349+
exclude_sweeper: true
350+
```
351+
352+
### `sweeper`
353+
354+
Configures how test resources are swept (cleaned up) after tests. The sweeper system helps ensure resources created during tests are properly removed, even when tests fail unexpectedly. All fields within the `sweeper` block are optional, with reasonable defaults provided when not specified. See [sweeper.go ↗](https://github.com/GoogleCloudPlatform/magic-modules/blob/main/mmv1/api/resource/sweeper.go) for the implementation.
355+
356+
- `identifier_field`: Specifies which field in the API resource object should be used to identify resources for deletion. If not specified, defaults to "name" if present in the resource, otherwise falls back to "id".
357+
358+
- `prefixes`: Specifies name prefixes that identify resources eligible for sweeping. Resources whose names start with any of these prefixes will be deleted. By default, resources with the `tf-test-` prefix are automatically eligible for sweeping even if no prefixes are specified.
359+
360+
- `url_substitutions`: Allows customizing URL parameters when listing resources. Each map entry represents a set of key-value pairs to substitute in the URL template. This is commonly used to specify regions to sweep in. If not specified, the sweeper will only run in the default region (us-central1) and zone (us-central1-a).
361+
362+
- `dependencies`: Lists other resource types that must be swept before this one. This ensures proper cleanup order for resources with dependencies. If not specified, no dependencies are assumed.
363+
364+
- `parent`: Configures sweeping for resources that depend on parent resources (like a nodepool that belongs to a cluster).
365+
366+
Required fields:
367+
- `resource_type`: The type of the parent resource (for example, "google_container_cluster")
368+
- `child_field`: The field in your resource that references the parent (for example, "cluster")
369+
- At least one of `parent_field` or `template` is required
370+
371+
Options for getting parent reference:
372+
- `parent_field`: The field from parent to use (typically "name" or "id")
373+
- `template`: A template string like "projects/{{project}}/locations/{{location}}/clusters/{{value}}"
374+
375+
Options for processing the parent field value:
376+
- `parent_field_extract_name`: When set to true, extracts just the resource name from a self_link URL by taking the portion after the last slash. This is useful when the parent field contains a fully-qualified resource URL (like "https://www.googleapis.com/compute/v1/projects/my-project/global/networks/my-network" or "projects/my-project/zones/us-central1-a/instances/my-instance") but you only need the final resource name component ("my-network" or "my-instance").
377+
378+
- `parent_field_regex`: A regex pattern with a capture group to extract a specific portion of the parent field value. This is useful when you need more control over extracting parts of complex resource identifiers. The pattern must contain at least one capture group (in parentheses), and the first capture group's match will be used as the extracted value.
379+
380+
- `query_string`: Allows appending additional query parameters to the resource's delete URL when performing delete operations. Format should include the starting character, for example, "?force=true" or "&verbose=true". If not specified, no additional query parameters are added.
381+
382+
- `ensure_value`: Specifies a field that must be set to a specific value before deletion. Used for resources that have fields like 'deletionProtectionEnabled' that must be explicitly disabled before the resource can be deleted. All fields within the `ensure_value` block are required except `include_full_resource`:
383+
384+
- `field`: The API field name that needs to be updated before deletion. Can include dot notation for nested fields (for example, "settings.deletionProtectionEnabled").
385+
386+
- `value`: The required value that `field` must be set to before deletion. For boolean fields use "true" or "false", for integers use string representation, for string fields use the exact string value required.
387+
388+
- `include_full_resource`: Determines whether to send the entire resource object with the updated field (true) or to send just the field that needs updating (false). Some APIs require the full resource to be sent in update operations. Default: `false`.
389+
390+
Examples:
391+
392+
Basic sweeper configuration:
393+
394+
```yaml
395+
sweeper:
396+
prefixes:
397+
- "tf-test-"
398+
- "tmp-"
399+
```
400+
401+
Sweeper with parent-child relationship (basic):
402+
403+
```yaml
404+
sweeper:
405+
dependencies: # sweep google_compute_instance before attempting to sweep this resource
406+
- "google_compute_instance"
407+
parent:
408+
resource_type: "google_container_cluster"
409+
parent_field: "name"
410+
child_field: "cluster"
411+
```
412+
413+
Sweeper with parent_field_extract_name:
414+
415+
```yaml
416+
sweeper:
417+
parent:
418+
resource_type: "google_compute_network"
419+
parent_field: "selfLink" # Contains: "projects/my-project/global/networks/my-network"
420+
parent_field_extract_name: true # Extracts just "my-network"
421+
child_field: "network"
422+
```
423+
424+
Sweeper with parent template and pre-deletion field update:
425+
426+
```yaml
427+
sweeper:
428+
parent:
429+
resource_type: "google_container_cluster"
430+
template: "projects/{{project}}/locations/{{location}}/clusters/{{value}}"
431+
parent_field: "displayName" # Provides the value for {{value}}
432+
child_field: "cluster"
433+
ensure_value:
434+
field: "deletionProtection"
435+
value: "false"
436+
include_full_resource: false
437+
438+
```
439+
440+
Sweeper with URL substitutions for multiple regions:
441+
442+
```yaml
443+
sweeper:
444+
url_substitutions:
445+
- collection_id: default_collection
446+
region: global
447+
- collection_id: default_collection
448+
region: eu
449+
```
450+
451+
Sweeper with URL substitutions specifying only regions:
452+
453+
```yaml
454+
sweeper:
455+
identifier_field: "displayName"
456+
url_substitutions:
457+
- region: "us-central1"
458+
- region: "us-east1"
459+
- region: "europe-west1"
460+
```
461+
328462
## Fields
329463

330464
### `virtual_fields`

mmv1/api/resource.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,24 @@ func (r Resource) InIdFormat(prop Type) bool {
12171217
return slices.Contains(fields, google.Underscore(prop.Name))
12181218
}
12191219

1220+
// Returns true if at least one of the fields in the ID format is computed
1221+
func (r Resource) HasComputedIdFormatFields() bool {
1222+
idFormatFields := map[string]struct{}{}
1223+
for _, f := range r.ExtractIdentifiers(r.GetIdFormat()) {
1224+
idFormatFields[f] = struct{}{}
1225+
}
1226+
for _, p := range r.GettableProperties() {
1227+
// Skip fields not in the id format
1228+
if _, ok := idFormatFields[google.Underscore(p.Name)]; !ok {
1229+
continue
1230+
}
1231+
if (p.Output || p.DefaultFromApi) && !p.IgnoreRead {
1232+
return true
1233+
}
1234+
}
1235+
return false
1236+
}
1237+
12201238
// ====================
12211239
// Template Methods
12221240
// ====================

0 commit comments

Comments
 (0)