Skip to content

Commit 7386dec

Browse files
committed
added argocd_repository_credentials resource (#21)
1 parent 0335263 commit 7386dec

8 files changed

+303
-13
lines changed

argocd/features.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/argoproj/argo-cd/pkg/apiclient"
77
"github.com/argoproj/argo-cd/pkg/apiclient/application"
88
"github.com/argoproj/argo-cd/pkg/apiclient/project"
9+
"github.com/argoproj/argo-cd/pkg/apiclient/repocreds"
910
"github.com/argoproj/argo-cd/pkg/apiclient/repository"
1011
"github.com/argoproj/argo-cd/pkg/apiclient/version"
1112
)
@@ -29,6 +30,7 @@ type ServerInterface struct {
2930
ApplicationClient *application.ApplicationServiceClient
3031
ProjectClient *project.ProjectServiceClient
3132
RepositoryClient *repository.RepositoryServiceClient
33+
RepoCredsClient *repocreds.RepoCredsServiceClient
3234
ServerVersion *semver.Version
3335
ServerVersionMessage *version.VersionMessage
3436
}

argocd/provider.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/argoproj/argo-cd/pkg/apiclient"
88
"github.com/argoproj/argo-cd/pkg/apiclient/application"
99
"github.com/argoproj/argo-cd/pkg/apiclient/project"
10+
"github.com/argoproj/argo-cd/pkg/apiclient/repocreds"
1011
"github.com/argoproj/argo-cd/pkg/apiclient/repository"
1112
"github.com/argoproj/argo-cd/pkg/apiclient/session"
1213
util "github.com/argoproj/gitops-engine/pkg/utils/io"
@@ -101,10 +102,11 @@ func Provider(doneCh chan bool) terraform.ResourceProvider {
101102
},
102103

