summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2021-03-21 23:24:46 +0700
committerShulhan <ms@kilabit.info>2021-03-21 23:24:46 +0700
commit7fed1f75a32c161ba0d296dbb7b6dc14e831336e (patch)
treea2d4f65b11a20aefaad4738f3a02a0b908ccb1b8
parent360bf84d2af244c7926c2aa7c1f23d0c280b8238 (diff)
downloadgorankusu-7fed1f75a32c161ba0d296dbb7b6dc14e831336e.tar.xz
all: implement interface and API to cancel running attack
On the top of the page, it will display currently running attack with target name and HTTP target name. User can cancel the attack by clicking "Cancel" button on the right.
-rw-r--r--_www/index.css17
-rw-r--r--_www/index.html6
-rw-r--r--_www/index.js82
-rw-r--r--environment.go8
-rw-r--r--errors.go11
-rw-r--r--example/example.go4
-rw-r--r--trunks.go49
7 files changed, 142 insertions, 35 deletions
diff --git a/_www/index.css b/_www/index.css
index f80f449..ce0eb27 100644
--- a/_www/index.css
+++ b/_www/index.css
@@ -30,6 +30,7 @@ body {
padding: 1em;
position: fixed;
width: 14em;
+ border-right: 1px solid lightgrey;
}
.nav .nav-item {
cursor: pointer;
@@ -48,6 +49,10 @@ body {
height: calc(100% - 4em);
overflow: auto;
}
+.main .mainState {
+ border: 1px solid lightgrey;
+ padding: 1em;
+}
.input {
margin-bottom: 1em;
}
@@ -62,12 +67,22 @@ body {
width: calc(100% - 18em);
}
.mono {
- background-color: cornsilk;
+ background-color: lightgrey;
font-family: monospace;
padding: 1em;
overflow: auto;
}
+.HttpTarget {
+ margin-top: 2em;
+}
+
+.HttpTarget h3 {
+ border-left: 10px solid gold;
+ margin-left: 0;
+ padding: 0.5em;
+}
+
.results > .result-name {
margin-bottom: 1em;
}
diff --git a/_www/index.html b/_www/index.html
index 56754af..f753ed4 100644
--- a/_www/index.html
+++ b/_www/index.html
@@ -13,7 +13,7 @@
<h1>Trunks</h1>
<div class="nav-item">
- <h3 onclick="environment()">Environment</h3>
+ <h3 onclick="environmentRender()">Environment</h3>
</div>
<div id="nav-content"></div>
@@ -24,6 +24,10 @@
</div>
<div class="main">
+ <div class="mainState">
+ Attack running:
+ <span id="stateAttack"> - </span>
+ </div>
<div id="main-content"></div>
</div>
diff --git a/_www/index.js b/_www/index.js
index 044bd57..e8a5e37 100644
--- a/_www/index.js
+++ b/_www/index.js
@@ -20,8 +20,15 @@ let _requestTypes = {
}
async function main() {
+ await environmentGet()
+
let fres = await fetch("/_trunks/api/targets")
let res = await fres.json()
+ if (res.code != 200) {
+ notifError(res.message)
+ return
+ }
+
let targets = res.data
let w = ""
@@ -39,15 +46,26 @@ async function main() {
document.getElementById("nav-content").innerHTML = w
}
-async function environment() {
- let el = document.getElementById("main-content")
-
+async function environmentGet() {
let fres = await fetch("/_trunks/api/environment")
let res = await fres.json()
+ if (res.code != 200) {
+ notifError(res.message)
+ return
+ }
_env = res.data
- el.innerHTML = `
+ if (_env.AttackRunning) {
+ updateStateAttack(
+ _env.AttackRunning.Target,
+ _env.AttackRunning.HttpTarget,
+ )
+ }
+}
+
+async function environmentRender() {
+ document.getElementById("main-content").innerHTML = `
<h2> Environment </h2>
<div class="environment">
<div class="input">
@@ -250,6 +268,10 @@ async function run(targetID, httpTargetID) {
})
let res = await fres.json()
+ if (res.code != 200) {
+ notifError(res.message)
+ return
+ }
let elResponse = document.getElementById(httpTargetID + "_response")
elResponse.innerHTML = JSON.stringify(res, null, 2)
@@ -280,6 +302,30 @@ async function attack(targetID, httpTargetID) {
})
let res = await fres.json()
+ if (res.code != 200) {
+ notifError(res.message)
+ return
+ }
+
+ updateStateAttack(target, httpTarget)
+
+ notif(res.message)
+}
+
+async function attackCancel() {
+ let fres = await fetch("/_trunks/api/target/attack", {
+ method: "DELETE",
+ })
+
+ let res = await fres.json()
+ if (res.code != 200) {
+ notifError(res.message)
+ return
+ }
+
+ updateStateAttack(null, null)
+
+ notif(res.message)
}
async function attackResultDelete(name) {
@@ -325,6 +371,11 @@ async function attackResultGet(button, name) {
let url = "/_trunks/api/target/attack/result?name=" + name
let fres = await fetch(url)
let res = await fres.json()
+ if (res.code != 200) {
+ notifError(res.message)
+ return
+ }
+
let result = res.data
el.innerHTML = `
@@ -363,22 +414,37 @@ function onChangeHttpParam(targetID, httpTargetID, key, val) {
function notif(msg) {
let root = document.getElementById("notif")
- let item = document.createElement("div");
+ let item = document.createElement("div")
item.innerHTML = msg
root.appendChild(item)
- setTimeout(function() {
+ setTimeout(function () {
root.removeChild(item)
}, 5000)
}
function notifError(msg) {
let root = document.getElementById("notif-error")
- let item = document.createElement("div");
+ let item = document.createElement("div")
item.innerHTML = msg
root.appendChild(item)
- setTimeout(function() {
+ setTimeout(function () {
root.removeChild(item)
}, 5000)
}
+
+function updateStateAttack(target, httpTarget) {
+ let el = document.getElementById("stateAttack")
+ if (httpTarget) {
+ el.innerHTML = `
+ ${target.Name} / ${httpTarget.Name}
+ &nbsp;
+ <button onclick="attackCancel('${target.ID}', '${httpTarget.ID}')">
+ Cancel
+ </button>
+ `
+ } else {
+ el.innerHTML = "-"
+ }
+}
diff --git a/environment.go b/environment.go
index ced921a..7531148 100644
--- a/environment.go
+++ b/environment.go
@@ -46,7 +46,7 @@ type Environment struct {
// AttackRunning will be set to non-nil if there is a load
// testing currently running.
- AttackRunning *AttackResult
+ AttackRunning *RunRequest
mtx sync.Mutex
}
@@ -71,11 +71,11 @@ func (env *Environment) init() (err error) {
return nil
}
-func (env *Environment) getRunningAttack() (ar *AttackResult) {
+func (env *Environment) getRunningAttack() (rr *RunRequest) {
env.mtx.Lock()
- ar = env.AttackRunning
+ rr = env.AttackRunning
env.mtx.Unlock()
- return ar
+ return rr
}
func (env *Environment) isAttackRunning() (yorn bool) {
diff --git a/errors.go b/errors.go
index 29dcfee..f5f04b4 100644
--- a/errors.go
+++ b/errors.go
@@ -12,14 +12,15 @@ import (
libhttp "github.com/shuLhan/share/lib/http"
)
-func errAttackConflict(ar *AttackResult) error {
+func errAttackConflict(rr *RunRequest) error {
res := &libhttp.EndpointResponse{
E: liberrors.E{
- Code: http.StatusConflict,
- Message: "another attack is already running",
- Name: "ERR_ATTACK_CONFLICT",
+ Code: http.StatusConflict,
+ Message: fmt.Sprintf(`Another attack is already running: "%s/%s`,
+ rr.Target.Name, rr.HttpTarget.Name),
+ Name: "ERR_ATTACK_CONFLICT",
},
- Data: ar,
+ Data: rr,
}
return res
}
diff --git a/example/example.go b/example/example.go
index 190b39a..9a91ebb 100644
--- a/example/example.go
+++ b/example/example.go
@@ -95,8 +95,8 @@ func (ex *Example) registerTargets() (err error) {
Name: "Example",
Opts: &trunks.AttackOptions{
BaseUrl: fmt.Sprintf("http://%s", ex.trunks.Env.ListenAddress),
- Duration: 5 * time.Second,
- RatePerSecond: 10,
+ Duration: 300 * time.Second,
+ RatePerSecond: 1,
},
Vars: map[string]string{
"A": "1",
diff --git a/trunks.go b/trunks.go
index 9b700e9..bc95a42 100644
--- a/trunks.go
+++ b/trunks.go
@@ -284,7 +284,7 @@ func (trunks *Trunks) apiTargetAttack(epr *libhttp.EndpointRequest) (resbody []b
trunks.attackq <- req
- msg := fmt.Sprintf("attacking %s/%s with %d RPS for %d seconds",
+ msg := fmt.Sprintf("Attacking %s/%s with %d RPS for %s seconds",
req.Target.Opts.BaseUrl, req.HttpTarget.Path,
req.Target.Opts.RatePerSecond, req.Target.Opts.Duration)
@@ -292,13 +292,32 @@ func (trunks *Trunks) apiTargetAttack(epr *libhttp.EndpointRequest) (resbody []b
res := libhttp.EndpointResponse{}
res.Code = http.StatusOK
+ res.Name = "OK_ATTACK"
res.Message = msg
return json.Marshal(res)
}
func (trunks *Trunks) apiTargetAttackCancel(epr *libhttp.EndpointRequest) (resbody []byte, err error) {
- return resbody, nil
+ res := &libhttp.EndpointResponse{}
+
+ rr := trunks.Env.getRunningAttack()
+ if rr == nil {
+ res.Code = http.StatusNotFound
+ res.Message = "No attack is currently running."
+ res.Name = "ERR_ATTACK_CANCEL_NOT_FOUND"
+ return nil, res
+ }
+
+ trunks.cancelq <- true
+
+ res.Code = http.StatusOK
+ res.Name = "OK_ATTACK_CANCEL"
+ res.Message = fmt.Sprintf(`Attack on target "%s / %s" has been canceled`,
+ rr.Target.Name, rr.HttpTarget.Name)
+ res.Data = rr
+
+ return json.Marshal(res)
}
func (trunks *Trunks) apiTargetAttackResultGet(epr *libhttp.EndpointRequest) (resbody []byte, err error) {
@@ -495,6 +514,8 @@ func (trunks *Trunks) workerAttackQueue() (err error) {
logp := "workerAttackQueue"
for rr := range trunks.attackq {
+ trunks.Env.AttackRunning = rr
+
rr.HttpTarget.PreAttack(rr)
isCancelled := false
@@ -534,22 +555,22 @@ func (trunks *Trunks) workerAttackQueue() (err error) {
// Inform the caller that the attack has been canceled.
trunks.cancelq <- true
}
+ } else {
+ err := rr.result.finish()
+ if err != nil {
+ mlog.Errf("%s %s: %s\n", logp, rr.result.Name, err)
+ }
- return nil
- }
+ rr.HttpTarget.Results = append(rr.HttpTarget.Results, rr.result)
- err := rr.result.finish()
- if err != nil {
- mlog.Errf("%s %s: %s\n", logp, rr.result.Name, err)
- }
-
- rr.HttpTarget.Results = append(rr.HttpTarget.Results, rr.result)
+ sort.Slice(rr.HttpTarget.Results, func(x, y int) bool {
+ return rr.HttpTarget.Results[x].Name > rr.HttpTarget.Results[y].Name
+ })
- sort.Slice(rr.HttpTarget.Results, func(x, y int) bool {
- return rr.HttpTarget.Results[x].Name > rr.HttpTarget.Results[y].Name
- })
+ mlog.Outf("%s: %s finished.\n", logp, rr.result.Name)
+ }
- mlog.Outf("%s: %s finished.\n", logp, rr.result.Name)
+ trunks.Env.AttackRunning = nil
}
return nil
}