Add google_logging_organization_sink resource (#923)

* Add google_logging_organization_sink resource

* cleanup
This commit is contained in:
Quinn Vallance 2018-01-08 18:15:30 -05:00 committed by Vincent Roseberry
parent de23b59463
commit 5a557546a7
5 changed files with 350 additions and 0 deletions

View File

@ -137,6 +137,7 @@ func Provider() terraform.ResourceProvider {
"google_folder_iam_policy": ResourceIamPolicyWithImport(IamFolderSchema, NewFolderIamUpdater, FolderIdParseFunc),
"google_folder_organization_policy": resourceGoogleFolderOrganizationPolicy(),
"google_logging_billing_account_sink": resourceLoggingBillingAccountSink(),
"google_logging_organization_sink": resourceLoggingOrganizationSink(),
"google_logging_folder_sink": resourceLoggingFolderSink(),
"google_logging_project_sink": resourceLoggingProjectSink(),
"google_kms_key_ring": resourceKmsKeyRing(),

View File

@ -0,0 +1,90 @@
package google
import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceLoggingOrganizationSink() *schema.Resource {
schm := &schema.Resource{
Create: resourceLoggingOrganizationSinkCreate,
Read: resourceLoggingOrganizationSinkRead,
Delete: resourceLoggingOrganizationSinkDelete,
Update: resourceLoggingOrganizationSinkUpdate,
Schema: resourceLoggingSinkSchema(),
}
schm.Schema["org_id"] = &schema.Schema{
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: optionalPrefixSuppress("organizations/"),
}
schm.Schema["include_children"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
Default: false,
}
return schm
}
func resourceLoggingOrganizationSinkCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
org := d.Get("org_id").(string)
id, sink := expandResourceLoggingSink(d, "organizations", org)
sink.IncludeChildren = d.Get("include_children").(bool)
// Must use a unique writer, since all destinations are in projects.
// The API will reject any requests that don't explicitly set 'uniqueWriterIdentity' to true.
_, err := config.clientLogging.Organizations.Sinks.Create(id.parent(), sink).UniqueWriterIdentity(true).Do()
if err != nil {
return err
}
d.SetId(id.canonicalId())
return resourceLoggingOrganizationSinkRead(d, meta)
}
func resourceLoggingOrganizationSinkRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
sink, err := config.clientLogging.Organizations.Sinks.Get(d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Organization Logging Sink %s", d.Get("name").(string)))
}
flattenResourceLoggingSink(d, sink)
d.Set("include_children", sink.IncludeChildren)
return nil
}
func resourceLoggingOrganizationSinkUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
sink := expandResourceLoggingSinkForUpdate(d)
// It seems the API might actually accept an update for include_children; this is not in the list of updatable
// properties though and might break in the future. Always include the value to prevent it changing.
sink.IncludeChildren = d.Get("include_children").(bool)
sink.ForceSendFields = append(sink.ForceSendFields, "IncludeChildren")
// The API will reject any requests that don't explicitly set 'uniqueWriterIdentity' to true.
_, err := config.clientLogging.Organizations.Sinks.Patch(d.Id(), sink).UniqueWriterIdentity(true).Do()
if err != nil {
return err
}
return resourceLoggingOrganizationSinkRead(d, meta)
}
func resourceLoggingOrganizationSinkDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
_, err := config.clientLogging.Projects.Sinks.Delete(d.Id()).Do()
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,180 @@
package google
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"google.golang.org/api/logging/v2"
"strconv"
)
func TestAccLoggingOrganizationSink_basic(t *testing.T) {
t.Parallel()
org := getTestOrgFromEnv(t)
sinkName := "tf-test-sink-" + acctest.RandString(10)
bucketName := "tf-test-sink-bucket-" + acctest.RandString(10)
var sink logging.LogSink
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLoggingOrganizationSinkDestroy,
Steps: []resource.TestStep{
{
Config: testAccLoggingOrganizationSink_basic(sinkName, bucketName, org),
Check: resource.ComposeTestCheckFunc(
testAccCheckLoggingOrganizationSinkExists("google_logging_organization_sink.basic", &sink),
testAccCheckLoggingOrganizationSink(&sink, "google_logging_organization_sink.basic"),
),
},
},
})
}
func TestAccLoggingOrganizationSink_update(t *testing.T) {
t.Parallel()
org := getTestOrgFromEnv(t)
sinkName := "tf-test-sink-" + acctest.RandString(10)
bucketName := "tf-test-sink-bucket-" + acctest.RandString(10)
updatedBucketName := "tf-test-sink-bucket-" + acctest.RandString(10)
var sinkBefore, sinkAfter logging.LogSink
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckLoggingOrganizationSinkDestroy,
Steps: []resource.TestStep{
{
Config: testAccLoggingOrganizationSink_update(sinkName, bucketName, org),
Check: resource.ComposeTestCheckFunc(
testAccCheckLoggingOrganizationSinkExists("google_logging_organization_sink.update", &sinkBefore),
testAccCheckLoggingOrganizationSink(&sinkBefore, "google_logging_organization_sink.update"),
),
}, {
Config: testAccLoggingOrganizationSink_update(sinkName, updatedBucketName, org),
Check: resource.ComposeTestCheckFunc(
testAccCheckLoggingOrganizationSinkExists("google_logging_organization_sink.update", &sinkAfter),
testAccCheckLoggingOrganizationSink(&sinkAfter, "google_logging_organization_sink.update"),
),
},
},
})
// Destination should have changed, but WriterIdentity should be the same
if sinkBefore.Destination == sinkAfter.Destination {
t.Errorf("Expected Destination to change, but it didn't: Destination = %#v", sinkBefore.Destination)
}
if sinkBefore.WriterIdentity != sinkAfter.WriterIdentity {
t.Errorf("Expected WriterIdentity to be the same, but it differs: before = %#v, after = %#v",
sinkBefore.WriterIdentity, sinkAfter.WriterIdentity)
}
}
func testAccCheckLoggingOrganizationSinkDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_logging_organization_sink" {
continue
}
attributes := rs.Primary.Attributes
_, err := config.clientLogging.Organizations.Sinks.Get(attributes["id"]).Do()
if err == nil {
return fmt.Errorf("organization sink still exists")
}
}
return nil
}
func testAccCheckLoggingOrganizationSinkExists(n string, sink *logging.LogSink) resource.TestCheckFunc {
return func(s *terraform.State) error {
attributes, err := getResourceAttributes(n, s)
if err != nil {
return err
}
config := testAccProvider.Meta().(*Config)
si, err := config.clientLogging.Organizations.Sinks.Get(attributes["id"]).Do()
if err != nil {
return err
}
*sink = *si
return nil
}
}
func testAccCheckLoggingOrganizationSink(sink *logging.LogSink, n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
attributes, err := getResourceAttributes(n, s)
if err != nil {
return err
}
if sink.Destination != attributes["destination"] {
return fmt.Errorf("mismatch on destination: api has %s but client has %s", sink.Destination, attributes["destination"])
}
if sink.Filter != attributes["filter"] {
return fmt.Errorf("mismatch on filter: api has %s but client has %s", sink.Filter, attributes["filter"])
}
if sink.WriterIdentity != attributes["writer_identity"] {
return fmt.Errorf("mismatch on writer_identity: api has %s but client has %s", sink.WriterIdentity, attributes["writer_identity"])
}
includeChildren := false
if attributes["include_children"] != "" {
includeChildren, err = strconv.ParseBool(attributes["include_children"])
if err != nil {
return err
}
}
if sink.IncludeChildren != includeChildren {
return fmt.Errorf("mismatch on include_children: api has %v but client has %v", sink.IncludeChildren, includeChildren)
}
return nil
}
}
func testAccLoggingOrganizationSink_basic(sinkName, bucketName, orgId string) string {
return fmt.Sprintf(`
resource "google_logging_organization_sink" "basic" {
name = "%s"
org_id = "%s"
destination = "storage.googleapis.com/${google_storage_bucket.log-bucket.name}"
filter = "logName=\"projects/%s/logs/compute.googleapis.com%%2Factivity_log\" AND severity>=ERROR"
include_children = true
}
resource "google_storage_bucket" "log-bucket" {
name = "%s"
}`, sinkName, orgId, getTestProjectFromEnv(), bucketName)
}
func testAccLoggingOrganizationSink_update(sinkName, bucketName, orgId string) string {
return fmt.Sprintf(`
resource "google_logging_organization_sink" "update" {
name = "%s"
org_id = "%s"
destination = "storage.googleapis.com/${google_storage_bucket.log-bucket.name}"
filter = "logName=\"projects/%s/logs/compute.googleapis.com%%2Factivity_log\" AND severity>=ERROR"
destination = "storage.googleapis.com/${google_storage_bucket.log-bucket.name}"
include_children = false
}
resource "google_storage_bucket" "log-bucket" {
name = "%s"
}`, sinkName, orgId, getTestProjectFromEnv(), bucketName)
}

