storagenode/heldamount/service added, console/heldamountapi added, console/server updated
Change-Id: I6290a6ea1b07b222908440defbbd7aec5f2a4cdf
This commit is contained in:
parent
5ccce04338
commit
7b0371e9e2
100
storagenode/console/consoleheldamount/heldamountapi.go
Normal file
100
storagenode/console/consoleheldamount/heldamountapi.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (C) 2020 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package consoleheldamount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/spacemonkeygo/monkit/v3"
|
||||||
|
"github.com/zeebo/errs"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"storj.io/storj/pkg/storj"
|
||||||
|
"storj.io/storj/storagenode/heldamount"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
contentType = "Content-Type"
|
||||||
|
|
||||||
|
applicationJSON = "application/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mon = monkit.Package()
|
||||||
|
|
||||||
|
// Error is error type of storagenode web console.
|
||||||
|
var Error = errs.Class("heldamount console web error")
|
||||||
|
|
||||||
|
// HeldAmount represents heldmount service.
|
||||||
|
// architecture: Service
|
||||||
|
type HeldAmount struct {
|
||||||
|
service *heldamount.Service
|
||||||
|
|
||||||
|
log *zap.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// jsonOutput defines json structure of api response data.
|
||||||
|
type jsonOutput struct {
|
||||||
|
Data interface{} `json:"data"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHeldAmount creates new instance of heldamount service.
|
||||||
|
func NewHeldAmount(log *zap.Logger, service *heldamount.Service) *HeldAmount {
|
||||||
|
return &HeldAmount{
|
||||||
|
log: log,
|
||||||
|
service: service,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMonthlyHeldAmount returns heldamount, storage holding and prices data for specific month from satellite.
|
||||||
|
func (heldamount *HeldAmount) GetMonthlyHeldAmount(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
defer mon.Task()(&ctx)(nil)
|
||||||
|
|
||||||
|
period := r.URL.Query().Get("period")
|
||||||
|
id := r.URL.Query().Get("satelliteID")
|
||||||
|
|
||||||
|
satelliteID, err := storj.NodeIDFromString(id)
|
||||||
|
if err != nil {
|
||||||
|
heldamount.writeError(w, http.StatusBadRequest, Error.Wrap(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
paystubData, err := heldamount.service.GetPaystubStats(ctx, satelliteID, period)
|
||||||
|
if err != nil {
|
||||||
|
heldamount.writeError(w, http.StatusInternalServerError, Error.Wrap(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
heldamount.writeData(w, paystubData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeData is helper method to write JSON to http.ResponseWriter and log encoding error.
|
||||||
|
func (heldamount *HeldAmount) writeData(w http.ResponseWriter, data interface{}) {
|
||||||
|
w.Header().Set(contentType, applicationJSON)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
output := jsonOutput{Data: data}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(output); err != nil {
|
||||||
|
heldamount.log.Error("json encoder error", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeError writes a JSON error payload to http.ResponseWriter log encoding error.
|
||||||
|
func (heldamount *HeldAmount) writeError(w http.ResponseWriter, status int, err error) {
|
||||||
|
if status >= http.StatusInternalServerError {
|
||||||
|
heldamount.log.Error("api handler server error", zap.Int("status code", status), zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set(contentType, applicationJSON)
|
||||||
|
w.WriteHeader(status)
|
||||||
|
|
||||||
|
output := jsonOutput{Error: err.Error()}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(output); err != nil {
|
||||||
|
heldamount.log.Error("json encoder error", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,9 @@ import (
|
|||||||
|
|
||||||
"storj.io/common/storj"
|
"storj.io/common/storj"
|
||||||
"storj.io/storj/storagenode/console"
|
"storj.io/storj/storagenode/console"
|
||||||
|
"storj.io/storj/storagenode/console/consoleheldamount"
|
||||||
"storj.io/storj/storagenode/console/consolenotifications"
|
"storj.io/storj/storagenode/console/consolenotifications"
|
||||||
|
"storj.io/storj/storagenode/heldamount"
|
||||||
"storj.io/storj/storagenode/notifications"
|
"storj.io/storj/storagenode/notifications"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,24 +50,28 @@ type Server struct {
|
|||||||
|
|
||||||
service *console.Service
|
service *console.Service
|
||||||
notifications *notifications.Service
|
notifications *notifications.Service
|
||||||
|
heldAmount *heldamount.Service
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
|
|
||||||
server http.Server
|
server http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates new instance of storagenode console web server.
|
// NewServer creates new instance of storagenode console web server.
|
||||||
func NewServer(logger *zap.Logger, assets http.FileSystem, notifications *notifications.Service, service *console.Service, listener net.Listener) *Server {
|
func NewServer(logger *zap.Logger, assets http.FileSystem, notifications *notifications.Service, service *console.Service, heldAmount *heldamount.Service, listener net.Listener) *Server {
|
||||||
server := Server{
|
server := Server{
|
||||||
log: logger,
|
log: logger,
|
||||||
service: service,
|
service: service,
|
||||||
listener: listener,
|
listener: listener,
|
||||||
notifications: notifications,
|
notifications: notifications,
|
||||||
|
heldAmount: heldAmount,
|
||||||
}
|
}
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
apiRouter := router.PathPrefix("/api").Subrouter()
|
apiRouter := router.PathPrefix("/api").Subrouter()
|
||||||
notificationRouter := router.PathPrefix("/api/notifications").Subrouter()
|
notificationRouter := router.PathPrefix("/api/notifications").Subrouter()
|
||||||
notificationController := consolenotifications.NewNotifications(server.log, server.notifications)
|
notificationController := consolenotifications.NewNotifications(server.log, server.notifications)
|
||||||
|
heldamountRouter := router.PathPrefix("/api/heldamount").Subrouter()
|
||||||
|
heldamountController := consoleheldamount.NewHeldAmount(server.log, server.heldAmount)
|
||||||
|
|
||||||
if assets != nil {
|
if assets != nil {
|
||||||
fs := http.FileServer(assets)
|
fs := http.FileServer(assets)
|
||||||
@ -84,6 +90,7 @@ func NewServer(logger *zap.Logger, assets http.FileSystem, notifications *notifi
|
|||||||
notificationRouter.Handle("/list", http.HandlerFunc(notificationController.ListNotifications)).Methods(http.MethodGet)
|
notificationRouter.Handle("/list", http.HandlerFunc(notificationController.ListNotifications)).Methods(http.MethodGet)
|
||||||
notificationRouter.Handle("/{id}/read", http.HandlerFunc(notificationController.ReadNotification)).Methods(http.MethodPost)
|
notificationRouter.Handle("/{id}/read", http.HandlerFunc(notificationController.ReadNotification)).Methods(http.MethodPost)
|
||||||
notificationRouter.Handle("/readall", http.HandlerFunc(notificationController.ReadAllNotifications)).Methods(http.MethodPost)
|
notificationRouter.Handle("/readall", http.HandlerFunc(notificationController.ReadAllNotifications)).Methods(http.MethodPost)
|
||||||
|
heldamountRouter.Handle("/{period}", http.HandlerFunc(heldamountController.GetMonthlyHeldAmount)).Methods(http.MethodGet)
|
||||||
|
|
||||||
server.server = http.Server{
|
server.server = http.Server{
|
||||||
Handler: router,
|
Handler: router,
|
||||||
|
132
storagenode/heldamount/service.go
Normal file
132
storagenode/heldamount/service.go
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// Copyright (C) 2020 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package heldamount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spacemonkeygo/monkit/v3"
|
||||||
|
"github.com/zeebo/errs"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"storj.io/common/pb"
|
||||||
|
"storj.io/common/rpc"
|
||||||
|
"storj.io/storj/pkg/storj"
|
||||||
|
"storj.io/storj/storagenode/trust"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// HeldAmountServiceErr defines held amount service error
|
||||||
|
HeldAmountServiceErr = errs.Class("node stats service error")
|
||||||
|
|
||||||
|
mon = monkit.Package()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client encapsulates HeldAmountClient with underlying connection
|
||||||
|
//
|
||||||
|
// architecture: Client
|
||||||
|
type Client struct {
|
||||||
|
conn *rpc.Conn
|
||||||
|
pb.DRPCHeldAmountClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes underlying client connection
|
||||||
|
func (c *Client) Close() error {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service retrieves info from satellites using an rpc client
|
||||||
|
//
|
||||||
|
// architecture: Service
|
||||||
|
type Service struct {
|
||||||
|
log *zap.Logger
|
||||||
|
|
||||||
|
dialer rpc.Dialer
|
||||||
|
trust *trust.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewService creates new instance of service
|
||||||
|
func NewService(log *zap.Logger, dialer rpc.Dialer, trust *trust.Pool) *Service {
|
||||||
|
return &Service{
|
||||||
|
log: log,
|
||||||
|
dialer: dialer,
|
||||||
|
trust: trust,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPaystubStats retrieves held amount for particular satellite
|
||||||
|
func (service *Service) GetPaystubStats(ctx context.Context, satelliteID storj.NodeID, period string) (_ *PayStub, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
client, err := service.dial(ctx, satelliteID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, HeldAmountServiceErr.Wrap(err)
|
||||||
|
}
|
||||||
|
defer func() { err = errs.Combine(err, client.Close()) }()
|
||||||
|
|
||||||
|
requestedPeriod, err := stringToTime(period)
|
||||||
|
if err != nil {
|
||||||
|
return nil, HeldAmountServiceErr.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.GetPayStub(ctx, &pb.GetHeldAmountRequest{Period: requestedPeriod})
|
||||||
|
if err != nil {
|
||||||
|
return nil, HeldAmountServiceErr.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &PayStub{
|
||||||
|
Period: period,
|
||||||
|
SatelliteID: satelliteID,
|
||||||
|
Created: resp.CreatedAt,
|
||||||
|
Codes: resp.Codes,
|
||||||
|
UsageAtRest: float64(resp.UsageAtRest),
|
||||||
|
UsageGet: resp.UsageGet,
|
||||||
|
UsagePut: resp.UsagePut,
|
||||||
|
UsageGetRepair: resp.CompGetRepair,
|
||||||
|
UsagePutRepair: resp.CompPutRepair,
|
||||||
|
UsageGetAudit: resp.UsageGetAudit,
|
||||||
|
CompAtRest: resp.CompAtRest,
|
||||||
|
CompGet: resp.CompGet,
|
||||||
|
CompPut: resp.CompPut,
|
||||||
|
CompGetRepair: resp.CompGetRepair,
|
||||||
|
CompPutRepair: resp.CompPutRepair,
|
||||||
|
CompGetAudit: resp.CompGetAudit,
|
||||||
|
SurgePercent: resp.SurgePercent,
|
||||||
|
Held: resp.Held,
|
||||||
|
Owed: resp.Owed,
|
||||||
|
Disposed: resp.Disposed,
|
||||||
|
Paid: resp.Paid,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// dial dials the HeldAmount client for the satellite by id
|
||||||
|
func (service *Service) dial(ctx context.Context, satelliteID storj.NodeID) (_ *Client, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
|
address, err := service.trust.GetAddress(ctx, satelliteID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.New("unable to find satellite %s: %w", satelliteID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := service.dialer.DialAddressID(ctx, address, satelliteID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.New("unable to connect to the satellite %s: %w", satelliteID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
conn: conn,
|
||||||
|
DRPCHeldAmountClient: pb.NewDRPCHeldAmountClient(conn.Raw()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringToTime(period string) (_ time.Time, err error) {
|
||||||
|
layout := "2006-01"
|
||||||
|
result, err := time.Parse(layout, period)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
@ -230,6 +230,10 @@ type Peer struct {
|
|||||||
Service *notifications.Service
|
Service *notifications.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Heldamount struct {
|
||||||
|
Service *heldamount.Service
|
||||||
|
}
|
||||||
|
|
||||||
Bandwidth *bandwidth.Service
|
Bandwidth *bandwidth.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,6 +488,14 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, revocationDB exten
|
|||||||
debug.Cycle("Orders Cleanup", peer.Storage2.Orders.Cleanup))
|
debug.Cycle("Orders Cleanup", peer.Storage2.Orders.Cleanup))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // setub heldamount service.
|
||||||
|
peer.Heldamount.Service = heldamount.NewService(
|
||||||
|
peer.Log.Named("heldamount:service"),
|
||||||
|
peer.Dialer,
|
||||||
|
peer.Storage2.Trust,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{ // setup node stats service
|
{ // setup node stats service
|
||||||
peer.NodeStats.Service = nodestats.NewService(
|
peer.NodeStats.Service = nodestats.NewService(
|
||||||
peer.Log.Named("nodestats:service"),
|
peer.Log.Named("nodestats:service"),
|
||||||
@ -547,6 +559,7 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB, revocationDB exten
|
|||||||
assets,
|
assets,
|
||||||
peer.Notifications.Service,
|
peer.Notifications.Service,
|
||||||
peer.Console.Service,
|
peer.Console.Service,
|
||||||
|
peer.Heldamount.Service,
|
||||||
peer.Console.Listener,
|
peer.Console.Listener,
|
||||||
)
|
)
|
||||||
peer.Services.Add(lifecycle.Item{
|
peer.Services.Add(lifecycle.Item{
|
||||||
|
Loading…
Reference in New Issue
Block a user