multinode/console: node list and get api
Change-Id: Icc3cc9e6997b715c865fee9f96c8a848b694f41f
This commit is contained in:
parent
cd2cdb616a
commit
26e65eeefc
45
multinode/console/controllers/common.go
Normal file
45
multinode/console/controllers/common.go
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/spacemonkeygo/monkit/v3"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var (
|
||||
mon = monkit.Package()
|
||||
)
|
||||
|
||||
// NotFound handles API response for not found routes.
|
||||
type NotFound struct {
|
||||
log *zap.Logger
|
||||
}
|
||||
|
||||
// NewNotFound creates new instance of NotFound handler.
|
||||
func NewNotFound(log *zap.Logger) http.Handler {
|
||||
return &NotFound{
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
// ServeHTTP serves 404 response with json error when resource is not found.
|
||||
func (handler *NotFound) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
var response struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
response.Error = "resource not found"
|
||||
|
||||
err := json.NewEncoder(w).Encode(response)
|
||||
if err != nil {
|
||||
handler.log.Error("failed to write json error response", zap.Error(err))
|
||||
}
|
||||
}
|
@ -34,52 +34,139 @@ func NewNodes(log *zap.Logger, service *nodes.Service) *Nodes {
|
||||
}
|
||||
}
|
||||
|
||||
// AddNodeRequest holds all data needed to add node.
|
||||
type AddNodeRequest struct {
|
||||
ID string `json:"id"`
|
||||
APISecret string `json:"apiSecret"`
|
||||
PublicAddress string `json:"publicAddress"`
|
||||
}
|
||||
|
||||
// Add handles node addition.
|
||||
func (controller *Nodes) Add(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
var request AddNodeRequest
|
||||
if err = json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
var payload struct {
|
||||
ID string `json:"id"`
|
||||
APISecret string `json:"apiSecret"`
|
||||
PublicAddress string `json:"publicAddress"`
|
||||
}
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
controller.serveError(w, http.StatusBadRequest, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
id, err := storj.NodeIDFromString(request.ID)
|
||||
id, err := storj.NodeIDFromString(payload.ID)
|
||||
if err != nil {
|
||||
controller.serveError(w, http.StatusBadRequest, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
apiSecret, err := nodes.APISecretFromBase64(request.APISecret)
|
||||
apiSecret, err := nodes.APISecretFromBase64(payload.APISecret)
|
||||
if err != nil {
|
||||
controller.serveError(w, http.StatusBadRequest, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
if err = controller.service.Add(ctx, id, apiSecret, request.PublicAddress); err != nil {
|
||||
// TODO: add more error checks in future, like bad request if address is invalid or unauthorized if secret invalid.
|
||||
if err = controller.service.Add(ctx, id, apiSecret, payload.PublicAddress); err != nil {
|
||||
// TODO: add more error checks in future, like bad payload if address is invalid or unauthorized if secret invalid.
|
||||
controller.log.Error("add node internal error", zap.Error(err))
|
||||
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateName is an endpoint to update node name.
|
||||
func (controller *Nodes) UpdateName(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
segmentParams := mux.Vars(r)
|
||||
|
||||
idString, ok := segmentParams["id"]
|
||||
if !ok {
|
||||
controller.serveError(w, http.StatusBadRequest, ErrNodes.New("id segment parameter is missing"))
|
||||
return
|
||||
}
|
||||
|
||||
id, err := storj.NodeIDFromString(idString)
|
||||
if err != nil {
|
||||
controller.serveError(w, http.StatusBadRequest, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
var payload struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
controller.serveError(w, http.StatusBadRequest, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
err = controller.service.UpdateName(ctx, id, payload.Name)
|
||||
if err != nil {
|
||||
// TODO: add more error checks in future, like not found if node is missing.
|
||||
controller.log.Error("update node name internal error", zap.Error(err))
|
||||
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get handles retrieving node by id.
|
||||
func (controller *Nodes) Get(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
vars := mux.Vars(r)
|
||||
|
||||
nodeID, err := storj.NodeIDFromString(vars["id"])
|
||||
if err != nil {
|
||||
controller.serveError(w, http.StatusBadRequest, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
node, err := controller.service.Get(ctx, nodeID)
|
||||
if err != nil {
|
||||
controller.log.Error("get node not found error", zap.Error(err))
|
||||
controller.serveError(w, http.StatusNotFound, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(w).Encode(node); err != nil {
|
||||
controller.log.Error("failed to write json response", zap.Error(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// List handles retrieving list of nodes.
|
||||
func (controller *Nodes) List(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
nodes, err := controller.service.List(ctx)
|
||||
if err != nil {
|
||||
controller.log.Error("list nodes internal error", zap.Error(err))
|
||||
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
if err = json.NewEncoder(w).Encode(nodes); err != nil {
|
||||
controller.log.Error("failed to write json response", zap.Error(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Delete handles node removal.
|
||||
func (controller *Nodes) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
@ -99,60 +186,17 @@ func (controller *Nodes) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err = controller.service.Remove(ctx, id); err != nil {
|
||||
// TODO: add more error checks in future, like not found if node is missing or unauthorized if secret invalid.
|
||||
// TODO: add more error checks in future, like not found if node is missing.
|
||||
controller.log.Error("delete node internal error", zap.Error(err))
|
||||
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateNodeNameRequest holds all data needed to add node.
|
||||
type UpdateNodeNameRequest struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// UpdateName is an endpoint to update node name.
|
||||
func (controller *Nodes) UpdateName(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
segmentParams := mux.Vars(r)
|
||||
|
||||
idString, ok := segmentParams["id"]
|
||||
if !ok {
|
||||
controller.serveError(w, http.StatusBadRequest, ErrNodes.New("id segment parameter is missing"))
|
||||
return
|
||||
}
|
||||
|
||||
id, err := storj.NodeIDFromString(idString)
|
||||
if err != nil {
|
||||
controller.serveError(w, http.StatusBadRequest, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
var request UpdateNodeNameRequest
|
||||
if err = json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
controller.serveError(w, http.StatusBadRequest, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
|
||||
err = controller.service.Update(ctx, id, request.Name)
|
||||
if err != nil {
|
||||
// TODO: add more error checks in future, like not found if node is missing or unauthorized if secret invalid.
|
||||
controller.serveError(w, http.StatusInternalServerError, ErrNodes.Wrap(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// serveError is used to log error, set http statuses and send error with json.
|
||||
// serveError set http statuses and send json error.
|
||||
func (controller *Nodes) serveError(w http.ResponseWriter, status int, err error) {
|
||||
w.WriteHeader(status)
|
||||
|
||||
controller.log.Error("", zap.Error(err))
|
||||
|
||||
var response struct {
|
||||
Error string `json:"error"`
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// Copyright (C) 2020 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/spacemonkeygo/monkit/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
mon = monkit.Package()
|
||||
)
|
@ -13,7 +13,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"storj.io/storj/multinode/console/server/controllers"
|
||||
"storj.io/storj/multinode/console/controllers"
|
||||
"storj.io/storj/multinode/nodes"
|
||||
)
|
||||
|
||||
@ -51,15 +51,16 @@ func NewServer(log *zap.Logger, config Config, nodes *nodes.Service, listener ne
|
||||
}
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.StrictSlash(true)
|
||||
|
||||
apiRouter := router.PathPrefix("/api/v0").Subrouter()
|
||||
apiRouter.NotFoundHandler = controllers.NewNotFound(server.log)
|
||||
|
||||
nodesController := controllers.NewNodes(server.log, server.nodes)
|
||||
nodesRouter := apiRouter.PathPrefix("/nodes").Subrouter()
|
||||
nodesRouter.HandleFunc("", nodesController.Add).Methods(http.MethodPost)
|
||||
nodesRouter.HandleFunc("/{id}", nodesController.Delete).Methods(http.MethodDelete)
|
||||
nodesRouter.HandleFunc("", nodesController.List).Methods(http.MethodGet)
|
||||
nodesRouter.HandleFunc("/{id}", nodesController.Get).Methods(http.MethodGet)
|
||||
nodesRouter.HandleFunc("/{id}", nodesController.UpdateName).Methods(http.MethodPatch)
|
||||
nodesRouter.HandleFunc("/{id}", nodesController.Delete).Methods(http.MethodDelete)
|
||||
|
||||
server.http = http.Server{
|
||||
Handler: router,
|
||||
|
@ -42,14 +42,39 @@ func (service *Service) Add(ctx context.Context, id storj.NodeID, apiSecret []by
|
||||
return Error.Wrap(service.nodes.Add(ctx, id, apiSecret, publicAddress))
|
||||
}
|
||||
|
||||
// UpdateName will update name of the specified node.
|
||||
func (service *Service) UpdateName(ctx context.Context, id storj.NodeID, name string) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
return Error.Wrap(service.nodes.UpdateName(ctx, id, name))
|
||||
}
|
||||
|
||||
// Get retrieves node by id.
|
||||
func (service *Service) Get(ctx context.Context, id storj.NodeID) (_ Node, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
node, err := service.nodes.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return Node{}, Error.Wrap(err)
|
||||
}
|
||||
|
||||
return node, nil
|
||||
|
||||
}
|
||||
|
||||
// List retrieves list of all added nodes.
|
||||
func (service *Service) List(ctx context.Context) (_ []Node, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
nodes, err := service.nodes.GetAll(ctx)
|
||||
if err != nil {
|
||||
return nil, Error.Wrap(err)
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
// Remove removes node from the system.
|
||||
func (service *Service) Remove(ctx context.Context, id storj.NodeID) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
return Error.Wrap(service.nodes.Remove(ctx, id))
|
||||
}
|
||||
|
||||
// Update will update name of the specified node.
|
||||
func (service *Service) Update(ctx context.Context, id storj.NodeID, name string) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
return Error.Wrap(service.nodes.UpdateName(ctx, id, name))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user