diff --git a/.circleci/config.yml b/.circleci/config.yml index fc18c2f0..b16a5fb2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,6 +4,8 @@ jobs: docker: - image: lbry/lbrytv-ci:latest - image: lbry/lbrynet-tv:latest + environment: + SDK_LBRYUM_SERVERS: spv1.lbry.com:50001 - image: postgres:11-alpine environment: POSTGRES_USER: lbrytv diff --git a/api/handlers.go b/api/handlers.go index 84ad559f..fbea7ff4 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -1,81 +1,22 @@ package api import ( - "encoding/json" "fmt" - "io/ioutil" "net/http" "github.com/lbryio/lbrytv/app/player" - "github.com/lbryio/lbrytv/app/proxy" - "github.com/lbryio/lbrytv/app/users" - "github.com/lbryio/lbrytv/config" "github.com/lbryio/lbrytv/internal/monitor" "github.com/gorilla/mux" - log "github.com/sirupsen/logrus" ) var logger = monitor.NewModuleLogger("api") -// Index just serves a blank home page +// Index serves a blank home page func Index(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusOK) } -// Proxy takes client request body and feeds it to the proxy module -func Proxy(w http.ResponseWriter, req *http.Request) { - var accountID string - - if req.Method == "OPTIONS" { - w.WriteHeader(http.StatusOK) - return - } - if req.Body == nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("empty request body")) - return - } - - body, err := ioutil.ReadAll(req.Body) - if err != nil { - log.Panicf("error: %v", err.Error()) - } - - ur, err := proxy.UnmarshalRequest(body) - if err != nil { - response, _ := json.Marshal(proxy.NewErrorResponse(err.Error(), proxy.ErrProxy)) - w.WriteHeader(http.StatusBadRequest) - w.Write(response) - return - } - - if config.AccountsEnabled() { - accountID, err = users.GetAccountIDFromRequest(req) - if err != nil { - response, _ := json.Marshal(proxy.NewErrorResponse(err.Error(), proxy.ErrProxyAuthFailed)) - w.WriteHeader(http.StatusForbidden) - w.Write(response) - return - } - } else { - accountID = "" - } - - lbrynetResponse, err := proxy.Proxy(ur, accountID) - if err != nil { - logger.LogF(monitor.F{"query": ur, "error": err}).Error("proxy errored") - response, _ := json.Marshal(proxy.NewErrorResponse(err.Error(), proxy.ErrProxy)) - w.WriteHeader(http.StatusServiceUnavailable) - w.Write(response) - return - } - - w.Header().Set("Content-Type", "application/json; charset=utf-8") - w.WriteHeader(http.StatusOK) - w.Write(lbrynetResponse) -} - func stream(uri string, w http.ResponseWriter, req *http.Request) { err := player.PlayURI(uri, w, req) // Only output error if player has not pushed anything to the client yet diff --git a/api/handlers_test.go b/api/handlers_test.go index 53115aa3..840eba9f 100644 --- a/api/handlers_test.go +++ b/api/handlers_test.go @@ -1,78 +1,20 @@ package api import ( - "bytes" - "encoding/json" "io" "net/http" "net/http/httptest" - "strings" "testing" - ljsonrpc "github.com/lbryio/lbry.go/extras/jsonrpc" "github.com/stretchr/testify/assert" - "github.com/ybbus/jsonrpc" ) -func TestProxyOptions(t *testing.T) { - request, _ := http.NewRequest("OPTIONS", "/api/proxy", nil) - rr := httptest.NewRecorder() - http.HandlerFunc(Proxy).ServeHTTP(rr, request) - response := rr.Result() - assert.Equal(t, http.StatusOK, response.StatusCode) -} - -func TestProxyNilQuery(t *testing.T) { - request, _ := http.NewRequest("POST", "/api/proxy", nil) - rr := httptest.NewRecorder() - http.HandlerFunc(Proxy).ServeHTTP(rr, request) - assert.Equal(t, http.StatusBadRequest, rr.Code) - assert.Equal(t, "empty request body", rr.Body.String()) -} - -func TestProxyInvalidQuery(t *testing.T) { - var parsedResponse jsonrpc.RPCResponse - - request, _ := http.NewRequest("POST", "/api/proxy", bytes.NewBuffer([]byte("yo"))) - rr := httptest.NewRecorder() - http.HandlerFunc(Proxy).ServeHTTP(rr, request) - assert.Equal(t, http.StatusBadRequest, rr.Code) - err := json.Unmarshal(rr.Body.Bytes(), &parsedResponse) - if err != nil { - panic(err) - } - assert.True(t, strings.HasPrefix(parsedResponse.Error.Message, "client json parse error: invalid character 'y'")) -} - -func TestProxy(t *testing.T) { - var query *jsonrpc.RPCRequest - var queryBody []byte - var parsedResponse jsonrpc.RPCResponse - resolveResponse := make(ljsonrpc.ResolveResponse) - - query = jsonrpc.NewRequest("resolve", map[string]string{"urls": "one"}) - queryBody, _ = json.Marshal(query) - request, _ := http.NewRequest("POST", "/api/proxy", bytes.NewBuffer(queryBody)) - rr := httptest.NewRecorder() - - http.HandlerFunc(Proxy).ServeHTTP(rr, request) - - assert.Equal(t, http.StatusOK, rr.Code) - assert.Equal(t, "application/json; charset=utf-8", rr.HeaderMap["Content-Type"][0]) - err := json.Unmarshal(rr.Body.Bytes(), &parsedResponse) - if err != nil { - panic(err) - } - ljsonrpc.Decode(parsedResponse.Result, &resolveResponse) - assert.Equal(t, "one", resolveResponse["one"].Name) -} - func TestContentByURL_NoPayment(t *testing.T) { - req, _ := http.NewRequest("GET", "http://localhost:40080/content/url", nil) - req.URL.RawQuery = "pra-onde-vamos-em-2018-seguran-a-online#3a508cce1fda3b7c1a2502cb4323141d40a2cf0b" - req.Header.Add("Range", "bytes=0-1023") + r, _ := http.NewRequest("GET", "http://localhost:40080/content/url", nil) + r.URL.RawQuery = "pra-onde-vamos-em-2018-seguran-a-online#3a508cce1fda3b7c1a2502cb4323141d40a2cf0b" + r.Header.Add("Range", "bytes=0-1023") rr := httptest.NewRecorder() - http.HandlerFunc(ContentByURL).ServeHTTP(rr, req) + http.HandlerFunc(ContentByURL).ServeHTTP(rr, r) assert.Equal(t, http.StatusPaymentRequired, rr.Code) _, err := rr.Body.ReadByte() diff --git a/api/routes.go b/api/routes.go index bfaa96fc..62bd92d5 100644 --- a/api/routes.go +++ b/api/routes.go @@ -3,6 +3,8 @@ package api import ( "net/http" + "github.com/lbryio/lbrytv/app/proxy" + sentryhttp "github.com/getsentry/sentry-go/http" "github.com/gorilla/mux" ) @@ -13,10 +15,12 @@ func captureErrors(handler func(http.ResponseWriter, *http.Request)) http.Handle } // InstallRoutes sets up global API handlers -func InstallRoutes(r *mux.Router) { +func InstallRoutes(ps *proxy.Service, r *mux.Router) { r.HandleFunc("/", Index) - r.HandleFunc("/api/proxy", captureErrors(Proxy)) - r.HandleFunc("/api/proxy/", captureErrors(Proxy)) + + proxyHandler := &proxy.RequestHandler{Service: ps} + r.HandleFunc("/api/proxy", captureErrors(proxyHandler.Handle)) + r.HandleFunc("/api/proxy/", captureErrors(proxyHandler.Handle)) r.HandleFunc("/content/claims/{uri}/{claim}/{filename}", captureErrors(ContentByClaimsURI)) r.HandleFunc("/content/url", captureErrors(ContentByURL)) } diff --git a/app/player/player.go b/app/player/player.go index 39946b7e..2fa11173 100644 --- a/app/player/player.go +++ b/app/player/player.go @@ -42,7 +42,7 @@ type reflectedStream struct { // - Seek simply implements io.Seeker // - Read calculates boundaries and finds blobs that contain the requested stream range, // then calls streamBlobs, which sequentially downloads and decrypts requested blobs -func PlayURI(uri string, w http.ResponseWriter, req *http.Request) (err error) { +func PlayURI(uri string, w http.ResponseWriter, req *http.Request) error { rs, err := newReflectedStream(uri) if err != nil { return err diff --git a/app/player/player_test.go b/app/player/player_test.go index 61fe090e..9f773863 100644 --- a/app/player/player_test.go +++ b/app/player/player_test.go @@ -15,7 +15,7 @@ const streamURL = "what#6769855a9aa43b67086f9ff3c1a5bacb5698a27a" // An MP4 file, size: 128791189 bytes, blobs: 63 const sizedStreamURL = "known-size#0590f924bbee6627a2e79f7f2ff7dfb50bf2877c" -func Test_newReflectedStream(t *testing.T) { +func TestNewReflectedStream(t *testing.T) { rs, err := newReflectedStream(streamURL) if err != nil { // If this fails, no point running other tests @@ -26,12 +26,12 @@ func Test_newReflectedStream(t *testing.T) { rs.SdHash) } -func Test_newReflectedStream_emptyURL(t *testing.T) { +func TestNewReflectedStreamWithEmptyURL(t *testing.T) { _, err := newReflectedStream("") assert.NotNil(t, err) } -func TestReflectedStream_fetchData(t *testing.T) { +func TestNewReflectedStreamFetchData(t *testing.T) { rs, err := newReflectedStream(streamURL) err = rs.fetchData() if err != nil { @@ -42,7 +42,7 @@ func TestReflectedStream_fetchData(t *testing.T) { assert.Equal(t, 38, rs.SDBlob.BlobInfos[38].BlobNum) } -func TestPlayURI_0B_52B(t *testing.T) { +func TestPlayURIFromByte0ToByte52(t *testing.T) { var err error r, _ := http.NewRequest("", "", nil) r.Header.Add("Range", "bytes=0-52") @@ -73,7 +73,7 @@ func TestPlayURI_0B_52B(t *testing.T) { assert.Equal(t, first52, responseFirst52) } -func TestPlayURI_156B_259B(t *testing.T) { +func TestPlayURIFromByte156ToByte259(t *testing.T) { var err error r, _ := http.NewRequest("", "", nil) r.Header.Add("Range", "bytes=156-259") @@ -108,7 +108,7 @@ func TestPlayURI_156B_259B(t *testing.T) { assert.Equal(t, responseData[104:], emptyData) } -func TestPlayURI_4MB_4MB105B(t *testing.T) { +func TestPlayURI104BytesFrom4thMB(t *testing.T) { var err error r, _ := http.NewRequest("", "", nil) r.Header.Add("Range", "bytes=4000000-4000104") @@ -143,7 +143,7 @@ func TestPlayURI_4MB_4MB105B(t *testing.T) { assert.Equal(t, responseData[106:], emptyData) } -func TestPlayURI_LastBytes(t *testing.T) { +func TestPlayURILastBytes(t *testing.T) { var err error r, _ := http.NewRequest("", "", nil) r.Header.Add("Range", "bytes=128791089-") @@ -174,7 +174,7 @@ func TestPlayURI_LastBytes(t *testing.T) { assert.Equal(t, expectedData, responseData) } -func TestPlayURI_Big(t *testing.T) { +func TestPlayURIFromZeroTo100KB(t *testing.T) { var err error r, _ := http.NewRequest("", "", nil) r.Header.Add("Range", "bytes=0-100000") diff --git a/app/player/service.go b/app/player/service.go new file mode 100644 index 00000000..9010c2d2 --- /dev/null +++ b/app/player/service.go @@ -0,0 +1,18 @@ +package player + +type Metrics struct { + ServingStreamsCount int +} + +type PlayerService struct { + Metrics *Metrics +} + +type Player struct { + reflectedStream + URI string +} + +func (ps *PlayerService) NewPlayer(uri string) *Player { + return &Player{URI: uri} +} diff --git a/api/accounts_test.go b/app/proxy/accounts_test.go similarity index 95% rename from api/accounts_test.go rename to app/proxy/accounts_test.go index 66c30920..91302315 100644 --- a/api/accounts_test.go +++ b/app/proxy/accounts_test.go @@ -1,4 +1,4 @@ -package api +package proxy import ( "bytes" @@ -25,8 +25,13 @@ const dummyServerURL = "http://127.0.0.1:59999" const proxySuffix = "/api/proxy" const testSetupWait = 200 * time.Millisecond +var svc *Service + func TestMain(m *testing.M) { // call flag.Parse() here if TestMain uses flags + go launchGrumpyServer() + svc = NewService(config.GetLbrynet()) + config.Override("AccountsEnabled", true) defer config.RestoreOverridden() @@ -38,6 +43,7 @@ func TestMain(m *testing.M) { } c, connCleanup := storage.CreateTestConn(params) c.SetDefaultConnection() + defer connCleanup() defer lbrynet.RemoveAccount(dummyUserID) @@ -117,7 +123,8 @@ func TestWithValidAuthToken(t *testing.T) { r.Header.Add("X-Lbry-Auth-Token", "d94ab9865f8416d107935d2ca644509c") rr := httptest.NewRecorder() - http.HandlerFunc(Proxy).ServeHTTP(rr, r) + handler := &RequestHandler{svc} + handler.Handle(rr, r) require.Equal(t, http.StatusOK, rr.Code) err := json.Unmarshal(rr.Body.Bytes(), &response) require.Nil(t, err) @@ -171,9 +178,11 @@ func TestWithValidAuthTokenConcurrent(t *testing.T) { qBody, _ := json.Marshal(q) r, _ := http.NewRequest("POST", proxySuffix, bytes.NewBuffer(qBody)) r.Header.Add("X-Lbry-Auth-Token", "d94ab9865f8416d107935d2ca644509c") + rr := httptest.NewRecorder() + handler := &RequestHandler{svc} + handler.Handle(rr, r) - http.HandlerFunc(Proxy).ServeHTTP(rr, r) require.Equal(t, http.StatusOK, rr.Code) json.Unmarshal(rr.Body.Bytes(), &response) require.Nil(t, response.Error) @@ -208,7 +217,9 @@ func TestWithWrongAuthToken(t *testing.T) { r.Header.Add("X-Lbry-Auth-Token", "xXxXxXx") rr := httptest.NewRecorder() - http.HandlerFunc(Proxy).ServeHTTP(rr, r) + handler := &RequestHandler{svc} + handler.Handle(rr, r) + assert.Equal(t, http.StatusForbidden, rr.Code) err := json.Unmarshal(rr.Body.Bytes(), &response) require.Nil(t, err) @@ -235,7 +246,9 @@ func TestWithoutToken(t *testing.T) { r, _ := http.NewRequest("POST", proxySuffix, bytes.NewBuffer(qBody)) rr := httptest.NewRecorder() - http.HandlerFunc(Proxy).ServeHTTP(rr, r) + handler := &RequestHandler{svc} + handler.Handle(rr, r) + require.Equal(t, http.StatusOK, rr.Code) err := json.Unmarshal(rr.Body.Bytes(), &response) diff --git a/app/proxy/errors.go b/app/proxy/errors.go new file mode 100644 index 00000000..5f5c5719 --- /dev/null +++ b/app/proxy/errors.go @@ -0,0 +1,99 @@ +package proxy + +import ( + "fmt" + + "github.com/ybbus/jsonrpc" +) + +// ErrProxy is for general errors that originate inside the proxy module +const ErrProxy int = -32080 + +// ErrInternal is a general server error code +const ErrInternal int = -32603 + +// ErrAuthFailed is when supplied auth_token / account_id is not present in the database. +const ErrAuthFailed int = -32085 + +// ErrJSONParse means invalid JSON was received by the server. +const ErrJSONParse int = -32700 + +// ErrInvalidParams signifies a client-supplied params error +const ErrInvalidParams int = -32602 + +// ErrInvalidRequest signifies a general client error +const ErrInvalidRequest int = -32600 + +// ErrMethodUnavailable means the client-requested method cannot be found +const ErrMethodUnavailable int = -32601 + +// CallError is for whatever errors might occur when processing or forwarding client JSON-RPC request +type CallError interface { + AsRPCResponse() *jsonrpc.RPCResponse + Code() int + Error() string +} + +type GenericError struct { + originalError error + code int +} + +// AsRPCResponse returns error as jsonrpc.RPCResponse +func (e GenericError) AsRPCResponse() *jsonrpc.RPCResponse { + return &jsonrpc.RPCResponse{ + Error: &jsonrpc.RPCError{ + Code: e.Code(), + Message: e.Error(), + }, + JSONRPC: "2.0", + } +} + +// AuthFailed is for authentication failures when jsonrpc client has provided a token +type AuthFailed struct { + CallError +} + +// NewError is for general internal errors +func NewError(e error) GenericError { + return GenericError{e, ErrInternal} +} + +// NewParseError is for json parsing errors +func NewParseError(e error) GenericError { + return GenericError{e, ErrJSONParse} +} + +// NewMethodError creates a call method error +func NewMethodError(e error) GenericError { + return GenericError{e, ErrMethodUnavailable} +} + +// NewParamsError creates a call method error +func NewParamsError(e error) GenericError { + return GenericError{e, ErrInvalidParams} +} + +// NewInternalError is for SDK-related errors (connection problems etc) +func NewInternalError(e error) GenericError { + return GenericError{e, ErrInternal} +} + +func (e GenericError) Error() string { + return e.originalError.Error() +} + +// Code returns JSRON-RPC error code +func (e GenericError) Code() int { + return e.code +} + +func (e AuthFailed) Error() string { + return fmt.Sprintf("couldn't find account for in lbrynet") +} + +// Code returns JSRON-RPC error code +func (e AuthFailed) Code() int { + return ErrAuthFailed +} diff --git a/app/proxy/handlers.go b/app/proxy/handlers.go new file mode 100644 index 00000000..217716cd --- /dev/null +++ b/app/proxy/handlers.go @@ -0,0 +1,56 @@ +package proxy + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" + + "github.com/lbryio/lbrytv/app/users" + "github.com/lbryio/lbrytv/config" +) + +// RequestHandler is a wrapper for passing proxy.Service instance to proxy HTTP handler. +type RequestHandler struct { + *Service +} + +// Handle forwards client JSON-RPC request to proxy. +func (rh *RequestHandler) Handle(w http.ResponseWriter, r *http.Request) { + var accountID string + + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusOK) + return + } + if r.Body == nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("empty request body")) + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + log.Panicf("error: %v", err.Error()) + } + + c := rh.Service.NewCaller() + + if config.AccountsEnabled() { + accountID, err = users.GetAccountIDFromRequest(r) + + // TODO: Refactor response creation out of this function + if err != nil { + response, _ := json.Marshal(NewErrorResponse(err.Error(), ErrAuthFailed)) + w.WriteHeader(http.StatusForbidden) + w.Write(response) + return + } + c.SetAccountID(accountID) + } + + rawCallReponse := c.Call(body) + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write(rawCallReponse) +} diff --git a/app/proxy/handlers_test.go b/app/proxy/handlers_test.go new file mode 100644 index 00000000..22519771 --- /dev/null +++ b/app/proxy/handlers_test.go @@ -0,0 +1,76 @@ +package proxy + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + ljsonrpc "github.com/lbryio/lbry.go/extras/jsonrpc" + "github.com/stretchr/testify/assert" + "github.com/ybbus/jsonrpc" +) + +func TestProxyOptions(t *testing.T) { + r, _ := http.NewRequest("OPTIONS", "/api/proxy", nil) + + rr := httptest.NewRecorder() + handler := &RequestHandler{svc} + handler.Handle(rr, r) + + response := rr.Result() + assert.Equal(t, http.StatusOK, response.StatusCode) +} + +func TestProxyNilQuery(t *testing.T) { + r, _ := http.NewRequest("POST", "/api/proxy", nil) + + rr := httptest.NewRecorder() + handler := &RequestHandler{svc} + handler.Handle(rr, r) + + assert.Equal(t, http.StatusBadRequest, rr.Code) + assert.Equal(t, "empty request body", rr.Body.String()) +} + +func TestProxyInvalidQuery(t *testing.T) { + var parsedResponse jsonrpc.RPCResponse + + r, _ := http.NewRequest("POST", "/api/proxy", bytes.NewBuffer([]byte("yo"))) + + rr := httptest.NewRecorder() + handler := &RequestHandler{svc} + handler.Handle(rr, r) + + assert.Equal(t, http.StatusOK, rr.Code) + err := json.Unmarshal(rr.Body.Bytes(), &parsedResponse) + if err != nil { + panic(err) + } + assert.Contains(t, parsedResponse.Error.Message, "invalid character 'y' looking for beginning of value") +} + +func TestProxy(t *testing.T) { + var query *jsonrpc.RPCRequest + var queryBody []byte + var parsedResponse jsonrpc.RPCResponse + resolveResponse := make(ljsonrpc.ResolveResponse) + + query = jsonrpc.NewRequest("resolve", map[string]string{"urls": "one"}) + queryBody, _ = json.Marshal(query) + r, _ := http.NewRequest("POST", "/api/proxy", bytes.NewBuffer(queryBody)) + + rr := httptest.NewRecorder() + handler := &RequestHandler{svc} + handler.Handle(rr, r) + + assert.Equal(t, http.StatusOK, rr.Code) + assert.Equal(t, "application/json; charset=utf-8", rr.HeaderMap["Content-Type"][0]) + err := json.Unmarshal(rr.Body.Bytes(), &parsedResponse) + if err != nil { + panic(err) + } + ljsonrpc.Decode(parsedResponse.Result, &resolveResponse) + assert.Equal(t, "one", resolveResponse["one"].Name) +} diff --git a/app/proxy/predefined_responses.go b/app/proxy/predefined_responses.go new file mode 100644 index 00000000..ca8c7ac8 --- /dev/null +++ b/app/proxy/predefined_responses.go @@ -0,0 +1,63 @@ +package proxy + +import "encoding/json" + +func getStatusResponse() map[string]interface{} { + var response map[string]interface{} + + rawResponse := ` + { + "blob_manager": { + "connections": { + "incoming_bps": {}, + "outgoing_bps": {}, + "time": 0.0, + "total_incoming_mbs": 0.0, + "total_outgoing_mbs": 0.0 + }, + "finished_blobs": 0 + }, + "connection_status": { + "code": "connected", + "message": "No connection problems detected" + }, + "installation_id": "692EAWhtoqDuAfQ6KHMXxFxt8tkhmt7sfprEMHWKjy5hf6PwZcHDV542VHqRnFnTCD", + "is_running": true, + "skipped_components": [ + "hash_announcer", + "blob_server", + "dht" + ], + "startup_status": { + "blob_manager": true, + "blockchain_headers": true, + "database": true, + "exchange_rate_manager": true, + "peer_protocol_server": true, + "stream_manager": true, + "upnp": true, + "wallet": true + }, + "stream_manager": { + "managed_files": 1 + }, + "upnp": { + "aioupnp_version": "0.0.13", + "dht_redirect_set": false, + "external_ip": "127.0.0.1", + "gateway": "No gateway found", + "peer_redirect_set": false, + "redirects": {} + }, + "wallet": { + "best_blockhash": "3d77791b9d87609a004b398e638bcdc91650247ee4448a2b30bf8474668d0ad3", + "blocks": 0, + "blocks_behind": 0, + "is_encrypted": false, + "is_locked": false + } + } + ` + json.Unmarshal([]byte(rawResponse), &response) + return response +} diff --git a/app/proxy/proxy.go b/app/proxy/proxy.go index 930dc1ae..226d4d46 100644 --- a/app/proxy/proxy.go +++ b/app/proxy/proxy.go @@ -57,27 +57,14 @@ var accountSpecificMethods = []string{ "utxo_release", } -// ErrProxy is for general errors that originate inside the proxy module -const ErrProxy int = -32080 - -// ErrProxyAuthFailed is when supplied auth_token / account_id is not present in the database -const ErrProxyAuthFailed int = -32085 - -// ErrInternal is a general server error code -const ErrInternal int = -32603 - -// ErrInvalidParams signifies a client-supplied params error -const ErrInvalidParams int = -32602 - -// ErrMethodNotFound means the client-requested method cannot be found -const ErrMethodNotFound int = -32601 - const methodGet = "get" const methodFileList = "file_list" const methodAccountList = "account_list" -const methodResolve = "resolve" const methodAccountBalance = "account_balance" const methodStatus = "status" +const methodResolve = "resolve" +const methodClaimSearch = "claim_search" +const methodCommentList = "comment_list" const paramAccountID = "account_id" const paramUrls = "urls" @@ -87,6 +74,8 @@ var ignoreLog = []string{ methodStatus, } +var ResolveTime float64 + // UnmarshalRequest takes a raw json request body and serializes it into RPCRequest struct for further processing. func UnmarshalRequest(r []byte) (*jsonrpc.RPCRequest, error) { var ur jsonrpc.RPCRequest @@ -190,13 +179,19 @@ func ForwardCall(request jsonrpc.RPCRequest) ([]byte, error) { return nil, err } if callResult.Error == nil { + execTime := time.Now().Sub(queryStartTime).Seconds() + processedResponse, err = processResponse(&request, callResult) if err != nil { return nil, err } if shouldLog(request.Method) { - monitor.LogSuccessfulQuery(request.Method, time.Now().Sub(queryStartTime).Seconds(), request.Params) + monitor.LogSuccessfulQuery(request.Method, execTime, request.Params) + } + + if request.Method == "resolve" { + ResolveTime = execTime } if shouldCache(request.Method, request.Params) { diff --git a/app/proxy/proxy_test.go b/app/proxy/proxy_test.go index c8926bbb..9c461e4d 100644 --- a/app/proxy/proxy_test.go +++ b/app/proxy/proxy_test.go @@ -4,8 +4,6 @@ import ( "encoding/json" "fmt" "net/http" - "os" - "strconv" "strings" "sync" "testing" @@ -176,11 +174,6 @@ func call(t *testing.T, method string, params ...interface{}) jsonrpc.RPCRespons return response } -func TestMain(m *testing.M) { - go launchGrumpyServer() - os.Exit(m.Run()) -} - // TestForwardCallWithHTTPError tests for HTTP level error connecting to a port that no server is listening on func TestForwardCall_HTTPError(t *testing.T) { config.Override("Lbrynet", "http://127.0.0.1:49999") @@ -236,79 +229,7 @@ func TestUnmarshalRequest(t *testing.T) { assert.True(t, strings.HasPrefix(err.Error(), "client json parse error: invalid character 'y'")) } -func TestForwardCall(t *testing.T) { - var err error - var query *jsonrpc.RPCRequest - var response jsonrpc.RPCResponse - var rawResponse []byte - - query = &jsonrpc.RPCRequest{Method: methodAccountBalance, ID: 123} - rawResponse, err = ForwardCall(*query) - json.Unmarshal(rawResponse, &response) - if err != nil { - t.Errorf("failed with an unexpected error: %v", err) - return - } else if response.Error != nil { - t.Errorf("daemon unexpectedly errored: %v", response.Error.Message) - } - - balanceResult, ok := response.Result.(string) - if !ok { - t.Errorf("unexpected balance result from daemon: %v", response.Result) - } - _, err = strconv.ParseFloat(balanceResult, 32) - if err != nil { - t.Errorf("error parsing result from daemon: %q", response.Result) - } - - streamURI := "what#6769855a9aa43b67086f9ff3c1a5bacb5698a27a" - query = jsonrpc.NewRequest(methodResolve, map[string]string{paramUrls: streamURI}) - queryBody, _ := json.Marshal(query) - query, err = UnmarshalRequest(queryBody) - rawResponse, err = ForwardCall(*query) - if err != nil { - t.Errorf("failed with an unexpected error: %v", err) - return - } else if response.Error != nil { - t.Errorf("daemon errored: %v", response.Error.Message) - return - } - - query = jsonrpc.NewRequest(methodGet, map[string]string{"uri": streamURI}) - _, err = ForwardCall(*query) - if err != nil { - t.Errorf("failed with an unexpected error: %v", err) - return - } - if response.Error != nil { - t.Errorf("daemon errored: %v", response.Error.Message) - return - } - - var resolveResponse *ljsonrpc.ResolveResponse - json.Unmarshal(rawResponse, &response) - response.GetObject(&resolveResponse) - outpoint := fmt.Sprintf("%v:%v", (*resolveResponse)[streamURI].Txid, 0) - - query = jsonrpc.NewRequest(methodFileList, map[string]string{"outpoint": outpoint}) - rawResponse, err = ForwardCall(*query) - if err != nil { - t.Errorf("file_list of outpoint %v failed with an unexpected error: %v", outpoint, err) - return - } - - var fileListResponse *ljsonrpc.FileListResponse - json.Unmarshal(rawResponse, &response) - assert.Nil(t, response.Error) - response.GetObject(&fileListResponse) - - if len(*fileListResponse) == 0 { - t.Errorf("not enough results, daemon responded with %v", fileListResponse) - return - } -} - -func TesProxy_WithCache(t *testing.T) { +func TesProxyWithCache(t *testing.T) { var ( err error query *jsonrpc.RPCRequest diff --git a/app/proxy/query_filters.go b/app/proxy/query_filters.go index 2d49d08a..a25960a6 100644 --- a/app/proxy/query_filters.go +++ b/app/proxy/query_filters.go @@ -1,7 +1,6 @@ package proxy import ( - "encoding/json" "fmt" "github.com/ybbus/jsonrpc" @@ -41,7 +40,7 @@ func methodInList(method string, checkMethods []string) bool { // getPreconditionedQueryResponse returns true if we got a resolve query with more than `cacheResolveLongerThan` urls in it func getPreconditionedQueryResponse(method string, params interface{}) *jsonrpc.RPCResponse { if methodInList(method, forbiddenMethods) { - return NewErrorResponse(fmt.Sprintf("Forbidden method requested: %v", method), ErrMethodNotFound) + return NewErrorResponse(fmt.Sprintf("Forbidden method requested: %v", method), ErrMethodUnavailable) } if paramsMap, ok := params.(map[string]interface{}); ok { @@ -57,63 +56,3 @@ func getPreconditionedQueryResponse(method string, params interface{}) *jsonrpc. } return nil } - -func getStatusResponse() map[string]interface{} { - var response map[string]interface{} - - rawResponse := ` - { - "blob_manager": { - "connections": { - "incoming_bps": {}, - "outgoing_bps": {}, - "time": 0.0, - "total_incoming_mbs": 0.0, - "total_outgoing_mbs": 0.0 - }, - "finished_blobs": 0 - }, - "connection_status": { - "code": "connected", - "message": "No connection problems detected" - }, - "installation_id": "692EAWhtoqDuAfQ6KHMXxFxt8tkhmt7sfprEMHWKjy5hf6PwZcHDV542VHqRnFnTCD", - "is_running": true, - "skipped_components": [ - "hash_announcer", - "blob_server", - "dht" - ], - "startup_status": { - "blob_manager": true, - "blockchain_headers": true, - "database": true, - "exchange_rate_manager": true, - "peer_protocol_server": true, - "stream_manager": true, - "upnp": true, - "wallet": true - }, - "stream_manager": { - "managed_files": 1 - }, - "upnp": { - "aioupnp_version": "0.0.13", - "dht_redirect_set": false, - "external_ip": "127.0.0.1", - "gateway": "No gateway found", - "peer_redirect_set": false, - "redirects": {} - }, - "wallet": { - "best_blockhash": "3d77791b9d87609a004b398e638bcdc91650247ee4448a2b30bf8474668d0ad3", - "blocks": 0, - "blocks_behind": 0, - "is_encrypted": false, - "is_locked": false - } - } - ` - json.Unmarshal([]byte(rawResponse), &response) - return response -} diff --git a/app/proxy/service.go b/app/proxy/service.go new file mode 100644 index 00000000..fb3e4cd8 --- /dev/null +++ b/app/proxy/service.go @@ -0,0 +1,267 @@ +// Service/Caller/Query is a refactoring/improvement over the previous version of proxy module +// currently contained in proxy.go. The old code should be gradually removed and replaced +// by the following approach. + +package proxy + +import ( + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/lbryio/lbrytv/internal/metrics" + "github.com/lbryio/lbrytv/internal/monitor" + + "github.com/ybbus/jsonrpc" +) + +// Service generates Caller objects and keeps execution time metrics +// for all calls proxied through those objects. +type Service struct { + *metrics.ExecTimeMetrics + TargetEndpoint string + logger monitor.QueryMonitor +} + +// Caller patches through JSON-RPC requests from clients, doing pre/post-processing, +// account processing and validation. +type Caller struct { + accountID string + query *jsonrpc.RPCRequest + client jsonrpc.RPCClient + service *Service +} + +// Query is a wrapper around client JSON-RPC query for easier (un)marshaling and processing. +type Query struct { + rawRequest []byte + Request *jsonrpc.RPCRequest +} + +// NewService is the entry point to proxy module. +// Normally only one instance of Service should be created per running server. +func NewService(targetEndpoint string) *Service { + s := Service{ + ExecTimeMetrics: metrics.NewMetrics(), + TargetEndpoint: targetEndpoint, + logger: monitor.NewProxyLogger(), + } + return &s +} + +// NewCaller returns an instance of Caller ready to proxy requests. +// Note that `SetAccountID` needs to be called if an authenticated user is making this call. +func (ps *Service) NewCaller() *Caller { + c := Caller{ + client: jsonrpc.NewClient(ps.TargetEndpoint), + service: ps, + } + return &c +} + +// NewQuery initializes Query object with JSON-RPC request supplied as bytes. +// The object is immediately usable and returns an error in case request parsing fails. +func NewQuery(r []byte) (*Query, error) { + q := &Query{r, &jsonrpc.RPCRequest{}} + err := q.unmarshal() + if err != nil { + return nil, err + } + return q, nil +} + +func (q *Query) unmarshal() error { + err := json.Unmarshal(q.rawRequest, q.Request) + if err != nil { + return err + } + return nil +} + +// Method is a shortcut for query method. +func (q *Query) Method() string { + return q.Request.Method +} + +// Params is a shortcut for query params. +func (q *Query) Params() interface{} { + return q.Request.Params +} + +// ParamsAsMap returns query params converted to plain map. +func (q *Query) ParamsAsMap() map[string]interface{} { + if paramsMap, ok := q.Params().(map[string]interface{}); ok { + return paramsMap + } + return nil +} + +// cacheHit returns true if we got a resolve query with more than `cacheResolveLongerThan` urls in it. +func (q *Query) isCacheable() bool { + if q.Method() == methodResolve && q.Params() != nil { + paramsMap := q.Params().(map[string]interface{}) + if urls, ok := paramsMap[paramUrls].([]interface{}); ok { + if len(urls) > cacheResolveLongerThan { + return true + } + } + } + return false +} + +func (q *Query) newResponse() *jsonrpc.RPCResponse { + var r jsonrpc.RPCResponse + r.ID = q.Request.ID + r.JSONRPC = q.Request.JSONRPC + return &r +} + +// attachAccountID gets called every time by Caller so it's up to Query to decide if it is account-specific +// and if account_id should be added to request params accordingly. +func (q *Query) attachAccountID(id string) { + if methodInList(q.Method(), accountSpecificMethods) { + // monitor.Logger.WithFields(log.Fields{ + // "method": r.Method, "params": r.Params, + // }).Info("got an account-specific method call") + + if p := q.ParamsAsMap(); p != nil { + p[paramAccountID] = id + q.Request.Params = p + } else { + q.Request.Params = map[string]string{"account_id": id} + } + } +} + +// cacheHit returns cached response or nil in case it's a miss or query shouldn't be cacheable. +func (q *Query) cacheHit() *jsonrpc.RPCResponse { + if q.isCacheable() { + if cached := responseCache.Retrieve(q.Method(), q.Params()); cached != nil { + // TODO: Temporary hack to find out why the following line doesn't work + // if mResp, ok := cResp.(map[string]interface{}); ok { + s, _ := json.Marshal(cached) + response := q.newResponse() + err := json.Unmarshal(s, &response) + if err == nil { + monitor.LogCachedQuery(q.Method()) + return response + } + } + } + return nil +} + +func (q *Query) predefinedResponse() *jsonrpc.RPCResponse { + if q.Method() == methodStatus { + response := q.newResponse() + response.Result = getStatusResponse() + return response + } + return nil +} + +func (q *Query) validate() CallError { + if methodInList(q.Method(), forbiddenMethods) { + return NewMethodError(errors.New("forbidden method")) + } + + if q.ParamsAsMap() != nil { + if _, ok := q.ParamsAsMap()[forbiddenParam]; ok { + return NewParamsError(fmt.Errorf("forbidden parameter supplied: %v", forbiddenParam)) + } + } + return nil +} + +// SetAccountID sets accountID for the current instance of Caller +func (c *Caller) SetAccountID(id string) { + c.accountID = id +} + +// AccountID is an SDK account ID for the client this caller instance is serving. +func (c *Caller) AccountID() string { + return c.accountID +} + +func (c *Caller) marshal(r *jsonrpc.RPCResponse) ([]byte, CallError) { + serialized, err := json.MarshalIndent(r, "", " ") + if err != nil { + return nil, NewError(err) + } + return serialized, nil +} + +func (c *Caller) marshalError(e CallError) []byte { + serialized, err := json.MarshalIndent(e.AsRPCResponse(), "", " ") + if err != nil { + return []byte(err.Error()) + } + return serialized +} + +func (c *Caller) sendQuery(q *Query) (*jsonrpc.RPCResponse, error) { + response, err := c.client.CallRaw(q.Request) + if err != nil { + return nil, err + } + return response, nil +} + +func (c *Caller) call(rawQuery []byte) (*jsonrpc.RPCResponse, CallError) { + q, err := NewQuery(rawQuery) + if err != nil { + c.service.logger.Errorf("malformed JSON from client: %s", err.Error()) + return nil, NewParseError(err) + } + if err := q.validate(); err != nil { + return nil, err + } + + if c.AccountID() != "" { + q.attachAccountID(c.AccountID()) + } + + if cachedResponse := q.cacheHit(); cachedResponse != nil { + return cachedResponse, nil + } + if predefinedResponse := q.predefinedResponse(); predefinedResponse != nil { + return predefinedResponse, nil + } + + queryStartTime := time.Now() + r, err := c.sendQuery(q) + if err != nil { + return nil, NewInternalError(err) + } + execTime := time.Now().Sub(queryStartTime).Seconds() + + c.service.LogExecTime(q.Method(), execTime, q.Params()) + + if r.Error == nil { + c.service.logger.LogSuccessfulQuery(q.Method(), execTime, q.Params()) + } else { + c.service.logger.LogFailedQuery(q.Method(), q.Params(), r.Error) + } + + r, err = processResponse(q.Request, r) + + if q.isCacheable() { + responseCache.Save(q.Method(), q.Params(), r) + } + return r, nil +} + +// Call method processes a raw query received from JSON-RPC client and forwards it to SDK. +// It returns a response that is ready to be sent back to the JSON-RPC client as is. +func (c *Caller) Call(rawQuery []byte) []byte { + r, err := c.call(rawQuery) + if err != nil { + return c.marshalError(err) + } + serialized, err := c.marshal(r) + if err != nil { + return c.marshalError(err) + } + return serialized +} diff --git a/app/proxy/service_test.go b/app/proxy/service_test.go new file mode 100644 index 00000000..fb50698f --- /dev/null +++ b/app/proxy/service_test.go @@ -0,0 +1,206 @@ +package proxy + +import ( + "encoding/json" + "fmt" + "math" + "math/rand" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/lbryio/lbrytv/config" + "github.com/lbryio/lbrytv/internal/lbrynet" + + ljsonrpc "github.com/lbryio/lbry.go/extras/jsonrpc" + logrus_test "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/assert" + "github.com/ybbus/jsonrpc" +) + +const endpoint = "http://localhost:5279/" + +func prettyPrint(i interface{}) { + s, _ := json.MarshalIndent(i, "", "\t") + fmt.Println(string(s)) +} + +func newRawRequest(t *testing.T, method string, params interface{}) []byte { + var ( + body []byte + err error + ) + if params != nil { + body, err = json.Marshal(jsonrpc.NewRequest(method, params)) + } else { + body, err = json.Marshal(jsonrpc.NewRequest(method)) + } + if err != nil { + t.Fatal(err) + } + return body +} + +func parseRawResponse(t *testing.T, rawCallReponse []byte, destinationVar interface{}) { + var rpcResponse jsonrpc.RPCResponse + + assert.NotNil(t, rawCallReponse) + + json.Unmarshal(rawCallReponse, &rpcResponse) + rpcResponse.GetObject(destinationVar) +} + +type DummyClient struct{} + +func (c DummyClient) Call(method string, params ...interface{}) (*jsonrpc.RPCResponse, error) { + return &jsonrpc.RPCResponse{ + JSONRPC: "2.0", + Result: "0.0", + }, nil +} + +func (c DummyClient) CallRaw(request *jsonrpc.RPCRequest) (*jsonrpc.RPCResponse, error) { + time.Sleep(250 * time.Millisecond) + return &jsonrpc.RPCResponse{ + JSONRPC: "2.0", + Result: "0.0", + }, nil +} + +func (c DummyClient) CallFor(out interface{}, method string, params ...interface{}) error { + return nil +} + +func (c DummyClient) CallBatch(requests jsonrpc.RPCRequests) (jsonrpc.RPCResponses, error) { + return nil, nil +} + +func (c DummyClient) CallBatchRaw(requests jsonrpc.RPCRequests) (jsonrpc.RPCResponses, error) { + return nil, nil +} + +func TestNewCaller(t *testing.T) { + svc := NewService(endpoint) + c := svc.NewCaller() + assert.Equal(t, svc, c.service) +} + +func TestSetAccountID(t *testing.T) { + svc := NewService(endpoint) + c := svc.NewCaller() + c.SetAccountID("abc") + assert.Equal(t, "abc", c.accountID) +} + +func TestCallerMetrics(t *testing.T) { + svc := NewService(endpoint) + c := Caller{ + client: DummyClient{}, + service: svc, + } + c.Call([]byte(newRawRequest(t, "resolve", map[string]string{"urls": "what"}))) + assert.Equal(t, 0.25, math.Round(svc.GetExecTimeMetrics("resolve").ExecTime*100)/100) +} + +func TestCallResolve(t *testing.T) { + var resolveResponse ljsonrpc.ResolveResponse + + svc := NewService(config.GetLbrynet()) + c := svc.NewCaller() + + resolvedURL := "one#3ae4ed38414e426c29c2bd6aeab7a6ac5da74a98" + resolvedClaimID := "3ae4ed38414e426c29c2bd6aeab7a6ac5da74a98" + + request := newRawRequest(t, "resolve", map[string]string{"urls": resolvedURL}) + rawCallReponse := c.Call(request) + parseRawResponse(t, rawCallReponse, &resolveResponse) + assert.Equal(t, resolvedClaimID, resolveResponse[resolvedURL].ClaimID) + assert.True(t, svc.GetExecTimeMetrics("resolve").ExecTime > 0) +} + +func TestCallAccountList(t *testing.T) { + var accResponse ljsonrpc.Account + + rand.Seed(time.Now().UnixNano()) + dummyAccountID := rand.Int() + + acc, _ := lbrynet.CreateAccount(dummyAccountID) + defer lbrynet.RemoveAccount(dummyAccountID) + + svc := NewService(config.GetLbrynet()) + c := svc.NewCaller() + c.SetAccountID(acc.ID) + + request := newRawRequest(t, "account_list", nil) + rawCallReponse := c.Call(request) + parseRawResponse(t, rawCallReponse, &accResponse) + assert.Equal(t, acc.ID, accResponse.ID) + assert.True(t, svc.GetExecTimeMetrics("account_list").ExecTime > 0) +} + +func TestCallSDKError(t *testing.T) { + var rpcResponse jsonrpc.RPCResponse + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(` + { + "jsonrpc": "2.0", + "error": { + "code": -32500, + "message": "After successfully executing the command, failed to encode result for JSON RPC response.", + "data": [ + "Traceback (most recent call last):", + " File \"lbry/extras/daemon/Daemon.py\", line 482, in handle_old_jsonrpc", + " File \"lbry/extras/daemon/Daemon.py\", line 235, in jsonrpc_dumps_pretty", + " File \"json/__init__.py\", line 238, in dumps", + " File \"json/encoder.py\", line 201, in encode", + " File \"json/encoder.py\", line 431, in _iterencode", + " File \"json/encoder.py\", line 405, in _iterencode_dict", + " File \"json/encoder.py\", line 405, in _iterencode_dict", + " File \"json/encoder.py\", line 325, in _iterencode_list", + " File \"json/encoder.py\", line 438, in _iterencode", + " File \"lbry/extras/daemon/json_response_encoder.py\", line 118, in default", + " File \"lbry/extras/daemon/json_response_encoder.py\", line 151, in encode_output", + " File \"torba/client/baseheader.py\", line 75, in __getitem__", + " File \"lbry/wallet/header.py\", line 35, in deserialize", + "struct.error: unpack requires a buffer of 4 bytes", + "" + ] + }, + "id": 0 + } + `)) + })) + svc := NewService(ts.URL) + c := svc.NewCaller() + + hook := logrus_test.NewLocal(svc.logger.Logger()) + response := c.Call([]byte(newRawRequest(t, "resolve", map[string]string{"urls": "what"}))) + json.Unmarshal(response, &rpcResponse) + assert.Equal(t, rpcResponse.Error.Code, -32500) + assert.Equal(t, "proxy", hook.LastEntry().Data["module"]) + assert.Equal(t, "resolve", hook.LastEntry().Data["method"]) +} + +func TestCallClientJSONError(t *testing.T) { + var rpcResponse jsonrpc.RPCResponse + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"method":"version}`)) + })) + svc := NewService(ts.URL) + c := svc.NewCaller() + + hook := logrus_test.NewLocal(svc.logger.Logger()) + response := c.Call([]byte(`{"method":"version}`)) + json.Unmarshal(response, &rpcResponse) + assert.Equal(t, "2.0", rpcResponse.JSONRPC) + assert.Equal(t, ErrJSONParse, rpcResponse.Error.Code) + assert.Equal(t, "unexpected end of JSON input", rpcResponse.Error.Message) + assert.Equal(t, "malformed JSON from client: unexpected end of JSON input", hook.LastEntry().Message) +} diff --git a/cmd/serve.go b/cmd/serve.go index bf38cd83..d932c31c 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -2,8 +2,12 @@ package cmd import ( "fmt" + "log" "os" + "github.com/lbryio/lbrytv/app/proxy" + "github.com/lbryio/lbrytv/config" + "github.com/lbryio/lbrytv/internal/metrics_server" "github.com/lbryio/lbrytv/server" "github.com/spf13/cobra" @@ -13,7 +17,20 @@ var rootCmd = &cobra.Command{ Use: "lbrytv", Short: "lbrytv is a backend API server for lbry.tv frontend", Run: func(cmd *cobra.Command, args []string) { - server.ServeUntilInterrupted() + s := server.NewServer(server.ServerOpts{ + Address: config.GetAddress(), + ProxyService: proxy.NewService(config.GetLbrynet()), + }) + err := s.Start() + if err != nil { + log.Fatal(err) + } + + ms := metrics_server.NewServer(config.MetricsAddress(), config.MetricsPath(), s.ProxyService) + ms.Serve() + + // ServeUntilShutdown is blocking, should be last + s.ServeUntilShutdown() }, } diff --git a/config/config.go b/config/config.go index 8d703d9e..95fb4b0c 100644 --- a/config/config.go +++ b/config/config.go @@ -50,8 +50,9 @@ func (c *ConfigWrapper) Init() { c.Viper = viper.New() c.Viper.SetEnvPrefix("LW") - c.Viper.BindEnv("Debug") c.Viper.SetDefault("Debug", false) + + c.Viper.BindEnv("Debug") c.Viper.BindEnv("Lbrynet") c.Viper.SetDefault("Lbrynet", "http://localhost:5279/") @@ -128,6 +129,16 @@ func GetAddress() string { return Config.Viper.GetString("Address") } +// MetricsAddress determines address to bind metrics HTTP server to +func MetricsAddress() string { + return Config.Viper.GetString("MetricsAddress") +} + +// MetricsPath determines the path to bind metrics HTTP server to +func MetricsPath() string { + return Config.Viper.GetString("MetricsPath") +} + // GetLbrynet returns the address of SDK server to use func GetLbrynet() string { return Config.Viper.GetString("Lbrynet") diff --git a/deployments/docker-dev/docker-compose.yml b/deployments/docker-dev/docker-compose.yml index c614e3fe..35571855 100644 --- a/deployments/docker-dev/docker-compose.yml +++ b/deployments/docker-dev/docker-compose.yml @@ -38,6 +38,12 @@ services: depends_on: - lbrynet - postgres + prometheus: + image: prom/prometheus:latest + ports: + - 9090:9090 + volumes: + - ${PWD}/prometheus.yml:/etc/prometheus/prometheus.yml volumes: storage: diff --git a/deployments/docker-production/docker-compose.yml b/deployments/docker-production/docker-compose.yml index a3026e37..c571ec77 100644 --- a/deployments/docker-production/docker-compose.yml +++ b/deployments/docker-production/docker-compose.yml @@ -38,10 +38,16 @@ services: target: /static environment: LW_LBRYNET: http://lbrynet:5279/ - LW_ACCOUNTSENABLED: 0 + LW_ACCOUNTSENABLED: 1 depends_on: - lbrynet - postgres + prometheus: + image: prom/prometheus:latest + ports: + - 9090:9090 + volumes: + - ${PWD}/prometheus.yml:/etc/prometheus/prometheus.yml volumes: storage: diff --git a/deployments/docker/app/Dockerfile b/deployments/docker/app/Dockerfile index a3cfd481..8a055ded 100644 --- a/deployments/docker/app/Dockerfile +++ b/deployments/docker/app/Dockerfile @@ -1,5 +1,6 @@ FROM alpine EXPOSE 8080 +EXPOSE 2112 # Needed by updater to connect to github RUN apk --update upgrade && \ diff --git a/deployments/docker/app/config/lbrytv.yml b/deployments/docker/app/config/lbrytv.yml index 682d8863..da29b7d6 100644 --- a/deployments/docker/app/config/lbrytv.yml +++ b/deployments/docker/app/config/lbrytv.yml @@ -1,3 +1,4 @@ +# Lbrynet: set in the docker-compose thru an env variable BaseContentURL: https://api.lbry.tv/content/ InternalAPIHost: https://api.lbry.com @@ -9,3 +10,6 @@ Database: Options: sslmode=disable AccountsEnabled: 0 + +MetricsAddress: :2112 +MetricsPath: /metrics diff --git a/deployments/docker/prometheus/prometheus.yml b/deployments/docker/prometheus/prometheus.yml new file mode 100644 index 00000000..b4c00e4f --- /dev/null +++ b/deployments/docker/prometheus/prometheus.yml @@ -0,0 +1,19 @@ +global: + scrape_interval: 15s + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'codelab-monitor' + +scrape_configs: + # The job name is added as a label `job=` to any timeseries scraped from this config. + - job_name: 'prometheus' + scrape_interval: 5s + static_configs: + - targets: ['localhost:9090'] + + - job_name: 'app' + scrape_interval: 1s + static_configs: + - targets: ['app:2112'] diff --git a/docker-compose.yml b/docker-compose.yml index 07d98ef4..10b251c5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,16 +8,6 @@ services: environment: POSTGRES_USER: lbrytv POSTGRES_PASSWORD: lbrytv - lbrynet37: - image: lbryweb/lbrynet:0.37.3 - ports: - - "5579:5279" - volumes: - - type: bind - source: storage - target: /storage - environment: - LBRY_DOCKER_CONFIG: /daemon/daemon_settings.yml lbrynet: image: lbry/lbrynet-tv:latest ports: @@ -72,16 +62,22 @@ services: app: image: lbry/lbrytv:latest-dev ports: - - "8080:8080" + - 8080:8080 volumes: - type: bind source: storage target: /storage - - type: bind environment: LW_LBRYNET: http://lbrynet:5279/ depends_on: - lbrynet + - postgres + prometheus: + image: prom/prometheus:latest + ports: + - 9090:9090 + volumes: + - ${PWD}/deployments/docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml volumes: storage: diff --git a/go.mod b/go.mod index d2a6ac74..f0ca01d8 100644 --- a/go.mod +++ b/go.mod @@ -1,28 +1,16 @@ module github.com/lbryio/lbrytv require ( - cloud.google.com/go v0.41.0 // indirect github.com/btcsuite/btcd v0.0.0-20190614013741-962a206e94e9 // indirect - github.com/coreos/bbolt v1.3.3 // indirect - github.com/coreos/etcd v3.3.13+incompatible // indirect - github.com/coreos/go-semver v0.3.0 // indirect - github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a // indirect - github.com/cosiner/argv v0.0.1 // indirect github.com/getsentry/sentry-go v0.1.3 - github.com/go-delve/delve v1.2.0 // indirect - github.com/go-kit/kit v0.9.0 // indirect github.com/gobuffalo/packr v1.30.1 // indirect github.com/gobuffalo/packr/v2 v2.5.1 github.com/gofrs/uuid v3.2.0+incompatible // indirect - github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect + github.com/golang/protobuf v1.3.2 // indirect github.com/gorilla/mux v1.7.2 - github.com/grpc-ecosystem/grpc-gateway v1.9.4 // indirect - github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 // indirect github.com/jinzhu/gorm v1.9.9 github.com/jmoiron/sqlx v1.2.0 github.com/kat-co/vala v0.0.0-20170210184112-42e1d8b61f12 - github.com/kisielk/errcheck v1.2.0 // indirect - github.com/kr/pty v1.1.8 // indirect github.com/lbryio/lbry.go v1.0.15-0.20190624042915-7e445e0cf875 github.com/lbryio/lbryschema.go v0.0.0-20190602173230-6d2f69a36f46 // indirect @@ -30,48 +18,31 @@ require ( github.com/lib/pq v1.1.1 github.com/lusis/slack-test v0.0.0-20190426140909-c40012f20018 // indirect github.com/magiconair/properties v1.8.1 // indirect - github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 // indirect - github.com/markbates/safe v1.0.1 // indirect - github.com/mattn/go-colorable v0.1.2 // indirect - github.com/mattn/go-runewidth v0.0.4 // indirect - github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/onsi/ginkgo v1.8.0 // indirect github.com/onsi/gomega v1.5.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pelletier/go-toml v1.4.0 // indirect - github.com/peterh/liner v1.1.0 // indirect github.com/pingcap/errors v0.11.4 // indirect github.com/pkg/errors v0.8.1 - github.com/pkg/profile v1.3.0 // indirect + github.com/prometheus/client_golang v1.0.0 github.com/prometheus/common v0.6.0 // indirect github.com/prometheus/procfs v0.0.3 // indirect - github.com/rogpeppe/fastuuid v1.2.0 // indirect github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c - github.com/russross/blackfriday v2.0.0+incompatible // indirect github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect github.com/sirupsen/logrus v1.4.2 github.com/spf13/afero v1.2.2 // indirect github.com/spf13/cobra v0.0.5 github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.4.0 - github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/testify v1.3.0 - github.com/ugorji/go v1.1.7 // indirect github.com/volatiletech/inflect v0.0.0-20170731032912-e7201282ae8d // indirect github.com/volatiletech/null v8.0.0+incompatible github.com/volatiletech/sqlboiler v3.4.0+incompatible github.com/ybbus/jsonrpc v2.1.2+incompatible github.com/ziutek/mymysql v1.5.4 // indirect - go.etcd.io/bbolt v1.3.3 // indirect - golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c // indirect golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect - golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f // indirect - golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9 // indirect - golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9 // indirect golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 // indirect - golang.org/x/tools v0.0.0-20190716221150-e98af2309876 // indirect - google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610 // indirect - google.golang.org/grpc v1.22.0 // indirect + google.golang.org/appengine v1.6.1 // indirect gopkg.in/gorp.v1 v1.7.2 // indirect ) diff --git a/go.sum b/go.sum index 87cf58be..b92005cb 100644 --- a/go.sum +++ b/go.sum @@ -2,25 +2,20 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro= -cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf h1:eg0MeVzsP1G42dRafH3vf+al2vQIJU0YHX+1Tw87oco= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32 h1:qkOC5Gd33k54tobS36cXdAzJbeHaduLtnLQQwNoIi78= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= @@ -39,21 +34,12 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ= -github.com/cosiner/argv v0.0.1 h1:2iAFN+sWPktbZ4tvxm33Ei8VY66FPCxdOxpncUGpAXE= -github.com/cosiner/argv v0.0.1/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ= -github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -62,7 +48,6 @@ github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgf github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dgryski/go-sip13 v0.0.0-20190329191031-25c5027a8c7b/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -72,18 +57,13 @@ github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/getsentry/sentry-go v0.1.0 h1:rIn718yh/2U0A9OBjcDwQ5TG47r/dac0EYkTX8bVzq0= -github.com/getsentry/sentry-go v0.1.0/go.mod h1:2QfSdvxz4IZGyB5izm1TtADFhlhfj1Dcesrg8+A/T9Y= github.com/getsentry/sentry-go v0.1.3 h1:BCBvQ9DVV8OC2viXb5AmOcvcXQUJBbimRx5RaBjHyKI= github.com/getsentry/sentry-go v0.1.3/go.mod h1:2QfSdvxz4IZGyB5izm1TtADFhlhfj1Dcesrg8+A/T9Y= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-delve/delve v1.2.0 h1:uwGyfYO0WsWqbnDWvxCBKOr2qFLpii3tLxwM+fTJs70= -github.com/go-delve/delve v1.2.0/go.mod h1:yP+LD36s/ud5nm4lsQY0TwNhYu2PAwk6xItz+442j74= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-ini/ini v1.38.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-ozzo/ozzo-validation v3.5.0+incompatible h1:sUy/in/P6askYr16XJgTKq/0SZhiWsdg4WZGaLsGQkM= @@ -94,22 +74,14 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2 h1:8thhT+kUJMTMy3HlX4+y9Da+BNJck+p109tqqKp7WDs= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= github.com/gobuffalo/logger v1.0.0 h1:xw9Ko9EcC5iAFprrjJ6oZco9UpzS5MQ4jAwghsLHdy4= github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= -github.com/gobuffalo/packd v0.1.0 h1:4sGKOD8yaYJ+dek1FDkwcxCHA40M4kfKgFHx8N2kwbU= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.2.0 h1:9Mu1TYbSfH75zQIQtZEgHEaYeRR4485U4T9N1OQYrXk= -github.com/gobuffalo/packd v0.2.0/go.mod h1:k2CkHP3bjbqL2GwxwhxUy1DgnlbW644hkLC9iIUvZwY= github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= github.com/gobuffalo/packr/v2 v2.5.1 h1:TFOeY2VoGamPjQLiNDT3mn//ytzk236VMO2j7iHxJR4= github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754 h1:tpom+2CJmpzAWj5/VEHync2rJGi+epHNIeRSWjzGA+4= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -117,10 +89,8 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= @@ -130,7 +100,6 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= @@ -139,10 +108,7 @@ github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f h1:Jnx61latede7zDD3DiiP4gmNz33uK0U5HDUaF0a/HVQ= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -157,16 +123,12 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/gorm v1.9.9 h1:Gc8bP20O+vroFUzZEXA1r7vNGQZGQ+RKgOnriuNF3ds= @@ -185,14 +147,11 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/karrick/godirwalk v1.10.3 h1:lOpSw2vJP0y5eLBW906QwKsUK/fe/QDyoqM5rnnuPDY= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU= github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kat-co/vala v0.0.0-20170210184112-42e1d8b61f12 h1:DQVOxR9qdYEybJUr/c7ku34r3PfajaMYXZwgDM7KuSk= github.com/kat-co/vala v0.0.0-20170210184112-42e1d8b61f12/go.mod h1:u9MdXq/QageOOSGp7qG4XAQsYUMP+V5zEel/Vrl6OOc= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= @@ -203,8 +162,6 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lbryio/errors.go v0.0.0-20180223142025-ad03d3cc6a5c h1:BhdcWGsuKif/XoSZnqVGNqJ1iEmH0czWR5upj+AuR8M= @@ -235,21 +192,10 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2 h1:JgVTCPf0uBVcUSWpyXmGpgOc62nK5HWUBKAGc3Qqa5k= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v0.0.0-20180511142126-bb74f1db0675/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -258,7 +204,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nlopes/slack v0.5.0 h1:NbIae8Kd0NpqaEI3iUrsuS0KbcEDhzhc939jLW5fNm0= github.com/nlopes/slack v0.5.0/go.mod h1:jVI4BBK3lSktibKahxBF74txcK2vyvkza1z/+rRnVAM= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -278,9 +223,6 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= -github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= -github.com/peterh/liner v1.1.0 h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os= -github.com/peterh/liner v1.1.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.1 h1:BXFZ6MdDd2U1uJUa2sRAWTmm+nieEzuyYM0R4aUTcC8= github.com/pingcap/errors v0.11.1/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= @@ -289,49 +231,44 @@ github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTw github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/prometheus/tsdb v0.8.0/go.mod h1:fSI0j+IUQrDd7+ZtR9WKIGtoYAYAJUKcKhYLG25tN4g= -github.com/prometheus/tsdb v0.9.1/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c h1:LCELEbde3/GT141OpHRs+jJZrI1tI3ayVd4VqW7Ui2U= github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY= -github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sebdah/goldie v0.0.0-20180424091453-8784dd1ab561/go.mod h1:lvjGftC8oe7XPtyrOidaMi0rp5B9+XY/ZRUynGnuaxQ= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shopspring/decimal v0.0.0-20180607144847-19e3cb6c2930/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/sirupsen/logrus v0.0.0-20180523074243-ea8897e79973/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -339,7 +276,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= @@ -347,14 +283,12 @@ github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTd github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= @@ -363,7 +297,6 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= @@ -371,11 +304,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/volatiletech/inflect v0.0.0-20170731032912-e7201282ae8d h1:gI4/tqP6lCY5k6Sg+4k9qSoBXmPwG+xXgMpK7jivD4M= github.com/volatiletech/inflect v0.0.0-20170731032912-e7201282ae8d/go.mod h1:jspfvgf53t5NLUT4o9L1IX0kIBNKamGq1tWc/MgWK9Q= github.com/volatiletech/null v8.0.0+incompatible h1:7wP8m5d/gZ6kW/9GnrLtMCRre2dlEnaQ9Km5OXlK4zg= @@ -390,47 +319,27 @@ github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/arch v0.0.0-20171004143515-077ac972c2e4/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8= -golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c h1:Rx/HTKi09myZ25t1SOlDHmHOy/mKxNAcu0hP1oPX9qM= -golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180614174826-fd5f17ee7299/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443 h1:IcSOAf4PyMp3U3XbIEj1/xJ2BjNN2jWv7JoyOsMxXUU= -golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190627132806-fd42eb6b336f/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190622003408-7e034cad6442/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= @@ -443,20 +352,14 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b h1:lkjdUzSyJ5P1+eal9fxXX9Xg2BTfswsonKUse48C0uE= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -464,7 +367,6 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180614134839-8883426083c0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -474,21 +376,12 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190520201301-c432e742b0af h1:NXfmMfXz6JqGfG3ikSxcz2N93j6DgScr19Oo2uwFu88= golang.org/x/sys v0.0.0-20190520201301-c432e742b0af/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190620070143-6f217b454f45 h1:Dl2hc890lrizvUppGbRWhnIh2f8jOTCQpY5IKWRS0oM= -golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI= golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= @@ -501,30 +394,15 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181120060634-fc4f04983f62/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190613204242-ed0dc450797f/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4 h1:1mMox4TgefDwqluYCv677yNXwlfTkija4owZve/jr78= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190716221150-e98af2309876/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -532,19 +410,9 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20181004005441-af9cb2a35e7f/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= @@ -560,7 +428,6 @@ gopkg.in/nullbio/null.v6 v6.0.0-20161116030900-40264a2e6b79/go.mod h1:gWkaRU7CoX gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170407172122-cd8b52f8269e/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= @@ -570,7 +437,3 @@ gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/environment/environment.go b/internal/environment/environment.go new file mode 100644 index 00000000..e0fae9b1 --- /dev/null +++ b/internal/environment/environment.go @@ -0,0 +1,26 @@ +package environment + +import ( + "github.com/lbryio/lbrytv/app/proxy" + "github.com/lbryio/lbrytv/config" + "github.com/lbryio/lbrytv/internal/monitor" +) + +type Env struct { + *monitor.ModuleLogger + *config.ConfigWrapper + + proxy *proxy.Service +} + +func NewEnvironment(logger *monitor.ModuleLogger, config *config.ConfigWrapper, ps *proxy.Service) *Env { + if logger == nil { + logger = &monitor.ModuleLogger{} + } + + return &Env{ModuleLogger: logger, ConfigWrapper: config, proxy: ps} +} + +func Null() *Env { + return NewEnvironment(nil, nil, nil) +} diff --git a/internal/metrics/collector.go b/internal/metrics/collector.go new file mode 100644 index 00000000..2e9ca19c --- /dev/null +++ b/internal/metrics/collector.go @@ -0,0 +1,47 @@ +package metrics + +type ExecTimeMetricsItem struct { + Name string + ExecTime float64 + Params interface{} +} + +type ExecTimeMetrics struct { + data map[string]ExecTimeMetricsItem + collector chan ExecTimeMetricsItem + queries chan metricsQuery +} + +type metricsQuery struct { + name string + responseChannel chan ExecTimeMetricsItem +} + +func NewMetrics() *ExecTimeMetrics { + metrics := ExecTimeMetrics{ + map[string]ExecTimeMetricsItem{}, + make(chan ExecTimeMetricsItem), + make(chan metricsQuery), + } + go func() { + for { + select { + case query := <-metrics.queries: + query.responseChannel <- metrics.data[query.name] + case m := <-metrics.collector: + metrics.data[m.Name] = m + } + } + }() + return &metrics +} + +func (m *ExecTimeMetrics) LogExecTime(name string, time float64, params interface{}) { + m.collector <- ExecTimeMetricsItem{name, time, params} +} + +func (m *ExecTimeMetrics) GetExecTimeMetrics(name string) ExecTimeMetricsItem { + response := make(chan ExecTimeMetricsItem) + m.queries <- metricsQuery{name, response} + return <-response +} diff --git a/internal/metrics/collector_test.go b/internal/metrics/collector_test.go new file mode 100644 index 00000000..120e6cf8 --- /dev/null +++ b/internal/metrics/collector_test.go @@ -0,0 +1,14 @@ +package metrics + +import ( + "math" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewMetrics(t *testing.T) { + m := NewMetrics() + m.LogExecTime("resolve", 0.25, nil) + assert.Equal(t, 0.25, math.Round(m.GetExecTimeMetrics("resolve").ExecTime*100)/100) +} diff --git a/internal/metrics_server/server.go b/internal/metrics_server/server.go new file mode 100644 index 00000000..8d5bb3e1 --- /dev/null +++ b/internal/metrics_server/server.go @@ -0,0 +1,86 @@ +package metrics_server + +import ( + "net/http" + "runtime" + "sync" + + "github.com/lbryio/lbrytv/app/proxy" + "github.com/lbryio/lbrytv/internal/monitor" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + // "github.com/prometheus/client_golang/prometheus/promauto" +) + +var once sync.Once + +type Server struct { + monitor.ModuleLogger + + proxy *proxy.Service + Address string + Path string +} + +func NewServer(address string, path string, p *proxy.Service) *Server { + return &Server{monitor.NewModuleLogger("metrics_server"), p, address, path} +} + +func (s *Server) Serve() { + once.Do(func() { + s.registerMetrics() + + go func() { + http.Handle(s.Path, promhttp.Handler()) + http.ListenAndServe(s.Address, nil) + }() + s.Log().Infof("metrics server listening on %v%v", s.Address, s.Path) + }) +} + +func (s *Server) registerMetrics() { + if err := prometheus.Register(prometheus.NewGaugeFunc( + prometheus.GaugeOpts{ + Subsystem: "proxy", + Name: "resolve_time", + Help: "Time to serve a single resolve call.", + }, + func() float64 { return s.proxy.GetExecTimeMetrics("resolve").ExecTime }, + )); err == nil { + s.Log().Info("gauge 'proxy_resolve_time' registered") + } + + if err := prometheus.Register(prometheus.NewGaugeFunc( + prometheus.GaugeOpts{ + Subsystem: "proxy", + Name: "claim_search_time", + Help: "Time to serve a claim_search call.", + }, + func() float64 { return s.proxy.GetExecTimeMetrics("claim_search").ExecTime }, + )); err == nil { + s.Log().Info("gauge 'proxy_claim_search' registered") + } + + if err := prometheus.Register(prometheus.NewGaugeFunc( + prometheus.GaugeOpts{ + Subsystem: "player", + Name: "serving_streams_count", + Help: "Number of blob streams currently being served.", + }, + func() float64 { return 0.0 }, + )); err == nil { + s.Log().Info("gauge 'player_serving_streams_count' registered") + } + + if err := prometheus.Register(prometheus.NewGaugeFunc( + prometheus.GaugeOpts{ + Subsystem: "runtime", + Name: "goroutines_count", + Help: "Number of goroutines that currently exist.", + }, + func() float64 { return float64(runtime.NumGoroutine()) }, + )); err == nil { + s.Log().Info("gauge 'goroutines_count' registered") + } +} diff --git a/internal/monitor/monitor.go b/internal/monitor/monitor.go index be298b56..aa5d6ad0 100644 --- a/internal/monitor/monitor.go +++ b/internal/monitor/monitor.go @@ -160,3 +160,54 @@ func RequestLoggingMiddleware(next http.Handler) http.Handler { } }) } + +type QueryMonitor interface { + LogSuccessfulQuery(method string, time float64, params interface{}) + LogFailedQuery(method string, params interface{}, errorResponse interface{}) + Error(message string) + Errorf(message string, args ...interface{}) + Logger() *logrus.Logger +} + +type ProxyLogger struct { + logger *logrus.Logger + entry *logrus.Entry +} + +func NewProxyLogger() *ProxyLogger { + l := ProxyLogger{ + logger: logrus.New(), + } + l.entry = l.logger.WithFields(logrus.Fields{"module": "proxy"}) + return &l +} + +func (l *ProxyLogger) LogSuccessfulQuery(method string, time float64, params interface{}) { + l.entry.WithFields(logrus.Fields{ + "method": method, + "exec_time": time, + "params": params, + }).Info("call proxied") +} + +func (l *ProxyLogger) LogFailedQuery(method string, params interface{}, errorResponse interface{}) { + l.entry.WithFields(logrus.Fields{ + "method": method, + "params": params, + "response": errorResponse, + }).Error("error from the target endpoint") + + captureFailedQuery(method, params, errorResponse) +} + +func (l *ProxyLogger) Error(message string) { + l.entry.Error(message) +} + +func (l *ProxyLogger) Errorf(message string, args ...interface{}) { + l.entry.Errorf(message, args...) +} + +func (l *ProxyLogger) Logger() *logrus.Logger { + return l.logger +} diff --git a/lbrytv.yml b/lbrytv.yml index 92e6396d..fad7d1ae 100644 --- a/lbrytv.yml +++ b/lbrytv.yml @@ -1,4 +1,3 @@ -# Lbrynet: http://localhost:5579/ Lbrynet: http://localhost:5581/ Debug: 1 InternalAPIHost: https://api.lbry.com @@ -11,3 +10,6 @@ Database: Options: sslmode=disable AccountsEnabled: 0 + +MetricsAddress: :2112 +MetricsPath: /metrics diff --git a/server/server.go b/server/server.go index a3949657..e01749f9 100644 --- a/server/server.go +++ b/server/server.go @@ -8,48 +8,56 @@ import ( "syscall" "time" - "github.com/lbryio/lbrytv/config" - "github.com/lbryio/lbrytv/internal/monitor" "github.com/lbryio/lbrytv/api" + "github.com/lbryio/lbrytv/app/proxy" + "github.com/lbryio/lbrytv/internal/environment" + "github.com/lbryio/lbrytv/internal/monitor" "github.com/gorilla/mux" - log "github.com/sirupsen/logrus" ) // Server holds entities that can be used to control the web server type Server struct { - Config *Config - Logger *log.Logger - router *mux.Router - httpListener *http.Server + monitor.ModuleLogger + InterruptChan chan os.Signal DefaultHeaders map[string]string + Environment *environment.Env + ProxyService *proxy.Service + + address string + router *mux.Router + listener *http.Server } -// Config holds basic web server settings -type Config struct { - Address string +// ServerOpts holds basic web server settings +type ServerOpts struct { + Address string + ProxyService *proxy.Service } -// NewConfiguredServer returns a server initialized with settings from global config. -func NewConfiguredServer() *Server { +// NewServer returns a server initialized with settings from global config. +func NewServer(opts ServerOpts) *Server { s := &Server{ - Config: &Config{ - Address: config.GetAddress(), - }, - Logger: monitor.Logger, + ModuleLogger: monitor.NewModuleLogger("server"), InterruptChan: make(chan os.Signal), DefaultHeaders: make(map[string]string), + ProxyService: opts.ProxyService, + address: opts.Address, } s.DefaultHeaders["Access-Control-Allow-Origin"] = "*" s.DefaultHeaders["Access-Control-Allow-Headers"] = "X-Lbry-Auth-Token, Origin, X-Requested-With, Content-Type, Accept" s.DefaultHeaders["Server"] = "api.lbry.tv" + + s.router = s.configureRouter() + s.listener = s.configureListener() + return s } -func (s *Server) configureHTTPListener() *http.Server { +func (s *Server) configureListener() *http.Server { return &http.Server{ - Addr: s.Config.Address, + Addr: s.address, Handler: s.router, ReadTimeout: 5 * time.Second, // WriteTimeout: 30 * time.Second, @@ -70,7 +78,7 @@ func (s *Server) defaultHeadersMiddleware(next http.Handler) http.Handler { func (s *Server) configureRouter() *mux.Router { r := mux.NewRouter() - api.InstallRoutes(r) + api.InstallRoutes(s.ProxyService, r) r.Use(monitor.RequestLoggingMiddleware) r.Use(s.defaultHeadersMiddleware) @@ -79,21 +87,18 @@ func (s *Server) configureRouter() *mux.Router { // Start starts a http server and returns immediately. func (s *Server) Start() error { - s.router = s.configureRouter() - s.httpListener = s.configureHTTPListener() - go func() { - err := s.httpListener.ListenAndServe() + err := s.listener.ListenAndServe() if err != nil { - //Normal graceful shutdown error + // Normal graceful shutdown error if err.Error() == "http: Server closed" { - s.Logger.Info(err) + s.Log().Info(err) } else { - s.Logger.Fatal(err) + s.Log().Fatal(err) } } }() - s.Logger.Printf("listening on %v", s.Config.Address) + s.Log().Infof("http server listening on %v", s.address) return nil } @@ -101,30 +106,17 @@ func (s *Server) Start() error { func (s *Server) ServeUntilShutdown() { signal.Notify(s.InterruptChan, os.Interrupt, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGINT) sig := <-s.InterruptChan - s.Logger.Printf("caught a signal (%v), shutting down http server...", sig) + s.Log().Printf("caught a signal (%v), shutting down http server...", sig) err := s.Shutdown() if err != nil { - s.Logger.Error("error shutting down server: ", err) + s.Log().Error("error shutting down server: ", err) } else { - s.Logger.Info("http server shut down") + s.Log().Info("http server shut down") } } // Shutdown gracefully shuts down the peer server. func (s *Server) Shutdown() error { - err := s.httpListener.Shutdown(context.Background()) + err := s.listener.Shutdown(context.Background()) return err } - -// ServeUntilInterrupted is the main module entry point that configures and starts a webserver, -// which runs until one of OS shutdown signals are received. The function is blocking. -func ServeUntilInterrupted() { - s := NewConfiguredServer() - s.Logger.Info("http server starting...") - err := s.Start() - if err != nil { - log.Fatal(err) - } - s.Logger.Infof("http server listening on %v", s.Config.Address) - s.ServeUntilShutdown() -} diff --git a/server/server_test.go b/server/server_test.go index ce21969a..8565d11a 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -6,15 +6,16 @@ import ( "testing" "time" - "github.com/lbryio/lbrytv/config" + "github.com/lbryio/lbrytv/app/proxy" + "github.com/stretchr/testify/assert" ) func TestStartAndServeUntilShutdown(t *testing.T) { - config.Override("Address", "localhost:40080") - defer config.RestoreOverridden() - - server := NewConfiguredServer() + server := NewServer(ServerOpts{ + Address: "localhost:40080", + ProxyService: proxy.NewService(""), + }) server.Start() go server.ServeUntilShutdown() @@ -41,10 +42,11 @@ func TestHeaders(t *testing.T) { err error response *http.Response ) - config.Override("Address", "localhost:40080") - defer config.RestoreOverridden() - server := NewConfiguredServer() + server := NewServer(ServerOpts{ + Address: "localhost:40080", + ProxyService: proxy.NewService(""), + }) server.Start() go server.ServeUntilShutdown()