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  }