db: Migrate FileUpload.ContentTypes to auto-detect

This commit is contained in:
Daan Sprenkels 2019-12-10 11:59:02 +01:00
parent eec5e4def4
commit 62e82d831e
3 changed files with 70 additions and 9 deletions

View File

@ -18,15 +18,15 @@ var (
func main() { func main() {
flag.Parse() flag.Parse()
database, err := db.OpenDB(*databasePath)
if err != nil {
log.Fatalln(err)
}
defer database.Close()
filestore, err := db.OpenFileStore(*fileStorePath) filestore, err := db.OpenFileStore(*fileStorePath)
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
database, err := db.OpenDB(*databasePath, filestore)
if err != nil {
log.Fatalln(err)
}
defer database.Close()
go rushlink.StartMetricsServer(*metricsListen, database, filestore) go rushlink.StartMetricsServer(*metricsListen, database, filestore)
rushlink.StartMainServer(*httpListen, database, filestore) rushlink.StartMainServer(*httpListen, database, filestore)

View File

@ -1,8 +1,12 @@
package db package db
import ( import (
"bytes"
"fmt" "fmt"
"io"
"log" "log"
"net/http"
"os"
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -24,7 +28,7 @@ type Database struct {
// //
// If we alter the database format, we bump this number and write a new // If we alter the database format, we bump this number and write a new
// database migration in migrate(). // database migration in migrate().
const CurrentMigrateVersion = 2 const CurrentMigrateVersion = 3
// BucketConf holds the name for the "configuration" bucket. // BucketConf holds the name for the "configuration" bucket.
// //
@ -42,7 +46,7 @@ const BucketFileUpload = "fileUpload"
const KeyMigrateVersion = "migrate_version" const KeyMigrateVersion = "migrate_version"
// OpenDB opens a database file located at path. // OpenDB opens a database file located at path.
func OpenDB(path string) (*Database, error) { func OpenDB(path string, fs *FileStore) (*Database, error) {
if path == "" { if path == "" {
return nil, errors.New("database not set") return nil, errors.New("database not set")
} }
@ -51,7 +55,7 @@ func OpenDB(path string) (*Database, error) {
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to open database at '%v'", path) return nil, errors.Wrapf(err, "failed to open database at '%v'", path)
} }
if err := db.Update(migrate); err != nil { if err := db.Update(func(tx *bolt.Tx) error { return migrate(tx, fs) }); err != nil {
return nil, err return nil, err
} }
return &Database{db}, nil return &Database{db}, nil
@ -66,7 +70,12 @@ func (db *Database) Close() error {
} }
// Initialize and migrate the database to the current version // Initialize and migrate the database to the current version
func migrate(tx *bolt.Tx) error { func migrate(tx *bolt.Tx, fs *FileStore) error {
// Guidelines for error handling:
// - Errors based on malformed *structure* should be fatal!
// - Errors based on malformed *data* should print a warning
// (and if possible try to fix the error).
dbVersion, err := dbVersion(tx) dbVersion, err := dbVersion(tx)
if err != nil { if err != nil {
return err return err
@ -104,6 +113,54 @@ func migrate(tx *bolt.Tx) error {
} }
} }
if dbVersion < 3 {
log.Println("migrating database to version 3")
// In this version, we changed te way how Content-Types are being
// stored. Previously, we allowed clients to provide their own
// Content-Types for files, using the Content-Disposition header in
// multipart forms. The new way detects these types using
// http.DetectContentType.
//
// Scan through all the FileUploads and update their ContentTypes.
bucket := tx.Bucket([]byte(BucketFileUpload))
cursor := bucket.Cursor()
var id, storedBytes []byte
id, storedBytes = cursor.First()
for id != nil {
fu, err := decodeFileUpload(storedBytes)
if err != nil {
log.Print("error: ", errors.Wrapf(err, "corrupted FileUpload in database at '%v'", id))
id, storedBytes = cursor.Next()
continue
}
if fu.State != FileUploadStatePresent {
id, storedBytes = cursor.Next()
continue
}
filePath := fu.Path(fs)
file, err := os.Open(fu.Path(fs))
if err != nil {
log.Print("error: ", errors.Wrapf(err, "could not open file at '%v'", filePath))
id, storedBytes = cursor.Next()
continue
}
var buf bytes.Buffer
buf.Grow(512)
io.CopyN(&buf, file, 512)
contentType := http.DetectContentType(buf.Bytes())
if contentType != fu.ContentType {
fu.ContentType = contentType
fu.Save(tx)
cursor.Seek(id)
}
id, storedBytes = cursor.Next()
}
// Update the version number
if err := setDBVersion(tx, 3); err != nil {
return err
}
}
return nil return nil
} }

View File

@ -153,6 +153,10 @@ func GetFileUpload(tx *bolt.Tx, id uuid.UUID) (*FileUpload, error) {
if storedBytes == nil { if storedBytes == nil {
return nil, nil return nil, nil
} }
return decodeFileUpload(storedBytes)
}
func decodeFileUpload(storedBytes []byte) (*FileUpload, error) {
fu := &FileUpload{} fu := &FileUpload{}
err := gobmarsh.Unmarshal(storedBytes, fu) err := gobmarsh.Unmarshal(storedBytes, fu)
return fu, err return fu, err