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 (#7128)
  • Loading branch information
bozaro authored May 29, 2024
1 parent 33faea8 commit 24e9024
Showing 1 changed file with 42 additions and 2 deletions.
44 changes: 42 additions & 2 deletions credentials/tls/certprovider/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
package certprovider

import (
"context"
"fmt"
"sync"
"sync/atomic"
)

// provStore is the global singleton certificate provider store.
Expand Down Expand Up @@ -53,6 +55,22 @@ type wrappedProvider struct {
store *store
}

// closedProvider always returns errProviderClosed error.
type closedProvider struct{}

func (c closedProvider) KeyMaterial(ctx context.Context) (*KeyMaterial, error) {
return nil, errProviderClosed
}

func (c closedProvider) Close() {
}

// singleCloseWrappedProvider wraps a provider instance with a reference count
// to properly handle multiple calls to Close.
type singleCloseWrappedProvider struct {
provider atomic.Pointer[Provider]
}

// store is a collection of provider instances, safe for concurrent access.
type store struct {
mu sync.Mutex
Expand All @@ -75,6 +93,28 @@ func (wp *wrappedProvider) Close() {
}
}

// Close overrides the Close method of the embedded provider to avoid release the
// already released reference.
func (w *singleCloseWrappedProvider) Close() {
newProvider := Provider(closedProvider{})
oldProvider := w.provider.Swap(&newProvider)
(*oldProvider).Close()
}

// KeyMaterial returns the key material sourced by the Provider.
// Callers are expected to use the returned value as read-only.
func (w *singleCloseWrappedProvider) KeyMaterial(ctx context.Context) (*KeyMaterial, error) {
return (*w.provider.Load()).KeyMaterial(ctx)
}

// newSingleCloseWrappedProvider create wrapper a provider instance with a reference count
// to properly handle multiple calls to Close.
func newSingleCloseWrappedProvider(provider Provider) *singleCloseWrappedProvider {
w := &singleCloseWrappedProvider{}
w.provider.Store(&provider)
return w
}

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

provider := bc.starter(opts)
Expand All @@ -126,7 +166,7 @@ func (bc *BuildableConfig) Build(opts BuildOptions) (Provider, error) {
store: provStore,
}
provStore.providers[sk] = wp
return wp, nil
return newSingleCloseWrappedProvider(wp), nil
}

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

0 comments on commit 24e9024

Please sign in to comment.