diff --git a/README.md b/README.md index 39d3c70..8a0d4ef 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ aurora is a web-based Beanstalkd queue server console written in Go and works on - You can move jobs between tubes - Ability to Pause tubes - Search jobs data field +- Search jobs by ID - Customizable UI (code highlighter, choose columns, edit auto refresh seconds, pause tube seconds) ## Installation diff --git a/handlers.go b/handlers.go index bc30fcb..a37f3b3 100644 --- a/handlers.go +++ b/handlers.go @@ -78,7 +78,7 @@ func handlerTube(w http.ResponseWriter, r *http.Request) { _, _ = io.WriteString(w, `{"result":true}`) return case "search": - content := searchTube(server, tube, r.URL.Query().Get("limit"), r.URL.Query().Get("searchStr")) + content := searchTube(server, tube, r.URL.Query().Get("limit"), r.URL.Query().Get("id"), r.URL.Query().Get("state"), r.URL.Query().Get("searchStr")) _, _ = io.WriteString(w, tplTube(content, server, tube)) return case "addSample": diff --git a/lib.go b/lib.go index 66a01f7..7b785fa 100644 --- a/lib.go +++ b/lib.go @@ -263,13 +263,14 @@ func clearTubes(server string, data url.Values) { // searchTube search job by given search string in ready, delayed and buried // stats. -func searchTube(server string, tube string, limit string, searchStr string) string { +func searchTube(server string, tube string, limit string, beanID string, state string, searchStr string) string { var ( bstkConn *beanstalk.Conn bstkConnStats map[string]string err error result = []SearchResult{} searchLimit int + searchID uint64 statsFilter = []string{"ready", "delayed", "buried"} table = currentTubeJobsSummaryTable(server, tube) totalJobs, id uint64 @@ -290,17 +291,24 @@ func searchTube(server string, tube string, limit string, searchStr string) stri if totalJobs, err = strconv.ParseUint(bstkConnStats["total-jobs"], 10, 64); err != nil { return table } - // Get ready stat job total - for _, state := range statsFilter { - var cnt int - for id = totalJobs; id > 0; id-- { - if cnt >= searchLimit { - continue - } - ret := searchTubeInStats(tube, searchStr, state, bstkConn, id) - if ret != nil { - result = append(result, *ret) - cnt++ + if beanID != "" { + searchID, err = strconv.ParseUint(beanID, 10, 64) + if err != nil { + return table + } + ret := searchTubeByID(tube, searchID, bstkConn) + if ret != nil { + result = append(result, *ret) + } + } else { + if state != "" { + ret := limitResults(tube, searchStr, state, searchLimit, id, totalJobs, bstkConn) + result = append(result, *ret...) + } else { + // Get ready stat job total + for _, state := range statsFilter { + ret := limitResults(tube, searchStr, state, searchLimit, id, totalJobs, bstkConn) + result = append(result, *ret...) } } } @@ -308,6 +316,22 @@ func searchTube(server string, tube string, limit string, searchStr string) stri return table + currentTubeSearchResults(server, tube, limit, searchStr, result) } +func limitResults(tube, searchStr, state string, searchLimit int, id, totalJobs uint64, bstkConn *beanstalk.Conn) *[]SearchResult { + var cnt int + var result = []SearchResult{} + for id = totalJobs; id > 0; id-- { + if cnt >= searchLimit { + continue + } + ret := searchTubeInStats(tube, searchStr, state, bstkConn, id) + if ret != nil { + result = append(result, *ret) + cnt++ + } + } + return &result +} + // searchTubeInStats search job in tube by given stats. func searchTubeInStats(tube, searchStr, stat string, bstkConn *beanstalk.Conn, id uint64) *SearchResult { jobStats, err := bstkConn.StatsJob(id) @@ -331,3 +355,22 @@ func searchTubeInStats(tube, searchStr, stat string, bstkConn *beanstalk.Conn, i Data: string(readyBody), } } + +func searchTubeByID(tube string, searchID uint64, bstkConn *beanstalk.Conn) *SearchResult { + jobStats, err := bstkConn.StatsJob(searchID) + if err != nil { + return nil + } + if jobStats["tube"] != tube { + return nil + } + readyBody, err := bstkConn.Peek(searchID) + if err != nil { + return nil + } + return &SearchResult{ + ID: searchID, + State: jobStats["state"], + Data: string(readyBody), + } +} diff --git a/main_test.go b/main_test.go index 89a9f1e..025a989 100644 --- a/main_test.go +++ b/main_test.go @@ -29,7 +29,7 @@ enabled = true username = "admin" [sample] - storage = "{\"jobs\":[{\"key\":\"97ec882fd75855dfa1b4bd00d4a367d4\",\"name\":\"sample_1\",\"tubes\":[\"default\"],\"data\":\"aurora_test_sample_job\",\"ttr\":60},{\"key\":\"d44782912092260cec11275b73f78434\",\"name\":\"sample_2\",\"tubes\":[\"aurora_test\"],\"data\":\"aurora_test_sample_job\",\"ttr\":60},{\"key\":\"5def7a2daf5e0292bed42db9f0017c94\",\"name\":\"sample_3\",\"tubes\":[\"default\",\"aurora_test\"],\"data\":\"aurora_test_sample_job\",\"ttr\":60}],\"tubes\":[{\"name\":\"default\",\"keys\":[\"97ec882fd75855dfa1b4bd00d4a367d4\",\"5def7a2daf5e0292bed42db9f0017c94\"]},{\"name\":\"aurora_test\",\"keys\":[\"d44782912092260cec11275b73f78434\",\"5def7a2daf5e0292bed42db9f0017c94\"]}]}"` + storage = "{\"jobs\":[{\"id\": 1, \"state\": ready, \"key\":\"97ec882fd75855dfa1b4bd00d4a367d4\",\"name\":\"sample_1\",\"tubes\":[\"default\"],\"data\":\"aurora_test_sample_job\",\"ttr\":60},{\"id\": 2, \"state\": ready, \"key\":\"d44782912092260cec11275b73f78434\",\"name\":\"sample_2\",\"tubes\":[\"aurora_test\"],\"data\":\"aurora_test_sample_job\",\"ttr\":60},{\"id\": 3, \"state\": ready, \"key\":\"5def7a2daf5e0292bed42db9f0017c94\",\"name\":\"sample_3\",\"tubes\":[\"default\",\"aurora_test\"],\"data\":\"aurora_test_sample_job\",\"ttr\":60}],\"tubes\":[{\"name\":\"default\",\"keys\":[\"97ec882fd75855dfa1b4bd00d4a367d4\",\"5def7a2daf5e0292bed42db9f0017c94\"]},{\"name\":\"aurora_test\",\"keys\":[\"d44782912092260cec11275b73f78434\",\"5def7a2daf5e0292bed42db9f0017c94\"]}]}"` ) var ( @@ -53,7 +53,10 @@ var ( "/tube?server=" + bstk + "&tube=default&state=ready&action=kickJob&jobid=badID", // Kick job by given ID with no exits ID "/tube?server=not_exist_server_addr&tube=default&state=ready&action=kickJob&jobid=1", // Kick job by given ID with no exits server "/tube?server=" + bstk + "&tube=default&action=loadSample&key=97ec882fd75855dfa1b4bd00d4a367d4&redirect=tube&action=manageSamples", // Load sample job by given key - "/tube?server=" + bstk + "&tube=aurora_test&state=&action=search&limit=25&searchStr=t", // Search job + "/tube?server=" + bstk + "&tube=aurora_test&state=&action=search&limit=25&searchStr=t&id=", // Search job + "/tube?server=" + bstk + "&tube=aurora_test&state=delayed&action=search&limit=25&searchStr=t&id=", // Search job by state + "/tube?server=" + bstk + "&tube=aurora_test&state=&action=search&limit=25&searchStr=t&id=1", // Search job by id + "/tube?server=" + bstk + "&tube=aurora_test&state=&action=search&limit=25&searchStr=t&id=test", // Search job by id that is not number "/tube?server=" + bstk + "&tube=aurora_test&state=&action=search&limit=25&searchStr=match", // Search job with not match string "/tube?server=" + bstk + "&tube=aurora_test&action=moveJobsTo&destState=buried&state=ready", // Move job from ready to buried state "/tube?server=not_exist_server_addr&tube=aurora_test&action=moveJobsTo&destState=buried&state=ready", // Move job from ready to buried state with no exits server @@ -296,8 +299,13 @@ func TestMoveReadyJobsTo(t *testing.T) { func TestSearchTube(t *testing.T) { once.Do(testSetup) - searchTube(bstk, `default`, `not_int`, `ready`) - searchTube(bstk, `aurora_test_2`, `1`, `ready`) + searchTube(bstk, `default`, `not_int`, ``, ``, `ready`) + searchTube(bstk, `aurora_test_2`, `1`, ``, ``, `ready`) + searchTube(bstk, `aurora_test`, `1`, `not_int`, ``, `ready`) + searchTube(bstk, `aurora_test`, `1`, `1`, ``, `ready`) + searchTube(bstk, `aurora_test`, `1`, `100`, ``, `ready`) + searchTube(bstk, `aurora_test`, `1`, ``, `delayed`, `ready`) + searchTube(bstk, `aurora_test`, `1`, ``, `ready`, `ready`) } func TestAddSample(t *testing.T) { diff --git a/tplSearchTube.go b/tplSearchTube.go index 61c2b59..24b5fc6 100644 --- a/tplSearchTube.go +++ b/tplSearchTube.go @@ -23,10 +23,11 @@ func tplSearchTube(server string, tube string, state string) string { buf.WriteString(server) buf.WriteString(`"/>
`) + buf.WriteString(`"/>
`) + buf.WriteString(``) + buf.WriteString(``) + buf.WriteString(`
`) return buf.String() }