-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
598 additions
and
1 deletion.
There are no files selected for viewing
Submodule golang
deleted from
c03143
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.DS_Store | ||
.idea | ||
go-bonsai | ||
*.iml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
## Go Bonsai! A lightweight Elasticsearch cluster exploration tool written in golang. | ||
|
||
To get started, clone the repo: | ||
|
||
``` | ||
git clone [email protected]:omc/dogfood-go.git | ||
``` | ||
|
||
Make sure to export your Bonsai cluster URL to the dev environment: | ||
|
||
``` | ||
export BONSAI_URL="https://user:[email protected]" | ||
``` | ||
|
||
Build the tool: | ||
|
||
``` | ||
go build bonsai-example-go.go | ||
``` | ||
|
||
Run it: | ||
|
||
``` | ||
$ chmod +x go-bonsai | ||
$ ./go-bonsai | ||
``` | ||
|
||
Check it out by opening up a browser and navigating to http://localhost:8080 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package main | ||
|
||
import( | ||
"./lib/controllers" | ||
"net/http" | ||
) | ||
|
||
func main() { | ||
http.HandleFunc("/", rootHandler) | ||
http.HandleFunc("/index/", indexHandler) | ||
http.ListenAndServe(":8080", nil) | ||
} | ||
|
||
func indexHandler(w http.ResponseWriter, r *http.Request) { | ||
controllers.IndexBuilder(w, r) | ||
} | ||
|
||
func rootHandler(w http.ResponseWriter, r *http.Request) { | ||
controllers.RootBuilder(w, r) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package controllers | ||
|
||
import( | ||
"../elasticsearch" | ||
"../views" | ||
"../errors" | ||
"gopkg.in/olivere/elastic.v2" | ||
"net/http" | ||
"sort" | ||
) | ||
|
||
func IndexBuilder(w http.ResponseWriter, r *http.Request) { | ||
es,index := elasticsearch.ConfirmIndex(w, r) | ||
if index != "" { | ||
|
||
// Get a list of sorted index names | ||
indices, _ := es.IndexNames() | ||
sort.Strings(indices) | ||
|
||
// Query for some docs | ||
// Use default settings for FROM and SIZE | ||
query := elastic.NewQueryStringQuery("*") | ||
docs,_ := es.Search().From(0).Size(10).Index(index).Query(query).Pretty(true).Do() | ||
|
||
// Get a mapping for the index | ||
mappings := elasticsearch.GetIndexMappings(es, index) | ||
var mappingTypes []string | ||
typeCounts := make(map[string]int64) | ||
if docs.TotalHits() > 0 { | ||
// No docs? Then there ain't no mappin' to get | ||
types := mappings[index].(map[string]interface{})["mappings"].(map[string]interface{}) | ||
mappingTypes = elasticsearch.GetTypesFromMapping(types) | ||
sort.Strings(mappingTypes) | ||
|
||
for _,i:=range mappingTypes { | ||
typeDocs,_ := es.Search().From(0).Size(10).Index(index).Type(i).Query(query).Pretty(true).Do() | ||
typeCounts[i] = typeDocs.TotalHits() | ||
} | ||
} | ||
|
||
//Create a view with the data and render it for the user | ||
p := &views.View{ | ||
Title: "Overview of " + index, | ||
Hits: docs.TotalHits(), | ||
Docs: docs.Hits.Hits, | ||
Name: index, | ||
IndexCount: len(indices), | ||
Indices: indices, | ||
Mappings: mappings, | ||
MappingTypes: mappingTypes, | ||
TypeCounts: typeCounts} | ||
views.Render(p, w, r, "index") | ||
} | ||
} | ||
|
||
func RootBuilder(w http.ResponseWriter, r *http.Request) { | ||
es := elasticsearch.GetClient() | ||
indices,err := es.IndexNames() | ||
|
||
if err != nil { | ||
errors.ExitOnError(err, errors.INDEX_NAME_ERROR) | ||
} | ||
sort.Strings(indices) | ||
health := elasticsearch.GetClusterHealth(es) | ||
stats := elasticsearch.GetClusterStats(es) | ||
p := &views.View{ | ||
Title: "Cluster Overview", | ||
Health: health, | ||
Stats: stats, | ||
IndexCount: len(indices), | ||
Indices: indices} | ||
views.Render(p, w, r, "root") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package elasticsearch | ||
|
||
import( | ||
"gopkg.in/olivere/elastic.v2" | ||
"os" | ||
"regexp" | ||
"strings" | ||
"net/http" | ||
"fmt" | ||
"../errors" | ||
"../views" | ||
) | ||
|
||
// Instantiate an Elasticsearch client | ||
func GetClient() *elastic.Client { | ||
user,pass,host := parseBonsaiURL() | ||
client, err := elastic.NewSimpleClient( | ||
elastic.SetURL(host), | ||
elastic.SetMaxRetries(10), | ||
elastic.SetBasicAuth(user,pass), | ||
elastic.SetSniff(false)) | ||
if err != nil { | ||
errors.ExitOnError(err, errors.CLIENT_FAILURE) | ||
} | ||
return client | ||
} | ||
|
||
// | ||
func GetClusterHealth(es *elastic.Client) *elastic.ClusterHealthResponse { | ||
health,_ := es.ClusterHealth(). | ||
Do() | ||
return health | ||
} | ||
|
||
// | ||
func GetClusterStats(es *elastic.Client) *elastic.IndicesStatsResponse { | ||
stats,_ := es.IndexStats(). | ||
Index("_all"). | ||
Human(true). | ||
Pretty(true). | ||
Do() | ||
return stats | ||
} | ||
|
||
func SearchDocuments(es *elastic.Client, index string) *elastic.SearchResult { | ||
docs,_ := es.Search(). | ||
Index(index). | ||
Do() | ||
return docs | ||
} | ||
|
||
func GetIndexMappings(es *elastic.Client, index string) map[string]interface{} { | ||
mappings,err := es.GetMapping().Index(index).Do() | ||
if err != nil { | ||
errors.ExitOnError(err, errors.INDEX_MAPPING_ERROR) | ||
} | ||
return mappings | ||
} | ||
|
||
// Make sure that an index exists before we do anything with it. | ||
// If it exists, return the client for reuse | ||
func ConfirmIndex(w http.ResponseWriter, r *http.Request) (*elastic.Client, string) { | ||
rex, err := regexp.Compile("/index/(.*?)") | ||
index := rex.ReplaceAllString(r.URL.Path, "$1") | ||
if err != nil { | ||
errors.ExitOnError(err, errors.REGEX_COMPILE) | ||
} | ||
es := GetClient() | ||
indexCheck, err := es.IndexExists(index).Do() | ||
if indexCheck == false { | ||
indices,_ := es.IndexNames() | ||
fmt.Printf("Aw, brah, I couldn't find an index called '%s'!\n", index) | ||
p := &views.View{IndexCount: len(indices), Indices: indices, ErrorMessage: "That index doesn't exist!"} | ||
views.Render(p, w, r, "root") | ||
return es,"" | ||
} else if err != nil { | ||
errors.ExitOnError(err, errors.INDEX_EXISTS_ERROR) | ||
} | ||
return es, index | ||
} | ||
|
||
func parseBonsaiURL() (string, string, string){ | ||
url := os.Getenv("BONSAI_URL") | ||
rex, _ := regexp.Compile(".*?://([a-z0-9]{1,}):([a-z0-9]{1,})@.*$") | ||
user := rex.ReplaceAllString(url, "$1") | ||
pass := rex.ReplaceAllString(url, "$2") | ||
host := strings.Replace(url, user+":"+pass+"@", "", -1) | ||
return user,pass,host | ||
} | ||
|
||
func GetTypesFromMapping(m map[string]interface{}) (types []string) { | ||
for k := range m { | ||
types = append(types, k) | ||
} | ||
return types | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package errors | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
) | ||
|
||
const ( | ||
CLIENT_FAILURE = "Could not instantiate a client to the Elasticsearch cluster" | ||
JSON_DECODE_FAILURE = "Could not decode JSON" | ||
INDEX_EXISTS_ERROR = "The Elasticsearch client failed to determine whether the index exists" | ||
INDEX_NAME_ERROR = "An error occurred while retrieving the cluster's index names" | ||
REGEX_COMPILE = "Could not compile the regular expression" | ||
TEMPLATE_PARSE_FAILURE = "The template engine failed to parse the requested view" | ||
TEMPLATE_EXECUTE_FAILURE = "The template engine failed to execute the requested view" | ||
INDEX_MAPPING_ERROR = "The requested index mapping could not be found" | ||
) | ||
|
||
// Quit the program if there is an error | ||
func ExitOnError(err error, errMsg string) { | ||
fmt.Printf("The program exited with error category '%s'. Specific failure: %s\n", errMsg, err) | ||
os.Exit(1) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{{define "container"}} | ||
<div class="container-fluid"> | ||
<div class="row"> | ||
<div class="col-sm-3 col-md-2 sidebar"> | ||
{{ template "sidebar" . }} | ||
</div> | ||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main"> | ||
|
||
{{if .ErrorMessage}} | ||
<div class="alert alert-danger"> | ||
{{ .ErrorMessage }} | ||
</div> | ||
{{end}} | ||
|
||
{{ template "content" . }} | ||
|
||
</div> | ||
</div> | ||
</div> | ||
{{end}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{{define "footer"}} | ||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> | ||
<script src="http://getbootstrap.com/dist/js/bootstrap.min.js"></script> | ||
{{end}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{{define "header"}} | ||
<meta charset=utf-8> | ||
<meta http-equiv=X-UA-Compatible content="IE=edge"> | ||
<meta name=viewport content="width=device-width,initial-scale=1"> | ||
<meta name=author content="Rob Sears"> | ||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"> | ||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css"> | ||
<link rel="stylesheet" href="http://getbootstrap.com/examples/dashboard/dashboard.css"> | ||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> | ||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> | ||
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> | ||
<title>{{ .Title }}</title> | ||
{{end}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
{{ define "content"}} | ||
<h1 class="page-header">Overview of {{ .Name }}</h1> | ||
|
||
{{if gt .Hits 0}} | ||
|
||
<div> | ||
|
||
<!-- Nav tabs --> | ||
<ul class="nav nav-tabs" role="tablist"> | ||
<li role="presentation" class="active"><a href="#home" aria-controls="home" role="tab" data-toggle="tab">Stats</a></li> | ||
<li role="presentation"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">Fields</a></li> | ||
<li role="presentation"><a href="#messages" aria-controls="messages" role="tab" data-toggle="tab">Documents</a></li> | ||
</ul> | ||
|
||
<!-- Tab panes --> | ||
<div class="tab-content"> | ||
<div role="tabpanel" class="tab-pane active" id="home"> | ||
<style> | ||
|
||
.chart div { | ||
font: 10px sans-serif; | ||
background-color: steelblue; | ||
text-align: right; | ||
padding: 3px; | ||
margin: 1px; | ||
color: white; | ||
min-width:100px; | ||
} | ||
|
||
</style> | ||
<div class="chart"></div> | ||
<script language="javascript" type="text/javascript"> | ||
var data = {{ d3data .TypeCounts }}; | ||
var keys = Object.keys(data) | ||
var vals = Object.keys(data).map(function(v) { return (((data[v]) * 100.0 / {{ .Hits }}).toFixed(0)); }) | ||
var x = d3.scale.linear() | ||
.domain([0, d3.max(vals)]) | ||
.range([0, 100]); | ||
|
||
d3.select(".chart") | ||
.selectAll("div") | ||
.data(vals) | ||
.enter().append("div") | ||
.style("width", function(d) { return x(d) + "%"; }) | ||
.text(function(d) { return keys.shift() + ": " + vals.shift() + "%"; }); | ||
</script> | ||
</div> | ||
<div role="tabpanel" class="tab-pane" id="profile"> | ||
<div class="table-responsive"> | ||
<table class="table table-striped"> | ||
<thead> | ||
<th>Type</th> | ||
<th>Fields</th> | ||
</thead> | ||
<tbody> | ||
{{range $type := .MappingTypes}} | ||
<tr> | ||
<td>{{ $type }}</td> | ||
<td> | ||
<ul> | ||
{{ range $field := mappingFields $.Mappings $.Name $type}} | ||
<li>{{ $field }}</li> | ||
{{ end }} | ||
</ul> | ||
</td> | ||
</tr> | ||
{{ end }} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
<div role="tabpanel" class="tab-pane" id="messages"> | ||
<div class="table-responsive"> | ||
<table class="table table-striped"> | ||
<thead> | ||
<th>ID</th> | ||
<th>Type</th> | ||
<th>Source</th> | ||
</thead> | ||
<tbody> | ||
{{range $doc := .Docs}} | ||
<tr> | ||
<td>{{ $doc.Id }}</td> | ||
<td>{{ $doc.Type }}</td> | ||
<td> | ||
{{ jsonDecode $doc }} | ||
</td> | ||
</tr> | ||
{{ end }} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
</div> | ||
{{end}} | ||
|
||
{{ end }} |
Oops, something went wrong.