Skip to content

Commit

Permalink
Add pubsubConfigs to cloud source repository
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <magic-modules@google.com>
  • Loading branch information
c2thorn authored and modular-magician committed Dec 3, 2019
1 parent 1c9f5e6 commit 5c75615
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 0 deletions.
169 changes: 169 additions & 0 deletions google/resource_source_repo_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ import (
"time"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceSourceRepoRepository() *schema.Resource {
return &schema.Resource{
Create: resourceSourceRepoRepositoryCreate,
Read: resourceSourceRepoRepositoryRead,
Update: resourceSourceRepoRepositoryUpdate,
Delete: resourceSourceRepoRepositoryDelete,

Importer: &schema.ResourceImporter{
Expand All @@ -37,6 +39,7 @@ func resourceSourceRepoRepository() *schema.Resource {

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(4 * time.Minute),
Update: schema.DefaultTimeout(4 * time.Minute),
Delete: schema.DefaultTimeout(4 * time.Minute),
},

Expand All @@ -48,6 +51,36 @@ func resourceSourceRepoRepository() *schema.Resource {
Description: `Resource name of the repository, of the form '{{repo}}'.
The repo name may contain slashes. eg, 'name/with/slash'`,
},
"pubsub_configs": {
Type: schema.TypeSet,
Optional: true,
Description: `How this repository publishes a change in the repository through Cloud Pub/Sub.
Keyed by the topic names.`,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"topic": {
Type: schema.TypeString,
Required: true,
},
"message_format": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"PROTOBUF", "JSON", ""}, false),
Description: `The format of the Cloud Pub/Sub messages.
- PROTOBUF: The message payload is a serialized protocol buffer of SourceRepoEvent.
- JSON: The message payload is a JSON string of SourceRepoEvent.`,
},
"service_account_email": {
Type: schema.TypeString,
Optional: true,
Description: `Email address of the service account used for publishing Cloud Pub/Sub messages.
This service account needs to be in the same project as the PubsubConfig. When added,
the caller needs to have iam.serviceAccounts.actAs permission on this service account.
If unspecified, it defaults to the compute engine default service account.`,
},
},
},
},
"size": {
Type: schema.TypeInt,
Computed: true,
Expand Down Expand Up @@ -78,6 +111,12 @@ func resourceSourceRepoRepositoryCreate(d *schema.ResourceData, meta interface{}
} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
obj["name"] = nameProp
}
pubsubConfigsProp, err := expandSourceRepoRepositoryPubsubConfigs(d.Get("pubsub_configs"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("pubsub_configs"); !isEmptyValue(reflect.ValueOf(pubsubConfigsProp)) && (ok || !reflect.DeepEqual(v, pubsubConfigsProp)) {
obj["pubsubConfigs"] = pubsubConfigsProp
}

url, err := replaceVars(d, config, "{{SourceRepoBasePath}}projects/{{project}}/repos")
if err != nil {
Expand All @@ -103,6 +142,12 @@ func resourceSourceRepoRepositoryCreate(d *schema.ResourceData, meta interface{}

log.Printf("[DEBUG] Finished creating Repository %q: %#v", d.Id(), res)

if v, ok := d.GetOkExists("pubsub_configs"); !isEmptyValue(reflect.ValueOf(pubsubConfigsProp)) && (ok || !reflect.DeepEqual(v, pubsubConfigsProp)) {
log.Printf("[DEBUG] Calling update after create to patch in pubsub_configs")
// pubsub_configs cannot be added on create
return resourceSourceRepoRepositoryUpdate(d, meta)
}

return resourceSourceRepoRepositoryRead(d, meta)
}

Expand Down Expand Up @@ -136,10 +181,60 @@ func resourceSourceRepoRepositoryRead(d *schema.ResourceData, meta interface{})
if err := d.Set("size", flattenSourceRepoRepositorySize(res["size"], d)); err != nil {
return fmt.Errorf("Error reading Repository: %s", err)
}
if err := d.Set("pubsub_configs", flattenSourceRepoRepositoryPubsubConfigs(res["pubsubConfigs"], d)); err != nil {
return fmt.Errorf("Error reading Repository: %s", err)
}

return nil
}

func resourceSourceRepoRepositoryUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

project, err := getProject(d, config)
if err != nil {
return err
}

obj := make(map[string]interface{})
pubsubConfigsProp, err := expandSourceRepoRepositoryPubsubConfigs(d.Get("pubsub_configs"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("pubsub_configs"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, pubsubConfigsProp)) {
obj["pubsubConfigs"] = pubsubConfigsProp
}

obj, err = resourceSourceRepoRepositoryUpdateEncoder(d, meta, obj)
if err != nil {
return err
}

url, err := replaceVars(d, config, "{{SourceRepoBasePath}}projects/{{project}}/repos/{{name}}")
if err != nil {
return err
}

log.Printf("[DEBUG] Updating Repository %q: %#v", d.Id(), obj)
updateMask := []string{}

if d.HasChange("pubsub_configs") {
updateMask = append(updateMask, "pubsubConfigs")
}
// updateMask is a URL parameter but not present in the schema, so replaceVars
// won't set it
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
if err != nil {
return err
}
_, err = sendRequestWithTimeout(config, "PATCH", project, url, obj, d.Timeout(schema.TimeoutUpdate))

if err != nil {
return fmt.Errorf("Error updating Repository %q: %s", d.Id(), err)
}

return resourceSourceRepoRepositoryRead(d, meta)
}

func resourceSourceRepoRepositoryDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

Expand Down Expand Up @@ -209,6 +304,80 @@ func flattenSourceRepoRepositorySize(v interface{}, d *schema.ResourceData) inte
return v
}

func flattenSourceRepoRepositoryPubsubConfigs(v interface{}, d *schema.ResourceData) interface{} {
if v == nil {
return v
}
l := v.(map[string]interface{})
transformed := make([]interface{}, 0, len(l))
for k, raw := range l {
original := raw.(map[string]interface{})
transformed = append(transformed, map[string]interface{}{
"topic": k,
"message_format": flattenSourceRepoRepositoryPubsubConfigsMessageFormat(original["messageFormat"], d),
"service_account_email": flattenSourceRepoRepositoryPubsubConfigsServiceAccountEmail(original["serviceAccountEmail"], d),
})
}
return transformed
}
func flattenSourceRepoRepositoryPubsubConfigsMessageFormat(v interface{}, d *schema.ResourceData) interface{} {
return v
}

func flattenSourceRepoRepositoryPubsubConfigsServiceAccountEmail(v interface{}, d *schema.ResourceData) interface{} {
return v
}

func expandSourceRepoRepositoryName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return replaceVars(d, config, "projects/{{project}}/repos/{{name}}")
}

