multinode/console: operators controller added

Change-Id: I170371baec3c6996bd2af3c332620bd6fee3ed63
This commit is contained in:
crawter 2021-06-01 10:50:18 +03:00
parent 0b3ee4bda7
commit 4af0037a67
5 changed files with 130 additions and 11 deletions

View File

@ -0,0 +1,95 @@
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
package controllers
import (
"encoding/json"
"net/http"
"strconv"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/storj/multinode/operators"
)
var (
// ErrOperators is an internal error type for operators web api controller.
ErrOperators = errs.Class("nodes web api controller")
)
const (
defaultLimit = 5
)
// Operators is a web api controller.
type Operators struct {
log *zap.Logger
service *operators.Service
}
// NewOperators is a constructor for Operators.
func NewOperators(log *zap.Logger, service *operators.Service) *Operators {
return &Operators{
log: log,
service: service,
}
}
// ListPaginated handles retrieval of operators.
func (controller *Operators) ListPaginated(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var err error
defer mon.Task()(&ctx)(&err)
w.Header().Add("Content-Type", "application/json")
limit := int64(defaultLimit)
if limitParam := r.URL.Query().Get("limit"); limitParam != "" {
limit, err = strconv.ParseInt(limitParam, 10, 64)
if err != nil {
controller.serveError(w, http.StatusBadRequest, ErrOperators.Wrap(err))
}
}
pageParam := r.URL.Query().Get("page")
if pageParam == "" {
controller.serveError(w, http.StatusBadRequest, ErrOperators.Wrap(errs.New("page is missing")))
return
}
pageNumber, err := strconv.ParseInt(pageParam, 10, 64)
if err != nil {
controller.serveError(w, http.StatusBadRequest, ErrOperators.Wrap(err))
return
}
cursor := operators.Cursor{
Limit: limit,
Page: pageNumber,
}
page, err := controller.service.ListPaginated(ctx, cursor)
if err != nil {
controller.log.Error("could not get operators page", zap.Error(ErrOperators.Wrap(err)))
controller.serveError(w, http.StatusInternalServerError, ErrOperators.Wrap(err))
return
}
if err = json.NewEncoder(w).Encode(page); err != nil {
controller.log.Error("failed to write json response", zap.Error(ErrOperators.Wrap(err)))
return
}
}
// serveError set http statuses and send json error.
func (controller *Operators) serveError(w http.ResponseWriter, status int, err error) {
w.WriteHeader(status)
var response struct {
Error string `json:"error"`
}
response.Error = err.Error()
err = json.NewEncoder(w).Encode(response)
if err != nil {
controller.log.Error("failed to write json error response", zap.Error(ErrOperators.Wrap(err)))
}
}

View File

@ -17,6 +17,7 @@ import (
"storj.io/storj/multinode/console/controllers"
"storj.io/storj/multinode/nodes"
"storj.io/storj/multinode/operators"
"storj.io/storj/multinode/payouts"
)
@ -37,9 +38,10 @@ type Config struct {
type Server struct {
log *zap.Logger
config Config
nodes *nodes.Service
payouts *payouts.Service
config Config
nodes *nodes.Service
payouts *payouts.Service
operators *operators.Service
listener net.Listener
http http.Server
@ -48,13 +50,14 @@ type Server struct {
}
// NewServer returns new instance of Multinode Dashboard http server.
func NewServer(log *zap.Logger, config Config, nodes *nodes.Service, payouts *payouts.Service, listener net.Listener) (*Server, error) {
func NewServer(log *zap.Logger, config Config, nodes *nodes.Service, payouts *payouts.Service, operators *operators.Service, listener net.Listener) (*Server, error) {
server := Server{
log: log,
config: config,
nodes: nodes,
listener: listener,
payouts: payouts,
log: log,
config: config,
nodes: nodes,
operators: operators,
payouts: payouts,
listener: listener,
}
router := mux.NewRouter()
@ -73,6 +76,10 @@ func NewServer(log *zap.Logger, config Config, nodes *nodes.Service, payouts *pa
nodesRouter.HandleFunc("/{id}", nodesController.UpdateName).Methods(http.MethodPatch)
nodesRouter.HandleFunc("/{id}", nodesController.Delete).Methods(http.MethodDelete)
operatorsController := controllers.NewOperators(server.log, server.operators)
operatorsRouter := apiRouter.PathPrefix("/operators").Subrouter()
operatorsRouter.HandleFunc("", operatorsController.ListPaginated).Methods(http.MethodGet)
payoutsController := controllers.NewPayouts(server.log, server.payouts)
payoutsRouter := apiRouter.PathPrefix("/payouts").Subrouter()
payoutsRouter.HandleFunc("/summaries", payoutsController.Summary).Methods(http.MethodGet)

View File

@ -21,6 +21,8 @@ type DB interface {
// List returns all connected nodes.
List(ctx context.Context) ([]Node, error)
// ListPaged returns paginated nodes list.
// TODO: rename to ListPaginated, because pagination is to divide up copy into pages,
// because paging doesn't necessarily mean pagination in computing.
ListPaged(ctx context.Context, cursor Cursor) (page Page, err error)
// Add creates new node in NodesDB.
// TODO: pass Node entity instead of set of a parameters.

View File

@ -43,8 +43,8 @@ func NewService(log *zap.Logger, dialer rpc.Dialer, nodes nodes.DB) *Service {
}
}
// ListOperatorsPaginated returns paginated list of operators.
func (service *Service) ListOperatorsPaginated(ctx context.Context, cursor Cursor) (_ Page, err error) {
// ListPaginated returns paginated list of operators.
func (service *Service) ListPaginated(ctx context.Context, cursor Cursor) (_ Page, err error) {
defer mon.Task()(&ctx)(&err)
if cursor.Limit > MaxOperatorsOnPage {
cursor.Limit = MaxOperatorsOnPage

View File

@ -17,6 +17,7 @@ import (
"storj.io/private/debug"
"storj.io/storj/multinode/console/server"
"storj.io/storj/multinode/nodes"
"storj.io/storj/multinode/operators"
"storj.io/storj/multinode/payouts"
"storj.io/storj/private/lifecycle"
)
@ -62,6 +63,11 @@ type Peer struct {
Service *nodes.Service
}
// exposes operators related logic.
Operators struct {
Service *operators.Service
}
// contains logic of payouts domain.
Payouts struct {
Service *payouts.Service
@ -105,6 +111,14 @@ func New(log *zap.Logger, full *identity.FullIdentity, config Config, db DB) (_
)
}
{ // operators setup
peer.Operators.Service = operators.NewService(
peer.Log.Named("operators:service"),
peer.Dialer,
peer.DB.Nodes(),
)
}
{ // payouts setup
peer.Payouts.Service = payouts.NewService(
peer.Log.Named("payouts:service"),
@ -124,6 +138,7 @@ func New(log *zap.Logger, full *identity.FullIdentity, config Config, db DB) (_
config.Console,
peer.Nodes.Service,
peer.Payouts.Service,
peer.Operators.Service,
peer.Console.Listener,
)
if err != nil {