MM-14143 config cleanup final (#10374)
* TestGetLicenseFileFromDisk: avoid using fileutils.FindConfigFile * config: abstract config-related file access, extend memory store * simplify config validate to avoid file knowledge * fix relative file tests * cluster: fix ConfigChanged event The old and new configurations were swapped when notifying the enterprise code of configuration changes, creating needless instability in propagating config updates across a cluster. * config/database: ignore duplicates * test cleanup * remove unnecessary Save() in test
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3716918c57
Коммит
1e462da2d4
@@ -75,6 +75,18 @@ func initializeConfigurationsTable(db *sqlx.DB) error {
|
||||
return errors.Wrap(err, "failed to create Configurations table")
|
||||
}
|
||||
|
||||
_, err = db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS ConfigurationFiles (
|
||||
Name VARCHAR(64) PRIMARY KEY,
|
||||
Data TEXT NOT NULL,
|
||||
CreateAt BIGINT NOT NULL,
|
||||
UpdateAt BIGINT NOT NULL
|
||||
)
|
||||
`)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create ConfigurationFiles table")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -113,8 +125,7 @@ func parseDSN(dsn string) (string, string, error) {
|
||||
|
||||
// Set replaces the current configuration in its entirety and updates the backing store.
|
||||
func (ds *DatabaseStore) Set(newCfg *model.Config) (*model.Config, error) {
|
||||
return ds.commonStore.set(newCfg, nil, ds.persist)
|
||||
|
||||
return ds.commonStore.set(newCfg, ds.commonStore.validate, ds.persist)
|
||||
}
|
||||
|
||||
// persist writes the configuration to the configured database.
|
||||
@@ -146,6 +157,16 @@ func (ds *DatabaseStore) persist(cfg *model.Config) error {
|
||||
"key": "ConfigurationId",
|
||||
}
|
||||
|
||||
// Skip the persist altogether if we're effectively writing the same configuration.
|
||||
var oldValue []byte
|
||||
row := ds.db.QueryRow("SELECT Value FROM Configurations WHERE Active")
|
||||
if err := row.Scan(&oldValue); err != nil && err != sql.ErrNoRows {
|
||||
return errors.Wrap(err, "failed to query active configuration")
|
||||
}
|
||||
if bytes.Equal(oldValue, b) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := tx.Exec("UPDATE Configurations SET Active = NULL WHERE Active"); err != nil {
|
||||
return errors.Wrap(err, "failed to deactivate current configuration")
|
||||
}
|
||||
@@ -175,7 +196,7 @@ func (ds *DatabaseStore) Load() (err error) {
|
||||
if len(configurationData) == 0 {
|
||||
needsSave = true
|
||||
|
||||
defaultCfg := model.Config{}
|
||||
defaultCfg := &model.Config{}
|
||||
defaultCfg.SetDefaults()
|
||||
|
||||
// Assume the database storing the config is also to be used for the application.
|
||||
@@ -184,13 +205,90 @@ func (ds *DatabaseStore) Load() (err error) {
|
||||
*defaultCfg.SqlSettings.DriverName = ds.driverName
|
||||
*defaultCfg.SqlSettings.DataSource = ds.dataSourceName
|
||||
|
||||
configurationData, err = marshalConfig(&defaultCfg)
|
||||
configurationData, err = marshalConfig(defaultCfg)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to serialize default config")
|
||||
}
|
||||
}
|
||||
|
||||
return ds.commonStore.load(ioutil.NopCloser(bytes.NewReader(configurationData)), needsSave, ds.persist)
|
||||
return ds.commonStore.load(ioutil.NopCloser(bytes.NewReader(configurationData)), needsSave, ds.commonStore.validate, ds.persist)
|
||||
}
|
||||
|
||||
// GetFile fetches the contents of a previously persisted configuration file.
|
||||
func (ds *DatabaseStore) GetFile(name string) ([]byte, error) {
|
||||
query, args, err := sqlx.Named("SELECT Data FROM ConfigurationFiles WHERE Name = :name", map[string]interface{}{
|
||||
"name": name,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var data []byte
|
||||
row := ds.db.QueryRowx(query, args...)
|
||||
if err = row.Scan(&data); err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to scan data from row for %s", name)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// SetFile sets or replaces the contents of a configuration file.
|
||||
func (ds *DatabaseStore) SetFile(name string, data []byte) error {
|
||||
params := map[string]interface{}{
|
||||
"name": name,
|
||||
"data": data,
|
||||
"create_at": model.GetMillis(),
|
||||
"update_at": model.GetMillis(),
|
||||
}
|
||||
|
||||
result, err := ds.db.NamedExec("UPDATE ConfigurationFiles SET Data = :data, UpdateAt = :update_at WHERE Name = :name", params)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to update row for %s", name)
|
||||
}
|
||||
|
||||
count, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to count rows affected for %s", name)
|
||||
} else if count > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = ds.db.NamedExec("INSERT INTO ConfigurationFiles (Name, Data, CreateAt, UpdateAt) VALUES (:name, :data, :create_at, :update_at)", params)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to insert row for %s", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasFile returns true if the given file was previously persisted.
|
||||
func (ds *DatabaseStore) HasFile(name string) (bool, error) {
|
||||
query, args, err := sqlx.Named("SELECT COUNT(*) FROM ConfigurationFiles WHERE Name = :name", map[string]interface{}{
|
||||
"name": name,
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var count int
|
||||
row := ds.db.QueryRowx(query, args...)
|
||||
if err = row.Scan(&count); err != nil {
|
||||
return false, errors.Wrapf(err, "failed to scan count of rows for %s", name)
|
||||
}
|
||||
|
||||
return count != 0, nil
|
||||
}
|
||||
|
||||
// RemoveFile remoevs a previously persisted configuration file.
|
||||
func (ds *DatabaseStore) RemoveFile(name string) error {
|
||||
_, err := ds.db.NamedExec("DELETE FROM ConfigurationFiles WHERE Name = :name", map[string]interface{}{
|
||||
"name": name,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to remove row for %s", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the path to the database backing the config, masking the password.
|
||||
|
||||
Ссылка в новой задаче
Block a user