bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/database/migrate.go (about) 1 package database 2 3 import ( 4 "encoding/json" 5 "sort" 6 7 "bosun.org/models" 8 "bosun.org/slog" 9 "github.com/garyburd/redigo/redis" 10 ) 11 12 // Version 0 is the schema that was never verisoned 13 // Version 1 migrates rendered templates from 14 15 var schemaKey = "schemaVersion" 16 17 type Migration struct { 18 UID string 19 Task func(d *dataAccess) error 20 Version int64 21 } 22 23 // Be sure to increment the value of `var SchemaVersion` in database.go when adding a new migration 24 var tasks = []Migration{ 25 { 26 UID: "Migrate Rendered Templates", 27 Task: migrateRenderedTemplates, 28 Version: 1, 29 }, 30 { 31 UID: "Populate Previous IncidentIds", 32 Task: populatePreviousIncidents, 33 Version: 2, 34 }, 35 } 36 37 type oldIncidentState struct { 38 *models.IncidentState 39 *models.RenderedTemplates 40 } 41 42 func migrateRenderedTemplates(d *dataAccess) error { 43 slog.Infoln("Running rendered template migration. This can take several minutes.") 44 45 // Hacky Work better? 46 ids, err := d.getAllIncidentIdsByKeys() 47 slog.Infof("migrating %v incidents", len(ids)) 48 if err != nil { 49 return err 50 } 51 52 conn := d.Get() 53 defer conn.Close() 54 55 for _, id := range ids { 56 b, err := redis.Bytes(conn.Do("GET", incidentStateKey(id))) 57 if err != nil { 58 return slog.Wrap(err) 59 } 60 oldState := &oldIncidentState{} 61 if err := json.Unmarshal(b, oldState); err != nil { 62 slog.Wrap(err) 63 } 64 65 incidentStateJSON, err := json.Marshal(oldState.IncidentState) 66 if err != nil { 67 return slog.Wrap(err) 68 } 69 if _, err := conn.Do("SET", incidentStateKey(oldState.Id), incidentStateJSON); err != nil { 70 return slog.Wrap(err) 71 } 72 73 renderedTemplatesJSON, err := json.Marshal(oldState.RenderedTemplates) 74 if err != nil { 75 return slog.Wrap(err) 76 } 77 if _, err := conn.Do("SET", renderedTemplatesKey(oldState.Id), renderedTemplatesJSON); err != nil { 78 return slog.Wrap(err) 79 } 80 81 } 82 return nil 83 } 84 85 func populatePreviousIncidents(d *dataAccess) error { 86 slog.Infoln("Adding fields for previous incidents and next incident on all incidents in order to link incidents together. This is a one time operation that can take several minutes.") 87 88 ids, err := d.getAllIncidentIdsByKeys() 89 slog.Infof("migrating %v incidents", len(ids)) 90 if err != nil { 91 return err 92 } 93 94 conn := d.Get() 95 defer conn.Close() 96 97 prevIdCache := make(map[models.AlertKey]*[]int64) 98 99 for _, id := range ids { 100 incident, err := d.State().GetIncidentState(id) 101 if err != nil { 102 return err 103 } 104 if _, ok := prevIdCache[incident.AlertKey]; !ok { 105 prevList, err := d.State().GetAllIncidentIdsByAlertKey(incident.AlertKey) 106 if err != nil { 107 return err 108 } 109 sort.Slice(prevList, func(i, j int) bool { 110 return prevList[i] < prevList[j] 111 }) 112 prevIdCache[incident.AlertKey] = &prevList 113 } 114 for _, pid := range *prevIdCache[incident.AlertKey] { 115 if incident.Id > pid { 116 incident.PreviousIds = append([]int64{pid}, incident.PreviousIds...) 117 continue 118 } 119 break 120 } 121 122 err = d.setIncident(incident, conn) 123 if err != nil { 124 return err 125 } 126 if len(incident.PreviousIds) > 0 { 127 err := d.State().SetIncidentNext(incident.PreviousIds[0], incident.Id) 128 if err != nil { 129 return err 130 } 131 } 132 133 } 134 return nil 135 } 136 137 func (d *dataAccess) Migrate() error { 138 slog.Infoln("checking migrations") 139 conn := d.Get() 140 defer conn.Close() 141 142 // Since we didn't record a schema version from the start 143 // we have to do some assumptions to see if this is a new 144 // database, or if was a database before we started recording 145 // a schema version number 146 147 version, err := redis.Int64(conn.Do("GET", schemaKey)) 148 if err != nil { 149 if err == redis.ErrNil { 150 slog.Infoln("schema version not found in db") 151 if _, err := redis.Bool(conn.Do("Get", "allIncidents")); err == redis.ErrNil { 152 slog.Infoln("assuming new installation because allIncidents key not found") 153 slog.Infoln("writing current schema version") 154 if _, err := conn.Do("SET", schemaKey, SchemaVersion); err != nil { 155 return slog.Wrap(err) 156 } 157 version = SchemaVersion 158 return nil 159 } 160 } else { 161 return slog.Wrap(err) 162 } 163 } 164 165 for _, task := range tasks { 166 if task.Version > version { 167 // Check if migration has been run if not that run 168 err := task.Task(d) 169 if err != nil { 170 return slog.Wrap(err) 171 } 172 if _, err := conn.Do("SET", schemaKey, task.Version); err != nil { 173 return slog.Wrap(err) 174 } 175 } 176 177 } 178 return nil 179 }