Skip to content

Commit

Permalink
UI: Make run load test button works
Browse files Browse the repository at this point in the history
  • Loading branch information
andylibrian committed Dec 19, 2020
1 parent 2b9d160 commit a6fc977
Show file tree
Hide file tree
Showing 4 changed files with 304 additions and 2 deletions.
18 changes: 18 additions & 0 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func (s *Server) setupRouter() (*httprouter.Router, error) {

router.GET("/cluster/join", s.acceptWorkerConn)
router.GET("/notifications", s.acceptNotificationConn)
router.POST("/api/v1/load_test", s.handleStartLoadTest)

// CORS
router.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -203,3 +204,20 @@ func loadTestStateToString(s int) string {

return ""
}

func (s *Server) handleStartLoadTest(responseWriter http.ResponseWriter, req *http.Request, _ httprouter.Params) {
var startLoadTestRequest messages.StartLoadTestRequest
err := json.NewDecoder(req.Body).Decode(&startLoadTestRequest)
if err != nil {
http.Error(responseWriter, err.Error(), http.StatusBadRequest)
return
}

s.StartLoadTest(&startLoadTestRequest)

header := responseWriter.Header()
header.Set("Access-Control-Allow-Origin", "*")

responseWriter.WriteHeader(200)
responseWriter.Write([]byte("ok"))
}
6 changes: 5 additions & 1 deletion pkg/worker/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ func (w *Worker) Run() {
w.conn = conn
defer conn.Close()
defer close(w.isConnectedCh)
w.isConnectedCh <- struct{}{}

// TODO: it's inefficient if nobody is reading this
go func() {
w.isConnectedCh <- struct{}{}
}()

go w.LoopSendMetricsToServer()

Expand Down
48 changes: 47 additions & 1 deletion web/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Lato&family=Open+Sans&family=Roboto&display=swap" rel="stylesheet">

<Navbar :serverInfo="serverInfo"/>
<Navbar :serverInfo="serverInfo" />
<div id="section-launch-test" class="section">
<LaunchTest :serverInfo="serverInfo" :serverBaseUrl="serverBaseUrl" />
</div>

</template>

<script>
import CreateWebsocket from './lib/websocket.js'
import Navbar from './components/navbar.vue'
import LaunchTest from './components/launch_test.vue'
let serverBaseUrl = process.env.VUE_APP_SERVER_BASE_URL;
if (!serverBaseUrl) {
Expand All @@ -22,13 +26,15 @@
name: 'App',
components: {
Navbar,
LaunchTest,
},
data: function() {
return {
serverInfo: {
num_of_workers: 0,
state: "",
},
serverBaseUrl: serverBaseUrl,
}
},
created: function() {
Expand Down Expand Up @@ -86,5 +92,45 @@
font-family: Lato, Roboto, 'Open sans', sans-serif;
}
.section {
padding: 1.5rem 0 1.5rem 0 !important;
}
.section .card {
box-shadow: none;
border: 1px solid rgba(14, 18, 23, 0.08);
background: rgba(255, 255, 255, 0.9);
}
.section .card .card-header {
background: rgba(100, 110, 128, 0.2) !important;
}
.section .card .card-header .card-header-title {
border-bottom: 1px solid rgba(100, 110, 128, 0.1) !important;
}
.section .card .card-footer strong {
color: #525252;
}
.button.is-primary {
background-color: #0477c1 !important;
}
.button.is-primary.is-hovered, .button.is-primary:hover {
background-color: #0467b1 !important;
}
.button.is-danger {
background-color: #c12424 !important;
}
.button.is-danger.is-hovered, .button.is-danger:hover {
background-color: #d11414 !important;
}
.button.is-danger[disabled], fieldset[disabled] .button.is-danger {
background-color: #e18888 !important;
}
</style>

234 changes: 234 additions & 0 deletions web/src/components/launch_test.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
<template>
<div class="tiles">
<div class="tile is-parent">
<div class="tile is-child is-6">
<div class="card">
<div class="card-header">
<strong class="card-header-title"><span class="icon"><i class="fas fa-rocket" aria-hidden="true"></i></span> Launch a load test</strong>
</div>
<div class="card-content">
<form @submit.prevent>
<div class="tabs">
<ul>
<li :class="{'is-active': launchFormActiveTab == 'basic'}"><a @click="launchFormSwitchTab('basic')">Basic Settings</a></li>
<li :class="{'is-active': launchFormActiveTab == 'headers'}"><a @click="launchFormSwitchTab('headers')">Headers</a></li>
<li :class="{'is-active': launchFormActiveTab == 'body'}"><a @click="launchFormSwitchTab('body')">Body</a></li>
</ul>
</div>
<div id="launchForm-tab-content-basic" :class="{'is-hidden': launchFormActiveTab != 'basic'}">
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label" for="load-test-url">URL</label>
</div>
<div class="field-body">
<div class="field has-addons">
<p class="control">
<span class="select">
<select id="load-test-method" name="load-test-method">
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>
</span>
</p>
<p class="control is-expanded has-icons-left">
<input class="input" id="load-test-url" type="text" placeholder="URL">
<span class="icon is-small is-left">
<span class="fas fa-globe-asia"></span>
</span>
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label" for="load-test-duration">Duration</label>
</div>
<div class="field-body">
<div class="field has-addons">
<p class="control has-icons-left">
<input class="input" name="load-test-duration" id="load-test-duration" type="text" placeholder="Duration" value="30">
<span class="icon is-small is-left">
<span class="fas fa-stopwatch"></span>
</span>
</p>
<p class="control">
<span class="select">
<select id="load-test-duration-unit" name="load-test-duration-unit">
<option value="second">seconds</option>
<option value="minute">minutes</option>
<option value="hour">hours</option>
</select>
</span>
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label" for="load-test-rate">Rate per worker</label>
</div>
<div class="field-body">
<div class="field has-addons">
<p class="control has-icons-left">
<input class="input" name="load-test-rate" id="load-test-rate" type="text" placeholder="Rate" value="100">
<span class="icon is-small is-left">
<span class="fas fa-bolt"></span>
</span>
</p>
<p class="control">
<a class="button is-static">
reqs per second
</a>
</p>
</div>
</div>
</div>
</div>
<div id="launchForm-tab-content-headers" :class="{'is-hidden': launchFormActiveTab != 'headers'}">
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label" for="load-test-header">Header</label>
</div>
<div class="field-body">
<div class="field has-addons">
<div class="control is-expanded">
<textarea class="textarea" id="load-test-header" placeholder="Key: Value
Key: Value"></textarea>
</div>
</div>
</div>
</div>
</div>
<div id="launchForm-tab-content-body" :class="{'is-hidden': launchFormActiveTab != 'body'}">
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label" for="load-test-body">Body</label>
</div>
<div class="field-body">
<textarea class="textarea" id="load-test-body"></textarea>
</div>
</div>
</div>
<div class="field is-horizontal mt-4">
<div class="field-label"></div>
<div class="field-body">
<div class="field is-grouped">
<p class="control buttons">
<button v-bind:disabled="serverInfo.state.toLowerCase() == 'running'" class="button is-primary" @click="runLoadTest()">
Run
</button>
<button v-bind:disabled="serverInfo.state.toLowerCase() != 'running'" class="button is-danger" @click="stopLoadTest()">
Stop
</button>
</p>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
export default {
name: 'LaunchTest',
props: {
serverInfo: Object,
serverBaseUrl: String,
},
data: function() {
return {
launchFormActiveTab: "basic",
}
},
methods: {
launchFormSwitchTab(tabId) {
this.launchFormActiveTab = tabId;
},
runLoadTest() {
// TODO: validate form
const methodEl = document.getElementById("load-test-method");
if (!methodEl) {
return false;
}
const urlEl = document.getElementById("load-test-url");
if (!urlEl) {
return false;
}
const durationUnitEl = document.getElementById("load-test-duration-unit");
if (!durationUnitEl) {
return false;
}
const durationEl = document.getElementById("load-test-duration");
if (!durationEl) {
return false;
}
const rateEl = document.getElementById("load-test-rate");
if (!rateEl) {
return false;
}
const headerEl = document.getElementById("load-test-header");
if (!headerEl) {
return false;
}
const bodyEl = document.getElementById("load-test-body");
if (!bodyEl) {
return false;
}
const method = methodEl.value;
const url = urlEl.value;
const durationUnit = durationUnitEl.value;
let duration = durationEl.value;
if (durationUnit == "minute") {
duration *= 60;
} else if (durationUnit == "hour") {
duration *= 60 * 60;
}
const rate = rateEl.value;
const body = bodyEl.value;
const header = headerEl.value;
var xhr = new XMLHttpRequest();
xhr.open("POST", this.serverBaseUrl + '/api/v1/load_test', true)
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
const postData = JSON.stringify({
method: method,
url: url,
duration: duration,
rate: rate,
header: header,
body: body,
});
xhr.send(postData);
// TODO: handle response
},
stopLoadTest() {
var xhr = new XMLHttpRequest();
xhr.open("DELETE", this.serverBaseUrl + '/api/v1/load_test', true)
xhr.send();
// TODO: handle response
},
},
}
</script>

<style scoped>
</style>

0 comments on commit a6fc977

Please sign in to comment.