From 4cc93891ee0dc7c64d894fb0add1e48678eaaac1 Mon Sep 17 00:00:00 2001 From: Julian Kornberger Date: Sun, 29 Jan 2017 20:06:56 +0100 Subject: [PATCH] Remove API and simplify webserver --- .travis.yml | 3 +- api/aliases.go | 75 ---------------------------------- api/lib.go | 63 ---------------------------- api/nodes.go | 27 ------------ cmd/respond-collector/main.go | 25 ++---------- config_example.toml | 8 ++-- models/aliases.go | 77 ----------------------------------- models/ansible.go | 55 ------------------------- models/config.go | 15 ++----- models/nodes.go | 2 +- webserver/webserver.go | 24 +++++++++++ 11 files changed, 39 insertions(+), 335 deletions(-) delete mode 100644 api/aliases.go delete mode 100644 api/lib.go delete mode 100644 api/nodes.go delete mode 100644 models/aliases.go delete mode 100644 models/ansible.go create mode 100644 webserver/webserver.go diff --git a/.travis.yml b/.travis.yml index 039ff0e..02756b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: go go: - - 1.7 - tip install: - go get -t github.com/FreifunkBremen/respond-collector/... @@ -8,3 +7,5 @@ install: - go get golang.org/x/tools/cmd/cover script: - ./.test-coverage + - go install github.com/FreifunkBremen/respond-collector/cmd/respond-collector + - go install github.com/FreifunkBremen/respond-collector/cmd/respond-query diff --git a/api/aliases.go b/api/aliases.go deleted file mode 100644 index 85eb1bd..0000000 --- a/api/aliases.go +++ /dev/null @@ -1,75 +0,0 @@ -package api - -import ( - "encoding/json" - "fmt" - "github.com/FreifunkBremen/respond-collector/models" - "github.com/julienschmidt/httprouter" - "net/http" -) - -// GEOROUND : 7 nachkommerstellen sollten genug sein (7cm genau) -// http://blog.3960.org/post/7309573249/genauigkeit-bei-geo-koordinaten -const GEOROUND = 0.0000001 - -func geoEqual(a, b float64) bool { - if (a-b) < GEOROUND && (b-a) < GEOROUND { - return true - } - return false -} - -// AliasesAPI struct for API -type AliasesAPI struct { - aliases *models.Aliases - config *models.Config - nodes *models.Nodes -} - -// NewAliases Bind to API -func NewAliases(config *models.Config, router *httprouter.Router, prefix string, nodes *models.Nodes) { - api := &AliasesAPI{ - aliases: models.NewAliases(config), - nodes: nodes, - config: config, - } - router.GET(prefix, api.GetAll) - router.GET(prefix+"/ansible", api.Ansible) - router.GET(prefix+"/alias/:nodeid", api.GetOne) - router.POST(prefix+"/alias/:nodeid", BasicAuth(api.SaveOne, []byte(config.Webserver.API.Passphrase))) -} - -// GetAll request for get all aliases -func (api *AliasesAPI) GetAll(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - jsonOutput(w, r, api.aliases.List) -} - -// GetOne request for get one alias -func (api *AliasesAPI) GetOne(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - if alias := api.aliases.List[ps.ByName("nodeid")]; alias != nil { - jsonOutput(w, r, alias) - return - } - fmt.Fprint(w, "Not found: ", ps.ByName("nodeid"), "\n") -} - -// SaveOne request for save a alias -func (api *AliasesAPI) SaveOne(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - var alias models.Alias - - err := json.NewDecoder(r.Body).Decode(&alias) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - fmt.Fprint(w, "Decode: ", ps.ByName("nodeid"), "\n") - return - } - api.aliases.Update(ps.ByName("nodeid"), &alias) - fmt.Print("[api] node updated '", ps.ByName("nodeid"), "'\n") - jsonOutput(w, r, alias) -} - -// Ansible json output -func (api *AliasesAPI) Ansible(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - fmt.Print("[api] ansible\n") - jsonOutput(w, r, models.GenerateAnsible(api.nodes, api.aliases.List)) -} diff --git a/api/lib.go b/api/lib.go deleted file mode 100644 index 2cc434e..0000000 --- a/api/lib.go +++ /dev/null @@ -1,63 +0,0 @@ -package api - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "net/http" - "strings" - - "github.com/julienschmidt/httprouter" -) - -func jsonOutput(w http.ResponseWriter, r *http.Request, data interface{}) { - js, err := json.Marshal(data) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - if origin := r.Header.Get("Origin"); origin != "" { - w.Header().Set("Access-Control-Allow-Origin", origin) - } - w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") - w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") - w.Header().Set("Access-Control-Allow-Credentials", "true") - w.Write(js) -} - -// BasicAuth for API request -func BasicAuth(h httprouter.Handle, pass []byte) httprouter.Handle { - return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { - if origin := r.Header.Get("Origin"); origin != "" { - w.Header().Set("Access-Control-Allow-Origin", origin) - } - w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") - w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") - w.Header().Set("Access-Control-Allow-Credentials", "true") - - const basicAuthPrefix string = "Basic " - - // Get the Basic Authentication credentials - auth := r.Header.Get("Authorization") - if strings.HasPrefix(auth, basicAuthPrefix) { - // Check credentials - payload, err := base64.StdEncoding.DecodeString(auth[len(basicAuthPrefix):]) - if err == nil { - pair := bytes.SplitN(payload, []byte(":"), 2) - if len(pair) == 2 && - bytes.Equal(pair[1], pass) { - - // Delegate request to the given handle - h(w, r, ps) - return - } - } - } - - // Request Basic Authentication otherwise - w.Header().Set("WWW-Authenticate", "Basic realm=Restricted") - http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) - } -} diff --git a/api/nodes.go b/api/nodes.go deleted file mode 100644 index c0c3586..0000000 --- a/api/nodes.go +++ /dev/null @@ -1,27 +0,0 @@ -package api - -import ( - "github.com/FreifunkBremen/respond-collector/models" - "github.com/julienschmidt/httprouter" - "net/http" -) - -// NodesAPI struct for API -type NodesAPI struct { - config *models.Config - nodes *models.Nodes -} - -// NewNodes Bind to API -func NewNodes(config *models.Config, router *httprouter.Router, prefix string, nodes *models.Nodes) { - api := &NodesAPI{ - nodes: nodes, - config: config, - } - router.GET(prefix, api.GetAll) -} - -// GetAll request for get all nodes -func (api *NodesAPI) GetAll(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - jsonOutput(w, r, api.nodes.List) -} diff --git a/cmd/respond-collector/main.go b/cmd/respond-collector/main.go index 3634731..4ed16ad 100644 --- a/cmd/respond-collector/main.go +++ b/cmd/respond-collector/main.go @@ -3,20 +3,15 @@ package main import ( "flag" "log" - "net" - "net/http" "os" "os/signal" "syscall" - "github.com/NYTimes/gziphandler" - "github.com/julienschmidt/httprouter" - - "github.com/FreifunkBremen/respond-collector/api" "github.com/FreifunkBremen/respond-collector/database" "github.com/FreifunkBremen/respond-collector/models" "github.com/FreifunkBremen/respond-collector/respond" "github.com/FreifunkBremen/respond-collector/rrd" + "github.com/FreifunkBremen/respond-collector/webserver" ) var ( @@ -54,21 +49,9 @@ func main() { } if config.Webserver.Enable { - router := httprouter.New() - if config.Webserver.API.NewNodes { - api.NewNodes(config, router, "/api/nodes", nodes) - log.Println("api nodes started") - } - if config.Webserver.API.Aliases { - api.NewAliases(config, router, "/api/aliases", nodes) - log.Println("api aliases started") - } - router.NotFound = gziphandler.GzipHandler(http.FileServer(http.Dir(config.Webserver.Webroot))) - - address := net.JoinHostPort(config.Webserver.Address, config.Webserver.Port) - log.Println("starting webserver on", address) - // TODO bad - log.Fatal(http.ListenAndServe(address, router)) + log.Println("starting webserver on", config.Webserver.Bind) + srv := webserver.New(config.Webserver.Bind, config.Webserver.Webroot) + go srv.Close() } // Wait for INT/TERM diff --git a/config_example.toml b/config_example.toml index 19576e1..6b9175d 100644 --- a/config_example.toml +++ b/config_example.toml @@ -3,18 +3,18 @@ enable = true interface = "eth0" collect_interval = "1m" + [webserver] enable = false -port = "8080" -address = "127.0.0.1" +bind = "127.0.0.1:8080" webroot = "webroot" + [nodes] enable = true nodes_version = 2 nodes_path = "/var/www/html/meshviewer/data/nodes_all.json" -graphs_path = "/var/www/html/meshviewer/data/graph.json" -aliases_path = "/var/www/html/meshviewer/data/aliases.json" +graph_path = "/var/www/html/meshviewer/data/graph.json" # Export nodes and graph periodically save_interval = "5s" diff --git a/models/aliases.go b/models/aliases.go deleted file mode 100644 index b263821..0000000 --- a/models/aliases.go +++ /dev/null @@ -1,77 +0,0 @@ -package models - -import ( - "encoding/json" - "io/ioutil" - "log" - "sync" - "time" - - "github.com/FreifunkBremen/respond-collector/data" -) - -// Alias a change request for other nodes -type Alias struct { - Hostname string `json:"hostname,omitempty"` - Location *data.Location `json:"location,omitempty"` - Wireless *data.Wireless `json:"wireless,omitempty"` - Owner string `json:"owner,omitempty"` -} - -// Aliases struct: cache DB of Node's structs -type Aliases struct { - List map[string]*Alias `json:"nodes"` // the current nodemap, indexed by node ID - config *Config - sync.Mutex -} - -// NewAliases create Nodes structs -func NewAliases(config *Config) *Aliases { - aliases := &Aliases{ - List: make(map[string]*Alias), - config: config, - } - - if config.Nodes.AliasesPath != "" { - aliases.load() - } - go aliases.worker() - - return aliases -} - -// Update a alias in aliases cache -func (e *Aliases) Update(nodeID string, newalias *Alias) { - e.Lock() - e.List[nodeID] = newalias - e.Unlock() - -} - -func (e *Aliases) load() { - path := e.config.Nodes.AliasesPath - log.Println("loading", path) - - if data, err := ioutil.ReadFile(path); err == nil { - if err = json.Unmarshal(data, e); err == nil { - log.Println("loaded", len(e.List), "aliases") - } else { - log.Println("failed to unmarshal nodes:", err) - } - - } else { - log.Println("failed loading cached nodes:", err) - } -} - -// Periodically saves the cached DB to json file -func (e *Aliases) worker() { - c := time.Tick(time.Second * 5) - - for range c { - log.Println("saving", len(e.List), "aliases") - e.Lock() - save(e, e.config.Nodes.AliasesPath) - e.Unlock() - } -} diff --git a/models/ansible.go b/models/ansible.go deleted file mode 100644 index 0d774fb..0000000 --- a/models/ansible.go +++ /dev/null @@ -1,55 +0,0 @@ -package models - -// Ansible struct -type Ansible struct { - Nodes []string `json:"nodes"` - Meta struct { - HostVars map[string]*AnsibleHostVars `json:"hostvars,omitempty"` - } `json:"_meta"` -} - -// AnsibleHostVars new values for a node -type AnsibleHostVars struct { - Address string `json:"ansible_ssh_host"` - Hostname string `json:"node_name,omitempty"` - Owner string `json:"owner,omitempty"` - Channel24 uint32 `json:"radio24_channel,omitempty"` - TxPower24 uint32 `json:"radio24_txpower,omitempty"` - Channel5 uint32 `json:"radio5_channel,omitempty"` - TxPower5 uint32 `json:"radio5_txpower,omitempty"` - GeoLatitude float64 `json:"geo_latitude,omitempty"` - GeoLongitude float64 `json:"geo_longitude,omitempty"` -} - -// GenerateAnsible but nodes and aliases together to a ansible change output -func GenerateAnsible(nodes *Nodes, aliases map[string]*Alias) *Ansible { - ansible := &Ansible{Nodes: make([]string, 0)} - ansible.Meta.HostVars = make(map[string]*AnsibleHostVars) - for nodeid, alias := range aliases { - if node := nodes.List[nodeid]; node != nil { - - ansible.Nodes = append(ansible.Nodes, nodeid) - - vars := &AnsibleHostVars{ - Hostname: alias.Hostname, - Owner: alias.Owner, - } - if node.Nodeinfo.Network.Addresses != nil { - vars.Address = node.Nodeinfo.Network.Addresses[0] - } - if alias.Wireless != nil { - vars.Channel24 = alias.Wireless.Channel24 - vars.TxPower24 = alias.Wireless.TxPower24 - vars.Channel5 = alias.Wireless.Channel5 - vars.TxPower5 = alias.Wireless.TxPower5 - } - if alias.Location != nil { - vars.GeoLatitude = alias.Location.Latitude - vars.GeoLongitude = alias.Location.Longtitude - } - ansible.Meta.HostVars[nodeid] = vars - - } - } - return ansible -} diff --git a/models/config.go b/models/config.go index da89ec2..bb63396 100644 --- a/models/config.go +++ b/models/config.go @@ -15,22 +15,15 @@ type Config struct { } Webserver struct { Enable bool - Port string - Address string + Bind string Webroot string - API struct { - Passphrase string - NewNodes bool - Aliases bool - } } Nodes struct { Enable bool - NodesDynamicPath string - NodesPath string NodesVersion int - GraphsPath string - AliasesPath string + NodesPath string + NodesDynamicPath string + GraphPath string SaveInterval Duration // Save nodes periodically PruneAfter Duration // Remove nodes after n days of inactivity } diff --git a/models/nodes.go b/models/nodes.go index 9a47be0..2b24fde 100644 --- a/models/nodes.go +++ b/models/nodes.go @@ -211,7 +211,7 @@ func (nodes *Nodes) save() { } } - if path := nodes.config.Nodes.GraphsPath; path != "" { + if path := nodes.config.Nodes.GraphPath; path != "" { save(nodes.BuildGraph(), path) } } diff --git a/webserver/webserver.go b/webserver/webserver.go new file mode 100644 index 0000000..53af047 --- /dev/null +++ b/webserver/webserver.go @@ -0,0 +1,24 @@ +package webserver + +import ( + "net/http" + + "github.com/NYTimes/gziphandler" +) + +// New creates a new webserver and starts it +func New(bindAddr, webroot string) *http.Server { + srv := &http.Server{ + Addr: bindAddr, + Handler: gziphandler.GzipHandler(http.FileServer(http.Dir(webroot))), + } + + go func() { + // service connections + if err := srv.ListenAndServe(); err != nil { + panic(err) + } + }() + + return srv +}