Skip to content

Commit

Permalink
Handle concurrent mass modification of repository and repository_cred…
Browse files Browse the repository at this point in the history
…entials resources (#26)
  • Loading branch information
oboukili authored Aug 6, 2020
1 parent 8f099f5 commit 391ce96
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 58 deletions.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,19 @@ provider "argocd" {
insecure = false # env ARGOCD_INSECURE
}
resource "argocd_repository" "nginx_helm" {
resource "argocd_repository_credentials" "private" {
url = "[email protected]"
username = "git"
ssh_private_key = "-----BEGIN OPENSSH PRIVATE KEY-----\nfoo\nbar\n-----END OPENSSH PRIVATE KEY-----"
}
// Uses previously defined repository credentials
resource "argocd_repository" "private" {
repo = "[email protected]:somerepo.git"
// insecure = true
}
resource "argocd_repository" "public_nginx_helm" {
repo = "https://helm.nginx.com/stable"
name = "nginx-stable"
type = "helm"
Expand Down
7 changes: 7 additions & 0 deletions argocd/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@ import (
"github.com/golang/protobuf/ptypes/empty"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
"sync"
)

var apiClientConnOpts apiclient.ClientOptions

// Used to handle concurrent access to ArgoCD common configuration
var tokenMutexConfiguration = &sync.RWMutex{}

// Used to handle concurrent access to each ArgoCD project
var tokenMutexProjectMap = make(map[string]*sync.RWMutex, 0)

func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
Expand Down
47 changes: 43 additions & 4 deletions argocd/resource_argocd_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
application "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"strings"
"sync"
"time"
)

Expand Down Expand Up @@ -34,9 +35,17 @@ func resourceArgoCDProjectCreate(d *schema.ResourceData, meta interface{}) error
}
server := meta.(ServerInterface)
c := *server.ProjectClient
projectName := objectMeta.Name
if _, ok := tokenMutexProjectMap[projectName]; !ok {
tokenMutexProjectMap[projectName] = &sync.RWMutex{}
}

tokenMutexProjectMap[projectName].RLock()
p, err := c.Get(context.Background(), &projectClient.ProjectQuery{
Name: objectMeta.Name,
Name: projectName,
})
tokenMutexProjectMap[projectName].RUnlock()

if err != nil {
switch strings.Contains(err.Error(), "NotFound") {
case true:
Expand All @@ -52,6 +61,8 @@ func resourceArgoCDProjectCreate(d *schema.ResourceData, meta interface{}) error
time.Sleep(time.Duration(*p.DeletionGracePeriodSeconds))
}
}

tokenMutexProjectMap[projectName].Lock()
p, err = c.Create(context.Background(), &projectClient.ProjectCreateRequest{
Project: &application.AppProject{
ObjectMeta: objectMeta,
Expand All @@ -61,6 +72,8 @@ func resourceArgoCDProjectCreate(d *schema.ResourceData, meta interface{}) error
// TODO: make that a resource flag with proper acceptance tests
Upsert: false,
})
tokenMutexProjectMap[projectName].Unlock()

if err != nil {
return err
}
Expand All @@ -74,9 +87,17 @@ func resourceArgoCDProjectCreate(d *schema.ResourceData, meta interface{}) error
func resourceArgoCDProjectRead(d *schema.ResourceData, meta interface{}) error {
server := meta.(ServerInterface)
c := *server.ProjectClient
projectName := d.Id()
if _, ok := tokenMutexProjectMap[projectName]; !ok {
tokenMutexProjectMap[projectName] = &sync.RWMutex{}
}

tokenMutexProjectMap[projectName].RLock()
p, err := c.Get(context.Background(), &projectClient.ProjectQuery{
Name: d.Id(),
Name: projectName,
})
tokenMutexProjectMap[projectName].RUnlock()

if err != nil {
switch strings.Contains(err.Error(), "NotFound") {
case true:
Expand All @@ -98,14 +119,23 @@ func resourceArgoCDProjectUpdate(d *schema.ResourceData, meta interface{}) error
}
server := meta.(ServerInterface)
c := *server.ProjectClient
projectName := objectMeta.Name
if _, ok := tokenMutexProjectMap[projectName]; !ok {
tokenMutexProjectMap[projectName] = &sync.RWMutex{}
}
projectRequest := &projectClient.ProjectUpdateRequest{
Project: &application.AppProject{
ObjectMeta: objectMeta,
Spec: spec,
}}
},
}

tokenMutexProjectMap[projectName].RLock()
p, err := c.Get(context.Background(), &projectClient.ProjectQuery{
Name: d.Id(),
})
tokenMutexProjectMap[projectName].RUnlock()

if p != nil {
// Kubernetes API requires providing the up-to-date correct ResourceVersion for updates
projectRequest.Project.ResourceVersion = p.ResourceVersion
Expand All @@ -123,7 +153,11 @@ func resourceArgoCDProjectUpdate(d *schema.ResourceData, meta interface{}) error
projectRequest.Project.Spec.Roles[i].JWTTokens = pr.JWTTokens
}
}

tokenMutexProjectMap[projectName].Lock()
_, err = c.Update(context.Background(), projectRequest)
tokenMutexProjectMap[projectName].Unlock()

if err != nil {
return err
}
Expand All @@ -134,7 +168,12 @@ func resourceArgoCDProjectUpdate(d *schema.ResourceData, meta interface{}) error
func resourceArgoCDProjectDelete(d *schema.ResourceData, meta interface{}) error {
server := meta.(ServerInterface)
c := *server.ProjectClient
_, err := c.Delete(context.Background(), &projectClient.ProjectQuery{Name: d.Id()})
projectName := d.Id()

tokenMutexProjectMap[projectName].Lock()
_, err := c.Delete(context.Background(), &projectClient.ProjectQuery{Name: projectName})
tokenMutexProjectMap[projectName].Unlock()

if err != nil {
return err
}
Expand Down
34 changes: 18 additions & 16 deletions argocd/resource_argocd_project_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import (
"time"
)

// For each project, implement a sync.RWMutex
var tokenMutexProjectMap = make(map[string]*sync.RWMutex, 0)

func resourceArgoCDProjectToken() *schema.Resource {
return &schema.Resource{
Create: resourceArgoCDProjectTokenCreate,
Expand Down Expand Up @@ -76,15 +73,15 @@ func resourceArgoCDProjectTokenCreate(d *schema.ResourceData, meta interface{})
var claims jwt.StandardClaims
var expiresIn int64

p := d.Get("project").(string)
projectName := d.Get("project").(string)
role := d.Get("role").(string)
opts := &project.ProjectTokenCreateRequest{
Project: p,
Project: projectName,
Role: role,
}

if _, ok := tokenMutexProjectMap[p]; !ok {
tokenMutexProjectMap[p] = &sync.RWMutex{}
if _, ok := tokenMutexProjectMap[projectName]; !ok {
tokenMutexProjectMap[projectName] = &sync.RWMutex{}
}
if d, ok := d.GetOk("description"); ok {
opts.Description = d.(string)
Expand Down Expand Up @@ -120,14 +117,14 @@ func resourceArgoCDProjectTokenCreate(d *schema.ResourceData, meta interface{})
return err
}

tokenMutexProjectMap[p].Lock()
tokenMutexProjectMap[projectName].Lock()
resp, err := c.CreateToken(context.Background(), opts)
// ensure issuedAt is unique upon multiple simultaneous resource creation invocations
// as this is the unique ID for old tokens
if !featureTokenIDSupported {
time.Sleep(1 * time.Second)
}
tokenMutexProjectMap[p].Unlock()
tokenMutexProjectMap[projectName].Unlock()
if err != nil {
return err
}
Expand Down Expand Up @@ -164,7 +161,7 @@ func resourceArgoCDProjectTokenCreate(d *schema.ResourceData, meta interface{})
}
d.SetId(claims.ID)
} else {
d.SetId(fmt.Sprintf("%s-%s-%d", p, role, claims.IssuedAt.Unix()))
d.SetId(fmt.Sprintf("%s-%s-%d", projectName, role, claims.IssuedAt.Unix()))
}
return resourceArgoCDProjectTokenRead(d, meta)
}
Expand All @@ -178,11 +175,18 @@ func resourceArgoCDProjectTokenRead(d *schema.ResourceData, meta interface{}) er

server := meta.(ServerInterface)
c := *server.ProjectClient
projectName := d.Get("project").(string)
if _, ok := tokenMutexProjectMap[projectName]; !ok {
tokenMutexProjectMap[projectName] = &sync.RWMutex{}
}

// Delete token from state if project has been deleted in an out-of-band fashion
tokenMutexProjectMap[projectName].RLock()
p, err := c.Get(context.Background(), &project.ProjectQuery{
Name: d.Get("project").(string),
Name: projectName,
})
tokenMutexProjectMap[projectName].RUnlock()

if err != nil {
switch strings.Contains(err.Error(), "NotFound") {
case true:
Expand All @@ -192,9 +196,7 @@ func resourceArgoCDProjectTokenRead(d *schema.ResourceData, meta interface{}) er
return err
}
}
if _, ok := tokenMutexProjectMap[p.Name]; !ok {
tokenMutexProjectMap[p.Name] = &sync.RWMutex{}
}

featureTokenIDSupported, err := server.isFeatureSupported(featureTokenIDs)
if err != nil {
return err
Expand All @@ -214,13 +216,13 @@ func resourceArgoCDProjectTokenRead(d *schema.ResourceData, meta interface{}) er
}
}

tokenMutexProjectMap[p.Name].RLock()
tokenMutexProjectMap[projectName].RLock()
token, _, err = p.GetJWTToken(
d.Get("role").(string),
requestTokenIAT,
requestTokenID,
)
tokenMutexProjectMap[p.Name].RUnlock()
tokenMutexProjectMap[projectName].RUnlock()
if err != nil {
// Token has been deleted in an out-of-band fashion
d.SetId("")
Expand Down
18 changes: 18 additions & 0 deletions argocd/resource_argocd_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ func resourceArgoCDRepositoryCreate(d *schema.ResourceData, meta interface{}) er
server := meta.(ServerInterface)
c := *server.RepositoryClient
repo := expandRepository(d)

tokenMutexConfiguration.Lock()
r, err := c.CreateRepository(
context.Background(),
&repository.RepoCreateRequest{
Expand All @@ -35,6 +37,8 @@ func resourceArgoCDRepositoryCreate(d *schema.ResourceData, meta interface{}) er
CredsOnly: false,
},
)
tokenMutexConfiguration.Unlock()

if err != nil {
return err
}
Expand Down Expand Up @@ -64,10 +68,13 @@ func resourceArgoCDRepositoryRead(d *schema.ResourceData, meta interface{}) erro

switch featureRepositoryGetSupported {
case true:
tokenMutexConfiguration.RLock()
r, err = c.Get(context.Background(), &repository.RepoQuery{
Repo: d.Id(),
ForceRefresh: false,
})
tokenMutexConfiguration.RUnlock()

if err != nil {
switch strings.Contains(err.Error(), "NotFound") {
// Repository has already been deleted in an out-of-band fashion
Expand All @@ -79,10 +86,13 @@ func resourceArgoCDRepositoryRead(d *schema.ResourceData, meta interface{}) erro
}
}
case false:
tokenMutexConfiguration.RLock()
rl, err := c.ListRepositories(context.Background(), &repository.RepoQuery{
Repo: d.Id(),
ForceRefresh: false,
})
tokenMutexConfiguration.RUnlock()

if err != nil {
// TODO: check for NotFound condition?
return err
Expand Down Expand Up @@ -111,10 +121,14 @@ func resourceArgoCDRepositoryUpdate(d *schema.ResourceData, meta interface{}) er
server := meta.(ServerInterface)
c := *server.RepositoryClient
repo := expandRepository(d)

tokenMutexConfiguration.Lock()
r, err := c.UpdateRepository(
context.Background(),
&repository.RepoUpdateRequest{Repo: repo},
)
tokenMutexConfiguration.Unlock()

if err != nil {
switch strings.Contains(err.Error(), "NotFound") {
// Repository has already been deleted in an out-of-band fashion
Expand Down Expand Up @@ -142,10 +156,14 @@ func resourceArgoCDRepositoryUpdate(d *schema.ResourceData, meta interface{}) er
func resourceArgoCDRepositoryDelete(d *schema.ResourceData, meta interface{}) error {
server := meta.(ServerInterface)
c := *server.RepositoryClient

tokenMutexConfiguration.Lock()
_, err := c.DeleteRepository(
context.Background(),
&repository.RepoQuery{Repo: d.Id()},
)
tokenMutexConfiguration.Unlock()

if err != nil {
return err
}
Expand Down
16 changes: 16 additions & 0 deletions argocd/resource_argocd_repository_credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@ func resourceArgoCDRepositoryCredentialsCreate(d *schema.ResourceData, meta inte
server := meta.(ServerInterface)
c := *server.RepoCredsClient
repoCreds := expandRepositoryCredentials(d)

tokenMutexConfiguration.Lock()
rc, err := c.CreateRepositoryCredentials(
context.Background(),
&repocreds.RepoCredsCreateRequest{
Creds: repoCreds,
Upsert: false,
},
)
tokenMutexConfiguration.Unlock()

if err != nil {
return err
}
Expand All @@ -44,9 +48,13 @@ func resourceArgoCDRepositoryCredentialsRead(d *schema.ResourceData, meta interf
server := meta.(ServerInterface)
c := *server.RepoCredsClient
rc := application.RepoCreds{}

tokenMutexConfiguration.RLock()
rcl, err := c.ListRepositoryCredentials(context.Background(), &repocreds.RepoCredsQuery{
Url: d.Id(),
})
tokenMutexConfiguration.RUnlock()

if err != nil {
// TODO: check for NotFound condition?
return err
Expand Down Expand Up @@ -74,11 +82,15 @@ func resourceArgoCDRepositoryCredentialsUpdate(d *schema.ResourceData, meta inte
server := meta.(ServerInterface)
c := *server.RepoCredsClient
repoCreds := expandRepositoryCredentials(d)

tokenMutexConfiguration.Lock()
r, err := c.UpdateRepositoryCredentials(
context.Background(),
&repocreds.RepoCredsUpdateRequest{
Creds: repoCreds},
)
tokenMutexConfiguration.Unlock()

if err != nil {
switch strings.Contains(err.Error(), "NotFound") {
// Repository credentials have already been deleted in an out-of-band fashion
Expand All @@ -96,10 +108,14 @@ func resourceArgoCDRepositoryCredentialsUpdate(d *schema.ResourceData, meta inte
func resourceArgoCDRepositoryCredentialsDelete(d *schema.ResourceData, meta interface{}) error {
server := meta.(ServerInterface)
c := *server.RepoCredsClient

tokenMutexConfiguration.Lock()
_, err := c.DeleteRepositoryCredentials(
context.Background(),
&repocreds.RepoCredsDeleteRequest{Url: d.Id()},
)
tokenMutexConfiguration.Unlock()

if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 391ce96

Please sign in to comment.