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 }