Add 'google_billing_account' data source (#889)

* Add 'google_billing_account' data source.

* Use 'GetResourceNameFromSelfLink'.

* Use 'ConflictsWith' in schema.

* Use pagination for List() API call.

* Add ability to filter by 'open' attribute.

* Don't use 'ForceNew' for data sources.

* Add 'billing_account' argument and make 'name' an output-only attribute.

* Correct error message.
This commit is contained in:
Kit Ewbank 2017-12-21 19:09:58 -05:00 committed by Vincent Roseberry
parent 5498a33d1b
commit a086e70e79
5 changed files with 286 additions and 1 deletions

View File

@ -0,0 +1,131 @@
package google
import (
"fmt"
"net/http"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/cloudbilling/v1"
"google.golang.org/api/googleapi"
)
func dataSourceGoogleBillingAccount() *schema.Resource {
return &schema.Resource{
Read: dataSourceBillingAccountRead,
Schema: map[string]*schema.Schema{
"billing_account": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"display_name"},
},
"display_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ConflictsWith: []string{"billing_account"},
},
"open": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"project_ids": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
}
}
func dataSourceBillingAccountRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
open, openOk := d.GetOkExists("open")
var billingAccount *cloudbilling.BillingAccount
if v, ok := d.GetOk("billing_account"); ok {
resp, err := config.clientBilling.BillingAccounts.Get(canonicalBillingAccountName(v.(string))).Do()
if err != nil {
if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == http.StatusNotFound {
return fmt.Errorf("Billing account not found: %s", v)
}
return fmt.Errorf("Error reading billing account: %s", err)
}
if openOk && resp.Open != open.(bool) {
return fmt.Errorf("Billing account not found: %s", v)
}
billingAccount = resp
} else if v, ok := d.GetOk("display_name"); ok {
token := ""
for paginate := true; paginate; {
resp, err := config.clientBilling.BillingAccounts.List().PageToken(token).Do()
if err != nil {
return fmt.Errorf("Error reading billing accounts: %s", err)
}
for _, ba := range resp.BillingAccounts {
if ba.DisplayName == v.(string) {
if openOk && ba.Open != open.(bool) {
continue
}
if billingAccount != nil {
return fmt.Errorf("More than one matching billing account found")
}
billingAccount = ba
}
}
token = resp.NextPageToken
paginate = token != ""
}
if billingAccount == nil {
return fmt.Errorf("Billing account not found: %s", v)
}
} else {
return fmt.Errorf("one of billing_account or display_name must be set")
}
resp, err := config.clientBilling.BillingAccounts.Projects.List(billingAccount.Name).Do()
if err != nil {
return fmt.Errorf("Error reading billing account projects: %s", err)
}
projectIds := flattenBillingProjects(resp.ProjectBillingInfo)
d.SetId(GetResourceNameFromSelfLink(billingAccount.Name))
d.Set("name", billingAccount.Name)
d.Set("display_name", billingAccount.DisplayName)
d.Set("open", billingAccount.Open)
d.Set("project_ids", projectIds)
return nil
}
func canonicalBillingAccountName(ba string) string {
if strings.HasPrefix(ba, "billingAccounts/") {
return ba
}
return "billingAccounts/" + ba
}
func flattenBillingProjects(billingProjects []*cloudbilling.ProjectBillingInfo) []string {
projectIds := make([]string, len(billingProjects))
for i, billingProject := range billingProjects {
projectIds[i] = billingProject.ProjectId
}
return projectIds
}

View File

