Skip to content

Commit dc8cc78

Browse files
authored
enhanced token validation (#12)
* introduced client connection pooling * removed import aliases * token: added renew_before duration option, renew token automatically upon expiration or near (renew_before) expiration
1 parent f02389b commit dc8cc78

15 files changed

+342
-188
lines changed

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,12 @@ resource "argocd_project" "myproject" {
136136
}
137137
138138
resource "argocd_project_token" "secret" {
139-
count = 20
140-
project = argocd_project.myproject.metadata.0.name
141-
role = "foobar"
142-
description = "short lived token"
143-
expires_in = 3600
139+
count = 20
140+
project = argocd_project.myproject.metadata.0.name
141+
role = "foobar"
142+
description = "short lived token"
143+
expires_in = "1h"
144+
renew_before = "30m"
144145
}
145146
```
146147

argocd/features.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package argocd
33
import (
44
"fmt"
55
"github.com/Masterminds/semver"
6-
argoCDApiClient "github.com/argoproj/argo-cd/pkg/apiclient"
6+
"github.com/argoproj/argo-cd/pkg/apiclient"
7+
"github.com/argoproj/argo-cd/pkg/apiclient/project"
78
"github.com/argoproj/argo-cd/pkg/apiclient/version"
89
)
910

@@ -18,7 +19,8 @@ var (
1819
)
1920

2021
type ServerInterface struct {
21-
ApiClient argoCDApiClient.Client
22+
ApiClient apiclient.Client
23+
ProjectClient project.ProjectServiceClient
2224
ServerVersion *semver.Version
2325
ServerVersionMessage *version.VersionMessage
2426
}

argocd/features_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,18 @@ func TestServerInterface_isFeatureSupported(t *testing.T) {
101101
}
102102
got, err := p.isFeatureSupported(tt.args.feature)
103103
if (err != nil) != tt.wantErr {
104-
t.Errorf("isFeatureSupported() error = %v, wantErr %v", err, tt.wantErr)
104+
t.Errorf("isFeatureSupported() error = %v, wantErr %v",
105+
err,
106+
tt.wantErr,
107+
)
105108
return
106109
}
107110
if got != tt.want {
108-
t.Errorf("isFeatureSupported() got = %v, want %v", got, tt.want)
111+
t.Errorf("isFeatureSupported() got = %v, want %v, version %s",
112+
got,
113+
tt.want,
114+
tt.fields.ServerVersion.String(),
115+
)
109116
}
110117
})
111118
}

argocd/provider.go

Lines changed: 81 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@ import (
44
"context"
55
"fmt"
66
"github.com/Masterminds/semver"
7-
argoCDApiClient "github.com/argoproj/argo-cd/pkg/apiclient"
7+
"github.com/argoproj/argo-cd/pkg/apiclient"
8+
"github.com/argoproj/argo-cd/pkg/apiclient/project"
89
"github.com/argoproj/argo-cd/pkg/apiclient/session"
910
"github.com/argoproj/argo-cd/util"
1011
"github.com/golang/protobuf/ptypes/empty"
1112
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
1213
"github.com/hashicorp/terraform-plugin-sdk/terraform"
1314
)
1415

15-
func Provider() terraform.ResourceProvider {
16+
var apiClientConnOpts apiclient.ClientOptions
17+
18+
func Provider(doneCh chan bool) terraform.ResourceProvider {
1619
return &schema.Provider{
1720
Schema: map[string]*schema.Schema{
1821
"server_addr": {
@@ -99,61 +102,107 @@ func Provider() terraform.ResourceProvider {
99102
"argocd_project": resourceArgoCDProject(),
100103
"argocd_project_token": resourceArgoCDProjectToken(),
101104
},
105+
ConfigureFunc: func(d *schema.ResourceData) (interface{}, error) {
106+
apiClient, err := initApiClient(d)
107+
if err != nil {
108+
return nil, err
109+
}
110+
pcCloser, projectClient, err := apiClient.NewProjectClient()
111+
if err != nil {
112+
return nil, err
113+
}
102114

103-
ConfigureFunc: providerConfigure,
115+
// Clients connection pooling, close when the provider execution ends
116+
go func(done chan bool) {
117+
<-done
118+
util.Close(pcCloser)
119+
}(doneCh)
120+
return initServerInterface(apiClient, projectClient)
121+
},
104122
}
105123
}
106124

107-
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
108-
opts := argoCDApiClient.ClientOptions{}
125+
func initServerInterface(apiClient apiclient.Client, projectClient project.ProjectServiceClient) (interface{}, error) {
126+
acCloser, versionClient, err := apiClient.NewVersionClient()
127+
if err != nil {
128+
return nil, err
129+
}
130+
defer util.Close(acCloser)
109131

110-
if d, ok := d.GetOk("server_addr"); ok {
111-
opts.ServerAddr = d.(string)
132+
serverVersionMessage, err := versionClient.Version(context.Background(), &empty.Empty{})
133+
if err != nil {
134+
return nil, err
135+
}
136+
if serverVersionMessage == nil {
137+
return nil, fmt.Errorf("could not get server version information")
138+
}
139+
serverVersion, err := semver.NewVersion(serverVersionMessage.Version)
140+
if err != nil {
141+
return nil, fmt.Errorf("could not parse server semantic version: %s", serverVersionMessage.Version)
112142
}
113-
if d, ok := d.GetOk("plain_text"); ok {
114-
opts.PlainText = d.(bool)
143+
144+
return ServerInterface{
145+
apiClient,
146+
projectClient,
147+
serverVersion,
148+
serverVersionMessage}, err
149+
}
150+
151+
func initApiClient(d *schema.ResourceData) (
152+
apiClient apiclient.Client,
153+
err error) {
154+
155+
var opts apiclient.ClientOptions
156+
157+
if v, ok := d.GetOk("server_addr"); ok {
158+
opts.ServerAddr = v.(string)
115159
}
116-
if d, ok := d.GetOk("insecure"); ok {
117-
opts.Insecure = d.(bool)
160+
if v, ok := d.GetOk("plain_text"); ok {
161+
opts.PlainText = v.(bool)
118162
}
119-
if d, ok := d.GetOk("cert_file"); ok {
120-
opts.CertFile = d.(string)
163+
if v, ok := d.GetOk("insecure"); ok {
164+
opts.Insecure = v.(bool)
121165
}
122-
if d, ok := d.GetOk("context"); ok {
123-
opts.Context = d.(string)
166+
if v, ok := d.GetOk("cert_file"); ok {
167+
opts.CertFile = v.(string)
124168
}
125-
if d, ok := d.GetOk("user_agent"); ok {
126-
opts.UserAgent = d.(string)
169+
if v, ok := d.GetOk("context"); ok {
170+
opts.Context = v.(string)
127171
}
128-
if d, ok := d.GetOk("grpc_web"); ok {
129-
opts.GRPCWeb = d.(bool)
172+
if v, ok := d.GetOk("user_agent"); ok {
173+
opts.UserAgent = v.(string)
130174
}
131-
if d, ok := d.GetOk("port_forward"); ok {
132-
opts.PortForward = d.(bool)
175+
if v, ok := d.GetOk("grpc_web"); ok {
176+
opts.GRPCWeb = v.(bool)
133177
}
134-
if d, ok := d.GetOk("port_forward_with_namespace"); ok {
135-
opts.PortForwardNamespace = d.(string)
178+
if v, ok := d.GetOk("port_forward"); ok {
179+
opts.PortForward = v.(bool)
136180
}
137-
if d, ok := d.GetOk("headers"); ok {
138-
opts.Headers = d.([]string)
181+
if v, ok := d.GetOk("port_forward_with_namespace"); ok {
182+
opts.PortForwardNamespace = v.(string)
183+
}
184+
if v, ok := d.GetOk("headers"); ok {
185+
opts.Headers = v.([]string)
139186
}
140187

141-
authToken, authTokenOk := d.GetOk("auth_token")
188+
// Export provider API client connections options for use in other spawned api clients
189+
apiClientConnOpts = opts
142190

191+
authToken, authTokenOk := d.GetOk("auth_token")
143192
switch authTokenOk {
144193
case true:
145194
opts.AuthToken = authToken.(string)
146195
case false:
147196
userName, userNameOk := d.GetOk("username")
148197
password, passwordOk := d.GetOk("password")
149198
if userNameOk && passwordOk {
150-
c, err := argoCDApiClient.NewClient(&opts)
199+
apiClient, err = apiclient.NewClient(&opts)
151200
if err != nil {
152-
return c, err
201+
return apiClient, err
153202
}
154-
closer, sc, err := c.NewSessionClient()
203+
closer, sc, err := apiClient.NewSessionClient()
155204
if err != nil {
156-
return c, err
205+
return apiClient, err
157206
}
158207
defer util.Close(closer)
159208
sessionOpts := session.SessionCreateRequest{
@@ -162,37 +211,10 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
162211
}
163212
resp, err := sc.Create(context.Background(), &sessionOpts)
164213
if err != nil {
165-
return c, err
214+
return apiClient, err
166215
}
167216
opts.AuthToken = resp.Token
168217
}
169218
}
170-
171-
apiClient, err := argoCDApiClient.NewClient(&opts)
172-
if err != nil {
173-
return nil, err
174-
}
175-
// Get API version
176-
acCloser, versionClient, err := apiClient.NewVersionClient()
177-
if err != nil {
178-
return nil, err
179-
}
180-
defer util.Close(acCloser)
181-
182-
serverVersionMessage, err := versionClient.Version(context.Background(), &empty.Empty{})
183-
if err != nil {
184-
return nil, err
185-
}
186-
if serverVersionMessage == nil {
187-
return nil, fmt.Errorf("could not get server version information")
188-
}
189-
serverVersion, err := semver.NewVersion(serverVersionMessage.Version)
190-
if err != nil {
191-
return nil, fmt.Errorf("could not parse server semantic version: %s", serverVersionMessage.Version)
192-
}
193-
194-
return ServerInterface{
195-
apiClient,
196-
serverVersion,
197-
serverVersionMessage}, err
219+
return apiclient.NewClient(&opts)
198220
}

argocd/provider_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,25 @@ import (
88
)
99

1010
var testAccProviders map[string]terraform.ResourceProvider
11-
var testAccProviderFactories func(providers *[]*schema.Provider) map[string]terraform.ResourceProviderFactory
1211
var testAccProvider *schema.Provider
13-
var testAccProviderFunc func() *schema.Provider
12+
var testDoneCh = make(chan bool, 1)
1413

1514
func init() {
16-
testAccProvider = Provider().(*schema.Provider)
15+
testAccProvider = Provider(testDoneCh).(*schema.Provider)
1716
testAccProviders = map[string]terraform.ResourceProvider{
1817
"argocd": testAccProvider,
1918
}
19+
testDoneCh <- true
2020
}
2121

2222
func TestProvider(t *testing.T) {
23-
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
23+
if err := Provider(testDoneCh).(*schema.Provider).InternalValidate(); err != nil {
2424
t.Fatalf("err: %s", err)
2525
}
2626
}
2727

2828
func TestProvider_impl(t *testing.T) {
29-
var _ terraform.ResourceProvider = Provider()
29+
var _ = Provider(testDoneCh)
3030
}
3131

3232
func testAccPreCheck(t *testing.T) {

0 commit comments

Comments
 (0)