github.com/whiteboxio/flow@v0.0.3-0.20190918184116-508d75d68a2c/pkg/cfg/repository.go (about) 1 package cfg 2 3 import ( 4 "fmt" 5 "sort" 6 "sync" 7 8 "github.com/awesome-flow/flow/pkg/cast" 9 "github.com/awesome-flow/flow/pkg/types" 10 "github.com/awesome-flow/flow/pkg/util/data" 11 ) 12 13 const ( 14 // CfgPathKey is a string constant used globally to reach up the config 15 // file path setting. 16 CfgPathKey = "config.path" 17 // PluginPathKey is a string constant used globally to reach up the plugin 18 // path setting. 19 PluginPathKey = "plugin.path" 20 21 SystemMaxprocs = "system.maxprocs" 22 ) 23 24 // TODO(olegs): implement listener interface 25 // type Listener func(*types.KeyValue) 26 27 // Provider is a generic interface for config providers. 28 // A method initializing a new instance of Provider must conform to Constructor 29 // type. 30 type Provider interface { 31 Name() string 32 Depends() []string 33 SetUp(*Repository) error 34 TearDown(*Repository) error 35 Get(types.Key) (*types.KeyValue, bool) 36 Weight() int 37 } 38 39 var ( 40 mappers *cast.MapperNode 41 mappersMx sync.Mutex 42 ) 43 44 // Constructor is the signature Provider instances are expected to implement 45 // as a producing function. 46 type Constructor func(*Repository, int) (Provider, error) 47 48 type node struct { 49 providers []Provider 50 //listeners []Listener 51 children map[string]*node 52 } 53 54 func newNode() *node { 55 return &node{ 56 providers: make([]Provider, 0), 57 //listeners: make([]Listener, 0), 58 children: make(map[string]*node), 59 } 60 } 61 62 func (n *node) explain(key types.Key) map[string]interface{} { 63 res := map[string]interface{}{} 64 if len(n.providers) > 0 { 65 valdescr := make([]map[string]interface{}, 0, len(n.providers)) 66 for _, prov := range n.providers { 67 if kv, ok := prov.Get(key); ok { 68 valdescr = append(valdescr, map[string]interface{}{ 69 "provider_name": prov.Name(), 70 "provider_weight": prov.Weight(), 71 "value": kv.Value, 72 }) 73 } 74 } 75 res["__value__"] = valdescr 76 } else if len(n.children) > 0 { 77 for k, ch := range n.children { 78 res[k] = ch.explain(append(key, k)) 79 } 80 } 81 return res 82 } 83 84 func (n *node) add(key types.Key, prov Provider) { 85 ptr := n 86 for _, k := range key { 87 if _, ok := ptr.children[k]; !ok { 88 ptr.children[k] = newNode() 89 } 90 ptr = ptr.children[k] 91 } 92 ptr.providers = append(ptr.providers, prov) 93 sort.Slice(ptr.providers, func(a, b int) bool { 94 return ptr.providers[a].Weight() > ptr.providers[b].Weight() 95 }) 96 } 97 98 func (n *node) find(key types.Key) *node { 99 ptr := n 100 for _, k := range key { 101 if _, ok := ptr.children[k]; !ok { 102 return nil 103 } 104 ptr = ptr.children[k] 105 } 106 return ptr 107 } 108 109 func (n *node) findOrCreate(key types.Key) *node { 110 ptr := n 111 for _, k := range key { 112 if _, ok := ptr.children[k]; !ok { 113 ptr.children[k] = newNode() 114 } 115 ptr = ptr.children[k] 116 } 117 return ptr 118 } 119 120 // func (n *node) subscribe(key types.Key, listener Listener) { 121 // panic("not implemented") 122 // } 123 124 func (n *node) get(repo *Repository, key types.Key) (*types.KeyValue, bool) { 125 ptr := n.find(key) 126 if ptr == nil { 127 return nil, false 128 } 129 if len(ptr.providers) != 0 { 130 for _, prov := range ptr.providers { 131 if kv, ok := prov.Get(key); ok { 132 if mkv, err := repo.doMap(kv); err != nil { 133 panic(err) 134 } else { 135 return mkv, ok 136 } 137 } 138 } 139 return nil, false 140 } 141 if len(ptr.children) != 0 { 142 return ptr.getAll(repo, key), true 143 } 144 return nil, false 145 } 146 147 func (n *node) getAll(repo *Repository, pref types.Key) *types.KeyValue { 148 res := make(map[string]types.Value) 149 for k, ch := range n.children { 150 key := types.Key(append(pref, k)) 151 if len(ch.providers) > 0 { 152 // Providers are expected to be sorted 153 for _, prov := range ch.providers { 154 if kv, ok := prov.Get(key); ok { 155 mkv, err := repo.doMap(kv) 156 if err != nil { 157 panic(err) 158 } 159 res[k] = mkv.Value 160 break 161 } 162 } 163 } else { 164 res[k] = ch.getAll(repo, key).Value 165 } 166 } 167 mkv, err := repo.doMap(&types.KeyValue{Key: pref, Value: res}) 168 if err != nil { 169 panic(err) 170 } 171 return mkv 172 } 173 174 // Repository is a generic structure used by flow to store config maps and 175 // corresponding type mappers. 176 // There is 1 globally registered repository instance available by loading from 177 // global storage: `global.Load("config")`. It keeps the init-stage system 178 // settings and might be used by any consumer. 179 // Plugin code can instantiate and use locally defined repositories. Having 180 // independent repositories is practical. 181 type Repository struct { 182 mappers *cast.MapperNode 183 root *node 184 providers map[string]Provider 185 mx sync.Mutex 186 } 187 188 // NewRepository returns a new instance of an empty Repository. 189 func NewRepository() *Repository { 190 return &Repository{ 191 mappers: cast.NewMapperNode(), 192 root: newNode(), 193 providers: make(map[string]Provider), 194 mx: sync.Mutex{}, 195 } 196 } 197 198 // SetUp traverses registered providers and calls `provider.SetUp(repo)`. 199 // Providers are traversed in topological order, based on the dependencies 200 // they defined using `Depends()` method. 201 // Firstly, it sets up providers with no dependencies and progresses forward 202 // as providers with non-zero dependencies turn to be unblocked. 203 // Returns an error if at least 1 provider failed to call `SetUp`. 204 func (repo *Repository) SetUp() error { 205 providers, err := repo.traverseProviders() 206 if err != nil { 207 return err 208 } 209 for _, prov := range providers { 210 if err := prov.SetUp(repo); err != nil { 211 return err 212 } 213 } 214 215 return nil 216 } 217 218 // TearDown does the opposite to `SetUp`: it prepares providers to get 219 // unloaded. The sequence of `provider.TearDown(repo)` is exactly the same 220 // as SetUp(): topologically sorted dependency list. 221 // Returns an error if at least 1 provider failed to call `TearDown`. 222 func (repo *Repository) TearDown() error { 223 providers, err := repo.traverseProviders() 224 if err != nil { 225 return err 226 } 227 for _, prov := range providers { 228 if err := prov.TearDown(repo); err != nil { 229 return err 230 } 231 } 232 return nil 233 } 234 235 func (repo *Repository) traverseProviders() ([]Provider, error) { 236 provList := make([]data.TopologyNode, 0, len(repo.providers)) 237 for _, prov := range repo.providers { 238 provList = append(provList, prov) 239 } 240 top := data.NewTopology(provList...) 241 for name, prov := range repo.providers { 242 for _, dep := range prov.Depends() { 243 top.Connect(repo.providers[name], repo.providers[dep]) 244 } 245 } 246 resolved, err := top.Sort() 247 if err != nil { 248 return []Provider{}, err 249 } 250 res := make([]Provider, len(resolved)) 251 for ix, prov := range resolved { 252 res[ix] = prov.(Provider) 253 } 254 return res, nil 255 } 256 257 // DefineSchema registers a schema in the repo. 258 // Multiple non-overlapping schemas might be registered sequentually with 259 // an equivalence of registering a composite schema at once. 260 // Returns an error if the root mapper node failes to register the schema. 261 func (repo *Repository) DefineSchema(s cast.Schema) error { 262 return repo.mappers.DefineSchema(s) 263 } 264 265 func (repo *Repository) doMap(kv *types.KeyValue) (*types.KeyValue, error) { 266 return repo.mappers.Map(kv) 267 } 268 269 // RegisterProvider marks a provider as known to the repository. 270 // A registered provider will be visited by `SetUp` and `TearDown` methods, 271 // but won't serve any key lookup requests yet. Used at the very early stage 272 // of the system initialization in order to trigger providers's `SetUp` method. 273 // This method is thread safe. 274 func (repo *Repository) RegisterProvider(prov Provider) { 275 repo.mx.Lock() 276 defer repo.mx.Unlock() 277 repo.providers[prov.Name()] = prov 278 } 279 280 // RegisterKey registers a provider as a potential servant for the specified 281 // key. 282 // If a provider can serve multiple keys, every key registration must be 283 // created explicitly, 1 at a time. 284 // This method is thread safe. 285 func (repo *Repository) RegisterKey(key types.Key, prov Provider) error { 286 if prov == nil { 287 return fmt.Errorf("provider for key %s can not be nil", key) 288 } 289 repo.mx.Lock() 290 defer repo.mx.Unlock() 291 repo.root.add(key, prov) 292 if _, ok := repo.providers[prov.Name()]; !ok { 293 repo.providers[prov.Name()] = prov 294 } 295 296 return nil 297 } 298 299 //func (repo *Repository) Subscribe(key cast.Key, listener Listener) { 300 // repo.root.subscribe(key, listener) 301 //} 302 303 // Get is the primary interface for the stored data retrieval. 304 // Returns the fetched value and a bool flag indicating the lookup result. 305 // If no value was retrived from the providers, bool flag is set to false. 306 func (repo *Repository) Get(key types.Key) (types.Value, bool) { 307 // Non-empty key check prevents users from accessing a protected 308 // root node 309 if len(key) != 0 { 310 if kv, ok := repo.root.get(repo, key); ok { 311 return kv.Value, ok 312 } 313 } 314 return nil, false 315 } 316 317 // Explain returns a structure with a detailed explanation of the repository. 318 // The resulting map mimics the original config map structure and leafs 319 // indicate per-provider breakdown with a corresponding value returned by 320 // each of them. 321 func (repo *Repository) Explain() map[string]interface{} { 322 return repo.root.explain(nil) 323 }