2023-02-14 18:18:07 +00:00
|
|
|
package boltdb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2023-05-05 00:19:47 +00:00
|
|
|
"fmt"
|
2023-02-14 18:18:07 +00:00
|
|
|
|
|
|
|
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
|
|
|
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
bolt "go.etcd.io/bbolt"
|
|
|
|
)
|
|
|
|
|
|
|
|
type DbTransaction struct {
|
|
|
|
conn *DbConnection
|
|
|
|
tx *bolt.Tx
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *DbTransaction) SetServiceName(bucketName string) error {
|
|
|
|
_, err := tx.tx.CreateBucketIfNotExists([]byte(bucketName))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *DbTransaction) GetObject(bucketName string, key []byte, object interface{}) error {
|
|
|
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
|
|
|
|
|
|
|
value := bucket.Get(key)
|
|
|
|
if value == nil {
|
2023-05-05 00:19:47 +00:00
|
|
|
return fmt.Errorf("%w (bucket=%s, key=%s)", dserrors.ErrObjectNotFound, bucketName, keyToString(key))
|
2023-02-14 18:18:07 +00:00
|
|
|
}
|
|
|
|
|
2023-10-24 16:55:11 +00:00
|
|
|
return tx.conn.UnmarshalObject(value, object)
|
2023-02-14 18:18:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *DbTransaction) UpdateObject(bucketName string, key []byte, object interface{}) error {
|
|
|
|
data, err := tx.conn.MarshalObject(object)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
|
|
|
return bucket.Put(key, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *DbTransaction) DeleteObject(bucketName string, key []byte) error {
|
|
|
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
|
|
|
return bucket.Delete(key)
|
|
|
|
}
|
|
|
|
|
2023-06-20 20:51:34 +00:00
|
|
|
func (tx *DbTransaction) DeleteAllObjects(bucketName string, obj interface{}, matchingFn func(o interface{}) (id int, ok bool)) error {
|
2023-06-27 19:42:52 +00:00
|
|
|
var ids []int
|
|
|
|
|
2023-02-14 18:18:07 +00:00
|
|
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
|
|
|
|
|
|
|
cursor := bucket.Cursor()
|
|
|
|
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
|
|
|
err := tx.conn.UnmarshalObject(v, &obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-06-20 20:51:34 +00:00
|
|
|
if id, ok := matchingFn(obj); ok {
|
2023-06-27 19:42:52 +00:00
|
|
|
ids = append(ids, id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, id := range ids {
|
|
|
|
if err := bucket.Delete(tx.conn.ConvertToKey(id)); err != nil {
|
|
|
|
return err
|
2023-02-14 18:18:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *DbTransaction) GetNextIdentifier(bucketName string) int {
|
|
|
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
|
|
|
id, err := bucket.NextSequence()
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Str("bucket", bucketName).Msg("failed to get the next identifer")
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return int(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *DbTransaction) CreateObject(bucketName string, fn func(uint64) (int, interface{})) error {
|
|
|
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
|
|
|
|
|
|
|
seqId, _ := bucket.NextSequence()
|
|
|
|
id, obj := fn(seqId)
|
|
|
|
|
|
|
|
data, err := tx.conn.MarshalObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-03-13 16:18:28 +00:00
|
|
|
return bucket.Put(tx.conn.ConvertToKey(id), data)
|
2023-02-14 18:18:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *DbTransaction) CreateObjectWithId(bucketName string, id int, obj interface{}) error {
|
|
|
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
|
|
|
data, err := tx.conn.MarshalObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return bucket.Put(tx.conn.ConvertToKey(id), data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tx *DbTransaction) CreateObjectWithStringId(bucketName string, id []byte, obj interface{}) error {
|
|
|
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
|
|
|
data, err := tx.conn.MarshalObject(obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return bucket.Put(id, data)
|
|
|
|
}
|
|
|
|
|
2023-06-20 20:51:34 +00:00
|
|
|
func (tx *DbTransaction) GetAll(bucketName string, obj interface{}, appendFn func(o interface{}) (interface{}, error)) error {
|
2023-02-14 18:18:07 +00:00
|
|
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
|
|
|
|
2023-06-20 20:51:34 +00:00
|
|
|
return bucket.ForEach(func(k []byte, v []byte) error {
|
2023-02-14 18:18:07 +00:00
|
|
|
err := tx.conn.UnmarshalObject(v, obj)
|
2023-06-20 20:51:34 +00:00
|
|
|
if err == nil {
|
|
|
|
obj, err = appendFn(obj)
|
2023-02-14 18:18:07 +00:00
|
|
|
}
|
|
|
|
|
2023-06-20 20:51:34 +00:00
|
|
|
return err
|
|
|
|
})
|
2023-02-14 18:18:07 +00:00
|
|
|
}
|
|
|
|
|
2023-06-20 20:51:34 +00:00
|
|
|
func (tx *DbTransaction) GetAllWithJsoniter(bucketName string, obj interface{}, appendFn func(o interface{}) (interface{}, error)) error {
|
2023-02-14 18:18:07 +00:00
|
|
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
|
|
|
|
2023-06-20 20:51:34 +00:00
|
|
|
return bucket.ForEach(func(k []byte, v []byte) error {
|
2023-10-24 16:55:11 +00:00
|
|
|
err := tx.conn.UnmarshalObject(v, obj)
|
2023-06-20 20:51:34 +00:00
|
|
|
if err == nil {
|
|
|
|
obj, err = appendFn(obj)
|
2023-02-14 18:18:07 +00:00
|
|
|
}
|
|
|
|
|
2023-06-20 20:51:34 +00:00
|
|
|
return err
|
|
|
|
})
|
2023-02-14 18:18:07 +00:00
|
|
|
}
|
|
|
|
|
2023-06-20 20:51:34 +00:00
|
|
|
func (tx *DbTransaction) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj interface{}, appendFn func(o interface{}) (interface{}, error)) error {
|
2023-02-14 18:18:07 +00:00
|
|
|
cursor := tx.tx.Bucket([]byte(bucketName)).Cursor()
|
|
|
|
|
|
|
|
for k, v := cursor.Seek(keyPrefix); k != nil && bytes.HasPrefix(k, keyPrefix); k, v = cursor.Next() {
|
2023-10-24 16:55:11 +00:00
|
|
|
err := tx.conn.UnmarshalObject(v, obj)
|
2023-02-14 18:18:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-06-20 20:51:34 +00:00
|
|
|
obj, err = appendFn(obj)
|
2023-02-14 18:18:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|