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  }