Skip to content

Commit

Permalink
[MM-23900] api/server: accept loadtest.Config and control.Config for …
Browse files Browse the repository at this point in the history
…agent creation (#223)

* api/server: accept loadtest.Config and control.Config for agent creation

* api/server: reflect review comments

* docs: reflect changes on API update

* cmd/ltagent: add log initialization to server command
  • Loading branch information
isacikgoz authored Apr 13, 2020
1 parent 590bc9c commit 8618dcd
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 87 deletions.
71 changes: 45 additions & 26 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import (
"github.com/mattermost/mattermost-load-test-ng/loadtest/control/simulcontroller"
"github.com/mattermost/mattermost-load-test-ng/loadtest/store/memstore"
"github.com/mattermost/mattermost-load-test-ng/loadtest/user/userentity"
"github.com/mattermost/mattermost-load-test-ng/logger"

"github.com/gorilla/mux"
"github.com/mattermost/mattermost-server/v5/mlog"
)

// API contains information about all load tests.
Expand All @@ -42,55 +42,74 @@ func writeResponse(w http.ResponseWriter, status int, response *Response) {
}

func (a *API) createLoadAgentHandler(w http.ResponseWriter, r *http.Request) {
var config loadtest.Config
err := json.NewDecoder(r.Body).Decode(&config)
if err != nil {
writeResponse(w, http.StatusBadRequest, &Response{
Error: err.Error(),
})
return
var data struct {
LoadTestConfig loadtest.Config
SimpleControllerConfig *simplecontroller.Config `json:",omitempty"`
SimulControllerConfig *simulcontroller.Config `json:",omitempty"`
}

if err := config.IsValid(); err != nil {
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
writeResponse(w, http.StatusBadRequest, &Response{
Error: err.Error(),
Error: fmt.Sprintf("could not read request: %s", err),
})
return
}
logger.Init(&config.LogSettings)

agentId := r.FormValue("id")
if a.agents[agentId] != nil {
ltConfig := data.LoadTestConfig
if err := ltConfig.IsValid(); err != nil {
writeResponse(w, http.StatusBadRequest, &Response{
Error: fmt.Sprintf("load-test agent with id %s already exists", agentId),
Error: fmt.Sprintf("could not validate config: %s", err),
})
return
}

var ucConfig control.Config
switch config.UserControllerConfiguration.Type {
var err error
switch ltConfig.UserControllerConfiguration.Type {
case loadtest.UserControllerSimple:
// TODO: pass simplecontroller path appropriately
ucConfig, err = simplecontroller.ReadConfig("")
ucConfig = data.SimpleControllerConfig
if ucConfig == nil {
mlog.Warn("could not read controller config from the request")
ucConfig, err = simplecontroller.ReadConfig("")
}
case loadtest.UserControllerSimulative:
ucConfig, err = simulcontroller.ReadConfig("")
ucConfig = data.SimulControllerConfig
if ucConfig == nil {
mlog.Warn("clould not read controller config from the request")
ucConfig, err = simulcontroller.ReadConfig("")
}
}
if err != nil {
writeResponse(w, http.StatusBadRequest, &Response{
Error: fmt.Errorf("failed to read controller configuration: %w", err).Error(),
Error: fmt.Sprintf("could not read controller configuration: %s", err),
})
return
}
if ucConfig != nil {
if err := ucConfig.IsValid(); err != nil {
writeResponse(w, http.StatusBadRequest, &Response{
Error: fmt.Sprintf("could not validate controller configuration: %s", err),
})
return
}
}

agentId := r.FormValue("id")
if a.agents[agentId] != nil {
writeResponse(w, http.StatusBadRequest, &Response{
Error: fmt.Sprintf("load-test agent with id %s already exists", agentId),
})
return
}

newControllerFn := func(id int, status chan<- control.UserStatus) (control.UserController, error) {
ueConfig := userentity.Config{
ServerURL: config.ConnectionConfiguration.ServerURL,
WebSocketURL: config.ConnectionConfiguration.WebSocketURL,
ServerURL: ltConfig.ConnectionConfiguration.ServerURL,
WebSocketURL: ltConfig.ConnectionConfiguration.WebSocketURL,
Username: fmt.Sprintf("%s-user%d", agentId, id),
Email: fmt.Sprintf("%s-user%[email protected]", agentId, id),
Password: "testPass123$",
}
ue := userentity.New(memstore.New(), ueConfig)
switch config.UserControllerConfiguration.Type {
switch ltConfig.UserControllerConfiguration.Type {
case loadtest.UserControllerSimple:
return simplecontroller.New(id, ue, ucConfig.(*simplecontroller.Config), status)
case loadtest.UserControllerSimulative:
Expand All @@ -102,12 +121,12 @@ func (a *API) createLoadAgentHandler(w http.ResponseWriter, r *http.Request) {
}
}

lt, err := loadtest.New(&config, newControllerFn)
lt, err := loadtest.New(&ltConfig, newControllerFn)
if err != nil {
writeResponse(w, http.StatusBadRequest, &Response{
Id: agentId,
Message: "load-test agent creation failed",
Error: err.Error(),
Error: fmt.Sprintf("could not create agent: %s", err),
})
return
}
Expand Down
167 changes: 108 additions & 59 deletions api/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
package api

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"

"github.com/mattermost/mattermost-load-test-ng/loadtest"
"github.com/mattermost/mattermost-load-test-ng/loadtest/control/simplecontroller"
"github.com/mattermost/mattermost-load-test-ng/loadtest/control/simulcontroller"

"github.com/gavv/httpexpect"
"github.com/stretchr/testify/require"
)

type requestData struct {
LoadTestConfig loadtest.Config
SimpleControllerConfig *simplecontroller.Config `json:",omitempty"`
SimulControllerConfig *simulcontroller.Config `json:",omitempty"`
}

func TestAPI(t *testing.T) {
// create http.Handler
handler := SetupAPIRouter()
Expand All @@ -33,61 +39,104 @@ func TestAPI(t *testing.T) {
Expect().
Status(http.StatusNotFound)

sampleConfigBytes, _ := ioutil.ReadFile("../config/config.default.json")
var sampleConfig loadtest.Config
_ = json.Unmarshal(sampleConfigBytes, &sampleConfig)
sampleConfig.ConnectionConfiguration.ServerURL = "http://fakesitetotallydoesntexist.com"
sampleConfig.UsersConfiguration.MaxActiveUsers = 100
ltId := "lt0"
obj := e.POST("/create").WithQuery("id", ltId).WithJSON(sampleConfig).
Expect().Status(http.StatusCreated).
JSON().Object().ValueEqual("id", ltId)
rawMsg := obj.Value("message").String().Raw()
require.Equal(t, rawMsg, "load-test agent created")

obj = e.POST("/create").WithQuery("id", ltId).WithJSON(sampleConfig).
Expect().Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")
rawMsg = obj.Value("error").String().Raw()
require.Equal(t, rawMsg, fmt.Sprintf("load-test agent with id %s already exists", ltId))

e.GET(ltId + "/status").
Expect().
Status(http.StatusOK).
JSON().Object().NotContainsKey("error")

e.GET(ltId).
Expect().
Status(http.StatusOK).
JSON().Object().NotContainsKey("error")

e.POST(ltId + "/run").Expect().Status(http.StatusOK)
e.POST(ltId+"/addusers").WithQuery("amount", 10).Expect().Status(http.StatusOK)
e.POST(ltId+"/removeusers").WithQuery("amount", 3).Expect().Status(http.StatusOK)
e.POST(ltId+"/addusers").WithQuery("amount", 0).Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId+"/addusers").WithQuery("amount", -2).Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId+"/addusers").WithQuery("amount", "bad").Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId+"/removeusers").WithQuery("amount", 0).Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId+"/removeusers").WithQuery("amount", -2).Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId+"/removeusers").WithQuery("amount", "bad").Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId + "/stop").Expect().Status(http.StatusOK)
e.DELETE(ltId).Expect().Status(http.StatusOK)
ltConfig, err := loadtest.ReadConfig("../config/config.default.json")
require.NoError(t, err)

ltConfig.ConnectionConfiguration.ServerURL = "http://fakesitetotallydoesntexist.com"
ltConfig.UsersConfiguration.MaxActiveUsers = 100

ucConfig1, err := simplecontroller.ReadConfig("../config/simplecontroller.default.json")
require.NoError(t, err)

ucConfig2, err := simulcontroller.ReadConfig("../config/simulcontroller.default.json")
require.NoError(t, err)

t.Run("test with loadtest.Config only", func(t *testing.T) {
rd := requestData{
LoadTestConfig: *ltConfig,
}
ltId := "lt0"
obj := e.POST("/create").WithQuery("id", ltId).WithJSON(rd).
Expect().Status(http.StatusCreated).
JSON().Object().ValueEqual("id", ltId)
rawMsg := obj.Value("message").String().Raw()
require.Equal(t, rawMsg, "load-test agent created")

e.GET(ltId + "/status").
Expect().
Status(http.StatusOK).
JSON().Object().NotContainsKey("error")

e.GET(ltId).
Expect().
Status(http.StatusOK).
JSON().Object().NotContainsKey("error")

obj = e.POST("/create").WithQuery("id", ltId).WithJSON(rd).
Expect().Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")
rawMsg = obj.Value("error").String().Raw()
require.Equal(t, fmt.Sprintf("load-test agent with id %s already exists", ltId), rawMsg)

e.POST(ltId + "/run").Expect().Status(http.StatusOK)
e.POST(ltId+"/addusers").WithQuery("amount", 10).Expect().Status(http.StatusOK)
e.POST(ltId+"/removeusers").WithQuery("amount", 3).Expect().Status(http.StatusOK)
e.POST(ltId+"/addusers").WithQuery("amount", 0).Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId+"/addusers").WithQuery("amount", -2).Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId+"/addusers").WithQuery("amount", "bad").Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId+"/removeusers").WithQuery("amount", 0).Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId+"/removeusers").WithQuery("amount", -2).Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId+"/removeusers").WithQuery("amount", "bad").Expect().
Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")

