2017-03-14 15:37:29 +00:00
|
|
|
// Package restapi implements an IPFS Cluster API component. It provides
|
|
|
|
// a REST-ish API to interact with Cluster over HTTP.
|
2017-03-10 14:29:11 +00:00
|
|
|
package restapi
|
2016-12-02 18:33:39 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2017-07-19 16:54:57 +00:00
|
|
|
"crypto/tls"
|
2016-12-02 18:33:39 +00:00
|
|
|
"encoding/json"
|
2016-12-05 15:24:41 +00:00
|
|
|
"net"
|
2016-12-02 18:33:39 +00:00
|
|
|
"net/http"
|
2017-01-23 17:38:59 +00:00
|
|
|
"strconv"
|
2016-12-15 13:07:19 +00:00
|
|
|
"strings"
|
|
|
|
"sync"
|
2016-12-22 10:31:09 +00:00
|
|
|
"time"
|
2016-12-02 18:33:39 +00:00
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
"github.com/ipfs/ipfs-cluster/api"
|
|
|
|
|
2017-01-30 12:12:25 +00:00
|
|
|
mux "github.com/gorilla/mux"
|
2017-01-25 11:14:39 +00:00
|
|
|
rpc "github.com/hsanjuan/go-libp2p-gorpc"
|
2016-12-16 11:40:28 +00:00
|
|
|
cid "github.com/ipfs/go-cid"
|
2017-03-10 14:29:11 +00:00
|
|
|
logging "github.com/ipfs/go-log"
|
2017-01-30 12:12:25 +00:00
|
|
|
peer "github.com/libp2p/go-libp2p-peer"
|
2017-01-23 17:38:59 +00:00
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
2017-03-16 14:51:24 +00:00
|
|
|
manet "github.com/multiformats/go-multiaddr-net"
|
2016-12-02 18:33:39 +00:00
|
|
|
)
|
|
|
|
|
2017-03-10 14:29:11 +00:00
|
|
|
var logger = logging.Logger("restapi")
|
|
|
|
|
2016-12-22 10:31:09 +00:00
|
|
|
// Server settings
|
|
|
|
var (
|
|
|
|
// maximum duration before timing out read of the request
|
2017-05-18 11:16:27 +00:00
|
|
|
RESTAPIServerReadTimeout = 30 * time.Second
|
2016-12-22 10:31:09 +00:00
|
|
|
// maximum duration before timing out write of the response
|
2017-05-18 11:16:27 +00:00
|
|
|
RESTAPIServerWriteTimeout = 60 * time.Second
|
2016-12-22 10:31:09 +00:00
|
|
|
// server-side the amount of time a Keep-Alive connection will be
|
|
|
|
// kept idle before being reused
|
2017-05-18 11:16:27 +00:00
|
|
|
RESTAPIServerIdleTimeout = 120 * time.Second
|
2016-12-22 10:31:09 +00:00
|
|
|
)
|
|
|
|
|
2016-12-16 18:14:45 +00:00
|
|
|
// RESTAPI implements an API and aims to provides
|
2016-12-02 18:33:39 +00:00
|
|
|
// a RESTful HTTP API for Cluster.
|
2016-12-16 18:14:45 +00:00
|
|
|
type RESTAPI struct {
|
2017-03-02 12:57:37 +00:00
|
|
|
ctx context.Context
|
|
|
|
cancel func()
|
|
|
|
|
2017-03-16 14:51:24 +00:00
|
|
|
apiAddr ma.Multiaddr
|
|
|
|
rpcClient *rpc.Client
|
|
|
|
rpcReady chan struct{}
|
|
|
|
router *mux.Router
|
2016-12-05 15:24:41 +00:00
|
|
|
|
|
|
|
listener net.Listener
|
2016-12-09 19:54:46 +00:00
|
|
|
server *http.Server
|
2016-12-05 15:24:41 +00:00
|
|
|
|
2016-12-15 13:07:19 +00:00
|
|
|
shutdownLock sync.Mutex
|
|
|
|
shutdown bool
|
|
|
|
wg sync.WaitGroup
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
2017-08-29 01:41:54 +00:00
|
|
|
// Config provide is used in the NewRESTAPI constructor to define the desired
|
|
|
|
// parameters for the RESTAPI. The only required field is apiMAddr, the rest
|
|
|
|
// of the fields are optional. Generally, if an optional field is empty
|
|
|
|
// the corresponding feature will not be used.
|
|
|
|
type Config struct {
|
|
|
|
// required
|
|
|
|
ApiMAddr ma.Multiaddr
|
|
|
|
// optional
|
|
|
|
TLS *tls.Config
|
|
|
|
BasicAuthCreds map[string]string
|
|
|
|
}
|
|
|
|
|
2016-12-02 18:33:39 +00:00
|
|
|
type route struct {
|
|
|
|
Name string
|
|
|
|
Method string
|
|
|
|
Pattern string
|
|
|
|
HandlerFunc http.HandlerFunc
|
|
|
|
}
|
|
|
|
|
2017-01-30 12:12:25 +00:00
|
|
|
type peerAddBody struct {
|
|
|
|
PeerMultiaddr string `json:"peer_multiaddress"`
|
|
|
|
}
|
|
|
|
|
2017-08-29 01:41:54 +00:00
|
|
|
// NewRESTAPI creates a new REST API component. It receives the multiaddress on
|
|
|
|
// which the API listens and a Config object.
|
|
|
|
func NewRESTAPI(cfg *Config) (*RESTAPI, error) {
|
|
|
|
n, addr, err := manet.DialArgs(cfg.ApiMAddr)
|
2016-12-05 15:24:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-08-29 01:41:54 +00:00
|
|
|
var l net.Listener
|
|
|
|
if cfg.TLS != nil {
|
|
|
|
l, err = tls.Listen(n, addr, cfg.TLS)
|
|
|
|
} else {
|
|
|
|
l, err = net.Listen(n, addr)
|
2017-07-19 16:54:57 +00:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-12-05 15:24:41 +00:00
|
|
|
|
2016-12-09 19:54:46 +00:00
|
|
|
router := mux.NewRouter().StrictSlash(true)
|
2016-12-22 10:31:09 +00:00
|
|
|
s := &http.Server{
|
|
|
|
ReadTimeout: RESTAPIServerReadTimeout,
|
|
|
|
WriteTimeout: RESTAPIServerWriteTimeout,
|
|
|
|
//IdleTimeout: RESTAPIServerIdleTimeout, // TODO: Go 1.8
|
|
|
|
Handler: router,
|
|
|
|
}
|
2016-12-09 19:54:46 +00:00
|
|
|
s.SetKeepAlivesEnabled(true) // A reminder that this can be changed
|
|
|
|
|
2017-03-02 12:57:37 +00:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
2016-12-16 18:14:45 +00:00
|
|
|
api := &RESTAPI{
|
2017-03-16 14:51:24 +00:00
|
|
|
ctx: ctx,
|
|
|
|
cancel: cancel,
|
2017-08-29 01:41:54 +00:00
|
|
|
apiAddr: cfg.ApiMAddr,
|
2017-03-16 14:51:24 +00:00
|
|
|
listener: l,
|
|
|
|
server: s,
|
|
|
|
rpcReady: make(chan struct{}, 1),
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
2017-08-29 01:41:54 +00:00
|
|
|
api.addRoutes(router, cfg.BasicAuthCreds)
|
|
|
|
api.run()
|
2016-12-02 18:33:39 +00:00
|
|
|
|
2017-08-29 01:41:54 +00:00
|
|
|
return api, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *RESTAPI) addRoutes(router *mux.Router, basicAuthCreds map[string]string) {
|
2016-12-02 18:33:39 +00:00
|
|
|
for _, route := range api.routes() {
|
2017-08-29 01:41:54 +00:00
|
|
|
if basicAuthCreds != nil {
|
|
|
|
route.HandlerFunc = basicAuth(route.HandlerFunc, basicAuthCreds)
|
|
|
|
}
|
2016-12-02 18:33:39 +00:00
|
|
|
router.
|
|
|
|
Methods(route.Method).
|
|
|
|
Path(route.Pattern).
|
|
|
|
Name(route.Name).
|
|
|
|
Handler(route.HandlerFunc)
|
|
|
|
}
|
|
|
|
api.router = router
|
2017-08-29 01:41:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func basicAuth(h http.HandlerFunc, credentials map[string]string) http.HandlerFunc {
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
|
|
|
|
username, password, ok := r.BasicAuth()
|
|
|
|
if !ok {
|
|
|
|
resp, err := unauthorizedResp()
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
http.Error(w, resp, 401)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
authorized := false
|
|
|
|
for u, p := range credentials {
|
|
|
|
if u == username && p == password {
|
|
|
|
authorized = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !authorized {
|
|
|
|
resp, err := unauthorizedResp()
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
http.Error(w, resp, 401)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
h.ServeHTTP(w, r)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func unauthorizedResp() (string, error) {
|
|
|
|
apiError := api.Error{
|
|
|
|
Code: 401,
|
|
|
|
Message: "Unauthorized",
|
|
|
|
}
|
|
|
|
resp, err := json.Marshal(apiError)
|
|
|
|
return string(resp), err
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) routes() []route {
|
2016-12-02 18:33:39 +00:00
|
|
|
return []route{
|
2017-01-24 15:19:23 +00:00
|
|
|
{
|
|
|
|
"ID",
|
|
|
|
"GET",
|
|
|
|
"/id",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.idHandler,
|
2017-01-24 15:19:23 +00:00
|
|
|
},
|
2017-01-25 18:38:23 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
"Version",
|
|
|
|
"GET",
|
|
|
|
"/version",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.versionHandler,
|
2017-01-25 18:38:23 +00:00
|
|
|
},
|
|
|
|
|
2017-01-24 11:39:08 +00:00
|
|
|
{
|
2017-01-26 18:59:31 +00:00
|
|
|
"Peers",
|
2016-12-02 18:33:39 +00:00
|
|
|
"GET",
|
2017-01-26 18:59:31 +00:00
|
|
|
"/peers",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.peerListHandler,
|
2016-12-02 18:33:39 +00:00
|
|
|
},
|
2017-01-30 12:12:25 +00:00
|
|
|
{
|
|
|
|
"PeerAdd",
|
|
|
|
"POST",
|
|
|
|
"/peers",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.peerAddHandler,
|
2017-01-30 12:12:25 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"PeerRemove",
|
|
|
|
"DELETE",
|
|
|
|
"/peers/{peer}",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.peerRemoveHandler,
|
2017-01-30 12:12:25 +00:00
|
|
|
},
|
2017-01-25 18:38:23 +00:00
|
|
|
|
2017-01-24 11:39:08 +00:00
|
|
|
{
|
2017-04-06 02:27:02 +00:00
|
|
|
"Allocations",
|
2016-12-02 18:33:39 +00:00
|
|
|
"GET",
|
2017-04-06 02:27:02 +00:00
|
|
|
"/allocations",
|
|
|
|
rest.allocationsHandler,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Allocation",
|
|
|
|
"GET",
|
|
|
|
"/allocations/{hash}",
|
|
|
|
rest.allocationHandler,
|
2016-12-02 18:33:39 +00:00
|
|
|
},
|
2017-01-24 11:39:08 +00:00
|
|
|
{
|
2017-01-25 18:38:23 +00:00
|
|
|
"StatusAll",
|
2016-12-02 18:33:39 +00:00
|
|
|
"GET",
|
2017-01-25 18:38:23 +00:00
|
|
|
"/pins",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.statusAllHandler,
|
2017-01-25 18:38:23 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"SyncAll",
|
|
|
|
"POST",
|
|
|
|
"/pins/sync",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.syncAllHandler,
|
2017-01-25 18:38:23 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"Status",
|
|
|
|
"GET",
|
|
|
|
"/pins/{hash}",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.statusHandler,
|
2016-12-02 18:33:39 +00:00
|
|
|
},
|
2017-01-24 11:39:08 +00:00
|
|
|
{
|
2016-12-02 18:33:39 +00:00
|
|
|
"Pin",
|
|
|
|
"POST",
|
|
|
|
"/pins/{hash}",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.pinHandler,
|
2016-12-02 18:33:39 +00:00
|
|
|
},
|
2017-01-24 11:39:08 +00:00
|
|
|
{
|
2016-12-02 18:33:39 +00:00
|
|
|
"Unpin",
|
|
|
|
"DELETE",
|
|
|
|
"/pins/{hash}",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.unpinHandler,
|
2016-12-02 18:33:39 +00:00
|
|
|
},
|
2017-01-24 11:39:08 +00:00
|
|
|
{
|
2016-12-15 18:08:46 +00:00
|
|
|
"Sync",
|
|
|
|
"POST",
|
2017-01-25 18:38:23 +00:00
|
|
|
"/pins/{hash}/sync",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.syncHandler,
|
2016-12-15 18:08:46 +00:00
|
|
|
},
|
2017-01-24 11:39:08 +00:00
|
|
|
{
|
2017-01-25 18:38:23 +00:00
|
|
|
"Recover",
|
2016-12-15 18:08:46 +00:00
|
|
|
"POST",
|
2017-01-25 18:38:23 +00:00
|
|
|
"/pins/{hash}/recover",
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.recoverHandler,
|
2016-12-15 18:08:46 +00:00
|
|
|
},
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) run() {
|
|
|
|
rest.wg.Add(1)
|
2016-12-02 18:33:39 +00:00
|
|
|
go func() {
|
2017-02-08 17:04:08 +00:00
|
|
|
defer rest.wg.Done()
|
|
|
|
<-rest.rpcReady
|
2016-12-23 18:35:37 +00:00
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
logger.Infof("REST API: %s", rest.apiAddr)
|
|
|
|
err := rest.server.Serve(rest.listener)
|
2016-12-15 13:07:19 +00:00
|
|
|
if err != nil && !strings.Contains(err.Error(), "closed network connection") {
|
|
|
|
logger.Error(err)
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shutdown stops any API listeners.
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) Shutdown() error {
|
|
|
|
rest.shutdownLock.Lock()
|
|
|
|
defer rest.shutdownLock.Unlock()
|
2016-12-15 13:07:19 +00:00
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
if rest.shutdown {
|
2016-12-15 13:07:19 +00:00
|
|
|
logger.Debug("already shutdown")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-12-15 13:19:41 +00:00
|
|
|
logger.Info("stopping Cluster API")
|
2016-12-15 13:07:19 +00:00
|
|
|
|
2017-03-02 12:57:37 +00:00
|
|
|
rest.cancel()
|
2017-02-08 17:04:08 +00:00
|
|
|
close(rest.rpcReady)
|
2016-12-15 13:07:19 +00:00
|
|
|
// Cancel any outstanding ops
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.server.SetKeepAlivesEnabled(false)
|
|
|
|
rest.listener.Close()
|
2016-12-15 13:07:19 +00:00
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
rest.wg.Wait()
|
|
|
|
rest.shutdown = true
|
2016-12-02 18:33:39 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-12-23 18:35:37 +00:00
|
|
|
// SetClient makes the component ready to perform RPC
|
|
|
|
// requests.
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) SetClient(c *rpc.Client) {
|
|
|
|
rest.rpcClient = c
|
|
|
|
rest.rpcReady <- struct{}{}
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) idHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
idSerial := api.IDSerial{}
|
|
|
|
err := rest.rpcClient.Call("",
|
2017-01-24 15:19:23 +00:00
|
|
|
"Cluster",
|
|
|
|
"ID",
|
|
|
|
struct{}{},
|
2017-01-26 18:59:31 +00:00
|
|
|
&idSerial)
|
2017-02-08 17:04:08 +00:00
|
|
|
|
|
|
|
sendResponse(w, err, idSerial)
|
2017-01-24 15:19:23 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) versionHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var v api.Version
|
|
|
|
err := rest.rpcClient.Call("",
|
2016-12-23 18:35:37 +00:00
|
|
|
"Cluster",
|
|
|
|
"Version",
|
|
|
|
struct{}{},
|
|
|
|
&v)
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
sendResponse(w, err, v)
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) peerListHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var peersSerial []api.IDSerial
|
|
|
|
err := rest.rpcClient.Call("",
|
2016-12-23 18:35:37 +00:00
|
|
|
"Cluster",
|
2017-01-26 18:59:31 +00:00
|
|
|
"Peers",
|
2016-12-23 18:35:37 +00:00
|
|
|
struct{}{},
|
2017-01-26 18:59:31 +00:00
|
|
|
&peersSerial)
|
2016-12-23 18:35:37 +00:00
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
sendResponse(w, err, peersSerial)
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) peerAddHandler(w http.ResponseWriter, r *http.Request) {
|
2017-01-30 12:12:25 +00:00
|
|
|
dec := json.NewDecoder(r.Body)
|
|
|
|
defer r.Body.Close()
|
|
|
|
|
|
|
|
var addInfo peerAddBody
|
|
|
|
err := dec.Decode(&addInfo)
|
|
|
|
if err != nil {
|
|
|
|
sendErrorResponse(w, 400, "error decoding request body")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
mAddr, err := ma.NewMultiaddr(addInfo.PeerMultiaddr)
|
|
|
|
if err != nil {
|
|
|
|
sendErrorResponse(w, 400, "error decoding peer_multiaddress")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
var ids api.IDSerial
|
|
|
|
err = rest.rpcClient.Call("",
|
2017-01-30 12:12:25 +00:00
|
|
|
"Cluster",
|
|
|
|
"PeerAdd",
|
2017-02-08 17:04:08 +00:00
|
|
|
api.MultiaddrToSerial(mAddr),
|
2017-01-30 12:12:25 +00:00
|
|
|
&ids)
|
2017-02-08 17:04:08 +00:00
|
|
|
sendResponse(w, err, ids)
|
2017-01-30 12:12:25 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) peerRemoveHandler(w http.ResponseWriter, r *http.Request) {
|
2017-01-30 12:12:25 +00:00
|
|
|
if p := parsePidOrError(w, r); p != "" {
|
2017-02-08 17:04:08 +00:00
|
|
|
err := rest.rpcClient.Call("",
|
2017-01-30 12:12:25 +00:00
|
|
|
"Cluster",
|
|
|
|
"PeerRemove",
|
|
|
|
p,
|
|
|
|
&struct{}{})
|
2017-02-08 17:04:08 +00:00
|
|
|
sendEmptyResponse(w, err)
|
2017-01-30 12:12:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) pinHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if c := parseCidOrError(w, r); c.Cid != "" {
|
|
|
|
err := rest.rpcClient.Call("",
|
2016-12-23 18:35:37 +00:00
|
|
|
"Cluster",
|
|
|
|
"Pin",
|
|
|
|
c,
|
|
|
|
&struct{}{})
|
2017-02-08 17:04:08 +00:00
|
|
|
sendAcceptedResponse(w, err)
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
2016-12-15 18:08:46 +00:00
|
|
|
}
|
2016-12-02 18:33:39 +00:00
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) unpinHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if c := parseCidOrError(w, r); c.Cid != "" {
|
|
|
|
err := rest.rpcClient.Call("",
|
2016-12-23 18:35:37 +00:00
|
|
|
"Cluster",
|
|
|
|
"Unpin",
|
|
|
|
c,
|
|
|
|
&struct{}{})
|
2017-02-08 17:04:08 +00:00
|
|
|
sendAcceptedResponse(w, err)
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-06 02:27:02 +00:00
|
|
|
func (rest *RESTAPI) allocationsHandler(w http.ResponseWriter, r *http.Request) {
|
2017-03-08 15:57:27 +00:00
|
|
|
var pins []api.PinSerial
|
2017-02-08 17:04:08 +00:00
|
|
|
err := rest.rpcClient.Call("",
|
2016-12-23 18:35:37 +00:00
|
|
|
"Cluster",
|
2017-04-06 02:27:02 +00:00
|
|
|
"Pins",
|
2016-12-23 18:35:37 +00:00
|
|
|
struct{}{},
|
|
|
|
&pins)
|
2017-02-08 17:04:08 +00:00
|
|
|
sendResponse(w, err, pins)
|
2016-12-15 18:08:46 +00:00
|
|
|
}
|
|
|
|
|
2017-04-06 02:27:02 +00:00
|
|
|
func (rest *RESTAPI) allocationHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if c := parseCidOrError(w, r); c.Cid != "" {
|
|
|
|
var pin api.PinSerial
|
|
|
|
err := rest.rpcClient.Call("",
|
|
|
|
"Cluster",
|
|
|
|
"PinGet",
|
|
|
|
c,
|
|
|
|
&pin)
|
|
|
|
if err != nil { // errors here are 404s
|
|
|
|
sendErrorResponse(w, 404, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
sendJSONResponse(w, 200, pin)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) statusAllHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var pinInfos []api.GlobalPinInfoSerial
|
|
|
|
err := rest.rpcClient.Call("",
|
2016-12-23 18:35:37 +00:00
|
|
|
"Cluster",
|
2017-01-25 18:38:23 +00:00
|
|
|
"StatusAll",
|
2016-12-23 18:35:37 +00:00
|
|
|
struct{}{},
|
|
|
|
&pinInfos)
|
2017-02-08 17:04:08 +00:00
|
|
|
sendResponse(w, err, pinInfos)
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) statusHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if c := parseCidOrError(w, r); c.Cid != "" {
|
|
|
|
var pinInfo api.GlobalPinInfoSerial
|
|
|
|
err := rest.rpcClient.Call("",
|
2016-12-23 18:35:37 +00:00
|
|
|
"Cluster",
|
2017-01-25 18:38:23 +00:00
|
|
|
"Status",
|
2016-12-23 18:35:37 +00:00
|
|
|
c,
|
|
|
|
&pinInfo)
|
2017-02-08 17:04:08 +00:00
|
|
|
sendResponse(w, err, pinInfo)
|
2016-12-15 18:08:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) syncAllHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
var pinInfos []api.GlobalPinInfoSerial
|
|
|
|
err := rest.rpcClient.Call("",
|
2016-12-23 18:35:37 +00:00
|
|
|
"Cluster",
|
2017-01-25 18:38:23 +00:00
|
|
|
"SyncAll",
|
2016-12-23 18:35:37 +00:00
|
|
|
struct{}{},
|
|
|
|
&pinInfos)
|
2017-02-08 17:04:08 +00:00
|
|
|
sendResponse(w, err, pinInfos)
|
2016-12-15 18:08:46 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) syncHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if c := parseCidOrError(w, r); c.Cid != "" {
|
|
|
|
var pinInfo api.GlobalPinInfoSerial
|
|
|
|
err := rest.rpcClient.Call("",
|
2017-01-25 18:38:23 +00:00
|
|
|
"Cluster",
|
|
|
|
"Sync",
|
|
|
|
c,
|
|
|
|
&pinInfo)
|
2017-02-08 17:04:08 +00:00
|
|
|
sendResponse(w, err, pinInfo)
|
2017-01-25 18:38:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func (rest *RESTAPI) recoverHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if c := parseCidOrError(w, r); c.Cid != "" {
|
|
|
|
var pinInfo api.GlobalPinInfoSerial
|
|
|
|
err := rest.rpcClient.Call("",
|
2016-12-23 18:35:37 +00:00
|
|
|
"Cluster",
|
2017-01-25 18:38:23 +00:00
|
|
|
"Recover",
|
2016-12-23 18:35:37 +00:00
|
|
|
c,
|
|
|
|
&pinInfo)
|
2017-02-08 17:04:08 +00:00
|
|
|
sendResponse(w, err, pinInfo)
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
2016-12-15 18:08:46 +00:00
|
|
|
}
|
2016-12-02 18:33:39 +00:00
|
|
|
|
2017-03-08 15:57:27 +00:00
|
|
|
func parseCidOrError(w http.ResponseWriter, r *http.Request) api.PinSerial {
|
2016-12-15 18:08:46 +00:00
|
|
|
vars := mux.Vars(r)
|
|
|
|
hash := vars["hash"]
|
2017-03-08 17:28:43 +00:00
|
|
|
|
2016-12-23 18:35:37 +00:00
|
|
|
_, err := cid.Decode(hash)
|
2016-12-15 18:08:46 +00:00
|
|
|
if err != nil {
|
|
|
|
sendErrorResponse(w, 400, "error decoding Cid: "+err.Error())
|
2017-03-08 15:57:27 +00:00
|
|
|
return api.PinSerial{Cid: ""}
|
2016-12-15 18:08:46 +00:00
|
|
|
}
|
2017-03-08 17:28:43 +00:00
|
|
|
|
|
|
|
pin := api.PinSerial{
|
|
|
|
Cid: hash,
|
|
|
|
}
|
|
|
|
|
|
|
|
queryValues := r.URL.Query()
|
|
|
|
rplStr := queryValues.Get("replication_factor")
|
|
|
|
if rpl, err := strconv.Atoi(rplStr); err == nil {
|
|
|
|
pin.ReplicationFactor = rpl
|
|
|
|
}
|
|
|
|
|
|
|
|
return pin
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
2017-01-30 12:12:25 +00:00
|
|
|
func parsePidOrError(w http.ResponseWriter, r *http.Request) peer.ID {
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
idStr := vars["peer"]
|
|
|
|
pid, err := peer.IDB58Decode(idStr)
|
|
|
|
if err != nil {
|
|
|
|
sendErrorResponse(w, 400, "error decoding Peer ID: "+err.Error())
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return pid
|
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func sendResponse(w http.ResponseWriter, rpcErr error, resp interface{}) {
|
|
|
|
if checkRPCErr(w, rpcErr) {
|
|
|
|
sendJSONResponse(w, 200, resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-23 18:35:37 +00:00
|
|
|
// checkRPCErr takes care of returning standard error responses if we
|
|
|
|
// pass an error to it. It returns true when everythings OK (no error
|
|
|
|
// was handled), or false otherwise.
|
2017-01-24 15:19:23 +00:00
|
|
|
func checkRPCErr(w http.ResponseWriter, err error) bool {
|
2016-12-23 18:35:37 +00:00
|
|
|
if err != nil {
|
2016-12-02 18:33:39 +00:00
|
|
|
sendErrorResponse(w, 500, err.Error())
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func sendEmptyResponse(w http.ResponseWriter, rpcErr error) {
|
|
|
|
if checkRPCErr(w, rpcErr) {
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
}
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 17:04:08 +00:00
|
|
|
func sendAcceptedResponse(w http.ResponseWriter, rpcErr error) {
|
|
|
|
if checkRPCErr(w, rpcErr) {
|
|
|
|
w.WriteHeader(http.StatusAccepted)
|
|
|
|
}
|
2016-12-07 16:28:46 +00:00
|
|
|
}
|
|
|
|
|
2016-12-02 18:33:39 +00:00
|
|
|
func sendJSONResponse(w http.ResponseWriter, code int, resp interface{}) {
|
2017-04-06 02:27:02 +00:00
|
|
|
w.Header().Add("Content-Type", "application/json")
|
2016-12-02 18:33:39 +00:00
|
|
|
w.WriteHeader(code)
|
|
|
|
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func sendErrorResponse(w http.ResponseWriter, code int, msg string) {
|
2017-03-28 15:48:26 +00:00
|
|
|
errorResp := api.Error{
|
|
|
|
Code: code,
|
|
|
|
Message: msg,
|
|
|
|
}
|
2016-12-15 13:19:41 +00:00
|
|
|
logger.Errorf("sending error response: %d: %s", code, msg)
|
2016-12-02 18:33:39 +00:00
|
|
|
sendJSONResponse(w, code, errorResp)
|
|
|
|
}
|