Skip to content

Commit

Permalink
Add retry logic for project deletion
Browse files Browse the repository at this point in the history
Tidy up other retry logic code

Replace programmatic API calls for repo, user, group with Terraform configuration 

Fix invalid JSON for TF registry manifest
  • Loading branch information
alexhung committed Feb 8, 2024
1 parent 220f69d commit 73ad597
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 42 deletions.
29 changes: 17 additions & 12 deletions pkg/project/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net/http"

"github.com/go-resty/resty/v2"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/jfrog/terraform-provider-shared/util"
Expand Down Expand Up @@ -109,8 +110,13 @@ var updateRepos = func(ctx context.Context, projectKey string, terraformRepoKeys
var addRepos = func(ctx context.Context, projectKey string, repoKeys []RepoKey, m interface{}) error {
tflog.Debug(ctx, fmt.Sprintf("addRepos: %s", repoKeys))

req := m.(util.ProvderMetadata).Client.R().
AddRetryCondition(retryOnSpecificMsgBody("A timeout occurred")).
AddRetryCondition(retryOnSpecificMsgBody("Web server is down")).
AddRetryCondition(retryOnSpecificMsgBody("Web server is returning an unknown error"))

for _, repoKey := range repoKeys {
err := addRepo(ctx, projectKey, repoKey, m)
err := addRepo(ctx, projectKey, repoKey, req)
if err != nil {
return fmt.Errorf("failed to add repo %s: %s", repoKey, err)
}
Expand All @@ -119,13 +125,10 @@ var addRepos = func(ctx context.Context, projectKey string, repoKeys []RepoKey,
return nil
}

var addRepo = func(ctx context.Context, projectKey string, repoKey RepoKey, m interface{}) error {
var addRepo = func(ctx context.Context, projectKey string, repoKey RepoKey, req *resty.Request) error {
tflog.Debug(ctx, fmt.Sprintf("addRepo: %s", repoKey))

_, err := m.(util.ProvderMetadata).Client.R().
AddRetryCondition(retryOnSpecificMsgBody("A timeout occurred")).
AddRetryCondition(retryOnSpecificMsgBody("Web server is down")).
AddRetryCondition(retryOnSpecificMsgBody("Web server is returning an unknown error")).
_, err := req.
SetPathParams(map[string]string{
"projectKey": projectKey,
"repoKey": string(repoKey),
Expand All @@ -139,8 +142,13 @@ var addRepo = func(ctx context.Context, projectKey string, repoKey RepoKey, m in
var deleteRepos = func(ctx context.Context, projectKey string, repoKeys []RepoKey, m interface{}) error {
tflog.Debug(ctx, fmt.Sprintf("deleteRepos: %s", repoKeys))

req := m.(util.ProvderMetadata).Client.R().
AddRetryCondition(retryOnSpecificMsgBody("A timeout occurred")).
AddRetryCondition(retryOnSpecificMsgBody("Web server is down")).
AddRetryCondition(retryOnSpecificMsgBody("Web server is returning an unknown error"))

for _, repoKey := range repoKeys {
err := deleteRepo(ctx, projectKey, repoKey, m)
err := deleteRepo(ctx, projectKey, repoKey, req)
if err != nil {
return fmt.Errorf("failed to delete repo %s: %s", repoKey, err)
}
Expand All @@ -149,7 +157,7 @@ var deleteRepos = func(ctx context.Context, projectKey string, repoKeys []RepoKe
return nil
}

var deleteRepo = func(ctx context.Context, projectKey string, repoKey RepoKey, m interface{}) error {
var deleteRepo = func(ctx context.Context, projectKey string, repoKey RepoKey, req *resty.Request) error {
tflog.Debug(ctx, fmt.Sprintf("deleteRepo: %s", repoKey))

type Error struct {
Expand All @@ -163,10 +171,7 @@ var deleteRepo = func(ctx context.Context, projectKey string, repoKey RepoKey, m

var errorResp ErrorResponse

resp, err := m.(util.ProvderMetadata).Client.R().
AddRetryCondition(retryOnSpecificMsgBody("A timeout occurred")).
AddRetryCondition(retryOnSpecificMsgBody("Web server is down")).
AddRetryCondition(retryOnSpecificMsgBody("Web server is returning an unknown error")).
resp, err := req.
SetPathParam("repoKey", string(repoKey)).
SetError(&errorResp).
Delete(projectsUrl + "/_/attach/repositories/{repoKey}")
Expand Down
11 changes: 10 additions & 1 deletion pkg/project/resource_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"
"strings"

"github.com/go-resty/resty/v2"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -489,7 +490,15 @@ func projectResource() *schema.Resource {
return diag.FromErr(fmt.Errorf("failed to delete repos for project: %s", deleteErr))
}

resp, err := m.(util.ProvderMetadata).Client.R().
req := m.(util.ProvderMetadata).Client.R()
req.AddRetryCondition(
func(r *resty.Response, _ error) bool {
return r.StatusCode() == http.StatusBadRequest &&
strings.Contains(r.String(), "project containing resources can't be removed")
},
)

resp, err := req.
SetPathParam("projectKey", data.Id()).
Delete(projectUrl)

Expand Down
83 changes: 58 additions & 25 deletions pkg/project/resource_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,13 +271,53 @@ func TestAccProject_full(t *testing.T) {
"project_key": strings.ToLower(randSeq(6)),
"username1": username1,
"username2": username2,
"email1": email1,
"email2": email2,
"group1": group1,
"group2": group2,
"repo1": repo1,
"repo2": repo2,
}

template := `
resource "artifactory_managed_user" "{{ .username1 }}" {
name = "{{ .username1 }}"
email = "{{ .email1 }}"
password = "Password1!"
admin = false
}
resource "artifactory_managed_user" "{{ .username2 }}" {
name = "{{ .username2 }}"
email = "{{ .email2 }}"
password = "Password1!"
admin = false
}
resource "artifactory_group" "{{ .group1 }}" {
name = "{{ .group1 }}"
}
resource "artifactory_group" "{{ .group2 }}" {
name = "{{ .group2 }}"
}
resource "artifactory_local_generic_repository" "{{ .repo1 }}" {
key = "{{ .repo1 }}"
lifecycle {
ignore_changes = ["project_key"]
}
}
resource "artifactory_local_generic_repository" "{{ .repo2 }}" {
key = "{{ .repo2 }}"
lifecycle {
ignore_changes = ["project_key"]
}
}
resource "project" "{{ .name }}" {
key = "{{ .project_key }}"
display_name = "{{ .name }}"
Expand All @@ -292,22 +332,22 @@ func TestAccProject_full(t *testing.T) {
email_notification = {{ .email_notification }}
member {
name = "{{ .username1 }}"
name = artifactory_managed_user.{{ .username1 }}.name
roles = ["Developer","Project Admin"]
}
member {
name = "{{ .username2 }}"
name = artifactory_managed_user.{{ .username2 }}.name
roles = ["Developer"]
}
group {
name = "{{ .group1 }}"
name = artifactory_group.{{ .group1 }}.name
roles = ["qa"]
}
group {
name = "{{ .group2 }}"
name = artifactory_group.{{ .group2 }}.name
roles = ["Release Manager"]
}
Expand All @@ -327,7 +367,10 @@ func TestAccProject_full(t *testing.T) {
actions = ["READ_REPOSITORY", "ANNOTATE_REPOSITORY", "DEPLOY_CACHE_REPOSITORY", "DELETE_OVERWRITE_REPOSITORY", "TRIGGER_PIPELINE", "READ_INTEGRATIONS_PIPELINE", "READ_POOLS_PIPELINE", "MANAGE_INTEGRATIONS_PIPELINE", "MANAGE_SOURCES_PIPELINE", "MANAGE_POOLS_PIPELINE", "READ_BUILD", "ANNOTATE_BUILD", "DEPLOY_BUILD", "DELETE_BUILD",]
}
repos = ["{{ .repo1 }}", "{{ .repo2 }}"]
repos = [
artifactory_local_generic_repository.{{ .repo1 }}.key,
artifactory_local_generic_repository.{{ .repo2 }}.key,
]
}
`

Expand All @@ -344,6 +387,8 @@ func TestAccProject_full(t *testing.T) {
"project_key": params["project_key"],
"username1": params["username1"],
"username2": params["username2"],
"email1": params["email1"],
"email2": params["email2"],
"group1": params["group1"],
"group2": params["group2"],
"repo1": params["repo1"],
Expand All @@ -352,27 +397,15 @@ func TestAccProject_full(t *testing.T) {
projectUpdated := test.ExecuteTemplate("TestAccProjects", template, updateParams)

resource.Test(t, resource.TestCase{
PreCheck: func() {
testAccPreCheck(t)
createTestUser(t, username1, email1)
createTestUser(t, username2, email2)
createTestGroup(t, group1)
createTestGroup(t, group2)
createTestRepo(t, repo1)
createTestRepo(t, repo2)
},
CheckDestroy: verifyDeleted(resourceName, func(id string, request *resty.Request) (*resty.Response, error) {
deleteTestUser(t, username1)
deleteTestUser(t, username2)
deleteTestGroup(t, group1)
deleteTestGroup(t, group2)
deleteTestRepo(t, repo1)
deleteTestRepo(t, repo2)
resp, err := verifyProject(id, request)

return resp, err
}),
PreCheck: func() { testAccPreCheck(t) },
CheckDestroy: verifyDeleted(resourceName, verifyProject),
ProviderFactories: testAccProviders(),
ExternalProviders: map[string]resource.ExternalProvider{
"artifactory": {
Source: "jfrog/artifactory",
VersionConstraint: "10.1.3",
},
},
Steps: []resource.TestStep{
{
Config: project,
Expand Down
3 changes: 1 addition & 2 deletions pkg/project/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ type Equatable interface {

func retryOnSpecificMsgBody(matchString string) func(response *resty.Response, err error) bool {
return func(response *resty.Response, err error) bool {
var responseBodyRegex = regexp.MustCompile(matchString)
return responseBodyRegex.MatchString(string(response.Body()[:]))
return regexp.MustCompile(matchString).MatchString(string(response.Body()[:]))
}
}
4 changes: 2 additions & 2 deletions terraform-registry-manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"metadata": {
"protocol_versions": ["5.0"],
},
"protocol_versions": ["5.0"]
}
}

0 comments on commit 73ad597

Please sign in to comment.