package boltdb import ( "encoding/json" "time" "github.com/sirupsen/logrus" bolt "go.etcd.io/bbolt" ) func backupMetadata(connection *bolt.DB) (map[string]interface{}, error) { buckets := map[string]interface{}{} err := connection.View(func(tx *bolt.Tx) error { err := tx.ForEach(func(name []byte, bucket *bolt.Bucket) error { bucketName := string(name) bucket = tx.Bucket([]byte(bucketName)) seqId := bucket.Sequence() buckets[bucketName] = int(seqId) return nil }) return err }) return buckets, err } // ExportJSON creates a JSON representation from a DbConnection. You can include // the database's metadata or ignore it. Ensure the database is closed before // using this function // inspired by github.com/konoui/boltdb-exporter (which has no license) // but very much simplified, based on how we use boltdb func (c *DbConnection) ExportJson(databasePath string, metadata bool) ([]byte, error) { logrus.WithField("databasePath", databasePath).Infof("exportJson") connection, err := bolt.Open(databasePath, 0600, &bolt.Options{Timeout: 1 * time.Second, ReadOnly: true}) if err != nil { return []byte("{}"), err } defer connection.Close() backup := make(map[string]interface{}) if metadata { meta, err := backupMetadata(connection) if err != nil { logrus.WithError(err).Errorf("Failed exporting metadata: %v", err) } backup["__metadata"] = meta } err = connection.View(func(tx *bolt.Tx) error { err = tx.ForEach(func(name []byte, bucket *bolt.Bucket) error { bucketName := string(name) var list []interface{} version := make(map[string]string) cursor := bucket.Cursor() for k, v := cursor.First(); k != nil; k, v = cursor.Next() { if v == nil { continue } var obj interface{} err := c.UnmarshalObject(v, &obj) if err != nil { logrus.WithError(err).Errorf("Failed to unmarshal (bucket %s): %v", bucketName, string(v)) obj = v } if bucketName == "version" { version[string(k)] = string(v) } else { list = append(list, obj) } } if bucketName == "version" { backup[bucketName] = version return nil } if len(list) > 0 { if bucketName == "ssl" || bucketName == "settings" || bucketName == "tunnel_server" { backup[bucketName] = nil if len(list) > 0 { backup[bucketName] = list[0] } return nil } backup[bucketName] = list return nil } return nil }) return err }) if err != nil { return []byte("{}"), err } return json.MarshalIndent(backup, "", " ") }