Allow cluster leave endpoint to remove any node in the cluster

This commit is contained in:
Ingo Oppermann
2024-06-07 11:28:54 +02:00
parent 0f344f1998
commit 1a64fddbb1
8 changed files with 117 additions and 10 deletions

View File

@@ -1719,15 +1719,35 @@ func (a *api) start(ctx context.Context) error {
// Start the cluster // Start the cluster
if a.cluster != nil { if a.cluster != nil {
ctx, cancel := context.WithTimeout(ctx, time.Duration(cfg.Cluster.StartupTimeout)*time.Second) wgStart.Add(1)
defer cancel() a.wgStop.Add(1)
err := a.cluster.Start(ctx) go func() {
if err != nil { logger := a.log.logger.core
return fmt.Errorf("failed to start cluster: %w", err)
} var err error
defer func() {
logger.Info().Log("Cluster exited")
a.wgStop.Done()
}()
ctx, cancel := context.WithTimeout(ctx, time.Duration(cfg.Cluster.StartupTimeout)*time.Second)
defer cancel()
wgStart.Done()
err = a.cluster.Start(ctx)
if err != nil {
err = fmt.Errorf("cluster failed: %w", err)
}
sendError(err)
}()
} }
wgStart.Wait()
// Start the service // Start the service
if a.service != nil { if a.service != nil {
a.service.Start() a.service.Start()

View File

@@ -432,6 +432,8 @@ func (c *cluster) Start(ctx context.Context) error {
return fmt.Errorf("failed to setup cluster: %w", err) return fmt.Errorf("failed to setup cluster: %w", err)
} }
<-c.shutdownCh
return nil return nil
} }
@@ -1014,6 +1016,13 @@ func (c *cluster) trackNodeChanges() {
} }
delete(c.nodes, id) delete(c.nodes, id)
/*
if id == c.nodeID {
c.logger.Warn().WithField("id", id).Log("This node left the cluster. Shutting down.")
// We got removed from the cluster, shutdown
c.Shutdown()
}
*/
} }
c.nodesLock.Unlock() c.nodesLock.Unlock()
@@ -1307,6 +1316,27 @@ func (c *cluster) trackLeaderChanges() {
c.hasRaftLeader = true c.hasRaftLeader = true
} }
c.leaderLock.Unlock() c.leaderLock.Unlock()
servers, err := c.raft.Servers()
if err != nil {
c.logger.Error().WithError(err).Log("Raft configuration")
break
}
isNodeInCluster := false
for _, server := range servers {
if c.nodeID == server.ID {
isNodeInCluster = true
break
}
}
if !isNodeInCluster {
// We're not anymore part of the cluster, shutdown
c.logger.Warn().WithField("id", c.nodeID).Log("This node left the cluster. Shutting down.")
c.Shutdown()
}
case <-c.shutdownCh: case <-c.shutdownCh:
return return
} }

View File

@@ -860,6 +860,17 @@ const docTemplate = `{
], ],
"summary": "Leave the cluster gracefully", "summary": "Leave the cluster gracefully",
"operationId": "cluster-3-leave", "operationId": "cluster-3-leave",
"parameters": [
{
"description": "Node ID",
"name": "nodeid",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.ClusterNodeID"
}
}
],
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",
@@ -5043,6 +5054,14 @@ const docTemplate = `{
} }
} }
}, },
"api.ClusterNodeID": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"api.ClusterNodeResources": { "api.ClusterNodeResources": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -852,6 +852,17 @@
], ],
"summary": "Leave the cluster gracefully", "summary": "Leave the cluster gracefully",
"operationId": "cluster-3-leave", "operationId": "cluster-3-leave",
"parameters": [
{
"description": "Node ID",
"name": "nodeid",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.ClusterNodeID"
}
}
],
"responses": { "responses": {
"200": { "200": {
"description": "OK", "description": "OK",
@@ -5035,6 +5046,14 @@
} }
} }
}, },
"api.ClusterNodeID": {
"type": "object",
"properties": {
"id": {
"type": "string"
}
}
},
"api.ClusterNodeResources": { "api.ClusterNodeResources": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@@ -181,6 +181,11 @@ definitions:
description: unix timestamp description: unix timestamp
type: integer type: integer
type: object type: object
api.ClusterNodeID:
properties:
id:
type: string
type: object
api.ClusterNodeResources: api.ClusterNodeResources:
properties: properties:
cpu_limit: cpu_limit:
@@ -3093,6 +3098,13 @@ paths:
put: put:
description: Leave the cluster gracefully description: Leave the cluster gracefully
operationId: cluster-3-leave operationId: cluster-3-leave
parameters:
- description: Node ID
in: body
name: nodeid
required: true
schema:
$ref: '#/definitions/api.ClusterNodeID'
produces: produces:
- application/json - application/json
responses: responses:

View File

@@ -4,6 +4,10 @@ import (
"time" "time"
) )
type ClusterNodeID struct {
ID string `json:"id"`
}
type ClusterNode struct { type ClusterNode struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`

View File

@@ -175,13 +175,13 @@ func (h *ClusterHandler) TransferLeadership(c echo.Context) error {
// @Tags v16.?.? // @Tags v16.?.?
// @ID cluster-3-leave // @ID cluster-3-leave
// @Produce json // @Produce json
// @Param nodeid body string true "Node ID" // @Param nodeid body api.ClusterNodeID true "Node ID"
// @Success 200 {string} string // @Success 200 {string} string
// @Failure 500 {object} api.Error // @Failure 500 {object} api.Error
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /api/v3/cluster/leave [put] // @Router /api/v3/cluster/leave [put]
func (h *ClusterHandler) Leave(c echo.Context) error { func (h *ClusterHandler) Leave(c echo.Context) error {
nodeid := "" nodeid := api.ClusterNodeID{}
req := c.Request() req := c.Request()
@@ -196,8 +196,7 @@ func (h *ClusterHandler) Leave(c echo.Context) error {
} }
} }
h.cluster.Leave("", nodeid) h.cluster.Leave("", nodeid.ID)
//h.cluster.Shutdown()
return c.JSON(http.StatusOK, "OK") return c.JSON(http.StatusOK, "OK")
} }

View File

@@ -319,6 +319,10 @@ func (e *Event) WithFields(f Fields) Logger {
} }
func (e *Event) WithError(err error) Logger { func (e *Event) WithError(err error) Logger {
if err == nil {
return e
}
return e.WithFields(Fields{ return e.WithFields(Fields{
"error": err, "error": err,
}) })