Ethanol aims to be a search aggregator, capable of querying multiple applications and return aggregate results. It is like a MetaSearch Engine but for applications like Jira, MediaWiki, Check_MK and others...
- Table of Content
- Supported Environments
- Installation
- Usage
- Help
- Build Core
- Build Plugins
- Write Plugins
- Utils
Ethanol is actually capable of querying the following systems:
root@localhost$ # Clone and access repository
root@localhost$ git clone https://github.com/areYouLazy/ethanol
root@localhost$ cd ethanol
root@localhost$
root@localhost$ # start the build.sh script
root@localhost$ ./build.sh
root@localhost$
root@localhost$ # generate a valid configuration file
root@localhost$ cp config.template.yml config.yml
root@localhost$
root@localhost$ # edit the configuration file according to your needs, than you can start ethanol
root@localhost$ ./ethanol
Once ethanol
is started, you can connect to the WebUI. The default address is http://<IP-address>:8888/ui/index
From there you can use the Search Bar to input your searches.
root@localhost$
Usage of ./ethanol:
-caller
print log messages caller (default false)
-config string
use a custom configuration file (default "config.yml")
-debug
print debug log messages (default false)
-json
print log messages in json format (default false)
The build.sh
script is designed to build the ethanol core, along with all available plugins
root@localhost$ ./build.sh
The build.sh
script is designed to build every plugin it find in the ethanol_plugins
folder,
ignoring folders whose name begins with an underscore _
root@localhost$ cd ./ethanol_plugins/$f
root@localhost$ go build -buildmode=plugin -o ../../search_providers/"$f".so ./*.go
root@localhost$ cd ..
root@localhost$ # example
root@localhost$ cd ./ethanol_plugins/check_mk
root@localhost$ go build -buildmode=plugins -o ../../search_providers/check_mk.so ./*.go
root@localhost$ cd ..
A plugin must export the Searcher
symbol as a structure that satisfies the types.SearchPlugin
interface.
type SearchResult map[string]interface{}
// SearchPlugin plugins must satisfy this interface
type SearchPlugin interface {
Name() string
Provider() string
Description() string
Version() string
Search(string, chan<- SearchResult)
}
You should use utils.NewEthanolHTTPClient()
to get a preconfigured HTTP Client. This will help Ethanol to be consist across HTTP calls
when it acts as an HTTP Client
In the same way, utils.NewEthanolHTTPClientGETRequest()
and utils.NewEthanolHTTPClientPOSTRequest()
will provides a GET and a POST requests. This will help Ethanol to be consist across HTTP calls when it acts as an HTTP Client
The Plugin export
is usually done at the end of the file
// example_plugin.go
// compile with # go build -buildmode=plugin -o example_plugin.so example_plugin.go
package main
const (
name = "example_search_plugin"
provider = "example_search_1.0_username_password"
description = "get results from an example_search installation through username/password authentication"
version = "0.1"
label = "Example Search"
raw_label = "example_search"
)
type searchPlugin interface{}
// Name exposes plugin name
func (s *searchPlugin) Name() string {
return name
}
// Provider exposes plugin provider
func (s *searchPlugin) Provider() string {
return provider
}
// Description exposes plugin description
func (s *searchPlugin) Description() string {
return description
}
// Version exposes plugin version
func (s *searchPlugin) Version() string {
return version
}
// Search prepare environment for queries
func (s *searchPlugin) Search(query string, resultsChan chan<- types.SearchResult) {
var backendWG sync.WaitGroup
for _, b := range backends {
backendWG.Add(1)
go func() {
defer backendWG.Done()
search(query, b, resultsChan)
}(b)
}
backendWG.Wait()
}
// search performs the actual search
func search(query string, backend backend, results chan<- types.SearchResult) {
client := utils.NewEthanolHTTPClient()
request := utils.NewEthanolHTTPClientGETRequest()
// do stuffs and send results to the channel
results <- res
}
// export symbol
var Searcher searchPlugin
Use utils.DumpHTTPRequest
and utils.DumpHTTPResponse
in your plugins (this will help in troubleshooting http related problems) like this:
// REQUESTS: just before client.Do()
// RESPONSES: just after client.Do()
// dump request
utils.DumpHTTPRequest(request, "request to plugin <plugin_name>")
// do request
response, err := client.Do(request)
if err != nil {
logrus.WithFields(logrus.Fields{
"error": err.Error(),
}).Error("error in plugin <plugin_name> request")
return nil, err
}
defer response.Body.Close()
// dump response
utils.DumpHTTPResponse(response, "response from plugin <plugin_name>")
You should append plugins data to every result before send it.
Those info's can be used in the UI to better diplay results
raw_label
is actualy used to select the correct template to render the result's card
// [...]
result["name"] = name
result["label"] = label
result["raw_label"] = raw_label
result["description"] = description
result["provider"] = provider
result["version"] = version
resultsChan <- result