Skip to content

Commit

Permalink
Merge pull request #147 from jllucas/infoEndpoint
Browse files Browse the repository at this point in the history
Move info endpoint to apihttp package
  • Loading branch information
Jose Luis Lucas committed Jul 24, 2019
2 parents 817604f + af461d2 commit 39f92ec
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 66 deletions.
108 changes: 78 additions & 30 deletions api/apihttp/apihttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package apihttp
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"time"

Expand All @@ -43,8 +42,9 @@ type HealthCheckResponse struct {
// /proofs/membership -> Membership query using event
// /proofs/digest-membership -> Membership query using event digest
// /proofs/incremental -> Incremental query
// /info -> Qed server information
// /info/shards -> Qed cluster information
func NewApiHttp(balloon raftwal.RaftBalloonApi) *http.ServeMux {
func NewApiHttp(balloon raftwal.RaftBalloonApi, conf protocol.NodeInfo) *http.ServeMux {

api := http.NewServeMux()
api.HandleFunc("/healthcheck", AuthHandlerMiddleware(HealthCheckHandler))
Expand All @@ -53,6 +53,7 @@ func NewApiHttp(balloon raftwal.RaftBalloonApi) *http.ServeMux {
api.HandleFunc("/proofs/membership", AuthHandlerMiddleware(Membership(balloon)))
api.HandleFunc("/proofs/digest-membership", AuthHandlerMiddleware(DigestMembership(balloon)))
api.HandleFunc("/proofs/incremental", AuthHandlerMiddleware(Incremental(balloon)))
api.HandleFunc("/info", AuthHandlerMiddleware(InfoHandler(conf)))
api.HandleFunc("/info/shards", AuthHandlerMiddleware(InfoShardsHandler(balloon)))

return api
Expand Down Expand Up @@ -420,7 +421,6 @@ func InfoShardsHandler(balloon raftwal.RaftBalloonApi) http.HandlerFunc {
info := balloon.Info()
details := make(map[string]protocol.ShardDetail)
for k, v := range info["meta"].(map[string]map[string]string) {
fmt.Println(k, v)
details[k] = protocol.ShardDetail{
NodeId: k,
HTTPAddr: v["HTTPAddr"],
Expand All @@ -445,6 +445,81 @@ func InfoShardsHandler(balloon raftwal.RaftBalloonApi) http.HandlerFunc {
}
}

// InfoHandler returns information about the QED server.
// The http post url is:
// GET /info
//
// The following statuses are expected:
// If everything is alright, the HTTP status is 200 and the body contains:
// {
// "APIKey": "my-key",
// "NodeID": "server0",
// "HTTPAddr": "127.0.0.1:8800",
// "RaftAddr": "127.0.0.1:8500",
// "MgmtAddr": "127.0.0.1:8700",
// "MetricsAddr": "127.0.0.1:8600",
// "RaftJoinAddr": "[]",
// "DBPath": "/var/tmp/db",
// "RaftPath": "/var/tmp/wal",
// "GossipAddr": "127.0.0.1:8400",
// "GossipJoinAddr": "[]",
// "PrivateKeyPath": "/var/tmp",
// "EnableTLS": false,
// "EnableProfiling": false,
// "ProfilingAddr": "127.0.0.1:6060",
// "SSLCertificate": "/var/tmp/certs/my-cert",
// "SSLCertificateKey": "/var/tmp/certs",
// }
func InfoHandler(conf protocol.NodeInfo) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var err error

// Make sure we can only be called with an HTTP GET request.
w, _, err = GetReqSanitizer(w, r)
if err != nil {
return
}

out, err := json.Marshal(conf)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
_, _ = w.Write(out)
return

}
}

// PostReqSanitizer function checks that certain request info exists and it is correct.
func PostReqSanitizer(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request, error) {
if r.Method != "POST" {
w.Header().Set("Allow", "POST")
w.WriteHeader(http.StatusMethodNotAllowed)
return w, r, errors.New("Method not allowed.")
}

if r.Body == nil {
http.Error(w, "Please send a request body", http.StatusBadRequest)
return w, r, errors.New("Bad request: nil body.")
}

return w, r, nil
}

// GetReqSanitizer function checks that certain request info exists and it is correct.
func GetReqSanitizer(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request, error) {
if r.Method != "GET" {
w.Header().Set("Allow", "GET")
w.WriteHeader(http.StatusMethodNotAllowed)
return w, r, errors.New("Method not allowed.")
}

return w, r, nil
}

// LogHandler Logs the Http Status for a request into fileHandler and returns a
// httphandler function which is a wrapper to log the requests.
func LogHandler(handle http.Handler) http.HandlerFunc {
Expand Down Expand Up @@ -482,30 +557,3 @@ func (w *statusWriter) Write(b []byte) (int, error) {
w.length = len(b)
return w.ResponseWriter.Write(b)
}

// PostReqSanitizer function checks that certain request info exists and it is correct.
func PostReqSanitizer(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request, error) {
if r.Method != "POST" {
w.Header().Set("Allow", "POST")
w.WriteHeader(http.StatusMethodNotAllowed)
return w, r, errors.New("Method not allowed.")
}

if r.Body == nil {
http.Error(w, "Please send a request body", http.StatusBadRequest)
return w, r, errors.New("Bad request: nil body.")
}

return w, r, nil
}

// GetReqSanitizer function checks that certain request info exists and it is correct.
func GetReqSanitizer(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request, error) {
if r.Method != "GET" {
w.Header().Set("Allow", "GET")
w.WriteHeader(http.StatusMethodNotAllowed)
return w, r, errors.New("Method not allowed.")
}

return w, r, nil
}
93 changes: 82 additions & 11 deletions api/apihttp/apihttp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"testing"
"time"

assert "github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"

"github.com/bbva/qed/balloon"
"github.com/bbva/qed/balloon/history"
Expand Down Expand Up @@ -141,7 +141,18 @@ func (b fakeRaftBalloon) QueryConsistency(start, end uint64) (*balloon.Increment
}

func (b fakeRaftBalloon) Info() map[string]interface{} {
return make(map[string]interface{})
m := make(map[string]interface{})
m["nodeID"] = "node01"
m["leaderID"] = "node01"

node01 := make(map[string]string)
node01["HTTPAddr"] = "127.0.0.1:8800"
meta := make(map[string]map[string]string)
meta["node01"] = node01

m["meta"] = meta

return m
}

func (b fakeRaftBalloon) Backup() error {
Expand Down Expand Up @@ -316,7 +327,7 @@ func TestMembership(t *testing.T) {
actualResult := new(protocol.MembershipResult)
json.Unmarshal([]byte(rr.Body.String()), actualResult)

assert.Equal(t, expectedResult, actualResult, "Incorrect proof")
require.Equal(t, expectedResult, actualResult, "Incorrect proof")

}

Expand Down Expand Up @@ -362,7 +373,7 @@ func TestMembershipConsistency(t *testing.T) {
actualResult := new(protocol.MembershipResult)
json.Unmarshal([]byte(rr.Body.String()), actualResult)

assert.Equal(t, expectedResult, actualResult, "Incorrect proof")
require.Equal(t, expectedResult, actualResult, "Incorrect proof")

}

Expand Down Expand Up @@ -408,7 +419,7 @@ func TestDigestMembership(t *testing.T) {
actualResult := new(protocol.MembershipResult)
json.Unmarshal([]byte(rr.Body.String()), actualResult)

assert.Equal(t, expectedResult, actualResult, "Incorrect proof")
require.Equal(t, expectedResult, actualResult, "Incorrect proof")

}

Expand Down Expand Up @@ -456,7 +467,7 @@ func TestDigestMembershipConsistency(t *testing.T) {
actualResult := new(protocol.MembershipResult)
json.Unmarshal([]byte(rr.Body.String()), actualResult)

assert.Equal(t, expectedResult, actualResult, "Incorrect proof")
require.Equal(t, expectedResult, actualResult, "Incorrect proof")

}

Expand All @@ -469,7 +480,7 @@ func TestIncremental(t *testing.T) {
})

req, err := http.NewRequest("POST", "/proofs/incremental", bytes.NewBuffer(query))
assert.NoError(t, err, "Error querying for incremental proof")
require.NoError(t, err, "Error querying for incremental proof")

// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()
Expand All @@ -486,13 +497,13 @@ func TestIncremental(t *testing.T) {

// Check the status code is what we expect.
status := rr.Code
assert.Equalf(t, http.StatusOK, status, "handler returned wrong status code: got %v want %v", status, http.StatusOK)
require.Equalf(t, http.StatusOK, status, "handler returned wrong status code: got %v want %v", status, http.StatusOK)

// Check the body response
actualResult := new(protocol.IncrementalResponse)
json.Unmarshal([]byte(rr.Body.String()), actualResult)

assert.Equal(t, expectedResult, actualResult, "Incorrect proof")
require.Equal(t, expectedResult, actualResult, "Incorrect proof")
}

func TestAuthHandlerMiddleware(t *testing.T) {
Expand Down Expand Up @@ -520,6 +531,65 @@ func TestAuthHandlerMiddleware(t *testing.T) {
}
}

func TestInfo(t *testing.T) {
req, err := http.NewRequest("GET", "/info", nil)
if err != nil {
t.Fatal(err)
}

// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()
handler := InfoHandler(protocol.NodeInfo{
NodeID: "node01",
})

// Our handlers satisfy http.Handler, so we can call their ServeHTTP method
// directly and pass in our Request and ResponseRecorder.
handler.ServeHTTP(rr, req)

// Check the status code is what we expect.
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}

// Check the body response
nodeInfo := &protocol.NodeInfo{}
_ = json.Unmarshal([]byte(rr.Body.String()), nodeInfo)

require.Equal(t, "node01", nodeInfo.NodeID, "Wrong node ID")
}

func TestInfoShard(t *testing.T) {
req, err := http.NewRequest("GET", "/info/shards", nil)
if err != nil {
t.Fatal(err)
}

// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()
handler := InfoShardsHandler(fakeRaftBalloon{})

// Our handlers satisfy http.Handler, so we can call their ServeHTTP method
// directly and pass in our Request and ResponseRecorder.
handler.ServeHTTP(rr, req)

// Check the status code is what we expect.
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}

// Check the body response
infoShards := &protocol.Shards{}
_ = json.Unmarshal([]byte(rr.Body.String()), infoShards)

require.Equal(t, "node01", infoShards.NodeId, "Wrong node ID")
require.Equal(t, "node01", infoShards.LeaderId, "Wrong leader ID")
require.Equal(t, protocol.Scheme("http"), infoShards.URIScheme, "Wrong scheme")
require.Equal(t, 1, len(infoShards.Shards), "Wrong number of shards")
}

func BenchmarkNoAuth(b *testing.B) {

req, err := http.NewRequest("GET", "/health-check", nil)
Expand Down Expand Up @@ -578,18 +648,19 @@ func newNodeBench(b *testing.B, id int) (*raftwal.RaftBalloon, func()) {
raftPath := fmt.Sprintf("/var/tmp/raft-test/node%d/raft", id)
os.MkdirAll(raftPath, os.FileMode(0755))
r, err := raftwal.NewRaftBalloon(raftPath, ":8301", fmt.Sprintf("%d", id), rocks, make(chan *protocol.Snapshot))
assert.NoError(b, err)
require.NoError(b, err)

return r, closeF

}

func BenchmarkApiAdd(b *testing.B) {

r, clean := newNodeBench(b, 1)
defer clean()

err := r.Open(true, map[string]string{"foo": "bar"})
assert.NoError(b, err)
require.NoError(b, err)

handler := Add(r)

Expand Down
22 changes: 22 additions & 0 deletions protocol/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,25 @@ type Shards struct {
URIScheme Scheme `json:"uriScheme"`
Shards map[string]ShardDetail `json:"shards"`
}

// NodeInfo is the public struct that apihttp.InfoHandler call returns.
type NodeInfo struct {
Log string `json:"log"`
APIKey string `json:"apiKey"`
NodeID string `json:"nodeID"`
HTTPAddr string `json:"httpAddr"`
RaftAddr string `json:"raftAddr"`
MgmtAddr string `json:"mgmtAddr"`
MetricsAddr string `json:"metricsAddr"`
RaftJoinAddr []string `json:"raftJoinAddr"`
DBPath string `json:"dbPath"`
RaftPath string `json:"raftPath"`
GossipAddr string `json:"gossipAddr"`
GossipJoinAddr []string `json:"gossipJoinAddr"`
PrivateKeyPath string `json:"privateKeyPath"`
EnableTLS bool `json:"enableTLS"`
EnableProfiling bool `json:"enableProfiling"`
ProfilingAddr string `json:"profilingAddr"`
SSLCertificate string `json:"sslCertificate"`
SSLCertificateKey string `json:"sslCertificateKey"`
}
26 changes: 1 addition & 25 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,28 +59,6 @@ type Server struct {
snapshotsCh chan *protocol.Snapshot
}

func serverInfo(conf *Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Make sure we can only be called with an HTTP POST request.
if r.Method != "GET" {
w.Header().Set("Allow", "GET")
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

out, err := json.Marshal(conf)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusOK)
w.Write(out)
return

}
}

// NewServer creates a new Server based on the parameters it receives.
func NewServer(conf *Config) (*Server, error) {

Expand Down Expand Up @@ -154,9 +132,7 @@ func NewServer(conf *Config) (*Server, error) {
}

// Create http endpoints
httpMux := apihttp.NewApiHttp(server.raftBalloon)
httpMux.HandleFunc("/info", serverInfo(conf))

httpMux := apihttp.NewApiHttp(server.raftBalloon, protocol.NodeInfo(*conf))
if conf.EnableTLS {
server.httpServer = newTLSServer(conf.HTTPAddr, httpMux)
} else {
Expand Down

0 comments on commit 39f92ec

Please sign in to comment.