Skip to content

Commit d384e00

Browse files
committed
Create v9.1.1 migratin to fix 9.0.0 migration
1 parent 05f544d commit d384e00

2 files changed

Lines changed: 122 additions & 53 deletions

File tree

internal/data/groups.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (d *database) CreateGroup(group string, initialMembers []string) error {
7676
operations = append(operations, d.generateOpsForGroupAddition(info.Created, group, initialMembers, false, true))
7777

7878
txn := d.etcd.Txn(context.Background())
79-
txn.If(clientv3util.KeyMissing(GroupsPrefix + group))
79+
txn.If(clientv3util.KeyMissing(GroupsIndexPrefix + group))
8080
txn.Then(
8181
operations...,
8282
)

internal/data/init.go

Lines changed: 121 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package data
22

33
import (
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+
241361
func (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

Comments
 (0)