diff --git a/api/resource.rb b/api/resource.rb index 6c4b3bdcc295..f776f4071742 100644 --- a/api/resource.rb +++ b/api/resource.rb @@ -305,57 +305,65 @@ def exclude_if_not_in_version!(version) # In newer resources there is much less standardisation in terms of value. # Generally for them though, it's the product.base_url + resource.name def self_link_url - base_url = @__product.base_url + [@__product.base_url, self_link_uri].flatten.join + end + + # Returns the partial uri / relative path of a resource. In newer resources, + # this is the name. This fn is named self_link_uri for consistency, but + # could otherwise be considered to be "path" + def self_link_uri if @self_link.nil? - [base_url, [@base_url, '{{name}}'].join('/')].flatten.join + [@base_url, '{{name}}'].join('/') else - self_link = @self_link - [base_url, self_link].flatten.join + @self_link end end def collection_url - [ - @__product.base_url, - @base_url - ].flatten.join + [@__product.base_url, collection_uri].flatten.join + end + + def collection_uri + @base_url end def async_operation_url + [@__product.base_url, async_operation_uri].flatten.join + end + + def async_operation_uri raise 'Not an async resource' if async.nil? - [@__product.base_url, async.operation.base_url].flatten.join + async.operation.base_url end - def default_create_url - if @create_verb.nil? || @create_verb == :POST - collection_url - elsif @create_verb == :PUT - self_link_url - else - raise "unsupported create verb #{@create_verb}" - end + def full_create_url + [@__product.base_url, create_uri].flatten.join end - def full_create_url + def create_uri if @create_url.nil? - default_create_url + if @create_verb.nil? || @create_verb == :POST + collection_uri + elsif @create_verb == :PUT + self_link_uri + else + raise "unsupported create verb #{@create_verb}" + end else - [ - @__product.base_url, - @create_url - ].flatten.join + @create_url end end def full_delete_url + [@__product.base_url, delete_uri].flatten.join + end + + def delete_uri if @delete_url.nil? - self_link_url + self_link_uri else - [ - @__product.base_url, - @delete_url - ].flatten.join + @delete_url end end diff --git a/build/terraform b/build/terraform index a0ef833a7931..2a40e5a749e5 160000 --- a/build/terraform +++ b/build/terraform @@ -1 +1 @@ -Subproject commit a0ef833a79313f4c8ee4cd19f2280a7507f76ad8 +Subproject commit 2a40e5a749e5058ce1406b708d14d75855e8607f diff --git a/build/terraform-beta b/build/terraform-beta index b5c0045412b4..7be1b03663d6 160000 --- a/build/terraform-beta +++ b/build/terraform-beta @@ -1 +1 @@ -Subproject commit b5c0045412b41e13262925cba2cfdbfd896c0d18 +Subproject commit 7be1b03663d6c315b7e1f5d6fad369f2c32cab64 diff --git a/build/terraform-mapper b/build/terraform-mapper index 7f8f824fd50c..9af028d29b62 160000 --- a/build/terraform-mapper +++ b/build/terraform-mapper @@ -1 +1 @@ -Subproject commit 7f8f824fd50cdbb4dfc6eab1ed5ec89654206fc2 +Subproject commit 9af028d29b62cf4e25278a9f747625ef690ffd8b diff --git a/provider/core.rb b/provider/core.rb index 671c5779b5ad..bf724b92ffd4 100644 --- a/provider/core.rb +++ b/provider/core.rb @@ -389,9 +389,13 @@ def custom_update_properties_by_url(properties, update_url) end def update_url(resource, url_part) - return resource.self_link_url if url_part.nil? + [resource.__product.base_url, update_uri(resource, url_part)].flatten.join + end + + def update_uri(resource, url_part) + return resource.self_link_uri if url_part.nil? - [resource.__product.base_url, url_part].flatten.join + url_part end # TODO(nelsonjr): Review all object interfaces and move to private methods diff --git a/templates/terraform/custom_expand/self_link_from_name.erb b/templates/terraform/custom_expand/self_link_from_name.erb index 00e3f89521b3..380369602984 100644 --- a/templates/terraform/custom_expand/self_link_from_name.erb +++ b/templates/terraform/custom_expand/self_link_from_name.erb @@ -25,7 +25,7 @@ func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d T return "https://www.googleapis.com/compute/v1/" + v.(string), nil } else if strings.HasPrefix(v.(string), "regions/") || strings.HasPrefix(v.(string), "zones/") { // For regional or zonal resources which include their region or zone, just put the project in front. - url, err := replaceVars(d, config, "https://www.googleapis.com/compute/v1/projects/{{project}}/") + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/") if err != nil { return nil, err } @@ -34,7 +34,7 @@ func expand<%= prefix -%><%= titlelize_property(property) -%>(v interface{}, d T // Anything else is assumed to be a regional resource, with a partial link that begins with the resource name. // This isn't very likely - it's a last-ditch effort to extract something useful here. We can do a better job // as soon as MultiResourceRefs are working since we'll know the types that this field is supposed to point to. - url, err := replaceVars(d, config, "https://www.googleapis.com/compute/v1/projects/{{project}}/regions/{{region}}/") + url, err := replaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/") if err != nil { return nil, err } diff --git a/templates/terraform/examples/base_configs/test_file.go.erb b/templates/terraform/examples/base_configs/test_file.go.erb index 18b9ab6a1d28..08c07acb0a95 100644 --- a/templates/terraform/examples/base_configs/test_file.go.erb +++ b/templates/terraform/examples/base_configs/test_file.go.erb @@ -114,7 +114,7 @@ func testAccCheck<%= resource_name -%>Destroy(s *terraform.State) error { config := testAccProvider.Meta().(*Config) - url, err := replaceVarsForTest(rs, "<%= object.self_link_url -%>") + url, err := replaceVarsForTest(rs, "<%= "{{#{object.__product.name}BasePath}}#{object.self_link_uri}" -%>") if err != nil { return err } diff --git a/templates/terraform/post_create/labels.erb b/templates/terraform/post_create/labels.erb index 5c5e263a953f..1aace6e83ca7 100644 --- a/templates/terraform/post_create/labels.erb +++ b/templates/terraform/post_create/labels.erb @@ -16,7 +16,7 @@ if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || labelFingerprintProp := d.Get("label_fingerprint") obj["labelFingerprint"] = labelFingerprintProp - url, err = replaceVars(d, config, "<%= object.self_link_url -%>/setLabels") + url, err = replaceVars(d, config, "<%= "{{#{object.__product.name}BasePath}}#{object.self_link_uri}" -%>/setLabels") if err != nil { return err } diff --git a/templates/terraform/pre_delete/detach_network.erb b/templates/terraform/pre_delete/detach_network.erb index f06fe608b84e..e9f4722ffd15 100644 --- a/templates/terraform/pre_delete/detach_network.erb +++ b/templates/terraform/pre_delete/detach_network.erb @@ -3,7 +3,7 @@ if d.Get("networks.#").(int) > 0 { patched := make(map[string]interface{}) patched["networks"] = nil - url, err := replaceVars(d, config, "https://www.googleapis.com/dns/v1beta2/projects/{{project}}/policies/{{name}}") + url, err := replaceVars(d, config, "{{DnsBasePath}}projects/{{project}}/policies/{{name}}") if err != nil { return err } diff --git a/templates/terraform/pre_delete/modify_delete_url.erb b/templates/terraform/pre_delete/modify_delete_url.erb index e54915c38159..aa0b8545a0e8 100644 --- a/templates/terraform/pre_delete/modify_delete_url.erb +++ b/templates/terraform/pre_delete/modify_delete_url.erb @@ -2,7 +2,7 @@ // in theory, we should find a way to disable the default URL and not construct // both, but that's a problem for another day. Today, we cheat. log.Printf("[DEBUG] replacing URL %q with a custom delete URL", url) -url, err = replaceVars(d, config, "<%= object.__product.base_url -%><%=object.base_url-%>/{{name}}") +url, err = replaceVars(d, config, "<%= "{{#{object.__product.name}BasePath}}" -%><%=object.base_url-%>/{{name}}") if err != nil { return err } diff --git a/templates/terraform/provider_gen.erb b/templates/terraform/provider_gen.erb index e6338d79ee96..388858d1db3d 100644 --- a/templates/terraform/provider_gen.erb +++ b/templates/terraform/provider_gen.erb @@ -18,6 +18,19 @@ package google import "github.com/hashicorp/terraform/helper/schema" +// If the base path has changed as a result of your PR, make sure to update +// the provider_reference page! +var <%= product_ns -%>DefaultBasePath = "<%= product.base_url -%>" +var <%= product_ns -%>CustomEndpointEntryKey = "<%= product_ns.underscore -%>_custom_endpoint" +var <%= product_ns -%>CustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_<%= product_ns.underscore.upcase -%>_CUSTOM_ENDPOINT", + }, <%= product_ns -%>DefaultBasePath), +} + var Generated<%= product_ns -%>ResourcesMap = map[string]*schema.Resource{ <% product.objects.reject { |r| r.exclude || r.not_in_version?(product.version_obj_or_default(version)) }.each do |object| -%> <% diff --git a/templates/terraform/resource.erb b/templates/terraform/resource.erb index 27e69179d696..b1e65bdd8bd2 100644 --- a/templates/terraform/resource.erb +++ b/templates/terraform/resource.erb @@ -141,7 +141,7 @@ func resource<%= resource_name -%>Create(d *schema.ResourceData, meta interface{ defer mutexKV.Unlock(lockName) <% end -%> - url, err := replaceVars(d, config, "<%= object.full_create_url -%>") + url, err := replaceVars(d, config, "<%= "{{#{object.__product.name}BasePath}}#{object.create_uri}" -%>") if err != nil { return err } @@ -199,7 +199,7 @@ func resource<%= resource_name -%>Create(d *schema.ResourceData, meta interface{ func resource<%= resource_name -%>Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) - url, err := replaceVars(d, config, "<%= object.self_link_url -%>") + url, err := replaceVars(d, config, "<%= "{{#{object.__product.name}BasePath}}#{object.self_link_uri}" -%>") if err != nil { return err } @@ -327,7 +327,7 @@ if <%= props.map { |prop| "d.HasChange(\"#{prop.name.underscore}\")" }.join ' || defer mutexKV.Unlock(lockName) <% end -%> - url, err := replaceVars(d, config, "<%= update_url(object, key[:update_url]) -%>") + url, err := replaceVars(d, config, "<%= "{{#{object.__product.name}BasePath}}#{update_uri(object, key[:update_url])}" -%>") if err != nil { return err } @@ -417,7 +417,7 @@ if <%= props.map { |prop| "d.HasChange(\"#{prop.name.underscore}\")" }.join ' || defer mutexKV.Unlock(lockName) <% end -%> - url, err := replaceVars(d, config, "<%= update_url(object, object.update_url) -%>") + url, err := replaceVars(d, config, "<%= "{{#{object.__product.name}BasePath}}#{update_uri(object, object.update_url)}" -%>") if err != nil { return err } @@ -485,7 +485,7 @@ func resource<%= resource_name -%>Delete(d *schema.ResourceData, meta interface{ defer mutexKV.Unlock(lockName) <% end -%> - url, err := replaceVars(d, config, "<%= object.full_delete_url -%>") + url, err := replaceVars(d, config, "<%= "{{#{object.__product.name}BasePath}}#{object.delete_uri}" -%>") if err != nil { return err } diff --git a/third_party/terraform/data_sources/data_source_google_composer_image_versions.go b/third_party/terraform/data_sources/data_source_google_composer_image_versions.go index 5ced5c4d5cea..1750b5e487be 100644 --- a/third_party/terraform/data_sources/data_source_google_composer_image_versions.go +++ b/third_party/terraform/data_sources/data_source_google_composer_image_versions.go @@ -56,7 +56,7 @@ func dataSourceGoogleComposerImageVersionsRead(d *schema.ResourceData, meta inte return err } - url, err := replaceVars(d, config, "https://composer.googleapis.com/v1/projects/{{project}}/locations/{{region}}/imageVersions") + url, err := replaceVars(d, config, "{{ComposerBasePath}}projects/{{project}}/locations/{{region}}/imageVersions") if err != nil { return err } diff --git a/third_party/terraform/data_sources/data_source_tpu_tensorflow_versions.go b/third_party/terraform/data_sources/data_source_tpu_tensorflow_versions.go index 0c36811b48d1..56806f74ed39 100644 --- a/third_party/terraform/data_sources/data_source_tpu_tensorflow_versions.go +++ b/third_party/terraform/data_sources/data_source_tpu_tensorflow_versions.go @@ -45,7 +45,7 @@ func dataSourceTpuTensorFlowVersionsRead(d *schema.ResourceData, meta interface{ return err } - url, err := replaceVars(d, config, "https://tpu.googleapis.com/v1/projects/{{project}}/locations/{{zone}}/tensorflowVersions") + url, err := replaceVars(d, config, "{{TpuBasePath}}projects/{{project}}/locations/{{zone}}/tensorflowVersions") if err != nil { return err } diff --git a/third_party/terraform/tests/resource_access_context_manager_access_level_test.go.erb b/third_party/terraform/tests/resource_access_context_manager_access_level_test.go.erb index 4e6ac5e53463..49458fdf9b83 100644 --- a/third_party/terraform/tests/resource_access_context_manager_access_level_test.go.erb +++ b/third_party/terraform/tests/resource_access_context_manager_access_level_test.go.erb @@ -68,7 +68,7 @@ func testAccCheckAccessContextManagerAccessLevelDestroy(s *terraform.State) erro config := testAccProvider.Meta().(*Config) - url, err := replaceVarsForTest(rs, "https://accesscontextmanager.googleapis.com/v1beta/{{name}}") + url, err := replaceVarsForTest(rs, "{{AccessContextManagerBasePath}}{{name}}") if err != nil { return err } diff --git a/third_party/terraform/tests/resource_access_context_manager_access_policy_test.go.erb b/third_party/terraform/tests/resource_access_context_manager_access_policy_test.go.erb index 9a4af0ca4831..48ee0c3af320 100644 --- a/third_party/terraform/tests/resource_access_context_manager_access_policy_test.go.erb +++ b/third_party/terraform/tests/resource_access_context_manager_access_policy_test.go.erb @@ -68,7 +68,7 @@ func testAccCheckAccessContextManagerAccessPolicyDestroy(s *terraform.State) err config := testAccProvider.Meta().(*Config) - url, err := replaceVarsForTest(rs, "https://accesscontextmanager.googleapis.com/v1beta/accessPolicies/{{name}}") + url, err := replaceVarsForTest(rs, "{{AccessContextManagerBasePath}}accessPolicies/{{name}}") if err != nil { return err } diff --git a/third_party/terraform/tests/resource_access_context_manager_service_perimeter_test.go.erb b/third_party/terraform/tests/resource_access_context_manager_service_perimeter_test.go.erb index 7eee550c9172..cc3b8756aa0f 100644 --- a/third_party/terraform/tests/resource_access_context_manager_service_perimeter_test.go.erb +++ b/third_party/terraform/tests/resource_access_context_manager_service_perimeter_test.go.erb @@ -67,7 +67,7 @@ func testAccCheckAccessContextManagerServicePerimeterDestroy(s *terraform.State) config := testAccProvider.Meta().(*Config) - url, err := replaceVarsForTest(rs, "https://accesscontextmanager.googleapis.com/v1beta/{{name}}") + url, err := replaceVarsForTest(rs, "{{AccessContextManagerBasePath}}{{name}}") if err != nil { return err } diff --git a/third_party/terraform/tests/resource_compute_backend_bucket_signed_url_key_test.go b/third_party/terraform/tests/resource_compute_backend_bucket_signed_url_key_test.go index 23aaeaa903a6..7e4a9c52255d 100644 --- a/third_party/terraform/tests/resource_compute_backend_bucket_signed_url_key_test.go +++ b/third_party/terraform/tests/resource_compute_backend_bucket_signed_url_key_test.go @@ -86,7 +86,7 @@ func checkComputeBackendBucketSignedUrlKeyExists(s *terraform.State) (bool, erro config := testAccProvider.Meta().(*Config) keyName := rs.Primary.ID - url, err := replaceVarsForTest(rs, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/backendBuckets/{{backend_bucket}}") + url, err := replaceVarsForTest(rs, "{{ComputeBasePath}}projects/{{project}}/global/backendBuckets/{{backend_bucket}}") if err != nil { return false, err } diff --git a/third_party/terraform/tests/resource_compute_backend_service_signed_url_key_test.go b/third_party/terraform/tests/resource_compute_backend_service_signed_url_key_test.go index 7d22301aca6a..27fbfa363fff 100644 --- a/third_party/terraform/tests/resource_compute_backend_service_signed_url_key_test.go +++ b/third_party/terraform/tests/resource_compute_backend_service_signed_url_key_test.go @@ -86,7 +86,7 @@ func checkComputeBackendServiceSignedUrlKeyExists(s *terraform.State) (bool, err config := testAccProvider.Meta().(*Config) keyName := rs.Primary.ID - url, err := replaceVarsForTest(rs, "https://www.googleapis.com/compute/v1/projects/{{project}}/global/backendServices/{{backend_service}}") + url, err := replaceVarsForTest(rs, "{{ComputeBasePath}}projects/{{project}}/global/backendServices/{{backend_service}}") if err != nil { return false, err } diff --git a/third_party/terraform/tests/resource_compute_instance_migrate_test.go b/third_party/terraform/tests/resource_compute_instance_migrate_test.go index d558e833e84f..8faebb2e016f 100644 --- a/third_party/terraform/tests/resource_compute_instance_migrate_test.go +++ b/third_party/terraform/tests/resource_compute_instance_migrate_test.go @@ -901,6 +901,9 @@ func getInitializedConfig(t *testing.T) *Config { Region: getTestRegionFromEnv(), Zone: getTestZoneFromEnv(), } + + ConfigureBasePaths(config) + err := config.LoadAndValidate() if err != nil { t.Fatal(err) diff --git a/third_party/terraform/utils/bootstrap_utils_test.go b/third_party/terraform/utils/bootstrap_utils_test.go index ec927656bbc0..1fa372796b46 100644 --- a/third_party/terraform/utils/bootstrap_utils_test.go +++ b/third_party/terraform/utils/bootstrap_utils_test.go @@ -51,13 +51,15 @@ func BootstrapKMSKeyInLocation(t *testing.T, locationID string) bootstrappedKMS keyParent := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s", projectID, locationID, SharedKeyRing) keyName := fmt.Sprintf("%s/cryptoKeys/%s", keyParent, SharedCyptoKey) - config := Config{ + config := &Config{ Credentials: getTestCredsFromEnv(), Project: getTestProjectFromEnv(), Region: getTestRegionFromEnv(), Zone: getTestZoneFromEnv(), } + ConfigureBasePaths(config) + if err := config.LoadAndValidate(); err != nil { t.Errorf("Unable to bootstrap KMS key: %s", err) } @@ -116,7 +118,7 @@ var serviceAccountDisplay = "Bootstrapped Service Account for Terraform tests" // Some tests need a second service account, other than the test runner, to assert functionality on. // This provides a well-known service account that can be used when dynamically creating a service // account isn't an option. -func getOrCreateServiceAccount(config Config, project string) (*iam.ServiceAccount, error) { +func getOrCreateServiceAccount(config *Config, project string) (*iam.ServiceAccount, error) { name := fmt.Sprintf("projects/%s/serviceAccounts/%s@%s.iam.gserviceaccount.com", project, serviceAccountEmail, project) log.Printf("[DEBUG] Verifying %s as bootstrapped service account.\n", name) @@ -148,7 +150,7 @@ func getOrCreateServiceAccount(config Config, project string) (*iam.ServiceAccou // on a different service account. Granting permissions takes time and there is no operation to wait on // so instead this creates a single service account once per test-suite with the correct permissions. // The first time this test is run it will fail, but subsequent runs will succeed. -func impersonationServiceAccountPermissions(config Config, sa *iam.ServiceAccount, testRunner string) error { +func impersonationServiceAccountPermissions(config *Config, sa *iam.ServiceAccount, testRunner string) error { log.Printf("[DEBUG] Setting service account permissions.\n") policy := iam.Policy{ Bindings: []*iam.Binding{}, @@ -179,13 +181,15 @@ func BootstrapServiceAccount(t *testing.T, project, testRunner string) string { return "" } - config := Config{ + config := &Config{ Credentials: getTestCredsFromEnv(), Project: getTestProjectFromEnv(), Region: getTestRegionFromEnv(), Zone: getTestZoneFromEnv(), } + ConfigureBasePaths(config) + if err := config.LoadAndValidate(); err != nil { t.Fatalf("Bootstrapping failed. Unable to load test config: %s", err) } diff --git a/third_party/terraform/utils/config.go.erb b/third_party/terraform/utils/config.go.erb index c00a985cd5f6..08934f5e1137 100644 --- a/third_party/terraform/utils/config.go.erb +++ b/third_party/terraform/utils/config.go.erb @@ -71,44 +71,124 @@ type Config struct { tokenSource oauth2.TokenSource - clientBilling *cloudbilling.APIService - clientBuild *cloudbuild.Service - clientComposer *composer.Service - clientCompute *compute.Service - clientComputeBeta *computeBeta.Service - clientContainer *container.Service - clientContainerBeta *containerBeta.Service - clientDataproc *dataproc.Service - clientDataprocBeta *dataprocBeta.Service - clientDataflow *dataflow.Service - clientDns *dns.Service - clientDnsBeta *dnsBeta.Service - clientFilestore *file.Service - clientIamCredentials *iamcredentials.Service - clientKms *cloudkms.Service - clientLogging *cloudlogging.Service - clientPubsub *pubsub.Service - clientResourceManager *cloudresourcemanager.Service - clientResourceManagerV2Beta1 *resourceManagerV2Beta1.Service - clientRuntimeconfig *runtimeconfig.Service - clientSpanner *spanner.Service - clientSourceRepo *sourcerepo.Service - clientStorage *storage.Service - clientSqlAdmin *sqladmin.Service - clientIAM *iam.Service <% unless version == 'ga' -%> - clientIAP *iap.Service + BinaryAuthorizationBasePath string + ContainerAnalysisBasePath string + MonitoringBasePath string + SecurityScannerBasePath string <% end -%> - clientServiceMan *servicemanagement.APIService - clientServiceUsage *serviceusage.Service - clientBigQuery *bigquery.Service - clientCloudFunctions *cloudfunctions.Service - clientCloudIoT *cloudiot.Service - clientAppEngine *appengine.APIService + + AccessContextManagerBasePath string + CloudSchedulerBasePath string + FirestoreBasePath string + RedisBasePath string + TpuBasePath string + + CloudBillingBasePath string + clientBilling *cloudbilling.APIService + + CloudBuildBasePath string + clientBuild *cloudbuild.Service + + ComposerBasePath string + clientComposer *composer.Service + + ComputeBasePath string + clientCompute *compute.Service + + ComputeBetaBasePath string + clientComputeBeta *computeBeta.Service + + ContainerBasePath string + clientContainer *container.Service + + ContainerBetaBasePath string + clientContainerBeta *containerBeta.Service + + DataprocBasePath string + clientDataproc *dataproc.Service + + DataprocBetaBasePath string + clientDataprocBeta *dataprocBeta.Service + + DataflowBasePath string + clientDataflow *dataflow.Service + + DnsBasePath string + clientDns *dns.Service + + DnsBetaBasePath string + clientDnsBeta *dnsBeta.Service + + FilestoreBasePath string + clientFilestore *file.Service + + IamCredentialsBasePath string + clientIamCredentials *iamcredentials.Service + + KmsBasePath string + clientKms *cloudkms.Service + + LoggingBasePath string + clientLogging *cloudlogging.Service + + PubsubBasePath string + clientPubsub *pubsub.Service + + ResourceManagerBasePath string + clientResourceManager *cloudresourcemanager.Service + + ResourceManagerV2Beta1BasePath string + clientResourceManagerV2Beta1 *resourceManagerV2Beta1.Service + + RuntimeconfigBasePath string + clientRuntimeconfig *runtimeconfig.Service + + SpannerBasePath string + clientSpanner *spanner.Service + + SourceRepoBasePath string + clientSourceRepo *sourcerepo.Service + + StorageBasePath string + clientStorage *storage.Service + + SqlBasePath string + clientSqlAdmin *sqladmin.Service + + IAMBasePath string + clientIAM *iam.Service <% unless version == 'ga' -%> - clientServiceNetworking *servicenetworking.APIService + + IAPBasePath string + clientIAP *iap.Service <% end -%> - clientStorageTransfer *storagetransfer.Service + + ServiceManagementBasePath string + clientServiceMan *servicemanagement.APIService + + ServiceUsageBasePath string + clientServiceUsage *serviceusage.Service + + BigQueryBasePath string + clientBigQuery *bigquery.Service + + CloudFunctionsBasePath string + clientCloudFunctions *cloudfunctions.Service + + CloudIoTBasePath string + clientCloudIoT *cloudiot.Service + + AppEngineBasePath string + clientAppEngine *appengine.APIService + <% unless version == 'ga' -%> + + ServiceNetworkingBasePath string + clientServiceNetworking *servicenetworking.APIService + <% end -%> + + StorageTransferBasePath string + clientStorageTransfer *storagetransfer.Service bigtableClientFactory *BigtableClientFactory } @@ -152,252 +232,326 @@ func (c *Config) LoadAndValidate() error { context := context.Background() - log.Printf("[INFO] Instantiating GCE client...") + // This base path and some others 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. + computeClientBasePath := removeBasePathVersion(c.ComputeBasePath) + "v1/projects/" + log.Printf("[INFO] Instantiating GCE client for path %s", computeClientBasePath) c.clientCompute, err = compute.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientCompute.UserAgent = userAgent + c.clientCompute.BasePath = computeClientBasePath - log.Printf("[INFO] Instantiating GCE Beta client...") + computeBetaClientBasePath := removeBasePathVersion(c.ComputeBetaBasePath) + "beta/projects/" + log.Printf("[INFO] Instantiating GCE Beta client for path %s", computeBetaClientBasePath) c.clientComputeBeta, err = computeBeta.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientComputeBeta.UserAgent = userAgent + c.clientComputeBeta.BasePath = computeBetaClientBasePath - log.Printf("[INFO] Instantiating GKE client...") + containerClientBasePath := removeBasePathVersion(c.ContainerBasePath) + log.Printf("[INFO] Instantiating GKE client for path %s", containerClientBasePath) c.clientContainer, err = container.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientContainer.UserAgent = userAgent + c.clientContainer.BasePath = containerClientBasePath - log.Printf("[INFO] Instantiating GKE Beta client...") + containerBetaClientBasePath := removeBasePathVersion(c.ContainerBetaBasePath) + log.Printf("[INFO] Instantiating GKE Beta client for path %s", containerBetaClientBasePath) c.clientContainerBeta, err = containerBeta.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientContainerBeta.UserAgent = userAgent + c.clientContainerBeta.BasePath = containerBetaClientBasePath - log.Printf("[INFO] Instantiating Google Cloud DNS client...") + dnsClientBasePath := removeBasePathVersion(c.DnsBasePath) + "v1/projects/" + log.Printf("[INFO] Instantiating Google Cloud DNS client for path %s", dnsClientBasePath) c.clientDns, err = dns.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientDns.UserAgent = userAgent + c.clientDns.BasePath = dnsClientBasePath - log.Printf("[INFO] Instantiating Google Cloud DNS Beta client...") + dnsBetaClientBasePath := removeBasePathVersion(c.DnsBetaBasePath) + "v1beta2/projects/" + log.Printf("[INFO] Instantiating Google Cloud DNS Beta client for path %s", dnsBetaClientBasePath) c.clientDnsBeta, err = dnsBeta.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientDnsBeta.UserAgent = userAgent + c.clientDnsBeta.BasePath = dnsBetaClientBasePath - log.Printf("[INFO] Instantiating Google Cloud KMS Client...") + kmsClientBasePath := removeBasePathVersion(c.KmsBasePath) + log.Printf("[INFO] Instantiating Google Cloud KMS client for path %s", kmsClientBasePath) c.clientKms, err = cloudkms.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientKms.UserAgent = userAgent + c.clientKms.BasePath = kmsClientBasePath - log.Printf("[INFO] Instantiating Google Stackdriver Logging client...") + loggingClientBasePath := removeBasePathVersion(c.LoggingBasePath) + log.Printf("[INFO] Instantiating Google Stackdriver Logging client for path %s", loggingClientBasePath) c.clientLogging, err = cloudlogging.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientLogging.UserAgent = userAgent + c.clientLogging.BasePath = loggingClientBasePath - log.Printf("[INFO] Instantiating Google Storage Client...") + storageClientBasePath := removeBasePathVersion(c.StorageBasePath) + "v1/" + log.Printf("[INFO] Instantiating Google Storage client for path %s", storageClientBasePath) c.clientStorage, err = storage.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientStorage.UserAgent = userAgent + c.clientStorage.BasePath = storageClientBasePath - log.Printf("[INFO] Instantiating Google SqlAdmin Client...") + sqlClientBasePath := removeBasePathVersion(c.SqlBasePath) + "v1beta4/" + log.Printf("[INFO] Instantiating Google SqlAdmin client for path %s", sqlClientBasePath) c.clientSqlAdmin, err = sqladmin.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientSqlAdmin.UserAgent = userAgent + c.clientSqlAdmin.BasePath = sqlClientBasePath - log.Printf("[INFO] Instantiating Google Pubsub Client...") + pubsubClientBasePath := removeBasePathVersion(c.PubsubBasePath) + log.Printf("[INFO] Instantiating Google Pubsub client for path %s", pubsubClientBasePath) c.clientPubsub, err = pubsub.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientPubsub.UserAgent = userAgent + c.clientPubsub.BasePath = pubsubClientBasePath - log.Printf("[INFO] Instantiating Google Dataflow Client...") + dataflowClientBasePath := removeBasePathVersion(c.DataflowBasePath) + log.Printf("[INFO] Instantiating Google Dataflow client for path %s", dataflowClientBasePath) c.clientDataflow, err = dataflow.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientDataflow.UserAgent = userAgent + c.clientDataflow.BasePath = dataflowClientBasePath - log.Printf("[INFO] Instantiating Google Cloud ResourceManager Client...") + resourceManagerBasePath := removeBasePathVersion(c.ResourceManagerBasePath) + log.Printf("[INFO] Instantiating Google Cloud ResourceManager client for path %s", resourceManagerBasePath) c.clientResourceManager, err = cloudresourcemanager.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientResourceManager.UserAgent = userAgent + c.clientResourceManager.BasePath = resourceManagerBasePath - log.Printf("[INFO] Instantiating Google Cloud ResourceManager V Client...") + resourceManagerV2Beta1BasePath := removeBasePathVersion(c.ResourceManagerV2Beta1BasePath) + log.Printf("[INFO] Instantiating Google Cloud ResourceManager V client for path %s", resourceManagerV2Beta1BasePath) c.clientResourceManagerV2Beta1, err = resourceManagerV2Beta1.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientResourceManagerV2Beta1.UserAgent = userAgent + c.clientResourceManagerV2Beta1.BasePath = resourceManagerV2Beta1BasePath - log.Printf("[INFO] Instantiating Google Cloud Runtimeconfig Client...") + runtimeconfigClientBasePath := removeBasePathVersion(c.RuntimeconfigBasePath) + log.Printf("[INFO] Instantiating Google Cloud Runtimeconfig client for path %s", runtimeconfigClientBasePath) c.clientRuntimeconfig, err = runtimeconfig.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientRuntimeconfig.UserAgent = userAgent + c.clientRuntimeconfig.BasePath = runtimeconfigClientBasePath - log.Printf("[INFO] Instantiating Google Cloud IAM Client...") + iamClientBasePath := removeBasePathVersion(c.IAMBasePath) + log.Printf("[INFO] Instantiating Google Cloud IAM client for path %s", iamClientBasePath) c.clientIAM, err = iam.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientIAM.UserAgent = userAgent + c.clientIAM.BasePath = iamClientBasePath - log.Printf("[INFO] Instantiating Google Cloud IAMCredentials Client...") + iamCredentialsClientBasePath := removeBasePathVersion(c.IamCredentialsBasePath) + log.Printf("[INFO] Instantiating Google Cloud IAMCredentials client for path %s", iamCredentialsClientBasePath) c.clientIamCredentials, err = iamcredentials.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientIamCredentials.UserAgent = userAgent + c.clientIamCredentials.BasePath = iamCredentialsClientBasePath <% unless version == 'ga' -%> - log.Printf("[INFO] Instantiating IAP Client...") + iapClientBasePath := removeBasePathVersion(c.IAPBasePath) + log.Printf("[INFO] Instantiating IAP client for path %s", iapClientBasePath) c.clientIAP, err = iap.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientIAP.UserAgent = userAgent + c.clientIAP.BasePath = iapClientBasePath <% end -%> - log.Printf("[INFO] Instantiating Google Cloud Service Management Client...") + serviceManagementClientBasePath := removeBasePathVersion(c.ServiceManagementBasePath) + log.Printf("[INFO] Instantiating Google Cloud Service Management client for path %s", serviceManagementClientBasePath) c.clientServiceMan, err = servicemanagement.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientServiceMan.UserAgent = userAgent + c.clientServiceMan.BasePath = serviceManagementClientBasePath - log.Printf("[INFO] Instantiating Google Cloud Service Usage Client...") + serviceUsageClientBasePath := removeBasePathVersion(c.ServiceUsageBasePath) + log.Printf("[INFO] Instantiating Google Cloud Service Usage client for path %s", serviceUsageClientBasePath) c.clientServiceUsage, err = serviceusage.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientServiceUsage.UserAgent = userAgent + c.clientServiceUsage.BasePath = serviceUsageClientBasePath - log.Printf("[INFO] Instantiating Google Cloud Billing Client...") + cloudBillingClientBasePath := removeBasePathVersion(c.CloudBillingBasePath) + log.Printf("[INFO] Instantiating Google Cloud Billing client for path %s", cloudBillingClientBasePath) c.clientBilling, err = cloudbilling.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientBilling.UserAgent = userAgent + c.clientBilling.BasePath = cloudBillingClientBasePath - log.Printf("[INFO] Instantiating Google Cloud Build Client...") + cloudBuildClientBasePath := removeBasePathVersion(c.CloudBuildBasePath) + log.Printf("[INFO] Instantiating Google Cloud Build client for path %s", cloudBuildClientBasePath) c.clientBuild, err = cloudbuild.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientBuild.UserAgent = userAgent + c.clientBuild.BasePath = cloudBuildClientBasePath - log.Printf("[INFO] Instantiating Google Cloud BigQuery Client...") + bigQueryClientBasePath := removeBasePathVersion(c.BigQueryBasePath) + "v2/" + log.Printf("[INFO] Instantiating Google Cloud BigQuery client for path %s", bigQueryClientBasePath) c.clientBigQuery, err = bigquery.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientBigQuery.UserAgent = userAgent + c.clientBigQuery.BasePath = bigQueryClientBasePath - log.Printf("[INFO] Instantiating Google Cloud CloudFunctions Client...") + cloudFunctionsClientBasePath := removeBasePathVersion(c.CloudFunctionsBasePath) + log.Printf("[INFO] Instantiating Google Cloud CloudFunctions Client for path %s", cloudFunctionsClientBasePath) c.clientCloudFunctions, err = cloudfunctions.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientCloudFunctions.UserAgent = userAgent + c.clientCloudFunctions.BasePath = cloudFunctionsClientBasePath c.bigtableClientFactory = &BigtableClientFactory{ UserAgent: userAgent, TokenSource: tokenSource, } - log.Printf("[INFO] Instantiating Google Cloud Source Repo Client...") + sourceRepoClientBasePath := removeBasePathVersion(c.SourceRepoBasePath) + log.Printf("[INFO] Instantiating Google Cloud Source Repo client for path %s", sourceRepoClientBasePath) c.clientSourceRepo, err = sourcerepo.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientSourceRepo.UserAgent = userAgent + c.clientSourceRepo.BasePath = sourceRepoClientBasePath - log.Printf("[INFO] Instantiating Google Cloud Spanner Client...") + spannerClientBasePath := removeBasePathVersion(c.SpannerBasePath) + log.Printf("[INFO] Instantiating Google Cloud Spanner client for path %s", spannerClientBasePath) c.clientSpanner, err = spanner.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientSpanner.UserAgent = userAgent + c.clientSpanner.BasePath = spannerClientBasePath - log.Printf("[INFO] Instantiating Google Cloud Dataproc Client...") + dataprocClientBasePath := removeBasePathVersion(c.DataprocBasePath) + log.Printf("[INFO] Instantiating Google Cloud Dataproc client for path %s", dataprocClientBasePath) c.clientDataproc, err = dataproc.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientDataproc.UserAgent = userAgent + c.clientDataproc.BasePath = dataprocClientBasePath - log.Printf("[INFO] Instantiating Google Cloud Dataproc Beta client...") + dataprocBetaClientBasePath := removeBasePathVersion(c.DataprocBetaBasePath) + log.Printf("[INFO] Instantiating Google Cloud Dataproc Beta client for path %s", dataprocBetaClientBasePath) c.clientDataprocBeta, err = dataprocBeta.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientDataprocBeta.UserAgent = userAgent + c.clientDataprocBeta.BasePath = dataprocClientBasePath + filestoreClientBasePath := removeBasePathVersion(c.FilestoreBasePath) + log.Printf("[INFO] Instantiating Filestore client for path %s", filestoreClientBasePath) c.clientFilestore, err = file.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientFilestore.UserAgent = userAgent + c.clientFilestore.BasePath = filestoreClientBasePath - log.Printf("[INFO] Instantiating Google Cloud IoT Core Client...") + cloudIoTClientBasePath := removeBasePathVersion(c.CloudIoTBasePath) + log.Printf("[INFO] Instantiating Google Cloud IoT Core client for path %s", cloudIoTClientBasePath) c.clientCloudIoT, err = cloudiot.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientCloudIoT.UserAgent = userAgent + c.clientCloudIoT.BasePath = cloudIoTClientBasePath - log.Printf("[INFO] Instantiating App Engine Client...") + appEngineClientBasePath := removeBasePathVersion(c.AppEngineBasePath) + log.Printf("[INFO] Instantiating App Engine client for path %s", appEngineClientBasePath) c.clientAppEngine, err = appengine.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientAppEngine.UserAgent = userAgent + c.clientAppEngine.BasePath = appEngineClientBasePath - log.Printf("[INFO] Instantiating Cloud Composer Client...") + composerClientBasePath := removeBasePathVersion(c.ComposerBasePath) + log.Printf("[INFO] Instantiating Cloud Composer client for path %s", composerClientBasePath) c.clientComposer, err = composer.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientComposer.UserAgent = userAgent + c.clientComposer.BasePath = composerClientBasePath <% unless version == 'ga' -%> - log.Printf("[INFO] Instantiating Service Networking Client...") + serviceNetworkingClientBasePath := removeBasePathVersion(c.ServiceNetworkingBasePath) + log.Printf("[INFO] Instantiating Service Networking client for path %s", serviceNetworkingClientBasePath) c.clientServiceNetworking, err = servicenetworking.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientServiceNetworking.UserAgent = userAgent + c.clientServiceNetworking.BasePath = serviceNetworkingClientBasePath <% end -%> - log.Printf("[INFO] Instantiating Google Cloud Storage Transfer Client...") + storageTransferClientBasePath := removeBasePathVersion(c.StorageTransferBasePath) + log.Printf("[INFO] Instantiating Google Cloud Storage Transfer client for path %s", storageTransferClientBasePath) c.clientStorageTransfer, err = storagetransfer.NewService(context, option.WithHTTPClient(client)) if err != nil { return err } c.clientStorageTransfer.UserAgent = userAgent + c.clientStorageTransfer.BasePath = storageTransferClientBasePath return nil } @@ -435,3 +589,8 @@ func (c *Config) getTokenSource(clientScopes []string) (oauth2.TokenSource, erro log.Printf("[INFO] -- Scopes: %s", clientScopes) return googleoauth.DefaultTokenSource(context.Background(), clientScopes...) } + +// Remove the `/{{version}}/` from a base path, replacing it with `/` +func removeBasePathVersion(url string) string { + return regexp.MustCompile(`/[^/]+/$`).ReplaceAllString(url, "/") +} diff --git a/third_party/terraform/utils/config_test.go b/third_party/terraform/utils/config_test.go index 3e67eab546ab..0862e8f2e78a 100644 --- a/third_party/terraform/utils/config_test.go +++ b/third_party/terraform/utils/config_test.go @@ -15,12 +15,14 @@ const testFakeCredentialsPath = "./test-fixtures/fake_account.json" const testOauthScope = "https://www.googleapis.com/auth/compute" func TestConfigLoadAndValidate_accountFilePath(t *testing.T) { - config := Config{ + config := &Config{ Credentials: testFakeCredentialsPath, Project: "my-gce-project", Region: "us-central1", } + ConfigureBasePaths(config) + err := config.LoadAndValidate() if err != nil { t.Fatalf("error: %v", err) @@ -32,12 +34,14 @@ func TestConfigLoadAndValidate_accountFileJSON(t *testing.T) { if err != nil { t.Fatalf("error: %v", err) } - config := Config{ + config := &Config{ Credentials: string(contents), Project: "my-gce-project", Region: "us-central1", } + ConfigureBasePaths(config) + err = config.LoadAndValidate() if err != nil { t.Fatalf("error: %v", err) @@ -45,12 +49,14 @@ func TestConfigLoadAndValidate_accountFileJSON(t *testing.T) { } func TestConfigLoadAndValidate_accountFileJSONInvalid(t *testing.T) { - config := Config{ + config := &Config{ Credentials: "{this is not json}", Project: "my-gce-project", Region: "us-central1", } + ConfigureBasePaths(config) + if config.LoadAndValidate() == nil { t.Fatalf("expected error, but got nil") } @@ -65,12 +71,14 @@ func TestAccConfigLoadValidate_credentials(t *testing.T) { creds := getTestCredsFromEnv() proj := getTestProjectFromEnv() - config := Config{ + config := &Config{ Credentials: creds, Project: proj, Region: "us-central1", } + ConfigureBasePaths(config) + err := config.LoadAndValidate() if err != nil { t.Fatalf("error: %v", err) @@ -101,12 +109,14 @@ func TestAccConfigLoadValidate_accessToken(t *testing.T) { t.Fatalf("Unable to generate test access token: %s", err) } - config := Config{ + config := &Config{ AccessToken: token.AccessToken, Project: proj, Region: "us-central1", } + ConfigureBasePaths(config) + err = config.LoadAndValidate() if err != nil { t.Fatalf("error: %v", err) @@ -119,12 +129,15 @@ func TestAccConfigLoadValidate_accessToken(t *testing.T) { } func TestConfigLoadAndValidate_customScopes(t *testing.T) { - config := Config{ + config := &Config{ Credentials: testFakeCredentialsPath, Project: "my-gce-project", Region: "us-central1", Scopes: []string{"https://www.googleapis.com/auth/compute"}, } + + ConfigureBasePaths(config) + err := config.LoadAndValidate() if err != nil { t.Fatalf("unexpected error: %v", err) diff --git a/third_party/terraform/utils/gcp_sweeper_test.go b/third_party/terraform/utils/gcp_sweeper_test.go index f04d8ae58765..f046eb11f3d9 100644 --- a/third_party/terraform/utils/gcp_sweeper_test.go +++ b/third_party/terraform/utils/gcp_sweeper_test.go @@ -30,5 +30,7 @@ func sharedConfigForRegion(region string) (*Config, error) { Project: project, } + ConfigureBasePaths(conf) + return conf, nil } diff --git a/third_party/terraform/utils/provider.go.erb b/third_party/terraform/utils/provider.go.erb index ba13fa3bbae6..eb74d35dea18 100644 --- a/third_party/terraform/utils/provider.go.erb +++ b/third_party/terraform/utils/provider.go.erb @@ -4,8 +4,6 @@ package google import ( "encoding/json" "fmt" - "github.com/terraform-providers/terraform-provider-google-beta/google-beta" - google2 "github.com/terraform-providers/terraform-provider-google/google" "os" "github.com/hashicorp/terraform/helper/mutexkv" @@ -72,11 +70,67 @@ func Provider() terraform.ResourceProvider { "CLOUDSDK_COMPUTE_ZONE", }, nil), }, + "scopes": &schema.Schema{ Type: schema.TypeList, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + + // Generated Products + <% unless version == 'ga' -%> + // start beta-only products + BinaryAuthorizationCustomEndpointEntryKey: BinaryAuthorizationCustomEndpointEntry, + ContainerAnalysisCustomEndpointEntryKey: ContainerAnalysisCustomEndpointEntry, + MonitoringCustomEndpointEntryKey: MonitoringCustomEndpointEntry, + SecurityScannerCustomEndpointEntryKey: SecurityScannerCustomEndpointEntry, + // end beta-only products + <% end -%> + AccessContextManagerCustomEndpointEntryKey: AccessContextManagerCustomEndpointEntry, + AppEngineCustomEndpointEntryKey: AppEngineCustomEndpointEntry, + ComputeCustomEndpointEntryKey: ComputeCustomEndpointEntry, + CloudBuildCustomEndpointEntryKey: CloudBuildCustomEndpointEntry, + CloudSchedulerCustomEndpointEntryKey: CloudSchedulerCustomEndpointEntry, + DnsCustomEndpointEntryKey: DnsCustomEndpointEntry, + FilestoreCustomEndpointEntryKey: FilestoreCustomEndpointEntry, + FirestoreCustomEndpointEntryKey: FirestoreCustomEndpointEntry, + KmsCustomEndpointEntryKey: KmsCustomEndpointEntry, + PubsubCustomEndpointEntryKey: PubsubCustomEndpointEntry, + RedisCustomEndpointEntryKey: RedisCustomEndpointEntry, + ResourceManagerCustomEndpointEntryKey: ResourceManagerCustomEndpointEntry, + SourceRepoCustomEndpointEntryKey: SourceRepoCustomEndpointEntry, + SpannerCustomEndpointEntryKey: SpannerCustomEndpointEntry, + SqlCustomEndpointEntryKey: SqlCustomEndpointEntry, + StorageCustomEndpointEntryKey: StorageCustomEndpointEntry, + TpuCustomEndpointEntryKey: TpuCustomEndpointEntry, + + // Handwritten Products / Versioned / Atypical Entries + <% unless version == 'ga' -%> + // start beta-only products + IAPCustomEndpointEntryKey: IAPCustomEndpointEntry, + ServiceNetworkingCustomEndpointEntryKey: ServiceNetworkingCustomEndpointEntry, + // end beta-only products + <% end -%> + CloudBillingCustomEndpointEntryKey: CloudBillingCustomEndpointEntry, + ComposerCustomEndpointEntryKey: ComposerCustomEndpointEntry, + ComputeBetaCustomEndpointEntryKey: ComputeBetaCustomEndpointEntry, + ContainerCustomEndpointEntryKey: ContainerCustomEndpointEntry, + ContainerBetaCustomEndpointEntryKey: ContainerBetaCustomEndpointEntry, + DataprocCustomEndpointEntryKey: DataprocCustomEndpointEntry, + DataprocBetaCustomEndpointEntryKey: DataprocBetaCustomEndpointEntry, + DataflowCustomEndpointEntryKey: DataflowCustomEndpointEntry, + DnsBetaCustomEndpointEntryKey: DnsBetaCustomEndpointEntry, + IamCredentialsCustomEndpointEntryKey: IamCredentialsCustomEndpointEntry, + LoggingCustomEndpointEntryKey: LoggingCustomEndpointEntry, + ResourceManagerV2Beta1CustomEndpointEntryKey: ResourceManagerV2Beta1CustomEndpointEntry, + RuntimeconfigCustomEndpointEntryKey: RuntimeconfigCustomEndpointEntry, + IAMCustomEndpointEntryKey: IAMCustomEndpointEntry, + ServiceManagementCustomEndpointEntryKey: ServiceManagementCustomEndpointEntry, + ServiceUsageCustomEndpointEntryKey: ServiceUsageCustomEndpointEntry, + BigQueryCustomEndpointEntryKey: BigQueryCustomEndpointEntry, + CloudFunctionsCustomEndpointEntryKey: CloudFunctionsCustomEndpointEntry, + CloudIoTCustomEndpointEntryKey: CloudIoTCustomEndpointEntry, + StorageTransferCustomEndpointEntryKey: StorageTransferCustomEndpointEntry, }, DataSourcesMap: map[string]*schema.Resource{ @@ -322,6 +376,57 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { config.Scopes[i] = scope.(string) } + <% unless version == 'ga' -%> + config.BinaryAuthorizationBasePath = d.Get(BinaryAuthorizationCustomEndpointEntryKey).(string) + config.ContainerAnalysisBasePath = d.Get(ContainerAnalysisCustomEndpointEntryKey).(string) + config.MonitoringBasePath = d.Get(MonitoringCustomEndpointEntryKey).(string) + config.SecurityScannerBasePath = d.Get(SecurityScannerCustomEndpointEntryKey).(string) + <% end -%> + + config.AccessContextManagerBasePath = d.Get(AccessContextManagerCustomEndpointEntryKey).(string) + config.CloudSchedulerBasePath = d.Get(CloudSchedulerCustomEndpointEntryKey).(string) + config.FirestoreBasePath = d.Get(FirestoreCustomEndpointEntryKey).(string) + + config.AppEngineBasePath = d.Get(AppEngineCustomEndpointEntryKey).(string) + config.ComputeBasePath = d.Get(ComputeCustomEndpointEntryKey).(string) + config.CloudBuildBasePath = d.Get(CloudBuildCustomEndpointEntryKey).(string) + config.DnsBasePath = d.Get(DnsCustomEndpointEntryKey).(string) + config.FilestoreBasePath = d.Get(FilestoreCustomEndpointEntryKey).(string) + config.KmsBasePath = d.Get(KmsCustomEndpointEntryKey).(string) + config.PubsubBasePath = d.Get(PubsubCustomEndpointEntryKey).(string) + config.RedisBasePath = d.Get(RedisCustomEndpointEntryKey).(string) + config.ResourceManagerBasePath = d.Get(ResourceManagerCustomEndpointEntryKey).(string) + config.SourceRepoBasePath = d.Get(SourceRepoCustomEndpointEntryKey).(string) + config.SpannerBasePath = d.Get(SpannerCustomEndpointEntryKey).(string) + config.SqlBasePath = d.Get(SqlCustomEndpointEntryKey).(string) + config.StorageBasePath = d.Get(StorageCustomEndpointEntryKey).(string) + config.TpuBasePath = d.Get(TpuCustomEndpointEntryKey).(string) + + <% unless version == 'ga' -%> + config.IAPBasePath = d.Get(IAPCustomEndpointEntryKey).(string) + config.ServiceNetworkingBasePath = d.Get(ServiceNetworkingCustomEndpointEntryKey).(string) + <% end -%> + config.CloudBillingBasePath = d.Get(CloudBillingCustomEndpointEntryKey).(string) + config.ComposerBasePath = d.Get(ComposerCustomEndpointEntryKey).(string) + config.ComputeBetaBasePath = d.Get(ComputeBetaCustomEndpointEntryKey).(string) + config.ContainerBasePath = d.Get(ContainerCustomEndpointEntryKey).(string) + config.ContainerBetaBasePath = d.Get(ContainerBetaCustomEndpointEntryKey).(string) + config.DataprocBasePath = d.Get(DataprocCustomEndpointEntryKey).(string) + config.DataprocBetaBasePath = d.Get(DataprocBetaCustomEndpointEntryKey).(string) + config.DataflowBasePath = d.Get(DataflowCustomEndpointEntryKey).(string) + config.DnsBetaBasePath = d.Get(DnsBetaCustomEndpointEntryKey).(string) + config.IamCredentialsBasePath = d.Get(IamCredentialsCustomEndpointEntryKey).(string) + config.LoggingBasePath = d.Get(LoggingCustomEndpointEntryKey).(string) + config.ResourceManagerV2Beta1BasePath = d.Get(ResourceManagerV2Beta1CustomEndpointEntryKey).(string) + config.RuntimeconfigBasePath = d.Get(RuntimeconfigCustomEndpointEntryKey).(string) + config.IAMBasePath = d.Get(IAMCustomEndpointEntryKey).(string) + config.ServiceManagementBasePath = d.Get(ServiceManagementCustomEndpointEntryKey).(string) + config.ServiceUsageBasePath = d.Get(ServiceUsageCustomEndpointEntryKey).(string) + config.BigQueryBasePath = d.Get(BigQueryCustomEndpointEntryKey).(string) + config.CloudFunctionsBasePath = d.Get(CloudFunctionsCustomEndpointEntryKey).(string) + config.CloudIoTBasePath = d.Get(CloudIoTCustomEndpointEntryKey).(string) + config.StorageTransferBasePath = d.Get(StorageTransferCustomEndpointEntryKey).(string) + if err := config.LoadAndValidate(); err != nil { return nil, err } @@ -329,6 +434,65 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) { return &config, nil } +// For a consumer of config.go that isn't a full fledged provider and doesn't +// have its own endpoint mechanism such as sweepers, init {{service}}BasePath +// values to a default. After using this, you should call config.LoadAndValidate. +func ConfigureBasePaths(c *Config) { + // Generated Products + <% unless version == 'ga' -%> + // start beta-only products + c.BinaryAuthorizationBasePath = BinaryAuthorizationDefaultBasePath + c.ContainerAnalysisBasePath = ContainerAnalysisDefaultBasePath + c.MonitoringBasePath = MonitoringDefaultBasePath + c.SecurityScannerBasePath = SecurityScannerDefaultBasePath + // end beta-only products + <% end -%> + c.AccessContextManagerBasePath = AccessContextManagerDefaultBasePath + c.AppEngineBasePath = AppEngineDefaultBasePath + c.ComputeBasePath = ComputeDefaultBasePath + c.CloudBuildBasePath = CloudBuildDefaultBasePath + c.CloudSchedulerBasePath = CloudSchedulerDefaultBasePath + c.DnsBasePath = DnsDefaultBasePath + c.FilestoreBasePath = FilestoreDefaultBasePath + c.FirestoreBasePath = FirestoreDefaultBasePath + c.KmsBasePath = KmsDefaultBasePath + c.PubsubBasePath = PubsubDefaultBasePath + c.RedisBasePath = RedisDefaultBasePath + c.ResourceManagerBasePath = ResourceManagerDefaultBasePath + c.SourceRepoBasePath = SourceRepoDefaultBasePath + c.SpannerBasePath = SpannerDefaultBasePath + c.SqlBasePath = SqlDefaultBasePath + c.StorageBasePath = StorageDefaultBasePath + c.TpuBasePath = TpuDefaultBasePath + + // Handwritten Products / Versioned / Atypical Entries + <% unless version == 'ga' -%> + // start beta-only products + c.IAPBasePath = IAPDefaultBasePath + c.ServiceNetworkingBasePath = ServiceNetworkingDefaultBasePath + // end beta-only products + <% end -%> + c.CloudBillingBasePath = CloudBillingDefaultBasePath + c.ComposerBasePath = ComposerDefaultBasePath + c.ComputeBetaBasePath = ComputeBetaDefaultBasePath + c.ContainerBasePath = ContainerDefaultBasePath + c.ContainerBetaBasePath = ContainerBetaDefaultBasePath + c.DataprocBasePath = DataprocDefaultBasePath + c.DataflowBasePath = DataflowDefaultBasePath + c.DnsBetaBasePath = DnsBetaDefaultBasePath + c.IamCredentialsBasePath = IamCredentialsDefaultBasePath + c.LoggingBasePath = LoggingDefaultBasePath + c.ResourceManagerV2Beta1BasePath = ResourceManagerV2Beta1DefaultBasePath + c.RuntimeconfigBasePath = RuntimeconfigDefaultBasePath + c.IAMBasePath = IAMDefaultBasePath + c.ServiceManagementBasePath = ServiceManagementDefaultBasePath + c.ServiceUsageBasePath = ServiceUsageDefaultBasePath + c.BigQueryBasePath = BigQueryDefaultBasePath + c.CloudFunctionsBasePath = CloudFunctionsDefaultBasePath + c.CloudIoTBasePath = CloudIoTDefaultBasePath + c.StorageTransferBasePath = StorageTransferDefaultBasePath +} + func validateCredentials(v interface{}, k string) (warnings []string, errors []error) { if v == nil || v.(string) == "" { return diff --git a/third_party/terraform/utils/provider_handwritten_endpoint.go.erb b/third_party/terraform/utils/provider_handwritten_endpoint.go.erb new file mode 100644 index 000000000000..0b28152e3e6f --- /dev/null +++ b/third_party/terraform/utils/provider_handwritten_endpoint.go.erb @@ -0,0 +1,260 @@ +<% autogen_exception -%> +package google + +import ( + "encoding/json" + "fmt" + "github.com/terraform-providers/terraform-provider-google-beta/google-beta" + "os" + + "github.com/hashicorp/terraform/helper/mutexkv" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" + + googleoauth "golang.org/x/oauth2/google" +) + +// For generated resources, endpoint entries live in product-specific provider +// files. Collect handwritten ones here. If any of these are modified, be sure +// to update the provider_reference docs page. + +var BigQueryDefaultBasePath = "https://www.googleapis.com/bigquery/v2/" +var BigQueryCustomEndpointEntryKey = "bigquery_custom_endpoint" +var BigQueryCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_BIGQUERY_CUSTOM_ENDPOINT", + }, BigQueryDefaultBasePath), +} + +var CloudBillingDefaultBasePath = "https://cloudbilling.googleapis.com/v1/" +var CloudBillingCustomEndpointEntryKey = "cloud_billing_custom_endpoint" +var CloudBillingCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_CLOUD_BILLING_CUSTOM_ENDPOINT", + }, CloudBillingDefaultBasePath), +} + +var CloudFunctionsDefaultBasePath = "https://cloudfunctions.googleapis.com/v1/" +var CloudFunctionsCustomEndpointEntryKey = "cloud_functions_custom_endpoint" +var CloudFunctionsCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_CLOUD_FUNCTIONS_CUSTOM_ENDPOINT", + }, CloudFunctionsDefaultBasePath), +} + + +var CloudIoTDefaultBasePath = "https://cloudiot.googleapis.com/v1/" +var CloudIoTCustomEndpointEntryKey = "cloud_iot_custom_endpoint" +var CloudIoTCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_CLOUD_IOT_CUSTOM_ENDPOINT", + }, CloudIoTDefaultBasePath), +} + + +var ComposerDefaultBasePath = "https://composer.googleapis.com/v1beta1/" +var ComposerCustomEndpointEntryKey = "composer_custom_endpoint" +var ComposerCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_COMPOSER_CUSTOM_ENDPOINT", + }, ComposerDefaultBasePath), +} + +var ComputeBetaDefaultBasePath = "https://www.googleapis.com/compute/beta/" +var ComputeBetaCustomEndpointEntryKey = "compute_beta_custom_endpoint" +var ComputeBetaCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_COMPUTE_BETA_CUSTOM_ENDPOINT", + }, ComputeBetaDefaultBasePath), +} + +var ContainerDefaultBasePath = "https://container.googleapis.com/v1/" +var ContainerCustomEndpointEntryKey = "container_custom_endpoint" +var ContainerCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_CONTAINER_CUSTOM_ENDPOINT", + }, ContainerDefaultBasePath), +} + +var ContainerBetaDefaultBasePath = "https://container.googleapis.com/v1beta1/" +var ContainerBetaCustomEndpointEntryKey = "container_beta_custom_endpoint" +var ContainerBetaCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_CONTAINER_BETA_CUSTOM_ENDPOINT", + }, ContainerBetaDefaultBasePath), +} + +var DataprocDefaultBasePath = "https://dataproc.googleapis.com/v1/" +var DataprocCustomEndpointEntryKey = "dataproc_custom_endpoint" +var DataprocCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_DATAPROC_CUSTOM_ENDPOINT", + }, DataprocDefaultBasePath), +} + +var DataprocBetaDefaultBasePath = "https://dataproc.googleapis.com/v1beta2/" +var DataprocBetaCustomEndpointEntryKey = "dataproc_beta_custom_endpoint" +var DataprocBetaCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_DATAPROC_BETA_CUSTOM_ENDPOINT", + }, DataprocBetaDefaultBasePath), +} + +var DataflowDefaultBasePath = "https://dataflow.googleapis.com/v1b3/" +var DataflowCustomEndpointEntryKey = "dataflow_custom_endpoint" +var DataflowCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_DATAFLOW_CUSTOM_ENDPOINT", + }, DataflowDefaultBasePath), +} +var DnsBetaDefaultBasePath = "https://www.googleapis.com/dns/v1beta2/" +var DnsBetaCustomEndpointEntryKey = "dns_beta_custom_endpoint" +var DnsBetaCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_DNS_BETA_CUSTOM_ENDPOINT", + }, DnsBetaDefaultBasePath), +} + +var IAMDefaultBasePath = "https://iam.googleapis.com/v1/" +var IAMCustomEndpointEntryKey = "iam_custom_endpoint" +var IAMCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_IAM_CUSTOM_ENDPOINT", + }, IAMDefaultBasePath), +} + +var IamCredentialsDefaultBasePath = "https://iamcredentials.googleapis.com/v1/" +var IamCredentialsCustomEndpointEntryKey = "iam_credentials_custom_endpoint" +var IamCredentialsCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_IAM_CREDENTIALS_CUSTOM_ENDPOINT", + }, IamCredentialsDefaultBasePath), +} + +<% unless version == 'ga' -%> +var IAPDefaultBasePath = "https://iap.googleapis.com/v1beta1/" +var IAPCustomEndpointEntryKey = "iap_custom_endpoint" +var IAPCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_IAP_CUSTOM_ENDPOINT", + }, IAPDefaultBasePath), +} + +<% end -%> +var ResourceManagerV2Beta1DefaultBasePath = "https://cloudresourcemanager.googleapis.com/v2beta1/" +var ResourceManagerV2Beta1CustomEndpointEntryKey = "resource_manager_v2beta1_custom_endpoint" +var ResourceManagerV2Beta1CustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_RESOURCE_MANAGER_V2BETA1_CUSTOM_ENDPOINT", + }, ResourceManagerV2Beta1DefaultBasePath), +} + +var RuntimeconfigDefaultBasePath = "https://runtimeconfig.googleapis.com/v1beta1/" +var RuntimeconfigCustomEndpointEntryKey = "runtimeconfig_custom_endpoint" +var RuntimeconfigCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_RUNTIMECONFIG_CUSTOM_ENDPOINT", + }, RuntimeconfigDefaultBasePath), +} + +var ServiceManagementDefaultBasePath = "https://servicemanagement.googleapis.com/v1/" +var ServiceManagementCustomEndpointEntryKey = "service_management_custom_endpoint" +var ServiceManagementCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_SERVICE_MANAGEMENT_CUSTOM_ENDPOINT", + }, ServiceManagementDefaultBasePath), +} + +<% unless version == 'ga' -%> +var ServiceNetworkingDefaultBasePath = "https://servicenetworking.googleapis.com/v1beta/" +var ServiceNetworkingCustomEndpointEntryKey = "service_networking_custom_endpoint" +var ServiceNetworkingCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_SERVICE_NETWORKING_CUSTOM_ENDPOINT", + }, ServiceNetworkingDefaultBasePath), +} + +<% end -%> +var ServiceUsageDefaultBasePath = "https://serviceusage.googleapis.com/v1/" +var ServiceUsageCustomEndpointEntryKey = "service_usage_custom_endpoint" +var ServiceUsageCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_SERVICE_USAGE_CUSTOM_ENDPOINT", + }, ServiceUsageDefaultBasePath), +} + +var StorageTransferDefaultBasePath = "https://storagetransfer.googleapis.com/v1/" +var StorageTransferCustomEndpointEntryKey = "storage_transfer_custom_endpoint" +var StorageTransferCustomEndpointEntry = &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateCustomEndpoint, + DefaultFunc: schema.MultiEnvDefaultFunc([]string{ + "GOOGLE_STORAGE_TRANSFER_CUSTOM_ENDPOINT", + }, StorageTransferDefaultBasePath), +} + +func validateCustomEndpoint(v interface{}, k string) (ws []string, errors []error) { + re := `.*/[^/]+/$` + return validateRegexp(re)(v, k) +} diff --git a/third_party/terraform/utils/provider_test.go.erb b/third_party/terraform/utils/provider_test.go.erb index 6a892c703788..98a65eebcdc7 100644 --- a/third_party/terraform/utils/provider_test.go.erb +++ b/third_party/terraform/utils/provider_test.go.erb @@ -164,6 +164,53 @@ func TestProvider_loadCredentialsFromJSON(t *testing.T) { } } +func TestAccProviderBasePath_setBasePath(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeAddressDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProviderBasePath_setBasePath("https://www.googleapis.com/compute/beta/", acctest.RandString(10)), + }, + { + ResourceName: "google_compute_address.default", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccProviderBasePath_setInvalidBasePath(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeAddressDestroy, + Steps: []resource.TestStep{ + { + Config: testAccProviderBasePath_setBasePath("https://www.example.com/compute/beta/", acctest.RandString(10)), + ExpectError: regexp.MustCompile("got HTTP response code 404 with body"), + }, + }, + }) +} + +func testAccProviderBasePath_setBasePath(endpoint, name string) string { + return fmt.Sprintf(` +provider "google" { + compute_custom_endpoint = "%s" +} + +resource "google_compute_address" "default" { + name = "address-test-%s" +}`, endpoint, name) +} + // getTestRegion has the same logic as the provider's getRegion, to be used in tests. func getTestRegion(is *terraform.InstanceState, config *Config) (string, error) { if res, ok := is.Attributes["region"]; ok { diff --git a/third_party/terraform/utils/transport.go b/third_party/terraform/utils/transport.go index 23970c47d94b..dd5566d9f4a2 100644 --- a/third_party/terraform/utils/transport.go +++ b/third_party/terraform/utils/transport.go @@ -179,6 +179,15 @@ func buildReplacementFunc(re *regexp.Regexp, d TerraformResourceData, config *Co return fmt.Sprintf("%v", v) } } + + // terraform-google-conversion doesn't provide a provider config in tests. + if config != nil { + // Attempt to draw values from the provider config if it's present. + if f := reflect.Indirect(reflect.ValueOf(config)).FieldByName(m); f.IsValid() { + return f.String() + } + } + return "" } diff --git a/third_party/terraform/website/docs/provider_reference.html.markdown b/third_party/terraform/website/docs/provider_reference.html.markdown index c45b7e47f031..f793c98bde9e 100644 --- a/third_party/terraform/website/docs/provider_reference.html.markdown +++ b/third_party/terraform/website/docs/provider_reference.html.markdown @@ -97,6 +97,13 @@ authenticate HTTP requests to GCP APIs. This is an alternative to `credentials`, and ignores the `scopes` field. If both are specified, `access_token` will be used over the `credentials` field. +* `{{service}}_custom_endpoint` - (Optional) The endpoint for a service's APIs, +such as `compute_custom_endpoint`. Defaults to the production GCP endpoint for +the service. This can be used to configure the Google provider to communicate +with GCP-like APIs such as [the Cloud Functions emulator](https://github.com/googlearchive/cloud-functions-emulator). +Values are expected to include the version of the service, such as +`https://www.googleapis.com/compute/v1/`. + ### Full Reference * `credentials` - (Optional) Either the path to or the contents of a @@ -191,6 +198,76 @@ an access token using the service account key specified in `credentials`. * https://www.googleapis.com/auth/ndev.clouddns.readwrite * https://www.googleapis.com/auth/devstorage.full_control +--- + +* `{{service}}_custom_endpoint` - (Optional) The endpoint for a service's APIs, +such as `compute_custom_endpoint`. Defaults to the production GCP endpoint for +the service. This can be used to configure the Google provider to communicate +with GCP-like APIs such as [the Cloud Functions emulator](https://github.com/googlearchive/cloud-functions-emulator). +Values are expected to include the version of the service, such as +`https://www.googleapis.com/compute/v1/`. + +~> Support for custom endpoints is on a best-effort basis. The underlying +endpoint and default values for a resource can be changed at any time without +being considered a breaking change. + +A full list of configurable keys, their default value (in the `google` provider +followed by `google-beta` if they differ), and an environment variable that can +be used for configuration are below: + +* `access_context_manager_custom_endpoint` (`GOOGLE_ACCESS_CONTEXT_MANAGER_CUSTOM_ENDPOINT`) - `https://accesscontextmanager.googleapis.com/v1/` +* `app_engine_custom_endpoint` (`GOOGLE_APP_ENGINE_CUSTOM_ENDPOINT`) - `https://appengine.googleapis.com/v1/` +* `bigquery_custom_endpoint` (`GOOGLE_BIGQUERY_CUSTOM_ENDPOINT`) - `https://www.googleapis.com/bigquery/v2/` +* `cloud_billing_custom_endpoint` (`GOOGLE_CLOUD_BILLING_CUSTOM_ENDPOINT`) - `https://cloudbilling.googleapis.com/v1/` +* `cloud_build_custom_endpoint` (`GOOGLE_CLOUD_BUILD_CUSTOM_ENDPOINT`) - `https://cloudbuild.googleapis.com/v1/` +* `cloud_functions_custom_endpoint` (`GOOGLE_CLOUD_FUNCTIONS_CUSTOM_ENDPOINT`) - `https://cloudfunctions.googleapis.com/v1/` +* `cloud_iot_custom_endpoint` (`GOOGLE_CLOUD_IOT_CUSTOM_ENDPOINT`) - `https://cloudiot.googleapis.com/v1/` +* `cloud_scheduler_custom_endpoint` (`GOOGLE_CLOUD_SCHEDULER_CUSTOM_ENDPOINT`) - `https://cloudscheduler.googleapis.com/v1/` +* `composer_custom_endpoint` (`GOOGLE_COMPOSER_CUSTOM_ENDPOINT`) - `https://composer.googleapis.com/v1beta1/` +* `compute_custom_endpoint` (`GOOGLE_COMPUTE_CUSTOM_ENDPOINT`) - `https://www.googleapis.com/compute/v1/` | `https://www.googleapis.com/compute/beta/` +* `compute_beta_custom_endpoint` (`GOOGLE_COMPUTE_BETA_CUSTOM_ENDPOINT`) - `https://www.googleapis.com/compute/beta/` +* `container_custom_endpoint` (`GOOGLE_CONTAINER_CUSTOM_ENDPOINT`) - `https://container.googleapis.com/v1/` +* `container_beta_custom_endpoint` (`GOOGLE_CONTAINER_BETA_CUSTOM_ENDPOINT`) - `https://container.googleapis.com/v1beta1/` +* `dataproc_custom_endpoint` (`GOOGLE_DATAPROC_CUSTOM_ENDPOINT`) - `https://dataproc.googleapis.com/v1/` +* `dataproc_beta_custom_endpoint` (`GOOGLE_DATAPROC_BETA_CUSTOM_ENDPOINT`) - `https://dataproc.googleapis.com/v1beta2/` +* `dataflow_custom_endpoint` (`GOOGLE_DATAFLOW_CUSTOM_ENDPOINT`) - `https://dataflow.googleapis.com/v1b3/` +* `dns_custom_endpoint` (`GOOGLE_DNS_CUSTOM_ENDPOINT`) - `https://www.googleapis.com/dns/v1/` | `https://www.googleapis.com/dns/v1beta2/` +* `dns_beta_custom_endpoint` (`GOOGLE_DNS_BETA_CUSTOM_ENDPOINT`) - `https://www.googleapis.com/dns/v1beta2/` +* `filestore_custom_endpoint` (`GOOGLE_FILESTORE_CUSTOM_ENDPOINT`) - `https://file.googleapis.com/v1/` +* `firestore_custom_endpoint` (`GOOGLE_FIRESTORE_CUSTOM_ENDPOINT`) - `https://firestore.googleapis.com/v1/` +* `iam_custom_endpoint` (`GOOGLE_IAM_CUSTOM_ENDPOINT`) - `https://iam.googleapis.com/v1/` +* `iam_credentials_custom_endpoint` (`GOOGLE_IAM_CREDENTIALS_CUSTOM_ENDPOINT`) - `https://iamcredentials.googleapis.com/v1/` +* `kms_custom_endpoint` (`GOOGLE_KMS_CUSTOM_ENDPOINT`) - `https://cloudkms.googleapis.com/v1/` +* `logging_custom_endpoint` (`GOOGLE_LOGGING_CUSTOM_ENDPOINT`) - `https://logging.googleapis.com/v2/` +* `monitoring_custom_endpoint` (`GOOGLE_MONITORING_CUSTOM_ENDPOINT`) - `https://monitoring.googleapis.com/v3/` +* `pubsub_custom_endpoint` (`GOOGLE_PUBSUB_CUSTOM_ENDPOINT`) - `https://pubsub.googleapis.com/v1/` +* `redis_custom_endpoint` (`GOOGLE_REDIS_CUSTOM_ENDPOINT`) - `https://redis.googleapis.com/v1/` | `https://redis.googleapis.com/v1beta1/` +* `resource_manager_custom_endpoint` (`GOOGLE_RESOURCE_MANAGER_CUSTOM_ENDPOINT`) - `https://cloudresourcemanager.googleapis.com/v1/` +* `resource_manager_v2beta1_custom_endpoint` (`GOOGLE_RESOURCE_MANAGER_V2BETA1_CUSTOM_ENDPOINT`) - `https://cloudresourcemanager.googleapis.com/v2beta1/` +* `runtimeconfig_custom_endpoint` (`GOOGLE_RUNTIMECONFIG_CUSTOM_ENDPOINT`) - `https://runtimeconfig.googleapis.com/v1beta1/` +* `service_management_custom_endpoint` (`GOOGLE_SERVICE_MANAGEMENT_CUSTOM_ENDPOINT`) - `https://servicemanagement.googleapis.com/v1/` +* `service_usage_custom_endpoint` (`GOOGLE_SERVICE_USAGE_CUSTOM_ENDPOINT`) - `https://serviceusage.googleapis.com/v1/` +* `source_repo_custom_endpoint` (`GOOGLE_SOURCE_REPO_CUSTOM_ENDPOINT`) - `https://sourcerepo.googleapis.com/v1/` +* `spanner_custom_endpoint` (`GOOGLE_SPANNER_CUSTOM_ENDPOINT`) - `https://spanner.googleapis.com/v1/` +* `sql_custom_endpoint` (`GOOGLE_SQL_CUSTOM_ENDPOINT`) - `https://www.googleapis.com/sql/v1beta4/` +* `storage_custom_endpoint` (`GOOGLE_STORAGE_CUSTOM_ENDPOINT`) - `https://www.googleapis.com/storage/v1/` +* `storage_transfer_custom_endpoint` (`GOOGLE_STORAGE_TRANSFER_CUSTOM_ENDPOINT`) - `https://storagetransfer.googleapis.com/v1/` +* `tpu_custom_endpoint` (`GOOGLE_TPU_CUSTOM_ENDPOINT`) - `https://tpu.googleapis.com/v1/` + +The following keys are available exclusively in the `google-beta` provider: + +* `binary_authorization_custom_endpoint` (`GOOGLE_BINARY_AUTHORIZATION_CUSTOM_ENDPOINT`) - `https://binaryauthorization.googleapis.com/v1beta1/` +* `container_analysis_custom_endpoint` (`GOOGLE_CONTAINER_ANALYSIS_CUSTOM_ENDPOINT`) - `https://containeranalysis.googleapis.com/v1beta1/` +* `iap_custom_endpoint` (`GOOGLE_IAP_CUSTOM_ENDPOINT`) - `https://iap.googleapis.com/v1beta1/` +* `monitoring_custom_endpoint` (`GOOGLE_MONITORING_CUSTOM_ENDPOINT`) - `https://monitoring.googleapis.com/v3/` +* `security_scanner_custom_endpoint` (`GOOGLE_SECURITY_SCANNER_CUSTOM_ENDPOINT`) - `https://websecurityscanner.googleapis.com/v1beta/` +* `service_networking_custom_endpoint` (`GOOGLE_SERVICE_NETWORKING_CUSTOM_ENDPOINT`) - `https://servicenetworking.googleapis.com/v1beta/` + +-> Note that some endpoints are a versioned variant of another. These exist in +cases where the `google` provider uses multiple distinct endpoints, and both +need to be set. Additionally, in `google-beta`, they'll often use the same value +as their versioned counterpart but that won't necessarily always be the case. + [OAuth 2.0 access token]: https://developers.google.com/identity/protocols/OAuth2 [service account key file]: https://cloud.google.com/iam/docs/creating-managing-service-account-keys [manage key files using the Cloud Console]: https://console.cloud.google.com/apis/credentials/serviceaccountkey