Skip to content

Commit

Permalink
Add create_ignore_already_exists to google_sourcerepo_repository (Goo…
Browse files Browse the repository at this point in the history
…gleCloudPlatform#11770)

Co-authored-by: Zhenhua Li <[email protected]>
  • Loading branch information
2 people authored and karolgorc committed Oct 11, 2024
1 parent a174bb0 commit ad8e6cb
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 0 deletions.
4 changes: 4 additions & 0 deletions mmv1/products/sourcerepo/Repository.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ references:
'Official Documentation': 'https://cloud.google.com/source-repositories/'
api: 'https://cloud.google.com/source-repositories/docs/reference/rest/v1/projects.repos'
docs:
optional_properties: |
* `create_ignore_already_exists` - (Optional) If set to true, skip repository creation if a repository with the same name already exists.
base_url: 'projects/{{project}}/repos'
self_link: 'projects/{{project}}/repos/{{name}}'
update_verb: 'PATCH'
Expand All @@ -41,6 +43,8 @@ custom_code:
constants: 'templates/terraform/constants/source_repo_repository.go.tmpl'
update_encoder: 'templates/terraform/update_encoder/source_repo_repository.tmpl'
post_create: 'templates/terraform/post_create/source_repo_repository_update.go.tmpl'
extra_schema_entry: templates/terraform/extra_schema_entry/source_repo_repository.tmpl
custom_create: templates/terraform/custom_create/source_repo_repository.go.tmpl
exclude_tgc: true
examples:
- name: 'sourcerepo_repository_basic'
Expand Down
44 changes: 44 additions & 0 deletions mmv1/templates/terraform/constants/source_repo_repository.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,47 @@ func resourceSourceRepoRepositoryPubSubConfigsHash(v interface{}) int {

return tpgresource.Hashcode(buf.String())
}

func resourceSourceRepoRepositoryPollRead(d *schema.ResourceData, meta interface{}) transport_tpg.PollReadFunc {
return func() (map[string]interface{}, error) {
config := meta.(*transport_tpg.Config)
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
if err != nil {
return nil, err
}

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

billingProject := ""

project, err := tpgresource.GetProject(d, config)
if err != nil {
return nil, fmt.Errorf("error fetching project for Repository: %s", err)
}
billingProject = project

// err == nil indicates that the billing_project value was found
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
billingProject = bp
}

// Confirm the source repository exists
headers := make(http.Header)
_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
Project: billingProject,
RawURL: url,
UserAgent: userAgent,
Headers: headers,
})

if err != nil {
return nil, err
}
return nil, nil
}
}
100 changes: 100 additions & 0 deletions mmv1/templates/terraform/custom_create/source_repo_repository.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
if err != nil {
return err
}

obj := make(map[string]interface{})
nameProp, err := expandSourceRepoRepositoryName(d.Get("name"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("name"); !tpgresource.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"); !tpgresource.IsEmptyValue(reflect.ValueOf(pubsubConfigsProp)) && (ok || !reflect.DeepEqual(v, pubsubConfigsProp)) {
obj["pubsubConfigs"] = pubsubConfigsProp
}

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

log.Printf("[DEBUG] Creating new Repository: %#v", obj)
billingProject := ""

project, err := tpgresource.GetProject(d, config)
if err != nil {
return fmt.Errorf("Error fetching project for Repository: %s", err)
}
billingProject = project

// err == nil indicates that the billing_project value was found
if bp, err := tpgresource.GetBillingProject(d, config); err == nil {
billingProject = bp
}

headers := make(http.Header)
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "POST",
Project: billingProject,
RawURL: url,
UserAgent: userAgent,
Body: obj,
Timeout: d.Timeout(schema.TimeoutCreate),
Headers: headers,
})
if err != nil {
gerr, ok := err.(*googleapi.Error)
alreadyExists := ok && gerr.Code == 409 && d.Get("create_ignore_already_exists").(bool)
if alreadyExists {
log.Printf("[DEBUG] Calling get Repository after already exists error")
res, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: config,
Method: "GET",
Project: billingProject,
RawURL: url,
UserAgent: userAgent,
Headers: headers,
})
if err != nil {
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("SourceRepoRepository %q", d.Id()))
}
} else {
return fmt.Errorf("Error creating Repository: %s", err)
}
}


// We poll until the resource is found due to eventual consistency issue
// on part of the api https://cloud.google.com/iam/docs/overview#consistency
err = transport_tpg.PollingWaitTime(resourceSourceRepoRepositoryPollRead(d, meta), transport_tpg.PollCheckForExistence, "Creating Source Repository", d.Timeout(schema.TimeoutCreate), 1)

