github.com/rajatvaryani/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 }