diff --git a/config/common_test.go b/config/common_test.go index 06e4f90057..0822dcda3b 100644 --- a/config/common_test.go +++ b/config/common_test.go @@ -180,3 +180,42 @@ func TestRemoveEnvironmentOverrides(t *testing.T) { newCfg := base.RemoveEnvironmentOverrides(oldCfg) assert.Equal(t, "", *newCfg.ServiceSettings.SiteURL) } + +func TestConfigEnvironmentOverridesPluginStates(t *testing.T) { + memstore, err := NewMemoryStore() + require.NoError(t, err) + base, err := NewStoreFromBacking(memstore, nil, false) + require.NoError(t, err) + originalConfig := &model.Config{} + originalConfig.PluginSettings.PluginStates = map[string]*model.PluginState{ + "focalboard": {Enable: true}, + "playbooks": {Enable: false}, + "com.mattermost.calls": {Enable: true}, + } + + os.Setenv("MM_PLUGINSETTINGS_PLUGINSTATES_PLAYBOOKS", "true") + os.Setenv("MM_PLUGINSETTINGS_PLUGINSTATES_FOCALBOARD", "false") + os.Setenv("MM_PLUGINSETTINGS_PLUGINSTATES_COM_MATTERMOST_CALLS", "false") + + defer os.Unsetenv("MM_PLUGINSETTINGS_PLUGINSTATES_PLAYBOOKS") + defer os.Unsetenv("MM_PLUGINSETTINGS_PLUGINSTATES_FOCALBOARD") + defer os.Unsetenv("MM_PLUGINSETTINGS_PLUGINSTATES_COM_MATTERMOST_CALLS") + + t.Run("loading config should respect environment variable overrides", func(t *testing.T) { + err := base.Load() + require.NoError(t, err) + + assert.False(t, base.Get().PluginSettings.PluginStates["focalboard"].Enable) + assert.True(t, base.Get().PluginSettings.PluginStates["playbooks"].Enable) + assert.False(t, base.Get().PluginSettings.PluginStates["com.mattermost.calls"].Enable) + }) + + t.Run("setting config should respect environment variable overrides", func(t *testing.T) { + _, _, err := base.Set(originalConfig) + require.NoError(t, err) + + assert.False(t, base.Get().PluginSettings.PluginStates["focalboard"].Enable) + assert.True(t, base.Get().PluginSettings.PluginStates["playbooks"].Enable) + assert.False(t, base.Get().PluginSettings.PluginStates["com.mattermost.calls"].Enable) + }) +} diff --git a/config/environment.go b/config/environment.go index 4ded8d7399..85700ca52f 100644 --- a/config/environment.go +++ b/config/environment.go @@ -26,6 +26,43 @@ func GetEnvironment() map[string]string { return mmenv } +func applyPluginStateOverride(m reflect.Value, key, value string) bool { + keyParts := strings.SplitN(key, "_", 2) + if len(keyParts) != 2 { + return false + } + + // supporting legacy pluginID format with dots in it (e.g. com.mattermost.plugin) + pluginID := strings.ReplaceAll(strings.ToLower(keyParts[1]), "_", ".") + + for _, key := range m.MapKeys() { + if key.String() == pluginID { + enable, err := strconv.ParseBool(value) + if err != nil { + return false + } + + mapVal := m.MapIndex(key) + if !mapVal.CanInterface() { + return false + } + + stateVal := mapVal.Interface() + state, ok := stateVal.(*model.PluginState) + if !ok { + return false + } + + state.Enable = enable + m.SetMapIndex(key, reflect.ValueOf(state)) + + return true + } + } + + return false +} + func applyEnvKey(key, value string, rValueSubject reflect.Value) { keyParts := strings.SplitN(key, "_", 2) if len(keyParts) < 1 { @@ -75,6 +112,12 @@ func applyEnvKey(key, value string, rValueSubject reflect.Value) { case reflect.SliceOf(reflect.TypeOf("")).Kind(): rFieldValue.Set(reflect.ValueOf(strings.Split(value, " "))) case reflect.Map: + if rFieldValue.Type().String() == "map[string]*model.PluginState" { + if applied := applyPluginStateOverride(rFieldValue, key, value); applied { + return + } + } + target := reflect.New(rFieldValue.Type()).Interface() if err := json.Unmarshal([]byte(value), target); err == nil { rFieldValue.Set(reflect.ValueOf(target).Elem())