diff options
author | Joram Wilander <jwawilander@gmail.com> | 2015-09-18 12:22:57 -0400 |
---|---|---|
committer | Joram Wilander <jwawilander@gmail.com> | 2015-09-18 12:22:57 -0400 |
commit | 5a436fd447753124b3f7705ecb123ecf5762bc24 (patch) | |
tree | d0b37402b2eead725bf0e3d1de37727f64c01315 /store | |
parent | 7caef4a7a7287433ed274c4f20d3593ea4065b66 (diff) | |
parent | c63da249640e50b1c93c5c5ff7b5d598aa737b1e (diff) | |
download | chat-5a436fd447753124b3f7705ecb123ecf5762bc24.tar.gz chat-5a436fd447753124b3f7705ecb123ecf5762bc24.tar.bz2 chat-5a436fd447753124b3f7705ecb123ecf5762bc24.zip |
Merge pull request #699 from mattermost/PLT-92
PLT-92 Adding server side versioning to the binary
Diffstat (limited to 'store')
-rw-r--r-- | store/sql_channel_store.go | 1 | ||||
-rw-r--r-- | store/sql_post_store.go | 4 | ||||
-rw-r--r-- | store/sql_post_store_test.go | 8 | ||||
-rw-r--r-- | store/sql_store.go | 93 | ||||
-rw-r--r-- | store/sql_system_store.go | 92 | ||||
-rw-r--r-- | store/sql_system_store_test.go | 33 | ||||
-rw-r--r-- | store/sql_user_store.go | 4 | ||||
-rw-r--r-- | store/store.go | 7 |
8 files changed, 231 insertions, 11 deletions
diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index 3d1007874..bad878501 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -37,7 +37,6 @@ func NewSqlChannelStore(sqlStore *SqlStore) ChannelStore { } func (s SqlChannelStore) UpgradeSchemaIfNeeded() { - s.CreateColumnIfNotExists("Channels", "CreatorId", "varchar(26)", "character varying(26)", "") } func (s SqlChannelStore) CreateIndexesIfNotExists() { diff --git a/store/sql_post_store.go b/store/sql_post_store.go index 20de23eb7..21e8e9d00 100644 --- a/store/sql_post_store.go +++ b/store/sql_post_store.go @@ -196,9 +196,9 @@ func (s SqlPostStore) GetEtag(channelId string) StoreChannel { var et etagPosts err := s.GetReplica().SelectOne(&et, "SELECT Id, UpdateAt FROM Posts WHERE ChannelId = :ChannelId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"ChannelId": channelId}) if err != nil { - result.Data = fmt.Sprintf("%v.0.%v", model.ETAG_ROOT_VERSION, model.GetMillis()) + result.Data = fmt.Sprintf("%v.0.%v", model.CurrentVersion, model.GetMillis()) } else { - result.Data = fmt.Sprintf("%v.%v.%v", model.ETAG_ROOT_VERSION, et.Id, et.UpdateAt) + result.Data = fmt.Sprintf("%v.%v.%v", model.CurrentVersion, et.Id, et.UpdateAt) } storeChannel <- result diff --git a/store/sql_post_store_test.go b/store/sql_post_store_test.go index d48dea51c..bc1cb2c2c 100644 --- a/store/sql_post_store_test.go +++ b/store/sql_post_store_test.go @@ -37,14 +37,14 @@ func TestPostStoreGet(t *testing.T) { o1.Message = "a" + model.NewId() + "b" etag1 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string) - if strings.Index(etag1, model.ETAG_ROOT_VERSION+".0.") != 0 { + if strings.Index(etag1, model.CurrentVersion+".0.") != 0 { t.Fatal("Invalid Etag") } o1 = (<-store.Post().Save(o1)).Data.(*model.Post) etag2 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string) - if strings.Index(etag2, model.ETAG_ROOT_VERSION+"."+o1.Id) != 0 { + if strings.Index(etag2, model.CurrentVersion+"."+o1.Id) != 0 { t.Fatal("Invalid Etag") } @@ -136,7 +136,7 @@ func TestPostStoreDelete(t *testing.T) { o1.Message = "a" + model.NewId() + "b" etag1 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string) - if strings.Index(etag1, model.ETAG_ROOT_VERSION+".0.") != 0 { + if strings.Index(etag1, model.CurrentVersion+".0.") != 0 { t.Fatal("Invalid Etag") } @@ -160,7 +160,7 @@ func TestPostStoreDelete(t *testing.T) { } etag2 := (<-store.Post().GetEtag(o1.ChannelId)).Data.(string) - if strings.Index(etag2, model.ETAG_ROOT_VERSION+"."+o1.Id) != 0 { + if strings.Index(etag2, model.CurrentVersion+"."+o1.Id) != 0 { t.Fatal("Invalid Etag") } } diff --git a/store/sql_store.go b/store/sql_store.go index cf6fae6d4..2f7ac2805 100644 --- a/store/sql_store.go +++ b/store/sql_store.go @@ -39,6 +39,7 @@ type SqlStore struct { audit AuditStore session SessionStore oauth OAuthStore + system SystemStore } func NewSqlStore() Store { @@ -56,9 +57,30 @@ func NewSqlStore() Store { utils.Cfg.SqlSettings.Trace) } + schemaVersion := sqlStore.GetCurrentSchemaVersion() + + // If the version is already set then we are potentially in an 'upgrade needed' state + if schemaVersion != "" { + // Check to see if it's the most current database schema version + if !model.IsCurrentVersion(schemaVersion) { + // If we are upgrading from the previous version then print a warning and continue + if model.IsPreviousVersion(schemaVersion) { + l4g.Warn("The database schema version of " + schemaVersion + " appears to be out of date") + l4g.Warn("Attempting to upgrade the database schema version to " + model.CurrentVersion) + } else { + // If this is an 'upgrade needed' state but the user is attempting to skip a version then halt the world + l4g.Critical("The database schema version of " + schemaVersion + " cannot be upgraded. You must not skip a version.") + time.Sleep(time.Second) + panic("The database schema version of " + schemaVersion + " cannot be upgraded. You must not skip a version.") + } + } + } + // Temporary upgrade code, remove after 0.8.0 release - if sqlStore.DoesColumnExist("Sessions", "AltId") { - sqlStore.GetMaster().Exec("DROP TABLE IF EXISTS Sessions") + if sqlStore.DoesTableExist("Sessions") { + if sqlStore.DoesColumnExist("Sessions", "AltId") { + sqlStore.GetMaster().Exec("DROP TABLE IF EXISTS Sessions") + } } sqlStore.team = NewSqlTeamStore(sqlStore) @@ -68,6 +90,7 @@ func NewSqlStore() Store { sqlStore.audit = NewSqlAuditStore(sqlStore) sqlStore.session = NewSqlSessionStore(sqlStore) sqlStore.oauth = NewSqlOAuthStore(sqlStore) + sqlStore.system = NewSqlSystemStore(sqlStore) sqlStore.master.CreateTablesIfNotExists() @@ -78,6 +101,7 @@ func NewSqlStore() Store { sqlStore.audit.(*SqlAuditStore).UpgradeSchemaIfNeeded() sqlStore.session.(*SqlSessionStore).UpgradeSchemaIfNeeded() sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded() + sqlStore.system.(*SqlSystemStore).UpgradeSchemaIfNeeded() sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists() sqlStore.channel.(*SqlChannelStore).CreateIndexesIfNotExists() @@ -86,6 +110,17 @@ func NewSqlStore() Store { sqlStore.audit.(*SqlAuditStore).CreateIndexesIfNotExists() sqlStore.session.(*SqlSessionStore).CreateIndexesIfNotExists() sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists() + sqlStore.system.(*SqlSystemStore).CreateIndexesIfNotExists() + + if model.IsPreviousVersion(schemaVersion) { + sqlStore.system.Update(&model.System{Name: "Version", Value: model.CurrentVersion}) + l4g.Warn("The database schema has been upgraded to version " + model.CurrentVersion) + } + + if schemaVersion == "" { + sqlStore.system.Save(&model.System{Name: "Version", Value: model.CurrentVersion}) + l4g.Info("The database schema has been set to version " + model.CurrentVersion) + } return sqlStore } @@ -131,6 +166,56 @@ func setupConnection(con_type string, driver string, dataSource string, maxIdle return dbmap } +func (ss SqlStore) GetCurrentSchemaVersion() string { + version, _ := ss.GetMaster().SelectStr("SELECT Value FROM Systems WHERE Name='Version'") + return version +} + +func (ss SqlStore) DoesTableExist(tableName string) bool { + if utils.Cfg.SqlSettings.DriverName == "postgres" { + count, err := ss.GetMaster().SelectInt( + `SELECT count(relname) FROM pg_class WHERE relname=$1`, + strings.ToLower(tableName), + ) + + if err != nil { + l4g.Critical("Failed to check if table exists %v", err) + time.Sleep(time.Second) + panic("Failed to check if table exists " + err.Error()) + } + + return count > 0 + + } else if utils.Cfg.SqlSettings.DriverName == "mysql" { + + count, err := ss.GetMaster().SelectInt( + `SELECT + COUNT(0) AS table_exists + FROM + information_schema.TABLES + WHERE + TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = ? + `, + tableName, + ) + + if err != nil { + l4g.Critical("Failed to check if table exists %v", err) + time.Sleep(time.Second) + panic("Failed to check if table exists " + err.Error()) + } + + return count > 0 + + } else { + l4g.Critical("Failed to check if column exists because of missing driver") + time.Sleep(time.Second) + panic("Failed to check if column exists because of missing driver") + } + +} + func (ss SqlStore) DoesColumnExist(tableName string, columnName string) bool { if utils.Cfg.SqlSettings.DriverName == "postgres" { count, err := ss.GetMaster().SelectInt( @@ -380,6 +465,10 @@ func (ss SqlStore) OAuth() OAuthStore { return ss.oauth } +func (ss SqlStore) System() SystemStore { + return ss.system +} + type mattermConverter struct{} func (me mattermConverter) ToDb(val interface{}) (interface{}, error) { diff --git a/store/sql_system_store.go b/store/sql_system_store.go new file mode 100644 index 000000000..ca22de2a6 --- /dev/null +++ b/store/sql_system_store.go @@ -0,0 +1,92 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "github.com/mattermost/platform/model" +) + +type SqlSystemStore struct { + *SqlStore +} + +func NewSqlSystemStore(sqlStore *SqlStore) SystemStore { + s := &SqlSystemStore{sqlStore} + + for _, db := range sqlStore.GetAllConns() { + table := db.AddTableWithName(model.System{}, "Systems").SetKeys(false, "Name") + table.ColMap("Name").SetMaxSize(64) + table.ColMap("Value").SetMaxSize(1024) + } + + return s +} + +func (s SqlSystemStore) UpgradeSchemaIfNeeded() { +} + +func (s SqlSystemStore) CreateIndexesIfNotExists() { +} + +func (s SqlSystemStore) Save(system *model.System) StoreChannel { + + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if err := s.GetMaster().Insert(system); err != nil { + result.Err = model.NewAppError("SqlSystemStore.Save", "We encounted an error saving the system property", "") + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlSystemStore) Update(system *model.System) StoreChannel { + + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + if _, err := s.GetMaster().Update(system); err != nil { + result.Err = model.NewAppError("SqlSystemStore.Save", "We encounted an error updating the system property", "") + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + +func (s SqlSystemStore) Get() StoreChannel { + + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + var systems []model.System + props := make(model.StringMap) + if _, err := s.GetReplica().Select(&systems, "SELECT * FROM Systems"); err != nil { + result.Err = model.NewAppError("SqlSystemStore.Get", "We encounted an error finding the system properties", "") + } else { + for _, prop := range systems { + props[prop.Name] = prop.Value + } + + result.Data = props + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} diff --git a/store/sql_system_store_test.go b/store/sql_system_store_test.go new file mode 100644 index 000000000..0f03b8f0e --- /dev/null +++ b/store/sql_system_store_test.go @@ -0,0 +1,33 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +package store + +import ( + "github.com/mattermost/platform/model" + "testing" +) + +func TestSqlSystemStore(t *testing.T) { + Setup() + + system := &model.System{Name: model.NewId(), Value: "value"} + Must(store.System().Save(system)) + + result := <-store.System().Get() + systems := result.Data.(model.StringMap) + + if systems[system.Name] != system.Value { + t.Fatal() + } + + system.Value = "value2" + Must(store.System().Update(system)) + + result2 := <-store.System().Get() + systems2 := result2.Data.(model.StringMap) + + if systems2[system.Name] != system.Value { + t.Fatal() + } +} diff --git a/store/sql_user_store.go b/store/sql_user_store.go index 52d670d56..778df367e 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -325,9 +325,9 @@ func (s SqlUserStore) GetEtagForProfiles(teamId string) StoreChannel { updateAt, err := s.GetReplica().SelectInt("SELECT UpdateAt FROM Users WHERE TeamId = :TeamId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"TeamId": teamId}) if err != nil { - result.Data = fmt.Sprintf("%v.%v", model.ETAG_ROOT_VERSION, model.GetMillis()) + result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, model.GetMillis()) } else { - result.Data = fmt.Sprintf("%v.%v", model.ETAG_ROOT_VERSION, updateAt) + result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, updateAt) } storeChannel <- result diff --git a/store/store.go b/store/store.go index 0218bc757..1344c4ebe 100644 --- a/store/store.go +++ b/store/store.go @@ -35,6 +35,7 @@ type Store interface { Audit() AuditStore Session() SessionStore OAuth() OAuthStore + System() SystemStore Close() } @@ -130,3 +131,9 @@ type OAuthStore interface { GetAccessDataByAuthCode(authCode string) StoreChannel RemoveAccessData(token string) StoreChannel } + +type SystemStore interface { + Save(system *model.System) StoreChannel + Update(system *model.System) StoreChannel + Get() StoreChannel +} |