[MM-13895] Implement config migration command (#10508)
* [MM-13895] Implement config migration command * Discarding unused error
Этот коммит содержится в:
коммит произвёл
Jesús Espino
родитель
7272864bea
Коммит
0edad1c3fa
@@ -68,8 +68,19 @@ var ConfigSetCmd = &cobra.Command{
|
|||||||
RunE: configSetCmdF,
|
RunE: configSetCmdF,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var MigrateConfigCmd = &cobra.Command{
|
||||||
|
Use: "migrate",
|
||||||
|
Short: "Migrate existing config between backends",
|
||||||
|
Long: "Migrate a file-based configuration to (or from) a database-based configuration. Point the Mattermost server at the target configuration to start using it",
|
||||||
|
Example: `config migrate --from=path/to/config.json --to="postgres://mmuser:mostest@dockerhost:5432/mattermost_test?sslmode=disable&connect_timeout=10"`,
|
||||||
|
RunE: configMigrateCmdF,
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ConfigSubpathCmd.Flags().String("path", "", "Optional subpath; defaults to value in SiteURL")
|
ConfigSubpathCmd.Flags().String("path", "", "Optional subpath; defaults to value in SiteURL")
|
||||||
|
MigrateConfigCmd.Flags().String("from", "", "Config from which to migrate")
|
||||||
|
MigrateConfigCmd.Flags().String("to", "", "Config to which to migrate")
|
||||||
|
MigrateConfigCmd.MarkFlagRequired("to")
|
||||||
|
|
||||||
ConfigCmd.AddCommand(
|
ConfigCmd.AddCommand(
|
||||||
ValidateConfigCmd,
|
ValidateConfigCmd,
|
||||||
@@ -77,6 +88,7 @@ func init() {
|
|||||||
ConfigGetCmd,
|
ConfigGetCmd,
|
||||||
ConfigShowCmd,
|
ConfigShowCmd,
|
||||||
ConfigSetCmd,
|
ConfigSetCmd,
|
||||||
|
MigrateConfigCmd,
|
||||||
)
|
)
|
||||||
RootCmd.AddCommand(ConfigCmd)
|
RootCmd.AddCommand(ConfigCmd)
|
||||||
}
|
}
|
||||||
@@ -217,6 +229,40 @@ func configSetCmdF(command *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configMigrateCmdF(command *cobra.Command, args []string) error {
|
||||||
|
// Parse source config; defaults to global --config unless overwritten by --from
|
||||||
|
from, err := command.Flags().GetString("from")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed reading source config parameter")
|
||||||
|
}
|
||||||
|
if from == "" {
|
||||||
|
from = viper.GetString("config")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse destination config store - MarkFlagRequired handles errors here
|
||||||
|
to, _ := command.Flags().GetString("to")
|
||||||
|
|
||||||
|
// Get source config store - invalid config will throw error here
|
||||||
|
fromConfigStore, err := config.NewStore(from, false)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to read --from config")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get destination config store
|
||||||
|
toConfigStore, err := config.NewStore(to, false)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to read --to config")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy config from source to destination
|
||||||
|
_, err = toConfigStore.Set(fromConfigStore.Get())
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to migrate config")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func updateConfigValue(configSetting string, newVal []string, oldConfig, newConfig *model.Config) func(*model.Config) {
|
func updateConfigValue(configSetting string, newVal []string, oldConfig, newConfig *model.Config) func(*model.Config) {
|
||||||
return func(update *model.Config) {
|
return func(update *model.Config) {
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/mattermost/mattermost-server/config"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -435,6 +437,43 @@ func TestUpdateMap(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigMigrate(t *testing.T) {
|
||||||
|
th := Setup()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
sqlSettings := mainHelper.GetSqlSettings()
|
||||||
|
sqlDSN := fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource)
|
||||||
|
fileDSN := "config.json"
|
||||||
|
|
||||||
|
ds, err := config.NewStore(sqlDSN, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
fs, err := config.NewStore(fileDSN, false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer ds.Close()
|
||||||
|
defer fs.Close()
|
||||||
|
|
||||||
|
t.Run("Should error without --to parameter", func(t *testing.T) {
|
||||||
|
assert.Error(t, th.RunCommand(t, "config", "migrate", "--from", fileDSN))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should work passing the --to and the --from", func(t *testing.T) {
|
||||||
|
assert.NoError(t, th.RunCommand(t, "config", "migrate", "--from", fileDSN, "--to", sqlDSN))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should work passing --to and no --from (taking the default config file)", func(t *testing.T) {
|
||||||
|
assert.NoError(t, th.RunCommand(t, "config", "migrate", "--to", sqlDSN))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should fail passing an invalid --to", func(t *testing.T) {
|
||||||
|
assert.Error(t, th.RunCommand(t, "config", "migrate", "--from", fileDSN, "--to", "mysql://asd"))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Should fail passing an invalid --from", func(t *testing.T) {
|
||||||
|
assert.Error(t, th.RunCommand(t, "config", "migrate", "--from", "invalid/path", "--to", sqlDSN))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func contains(configMap map[string]interface{}, v interface{}, configSettings []string) bool {
|
func contains(configMap map[string]interface{}, v interface{}, configSettings []string) bool {
|
||||||
res := configMap[configSettings[0]]
|
res := configMap[configSettings[0]]
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user