[MM-44056] Make Calls plugin's state on Cloud controllable by feature flag (#20161)
* Make Calls plugin's state on Cloud controllable by feature flag * Prepackage Calls v0.4.9 (#20148) * Update app/plugin.go Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com> * Update app/plugin_install.go Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com> * Bump Calls version to latest Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com> Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
a4ad6eda5d
Коммит
aee68c7ae4
1
Makefile
1
Makefile
@@ -119,6 +119,7 @@ TEMPLATES_DIR=templates
|
|||||||
PLUGIN_PACKAGES ?= mattermost-plugin-antivirus-v0.1.2
|
PLUGIN_PACKAGES ?= mattermost-plugin-antivirus-v0.1.2
|
||||||
PLUGIN_PACKAGES += mattermost-plugin-autolink-v1.2.2
|
PLUGIN_PACKAGES += mattermost-plugin-autolink-v1.2.2
|
||||||
PLUGIN_PACKAGES += mattermost-plugin-aws-SNS-v1.2.0
|
PLUGIN_PACKAGES += mattermost-plugin-aws-SNS-v1.2.0
|
||||||
|
PLUGIN_PACKAGES += mattermost-plugin-calls-v0.5.1
|
||||||
PLUGIN_PACKAGES += mattermost-plugin-channel-export-v1.0.0
|
PLUGIN_PACKAGES += mattermost-plugin-channel-export-v1.0.0
|
||||||
PLUGIN_PACKAGES += mattermost-plugin-custom-attributes-v1.3.0
|
PLUGIN_PACKAGES += mattermost-plugin-custom-attributes-v1.3.0
|
||||||
PLUGIN_PACKAGES += mattermost-plugin-github-v2.0.1
|
PLUGIN_PACKAGES += mattermost-plugin-github-v2.0.1
|
||||||
|
|||||||
@@ -98,11 +98,8 @@ func (ch *Channels) syncPluginsActiveState() {
|
|||||||
pluginEnabled = state.Enable
|
pluginEnabled = state.Enable
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tie Apps proxy disabled status to the feature flag.
|
if hasOverride, value := ch.getPluginStateOverride(pluginID); hasOverride {
|
||||||
if pluginID == "com.mattermost.apps" {
|
pluginEnabled = value
|
||||||
if !ch.cfgSvc.Config().FeatureFlags.AppsEnabled {
|
|
||||||
pluginEnabled = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if pluginEnabled {
|
if pluginEnabled {
|
||||||
@@ -1064,3 +1061,19 @@ func getIcon(iconPath string) (string, error) {
|
|||||||
|
|
||||||
return fmt.Sprintf("data:image/svg+xml;base64,%s", base64.StdEncoding.EncodeToString(icon)), nil
|
return fmt.Sprintf("data:image/svg+xml;base64,%s", base64.StdEncoding.EncodeToString(icon)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ch *Channels) getPluginStateOverride(pluginID string) (bool, bool) {
|
||||||
|
switch pluginID {
|
||||||
|
case "com.mattermost.apps":
|
||||||
|
// Tie Apps proxy disabled status to the feature flag.
|
||||||
|
if !ch.cfgSvc.Config().FeatureFlags.AppsEnabled {
|
||||||
|
return true, false
|
||||||
|
}
|
||||||
|
case "com.mattermost.calls":
|
||||||
|
if model.IsCloud() {
|
||||||
|
return true, ch.cfgSvc.Config().FeatureFlags.CallsEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
|||||||
@@ -393,9 +393,10 @@ func (ch *Channels) installExtractedPlugin(manifest *model.Manifest, fromPluginD
|
|||||||
// Activate the plugin if enabled.
|
// Activate the plugin if enabled.
|
||||||
pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[manifest.Id]
|
pluginState := ch.cfgSvc.Config().PluginSettings.PluginStates[manifest.Id]
|
||||||
if pluginState != nil && pluginState.Enable {
|
if pluginState != nil && pluginState.Enable {
|
||||||
if manifest.Id == "com.mattermost.apps" && !ch.cfgSvc.Config().FeatureFlags.AppsEnabled {
|
if hasOverride, enabled := ch.getPluginStateOverride(manifest.Id); hasOverride && !enabled {
|
||||||
return manifest, nil
|
return manifest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedManifest, _, err := pluginsEnvironment.Activate(manifest.Id)
|
updatedManifest, _, err := pluginsEnvironment.Activate(manifest.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.NewAppError("installExtractedPlugin", "app.plugin.restart.app_error", nil, err.Error(), http.StatusInternalServerError)
|
return nil, model.NewAppError("installExtractedPlugin", "app.plugin.restart.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
|||||||
@@ -965,3 +965,78 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
|
|||||||
require.Len(t, pluginStatus, 0)
|
require.Len(t, pluginStatus, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetPluginStateOverride(t *testing.T) {
|
||||||
|
th := Setup(t)
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
t.Run("no override", func(t *testing.T) {
|
||||||
|
overrides, value := th.App.ch.getPluginStateOverride("focalboard")
|
||||||
|
require.False(t, overrides)
|
||||||
|
require.False(t, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("calls override", func(t *testing.T) {
|
||||||
|
t.Run("on-prem", func(t *testing.T) {
|
||||||
|
overrides, value := th.App.ch.getPluginStateOverride("com.mattermost.calls")
|
||||||
|
require.False(t, overrides)
|
||||||
|
require.False(t, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Cloud, without enabled flag", func(t *testing.T) {
|
||||||
|
os.Setenv("MM_CLOUD_INSTALLATION_ID", "test")
|
||||||
|
defer os.Unsetenv("MM_CLOUD_INSTALLATION_ID")
|
||||||
|
overrides, value := th.App.ch.getPluginStateOverride("com.mattermost.calls")
|
||||||
|
require.True(t, overrides)
|
||||||
|
require.False(t, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Cloud, with enabled flag set to true", func(t *testing.T) {
|
||||||
|
os.Setenv("MM_CLOUD_INSTALLATION_ID", "test")
|
||||||
|
defer os.Unsetenv("MM_CLOUD_INSTALLATION_ID")
|
||||||
|
os.Setenv("MM_FEATUREFLAGS_CALLSENABLED", "true")
|
||||||
|
defer os.Unsetenv("MM_FEATUREFLAGS_CALLSENABLED")
|
||||||
|
|
||||||
|
th2 := Setup(t)
|
||||||
|
defer th2.TearDown()
|
||||||
|
|
||||||
|
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.calls")
|
||||||
|
require.True(t, overrides)
|
||||||
|
require.True(t, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Cloud, with enabled flag set to false", func(t *testing.T) {
|
||||||
|
os.Setenv("MM_CLOUD_INSTALLATION_ID", "test")
|
||||||
|
defer os.Unsetenv("MM_CLOUD_INSTALLATION_ID")
|
||||||
|
os.Setenv("MM_FEATUREFLAGS_CALLSENABLED", "false")
|
||||||
|
defer os.Unsetenv("MM_FEATUREFLAGS_CALLSENABLED")
|
||||||
|
|
||||||
|
th2 := Setup(t)
|
||||||
|
defer th2.TearDown()
|
||||||
|
|
||||||
|
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.calls")
|
||||||
|
require.True(t, overrides)
|
||||||
|
require.False(t, value)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("apps override", func(t *testing.T) {
|
||||||
|
t.Run("without enabled flag", func(t *testing.T) {
|
||||||
|
overrides, value := th.App.ch.getPluginStateOverride("com.mattermost.apps")
|
||||||
|
require.False(t, overrides)
|
||||||
|
require.False(t, value)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with enabled flag set to false", func(t *testing.T) {
|
||||||
|
os.Setenv("MM_FEATUREFLAGS_APPSENABLED", "false")
|
||||||
|
defer os.Unsetenv("MM_FEATUREFLAGS_APPSENABLED")
|
||||||
|
|
||||||
|
th2 := Setup(t)
|
||||||
|
defer th2.TearDown()
|
||||||
|
|
||||||
|
overrides, value := th2.App.ch.getPluginStateOverride("com.mattermost.apps")
|
||||||
|
require.True(t, overrides)
|
||||||
|
require.False(t, value)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -170,6 +170,11 @@ else
|
|||||||
@# Prepackage plugins
|
@# Prepackage plugins
|
||||||
@for plugin_package in $(PLUGIN_PACKAGES) ; do \
|
@for plugin_package in $(PLUGIN_PACKAGES) ; do \
|
||||||
ARCH=$(PLUGIN_ARCH); \
|
ARCH=$(PLUGIN_ARCH); \
|
||||||
|
if [ "$$ARCH" != "linux-amd64" ]; then \
|
||||||
|
case $$plugin_package in \
|
||||||
|
"mattermost-plugin-calls"*) continue ;; \
|
||||||
|
esac; \
|
||||||
|
fi; \
|
||||||
cp tmpprepackaged/$$plugin_package-$$ARCH.tar.gz $(DIST_PATH_GENERIC)/prepackaged_plugins; \
|
cp tmpprepackaged/$$plugin_package-$$ARCH.tar.gz $(DIST_PATH_GENERIC)/prepackaged_plugins; \
|
||||||
cp tmpprepackaged/$$plugin_package-$$ARCH.tar.gz.sig $(DIST_PATH_GENERIC)/prepackaged_plugins; \
|
cp tmpprepackaged/$$plugin_package-$$ARCH.tar.gz.sig $(DIST_PATH_GENERIC)/prepackaged_plugins; \
|
||||||
HAS_ARCH=`tar -tf $(DIST_PATH_GENERIC)/prepackaged_plugins/$$plugin_package-$$ARCH.tar.gz | grep -oE "dist/plugin-.*"`; \
|
HAS_ARCH=`tar -tf $(DIST_PATH_GENERIC)/prepackaged_plugins/$$plugin_package-$$ARCH.tar.gz | grep -oE "dist/plugin-.*"`; \
|
||||||
@@ -234,6 +239,9 @@ endif
|
|||||||
@# Prepackage plugins
|
@# Prepackage plugins
|
||||||
@for plugin_package in $(PLUGIN_PACKAGES) ; do \
|
@for plugin_package in $(PLUGIN_PACKAGES) ; do \
|
||||||
ARCH="windows-amd64"; \
|
ARCH="windows-amd64"; \
|
||||||
|
case $$plugin_package in \
|
||||||
|
"mattermost-plugin-calls"*) continue ;; \
|
||||||
|
esac; \
|
||||||
cp tmpprepackaged/$$plugin_package-$$ARCH.tar.gz $(DIST_PATH_WIN)/prepackaged_plugins; \
|
cp tmpprepackaged/$$plugin_package-$$ARCH.tar.gz $(DIST_PATH_WIN)/prepackaged_plugins; \
|
||||||
cp tmpprepackaged/$$plugin_package-$$ARCH.tar.gz.sig $(DIST_PATH_WIN)/prepackaged_plugins; \
|
cp tmpprepackaged/$$plugin_package-$$ARCH.tar.gz.sig $(DIST_PATH_WIN)/prepackaged_plugins; \
|
||||||
HAS_ARCH=`tar -tf $(DIST_PATH_WIN)/prepackaged_plugins/$$plugin_package-$$ARCH.tar.gz | grep -oE "dist/plugin-.*"`; \
|
HAS_ARCH=`tar -tf $(DIST_PATH_WIN)/prepackaged_plugins/$$plugin_package-$$ARCH.tar.gz | grep -oE "dist/plugin-.*"`; \
|
||||||
|
|||||||
@@ -2810,6 +2810,11 @@ func (s *PluginSettings) SetDefaults(ls LogSettings) {
|
|||||||
s.PluginStates["com.mattermost.apps"] = &PluginState{Enable: true}
|
s.PluginStates["com.mattermost.apps"] = &PluginState{Enable: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.PluginStates["com.mattermost.calls"] == nil && IsCloud() {
|
||||||
|
// Enable the calls plugin by default on Cloud only
|
||||||
|
s.PluginStates["com.mattermost.calls"] = &PluginState{Enable: true}
|
||||||
|
}
|
||||||
|
|
||||||
if s.EnableMarketplace == nil {
|
if s.EnableMarketplace == nil {
|
||||||
s.EnableMarketplace = NewBool(PluginSettingsDefaultEnableMarketplace)
|
s.EnableMarketplace = NewBool(PluginSettingsDefaultEnableMarketplace)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package model
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -1491,3 +1492,38 @@ func TestConfigServiceSettingsIsValid(t *testing.T) {
|
|||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
require.Equal(t, "model.config.is_valid.collapsed_threads.autofollow.app_error", err.Id)
|
require.Equal(t, "model.config.is_valid.collapsed_threads.autofollow.app_error", err.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigDefaultCallsPluginState(t *testing.T) {
|
||||||
|
t.Run("should not enable Calls plugin by default when not in Cloud", func(t *testing.T) {
|
||||||
|
c1 := Config{}
|
||||||
|
c1.SetDefaults()
|
||||||
|
|
||||||
|
assert.Nil(t, c1.PluginSettings.PluginStates["com.mattermost.calls"])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should enable Calls plugin by default on Cloud", func(t *testing.T) {
|
||||||
|
os.Setenv("MM_CLOUD_INSTALLATION_ID", "test")
|
||||||
|
defer os.Unsetenv("MM_CLOUD_INSTALLATION_ID")
|
||||||
|
c1 := Config{}
|
||||||
|
c1.SetDefaults()
|
||||||
|
|
||||||
|
assert.True(t, c1.PluginSettings.PluginStates["com.mattermost.calls"].Enable)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not re-enable Calls plugin after it has been disabled", func(t *testing.T) {
|
||||||
|
os.Setenv("MM_CLOUD_INSTALLATION_ID", "test")
|
||||||
|
defer os.Unsetenv("MM_CLOUD_INSTALLATION_ID")
|
||||||
|
c1 := Config{
|
||||||
|
PluginSettings: PluginSettings{
|
||||||
|
PluginStates: map[string]*PluginState{
|
||||||
|
"com.mattermost.calls": {
|
||||||
|
Enable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
c1.SetDefaults()
|
||||||
|
assert.False(t, c1.PluginSettings.PluginStates["com.mattermost.calls"].Enable)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,12 +32,16 @@ type FeatureFlags struct {
|
|||||||
PluginPlaybooks string `plugin_id:"playbooks"`
|
PluginPlaybooks string `plugin_id:"playbooks"`
|
||||||
PluginApps string `plugin_id:"com.mattermost.apps"`
|
PluginApps string `plugin_id:"com.mattermost.apps"`
|
||||||
PluginFocalboard string `plugin_id:"focalboard"`
|
PluginFocalboard string `plugin_id:"focalboard"`
|
||||||
|
PluginCalls string `plugin_id:"com.mattermost.calls"`
|
||||||
|
|
||||||
PermalinkPreviews bool
|
PermalinkPreviews bool
|
||||||
|
|
||||||
// Enable Calls plugin support in the mobile app
|
// Enable Calls plugin support in the mobile app
|
||||||
CallsMobile bool
|
CallsMobile bool
|
||||||
|
|
||||||
|
// CallsEnabled controls whether or not the Calls plugin should be enabled
|
||||||
|
CallsEnabled bool
|
||||||
|
|
||||||
// A dash separated list for feature flags to turn on for Boards
|
// A dash separated list for feature flags to turn on for Boards
|
||||||
BoardsFeatureFlags string
|
BoardsFeatureFlags string
|
||||||
|
|
||||||
@@ -93,6 +97,7 @@ func (f *FeatureFlags) SetDefaults() {
|
|||||||
f.CloudFree = false
|
f.CloudFree = false
|
||||||
f.CommandPalette = false
|
f.CommandPalette = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FeatureFlags) Plugins() map[string]string {
|
func (f *FeatureFlags) Plugins() map[string]string {
|
||||||
rFFVal := reflect.ValueOf(f).Elem()
|
rFFVal := reflect.ValueOf(f).Elem()
|
||||||
rFFType := reflect.TypeOf(f).Elem()
|
rFFType := reflect.TypeOf(f).Elem()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -691,3 +692,7 @@ func filterBlocklist(r rune) rune {
|
|||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsCloud() bool {
|
||||||
|
return os.Getenv("MM_CLOUD_INSTALLATION_ID") != ""
|
||||||
|
}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user