e.POST(ltId + "/stop").Expect().Status(http.StatusOK)
e.DELETE(ltId).Expect().Status(http.StatusOK)
})

t.Run("start agent with a simplecontroller.Config", func(t *testing.T) {
rd := requestData{
LoadTestConfig: *ltConfig,
SimpleControllerConfig: ucConfig1,
}
ltId := "lt1"
obj := e.POST("/create").WithQuery("id", ltId).WithJSON(rd).
Expect().Status(http.StatusCreated).
JSON().Object().ValueEqual("id", ltId)
rawMsg := obj.Value("message").String().Raw()
require.Equal(t, rawMsg, "load-test agent created")
e.POST(ltId + "/stop").Expect().Status(http.StatusOK)
e.DELETE(ltId).Expect().Status(http.StatusOK)
})

t.Run("start agent with simulcontroller.Config", func(t *testing.T) {
ltConfig.UserControllerConfiguration.Type = loadtest.UserControllerSimulative
rd := requestData{
LoadTestConfig: *ltConfig,
SimulControllerConfig: ucConfig2,
}
ltId := "lt2"
obj := e.POST("/create").WithQuery("id", ltId).WithJSON(rd).
Expect().Status(http.StatusCreated).
JSON().Object().ValueEqual("id", ltId)
rawMsg := obj.Value("message").String().Raw()
require.Equal(t, rawMsg, "load-test agent created")
e.POST(ltId + "/stop").Expect().Status(http.StatusOK)
e.DELETE(ltId).Expect().Status(http.StatusOK)
})
}
12 changes: 12 additions & 0 deletions cmd/ltagent/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,25 @@ import (
"net/http"

"github.com/mattermost/mattermost-load-test-ng/api"
"github.com/mattermost/mattermost-load-test-ng/logger"

"github.com/mattermost/mattermost-server/v5/mlog"
"github.com/spf13/cobra"
)

