mirror of
https://github.com/datarhei/core.git
synced 2025-10-04 23:53:12 +08:00
Add /v3/cluster/snapshot endpoint
This commit is contained in:
@@ -242,7 +242,13 @@ func (a *api) RemoveServer(c echo.Context) error {
|
|||||||
// @Success 500 {array} Error
|
// @Success 500 {array} Error
|
||||||
// @Router /v1/snapshot [get]
|
// @Router /v1/snapshot [get]
|
||||||
func (a *api) Snapshot(c echo.Context) error {
|
func (a *api) Snapshot(c echo.Context) error {
|
||||||
data, err := a.cluster.Snapshot()
|
origin := c.Request().Header.Get("X-Cluster-Origin")
|
||||||
|
|
||||||
|
if origin == a.id {
|
||||||
|
return Err(http.StatusLoopDetected, "", "breaking circuit")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := a.cluster.Snapshot(origin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.logger.Debug().WithError(err).Log("Unable to create snaphot")
|
a.logger.Debug().WithError(err).Log("Unable to create snaphot")
|
||||||
return Err(http.StatusInternalServerError, "", "unable to create snapshot: %s", err.Error())
|
return Err(http.StatusInternalServerError, "", "unable to create snapshot: %s", err.Error())
|
||||||
|
@@ -249,8 +249,8 @@ func (c *APIClient) UnsetKV(origin string, key string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *APIClient) Snapshot() (io.ReadCloser, error) {
|
func (c *APIClient) Snapshot(origin string) (io.ReadCloser, error) {
|
||||||
return c.stream(http.MethodGet, "/v1/snapshot", "", nil, "")
|
return c.stream(http.MethodGet, "/v1/snapshot", "", nil, origin)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *APIClient) IsReady(origin string) error {
|
func (c *APIClient) IsReady(origin string) error {
|
||||||
|
@@ -48,7 +48,7 @@ type Cluster interface {
|
|||||||
|
|
||||||
Join(origin, id, raftAddress, peerAddress string) error
|
Join(origin, id, raftAddress, peerAddress string) error
|
||||||
Leave(origin, id string) error // gracefully remove a node from the cluster
|
Leave(origin, id string) error // gracefully remove a node from the cluster
|
||||||
Snapshot() (io.ReadCloser, error)
|
Snapshot(origin string) (io.ReadCloser, error)
|
||||||
|
|
||||||
Shutdown() error
|
Shutdown() error
|
||||||
|
|
||||||
@@ -827,10 +827,10 @@ func (c *cluster) Join(origin, id, raftAddress, peerAddress string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cluster) Snapshot() (io.ReadCloser, error) {
|
func (c *cluster) Snapshot(origin string) (io.ReadCloser, error) {
|
||||||
if !c.IsRaftLeader() {
|
if !c.IsRaftLeader() {
|
||||||
c.logger.Debug().Log("Not leader, forwarding to leader")
|
c.logger.Debug().Log("Not leader, forwarding to leader")
|
||||||
return c.forwarder.Snapshot()
|
return c.forwarder.Snapshot(origin)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.raft.Snapshot()
|
return c.raft.Snapshot()
|
||||||
|
@@ -20,7 +20,7 @@ type Forwarder interface {
|
|||||||
|
|
||||||
Join(origin, id, raftAddress, peerAddress string) error
|
Join(origin, id, raftAddress, peerAddress string) error
|
||||||
Leave(origin, id string) error
|
Leave(origin, id string) error
|
||||||
Snapshot() (io.ReadCloser, error)
|
Snapshot(origin string) (io.ReadCloser, error)
|
||||||
|
|
||||||
AddProcess(origin string, config *app.Config) error
|
AddProcess(origin string, config *app.Config) error
|
||||||
UpdateProcess(origin string, id app.ProcessID, config *app.Config) error
|
UpdateProcess(origin string, id app.ProcessID, config *app.Config) error
|
||||||
@@ -140,12 +140,12 @@ func (f *forwarder) Leave(origin, id string) error {
|
|||||||
return client.Leave(origin, id)
|
return client.Leave(origin, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *forwarder) Snapshot() (io.ReadCloser, error) {
|
func (f *forwarder) Snapshot(origin string) (io.ReadCloser, error) {
|
||||||
f.lock.RLock()
|
f.lock.RLock()
|
||||||
client := f.client
|
client := f.client
|
||||||
f.lock.RUnlock()
|
f.lock.RUnlock()
|
||||||
|
|
||||||
return client.Snapshot()
|
return client.Snapshot(origin)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *forwarder) AddProcess(origin string, config *app.Config) error {
|
func (f *forwarder) AddProcess(origin string, config *app.Config) error {
|
||||||
|
@@ -2,7 +2,8 @@ package raft
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/gob"
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
gonet "net"
|
gonet "net"
|
||||||
@@ -287,7 +288,7 @@ func (rcw *readCloserWrapper) Close() error {
|
|||||||
|
|
||||||
type Snapshot struct {
|
type Snapshot struct {
|
||||||
Metadata *hcraft.SnapshotMeta
|
Metadata *hcraft.SnapshotMeta
|
||||||
Data []byte
|
Data string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *raft) Snapshot() (io.ReadCloser, error) {
|
func (r *raft) Snapshot() (io.ReadCloser, error) {
|
||||||
@@ -311,11 +312,11 @@ func (r *raft) Snapshot() (io.ReadCloser, error) {
|
|||||||
|
|
||||||
snapshot := Snapshot{
|
snapshot := Snapshot{
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
Data: data,
|
Data: base64.StdEncoding.EncodeToString(data),
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer := bytes.Buffer{}
|
buffer := bytes.Buffer{}
|
||||||
enc := gob.NewEncoder(&buffer)
|
enc := json.NewEncoder(&buffer)
|
||||||
err = enc.Encode(snapshot)
|
err = enc.Encode(snapshot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
26
docs/docs.go
26
docs/docs.go
@@ -1441,6 +1441,32 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v3/cluster/snapshot": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Retrieve snapshot of the cluster DB",
|
||||||
|
"produces": [
|
||||||
|
"application/octet-stream"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.?.?"
|
||||||
|
],
|
||||||
|
"summary": "Retrieve snapshot of the cluster DB",
|
||||||
|
"operationId": "cluster-3-snapshot",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "file"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v3/config": {
|
"/api/v3/config": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@@ -1433,6 +1433,32 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v3/cluster/snapshot": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Retrieve snapshot of the cluster DB",
|
||||||
|
"produces": [
|
||||||
|
"application/octet-stream"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"v16.?.?"
|
||||||
|
],
|
||||||
|
"summary": "Retrieve snapshot of the cluster DB",
|
||||||
|
"operationId": "cluster-3-snapshot",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"type": "file"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v3/config": {
|
"/api/v3/config": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@@ -3323,6 +3323,22 @@ paths:
|
|||||||
summary: Add JSON metadata with a process under the given key
|
summary: Add JSON metadata with a process under the given key
|
||||||
tags:
|
tags:
|
||||||
- v16.?.?
|
- v16.?.?
|
||||||
|
/api/v3/cluster/snapshot:
|
||||||
|
get:
|
||||||
|
description: Retrieve snapshot of the cluster DB
|
||||||
|
operationId: cluster-3-snapshot
|
||||||
|
produces:
|
||||||
|
- application/octet-stream
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
type: file
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Retrieve snapshot of the cluster DB
|
||||||
|
tags:
|
||||||
|
- v16.?.?
|
||||||
/api/v3/config:
|
/api/v3/config:
|
||||||
get:
|
get:
|
||||||
description: Retrieve the currently active Restreamer configuration
|
description: Retrieve the currently active Restreamer configuration
|
||||||
|
@@ -1291,3 +1291,23 @@ func (h *ClusterHandler) ListStoreKV(c echo.Context) error {
|
|||||||
|
|
||||||
return c.JSON(http.StatusOK, kvs)
|
return c.JSON(http.StatusOK, kvs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSnapshot returns a current snapshot of the cluster DB
|
||||||
|
// @Summary Retrieve snapshot of the cluster DB
|
||||||
|
// @Description Retrieve snapshot of the cluster DB
|
||||||
|
// @Tags v16.?.?
|
||||||
|
// @ID cluster-3-snapshot
|
||||||
|
// @Produce application/octet-stream
|
||||||
|
// @Success 200 {file} byte
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /api/v3/cluster/snapshot [get]
|
||||||
|
func (h *ClusterHandler) GetSnapshot(c echo.Context) error {
|
||||||
|
r, err := h.cluster.Snapshot("")
|
||||||
|
if err != nil {
|
||||||
|
return api.Err(http.StatusInternalServerError, "", "failed to retrieve snapshot: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
return c.Stream(http.StatusOK, "application/octet-stream", r)
|
||||||
|
}
|
||||||
|
@@ -675,6 +675,8 @@ func (s *server) setRoutesV3(v3 *echo.Group) {
|
|||||||
if s.v3handler.cluster != nil {
|
if s.v3handler.cluster != nil {
|
||||||
v3.GET("/cluster", s.v3handler.cluster.About)
|
v3.GET("/cluster", s.v3handler.cluster.About)
|
||||||
|
|
||||||
|
v3.GET("/cluster/snapshot", s.v3handler.cluster.GetSnapshot)
|
||||||
|
|
||||||
v3.GET("/cluster/db/process", s.v3handler.cluster.ListStoreProcesses)
|
v3.GET("/cluster/db/process", s.v3handler.cluster.ListStoreProcesses)
|
||||||
v3.GET("/cluster/db/user", s.v3handler.cluster.ListStoreIdentities)
|
v3.GET("/cluster/db/user", s.v3handler.cluster.ListStoreIdentities)
|
||||||
v3.GET("/cluster/db/user/:name", s.v3handler.cluster.ListStoreIdentity)
|
v3.GET("/cluster/db/user/:name", s.v3handler.cluster.ListStoreIdentity)
|
||||||
|
Reference in New Issue
Block a user