Skip to content

Commit af82b5b

Browse files
BBBmauSarahFrenchzli82016
authored andcommitted
FEATURE: release ephemeral resources support (GoogleCloudPlatform#12469)
Co-authored-by: Sarah French <[email protected]> Co-authored-by: Zhenhua Li <[email protected]> Co-authored-by: Sarah French <[email protected]>
1 parent e61e0df commit af82b5b

23 files changed

+1801
-146
lines changed

mmv1/provider/terraform.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ func (t Terraform) getCommonCopyFiles(versionName string, generateCode, generate
313313
// save the folder name to foldersCopiedToGoogleDir
314314
var foldersCopiedToGoogleDir []string
315315
if generateCode {
316-
foldersCopiedToGoogleDir = []string{"third_party/terraform/services", "third_party/terraform/acctest", "third_party/terraform/sweeper", "third_party/terraform/provider", "third_party/terraform/tpgdclresource", "third_party/terraform/tpgiamresource", "third_party/terraform/tpgresource", "third_party/terraform/transport", "third_party/terraform/fwmodels", "third_party/terraform/fwprovider", "third_party/terraform/fwtransport", "third_party/terraform/fwresource", "third_party/terraform/verify", "third_party/terraform/envvar", "third_party/terraform/functions", "third_party/terraform/test-fixtures"}
316+
foldersCopiedToGoogleDir = []string{"third_party/terraform/services", "third_party/terraform/acctest", "third_party/terraform/sweeper", "third_party/terraform/provider", "third_party/terraform/tpgdclresource", "third_party/terraform/tpgiamresource", "third_party/terraform/tpgresource", "third_party/terraform/transport", "third_party/terraform/fwmodels", "third_party/terraform/fwprovider", "third_party/terraform/fwtransport", "third_party/terraform/fwresource", "third_party/terraform/fwutils", "third_party/terraform/fwvalidators", "third_party/terraform/verify", "third_party/terraform/envvar", "third_party/terraform/functions", "third_party/terraform/test-fixtures"}
317317
}
318318
googleDir := "google"
319319
if versionName != "ga" {

mmv1/provider/terraform/common~copy.yaml

+14
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,20 @@
101101
'<%= dir -%>/fwprovider/<%= fname -%>': 'third_party/terraform/fwprovider/<%= fname -%>'
102102
<% end -%>
103103

104+
<%
105+
Dir["third_party/terraform/fwutils/*.go"].each do |file_path|
106+
fname = file_path.split('/')[-1]
107+
-%>
108+
'<%= dir -%>/fwutils/<%= fname -%>': 'third_party/terraform/fwutils/<%= fname -%>'
109+
<% end -%>
110+
111+
<%
112+
Dir["third_party/terraform/fwvalidators/*.go"].each do |file_path|
113+
fname = file_path.split('/')[-1]
114+
-%>
115+
'<%= dir -%>/fwvalidators/<%= fname -%>': 'third_party/terraform/fwvalidators/<%= fname -%>'
116+
<% end -%>
117+
104118
<%
105119
Dir["third_party/terraform/fwtransport/*.go"].each do |file_path|
106120
fname = file_path.split('/')[-1]

mmv1/third_party/terraform/fwprovider/framework_provider.go.tmpl

+25-11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
99
"github.com/hashicorp/terraform-plugin-framework/datasource"
10+
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
1011
"github.com/hashicorp/terraform-plugin-framework/function"
1112
"github.com/hashicorp/terraform-plugin-framework/path"
1213
"github.com/hashicorp/terraform-plugin-framework/provider"
@@ -16,6 +17,7 @@ import (
1617
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1718
"github.com/hashicorp/terraform-plugin-framework/types"
1819

20+
"github.com/hashicorp/terraform-provider-google/google/fwvalidators"
1921
"github.com/hashicorp/terraform-provider-google/google/functions"
2022
"github.com/hashicorp/terraform-provider-google/google/fwmodels"
2123
"github.com/hashicorp/terraform-provider-google/google/services/resourcemanager"
@@ -32,6 +34,7 @@ var (
3234
_ provider.Provider = &FrameworkProvider{}
3335
_ provider.ProviderWithMetaSchema = &FrameworkProvider{}
3436
_ provider.ProviderWithFunctions = &FrameworkProvider{}
37+
_ provider.ProviderWithEphemeralResources = &FrameworkProvider{}
3538
)
3639

3740
// New is a helper function to simplify provider server and testing implementation.
@@ -80,8 +83,8 @@ func (p *FrameworkProvider) Schema(_ context.Context, _ provider.SchemaRequest,
8083
stringvalidator.ConflictsWith(path.Expressions{
8184
path.MatchRoot("access_token"),
8285
}...),
83-
CredentialsValidator(),
84-
NonEmptyStringValidator(),
86+
fwvalidators.CredentialsValidator(),
87+
fwvalidators.NonEmptyStringValidator(),
8588
},
8689
},
8790
"access_token": schema.StringAttribute{
@@ -90,13 +93,13 @@ func (p *FrameworkProvider) Schema(_ context.Context, _ provider.SchemaRequest,
9093
stringvalidator.ConflictsWith(path.Expressions{
9194
path.MatchRoot("credentials"),
9295
}...),
93-
NonEmptyStringValidator(),
96+
fwvalidators.NonEmptyStringValidator(),
9497
},
9598
},
9699
"impersonate_service_account": schema.StringAttribute{
97100
Optional: true,
98101
Validators: []validator.String{
99-
NonEmptyStringValidator(),
102+
fwvalidators.NonEmptyStringValidator(),
100103
},
101104
},
102105
"impersonate_service_account_delegates": schema.ListAttribute{
@@ -106,25 +109,25 @@ func (p *FrameworkProvider) Schema(_ context.Context, _ provider.SchemaRequest,
106109
"project": schema.StringAttribute{
107110
Optional: true,
108111
Validators: []validator.String{
109-
NonEmptyStringValidator(),
112+
fwvalidators.NonEmptyStringValidator(),
110113
},
111114
},
112115
"billing_project": schema.StringAttribute{
113116
Optional: true,
114117
Validators: []validator.String{
115-
NonEmptyStringValidator(),
118+
fwvalidators.NonEmptyStringValidator(),
116119
},
117120
},
118121
"region": schema.StringAttribute{
119122
Optional: true,
120123
Validators: []validator.String{
121-
NonEmptyStringValidator(),
124+
fwvalidators.NonEmptyStringValidator(),
122125
},
123126
},
124127
"zone": schema.StringAttribute{
125128
Optional: true,
126129
Validators: []validator.String{
127-
NonEmptyStringValidator(),
130+
fwvalidators.NonEmptyStringValidator(),
128131
},
129132
},
130133
"scopes": schema.ListAttribute{
@@ -137,8 +140,8 @@ func (p *FrameworkProvider) Schema(_ context.Context, _ provider.SchemaRequest,
137140
"request_timeout": schema.StringAttribute{
138141
Optional: true,
139142
Validators: []validator.String{
140-
NonEmptyStringValidator(),
141-
NonNegativeDurationValidator(),
143+
fwvalidators.NonEmptyStringValidator(),
144+
fwvalidators.NonNegativeDurationValidator(),
142145
},
143146
},
144147
"request_reason": schema.StringAttribute{
@@ -240,7 +243,7 @@ func (p *FrameworkProvider) Schema(_ context.Context, _ provider.SchemaRequest,
240243
"send_after": schema.StringAttribute{
241244
Optional: true,
242245
Validators: []validator.String{
243-
NonNegativeDurationValidator(),
246+
fwvalidators.NonNegativeDurationValidator(),
244247
},
245248
},
246249
"enable_batching": schema.BoolAttribute{
@@ -282,6 +285,7 @@ func (p *FrameworkProvider) Configure(ctx context.Context, req provider.Configur
282285
meta := p.Primary.Meta().(*transport_tpg.Config)
283286
resp.DataSourceData = meta
284287
resp.ResourceData = meta
288+
resp.EphemeralResourceData = meta
285289
}
286290

287291

@@ -314,3 +318,13 @@ func (p *FrameworkProvider) Functions(_ context.Context) []func() function.Funct
314318
functions.NewZoneFromIdFunction,
315319
}
316320
}
321+
322+
// EphemeralResources defines the resources that are of ephemeral type implemented in the provider.
323+
func (p *FrameworkProvider) EphemeralResources(_ context.Context) []func() ephemeral.EphemeralResource {
324+
return []func() ephemeral.EphemeralResource{
325+
resourcemanager.GoogleEphemeralServiceAccountAccessToken,
326+
resourcemanager.GoogleEphemeralServiceAccountIdToken,
327+
resourcemanager.GoogleEphemeralServiceAccountJwt,
328+
resourcemanager.GoogleEphemeralServiceAccountKey,
329+
}
330+
}

mmv1/third_party/terraform/fwprovider/framework_validators_test.go

-119
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package fwutils
2+
3+
import "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
4+
5+
func StringSet(d basetypes.SetValue) []string {
6+
7+
StringSlice := make([]string, 0)
8+
for _, v := range d.Elements() {
9+
StringSlice = append(StringSlice, v.(basetypes.StringValue).ValueString())
10+
}
11+
return StringSlice
12+
}

mmv1/third_party/terraform/fwprovider/framework_validators.go renamed to mmv1/third_party/terraform/fwvalidators/framework_validators.go

+92-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
package fwprovider
1+
package fwvalidators
22

33
import (
44
"context"
55
"fmt"
66
"os"
7+
"regexp"
78
"time"
89

910
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
@@ -115,3 +116,93 @@ func (v nonEmptyStringValidator) ValidateString(ctx context.Context, request val
115116
func NonEmptyStringValidator() validator.String {
116117
return nonEmptyStringValidator{}
117118
}
119+
120+
// Define the possible service account name patterns
121+
var ServiceAccountEmailPatterns = []string{
122+
`^.+@.+\.iam\.gserviceaccount\.com$`, // Standard IAM service account
123+
`^.+@developer\.gserviceaccount\.com$`, // Legacy developer service account
124+
`^.+@appspot\.gserviceaccount\.com$`, // App Engine service account
125+
`^.+@cloudservices\.gserviceaccount\.com$`, // Google Cloud services service account
126+
`^.+@cloudbuild\.gserviceaccount\.com$`, // Cloud Build service account
127+
`^service-[0-9]+@.+-compute\.iam\.gserviceaccount\.com$`, // Compute Engine service account
128+
}
129+
130+
// Create a custom validator for service account names
131+
type ServiceAccountEmailValidator struct{}
132+
133+
func (v ServiceAccountEmailValidator) Description(ctx context.Context) string {
134+
return "value must be a valid service account email address"
135+
}
136+
137+
func (v ServiceAccountEmailValidator) MarkdownDescription(ctx context.Context) string {
138+
return v.Description(ctx)
139+
}
140+
141+
func (v ServiceAccountEmailValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) {
142+
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
143+
return
144+
}
145+
146+
value := req.ConfigValue.ValueString()
147+
148+
// Check for empty string
149+
if value == "" {
150+
resp.Diagnostics.AddError("Invalid Service Account Name", "Service account name must not be empty")
151+
return
152+
}
153+
154+
valid := false
155+
for _, pattern := range ServiceAccountEmailPatterns {
156+
if matched, _ := regexp.MatchString(pattern, value); matched {
157+
valid = true
158+
break
159+
}
160+
}
161+
162+
if !valid {
163+
resp.Diagnostics.AddAttributeError(
164+
req.Path,
165+
"Invalid Service Account Name",
166+
"Service account name must match one of the expected patterns for Google service accounts",
167+
)
168+
}
169+
}
170+
171+
// Create a custom validator for duration
172+
type BoundedDuration struct {
173+
MinDuration time.Duration
174+
MaxDuration time.Duration
175+
}
176+
177+
func (v BoundedDuration) Description(ctx context.Context) string {
178+
return fmt.Sprintf("value must be a valid duration string between %v and %v", v.MinDuration, v.MaxDuration)
179+
}
180+
181+
func (v BoundedDuration) MarkdownDescription(ctx context.Context) string {
182+
return v.Description(ctx)
183+
}
184+
185+
func (v BoundedDuration) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) {
186+
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
187+
return
188+
}
189+
190+
value := req.ConfigValue.ValueString()
191+
duration, err := time.ParseDuration(value)
192+
if err != nil {
193+
resp.Diagnostics.AddAttributeError(
194+
req.Path,
195+
"Invalid Duration Format",
196+
"Duration must be a valid duration string (e.g., '3600s', '1h')",
197+
)
198+
return
199+
}
200+
201+
if duration < v.MinDuration || duration > v.MaxDuration {
202+
resp.Diagnostics.AddAttributeError(
203+
req.Path,
204+
"Invalid Duration",
205+
fmt.Sprintf("Duration must be between %v and %v", v.MinDuration, v.MaxDuration),
206+
)
207+
}
208+
}

0 commit comments

Comments
 (0)