dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/credentials/certprovider/store.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  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   *
    20   * Copyright 2020 gRPC authors.
    21   *
    22   */
    23  
    24  package certprovider
    25  
    26  import (
    27  	"fmt"
    28  	"sync"
    29  )
    30  
    31  // provStore is the global singleton certificate provider store.
    32  var provStore = &store{
    33  	providers: make(map[storeKey]*wrappedProvider),
    34  }
    35  
    36  // storeKey acts as the key to the map of providers maintained by the store. A
    37  // combination of provider name and configuration is used to uniquely identify
    38  // every provider instance in the store. Go maps need to be indexed by
    39  // comparable types, so the provider configuration is converted from
    40  // `interface{}` to string using the ParseConfig method while creating this key.
    41  type storeKey struct {
    42  	// name of the certificate provider.
    43  	name string
    44  	// configuration of the certificate provider in string form.
    45  	config string
    46  	// opts contains the certificate name and other keyMaterial options.
    47  	opts BuildOptions
    48  }
    49  
    50  // wrappedProvider wraps a provider instance with a reference count.
    51  type wrappedProvider struct {
    52  	Provider
    53  	refCount int
    54  
    55  	// A reference to the key and store are also kept here to override the
    56  	// Close method on the provider.
    57  	storeKey storeKey
    58  	store    *store
    59  }
    60  
    61  // store is a collection of provider instances, safe for concurrent access.
    62  type store struct {
    63  	mu        sync.Mutex
    64  	providers map[storeKey]*wrappedProvider
    65  }
    66  
    67  // Close overrides the Close method of the embedded provider. It releases the
    68  // reference held by the caller on the underlying provider and if the
    69  // provider's reference count reaches zero, it is removed from the store, and
    70  // its Close method is also invoked.
    71  func (wp *wrappedProvider) Close() {
    72  	ps := wp.store
    73  	ps.mu.Lock()
    74  	defer ps.mu.Unlock()
    75  
    76  	wp.refCount--
    77  	if wp.refCount == 0 {
    78  		wp.Provider.Close()
    79  		delete(ps.providers, wp.storeKey)
    80  	}
    81  }
    82  
    83  // BuildableConfig wraps parsed provider configuration and functionality to
    84  // instantiate provider instances.
    85  type BuildableConfig struct {
    86  	name    string
    87  	config  []byte
    88  	starter func(BuildOptions) Provider
    89  	pStore  *store
    90  }
    91  
    92  // NewBuildableConfig creates a new BuildableConfig with the given arguments.
    93  // Provider implementations are expected to invoke this function after parsing
    94  // the given configuration as part of their ParseConfig() method.
    95  // Equivalent configurations are expected to invoke this function with the same
    96  // config argument.
    97  func NewBuildableConfig(name string, config []byte, starter func(BuildOptions) Provider) *BuildableConfig {
    98  	return &BuildableConfig{
    99  		name:    name,
   100  		config:  config,
   101  		starter: starter,
   102  		pStore:  provStore,
   103  	}
   104  }
   105  
   106  // Build kicks off a provider instance with the wrapped configuration. Multiple
   107  // invocations of this method with the same opts will result in provider
   108  // instances being reused.
   109  func (bc *BuildableConfig) Build(opts BuildOptions) (Provider, error) {
   110  	provStore.mu.Lock()
   111  	defer provStore.mu.Unlock()
   112  
   113  	sk := storeKey{
   114  		name:   bc.name,
   115  		config: string(bc.config),
   116  		opts:   opts,
   117  	}
   118  	if wp, ok := provStore.providers[sk]; ok {
   119  		wp.refCount++
   120  		return wp, nil
   121  	}
   122  
   123  	provider := bc.starter(opts)
   124  	if provider == nil {
   125  		return nil, fmt.Errorf("provider(%q, %q).Build(%v) failed", sk.name, sk.config, opts)
   126  	}
   127  	wp := &wrappedProvider{
   128  		Provider: provider,
   129  		refCount: 1,
   130  		storeKey: sk,
   131  		store:    provStore,
   132  	}
   133  	provStore.providers[sk] = wp
   134  	return wp, nil
   135  }
   136  
   137  // String returns the provider name and config as a colon separated string.
   138  func (bc *BuildableConfig) String() string {
   139  	return fmt.Sprintf("%s:%s", bc.name, string(bc.config))
   140  }
   141  
   142  // ParseConfig is a convenience function to create a BuildableConfig given a
   143  // provider name and configuration. Returns an error if there is no registered
   144  // builder for the given name or if the config parsing fails.
   145  func ParseConfig(name string, config interface{}) (*BuildableConfig, error) {
   146  	parser := GetBuilder(name)
   147  	if parser == nil {
   148  		return nil, fmt.Errorf("no certificate provider builder found for %q", name)
   149  	}
   150  	return parser.ParseConfig(config)
   151  }
   152  
   153  // GetProvider is a convenience function to create a provider given the name,
   154  // config and build options.
   155  func GetProvider(name string, config interface{}, opts BuildOptions) (Provider, error) {
   156  	bc, err := ParseConfig(name, config)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	return bc.Build(opts)
   161  }