8f06baa1bf
The following commit reimplements ipfs-cluster configuration under the following premises: * Each component is initialized with a configuration object defined by its module * Each component decides how the JSON representation of its configuration looks like * Each component parses and validates its own configuration * Each component exposes its own defaults * Component configurations are make the sections of a central JSON configuration file (which replaces the current JSON format) * Component configurations implement a common interface (config.ComponentConfig) with a set of common operations * The central configuration file is managed by a config.ConfigManager which: * Registers ComponentConfigs * Assigns the correspondent sections from the JSON file to each component and delegates the parsing * Delegates the JSON generation for each section * Can be notified when the configuration is updated and must be saved to disk The new service.json would then look as follows: ```json { "cluster": { "id": "QmTVW8NoRxC5wBhV7WtAYtRn7itipEESfozWN5KmXUQnk2", "private_key": "<...>", "secret": "00224102ae6aaf94f2606abf69a0e278251ecc1d64815b617ff19d6d2841f786", "peers": [], "bootstrap": [], "leave_on_shutdown": false, "listen_multiaddress": "/ip4/0.0.0.0/tcp/9096", "state_sync_interval": "1m0s", "ipfs_sync_interval": "2m10s", "replication_factor": -1, "monitor_ping_interval": "15s" }, "consensus": { "raft": { "heartbeat_timeout": "1s", "election_timeout": "1s", "commit_timeout": "50ms", "max_append_entries": 64, "trailing_logs": 10240, "snapshot_interval": "2m0s", "snapshot_threshold": 8192, "leader_lease_timeout": "500ms" } }, "api": { "restapi": { "listen_multiaddress": "/ip4/127.0.0.1/tcp/9094", "read_timeout": "30s", "read_header_timeout": "5s", "write_timeout": "1m0s", "idle_timeout": "2m0s" } }, "ipfs_connector": { "ipfshttp": { "proxy_listen_multiaddress": "/ip4/127.0.0.1/tcp/9095", "node_multiaddress": "/ip4/127.0.0.1/tcp/5001", "connect_swarms_delay": "7s", "proxy_read_timeout": "10m0s", "proxy_read_header_timeout": "5s", "proxy_write_timeout": "10m0s", "proxy_idle_timeout": "1m0s" } }, "monitor": { "monbasic": { "check_interval": "15s" } }, "informer": { "disk": { "metric_ttl": "30s", "metric_type": "freespace" }, "numpin": { "metric_ttl": "10s" } } } ``` This new format aims to be easily extensible per component. As such, it already surfaces quite a few new options which were hardcoded before. Additionally, since Go API have changed, some redundant methods have been removed and small refactoring has happened to take advantage of the new way. License: MIT Signed-off-by: Hector Sanjuan <hector@protocol.ai>
309 lines
7.1 KiB
Go
309 lines
7.1 KiB
Go
package rest
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/ipfs/ipfs-cluster/api"
|
|
"github.com/ipfs/ipfs-cluster/test"
|
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
|
)
|
|
|
|
var (
|
|
apiHost = "http://127.0.0.1:10002" // should match testingConfig()
|
|
)
|
|
|
|
func testAPI(t *testing.T) *API {
|
|
//logging.SetDebugLogging()
|
|
apiMAddr, _ := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/10002")
|
|
|
|
cfg := &Config{}
|
|
cfg.Default()
|
|
cfg.ListenAddr = apiMAddr
|
|
|
|
rest, err := NewAPI(cfg)
|
|
if err != nil {
|
|
t.Fatal("should be able to create a new Api: ", err)
|
|
}
|
|
|
|
// No keep alive! Otherwise tests hang with
|
|
// connections re-used from previous tests
|
|
rest.server.SetKeepAlivesEnabled(false)
|
|
rest.SetClient(test.NewMockRPCClient(t))
|
|
return rest
|
|
}
|
|
|
|
func processResp(t *testing.T, httpResp *http.Response, err error, resp interface{}) {
|
|
if err != nil {
|
|
t.Fatal("error making get request: ", err)
|
|
}
|
|
body, err := ioutil.ReadAll(httpResp.Body)
|
|
defer httpResp.Body.Close()
|
|
if err != nil {
|
|
t.Fatal("error reading body: ", err)
|
|
}
|
|
|
|
if len(body) != 0 {
|
|
err = json.Unmarshal(body, resp)
|
|
if err != nil {
|
|
t.Error(string(body))
|
|
t.Fatal("error parsing json: ", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func makeGet(t *testing.T, path string, resp interface{}) {
|
|
httpResp, err := http.Get(apiHost + path)
|
|
processResp(t, httpResp, err, resp)
|
|
}
|
|
|
|
func makePost(t *testing.T, path string, body []byte, resp interface{}) {
|
|
httpResp, err := http.Post(apiHost+path, "application/json", bytes.NewReader(body))
|
|
processResp(t, httpResp, err, resp)
|
|
}
|
|
|
|
func makeDelete(t *testing.T, path string, resp interface{}) {
|
|
req, _ := http.NewRequest("DELETE", apiHost+path, bytes.NewReader([]byte{}))
|
|
c := &http.Client{}
|
|
httpResp, err := c.Do(req)
|
|
processResp(t, httpResp, err, resp)
|
|
}
|
|
|
|
func TestAPIShutdown(t *testing.T) {
|
|
rest := testAPI(t)
|
|
err := rest.Shutdown()
|
|
if err != nil {
|
|
t.Error("should shutdown cleanly: ", err)
|
|
}
|
|
// test shutting down twice
|
|
rest.Shutdown()
|
|
}
|
|
|
|
func TestRestAPIIDEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
id := api.IDSerial{}
|
|
makeGet(t, "/id", &id)
|
|
if id.ID != test.TestPeerID1.Pretty() {
|
|
t.Error("expected correct id")
|
|
}
|
|
}
|
|
|
|
func TestAPIVersionEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
ver := api.Version{}
|
|
makeGet(t, "/version", &ver)
|
|
if ver.Version != "0.0.mock" {
|
|
t.Error("expected correct version")
|
|
}
|
|
}
|
|
|
|
func TestAPIPeerstEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
var list []api.IDSerial
|
|
makeGet(t, "/peers", &list)
|
|
if len(list) != 1 {
|
|
t.Fatal("expected 1 element")
|
|
}
|
|
if list[0].ID != test.TestPeerID1.Pretty() {
|
|
t.Error("expected a different peer id list: ", list)
|
|
}
|
|
}
|
|
|
|
func TestAPIPeerAddEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
id := api.IDSerial{}
|
|
// post with valid body
|
|
body := fmt.Sprintf("{\"peer_multiaddress\":\"/ip4/1.2.3.4/tcp/1234/ipfs/%s\"}", test.TestPeerID1.Pretty())
|
|
t.Log(body)
|
|
makePost(t, "/peers", []byte(body), &id)
|
|
|
|
if id.ID != test.TestPeerID1.Pretty() {
|
|
t.Error("expected correct ID")
|
|
}
|
|
if id.Error != "" {
|
|
t.Error("did not expect an error")
|
|
}
|
|
|
|
// Send invalid body
|
|
errResp := api.Error{}
|
|
makePost(t, "/peers", []byte("oeoeoeoe"), &errResp)
|
|
if errResp.Code != 400 {
|
|
t.Error("expected error with bad body")
|
|
}
|
|
// Send invalid multiaddr
|
|
makePost(t, "/peers", []byte("{\"peer_multiaddress\": \"ab\"}"), &errResp)
|
|
if errResp.Code != 400 {
|
|
t.Error("expected error with bad multiaddress")
|
|
}
|
|
}
|
|
|
|
func TestAPIPeerRemoveEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
makeDelete(t, "/peers/"+test.TestPeerID1.Pretty(), &struct{}{})
|
|
}
|
|
|
|
func TestAPIPinEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
// test regular post
|
|
makePost(t, "/pins/"+test.TestCid1, []byte{}, &struct{}{})
|
|
|
|
errResp := api.Error{}
|
|
makePost(t, "/pins/"+test.ErrorCid, []byte{}, &errResp)
|
|
if errResp.Message != test.ErrBadCid.Error() {
|
|
t.Error("expected different error: ", errResp.Message)
|
|
}
|
|
|
|
makePost(t, "/pins/abcd", []byte{}, &errResp)
|
|
if errResp.Code != 400 {
|
|
t.Error("should fail with bad Cid")
|
|
}
|
|
}
|
|
|
|
func TestAPIUnpinEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
// test regular delete
|
|
makeDelete(t, "/pins/"+test.TestCid1, &struct{}{})
|
|
|
|
errResp := api.Error{}
|
|
makeDelete(t, "/pins/"+test.ErrorCid, &errResp)
|
|
if errResp.Message != test.ErrBadCid.Error() {
|
|
t.Error("expected different error: ", errResp.Message)
|
|
}
|
|
|
|
makeDelete(t, "/pins/abcd", &errResp)
|
|
if errResp.Code != 400 {
|
|
t.Error("should fail with bad Cid")
|
|
}
|
|
}
|
|
|
|
func TestAPIAllocationsEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
var resp []api.PinSerial
|
|
makeGet(t, "/allocations", &resp)
|
|
if len(resp) != 3 ||
|
|
resp[0].Cid != test.TestCid1 || resp[1].Cid != test.TestCid2 ||
|
|
resp[2].Cid != test.TestCid3 {
|
|
t.Error("unexpected pin list: ", resp)
|
|
}
|
|
}
|
|
|
|
func TestAPIAllocationEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
var resp api.PinSerial
|
|
makeGet(t, "/allocations/"+test.TestCid1, &resp)
|
|
if resp.Cid != test.TestCid1 {
|
|
t.Error("cid should be the same")
|
|
}
|
|
|
|
errResp := api.Error{}
|
|
makeGet(t, "/allocations/"+test.ErrorCid, &errResp)
|
|
if errResp.Code != 404 {
|
|
t.Error("a non-pinned cid should 404")
|
|
}
|
|
}
|
|
|
|
func TestAPIStatusAllEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
var resp []api.GlobalPinInfoSerial
|
|
makeGet(t, "/pins", &resp)
|
|
if len(resp) != 3 ||
|
|
resp[0].Cid != test.TestCid1 ||
|
|
resp[1].PeerMap[test.TestPeerID1.Pretty()].Status != "pinning" {
|
|
t.Errorf("unexpected statusResp:\n %+v", resp)
|
|
}
|
|
}
|
|
|
|
func TestAPIStatusEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
var resp api.GlobalPinInfoSerial
|
|
makeGet(t, "/pins/"+test.TestCid1, &resp)
|
|
|
|
if resp.Cid != test.TestCid1 {
|
|
t.Error("expected the same cid")
|
|
}
|
|
info, ok := resp.PeerMap[test.TestPeerID1.Pretty()]
|
|
if !ok {
|
|
t.Fatal("expected info for test.TestPeerID1")
|
|
}
|
|
if info.Status != "pinned" {
|
|
t.Error("expected different status")
|
|
}
|
|
}
|
|
|
|
func TestAPISyncAllEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
var resp []api.GlobalPinInfoSerial
|
|
makePost(t, "/pins/sync", []byte{}, &resp)
|
|
|
|
if len(resp) != 3 ||
|
|
resp[0].Cid != test.TestCid1 ||
|
|
resp[1].PeerMap[test.TestPeerID1.Pretty()].Status != "pinning" {
|
|
t.Errorf("unexpected statusResp:\n %+v", resp)
|
|
}
|
|
}
|
|
|
|
func TestAPISyncEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
var resp api.GlobalPinInfoSerial
|
|
makePost(t, "/pins/"+test.TestCid1+"/sync", []byte{}, &resp)
|
|
|
|
if resp.Cid != test.TestCid1 {
|
|
t.Error("expected the same cid")
|
|
}
|
|
info, ok := resp.PeerMap[test.TestPeerID1.Pretty()]
|
|
if !ok {
|
|
t.Fatal("expected info for test.TestPeerID1")
|
|
}
|
|
if info.Status != "pinned" {
|
|
t.Error("expected different status")
|
|
}
|
|
}
|
|
|
|
func TestAPIRecoverEndpoint(t *testing.T) {
|
|
rest := testAPI(t)
|
|
defer rest.Shutdown()
|
|
|
|
var resp api.GlobalPinInfoSerial
|
|
makePost(t, "/pins/"+test.TestCid1+"/recover", []byte{}, &resp)
|
|
|
|
if resp.Cid != test.TestCid1 {
|
|
t.Error("expected the same cid")
|
|
}
|
|
info, ok := resp.PeerMap[test.TestPeerID1.Pretty()]
|
|
if !ok {
|
|
t.Fatal("expected info for test.TestPeerID1")
|
|
}
|
|
if info.Status != "pinned" {
|
|
t.Error("expected different status")
|
|
}
|
|
}
|