database.go (2540B)
1 package database 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "fmt" 7 "log" 8 "time" 9 10 "go.etcd.io/bbolt" 11 ) 12 13 // bbolt bucket keys 14 var ( 15 BucketKeyConfig = []byte("config") // bucket key for config bucket key 16 BucketKeyEvents = []byte("events") // bucket key for events bucket 17 BucketKeyServices = []byte("services") // bucket key for services bucket 18 BucketKeyServiceTokens = []byte("service-tokens") // bucket key for service-tokens bucket 19 ) 20 21 // ErrConfigBucket is returned when bbolt is unable to open the config bucket 22 // TODO: I'm not sure if this is the idiomatic way to use errors 23 var ErrConfigBucket = errors.New("unable to open config bucket") 24 25 var db *bbolt.DB 26 27 // check for fatal errors 28 func check(err error) { 29 if err != nil { 30 log.Fatal(err) 31 } 32 } 33 34 // Open opens the database for the given file name or creates it if it doesn't exist. 35 // Returns true if the database is already configured, false if it was just created. 36 func Open(dbFileName string) (configured bool) { 37 var err error 38 db, err = bbolt.Open(dbFileName, 0600, &bbolt.Options{Timeout: 1 * time.Second}) 39 check(err) 40 // Create the necessary bbolt buckets if they don't exist 41 db.Update(func(tx *bbolt.Tx) error { 42 configured = tx.Bucket(BucketKeyConfig) != nil 43 _, err = tx.CreateBucketIfNotExists(BucketKeyConfig) 44 check(err) 45 _, err = tx.CreateBucketIfNotExists(BucketKeyServices) 46 check(err) 47 _, err = tx.CreateBucketIfNotExists(BucketKeyEvents) 48 check(err) 49 _, err = tx.CreateBucketIfNotExists(BucketKeyServiceTokens) 50 check(err) 51 return nil 52 }) 53 return configured 54 } 55 56 // Close is just a wrapper around db.Close() in order to keep all database 57 // management in one file 58 func Close() { 59 db.Close() 60 } 61 62 // GetDB gets the database structure 63 func GetDB() *bbolt.DB { 64 return db 65 } 66 67 // BytesToUint64 converts a byte array to a uint64 number, an operation that is 68 // often repeated for IDs. It is assumed that the data will parse successfully 69 // (i.e. type checking is performed in an earlier stage). 70 // If the parsing fails, the function therefore panics 71 func BytesToUint64(b []byte) uint64 { 72 return binary.BigEndian.Uint64(b) 73 } 74 75 // Uint64ToBytes converts a uint64 formatted number to a byte array 76 func Uint64ToBytes(ui64 uint64) (b []byte) { 77 b = make([]byte, 8) 78 binary.BigEndian.PutUint64(b, ui64) 79 return 80 } 81 82 // PrintBucket simply prints the K-V pairs in the bucket 83 func PrintBucket(eb *bbolt.Bucket) error { 84 return eb.ForEach(func(k, v []byte) error { 85 fmt.Printf("- %v: %v\n", string(k), string(v)) 86 return nil 87 }) 88 }