google.golang.org/grpc@v1.72.2/credentials/tls/certprovider/store.go (about)

     1  /*
     2   *
     3   * Copyright 2020 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package certprovider
    20  
    21  import (
    22  	"context"
    23  	"fmt"
    24  	"sync"
    25  	"sync/atomic"
    26  )
    27  
    28  // provStore is the global singleton certificate provider store.
    29  var provStore = &store{
    30  	providers: make(map[storeKey]*wrappedProvider),
    31  }
    32  
    33  // storeKey acts as the key to the map of providers maintained by the store. A
    34  // combination of provider name and configuration is used to uniquely identify
    35  // every provider instance in the store. Go maps need to be indexed by
    36  // comparable types, so the provider configuration is converted from `any` to
    37  // `string` using the ParseConfig method while creating this key.
    38  type storeKey struct {
    39  	// name of the certificate provider.
    40  	name string
    41  	// configuration of the certificate provider in string form.
    42  	config string
    43  	// opts contains the certificate name and other keyMaterial options.
    44  	opts BuildOptions
    45  }
    46  
    47  // wrappedProvider wraps a provider instance with a reference count.
    48  type wrappedProvider struct {
    49  	Provider
    50  	refCount int
    51  
    52  	// A reference to the key and store are also kept here to override the
    53  	// Close method on the provider.
    54  	storeKey storeKey
    55  	store    *store
    56  }
    57  
    58  // closedProvider always returns errProviderClosed error.
    59  type closedProvider struct{}
    60  
    61  func (c closedProvider) KeyMaterial(context.Context) (*KeyMaterial, error) {
    62  	return nil, errProviderClosed
    63  }
    64  
    65  func (c closedProvider) Close() {
    66  }
    67  
    68  // singleCloseWrappedProvider wraps a provider instance with a reference count
    69  // to properly handle multiple calls to Close.
    70  type singleCloseWrappedProvider struct {
    71  	provider atomic.Pointer[Provider]
    72  }
    73  
    74  // store is a collection of provider instances, safe for concurrent access.
    75  type store struct {
    76  	mu        sync.Mutex
    77  	providers map[storeKey]*wrappedProvider
    78  }
    79  
    80  // Close overrides the Close method of the embedded provider. It releases the
    81  // reference held by the caller on the underlying provider and if the
    82  // provider's reference count reaches zero, it is removed from the store, and
    83  // its Close method is also invoked.
    84  func (wp *wrappedProvider) Close() {
    85  	ps := wp.store
    86  	ps.mu.Lock()
    87  	defer ps.mu.Unlock()
    88  
    89  	wp.refCount--
    90  	if wp.refCount == 0 {
    91  		wp.Provider.Close()
    92  		delete(ps.providers, wp.storeKey)
    93  	}
    94  }
    95  
    96  // Close overrides the Close method of the embedded provider to avoid release the
    97  // already released reference.
    98  func (w *singleCloseWrappedProvider) Close() {
    99  	newProvider := Provider(closedProvider{})
   100  	oldProvider := w.provider.Swap(&newProvider)
   101  	(*oldProvider).Close()
   102  }
   103  
   104  // KeyMaterial returns the key material sourced by the Provider.
   105  // Callers are expected to use the returned value as read-only.
   106  func (w *singleCloseWrappedProvider) KeyMaterial(ctx context.Context) (*KeyMaterial, error) {
   107  	return (*w.provider.Load()).KeyMaterial(ctx)
   108  }
   109  
   110  // newSingleCloseWrappedProvider create wrapper a provider instance with a reference count
   111  // to properly handle multiple calls to Close.
   112  func newSingleCloseWrappedProvider(provider Provider) *singleCloseWrappedProvider {
   113  	w := &singleCloseWrappedProvider{}
   114  	w.provider.Store(&provider)
   115  	return w
   116  }
   117  
   118  // BuildableConfig wraps parsed provider configuration and functionality to
   119  // instantiate provider instances.
   120  type BuildableConfig struct {
   121  	name    string
   122  	config  []byte
   123  	starter func(BuildOptions) Provider
   124  	pStore  *store
   125  }
   126  
   127  // NewBuildableConfig creates a new BuildableConfig with the given arguments.
   128  // Provider implementations are expected to invoke this function after parsing
   129  // the given configuration as part of their ParseConfig() method.
   130  // Equivalent configurations are expected to invoke this function with the same
   131  // config argument.
   132  func NewBuildableConfig(name string, config []byte, starter func(BuildOptions) Provider) *BuildableConfig {
   133  	return &BuildableConfig{
   134  		name:    name,
   135  		config:  config,
   136  		starter: starter,
   137  		pStore:  provStore,
   138  	}
   139  }
   140  
   141  // Build kicks off a provider instance with the wrapped configuration. Multiple
   142  // invocations of this method with the same opts will result in provider
   143  // instances being reused.
   144  func (bc *BuildableConfig) Build(opts BuildOptions) (Provider, error) {
   145  	provStore.mu.Lock()
   146  	defer provStore.mu.Unlock()
   147  
   148  	sk := storeKey{
   149  		name:   bc.name,
   150  		config: string(bc.config),
   151  		opts:   opts,
   152  	}
   153  	if wp, ok := provStore.providers[sk]; ok {
   154  		wp.refCount++
   155  		return newSingleCloseWrappedProvider(wp), nil
   156  	}
   157  
   158  	provider := bc.starter(opts)
   159  	if provider == nil {
   160  		return nil, fmt.Errorf("provider(%q, %q).Build(%v) failed", sk.name, sk.config, opts)
   161  	}
   162  	wp := &wrappedProvider{
   163  		Provider: provider,
   164  		refCount: 1,
   165  		storeKey: sk,
   166  		store:    provStore,
   167  	}
   168  	provStore.providers[sk] = wp
   169  	return newSingleCloseWrappedProvider(wp), nil
   170  }
   171  
   172  // String returns the provider name and config as a colon separated string.
   173  func (bc *BuildableConfig) String() string {
   174  	return fmt.Sprintf("%s:%s", bc.name, string(bc.config))
   175  }
   176  
   177  // ParseConfig is a convenience function to create a BuildableConfig given a
   178  // provider name and configuration. Returns an error if there is no registered
   179  // builder for the given name or if the config parsing fails.
   180  func ParseConfig(name string, config any) (*BuildableConfig, error) {
   181  	parser := getBuilder(name)
   182  	if parser == nil {
   183  		return nil, fmt.Errorf("no certificate provider builder found for %q", name)
   184  	}
   185  	return parser.ParseConfig(config)
   186  }
   187  
   188  // GetProvider is a convenience function to create a provider given the name,
   189  // config and build options.
   190  func GetProvider(name string, config any, opts BuildOptions) (Provider, error) {
   191  	bc, err := ParseConfig(name, config)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	return bc.Build(opts)
   196  }