11package data
22
33import (
4+ "bytes"
45 "context"
56 "crypto/rand"
67 "encoding/binary"
@@ -110,6 +111,10 @@ func Load(joinToken string, testing bool) (db *database, err error) {
110111 return nil , err
111112 }
112113
114+ if err := db .doMigrations (); err != nil {
115+ return nil , err
116+ }
117+
113118 err = db .loadInitialSettings ()
114119 if err != nil {
115120 return nil , err
@@ -217,6 +222,12 @@ func Load(joinToken string, testing bool) (db *database, err error) {
217222 log .Println ("Successfully connected to etcd" )
218223
219224 if ! db .etcdServer .Server .IsLearner () {
225+
226+ // ugh duplicated code
227+ if err := db .doMigrations (); err != nil {
228+ return nil , err
229+ }
230+
220231 // After first run this will be a no-op
221232 err = db .loadInitialSettings ()
222233 if err != nil {
@@ -238,7 +249,117 @@ func (d *database) Raw() *clientv3.Client {
238249 return d .etcd
239250}
240251
252+ func (d * database ) doMigrations () error {
253+ type migration struct {
254+ version string
255+ run func () error
256+ }
257+
258+ migrations := []migration {
259+ {
260+ version : "9.0.0" ,
261+ run : func () error {
262+
263+ resp , err := d .etcd .Delete (context .Background (), GroupMembershipPrefix , clientv3 .WithPrefix (), clientv3 .WithPrevKV ())
264+ if err != nil {
265+ return fmt .Errorf ("failed to apply migration: %w" , err )
266+ }
267+
268+ ops := []clientv3.Op {}
269+ for _ , kv := range resp .PrevKvs {
270+ key := string (kv .Key )
271+ log .Printf ("Migrating user(%q) membership to new group layout" , key )
272+ var memberCurrentGroups []string
273+ err = json .Unmarshal (kv .Value , & memberCurrentGroups )
274+ if err != nil {
275+ log .Println ("FAILED TO migrate group: " , key , err )
276+ continue
277+ }
278+
279+ parts , err := d .SplitKey (1 , GroupMembershipPrefix , key )
280+ if err != nil {
281+ log .Println ("FAILED TO migrate group: " , key , err )
282+ continue
283+ }
284+
285+ // should now be left with group membership for <username> (which is parts[0])
286+ for _ , group := range memberCurrentGroups {
287+
288+ ops = append (ops , d .generateOpsForGroupAddition (time .Now ().Unix (), group , []string {parts [0 ]}, false , false ))
289+ }
290+ }
291+
292+ if len (ops ) > 0 {
293+
294+ txn := d .etcd .Txn (context .Background ())
295+ txn .Then (ops ... )
296+ _ , err = txn .Commit ()
297+ if err != nil {
298+ return fmt .Errorf ("failed to commit migration to db: %w" , err )
299+ }
300+ }
301+
302+ return nil
303+ },
304+ },
305+ {
306+ version : "9.1.1" ,
307+ run : func () error {
308+ // the previous migration did not set the group creation information or create the group index
309+
310+ response , err := d .etcd .Get (context .Background (), GroupsPrefix , clientv3 .WithPrefix ())
311+ if err != nil {
312+ return fmt .Errorf ("unable to load all groups: %w" , err )
313+ }
314+
315+ for _ , kv := range response .Kvs {
316+ if bytes .Contains (kv .Key , []byte ("-members-" )) {
317+ continue
318+ }
319+
320+ group := strings .TrimPrefix (string (kv .Key ), GroupsPrefix )
321+
322+ // create group now uses the group index to check if the group exists
323+ // previously it used the group information
324+ // the index is not created in the previous migration
325+ err := d .CreateGroup (group , []string {})
326+ if err != nil && ! strings .Contains (err .Error (), "group already exists" ) {
327+ return fmt .Errorf ("failed to migrate group: %s: %w" , group , err )
328+ }
329+ }
330+
331+ return nil
332+ },
333+ },
334+ }
335+
336+ for _ , action := range migrations {
337+
338+ migrationKey := fmt .Sprintf ("%s-%q" , dbMigrations , action .version )
339+
340+ txn := d .etcd .Txn (context .Background ())
341+ txn .If (clientv3util .KeyMissing (migrationKey ))
342+
343+ resp , err := txn .Commit ()
344+ if err != nil {
345+ return fmt .Errorf ("failed to apply migration %s: %w" , action .version , err )
346+ }
347+
348+ if resp .Succeeded {
349+ err = action .run ()
350+ if err != nil {
351+ return fmt .Errorf ("failed to apply migration: %s: %v" , action .version , err )
352+ }
353+
354+ Set (d .etcd , migrationKey , false , migrationKey )
355+ }
356+ }
357+
358+ return nil
359+ }
360+
241361func (d * database ) loadInitialSettings () error {
362+
242363 response , err := d .etcd .Get (context .Background (), "wag-acls-" , clientv3 .WithPrefix ())
243364 if err != nil {
244365 return err
@@ -261,8 +382,6 @@ func (d *database) loadInitialSettings() error {
261382 return err
262383 }
263384
264- latestMigrationKey := fmt .Sprintf ("%s-%q" , dbMigrations , "9.0.0" )
265-
266385 if len (response .Kvs ) == 0 {
267386 log .Println ("no groups found in database, importing from .json file (from this point the json file will be ignored)" )
268387
@@ -271,56 +390,6 @@ func (d *database) loadInitialSettings() error {
271390 return err
272391 }
273392 }
274-
275- Set (d .etcd , latestMigrationKey , false , latestMigrationKey )
276-
277- } else {
278- txn := d .etcd .Txn (context .Background ())
279- txn .If (clientv3util .KeyMissing (latestMigrationKey ))
280- txn .Then (clientv3 .OpPut (latestMigrationKey , config .Version ), clientv3 .OpDelete (GroupMembershipPrefix , clientv3 .WithPrefix (), clientv3 .WithPrevKV ()))
281-
282- resp , err := txn .Commit ()
283- if err != nil {
284- return fmt .Errorf ("failed to apply migration: %w" , err )
285- }
286-
287- if resp .Succeeded {
288-
289- if len (resp .Responses ) != 2 {
290- return fmt .Errorf ("doing migrations failed as the number of responses from the db wasnt correct: %d" , len (resp .Responses ))
291- }
292-
293- ops := []clientv3.Op {}
294- deleteResp := resp .Responses [1 ].GetResponseDeleteRange ()
295- for _ , kv := range deleteResp .PrevKvs {
296- key := string (kv .Key )
297- log .Printf ("Migrating user(%q) membership to new group layout" , key )
298- var memberCurrentGroups []string
299- err = json .Unmarshal (kv .Value , & memberCurrentGroups )
300- if err != nil {
301- log .Println ("FAILED TO migrate group: " , key , err )
302- continue
303- }
304-
305- parts , err := d .SplitKey (1 , GroupMembershipPrefix , key )
306- if err != nil {
307- log .Println ("FAILED TO migrate group: " , key , err )
308- continue
309- }
310-
311- // should now be left with <username>
312- for _ , group := range memberCurrentGroups {
313- ops = append (ops , d .generateOpsForGroupAddition (time .Now ().Unix (), group , []string {parts [0 ]}, false , false ))
314- }
315- }
316-
317- txn = d .etcd .Txn (context .Background ())
318- txn .Then (ops ... )
319- _ , err = txn .Commit ()
320- if err != nil {
321- return fmt .Errorf ("failed to commit migration to db: %w" , err )
322- }
323- }
324393 }
325394
326395 configData , _ := json .Marshal (config .Values )
0 commit comments