github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/sqlstore/migrations/migrations.go (about) 1 package migrations 2 3 import ( 4 "time" 5 6 "github.com/go-gormigrate/gormigrate/v2" 7 "gorm.io/gorm" 8 9 "github.com/pyroscope-io/pyroscope/pkg/config" 10 "github.com/pyroscope-io/pyroscope/pkg/model" 11 ) 12 13 // Migrate executes all migrations UP that did not run yet. 14 // 15 // 1. Migrations must be backward compatible and only extend the schema. 16 // 2. Migration ID must be a unix epoch time in seconds (use 'date +%s'). 17 // 3. Although in the current shape schema downgrades are not supported, 18 // Rollback function must be also provided, and migrations must not import 19 // any models in order to avoid side effects: instead, the type should be 20 // explicitly defined within the migration body. 21 // 4. Migration code must be tested within the corresponding service package. 22 // 23 // A note on schema downgrades (not supported yet): 24 // 25 // As long as migrations are backward compatible, support for schema downgrade 26 // is not required. Once we introduce a breaking change (e.g, change column 27 // name or modify the data in any way), downgrades must be supported. 28 // 29 // The problem is that this requires migrator to know the future schema: 30 // v0.4.4 can't know which actions should be taken to revert the database from 31 // v0.4.5, but not vice-versa (unless they use the same source of migration 32 // scripts). There are some options: 33 // - Changes can be reverted with the later version (that is installed before 34 // the application downgrade) or with an independent tool – in containerized 35 // deployments this can cause significant difficulties. 36 // - Store migrations (as raw SQL scripts) in the database itself or elsewhere 37 // locally/remotely. 38 // 39 // Before we have a very strong reason to perform a schema downgrade or violate 40 // the schema backward compatibility guaranties, we should follow the basic 41 // principle: "... to maintain backwards compatibility between the DB and all 42 // versions of the code currently deployed in production." 43 // (https://flywaydb.org/documentation/concepts/migrations#important-notes). 44 // On the other hand, "the lack of an effective rollback script can be a gating 45 // factor in the integration and deployment process" (Database Reliability 46 // Engineering by Laine Campbell & Charity Majors). 47 func Migrate(db *gorm.DB, c *config.Server) error { 48 return gormigrate.New(db, gormigrate.DefaultOptions, []*gormigrate.Migration{ 49 createUserTableMigration(c.Auth.Internal.AdminUser), 50 createAPIKeyTableMigration(), 51 createAnnotationsTableMigration(), 52 addIndexesUniqueTableMigration(), 53 createApplicationMetadataTableMigration(), 54 }).Migrate() 55 } 56 57 func createUserTableMigration(adminUser config.AdminUser) *gormigrate.Migration { 58 type user struct { 59 ID uint `gorm:"primarykey"` 60 Name string `gorm:"type:varchar(255);not null;default:null;index:,unique"` 61 Email *string `gorm:"type:varchar(255);default:null;index:,unique"` 62 FullName *string `gorm:"type:varchar(255);default:null"` 63 PasswordHash []byte `gorm:"type:varchar(255);not null;default:null"` 64 Role int `gorm:"not null;default:null"` 65 IsDisabled *bool `gorm:"not null;default:false"` 66 IsExternal *bool `gorm:"not null;default:false"` 67 LastSeenAt *time.Time `gorm:"default:null"` 68 PasswordChangedAt time.Time 69 CreatedAt time.Time 70 UpdatedAt time.Time 71 } 72 return &gormigrate.Migration{ 73 ID: "1638496809", 74 Migrate: func(tx *gorm.DB) error { 75 if err := tx.AutoMigrate(&user{}); err != nil { 76 return err 77 } 78 if adminUser.Create { 79 return tx.Create(&user{ 80 Name: adminUser.Name, 81 Email: model.String(adminUser.Email), 82 PasswordHash: model.MustPasswordHash(adminUser.Password), 83 PasswordChangedAt: time.Now(), 84 Role: int(model.AdminRole), 85 }).Error 86 } 87 return nil 88 }, 89 Rollback: func(tx *gorm.DB) error { 90 return tx.Migrator().DropTable(&user{}) 91 }, 92 } 93 } 94 95 func createAPIKeyTableMigration() *gormigrate.Migration { 96 type apiKey struct { 97 ID uint `gorm:"primarykey"` 98 Name string `gorm:"type:varchar(255);not null;default:null;index:,unique"` 99 Hash []byte `gorm:"type:varchar(255);not null;default:null"` 100 Role int `gorm:"not null;default:null"` 101 ExpiresAt *time.Time `gorm:"default:null"` 102 LastSeenAt *time.Time `gorm:"default:null"` 103 CreatedAt time.Time 104 } 105 106 return &gormigrate.Migration{ 107 ID: "1641917891", 108 Migrate: func(tx *gorm.DB) error { 109 return tx.AutoMigrate(&apiKey{}) 110 }, 111 Rollback: func(tx *gorm.DB) error { 112 return tx.Migrator().DropTable(&apiKey{}) 113 }, 114 } 115 } 116 117 func createAnnotationsTableMigration() *gormigrate.Migration { 118 type annotation struct { 119 ID uint `gorm:"primarykey"` 120 AppName string `gorm:"not null;default:null"` 121 Timestamp time.Time `gorm:"not null;default:null"` 122 Content string `gorm:"not null;default:null"` 123 CreatedAt time.Time 124 UpdatedAt time.Time 125 } 126 127 return &gormigrate.Migration{ 128 ID: "1661975049", 129 Migrate: func(tx *gorm.DB) error { 130 return tx.AutoMigrate(&annotation{}) 131 }, 132 Rollback: func(tx *gorm.DB) error { 133 return tx.Migrator().DropTable(&annotation{}) 134 }, 135 } 136 } 137 138 func addIndexesUniqueTableMigration() *gormigrate.Migration { 139 type annotation struct { 140 AppName string `gorm:"index:idx_appname_timestamp,unique;not null;default:null"` 141 Timestamp time.Time `gorm:"index:idx_appname_timestamp,unique;not null;default:null"` 142 } 143 144 return &gormigrate.Migration{ 145 ID: "1663269650", 146 Migrate: func(tx *gorm.DB) error { 147 return tx.AutoMigrate(&annotation{}) 148 }, 149 Rollback: func(tx *gorm.DB) error { 150 return tx.Migrator().DropIndex(&annotation{}, "idx_appname_timestamp") 151 }, 152 } 153 } 154 155 func createApplicationMetadataTableMigration() *gormigrate.Migration { 156 type applicationMetadata struct { 157 ID uint `gorm:"primarykey"` 158 FQName string `gorm:"uniqueIndex;not null;default:null"` 159 SpyName string 160 SampleRate uint32 161 Units string 162 AggregationType string 163 CreatedAt time.Time 164 UpdatedAt time.Time 165 } 166 167 return &gormigrate.Migration{ 168 ID: "1667213046", 169 Migrate: func(tx *gorm.DB) error { 170 return tx.AutoMigrate(&applicationMetadata{}) 171 }, 172 Rollback: func(tx *gorm.DB) error { 173 return tx.Migrator().DropTable(&applicationMetadata{}) 174 }, 175 } 176 }