From 964631642a57ab546bfab7877fedbc5a39c7d1ad Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Wed, 28 Jul 2021 22:14:48 +0000 Subject: [PATCH] Backfill support for `user_project_override` and `billing_project` for `google_service_networking_connection` (#4995) * add project overrides to service networking Co-authored-by: upodroid * add billing_project overrides * add override to docs Signed-off-by: Modular Magician --- .changelog/4995.txt | 3 + .../resource_service_networking_connection.go | 71 +++++++++++++++++-- ...urce_service_networking_connection_test.go | 8 ++- google/service_networking_operation.go | 16 +++-- ...ervice_networking_connection.html.markdown | 5 ++ 5 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 .changelog/4995.txt diff --git a/.changelog/4995.txt b/.changelog/4995.txt new file mode 100644 index 00000000000..26594d96880 --- /dev/null +++ b/.changelog/4995.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +servicenetworking: added support for `user_project_override` and `billing_project ` to `google_service_networking_connection` +``` diff --git a/google/resource_service_networking_connection.go b/google/resource_service_networking_connection.go index 122050e44f7..65396df5b93 100644 --- a/google/resource_service_networking_connection.go +++ b/google/resource_service_networking_connection.go @@ -83,6 +83,12 @@ func resourceServiceNetworkingConnectionCreate(d *schema.ResourceData, meta inte ReservedPeeringRanges: convertStringArr(d.Get("reserved_peering_ranges").([]interface{})), } + networkFieldValue, err := ParseNetworkFieldValue(network, d, config) + if err != nil { + return errwrap.Wrapf("Failed to retrieve network field value, err: {{err}}", err) + } + project := networkFieldValue.Project + parentService := formatParentService(d.Get("service").(string)) // We use Patch instead of Create, because we're getting // "Error waiting for Create Service Networking Connection: @@ -98,12 +104,22 @@ func resourceServiceNetworkingConnectionCreate(d *schema.ResourceData, meta inte // The API docs don't specify that you can do connections/-, // but that's what gcloud does, and it's easier than grabbing // the connection name. - op, err := config.NewServiceNetworkingClient(userAgent).Services.Connections.Patch(parentService+"/connections/-", connection).UpdateMask("reservedPeeringRanges").Force(true).Do() + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + project = bp + } + + createCall := config.NewServiceNetworkingClient(userAgent).Services.Connections.Patch(parentService+"/connections/-", connection).UpdateMask("reservedPeeringRanges").Force(true) + if config.UserProjectOverride { + createCall.Header().Add("X-Goog-User-Project", project) + } + op, err := createCall.Do() if err != nil { return err } - if err := serviceNetworkingOperationWaitTime(config, op, "Create Service Networking Connection", userAgent, d.Timeout(schema.TimeoutCreate)); err != nil { + if err := serviceNetworkingOperationWaitTime(config, op, "Create Service Networking Connection", userAgent, project, d.Timeout(schema.TimeoutCreate)); err != nil { return err } @@ -133,9 +149,24 @@ func resourceServiceNetworkingConnectionRead(d *schema.ResourceData, meta interf return errwrap.Wrapf("Failed to find Service Networking Connection, err: {{err}}", err) } + network := d.Get("network").(string) + networkFieldValue, err := ParseNetworkFieldValue(network, d, config) + if err != nil { + return errwrap.Wrapf("Failed to retrieve network field value, err: {{err}}", err) + } + project := networkFieldValue.Project + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + project = bp + } + parentService := formatParentService(connectionId.Service) - response, err := config.NewServiceNetworkingClient(userAgent).Services.Connections.List(parentService). - Network(serviceNetworkingNetworkName).Do() + readCall := config.NewServiceNetworkingClient(userAgent).Services.Connections.List(parentService).Network(serviceNetworkingNetworkName) + if config.UserProjectOverride { + readCall.Header().Add("X-Goog-User-Project", project) + } + response, err := readCall.Do() if err != nil { return err } @@ -195,13 +226,29 @@ func resourceServiceNetworkingConnectionUpdate(d *schema.ResourceData, meta inte ReservedPeeringRanges: convertStringArr(d.Get("reserved_peering_ranges").([]interface{})), } + networkFieldValue, err := ParseNetworkFieldValue(network, d, config) + if err != nil { + return errwrap.Wrapf("Failed to retrieve network field value, err: {{err}}", err) + } + project := networkFieldValue.Project + // The API docs don't specify that you can do connections/-, but that's what gcloud does, // and it's easier than grabbing the connection name. - op, err := config.NewServiceNetworkingClient(userAgent).Services.Connections.Patch(parentService+"/connections/-", connection).UpdateMask("reservedPeeringRanges").Force(true).Do() + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + project = bp + } + + patchCall := config.NewServiceNetworkingClient(userAgent).Services.Connections.Patch(parentService+"/connections/-", connection).UpdateMask("reservedPeeringRanges").Force(true) + if config.UserProjectOverride { + patchCall.Header().Add("X-Goog-User-Project", project) + } + op, err := patchCall.Do() if err != nil { return err } - if err := serviceNetworkingOperationWaitTime(config, op, "Update Service Networking Connection", userAgent, d.Timeout(schema.TimeoutUpdate)); err != nil { + if err := serviceNetworkingOperationWaitTime(config, op, "Update Service Networking Connection", userAgent, project, d.Timeout(schema.TimeoutUpdate)); err != nil { return err } } @@ -322,7 +369,17 @@ func retrieveServiceNetworkingNetworkName(d *schema.ResourceData, config *Config return "", fmt.Errorf("Could not determine project") } log.Printf("[DEBUG] Retrieving project number by doing a GET with the project id, as required by service networking") - project, err := config.NewResourceManagerClient(userAgent).Projects.Get(pid).Do() + // err == nil indicates that the billing_project value was found + billingProject := pid + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + getProjectCall := config.NewResourceManagerClient(userAgent).Projects.Get(pid) + if config.UserProjectOverride { + getProjectCall.Header().Add("X-Goog-User-Project", billingProject) + } + project, err := getProjectCall.Do() if err != nil { // note: returning a wrapped error is part of this method's contract! // https://blog.golang.org/go1.13-errors diff --git a/google/resource_service_networking_connection_test.go b/google/resource_service_networking_connection_test.go index 4a0a2d0f2e1..b67f3e29ec0 100644 --- a/google/resource_service_networking_connection_test.go +++ b/google/resource_service_networking_connection_test.go @@ -71,9 +71,11 @@ func testServiceNetworkingConnectionDestroy(t *testing.T, parent, network string config := googleProviderConfig(t) parentService := "services/" + parent networkName := fmt.Sprintf("projects/%s/global/networks/%s", getTestProjectFromEnv(), network) - - response, err := config.NewServiceNetworkingClient(config.userAgent).Services.Connections.List(parentService). - Network(networkName).Do() + listCall := config.NewServiceNetworkingClient(config.userAgent).Services.Connections.List(parentService).Network(networkName) + if config.UserProjectOverride { + listCall.Header().Add("X-Goog-User-Project", getTestProjectFromEnv()) + } + response, err := listCall.Do() if err != nil { return err } diff --git a/google/service_networking_operation.go b/google/service_networking_operation.go index fa7695b3676..c201158132c 100644 --- a/google/service_networking_operation.go +++ b/google/service_networking_operation.go @@ -7,17 +7,25 @@ import ( ) type ServiceNetworkingOperationWaiter struct { - Service *servicenetworking.APIService + Service *servicenetworking.APIService + Project string + UserProjectOverride bool CommonOperationWaiter } func (w *ServiceNetworkingOperationWaiter) QueryOp() (interface{}, error) { - return w.Service.Operations.Get(w.Op.Name).Do() + opGetCall := w.Service.Operations.Get(w.Op.Name) + if w.UserProjectOverride { + opGetCall.Header().Add("X-Goog-User-Project", w.Project) + } + return opGetCall.Do() } -func serviceNetworkingOperationWaitTime(config *Config, op *servicenetworking.Operation, activity, userAgent string, timeout time.Duration) error { +func serviceNetworkingOperationWaitTime(config *Config, op *servicenetworking.Operation, activity, userAgent, project string, timeout time.Duration) error { w := &ServiceNetworkingOperationWaiter{ - Service: config.NewServiceNetworkingClient(userAgent), + Service: config.NewServiceNetworkingClient(userAgent), + Project: project, + UserProjectOverride: config.UserProjectOverride, } if err := w.SetOp(op); err != nil { diff --git a/website/docs/r/service_networking_connection.html.markdown b/website/docs/r/service_networking_connection.html.markdown index 25f56f19aa2..cd490b07d49 100644 --- a/website/docs/r/service_networking_connection.html.markdown +++ b/website/docs/r/service_networking_connection.html.markdown @@ -56,3 +56,8 @@ ServiceNetworkingConnection can be imported using any of these accepted formats * terraform import google_service_networking_connection.peering_connection {{peering-network}}:{{service}} * terraform import google_service_networking_connection.peering_connection /projects/{{project}}/global/networks/{{peering-network}}:{{service}} + + +## User Project Overrides + +This resource supports [User Project Overrides](https://www.terraform.io/docs/providers/google/guides/provider_reference.html#user_project_override). \ No newline at end of file