103104
ResourcesMap: map[string]*schema.Resource{
104-
"argocd_application": resourceArgoCDApplication(),
105-
"argocd_project": resourceArgoCDProject(),
106-
"argocd_project_token": resourceArgoCDProjectToken(),
107-
"argocd_repository": resourceArgoCDRepository(),
105+
"argocd_application": resourceArgoCDApplication(),
106+
"argocd_project": resourceArgoCDProject(),
107+
"argocd_project_token": resourceArgoCDProjectToken(),
108+
"argocd_repository": resourceArgoCDRepository(),
109+
"argocd_repository_credentials": resourceArgoCDRepositoryCredentials(),
108110
},
109111
ConfigureFunc: func(d *schema.ResourceData) (interface{}, error) {
110112
apiClient, err := initApiClient(d)
@@ -126,18 +128,25 @@ func Provider(doneCh chan bool) terraform.ResourceProvider {
126128
return nil, err
127129
}
128130

131+
rcredsCloser, repoCredsClient, err := apiClient.NewRepoCredsClient()
132+
if err != nil {
133+
return nil, err
134+
}
135+
129136
// Clients connection pooling, close when the provider execution ends
130137
go func(done chan bool) {
131138
<-done
132139
util.Close(pcCloser)
133140
util.Close(acCloser)
134141
util.Close(rcCloser)
142+
util.Close(rcredsCloser)
135143
}(doneCh)
136144
return initServerInterface(
137145
apiClient,
138146
projectClient,
139147
applicationClient,
140148
repositoryClient,
149+
repoCredsClient,
141150
)
142151
},
143152
}
@@ -148,6 +157,7 @@ func initServerInterface(
148157
projectClient project.ProjectServiceClient,
149158
applicationClient application.ApplicationServiceClient,
150159
repositoryClient repository.RepositoryServiceClient,
160+
repoCredsClient repocreds.RepoCredsServiceClient,
151161
) (interface{}, error) {
152162
acCloser, versionClient, err := apiClient.NewVersionClient()
153163
if err != nil {
@@ -172,6 +182,7 @@ func initServerInterface(
172182
&applicationClient,
173183
&projectClient,
174184
&repositoryClient,
185+
&repoCredsClient,
175186
serverVersion,
176187
serverVersionMessage}, err
177188
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package argocd
2+
3+
import (
4+
"context"
5+
"github.com/argoproj/argo-cd/pkg/apiclient/repocreds"
6+
application "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
7+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
8+
"strings"
9+
)
10+
11+
func resourceArgoCDRepositoryCredentials() *schema.Resource {
12+
return &schema.Resource{
13+
Create: resourceArgoCDRepositoryCredentialsCreate,
14+
Read: resourceArgoCDRepositoryCredentialsRead,
15+
Update: resourceArgoCDRepositoryCredentialsUpdate,
16+
Delete: resourceArgoCDRepositoryCredentialsDelete,
17+
// TODO: add importer acceptance tests
18+
Importer: &schema.ResourceImporter{
19+
State: schema.ImportStatePassthrough,
20+
},
21+
Schema: repositoryCredentialsSchema(),
22+
}
23+
}
24+
25+
func resourceArgoCDRepositoryCredentialsCreate(d *schema.ResourceData, meta interface{}) error {
26+
server := meta.(ServerInterface)
27+
c := *server.RepoCredsClient
28+
repoCreds := expandRepositoryCredentials(d)
29+
rc, err := c.CreateRepositoryCredentials(
30+
context.Background(),
31+
&repocreds.RepoCredsCreateRequest{
32+
Creds: repoCreds,
33+
Upsert: false,
34+
},
35+
)
36+
if err != nil {
37+
return err
38+
}
39+
d.SetId(rc.URL)
40+
return resourceArgoCDRepositoryCredentialsRead(d, meta)
41+
}
42+
43+
func resourceArgoCDRepositoryCredentialsRead(d *schema.ResourceData, meta interface{}) error {
44+
server := meta.(ServerInterface)
45+
c := *server.RepoCredsClient
46+
rc := application.RepoCreds{}
47+
rcl, err := c.ListRepositoryCredentials(context.Background(), &repocreds.RepoCredsQuery{
48+
Url: d.Id(),
49+
})
50+
if err != nil {
51+
// TODO: check for NotFound condition?
52+
return err
53+
}
54+
if rcl == nil {
55+
// Repository credentials have already been deleted in an out-of-band fashion
56+
d.SetId("")
57+
return nil
58+
}
59+
for i, _rc := range rcl.Items {
60+
if _rc.URL == d.Id() {
61+
rc = _rc
62+
break
63+
}
64+
// Repository credentials have already been deleted in an out-of-band fashion
65+
if i == len(rcl.Items)-1 {
66+
d.SetId("")
67+
return nil
68+
}
69+
}
70+
return flattenRepositoryCredentials(rc, d)
71+
}
72+
73+
func resourceArgoCDRepositoryCredentialsUpdate(d *schema.ResourceData, meta interface{}) error {
74+
server := meta.(ServerInterface)
75+
c := *server.RepoCredsClient
76+
repoCreds := expandRepositoryCredentials(d)
77+
r, err := c.UpdateRepositoryCredentials(
78+
context.Background(),
79+
&repocreds.RepoCredsUpdateRequest{
80+
Creds: repoCreds},
81+
)
82+
if err != nil {
83+
switch strings.Contains(err.Error(), "NotFound") {
84+
// Repository credentials have already been deleted in an out-of-band fashion
85+
case true:
86+
d.SetId("")
87+
return nil
88+
default:
89+
return err
90+
}
91+
}
92+
d.SetId(r.URL)
93+
return resourceArgoCDRepositoryCredentialsRead(d, meta)
94+
}
95+
96+
func resourceArgoCDRepositoryCredentialsDelete(d *schema.ResourceData, meta interface{}) error {
97+
server := meta.(ServerInterface)
98+
c := *server.RepoCredsClient
99+
_, err := c.DeleteRepositoryCredentials(
100+
context.Background(),
101+
&repocreds.RepoCredsDeleteRequest{Url: d.Id()},
102+
)
103+
if err != nil {
104+
return err
105+
}
106+
d.SetId("")
107+
return nil
108+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package argocd
2+
3+
import (
4+
"crypto/rand"
5+
"crypto/rsa"
6+
"crypto/x509"
7+
"encoding/pem"
8+
"fmt"
9+
"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
10+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
11+
"testing"
12+
)
13+
14+
func TestAccArgoCDRepositoryCredentials(t *testing.T) {
15+
repoUrl := fmt.Sprintf("https://git.local/%s/%s",
16+
acctest.RandString(10),
17+
acctest.RandString(10))
18+
username := fmt.Sprintf(acctest.RandString(10))
19+
sshPrivateKey, err := generateSSHPrivateKey()
20+
if err != nil {
21+
panic(err)
22+
}
23+
resource.ParallelTest(t, resource.TestCase{
24+
PreCheck: func() { testAccPreCheck(t) },
25+
Providers: testAccProviders,
26+
Steps: []resource.TestStep{
27+
{
28+
Config: testAccArgoCDRepositoryCredentialsSimple(repoUrl, username, sshPrivateKey),
29+
Check: resource.TestCheckResourceAttr(
30+
"argocd_repository_credentials.simple",
31+
"username",
32+
username,
33+
),
34+
},
35+
},
36+
})
37+
}
38+
39+
func testAccArgoCDRepositoryCredentialsSimple(repoUrl, username, sshPrivateKey string) string {
40+
return fmt.Sprintf(`
41+
resource "argocd_repository_credentials" "simple" {
42+
url = "%s"
43+
username = "%s"
44+
ssh_private_key = <<EOT
45+
%s
46+
EOT
47+
}
48+
`, repoUrl, username, sshPrivateKey)
49+
}
50+
51+
func generateSSHPrivateKey() (privateKey string, err error) {
52+
pk, err := rsa.GenerateKey(rand.Reader, 2048)
53+
if err != nil {
54+
return
55+
}
56+
err = pk.Validate()
57+
if err != nil {
58+
return
59+
}
60+
privDER := x509.MarshalPKCS1PrivateKey(pk)
61+
privBlock := pem.Block{
62+
Type: "RSA PRIVATE KEY",
63+
Headers: nil,
64+
Bytes: privDER,
65+
}
66+
return string(pem.EncodeToMemory(&privBlock)), nil
67+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package argocd
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
5+
)
6+
7+
func repositoryCredentialsSchema() map[string]*schema.Schema {
8+
return map[string]*schema.Schema{
9+
"url": {
10+
Type: schema.TypeString,
11+
Description: "URL is the URL that this credentials matches to",
12+
Required: true,
13+
},
14+
"username": {
15+
Type: schema.TypeString,
16+
Description: "Username for authenticating at the repo server",
17+
Optional: true,
18+
},
19+
"password": {
20+
Type: schema.TypeString,
21+
Sensitive: true,
22+
Description: "Password for authenticating at the repo server, cannot be managed once created!",
23+
Optional: true,
24+
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
25+
return true
26+
},
27+
},
28+
"ssh_private_key": {
29+
Type: schema.TypeString,
30+
Sensitive: true,
31+
Description: "SSH private key data for authenticating at the repo server only for Git repos, cannot be managed once created!",
32+
// TODO: add a validator
33+
Optional: true,
34+
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
35+
return true
36+
},
37+
},
38+
"tls_client_cert_data": {
39+
Type: schema.TypeString,
40+
Description: "TLS client cert data for authenticating at the repo server",
41+
// TODO: add a validator
42+
Optional: true,
43+
},
44+
"tls_client_cert_key": {
45+
Type: schema.TypeString,
46+
Sensitive: true,
47+
Description: "TLS client cert key for authenticating at the repo server ",
48+
// TODO: add a validator
49+
Optional: true,
50+
},
51+
}
52+
}

argocd/structure_repository.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package argocd
22

33
import (
4-
"fmt"
54
application "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
65

76
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
@@ -64,18 +63,10 @@ func flattenRepository(repository *application.Repository, d *schema.ResourceDat
6463
"tls_client_cert_key": repository.TLSClientCertKey,
6564
"type": repository.Type,
6665
}
67-
6866
for k, v := range r {
6967
if err := persistToState(k, v, d); err != nil {
7068
return err
7169
}
7270
}
7371
return nil
7472
}
75-
76-
func persistToState(key string, data interface{}, d *schema.ResourceData) error {
77-
if err := d.Set(key, data); err != nil {
78-
return fmt.Errorf("error persisting %s: %s", key, err)
79-
}
80-
return nil
81-
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package argocd
2+
3+
import (
4+
application "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
7+
)
8+
9+
// Expand
10+
11+
func expandRepositoryCredentials(d *schema.ResourceData) *application.RepoCreds {
12+
repoCreds := &application.RepoCreds{}
13+
if v, ok := d.GetOk("url"); ok {
14+
repoCreds.URL = v.(string)
15+
}
16+
if v, ok := d.GetOk("username"); ok {
17+
repoCreds.Username = v.(string)
18+
}
19+
if v, ok := d.GetOk("password"); ok {
20+
repoCreds.Password = v.(string)
21+
}
22+
if v, ok := d.GetOk("ssh_private_key"); ok {
23+
repoCreds.SSHPrivateKey = v.(string)
24+
}
25+
if v, ok := d.GetOk("tls_client_cert_data"); ok {
26+
repoCreds.TLSClientCertData = v.(string)
27+
}
28+
if v, ok := d.GetOk("tls_client_cert_key"); ok {
29+
repoCreds.TLSClientCertKey = v.(string)
30+
}
31+
return repoCreds
32+
}
33+
34+
// Flatten
35+
36+
func flattenRepositoryCredentials(repository application.RepoCreds, d *schema.ResourceData) error {
37+
r := map[string]interface{}{
38+
"url": repository.URL,
39+
"username": repository.Username,
40+
"password": repository.Password,
41+
"ssh_private_key": repository.SSHPrivateKey,
42+
"tls_client_cert_data": repository.TLSClientCertData,
43+
"tls_client_cert_key": repository.TLSClientCertKey,
44+
}
45+
for k, v := range r {
46+
if err := persistToState(k, v, d); err != nil {
47+
return err
48+
}
49+
}
50+
return nil
51+
}

0 commit comments

Comments
 (0)