View File

@ -0,0 +1,75 @@
---
layout: "google"
page_title: "Google: google_logging_organization_sink"
sidebar_current: "docs-google-logging-organization-sink"
description: |-
Manages a organization-level logging sink.
---
# google\_logging\_organization\_sink
Manages a organization-level logging sink. For more information see
[the official documentation](https://cloud.google.com/logging/docs/) and
[Exporting Logs in the API](https://cloud.google.com/logging/docs/api/tasks/exporting-logs).
Note that you must have the "Logs Configuration Writer" IAM role (`roles/logging.configWriter`)
granted to the credentials used with terraform.
## Example Usage
```hcl
resource "google_logging_organization_sink" "my-sink" {
name = "my-sink"
org_id = "123456789"
# Can export to pubsub, cloud storage, or bigtable
destination = "storage.googleapis.com/${google_storage_bucket.log-bucket.name}"
# Log all WARN or higher severity messages relating to instances
filter = "resource.type = gce_instance AND severity >= WARN"
}
resource "google_storage_bucket" "log-bucket" {
name = "organization-logging-bucket"
}
resource "google_project_iam_binding" "log-writer" {
role = "roles/storage.objectCreator"
members = [
"${google_logging_organization_sink.my-sink.writer_identity}",
]
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the logging sink.
* `org_id` - (Required) The numeric ID of the organization to be exported to the sink.
* `destination` - (Required) The destination of the sink (or, in other words, where logs are written to). Can be a
Cloud Storage bucket, a PubSub topic, or a BigQuery dataset. Examples:
```
"storage.googleapis.com/[GCS_BUCKET]"
"bigquery.googleapis.com/projects/[PROJECT_ID]/datasets/[DATASET]"
"pubsub.googleapis.com/projects/[PROJECT_ID]/topics/[TOPIC_ID]"
```
The writer associated with the sink must have access to write to the above resource.
* `filter` - (Optional) The filter to apply when exporting logs. Only log entries that match the filter are exported.
See [Advanced Log Filters](https://cloud.google.com/logging/docs/view/advanced_filters) for information on how to
write a filter.
* `include_children` - (Optional) Whether or not to include children organizations in the sink export. If true, logs
associated with child projects are also exported; otherwise only logs relating to the provided organization are included.
## Attributes Reference
In addition to the arguments listed above, the following computed attributes are
exported:
* `writer_identity` - The identity associated with this sink. This identity must be granted write access to the
configured `destination`.

View File

@ -448,6 +448,10 @@
<a href="/docs/providers/google/r/logging_billing_account_sink.html">google_logging_billing_account_sink</a>
</li>
<li<%= sidebar_current("docs-google-logging-organization-sink") %>>
<a href="/docs/providers/google/r/logging_organization_sink.html">google_logging_organization_sink</a>
</li>
<li<%= sidebar_current("docs-google-logging-folder-sink") %>>
<a href="/docs/providers/google/r/logging_folder_sink.html">google_logging_folder_sink</a>
</li>