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
0 parents
commit fac436d
Showing
8 changed files
with
710 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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 @@ | ||
GEF-Docker | ||
========== | ||
|
||
The GEF backend for Docker |
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,153 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"log" | ||
"net/http" | ||
|
||
"../eudock" | ||
|
||
"github.com/gorilla/mux" | ||
) | ||
|
||
// ErrPreMarshal reports a coding error before marshalling json | ||
var ErrPreMarshal = errors.New("pre-marshalling json error") | ||
|
||
// ServerConfig keeps the configuration options needed to make a Server | ||
type ServerConfig struct { | ||
Address string | ||
ReadTimeoutSecs int | ||
WriteTimeoutSecs int | ||
} | ||
|
||
// Server is a master struct for serving HTTP API requests | ||
type Server struct { | ||
server http.Server | ||
docker *eudock.Client | ||
} | ||
|
||
// NewServer creates a new Server | ||
func NewServer(cfg ServerConfig, docker eudock.Client) *Server { | ||
server := &Server{ | ||
http.Server{ | ||
Addr: cfg.Address, | ||
// timeouts seem to trigger even after a correct read | ||
// ReadTimeout: cfg.ReadTimeoutSecs * time.Second, | ||
// WriteTimeout: cfg.WriteTimeoutSecs * time.Second, | ||
|
||
}, &docker, | ||
} | ||
router := mux.NewRouter() | ||
apirouter := router.PathPrefix("/api").Subrouter() | ||
apirouter.HandleFunc("/", server.listServicesHandler).Methods("GET") | ||
apirouter.HandleFunc("/", server.buildServiceImageHandler).Methods("POST") | ||
apirouter.HandleFunc("/{imageID}", server.inspectServiceHandler).Methods("GET") | ||
|
||
server.server.Handler = router | ||
return server | ||
} | ||
|
||
// Start starts a new http listener | ||
func (s *Server) Start() error { | ||
return s.server.ListenAndServe() | ||
} | ||
|
||
func (s Server) listServicesHandler(w http.ResponseWriter, r *http.Request) { | ||
imgIDs, err := s.docker.ListImages() | ||
if err != nil { | ||
log.Println("ERROR: list of docker images: ", err) | ||
s.serverError(w, err) | ||
return | ||
} | ||
s.okJMap(w, "ImageIDs", imgIDs) | ||
} | ||
|
||
func (s Server) inspectServiceHandler(w http.ResponseWriter, r *http.Request) { | ||
vars := mux.Vars(r) | ||
imageID := eudock.ImageID(vars["imageID"]) | ||
image, err := s.docker.InspectImage(imageID) | ||
if err != nil { | ||
log.Println("ERROR: inspect docker image: ", err) | ||
s.serverError(w, err) | ||
return | ||
} | ||
srv := extractServiceInfo(image.Labels) | ||
s.okJMap(w, "Image", image, "Service", srv) | ||
} | ||
|
||
func (s Server) buildServiceImageHandler(w http.ResponseWriter, r *http.Request) { | ||
image, err := s.docker.BuildImage("../gef-service-example") | ||
if err != nil { | ||
log.Println("ERROR: build docker image: ", err) | ||
s.serverError(w, err) | ||
return | ||
} | ||
s.okJMap(w, "Image", image) | ||
} | ||
|
||
func (s Server) executeServiceHandler(w http.ResponseWriter, r *http.Request) { | ||
vars := mux.Vars(r) | ||
imageID := eudock.ImageID(vars["imageID"]) | ||
container, err := s.docker.ExecuteImage(imageID) | ||
if err != nil { | ||
log.Println("ERROR: execute docker image: ", err) | ||
s.serverError(w, err) | ||
return | ||
} | ||
s.okJMap(w, "ContainerID", container.ID) | ||
} | ||
|
||
func (s Server) okJMap(w http.ResponseWriter, kv ...interface{}) { | ||
if len(kv) == 0 { | ||
log.Println("ERROR: jsonmap: empty call") | ||
s.serverError(w, ErrPreMarshal) | ||
return | ||
} else if len(kv)%2 == 1 { | ||
log.Println("ERROR: jsonmap: unbalanced call") | ||
s.serverError(w, ErrPreMarshal) | ||
return | ||
} | ||
m := make(map[string]interface{}) | ||
k := "" | ||
for _, kv := range kv { | ||
if k == "" { | ||
if skv, ok := kv.(string); !ok { | ||
log.Println("ERROR: jsonmap: expected string key") | ||
s.serverError(w, ErrPreMarshal) | ||
return | ||
} else if skv == "" { | ||
log.Println("ERROR: jsonmap: string key is empty") | ||
s.serverError(w, ErrPreMarshal) | ||
return | ||
} else { | ||
k = skv | ||
} | ||
} else { | ||
m[k] = kv | ||
k = "" | ||
} | ||
} | ||
s.okJSON(w, m) | ||
} | ||
|
||
func (s Server) okJSON(w http.ResponseWriter, data interface{}) { | ||
json, err := json.Marshal(data) | ||
if err != nil { | ||
log.Println("ERROR: json marshal: ", err) | ||
s.serverError(w, err) | ||
return | ||
} | ||
w.Header().Set("Content-Type", "application/json; charset=utf-8") | ||
w.Write(json) | ||
} | ||
|
||
func (s Server) serverError(w http.ResponseWriter, err error) { | ||
http.Error(w, fmt.Sprintln("Server Error: ", err), 500) | ||
} | ||
|
||
// func notFoundHandler(w http.ResponseWriter, r *http.Request) { | ||
// w.Header().Set("Content-Type", "text/plain; charset=utf-8") | ||
// w.Write([]byte("Resource not found")) | ||
// } |
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,42 @@ | ||
#!/bin/bash | ||
|
||
BUILD=1 | ||
RUN= | ||
|
||
while [[ $# > 0 ]] | ||
do | ||
key="$1" | ||
# echo $# " :" $key | ||
case $key in | ||
--build) | ||
BUILD=1 | ||
;; | ||
--run) | ||
RUN=1 | ||
;; | ||
*) | ||
echo "Unknown option:" $1 | ||
exit 1 | ||
;; | ||
esac | ||
shift | ||
done | ||
|
||
if [ $BUILD ] | ||
then | ||
echo; echo "---- go lint" | ||
golint ./... | ||
echo; echo "---- go vet" | ||
go vet ./... | ||
echo; echo "---- go build" | ||
go build | ||
fi | ||
|
||
if [ $RUN ] | ||
then | ||
echo; echo "---- run devel" | ||
# echo "-- vagrant up" | ||
# (cd vagrant && vagrant up) | ||
echo "-- ./gef-docker" | ||
"./gef-docker" | ||
fi |
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,25 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"os" | ||
|
||
"../eudock" | ||
) | ||
|
||
type configuration struct { | ||
Docker []eudock.Config | ||
Server ServerConfig | ||
} | ||
|
||
func readConfigFile(configfilepath string) (configuration, error) { | ||
var config configuration | ||
file, err := os.Open(configfilepath) | ||
if err != nil { | ||
return config, err | ||
} | ||
defer file.Close() | ||
decoder := json.NewDecoder(file) | ||
err = decoder.Decode(&config) | ||
return config, err | ||
} |
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,16 @@ | ||
{ | ||
"Docker": [ | ||
{ | ||
"Endpoint" : "unix:///var/run/docker.sock" | ||
}, | ||
{ | ||
"UseBoot2Docker": true, | ||
"Description": "Using Boot2Docker with its environment variables: $DOCKER_HOST and $DOCKER_CERT_PATH" | ||
} | ||
], | ||
"Server": { | ||
"Address": ":4142", | ||
"ReadTimeoutSecs": 10, | ||
"WriteTimeoutSecs": 10 | ||
} | ||
} |
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,95 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// GefSrvLabelPrefix is the prefix identifying GEF related labels | ||
const GefSrvLabelPrefix = "eudat.gef.service." | ||
|
||
// Service describes metadata for a GEF service | ||
type Service struct { | ||
Name string | ||
Description string | ||
Version string | ||
Input []IOPort | ||
Output []IOPort | ||
} | ||
|
||
// IOPort is an i/o specification for a service | ||
type IOPort struct { | ||
Path string | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
|
||
func extractServiceInfo(labels map[string]string) Service { | ||
srv := Service{} | ||
|
||
for k, v := range labels { | ||
if !strings.HasPrefix(k, GefSrvLabelPrefix) { | ||
continue | ||
} | ||
k = k[len(GefSrvLabelPrefix):] | ||
// fmt.Println(k, " -> ", v) | ||
ks := strings.Split(k, ".") | ||
if len(ks) == 0 { | ||
continue | ||
} | ||
switch ks[0] { | ||
case "name": | ||
srv.Name = v | ||
case "description": | ||
srv.Description = v | ||
case "version": | ||
srv.Version = v | ||
case "input": | ||
addVecValue(&srv.Input, ks[1:], v) | ||
case "output": | ||
addVecValue(&srv.Output, ks[1:], v) | ||
default: | ||
log.Println("Unknown GEF service label: ", k, "=", v) | ||
} | ||
} | ||
|
||
{ | ||
in := make([]IOPort, 0, len(srv.Input)) | ||
for _, p := range srv.Input { | ||
if p.Path != "" { | ||
in = append(in, p) | ||
} | ||
} | ||
srv.Input = in | ||
} | ||
{ | ||
out := make([]IOPort, 0, len(srv.Output)) | ||
for _, p := range srv.Output { | ||
if p.Path != "" { | ||
out = append(out, p) | ||
} | ||
} | ||
srv.Output = out | ||
} | ||
return srv | ||
} | ||
|
||
func addVecValue(vec *[]IOPort, ks []string, value string) { | ||
if len(ks) == 0 { | ||
log.Println("ERROR: GEF service label I/O key empty") | ||
return | ||
} | ||
if len(ks) > 1 { | ||
log.Println("ERROR: GEF service label I/O key has too many parts: ", ks) | ||
return | ||
} | ||
id, err := strconv.ParseUint(ks[0], 10, 8) | ||
if err != nil { | ||
log.Println("ERROR: GEF service label: expecting integer argument for IOPort, instead got: ", ks) | ||
} | ||
for len(*vec) < int(id)+1 { | ||
*vec = append(*vec, IOPort{}) | ||
} | ||
(*vec)[id].Path = value | ||
} |
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,36 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
|
||
"../eudock" | ||
) | ||
import "log" | ||
|
||
var configFilePath = "config.json" | ||
|
||
var config configuration | ||
|
||
func main() { | ||
flag.StringVar(&configFilePath, "config", configFilePath, "configuration file") | ||
flag.Parse() | ||
|
||
var err error | ||
config, err = readConfigFile(configFilePath) | ||
if err != nil { | ||
log.Fatal("FATAL while reading config files: ", err) | ||
} | ||
if len(config.Docker) == 0 { | ||
log.Fatal("FATAL: empty docker configuration list:\n", config) | ||
} | ||
|
||
client, err := eudock.NewClientFirstOf(config.Docker) | ||
if err != nil { | ||
log.Print(err) | ||
log.Fatal("Failed to make any docker client, exiting") | ||
} | ||
|
||
server := NewServer(config.Server, client) | ||
log.Println("Starting GEF server at: ", config.Server.Address) | ||
server.Start() | ||
} |