From 07ca6d57ad19f87412211efe760d81e1ed66f21a Mon Sep 17 00:00:00 2001 From: Matthew Jaffee Date: Wed, 11 Apr 2018 09:42:05 -0500 Subject: [PATCH] variety of cleanup in api and handler don't export QueryValidationSpecRequired add docs to some methods simplify /debug/vars handling using expvar.Handler() implement Version in API and simplify --- api.go | 22 +++++++++++++++++++--- handler.go | 52 ++++++++++++++++------------------------------------ 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/api.go b/api.go index 82ab3a4ce..35d478d30 100644 --- a/api.go +++ b/api.go @@ -31,6 +31,8 @@ import ( "github.com/pkg/errors" ) +// API provides the top level programmatic interface to Pilosa. It is usually +// wrapped by a handler which provides an external interface (e.g. HTTP). type API struct { Holder *Holder // The execution engine for running queries. @@ -46,6 +48,7 @@ type API struct { Logger Logger } +// NewAPI returns a new API instance. func NewAPI() *API { return &API{ Broadcaster: NopBroadcaster, @@ -55,7 +58,8 @@ func NewAPI() *API { } } -func (a *API) ExecuteQuery(ctx context.Context, req *QueryRequest) (QueryResponse, error) { +// ExecuteQuery parses a PQL query out of the request and executes it. +func (api *API) ExecuteQuery(ctx context.Context, req *QueryRequest) (QueryResponse, error) { resp := QueryResponse{} q, err := pql.NewParser(strings.NewReader(req.Query)).Parse() @@ -67,7 +71,7 @@ func (a *API) ExecuteQuery(ctx context.Context, req *QueryRequest) (QueryRespons ExcludeAttrs: req.ExcludeAttrs, ExcludeBits: req.ExcludeBits, } - results, err := a.Executor.Execute(ctx, req.Index, q, req.Slices, execOpts) + results, err := api.Executor.Execute(ctx, req.Index, q, req.Slices, execOpts) if err != nil { return resp, err } @@ -86,7 +90,7 @@ func (a *API) ExecuteQuery(ctx context.Context, req *QueryRequest) (QueryRespons } // Retrieve column attributes across all calls. - columnAttrSets, err := a.readColumnAttrSets(a.Holder.Index(req.Index), columnIDs) + columnAttrSets, err := api.readColumnAttrSets(api.Holder.Index(req.Index), columnIDs) if err != nil { return resp, err } @@ -118,6 +122,7 @@ func (api *API) readColumnAttrSets(index *Index, ids []uint64) ([]*ColumnAttrSet return ax, nil } +// CreateIndex makes a new Pilosa index. func (api *API) CreateIndex(ctx context.Context, indexName string, options IndexOptions) (*Index, error) { // Create index. index, err := api.Holder.CreateIndex(indexName, options) @@ -845,6 +850,7 @@ func (api *API) inputJSONDataParser(req map[string]interface{}, index *Index, na return setBits, nil } +// SetCoordinator makes a new Node the cluster coordinator. func (api *API) SetCoordinator(ctx context.Context, id string) (oldNode, newNode *Node, err error) { oldNode = api.Cluster.nodeByID(api.Cluster.Coordinator) newNode = api.Cluster.nodeByID(id) @@ -869,6 +875,8 @@ func (api *API) SetCoordinator(ctx context.Context, id string) (oldNode, newNode return oldNode, newNode, nil } +// RemoveNode puts the cluster into the "RESIZING" state and begins the job of +// removing the given node. func (api *API) RemoveNode(id string) (*Node, error) { removeNode := api.Cluster.nodeByID(id) if removeNode == nil { @@ -891,6 +899,14 @@ func (api *API) ResizeAbort() error { return errors.Wrap(err, "complete current job") } +// State returns the cluster state which is usually "NORMAL", but could be +// "STARTING", "RESIZING", or potentially others. See cluster.go for more +// details. func (api *API) State() string { return api.Cluster.State() } + +// Version returns the Pilosa version. +func (api *API) Version() string { + return strings.TrimPrefix(Version, "v") +} diff --git a/handler.go b/handler.go index d7536f738..4cedeb866 100644 --- a/handler.go +++ b/handler.go @@ -108,14 +108,14 @@ func BuildRouters(handler *Handler) { func (h *Handler) populateValidators() { h.validators = map[string]*queryValidationSpec{} - h.validators["GetFragmentNodes"] = QueryValidationSpecRequired("slice").Optional("index") - h.validators["GetSliceMax"] = QueryValidationSpecRequired().Optional("inverse") - h.validators["PostQuery"] = QueryValidationSpecRequired().Optional("slices", "columnAttrs", "excludeAttrs", "excludeBits") - h.validators["GetExport"] = QueryValidationSpecRequired("index", "frame", "view", "slice") - h.validators["GetFragmentData"] = QueryValidationSpecRequired("index", "frame", "view", "slice") - h.validators["PostFragmentData"] = QueryValidationSpecRequired("index", "frame", "view", "slice") - h.validators["GetFragmentBlocks"] = QueryValidationSpecRequired("index", "frame", "view", "slice") - h.validators["PostFrameRestore"] = QueryValidationSpecRequired("host") + h.validators["GetFragmentNodes"] = queryValidationSpecRequired("slice").Optional("index") + h.validators["GetSliceMax"] = queryValidationSpecRequired().Optional("inverse") + h.validators["PostQuery"] = queryValidationSpecRequired().Optional("slices", "columnAttrs", "excludeAttrs", "excludeBits") + h.validators["GetExport"] = queryValidationSpecRequired("index", "frame", "view", "slice") + h.validators["GetFragmentData"] = queryValidationSpecRequired("index", "frame", "view", "slice") + h.validators["PostFragmentData"] = queryValidationSpecRequired("index", "frame", "view", "slice") + h.validators["GetFragmentBlocks"] = queryValidationSpecRequired("index", "frame", "view", "slice") + h.validators["PostFrameRestore"] = queryValidationSpecRequired("host") } func (h *Handler) queryArgValidator(next http.Handler) http.Handler { @@ -154,7 +154,7 @@ func loadCommon(router *mux.Router, handler *Handler) { router.HandleFunc("/cluster/message", handler.handlePostClusterMessage).Methods("POST") router.HandleFunc("/cluster/resize/set-coordinator", handler.handlePostClusterResizeSetCoordinator).Methods("POST") router.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux).Methods("GET") - router.HandleFunc("/debug/vars", handler.handleExpvar).Methods("GET") + router.Handle("/debug/vars", expvar.Handler()).Methods("GET") router.HandleFunc("/fragment/data", handler.handleGetFragmentData).Methods("GET").Name("GetFragmentData") router.HandleFunc("/hosts", handler.handleGetHosts).Methods("GET") router.HandleFunc("/id", handler.handleGetID).Methods("GET") @@ -174,7 +174,7 @@ func loadRestricted(router *mux.Router, handler *Handler) { func loadNormal(router *mux.Router, handler *Handler) { router.HandleFunc("/cluster/resize/remove-node", handler.handlePostClusterResizeRemoveNode).Methods("POST") router.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux).Methods("GET") - router.HandleFunc("/debug/vars", handler.handleExpvar).Methods("GET") + router.Handle("/debug/vars", expvar.Handler()).Methods("GET") router.HandleFunc("/export", handler.handleGetExport).Methods("GET").Name("GetExport") router.HandleFunc("/fragment/block/data", handler.handleGetFragmentBlockData).Methods("GET") router.HandleFunc("/fragment/blocks", handler.handleGetFragmentBlocks).Methods("GET").Name("GetFragmentBlocks") @@ -270,7 +270,7 @@ func (h *Handler) handleWebUI(w http.ResponseWriter, r *http.Request) { } filesystem, err := h.FileSystem.New() if err != nil { - h.writeQueryResponse(w, r, &QueryResponse{Err: err}) + _ = h.writeQueryResponse(w, r, &QueryResponse{Err: err}) h.Logger.Printf("Pilosa WebUI is not available. Please run `make generate-statik` before building Pilosa with `make install`.") return } @@ -1337,36 +1337,16 @@ func (h *Handler) handleGetHosts(w http.ResponseWriter, r *http.Request) { // handleGetVersion handles /version requests. func (h *Handler) handleGetVersion(w http.ResponseWriter, r *http.Request) { - version := Version - if strings.HasPrefix(version, "v") { - // make the version string semver-compatible - version = version[1:] - } - if err := json.NewEncoder(w).Encode(struct { + err := json.NewEncoder(w).Encode(struct { Version string `json:"version"` }{ - Version: version, - }); err != nil { + Version: h.API.Version(), + }) + if err != nil { h.Logger.Printf("write version response error: %s", err) } } -// handleExpvar handles /debug/vars requests. -func (h *Handler) handleExpvar(w http.ResponseWriter, r *http.Request) { - // Copied from $GOROOT/src/expvar/expvar.go - w.Header().Set("Content-Type", "application/json; charset=utf-8") - fmt.Fprintf(w, "{\n") - first := true - expvar.Do(func(kv expvar.KeyValue) { - if !first { - fmt.Fprintf(w, ",\n") - } - first = false - fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value) - }) - fmt.Fprintf(w, "\n}\n") -} - // QueryResult types. const ( QueryResultTypeNil uint32 = iota @@ -1809,7 +1789,7 @@ type queryValidationSpec struct { args map[string]struct{} } -func QueryValidationSpecRequired(requiredArgs ...string) *queryValidationSpec { +func queryValidationSpecRequired(requiredArgs ...string) *queryValidationSpec { args := map[string]struct{}{} for _, arg := range requiredArgs { args[arg] = struct{}{}