Skip to content

Commit

Permalink
Fix close in use certificate providers after double Close() method …
Browse files Browse the repository at this point in the history
…call on wrapper object

Fix errors like:
```
[core][Channel #39 SubChannel #941] grpc: addrConn.createTransport failed to connect to ...
Err: connection error: desc = "transport: authentication handshake failed:
xds: fetching trusted roots from CertificateProvider failed: provider instance is closed"
```
  • Loading branch information
bozaro committed Apr 16, 2024
1 parent a4afd4d commit 3bf8e1f
Showing 1 changed file with 36 additions and 5 deletions.
41 changes: 36 additions & 5 deletions credentials/tls/certprovider/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package certprovider

import (
"context"
"fmt"
"sync"
)
Expand All @@ -44,7 +45,7 @@ type storeKey struct {

// wrappedProvider wraps a provider instance with a reference count.
type wrappedProvider struct {
Provider
provider Provider
refCount int

// A reference to the key and store are also kept here to override the
Expand All @@ -53,6 +54,13 @@ type wrappedProvider struct {
store *store
}

// wrappedProviderCloser wraps a provider instance with a reference count to avoid double
// close still in use provider.
type wrappedProviderCloser struct {
mu sync.RWMutex
wp *wrappedProvider
}

// store is a collection of provider instances, safe for concurrent access.
type store struct {
mu sync.Mutex
Expand All @@ -70,11 +78,34 @@ func (wp *wrappedProvider) Close() {

wp.refCount--
if wp.refCount == 0 {
wp.Provider.Close()
wp.provider.Close()
delete(ps.providers, wp.storeKey)
}
}

// Close overrides the Close method of the embedded provider to avoid release the
// already released reference.
func (w *wrappedProviderCloser) Close() {
w.mu.Lock()
defer w.mu.Unlock()
if wp := w.wp; wp != nil {
w.wp = nil
wp.Close()
}
}

// KeyMaterial returns the key material sourced by the Provider.
// Callers are expected to use the returned value as read-only.
func (w *wrappedProviderCloser) KeyMaterial(ctx context.Context) (*KeyMaterial, error) {
w.mu.RLock()
defer w.mu.RUnlock()

if w.wp == nil {
return nil, errProviderClosed
}
return w.wp.provider.KeyMaterial(ctx)
}

// BuildableConfig wraps parsed provider configuration and functionality to
// instantiate provider instances.
type BuildableConfig struct {
Expand Down Expand Up @@ -112,21 +143,21 @@ func (bc *BuildableConfig) Build(opts BuildOptions) (Provider, error) {
}
if wp, ok := provStore.providers[sk]; ok {
wp.refCount++
return wp, nil
return &wrappedProviderCloser{wp: wp}, nil
}

provider := bc.starter(opts)
if provider == nil {
return nil, fmt.Errorf("provider(%q, %q).Build(%v) failed", sk.name, sk.config, opts)
}
wp := &wrappedProvider{
Provider: provider,
provider: provider,
refCount: 1,
storeKey: sk,
store: provStore,
}
provStore.providers[sk] = wp
return wp, nil
return &wrappedProviderCloser{wp: wp}, nil
}

// String returns the provider name and config as a colon separated string.
Expand Down

0 comments on commit 3bf8e1f

Please sign in to comment.