Skip to content

Commit e9c7b3b

Browse files
provider: automatically generate the *_wo and *_wo_version for write-only arguments + compute: added write-only support for shared_secret argument for google_compute_vpn_tunnel resource (#14933)
Co-authored-by: Stephen Lewis (Burrows) <[email protected]>
1 parent 464f279 commit e9c7b3b

File tree

16 files changed

+716
-27
lines changed

16 files changed

+716
-27
lines changed

docs/content/reference/field.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,27 @@ Example:
107107
sensitive: true
108108
```
109109

110+
### `write_only`
111+
Set to true to enable write-only functionality for this field.
112+
If true, the [**Write-only Arguments**](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/write-only-arguments) will be automatically generated by the code generator (`[field_name]_wo` and `[field_name]_wo_version`).
113+
The base field should also be marked as `sensitive` so that the value is obscured in Terraform output but it will still be stored in state. (This is necessary for compatibility with Terraform <v1.11.) The write-only version of the field will be obscured in Terraform output and _won't_ be stored in state. See [Ephemerality in Resources - Use Write-only arguments](https://developer.hashicorp.com/terraform/language/resources/ephemeral/write-only) for more information.
114+
115+
Example:
116+
117+
```yaml
118+
write_only: true
119+
```
120+
121+
**Warning:** This field cannot be used in combination with `exactly_one_of` on multiple write-only fields. This is planned to be [fixed in the future](https://github.com/hashicorp/terraform-provider-google/issues/24327).
122+
110123
### `write_only_legacy` (deprecated)
111124
If true, the field is considered "write-only", which means that its value will
112125
be obscured in Terraform output as well as not be stored in state. This field is meant to replace `sensitive` as it doesn't store the value in state.
113126
See [Ephemerality in Resources - Use Write-only arguments](https://developer.hashicorp.com/terraform/language/resources/ephemeral/write-only)
114127
for more information.
115128

116129
Write-only fields are only supported in Terraform v1.11+. Because the provider supports earlier Terraform versions, write only fields must be paired with (mutually exclusive) `sensitive` fields covering the same functionality for compatibility with those older versions.
117-
This field cannot be used in conjuction with `immutable` or `sensitive`.
130+
This field cannot be used in conjunction with `immutable` or `sensitive`.
118131

119132
**Note**: Due to write-only not being read from the API, it is not possible to update the field directly unless a sidecar field is used. (e.g. `password` as a write-only field and `password_wo_version` as an immutable field meant for updating).
120133

mmv1/api/resource.go

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ func (r Resource) SensitiveProps() []*Type {
616616
func (r Resource) WriteOnlyProps() []*Type {
617617
props := r.AllNestedProperties(r.RootProperties())
618618
return google.Select(props, func(p *Type) bool {
619-
return p.WriteOnlyLegacy
619+
return p.WriteOnlyLegacy || p.WriteOnly
620620
})
621621
}
622622

@@ -748,14 +748,100 @@ func (r Resource) GetIdentity() []*Type {
748748
})
749749
}
750750

751-
func (r *Resource) AddLabelsRelatedFields(props []*Type, parent *Type) []*Type {
751+
func deduplicateSliceOfStrings(slice []string) []string {
752+
seen := make(map[string]bool, len(slice))
753+
result := make([]string, 0, len(slice))
754+
755+
for _, str := range slice {
756+
if !seen[str] {
757+
seen[str] = true
758+
result = append(result, str)
759+
}
760+
}
761+
762+
return result
763+
}
764+
765+
func buildWriteOnlyField(name string, versionFieldName string, originalField *Type) *Type {
766+
description := fmt.Sprintf("%s Note: This property is write-only and will not be read from the API. For more info see [updating write-only attributes](/docs/providers/google/guides/using_write_only_attributes.html#updating-write-only-attributes)", originalField.Description)
767+
originalFieldLineage := originalField.TerraformLineage()
768+
fieldPathCurrentField := strings.ReplaceAll(originalFieldLineage, google.Underscore(originalField.Name), google.Underscore(name))
769+
requiredWith := strings.ReplaceAll(originalFieldLineage, google.Underscore(originalField.Name), google.Underscore(versionFieldName))
770+
771+
apiName := originalField.ApiName
772+
if apiName == "" {
773+
apiName = originalField.Name
774+
}
775+
776+
options := []func(*Type){
777+
propertyWithType("String"),
778+
propertyWithRequired(false),
779+
propertyWithDescription(description),
780+
propertyWithWriteOnly(true),
781+
propertyWithApiName(apiName),
782+
propertyWithIgnoreRead(true),
783+
propertyWithRequiredWith([]string{requiredWith}),
784+
}
785+
786+
if originalField.Required {
787+
originalField.Required = false
788+
exactlyOneOf := append(originalField.ExactlyOneOf, originalFieldLineage, fieldPathCurrentField)
789+
options = append(options, propertyWithExactlyOneOf(deduplicateSliceOfStrings(exactlyOneOf)))
790+
originalField.ExactlyOneOf = deduplicateSliceOfStrings(exactlyOneOf)
791+
} else {
792+
conflicts := append(originalField.Conflicts, originalFieldLineage)
793+
options = append(options, propertyWithConflicts(deduplicateSliceOfStrings(conflicts)))
794+
}
795+
796+
if len(originalField.AtLeastOneOf) > 0 {
797+
atLeastOneOf := append(originalField.AtLeastOneOf, originalFieldLineage, fieldPathCurrentField)
798+
options = append(options, propertyWithAtLeastOneOf(deduplicateSliceOfStrings(atLeastOneOf)))
799+
}
800+
801+
return NewProperty(name, originalField.ApiName, options)
802+
}
803+
804+
func buildWriteOnlyVersionField(name string, originalField *Type, writeOnlyField *Type) *Type {
805+
description := fmt.Sprintf("Triggers update of %s write-only. For more info see [updating write-only attributes](/docs/providers/google/guides/using_write_only_attributes.html#updating-write-only-attributes)", google.Underscore(writeOnlyField.Name))
806+
requiredWith := strings.ReplaceAll(originalField.TerraformLineage(), google.Underscore(originalField.Name), google.Underscore(writeOnlyField.Name))
807+
808+
options := []func(*Type){
809+
propertyWithType("Int"),
810+
propertyWithImmutable(originalField.IsForceNew()),
811+
propertyWithDescription(description),
812+
propertyWithRequiredWith([]string{requiredWith}),
813+
propertyWithClientSide(true),
814+
}
815+
816+
return NewProperty(name, name, options)
817+
}
818+
819+
func (r *Resource) addWriteOnlyFields(props []*Type, propWithWoConfigured *Type) []*Type {
820+
propWithWoConfigured.WriteOnly = false
821+
if len(propWithWoConfigured.RequiredWith) > 0 {
822+
log.Fatalf("WriteOnly property '%s' in resource '%s' cannot have RequiredWith set. This combination is not supported.", propWithWoConfigured.Name, r.Name)
823+
}
824+
woFieldName := fmt.Sprintf("%sWo", propWithWoConfigured.Name)
825+
woVersionFieldName := fmt.Sprintf("%sVersion", woFieldName)
826+
writeOnlyField := buildWriteOnlyField(woFieldName, woVersionFieldName, propWithWoConfigured)
827+
writeOnlyVersionField := buildWriteOnlyVersionField(woVersionFieldName, propWithWoConfigured, writeOnlyField)
828+
props = append(props, writeOnlyField, writeOnlyVersionField)
829+
return props
830+
}
831+
832+
// AddExtraFields processes properties and adds supplementary fields based on property types.
833+
// It handles write-only properties, labels, and annotations.
834+
func (r *Resource) AddExtraFields(props []*Type, parent *Type) []*Type {
752835
for _, p := range props {
836+
if p.WriteOnly && !strings.HasSuffix(p.Name, "Wo") {
837+
props = r.addWriteOnlyFields(props, p)
838+
}
753839
if p.IsA("KeyValueLabels") {
754840
props = r.addLabelsFields(props, parent, p)
755841
} else if p.IsA("KeyValueAnnotations") {
756842
props = r.addAnnotationsFields(props, parent, p)
757843
} else if p.IsA("NestedObject") && len(p.AllProperties()) > 0 {
758-
p.Properties = r.AddLabelsRelatedFields(p.AllProperties(), p)
844+
p.Properties = r.AddExtraFields(p.AllProperties(), p)
759845
}
760846
}
761847
return props

0 commit comments

Comments
 (0)