Merge branch 'master' of github.com:mattermost/mattermost-server into top-dms-clean

Этот коммит содержится в:
Shivashis Padhi
2022-08-10 21:00:57 +05:30
родитель 4fc8ef0125 1738bd6e92
Коммит 4ec3eade3b
214 изменённых файлов: 3322 добавлений и 3004 удалений

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

@@ -7,7 +7,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"runtime/debug"
@@ -29,7 +28,7 @@ func (s *Server) GetLogs(page, perPage int) ([]string, *model.AppError) {
var lines []string
license := s.License()
if license != nil && *license.Features.Cluster && s.Cluster != nil && *s.Config().ClusterSettings.Enable {
if license != nil && *license.Features.Cluster && s.Cluster != nil && *s.platform.Config().ClusterSettings.Enable {
if info := s.Cluster.GetMyClusterInfo(); info != nil {
lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
lines = append(lines, "-----------------------------------------------------------------------------------------------------------")
@@ -48,7 +47,7 @@ func (s *Server) GetLogs(page, perPage int) ([]string, *model.AppError) {
lines = append(lines, melines...)
if s.Cluster != nil && *s.Config().ClusterSettings.Enable {
if s.Cluster != nil && *s.platform.Config().ClusterSettings.Enable {
clines, err := s.Cluster.GetLogs(page, perPage)
if err != nil {
return nil, err
@@ -67,9 +66,9 @@ func (a *App) GetLogs(page, perPage int) ([]string, *model.AppError) {
func (s *Server) GetLogsSkipSend(page, perPage int) ([]string, *model.AppError) {
var lines []string
if *s.Config().LogSettings.EnableFile {
if *s.platform.Config().LogSettings.EnableFile {
s.Log.Flush()
logFile := config.GetLogFileLocation(*s.Config().LogSettings.FileLocation)
logFile := config.GetLogFileLocation(*s.platform.Config().LogSettings.FileLocation)
file, err := os.Open(logFile)
if err != nil {
return nil, model.NewAppError("getLogs", "api.admin.file_read_error", nil, err.Error(), http.StatusInternalServerError)
@@ -261,7 +260,7 @@ func (a *App) GetLatestVersion(latestVersionUrl string) (*model.GithubReleaseInf
defer res.Body.Close()
responseData, err := ioutil.ReadAll(res.Body)
responseData, err := io.ReadAll(res.Body)
if err != nil {
return nil, model.NewAppError("GetLatestVersion", "app.admin.latest_version_read_all.failure", nil, "", http.StatusInternalServerError)
}

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

@@ -283,6 +283,8 @@ type AppIface interface {
// PromoteGuestToUser Convert user's roles and all his membership's roles from
// guest roles to regular user roles.
PromoteGuestToUser(c *request.Context, user *model.User, requestorId string) *model.AppError
// Removes a listener function by the unique ID returned when AddConfigListener was called
RemoveConfigListener(id string)
// RenameChannel is used to rename the channel Name and the DisplayName fields
RenameChannel(c request.CTX, channel *model.Channel, newChannelName string, newDisplayName string) (*model.Channel, *model.AppError)
// RenameTeam is used to rename the team Name and the DisplayName fields
@@ -942,7 +944,6 @@ type AppIface interface {
ReloadConfig() error
RemoveAllDeactivatedMembersFromChannel(c request.CTX, channel *model.Channel) *model.AppError
RemoveChannelsFromRetentionPolicy(policyID string, channelIDs []string) *model.AppError
RemoveConfigListener(id string)
RemoveCustomStatus(c request.CTX, userID string) *model.AppError
RemoveDirectory(path string) *model.AppError
RemoveFile(path string) *model.AppError

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

@@ -109,10 +109,10 @@ func (s *Server) configureAudit(adt *audit.Audit, bAllowAdvancedLogging bool) er
adt.OnError = s.onAuditError
var logConfigSrc config.LogConfigSrc
dsn := *s.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
dsn := *s.platform.Config().ExperimentalAuditSettings.AdvancedLoggingConfig
if bAllowAdvancedLogging && dsn != "" {
var err error
logConfigSrc, err = config.NewLogConfigSrc(dsn, s.configStore.Store)
logConfigSrc, err = config.NewLogConfigSrc(dsn, s.platform.GetConfigStore())
if err != nil {
return fmt.Errorf("invalid config source for audit, %w", err)
}
@@ -120,7 +120,7 @@ func (s *Server) configureAudit(adt *audit.Audit, bAllowAdvancedLogging bool) er
}
// ExperimentalAuditSettings provides basic file audit (E0, E10); logConfigSrc provides advanced config (E20).
cfg, err := config.MloggerConfigFromAuditConfig(s.Config().ExperimentalAuditSettings, logConfigSrc)
cfg, err := config.MloggerConfigFromAuditConfig(s.platform.Config().ExperimentalAuditSettings, logConfigSrc)
if err != nil {
return fmt.Errorf("invalid config for audit, %w", err)
}

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

@@ -7,7 +7,7 @@ import (
"context"
"encoding/csv"
"fmt"
"io/ioutil"
"io"
"os"
"strconv"
"strings"
@@ -133,7 +133,7 @@ func TestSessionHasPermissionToGroup(t *testing.T) {
require.NoError(t, e)
defer file.Close()
b, e := ioutil.ReadAll(file)
b, e := io.ReadAll(file)
require.NoError(t, e)
r := csv.NewReader(strings.NewReader(string(b)))

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

@@ -641,11 +641,11 @@ func (a *App) UpdateChannel(c request.CTX, channel *model.Channel) (*model.Chann
var invErr *store.ErrInvalidInput
switch {
case errors.As(err, &invErr):
return nil, model.NewAppError("UpdateChannel", "app.channel.update.bad_id", nil, invErr.Error(), http.StatusBadRequest)
return nil, model.NewAppError("UpdateChannel", "app.channel.update.bad_id", nil, "", http.StatusBadRequest).Wrap(invErr)
case errors.As(err, &appErr):
return nil, appErr
default:
return nil, model.NewAppError("UpdateChannel", "app.channel.update_channel.internal_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("UpdateChannel", "app.channel.update_channel.internal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
@@ -1267,9 +1267,9 @@ func (a *App) UpdateChannelMemberNotifyProps(c request.CTX, data map[string]stri
case errors.As(err, &appErr):
return nil, appErr
case errors.As(err, &nfErr):
return nil, model.NewAppError("updateMemberNotifyProps", MissingChannelMemberError, nil, nfErr.Error(), http.StatusNotFound)
return nil, model.NewAppError("updateMemberNotifyProps", MissingChannelMemberError, nil, "", http.StatusNotFound).Wrap(nfErr)
default:
return nil, model.NewAppError("updateMemberNotifyProps", "app.channel.get_member.app_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("updateMemberNotifyProps", "app.channel.get_member.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
@@ -1289,17 +1289,17 @@ func (a *App) UpdateChannelMemberNotifyProps(c request.CTX, data map[string]stri
}
func (a *App) updateChannelMember(c request.CTX, member *model.ChannelMember) (*model.ChannelMember, *model.AppError) {
member, nErr := a.Srv().Store.Channel().UpdateMember(member)
if nErr != nil {
member, err := a.Srv().Store.Channel().UpdateMember(member)
if err != nil {
var appErr *model.AppError
var nfErr *store.ErrNotFound
switch {
case errors.As(nErr, &appErr):
case errors.As(err, &appErr):
return nil, appErr
case errors.As(nErr, &nfErr):
return nil, model.NewAppError("updateChannelMember", MissingChannelMemberError, nil, nfErr.Error(), http.StatusNotFound)
case errors.As(err, &nfErr):
return nil, model.NewAppError("updateChannelMember", MissingChannelMemberError, nil, "", http.StatusNotFound).Wrap(nfErr)
default:
return nil, model.NewAppError("updateChannelMember", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("updateChannelMember", "app.channel.get_member.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
@@ -2604,14 +2604,14 @@ func (a *App) MarkChannelAsUnreadFromPost(c request.CTX, postID string, userID s
}
func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID string, userID string) (*model.ChannelUnreadAt, *model.AppError) {
post, err := a.GetSinglePost(postID, false)
if err != nil {
return nil, err
post, appErr := a.GetSinglePost(postID, false)
if appErr != nil {
return nil, appErr
}
user, err := a.GetUser(userID)
if err != nil {
return nil, err
user, appErr := a.GetUser(userID)
if appErr != nil {
return nil, appErr
}
threadId := post.RootId
@@ -2619,18 +2619,18 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
threadId = post.Id
}
unreadMentions, unreadMentionsRoot, err := a.countMentionsFromPost(c, user, post)
if err != nil {
return nil, err
unreadMentions, unreadMentionsRoot, appErr := a.countMentionsFromPost(c, user, post)
if appErr != nil {
return nil, appErr
}
// if root post,
// In CRT Supported Client: badge on channel only sums mentions in root posts including and below the post that was marked.
// In CRT Unsupported Client: badge on channel sums mentions in all posts (root & replies) including and below the post that was marked unread.
if post.RootId == "" {
channelUnread, nErr := a.Srv().Store.Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, unreadMentionsRoot, true)
if nErr != nil {
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, nErr.Error(), http.StatusInternalServerError)
channelUnread, err := a.Srv().Store.Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, unreadMentionsRoot, true)
if err != nil {
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
a.sendWebSocketPostUnreadEvent(c, channelUnread, postID, true)
@@ -2643,21 +2643,21 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
// If there are replies with mentions below the marked reply in the thread, then sum the mentions for the threads mention badge.
// In CRT Unsupported Client: Channel is marked as unread and new messages line inserted above the marked post.
// Badge on channel sums mentions in all posts (root & replies) including and below the post that was marked unread.
rootPost, err := a.GetSinglePost(post.RootId, false)
if err != nil {
return nil, err
rootPost, appErr := a.GetSinglePost(post.RootId, false)
if appErr != nil {
return nil, appErr
}
channel, nErr := a.Srv().Store.Channel().Get(post.ChannelId, true)
if nErr != nil {
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, nErr.Error(), http.StatusInternalServerError)
channel, err := a.Srv().Store.Channel().Get(post.ChannelId, true)
if err != nil {
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
if *a.Config().ServiceSettings.ThreadAutoFollow {
threadMembership, sErr := a.Srv().Store.Thread().GetMembershipForUser(user.Id, threadId)
threadMembership, mErr := a.Srv().Store.Thread().GetMembershipForUser(user.Id, threadId)
var errNotFound *store.ErrNotFound
if sErr != nil && !errors.As(sErr, &errNotFound) {
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, sErr.Error(), http.StatusInternalServerError)
if mErr != nil && !errors.As(mErr, &errNotFound) {
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(mErr)
}
// Follow thread if we're not already following it
if threadMembership == nil {
@@ -2668,25 +2668,25 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
UpdateViewedTimestamp: false,
UpdateParticipants: false,
}
threadMembership, sErr = a.Srv().Store.Thread().MaintainMembership(user.Id, threadId, opts)
if sErr != nil {
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, sErr.Error(), http.StatusInternalServerError)
threadMembership, mErr = a.Srv().Store.Thread().MaintainMembership(user.Id, threadId, opts)
if mErr != nil {
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(mErr)
}
}
// If threadmembership already exists but user had previously unfollowed the thread, then follow the thread again.
threadMembership.Following = true
threadMembership.LastViewed = post.CreateAt - 1
threadMembership.UnreadMentions, err = a.countThreadMentions(c, user, rootPost, channel.TeamId, post.CreateAt-1)
if err != nil {
return nil, err
threadMembership.UnreadMentions, appErr = a.countThreadMentions(c, user, rootPost, channel.TeamId, post.CreateAt-1)
if appErr != nil {
return nil, appErr
}
threadMembership, sErr = a.Srv().Store.Thread().UpdateMembership(threadMembership)
if sErr != nil {
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, sErr.Error(), http.StatusInternalServerError)
threadMembership, mErr = a.Srv().Store.Thread().UpdateMembership(threadMembership)
if mErr != nil {
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(mErr)
}
thread, sErr := a.Srv().Store.Thread().GetThreadForUser(channel.TeamId, threadMembership, true)
if sErr != nil {
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, sErr.Error(), http.StatusInternalServerError)
thread, mErr := a.Srv().Store.Thread().GetThreadForUser(channel.TeamId, threadMembership, true)
if mErr != nil {
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(mErr)
}
a.sanitizeProfiles(thread.Participants, false)
thread.Post.SanitizeProps()
@@ -2702,9 +2702,9 @@ func (a *App) markChannelAsUnreadFromPostCRTUnsupported(c request.CTX, postID st
}
}
channelUnread, nErr := a.Srv().Store.Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, 0, false)
if nErr != nil {
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, nErr.Error(), http.StatusInternalServerError)
channelUnread, err := a.Srv().Store.Channel().UpdateLastViewedAtPost(post, userID, unreadMentions, 0, false)
if err != nil {
return channelUnread, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
a.sendWebSocketPostUnreadEvent(c, channelUnread, postID, false)
a.UpdateMobileAppBadge(userID)
@@ -3213,14 +3213,14 @@ func (a *App) ToggleMuteChannel(c request.CTX, channelID, userID string) (*model
}
func (a *App) setChannelsMuted(c request.CTX, channelIDs []string, userID string, muted bool) ([]*model.ChannelMember, *model.AppError) {
members, nErr := a.Srv().Store.Channel().GetMembersByChannelIds(channelIDs, userID)
if nErr != nil {
members, err := a.Srv().Store.Channel().GetMembersByChannelIds(channelIDs, userID)
if err != nil {
var appErr *model.AppError
switch {
case errors.As(nErr, &appErr):
case errors.As(err, &appErr):
return nil, appErr
default:
return nil, model.NewAppError("setChannelsMuted", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("setChannelsMuted", "app.channel.get_member.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
@@ -3240,17 +3240,17 @@ func (a *App) setChannelsMuted(c request.CTX, channelIDs []string, userID string
return nil, nil
}
updated, nErr := a.Srv().Store.Channel().UpdateMultipleMembers(membersToUpdate)
if nErr != nil {
updated, err := a.Srv().Store.Channel().UpdateMultipleMembers(membersToUpdate)
if err != nil {
var appErr *model.AppError
var nfErr *store.ErrNotFound
switch {
case errors.As(nErr, &appErr):
case errors.As(err, &appErr):
return nil, appErr
case errors.As(nErr, &nfErr):
return nil, model.NewAppError("setChannelsMuted", MissingChannelMemberError, nil, nfErr.Error(), http.StatusNotFound)
case errors.As(err, &nfErr):
return nil, model.NewAppError("setChannelsMuted", MissingChannelMemberError, nil, "", http.StatusNotFound).Wrap(nfErr)
default:
return nil, model.NewAppError("setChannelsMuted", "app.channel.get_member.app_error", nil, nErr.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("setChannelsMuted", "app.channel.get_member.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
@@ -3375,7 +3375,7 @@ func (a *App) ClearChannelMembersCache(c request.CTX, channelID string) error {
return nil
}
if err := a.forEachChannelMember(c, channelID, clearSessionCache); err != nil {
return fmt.Errorf("error clearing cache for channel members: channel_id: %s, error: %v", channelID, err)
return fmt.Errorf("error clearing cache for channel members: channel_id: %s, error: %w", channelID, err)
}
return nil
}
@@ -3383,7 +3383,7 @@ func (a *App) ClearChannelMembersCache(c request.CTX, channelID string) error {
func (a *App) GetMemberCountsByGroup(ctx context.Context, channelID string, includeTimezones bool) ([]*model.ChannelMemberCountByGroup, *model.AppError) {
channelMemberCounts, err := a.Srv().Store.Channel().GetMemberCountsByGroup(ctx, channelID, includeTimezones)
if err != nil {
return nil, model.NewAppError("GetMemberCountsByGroup", "app.channel.get_member_count.app_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("GetMemberCountsByGroup", "app.channel.get_member_count.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return channelMemberCounts, nil

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

@@ -144,7 +144,7 @@ func (a *App) UpdateSidebarCategoryOrder(c request.CTX, userID, teamID string, c
func (a *App) UpdateSidebarCategories(c request.CTX, userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) {
updatedCategories, originalCategories, err := a.Srv().Store.Channel().UpdateSidebarCategories(userID, teamID, categories)
if err != nil {
return nil, model.NewAppError("UpdateSidebarCategories", "app.channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("UpdateSidebarCategories", "app.channel.sidebar_categories.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
message := model.NewWebSocketEvent(model.WebsocketEventSidebarCategoryUpdated, teamID, "", userID, nil)

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

@@ -2056,7 +2056,7 @@ func TestMarkChannelsAsViewedPanic(t *testing.T) {
UserStore: &mockUserStore,
SessionStore: &mockSessionStore,
OAuthStore: &mockOAuthStore,
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)

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

@@ -31,12 +31,6 @@ type licenseSvc interface {
RequestTrialLicense(requesterID string, users int, termsAccepted bool, receiveEmailsAccepted bool) *model.AppError
}
// namer is an interface which enforces that
// all services can return their names.
type namer interface {
Name() ServiceKey
}
// Channels contains all channels related state.
type Channels struct {
srv *Server
@@ -107,7 +101,7 @@ func init() {
func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
ch := &Channels{
srv: s,
imageProxy: imageproxy.MakeImageProxy(s, s.httpService, s.Log),
imageProxy: imageproxy.MakeImageProxy(s.platform, s.httpService, s.Log),
uploadLockMap: map[string]bool{},
}
@@ -133,10 +127,6 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
if !ok {
return nil, errors.New("Config service did not satisfy ConfigSvc interface")
}
_, ok = svc.(namer)
if !ok {
return nil, errors.New("Config service does not contain Name method")
}
ch.cfgSvc = cfgSvc
case FilestoreKey:
filestore, ok := svc.(filestore.FileBackend)
@@ -149,10 +139,6 @@ func NewChannels(s *Server, services map[ServiceKey]any) (*Channels, error) {
if !ok {
return nil, errors.New("License service did not satisfy licenseSvc interface")
}
_, ok = svc.(namer)
if !ok {
return nil, errors.New("License service does not contain Name method")
}
ch.licenseSvc = svc
}
}

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

@@ -83,7 +83,7 @@ func (cds *ClusterDiscoveryService) Stop() {
}
func (s *Server) IsLeader() bool {
if s.License() != nil && *s.Config().ClusterSettings.Enable && s.Cluster != nil {
if s.License() != nil && *s.platform.Config().ClusterSettings.Enable && s.Cluster != nil {
return s.Cluster.IsLeader()
}
return true

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

@@ -7,7 +7,6 @@ import (
"context"
"errors"
"io"
"io/ioutil"
"net/http"
"net/url"
"regexp"
@@ -521,7 +520,7 @@ func (a *App) DoCommandRequest(cmd *model.Command, p url.Values) (*model.Command
if resp.StatusCode != http.StatusOK {
// Ignore the error below because the resulting string will just be the empty string if bodyBytes is nil
bodyBytes, _ := ioutil.ReadAll(body)
bodyBytes, _ := io.ReadAll(body)
return cmd, nil, model.NewAppError("command", "api.command.execute_command.failed_resp.app_error", map[string]any{"Trigger": cmd.Trigger, "Status": resp.Status}, string(bodyBytes), http.StatusInternalServerError)
}

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

@@ -280,7 +280,7 @@ func (a *App) getDynamicListArgument(c *request.Context, commandArgs *model.Comm
var listItems []model.AutocompleteListItem
if jsonErr := json.NewDecoder(resp.Body).Decode(&listItems); jsonErr != nil {
mlog.Warn("Failed to decode from JSON", mlog.Err(jsonErr))
c.Logger().Warn("Failed to decode from JSON", mlog.Err(jsonErr))
}
return parseListItems(listItems, parsed, toBeParsed)

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

@@ -5,8 +5,8 @@ package app
import (
"errors"
"io/ioutil"
"net/http"
"os"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@@ -75,7 +75,7 @@ func (a *App) GetComplianceReport(reportId string) (*model.Compliance, *model.Ap
}
func (a *App) GetComplianceFile(job *model.Compliance) ([]byte, *model.AppError) {
f, err := ioutil.ReadFile(*a.Config().ComplianceSettings.Directory + "compliance/" + job.JobName() + ".zip")
f, err := os.ReadFile(*a.Config().ComplianceSettings.Directory + "compliance/" + job.JobName() + ".zip")
if err != nil {
return nil, model.NewAppError("readFile", "api.file.read_file.reading_local.app_error", nil, err.Error(), http.StatusNotImplemented)
}

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

@@ -12,7 +12,6 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"net/url"
"reflect"
"strconv"
@@ -22,7 +21,6 @@ import (
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/mattermost/mattermost-server/v6/shared/mail"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
"github.com/mattermost/mattermost-server/v6/utils"
@@ -32,113 +30,24 @@ const (
ErrorTermsOfServiceNoRowsFound = "app.terms_of_service.get.no_rows.app_error"
)
// ensure the config wrapper implements `product.ConfigService`
var _ product.ConfigService = (*configWrapper)(nil)
// configWrapper is an adapter struct that only exposes the
// config related functionality to be passed down to other products.
type configWrapper struct {
srv *Server
*config.Store
}
func (w *configWrapper) Name() ServiceKey {
return ConfigKey
}
func (w *configWrapper) Config() *model.Config {
return w.Store.Get()
}
func (w *configWrapper) AddConfigListener(listener func(*model.Config, *model.Config)) string {
return w.Store.AddListener(listener)
}
func (w *configWrapper) RemoveConfigListener(id string) {
w.Store.RemoveListener(id)
}
func (w *configWrapper) UpdateConfig(f func(*model.Config)) {
if w.Store.IsReadOnly() {
return
}
old := w.Config()
updated := old.Clone()
f(updated)
if _, _, err := w.Store.Set(updated); err != nil {
mlog.Error("Failed to update config", mlog.Err(err))
}
}
func (w *configWrapper) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
oldCfg, newCfg, err := w.Store.Set(newCfg)
if errors.Cause(err) == config.ErrReadOnlyConfiguration {
return nil, nil, model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden)
} else if err != nil {
return nil, nil, model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if w.srv.startMetrics && *w.Config().MetricsSettings.Enable {
if w.srv.GetMetrics() != nil {
w.srv.GetMetrics().Register()
}
w.srv.platform.RestartMetrics() // TODO: remove when this moved to the platform service
} else {
w.srv.platform.ShutdownMetrics() // TODO: remove when this moved to the platform service
}
if w.srv.Cluster != nil {
err := w.srv.Cluster.ConfigChanged(w.Store.RemoveEnvironmentOverrides(oldCfg),
w.Store.RemoveEnvironmentOverrides(newCfg), sendConfigChangeClusterMessage)
if err != nil {
return nil, nil, err
}
}
return oldCfg, newCfg, nil
}
func (w *configWrapper) ReloadConfig() error {
if err := w.Store.Load(); err != nil {
return err
}
return nil
}
func (s *Server) Config() *model.Config {
return s.configStore.Config()
}
func (s *Server) ConfigStore() *configWrapper {
return s.configStore
return s.platform.Config()
}
func (a *App) Config() *model.Config {
return a.ch.cfgSvc.Config()
}
func (s *Server) EnvironmentConfig(filter func(reflect.StructField) bool) map[string]any {
return s.configStore.GetEnvironmentOverridesWithFilter(filter)
}
func (a *App) EnvironmentConfig(filter func(reflect.StructField) bool) map[string]any {
return a.Srv().EnvironmentConfig(filter)
}
func (s *Server) UpdateConfig(f func(*model.Config)) {
s.configStore.UpdateConfig(f)
return a.Srv().platform.GetEnvironmentOverridesWithFilter(filter)
}
func (a *App) UpdateConfig(f func(*model.Config)) {
a.Srv().UpdateConfig(f)
}
func (s *Server) ReloadConfig() error {
return s.configStore.ReloadConfig()
a.Srv().platform.UpdateConfig(f)
}
func (a *App) ReloadConfig() error {
return a.Srv().ReloadConfig()
return a.Srv().platform.ReloadConfig()
}
func (a *App) ClientConfig() map[string]string {
@@ -153,24 +62,13 @@ func (a *App) LimitedClientConfig() map[string]string {
return a.ch.limitedClientConfig.Load().(map[string]string)
}
// Registers a function with a given listener to be called when the config is reloaded and may have changed. The function
// will be called with two arguments: the old config and the new config. AddConfigListener returns a unique ID
// for the listener that can later be used to remove it.
func (s *Server) AddConfigListener(listener func(*model.Config, *model.Config)) string {
return s.configStore.AddConfigListener(listener)
}
func (a *App) AddConfigListener(listener func(*model.Config, *model.Config)) string {
return a.Srv().AddConfigListener(listener)
return a.Srv().platform.AddConfigListener(listener)
}
// Removes a listener function by the unique ID returned when AddConfigListener was called
func (s *Server) RemoveConfigListener(id string) {
s.configStore.RemoveConfigListener(id)
}
func (a *App) RemoveConfigListener(id string) {
a.Srv().RemoveConfigListener(id)
a.Srv().platform.RemoveConfigListener(id)
}
// ensurePostActionCookieSecret ensures that the key for encrypting PostActionCookie exists
@@ -449,7 +347,7 @@ func (a *App) LimitedClientConfigWithComputed() map[string]string {
// GetConfigFile proxies access to the given configuration file to the underlying config store.
func (a *App) GetConfigFile(name string) ([]byte, error) {
data, err := a.Srv().configStore.GetFile(name)
data, err := a.Srv().platform.GetConfigFile(name)
if err != nil {
return nil, errors.Wrapf(err, "failed to get config file %s", name)
}
@@ -471,15 +369,9 @@ func (a *App) GetEnvironmentConfig(filter func(reflect.StructField) bool) map[st
return a.EnvironmentConfig(filter)
}
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
// It returns both the previous and current configs.
func (s *Server) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
return s.configStore.SaveConfig(newCfg, sendConfigChangeClusterMessage)
}
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
func (a *App) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
return a.Srv().SaveConfig(newCfg, sendConfigChangeClusterMessage)
return a.Srv().platform.SaveConfig(newCfg, sendConfigChangeClusterMessage)
}
func (a *App) HandleMessageExportConfig(cfg *model.Config, appCfg *model.Config) {
@@ -499,8 +391,8 @@ func (a *App) HandleMessageExportConfig(cfg *model.Config, appCfg *model.Config)
}
func (s *Server) MailServiceConfig() *mail.SMTPConfig {
emailSettings := s.Config().EmailSettings
hostname := utils.GetHostnameFromSiteURL(*s.Config().ServiceSettings.SiteURL)
emailSettings := s.platform.Config().EmailSettings
hostname := utils.GetHostnameFromSiteURL(*s.platform.Config().ServiceSettings.SiteURL)
cfg := mail.SMTPConfig{
Hostname: hostname,
ConnectionSecurity: *emailSettings.ConnectionSecurity,

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

@@ -16,41 +16,6 @@ import (
"github.com/mattermost/mattermost-server/v6/utils"
)
func TestConfigListener(t *testing.T) {
th := Setup(t)
defer th.TearDown()
originalSiteName := th.App.Config().TeamSettings.SiteName
listenerCalled := false
listener := func(oldConfig *model.Config, newConfig *model.Config) {
assert.False(t, listenerCalled, "listener called twice")
assert.Equal(t, *originalSiteName, *oldConfig.TeamSettings.SiteName, "old config contains incorrect site name")
assert.Equal(t, "test123", *newConfig.TeamSettings.SiteName, "new config contains incorrect site name")
listenerCalled = true
}
listenerId := th.App.AddConfigListener(listener)
defer th.App.RemoveConfigListener(listenerId)
listener2Called := false
listener2 := func(oldConfig *model.Config, newConfig *model.Config) {
assert.False(t, listener2Called, "listener2 called twice")
listener2Called = true
}
listener2Id := th.App.AddConfigListener(listener2)
defer th.App.RemoveConfigListener(listener2Id)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.SiteName = "test123"
})
assert.True(t, listenerCalled, "listener should've been called")
assert.True(t, listener2Called, "listener 2 should've been called")
}
func TestAsymmetricSigningKey(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()

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

@@ -5,7 +5,6 @@ package app
import (
"io"
"io/ioutil"
"net/http"
"net/url"
"time"
@@ -35,7 +34,7 @@ func (s *Server) downloadFromURL(downloadURL string) ([]byte, error) {
if err != nil {
return nil, errors.Errorf("failed to parse url %s", downloadURL)
}
if !*s.Config().PluginSettings.AllowInsecureDownloadURL && u.Scheme != "https" {
if !*s.platform.Config().PluginSettings.AllowInsecureDownloadURL && u.Scheme != "https" {
return nil, errors.Errorf("insecure url not allowed %s", downloadURL)
}
@@ -64,5 +63,5 @@ func (s *Server) downloadFromURL(downloadURL string) ([]byte, error) {
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
return io.ReadAll(resp.Body)
}

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

@@ -5,7 +5,6 @@ package email
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
@@ -64,7 +63,7 @@ func SetupWithStoreMock(tb testing.TB) *TestHelper {
}
func setupTestHelper(s store.Store, tb testing.TB) *TestHelper {
tempWorkspace, err := ioutil.TempDir("", "userservicetest")
tempWorkspace, err := os.MkdirTemp("", "userservicetest")
if err != nil {
panic(err)
}

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

@@ -39,11 +39,11 @@ const (
func (a *App) CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartImageData *multipart.Form) (*model.Emoji, *model.AppError) {
if !*a.Config().ServiceSettings.EnableCustomEmoji {
return nil, model.NewAppError("UploadEmojiImage", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
return nil, model.NewAppError("UploadEmojiImage", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
}
if *a.Config().FileSettings.DriverName == "" {
return nil, model.NewAppError("GetEmoji", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
return nil, model.NewAppError("GetEmoji", "api.emoji.storage.app_error", nil, "", http.StatusForbidden)
}
// wipe the emoji id so that existing emojis can't get overwritten
@@ -52,8 +52,8 @@ func (a *App) CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartIma
// do our best to validate the emoji before committing anything to the DB so that we don't have to clean up
// orphaned files left over when validation fails later on
emoji.PreSave()
if err := emoji.IsValid(); err != nil {
return nil, err
if appErr := emoji.IsValid(); appErr != nil {
return nil, appErr
}
if emoji.CreatorId != sessionUserId {
@@ -61,22 +61,21 @@ func (a *App) CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartIma
}
if existingEmoji, err := a.Srv().Store.Emoji().GetByName(context.Background(), emoji.Name, true); err == nil && existingEmoji != nil {
return nil, model.NewAppError("createEmoji", "api.emoji.create.duplicate.app_error", nil, "", http.StatusBadRequest)
return nil, model.NewAppError("createEmoji", "api.emoji.create.duplicate.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
imageData := multiPartImageData.File["image"]
if len(imageData) == 0 {
err := model.NewAppError("Context", "api.context.invalid_body_param.app_error", map[string]any{"Name": "createEmoji"}, "", http.StatusBadRequest)
return nil, err
return nil, model.NewAppError("Context", "api.context.invalid_body_param.app_error", map[string]any{"Name": "createEmoji"}, "", http.StatusBadRequest)
}
if err := a.UploadEmojiImage(emoji.Id, imageData[0]); err != nil {
return nil, err
if appErr := a.UploadEmojiImage(emoji.Id, imageData[0]); appErr != nil {
return nil, appErr
}
emoji, err := a.Srv().Store.Emoji().Save(emoji)
if err != nil {
return nil, model.NewAppError("CreateEmoji", "app.emoji.create.internal_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("CreateEmoji", "app.emoji.create.internal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
message := model.NewWebSocketEvent(model.WebsocketEventEmojiAdded, "", "", "", nil)
@@ -100,11 +99,11 @@ func (a *App) GetEmojiList(page, perPage int, sort string) ([]*model.Emoji, *mod
func (a *App) UploadEmojiImage(id string, imageData *multipart.FileHeader) *model.AppError {
if !*a.Config().ServiceSettings.EnableCustomEmoji {
return model.NewAppError("UploadEmojiImage", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
return model.NewAppError("UploadEmojiImage", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
}
if *a.Config().FileSettings.DriverName == "" {
return model.NewAppError("UploadEmojiImage", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
return model.NewAppError("UploadEmojiImage", "api.emoji.storage.app_error", nil, "", http.StatusForbidden)
}
file, err := imageData.Open()
@@ -185,11 +184,11 @@ func (a *App) DeleteEmoji(emoji *model.Emoji) *model.AppError {
func (a *App) GetEmoji(emojiId string) (*model.Emoji, *model.AppError) {
if !*a.Config().ServiceSettings.EnableCustomEmoji {
return nil, model.NewAppError("GetEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
return nil, model.NewAppError("GetEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
}
if *a.Config().FileSettings.DriverName == "" {
return nil, model.NewAppError("GetEmoji", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
return nil, model.NewAppError("GetEmoji", "api.emoji.storage.app_error", nil, "", http.StatusForbidden)
}
emoji, err := a.Srv().Store.Emoji().Get(context.Background(), emojiId, true)
@@ -208,11 +207,11 @@ func (a *App) GetEmoji(emojiId string) (*model.Emoji, *model.AppError) {
func (a *App) GetEmojiByName(emojiName string) (*model.Emoji, *model.AppError) {
if !*a.Config().ServiceSettings.EnableCustomEmoji {
return nil, model.NewAppError("GetEmojiByName", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
return nil, model.NewAppError("GetEmojiByName", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
}
if *a.Config().FileSettings.DriverName == "" {
return nil, model.NewAppError("GetEmojiByName", "api.emoji.storage.app_error", nil, "", http.StatusNotImplemented)
return nil, model.NewAppError("GetEmojiByName", "api.emoji.storage.app_error", nil, "", http.StatusForbidden)
}
emoji, err := a.Srv().Store.Emoji().GetByName(context.Background(), emojiName, true)
@@ -231,7 +230,7 @@ func (a *App) GetEmojiByName(emojiName string) (*model.Emoji, *model.AppError) {
func (a *App) GetMultipleEmojiByName(names []string) ([]*model.Emoji, *model.AppError) {
if !*a.Config().ServiceSettings.EnableCustomEmoji {
return nil, model.NewAppError("GetMultipleEmojiByName", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
return nil, model.NewAppError("GetMultipleEmojiByName", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
}
emoji, err := a.Srv().Store.Emoji().GetMultipleByName(names)
@@ -269,7 +268,7 @@ func (a *App) GetEmojiImage(emojiId string) ([]byte, string, *model.AppError) {
func (a *App) SearchEmoji(name string, prefixOnly bool, limit int) ([]*model.Emoji, *model.AppError) {
if !*a.Config().ServiceSettings.EnableCustomEmoji {
return nil, model.NewAppError("SearchEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusNotImplemented)
return nil, model.NewAppError("SearchEmoji", "api.emoji.disabled.app_error", nil, "", http.StatusForbidden)
}
list, err := a.Srv().Store.Emoji().Search(name, prefixOnly, limit)

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

@@ -87,9 +87,9 @@ func RegisterCloudInterface(f func(*Server) einterfaces.CloudInterface) {
cloudInterface = f
}
var metricsInterface func(*Server) einterfaces.MetricsInterface
var metricsInterface func(*Server, string, string) einterfaces.MetricsInterface
func RegisterMetricsInterface(f func(*Server) einterfaces.MetricsInterface) {
func RegisterMetricsInterface(f func(*Server, string, string) einterfaces.MetricsInterface) {
metricsInterface = f
}

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

@@ -141,14 +141,14 @@ func (a *App) BulkExport(ctx request.CTX, writer io.Writer, outPath string, opts
return nil
}
func (a *App) exportWriteLine(writer io.Writer, line *LineImportData) *model.AppError {
func (a *App) exportWriteLine(w io.Writer, line *LineImportData) *model.AppError {
b, err := json.Marshal(line)
if err != nil {
return model.NewAppError("BulkExport", "app.export.export_write_line.json_marshall.error", nil, "err="+err.Error(), http.StatusBadRequest)
return model.NewAppError("BulkExport", "app.export.export_write_line.json_marshall.error", nil, "", http.StatusBadRequest).Wrap(err)
}
if _, err := writer.Write(append(b, '\n')); err != nil {
return model.NewAppError("BulkExport", "app.export.export_write_line.io_writer.error", nil, "err="+err.Error(), http.StatusBadRequest)
if _, err := w.Write(append(b, '\n')); err != nil {
return model.NewAppError("BulkExport", "app.export.export_write_line.io_writer.error", nil, "", http.StatusBadRequest).Wrap(err)
}
return nil

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

@@ -6,7 +6,6 @@ package app
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
@@ -590,7 +589,7 @@ func TestBulkExport(t *testing.T) {
th := Setup(t)
testsDir, _ := fileutils.FindDir("tests")
dir, err := ioutil.TempDir("", "import_test")
dir, err := os.MkdirTemp("", "import_test")
require.NoError(t, err)
defer os.RemoveAll(dir)

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

@@ -8,7 +8,6 @@ import (
"bytes"
"compress/gzip"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
@@ -81,7 +80,7 @@ func TestExtractTarGz(t *testing.T) {
})
}
dst, err := ioutil.TempDir("", "TestExtractTarGz")
dst, err := os.MkdirTemp("", "TestExtractTarGz")
require.NoError(t, err)
defer os.RemoveAll(dst)
@@ -175,7 +174,7 @@ func TestExtractTarGz(t *testing.T) {
for i, testCase := range testCases {
t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
dst, err := ioutil.TempDir("", "TestExtractTarGz")
dst, err := os.MkdirTemp("", "TestExtractTarGz")
require.NoError(t, err)
defer os.RemoveAll(dst)

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

@@ -1,118 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"encoding/json"
"os"
"time"
"github.com/mattermost/mattermost-server/v6/app/featureflag"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
// setupFeatureFlags called on startup and when the cluster leader changes.
// Starts or stops the synchronization of feature flags from upstream management.
func (s *Server) setupFeatureFlags() {
s.featureFlagSynchronizerMutex.Lock()
defer s.featureFlagSynchronizerMutex.Unlock()
splitKey := *s.Config().ServiceSettings.SplitKey
splitConfigured := splitKey != ""
syncFeatureFlags := splitConfigured && s.IsLeader()
s.configStore.SetReadOnlyFF(!splitConfigured)
if syncFeatureFlags {
if err := s.startFeatureFlagUpdateJob(); err != nil {
s.Log.Warn("Unable to setup synchronization with feature flag management. Will fallback to cache.", mlog.Err(err))
}
} else {
s.stopFeatureFlagUpdateJob()
}
if err := s.configStore.Load(); err != nil {
s.Log.Warn("Unable to load config store after feature flag setup.", mlog.Err(err))
}
}
func (s *Server) updateFeatureFlagValuesFromManagement() {
newCfg := s.configStore.GetNoEnv().Clone()
oldFlags := *newCfg.FeatureFlags
newFlags := s.featureFlagSynchronizer.UpdateFeatureFlagValues(oldFlags)
oldFlagsBytes, _ := json.Marshal(oldFlags)
newFlagsBytes, _ := json.Marshal(newFlags)
s.Log.Debug("Checking feature flags from management service", mlog.String("old_flags", string(oldFlagsBytes)), mlog.String("new_flags", string(newFlagsBytes)))
if oldFlags != newFlags {
s.Log.Debug("Feature flag change detected, updating config")
*newCfg.FeatureFlags = newFlags
s.SaveConfig(newCfg, true)
}
}
func (s *Server) startFeatureFlagUpdateJob() error {
// Can be run multiple times
if s.featureFlagSynchronizer != nil {
return nil
}
var log *mlog.Logger
if *s.Config().ServiceSettings.DebugSplit {
log = s.Log
}
attributes := map[string]any{}
// if we are part of a cloud installation, add its installation and group id
if installationId := os.Getenv("MM_CLOUD_INSTALLATION_ID"); installationId != "" {
attributes["installation_id"] = installationId
}
if groupId := os.Getenv("MM_CLOUD_GROUP_ID"); groupId != "" {
attributes["group_id"] = groupId
}
synchronizer, err := featureflag.NewSynchronizer(featureflag.SyncParams{
ServerID: s.TelemetryId(),
SplitKey: *s.Config().ServiceSettings.SplitKey,
Log: log,
Attributes: attributes,
})
if err != nil {
return err
}
s.featureFlagStop = make(chan struct{})
s.featureFlagStopped = make(chan struct{})
s.featureFlagSynchronizer = synchronizer
syncInterval := *s.Config().ServiceSettings.FeatureFlagSyncIntervalSeconds
go func() {
ticker := time.NewTicker(time.Duration(syncInterval) * time.Second)
defer ticker.Stop()
defer close(s.featureFlagStopped)
if err := synchronizer.EnsureReady(); err != nil {
s.Log.Warn("Problem connecting to feature flag management. Will fallback to cloud cache.", mlog.Err(err))
return
}
s.updateFeatureFlagValuesFromManagement()
for {
select {
case <-s.featureFlagStop:
return
case <-ticker.C:
s.updateFeatureFlagValuesFromManagement()
}
}
}()
return nil
}
func (s *Server) stopFeatureFlagUpdateJob() {
if s.featureFlagSynchronizer != nil {
close(s.featureFlagStop)
<-s.featureFlagStopped
s.featureFlagSynchronizer.Close()
s.featureFlagSynchronizer = nil
}
}

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

@@ -122,9 +122,9 @@ func (a *App) isUniqueToUsernames(val string) *model.AppError {
}
func (a *App) CreateGroupWithUserIds(group *model.GroupWithUserIds) (*model.Group, *model.AppError) {
if err := a.isUniqueToUsernames(group.GetName()); err != nil {
err.Where = "CreateGroupWithUserIds"
return nil, err
if appErr := a.isUniqueToUsernames(group.GetName()); appErr != nil {
appErr.Where = "CreateGroupWithUserIds"
return nil, appErr
}
newGroup, err := a.Srv().Store.Group().CreateWithUserIds(group)
@@ -136,18 +136,18 @@ func (a *App) CreateGroupWithUserIds(group *model.GroupWithUserIds) (*model.Grou
case errors.As(err, &appErr):
return nil, appErr
case errors.As(err, &invErr):
return nil, model.NewAppError("CreateGroupWithUserIds", "app.group.id.app_error", nil, invErr.Error(), http.StatusBadRequest)
return nil, model.NewAppError("CreateGroupWithUserIds", "app.group.id.app_error", nil, "", http.StatusBadRequest).Wrap(invErr)
case errors.As(err, &dupKey):
return nil, model.NewAppError("CreateGroupWithUserIds", "app.custom_group.unique_name", nil, dupKey.Error(), http.StatusBadRequest)
return nil, model.NewAppError("CreateGroupWithUserIds", "app.custom_group.unique_name", nil, "", http.StatusBadRequest).Wrap(dupKey)
default:
return nil, model.NewAppError("CreateGroupWithUserIds", "app.insert_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("CreateGroupWithUserIds", "app.insert_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
messageWs := model.NewWebSocketEvent(model.WebsocketEventReceivedGroup, "", "", "", nil)
count, err := a.Srv().Store.Group().GetMemberCount(newGroup.Id)
if err != nil {
return nil, model.NewAppError("CreateGroupWithUserIds", "app.group.id.app_error", nil, err.Error(), http.StatusBadRequest)
return nil, model.NewAppError("CreateGroupWithUserIds", "app.group.id.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
group.MemberCount = model.NewInt(int(count))
groupJSON, jsonErr := json.Marshal(newGroup)
@@ -161,28 +161,12 @@ func (a *App) CreateGroupWithUserIds(group *model.GroupWithUserIds) (*model.Grou
}
func (a *App) UpdateGroup(group *model.Group) (*model.Group, *model.AppError) {
if err := a.isUniqueToUsernames(group.GetName()); err != nil {
err.Where = "UpdateGroup"
return nil, err
if appErr := a.isUniqueToUsernames(group.GetName()); appErr != nil {
appErr.Where = "UpdateGroup"
return nil, appErr
}
updatedGroup, err := a.Srv().Store.Group().Update(group)
if err == nil {
count, countErr := a.Srv().Store.Group().GetMemberCount(updatedGroup.Id)
if countErr != nil {
return nil, model.NewAppError("UpdateGroup", "app.group.id.app_error", nil, countErr.Error(), http.StatusBadRequest)
}
updatedGroup.MemberCount = model.NewInt(int(count))
messageWs := model.NewWebSocketEvent(model.WebsocketEventReceivedGroup, "", "", "", nil)
groupJSON, jsonErr := json.Marshal(updatedGroup)
if jsonErr != nil {
return nil, model.NewAppError("UpdateGroup", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
}
messageWs.Add("group", string(groupJSON))
a.Publish(messageWs)
}
if err != nil {
var nfErr *store.ErrNotFound
var appErr *model.AppError
@@ -191,14 +175,29 @@ func (a *App) UpdateGroup(group *model.Group) (*model.Group, *model.AppError) {
case errors.As(err, &appErr):
return nil, appErr
case errors.As(err, &nfErr):
return nil, model.NewAppError("UpdateGroup", "app.group.no_rows", nil, nfErr.Error(), http.StatusNotFound)
return nil, model.NewAppError("UpdateGroup", "app.group.no_rows", nil, "", http.StatusNotFound).Wrap(nfErr)
case errors.As(err, &dupKey):
return nil, model.NewAppError("CreateGroup", "app.custom_group.unique_name", nil, dupKey.Error(), http.StatusBadRequest)
return nil, model.NewAppError("CreateGroup", "app.custom_group.unique_name", nil, "", http.StatusBadRequest).Wrap(dupKey)
default:
return nil, model.NewAppError("UpdateGroup", "app.select_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("UpdateGroup", "app.select_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
count, err := a.Srv().Store.Group().GetMemberCount(updatedGroup.Id)
if err != nil {
return nil, model.NewAppError("UpdateGroup", "app.group.id.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
updatedGroup.MemberCount = model.NewInt(int(count))
messageWs := model.NewWebSocketEvent(model.WebsocketEventReceivedGroup, "", "", "", nil)
groupJSON, err := json.Marshal(updatedGroup)
if err != nil {
return nil, model.NewAppError("UpdateGroup", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
messageWs.Add("group", string(groupJSON))
a.Publish(messageWs)
return updatedGroup, nil
}
@@ -763,9 +762,9 @@ func (a *App) DeleteGroupMembers(groupID string, userIDs []string) ([]*model.Gro
case errors.As(err, &appErr):
return nil, appErr
case errors.As(err, &invErr):
return nil, model.NewAppError("DeleteGroupMember", "app.group.uniqueness_error", nil, invErr.Error(), http.StatusBadRequest)
return nil, model.NewAppError("DeleteGroupMember", "app.group.uniqueness_error", nil, "", http.StatusBadRequest).Wrap(invErr)
default:
return nil, model.NewAppError("DeleteGroupMember", "app.update_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("DeleteGroupMember", "app.update_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}

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

@@ -5,7 +5,6 @@ package app
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -48,7 +47,7 @@ type TestHelper struct {
}
func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer bool, options []Option, tb testing.TB) *TestHelper {
tempWorkspace, err := ioutil.TempDir("", "apptest")
tempWorkspace, err := os.MkdirTemp("", "apptest")
if err != nil {
panic(err)
}

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

@@ -6,7 +6,6 @@ package imaging
import (
"bytes"
"image/color"
"io/ioutil"
"os"
"testing"
@@ -77,7 +76,7 @@ func TestFillImageTransparency(t *testing.T) {
require.NotNil(t, inputImg)
require.Equal(t, "png", format)
expectedBytes, err := ioutil.ReadFile(imgDir + "/" + tc.outputName)
expectedBytes, err := os.ReadFile(imgDir + "/" + tc.outputName)
require.NoError(t, err)
FillImageTransparency(inputImg, tc.fillColor)

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

@@ -10,7 +10,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
@@ -1217,7 +1216,7 @@ func (a *App) importAttachment(c *request.Context, data *AttachmentImportData, p
timestamp := utils.TimeFromMillis(post.CreateAt)
fileData, err := ioutil.ReadAll(file)
fileData, err := io.ReadAll(file)
if err != nil {
return nil, model.NewAppError("BulkImport", "app.import.attachment.read_file_data.error", map[string]any{"FilePath": *data.Path}, "", http.StatusBadRequest)
}

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

@@ -6,7 +6,6 @@ package app
import (
"archive/zip"
"context"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -3100,7 +3099,6 @@ func TestImportImportPost(t *testing.T) {
})
t.Run("Reply CreateAt before parent post CreateAt", func(t *testing.T) {
t.Skip("MM-44922")
now := model.GetMillis()
before := now - 10
data := LineImportWorkerData{
@@ -3128,6 +3126,7 @@ func TestImportImportPost(t *testing.T) {
posts, nErr := th.App.Srv().Store.Post().GetPostsCreatedAt(channel.Id, now)
require.NoError(t, nErr)
require.Len(t, posts, 2, "Unexpected number of posts found.")
require.NoError(t, th.TestLogger.Flush())
testlib.AssertLog(t, th.LogBuffer, mlog.LvlWarn.Name, "Reply CreateAt is before parent post CreateAt, setting it to parent post CreateAt")
rootPost := posts[0]
@@ -4378,11 +4377,11 @@ func TestImportDirectPostWithAttachments(t *testing.T) {
testImage := filepath.Join(testsDir, "test.png")
testImage2 := filepath.Join(testsDir, "test.svg")
// create a temp file with same name as original but with a different first byte
tmpFolder, _ := ioutil.TempDir("", "imgFake")
tmpFolder, _ := os.MkdirTemp("", "imgFake")
testImageFake := filepath.Join(tmpFolder, "test.png")
fakeFileData, _ := ioutil.ReadFile(testImage)
fakeFileData, _ := os.ReadFile(testImage)
fakeFileData[0] = 0
_ = ioutil.WriteFile(testImageFake, fakeFileData, 0644)
_ = os.WriteFile(testImageFake, fakeFileData, 0644)
defer os.RemoveAll(tmpFolder)
// Create a user.

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

@@ -6,7 +6,6 @@ package app
import (
"archive/zip"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
@@ -439,7 +438,7 @@ func BenchmarkBulkImport(b *testing.B) {
info, err := importFile.Stat()
require.NoError(b, err)
dir, err := ioutil.TempDir("", "testimport")
dir, err := os.MkdirTemp("", "testimport")
require.NoError(b, err)
defer os.RemoveAll(dir)

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

@@ -332,10 +332,10 @@ func validateUserTeamsImportData(data *[]UserTeamImportData) *model.AppError {
}
}
if tdata.Theme != nil && 0 < len(strings.Trim(*tdata.Theme, " \t\r")) {
if tdata.Theme != nil && strings.Trim(*tdata.Theme, " \t\r") != "" {
var unused map[string]string
if err := json.NewDecoder(strings.NewReader(*tdata.Theme)).Decode(&unused); err != nil {
return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.invalid_team_theme.error", nil, err.Error(), http.StatusBadRequest)
return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.invalid_team_theme.error", nil, "", http.StatusBadRequest).Wrap(err)
}
}
}

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

@@ -23,7 +23,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"path"
@@ -98,9 +98,9 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
var nfErr *store.ErrNotFound
switch {
case errors.As(result.NErr, &nfErr):
return "", model.NewAppError("DoPostActionWithCookie", "app.post.get.app_error", nil, nfErr.Error(), http.StatusNotFound)
return "", model.NewAppError("DoPostActionWithCookie", "app.post.get.app_error", nil, "", http.StatusNotFound).Wrap(nfErr)
default:
return "", model.NewAppError("DoPostActionWithCookie", "app.post.get.app_error", nil, result.NErr.Error(), http.StatusInternalServerError)
return "", model.NewAppError("DoPostActionWithCookie", "app.post.get.app_error", nil, "", http.StatusInternalServerError).Wrap(result.NErr)
}
}
if cookie.Integration == nil {
@@ -116,9 +116,9 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
var nfErr *store.ErrNotFound
switch {
case errors.As(err, &nfErr):
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.existing.app_error", nil, nfErr.Error(), http.StatusNotFound)
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.existing.app_error", nil, "", http.StatusNotFound).Wrap(nfErr)
default:
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.find.app_error", nil, err.Error(), http.StatusInternalServerError)
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get.find.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
@@ -137,7 +137,7 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
post := result.Data.(*model.Post)
result = <-cchan
if result.NErr != nil {
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get_for_post.app_error", nil, result.NErr.Error(), http.StatusInternalServerError)
return "", model.NewAppError("DoPostActionWithCookie", "app.channel.get_for_post.app_error", nil, "", http.StatusInternalServerError).Wrap(result.NErr)
}
channel := result.Data.(*model.Channel)
@@ -195,9 +195,9 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
var nfErr *store.ErrNotFound
switch {
case errors.As(ur.NErr, &nfErr):
return "", model.NewAppError("DoPostActionWithCookie", MissingAccountError, nil, nfErr.Error(), http.StatusNotFound)
return "", model.NewAppError("DoPostActionWithCookie", MissingAccountError, nil, "", http.StatusNotFound).Wrap(nfErr)
default:
return "", model.NewAppError("DoPostActionWithCookie", "app.user.get.app_error", nil, ur.NErr.Error(), http.StatusInternalServerError)
return "", model.NewAppError("DoPostActionWithCookie", "app.user.get.app_error", nil, "", http.StatusInternalServerError).Wrap(ur.NErr)
}
}
user := ur.Data.(*model.User)
@@ -209,9 +209,9 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
var nfErr *store.ErrNotFound
switch {
case errors.As(tr.NErr, &nfErr):
return "", model.NewAppError("DoPostActionWithCookie", "app.team.get.find.app_error", nil, nfErr.Error(), http.StatusNotFound)
return "", model.NewAppError("DoPostActionWithCookie", "app.team.get.find.app_error", nil, "", http.StatusNotFound).Wrap(nfErr)
default:
return "", model.NewAppError("DoPostActionWithCookie", "app.team.get.finding.app_error", nil, tr.NErr.Error(), http.StatusInternalServerError)
return "", model.NewAppError("DoPostActionWithCookie", "app.team.get.finding.app_error", nil, "", http.StatusInternalServerError).Wrap(tr.NErr)
}
}
@@ -234,7 +234,6 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
return "", appErr
}
var resp *http.Response
if strings.HasPrefix(upstreamURL, "/warn_metrics/") {
appErr = a.doLocalWarnMetricsRequest(c, upstreamURL, upstreamRequest)
if appErr != nil {
@@ -242,25 +241,26 @@ func (a *App) DoPostActionWithCookie(c *request.Context, postID, actionId, userI
}
return "", nil
}
requestJSON, jsonErr := json.Marshal(upstreamRequest)
if jsonErr != nil {
return "", model.NewAppError("DoPostActionWithCookie", "api.marshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
requestJSON, err := json.Marshal(upstreamRequest)
if err != nil {
return "", model.NewAppError("DoPostActionWithCookie", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
resp, appErr = a.DoActionRequest(c, upstreamURL, requestJSON)
resp, appErr := a.DoActionRequest(c, upstreamURL, requestJSON)
if appErr != nil {
return "", appErr
}
defer resp.Body.Close()
var response model.PostActionIntegrationResponse
respBytes, err := ioutil.ReadAll(resp.Body)
respBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", model.NewAppError("DoPostActionWithCookie", "api.post.do_action.action_integration.app_error", nil, "err="+err.Error(), http.StatusBadRequest)
return "", model.NewAppError("DoPostActionWithCookie", "api.post.do_action.action_integration.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
if len(respBytes) > 0 {
if err = json.Unmarshal(respBytes, &response); err != nil {
return "", model.NewAppError("DoPostActionWithCookie", "api.post.do_action.action_integration.app_error", nil, "err="+err.Error(), http.StatusBadRequest)
return "", model.NewAppError("DoPostActionWithCookie", "api.post.do_action.action_integration.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
}
@@ -435,7 +435,7 @@ func (ch *Channels) doPluginRequest(c *request.Context, method, rawURL string, v
ProtoMajor: 1,
ProtoMinor: 1,
Header: w.headers,
Body: ioutil.NopCloser(bytes.NewReader(w.data)),
Body: io.NopCloser(bytes.NewReader(w.data)),
}
if resp.StatusCode == 0 {
resp.StatusCode = http.StatusOK
@@ -585,14 +585,17 @@ func (a *App) DoLocalRequest(c *request.Context, rawURL string, body []byte) (*h
}
func (a *App) OpenInteractiveDialog(request model.OpenDialogRequest) *model.AppError {
clientTriggerId, userID, err := request.DecodeAndVerifyTriggerId(a.AsymmetricSigningKey())
if err != nil {
return err
clientTriggerId, userID, appErr := request.DecodeAndVerifyTriggerId(a.AsymmetricSigningKey())
if appErr != nil {
return appErr
}
request.TriggerId = clientTriggerId
jsonRequest, _ := json.Marshal(request)
jsonRequest, err := json.Marshal(request)
if err != nil {
a.ch.srv.GetLogger().Warn("Error encoding request", mlog.Err(err))
}
message := model.NewWebSocketEvent(model.WebsocketEventOpenDialog, "", "", userID, nil)
message.Add("dialog", string(jsonRequest))
@@ -606,23 +609,19 @@ func (a *App) SubmitInteractiveDialog(c *request.Context, request model.SubmitDi
request.URL = ""
request.Type = "dialog_submission"
b, jsonErr := json.Marshal(request)
if jsonErr != nil {
return nil, model.NewAppError("SubmitInteractiveDialog", "app.submit_interactive_dialog.json_error", nil, jsonErr.Error(), http.StatusBadRequest)
}
resp, err := a.DoActionRequest(c, url, b)
b, err := json.Marshal(request)
if err != nil {
return nil, err
return nil, model.NewAppError("SubmitInteractiveDialog", "app.submit_interactive_dialog.json_error", nil, "", http.StatusBadRequest).Wrap(err)
}
resp, appErr := a.DoActionRequest(c, url, b)
if appErr != nil {
return nil, appErr
}
defer resp.Body.Close()
var response model.SubmitDialogResponse
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
// Don't fail, an empty response is acceptable
return &response, nil
}
json.NewDecoder(resp.Body).Decode(&response) // Don't fail, an empty response is acceptable
return &response, nil
}

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

@@ -6,7 +6,7 @@ package app
import (
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"net/url"
@@ -1079,47 +1079,47 @@ func TestDoPluginRequest(t *testing.T) {
resp, err := th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin", nil, nil)
assert.Nil(t, err)
require.NotNil(t, resp)
body, _ := ioutil.ReadAll(resp.Body)
body, _ := io.ReadAll(resp.Body)
assert.Equal(t, "could not find param abc=xyz", string(body))
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin?abc=xyz", nil, nil)
assert.Nil(t, err)
require.NotNil(t, resp)
body, _ = ioutil.ReadAll(resp.Body)
body, _ = io.ReadAll(resp.Body)
assert.Equal(t, "param multiple should have 3 values", string(body))
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin",
url.Values{"abc": []string{"xyz"}, "multiple": []string{"1 first", "2 second", "3 third"}}, nil)
assert.Nil(t, err)
require.NotNil(t, resp)
body, _ = ioutil.ReadAll(resp.Body)
body, _ = io.ReadAll(resp.Body)
assert.Equal(t, "OK", string(body))
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin?abc=xyz&multiple=1%20first",
url.Values{"multiple": []string{"2 second", "3 third"}}, nil)
assert.Nil(t, err)
require.NotNil(t, resp)
body, _ = ioutil.ReadAll(resp.Body)
body, _ = io.ReadAll(resp.Body)
assert.Equal(t, "OK", string(body))
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin?abc=xyz&multiple=1%20first&multiple=3%20third",
url.Values{"multiple": []string{"2 second"}}, nil)
assert.Nil(t, err)
require.NotNil(t, resp)
body, _ = ioutil.ReadAll(resp.Body)
body, _ = io.ReadAll(resp.Body)
assert.Equal(t, "OK", string(body))
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin?multiple=1%20first&multiple=3%20third",
url.Values{"multiple": []string{"2 second"}, "abc": []string{"xyz"}}, nil)
assert.Nil(t, err)
require.NotNil(t, resp)
body, _ = ioutil.ReadAll(resp.Body)
body, _ = io.ReadAll(resp.Body)
assert.Equal(t, "OK", string(body))
resp, err = th.App.doPluginRequest(th.Context, "GET", "/plugins/myplugin?multiple=1%20first&multiple=3%20third",
url.Values{"multiple": []string{"4 fourth"}, "abc": []string{"xyz"}}, nil)
assert.Nil(t, err)
require.NotNil(t, resp)
body, _ = ioutil.ReadAll(resp.Body)
body, _ = io.ReadAll(resp.Body)
assert.Equal(t, "param multiple not correct", string(body))
}

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

@@ -10,7 +10,7 @@ import (
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"io"
"log"
"os"
"path"
@@ -58,7 +58,7 @@ func main() {
log.Fatal(err)
}
err = ioutil.WriteFile(outputFile, formattedCode, 0644)
err = os.WriteFile(outputFile, formattedCode, 0644)
if err != nil {
log.Fatal(err)
}
@@ -162,7 +162,7 @@ func extractStoreMetadata() (*storeMetadata, error) {
if err != nil {
return nil, fmt.Errorf("unable to open %s file: %w", inputFile, err)
}
src, err := ioutil.ReadAll(file)
src, err := io.ReadAll(file)
if err != nil {
return nil, err
}

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

@@ -4,7 +4,7 @@
package app
import (
"io/ioutil"
"io"
"mime/multipart"
"net/http"
@@ -186,12 +186,12 @@ func (a *App) writeLdapFile(filename string, fileData *multipart.FileHeader) *mo
}
defer file.Close()
data, err := ioutil.ReadAll(file)
data, err := io.ReadAll(file)
if err != nil {
return model.NewAppError("AddLdapCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
}
err = a.Srv().configStore.SetFile(filename, data)
err = a.Srv().platform.SetConfigFile(filename, data)
if err != nil {
return model.NewAppError("AddLdapCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -234,7 +234,7 @@ func (a *App) AddLdapPrivateCertificate(fileData *multipart.FileHeader) *model.A
}
func (a *App) removeLdapFile(filename string) *model.AppError {
if err := a.Srv().configStore.RemoveFile(filename); err != nil {
if err := a.Srv().platform.RemoveConfigFile(filename); err != nil {
return model.NewAppError("RemoveLdapFile", "api.admin.remove_certificate.delete.app_error", map[string]any{"Filename": filename}, err.Error(), http.StatusInternalServerError)
}
return nil

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

@@ -48,7 +48,7 @@ func (w *licenseWrapper) GetLicense() *model.License {
}
func (w *licenseWrapper) RequestTrialLicense(requesterID string, users int, termsAccepted bool, receiveEmailsAccepted bool) *model.AppError {
if *w.srv.Config().ExperimentalSettings.RestrictSystemAdmin {
if *w.srv.platform.Config().ExperimentalSettings.RestrictSystemAdmin {
return model.NewAppError("RequestTrialLicense", "api.restricted_system_admin", nil, "", http.StatusForbidden)
}
@@ -75,8 +75,8 @@ func (w *licenseWrapper) RequestTrialLicense(requesterID string, users int, term
ServerID: w.srv.TelemetryId(),
Name: requester.GetDisplayName(model.ShowFullName),
Email: requester.Email,
SiteName: *w.srv.Config().TeamSettings.SiteName,
SiteURL: *w.srv.Config().ServiceSettings.SiteURL,
SiteName: *w.srv.platform.Config().TeamSettings.SiteName,
SiteURL: *w.srv.platform.Config().ServiceSettings.SiteURL,
Users: users,
TermsAccepted: termsAccepted,
ReceiveEmailsAccepted: receiveEmailsAccepted,
@@ -93,6 +93,11 @@ type JWTClaims struct {
jwt.StandardClaims
}
func (s *Server) License() *model.License {
license, _ := s.licenseValue.Load().(*model.License)
return license
}
func (s *Server) LoadLicense() {
// ENV var overrides all other sources of license.
licenseStr := os.Getenv(LicenseEnv)
@@ -131,7 +136,7 @@ func (s *Server) LoadLicense() {
if !model.IsValidId(licenseId) {
// Lets attempt to load the file from disk since it was missing from the DB
license, licenseBytes := utils.GetAndValidateLicenseFileFromDisk(*s.Config().ServiceSettings.LicenseFileLocation)
license, licenseBytes := utils.GetAndValidateLicenseFileFromDisk(*s.platform.Config().ServiceSettings.LicenseFileLocation)
if license != nil {
if _, err := s.SaveLicense(licenseBytes); err != nil {
@@ -177,13 +182,13 @@ func (s *Server) SaveLicense(licenseBytes []byte) (*model.License, *model.AppErr
return nil, model.NewAppError("addLicense", model.ExpiredLicenseError, nil, "", http.StatusBadRequest)
}
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunJobs && s.Jobs != nil {
if err := s.Jobs.StopWorkers(); err != nil && !errors.Is(err, jobs.ErrWorkersNotRunning) {
mlog.Warn("Stopping job server workers failed", mlog.Err(err))
}
}
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunScheduler && s.Jobs != nil {
if err := s.Jobs.StopSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersNotRunning) {
mlog.Error("Stopping job server schedulers failed", mlog.Err(err))
}
@@ -193,12 +198,12 @@ func (s *Server) SaveLicense(licenseBytes []byte) (*model.License, *model.AppErr
// restart job server workers - this handles the edge case where a license file is uploaded, but the job server
// doesn't start until the server is restarted, which prevents the 'run job now' buttons in system console from
// functioning as expected
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunJobs && s.Jobs != nil {
if err := s.Jobs.StartWorkers(); err != nil {
mlog.Error("Starting job server workers failed", mlog.Err(err))
}
}
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunScheduler && s.Jobs != nil {
if err := s.Jobs.StartSchedulers(); err != nil && !errors.Is(err, jobs.ErrSchedulersRunning) {
mlog.Error("Starting job server schedulers failed", mlog.Err(err))
}
@@ -233,7 +238,7 @@ func (s *Server) SaveLicense(licenseBytes []byte) (*model.License, *model.AppErr
return nil, model.NewAppError("addLicense", "api.license.add_license.save_active.app_error", nil, "", http.StatusInternalServerError)
}
s.ReloadConfig()
s.platform.ReloadConfig()
s.InvalidateAllCaches()
return &license, nil
@@ -256,12 +261,20 @@ func (s *Server) SetLicense(license *model.License) bool {
license.Features.SetDefaults()
s.licenseValue.Store(license)
if s.platform != nil {
s.platform.SetLicense(license)
}
s.clientLicenseValue.Store(utils.GetClientLicense(license))
return true
}
s.licenseValue.Store((*model.License)(nil))
s.clientLicenseValue.Store(map[string]string(nil))
if s.platform != nil {
s.platform.SetLicense((*model.License)(nil))
}
return false
}
@@ -307,7 +320,7 @@ func (s *Server) RemoveLicense() *model.AppError {
}
s.SetLicense(nil)
s.ReloadConfig()
s.platform.ReloadConfig()
s.InvalidateAllCaches()
return nil
@@ -329,14 +342,14 @@ func (s *Server) GetSanitizedClientLicense() map[string]string {
// RequestTrialLicense request a trial license from the mattermost official license server
func (s *Server) RequestTrialLicense(trialRequest *model.TrialLicenseRequest) *model.AppError {
trialRequestJSON, jsonErr := json.Marshal(trialRequest)
if jsonErr != nil {
return model.NewAppError("RequestTrialLicense", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
trialRequestJSON, err := json.Marshal(trialRequest)
if err != nil {
return model.NewAppError("RequestTrialLicense", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
resp, err := http.Post(RequestTrialURL, "application/json", bytes.NewBuffer(trialRequestJSON))
if err != nil {
return model.NewAppError("RequestTrialLicense", "api.license.request_trial_license.app_error", nil, err.Error(), http.StatusBadRequest)
return model.NewAppError("RequestTrialLicense", "api.license.request_trial_license.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
defer resp.Body.Close()
@@ -350,7 +363,11 @@ func (s *Server) RequestTrialLicense(trialRequest *model.TrialLicenseRequest) *m
fmt.Sprintf("Unexpected HTTP status code %q returned by server", resp.Status), http.StatusInternalServerError)
}
licenseResponse := model.MapFromJSON(resp.Body)
var licenseResponse map[string]string
err = json.NewDecoder(resp.Body).Decode(&licenseResponse)
if err != nil {
s.GetLogger().Warn("Error decoding license response", mlog.Err(err))
}
if _, ok := licenseResponse["license"]; !ok {
return model.NewAppError("RequestTrialLicense", "api.license.request_trial_license.app_error", nil, licenseResponse["message"], http.StatusBadRequest)
@@ -360,7 +377,7 @@ func (s *Server) RequestTrialLicense(trialRequest *model.TrialLicenseRequest) *m
return err
}
s.ReloadConfig()
s.platform.ReloadConfig()
s.InvalidateAllCaches()
return nil

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

@@ -69,9 +69,9 @@ func (s *Server) doAdvancedPermissionsMigration() {
return
}
config := s.Config()
config := s.platform.Config()
*config.ServiceSettings.PostEditTimeLimit = -1
if _, _, err := s.SaveConfig(config, true); err != nil {
if _, _, err := s.platform.SaveConfig(config, true); err != nil {
mlog.Error("Failed to update config in Advanced Permissions Phase 1 Migration.", mlog.Err(err))
}
@@ -327,7 +327,7 @@ func (s *Server) doContentExtractionConfigDefaultTrueMigration() {
return
}
s.UpdateConfig(func(config *model.Config) {
s.platform.UpdateConfig(func(config *model.Config) {
config.FileSettings.ExtractContent = model.NewBool(true)
})
@@ -474,7 +474,7 @@ const existingInstallationPostsThreshold = 10
func (s *Server) doFirstAdminSetupCompleteMigration() {
// Don't run the migration until the flag is turned on.
if !s.Config().FeatureFlags.UseCaseOnboarding {
if !s.platform.Config().FeatureFlags.UseCaseOnboarding {
return
}

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

@@ -6,14 +6,14 @@ package app
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"runtime"
"strings"
"sync"
"github.com/pkg/errors"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/i18n"
@@ -290,7 +290,7 @@ func (a *App) UpdateMobileAppBadge(userID string) {
}
func (s *Server) createPushNotificationsHub(c request.CTX) {
buffer := *s.Config().EmailSettings.PushNotificationBuffer
buffer := *s.platform.Config().EmailSettings.PushNotificationBuffer
hub := PushNotificationsHub{
notificationsChan: make(chan PushNotification, buffer),
app: New(ServerConnector(s.Channels())),
@@ -382,9 +382,9 @@ func (s *Server) StopPushNotificationsHubWorkers() {
}
func (a *App) rawSendToPushProxy(msg *model.PushNotification) (model.PushResponse, error) {
msgJSON, jsonErr := json.Marshal(msg)
if jsonErr != nil {
return nil, errors.Wrap(jsonErr, "failed to encode to JSON")
msgJSON, err := json.Marshal(msg)
if err != nil {
return nil, fmt.Errorf("failed to encode to JSON: %w", err)
}
url := strings.TrimRight(*a.Config().EmailSettings.PushNotificationServer, "/") + model.APIURLSuffixV1 + "/send_push"
@@ -400,8 +400,8 @@ func (a *App) rawSendToPushProxy(msg *model.PushNotification) (model.PushRespons
defer resp.Body.Close()
var pushResponse model.PushResponse
if jsonErr := json.NewDecoder(resp.Body).Decode(&pushResponse); jsonErr != nil {
return nil, errors.Wrap(jsonErr, "failed to decode from JSON")
if err := json.NewDecoder(resp.Body).Decode(&pushResponse); err != nil {
return nil, fmt.Errorf("failed to decode from JSON: %w", err)
}
return pushResponse, nil
@@ -427,7 +427,7 @@ func (a *App) sendToPushProxy(msg *model.PushNotification, session *model.Sessio
case model.PushStatusRemove:
a.AttachDeviceId(session.Id, "", session.ExpiresAt)
a.ClearSessionCacheForUser(session.UserId)
return errors.New("Device was reported as removed")
return errors.New("device was reported as removed")
case model.PushStatusFail:
return errors.New(pushResponse[model.PushStatusErrorMsg])
}
@@ -447,9 +447,9 @@ func (a *App) SendAckToPushProxy(ack *model.PushNotificationAck) error {
mlog.String("status", model.PushReceived),
)
ackJSON, jsonErr := json.Marshal(ack)
if jsonErr != nil {
return errors.Wrap(jsonErr, "failed to encode to JSON")
ackJSON, err := json.Marshal(ack)
if err != nil {
return fmt.Errorf("failed to encode to JSON: %w", err)
}
request, err := http.NewRequest(
@@ -457,7 +457,6 @@ func (a *App) SendAckToPushProxy(ack *model.PushNotificationAck) error {
strings.TrimRight(*a.Config().EmailSettings.PushNotificationServer, "/")+model.APIURLSuffixV1+"/ack",
bytes.NewReader(ackJSON),
)
if err != nil {
return err
}
@@ -467,19 +466,16 @@ func (a *App) SendAckToPushProxy(ack *model.PushNotificationAck) error {
return err
}
defer resp.Body.Close()
// Reading the body to completion.
_, err = io.Copy(io.Discard, resp.Body)
if err != nil {
return err
}
return nil
return err
}
func (a *App) getMobileAppSessions(userID string) ([]*model.Session, *model.AppError) {
sessions, err := a.Srv().Store.Session().GetSessionsWithActiveDeviceIds(userID)
if err != nil {
return nil, model.NewAppError("getMobileAppSessions", "app.session.get_sessions.app_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("getMobileAppSessions", "app.session.get_sessions.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return sessions, nil
@@ -572,7 +568,7 @@ func (a *App) BuildPushNotificationMessage(c request.CTX, contentsConfig string,
unreadCount, err := a.Srv().Store.User().GetUnreadCount(user.Id)
if err != nil {
return nil, model.NewAppError("BuildPushNotificationMessage", "app.user.get_unread_count.app_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("BuildPushNotificationMessage", "app.user.get_unread_count.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
msg.Badge = int(unreadCount)

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

@@ -17,6 +17,7 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
fmocks "github.com/mattermost/mattermost-server/v6/shared/filestore/mocks"
@@ -1443,9 +1444,13 @@ func TestPushNotificationRace(t *testing.T) {
Router: mux.NewRouter(),
filestore: &fmocks.FileBackend{},
}
s.configStore = &configWrapper{srv: s, Store: memoryStore}
var err error
s.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: memoryStore,
})
require.NoError(t, err)
serviceMap := map[ServiceKey]any{
ConfigKey: s.configStore,
ConfigKey: s.platform,
LicenseKey: &licenseWrapper{s},
FilestoreKey: s.filestore,
}

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

@@ -11,7 +11,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
@@ -853,7 +852,7 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service
var ar *model.AccessResponse
err = json.NewDecoder(tee).Decode(&ar)
if err != nil || resp.StatusCode != http.StatusOK {
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, fmt.Sprintf("response_body=%s, status_code=%d, error=%v", buf.String(), resp.StatusCode, err), http.StatusInternalServerError)
return nil, "", stateProps, nil, model.NewAppError("AuthorizeOAuthUser", "api.user.authorize_oauth_user.bad_response.app_error", nil, fmt.Sprintf("response_body=%s, status_code=%d, error=%v", buf.String(), resp.StatusCode, err), http.StatusInternalServerError).Wrap(err)
}
if strings.ToLower(ar.TokenType) != model.AccessTokenType {
@@ -891,7 +890,7 @@ func (a *App) AuthorizeOAuthUser(w http.ResponseWriter, r *http.Request, service
defer resp.Body.Close()
// Ignore the error below because the resulting string will just be the empty string if bodyBytes is nil
bodyBytes, _ := ioutil.ReadAll(resp.Body)
bodyBytes, _ := io.ReadAll(resp.Body)
bodyString := string(bodyBytes)
mlog.Error("Error getting OAuth user", mlog.Int("response", resp.StatusCode), mlog.String("body_string", bodyString))

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

@@ -7,7 +7,7 @@ import (
"encoding/base64"
"encoding/json"
"errors"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"testing"
@@ -517,7 +517,7 @@ func TestAuthorizeOAuthUser(t *testing.T) {
body, receivedTeamId, receivedStateProps, _, err := th.App.AuthorizeOAuthUser(&recorder, request, model.ServiceGitlab, "", state, "")
require.NotNil(t, body)
bodyBytes, bodyErr := ioutil.ReadAll(body)
bodyBytes, bodyErr := io.ReadAll(body)
require.NoError(t, bodyErr)
assert.Equal(t, userData, string(bodyBytes))

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

@@ -6,6 +6,7 @@ package app
import (
"github.com/pkg/errors"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/model"
@@ -52,7 +53,22 @@ func Config(dsn string, readOnly bool, configDefaults *model.Config) Option {
return errors.Wrap(err, "failed to apply Config option")
}
s.configStore = &configWrapper{srv: s, Store: configStore}
platformCfg := platform.ServiceConfig{
ConfigStore: configStore,
Logger: s.Log,
StartMetrics: s.startMetrics,
Cluster: s.Cluster,
}
if metricsInterface != nil {
platformCfg.Metrics = metricsInterface(s, *configStore.Get().SqlSettings.DriverName, *configStore.Get().SqlSettings.DataSource)
}
ps, sErr := platform.New(platformCfg)
if sErr != nil {
return errors.Wrap(sErr, "failed to initialize platform")
}
s.platform = ps
return nil
}
}
@@ -60,7 +76,21 @@ func Config(dsn string, readOnly bool, configDefaults *model.Config) Option {
// ConfigStore applies the given config store, typically to replace the traditional sources with a memory store for testing.
func ConfigStore(configStore *config.Store) Option {
return func(s *Server) error {
s.configStore = &configWrapper{srv: s, Store: configStore}
platformCfg := platform.ServiceConfig{
ConfigStore: configStore,
Logger: s.Log,
StartMetrics: s.startMetrics,
Cluster: s.Cluster,
}
if metricsInterface != nil {
platformCfg.Metrics = metricsInterface(s, *configStore.Get().SqlSettings.DriverName, *configStore.Get().SqlSettings.DataSource)
}
ps, sErr := platform.New(platformCfg)
if sErr != nil {
return errors.Wrap(sErr, "failed to initialize platform")
}
s.platform = ps
return nil
}

12
app/platform/cluster.go Обычный файл
Просмотреть файл

@@ -0,0 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
func (ps *PlatformService) IsLeader() bool {
if ps.License() != nil && *ps.Config().ClusterSettings.Enable && ps.cluster != nil {
return ps.cluster.IsLeader()
}
return true
}

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

@@ -5,9 +5,14 @@ package platform
import (
"errors"
"fmt"
"net/http"
"reflect"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/product"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
@@ -30,7 +35,145 @@ func (c *ServiceConfig) validate() error {
}
if c.Logger == nil {
return errors.New("Logger is required")
var err error
// If Logger is not set, use a default logger temporarily.
// this should be removed once the logger is properly configured with the service config.
// MM-45841
c.Logger, err = mlog.NewLogger()
if err != nil {
return err
}
}
return nil
}
// ensure the config wrapper implements `product.ConfigService`
var _ product.ConfigService = (*PlatformService)(nil)
func (ps *PlatformService) Config() *model.Config {
return ps.configStore.Get()
}
// Registers a function with a given listener to be called when the config is reloaded and may have changed. The function
// will be called with two arguments: the old config and the new config. AddConfigListener returns a unique ID
// for the listener that can later be used to remove it.
func (ps *PlatformService) AddConfigListener(listener func(*model.Config, *model.Config)) string {
return ps.configStore.AddListener(listener)
}
func (ps *PlatformService) RemoveConfigListener(id string) {
ps.configStore.RemoveListener(id)
}
func (ps *PlatformService) UpdateConfig(f func(*model.Config)) {
if ps.configStore.IsReadOnly() {
return
}
old := ps.Config()
updated := old.Clone()
f(updated)
if _, _, err := ps.configStore.Set(updated); err != nil {
ps.logger.Error("Failed to update config", mlog.Err(err))
}
}
// SaveConfig replaces the active configuration, optionally notifying cluster peers.
// It returns both the previous and current configs.
func (ps *PlatformService) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
oldCfg, newCfg, err := ps.configStore.Set(newCfg)
if errors.Is(err, config.ErrReadOnlyConfiguration) {
return nil, nil, model.NewAppError("saveConfig", "ent.cluster.save_config.error", nil, err.Error(), http.StatusForbidden)
} else if err != nil {
return nil, nil, model.NewAppError("saveConfig", "app.save_config.app_error", nil, err.Error(), http.StatusInternalServerError)
}
if ps.serviceConfig.StartMetrics && *ps.Config().MetricsSettings.Enable {
ps.RestartMetrics()
} else {
ps.ShutdownMetrics()
}
if ps.cluster != nil {
err := ps.cluster.ConfigChanged(ps.configStore.RemoveEnvironmentOverrides(oldCfg),
ps.configStore.RemoveEnvironmentOverrides(newCfg), sendConfigChangeClusterMessage)
if err != nil {
return nil, nil, err
}
}
return oldCfg, newCfg, nil
}
func (ps *PlatformService) ReloadConfig() error {
if err := ps.configStore.Load(); err != nil {
return err
}
return nil
}
func (ps *PlatformService) GetEnvironmentOverridesWithFilter(filter func(reflect.StructField) bool) map[string]interface{} {
return ps.configStore.GetEnvironmentOverridesWithFilter(filter)
}
func (ps *PlatformService) GetEnvironmentOverrides() map[string]interface{} {
return ps.configStore.GetEnvironmentOverrides()
}
func (ps *PlatformService) DescribeConfig() string {
return ps.configStore.String()
}
func (ps *PlatformService) CleanUpConfig() error {
return ps.configStore.CleanUp()
}
// ConfigureLogger applies the specified configuration to a logger.
func (ps *PlatformService) ConfigureLogger(name string, logger *mlog.Logger, logSettings *model.LogSettings, getPath func(string) string) error {
// Advanced logging is E20 only, however logging must be initialized before the license
// file is loaded. If no valid E20 license exists then advanced logging will be
// shutdown once license is loaded/checked.
var err error
dsn := *logSettings.AdvancedLoggingConfig
var logConfigSrc config.LogConfigSrc
if dsn != "" {
logConfigSrc, err = config.NewLogConfigSrc(dsn, ps.configStore)
if err != nil {
return fmt.Errorf("invalid config source for %s, %w", name, err)
}
ps.logger.Info("Loaded configuration for "+name, mlog.String("source", dsn))
}
cfg, err := config.MloggerConfigFromLoggerConfig(logSettings, logConfigSrc, getPath)
if err != nil {
return fmt.Errorf("invalid config source for %s, %w", name, err)
}
if err := logger.ConfigureTargets(cfg, nil); err != nil {
return fmt.Errorf("invalid config for %s, %w", name, err)
}
return nil
}
func (ps *PlatformService) GetConfigStore() *config.Store {
return ps.configStore
}
func (ps *PlatformService) GetConfigFile(name string) ([]byte, error) {
return ps.configStore.GetFile(name)
}
func (ps *PlatformService) SetConfigFile(name string, data []byte) error {
return ps.configStore.SetFile(name, data)
}
func (ps *PlatformService) RemoveConfigFile(name string) error {
return ps.configStore.RemoveFile(name)
}
func (ps *PlatformService) HasConfigFile(name string) (bool, error) {
return ps.configStore.HasFile(name)
}
func (ps *PlatformService) SetConfigReadOnlyFF(readOnly bool) {
ps.configStore.SetReadOnlyFF(readOnly)
}

47
app/platform/config_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,47 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/mattermost/mattermost-server/v6/model"
)
func TestConfigListener(t *testing.T) {
th := Setup(t)
defer th.TearDown()
originalSiteName := th.Service.Config().TeamSettings.SiteName
listenerCalled := false
listener := func(oldConfig *model.Config, newConfig *model.Config) {
assert.False(t, listenerCalled, "listener called twice")
assert.Equal(t, *originalSiteName, *oldConfig.TeamSettings.SiteName, "old config contains incorrect site name")
assert.Equal(t, "test123", *newConfig.TeamSettings.SiteName, "new config contains incorrect site name")
listenerCalled = true
}
listenerId := th.Service.AddConfigListener(listener)
defer th.Service.RemoveConfigListener(listenerId)
listener2Called := false
listener2 := func(oldConfig *model.Config, newConfig *model.Config) {
assert.False(t, listener2Called, "listener2 called twice")
listener2Called = true
}
listener2Id := th.Service.AddConfigListener(listener2)
defer th.Service.RemoveConfigListener(listener2Id)
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.SiteName = "test123"
})
assert.True(t, listenerCalled, "listener should've been called")
assert.True(t, listener2Called, "listener 2 should've been called")
}

118
app/platform/feature_flags.go Обычный файл
Просмотреть файл

@@ -0,0 +1,118 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"encoding/json"
"os"
"time"
"github.com/mattermost/mattermost-server/v6/app/featureflag"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)
// SetupFeatureFlags called on startup and when the cluster leader changes.
// Starts or stops the synchronization of feature flags from upstream management.
func (ps *PlatformService) SetupFeatureFlags() {
ps.featureFlagSynchronizerMutex.Lock()
defer ps.featureFlagSynchronizerMutex.Unlock()
splitKey := *ps.Config().ServiceSettings.SplitKey
splitConfigured := splitKey != ""
syncFeatureFlags := splitConfigured && ps.IsLeader()
ps.configStore.SetReadOnlyFF(!splitConfigured)
if syncFeatureFlags {
if err := ps.startFeatureFlagUpdateJob(); err != nil {
ps.logger.Warn("Unable to setup synchronization with feature flag management. Will fallback to cache.", mlog.Err(err))
}
} else {
ps.StopFeatureFlagUpdateJob()
}
if err := ps.configStore.Load(); err != nil {
ps.logger.Warn("Unable to load config store after feature flag setup.", mlog.Err(err))
}
}
func (ps *PlatformService) updateFeatureFlagValuesFromManagement() {
newCfg := ps.configStore.GetNoEnv().Clone()
oldFlags := *newCfg.FeatureFlags
newFlags := ps.featureFlagSynchronizer.UpdateFeatureFlagValues(oldFlags)
oldFlagsBytes, _ := json.Marshal(oldFlags)
newFlagsBytes, _ := json.Marshal(newFlags)
ps.logger.Debug("Checking feature flags from management service", mlog.String("old_flags", string(oldFlagsBytes)), mlog.String("new_flags", string(newFlagsBytes)))
if oldFlags != newFlags {
ps.logger.Debug("Feature flag change detected, updating config")
*newCfg.FeatureFlags = newFlags
ps.SaveConfig(newCfg, true)
}
}
func (ps *PlatformService) startFeatureFlagUpdateJob() error {
// Can be run multiple times
if ps.featureFlagSynchronizer != nil {
return nil
}
var log *mlog.Logger
if *ps.Config().ServiceSettings.DebugSplit {
log = ps.logger
}
attributes := map[string]any{}
// if we are part of a cloud installation, add its installation and group id
if installationId := os.Getenv("MM_CLOUD_INSTALLATION_ID"); installationId != "" {
attributes["installation_id"] = installationId
}
if groupId := os.Getenv("MM_CLOUD_GROUP_ID"); groupId != "" {
attributes["group_id"] = groupId
}
synchronizer, err := featureflag.NewSynchronizer(featureflag.SyncParams{
ServerID: ps.telemetryId,
SplitKey: *ps.Config().ServiceSettings.SplitKey,
Log: log,
Attributes: attributes,
})
if err != nil {
return err
}
ps.featureFlagStop = make(chan struct{})
ps.featureFlagStopped = make(chan struct{})
ps.featureFlagSynchronizer = synchronizer
syncInterval := *ps.Config().ServiceSettings.FeatureFlagSyncIntervalSeconds
go func() {
ticker := time.NewTicker(time.Duration(syncInterval) * time.Second)
defer ticker.Stop()
defer close(ps.featureFlagStopped)
if err := synchronizer.EnsureReady(); err != nil {
ps.logger.Warn("Problem connecting to feature flag management. Will fallback to cloud cache.", mlog.Err(err))
return
}
ps.updateFeatureFlagValuesFromManagement()
for {
select {
case <-ps.featureFlagStop:
return
case <-ticker.C:
ps.updateFeatureFlagValuesFromManagement()
}
}
}()
return nil
}
func (ps *PlatformService) StopFeatureFlagUpdateJob() {
if ps.featureFlagSynchronizer != nil {
close(ps.featureFlagStop)
<-ps.featureFlagStopped
ps.featureFlagSynchronizer.Close()
ps.featureFlagSynchronizer = nil
}
}

87
app/platform/helper_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,87 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"io/ioutil"
"path/filepath"
"testing"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/store"
)
type TestHelper struct {
Service *PlatformService
}
func Setup(tb testing.TB) *TestHelper {
if testing.Short() {
tb.SkipNow()
}
dbStore := mainHelper.GetStore()
dbStore.DropAllTables()
dbStore.MarkSystemRanUnitTests()
mainHelper.PreloadMigrations()
return setupTestHelper(dbStore, false, true, tb)
}
func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer bool, tb testing.TB) *TestHelper {
tempWorkspace, err := ioutil.TempDir("", "apptest")
if err != nil {
panic(err)
}
configStore := config.NewTestMemoryStore()
memoryConfig := configStore.Get()
*memoryConfig.PluginSettings.Directory = filepath.Join(tempWorkspace, "plugins")
*memoryConfig.PluginSettings.ClientDirectory = filepath.Join(tempWorkspace, "webapp")
*memoryConfig.PluginSettings.AutomaticPrepackagedPlugins = false
*memoryConfig.LogSettings.EnableSentry = false // disable error reporting during tests
*memoryConfig.AnnouncementSettings.AdminNoticesEnabled = false
*memoryConfig.AnnouncementSettings.UserNoticesEnabled = false
configStore.Set(memoryConfig)
ps, err := New(ServiceConfig{
ConfigStore: configStore,
})
if err != nil {
panic(err)
}
th := &TestHelper{
Service: ps,
}
// Share same configuration with app.TestHelper
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.MaxUsersPerTeam = 50
*cfg.RateLimitSettings.Enable = false
*cfg.TeamSettings.EnableOpenServer = true
})
// Disable strict password requirements for test
th.Service.UpdateConfig(func(cfg *model.Config) {
*cfg.PasswordSettings.MinimumLength = 5
*cfg.PasswordSettings.Lowercase = false
*cfg.PasswordSettings.Uppercase = false
*cfg.PasswordSettings.Symbol = false
*cfg.PasswordSettings.Number = false
})
if enterprise {
th.Service.SetLicense(model.NewTestLicense())
} else {
th.Service.SetLicense(nil)
}
return th
}
func (th *TestHelper) TearDown() {
// Add cleaning code here
}

32
app/platform/main_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,32 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"flag"
"testing"
"github.com/mattermost/mattermost-server/v6/testlib"
)
var mainHelper *testlib.MainHelper
var replicaFlag bool
func TestMain(m *testing.M) {
if f := flag.Lookup("mysql-replica"); f == nil {
flag.BoolVar(&replicaFlag, "mysql-replica", false, "")
flag.Parse()
}
var options = testlib.HelperOptions{
EnableStore: true,
EnableResources: true,
WithReadReplica: replicaFlag,
}
mainHelper = testlib.NewMainHelperWithOptions(&options)
defer mainHelper.Close()
mainHelper.Main(m)
}

19
app/platform/server_license.go Обычный файл
Просмотреть файл

@@ -0,0 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"github.com/mattermost/mattermost-server/v6/model"
)
// License returns the license stored in the server struct.
// This should be removed with MM-45839
func (ps *PlatformService) License() *model.License {
license, _ := ps.licenseValue.Load().(*model.License)
return license
}
func (ps *PlatformService) SetLicense(license *model.License) {
ps.licenseValue.Store(license)
}

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

@@ -4,6 +4,11 @@
package platform
import (
"fmt"
"sync"
"sync/atomic"
"github.com/mattermost/mattermost-server/v6/app/featureflag"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
@@ -19,6 +24,14 @@ type PlatformService struct {
metrics *platformMetrics
featureFlagSynchronizerMutex sync.Mutex
featureFlagSynchronizer *featureflag.Synchronizer
featureFlagStop chan struct{}
featureFlagStopped chan struct{}
licenseValue atomic.Value
telemetryId string
cluster einterfaces.ClusterInterface
}
@@ -49,3 +62,18 @@ func (ps *PlatformService) ShutdownMetrics() error {
return nil
}
func (ps *PlatformService) ShutdownConfig() error {
if ps.configStore != nil {
err := ps.configStore.Close()
if err != nil {
return fmt.Errorf("failed to close config store: %w", err)
}
}
return nil
}
func (ps *PlatformService) SetTelemetryId(id string) {
ps.telemetryId = id
}

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

@@ -7,7 +7,6 @@ import (
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
@@ -39,7 +38,7 @@ type pluginSignaturePath struct {
signaturePath string
}
//Ensure routerService implements `product.RouterService`
// Ensure routerService implements `product.RouterService`
var _ product.RouterService = (*routerService)(nil)
type routerService struct {
@@ -976,7 +975,7 @@ func (ch *Channels) processPrepackagedPlugin(pluginPath *pluginSignaturePath) (*
}
defer fileReader.Close()
tmpDir, err := ioutil.TempDir("", "plugintmp")
tmpDir, err := os.MkdirTemp("", "plugintmp")
if err != nil {
return nil, errors.Wrap(err, "Failed to create temp dir plugintmp")
}
@@ -1086,7 +1085,7 @@ func getPrepackagedPlugin(pluginPath *pluginSignaturePath, pluginFile io.ReadSee
if sigErr != nil {
return nil, "", errors.Wrapf(sigErr, "Failed to open prepackaged plugin signature %s", sig)
}
bytes, sigErr := ioutil.ReadAll(sigReader)
bytes, sigErr := io.ReadAll(sigReader)
if sigErr != nil {
return nil, "", errors.Wrapf(sigErr, "Failed to read prepackaged plugin signature %s", sig)
}
@@ -1105,7 +1104,7 @@ func getPrepackagedPlugin(pluginPath *pluginSignaturePath, pluginFile io.ReadSee
}
func getIcon(iconPath string) (string, error) {
icon, err := ioutil.ReadFile(iconPath)
icon, err := os.ReadFile(iconPath)
if err != nil {
return "", errors.Wrapf(err, "failed to open icon at path %s", iconPath)
}

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

@@ -8,7 +8,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"path/filepath"
@@ -897,7 +896,7 @@ func (api *PluginAPI) InstallPlugin(file io.Reader, replace bool) (*model.Manife
return nil, model.NewAppError("installPlugin", "app.plugin.upload_disabled.app_error", nil, "", http.StatusNotImplemented)
}
fileBuffer, err := ioutil.ReadAll(file)
fileBuffer, err := io.ReadAll(file)
if err != nil {
return nil, model.NewAppError("InstallPlugin", "api.plugin.upload.file.app_error", nil, "", http.StatusBadRequest)
}
@@ -1029,7 +1028,7 @@ func (api *PluginAPI) PluginHTTP(request *http.Request) *http.Response {
if len(split) != 3 {
return &http.Response{
StatusCode: http.StatusBadRequest,
Body: ioutil.NopCloser(bytes.NewBufferString("Not enough URL. Form of URL should be /<pluginid>/*")),
Body: io.NopCloser(bytes.NewBufferString("Not enough URL. Form of URL should be /<pluginid>/*")),
}
}
destinationPluginId := split[1]
@@ -1043,7 +1042,7 @@ func (api *PluginAPI) PluginHTTP(request *http.Request) *http.Response {
}
return &http.Response{
StatusCode: http.StatusBadRequest,
Body: ioutil.NopCloser(bytes.NewBufferString(message)),
Body: io.NopCloser(bytes.NewBufferString(message)),
}
}
responseTransfer := &PluginResponseWriter{}

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

@@ -11,7 +11,7 @@ import (
"image"
"image/color"
"image/png"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"os"
@@ -70,7 +70,7 @@ func setDefaultPluginConfig(th *TestHelper, pluginID string) {
}
func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests []string, pluginIDs []string, asMain bool, app *App, c *request.Context) string {
pluginDir, err := ioutil.TempDir("", "")
pluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
t.Cleanup(func() {
err = os.RemoveAll(pluginDir)
@@ -79,7 +79,7 @@ func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests
}
})
webappPluginDir, err := ioutil.TempDir("", "")
webappPluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
t.Cleanup(func() {
err = os.RemoveAll(webappPluginDir)
@@ -106,7 +106,7 @@ func setupMultiPluginAPITest(t *testing.T, pluginCodes []string, pluginManifests
utils.CompileGoTest(t, pluginCodes[i], backend)
}
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifests[i]), 0600)
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifests[i]), 0600)
manifest, activated, reterr := env.Activate(pluginID)
require.NoError(t, reterr)
require.NotNil(t, manifest)
@@ -841,9 +841,9 @@ func TestPluginAPIGetPlugins(t *testing.T) {
}
`
pluginDir, err := ioutil.TempDir("", "")
pluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
webappPluginDir, err := ioutil.TempDir("", "")
webappPluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
defer os.RemoveAll(pluginDir)
defer os.RemoveAll(webappPluginDir)
@@ -857,7 +857,7 @@ func TestPluginAPIGetPlugins(t *testing.T) {
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
utils.CompileGo(t, pluginCode, backend)
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(fmt.Sprintf(`{"id": "%s", "server": {"executable": "backend.exe"}}`, pluginID)), 0600)
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(fmt.Sprintf(`{"id": "%s", "server": {"executable": "backend.exe"}}`, pluginID)), 0600)
manifest, activated, reterr := env.Activate(pluginID)
require.NoError(t, reterr)
@@ -884,7 +884,7 @@ func TestPluginAPIInstallPlugin(t *testing.T) {
api := th.SetupPluginAPI()
path, _ := fileutils.FindDir("tests")
tarData, err := ioutil.ReadFile(filepath.Join(path, "testplugin.tar.gz"))
tarData, err := os.ReadFile(filepath.Join(path, "testplugin.tar.gz"))
require.NoError(t, err)
_, appErr := api.InstallPlugin(bytes.NewReader(tarData), true)
@@ -922,9 +922,9 @@ func TestInstallPlugin(t *testing.T) {
// since it removes plugin dirs right after it returns, does not update App configs with the plugin
// dirs and this behavior tends to break this test as a result.
setupTest := func(t *testing.T, pluginCode string, pluginManifest string, pluginID string, app *App, c *request.Context) (func(), string) {
pluginDir, err := ioutil.TempDir("", "")
pluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
webappPluginDir, err := ioutil.TempDir("", "")
webappPluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
app.UpdateConfig(func(cfg *model.Config) {
@@ -944,7 +944,7 @@ func TestInstallPlugin(t *testing.T) {
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
utils.CompileGo(t, pluginCode, backend)
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifest), 0600)
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(pluginManifest), 0600)
manifest, activated, reterr := env.Activate(pluginID)
require.NoError(t, reterr)
require.NotNil(t, manifest)
@@ -1126,7 +1126,7 @@ func TestPluginAPIRemoveTeamIcon(t *testing.T) {
}
func pluginAPIHookTest(t *testing.T, th *TestHelper, fileName string, id string, settingsSchema string) error {
data, err := ioutil.ReadFile(fileName)
data, err := os.ReadFile(fileName)
if err != nil {
return err
}
@@ -1161,7 +1161,7 @@ func TestBasicAPIPlugins(t *testing.T) {
defaultSchema := getDefaultPluginSettingsSchema()
testFolder, found := fileutils.FindDir("mattermost-server/app/plugin_api_tests")
require.True(t, found, "Cannot read find app folder")
dirs, err := ioutil.ReadDir(testFolder)
dirs, err := os.ReadDir(testFolder)
require.NoError(t, err, "Cannot read test folder %v", testFolder)
for _, dir := range dirs {
d := dir.Name()
@@ -1523,7 +1523,7 @@ func TestInterpluginPluginHTTP(t *testing.T) {
"github.com/mattermost/mattermost-server/v6/model"
"bytes"
"net/http"
"io/ioutil"
"io"
)
type MyPlugin struct {
@@ -1545,7 +1545,7 @@ func TestInterpluginPluginHTTP(t *testing.T) {
if resp.Body == nil {
return nil, "Nil body"
}
respbody, err := ioutil.ReadAll(resp.Body)
respbody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err.Error()
}
@@ -1605,9 +1605,9 @@ func TestAPIMetrics(t *testing.T) {
t.Run("", func(t *testing.T) {
metricsMock := &mocks.MetricsInterface{}
pluginDir, err := ioutil.TempDir("", "")
pluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
webappPluginDir, err := ioutil.TempDir("", "")
webappPluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
defer os.RemoveAll(pluginDir)
defer os.RemoveAll(webappPluginDir)
@@ -1642,7 +1642,7 @@ func TestAPIMetrics(t *testing.T) {
}
`
utils.CompileGo(t, code, backend)
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
// Don't care about these mocks
metricsMock.On("ObservePluginHookDuration", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return()
@@ -1730,7 +1730,7 @@ func TestPluginHTTPConnHijack(t *testing.T) {
require.True(t, found, "Cannot find tests folder")
fullPath := path.Join(testFolder, "manual.test_http_hijack_plugin", "main.go")
pluginCode, err := ioutil.ReadFile(fullPath)
pluginCode, err := os.ReadFile(fullPath)
require.NoError(t, err)
require.NotEmpty(t, pluginCode)
@@ -1752,7 +1752,7 @@ func TestPluginHTTPConnHijack(t *testing.T) {
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "OK", string(body))
}
@@ -1765,7 +1765,7 @@ func TestPluginHTTPUpgradeWebSocket(t *testing.T) {
require.True(t, found, "Cannot find tests folder")
fullPath := path.Join(testFolder, "manual.test_http_upgrade_websocket_plugin", "main.go")
pluginCode, err := ioutil.ReadFile(fullPath)
pluginCode, err := os.ReadFile(fullPath)
require.NoError(t, err)
require.NotEmpty(t, pluginCode)

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

@@ -7,7 +7,6 @@ import (
"bytes"
"context"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
@@ -29,9 +28,9 @@ import (
)
func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, apiFunc func(*model.Manifest) plugin.API) (func(), []string, []error) {
pluginDir, err := ioutil.TempDir("", "")
pluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
webappPluginDir, err := ioutil.TempDir("", "")
webappPluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
env, err := plugin.NewEnvironment(apiFunc, NewDriverImpl(app.Srv()), pluginDir, webappPluginDir, app.Log(), nil)
@@ -45,7 +44,7 @@ func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, a
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
utils.CompileGo(t, code, backend)
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
_, _, activationErr := env.Activate(pluginID)
pluginIDs = append(pluginIDs, pluginID)
activationErrors = append(activationErrors, activationErr)
@@ -1024,9 +1023,9 @@ func TestHookMetrics(t *testing.T) {
t.Run("", func(t *testing.T) {
metricsMock := &mocks.MetricsInterface{}
pluginDir, err := ioutil.TempDir("", "")
pluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
webappPluginDir, err := ioutil.TempDir("", "")
webappPluginDir, err := os.MkdirTemp("", "")
require.NoError(t, err)
defer os.RemoveAll(pluginDir)
defer os.RemoveAll(webappPluginDir)
@@ -1069,7 +1068,7 @@ func TestHookMetrics(t *testing.T) {
}
`
utils.CompileGo(t, code, backend)
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
os.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(`{"id": "`+pluginID+`", "server": {"executable": "backend.exe"}}`), 0600)
// Setup mocks before activating
metricsMock.On("ObservePluginHookDuration", pluginID, "Implemented", true, mock.Anything).Return()

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

@@ -33,14 +33,12 @@
// Prepackaged plugins are included with the server. They otherwise follow the above flow, except do not get uploaded
// to the filestore. Prepackaged plugins override all other plugins with the same plugin id, but only when the prepackaged
// plugin is newer. Managed plugins unconditionally override unmanaged plugins with the same plugin id.
//
package app
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
@@ -280,7 +278,7 @@ func (ch *Channels) installPluginLocally(pluginFile, signature io.ReadSeeker, in
}
}
tmpDir, err := ioutil.TempDir("", "plugintmp")
tmpDir, err := os.MkdirTemp("", "plugintmp")
if err != nil {
return nil, model.NewAppError("installPluginLocally", "app.plugin.filesystem.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -305,7 +303,7 @@ func extractPlugin(pluginFile io.ReadSeeker, extractDir string) (*model.Manifest
return nil, "", model.NewAppError("extractPlugin", "app.plugin.extract.app_error", nil, err.Error(), http.StatusBadRequest)
}
dir, err := ioutil.ReadDir(extractDir)
dir, err := os.ReadDir(extractDir)
if err != nil {
return nil, "", model.NewAppError("extractPlugin", "app.plugin.filesystem.app_error", nil, err.Error(), http.StatusInternalServerError)
}

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

@@ -6,7 +6,7 @@ package app
import (
"bytes"
"fmt"
"io/ioutil"
"io"
"net/http"
"path"
"path/filepath"
@@ -157,11 +157,11 @@ func (ch *Channels) servePluginRequest(w http.ResponseWriter, r *http.Request, h
sentToken := ""
if r.Header.Get(model.HeaderCsrfToken) == "" {
bodyBytes, _ := ioutil.ReadAll(r.Body)
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
bodyBytes, _ := io.ReadAll(r.Body)
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
r.ParseForm()
sentToken = r.FormValue("csrf")
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
} else {
sentToken = r.Header.Get(model.HeaderCsrfToken)
}

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

@@ -6,7 +6,6 @@ package app
import (
"bytes"
"io"
"io/ioutil"
"net/http"
"path/filepath"
@@ -25,7 +24,7 @@ func (a *App) GetPublicKey(name string) ([]byte, *model.AppError) {
}
func (s *Server) getPublicKey(name string) ([]byte, *model.AppError) {
data, err := s.configStore.GetFile(name)
data, err := s.platform.GetConfigFile(name)
if err != nil {
return nil, model.NewAppError("GetPublicKey", "app.plugin.get_public_key.get_file.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -37,11 +36,11 @@ func (a *App) AddPublicKey(name string, key io.Reader) *model.AppError {
if isSamlFile(&a.Config().SamlSettings, name) {
return model.NewAppError("AddPublicKey", "app.plugin.modify_saml.app_error", nil, "", http.StatusInternalServerError)
}
data, err := ioutil.ReadAll(key)
data, err := io.ReadAll(key)
if err != nil {
return model.NewAppError("AddPublicKey", "app.plugin.write_file.read.app_error", nil, err.Error(), http.StatusInternalServerError)
}
err = a.Srv().configStore.SetFile(name, data)
err = a.Srv().platform.SetConfigFile(name, data)
if err != nil {
return model.NewAppError("AddPublicKey", "app.plugin.write_file.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -61,7 +60,7 @@ func (a *App) DeletePublicKey(name string) *model.AppError {
return model.NewAppError("AddPublicKey", "app.plugin.modify_saml.app_error", nil, "", http.StatusInternalServerError)
}
filename := filepath.Base(name)
if err := a.Srv().configStore.RemoveFile(filename); err != nil {
if err := a.Srv().platform.RemoveConfigFile(filename); err != nil {
return model.NewAppError("DeletePublicKey", "app.plugin.delete_public_key.delete.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -122,7 +121,7 @@ func verifyBinarySignature(publicKey, signedFile, signature io.Reader) error {
}
func decodeIfArmored(reader io.Reader) (io.Reader, error) {
readBytes, err := ioutil.ReadAll(reader)
readBytes, err := io.ReadAll(reader)
if err != nil {
return nil, errors.Wrap(err, "can't read the file")
}

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

@@ -4,7 +4,6 @@
package app
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
@@ -38,7 +37,7 @@ func TestPluginPublicKeys(t *testing.T) {
path, _ := fileutils.FindDir("tests")
publicKeyFilename := "test-public-key.plugin.gpg"
publicKey, err := ioutil.ReadFile(filepath.Join(path, publicKeyFilename))
publicKey, err := os.ReadFile(filepath.Join(path, publicKeyFilename))
require.NoError(t, err)
fileReader, err := os.Open(filepath.Join(path, publicKeyFilename))
require.NoError(t, err)

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

@@ -9,7 +9,7 @@ import (
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"os"
@@ -384,7 +384,7 @@ func TestPrivateServePluginRequest(t *testing.T) {
handler := func(context *plugin.Context, w http.ResponseWriter, r *http.Request) {
assert.Equal(t, testCase.ExpectedURL, r.URL.Path)
body, _ := ioutil.ReadAll(r.Body)
body, _ := io.ReadAll(r.Body)
assert.Equal(t, expectedBody, body)
}
@@ -827,7 +827,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
t.Run("automatic, enabled plugin, no signature", func(t *testing.T) {
// Install the plugin and enable
pluginBytes, err := ioutil.ReadFile(testPluginPath)
pluginBytes, err := os.ReadFile(testPluginPath)
require.NoError(t, err)
require.NotNil(t, pluginBytes)
@@ -935,7 +935,7 @@ func TestProcessPrepackagedPlugins(t *testing.T) {
require.NoError(t, err)
// Install first plugin and enable
pluginBytes, err := ioutil.ReadFile(testPluginPath)
pluginBytes, err := os.ReadFile(testPluginPath)
require.NoError(t, err)
require.NotNil(t, pluginBytes)

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

@@ -251,7 +251,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
post.AddProp("attachments", attachmentsInterface)
}
if err != nil {
mlog.Warn("Could not convert post attachments to map interface.", mlog.Err(err))
c.Logger().Warn("Could not convert post attachments to map interface.", mlog.Err(err))
}
}
@@ -329,7 +329,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
if len(post.FileIds) > 0 {
if err = a.attachFilesToPost(post); err != nil {
mlog.Warn("Encountered error attaching files to post", mlog.String("post_id", post.Id), mlog.Any("file_ids", post.FileIds), mlog.Err(err))
c.Logger().Warn("Encountered error attaching files to post", mlog.String("post_id", post.Id), mlog.Any("file_ids", post.FileIds), mlog.Err(err))
}
if a.Metrics() != nil {
@@ -348,12 +348,12 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
UpdateFollowing: true,
})
if err != nil {
mlog.Warn("Failed to update thread membership", mlog.Err(err))
c.Logger().Warn("Failed to update thread membership", mlog.Err(err))
}
}
if err := a.handlePostEvents(c, rpost, user, channel, triggerWebhooks, parentPostList, setOnline); err != nil {
mlog.Warn("Failed to handle post events", mlog.Err(err))
c.Logger().Warn("Failed to handle post events", mlog.Err(err))
}
// Send any ephemeral posts after the post is created to ensure it shows up after the latest post created
@@ -1224,34 +1224,35 @@ func (a *App) GetPostsForChannelAroundLastUnread(c request.CTX, channelID, userI
}
func (a *App) DeletePost(c request.CTX, postID, deleteByID string) (*model.Post, *model.AppError) {
post, nErr := a.Srv().Store.Post().GetSingle(postID, false)
if nErr != nil {
return nil, model.NewAppError("DeletePost", "app.post.get.app_error", nil, nErr.Error(), http.StatusBadRequest)
post, err := a.Srv().Store.Post().GetSingle(postID, false)
if err != nil {
return nil, model.NewAppError("DeletePost", "app.post.get.app_error", nil, err.Error(), http.StatusBadRequest)
}
channel, err := a.GetChannel(c, post.ChannelId)
if err != nil {
return nil, err
channel, appErr := a.GetChannel(c, post.ChannelId)
if appErr != nil {
return nil, appErr
}
if channel.DeleteAt != 0 {
err := model.NewAppError("DeletePost", "api.post.delete_post.can_not_delete_post_in_deleted.error", nil, "", http.StatusBadRequest)
return nil, err
appErr := model.NewAppError("DeletePost", "api.post.delete_post.can_not_delete_post_in_deleted.error", nil, "", http.StatusBadRequest)
return nil, appErr
}
if err := a.Srv().Store.Post().Delete(postID, model.GetMillis(), deleteByID); err != nil {
err = a.Srv().Store.Post().Delete(postID, model.GetMillis(), deleteByID)
if err != nil {
var nfErr *store.ErrNotFound
switch {
case errors.As(err, &nfErr):
return nil, model.NewAppError("DeletePost", "app.post.delete.app_error", nil, nfErr.Error(), http.StatusNotFound)
return nil, model.NewAppError("DeletePost", "app.post.delete.app_error", nil, "", http.StatusNotFound).Wrap(nfErr)
default:
return nil, model.NewAppError("DeletePost", "app.post.delete.app_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("DeletePost", "app.post.delete.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
postJSON, jsonErr := json.Marshal(post)
if jsonErr != nil {
return nil, model.NewAppError("DeletePost", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
postJSON, err := json.Marshal(post)
if err != nil {
return nil, model.NewAppError("DeletePost", "api.marshal_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
userMessage := model.NewWebSocketEvent(model.WebsocketEventPostDeleted, "", post.ChannelId, "", nil)
@@ -1283,14 +1284,14 @@ func (a *App) DeletePost(c request.CTX, postID, deleteByID string) (*model.Post,
func (a *App) deleteFlaggedPosts(postID string) {
if err := a.Srv().Store.Preference().DeleteCategoryAndName(model.PreferenceCategoryFlaggedPost, postID); err != nil {
mlog.Warn("Unable to delete flagged post preference when deleting post.", mlog.Err(err))
a.Log().Warn("Unable to delete flagged post preference when deleting post.", mlog.Err(err))
return
}
}
func (a *App) deletePostFiles(postID string) {
if _, err := a.Srv().Store.FileInfo().DeleteForPost(postID); err != nil {
mlog.Warn("Encountered error when deleting files for post", mlog.String("post_id", postID), mlog.Err(err))
a.Log().Warn("Encountered error when deleting files for post", mlog.String("post_id", postID), mlog.Err(err))
}
}
@@ -1358,7 +1359,7 @@ func (a *App) searchPostsInTeam(teamID string, userID string, paramsList []*mode
for result := range pchan {
if result.NErr != nil {
return nil, model.NewAppError("searchPostsInTeam", "app.post.search.app_error", nil, result.NErr.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("searchPostsInTeam", "app.post.search.app_error", nil, "", http.StatusInternalServerError).Wrap(result.NErr)
}
data := result.Data.(*model.PostList)
posts.Extend(data)
@@ -1375,7 +1376,7 @@ func (a *App) convertChannelNamesToChannelIds(c *request.Context, channels []str
for idx, channelName := range channels {
channel, err := a.parseAndFetchChannelIdByNameFromInFilter(c, channelName, userID, teamID, includeDeletedChannels)
if err != nil {
mlog.Warn("error getting channel id by name from in filter", mlog.Err(err))
a.Log().Warn("error getting channel id by name from in filter", mlog.Err(err))
continue
}
channels[idx] = channel.Id
@@ -1387,7 +1388,7 @@ func (a *App) convertUserNameToUserIds(usernames []string) []string {
for idx, username := range usernames {
user, err := a.GetUserByUsername(username)
if err != nil {
mlog.Warn("error getting user by username", mlog.String("user_name", username), mlog.Err(err))
a.Log().Warn("error getting user by username", mlog.String("user_name", username), mlog.Err(err))
continue
}
usernames[idx] = user.Id
@@ -1410,13 +1411,13 @@ func (a *App) GetLastAccessiblePostTime() (int64, *model.AppError) {
// All posts are accessible
return 0, nil
default:
return 0, model.NewAppError("GetLastAccessiblePostTime", "app.system.get_by_name.app_error", nil, err.Error(), http.StatusInternalServerError)
return 0, model.NewAppError("GetLastAccessiblePostTime", "app.system.get_by_name.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
lastAccessiblePostTime, err := strconv.ParseInt(system.Value, 10, 64)
if err != nil {
return 0, model.NewAppError("GetLastAccessiblePostTime", "common.parse_error_int64", map[string]interface{}{"Value": system.Value}, err.Error(), http.StatusInternalServerError)
return 0, model.NewAppError("GetLastAccessiblePostTime", "common.parse_error_int64", map[string]interface{}{"Value": system.Value}, "", http.StatusInternalServerError).Wrap(err)
}
return lastAccessiblePostTime, nil
@@ -1434,7 +1435,7 @@ func (a *App) ComputeLastAccessiblePostTime() error {
if err != nil {
var nfErr *store.ErrNotFound
if !errors.As(err, &nfErr) {
return model.NewAppError("ComputeLastAccessiblePostTime", "app.last_accessible_post.app_error", nil, err.Error(), http.StatusInternalServerError)
return model.NewAppError("ComputeLastAccessiblePostTime", "app.last_accessible_post.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
@@ -1444,7 +1445,7 @@ func (a *App) ComputeLastAccessiblePostTime() error {
Value: strconv.FormatInt(createdAt, 10),
})
if err != nil {
return model.NewAppError("ComputeLastAccessiblePostTime", "app.system.save.app_error", nil, err.Error(), http.StatusInternalServerError)
return model.NewAppError("ComputeLastAccessiblePostTime", "app.system.save.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return nil
@@ -1458,7 +1459,7 @@ func (a *App) getCloudMessagesHistoryLimit() (int64, *model.AppError) {
limits, err := a.Cloud().GetCloudLimits("")
if err != nil {
return 0, model.NewAppError("getCloudMessagesHistoryLimit", "api.cloud.app_error", nil, err.Error(), http.StatusInternalServerError)
return 0, model.NewAppError("getCloudMessagesHistoryLimit", "api.cloud.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
if limits == nil || limits.Messages == nil || limits.Messages.History == nil {
@@ -1516,14 +1517,14 @@ func (a *App) SearchPostsForUser(c *request.Context, terms string, userID string
return model.MakePostSearchResults(model.NewPostList(), nil), nil
}
postSearchResults, nErr := a.Srv().Store.Post().SearchPostsForUser(finalParamsList, userID, teamID, page, perPage)
if nErr != nil {
postSearchResults, err := a.Srv().Store.Post().SearchPostsForUser(finalParamsList, userID, teamID, page, perPage)
if err != nil {
var appErr *model.AppError
switch {
case errors.As(nErr, &appErr):
case errors.As(err, &appErr):
return nil, appErr
default:
return nil, model.NewAppError("SearchPostsForUser", "app.post.search.app_error", nil, nErr.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("SearchPostsForUser", "app.post.search.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
@@ -1535,9 +1536,9 @@ func (a *App) SearchPostsForUser(c *request.Context, terms string, userID string
}
func (a *App) GetRecentSearchesForUser(userID string) ([]*model.SearchParams, *model.AppError) {
searchParams, nErr := a.Srv().Store.Post().GetRecentSearchesForUser(userID)
if nErr != nil {
return nil, model.NewAppError("GetRecentSearchesForUser", "app.recent_searches.app_error", nil, nErr.Error(), http.StatusInternalServerError)
searchParams, err := a.Srv().Store.Post().GetRecentSearchesForUser(userID)
if err != nil {
return nil, model.NewAppError("GetRecentSearchesForUser", "app.recent_searches.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
return searchParams, nil

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

@@ -41,7 +41,7 @@ var linkCache = cache.NewLRU(cache.LRUOptions{
func (s *Server) initPostMetadata() {
// Dump any cached links if the proxy settings have changed so image URLs can be updated
s.AddConfigListener(func(before, after *model.Config) {
s.platform.AddConfigListener(func(before, after *model.Config) {
if (before.ImageProxySettings.Enable != after.ImageProxySettings.Enable) ||
(before.ImageProxySettings.ImageProxyType != after.ImageProxySettings.ImageProxyType) ||
(before.ImageProxySettings.RemoteImageProxyURL != after.ImageProxySettings.RemoteImageProxyURL) ||

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

@@ -796,7 +796,7 @@ func TestPreparePostForClientWithImageProxy(t *testing.T) {
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
return th
}

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

@@ -472,7 +472,7 @@ func TestImageProxy(t *testing.T) {
*cfg.ServiceSettings.SiteURL = "http://mymattermost.com"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
for name, tc := range map[string]struct {
ProxyType string
@@ -686,7 +686,7 @@ func TestCreatePost(t *testing.T) {
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
imageURL := "http://mydomain.com/myimage"
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
@@ -956,7 +956,7 @@ func TestPatchPost(t *testing.T) {
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
imageURL := "http://mydomain.com/myimage"
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"
@@ -1252,7 +1252,7 @@ func TestUpdatePost(t *testing.T) {
*cfg.ImageProxySettings.RemoteImageProxyOptions = "foo"
})
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server, th.Server.HTTPService(), th.Server.Log)
th.App.ch.imageProxy = imageproxy.MakeImageProxy(th.Server.platform, th.Server.HTTPService(), th.Server.Log)
imageURL := "http://mydomain.com/myimage"
proxiedImageURL := "http://mymattermost.com/api/v4/image?url=http%3A%2F%2Fmydomain.com%2Fmyimage"

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

@@ -53,12 +53,12 @@ func (a *App) UpdatePreferences(userID string, preferences model.Preferences) *m
case errors.As(err, &appErr):
return appErr
default:
return model.NewAppError("UpdatePreferences", "app.preference.save.updating.app_error", nil, err.Error(), http.StatusBadRequest)
return model.NewAppError("UpdatePreferences", "app.preference.save.updating.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
}
if err := a.Srv().Store.Channel().UpdateSidebarChannelsByPreferences(preferences); err != nil {
return model.NewAppError("UpdatePreferences", "api.preference.update_preferences.update_sidebar.app_error", nil, err.Error(), http.StatusInternalServerError)
return model.NewAppError("UpdatePreferences", "api.preference.update_preferences.update_sidebar.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
message := model.NewWebSocketEvent(model.WebsocketEventSidebarCategoryUpdated, "", "", userID, nil)
@@ -87,12 +87,12 @@ func (a *App) DeletePreferences(userID string, preferences model.Preferences) *m
for _, preference := range preferences {
if err := a.Srv().Store.Preference().Delete(userID, preference.Category, preference.Name); err != nil {
return model.NewAppError("DeletePreferences", "app.preference.delete.app_error", nil, err.Error(), http.StatusBadRequest)
return model.NewAppError("DeletePreferences", "app.preference.delete.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
}
if err := a.Srv().Store.Channel().DeleteSidebarChannelsByPreferences(preferences); err != nil {
return model.NewAppError("DeletePreferences", "api.preference.delete_preferences.update_sidebar.app_error", nil, err.Error(), http.StatusInternalServerError)
return model.NewAppError("DeletePreferences", "api.preference.delete_preferences.update_sidebar.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
message := model.NewWebSocketEvent(model.WebsocketEventSidebarCategoryUpdated, "", "", userID, nil)

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

@@ -162,9 +162,9 @@ func (a *App) DeleteReactionForPost(c *request.Context, reaction *model.Reaction
func (a *App) sendReactionEvent(event string, reaction *model.Reaction, post *model.Post) {
// send out that a reaction has been added/removed
message := model.NewWebSocketEvent(event, "", post.ChannelId, "", nil)
reactionJSON, jsonErr := json.Marshal(reaction)
if jsonErr != nil {
mlog.Warn("Failed to encode reaction to JSON")
reactionJSON, err := json.Marshal(reaction)
if err != nil {
a.Log().Warn("Failed to encode reaction to JSON", mlog.Err(err))
}
message.Add("reaction", string(reactionJSON))
a.Publish(message)

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

@@ -90,8 +90,8 @@ func TestGetTopReactionsForTeamSince(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.Server.configStore.SetReadOnlyFF(false)
defer th.Server.configStore.SetReadOnlyFF(true)
th.Server.platform.SetConfigReadOnlyFF(false)
defer th.Server.platform.SetConfigReadOnlyFF(true)
userId := th.BasicUser.Id
user2Id := th.BasicUser2.Id
@@ -261,8 +261,8 @@ func TestGetTopReactionsForUserSince(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.Server.configStore.SetReadOnlyFF(false)
defer th.Server.configStore.SetReadOnlyFF(true)
th.Server.platform.SetConfigReadOnlyFF(false)
defer th.Server.platform.SetConfigReadOnlyFF(true)
userId := th.BasicUser.Id

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

@@ -6,7 +6,7 @@ package app
import (
"bytes"
"fmt"
"io/ioutil"
"io"
"net/http"
"strconv"
"strings"
@@ -59,7 +59,7 @@ func (rt *PluginResponseWriter) GenerateResponse() *http.Response {
res.Status = fmt.Sprintf("%03d %s", res.StatusCode, http.StatusText(res.StatusCode))
if rt.Len() > 0 {
res.Body = ioutil.NopCloser(rt)
res.Body = io.NopCloser(rt)
} else {
res.Body = http.NoBody
}

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

@@ -6,7 +6,7 @@ package app
import (
"context"
"encoding/csv"
"io/ioutil"
"io"
"os"
"strconv"
"strings"
@@ -130,7 +130,7 @@ func testPermissionInheritance(t *testing.T, testCallback func(t *testing.T, th
require.NoError(t, e)
defer file.Close()
b, e := ioutil.ReadAll(file)
b, e := io.ReadAll(file)
require.NoError(t, e)
r := csv.NewReader(strings.NewReader(string(b)))

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

@@ -8,7 +8,7 @@ import (
"encoding/pem"
"encoding/xml"
"fmt"
"io/ioutil"
"io"
"mime/multipart"
"net/http"
"strings"
@@ -42,12 +42,12 @@ func (a *App) writeSamlFile(filename string, fileData *multipart.FileHeader) *mo
}
defer file.Close()
data, err := ioutil.ReadAll(file)
data, err := io.ReadAll(file)
if err != nil {
return model.NewAppError("AddSamlCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
}
err = a.Srv().configStore.SetFile(filename, data)
err = a.Srv().platform.SetConfigFile(filename, data)
if err != nil {
return model.NewAppError("AddSamlCertificate", "api.admin.add_certificate.saving.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -107,7 +107,7 @@ func (a *App) AddSamlIdpCertificate(fileData *multipart.FileHeader) *model.AppEr
}
func (a *App) removeSamlFile(filename string) *model.AppError {
if err := a.Srv().configStore.RemoveFile(filename); err != nil {
if err := a.Srv().platform.RemoveConfigFile(filename); err != nil {
return model.NewAppError("RemoveSamlFile", "api.admin.remove_certificate.delete.app_error", map[string]any{"Filename": filename}, err.Error(), http.StatusInternalServerError)
}
@@ -171,9 +171,9 @@ func (a *App) RemoveSamlIdpCertificate() *model.AppError {
func (a *App) GetSamlCertificateStatus() *model.SamlCertificateStatus {
status := &model.SamlCertificateStatus{}
status.IdpCertificateFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.IdpCertificateFile)
status.PrivateKeyFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.PrivateKeyFile)
status.PublicCertificateFile, _ = a.Srv().configStore.HasFile(*a.Config().SamlSettings.PublicCertificateFile)
status.IdpCertificateFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.IdpCertificateFile)
status.PrivateKeyFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.PrivateKeyFile)
status.PublicCertificateFile, _ = a.Srv().platform.HasConfigFile(*a.Config().SamlSettings.PublicCertificateFile)
return status
}
@@ -212,7 +212,7 @@ func (a *App) FetchSamlMetadataFromIdp(url string) ([]byte, *model.AppError) {
}
defer resp.Body.Close()
bodyXML, err := ioutil.ReadAll(resp.Body)
bodyXML, err := io.ReadAll(resp.Body)
if err != nil {
return nil, model.NewAppError("FetchSamlMetadataFromIdp", "app.admin.saml.failure_read_response_body_from_idp.app_error", nil, err.Error(), http.StatusInternalServerError)
}
@@ -267,7 +267,7 @@ func (a *App) SetSamlIdpCertificateFromMetadata(data []byte) *model.AppError {
Bytes: block.Bytes,
})
if err := a.Srv().configStore.SetFile(SamlIdpCertificateName, data); err != nil {
if err := a.Srv().platform.SetConfigFile(SamlIdpCertificateName, data); err != nil {
return model.NewAppError("SetSamlIdpCertificateFromMetadata", "api.admin.saml.failure_save_idp_certificate_file.app_error", nil, err.Error(), http.StatusInternalServerError)
}

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

@@ -5,7 +5,7 @@ package app
import (
"encoding/json"
"io/ioutil"
"io"
"net/http"
"net/url"
"runtime"
@@ -33,7 +33,7 @@ const (
)
func (s *Server) DoSecurityUpdateCheck() {
if !*s.Config().ServiceSettings.EnableSecurityFixAlert {
if !*s.platform.Config().ServiceSettings.EnableSecurityFixAlert {
return
}
@@ -53,7 +53,7 @@ func (s *Server) DoSecurityUpdateCheck() {
v.Set(PropSecurityID, s.TelemetryId())
v.Set(PropSecurityBuild, model.CurrentVersion+"."+model.BuildNumber)
v.Set(PropSecurityEnterpriseReady, model.BuildEnterpriseReady)
v.Set(PropSecurityDatabase, *s.Config().SqlSettings.DriverName)
v.Set(PropSecurityDatabase, *s.platform.Config().SqlSettings.DriverName)
v.Set(PropSecurityOS, runtime.GOOS)
if props[model.SystemRanUnitTests] != "" {
@@ -91,7 +91,7 @@ func (s *Server) DoSecurityUpdateCheck() {
var bulletins model.SecurityBulletins
if jsonErr := json.NewDecoder(res.Body).Decode(&bulletins); jsonErr != nil {
mlog.Error("Failed to decode JSON", mlog.Err(jsonErr))
s.Log.Error("Failed to decode JSON", mlog.Err(jsonErr))
return
}
@@ -110,7 +110,7 @@ func (s *Server) DoSecurityUpdateCheck() {
return
}
body, err := ioutil.ReadAll(resBody.Body)
body, err := io.ReadAll(resBody.Body)
resBody.Body.Close()
if err != nil || resBody.StatusCode != 200 {
mlog.Error("Failed to read security bulletin details")

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

@@ -31,7 +31,6 @@ import (
"golang.org/x/crypto/acme/autocert"
"github.com/mattermost/mattermost-server/v6/app/email"
"github.com/mattermost/mattermost-server/v6/app/featureflag"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/app/teams"
@@ -168,7 +167,6 @@ type Server struct {
searchConfigListenerId string
searchLicenseListenerId string
loggerLicenseListenerId string
configStore *configWrapper
filestore filestore.FileBackend
platform *platform.PlatformService
@@ -201,11 +199,6 @@ type Server struct {
tracer *tracing.Tracer
featureFlagSynchronizer *featureflag.Synchronizer
featureFlagStop chan struct{}
featureFlagStopped chan struct{}
featureFlagSynchronizerMutex sync.Mutex
products map[string]Product
}
@@ -237,7 +230,7 @@ func NewServer(options ...Option) (*Server, error) {
// and has dependency requirements with the previous step.
//
// Step 1: Config.
if s.configStore == nil {
if s.platform == nil {
innerStore, err := config.NewFileStore("config.json", true)
if err != nil {
return nil, errors.Wrap(err, "failed to load config")
@@ -247,7 +240,25 @@ func NewServer(options ...Option) (*Server, error) {
return nil, errors.Wrap(err, "failed to load config")
}
s.configStore = &configWrapper{srv: s, Store: configStore}
platformCfg := platform.ServiceConfig{
ConfigStore: configStore,
Logger: s.Log,
StartMetrics: s.startMetrics,
Cluster: s.Cluster,
}
if metricsInterface != nil {
platformCfg.Metrics = metricsInterface(s, *configStore.Get().SqlSettings.DriverName, *configStore.Get().SqlSettings.DataSource)
}
ps, sErr := platform.New(platformCfg)
if sErr != nil {
return nil, errors.Wrap(sErr, "failed to initialize platform")
}
s.platform = ps
if s.licenseValue.Load() != nil {
ps.SetLicense(s.licenseValue.Load().(*model.License)) // in case license is set in server options
}
}
// Step 2: Logging
@@ -255,7 +266,7 @@ func NewServer(options ...Option) (*Server, error) {
mlog.Error("Could not initiate logging", mlog.Err(err))
}
subpath, err := utils.GetSubpathFromConfig(s.Config())
subpath, err := utils.GetSubpathFromConfig(s.platform.Config())
if err != nil {
return nil, errors.Wrap(err, "failed to parse SiteURL subpath")
}
@@ -264,12 +275,12 @@ func NewServer(options ...Option) (*Server, error) {
// This is called after initLogging() to avoid a race condition.
mlog.Info("Server is initializing...", mlog.String("go_version", runtime.Version()))
s.httpService = httpservice.MakeHTTPService(s)
s.httpService = httpservice.MakeHTTPService(s.platform)
// Step 3: Search Engine
// Depends on Step 1 (config).
searchEngine := searchengine.NewBroker(s.Config())
bleveEngine := bleveengine.NewBleveEngine(s.Config())
searchEngine := searchengine.NewBroker(s.platform.Config())
bleveEngine := bleveengine.NewBleveEngine(s.platform.Config())
if err := bleveEngine.Start(); err != nil {
return nil, err
}
@@ -280,22 +291,6 @@ func NewServer(options ...Option) (*Server, error) {
// Depends on step 3 (s.SearchEngine must be non-nil)
s.initEnterprise()
platformCfg := platform.ServiceConfig{
ConfigStore: s.configStore.Store,
Logger: s.Log,
StartMetrics: s.startMetrics,
Cluster: s.Cluster,
}
if metricsInterface != nil {
platformCfg.Metrics = metricsInterface(s)
}
ps, sErr := platform.New(platformCfg)
if sErr != nil {
return nil, errors.Wrap(sErr, "failed to initialize platform")
}
s.platform = ps
// Step 5: Cache provider.
// At the moment we only have this implementation
// in the future the cache provider will be built based on the loaded config
@@ -308,7 +303,7 @@ func NewServer(options ...Option) (*Server, error) {
// Depends on Step 1 (config), 4 (metrics, cluster) and 5 (cacheProvider).
if s.newStore == nil {
s.newStore = func() (store.Store, error) {
s.sqlStore = sqlstore.New(s.Config().SqlSettings, s.GetMetrics())
s.sqlStore = sqlstore.New(s.platform.Config().SqlSettings, s.GetMetrics())
lcl, err2 := localcachelayer.NewLocalCacheLayer(
retrylayer.New(s.sqlStore),
@@ -323,10 +318,10 @@ func NewServer(options ...Option) (*Server, error) {
searchStore := searchlayer.NewSearchLayer(
lcl,
s.SearchEngine,
s.Config(),
s.platform.Config(),
)
s.AddConfigListener(func(prevCfg, cfg *model.Config) {
s.platform.AddConfigListener(func(prevCfg, cfg *model.Config) {
searchStore.UpdateConfig(cfg)
})
@@ -352,7 +347,7 @@ func NewServer(options ...Option) (*Server, error) {
UserStore: s.Store.User(),
SessionStore: s.Store.Session(),
OAuthStore: s.Store.OAuth(),
ConfigFn: s.Config,
ConfigFn: s.platform.Config,
Metrics: s.GetMetrics(),
Cluster: s.Cluster,
LicenseFn: s.License,
@@ -376,9 +371,9 @@ func NewServer(options ...Option) (*Server, error) {
}
license := s.License()
insecure := s.Config().ServiceSettings.EnableInsecureOutgoingConnections
insecure := s.platform.Config().ServiceSettings.EnableInsecureOutgoingConnections
// Step 7: Initialize filestore
backend, err := filestore.NewFileBackend(s.Config().FileSettings.ToFileBackendSettings(license != nil && *license.Features.Compliance, insecure != nil && *insecure))
backend, err := filestore.NewFileBackend(s.platform.Config().FileSettings.ToFileBackendSettings(license != nil && *license.Features.Compliance, insecure != nil && *insecure))
if err != nil {
return nil, errors.Wrap(err, "failed to initialize filebackend")
}
@@ -398,7 +393,7 @@ func NewServer(options ...Option) (*Server, error) {
GroupStore: s.Store.Group(),
Users: s.userService,
WebHub: s,
ConfigFn: s.Config,
ConfigFn: s.platform.Config,
LicenseFn: s.License,
})
if err != nil {
@@ -410,7 +405,7 @@ func NewServer(options ...Option) (*Server, error) {
serviceMap := map[ServiceKey]any{
ChannelKey: &channelsWrapper{srv: s},
ConfigKey: s.configStore,
ConfigKey: s.platform,
LicenseKey: s.licenseWrapper,
FilestoreKey: s.filestore,
FileInfoStoreKey: &fileInfoWrapper{srv: s},
@@ -441,7 +436,7 @@ func NewServer(options ...Option) (*Server, error) {
// below this. Otherwise, please add it to Channels struct in app/channels.go.
// -------------------------------------------------------------------------
if *s.Config().LogSettings.EnableDiagnostics && *s.Config().LogSettings.EnableSentry {
if *s.platform.Config().LogSettings.EnableDiagnostics && *s.platform.Config().LogSettings.EnableSentry {
if strings.Contains(SentryDSN, "placeholder") {
mlog.Warn("Sentry reporting is enabled, but SENTRY_DSN is not set. Disabling reporting.")
} else {
@@ -468,7 +463,7 @@ func NewServer(options ...Option) (*Server, error) {
}
}
if *s.Config().ServiceSettings.EnableOpenTracing {
if *s.platform.Config().ServiceSettings.EnableOpenTracing {
tracer, err2 := tracing.New()
if err2 != nil {
return nil, err2
@@ -496,7 +491,7 @@ func NewServer(options ...Option) (*Server, error) {
s.createPushNotificationsHub(request.EmptyContext(s.GetLogger()))
if err2 := i18n.InitTranslations(*s.Config().LocalizationSettings.DefaultServerLocale, *s.Config().LocalizationSettings.DefaultClientLocale); err2 != nil {
if err2 := i18n.InitTranslations(*s.platform.Config().LocalizationSettings.DefaultServerLocale, *s.platform.Config().LocalizationSettings.DefaultClientLocale); err2 != nil {
return nil, errors.Wrapf(err2, "unable to load Mattermost translation files")
}
@@ -515,7 +510,7 @@ func NewServer(options ...Option) (*Server, error) {
})
s.htmlTemplateWatcher = htmlTemplateWatcher
s.configListenerId = s.AddConfigListener(func(_, _ *model.Config) {
s.configListenerId = s.platform.AddConfigListener(func(_, _ *model.Config) {
ch := s.Channels()
ch.regenerateClientConfig()
@@ -544,9 +539,10 @@ func NewServer(options ...Option) (*Server, error) {
})
s.telemetryService = telemetry.New(New(ServerConnector(s.Channels())), s.Store, s.SearchEngine, s.Log)
s.platform.SetTelemetryId(s.TelemetryId()) // TODO: move this into platform once telemetry service moved to platform.
emailService, err := email.NewService(email.ServiceConfig{
ConfigFn: s.Config,
ConfigFn: s.platform.Config,
LicenseFn: s.License,
GoFn: s.Go,
TemplatesContainer: s.TemplatesContainer(),
@@ -558,7 +554,7 @@ func NewServer(options ...Option) (*Server, error) {
}
s.EmailService = emailService
s.setupFeatureFlags()
s.platform.SetupFeatureFlags()
s.initJobs()
@@ -567,7 +563,7 @@ func NewServer(options ...Option) (*Server, error) {
if s.Jobs != nil {
s.Jobs.HandleClusterLeaderChange(s.IsLeader())
}
s.setupFeatureFlags()
s.platform.SetupFeatureFlags()
})
// If configured with a subpath, redirect 404s at the root back into the subpath.
@@ -578,12 +574,12 @@ func NewServer(options ...Option) (*Server, error) {
})
}
if _, err = url.ParseRequestURI(*s.Config().ServiceSettings.SiteURL); err != nil {
if _, err = url.ParseRequestURI(*s.platform.Config().ServiceSettings.SiteURL); err != nil {
mlog.Error("SiteURL must be set. Some features will operate incorrectly if the SiteURL is not set. See documentation for details: https://docs.mattermost.com/configure/configuration-settings.html#site-url")
}
// Start email batching because it's not like the other jobs
s.AddConfigListener(func(_, _ *model.Config) {
s.platform.AddConfigListener(func(_, _ *model.Config) {
s.EmailService.InitEmailBatching()
})
@@ -604,7 +600,7 @@ func NewServer(options ...Option) (*Server, error) {
pwd, _ := os.Getwd()
mlog.Info("Printing current working", mlog.String("directory", pwd))
mlog.Info("Loaded config", mlog.String("source", s.configStore.String()))
mlog.Info("Loaded config", mlog.String("source", s.platform.DescribeConfig()))
allowAdvancedLogging := license != nil && *license.Features.AdvancedLogging
@@ -626,7 +622,7 @@ func NewServer(options ...Option) (*Server, error) {
// Enable developer settings if this is a "dev" build
if model.BuildNumber == "dev" {
s.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
s.platform.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.EnableDeveloper = true })
}
if s.startMetrics {
@@ -649,13 +645,13 @@ func NewServer(options ...Option) (*Server, error) {
}
})
s.SearchEngine.UpdateConfig(s.Config())
s.SearchEngine.UpdateConfig(s.platform.Config())
searchConfigListenerId, searchLicenseListenerId := s.StartSearchEngine()
s.searchConfigListenerId = searchConfigListenerId
s.searchLicenseListenerId = searchLicenseListenerId
// if enabled - perform initial product notices fetch
if *s.Config().AnnouncementSettings.AdminNoticesEnabled || *s.Config().AnnouncementSettings.UserNoticesEnabled {
if *s.platform.Config().AnnouncementSettings.AdminNoticesEnabled || *s.platform.Config().AnnouncementSettings.UserNoticesEnabled {
go func() {
appInstance := New(ServerConnector(s.Channels()))
if err := appInstance.UpdateProductNotices(); err != nil {
@@ -668,7 +664,7 @@ func NewServer(options ...Option) (*Server, error) {
return s, nil
}
s.AddConfigListener(func(old, new *model.Config) {
s.platform.AddConfigListener(func(old, new *model.Config) {
appInstance := New(ServerConnector(s.Channels()))
if *old.GuestAccountsSettings.Enable && !*new.GuestAccountsSettings.Enable {
c := request.EmptyContext(s.GetLogger())
@@ -679,7 +675,7 @@ func NewServer(options ...Option) (*Server, error) {
})
// Disable active guest accounts on first run if guest accounts are disabled
if !*s.Config().GuestAccountsSettings.Enable {
if !*s.platform.Config().GuestAccountsSettings.Enable {
appInstance := New(ServerConnector(s.Channels()))
c := request.EmptyContext(s.GetLogger())
if appErr := appInstance.DeactivateGuests(c); appErr != nil {
@@ -703,7 +699,7 @@ func NewServer(options ...Option) (*Server, error) {
s.initPostMetadata()
// Dump the image cache if the proxy settings have changed. (need switch URLs to the correct proxy)
s.AddConfigListener(func(oldCfg, newCfg *model.Config) {
s.platform.AddConfigListener(func(oldCfg, newCfg *model.Config) {
if (oldCfg.ImageProxySettings.Enable != newCfg.ImageProxySettings.Enable) ||
(oldCfg.ImageProxySettings.ImageProxyType != newCfg.ImageProxySettings.ImageProxyType) ||
(oldCfg.ImageProxySettings.RemoteImageProxyURL != newCfg.ImageProxySettings.RemoteImageProxyURL) ||
@@ -755,18 +751,18 @@ func (s *Server) runJobs() {
complianceI.StartComplianceDailyJob()
}
if *s.Config().JobSettings.RunJobs && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunJobs && s.Jobs != nil {
if err := s.Jobs.StartWorkers(); err != nil {
mlog.Error("Failed to start job server workers", mlog.Err(err))
}
}
if *s.Config().JobSettings.RunScheduler && s.Jobs != nil {
if *s.platform.Config().JobSettings.RunScheduler && s.Jobs != nil {
if err := s.Jobs.StartSchedulers(); err != nil {
mlog.Error("Failed to start job server schedulers", mlog.Err(err))
}
}
if *s.Config().ServiceSettings.EnableAWSMetering {
if *s.platform.Config().ServiceSettings.EnableAWSMetering {
runReportToAWSMeterJob(s)
}
}
@@ -786,7 +782,7 @@ func (s *Server) Channels() *Channels {
// Return Database type (postgres or mysql) and current version of the schema
func (s *Server) DatabaseTypeAndSchemaVersion() (string, string) {
schemaVersion, _ := s.Store.GetDBSchemaVersion()
return *s.Config().SqlSettings.DriverName, strconv.Itoa(schemaVersion)
return *s.platform.Config().SqlSettings.DriverName, strconv.Itoa(schemaVersion)
}
// initLogging initializes and configures the logger(s). This may be called more than once.
@@ -809,7 +805,7 @@ func (s *Server) initLogging() error {
s.NotificationsLog = l.With(mlog.String("logSource", "notifications"))
}
if err := s.configureLogger("logging", s.Log, &s.Config().LogSettings, s.configStore.Store, config.GetLogFileLocation); err != nil {
if err := s.platform.ConfigureLogger("logging", s.Log, &s.platform.Config().LogSettings, config.GetLogFileLocation); err != nil {
// if the config is locked then a unit test has already configured and locked the logger; not an error.
if !errors.Is(err, mlog.ErrConfigurationLock) {
// revert to default logger if the config is invalid
@@ -824,8 +820,8 @@ func (s *Server) initLogging() error {
// Use the app logger as the global logger (eventually remove all instances of global logging).
mlog.InitGlobalLogger(s.Log)
notificationLogSettings := config.GetLogSettingsFromNotificationsLogSettings(&s.Config().NotificationLogSettings)
if err := s.configureLogger("notification logging", s.NotificationsLog, notificationLogSettings, s.configStore.Store, config.GetNotificationsLogFileLocation); err != nil {
notificationLogSettings := config.GetLogSettingsFromNotificationsLogSettings(&s.platform.Config().NotificationLogSettings)
if err := s.platform.ConfigureLogger("notification logging", s.NotificationsLog, notificationLogSettings, config.GetNotificationsLogFileLocation); err != nil {
if !errors.Is(err, mlog.ErrConfigurationLock) {
mlog.Error("Error configuring notification logger", mlog.Err(err))
return err
@@ -834,33 +830,6 @@ func (s *Server) initLogging() error {
return nil
}
// configureLogger applies the specified configuration to a logger.
func (s *Server) configureLogger(name string, logger *mlog.Logger, logSettings *model.LogSettings, configStore *config.Store, getPath func(string) string) error {
// Advanced logging is E20 only, however logging must be initialized before the license
// file is loaded. If no valid E20 license exists then advanced logging will be
// shutdown once license is loaded/checked.
var err error
dsn := *logSettings.AdvancedLoggingConfig
var logConfigSrc config.LogConfigSrc
if dsn != "" {
logConfigSrc, err = config.NewLogConfigSrc(dsn, configStore)
if err != nil {
return fmt.Errorf("invalid config source for %s, %w", name, err)
}
mlog.Info("Loaded configuration for "+name, mlog.String("source", dsn))
}
cfg, err := config.MloggerConfigFromLoggerConfig(logSettings, logConfigSrc, getPath)
if err != nil {
return fmt.Errorf("invalid config source for %s, %w", name, err)
}
if err := logger.ConfigureTargets(cfg, nil); err != nil {
return fmt.Errorf("invalid config for %s, %w", name, err)
}
return nil
}
// removeUnlicensedLogTargets removes any unlicensed log target types.
func (s *Server) removeUnlicensedLogTargets(license *model.License) {
if license != nil && *license.Features.AdvancedLogging {
@@ -895,7 +864,7 @@ func (s *Server) startInterClusterServices(license *model.License) error {
}
// Config check
if !*s.Config().ExperimentalSettings.EnableRemoteClusterService {
if !*s.platform.Config().ExperimentalSettings.EnableRemoteClusterService {
mlog.Debug("Remote Cluster Service disabled via config")
return nil
}
@@ -924,7 +893,7 @@ func (s *Server) startInterClusterServices(license *model.License) error {
}
// Config check
if !*s.Config().ExperimentalSettings.EnableSharedChannels {
if !*s.platform.Config().ExperimentalSettings.EnableSharedChannels {
mlog.Debug("Shared Channels Service disabled via config")
return nil
}
@@ -1029,14 +998,16 @@ func (s *Server) Shutdown() {
s.WaitForGoroutines()
s.RemoveConfigListener(s.configListenerId)
s.platform.RemoveConfigListener(s.configListenerId)
s.stopSearchEngine()
s.Audit.Shutdown()
s.stopFeatureFlagUpdateJob()
s.platform.StopFeatureFlagUpdateJob()
s.configStore.Close()
if err = s.platform.ShutdownConfig(); err != nil {
s.Log.Warn("Failed to shut down config store", mlog.Err(err))
}
if s.Cluster != nil {
s.Cluster.StopInterNodeCommunication()
@@ -1239,23 +1210,23 @@ func (s *Server) Start() error {
s.checkPushNotificationServerURL()
s.ReloadConfig()
s.platform.ReloadConfig()
mlog.Info("Starting Server...")
var handler http.Handler = s.RootRouter
if *s.Config().LogSettings.EnableDiagnostics && *s.Config().LogSettings.EnableSentry && !strings.Contains(SentryDSN, "placeholder") {
if *s.platform.Config().LogSettings.EnableDiagnostics && *s.platform.Config().LogSettings.EnableSentry && !strings.Contains(SentryDSN, "placeholder") {
sentryHandler := sentryhttp.New(sentryhttp.Options{
Repanic: true,
})
handler = sentryHandler.Handle(handler)
}
if allowedOrigins := *s.Config().ServiceSettings.AllowCorsFrom; allowedOrigins != "" {
exposedCorsHeaders := *s.Config().ServiceSettings.CorsExposedHeaders
allowCredentials := *s.Config().ServiceSettings.CorsAllowCredentials
debug := *s.Config().ServiceSettings.CorsDebug
if allowedOrigins := *s.platform.Config().ServiceSettings.AllowCorsFrom; allowedOrigins != "" {
exposedCorsHeaders := *s.platform.Config().ServiceSettings.CorsExposedHeaders
allowCredentials := *s.platform.Config().ServiceSettings.CorsAllowCredentials
debug := *s.platform.Config().ServiceSettings.CorsDebug
corsWrapper := cors.New(cors.Options{
AllowedOrigins: strings.Fields(allowedOrigins),
AllowedMethods: corsAllowedMethods,
@@ -1274,10 +1245,10 @@ func (s *Server) Start() error {
handler = corsWrapper.Handler(handler)
}
if *s.Config().RateLimitSettings.Enable {
if *s.platform.Config().RateLimitSettings.Enable {
mlog.Info("RateLimiter is enabled")
rateLimiter, err2 := NewRateLimiter(&s.Config().RateLimitSettings, s.Config().ServiceSettings.TrustedProxyIPHeader)
rateLimiter, err2 := NewRateLimiter(&s.platform.Config().RateLimitSettings, s.platform.Config().ServiceSettings.TrustedProxyIPHeader)
if err2 != nil {
return err2
}
@@ -1292,15 +1263,15 @@ func (s *Server) Start() error {
s.Server = &http.Server{
Handler: handler,
ReadTimeout: time.Duration(*s.Config().ServiceSettings.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(*s.Config().ServiceSettings.WriteTimeout) * time.Second,
IdleTimeout: time.Duration(*s.Config().ServiceSettings.IdleTimeout) * time.Second,
ReadTimeout: time.Duration(*s.platform.Config().ServiceSettings.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(*s.platform.Config().ServiceSettings.WriteTimeout) * time.Second,
IdleTimeout: time.Duration(*s.platform.Config().ServiceSettings.IdleTimeout) * time.Second,
ErrorLog: errStdLog,
}
addr := *s.Config().ServiceSettings.ListenAddress
addr := *s.platform.Config().ServiceSettings.ListenAddress
if addr == "" {
if *s.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
if *s.platform.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
addr = ":https"
} else {
addr = ":http"
@@ -1317,11 +1288,11 @@ func (s *Server) Start() error {
mlog.Info(logListeningPort, mlog.String("address", listener.Addr().String()))
m := &autocert.Manager{
Cache: autocert.DirCache(*s.Config().ServiceSettings.LetsEncryptCertificateCacheFile),
Cache: autocert.DirCache(*s.platform.Config().ServiceSettings.LetsEncryptCertificateCacheFile),
Prompt: autocert.AcceptTOS,
}
if *s.Config().ServiceSettings.Forward80To443 {
if *s.platform.Config().ServiceSettings.Forward80To443 {
if host, port, err := net.SplitHostPort(addr); err != nil {
mlog.Error("Unable to setup forwarding", mlog.Err(err))
} else if port != "443" {
@@ -1329,7 +1300,7 @@ func (s *Server) Start() error {
} else {
httpListenAddress := net.JoinHostPort(host, "http")
if *s.Config().ServiceSettings.UseLetsEncrypt {
if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
server := &http.Server{
Addr: httpListenAddress,
Handler: m.HTTPHandler(nil),
@@ -1353,21 +1324,21 @@ func (s *Server) Start() error {
}()
}
}
} else if *s.Config().ServiceSettings.UseLetsEncrypt {
} else if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
return errors.New(i18n.T("api.server.start_server.forward80to443.disabled_while_using_lets_encrypt"))
}
s.didFinishListen = make(chan struct{})
go func() {
var err error
if *s.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
if *s.platform.Config().ServiceSettings.ConnectionSecurity == model.ConnSecurityTLS {
tlsConfig := &tls.Config{
PreferServerCipherSuites: true,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
}
switch *s.Config().ServiceSettings.TLSMinVer {
switch *s.platform.Config().ServiceSettings.TLSMinVer {
case "1.0":
tlsConfig.MinVersion = tls.VersionTLS10
case "1.1":
@@ -1385,11 +1356,11 @@ func (s *Server) Start() error {
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
}
if len(s.Config().ServiceSettings.TLSOverwriteCiphers) == 0 {
if len(s.platform.Config().ServiceSettings.TLSOverwriteCiphers) == 0 {
tlsConfig.CipherSuites = defaultCiphers
} else {
var cipherSuites []uint16
for _, cipher := range s.Config().ServiceSettings.TLSOverwriteCiphers {
for _, cipher := range s.platform.Config().ServiceSettings.TLSOverwriteCiphers {
value, ok := model.ServerTLSSupportedCiphers[cipher]
if !ok {
@@ -1411,12 +1382,12 @@ func (s *Server) Start() error {
certFile := ""
keyFile := ""
if *s.Config().ServiceSettings.UseLetsEncrypt {
if *s.platform.Config().ServiceSettings.UseLetsEncrypt {
tlsConfig.GetCertificate = m.GetCertificate
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
} else {
certFile = *s.Config().ServiceSettings.TLSCertFile
keyFile = *s.Config().ServiceSettings.TLSKeyFile
certFile = *s.platform.Config().ServiceSettings.TLSCertFile
keyFile = *s.platform.Config().ServiceSettings.TLSKeyFile
}
s.Server.TLSConfig = tlsConfig
@@ -1433,7 +1404,7 @@ func (s *Server) Start() error {
close(s.didFinishListen)
}()
if *s.Config().ServiceSettings.EnableLocalMode {
if *s.platform.Config().ServiceSettings.EnableLocalMode {
if err := s.startLocalModeServer(); err != nil {
mlog.Critical(err.Error())
}
@@ -1451,7 +1422,7 @@ func (s *Server) startLocalModeServer() error {
Handler: s.LocalRouter,
}
socket := *s.configStore.Get().ServiceSettings.LocalModeSocketLocation
socket := *s.platform.Config().ServiceSettings.LocalModeSocketLocation
if err := os.RemoveAll(socket); err != nil {
return errors.Wrapf(err, i18n.T("api.server.start_server.starting.critical"), err)
}
@@ -1495,7 +1466,7 @@ func (a *App) OriginChecker() func(*http.Request) bool {
}
func (s *Server) checkPushNotificationServerURL() {
notificationServer := *s.Config().EmailSettings.PushNotificationServer
notificationServer := *s.platform.Config().EmailSettings.PushNotificationServer
if strings.HasPrefix(notificationServer, "http://") {
mlog.Warn("Your push notification server is configured with HTTP. For improved security, update to HTTPS in your configuration.")
}
@@ -1563,7 +1534,7 @@ func runReportToAWSMeterJob(s *Server) {
}
func doReportUsageToAWSMeteringService(s *Server) {
awsMeter := awsmeter.New(s.Store, s.Config())
awsMeter := awsmeter.New(s.Store, s.platform.Config())
if awsMeter == nil {
mlog.Error("Cannot obtain instance of AWS Metering Service.")
return
@@ -1604,12 +1575,12 @@ func doSessionCleanup(s *Server) {
}
func doJobsCleanup(s *Server) {
if *s.Config().JobSettings.CleanupJobsThresholdDays < 0 {
if *s.platform.Config().JobSettings.CleanupJobsThresholdDays < 0 {
return
}
mlog.Debug("Cleaning up jobs store.")
dur := time.Duration(*s.Config().JobSettings.CleanupJobsThresholdDays) * time.Hour * 24
dur := time.Duration(*s.platform.Config().JobSettings.CleanupJobsThresholdDays) * time.Hour * 24
expiry := model.GetMillisForTime(time.Now().Add(-dur))
err := s.Store.Job().Cleanup(expiry, jobsCleanupBatchSize)
if err != nil {
@@ -1618,12 +1589,12 @@ func doJobsCleanup(s *Server) {
}
func doConfigCleanup(s *Server) {
if *s.Config().JobSettings.CleanupConfigThresholdDays < 0 || !config.IsDatabaseDSN(s.ConfigStore().Store.String()) {
if *s.platform.Config().JobSettings.CleanupConfigThresholdDays < 0 || !config.IsDatabaseDSN(s.platform.DescribeConfig()) {
return
}
mlog.Info("Cleaning up configuration store.")
if err := s.ConfigStore().Store.CleanUp(); err != nil {
if err := s.platform.CleanUpConfig(); err != nil {
mlog.Warn("Error while cleaning up configurations", mlog.Err(err))
}
}
@@ -1654,7 +1625,7 @@ func (s *Server) sendLicenseUpForRenewalEmail(users map[string]*model.User, lice
if name == "" {
name = user.Username
}
if err := s.EmailService.SendLicenseUpForRenewalEmail(user.Email, name, user.Locale, *s.Config().ServiceSettings.SiteURL, renewalLink, daysToExpiration); err != nil {
if err := s.EmailService.SendLicenseUpForRenewalEmail(user.Email, name, user.Locale, *s.platform.Config().ServiceSettings.SiteURL, renewalLink, daysToExpiration); err != nil {
mlog.Error("Error sending license up for renewal email to", mlog.String("user_email", user.Email), mlog.Err(err))
countNotOks++
}
@@ -1735,7 +1706,7 @@ func (s *Server) doLicenseExpirationCheck() {
mlog.Debug("Sending license expired email.", mlog.String("user_email", user.Email))
s.Go(func() {
if err := s.SendRemoveExpiredLicenseEmail(user.Email, renewalLink, user.Locale, *s.Config().ServiceSettings.SiteURL); err != nil {
if err := s.SendRemoveExpiredLicenseEmail(user.Email, renewalLink, user.Locale, *s.platform.Config().ServiceSettings.SiteURL); err != nil {
mlog.Error("Error while sending the license expired email.", mlog.String("user_email", user.Email), mlog.Err(err))
}
})
@@ -1765,7 +1736,7 @@ func (s *Server) StartSearchEngine() (string, string) {
})
}
configListenerId := s.AddConfigListener(func(oldConfig *model.Config, newConfig *model.Config) {
configListenerId := s.platform.AddConfigListener(func(oldConfig *model.Config, newConfig *model.Config) {
if s.SearchEngine == nil {
return
}
@@ -1824,7 +1795,7 @@ func (s *Server) StartSearchEngine() (string, string) {
}
func (s *Server) stopSearchEngine() {
s.RemoveConfigListener(s.searchConfigListenerId)
s.platform.RemoveConfigListener(s.searchConfigListenerId)
s.RemoveLicenseListener(s.searchLicenseListenerId)
if s.SearchEngine != nil && s.SearchEngine.ElasticsearchEngine != nil && s.SearchEngine.ElasticsearchEngine.IsActive() {
s.SearchEngine.ElasticsearchEngine.Stop()
@@ -1858,7 +1829,7 @@ func (ch *Channels) ClientConfigHash() string {
}
func (s *Server) initJobs() {
s.Jobs = jobs.NewJobServer(s, s.Store, s.GetMetrics())
s.Jobs = jobs.NewJobServer(s.platform, s.Store, s.GetMetrics())
if jobsDataRetentionJobInterface != nil {
builder := jobsDataRetentionJobInterface(s)
@@ -2031,7 +2002,7 @@ func (s *Server) SetSharedChannelSyncService(sharedChannelService SharedChannelS
}
func (s *Server) GetProfileImage(user *model.User) ([]byte, bool, *model.AppError) {
if *s.Config().FileSettings.DriverName == "" {
if *s.platform.Config().FileSettings.DriverName == "" {
img, appErr := s.GetDefaultProfileImage(user)
if appErr != nil {
return nil, false, appErr
@@ -2141,3 +2112,8 @@ func (a *App) GetAppliedSchemaMigrations() ([]model.AppliedMigration, *model.App
}
return table, nil
}
// Expose platform service from server, this should be replaced with server itself in time.
func (s *Server) Platform() *platform.PlatformService {
return s.platform
}

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

@@ -17,17 +17,17 @@ const inactivityEmailSent = "INACTIVITY"
func (s *Server) doInactivityCheck() {
if *s.Config().ServiceSettings.EnableDeveloper {
if *s.platform.Config().ServiceSettings.EnableDeveloper {
mlog.Info("No activity check because developer mode is enabled")
return
}
if !*s.Config().EmailSettings.EnableInactivityEmail {
if !*s.platform.Config().EmailSettings.EnableInactivityEmail {
mlog.Info("No activity check because EnableInactivityEmail is false")
return
}
if !s.Config().FeatureFlags.EnableInactivityCheckJob {
if !s.platform.Config().FeatureFlags.EnableInactivityCheckJob {
mlog.Info("No activity check because EnableInactivityCheckJob feature flag is disabled")
return
}
@@ -70,7 +70,7 @@ func (s *Server) doInactivityCheck() {
}
func (s *Server) takeInactivityAction() {
siteURL := *s.Config().ServiceSettings.SiteURL
siteURL := *s.platform.Config().ServiceSettings.SiteURL
if siteURL == "" {
mlog.Warn("No SiteURL configured")
}

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

@@ -1,13 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package app
import (
"github.com/mattermost/mattermost-server/v6/model"
)
func (s *Server) License() *model.License {
license, _ := s.licenseValue.Load().(*model.License)
return license
}

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

@@ -23,6 +23,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v6/app/platform"
"github.com/mattermost/mattermost-server/v6/config"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/shared/filestore"
@@ -83,54 +84,70 @@ func TestReadReplicaDisabledBasedOnLicense(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = &configWrapper{srv: server, Store: configStore}
var err error
server.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: configStore,
})
require.NoError(t, err)
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.Same(t, s.sqlStore.GetMasterX(), s.sqlStore.GetReplicaX())
require.Len(t, s.Config().SqlSettings.DataSourceReplicas, 1)
require.Len(t, s.platform.Config().SqlSettings.DataSourceReplicas, 1)
})
t.Run("Read Replicas With License", func(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = &configWrapper{srv: server, Store: configStore}
var err error
server.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: configStore,
})
require.NoError(t, err)
server.licenseValue.Store(model.NewTestLicense())
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.NotSame(t, s.sqlStore.GetMasterX(), s.sqlStore.GetReplicaX())
require.Len(t, s.Config().SqlSettings.DataSourceReplicas, 1)
require.Len(t, s.platform.Config().SqlSettings.DataSourceReplicas, 1)
})
t.Run("Search Replicas with no License", func(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = &configWrapper{srv: server, Store: configStore}
var err error
server.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: configStore,
})
require.NoError(t, err)
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.Same(t, s.sqlStore.GetMasterX(), s.sqlStore.GetSearchReplicaX())
require.Len(t, s.Config().SqlSettings.DataSourceSearchReplicas, 1)
require.Len(t, s.platform.Config().SqlSettings.DataSourceSearchReplicas, 1)
})
t.Run("Search Replicas With License", func(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore := config.NewTestMemoryStore()
configStore.Set(&cfg)
server.configStore = &configWrapper{srv: server, Store: configStore}
var err error
server.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: configStore,
})
require.NoError(t, err)
server.licenseValue.Store(model.NewTestLicense())
return nil
})
require.NoError(t, err)
defer s.Shutdown()
require.NotSame(t, s.sqlStore.GetMasterX(), s.sqlStore.GetSearchReplicaX())
require.Len(t, s.Config().SqlSettings.DataSourceSearchReplicas, 1)
require.Len(t, s.platform.Config().SqlSettings.DataSourceSearchReplicas, 1)
})
}
@@ -143,7 +160,7 @@ func TestStartServerPortUnavailable(t *testing.T) {
require.NoError(t, err)
// Attempt to listen on the port used above.
s.UpdateConfig(func(cfg *model.Config) {
s.platform.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = listener.Addr().String()
})
@@ -168,8 +185,12 @@ func TestStartServerNoS3Bucket(t *testing.T) {
s, err := NewServer(func(server *Server) error {
configStore, _ := config.NewFileStore("config.json", true)
store, _ := config.NewStoreFromBacking(configStore, nil, false)
server.configStore = &configWrapper{srv: server, Store: store}
server.UpdateConfig(func(cfg *model.Config) {
var err error
server.platform, err = platform.New(platform.ServiceConfig{
ConfigStore: store,
})
require.NoError(t, err)
server.platform.UpdateConfig(func(cfg *model.Config) {
cfg.FileSettings = model.FileSettings{
DriverName: model.NewString(model.ImageDriverS3),
AmazonS3AccessKeyId: model.NewString(model.MinioAccessKey),
@@ -393,7 +414,7 @@ func TestPanicLog(t *testing.T) {
})
testDir, _ := fileutils.FindDir("tests")
s.UpdateConfig(func(cfg *model.Config) {
s.platform.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ListenAddress = ":0"
*cfg.ServiceSettings.ConnectionSecurity = "TLS"
*cfg.ServiceSettings.TLSKeyFile = path.Join(testDir, "tls_test_key.pem")

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

@@ -77,9 +77,9 @@ func setCollapsePreference(a *app.App, args *model.CommandArgs, isCollapse bool)
socketMessage := model.NewWebSocketEvent(model.WebsocketEventPreferenceChanged, "", "", args.UserId, nil)
prefJSON, jsonErr := json.Marshal(pref)
if jsonErr != nil {
return &model.CommandResponse{Text: args.T("api.marshal_error") + jsonErr.Error(), ResponseType: model.CommandResponseTypeEphemeral}
prefJSON, err := json.Marshal(pref)
if err != nil {
return &model.CommandResponse{Text: args.T("api.marshal_error") + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}
}
socketMessage.Add("preference", string(prefJSON))
a.Publish(socketMessage)

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

@@ -570,7 +570,7 @@ func (*LoadTestProvider) JsonCommand(a *app.App, c request.CTX, args *model.Comm
var post model.Post
if jsonErr := json.NewDecoder(r.Body).Decode(&post); jsonErr != nil {
return &model.CommandResponse{Text: "Unable to decode post", ResponseType: model.CommandResponseTypeEphemeral}, errors.Errorf("could not decode post from json")
return &model.CommandResponse{Text: "Unable to decode post", ResponseType: model.CommandResponseTypeEphemeral}, errors.Wrapf(jsonErr, "could not decode post from json")
}
post.ChannelId = args.ChannelId
post.UserId = args.UserId

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

@@ -6,7 +6,6 @@ package slashcommands
import (
"bytes"
"context"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -42,7 +41,7 @@ type TestHelper struct {
}
func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer bool, tb testing.TB, configSet func(*model.Config)) *TestHelper {
tempWorkspace, err := ioutil.TempDir("", "apptest")
tempWorkspace, err := os.MkdirTemp("", "apptest")
if err != nil {
panic(err)
}

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

@@ -22,9 +22,9 @@ func (a *App) AddStatusCache(status *model.Status) {
a.AddStatusCacheSkipClusterSend(status)
if a.Cluster() != nil {
statusJSON, jsonErr := json.Marshal(status)
if jsonErr != nil {
mlog.Warn("Failed to encode status to JSON")
statusJSON, err := json.Marshal(status)
if err != nil {
a.Log().Warn("Failed to encode status to JSON", mlog.Err(err))
}
msg := &model.ClusterMessage{
Event: model.ClusterEventUpdateStatus,
@@ -456,20 +456,20 @@ func (a *App) GetCustomStatus(userID string) (*model.CustomStatus, *model.AppErr
func (a *App) addRecentCustomStatus(userID string, status *model.CustomStatus) *model.AppError {
var newRCS model.RecentCustomStatuses
pref, err := a.GetPreferenceByCategoryAndNameForUser(userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
if err != nil || pref.Value == "" {
pref, appErr := a.GetPreferenceByCategoryAndNameForUser(userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
if appErr != nil || pref.Value == "" {
newRCS = model.RecentCustomStatuses{*status}
} else {
var existingRCS model.RecentCustomStatuses
if jsonErr := json.Unmarshal([]byte(pref.Value), &existingRCS); jsonErr != nil {
return model.NewAppError("addRecentCustomStatus", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusBadRequest)
if err := json.Unmarshal([]byte(pref.Value), &existingRCS); err != nil {
return model.NewAppError("addRecentCustomStatus", "api.unmarshal_error", nil, "", http.StatusBadRequest).Wrap(err)
}
newRCS = existingRCS.Add(status)
}
newRCSJSON, jsonErr := json.Marshal(newRCS)
if jsonErr != nil {
return model.NewAppError("addRecentCustomStatus", "api.marshal_error", nil, jsonErr.Error(), http.StatusBadRequest)
newRCSJSON, err := json.Marshal(newRCS)
if err != nil {
return model.NewAppError("addRecentCustomStatus", "api.marshal_error", nil, "", http.StatusBadRequest).Wrap(err)
}
pref = &model.Preference{
UserId: userID,
@@ -477,17 +477,17 @@ func (a *App) addRecentCustomStatus(userID string, status *model.CustomStatus) *
Name: model.PreferenceNameRecentCustomStatuses,
Value: string(newRCSJSON),
}
if err := a.UpdatePreferences(userID, model.Preferences{*pref}); err != nil {
return err
if appErr := a.UpdatePreferences(userID, model.Preferences{*pref}); appErr != nil {
return appErr
}
return nil
}
func (a *App) RemoveRecentCustomStatus(userID string, status *model.CustomStatus) *model.AppError {
pref, err := a.GetPreferenceByCategoryAndNameForUser(userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
if err != nil {
return err
pref, appErr := a.GetPreferenceByCategoryAndNameForUser(userID, model.PreferenceCategoryCustomStatus, model.PreferenceNameRecentCustomStatuses)
if appErr != nil {
return appErr
}
if pref.Value == "" {
@@ -495,26 +495,26 @@ func (a *App) RemoveRecentCustomStatus(userID string, status *model.CustomStatus
}
var existingRCS model.RecentCustomStatuses
if jsonErr := json.Unmarshal([]byte(pref.Value), &existingRCS); jsonErr != nil {
return model.NewAppError("RemoveRecentCustomStatus", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusBadRequest)
if err := json.Unmarshal([]byte(pref.Value), &existingRCS); err != nil {
return model.NewAppError("RemoveRecentCustomStatus", "api.unmarshal_error", nil, "", http.StatusBadRequest).Wrap(err)
}
if ok, err := existingRCS.Contains(status); !ok || err != nil {
return model.NewAppError("RemoveRecentCustomStatus", "api.custom_status.recent_custom_statuses.delete.app_error", nil, "", http.StatusBadRequest)
}
newRCS, removeErr := existingRCS.Remove(status)
if removeErr != nil {
return model.NewAppError("RemoveRecentCustomStatus", "api.custom_status.recent_custom_statuses.delete.app_error", nil, removeErr.Error(), http.StatusBadRequest)
newRCS, err := existingRCS.Remove(status)
if err != nil {
return model.NewAppError("RemoveRecentCustomStatus", "api.custom_status.recent_custom_statuses.delete.app_error", nil, "", http.StatusBadRequest).Wrap(err)
}
newRCSJSON, jsonErr := json.Marshal(newRCS)
if jsonErr != nil {
return model.NewAppError("RemoveRecentCustomStatus", "api.marshal_error", nil, jsonErr.Error(), http.StatusBadRequest)
newRCSJSON, err := json.Marshal(newRCS)
if err != nil {
return model.NewAppError("RemoveRecentCustomStatus", "api.marshal_error", nil, "", http.StatusBadRequest).Wrap(err)
}
pref.Value = string(newRCSJSON)
if err := a.UpdatePreferences(userID, model.Preferences{*pref}); err != nil {
return err
if appErr := a.UpdatePreferences(userID, model.Preferences{*pref}); appErr != nil {
return appErr
}
return nil

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

@@ -104,7 +104,7 @@ func TestCustomStatusErrors(t *testing.T) {
UserStore: &mockUserStore,
SessionStore: &mockSessionStore,
OAuthStore: &mockOAuthStore,
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)

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

@@ -6,7 +6,7 @@ package app
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"runtime"
"strings"
@@ -145,7 +145,7 @@ func (a *App) getNotificationsLog() (*model.FileData, string) {
// notifications.log
notificationsLog := config.GetNotificationsLogFileLocation(*a.Config().LogSettings.FileLocation)
notificationsLogFileData, notificationsLogFileDataErr := ioutil.ReadFile(notificationsLog)
notificationsLogFileData, notificationsLogFileDataErr := os.ReadFile(notificationsLog)
if notificationsLogFileDataErr == nil {
fileData := model.FileData{
@@ -155,7 +155,7 @@ func (a *App) getNotificationsLog() (*model.FileData, string) {
return &fileData, ""
}
warning = fmt.Sprintf("ioutil.ReadFile(notificationsLog) Error: %s", notificationsLogFileDataErr.Error())
warning = fmt.Sprintf("os.ReadFile(notificationsLog) Error: %s", notificationsLogFileDataErr.Error())
} else {
warning = "Unable to retrieve notifications.log because LogSettings: EnableFile is false in config.json"
@@ -172,7 +172,7 @@ func (a *App) getMattermostLog() (*model.FileData, string) {
// mattermost.log
mattermostLog := config.GetLogFileLocation(*a.Config().LogSettings.FileLocation)
mattermostLogFileData, mattermostLogFileDataErr := ioutil.ReadFile(mattermostLog)
mattermostLogFileData, mattermostLogFileDataErr := os.ReadFile(mattermostLog)
if mattermostLogFileDataErr == nil {
fileData := model.FileData{
@@ -181,7 +181,7 @@ func (a *App) getMattermostLog() (*model.FileData, string) {
}
return &fileData, ""
}
warning = fmt.Sprintf("ioutil.ReadFile(mattermostLog) Error: %s", mattermostLogFileDataErr.Error())
warning = fmt.Sprintf("os.ReadFile(mattermostLog) Error: %s", mattermostLogFileDataErr.Error())
} else {
warning = "Unable to retrieve mattermost.log because LogSettings: EnableFile is false in config.json"

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

@@ -4,7 +4,6 @@
package app
import (
"io/ioutil"
"os"
"testing"
@@ -61,9 +60,9 @@ func TestGenerateSupportPacket(t *testing.T) {
defer th.TearDown()
d1 := []byte("hello\ngo\n")
err := ioutil.WriteFile("mattermost.log", d1, 0777)
err := os.WriteFile("mattermost.log", d1, 0777)
require.NoError(t, err)
err = ioutil.WriteFile("notifications.log", d1, 0777)
err = os.WriteFile("notifications.log", d1, 0777)
require.NoError(t, err)
fileDatas := th.App.GenerateSupportPacket()
@@ -111,11 +110,11 @@ func TestGetNotificationsLog(t *testing.T) {
fileData, warning = th.App.getNotificationsLog()
assert.Nil(t, fileData)
assert.Contains(t, warning, "ioutil.ReadFile(notificationsLog) Error:")
assert.Contains(t, warning, "os.ReadFile(notificationsLog) Error:")
// Happy path where we have file and no warning
d1 := []byte("hello\ngo\n")
err := ioutil.WriteFile("notifications.log", d1, 0777)
err := os.WriteFile("notifications.log", d1, 0777)
defer os.Remove("notifications.log")
require.NoError(t, err)
@@ -149,11 +148,11 @@ func TestGetMattermostLog(t *testing.T) {
fileData, warning = th.App.getMattermostLog()
assert.Nil(t, fileData)
assert.Contains(t, warning, "ioutil.ReadFile(mattermostLog) Error:")
assert.Contains(t, warning, "os.ReadFile(mattermostLog) Error:")
// Happy path where we get a log file and no warning
d1 := []byte("hello\ngo\n")
err := ioutil.WriteFile("mattermost.log", d1, 0777)
err := os.WriteFile("mattermost.log", d1, 0777)
defer os.Remove("mattermost.log")
require.NoError(t, err)

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

@@ -1044,7 +1044,7 @@ func TestLeaveTeamPanic(t *testing.T) {
UserStore: &mockUserStore,
SessionStore: &mocks.SessionStore{},
OAuthStore: &mocks.OAuthStore{},
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)
@@ -1088,7 +1088,7 @@ func TestLeaveTeamPanic(t *testing.T) {
GroupStore: &mocks.GroupStore{},
Users: th.App.ch.srv.userService,
WebHub: th.App.ch.srv,
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)

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

@@ -5,7 +5,6 @@ package teams
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"testing"
@@ -43,7 +42,7 @@ func Setup(tb testing.TB) *TestHelper {
}
func setupTestHelper(s store.Store, includeCacheLayer bool, tb testing.TB) *TestHelper {
tempWorkspace, err := ioutil.TempDir("", "teamservicetest")
tempWorkspace, err := os.MkdirTemp("", "teamservicetest")
if err != nil {
panic(err)
}

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

@@ -6,8 +6,8 @@ package app
import (
"bytes"
"io"
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"sync"
"sync/atomic"
@@ -213,7 +213,7 @@ func TestUploadData(t *testing.T) {
t.Run("image processing", func(t *testing.T) {
testDir, _ := fileutils.FindDir("tests")
data, err := ioutil.ReadFile(filepath.Join(testDir, "test.png"))
data, err := os.ReadFile(filepath.Join(testDir, "test.png"))
require.NoError(t, err)
require.NotEmpty(t, data)

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

@@ -1241,7 +1241,7 @@ func (a *App) updateUserNotifyProps(userID string, props map[string]string) *mod
case errors.As(err, &appErr):
return appErr
default:
return model.NewAppError("UpdateUser", "app.user.update.finding.app_error", nil, err.Error(), http.StatusInternalServerError)
return model.NewAppError("UpdateUser", "app.user.update.finding.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
@@ -1417,7 +1417,7 @@ func (a *App) CreatePasswordRecoveryToken(userID, email string) (*model.Token, *
}
jsonData, err := json.Marshal(tokenExtra)
if err != nil {
return nil, model.NewAppError("CreatePasswordRecoveryToken", "api.user.create_password_token.error", nil, "", http.StatusInternalServerError)
return nil, model.NewAppError("CreatePasswordRecoveryToken", "api.user.create_password_token.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
token := model.NewToken(TokenTypePasswordRecovery, string(jsonData))
@@ -2184,9 +2184,9 @@ func (a *App) PromoteGuestToUser(c *request.Context, user *model.User, requestor
for _, member := range teamMembers {
a.sendUpdatedMemberRoleEvent(user.Id, member)
channelMembers, err := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
if err != nil {
c.Logger().Warn("Failed to get channel members for user on promote guest to user", mlog.Err(err))
channelMembers, appErr := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
if appErr != nil {
c.Logger().Warn("Failed to get channel members for user on promote guest to user", mlog.Err(appErr))
}
for _, member := range channelMembers {
@@ -2228,9 +2228,9 @@ func (a *App) DemoteUserToGuest(c request.CTX, user *model.User) *model.AppError
for _, member := range teamMembers {
a.sendUpdatedMemberRoleEvent(user.Id, member)
channelMembers, err := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
if err != nil {
c.Logger().Warn("Failed to get channel members for users on demote user to guest", mlog.Err(err))
channelMembers, appErr := a.GetChannelMembersForUser(c, member.TeamId, user.Id)
if appErr != nil {
c.Logger().Warn("Failed to get channel members for users on demote user to guest", mlog.Err(appErr))
continue
}

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

@@ -1712,7 +1712,7 @@ func TestUpdateThreadReadForUser(t *testing.T) {
UserStore: &mockUserStore,
SessionStore: &storemocks.SessionStore{},
OAuthStore: &storemocks.OAuthStore{},
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)
@@ -1749,8 +1749,8 @@ func TestCreateUserWithInitialPreferences(t *testing.T) {
})
t.Run("successfully create a user with insights feature flag disabled", func(t *testing.T) {
th.Server.configStore.SetReadOnlyFF(false)
defer th.Server.configStore.SetReadOnlyFF(true)
th.Server.platform.SetConfigReadOnlyFF(false)
defer th.Server.platform.SetConfigReadOnlyFF(true)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = false })
defer th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
testUser := th.CreateUser()
@@ -1769,8 +1769,8 @@ func TestCreateUserWithInitialPreferences(t *testing.T) {
})
t.Run("successfully create a guest user with initial tutorial, insights and recommended steps preferences", func(t *testing.T) {
th.Server.configStore.SetReadOnlyFF(false)
defer th.Server.configStore.SetReadOnlyFF(true)
th.Server.platform.SetConfigReadOnlyFF(false)
defer th.Server.platform.SetConfigReadOnlyFF(true)
th.App.UpdateConfig(func(cfg *model.Config) { cfg.FeatureFlags.InsightsEnabled = true })
testUser := th.CreateGuest()
defer th.App.PermanentDeleteUser(th.Context, testUser)

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

@@ -5,7 +5,6 @@ package users
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"runtime"
@@ -48,7 +47,7 @@ func Setup(tb testing.TB) *TestHelper {
}
func setupTestHelper(s store.Store, includeCacheLayer bool, tb testing.TB) *TestHelper {
tempWorkspace, err := ioutil.TempDir("", "userservicetest")
tempWorkspace, err := os.MkdirTemp("", "userservicetest")
if err != nil {
panic(err)
}

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

@@ -11,7 +11,7 @@ import (
"image/draw"
"image/png"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
@@ -174,7 +174,7 @@ func getFont(initialFont string) (*truetype.Font, error) {
}
fontDir, _ := fileutils.FindDir("fonts")
fontBytes, err := ioutil.ReadFile(filepath.Join(fontDir, initialFont))
fontBytes, err := os.ReadFile(filepath.Join(fontDir, initialFont))
if err != nil {
return nil, err
}

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

@@ -167,7 +167,7 @@ func TestHubSessionRevokeRace(t *testing.T) {
UserStore: &mockUserStore,
SessionStore: &mockSessionStore,
OAuthStore: &mockOAuthStore,
ConfigFn: th.App.ch.srv.Config,
ConfigFn: th.App.ch.srv.platform.Config,
Metrics: th.App.Metrics(),
Cluster: th.App.Cluster(),
LicenseFn: th.App.ch.srv.License,

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

@@ -98,9 +98,9 @@ func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayloa
var body io.Reader
var contentType string
if hook.ContentType == "application/json" {
js, jsonErr := json.Marshal(payload)
if jsonErr != nil {
mlog.Warn("Failed to encode to JSON", mlog.Err(jsonErr))
js, err := json.Marshal(payload)
if err != nil {
c.Logger().Warn("Failed to encode to JSON", mlog.Err(err))
}
body = bytes.NewReader(js)
contentType = "application/json"
@@ -116,7 +116,7 @@ func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayloa
a.Srv().Go(func() {
webhookResp, err := a.doOutgoingWebhookRequest(url, body, contentType)
if err != nil {
mlog.Error("Event POST failed.", mlog.Err(err))
c.Logger().Error("Event POST failed.", mlog.Err(err))
return
}
@@ -147,7 +147,7 @@ func (a *App) TriggerWebhook(c request.CTX, payload *model.OutgoingWebhookPayloa
webhookResp.IconURL = hook.IconURL
}
if _, err := a.CreateWebhookPost(c, hook.CreatorId, channel, text, webhookResp.Username, webhookResp.IconURL, "", webhookResp.Props, webhookResp.Type, postRootId); err != nil {
mlog.Error("Failed to create response post.", mlog.Err(err))
c.Logger().Error("Failed to create response post.", mlog.Err(err))
}
}
})
@@ -175,7 +175,7 @@ func (a *App) doOutgoingWebhookRequest(url string, body io.Reader, contentType s
if jsonErr == io.EOF {
return nil, nil
}
return nil, model.NewAppError("doOutgoingWebhookRequest", "api.unmarshal_error", nil, jsonErr.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("doOutgoingWebhookRequest", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
}
return &hookResp, nil

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

@@ -6,7 +6,6 @@
package app
import (
"io/ioutil"
"math/rand"
"net"
"net/http"
@@ -273,7 +272,7 @@ func generateInitialCorpus() error {
if err != nil {
return err
}
err = ioutil.WriteFile("./workdir/corpus"+strconv.Itoa(i), data, 0644)
err = os.WriteFile("./workdir/corpus"+strconv.Itoa(i), data, 0644)
if err != nil {
return err
}