github.com/safing/portbase@v0.19.5/config/database.go (about)

     1  package config
     2  
     3  import (
     4  	"errors"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/safing/portbase/database"
     9  	"github.com/safing/portbase/database/iterator"
    10  	"github.com/safing/portbase/database/query"
    11  	"github.com/safing/portbase/database/record"
    12  	"github.com/safing/portbase/database/storage"
    13  	"github.com/safing/portbase/log"
    14  )
    15  
    16  var dbController *database.Controller
    17  
    18  // StorageInterface provices a storage.Interface to the configuration manager.
    19  type StorageInterface struct {
    20  	storage.InjectBase
    21  }
    22  
    23  // Get returns a database record.
    24  func (s *StorageInterface) Get(key string) (record.Record, error) {
    25  	opt, err := GetOption(key)
    26  	if err != nil {
    27  		return nil, storage.ErrNotFound
    28  	}
    29  
    30  	return opt.Export()
    31  }
    32  
    33  // Put stores a record in the database.
    34  func (s *StorageInterface) Put(r record.Record) (record.Record, error) {
    35  	if r.Meta().Deleted > 0 {
    36  		return r, setConfigOption(r.DatabaseKey(), nil, false)
    37  	}
    38  
    39  	acc := r.GetAccessor(r)
    40  	if acc == nil {
    41  		return nil, errors.New("invalid data")
    42  	}
    43  
    44  	val, ok := acc.Get("Value")
    45  	if !ok || val == nil {
    46  		err := setConfigOption(r.DatabaseKey(), nil, false)
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  		return s.Get(r.DatabaseKey())
    51  	}
    52  
    53  	option, err := GetOption(r.DatabaseKey())
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	var value interface{}
    59  	switch option.OptType {
    60  	case OptTypeString:
    61  		value, ok = acc.GetString("Value")
    62  	case OptTypeStringArray:
    63  		value, ok = acc.GetStringArray("Value")
    64  	case OptTypeInt:
    65  		value, ok = acc.GetInt("Value")
    66  	case OptTypeBool:
    67  		value, ok = acc.GetBool("Value")
    68  	case optTypeAny:
    69  		ok = false
    70  	}
    71  	if !ok {
    72  		return nil, errors.New("received invalid value in \"Value\"")
    73  	}
    74  
    75  	if err := setConfigOption(r.DatabaseKey(), value, false); err != nil {
    76  		return nil, err
    77  	}
    78  	return option.Export()
    79  }
    80  
    81  // Delete deletes a record from the database.
    82  func (s *StorageInterface) Delete(key string) error {
    83  	return setConfigOption(key, nil, false)
    84  }
    85  
    86  // Query returns a an iterator for the supplied query.
    87  func (s *StorageInterface) Query(q *query.Query, local, internal bool) (*iterator.Iterator, error) {
    88  	optionsLock.RLock()
    89  	defer optionsLock.RUnlock()
    90  
    91  	it := iterator.New()
    92  	var opts []*Option
    93  	for _, opt := range options {
    94  		if strings.HasPrefix(opt.Key, q.DatabaseKeyPrefix()) {
    95  			opts = append(opts, opt)
    96  		}
    97  	}
    98  
    99  	go s.processQuery(it, opts)
   100  
   101  	return it, nil
   102  }
   103  
   104  func (s *StorageInterface) processQuery(it *iterator.Iterator, opts []*Option) {
   105  	sort.Sort(sortByKey(opts))
   106  
   107  	for _, opt := range opts {
   108  		r, err := opt.Export()
   109  		if err != nil {
   110  			it.Finish(err)
   111  			return
   112  		}
   113  		it.Next <- r
   114  	}
   115  
   116  	it.Finish(nil)
   117  }
   118  
   119  // ReadOnly returns whether the database is read only.
   120  func (s *StorageInterface) ReadOnly() bool {
   121  	return false
   122  }
   123  
   124  func registerAsDatabase() error {
   125  	_, err := database.Register(&database.Database{
   126  		Name:        "config",
   127  		Description: "Configuration Manager",
   128  		StorageType: "injected",
   129  	})
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	controller, err := database.InjectDatabase("config", &StorageInterface{})
   135  	if err != nil {
   136  		return err
   137  	}
   138  
   139  	dbController = controller
   140  	return nil
   141  }
   142  
   143  // handleOptionUpdate updates the expertise and release level options,
   144  // if required, and eventually pushes a update for the option.
   145  // The caller must hold the option lock.
   146  func handleOptionUpdate(option *Option, push bool) {
   147  	if expertiseLevelOptionFlag.IsSet() && option == expertiseLevelOption {
   148  		updateExpertiseLevel()
   149  	}
   150  
   151  	if releaseLevelOptionFlag.IsSet() && option == releaseLevelOption {
   152  		updateReleaseLevel()
   153  	}
   154  
   155  	if push {
   156  		pushUpdate(option)
   157  	}
   158  }
   159  
   160  // pushUpdate pushes an database update notification for option.
   161  // The caller must hold the option lock.
   162  func pushUpdate(option *Option) {
   163  	r, err := option.export()
   164  	if err != nil {
   165  		log.Errorf("failed to export option to push update: %s", err)
   166  	} else {
   167  		dbController.PushUpdate(r)
   168  	}
   169  }