github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/initial/local_migrations/migrations.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package local_migrations 20 21 import ( 22 "context" 23 "fmt" 24 "reflect" 25 "time" 26 27 "github.com/e154/smart-home/adaptors" 28 "github.com/e154/smart-home/common/logger" 29 ) 30 31 const ( 32 ctxTimeout = 30 * time.Second 33 ) 34 35 var ( 36 log = logger.MustGetLogger("local_migrations") 37 ) 38 39 type Migrations struct { 40 list []Migration 41 } 42 43 func NewMigrations(list []Migration) *Migrations { 44 return &Migrations{ 45 list: list, 46 } 47 } 48 49 func (t *Migrations) Up(ctx context.Context, adaptors *adaptors.Adaptors, ver string) (newVersion string, err error) { 50 51 ctx, ctxCancel := context.WithTimeout(ctx, ctxTimeout) 52 defer ctxCancel() 53 54 ch := make(chan error, 1) 55 56 newVersion = ver 57 58 go func() { 59 var err error 60 var ok []string 61 62 defer func() { 63 ch <- err 64 close(ch) 65 if len(ok) > 0 { 66 fmt.Println("\n\r") 67 for _, item := range ok { 68 log.Infof("migration '%s' ... installed", item) 69 } 70 } 71 log.Infof("Applied %d migrations!", len(ok)) 72 }() 73 74 var list []string 75 var position = 0 76 var exist = false 77 if ver != "" { 78 for i, migration := range t.list { 79 name := reflect.TypeOf(migration).String() 80 list = append(list, name) 81 if ver == name { 82 exist = true 83 position = i 84 break 85 } 86 } 87 } 88 89 if !exist { 90 log.Errorf("Unknown migration %s!", ver) 91 err = fmt.Errorf(fmt.Sprintf("Unknown migration %s!", ver)) 92 return 93 } 94 95 if position >= len(t.list)-1 { 96 return 97 } 98 99 if position > 0 { 100 position++ 101 } 102 103 for _, migration := range t.list[position:len(t.list)] { 104 if err = ctx.Err(); err != nil { 105 log.Error(err.Error()) 106 return 107 } 108 109 newVersion = reflect.TypeOf(migration).String() 110 tx := adaptors.Begin() 111 if err = migration.Up(ctx, tx); err != nil { 112 fmt.Printf("\n\nmigration '%s' ... error\n", newVersion) 113 log.Error(err.Error()) 114 _ = tx.Rollback() 115 return 116 } 117 _ = tx.Commit() 118 ok = append(ok, newVersion) 119 } 120 }() 121 122 select { 123 case v := <-ch: 124 err = v 125 case <-ctx.Done(): 126 err = ctx.Err() 127 } 128 129 return 130 }