if err != nil {
return err
}

// We can't guarantee complete consistency even after polling,
// so sleep for some additional time to reduce the likelihood of
// eventual consistency failures.
time.Sleep(10 * time.Second)

// Store the ID now
id, err := tpgresource.ReplaceVars(d, config, "{{"projects/{{project}}/repos/{{name}}"}}")
if err != nil {
return fmt.Errorf("Error constructing id: %s", err)
}
d.SetId(id)

if v, ok := d.GetOkExists("pubsub_configs"); !tpgresource.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)
}

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

return resourceSourceRepoRepositoryRead(d, meta)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"create_ignore_already_exists": {
Type: schema.TypeBool,
Optional: true,
Computed: false,
Description: `If set to true, skip repository creation if a repository with the same name already exists.`,
},
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-provider-google/google/acctest"
"github.com/hashicorp/terraform-provider-google/google/envvar"
)

func TestAccSourceRepoRepository_basic(t *testing.T) {
Expand Down Expand Up @@ -89,3 +90,95 @@ func testAccSourceRepoRepository_extended(accountId string, topicName string, re
}
`, accountId, topicName, repositoryName)
}

// Test setting create_ignore_already_exists on an existing resource
func TestAccSourceRepoRepository_existingResourceCreateIgnoreAlreadyExists(t *testing.T) {
t.Parallel()

project := envvar.GetTestProjectFromEnv()
repositoryName := fmt.Sprintf("source-repo-repository-test-%s", acctest.RandString(t, 10))
id := fmt.Sprintf("projects/%s/repos/%s", project, repositoryName)

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckSourceRepoRepositoryDestroyProducer(t),
Steps: []resource.TestStep{
// The first step creates a new resource with create_ignore_already_exists=false
{
Config: testAccSourceRepoRepositoryCreateIgnoreAlreadyExists(repositoryName, false),
Check: resource.TestCheckResourceAttr("google_sourcerepo_repository.acceptance", "id", id),
},
{
ResourceName: "google_sourcerepo_repository.acceptance",
ImportStateId: id,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"create_ignore_already_exists"}, // Import leaves this field out when false
},
// The second step updates the resource to have create_ignore_already_exists=true
{
Config: testAccSourceRepoRepositoryCreateIgnoreAlreadyExists(repositoryName, true),
Check: resource.TestCheckResourceAttr("google_sourcerepo_repository.acceptance", "id", id),
},
},
})
}

// Test the option to ignore ALREADY_EXISTS error from creating a Source Repository.
func TestAccSourceRepoRepository_createIgnoreAlreadyExists(t *testing.T) {
t.Parallel()

project := envvar.GetTestProjectFromEnv()
repositoryName := fmt.Sprintf("source-repo-repository-test-%s", acctest.RandString(t, 10))
id := fmt.Sprintf("projects/%s/repos/%s", project, repositoryName)

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
CheckDestroy: testAccCheckSourceRepoRepositoryDestroyProducer(t),
Steps: []resource.TestStep{
// The first step creates a basic Source Repository
{
Config: testAccSourceRepoRepository_basic(repositoryName),
Check: resource.TestCheckResourceAttr("google_sourcerepo_repository.acceptance", "id", id),
},
{
ResourceName: "google_sourcerepo_repository.acceptance",
ImportStateId: id,
ImportState: true,
ImportStateVerify: true,
},
// The second step creates a new resource that duplicates with the existing Source Repository.
{
Config: testAccSourceRepoRepositoryDuplicateIgnoreAlreadyExists(repositoryName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("google_sourcerepo_repository.acceptance", "id", id),
resource.TestCheckResourceAttr("google_sourcerepo_repository.duplicate", "id", id),
),
},
},
})
}

func testAccSourceRepoRepositoryCreateIgnoreAlreadyExists(repositoryName string, ignore_already_exists bool) string {
return fmt.Sprintf(`
resource "google_sourcerepo_repository" "acceptance" {
name = "%s"
create_ignore_already_exists = %t
}
`, repositoryName, ignore_already_exists)
}

func testAccSourceRepoRepositoryDuplicateIgnoreAlreadyExists(repositoryName string) string {
return fmt.Sprintf(`
resource "google_sourcerepo_repository" "acceptance" {
name = "%s"
}
resource "google_sourcerepo_repository" "duplicate" {
name = "%s"
create_ignore_already_exists = true
}
`, repositoryName, repositoryName)
}

0 comments on commit ad8e6cb

Please sign in to comment.