github.com/ashishbhate/mattermost-server@v5.11.1+incompatible/cmd/mattermost/commands/config.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package commands
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"os"
    10  	"reflect"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/pkg/errors"
    15  	"github.com/spf13/cobra"
    16  
    17  	"github.com/mattermost/mattermost-server/config"
    18  	"github.com/mattermost/mattermost-server/model"
    19  	"github.com/mattermost/mattermost-server/utils"
    20  	"github.com/mattermost/viper"
    21  )
    22  
    23  var ConfigCmd = &cobra.Command{
    24  	Use:   "config",
    25  	Short: "Configuration",
    26  }
    27  
    28  var ValidateConfigCmd = &cobra.Command{
    29  	Use:   "validate",
    30  	Short: "Validate config file",
    31  	Long:  "If the config file is valid, this command will output a success message and have a zero exit code. If it is invalid, this command will output an error and have a non-zero exit code.",
    32  	RunE:  configValidateCmdF,
    33  }
    34  
    35  var ConfigSubpathCmd = &cobra.Command{
    36  	Use:   "subpath",
    37  	Short: "Update client asset loading to use the configured subpath",
    38  	Long:  "Update the hard-coded production client asset paths to take into account Mattermost running on a subpath.",
    39  	Example: `  config subpath
    40    config subpath --path /mattermost
    41    config subpath --path /`,
    42  	RunE: configSubpathCmdF,
    43  }
    44  
    45  var ConfigGetCmd = &cobra.Command{
    46  	Use:     "get",
    47  	Short:   "Get config setting",
    48  	Long:    "Gets the value of a config setting by its name in dot notation.",
    49  	Example: `config get SqlSettings.DriverName`,
    50  	Args:    cobra.ExactArgs(1),
    51  	RunE:    configGetCmdF,
    52  }
    53  
    54  var ConfigShowCmd = &cobra.Command{
    55  	Use:     "show",
    56  	Short:   "Writes the server configuration to STDOUT",
    57  	Long:    "Pretty-prints the server configuration and writes to STDOUT",
    58  	Example: "config show",
    59  	RunE:    configShowCmdF,
    60  }
    61  
    62  var ConfigSetCmd = &cobra.Command{
    63  	Use:     "set",
    64  	Short:   "Set config setting",
    65  	Long:    "Sets the value of a config setting by its name in dot notation. Accepts multiple values for array settings",
    66  	Example: "config set SqlSettings.DriverName mysql",
    67  	Args:    cobra.MinimumNArgs(2),
    68  	RunE:    configSetCmdF,
    69  }
    70  
    71  func init() {
    72  	ConfigSubpathCmd.Flags().String("path", "", "Optional subpath; defaults to value in SiteURL")
    73  
    74  	ConfigCmd.AddCommand(
    75  		ValidateConfigCmd,
    76  		ConfigSubpathCmd,
    77  		ConfigGetCmd,
    78  		ConfigShowCmd,
    79  		ConfigSetCmd,
    80  	)
    81  	RootCmd.AddCommand(ConfigCmd)
    82  }
    83  
    84  func configValidateCmdF(command *cobra.Command, args []string) error {
    85  	utils.TranslationsPreInit()
    86  	model.AppErrorInit(utils.T)
    87  
    88  	_, err := getConfigStore(command)
    89  	if err != nil {
    90  		return err
    91  	}
    92  
    93  	CommandPrettyPrintln("The document is valid")
    94  	return nil
    95  }
    96  
    97  func configSubpathCmdF(command *cobra.Command, args []string) error {
    98  	a, err := InitDBCommandContextCobra(command)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	defer a.Shutdown()
   103  
   104  	path, err := command.Flags().GetString("path")
   105  	if err != nil {
   106  		return errors.Wrap(err, "failed reading path")
   107  	}
   108  
   109  	if path == "" {
   110  		return utils.UpdateAssetsSubpathFromConfig(a.Config())
   111  	}
   112  
   113  	if err := utils.UpdateAssetsSubpath(path); err != nil {
   114  		return errors.Wrap(err, "failed to update assets subpath")
   115  	}
   116  
   117  	return nil
   118  }
   119  
   120  func getConfigStore(command *cobra.Command) (config.Store, error) {
   121  	if err := utils.TranslationsPreInit(); err != nil {
   122  		return nil, errors.Wrap(err, "failed to initialize i18n")
   123  	}
   124  
   125  	configDSN := viper.GetString("config")
   126  
   127  	configStore, err := config.NewStore(configDSN, false)
   128  	if err != nil {
   129  		return nil, errors.Wrap(err, "failed to initialize config store")
   130  	}
   131  
   132  	return configStore, nil
   133  }
   134  
   135  func configGetCmdF(command *cobra.Command, args []string) error {
   136  	configStore, err := getConfigStore(command)
   137  	if err != nil {
   138  		return err
   139  	}
   140  
   141  	out, err := printConfigValues(configToMap(*configStore.Get()), strings.Split(args[0], "."), args[0])
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	fmt.Printf("%s", out)
   147  
   148  	return nil
   149  }
   150  
   151  func configShowCmdF(command *cobra.Command, args []string) error {
   152  	configStore, err := getConfigStore(command)
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	err = cobra.NoArgs(command, args)
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	fmt.Printf("%s", prettyPrintStruct(*configStore.Get()))
   163  	return nil
   164  }
   165  
   166  // printConfigValues function prints out the value of the configSettings working recursively or
   167  // gives an error if config setting is not in the file.
   168  func printConfigValues(configMap map[string]interface{}, configSetting []string, name string) (string, error) {
   169  	res, ok := configMap[configSetting[0]]
   170  	if !ok {
   171  		return "", fmt.Errorf("%s configuration setting is not in the file", name)
   172  	}
   173  	value := reflect.ValueOf(res)
   174  	switch value.Kind() {
   175  	case reflect.Map:
   176  		if len(configSetting) == 1 {
   177  			return printMap(value, 0), nil
   178  		}
   179  		return printConfigValues(res.(map[string]interface{}), configSetting[1:], name)
   180  	default:
   181  		if len(configSetting) == 1 {
   182  			return fmt.Sprintf("%s: \"%v\"\n", name, res), nil
   183  		}
   184  		return "", fmt.Errorf("%s configuration setting is not in the file", name)
   185  	}
   186  }
   187  
   188  func configSetCmdF(command *cobra.Command, args []string) error {
   189  	configStore, err := getConfigStore(command)
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	// args[0] -> holds the config setting that we want to change
   195  	// args[1:] -> the new value of the config setting
   196  	configSetting := args[0]
   197  	newVal := args[1:]
   198  
   199  	// create the function to update config
   200  	oldConfig := configStore.Get()
   201  	newConfig := configStore.Get()
   202  
   203  	f := updateConfigValue(configSetting, newVal, oldConfig, newConfig)
   204  	f(newConfig)
   205  
   206  	// UpdateConfig above would have already fixed these invalid locales, but we check again
   207  	// in the context of an explicit change to these parameters to avoid saving the fixed
   208  	// settings in the first place.
   209  	if changed := config.FixInvalidLocales(newConfig); changed {
   210  		return errors.New("Invalid locale configuration")
   211  	}
   212  
   213  	if _, err := configStore.Set(newConfig); err != nil {
   214  		return errors.Wrap(err, "failed to set config")
   215  	}
   216  
   217  	return nil
   218  }
   219  
   220  func updateConfigValue(configSetting string, newVal []string, oldConfig, newConfig *model.Config) func(*model.Config) {
   221  	return func(update *model.Config) {
   222  
   223  		// convert config to map[string]interface
   224  		configMap := configToMap(*oldConfig)
   225  
   226  		// iterate through the map and update the value or print an error and exit
   227  		err := UpdateMap(configMap, strings.Split(configSetting, "."), newVal)
   228  		if err != nil {
   229  			fmt.Printf("%s\n", err)
   230  			os.Exit(1)
   231  		}
   232  
   233  		// convert map to json
   234  		bs, err := json.Marshal(configMap)
   235  		if err != nil {
   236  			fmt.Printf("Error while marshalling map to json %s\n", err)
   237  			os.Exit(1)
   238  		}
   239  
   240  		// convert json to struct
   241  		err = json.Unmarshal(bs, newConfig)
   242  		if err != nil {
   243  			fmt.Printf("Error while unmarshalling json to struct %s\n", err)
   244  			os.Exit(1)
   245  		}
   246  
   247  		*update = *newConfig
   248  
   249  	}
   250  }
   251  
   252  func UpdateMap(configMap map[string]interface{}, configSettings []string, newVal []string) error {
   253  	res, ok := configMap[configSettings[0]]
   254  	if !ok {
   255  		return fmt.Errorf("unable to find a setting with that name %s", configSettings[0])
   256  	}
   257  
   258  	value := reflect.ValueOf(res)
   259  
   260  	switch value.Kind() {
   261  
   262  	case reflect.Map:
   263  		// we can only change the value of a particular setting, not the whole map, return error
   264  		if len(configSettings) == 1 {
   265  			return errors.New("unable to set multiple settings at once")
   266  		}
   267  		return UpdateMap(res.(map[string]interface{}), configSettings[1:], newVal)
   268  
   269  	case reflect.Int:
   270  		if len(configSettings) == 1 {
   271  			val, err := strconv.Atoi(newVal[0])
   272  			if err != nil {
   273  				return err
   274  			}
   275  			configMap[configSettings[0]] = val
   276  			return nil
   277  		}
   278  		return fmt.Errorf("unable to find a setting with that name %s", configSettings[0])
   279  
   280  	case reflect.Int64:
   281  		if len(configSettings) == 1 {
   282  			val, err := strconv.Atoi(newVal[0])
   283  			if err != nil {
   284  				return err
   285  			}
   286  			configMap[configSettings[0]] = int64(val)
   287  			return nil
   288  		}
   289  		return fmt.Errorf("unable to find a setting with that name %s", configSettings[0])
   290  
   291  	case reflect.Bool:
   292  		if len(configSettings) == 1 {
   293  			val, err := strconv.ParseBool(newVal[0])
   294  			if err != nil {
   295  				return err
   296  			}
   297  			configMap[configSettings[0]] = val
   298  			return nil
   299  		}
   300  		return fmt.Errorf("unable to find a setting with that name %s", configSettings[0])
   301  
   302  	case reflect.String:
   303  		if len(configSettings) == 1 {
   304  			configMap[configSettings[0]] = newVal[0]
   305  			return nil
   306  		}
   307  		return fmt.Errorf("unable to find a setting with that name %s", configSettings[0])
   308  
   309  	case reflect.Slice:
   310  		if len(configSettings) == 1 {
   311  			configMap[configSettings[0]] = newVal
   312  			return nil
   313  		}
   314  		return fmt.Errorf("unable to find a setting with that name %s", configSettings[0])
   315  
   316  	default:
   317  		return errors.New("type not supported yet")
   318  	}
   319  }
   320  
   321  // configToMap converts our config into a map
   322  func configToMap(s interface{}) map[string]interface{} {
   323  	return structToMap(s)
   324  }