func expandSourceRepoRepositoryPubsubConfigs(v interface{}, d TerraformResourceData, config *Config) (map[string]interface{}, error) {
if v == nil {
return map[string]interface{}{}, nil
}
m := make(map[string]interface{})
for _, raw := range v.(*schema.Set).List() {
original := raw.(map[string]interface{})
transformed := make(map[string]interface{})

transformedMessageFormat, err := expandSourceRepoRepositoryPubsubConfigsMessageFormat(original["message_format"], d, config)
if err != nil {
return nil, err
}
transformed["messageFormat"] = transformedMessageFormat
transformedServiceAccountEmail, err := expandSourceRepoRepositoryPubsubConfigsServiceAccountEmail(original["service_account_email"], d, config)
if err != nil {
return nil, err
}
transformed["serviceAccountEmail"] = transformedServiceAccountEmail

m[original["topic"].(string)] = transformed
}
return m, nil
}

func expandSourceRepoRepositoryPubsubConfigsMessageFormat(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func expandSourceRepoRepositoryPubsubConfigsServiceAccountEmail(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func resourceSourceRepoRepositoryUpdateEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
// Add "topic" field using pubsubConfig map key
pubsubConfigsVal := obj["pubsubConfigs"]
if pubsubConfigsVal != nil {
pubsubConfigs := pubsubConfigsVal.(map[string]interface{})
for key := range pubsubConfigs {
config := pubsubConfigs[key].(map[string]interface{})
config["topic"] = key
}
}

// Nest request body in "repo" field
newObj := make(map[string]interface{})
newObj["repo"] = obj
return newObj, nil
}
46 changes: 46 additions & 0 deletions google/resource_source_repo_repository_generated_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,52 @@ resource "google_sourcerepo_repository" "my-repo" {
`, context)
}

func TestAccSourceRepoRepository_sourcerepoRepositoryFullExample(t *testing.T) {
t.Parallel()

context := map[string]interface{}{
"random_suffix": acctest.RandString(10),
}

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckSourceRepoRepositoryDestroy,
Steps: []resource.TestStep{
{
Config: testAccSourceRepoRepository_sourcerepoRepositoryFullExample(context),
},
{
ResourceName: "google_sourcerepo_repository.my-repo",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccSourceRepoRepository_sourcerepoRepositoryFullExample(context map[string]interface{}) string {
return Nprintf(`
resource "google_service_account" "test-account" {
account_id = "my-service-account"
display_name = "Test Service Account"
}
resource "google_pubsub_topic" "topic" {
name = "my-topic"
}
resource "google_sourcerepo_repository" "my-repo" {
name = "my-repository%{random_suffix}"
pubsub_configs {
topic = google_pubsub_topic.topic.id
message_format = "JSON"
service_account_email = google_service_account.test-account.email
}
}
`, context)
}

func testAccCheckSourceRepoRepositoryDestroy(s *terraform.State) error {
for name, rs := range s.RootModule().Resources {
if rs.Type != "google_sourcerepo_repository" {
Expand Down
50 changes: 50 additions & 0 deletions website/docs/r/sourcerepo_repository.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,33 @@ resource "google_sourcerepo_repository" "my-repo" {
name = "my-repository"
}
```
<div class = "oics-button" style="float: right; margin: 0 0 -15px">
<a href="https://console.cloud.google.com/cloudshell/open?cloudshell_git_repo=https%3A%2F%2Fgit.luolix.top%2Fterraform-google-modules%2Fdocs-examples.git&cloudshell_working_dir=sourcerepo_repository_full&cloudshell_image=gcr.io%2Fgraphite-cloud-shell-images%2Fterraform%3Alatest&open_in_editor=main.tf&cloudshell_print=.%2Fmotd&cloudshell_tutorial=.%2Ftutorial.md" target="_blank">
<img alt="Open in Cloud Shell" src="//gstatic.com/cloudssh/images/open-btn.svg" style="max-height: 44px; margin: 32px auto; max-width: 100%;">
</a>
</div>
## Example Usage - Sourcerepo Repository Full


```hcl
resource "google_service_account" "test-account" {
account_id = "my-service-account"
display_name = "Test Service Account"
}
resource "google_pubsub_topic" "topic" {
name = "my-topic"
}
resource "google_sourcerepo_repository" "my-repo" {
name = "my-repository"
pubsub_configs {
topic = google_pubsub_topic.topic.id
message_format = "JSON"
service_account_email = google_service_account.test-account.email
}
}
```

## Argument Reference

Expand All @@ -59,10 +86,32 @@ The following arguments are supported:
- - -


* `pubsub_configs` -
(Optional)
How this repository publishes a change in the repository through Cloud Pub/Sub.
Keyed by the topic names. Structure is documented below.

* `project` - (Optional) The ID of the project in which the resource belongs.
If it is not provided, the provider project is used.


The `pubsub_configs` block supports:

* `topic` - (Required) The identifier for this object. Format specified above.

* `message_format` -
(Optional)
The format of the Cloud Pub/Sub messages.
- PROTOBUF: The message payload is a serialized protocol buffer of SourceRepoEvent.
- JSON: The message payload is a JSON string of SourceRepoEvent.

* `service_account_email` -
(Optional)
Email address of the service account used for publishing Cloud Pub/Sub messages.
This service account needs to be in the same project as the PubsubConfig. When added,
the caller needs to have iam.serviceAccounts.actAs permission on this service account.
If unspecified, it defaults to the compute engine default service account.

## Attributes Reference

In addition to the arguments listed above, the following computed attributes are exported:
Expand All @@ -81,6 +130,7 @@ This resource provides the following
[Timeouts](/docs/configuration/resources.html#timeouts) configuration options:

- `create` - Default is 4 minutes.
- `update` - Default is 4 minutes.
- `delete` - Default is 4 minutes.

## Import
Expand Down

0 comments on commit 5c75615

Please sign in to comment.