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 }