simple database migration versioning, todo: implement migrations

This commit is contained in:
Christoph Haas 2021-04-06 00:07:05 +02:00
parent b4f3228bec
commit 8f21c12c3d
4 changed files with 84 additions and 9 deletions

View File

@ -74,3 +74,36 @@ func GetDatabaseForConfig(cfg *DatabaseConfig) (db *gorm.DB, err error) {
db.Config.Logger = logger.New(logrus.StandardLogger(), logCfg)
return
}
type DatabaseMigrationInfo struct {
Version string `gorm:"primaryKey"`
Applied time.Time
}
func MigrateDatabase(db *gorm.DB, version string) error {
if err := db.AutoMigrate(&DatabaseMigrationInfo{}); err != nil {
return errors.Wrap(err, "failed to migrate version database")
}
newVersion := DatabaseMigrationInfo{
Version: version,
Applied: time.Now(),
}
existingMigration := DatabaseMigrationInfo{}
db.Where("version = ?", version).FirstOrInit(&existingMigration)
if existingMigration.Version == "" {
lastVersion := DatabaseMigrationInfo{}
db.Order("applied desc, version desc").FirstOrInit(&lastVersion)
// TODO: migrate database
res := db.Create(&newVersion)
if res.Error != nil {
return errors.Wrap(res.Error, "failed to write version to database")
}
}
return nil
}

View File

@ -103,6 +103,10 @@ func (s *Server) Setup(ctx context.Context) error {
if err != nil {
return errors.WithMessage(err, "database setup failed")
}
err = common.MigrateDatabase(s.db, Version)
if err != nil {
return errors.WithMessage(err, "database migration failed")
}
// Setup http server
gin.SetMode(gin.DebugMode)
@ -183,9 +187,6 @@ func (s *Server) Setup(ctx context.Context) error {
if s.peers, err = wireguard.NewPeerManager(s.db, s.wg); err != nil {
return errors.WithMessage(err, "unable to setup peer manager")
}
if err = s.peers.InitFromPhysicalInterface(); err != nil {
return errors.WithMessagef(err, "unable to initialize peer manager")
}
for _, deviceName := range s.wg.Cfg.DeviceNames {
if err = s.RestoreWireGuardInterface(deviceName); err != nil {

View File

@ -0,0 +1,3 @@
package server
var Version = "1.0.5"

View File

@ -70,7 +70,7 @@ type Peer struct {
UID string `form:"uid" binding:"required,alphanum"` // uid for html identification
DeviceName string `gorm:"index" form:"device" binding:"required"`
DeviceType DeviceType `gorm:"-" form:"devicetype" binding:"required,oneof=client server custom"`
DeviceType DeviceType `gorm:"-" form:"devicetype" binding:"required,oneof=client server"`
Identifier string `form:"identifier" binding:"required,max=64"` // Identifier AND Email make a WireGuard peer unique
Email string `gorm:"index" form:"mail" binding:"required,email"`
IgnoreGlobalSettings bool `form:"ignoreglobalsettings"`
@ -223,7 +223,7 @@ const (
type Device struct {
Interface *wgtypes.Device `gorm:"-"`
Type DeviceType `form:"devicetype" binding:"required,oneof=client server custom"`
Type DeviceType `form:"devicetype" binding:"required,oneof=client server"`
DeviceName string `form:"device" gorm:"primaryKey" binding:"required,alphanum"`
DisplayName string `form:"displayname" binding:"omitempty,max=200"`
@ -344,10 +344,31 @@ type PeerManager struct {
func NewPeerManager(db *gorm.DB, wg *Manager) (*PeerManager, error) {
pm := &PeerManager{db: db, wg: wg}
// check if old device table exists (from version <= 1.0.3), if so migrate it.
if db.Migrator().HasColumn(&Device{}, "endpoint") {
if err := db.Migrator().RenameColumn(&Device{}, "endpoint", "default_endpoint"); err != nil {
return nil, errors.Wrapf(err, "failed to migrate old database structure for column endpoint")
}
}
if db.Migrator().HasColumn(&Device{}, "allowed_ips_str") {
if err := db.Migrator().RenameColumn(&Device{}, "allowed_ips_str", "default_allowed_ips_str"); err != nil {
return nil, errors.Wrapf(err, "failed to migrate old database structure for column allowed_ips_str")
}
}
if db.Migrator().HasColumn(&Device{}, "persistent_keepalive") {
if err := db.Migrator().RenameColumn(&Device{}, "persistent_keepalive", "default_persistent_keepalive"); err != nil {
return nil, errors.Wrapf(err, "failed to migrate old database structure for column persistent_keepalive")
}
}
if err := pm.db.AutoMigrate(&Peer{}, &Device{}); err != nil {
return nil, errors.WithMessage(err, "failed to migrate peer database")
}
if err := pm.initFromPhysicalInterface(); err != nil {
return nil, errors.WithMessagef(err, "unable to initialize peer manager")
}
// check if peers without device name exist (from version <= 1.0.3), if so assign them to the default device.
peers := make([]Peer, 0)
pm.db.Find(&peers)
@ -361,7 +382,7 @@ func NewPeerManager(db *gorm.DB, wg *Manager) (*PeerManager, error) {
// validate and update existing peers if needed
for _, deviceName := range wg.Cfg.DeviceNames {
dev := pm.GetDevice(deviceName)
peers = pm.GetAllPeers(deviceName)
peers := pm.GetAllPeers(deviceName)
for i := range peers {
if err := pm.fixPeerDefaultData(&peers[i], &dev); err != nil {
return nil, errors.WithMessagef(err, "unable to fix peers for interface %s", deviceName)
@ -372,9 +393,9 @@ func NewPeerManager(db *gorm.DB, wg *Manager) (*PeerManager, error) {
return pm, nil
}
// InitFromPhysicalInterface read all WireGuard peers from the WireGuard interface configuration. If a peer does not
// initFromPhysicalInterface read all WireGuard peers from the WireGuard interface configuration. If a peer does not
// exist in the local database, it gets created.
func (m *PeerManager) InitFromPhysicalInterface() error {
func (m *PeerManager) initFromPhysicalInterface() error {
for _, deviceName := range m.wg.Cfg.DeviceNames {
peers, err := m.wg.GetPeerList(deviceName)
if err != nil {
@ -415,7 +436,7 @@ func (m *PeerManager) InitFromPhysicalInterface() error {
// assumption: server mode is used
func (m *PeerManager) validateOrCreatePeer(device string, wgPeer wgtypes.Peer) error {
peer := Peer{}
m.db.Where("public_key = ? OR endpoint_public_key = ?", wgPeer.PublicKey.String(), wgPeer.PublicKey.String()).FirstOrInit(&peer)
m.db.Where("public_key = ?", wgPeer.PublicKey.String()).FirstOrInit(&peer)
dev := m.GetDevice(device)
@ -450,6 +471,14 @@ func (m *PeerManager) validateOrCreatePeer(device string, wgPeer wgtypes.Peer) e
}
}
if peer.DeviceName == "" {
peer.DeviceName = device
res := m.db.Save(&peer)
if res.Error != nil {
return errors.Wrapf(res.Error, "failed to update autodetected peer %s", peer.PublicKey)
}
}
return nil
}
@ -479,6 +508,15 @@ func (m *PeerManager) validateOrCreateDevice(dev wgtypes.Device, ipAddresses []s
}
}
if device.Type == "" {
device.Type = DeviceTypeServer // from version <= 1.0.3, only server mode devices were supported
res := m.db.Save(&device)
if res.Error != nil {
return errors.Wrapf(res.Error, "failed to update autodetected device")
}
}
return nil
}