[MM-12623] Create CLI command "config reset" (#10296)

Этот коммит содержится в:
Joshua Bezaleel Abednego
2019-11-02 01:29:02 +07:00
коммит произвёл Ben Schumacher
родитель 3687a0b6c1
Коммит 63a28700f5
2 изменённых файлов: 156 добавлений и 3 удалений

Просмотреть файл

@@ -11,6 +11,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/mattermost/viper"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -18,7 +19,6 @@ import (
"github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils" "github.com/mattermost/mattermost-server/utils"
"github.com/mattermost/viper"
) )
const noSettingsNamed = "unable to find a setting named: %s" const noSettingsNamed = "unable to find a setting named: %s"
@@ -80,8 +80,17 @@ var MigrateConfigCmd = &cobra.Command{
RunE: configMigrateCmdF, RunE: configMigrateCmdF,
} }
var ConfigResetCmd = &cobra.Command{
Use: "reset",
Short: "Reset config setting",
Long: "Resets the value of a config setting by its name in dot notation or a setting section. Accepts multiple values for array settings.",
Example: "config reset SqlSettings.DriverName LogSettings",
RunE: configResetCmdF,
}
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")
ConfigResetCmd.Flags().Bool("confirm", false, "Confirm you really want to reset all configuration settings to its default value")
ConfigShowCmd.Flags().Bool("json", false, "Output the configuration as JSON.") ConfigShowCmd.Flags().Bool("json", false, "Output the configuration as JSON.")
ConfigCmd.AddCommand( ConfigCmd.AddCommand(
@@ -91,6 +100,7 @@ func init() {
ConfigShowCmd, ConfigShowCmd,
ConfigSetCmd, ConfigSetCmd,
MigrateConfigCmd, MigrateConfigCmd,
ConfigResetCmd,
) )
RootCmd.AddCommand(ConfigCmd) RootCmd.AddCommand(ConfigCmd)
} }
@@ -367,6 +377,92 @@ func UpdateMap(configMap map[string]interface{}, configSettings []string, newVal
} }
} }
func configResetCmdF(command *cobra.Command, args []string) error {
configStore, err := getConfigStore(command)
if err != nil {
return err
}
defaultConfig := &model.Config{}
defaultConfig.SetDefaults()
confirmFlag, _ := command.Flags().GetBool("confirm")
if confirmFlag {
if _, err = configStore.Set(defaultConfig); err != nil {
return errors.Wrap(err, "failed to set config")
}
}
if !confirmFlag && len(args) == 0 {
var confirmResetAll string
CommandPrettyPrintln("Are you sure you want to reset all the configuration settings?(YES/NO): ")
fmt.Scanln(&confirmResetAll)
if confirmResetAll == "YES" {
if _, err = configStore.Set(defaultConfig); err != nil {
return errors.Wrap(err, "failed to set config")
}
}
}
tempConfig := configStore.Get()
tempConfigMap := configToMap(*tempConfig)
defaultConfigMap := configToMap(*defaultConfig)
for _, arg := range args {
err = changeMap(tempConfigMap, defaultConfigMap, strings.Split(arg, "."))
if err != nil {
return errors.Wrap(err, "Failed to reset config")
}
}
bs, err := json.Marshal(tempConfigMap)
if err != nil {
fmt.Printf("Error while marshalling map to json %s\n", err)
os.Exit(1)
}
err = json.Unmarshal(bs, tempConfig)
if err != nil {
fmt.Printf("Error while unmarshalling json to struct %s\n", err)
os.Exit(1)
}
if changed := config.FixInvalidLocales(tempConfig); changed {
return errors.New("Invalid locale configuration")
}
if _, err := configStore.Set(tempConfig); err != nil {
return errors.Wrap(err, "failed to set config")
}
return nil
}
func changeMap(oldConfigMap, defaultConfigMap map[string]interface{}, configSettings []string) error {
resOld, ok := oldConfigMap[configSettings[0]]
if !ok {
return fmt.Errorf("Unable to find a setting with that name %s", configSettings[0])
}
resDef := defaultConfigMap[configSettings[0]]
valueOld := reflect.ValueOf(resOld)
if valueOld.Kind() == reflect.Map {
if len(configSettings) == 1 {
return changeSection(resOld.(map[string]interface{}), resDef.(map[string]interface{}))
}
return changeMap(resOld.(map[string]interface{}), resDef.(map[string]interface{}), configSettings[1:])
}
if len(configSettings) == 1 {
oldConfigMap[configSettings[0]] = defaultConfigMap[configSettings[0]]
return nil
}
return fmt.Errorf("Unable to find a setting with that name %s", configSettings[0])
}
func changeSection(oldConfigMap, defaultConfigMap map[string]interface{}) error {
valueOld := reflect.ValueOf(oldConfigMap)
for _, key := range valueOld.MapKeys() {
oldConfigMap[key.String()] = defaultConfigMap[key.String()]
}
return nil
}
// configToMap converts our config into a map // configToMap converts our config into a map
func configToMap(s interface{}) map[string]interface{} { func configToMap(s interface{}) map[string]interface{} {
return structToMap(s) return structToMap(s)

Просмотреть файл

@@ -13,11 +13,10 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/mattermost/mattermost-server/config"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/config"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
) )
@@ -168,6 +167,64 @@ func TestConfigSet(t *testing.T) {
}) })
} }
func TestConfigReset(t *testing.T) {
th := Setup()
defer th.TearDown()
t.Run("No Error when no arguments are given (reset all the configurations)", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "reset"))
})
t.Run("No Error when a configuration section is given", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings"))
})
t.Run("No Error when a configuration setting is given", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings.RunJobs"))
})
t.Run("Error when the wrong configuration section is given", func(t *testing.T) {
assert.Error(t, th.RunCommand(t, "config", "reset", "InvalidSettings"))
})
t.Run("Error when the wrong configuration setting is given", func(t *testing.T) {
assert.Error(t, th.RunCommand(t, "config", "reset", "JobSettings.InvalidConfiguration"))
})
t.Run("Success when the confirm boolean flag is given", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunJobs", "false"))
assert.NoError(t, th.RunCommand(t, "config", "set", "PrivacySettings.ShowFullName", "false"))
assert.NoError(t, th.RunCommand(t, "config", "reset", "--confirm"))
output1 := th.CheckCommand(t, "config", "get", "JobSettings.RunJobs")
output2 := th.CheckCommand(t, "config", "get", "PrivacySettings.ShowFullName")
assert.Contains(t, output1, "true")
assert.Contains(t, output2, "true")
})
t.Run("Success when a configuration section is given", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunJobs", "false"))
assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunScheduler", "false"))
assert.NoError(t, th.RunCommand(t, "config", "set", "PrivacySettings.ShowFullName", "false"))
assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings"))
output1 := th.CheckCommand(t, "config", "get", "JobSettings.RunJobs")
output2 := th.CheckCommand(t, "config", "get", "JobSettings.RunScheduler")
output3 := th.CheckCommand(t, "config", "get", "PrivacySettings.ShowFullName")
assert.Contains(t, output1, "true")
assert.Contains(t, output2, "true")
assert.Contains(t, output3, "false")
})
t.Run("Success when a configuration setting is given", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunJobs", "false"))
assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunScheduler", "false"))
assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings.RunJobs"))
output1 := th.CheckCommand(t, "config", "get", "JobSettings.RunJobs")
output2 := th.CheckCommand(t, "config", "get", "JobSettings.RunScheduler")
assert.Contains(t, output1, "true")
assert.Contains(t, output2, "false")
})
}
func TestConfigToMap(t *testing.T) { func TestConfigToMap(t *testing.T) {
// This test is almost the same as TestStructToMap, but I have it here for the sake of completions // This test is almost the same as TestStructToMap, but I have it here for the sake of completions
cases := []struct { cases := []struct {