Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Channel bookmarks to loadtest #828

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/agent_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ func createFakeMMServer() *httptest.Server {
case "/api/v4/config":
mmCfg := model.Config{}
mmCfg.SetDefaults()
mmCfg.TeamSettings.MaxUsersPerTeam = model.NewInt(10000)
maxUsers := 10000
mmCfg.TeamSettings.MaxUsersPerTeam = &maxUsers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohhh my dear ..

https://hub.mattermost.com/private-core/pl/hih74enmn7bwunmy6eamgct67r

Let's fix it everywhere else as well :)

I also mentioned this additionally in the dev meeting, but probably I could have done a better job.

json.NewEncoder(w).Encode(mmCfg)
case "/api/v4/emoji":
json.NewEncoder(w).Encode(&model.Emoji{})
Expand Down
2 changes: 1 addition & 1 deletion cmd/ltctl/collect.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func collect(config deployment.Config, deploymentId string, outputName string) e
if err := json.Unmarshal(input, &cfg); err != nil {
return nil, fmt.Errorf("failed to unmarshal MM configuration: %w", err)
}
cfg.Sanitize()
cfg.Sanitize([]*model.Manifest{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can just be nil

sanitizedCfg, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return nil, fmt.Errorf("failed to sanitize MM configuration: %w", err)
Expand Down
6 changes: 3 additions & 3 deletions deployment/terraform/cloudwatchlogs.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

awsconfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
"github.com/mattermost/mattermost/server/public/model"
)

const (
Expand Down Expand Up @@ -140,9 +139,10 @@ func (t *Terraform) createCloudWatchLogsPolicy() error {
}
docJsonStr := string(docJsonBytes)

policyName := "lt-cloudwatch-log-policy"
input := cloudwatchlogs.PutResourcePolicyInput{
PolicyName: model.NewString("lt-cloudwatch-log-policy"),
PolicyDocument: model.NewString(docJsonStr),
PolicyName: &policyName,
PolicyDocument: &docJsonStr,
}
if _, err := cwclient.PutResourcePolicy(context.Background(), &input); err != nil {
return fmt.Errorf("failed to create CloudWatchLogs policy; it can be manually created by running `aws logs put-resource-policy --policy-name lt-cloudwatch-log-policy --policy-document %q`; the next `deployment create` should work when such a policy is present in the AWS account; original error: %w", docJsonStr, err)
Expand Down
8 changes: 5 additions & 3 deletions deployment/terraform/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -1072,10 +1072,12 @@ func (t *Terraform) updateAppConfig(siteURL string, sshc *ssh.Client, jobServerE
}

if t.output.HasRedis() {
cfg.CacheSettings.CacheType = model.NewString(model.CacheTypeRedis)
cacheRedis := model.CacheTypeRedis
redisDB := 0
cfg.CacheSettings.CacheType = &cacheRedis
redisEndpoint := net.JoinHostPort(t.output.RedisServer.Address, strconv.Itoa(t.output.RedisServer.Port))
cfg.CacheSettings.RedisAddress = model.NewString(redisEndpoint)
cfg.CacheSettings.RedisDB = model.NewInt(0)
cfg.CacheSettings.RedisAddress = &redisEndpoint
cfg.CacheSettings.RedisDB = &redisDB
}

if t.config.MattermostConfigPatchFile != "" {
Expand Down
1 change: 1 addition & 0 deletions deployment/terraform/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Group=ubuntu
LimitNOFILE=49152
Environment=MM_FEATUREFLAGS_POSTPRIORITY=true
Environment=MM_FEATUREFLAGS_WEBSOCKETEVENTSCOPE=true
Environment=MM_FEATUREFLAGS_CHANNELBOOKMARKS=true
Environment=MM_SERVICEENVIRONMENT=%s

[Install]
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/mattermost/mattermost-load-test-ng

go 1.22
go 1.22.0
agarciamontoro marked this conversation as resolved.
Show resolved Hide resolved

toolchain go1.22.8

Expand Down Expand Up @@ -32,8 +32,8 @@ require (
github.com/gliderlabs/ssh v0.1.1
github.com/grafana/alloy/syntax v0.1.0
github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a
github.com/mattermost/mattermost/server/public v0.1.7-0.20240806035841-540febd866aa
github.com/mattermost/mattermost/server/v8 v8.0.0-20240726090344-5547504c1d68
github.com/mattermost/mattermost/server/public v0.1.8-0.20241015185928-63c97f5a6d8f
github.com/mattermost/mattermost/server/v8 v8.0.0-20241015185928-63c97f5a6d8f
github.com/opensearch-project/opensearch-go/v4 v4.1.0
github.com/pelletier/go-toml/v2 v2.2.2
github.com/vmihailenco/msgpack/v5 v5.4.1
Expand Down Expand Up @@ -141,7 +141,7 @@ require (
github.com/yudai/pp v2.0.1+incompatible // indirect
github.com/yuin/goldmark v1.7.4 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240725223205-93522f1f2a9f // indirect
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,10 @@ github.com/mattermost/ldap v3.0.4+incompatible h1:SOeNnz+JNR+foQ3yHkYqijb9MLPhXN
github.com/mattermost/ldap v3.0.4+incompatible/go.mod h1:b4reDCcGpBxJ4WX0f224KFY+OR0npin7or7EFpeIko4=
github.com/mattermost/logr/v2 v2.0.21 h1:CMHsP+nrbRlEC4g7BwOk1GAnMtHkniFhlSQPXy52be4=
github.com/mattermost/logr/v2 v2.0.21/go.mod h1:kZkB/zqKL9e+RY5gB3vGpsyenC+TpuiOenjMkvJJbzc=
github.com/mattermost/mattermost/server/public v0.1.7-0.20240806035841-540febd866aa h1:KB8BE35ITSzVaCBoMj3NP9WHiJi/PLJD0Hgw5N9QpNM=
github.com/mattermost/mattermost/server/public v0.1.7-0.20240806035841-540febd866aa/go.mod h1:Dm5uf3z8ckDOKYD1cbnb1Uqm/G9WYIaouSP/HnH+Rbs=
github.com/mattermost/mattermost/server/v8 v8.0.0-20240726090344-5547504c1d68 h1:5EoqvfOGhU+b0E2h4XdMxtCReyGZVBIWBiYYk6WTEoU=
github.com/mattermost/mattermost/server/v8 v8.0.0-20240726090344-5547504c1d68/go.mod h1:bCPUN2odATk4v9Gox88gBphLhnrJfEvqNQtaFVVVso4=
github.com/mattermost/mattermost/server/public v0.1.8-0.20241015185928-63c97f5a6d8f h1:NUAf56HZHFLayAyCqHxeVLmxJUN9xw3Qxc9m3ghy+Xw=
github.com/mattermost/mattermost/server/public v0.1.8-0.20241015185928-63c97f5a6d8f/go.mod h1:SkTKbMul91Rq0v2dIxe8mqzUOY+3KwlwwLmAlxDfGCk=
github.com/mattermost/mattermost/server/v8 v8.0.0-20241015185928-63c97f5a6d8f h1:h4T89Qkb3kddGnRN7xQAuB+fDaL3OgUgc5YPmssmzj8=
github.com/mattermost/mattermost/server/v8 v8.0.0-20241015185928-63c97f5a6d8f/go.mod h1:zwMZYGK4/xDy/kyepVBqXhM58YMTCRlanv/uKrZzJdw=
github.com/mattermost/morph v1.1.0 h1:Q9vrJbeM3s2jfweGheq12EFIzdNp9a/6IovcbvOQ6Cw=
github.com/mattermost/morph v1.1.0/go.mod h1:gD+EaqX2UMyyuzmF4PFh4r33XneQ8Nzi+0E8nXjMa3A=
github.com/mattermost/squirrel v0.4.0 h1:azf9LZ+8JUTAvwt/njB1utkPqWQ6e7Rje2ya5N0P2i4=
Expand Down Expand Up @@ -586,8 +586,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
Expand Down
21 changes: 16 additions & 5 deletions loadtest/control/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,14 +249,16 @@ func CreateAckPost(u user.User) UserActionResponse {
return UserActionResponse{Err: NewUserError(err)}
}

var urgent string = model.PostPriorityUrgent
var ack bool = true
postId, err := u.CreatePost(&model.Post{
Message: "Priority Post Lorem ipsum dolor sit amet, consectetur adipiscing elit",
ChannelId: channel.Id,
CreateAt: time.Now().UnixMilli(),
Metadata: &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewString(model.PostPriorityUrgent),
RequestedAck: model.NewBool(true),
Priority: &urgent,
RequestedAck: &ack,
agarciamontoro marked this conversation as resolved.
Show resolved Hide resolved
},
},
})
Expand Down Expand Up @@ -339,16 +341,19 @@ func CreatePersistentNotificationPost(u user.User) UserActionResponse {
return UserActionResponse{Info: fmt.Sprintf("user has empty username: %s", mentionedUser.Id)}
}

var urgent string = model.PostPriorityUrgent
var ack bool = false
var persistent bool = true
postId, err := u.CreatePost(&model.Post{
Message: fmt.Sprintf("Persistent Notification Post mention @%s", mentionedUser.Username),
UserId: postOwnerID,
ChannelId: channel.Id,
CreateAt: time.Now().UnixMilli(),
Metadata: &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewString(model.PostPriorityUrgent),
RequestedAck: model.NewBool(false),
PersistentNotifications: model.NewBool(true),
Priority: &urgent,
RequestedAck: &ack,
PersistentNotifications: &persistent,
},
},
})
Expand Down Expand Up @@ -1032,6 +1037,12 @@ func DraftsEnabled(u user.User) (bool, UserActionResponse) {
return allow, UserActionResponse{}
}

func ChannelBookmarkEnabled(u user.User) (bool, UserActionResponse) {
allow := u.Store().FeatureFlags()["ChannelBookmarks"]

return allow, UserActionResponse{}
}

// MessageExport simulates the given user performing
// a compliance message export
func MessageExport(u user.User) UserActionResponse {
Expand Down
8 changes: 5 additions & 3 deletions loadtest/control/gencontroller/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,12 @@ func (c *GenController) createPost(u user.User) (res control.UserActionResponse)

if isUrgent {
post.Metadata = &model.PostMetadata{}
var urgent string = "urgent"
var falseValue bool = false
post.Metadata.Priority = &model.PostPriority{
Priority: model.NewString("urgent"),
RequestedAck: model.NewBool(false),
PersistentNotifications: model.NewBool(false),
Priority: &urgent,
RequestedAck: &falseValue,
PersistentNotifications: &falseValue,
}
}

Expand Down
14 changes: 11 additions & 3 deletions loadtest/control/simulcontroller/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,12 @@ func switchChannel(u user.User) control.UserActionResponse {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

if u.Store().FeatureFlags()["ChannelBookmarks"] {
if err := u.GetChannelBookmarks(channel.Id, 0); err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}
}

if u.Store().FeatureFlags()["WebSocketEventScope"] {
if err := u.UpdateActiveChannel(channel.Id); err != nil {
mlog.Warn("Failed to update active channel", mlog.String("channel_id", channel.Id))
Expand Down Expand Up @@ -794,11 +800,13 @@ func (c *SimulController) createPost(u user.User) control.UserActionResponse {
}

if isUrgent {
var urgent string = "urgent"
var falseValue bool = false
post.Metadata = &model.PostMetadata{}
post.Metadata.Priority = &model.PostPriority{
Priority: model.NewString("urgent"),
RequestedAck: model.NewBool(false),
PersistentNotifications: model.NewBool(false),
Priority: &urgent,
RequestedAck: &falseValue,
PersistentNotifications: &falseValue,
}
}

Expand Down
162 changes: 162 additions & 0 deletions loadtest/control/simulcontroller/bookmarks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright (c) 2019-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

package simulcontroller

import (
"fmt"
"math/rand"

"github.com/mattermost/mattermost-load-test-ng/loadtest/control"
"github.com/mattermost/mattermost-load-test-ng/loadtest/user"
"github.com/mattermost/mattermost/server/public/model"
)

var (
bookmarkNames = []string{"this is a file", "this is a link", "this is another file", "this is another link"}
bookmarkType = []model.ChannelBookmarkType{model.ChannelBookmarkLink, model.ChannelBookmarkFile}
)

func (c *SimulController) addBookmark(u user.User) control.UserActionResponse {
channel, err := u.Store().CurrentChannel()
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

emoji := ""
// 10% of the times bookmarks will have an emoji assigned.
agnivade marked this conversation as resolved.
Show resolved Hide resolved
if rand.Float64() < 0.1 {
emoji = control.RandomEmoji()
}

bookmark := &model.ChannelBookmark{
ChannelId: channel.Id,
DisplayName: control.PickRandomString(bookmarkNames),
Emoji: emoji,
Type: bookmarkType[rand.Intn(len(bookmarkType))],
}

if bookmark.Type == model.ChannelBookmarkFile {
control.AttachFileToBookmark(u, bookmark)
} else {
bookmark.LinkUrl = control.RandomLink()
}

err = u.AddChannelBookmark(channel.Id, bookmark)
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

return control.UserActionResponse{Info: fmt.Sprintf("bookmark created in channel id %v", channel.Id)}
}

func (c *SimulController) AddChannelBookmark(u user.User) control.UserActionResponse {
if ok, resp := control.ChannelBookmarkEnabled(u); resp.Err != nil {
return resp
} else if !ok {
return control.UserActionResponse{Info: "channel bookmarks not enabled"}
}

return c.addBookmark(u)
}

func (c *SimulController) UpdateOrAddBookmark(u user.User) control.UserActionResponse {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the server API, there is no updateOrAdd API. Just individual update and add. Could we do the same here? Or do we need to keep the add logic if there are no bookmarks?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can just make sure that if the channel has no bookmarks, nothing happens

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, let's do that.

if ok, resp := control.ChannelBookmarkEnabled(u); resp.Err != nil {
return resp
} else if !ok {
return control.UserActionResponse{Info: "channel bookmarks not enabled"}
}

channel, err := u.Store().CurrentChannel()
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

currentBookmarks := u.Store().ChannelBookmarks(channel.Id)
if len(currentBookmarks) > 0 {
// here we update
bookmark := currentBookmarks[rand.Intn(len(currentBookmarks))]
if bookmark != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this check necessary?

bookmarkWithFileInfo := bookmark.Clone()
bookmarkWithFileInfo.DisplayName = control.PickRandomString(bookmarkNames)

// 10% of the times bookmarks will have an emoji assigned.
if bookmarkWithFileInfo.Emoji == "" && rand.Float64() < 0.1 {
bookmarkWithFileInfo.Emoji = control.RandomEmoji()
}

if bookmarkWithFileInfo.Type == model.ChannelBookmarkFile {
control.AttachFileToBookmark(u, bookmarkWithFileInfo.ChannelBookmark)
} else {
bookmarkWithFileInfo.LinkUrl = control.RandomLink()
}

err = u.UpdateChannelBookmark(bookmarkWithFileInfo)

if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

return control.UserActionResponse{Info: fmt.Sprintf("bookmark %v updated in channel id %v", bookmarkWithFileInfo.Id, channel.Id)}
}
}

return c.addBookmark(u)
}

func (c *SimulController) DeleteBookmark(u user.User) control.UserActionResponse {
if ok, resp := control.ChannelBookmarkEnabled(u); resp.Err != nil {
return resp
} else if !ok {
return control.UserActionResponse{Info: "channel bookmarks not enabled"}
}

channel, err := u.Store().CurrentChannel()
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

currentBookmarks := u.Store().ChannelBookmarks(channel.Id)
if len(currentBookmarks) > 0 {
bookmark := currentBookmarks[rand.Intn(len(currentBookmarks))]
if bookmark != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above

err = u.DeleteChannelBookmark(bookmark.ChannelId, bookmark.Id)
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

return control.UserActionResponse{Info: fmt.Sprintf("bookmark id %v deleted in channel id %v", bookmark.Id, channel.Id)}
}
}

return control.UserActionResponse{Info: "no channel bookmarks deleted"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a better string would be "no channel bookmarks found"?

}

func (c *SimulController) UpdateBookmarksSortOrder(u user.User) control.UserActionResponse {
if ok, resp := control.ChannelBookmarkEnabled(u); resp.Err != nil {
return resp
} else if !ok {
return control.UserActionResponse{Info: "channel bookmarks not enabled"}
}

channel, err := u.Store().CurrentChannel()
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

currentBookmarks := u.Store().ChannelBookmarks(channel.Id)
if len(currentBookmarks) > 1 {
bookmark := currentBookmarks[rand.Intn(len(currentBookmarks))]
newIndex := rand.Int63n(int64(len(currentBookmarks)))
if bookmark != nil {
err = u.UpdateChannelBookmarkSortOrder(channel.Id, bookmark.Id, newIndex)
if err != nil {
return control.UserActionResponse{Err: control.NewUserError(err)}
}

return control.UserActionResponse{Info: fmt.Sprintf("bookmark id %v in channel id %v sorted at index %d", bookmark.Id, channel.Id, newIndex)}
}
}

return control.UserActionResponse{Info: "not enough channel bookmarks to sort"}
}
Loading
Loading