Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
19 changes: 19 additions & 0 deletions api/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,25 @@ func (a *api) createLoadAgentHandler(w http.ResponseWriter, r *http.Request) {
mlog.Warn("failed to detect agent_type. Going ahead assuming it's a server agent", mlog.Err(err))
}

// Read and validate the browsercontroller.json that was uploaded by
// Terraform to confirm it landed correctly and contains valid values
// before proceeding with browser agent creation if it's a browser agent instance.
if isBAInstance {
bccfg, err := browsercontroller.ReadConfig("./config/browsercontroller.json")
if err != nil {
writeAgentResponse(w, http.StatusBadRequest, &client.AgentResponse{
Error: fmt.Sprintf("could not read browser controller config: %s", err),
})
return
}
if err := defaults.Validate(bccfg); err != nil {
writeAgentResponse(w, http.StatusBadRequest, &client.AgentResponse{
Error: fmt.Sprintf("could not validate browser controller config: %s", err),
})
return
}
}
Comment on lines +125 to +142
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just to confirm for my own understanding: this is only to prevent malformed or missing config files, right? The ltbrowserapi process is the one that will read this later.


newC, err := NewControllerWrapper(&ltConfig, ucConfig, 0, agentId, a.metrics, isBAInstance)
if err != nil {
writeAgentResponse(w, http.StatusBadRequest, &client.AgentResponse{
Expand Down
15 changes: 15 additions & 0 deletions api/agent_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

client "github.com/mattermost/mattermost-load-test-ng/api/client/agent"
"github.com/mattermost/mattermost-load-test-ng/defaults"
"github.com/mattermost/mattermost-load-test-ng/deployment"
"github.com/mattermost/mattermost-load-test-ng/loadtest"
"github.com/mattermost/mattermost-load-test-ng/loadtest/control"
"github.com/mattermost/mattermost-load-test-ng/loadtest/control/simulcontroller"
Expand Down Expand Up @@ -64,6 +65,8 @@ func createAgent(t *testing.T, id, serverURL string) *client.Agent {
}

func TestCreateAgent(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down Expand Up @@ -155,6 +158,8 @@ func TestCreateAgent(t *testing.T) {
}

func TestAgentId(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand All @@ -168,6 +173,8 @@ func TestAgentId(t *testing.T) {
}

func TestAgentStatus(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand All @@ -185,6 +192,8 @@ func TestAgentStatus(t *testing.T) {
}

func TestAgentRunStop(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down Expand Up @@ -245,6 +254,8 @@ func TestAgentRunStop(t *testing.T) {
}

func TestAgentAddRemoveUsers(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down Expand Up @@ -301,6 +312,8 @@ func TestAgentAddRemoveUsers(t *testing.T) {
}

func TestAgentDestroy(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down Expand Up @@ -332,6 +345,8 @@ func TestAgentDestroy(t *testing.T) {
}

func TestAgentInjectAction(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down
157 changes: 118 additions & 39 deletions api/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,17 @@ type requestData struct {
SimulControllerConfig *simulcontroller.Config `json:",omitempty"`
}

func setupAgentType(t *testing.T, agentType string) {
t.Helper()
tempDir := t.TempDir()
agentTypeFile := filepath.Join(tempDir, deployment.AgentTypeFileName)
require.NoError(t, os.WriteFile(agentTypeFile, []byte(agentType), 0644))
t.Setenv("HOME", tempDir)
}

func TestAgentAPI(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down Expand Up @@ -181,6 +191,8 @@ func TestAgentAPI(t *testing.T) {
}

func TestAgentAPIConcurrency(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down Expand Up @@ -303,72 +315,139 @@ func TestGetUserCredentials(t *testing.T) {
}

func TestIsBrowserAgentInstance(t *testing.T) {
// Get original home directory to restore later
originalHome := os.Getenv("HOME")

t.Run("returns true when agent_type.txt contains browser_agent", func(t *testing.T) {
// Create temporary directory to use as home
tempDir, err := os.MkdirTemp("", "test_home_browser")
require.NoError(t, err)
defer os.RemoveAll(tempDir)

// Set temporary home directory
os.Setenv("HOME", tempDir)
defer os.Setenv("HOME", originalHome)

agentTypeFile := filepath.Join(tempDir, deployment.AgentTypeFileName)
err = os.WriteFile(agentTypeFile, []byte(" "+deployment.AgentTypeBrowser+" \n"), 0644)
require.NoError(t, err)
setupAgentType(t, deployment.AgentTypeBrowser)

result, err := isBrowserAgentInstance()
require.NoError(t, err)
require.True(t, result)
})

t.Run("returns false when agent_type.txt contains server_agent", func(t *testing.T) {
tempDir, err := os.MkdirTemp("", "test_home_server")
require.NoError(t, err)
defer os.RemoveAll(tempDir)

os.Setenv("HOME", tempDir)
defer os.Setenv("HOME", originalHome)

agentTypeFile := filepath.Join(tempDir, deployment.AgentTypeFileName)
err = os.WriteFile(agentTypeFile, []byte(deployment.AgentTypeServer), 0644)
require.NoError(t, err)
setupAgentType(t, deployment.AgentTypeServer)

result, err := isBrowserAgentInstance()
require.NoError(t, err)
require.False(t, result)
})

t.Run("returns false when agent_type.txt file does not exist", func(t *testing.T) {
tempDir, err := os.MkdirTemp("", "test_home_missing")
require.NoError(t, err)
defer os.RemoveAll(tempDir)

os.Setenv("HOME", tempDir)
defer os.Setenv("HOME", originalHome)
tempDir := t.TempDir()
t.Setenv("HOME", tempDir)

result, err := isBrowserAgentInstance()
require.Error(t, err)
require.False(t, result)
})

t.Run("returns false when agent_type.txt contains unknown content", func(t *testing.T) {
tempDir, err := os.MkdirTemp("", "test_home_unknown")
require.NoError(t, err)
defer os.RemoveAll(tempDir)

os.Setenv("HOME", tempDir)
defer os.Setenv("HOME", originalHome)
tempDir := t.TempDir()
t.Setenv("HOME", tempDir)

agentTypeFile := filepath.Join(tempDir, deployment.AgentTypeFileName)
err = os.WriteFile(agentTypeFile, []byte("unknown_agent_type"), 0644)
require.NoError(t, err)
require.NoError(t, os.WriteFile(agentTypeFile, []byte("unknown_agent_type"), 0644))

result, err := isBrowserAgentInstance()
require.Error(t, err)
require.False(t, result)
})
}

func TestBrowserAgentConfigValidation(t *testing.T) {
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))
server := httptest.NewServer(handler)
defer server.Close()

mmServer := createFakeMMServer()
defer mmServer.Close()

e := httpexpect.New(t, server.URL+"/loadagent")

ltConfig := loadtest.Config{}
err := defaults.Set(&ltConfig)
require.NoError(t, err)
ltConfig.UserControllerConfiguration.ServerVersion = control.MinSupportedVersion.String()
ltConfig.ConnectionConfiguration.ServerURL = mmServer.URL
ltConfig.UsersConfiguration.MaxActiveUsers = 100

setupBaseCfgDir := func(t *testing.T) string {
t.Helper()
tempDir := t.TempDir()
t.Chdir(tempDir)
cfgDir := filepath.Join(tempDir, "config")
require.NoError(t, os.MkdirAll(cfgDir, 0755))

return cfgDir
}

t.Run("fails when browsercontroller.json is missing", func(t *testing.T) {
setupAgentType(t, deployment.AgentTypeBrowser)

// Empty config directory
_ = setupBaseCfgDir(t)

rd := requestData{LoadTestConfig: ltConfig}
e.POST("/create").WithQuery("id", "ltb0").WithJSON(rd).
Expect().Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")
})

t.Run("succeeds with valid browsercontroller.json", func(t *testing.T) {
setupAgentType(t, deployment.AgentTypeBrowser)

// Config directory with a valid browsercontroller.json
cfgDir := setupBaseCfgDir(t)

validConfig := `{
"SimulationId": "mattermostPostAndScroll",
"RunInHeadless": true,
"SimulationTimeoutMs": 60000,
"EnabledPlugins": false,
"LogSettings": {
"EnableConsole": true,
"ConsoleLevel": "debug",
"EnableFile": true,
"FileLevel": "debug",
"FileLocation": "browseragent.log"
}
}`
require.NoError(t, os.WriteFile(filepath.Join(cfgDir, "browsercontroller.json"), []byte(validConfig), 0644))

rd := requestData{LoadTestConfig: ltConfig}
obj := e.POST("/create").WithQuery("id", "ltb1").WithJSON(rd).
Expect().Status(http.StatusCreated).
JSON().Object().ValueEqual("id", "ltb1")
rawMsg := obj.Value("message").String().Raw()
require.Equal(t, "load-test agent created", rawMsg)

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

t.Run("fails with invalid browsercontroller.json values", func(t *testing.T) {
setupAgentType(t, deployment.AgentTypeBrowser)

// Config directory with an invalid browsercontroller.json
cfgDir := setupBaseCfgDir(t)

invalidConfig := `{
"SimulationId": "",
"RunInHeadless": true,
"SimulationTimeoutMs": -1,
"EnabledPlugins": false,
"LogSettings": {
"EnableConsole": true,
"ConsoleLevel": "invalid_level",
"EnableFile": true,
"FileLevel": "debug",
"FileLocation": "browseragent.log"
}
}`
require.NoError(t, os.WriteFile(filepath.Join(cfgDir, "browsercontroller.json"), []byte(invalidConfig), 0644))

rd := requestData{LoadTestConfig: ltConfig}
e.POST("/create").WithQuery("id", "ltb2").WithJSON(rd).
Expect().Status(http.StatusBadRequest).
JSON().Object().ContainsKey("error")
})
}
5 changes: 5 additions & 0 deletions api/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
coordClient "github.com/mattermost/mattermost-load-test-ng/api/client/coordinator"
"github.com/mattermost/mattermost-load-test-ng/coordinator"
"github.com/mattermost/mattermost-load-test-ng/defaults"
"github.com/mattermost/mattermost-load-test-ng/deployment"
"github.com/mattermost/mattermost-load-test-ng/loadtest"
"github.com/mattermost/mattermost-load-test-ng/loadtest/control"
"github.com/mattermost/mattermost-load-test-ng/loadtest/control/simulcontroller"
Expand All @@ -26,6 +27,8 @@ import (
const n = 8

func TestAgentClientConcurrency(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down Expand Up @@ -178,6 +181,8 @@ func TestAgentClientConcurrency(t *testing.T) {
}

func TestCoordClientConcurrency(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down
8 changes: 8 additions & 0 deletions api/coordinator_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
client "github.com/mattermost/mattermost-load-test-ng/api/client/coordinator"
"github.com/mattermost/mattermost-load-test-ng/coordinator"
"github.com/mattermost/mattermost-load-test-ng/defaults"
"github.com/mattermost/mattermost-load-test-ng/deployment"
"github.com/mattermost/mattermost-load-test-ng/loadtest"
"github.com/mattermost/mattermost-load-test-ng/loadtest/control"
"github.com/mattermost/mattermost-load-test-ng/logger"
Expand Down Expand Up @@ -38,6 +39,8 @@ func createCoordinator(t *testing.T, id, serverURL string) *client.Coordinator {
}

func TestCreateCoordinator(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)

// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down Expand Up @@ -95,6 +98,7 @@ func TestCreateCoordinator(t *testing.T) {
}

func TestCoordinatorId(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)
// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand All @@ -108,6 +112,7 @@ func TestCoordinatorId(t *testing.T) {
}

func TestCoordinatorStatus(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)
// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand All @@ -125,6 +130,7 @@ func TestCoordinatorStatus(t *testing.T) {
}

func TestCoordinatorStartStop(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)
// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down Expand Up @@ -196,6 +202,7 @@ func TestCoordinatorStartStop(t *testing.T) {
}

func TestCoordinatorDestroy(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)
// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down Expand Up @@ -226,6 +233,7 @@ func TestCoordinatorDestroy(t *testing.T) {
}

func TestCoordinatorInjectAction(t *testing.T) {
setupAgentType(t, deployment.AgentTypeServer)
// create http.Handler
handler := SetupAPIRouter(logger.New(&logger.Settings{}), logger.New(&logger.Settings{}))

Expand Down
Loading