summaryrefslogtreecommitdiffstats
path: root/app/command_loadtest.go
diff options
context:
space:
mode:
authorJoram Wilander <jwawilander@gmail.com>2017-03-13 09:23:16 -0400
committerChristopher Speller <crspeller@gmail.com>2017-03-13 09:23:16 -0400
commite9c6cc269b5c9fe82e5f38d63344a07365bccd6b (patch)
tree711fefd3511dbd5a7f1a20225f00b766eb4808f7 /app/command_loadtest.go
parent8b0eedbbcd47ba09142c72a71969840aa6e121d2 (diff)
downloadchat-e9c6cc269b5c9fe82e5f38d63344a07365bccd6b.tar.gz
chat-e9c6cc269b5c9fe82e5f38d63344a07365bccd6b.tar.bz2
chat-e9c6cc269b5c9fe82e5f38d63344a07365bccd6b.zip
Move command logic into app layer (#5617)
Diffstat (limited to 'app/command_loadtest.go')
-rw-r--r--app/command_loadtest.go437
1 files changed, 437 insertions, 0 deletions
diff --git a/app/command_loadtest.go b/app/command_loadtest.go
new file mode 100644
index 000000000..d3c7474ae
--- /dev/null
+++ b/app/command_loadtest.go
@@ -0,0 +1,437 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package app
+
+import (
+ "io"
+ "net/http"
+ "path"
+ "strconv"
+ "strings"
+
+ l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
+ goi18n "github.com/nicksnyder/go-i18n/i18n"
+)
+
+var usage = `Mattermost load testing commands to help configure the system
+
+ COMMANDS:
+
+ Setup - Creates a testing environment in current team.
+ /loadtest setup [teams] [fuzz] <Num Channels> <Num Users> <NumPosts>
+
+ Example:
+ /loadtest setup teams fuzz 10 20 50
+
+ Users - Add a specified number of random users with fuzz text to current team.
+ /loadtest users [fuzz] <Min Users> <Max Users>
+
+ Example:
+ /loadtest users fuzz 5 10
+
+ Channels - Add a specified number of random channels with fuzz text to current team.
+ /loadtest channels [fuzz] <Min Channels> <Max Channels>
+
+ Example:
+ /loadtest channels fuzz 5 10
+
+ Posts - Add some random posts with fuzz text to current channel.
+ /loadtest posts [fuzz] <Min Posts> <Max Posts> <Max Images>
+
+ Example:
+ /loadtest posts fuzz 5 10 3
+
+ Url - Add a post containing the text from a given url to current channel.
+ /loadtest url
+
+ Example:
+ /loadtest http://www.example.com/sample_file.md
+
+ Json - Add a post using the JSON file as payload to the current channel.
+ /loadtest json url
+
+ Example
+ /loadtest json http://www.example.com/sample_body.json
+
+`
+
+const (
+ CMD_LOADTEST = "loadtest"
+)
+
+type LoadTestProvider struct {
+}
+
+func init() {
+ if !utils.Cfg.ServiceSettings.EnableTesting {
+ RegisterCommandProvider(&LoadTestProvider{})
+ }
+}
+
+func (me *LoadTestProvider) GetTrigger() string {
+ return CMD_LOADTEST
+}
+
+func (me *LoadTestProvider) GetCommand(T goi18n.TranslateFunc) *model.Command {
+ return &model.Command{
+ Trigger: CMD_LOADTEST,
+ AutoComplete: false,
+ AutoCompleteDesc: "Debug Load Testing",
+ AutoCompleteHint: "help",
+ DisplayName: "loadtest",
+ }
+}
+
+func (me *LoadTestProvider) DoCommand(args *model.CommandArgs, message string) *model.CommandResponse {
+ //This command is only available when EnableTesting is true
+ if !utils.Cfg.ServiceSettings.EnableTesting {
+ return &model.CommandResponse{}
+ }
+
+ if strings.HasPrefix(message, "setup") {
+ return me.SetupCommand(args, message)
+ }
+
+ if strings.HasPrefix(message, "users") {
+ return me.UsersCommand(args, message)
+ }
+
+ if strings.HasPrefix(message, "channels") {
+ return me.ChannelsCommand(args, message)
+ }
+
+ if strings.HasPrefix(message, "posts") {
+ return me.PostsCommand(args, message)
+ }
+
+ if strings.HasPrefix(message, "url") {
+ return me.UrlCommand(args, message)
+ }
+ if strings.HasPrefix(message, "json") {
+ return me.JsonCommand(args, message)
+ }
+ return me.HelpCommand(args, message)
+}
+
+func (me *LoadTestProvider) HelpCommand(args *model.CommandArgs, message string) *model.CommandResponse {
+ return &model.CommandResponse{Text: usage, ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) SetupCommand(args *model.CommandArgs, message string) *model.CommandResponse {
+ tokens := strings.Fields(strings.TrimPrefix(message, "setup"))
+ doTeams := contains(tokens, "teams")
+ doFuzz := contains(tokens, "fuzz")
+
+ numArgs := 0
+ if doTeams {
+ numArgs++
+ }
+ if doFuzz {
+ numArgs++
+ }
+
+ var numTeams int
+ var numChannels int
+ var numUsers int
+ var numPosts int
+
+ // Defaults
+ numTeams = 10
+ numChannels = 10
+ numUsers = 10
+ numPosts = 10
+
+ if doTeams {
+ if (len(tokens) - numArgs) >= 4 {
+ numTeams, _ = strconv.Atoi(tokens[numArgs+0])
+ numChannels, _ = strconv.Atoi(tokens[numArgs+1])
+ numUsers, _ = strconv.Atoi(tokens[numArgs+2])
+ numPosts, _ = strconv.Atoi(tokens[numArgs+3])
+ }
+ } else {
+ if (len(tokens) - numArgs) >= 3 {
+ numChannels, _ = strconv.Atoi(tokens[numArgs+0])
+ numUsers, _ = strconv.Atoi(tokens[numArgs+1])
+ numPosts, _ = strconv.Atoi(tokens[numArgs+2])
+ }
+ }
+ client := model.NewClient(args.SiteURL)
+
+ if doTeams {
+ if err := CreateBasicUser(client); err != nil {
+ return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+ client.Login(BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
+ environment, err := CreateTestEnvironmentWithTeams(
+ client,
+ utils.Range{Begin: numTeams, End: numTeams},
+ utils.Range{Begin: numChannels, End: numChannels},
+ utils.Range{Begin: numUsers, End: numUsers},
+ utils.Range{Begin: numPosts, End: numPosts},
+ doFuzz)
+ if err != true {
+ return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else {
+ l4g.Info("Testing environment created")
+ for i := 0; i < len(environment.Teams); i++ {
+ l4g.Info("Team Created: " + environment.Teams[i].Name)
+ l4g.Info("\t User to login: " + environment.Environments[i].Users[0].Email + ", " + USER_PASSWORD)
+ }
+ }
+ } else {
+
+ var team *model.Team
+ if tr := <-Srv.Store.Team().Get(args.TeamId); tr.Err != nil {
+ return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else {
+ team = tr.Data.(*model.Team)
+ }
+
+ client.MockSession(args.Session.Token)
+ client.SetTeamId(args.TeamId)
+ CreateTestEnvironmentInTeam(
+ client,
+ team,
+ utils.Range{Begin: numChannels, End: numChannels},
+ utils.Range{Begin: numUsers, End: numUsers},
+ utils.Range{Begin: numPosts, End: numPosts},
+ doFuzz)
+ }
+
+ return &model.CommandResponse{Text: "Created enviroment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) UsersCommand(args *model.CommandArgs, message string) *model.CommandResponse {
+ cmd := strings.TrimSpace(strings.TrimPrefix(message, "users"))
+
+ doFuzz := false
+ if strings.Index(cmd, "fuzz") == 0 {
+ doFuzz = true
+ cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
+ }
+
+ usersr, err := parseRange(cmd, "")
+ if err == false {
+ usersr = utils.Range{Begin: 2, End: 5}
+ }
+
+ var team *model.Team
+ if tr := <-Srv.Store.Team().Get(args.TeamId); tr.Err != nil {
+ return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else {
+ team = tr.Data.(*model.Team)
+ }
+
+ client := model.NewClient(args.SiteURL)
+ client.SetTeamId(team.Id)
+ userCreator := NewAutoUserCreator(client, team)
+ userCreator.Fuzzy = doFuzz
+ userCreator.CreateTestUsers(usersr)
+
+ return &model.CommandResponse{Text: "Added users", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) ChannelsCommand(args *model.CommandArgs, message string) *model.CommandResponse {
+ cmd := strings.TrimSpace(strings.TrimPrefix(message, "channels"))
+
+ doFuzz := false
+ if strings.Index(cmd, "fuzz") == 0 {
+ doFuzz = true
+ cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
+ }
+
+ channelsr, err := parseRange(cmd, "")
+ if err == false {
+ channelsr = utils.Range{Begin: 2, End: 5}
+ }
+
+ var team *model.Team
+ if tr := <-Srv.Store.Team().Get(args.TeamId); tr.Err != nil {
+ return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else {
+ team = tr.Data.(*model.Team)
+ }
+
+ client := model.NewClient(args.SiteURL)
+ client.SetTeamId(team.Id)
+ client.MockSession(args.Session.Token)
+ channelCreator := NewAutoChannelCreator(client, team)
+ channelCreator.Fuzzy = doFuzz
+ channelCreator.CreateTestChannels(channelsr)
+
+ return &model.CommandResponse{Text: "Added channels", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) PostsCommand(args *model.CommandArgs, message string) *model.CommandResponse {
+ cmd := strings.TrimSpace(strings.TrimPrefix(message, "posts"))
+
+ doFuzz := false
+ if strings.Index(cmd, "fuzz") == 0 {
+ doFuzz = true
+ cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
+ }
+
+ postsr, err := parseRange(cmd, "")
+ if err == false {
+ postsr = utils.Range{Begin: 20, End: 30}
+ }
+
+ tokens := strings.Fields(cmd)
+ rimages := utils.Range{Begin: 0, End: 0}
+ if len(tokens) >= 3 {
+ if numImages, err := strconv.Atoi(tokens[2]); err == nil {
+ rimages = utils.Range{Begin: numImages, End: numImages}
+ }
+ }
+
+ var usernames []string
+ if result := <-Srv.Store.User().GetProfiles(args.TeamId, 0, 1000); result.Err == nil {
+ profileUsers := result.Data.([]*model.User)
+ usernames = make([]string, len(profileUsers))
+ i := 0
+ for _, userprof := range profileUsers {
+ usernames[i] = userprof.Username
+ i++
+ }
+ }
+
+ client := model.NewClient(args.SiteURL)
+ client.SetTeamId(args.TeamId)
+ client.MockSession(args.Session.Token)
+ testPoster := NewAutoPostCreator(client, args.ChannelId)
+ testPoster.Fuzzy = doFuzz
+ testPoster.Users = usernames
+
+ numImages := utils.RandIntFromRange(rimages)
+ numPosts := utils.RandIntFromRange(postsr)
+ for i := 0; i < numPosts; i++ {
+ testPoster.HasImage = (i < numImages)
+ testPoster.CreateRandomPost()
+ }
+
+ return &model.CommandResponse{Text: "Added posts", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) UrlCommand(args *model.CommandArgs, message string) *model.CommandResponse {
+ url := strings.TrimSpace(strings.TrimPrefix(message, "url"))
+ if len(url) == 0 {
+ return &model.CommandResponse{Text: "Command must contain a url", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+
+ // provide a shortcut to easily access tests stored in doc/developer/tests
+ if !strings.HasPrefix(url, "http") {
+ url = "https://raw.githubusercontent.com/mattermost/platform/master/tests/" + url
+
+ if path.Ext(url) == "" {
+ url += ".md"
+ }
+ }
+
+ var contents io.ReadCloser
+ if r, err := http.Get(url); err != nil {
+ return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else if r.StatusCode > 400 {
+ return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else {
+ contents = r.Body
+ }
+
+ bytes := make([]byte, 4000)
+
+ // break contents into 4000 byte posts
+ for {
+ length, err := contents.Read(bytes)
+ if err != nil && err != io.EOF {
+ return &model.CommandResponse{Text: "Encountered error reading file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+
+ if length == 0 {
+ break
+ }
+
+ post := &model.Post{}
+ post.Message = string(bytes[:length])
+ post.ChannelId = args.ChannelId
+ post.UserId = args.UserId
+
+ if _, err := CreatePost(post, args.TeamId, false); err != nil {
+ return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+ }
+
+ return &model.CommandResponse{Text: "Loaded data", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) JsonCommand(args *model.CommandArgs, message string) *model.CommandResponse {
+ url := strings.TrimSpace(strings.TrimPrefix(message, "json"))
+ if len(url) == 0 {
+ return &model.CommandResponse{Text: "Command must contain a url", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+
+ // provide a shortcut to easily access tests stored in doc/developer/tests
+ if !strings.HasPrefix(url, "http") {
+ url = "https://raw.githubusercontent.com/mattermost/platform/master/tests/" + url
+
+ if path.Ext(url) == "" {
+ url += ".json"
+ }
+ }
+
+ var contents io.ReadCloser
+ if r, err := http.Get(url); err != nil {
+ return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else if r.StatusCode > 400 {
+ return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else {
+ contents = r.Body
+ }
+
+ post := model.PostFromJson(contents)
+ post.ChannelId = args.ChannelId
+ post.UserId = args.UserId
+ if post.Message == "" {
+ post.Message = message
+ }
+
+ if _, err := CreatePost(post, args.TeamId, false); err != nil {
+ return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+ return &model.CommandResponse{Text: "Loaded data", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func parseRange(command string, cmd string) (utils.Range, bool) {
+ tokens := strings.Fields(strings.TrimPrefix(command, cmd))
+ var begin int
+ var end int
+ var err1 error
+ var err2 error
+ switch {
+ case len(tokens) == 1:
+ begin, err1 = strconv.Atoi(tokens[0])
+ end = begin
+ if err1 != nil {
+ return utils.Range{Begin: 0, End: 0}, false
+ }
+ case len(tokens) >= 2:
+ begin, err1 = strconv.Atoi(tokens[0])
+ end, err2 = strconv.Atoi(tokens[1])
+ if err1 != nil || err2 != nil {
+ return utils.Range{Begin: 0, End: 0}, false
+ }
+ default:
+ return utils.Range{Begin: 0, End: 0}, false
+ }
+ return utils.Range{Begin: begin, End: end}, true
+}
+
+func contains(items []string, token string) bool {
+ for _, elem := range items {
+ if elem == token {
+ return true
+ }
+ }
+ return false
+}