Skip to content

Add create_ignore_already_exists to google_sourcerepo_repository #8329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/11770.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
sourcerepo: added `create_ignore_already_exists` field to `google_sourcerepo_repository` resource
```
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"google.golang.org/api/googleapi"

"github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
Expand All @@ -51,6 +52,50 @@ 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
}
}

func ResourceSourceRepoRepository() *schema.Resource {
return &schema.Resource{
Create: resourceSourceRepoRepositoryCreate,
Expand Down Expand Up @@ -122,6 +167,12 @@ If unspecified, it defaults to the compute engine default service account.`,
Computed: true,
Description: `URL to clone the repository from Google Cloud Source Repositories.`,
},
"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.`,
},
"project": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -185,9 +236,39 @@ func resourceSourceRepoRepositoryCreate(d *schema.ResourceData, meta interface{}
Headers: headers,
})
if err != nil {
return fmt.Errorf("Error creating Repository: %s", err)
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

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

func TestAccSourceRepoRepository_basic(t *testing.T) {
Expand Down Expand Up @@ -91,3 +92,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)
}
1 change: 1 addition & 0 deletions website/docs/r/sourcerepo_repository.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ The following arguments are supported:
* `project` - (Optional) The ID of the project in which the resource belongs.
If it is not provided, the provider project is used.

* `create_ignore_already_exists` - (Optional) If set to true, skip repository creation if a repository with the same name already exists.

<a name="nested_pubsub_configs"></a>The `pubsub_configs` block supports:

Expand Down