diff --git a/mmv1/third_party/terraform/fwtransport/framework_config.go.tmpl b/mmv1/third_party/terraform/fwtransport/framework_config.go.tmpl deleted file mode 100644 index 25e715009913..000000000000 --- a/mmv1/third_party/terraform/fwtransport/framework_config.go.tmpl +++ /dev/null @@ -1,740 +0,0 @@ -package fwtransport - -import ( - "context" - "fmt" - "net/http" - "os" - "regexp" - "strconv" - "time" - - "golang.org/x/oauth2" - googleoauth "golang.org/x/oauth2/google" - - "google.golang.org/api/option" - "google.golang.org/api/transport" - "google.golang.org/grpc" - - "github.com/hashicorp/go-cleanhttp" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging" - - "github.com/hashicorp/terraform-provider-google/google/fwmodels" - transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" - "github.com/hashicorp/terraform-provider-google/google/verify" - - grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus" - "github.com/sirupsen/logrus" -) - -type FrameworkProviderConfig struct { - // Temporary, as we'll replace use of FrameworkProviderConfig with transport_tpg.Config soon - // transport_tpg.Config has a the fields below, hence these changes are needed - Credentials types.String - AccessToken types.String - ImpersonateServiceAccount types.String - ImpersonateServiceAccountDelegates types.List - RequestReason types.String - RequestTimeout types.String - AddTerraformAttributionLabel types.Bool - TerraformAttributionLabelAdditionStrategy types.String - // End temporary - - BillingProject types.String - Client *http.Client - Context context.Context - gRPCLoggingOptions []option.ClientOption - PollInterval time.Duration - Project types.String - Region types.String - Zone types.String - RequestBatcherIam *transport_tpg.RequestBatcher - RequestBatcherServiceUsage *transport_tpg.RequestBatcher - Scopes types.List - TokenSource oauth2.TokenSource - UniverseDomain types.String - UserAgent string - UserProjectOverride types.Bool - DefaultLabels types.Map - - // paths for client setup - {{- range $product := $.Products }} - {{ $product.Name }}BasePath string - {{- end }} -} - -// LoadAndValidateFramework handles the bulk of configuring the provider -// it is pulled out so that we can manually call this from our testing provider as well -func (p *FrameworkProviderConfig) LoadAndValidateFramework(ctx context.Context, data *fwmodels.ProviderModel, tfVersion string, diags *diag.Diagnostics, providerversion string) { - - // Set defaults if needed - p.HandleDefaults(ctx, data, diags) - if diags.HasError() { - return - } - - p.Context = ctx - - // Handle User Agent string - p.UserAgent = CompileUserAgentString(ctx, "terraform-provider-google{{- if ne $.TargetVersionName "ga" -}}-{{$.TargetVersionName}}{{- end }}", tfVersion, providerversion) - // opt in extension for adding to the User-Agent header - if ext := os.Getenv("GOOGLE_TERRAFORM_USERAGENT_EXTENSION"); ext != "" { - ua := p.UserAgent - p.UserAgent = fmt.Sprintf("%s %s", ua, ext) - } - - // Set up client configuration - p.SetupClient(ctx, *data, diags) - if diags.HasError() { - return - } - - // gRPC Logging setup - p.SetupGrpcLogging() - - // Handle Batching Config - batchingConfig := GetBatchingConfig(ctx, data.Batching, diags) - if diags.HasError() { - return - } - - // Setup Base Paths for clients - // Generated products - {{- range $product := $.Products }} - p.{{ $product.Name }}BasePath = data.{{ $product.Name }}CustomEndpoint.ValueString() - {{- end }} - - // Temporary - p.Credentials = data.Credentials - p.AccessToken = data.AccessToken - p.ImpersonateServiceAccount = data.ImpersonateServiceAccount - p.ImpersonateServiceAccountDelegates = data.ImpersonateServiceAccountDelegates - p.RequestReason = data.RequestReason - p.RequestTimeout = data.RequestTimeout - p.AddTerraformAttributionLabel = data.AddTerraformAttributionLabel - p.TerraformAttributionLabelAdditionStrategy = data.TerraformAttributionLabelAdditionStrategy - // End temporary - - // Copy values from the ProviderModel struct containing data about the provider configuration (present only when responsing to ConfigureProvider rpc calls) - // to the FrameworkProviderConfig struct that will be passed and available to all resources/data sources - p.Context = ctx - p.BillingProject = data.BillingProject - p.DefaultLabels = data.DefaultLabels - p.Project = data.Project - p.Region = GetRegionFromRegionSelfLink(data.Region) - p.Scopes = data.Scopes - p.Zone = data.Zone - p.UserProjectOverride = data.UserProjectOverride - p.PollInterval = 10 * time.Second - p.UniverseDomain = data.UniverseDomain - p.RequestBatcherServiceUsage = transport_tpg.NewRequestBatcher("Service Usage", ctx, batchingConfig) - p.RequestBatcherIam = transport_tpg.NewRequestBatcher("IAM", ctx, batchingConfig) -} - -// HandleDefaults will handle all the defaults necessary in the provider -func (p *FrameworkProviderConfig) HandleDefaults(ctx context.Context, data *fwmodels.ProviderModel, diags *diag.Diagnostics) { - if (data.AccessToken.IsNull() || data.AccessToken.IsUnknown()) && (data.Credentials.IsNull() || data.Credentials.IsUnknown()) { - credentials := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_CREDENTIALS", - "GOOGLE_CLOUD_KEYFILE_JSON", - "GCLOUD_KEYFILE_JSON", - }, nil) - - if credentials != nil { - data.Credentials = types.StringValue(credentials.(string)) - } - - accessToken := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_OAUTH_ACCESS_TOKEN", - }, nil) - - if accessToken != nil { - data.AccessToken = types.StringValue(accessToken.(string)) - } - } - - if (data.ImpersonateServiceAccount.IsNull() || data.ImpersonateServiceAccount.IsUnknown()) && os.Getenv("GOOGLE_IMPERSONATE_SERVICE_ACCOUNT") != "" { - data.ImpersonateServiceAccount = types.StringValue(os.Getenv("GOOGLE_IMPERSONATE_SERVICE_ACCOUNT")) - } - - if data.Project.IsNull() || data.Project.IsUnknown() { - project := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_PROJECT", - "GOOGLE_CLOUD_PROJECT", - "GCLOUD_PROJECT", - "CLOUDSDK_CORE_PROJECT", - }, nil) - if project != nil { - data.Project = types.StringValue(project.(string)) - } - } - - if data.BillingProject.IsNull() && os.Getenv("GOOGLE_BILLING_PROJECT") != "" { - data.BillingProject = types.StringValue(os.Getenv("GOOGLE_BILLING_PROJECT")) - } - - if data.Region.IsNull() || data.Region.IsUnknown() { - region := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_REGION", - "GCLOUD_REGION", - "CLOUDSDK_COMPUTE_REGION", - }, nil) - - if region != nil { - data.Region = types.StringValue(region.(string)) - } - } - - if data.Zone.IsNull() || data.Zone.IsUnknown() { - zone := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_ZONE", - "GCLOUD_ZONE", - "CLOUDSDK_COMPUTE_ZONE", - }, nil) - - if zone != nil { - data.Zone = types.StringValue(zone.(string)) - } - } - - if len(data.Scopes.Elements()) == 0 { - var d diag.Diagnostics - data.Scopes, d = types.ListValueFrom(ctx, types.StringType, transport_tpg.DefaultClientScopes) - diags.Append(d...) - if diags.HasError() { - return - } - } - - if !data.Batching.IsNull() && !data.Batching.IsUnknown() { - var pbConfigs []fwmodels.ProviderBatching - d := data.Batching.ElementsAs(ctx, &pbConfigs, true) - diags.Append(d...) - if diags.HasError() { - return - } - - if pbConfigs[0].SendAfter.IsNull() || pbConfigs[0].SendAfter.IsUnknown() { - pbConfigs[0].SendAfter = types.StringValue("10s") - } - - if pbConfigs[0].EnableBatching.IsNull() || pbConfigs[0].EnableBatching.IsUnknown() { - pbConfigs[0].EnableBatching = types.BoolValue(true) - } - - data.Batching, d = types.ListValueFrom(ctx, types.ObjectType{}.WithAttributeTypes(fwmodels.ProviderBatchingAttributes), pbConfigs) - } - - if (data.UserProjectOverride.IsNull() || data.UserProjectOverride.IsUnknown()) && os.Getenv("USER_PROJECT_OVERRIDE") != "" { - override, err := strconv.ParseBool(os.Getenv("USER_PROJECT_OVERRIDE")) - if err != nil { - diags.AddError( - "error parsing environment variable `USER_PROJECT_OVERRIDE` into bool", err.Error()) - } - data.UserProjectOverride = types.BoolValue(override) - } - - if (data.RequestReason.IsNull() || data.RequestReason.IsUnknown()) && os.Getenv("CLOUDSDK_CORE_REQUEST_REASON") != "" { - data.RequestReason = types.StringValue(os.Getenv("CLOUDSDK_CORE_REQUEST_REASON")) - } - - if data.RequestTimeout.IsNull() || data.RequestTimeout.IsUnknown() { - data.RequestTimeout = types.StringValue("120s") - } - - // Generated Products -{{- range $product := $.Products }} - if data.{{ $product.Name }}CustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_{{ upper (underscore $product.Name) }}_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.{{ $product.Name }}BasePathKey]) - if customEndpoint != nil { - data.{{ $product.Name }}CustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } -{{- end }} - - // Handwritten Products / Versioned / Atypical Entries - if data.CloudBillingCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_CLOUD_BILLING_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths["cloud_billing_custom_endpoint"]) - if customEndpoint != nil { - data.CloudBillingCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.ComposerCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_COMPOSER_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.ComposerBasePathKey]) - if customEndpoint != nil { - data.ComposerCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.ContainerCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_CONTAINER_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.ContainerBasePathKey]) - if customEndpoint != nil { - data.ContainerCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.DataflowCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_DATAFLOW_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.DataflowBasePathKey]) - if customEndpoint != nil { - data.DataflowCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.IamCredentialsCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_IAM_CREDENTIALS_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.IamCredentialsBasePathKey]) - if customEndpoint != nil { - data.IamCredentialsCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.ResourceManagerV3CustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_RESOURCE_MANAGER_V3_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.ResourceManagerV3BasePathKey]) - if customEndpoint != nil { - data.ResourceManagerV3CustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - -{{ if ne $.TargetVersionName `ga` -}} - if data.RuntimeConfigCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_RUNTIMECONFIG_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.RuntimeConfigBasePathKey]) - if customEndpoint != nil { - data.RuntimeConfigCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } -{{- end }} - - if data.IAMCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_IAM_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.IAMBasePathKey]) - if customEndpoint != nil { - data.IAMCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.ServiceNetworkingCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_SERVICE_NETWORKING_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.ServiceNetworkingBasePathKey]) - if customEndpoint != nil { - data.ServiceNetworkingCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.TagsLocationCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_TAGS_LOCATION_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.TagsLocationBasePathKey]) - if customEndpoint != nil { - data.TagsLocationCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - // dcl - if data.ContainerAwsCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_CONTAINERAWS_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.ContainerAwsBasePathKey]) - if customEndpoint != nil { - data.ContainerAwsCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.ContainerAzureCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_CONTAINERAZURE_CUSTOM_ENDPOINT", - }, transport_tpg.DefaultBasePaths[transport_tpg.ContainerAzureBasePathKey]) - if customEndpoint != nil { - data.ContainerAzureCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - // DCL generated defaults - if data.ApikeysCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_APIKEYS_CUSTOM_ENDPOINT", - }, "") - if customEndpoint != nil { - data.ApikeysCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.AssuredWorkloadsCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_ASSURED_WORKLOADS_CUSTOM_ENDPOINT", - }, "") - if customEndpoint != nil { - data.AssuredWorkloadsCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.CloudBuildWorkerPoolCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_CLOUD_BUILD_WORKER_POOL_CUSTOM_ENDPOINT", - }, "") - if customEndpoint != nil { - data.CloudBuildWorkerPoolCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.CloudResourceManagerCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_CLOUD_RESOURCE_MANAGER_CUSTOM_ENDPOINT", - }, "") - if customEndpoint != nil { - data.CloudResourceManagerCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.DataplexCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_DATAPLEX_CUSTOM_ENDPOINT", - }, "") - if customEndpoint != nil { - data.DataplexCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.EventarcCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_EVENTARC_CUSTOM_ENDPOINT", - }, "") - if customEndpoint != nil { - data.EventarcCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.FirebaserulesCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_FIREBASERULES_CUSTOM_ENDPOINT", - }, "") - if customEndpoint != nil { - data.FirebaserulesCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.NetworkConnectivityCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_NETWORK_CONNECTIVITY_CUSTOM_ENDPOINT", - }, "") - if customEndpoint != nil { - data.NetworkConnectivityCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } - - if data.RecaptchaEnterpriseCustomEndpoint.IsNull() { - customEndpoint := transport_tpg.MultiEnvDefault([]string{ - "GOOGLE_RECAPTCHA_ENTERPRISE_CUSTOM_ENDPOINT", - }, "") - if customEndpoint != nil { - data.RecaptchaEnterpriseCustomEndpoint = types.StringValue(customEndpoint.(string)) - } - } -} - -func (p *FrameworkProviderConfig) SetupClient(ctx context.Context, data fwmodels.ProviderModel, diags *diag.Diagnostics) { - tokenSource := GetTokenSource(ctx, data, false, diags) - if diags.HasError() { - return - } - - cleanCtx := context.WithValue(ctx, oauth2.HTTPClient, cleanhttp.DefaultClient()) - - // 1. MTLS TRANSPORT/CLIENT - sets up proper auth headers - client, _, err := transport.NewHTTPClient(cleanCtx, option.WithTokenSource(tokenSource)) - if err != nil { - diags.AddError("error creating new http client", err.Error()) - return - } - - // Userinfo is fetched before request logging is enabled to reduce additional noise. - p.logGoogleIdentities(ctx, data, diags) - if diags.HasError() { - return - } - - // 2. Logging Transport - ensure we log HTTP requests to GCP APIs. - loggingTransport := logging.NewTransport("Google", client.Transport) - - // 3. Retry Transport - retries common temporary errors - // Keep order for wrapping logging so we log each retried request as well. - // This value should be used if needed to create shallow copies with additional retry predicates. - // See ClientWithAdditionalRetries - retryTransport := transport_tpg.NewTransportWithDefaultRetries(loggingTransport) - - // 4. Header Transport - outer wrapper to inject additional headers we want to apply - // before making requests - headerTransport := transport_tpg.NewTransportWithHeaders(retryTransport) - if !data.RequestReason.IsNull() { - headerTransport.Set("X-Goog-Request-Reason", data.RequestReason.ValueString()) - } - - // Ensure $userProject is set for all HTTP requests using the client if specified by the provider config - // See https://cloud.google.com/apis/docs/system-parameters - if data.UserProjectOverride.ValueBool() && !data.BillingProject.IsNull() { - headerTransport.Set("X-Goog-User-Project", data.BillingProject.ValueString()) - } - - // Set final transport value. - client.Transport = headerTransport - - // This timeout is a timeout per HTTP request, not per logical operation. - timeout, err := time.ParseDuration(data.RequestTimeout.ValueString()) - if err != nil { - diags.AddError("error parsing request timeout", err.Error()) - } - client.Timeout = timeout - - p.TokenSource = tokenSource - p.Client = client -} - -func (p *FrameworkProviderConfig) SetupGrpcLogging() { - logger := logrus.StandardLogger() - - logrus.SetLevel(logrus.DebugLevel) - logrus.SetFormatter(&transport_tpg.Formatter{ - TimestampFormat: "2006/01/02 15:04:05", - LogFormat: "%time% [%lvl%] %msg% \n", - }) - - alwaysLoggingDeciderClient := func(ctx context.Context, fullMethodName string) bool { return true } - grpc_logrus.ReplaceGrpcLogger(logrus.NewEntry(logger)) - - p.gRPCLoggingOptions = append( - p.gRPCLoggingOptions, option.WithGRPCDialOption(grpc.WithUnaryInterceptor( - grpc_logrus.PayloadUnaryClientInterceptor(logrus.NewEntry(logger), alwaysLoggingDeciderClient))), - option.WithGRPCDialOption(grpc.WithStreamInterceptor( - grpc_logrus.PayloadStreamClientInterceptor(logrus.NewEntry(logger), alwaysLoggingDeciderClient))), - ) -} - -func (p *FrameworkProviderConfig) logGoogleIdentities(ctx context.Context, data fwmodels.ProviderModel, diags *diag.Diagnostics) { - // GetCurrentUserEmailFramework doesn't pass an error back from logGoogleIdentities, so we want - // a separate diagnostics here - var d diag.Diagnostics - - if data.ImpersonateServiceAccount.IsNull() || data.ImpersonateServiceAccount.IsUnknown() { - - tokenSource := GetTokenSource(ctx, data, true, diags) - if diags.HasError() { - return - } - - p.Client = oauth2.NewClient(ctx, tokenSource) // p.Client isn't initialised fully when this code is called. - - email := GetCurrentUserEmailFramework(p, p.UserAgent, &d) - if d.HasError() { - tflog.Info(ctx, "error retrieving userinfo for your provider credentials. have you enabled the 'https://www.googleapis.com/auth/userinfo.email' scope?") - } - - tflog.Info(ctx, fmt.Sprintf("Terraform is using this identity: %s", email)) - return - } - - // Drop Impersonated ClientOption from OAuth2 TokenSource to infer original identity - tokenSource := GetTokenSource(ctx, data, true, diags) - if diags.HasError() { - return - } - - p.Client = oauth2.NewClient(ctx, tokenSource) // p.Client isn't initialised fully when this code is called. - email := GetCurrentUserEmailFramework(p, p.UserAgent, &d) - if d.HasError() { - tflog.Info(ctx, "error retrieving userinfo for your provider credentials. have you enabled the 'https://www.googleapis.com/auth/userinfo.email' scope?") - } - - tflog.Info(ctx, fmt.Sprintf("Terraform is configured with service account impersonation, original identity: %s, impersonated identity: %s", email, data.ImpersonateServiceAccount.ValueString())) - - // Add the Impersonated ClientOption back in to the OAuth2 TokenSource - tokenSource = GetTokenSource(ctx, data, false, diags) - if diags.HasError() { - return - } - - p.Client = oauth2.NewClient(ctx, tokenSource) // p.Client isn't initialised fully when this code is called. - - return -} - -// Configuration helpers - -// GetTokenSource gets token source based on the Google Credentials configured. -// If initialCredentialsOnly is true, don't follow the impersonation settings and return the initial set of creds. -func GetTokenSource(ctx context.Context, data fwmodels.ProviderModel, initialCredentialsOnly bool, diags *diag.Diagnostics) oauth2.TokenSource { - creds := GetCredentials(ctx, data, initialCredentialsOnly, diags) - - return creds.TokenSource -} - -// GetCredentials gets credentials with a given scope (clientScopes). -// If initialCredentialsOnly is true, don't follow the impersonation -// settings and return the initial set of creds instead. -func GetCredentials(ctx context.Context, data fwmodels.ProviderModel, initialCredentialsOnly bool, diags *diag.Diagnostics) googleoauth.Credentials { - var clientScopes []string - var delegates []string - - if !data.Scopes.IsNull() && !data.Scopes.IsUnknown() { - d := data.Scopes.ElementsAs(ctx, &clientScopes, false) - diags.Append(d...) - if diags.HasError() { - return googleoauth.Credentials{} - } - } - - if !data.ImpersonateServiceAccountDelegates.IsNull() && !data.ImpersonateServiceAccountDelegates.IsUnknown() { - d := data.ImpersonateServiceAccountDelegates.ElementsAs(ctx, &delegates, false) - diags.Append(d...) - if diags.HasError() { - return googleoauth.Credentials{} - } - } - - if !data.AccessToken.IsNull() && !data.AccessToken.IsUnknown() { - contents, _, err := verify.PathOrContents(data.AccessToken.ValueString()) - if err != nil { - diags.AddError("error loading access token", err.Error()) - return googleoauth.Credentials{} - } - - token := &oauth2.Token{AccessToken: contents} - if !data.ImpersonateServiceAccount.IsNull() && !initialCredentialsOnly { - opts := []option.ClientOption{option.WithTokenSource(oauth2.StaticTokenSource(token)), option.ImpersonateCredentials(data.ImpersonateServiceAccount.ValueString(), delegates...), option.WithScopes(clientScopes...)} - creds, err := transport.Creds(context.TODO(), opts...) - if err != nil { - diags.AddError("error impersonating credentials", err.Error()) - return googleoauth.Credentials{} - } - return *creds - } - - tflog.Info(ctx, "Authenticating using configured Google JSON 'access_token'...") - tflog.Info(ctx, fmt.Sprintf(" -- Scopes: %s", clientScopes)) - return googleoauth.Credentials{ - TokenSource: transport_tpg.StaticTokenSource{oauth2.StaticTokenSource(token)}, - } - } - - if !data.Credentials.IsNull() && !data.Credentials.IsUnknown() { - contents, _, err := verify.PathOrContents(data.Credentials.ValueString()) - if err != nil { - diags.AddError(fmt.Sprintf("error loading credentials: %s", err), err.Error()) - return googleoauth.Credentials{} - } - if len(contents) == 0 { - diags.AddError("error loading credentials", "provided credentials are empty") - return googleoauth.Credentials{} - } - - if !data.ImpersonateServiceAccount.IsNull() && !initialCredentialsOnly { - opts := []option.ClientOption{option.WithCredentialsJSON([]byte(contents)), option.ImpersonateCredentials(data.ImpersonateServiceAccount.ValueString(), delegates...), option.WithScopes(clientScopes...)} - creds, err := transport.Creds(context.TODO(), opts...) - if err != nil { - diags.AddError("error impersonating credentials", err.Error()) - return googleoauth.Credentials{} - } - return *creds - } - - creds, err := transport.Creds(ctx, option.WithCredentialsJSON([]byte(contents)), option.WithScopes(clientScopes...)) - if err != nil { - diags.AddError("unable to parse credentials", err.Error()) - return googleoauth.Credentials{} - } - - tflog.Info(ctx, "Authenticating using configured Google JSON 'credentials'...") - tflog.Info(ctx, fmt.Sprintf(" -- Scopes: %s", clientScopes)) - return *creds - } - - if !data.ImpersonateServiceAccount.IsNull() && !initialCredentialsOnly { - opts := option.ImpersonateCredentials(data.ImpersonateServiceAccount.ValueString(), delegates...) - creds, err := transport.Creds(context.TODO(), opts, option.WithScopes(clientScopes...)) - if err != nil { - diags.AddError("error impersonating credentials", err.Error()) - return googleoauth.Credentials{} - } - - return *creds - } - - tflog.Info(ctx, "Authenticating using DefaultClient...") - tflog.Info(ctx, fmt.Sprintf(" -- Scopes: %s", clientScopes)) - creds, err := transport.Creds(context.Background(), option.WithScopes(clientScopes...)) - if err != nil { - diags.AddError(fmt.Sprintf("Attempted to load application default credentials since neither `credentials` nor `access_token` was set in the provider block. "+ - "No credentials loaded. To use your gcloud credentials, run 'gcloud auth application-default login'"), err.Error()) - return googleoauth.Credentials{} - } - - return *creds -} - -// GetBatchingConfig returns the batching config object given the -// provider configuration set for batching -func GetBatchingConfig(ctx context.Context, data types.List, diags *diag.Diagnostics) *transport_tpg.BatchingConfig { - bc := &transport_tpg.BatchingConfig{ - SendAfter: time.Second * transport_tpg.DefaultBatchSendIntervalSec, - EnableBatching: true, - } - - // Handle if entire batching block is null/unknown - if data.IsNull() || data.IsUnknown() { - return bc - } - - var pbConfigs []fwmodels.ProviderBatching - d := data.ElementsAs(ctx, &pbConfigs, true) - diags.Append(d...) - if diags.HasError() { - return bc - } - - sendAfter, err := time.ParseDuration(pbConfigs[0].SendAfter.ValueString()) - if err != nil { - diags.AddError("error parsing send after time duration", err.Error()) - return bc - } - - bc.SendAfter = sendAfter - - if !pbConfigs[0].EnableBatching.IsNull() { - bc.EnableBatching = pbConfigs[0].EnableBatching.ValueBool() - } - - return bc -} - -func GetRegionFromRegionSelfLink(selfLink basetypes.StringValue) basetypes.StringValue { - re := regexp.MustCompile("/compute/[a-zA-Z0-9]*/projects/[a-zA-Z0-9-]*/regions/([a-zA-Z0-9-]*)") - value := selfLink.String() - switch { - case re.MatchString(value): - if res := re.FindStringSubmatch(value); len(res) == 2 && res[1] != "" { - region := res[1] - return types.StringValue(region) - } - } - return selfLink -} diff --git a/mmv1/third_party/terraform/fwtransport/framework_config_test.go.tmpl b/mmv1/third_party/terraform/fwtransport/framework_config_test.go.tmpl deleted file mode 100644 index d4e34eae78bb..000000000000 --- a/mmv1/third_party/terraform/fwtransport/framework_config_test.go.tmpl +++ /dev/null @@ -1,1585 +0,0 @@ -package fwtransport_test - -import ( - "context" - "testing" - - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-provider-google/google/acctest" - "github.com/hashicorp/terraform-provider-google/google/fwmodels" - "github.com/hashicorp/terraform-provider-google/google/fwtransport" - transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" -) - -func TestFrameworkProvider_LoadAndValidateFramework_project(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under test experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - ConfigValues fwmodels.ProviderModel - EnvVariables map[string]string - ExpectedDataModelValue basetypes.StringValue // Sometimes the value is mutated, and no longer matches the original value we supply - ExpectedConfigStructValue basetypes.StringValue // Sometimes the value in config struct differs from what is in the data model - ExpectError bool - }{ - "project value set in the provider schema is not overridden by environment variables": { - ConfigValues: fwmodels.ProviderModel{ - Project: types.StringValue("project-from-config"), - }, - EnvVariables: map[string]string{ - "GOOGLE_PROJECT": "project-from-GOOGLE_PROJECT", - "GOOGLE_CLOUD_PROJECT": "project-from-GOOGLE_CLOUD_PROJECT", - "GCLOUD_PROJECT": "project-from-GCLOUD_PROJECT", - "CLOUDSDK_CORE_PROJECT": "project-from-CLOUDSDK_CORE_PROJECT", - }, - ExpectedDataModelValue: types.StringValue("project-from-config"), - ExpectedConfigStructValue: types.StringValue("project-from-config"), - }, - "project value can be set by environment variable: GOOGLE_PROJECT is used first": { - ConfigValues: fwmodels.ProviderModel{ - Project: types.StringNull(), // unset - }, - EnvVariables: map[string]string{ - "GOOGLE_PROJECT": "project-from-GOOGLE_PROJECT", - "GOOGLE_CLOUD_PROJECT": "project-from-GOOGLE_CLOUD_PROJECT", - "GCLOUD_PROJECT": "project-from-GCLOUD_PROJECT", - "CLOUDSDK_CORE_PROJECT": "project-from-CLOUDSDK_CORE_PROJECT", - }, - ExpectedDataModelValue: types.StringValue("project-from-GOOGLE_PROJECT"), - ExpectedConfigStructValue: types.StringValue("project-from-GOOGLE_PROJECT"), - }, - "project value can be set by environment variable: GOOGLE_CLOUD_PROJECT is used second": { - ConfigValues: fwmodels.ProviderModel{ - Project: types.StringNull(), // unset - }, - EnvVariables: map[string]string{ - // GOOGLE_PROJECT unset - "GOOGLE_CLOUD_PROJECT": "project-from-GOOGLE_CLOUD_PROJECT", - "GCLOUD_PROJECT": "project-from-GCLOUD_PROJECT", - "CLOUDSDK_CORE_PROJECT": "project-from-CLOUDSDK_CORE_PROJECT", - }, - ExpectedDataModelValue: types.StringValue("project-from-GOOGLE_CLOUD_PROJECT"), - ExpectedConfigStructValue: types.StringValue("project-from-GOOGLE_CLOUD_PROJECT"), - }, - "project value can be set by environment variable: GCLOUD_PROJECT is used third": { - ConfigValues: fwmodels.ProviderModel{ - Project: types.StringNull(), // unset - }, - EnvVariables: map[string]string{ - // GOOGLE_PROJECT unset - // GOOGLE_CLOUD_PROJECT unset - "GCLOUD_PROJECT": "project-from-GCLOUD_PROJECT", - "CLOUDSDK_CORE_PROJECT": "project-from-CLOUDSDK_CORE_PROJECT", - }, - ExpectedDataModelValue: types.StringValue("project-from-GCLOUD_PROJECT"), - ExpectedConfigStructValue: types.StringValue("project-from-GCLOUD_PROJECT"), - }, - "project value can be set by environment variable: CLOUDSDK_CORE_PROJECT is used fourth": { - ConfigValues: fwmodels.ProviderModel{ - Project: types.StringNull(), // unset - }, - EnvVariables: map[string]string{ - // GOOGLE_PROJECT unset - // GOOGLE_CLOUD_PROJECT unset - // GCLOUD_PROJECT unset - "CLOUDSDK_CORE_PROJECT": "project-from-CLOUDSDK_CORE_PROJECT", - }, - ExpectedDataModelValue: types.StringValue("project-from-CLOUDSDK_CORE_PROJECT"), - ExpectedConfigStructValue: types.StringValue("project-from-CLOUDSDK_CORE_PROJECT"), - }, - "when no project values are provided via config or environment variables, the field remains unset without error": { - ConfigValues: fwmodels.ProviderModel{ - Project: types.StringNull(), // unset - }, - ExpectedDataModelValue: types.StringNull(), - ExpectedConfigStructValue: types.StringNull(), - }, - // Handling empty strings in config - "when project is set as an empty string the empty string is used and not ignored": { - ConfigValues: fwmodels.ProviderModel{ - Project: types.StringValue(""), - }, - ExpectedDataModelValue: types.StringValue(""), - ExpectedConfigStructValue: types.StringValue(""), - }, - "when project is set as an empty string, the empty string is not ignored in favor of an environment variable": { - ConfigValues: fwmodels.ProviderModel{ - Project: types.StringValue(""), - }, - EnvVariables: map[string]string{ - "GOOGLE_PROJECT": "project-from-GOOGLE_PROJECT", - }, - ExpectedDataModelValue: types.StringValue(""), - ExpectedConfigStructValue: types.StringValue(""), - }, - // Handling unknown values - "when project is an unknown value, the provider treats it as if it's unset and uses an environment variable instead": { - ConfigValues: fwmodels.ProviderModel{ - Project: types.StringUnknown(), - }, - EnvVariables: map[string]string{ - "GOOGLE_PROJECT": "project-from-GOOGLE_PROJECT", - }, - ExpectedDataModelValue: types.StringValue("project-from-GOOGLE_PROJECT"), - ExpectedConfigStructValue: types.StringValue("project-from-GOOGLE_PROJECT"), - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := tc.ConfigValues - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - if !data.Project.Equal(tc.ExpectedDataModelValue) { - t.Fatalf("want project in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectedDataModelValue, data.Project.String()) - } - // Checking the value passed to the config structs - if !p.Project.Equal(tc.ExpectedConfigStructValue) { - t.Fatalf("want project in the `FrameworkProviderConfig` struct to be `%s`, but got the value `%s`", tc.ExpectedConfigStructValue, p.Project.String()) - } - }) - } -} - -// NOTE: these tests can't run in Cloud Build due to ADC locating credentials despite `GOOGLE_APPLICATION_CREDENTIALS` being unset -// See https://cloud.google.com/docs/authentication/application-default-credentials#search_order -// Also, when running these tests locally you need to run `gcloud auth application-default revoke` to ensure your machine isn't supplying ADCs -// func TestFrameworkProvider_LoadAndValidateFramework_credentials_unknown(t *testing.T) { -// // This test case is kept separate from other credentials tests, as it requires comparing -// // error messages returned by two different error states: -// // - When credentials = Null -// // - When credentials = Unknown - -// t.Run("the same error is returned whether credentials is set as a null or unknown value (and access_token isn't set)", func(t *testing.T) { -// // Arrange -// acctest.UnsetTestProviderConfigEnvs(t) - -// ctx := context.Background() -// tfVersion := "foobar" -// providerversion := "999" - -// impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - -// // Null data and error collection -// diagsNull := diag.Diagnostics{} -// dataNull := fwmodels.ProviderModel{ -// Credentials: types.StringNull(), -// } -// dataNull.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - -// // Unknown data and error collection -// diagsUnknown := diag.Diagnostics{} -// dataUnknown := fwmodels.ProviderModel{ -// Credentials: types.StringUnknown(), -// } -// dataUnknown.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - -// pNull := fwtransport.FrameworkProviderConfig{} -// pUnknown := fwtransport.FrameworkProviderConfig{} - -// // Act -// pNull.LoadAndValidateFramework(ctx, &dataNull, tfVersion, &diagsNull, providerversion) -// pUnknown.LoadAndValidateFramework(ctx, &dataUnknown, tfVersion, &diagsUnknown, providerversion) - -// // Assert -// if !diagsNull.HasError() { -// t.Fatalf("expect errors when credentials is null, but [%d] errors occurred", diagsNull.ErrorsCount()) -// } -// if !diagsUnknown.HasError() { -// t.Fatalf("expect errors when credentials is unknown, but [%d] errors occurred", diagsUnknown.ErrorsCount()) -// } - -// errNull := diagsNull.Errors() -// errUnknown := diagsUnknown.Errors() -// for i := 0; i < len(errNull); i++ { -// if errNull[i] != errUnknown[i] { -// t.Fatalf("expect errors to be the same for null and unknown credentials values, instead got \nnull=`%s` \nunknown=%s", errNull[i], errUnknown[i]) -// } -// } -// }) -// } - -func TestFrameworkProvider_LoadAndValidateFramework_billingProject(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under test experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - ConfigValues fwmodels.ProviderModel - EnvVariables map[string]string - ExpectedDataModelValue basetypes.StringValue - ExpectedConfigStructValue basetypes.StringValue - ExpectError bool - }{ - "billing_project value set in the provider schema is not overridden by environment variables": { - ConfigValues: fwmodels.ProviderModel{ - BillingProject: types.StringValue("billing-project-from-config"), - }, - EnvVariables: map[string]string{ - "GOOGLE_BILLING_PROJECT": "billing-project-from-env", - }, - ExpectedDataModelValue: types.StringValue("billing-project-from-config"), - ExpectedConfigStructValue: types.StringValue("billing-project-from-config"), - }, - "billing_project can be set by environment variable, when no value supplied via the config": { - ConfigValues: fwmodels.ProviderModel{ - BillingProject: types.StringNull(), - }, - EnvVariables: map[string]string{ - "GOOGLE_BILLING_PROJECT": "billing-project-from-env", - }, - ExpectedDataModelValue: types.StringValue("billing-project-from-env"), - ExpectedConfigStructValue: types.StringValue("billing-project-from-env"), - }, - "when no billing_project values are provided via config or environment variables, the field remains unset without error": { - ConfigValues: fwmodels.ProviderModel{ - BillingProject: types.StringNull(), - }, - ExpectedDataModelValue: types.StringNull(), - ExpectedConfigStructValue: types.StringNull(), - }, - // Handling empty strings in config - "when billing_project is set as an empty string the empty string is used and not ignored": { - ConfigValues: fwmodels.ProviderModel{ - BillingProject: types.StringValue(""), - }, - ExpectedDataModelValue: types.StringValue(""), - ExpectedConfigStructValue: types.StringValue(""), - }, - "when billing_project is set as an empty string, the empty string is not ignored in favor of an environment variable": { - ConfigValues: fwmodels.ProviderModel{ - BillingProject: types.StringValue(""), - }, - EnvVariables: map[string]string{ - "GOOGLE_BILLING_PROJECT": "billing-project-from-env", - }, - ExpectedDataModelValue: types.StringValue(""), - ExpectedConfigStructValue: types.StringValue(""), - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - data := tc.ConfigValues - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - if !data.BillingProject.Equal(tc.ExpectedDataModelValue) { - t.Fatalf("want billing_project in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectedDataModelValue, data.BillingProject.String()) - } - // Checking the value passed to the config structs - if !p.BillingProject.Equal(tc.ExpectedConfigStructValue) { - t.Fatalf("want billing_project in the `FrameworkProviderConfig` struct to be `%s`, but got the value `%s`", tc.ExpectedConfigStructValue, p.BillingProject.String()) - } - }) - } -} - -func TestFrameworkProvider_LoadAndValidateFramework_region(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under test experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - ConfigValues fwmodels.ProviderModel - EnvVariables map[string]string - ExpectedDataModelValue basetypes.StringValue - ExpectedConfigStructValue basetypes.StringValue - ExpectError bool - }{ - "region value set in the provider config is not overridden by ENVs": { - ConfigValues: fwmodels.ProviderModel{ - Region: types.StringValue("region-from-config"), - }, - EnvVariables: map[string]string{ - "GOOGLE_REGION": "region-from-env", - }, - ExpectedDataModelValue: types.StringValue("region-from-config"), - ExpectedConfigStructValue: types.StringValue("region-from-config"), - }, - "region values can be supplied as a self link": { - ConfigValues: fwmodels.ProviderModel{ - Region: types.StringValue("https://www.googleapis.com/compute/v1/projects/my-project/regions/us-central1"), - }, - ExpectedDataModelValue: types.StringValue("https://www.googleapis.com/compute/v1/projects/my-project/regions/us-central1"), - ExpectedConfigStructValue: types.StringValue("us-central1"), - }, - "region value can be set by environment variable: GOOGLE_REGION is used": { - ConfigValues: fwmodels.ProviderModel{ - Region: types.StringNull(), - }, - EnvVariables: map[string]string{ - "GOOGLE_REGION": "region-from-env", - }, - ExpectedDataModelValue: types.StringValue("region-from-env"), - ExpectedConfigStructValue: types.StringValue("region-from-env"), - }, - "when no region values are provided via config or environment variables, the field remains unset without error": { - ConfigValues: fwmodels.ProviderModel{ - Region: types.StringNull(), - }, - ExpectedDataModelValue: types.StringNull(), - ExpectedConfigStructValue: types.StringNull(), - }, - // Handling empty strings in config - "when region is set as an empty string the empty string is used and not ignored": { - ConfigValues: fwmodels.ProviderModel{ - Region: types.StringValue(""), - }, - ExpectedDataModelValue: types.StringValue(""), - ExpectedConfigStructValue: types.StringValue(""), - }, - "when region is set as an empty string, the empty string is not ignored in favor of an environment variable": { - ConfigValues: fwmodels.ProviderModel{ - Region: types.StringValue(""), - }, - EnvVariables: map[string]string{ - "GOOGLE_REGION": "region-from-env", - }, - ExpectedDataModelValue: types.StringValue(""), - ExpectedConfigStructValue: types.StringValue(""), - }, - // Handling unknown values - "when region is an unknown value, the provider treats it as if it's unset and uses an environment variable instead": { - ConfigValues: fwmodels.ProviderModel{ - Region: types.StringUnknown(), - }, - EnvVariables: map[string]string{ - "GOOGLE_REGION": "region-from-env", - }, - ExpectedDataModelValue: types.StringValue("region-from-env"), - ExpectedConfigStructValue: types.StringValue("region-from-env"), - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := tc.ConfigValues - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - if !data.Region.Equal(tc.ExpectedDataModelValue) { - t.Fatalf("want region in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectedDataModelValue, data.Region.String()) - } - // Checking the value passed to the config structs - if !p.Region.Equal(tc.ExpectedConfigStructValue) { - t.Fatalf("want region in the `FrameworkProviderConfig` struct to be `%s`, but got the value `%s`", tc.ExpectedConfigStructValue, p.Region.String()) - } - }) - } -} - -func TestFrameworkProvider_LoadAndValidateFramework_zone(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under test experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - ConfigValues fwmodels.ProviderModel - EnvVariables map[string]string - ExpectedDataModelValue basetypes.StringValue - ExpectedConfigStructValue basetypes.StringValue - ExpectError bool - }{ - "zone value set in the provider config is not overridden by ENVs": { - ConfigValues: fwmodels.ProviderModel{ - Zone: types.StringValue("zone-from-config"), - }, - EnvVariables: map[string]string{ - "GOOGLE_ZONE": "zone-from-env", - }, - ExpectedDataModelValue: types.StringValue("zone-from-config"), - ExpectedConfigStructValue: types.StringValue("zone-from-config"), - }, - "does not shorten zone values when provided as a self link": { - ConfigValues: fwmodels.ProviderModel{ - Zone: types.StringValue("https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1"), - }, - ExpectedDataModelValue: types.StringValue("https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1"), - ExpectedConfigStructValue: types.StringValue("https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1"), // Value is not shortened from URI to name - }, - "when multiple zone environment variables are provided, `GOOGLE_ZONE` is used first": { - ConfigValues: fwmodels.ProviderModel{ - Zone: types.StringNull(), - }, - EnvVariables: map[string]string{ - "GOOGLE_ZONE": "zone-from-GOOGLE_ZONE", - "GCLOUD_ZONE": "zone-from-GCLOUD_ZONE", - "CLOUDSDK_COMPUTE_ZONE": "zone-from-CLOUDSDK_COMPUTE_ZONE", - }, - ExpectedDataModelValue: types.StringValue("zone-from-GOOGLE_ZONE"), - ExpectedConfigStructValue: types.StringValue("zone-from-GOOGLE_ZONE"), - }, - "when multiple zone environment variables are provided, `GCLOUD_ZONE` is used second": { - ConfigValues: fwmodels.ProviderModel{ - Zone: types.StringNull(), - }, - EnvVariables: map[string]string{ - // GOOGLE_ZONE unset - "GCLOUD_ZONE": "zone-from-GCLOUD_ZONE", - "CLOUDSDK_COMPUTE_ZONE": "zone-from-CLOUDSDK_COMPUTE_ZONE", - }, - ExpectedDataModelValue: types.StringValue("zone-from-GCLOUD_ZONE"), - ExpectedConfigStructValue: types.StringValue("zone-from-GCLOUD_ZONE"), - }, - "when multiple zone environment variables are provided, `CLOUDSDK_COMPUTE_ZONE` is used third": { - ConfigValues: fwmodels.ProviderModel{ - Zone: types.StringNull(), - }, - EnvVariables: map[string]string{ - // GOOGLE_ZONE unset - // GCLOUD_ZONE unset - "CLOUDSDK_COMPUTE_ZONE": "zone-from-CLOUDSDK_COMPUTE_ZONE", - }, - ExpectedDataModelValue: types.StringValue("zone-from-CLOUDSDK_COMPUTE_ZONE"), - ExpectedConfigStructValue: types.StringValue("zone-from-CLOUDSDK_COMPUTE_ZONE"), - }, - "when no zone values are provided via config or environment variables, the field remains unset without error": { - ConfigValues: fwmodels.ProviderModel{ - Zone: types.StringNull(), - }, - ExpectedDataModelValue: types.StringNull(), - ExpectedConfigStructValue: types.StringNull(), - }, - // Handling empty strings in config - "when zone is set as an empty string the empty string is used and not ignored": { - ConfigValues: fwmodels.ProviderModel{ - Zone: types.StringValue(""), - }, - ExpectedDataModelValue: types.StringValue(""), - ExpectedConfigStructValue: types.StringValue(""), - }, - "when zone is set as an empty string, the empty string is not ignored in favor of an environment variable": { - ConfigValues: fwmodels.ProviderModel{ - Zone: types.StringValue(""), - }, - EnvVariables: map[string]string{ - "GOOGLE_ZONE": "zone-from-env", - }, - ExpectedDataModelValue: types.StringValue(""), - ExpectedConfigStructValue: types.StringValue(""), - }, - // Handling unknown values - "when zone is an unknown value, the provider treats it as if it's unset and uses an environment variable instead": { - ConfigValues: fwmodels.ProviderModel{ - Zone: types.StringUnknown(), - }, - EnvVariables: map[string]string{ - "GOOGLE_ZONE": "zone-from-env", - }, - ExpectedDataModelValue: types.StringValue("zone-from-env"), - ExpectedConfigStructValue: types.StringValue("zone-from-env"), - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := tc.ConfigValues - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - if !data.Zone.Equal(tc.ExpectedDataModelValue) { - t.Fatalf("want zone in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectedDataModelValue, data.Zone.String()) - } - // Checking the value passed to the config structs - if !p.Zone.Equal(tc.ExpectedConfigStructValue) { - t.Fatalf("want zone in the `FrameworkProviderConfig` struct to be `%s`, but got the value `%s`", tc.ExpectedConfigStructValue, p.Zone.String()) - } - }) - } -} - -func TestFrameworkProvider_LoadAndValidateFramework_accessToken(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under tests experiencing errors, and could be addressed in future refactoring. - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - ConfigValues fwmodels.ProviderModel - EnvVariables map[string]string - ExpectedDataModelValue basetypes.StringValue // Sometimes the value is mutated, and no longer matches the original value we supply - // ExpectedConfigStructValue not used here, as credentials info isn't stored in the config struct - ExpectError bool - }{ - "access_token configured in the provider can be invalid without resulting in errors": { - ConfigValues: fwmodels.ProviderModel{ - AccessToken: types.StringValue("This is not a valid token string"), - }, - ExpectedDataModelValue: types.StringValue("This is not a valid token string"), - }, - "access_token set in the provider config is not overridden by environment variables": { - ConfigValues: fwmodels.ProviderModel{ - AccessToken: types.StringValue("value-from-config"), - }, - EnvVariables: map[string]string{ - "GOOGLE_OAUTH_ACCESS_TOKEN": "value-from-env", - }, - ExpectedDataModelValue: types.StringValue("value-from-config"), - }, - "when access_token is unset in the config, the GOOGLE_OAUTH_ACCESS_TOKEN environment variable is used": { - EnvVariables: map[string]string{ - "GOOGLE_OAUTH_ACCESS_TOKEN": "value-from-GOOGLE_OAUTH_ACCESS_TOKEN", - }, - ExpectedDataModelValue: types.StringValue("value-from-GOOGLE_OAUTH_ACCESS_TOKEN"), - }, - "when no access_token values are provided via config or environment variables there's no error (as long as credentials supplied in its absence)": { - ConfigValues: fwmodels.ProviderModel{ - AccessToken: types.StringNull(), - Credentials: types.StringValue(transport_tpg.TestFakeCredentialsPath), - }, - ExpectedDataModelValue: types.StringNull(), - }, - // Handling empty strings in config - "when access_token is set as an empty string the empty string is used and not ignored": { - ConfigValues: fwmodels.ProviderModel{ - AccessToken: types.StringValue(""), - }, - ExpectedDataModelValue: types.StringValue(""), - }, - "when access_token is set as an empty string, the empty string is not ignored in favor of an environment variable": { - ConfigValues: fwmodels.ProviderModel{ - AccessToken: types.StringValue(""), - }, - EnvVariables: map[string]string{ - "GOOGLE_OAUTH_ACCESS_TOKEN": "value-from-GOOGLE_OAUTH_ACCESS_TOKEN", - }, - ExpectedDataModelValue: types.StringValue(""), - }, - // Handling unknown values - "when access_token is an unknown value, the provider treats it as if it's unset and uses an environment variable instead": { - ConfigValues: fwmodels.ProviderModel{ - AccessToken: types.StringUnknown(), - }, - EnvVariables: map[string]string{ - "GOOGLE_OAUTH_ACCESS_TOKEN": "value-from-GOOGLE_OAUTH_ACCESS_TOKEN", - }, - ExpectedDataModelValue: types.StringValue("value-from-GOOGLE_OAUTH_ACCESS_TOKEN"), - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := tc.ConfigValues - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - if !data.AccessToken.Equal(tc.ExpectedDataModelValue) { - t.Fatalf("want project in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectedDataModelValue, data.AccessToken.String()) - } - // fwtransport.FrameworkProviderConfig does not store the credentials info, so test does not make assertions on config struct - }) - } -} - -func TestFrameworkProvider_LoadAndValidateFramework_userProjectOverride(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under tests experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - ConfigValues fwmodels.ProviderModel - EnvVariables map[string]string - ExpectedDataModelValue basetypes.BoolValue - ExpectedConfigStructValue basetypes.BoolValue - ExpectError bool - }{ - "user_project_override value set in the provider schema is not overridden by ENVs": { - ConfigValues: fwmodels.ProviderModel{ - UserProjectOverride: types.BoolValue(false), - }, - EnvVariables: map[string]string{ - "USER_PROJECT_OVERRIDE": "true", - }, - ExpectedDataModelValue: types.BoolValue(false), - ExpectedConfigStructValue: types.BoolValue(false), - }, - "user_project_override can be set by environment variable: value = true": { - ConfigValues: fwmodels.ProviderModel{ - UserProjectOverride: types.BoolNull(), // not set - }, - EnvVariables: map[string]string{ - "USER_PROJECT_OVERRIDE": "true", - }, - ExpectedDataModelValue: types.BoolValue(true), - ExpectedConfigStructValue: types.BoolValue(true), - }, - "user_project_override can be set by environment variable: value = false": { - ConfigValues: fwmodels.ProviderModel{ - UserProjectOverride: types.BoolNull(), // not set - }, - EnvVariables: map[string]string{ - "USER_PROJECT_OVERRIDE": "false", - }, - ExpectedDataModelValue: types.BoolValue(false), - ExpectedConfigStructValue: types.BoolValue(false), - }, - "user_project_override can be set by environment variable: value = 1": { - ConfigValues: fwmodels.ProviderModel{ - UserProjectOverride: types.BoolNull(), // not set - }, - EnvVariables: map[string]string{ - "USER_PROJECT_OVERRIDE": "1", - }, - ExpectedDataModelValue: types.BoolValue(true), - ExpectedConfigStructValue: types.BoolValue(true), - }, - "user_project_override can be set by environment variable: value = 0": { - ConfigValues: fwmodels.ProviderModel{ - UserProjectOverride: types.BoolNull(), // not set - }, - EnvVariables: map[string]string{ - "USER_PROJECT_OVERRIDE": "0", - }, - ExpectedDataModelValue: types.BoolValue(false), - ExpectedConfigStructValue: types.BoolValue(false), - }, - "setting user_project_override using a non-boolean environment variables results in an error": { - EnvVariables: map[string]string{ - "USER_PROJECT_OVERRIDE": "I'm not a boolean", - }, - ExpectError: true, - }, - "when no user_project_override values are provided via config or environment variables, the field remains unset without error": { - ConfigValues: fwmodels.ProviderModel{ - UserProjectOverride: types.BoolNull(), // not set - }, - ExpectedDataModelValue: types.BoolNull(), - ExpectedConfigStructValue: types.BoolNull(), - }, - // Handling unknown values - "when user_project_override is an unknown value, the provider treats it as if it's unset and uses an environment variable instead": { - ConfigValues: fwmodels.ProviderModel{ - UserProjectOverride: types.BoolUnknown(), - }, - EnvVariables: map[string]string{ - "USER_PROJECT_OVERRIDE": "true", - }, - ExpectedDataModelValue: types.BoolValue(true), - ExpectedConfigStructValue: types.BoolValue(true), - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := tc.ConfigValues - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - if !data.UserProjectOverride.Equal(tc.ExpectedDataModelValue) { - t.Fatalf("want user_project_override in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectedDataModelValue, data.UserProjectOverride.String()) - } - // Checking the value passed to the config structs - if !p.UserProjectOverride.Equal(tc.ExpectedConfigStructValue) { - t.Fatalf("want user_project_override in the `FrameworkProviderConfig` struct to be `%s`, but got the value `%s`", tc.ExpectedConfigStructValue, p.UserProjectOverride.String()) - } - }) - } -} - -func TestFrameworkProvider_LoadAndValidateFramework_impersonateServiceAccount(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under tests experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - ConfigValues fwmodels.ProviderModel - EnvVariables map[string]string - ExpectedDataModelValue basetypes.StringValue - ExpectedConfigStructValue basetypes.StringValue - ExpectError bool - }{ - "impersonate_service_account value set in the provider schema is not overridden by environment variables": { - ConfigValues: fwmodels.ProviderModel{ - ImpersonateServiceAccount: types.StringValue("value-from-config@example.com"), - }, - EnvVariables: map[string]string{ - "GOOGLE_IMPERSONATE_SERVICE_ACCOUNT": "value-from-env@example.com", - }, - ExpectedDataModelValue: types.StringValue("value-from-config@example.com"), - }, - "impersonate_service_account value can be set by environment variable": { - ConfigValues: fwmodels.ProviderModel{ - ImpersonateServiceAccount: types.StringNull(), // not set - }, - EnvVariables: map[string]string{ - "GOOGLE_IMPERSONATE_SERVICE_ACCOUNT": "value-from-env@example.com", - }, - ExpectedDataModelValue: types.StringValue("value-from-env@example.com"), - }, - "when no values are provided via config or environment variables, the field remains unset without error": { - ConfigValues: fwmodels.ProviderModel{ - ImpersonateServiceAccount: types.StringNull(), // not set - }, - ExpectedDataModelValue: types.StringNull(), - }, - // Handling empty strings in config - "when impersonate_service_account is set as an empty string the empty string is used and not ignored": { - ConfigValues: fwmodels.ProviderModel{ - ImpersonateServiceAccount: types.StringValue(""), - }, - ExpectedDataModelValue: types.StringValue(""), - }, - "when impersonate_service_account is set as an empty string, the empty string is not ignored in favor of an environment variable": { - ConfigValues: fwmodels.ProviderModel{ - ImpersonateServiceAccount: types.StringValue(""), - }, - EnvVariables: map[string]string{ - "GOOGLE_IMPERSONATE_SERVICE_ACCOUNT": "value-from-env@example.com", - }, - ExpectedDataModelValue: types.StringValue(""), - }, - // Handling unknown values - "when impersonate_service_account is an unknown value, the provider treats it as if it's unset and uses an environment variable instead": { - ConfigValues: fwmodels.ProviderModel{ - ImpersonateServiceAccount: types.StringUnknown(), - }, - EnvVariables: map[string]string{ - "GOOGLE_IMPERSONATE_SERVICE_ACCOUNT": "value-from-env@example.com", - }, - ExpectedDataModelValue: types.StringValue("value-from-env@example.com"), - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := tc.ConfigValues - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - if !data.ImpersonateServiceAccount.Equal(tc.ExpectedDataModelValue) { - t.Fatalf("want impersonate_service_account in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectedDataModelValue, data.ImpersonateServiceAccount.String()) - } - // fwtransport.FrameworkProviderConfig does not store impersonate_service_account info, so test does not make assertions on config struct - }) - } -} - -func TestFrameworkProvider_LoadAndValidateFramework_impersonateServiceAccountDelegates(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under tests experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - - cases := map[string]struct { - // It's not easy to define basetypes.ListValue values directly in test case, so instead - // pass values into test function to control construction of basetypes.ListValue there. - SetAsNull bool - SetAsUnknown bool - ImpersonateServiceAccountDelegatesValue []string - EnvVariables map[string]string - - ExpectedNull bool - ExpectedUnknown bool - ExpectedDataModelValue []string - ExpectError bool - }{ - "impersonate_service_account_delegates value can be set in the provider schema": { - ImpersonateServiceAccountDelegatesValue: []string{ - "projects/-/serviceAccounts/my-service-account-1@example.iam.gserviceaccount.com", - "projects/-/serviceAccounts/my-service-account-2@example.iam.gserviceaccount.com", - }, - ExpectedDataModelValue: []string{ - "projects/-/serviceAccounts/my-service-account-1@example.iam.gserviceaccount.com", - "projects/-/serviceAccounts/my-service-account-2@example.iam.gserviceaccount.com", - }, - }, - // Note: no environment variables can be used for impersonate_service_account_delegates - "when no impersonate_service_account_delegates value is provided via config, the field remains unset without error": { - SetAsNull: true, // not setting impersonate_service_account_delegates - ExpectedNull: true, - }, - // Handling empty values in config - "when impersonate_service_account_delegates is set as an empty array, that value isn't ignored": { - ImpersonateServiceAccountDelegatesValue: []string{}, - ExpectedDataModelValue: []string{}, - }, - // Handling unknown values - "when impersonate_service_account_delegates is an unknown value, the provider treats it as if it's unset, without error": { - SetAsUnknown: true, - ExpectedUnknown: true, - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := fwmodels.ProviderModel{} - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - // Set ImpersonateServiceAccountDelegates depending on test case - if !tc.SetAsNull && !tc.SetAsUnknown { - isad, _ := types.ListValueFrom(ctx, types.StringType, tc.ImpersonateServiceAccountDelegatesValue) - data.ImpersonateServiceAccountDelegates = isad - } - if tc.SetAsNull { - data.ImpersonateServiceAccountDelegates = types.ListNull(types.StringType) - } - if tc.SetAsUnknown { - data.ImpersonateServiceAccountDelegates = types.ListUnknown(types.StringType) - } - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - var expected attr.Value - if !tc.ExpectedNull && !tc.ExpectedUnknown { - expected, _ = types.ListValueFrom(ctx, types.StringType, tc.ExpectedDataModelValue) - } - if tc.ExpectedNull { - expected = types.ListNull(types.StringType) - } - if tc.ExpectedUnknown { - expected = types.ListUnknown(types.StringType) - } - if !data.ImpersonateServiceAccountDelegates.Equal(expected) { - t.Fatalf("want impersonate_service_account in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", expected, data.ImpersonateServiceAccountDelegates.String()) - } - // fwtransport.FrameworkProviderConfig does not store impersonate_service_account info, so test does not make assertions on config struct - }) - } -} - -func TestFrameworkProvider_LoadAndValidateFramework_scopes(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under tests experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - ScopesValue []string - EnvVariables map[string]string - ExpectedDataModelValue []string - ExpectedConfigStructValue []string - SetAsNull bool - SetAsUnknown bool - ExpectError bool - }{ - "scopes are set in the provider config as a list": { - ScopesValue: []string{"fizz", "buzz", "baz"}, - ExpectedDataModelValue: []string{"fizz", "buzz", "baz"}, - ExpectedConfigStructValue: []string{"fizz", "buzz", "baz"}, - }, - "scopes can be left unset in the provider config without any issues, and a default value is used": { - SetAsNull: true, - ExpectedDataModelValue: transport_tpg.DefaultClientScopes, - ExpectedConfigStructValue: transport_tpg.DefaultClientScopes, - }, - // Handling empty values in config - "scopes set as an empty list the field is treated as if it's unset and a default value is used without errors": { - ScopesValue: []string{}, - ExpectedDataModelValue: transport_tpg.DefaultClientScopes, - ExpectedConfigStructValue: transport_tpg.DefaultClientScopes, - }, - // Handling unknown values - "when scopes is an unknown value, the provider treats it as if it's unset and a default value is used without errors": { - SetAsUnknown: true, - ExpectedDataModelValue: transport_tpg.DefaultClientScopes, - ExpectedConfigStructValue: transport_tpg.DefaultClientScopes, - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := fwmodels.ProviderModel{} - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - // Set ImpersonateServiceAccountDelegates depending on test case - if !tc.SetAsNull && !tc.SetAsUnknown { - s, _ := types.ListValueFrom(ctx, types.StringType, tc.ScopesValue) - data.Scopes = s - } - if tc.SetAsNull { - data.Scopes = types.ListNull(types.StringType) - } - if tc.SetAsUnknown { - data.Scopes = types.ListUnknown(types.StringType) - } - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - expectedDm, _ := types.ListValueFrom(ctx, types.StringType, tc.ExpectedDataModelValue) - if !data.Scopes.Equal(expectedDm) { - t.Fatalf("want project in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectedDataModelValue, data.Scopes.String()) - } - // Checking the value passed to the config structs - expectedFpc, _ := types.ListValueFrom(ctx, types.StringType, tc.ExpectedConfigStructValue) - if !p.Scopes.Equal(expectedFpc) { - t.Fatalf("want project in the `FrameworkProviderConfig` struct to be `%s`, but got the value `%s`", tc.ExpectedConfigStructValue, p.Scopes.String()) - } - }) - } -} - -func TestFrameworkProvider_LoadAndValidateFramework_requestReason(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under tests experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - ConfigValues fwmodels.ProviderModel - EnvVariables map[string]string - ExpectedDataModelValue basetypes.StringValue - // ExpectedConfigStructValue not used here, as credentials info isn't stored in the config struct - ExpectError bool - }{ - "when request_reason is unset in the config, environment variable CLOUDSDK_CORE_REQUEST_REASON is used": { - ConfigValues: fwmodels.ProviderModel{ - RequestReason: types.StringNull(), - }, - EnvVariables: map[string]string{ - "CLOUDSDK_CORE_REQUEST_REASON": "foo", - }, - ExpectedDataModelValue: types.StringValue("foo"), - }, - "request_reason set in the config is not overridden by environment variables": { - ConfigValues: fwmodels.ProviderModel{ - RequestReason: types.StringValue("value-from-config"), - }, - EnvVariables: map[string]string{ - "CLOUDSDK_CORE_REQUEST_REASON": "value-from-env", - }, - ExpectedDataModelValue: types.StringValue("value-from-config"), - }, - "when no request_reason is provided via config or environment variables, the field remains unset without error": { - ConfigValues: fwmodels.ProviderModel{ - RequestReason: types.StringNull(), - }, - ExpectedDataModelValue: types.StringNull(), - }, - // Handling empty strings in config - "when request_reason is set as an empty string, the empty string is not ignored in favor of an environment variable": { - ConfigValues: fwmodels.ProviderModel{ - RequestReason: types.StringValue(""), - }, - EnvVariables: map[string]string{ - "CLOUDSDK_CORE_REQUEST_REASON": "foo", - }, - ExpectedDataModelValue: types.StringValue(""), - }, - "when request_reason is set as an empty string the empty string is used and not ignored": { - ConfigValues: fwmodels.ProviderModel{ - RequestReason: types.StringValue(""), - }, - ExpectedDataModelValue: types.StringValue(""), - }, - // Handling unknown values - "when request_reason is an unknown value, the provider treats it as if it's unset and uses an environment variable instead": { - ConfigValues: fwmodels.ProviderModel{ - RequestReason: types.StringUnknown(), - }, - EnvVariables: map[string]string{ - "CLOUDSDK_CORE_REQUEST_REASON": "foo", - }, - ExpectedDataModelValue: types.StringValue("foo"), - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := tc.ConfigValues - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - if !data.RequestReason.Equal(tc.ExpectedDataModelValue) { - t.Fatalf("want request_reason in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectedDataModelValue, data.RequestReason.String()) - } - // fwtransport.FrameworkProviderConfig does not store the request reason info, so test does not make assertions on config struct - }) - } -} - -func TestFrameworkProvider_LoadAndValidateFramework_requestTimeout(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under tests experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - ConfigValues fwmodels.ProviderModel - EnvVariables map[string]string - ExpectedDataModelValue basetypes.StringValue - // ExpectedConfigStructValue not used here, as credentials info isn't stored in the config struct - ExpectError bool - }{ - "if a valid request_timeout is configured in the provider, no error will occur": { - ConfigValues: fwmodels.ProviderModel{ - RequestTimeout: types.StringValue("10s"), - }, - ExpectedDataModelValue: types.StringValue("10s"), - }, - "if an invalid request_timeout is configured in the provider, an error will occur": { - ConfigValues: fwmodels.ProviderModel{ - RequestTimeout: types.StringValue("timeout"), - }, - ExpectError: true, - }, - "when request_timeout is set as an empty string, the empty string isn't ignored and an error will occur": { - ConfigValues: fwmodels.ProviderModel{ - RequestTimeout: types.StringValue(""), - }, - ExpectError: true, - }, - // In the SDK version of the provider config code, this scenario results in a value of "0s" - // instead of "120s", but the final 'effective' value is also "120s" - // See : https://github.com/hashicorp/terraform-provider-google/blob/09cb850ee64bcd78e4457df70905530c1ed75f19/google/transport/config.go#L1228-L1233 - "when request_timeout is unset in the config, the default value is 120s.": { - ConfigValues: fwmodels.ProviderModel{ - RequestTimeout: types.StringNull(), - }, - ExpectedDataModelValue: types.StringValue("120s"), - }, - // Handling unknown values - "when request_timeout is an unknown value, the provider treats it as if it's unset and uses the default value 120s": { - ConfigValues: fwmodels.ProviderModel{ - RequestTimeout: types.StringUnknown(), - }, - ExpectedDataModelValue: types.StringValue("120s"), - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := tc.ConfigValues - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s : %s", num, err.Summary(), err.Detail()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - if !data.RequestTimeout.Equal(tc.ExpectedDataModelValue) { - t.Fatalf("want request_timeout in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectedDataModelValue, data.RequestTimeout.String()) - } - // fwtransport.FrameworkProviderConfig does not store the request timeout info, so test does not make assertions on config struct - }) - } -} - -func TestFrameworkProvider_LoadAndValidateFramework_batching(t *testing.T) { - - // Note: In the test function we need to set the below fields in test case's fwmodels.ProviderModel value - // this is to stop the code under tests experiencing errors, and could be addressed in future refactoring. - // - Credentials: If we don't set this then the test looks for application default credentials and can fail depending on the machine running the test - // - ImpersonateServiceAccountDelegates: If we don't set this, we get a nil pointer exception ¯\_(ツ)_/¯ - - cases := map[string]struct { - // It's not easy to create the value of Batching in the test case, so these inputs are used in the test function - SetBatchingAsNull bool - SetBatchingAsUnknown bool - EnableBatchingValue basetypes.BoolValue - SendAfterValue basetypes.StringValue - - EnvVariables map[string]string - - ExpectBatchingNull bool - ExpectBatchingUnknown bool - ExpectEnableBatchingValue basetypes.BoolValue - ExpectSendAfterValue basetypes.StringValue - ExpectError bool - }{ - "batching can be configured with values for enable_batching and send_after": { - EnableBatchingValue: types.BoolValue(true), - SendAfterValue: types.StringValue("45s"), - ExpectEnableBatchingValue: types.BoolValue(true), - ExpectSendAfterValue: types.StringValue("45s"), - }, - "if batching is an empty block, it will set the default values for enable_batching and send_after": { - // In this test, we try to create a list containing only null values - EnableBatchingValue: types.BoolNull(), - SendAfterValue: types.StringNull(), - ExpectEnableBatchingValue: types.BoolValue(true), - ExpectSendAfterValue: types.StringValue("10s"), - }, - "when batching is configured with only enable_batching, send_after will be set to a default value": { - EnableBatchingValue: types.BoolValue(true), - SendAfterValue: types.StringNull(), - ExpectEnableBatchingValue: types.BoolValue(true), - ExpectSendAfterValue: types.StringValue("10s"), - }, - "when batching is configured with only send_after, enable_batching will be set to a default value": { - EnableBatchingValue: types.BoolNull(), - SendAfterValue: types.StringValue("45s"), - ExpectEnableBatchingValue: types.BoolValue(true), - ExpectSendAfterValue: types.StringValue("45s"), - }, - "when the whole batching block is a null value, the provider provides default values for send_after and enable_batching": { - SetBatchingAsNull: true, - ExpectEnableBatchingValue: types.BoolValue(true), - ExpectSendAfterValue: types.StringValue("3s"), - }, - // Handling unknown values - "when batching is an unknown value, the provider treats it as if it's unset (align to SDK behaviour)": { - SetBatchingAsUnknown: true, - ExpectEnableBatchingValue: types.BoolValue(true), - ExpectSendAfterValue: types.StringValue("3s"), - }, - "when batching is configured with send_after as an unknown value, send_after will be set to a default value": { - EnableBatchingValue: types.BoolValue(true), - SendAfterValue: types.StringUnknown(), - ExpectEnableBatchingValue: types.BoolValue(true), - ExpectSendAfterValue: types.StringValue("10s"), - }, - "when batching is configured with enable_batching as an unknown value, enable_batching will be set to a default value": { - EnableBatchingValue: types.BoolUnknown(), - SendAfterValue: types.StringValue("45s"), - ExpectEnableBatchingValue: types.BoolValue(true), - ExpectSendAfterValue: types.StringValue("45s"), - }, - // Error states - "when batching is configured with send_after as an empty string, the empty string is not ignored and results in an error": { - EnableBatchingValue: types.BoolValue(true), - SendAfterValue: types.StringValue(""), - ExpectError: true, - }, - "if batching is configured with send_after as an invalid value, there's an error": { - SendAfterValue: types.StringValue("invalid value"), - ExpectError: true, - }, - "if batching is configured with send_after as number value without seconds (s), there's an error": { - SendAfterValue: types.StringValue("123"), - ExpectError: true, - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - // Arrange - acctest.UnsetTestProviderConfigEnvs(t) - acctest.SetupTestEnvs(t, tc.EnvVariables) - - ctx := context.Background() - tfVersion := "foobar" - providerversion := "999" - diags := diag.Diagnostics{} - - data := fwmodels.ProviderModel{} - data.Credentials = types.StringValue(transport_tpg.TestFakeCredentialsPath) - impersonateServiceAccountDelegates, _ := types.ListValue(types.StringType, []attr.Value{}) // empty list - data.ImpersonateServiceAccountDelegates = impersonateServiceAccountDelegates - - // TODO(SarahFrench) - this code will change when batching is reworked - // See https://github.com/GoogleCloudPlatform/magic-modules/pull/7668 - if !tc.SetBatchingAsNull && !tc.SetBatchingAsUnknown { - b, _ := types.ObjectValue( - map[string]attr.Type{ - "enable_batching": types.BoolType, - "send_after": types.StringType, - }, - map[string]attr.Value{ - "enable_batching": tc.EnableBatchingValue, - "send_after": tc.SendAfterValue, - }, - ) - batching, _ := types.ListValue(types.ObjectType{}.WithAttributeTypes(fwmodels.ProviderBatchingAttributes), []attr.Value{b}) - data.Batching = batching - } - if tc.SetBatchingAsNull { - data.Batching = types.ListNull(types.ObjectType{}.WithAttributeTypes(fwmodels.ProviderBatchingAttributes)) - } - if tc.SetBatchingAsUnknown { - data.Batching = types.ListUnknown(types.ObjectType{}.WithAttributeTypes(fwmodels.ProviderBatchingAttributes)) - } - - p := fwtransport.FrameworkProviderConfig{} - - // Act - p.LoadAndValidateFramework(ctx, &data, tfVersion, &diags, providerversion) - - // Assert - if diags.HasError() && tc.ExpectError { - return - } - if diags.HasError() && !tc.ExpectError { - for i, err := range diags.Errors() { - num := i + 1 - t.Logf("unexpected error #%d : %s", num, err.Summary()) - } - t.Fatalf("did not expect error, but [%d] error(s) occurred", diags.ErrorsCount()) - } - // Checking mutation of the data model - if !data.Batching.IsNull() && tc.ExpectBatchingNull { - t.Fatalf("want batching in the `fwmodels.ProviderModel` struct to be null, but got the value `%s`", data.Batching.String()) - } - if !data.Batching.IsUnknown() && tc.ExpectBatchingUnknown { - t.Fatalf("want batching in the `fwmodels.ProviderModel` struct to be unknown, but got the value `%s`", data.Batching.String()) - } - - // The code doesn't mutate values in the fwmodels.ProviderModel struct if the whole batching block is null/unknown, - // so run these checks below only if we're not setting the whole batching block is null/unknown - if !tc.SetBatchingAsNull && !tc.SetBatchingAsUnknown { - var pbConfigs []fwmodels.ProviderBatching - _ = data.Batching.ElementsAs(ctx, &pbConfigs, true) - if !pbConfigs[0].EnableBatching.Equal(tc.ExpectEnableBatchingValue) { - t.Fatalf("want batching.enable_batching in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectEnableBatchingValue.String(), pbConfigs[0].EnableBatching.String()) - } - if !pbConfigs[0].SendAfter.Equal(tc.ExpectSendAfterValue) { - t.Fatalf("want batching.send_after in the `fwmodels.ProviderModel` struct to be `%s`, but got the value `%s`", tc.ExpectSendAfterValue.String(), pbConfigs[0].SendAfter.String()) - } - } - - // Check how the batching block's values are used to configure other parts of the `FrameworkProviderConfig` struct - // - RequestBatcherServiceUsage - // - RequestBatcherIam - if p.RequestBatcherServiceUsage.BatchingConfig.EnableBatching != tc.ExpectEnableBatchingValue.ValueBool() { - t.Fatalf("want batching.enable_batching to be `%s`, but got the value `%v`", tc.ExpectEnableBatchingValue.String(), p.RequestBatcherServiceUsage.BatchingConfig.EnableBatching) - } - if !types.StringValue(p.RequestBatcherServiceUsage.BatchingConfig.SendAfter.String()).Equal(tc.ExpectSendAfterValue) { - t.Fatalf("want batching.send_after to be `%s`, but got the value `%s`", tc.ExpectSendAfterValue.String(), p.RequestBatcherServiceUsage.BatchingConfig.SendAfter.String()) - } - if p.RequestBatcherIam.BatchingConfig.EnableBatching != tc.ExpectEnableBatchingValue.ValueBool() { - t.Fatalf("want batching.enable_batching to be `%s`, but got the value `%v`", tc.ExpectEnableBatchingValue.String(), p.RequestBatcherIam.BatchingConfig.EnableBatching) - } - if !types.StringValue(p.RequestBatcherIam.BatchingConfig.SendAfter.String()).Equal(tc.ExpectSendAfterValue) { - t.Fatalf("want batching.send_after to be `%s`, but got the value `%s`", tc.ExpectSendAfterValue.String(), p.RequestBatcherIam.BatchingConfig.SendAfter.String()) - } - }) - } -} - -func TestGetRegionFromRegionSelfLink(t *testing.T) { - cases := map[string]struct { - Input basetypes.StringValue - ExpectedOutput basetypes.StringValue - }{ - "A short region name is returned unchanged": { - Input: types.StringValue("us-central1"), - ExpectedOutput: types.StringValue("us-central1"), - }, - "A selflink is shortened to a region name": { - Input: types.StringValue("https://www.googleapis.com/compute/v1/projects/my-project/regions/us-central1"), - ExpectedOutput: types.StringValue("us-central1"), - }, - "Logic is specific to region selflinks; zone selflinks are not shortened": { - Input: types.StringValue("https://www.googleapis.com/compute/v1/projects/my-project/zones/asia-east1-a"), - ExpectedOutput: types.StringValue("https://www.googleapis.com/compute/v1/projects/my-project/zones/asia-east1-a"), - }, - } - - for tn, tc := range cases { - t.Run(tn, func(t *testing.T) { - - region := fwtransport.GetRegionFromRegionSelfLink(tc.Input) - - if region != tc.ExpectedOutput { - t.Fatalf("want %s, got %s", region, tc.ExpectedOutput) - } - }) - } -} diff --git a/mmv1/third_party/terraform/fwtransport/framework_provider_clients.go.tmpl b/mmv1/third_party/terraform/fwtransport/framework_provider_clients.go.tmpl deleted file mode 100644 index 1001fa557202..000000000000 --- a/mmv1/third_party/terraform/fwtransport/framework_provider_clients.go.tmpl +++ /dev/null @@ -1,55 +0,0 @@ -package fwtransport - -import ( - "fmt" - "strings" - - "google.golang.org/api/dns/v1" -{{- if ne $.TargetVersionName "ga" }} - firebase "google.golang.org/api/firebase/v1beta1" -{{- end }} - "google.golang.org/api/option" - - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-log/tflog" - transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" -) - -// Methods to create new services from config -// Some base paths below need the version and possibly more of the path -// set on them. The client libraries are inconsistent about which values they need; -// while most only want the host URL, some older ones also want the version and some -// of those "projects" as well. You can find out if this is required by looking at -// the basePath value in the client library file. - -func (p *FrameworkProviderConfig) NewDnsClient(userAgent string, diags *diag.Diagnostics) *dns.Service { - dnsClientBasePath := transport_tpg.RemoveBasePathVersion(p.DNSBasePath) - dnsClientBasePath = strings.ReplaceAll(dnsClientBasePath, "/dns/", "") - tflog.Info(p.Context, fmt.Sprintf("Instantiating Google Cloud DNS client for path %s", dnsClientBasePath)) - clientDns, err := dns.NewService(p.Context, option.WithHTTPClient(p.Client)) - if err != nil { - diags.AddWarning("error creating client dns", err.Error()) - return nil - } - clientDns.UserAgent = userAgent - clientDns.BasePath = dnsClientBasePath - - return clientDns -} - -{{ if ne $.TargetVersionName `ga` -}} -func (p *FrameworkProviderConfig) NewFirebaseClient(userAgent string, diags *diag.Diagnostics) *firebase.Service { - firebaseClientBasePath := transport_tpg.RemoveBasePathVersion(p.FirebaseBasePath) - firebaseClientBasePath = strings.ReplaceAll(firebaseClientBasePath, "/firebase/", "") - tflog.Info(p.Context, fmt.Sprintf("Instantiating Google Cloud firebase client for path %s", firebaseClientBasePath)) - clientFirebase, err := firebase.NewService(p.Context, option.WithHTTPClient(p.Client)) - if err != nil { - diags.AddWarning("error creating client firebase", err.Error()) - return nil - } - clientFirebase.UserAgent = userAgent - clientFirebase.BasePath = firebaseClientBasePath - - return clientFirebase -} -{{- end }} diff --git a/mmv1/third_party/terraform/fwtransport/framework_transport.go b/mmv1/third_party/terraform/fwtransport/framework_transport.go deleted file mode 100644 index 9764518c506b..000000000000 --- a/mmv1/third_party/terraform/fwtransport/framework_transport.go +++ /dev/null @@ -1,103 +0,0 @@ -package fwtransport - -import ( - "bytes" - "encoding/json" - "net/http" - "time" - - "github.com/hashicorp/terraform-plugin-framework/diag" - transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" - - "google.golang.org/api/googleapi" -) - -func SendFrameworkRequest(p *FrameworkProviderConfig, method, project, rawurl, userAgent string, body map[string]interface{}, errorRetryPredicates ...transport_tpg.RetryErrorPredicateFunc) (map[string]interface{}, diag.Diagnostics) { - return SendFrameworkRequestWithTimeout(p, method, project, rawurl, userAgent, body, transport_tpg.DefaultRequestTimeout, errorRetryPredicates...) -} - -func SendFrameworkRequestWithTimeout(p *FrameworkProviderConfig, method, project, rawurl, userAgent string, body map[string]interface{}, timeout time.Duration, errorRetryPredicates ...transport_tpg.RetryErrorPredicateFunc) (map[string]interface{}, diag.Diagnostics) { - var diags diag.Diagnostics - - reqHeaders := make(http.Header) - reqHeaders.Set("User-Agent", userAgent) - reqHeaders.Set("Content-Type", "application/json") - - if p.UserProjectOverride.ValueBool() && project != "" { - // When project is "NO_BILLING_PROJECT_OVERRIDE" in the function GetCurrentUserEmail, - // set the header X-Goog-User-Project to be empty string. - if project == "NO_BILLING_PROJECT_OVERRIDE" { - reqHeaders.Set("X-Goog-User-Project", "") - } else { - // Pass the project into this fn instead of parsing it from the URL because - // both project names and URLs can have colons in them. - reqHeaders.Set("X-Goog-User-Project", project) - } - } - - if timeout == 0 { - timeout = time.Hour - } - - var res *http.Response - err := transport_tpg.Retry(transport_tpg.RetryOptions{ - RetryFunc: func() error { - var buf bytes.Buffer - if body != nil { - err := json.NewEncoder(&buf).Encode(body) - if err != nil { - return err - } - } - - u, err := transport_tpg.AddQueryParams(rawurl, map[string]string{"alt": "json"}) - if err != nil { - return err - } - req, err := http.NewRequest(method, u, &buf) - if err != nil { - return err - } - - req.Header = reqHeaders - res, err = p.Client.Do(req) - if err != nil { - return err - } - - if err := googleapi.CheckResponse(res); err != nil { - googleapi.CloseBody(res) - return err - } - - return nil - }, - Timeout: timeout, - ErrorRetryPredicates: errorRetryPredicates, - }) - if err != nil { - diags.AddError("error sending request", err.Error()) - return nil, diags - } - - if res == nil { - diags.AddError("Unable to parse server response.", "This is most likely a terraform problem, please file a bug at https://github.com/hashicorp/terraform-provider-google/issues.") - return nil, diags - } - - // The defer call must be made outside of the retryFunc otherwise it's closed too soon. - defer googleapi.CloseBody(res) - - // 204 responses will have no body, so we're going to error with "EOF" if we - // try to parse it. Instead, we can just return nil. - if res.StatusCode == 204 { - return nil, diags - } - result := make(map[string]interface{}) - if err := json.NewDecoder(res.Body).Decode(&result); err != nil { - diags.AddError("error decoding response body", err.Error()) - return nil, diags - } - - return result, diags -} diff --git a/mmv1/third_party/terraform/fwtransport/framework_utils.go b/mmv1/third_party/terraform/fwtransport/framework_utils.go index a59dd230bdfe..b297b475cb25 100644 --- a/mmv1/third_party/terraform/fwtransport/framework_utils.go +++ b/mmv1/third_party/terraform/fwtransport/framework_utils.go @@ -30,28 +30,6 @@ func CompileUserAgentString(ctx context.Context, name, tfVersion, provVersion st return ua } -func GetCurrentUserEmailFramework(p *FrameworkProviderConfig, userAgent string, diags *diag.Diagnostics) string { - // When environment variables UserProjectOverride and BillingProject are set for the provider, - // the header X-Goog-User-Project is set for the API requests. - // But it causes an error when calling GetCurrUserEmail. Set the project to be "NO_BILLING_PROJECT_OVERRIDE". - // And then it triggers the header X-Goog-User-Project to be set to empty string. - - // See https://github.com/golang/oauth2/issues/306 for a recommendation to do this from a Go maintainer - // URL retrieved from https://accounts.google.com/.well-known/openid-configuration - res, d := SendFrameworkRequest(p, "GET", "NO_BILLING_PROJECT_OVERRIDE", "https://openidconnect.googleapis.com/v1/userinfo", userAgent, nil) - diags.Append(d...) - - if diags.HasError() { - tflog.Info(p.Context, "error retrieving userinfo for your provider credentials. have you enabled the 'https://www.googleapis.com/auth/userinfo.email' scope?") - return "" - } - if res["email"] == nil { - diags.AddError("error retrieving email from userinfo.", "email was nil in the response.") - return "" - } - return res["email"].(string) -} - func GenerateFrameworkUserAgentString(metaData *fwmodels.ProviderMetaModel, currUserAgent string) string { if metaData != nil && !metaData.ModuleName.IsNull() && metaData.ModuleName.ValueString() != "" { return strings.Join([]string{currUserAgent, metaData.ModuleName.ValueString()}, " ")