@ -0,0 +1,104 @@
package google
import (
"fmt"
"regexp"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
)
func TestAccDataSourceGoogleBillingAccount_byFullName(t *testing.T) {
billingId := getTestBillingAccountFromEnv(t)
name := "billingAccounts/" + billingId
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccCheckGoogleBillingAccount_byName(name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.google_billing_account.acct", "id", billingId),
resource.TestCheckResourceAttr("data.google_billing_account.acct", "name", name),
resource.TestCheckResourceAttr("data.google_billing_account.acct", "open", "true"),
),
},
},
})
}
func TestAccDataSourceGoogleBillingAccount_byShortName(t *testing.T) {
billingId := getTestBillingAccountFromEnv(t)
name := "billingAccounts/" + billingId
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccCheckGoogleBillingAccount_byName(billingId),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.google_billing_account.acct", "id", billingId),
resource.TestCheckResourceAttr("data.google_billing_account.acct", "name", name),
resource.TestCheckResourceAttr("data.google_billing_account.acct", "open", "true"),
),
},
},
})
}
func TestAccDataSourceGoogleBillingAccount_byFullNameClosed(t *testing.T) {
billingId := getTestBillingAccountFromEnv(t)
name := "billingAccounts/" + billingId
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccCheckGoogleBillingAccount_byNameClosed(name),
ExpectError: regexp.MustCompile("Billing account not found: " + name),
},
},
})
}
func TestAccDataSourceGoogleBillingAccount_byDisplayName(t *testing.T) {
name := acctest.RandString(16)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccCheckGoogleBillingAccount_byDisplayName(name),
ExpectError: regexp.MustCompile("Billing account not found: " + name),
},
},
})
}
func testAccCheckGoogleBillingAccount_byName(name string) string {
return fmt.Sprintf(`
data "google_billing_account" "acct" {
billing_account = "%s"
}`, name)
}
func testAccCheckGoogleBillingAccount_byNameClosed(name string) string {
return fmt.Sprintf(`
data "google_billing_account" "acct" {
billing_account = "%s"
open = false
}`, name)
}
func testAccCheckGoogleBillingAccount_byDisplayName(name string) string {
return fmt.Sprintf(`
data "google_billing_account" "acct" {
display_name = "%s"
}`, name)
}

View File

@ -60,6 +60,7 @@ func Provider() terraform.ResourceProvider {
},
DataSourcesMap: map[string]*schema.Resource{
"google_billing_account": dataSourceGoogleBillingAccount(),
"google_dns_managed_zone": dataSourceDnsManagedZone(),
"google_client_config": dataSourceGoogleClientConfig(),
"google_compute_address": dataSourceGoogleComputeAddress(),

View File

@ -0,0 +1,46 @@
---
layout: "google"
page_title: "Google: google_billing_account"
sidebar_current: "docs-google-datasource-billing-account"
description: |-
Get information about a Google Billing Account.
---
# google\_billing\_account
Use this data source to get information about a Google Billing Account.
```hcl
data "google_billing_account" "acct" {
display_name = "My Billing Account"
open = true
}
resource "google_project" "my_project" {
name = "My Project"
project_id = "your-project-id"
org_id = "1234567"
billing_account = "${data.google_billing_account.acct.id}"
}
```
## Argument Reference
The arguments of this data source act as filters for querying the available billing accounts.
The given filters must match exactly one billing account whose data will be exported as attributes.
The following arguments are supported:
* `billing_account` (Optional) - The name of the billing account in the form `{billing_account_id}` or `billingAccounts/{billing_account_id}`.
* `display_name` (Optional) - The display name of the billing account.
* `open` (Optional) - `true` if the billing account is open, `false` if the billing account is closed.
~> **NOTE:** One of `billing_account` or `display_name` must be specified.
## Attributes Reference
The following additional attributes are exported:
* `id` - The billing account ID.
* `name` - The resource name of the billing account in the form `billingAccounts/{billing_account_id}`.
* `project_ids` - The IDs of any projects associated with the billing account.

View File

@ -13,6 +13,9 @@
<li<%= sidebar_current("docs-google-datasource") %>>
<a href="#">Google Cloud Platform Data Sources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-google-datasource-billing-account") %>>
<a href="/docs/providers/google/d/google_billing_account.html">google_billing_account</a>
</li>
<li<%= sidebar_current("docs-google-datasource-client-config") %>>
<a href="/docs/providers/google/d/datasource_client_config.html">google_client_config</a>
</li>
@ -136,7 +139,7 @@
</li>
<li<%= sidebar_current("docs-google-service-account") %>>
<a href="/docs/providers/google/r/google_service_account.html">google_service_account</a>
</li>
</li>
<li<%= sidebar_current("docs-google-service-account-iam") %>>
<a href="/docs/providers/google/r/google_service_account_iam.html">google_service_account_iam_binding</a>
</li>