From 3c105785841e246a0e15223ff8e99179d36a3e90 Mon Sep 17 00:00:00 2001 From: Manfred Touron Date: Sat, 2 Dec 2017 00:01:31 +0100 Subject: [PATCH] Fix some backup/restore bugs + improve MySQL support --- CHANGELOG.md | 2 ++ db.go | 20 +++++++------ dbinit.go | 39 ++++++++++++++++++++++++ main.go | 3 +- shell.go | 84 +++++++++++++++++++++++++++++++++++++++++----------- 5 files changed, 120 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e60e571..8d6ed29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * Connection history * Audit log * Add dynamic strict host key checking (learning on the first time, strict on the next ones) +* Add-back MySQL support (experimental) +* Fix some backup/restore bugs ## v1.4.0 (2017-11-24) diff --git a/db.go b/db.go index 4dc5f91..9504609 100644 --- a/db.go +++ b/db.go @@ -24,8 +24,10 @@ type Config struct { HostGroups []*HostGroup `json:"host_groups"` ACLs []*ACL `json:"acls"` Settings []*Setting `json:"settings"` + Events []*Event `json:"events"` Sessions []*Session `json:"sessions"` - Date time.Time `json:"date"` + // FIXME: add latest migration + Date time.Time `json:"date"` } type Setting struct { @@ -118,14 +120,14 @@ type ACL struct { type Session struct { gorm.Model - StoppedAt time.Time `valid:"optional"` - Status string `valid:"required"` - User *User `gorm:"ForeignKey:UserID"` - Host *Host `gorm:"ForeignKey:HostID"` - UserID uint `valid:"optional"` - HostID uint `valid:"optional"` - ErrMsg string `valid:"optional"` - Comment string `valid:"optional"` + StoppedAt *time.Time `sql:"index" valid:"optional"` + Status string `valid:"required"` + User *User `gorm:"ForeignKey:UserID"` + Host *Host `gorm:"ForeignKey:HostID"` + UserID uint `valid:"optional"` + HostID uint `valid:"optional"` + ErrMsg string `valid:"optional"` + Comment string `valid:"optional"` } type Event struct { diff --git a/dbinit.go b/dbinit.go index 7915741..afb76d4 100644 --- a/dbinit.go +++ b/dbinit.go @@ -392,6 +392,45 @@ func dbInit(db *gorm.DB) error { Rollback: func(tx *gorm.DB) error { return fmt.Errorf("not implemented") }, + }, { + ID: "26", + Migrate: func(tx *gorm.DB) error { + type Session struct { + gorm.Model + StoppedAt *time.Time `sql:"index" valid:"optional"` + Status string `valid:"required"` + User *User `gorm:"ForeignKey:UserID"` + Host *Host `gorm:"ForeignKey:HostID"` + UserID uint `valid:"optional"` + HostID uint `valid:"optional"` + ErrMsg string `valid:"optional"` + Comment string `valid:"optional"` + } + return tx.AutoMigrate(&Session{}).Error + }, + Rollback: func(tx *gorm.DB) error { + return fmt.Errorf("not implemented") + }, + }, { + ID: "27", + Migrate: func(tx *gorm.DB) error { + var sessions []Session + if err := db.Find(&sessions).Error; err != nil { + return err + } + + for _, session := range sessions { + if session.StoppedAt != nil && session.StoppedAt.IsZero() { + if err := db.Model(&session).Updates(map[string]interface{}{"stopped_at": nil}).Error; err != nil { + return err + } + } + } + return nil + }, + Rollback: func(tx *gorm.DB) error { + return fmt.Errorf("not implemented") + }, }, }) if err := m.Migrate(); err != nil { diff --git a/main.go b/main.go index c03817e..4e1b3d6 100644 --- a/main.go +++ b/main.go @@ -178,7 +178,8 @@ func server(c *cli.Context) error { } } sessUpdate.Status = SessionStatusClosed - sessUpdate.StoppedAt = time.Now() + now := time.Now() + sessUpdate.StoppedAt = &now db.Model(&sess).Updates(&sessUpdate) case "deny": fmt.Fprintf(s, "You don't have permission to that host.\n") diff --git a/shell.go b/shell.go index 87ff649..f8cd8df 100644 --- a/shell.go +++ b/shell.go @@ -286,11 +286,11 @@ GLOBAL OPTIONS: } config := Config{} - if err := db.Find(&config.Hosts).Error; err != nil { + if err := HostsPreload(db).Find(&config.Hosts).Error; err != nil { return err } - if err := db.Find(&config.SSHKeys).Error; err != nil { + if err := SSHKeysPreload(db).Find(&config.SSHKeys).Error; err != nil { return err } for _, key := range config.SSHKeys { @@ -304,7 +304,7 @@ GLOBAL OPTIONS: } } - if err := db.Find(&config.Hosts).Error; err != nil { + if err := HostsPreload(db).Find(&config.Hosts).Error; err != nil { return err } for _, host := range config.Hosts { @@ -318,24 +318,30 @@ GLOBAL OPTIONS: } } - if err := db.Find(&config.UserKeys).Error; err != nil { + if err := UserKeysPreload(db).Find(&config.UserKeys).Error; err != nil { return err } - if err := db.Find(&config.Users).Error; err != nil { + if err := UsersPreload(db).Find(&config.Users).Error; err != nil { return err } - if err := db.Find(&config.UserGroups).Error; err != nil { + if err := UserGroupsPreload(db).Find(&config.UserGroups).Error; err != nil { return err } - if err := db.Find(&config.HostGroups).Error; err != nil { + if err := HostGroupsPreload(db).Find(&config.HostGroups).Error; err != nil { return err } - if err := db.Find(&config.ACLs).Error; err != nil { + if err := ACLsPreload(db).Find(&config.ACLs).Error; err != nil { return err } if err := db.Find(&config.Settings).Error; err != nil { return err } + if err := SessionsPreload(db).Find(&config.Sessions).Error; err != nil { + return err + } + if err := EventsPreload(db).Find(&config.Events).Error; err != nil { + return err + } config.Date = time.Now() enc := json.NewEncoder(s) if c.Bool("indent") { @@ -372,6 +378,8 @@ GLOBAL OPTIONS: fmt.Fprintf(s, "* %d Userkeys\n", len(config.UserKeys)) fmt.Fprintf(s, "* %d Users\n", len(config.Users)) fmt.Fprintf(s, "* %d Settings\n", len(config.Settings)) + fmt.Fprintf(s, "* %d Sessions\n", len(config.Sessions)) + fmt.Fprintf(s, "* %d Events\n", len(config.Events)) if !c.Bool("confirm") { fmt.Fprintf(s, "restore will erase and replace everything in the database.\nIf you are ok, add the '--confirm' to the restore command\n") @@ -380,8 +388,36 @@ GLOBAL OPTIONS: tx := db.Begin() + // FIXME: handle different migrations: + // 1. drop tables + // 2. apply migrations `1` to `` + // 3. restore data + // 4. continues migrations + + // FIXME: tell the administrator to restart the server + // if the master host key changed + // FIXME: do everything in a transaction - for _, tableName := range []string{"hosts", "users", "acls", "host_groups", "user_groups", "ssh_keys", "user_keys", "settings"} { + tableNames := []string{ + "acls", + "events", + "host_group_acls", + "host_groups", + "host_host_groups", + "hosts", + //"migrations", + "sessions", + "settings", + "ssh_keys", + "user_group_acls", + "user_groups", + "user_keys", + "user_roles", + "user_user_groups", + "user_user_roles", + "users", + } + for _, tableName := range tableNames { if err := tx.Exec(fmt.Sprintf("DELETE FROM %s;", tableName)).Error; err != nil { tx.Rollback() return err @@ -394,31 +430,31 @@ GLOBAL OPTIONS: return err } } - if err := tx.Create(&host).Error; err != nil { + if err := tx.FirstOrCreate(&host).Error; err != nil { tx.Rollback() return err } } for _, user := range config.Users { - if err := tx.Create(&user).Error; err != nil { + if err := tx.FirstOrCreate(&user).Error; err != nil { tx.Rollback() return err } } for _, acl := range config.ACLs { - if err := tx.Create(&acl).Error; err != nil { + if err := tx.FirstOrCreate(&acl).Error; err != nil { tx.Rollback() return err } } for _, hostGroup := range config.HostGroups { - if err := tx.Create(&hostGroup).Error; err != nil { + if err := tx.FirstOrCreate(&hostGroup).Error; err != nil { tx.Rollback() return err } } for _, userGroup := range config.UserGroups { - if err := tx.Create(&userGroup).Error; err != nil { + if err := tx.FirstOrCreate(&userGroup).Error; err != nil { tx.Rollback() return err } @@ -430,19 +466,31 @@ GLOBAL OPTIONS: return err } } - if err := tx.Create(&sshKey).Error; err != nil { + if err := tx.FirstOrCreate(&sshKey).Error; err != nil { tx.Rollback() return err } } for _, userKey := range config.UserKeys { - if err := tx.Create(&userKey).Error; err != nil { + if err := tx.FirstOrCreate(&userKey).Error; err != nil { tx.Rollback() return err } } for _, setting := range config.Settings { - if err := tx.Create(&setting).Error; err != nil { + if err := tx.FirstOrCreate(&setting).Error; err != nil { + tx.Rollback() + return err + } + } + for _, session := range config.Sessions { + if err := tx.FirstOrCreate(&session).Error; err != nil { + tx.Rollback() + return err + } + } + for _, event := range config.Events { + if err := tx.FirstOrCreate(&event).Error; err != nil { tx.Rollback() return err } @@ -1560,7 +1608,7 @@ GLOBAL OPTIONS: if session.StoppedAt.IsZero() { duration = humanize.RelTime(session.CreatedAt, time.Now(), "", "") } else { - duration = humanize.RelTime(session.CreatedAt, session.StoppedAt, "", "") + duration = humanize.RelTime(session.CreatedAt, *session.StoppedAt, "", "") } duration = strings.Replace(duration, "now", "1 second", 1) table.Append([]string{