func RunServerCmdF(cmd *cobra.Command, args []string) error {
port, _ := cmd.Flags().GetInt("port")

logger.Init(&logger.Settings{
EnableConsole: true,
ConsoleLevel: "INFO",
ConsoleJson: false,
EnableFile: true,
FileLevel: "INFO",
FileJson: true,
FileLocation: "ltagent.log",
})

mlog.Info("API server started, listening on", mlog.Int("port", port))
return http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), api.SetupAPIRouter())
}
Expand Down
27 changes: 26 additions & 1 deletion coordinator/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (

"github.com/mattermost/mattermost-load-test-ng/api"
"github.com/mattermost/mattermost-load-test-ng/loadtest"
"github.com/mattermost/mattermost-load-test-ng/loadtest/control/simplecontroller"
"github.com/mattermost/mattermost-load-test-ng/loadtest/control/simulcontroller"

"github.com/mattermost/mattermost-server/v5/mlog"
)
Expand Down Expand Up @@ -77,7 +79,30 @@ func (a *LoadAgent) RemoveUsers(n int) error {

func (a *LoadAgent) Start() error {
a.config.LoadTestConfig.UsersConfiguration.InitialActiveUsers = 0
configData, err := json.Marshal(a.config.LoadTestConfig)
var data = struct {
LoadTestConfig loadtest.Config
SimpleControllerConfig *simplecontroller.Config `json:",omitempty"`
SimulControllerConfig *simulcontroller.Config `json:",omitempty"`
}{
LoadTestConfig: a.config.LoadTestConfig,
}

var err error
switch a.config.LoadTestConfig.UserControllerConfiguration.Type {
case loadtest.UserControllerSimple:
var scc *simplecontroller.Config
scc, err = simplecontroller.ReadConfig("")
data.SimpleControllerConfig = scc
case loadtest.UserControllerSimulative:
var scc *simulcontroller.Config
scc, err = simulcontroller.ReadConfig("")
data.SimulControllerConfig = scc
}
if err != nil {
return err
}

configData, err := json.MarshalIndent(data, "", " ")
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 8618dcd

Please sign in to comment.