mirror of
https://github.com/datarhei/core.git
synced 2025-10-05 16:07:07 +08:00
Refactor cluster.About() data
This commit is contained in:
2
Makefile
2
Makefile
@@ -90,7 +90,7 @@ coverage:
|
|||||||
go tool cover -html=test/cover.out -o test/cover.html
|
go tool cover -html=test/cover.out -o test/cover.html
|
||||||
|
|
||||||
## commit: Prepare code for commit (vet, fmt, test)
|
## commit: Prepare code for commit (vet, fmt, test)
|
||||||
commit: vet fmt lint test build
|
commit: vet fmt lint test vulncheck build
|
||||||
@echo "No errors found. Ready for a commit."
|
@echo "No errors found. Ready for a commit."
|
||||||
|
|
||||||
## release: Build a release binary of core
|
## release: Build a release binary of core
|
||||||
|
@@ -1301,34 +1301,59 @@ func (c *cluster) applyCommand(cmd *store.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterRaftServer struct {
|
type ClusterRaft struct {
|
||||||
ID string
|
Address string
|
||||||
Address string
|
|
||||||
Voter bool
|
|
||||||
Leader bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClusterRaftStats struct {
|
|
||||||
State string
|
State string
|
||||||
LastContact time.Duration
|
LastContact time.Duration
|
||||||
NumPeers uint64
|
NumPeers uint64
|
||||||
|
LogTerm uint64
|
||||||
|
LogIndex uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterRaft struct {
|
type ClusterNodeResources struct {
|
||||||
Server []ClusterRaftServer
|
IsThrottling bool // Whether this core is currently throttling
|
||||||
Stats ClusterRaftStats
|
NCPU float64 // Number of CPU on this node
|
||||||
|
CPU float64 // Current CPU load, 0-100*ncpu
|
||||||
|
CPULimit float64 // Defined CPU load limit, 0-100*ncpu
|
||||||
|
Mem uint64 // Currently used memory in bytes
|
||||||
|
MemLimit uint64 // Defined memory limit in bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClusterNode struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Version string
|
||||||
|
Status string
|
||||||
|
Error error
|
||||||
|
Voter bool
|
||||||
|
Leader bool
|
||||||
|
Address string
|
||||||
|
CreatedAt time.Time
|
||||||
|
Uptime time.Duration
|
||||||
|
LastContact time.Duration
|
||||||
|
Latency time.Duration
|
||||||
|
Core ClusterNodeCore
|
||||||
|
Resources ClusterNodeResources
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClusterNodeCore struct {
|
||||||
|
Address string
|
||||||
|
Status string
|
||||||
|
Error error
|
||||||
|
LastContact time.Duration
|
||||||
|
Latency time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterAbout struct {
|
type ClusterAbout struct {
|
||||||
ID string
|
ID string
|
||||||
Address string
|
Name string
|
||||||
ClusterAPIAddress string
|
Leader bool
|
||||||
CoreAPIAddress string
|
Address string
|
||||||
Raft ClusterRaft
|
Raft ClusterRaft
|
||||||
Nodes []proxy.NodeAbout
|
Nodes []ClusterNode
|
||||||
Version ClusterVersion
|
Version ClusterVersion
|
||||||
Degraded bool
|
Degraded bool
|
||||||
DegradedErr error
|
DegradedErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cluster) About() (ClusterAbout, error) {
|
func (c *cluster) About() (ClusterAbout, error) {
|
||||||
@@ -1336,48 +1361,79 @@ func (c *cluster) About() (ClusterAbout, error) {
|
|||||||
|
|
||||||
about := ClusterAbout{
|
about := ClusterAbout{
|
||||||
ID: c.id,
|
ID: c.id,
|
||||||
Address: c.Address(),
|
|
||||||
Degraded: degraded,
|
Degraded: degraded,
|
||||||
DegradedErr: degradedErr,
|
DegradedErr: degradedErr,
|
||||||
|
Version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
if address, err := c.ClusterAPIAddress(""); err == nil {
|
if address, err := c.ClusterAPIAddress(""); err == nil {
|
||||||
about.ClusterAPIAddress = address
|
about.Address = address
|
||||||
}
|
|
||||||
|
|
||||||
if address, err := c.CoreAPIAddress(""); err == nil {
|
|
||||||
about.CoreAPIAddress = address
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := c.raft.Stats()
|
stats := c.raft.Stats()
|
||||||
|
|
||||||
about.Raft.Stats.State = stats.State
|
about.Raft.Address = stats.Address
|
||||||
about.Raft.Stats.LastContact = stats.LastContact
|
about.Raft.State = stats.State
|
||||||
about.Raft.Stats.NumPeers = stats.NumPeers
|
about.Raft.LastContact = stats.LastContact
|
||||||
|
about.Raft.NumPeers = stats.NumPeers
|
||||||
|
about.Raft.LogIndex = stats.LogIndex
|
||||||
|
about.Raft.LogTerm = stats.LogTerm
|
||||||
|
|
||||||
servers, err := c.raft.Servers()
|
servers, err := c.raft.Servers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Error().WithError(err).Log("Raft configuration")
|
c.logger.Warn().WithError(err).Log("Raft configuration")
|
||||||
return ClusterAbout{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, server := range servers {
|
serversMap := map[string]raft.Server{}
|
||||||
node := ClusterRaftServer{
|
|
||||||
ID: server.ID,
|
for _, s := range servers {
|
||||||
Address: server.Address,
|
serversMap[s.ID] = s
|
||||||
Voter: server.Voter,
|
}
|
||||||
Leader: server.Leader,
|
|
||||||
|
c.nodesLock.Lock()
|
||||||
|
for id, node := range c.nodes {
|
||||||
|
nodeAbout := node.About()
|
||||||
|
|
||||||
|
node := ClusterNode{
|
||||||
|
ID: id,
|
||||||
|
Name: nodeAbout.Name,
|
||||||
|
Version: nodeAbout.Version,
|
||||||
|
Status: nodeAbout.Status,
|
||||||
|
Error: nodeAbout.Error,
|
||||||
|
Address: nodeAbout.Address,
|
||||||
|
LastContact: nodeAbout.LastContact,
|
||||||
|
Latency: nodeAbout.Latency,
|
||||||
|
CreatedAt: nodeAbout.Core.CreatedAt,
|
||||||
|
Uptime: nodeAbout.Core.Uptime,
|
||||||
|
Core: ClusterNodeCore{
|
||||||
|
Address: nodeAbout.Core.Address,
|
||||||
|
Status: nodeAbout.Core.Status,
|
||||||
|
Error: nodeAbout.Core.Error,
|
||||||
|
LastContact: nodeAbout.Core.LastContact,
|
||||||
|
Latency: nodeAbout.Core.Latency,
|
||||||
|
},
|
||||||
|
Resources: ClusterNodeResources{
|
||||||
|
IsThrottling: nodeAbout.Resources.IsThrottling,
|
||||||
|
NCPU: nodeAbout.Resources.NCPU,
|
||||||
|
CPU: nodeAbout.Resources.CPU,
|
||||||
|
CPULimit: nodeAbout.Resources.CPULimit,
|
||||||
|
Mem: nodeAbout.Resources.Mem,
|
||||||
|
MemLimit: nodeAbout.Resources.MemLimit,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
about.Raft.Server = append(about.Raft.Server, node)
|
if id == c.id {
|
||||||
}
|
about.Name = nodeAbout.Name
|
||||||
|
}
|
||||||
|
|
||||||
about.Version = Version
|
if s, ok := serversMap[id]; ok {
|
||||||
|
node.Voter = s.Voter
|
||||||
|
node.Leader = s.Leader
|
||||||
|
}
|
||||||
|
|
||||||
nodes := c.ProxyReader().ListNodes()
|
about.Nodes = append(about.Nodes, node)
|
||||||
for _, node := range nodes {
|
|
||||||
about.Nodes = append(about.Nodes, node.About())
|
|
||||||
}
|
}
|
||||||
|
c.nodesLock.Unlock()
|
||||||
|
|
||||||
return about, nil
|
return about, nil
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
type Node interface {
|
type Node interface {
|
||||||
Stop() error
|
Stop() error
|
||||||
|
About() About
|
||||||
Version() string
|
Version() string
|
||||||
IPs() []string
|
IPs() []string
|
||||||
Status() (string, error)
|
Status() (string, error)
|
||||||
@@ -42,7 +43,7 @@ type node struct {
|
|||||||
lastContactErr error
|
lastContactErr error
|
||||||
lastCoreContact time.Time
|
lastCoreContact time.Time
|
||||||
lastCoreContactErr error
|
lastCoreContactErr error
|
||||||
latency time.Duration
|
latency float64
|
||||||
pingLock sync.RWMutex
|
pingLock sync.RWMutex
|
||||||
|
|
||||||
runLock sync.Mutex
|
runLock sync.Mutex
|
||||||
@@ -96,8 +97,9 @@ func (n *node) start(id string) error {
|
|||||||
address, config, err := n.CoreEssentials()
|
address, config, err := n.CoreEssentials()
|
||||||
n.proxyNode = proxy.NewNode(id, address, config)
|
n.proxyNode = proxy.NewNode(id, address, config)
|
||||||
|
|
||||||
|
n.lastCoreContactErr = err
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n.lastCoreContactErr = err
|
|
||||||
go func(ctx context.Context) {
|
go func(ctx context.Context) {
|
||||||
ticker := time.NewTicker(5 * time.Second)
|
ticker := time.NewTicker(5 * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
@@ -143,6 +145,74 @@ func (n *node) Stop() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var maxLastContact time.Duration = 5 * time.Second
|
||||||
|
|
||||||
|
type AboutCore struct {
|
||||||
|
Address string
|
||||||
|
State string
|
||||||
|
StateError error
|
||||||
|
Status string
|
||||||
|
Error error
|
||||||
|
CreatedAt time.Time
|
||||||
|
Uptime time.Duration
|
||||||
|
LastContact time.Duration
|
||||||
|
Latency time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type About struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Version string
|
||||||
|
Address string
|
||||||
|
Status string
|
||||||
|
LastContact time.Duration
|
||||||
|
Latency time.Duration
|
||||||
|
Error error
|
||||||
|
Core AboutCore
|
||||||
|
Resources proxy.NodeResources
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *node) About() About {
|
||||||
|
a := About{
|
||||||
|
ID: n.id,
|
||||||
|
Version: n.Version(),
|
||||||
|
Address: n.address,
|
||||||
|
}
|
||||||
|
|
||||||
|
n.pingLock.RLock()
|
||||||
|
a.LastContact = time.Since(n.lastContact)
|
||||||
|
if a.LastContact > maxLastContact {
|
||||||
|
a.Status = "offline"
|
||||||
|
} else {
|
||||||
|
a.Status = "online"
|
||||||
|
}
|
||||||
|
a.Latency = time.Duration(n.latency * float64(time.Second))
|
||||||
|
a.Error = n.lastContactErr
|
||||||
|
|
||||||
|
coreError := n.lastCoreContactErr
|
||||||
|
n.pingLock.RUnlock()
|
||||||
|
|
||||||
|
about := n.CoreAbout()
|
||||||
|
|
||||||
|
a.Name = about.Name
|
||||||
|
a.Core.Address = about.Address
|
||||||
|
a.Core.State = about.State
|
||||||
|
a.Core.StateError = about.Error
|
||||||
|
a.Core.CreatedAt = about.CreatedAt
|
||||||
|
a.Core.Uptime = about.Uptime
|
||||||
|
a.Core.LastContact = time.Since(about.LastContact)
|
||||||
|
if a.Core.LastContact > maxLastContact {
|
||||||
|
a.Core.Status = "offline"
|
||||||
|
} else {
|
||||||
|
a.Core.Status = "online"
|
||||||
|
}
|
||||||
|
a.Core.Error = coreError
|
||||||
|
a.Core.Latency = about.Latency
|
||||||
|
a.Resources = about.Resources
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
func (n *node) Version() string {
|
func (n *node) Version() string {
|
||||||
n.pingLock.RLock()
|
n.pingLock.RLock()
|
||||||
defer n.pingLock.RUnlock()
|
defer n.pingLock.RUnlock()
|
||||||
@@ -159,7 +229,7 @@ func (n *node) Status() (string, error) {
|
|||||||
defer n.pingLock.RUnlock()
|
defer n.pingLock.RUnlock()
|
||||||
|
|
||||||
since := time.Since(n.lastContact)
|
since := time.Since(n.lastContact)
|
||||||
if since > 5*time.Second {
|
if since > maxLastContact {
|
||||||
return "offline", fmt.Errorf("the cluster API didn't respond for %s because: %w", since, n.lastContactErr)
|
return "offline", fmt.Errorf("the cluster API didn't respond for %s because: %w", since, n.lastContactErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +241,7 @@ func (n *node) CoreStatus() (string, error) {
|
|||||||
defer n.pingLock.RUnlock()
|
defer n.pingLock.RUnlock()
|
||||||
|
|
||||||
since := time.Since(n.lastCoreContact)
|
since := time.Since(n.lastCoreContact)
|
||||||
if since > 5*time.Second {
|
if since > maxLastContact {
|
||||||
return "offline", fmt.Errorf("the core API didn't respond for %s because: %w", since, n.lastCoreContactErr)
|
return "offline", fmt.Errorf("the core API didn't respond for %s because: %w", since, n.lastCoreContactErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,6 +281,10 @@ func (n *node) CoreAPIAddress() (string, error) {
|
|||||||
return n.client.CoreAPIAddress()
|
return n.client.CoreAPIAddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *node) CoreAbout() proxy.NodeAbout {
|
||||||
|
return n.proxyNode.About()
|
||||||
|
}
|
||||||
|
|
||||||
func (n *node) Barrier(name string) (bool, error) {
|
func (n *node) Barrier(name string) (bool, error) {
|
||||||
return n.client.Barrier(name)
|
return n.client.Barrier(name)
|
||||||
}
|
}
|
||||||
@@ -232,7 +306,8 @@ func (n *node) ping(ctx context.Context) {
|
|||||||
n.pingLock.Lock()
|
n.pingLock.Lock()
|
||||||
n.version = version
|
n.version = version
|
||||||
n.lastContact = time.Now()
|
n.lastContact = time.Now()
|
||||||
n.latency = time.Since(start)
|
n.lastContactErr = nil
|
||||||
|
n.latency = n.latency*0.2 + time.Since(start).Seconds()*0.8
|
||||||
n.pingLock.Unlock()
|
n.pingLock.Unlock()
|
||||||
} else {
|
} else {
|
||||||
n.pingLock.Lock()
|
n.pingLock.Lock()
|
||||||
|
@@ -541,6 +541,7 @@ func (n *node) About() NodeAbout {
|
|||||||
Name: about.Name,
|
Name: about.Name,
|
||||||
Address: n.address,
|
Address: n.address,
|
||||||
State: state.String(),
|
State: state.String(),
|
||||||
|
Error: n.peerErr,
|
||||||
CreatedAt: createdAt,
|
CreatedAt: createdAt,
|
||||||
Uptime: time.Since(createdAt),
|
Uptime: time.Since(createdAt),
|
||||||
LastContact: n.lastContact,
|
LastContact: n.lastContact,
|
||||||
|
@@ -83,9 +83,12 @@ type Server struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Stats struct {
|
type Stats struct {
|
||||||
|
Address string
|
||||||
State string
|
State string
|
||||||
LastContact time.Duration
|
LastContact time.Duration
|
||||||
NumPeers uint64
|
NumPeers uint64
|
||||||
|
LogTerm uint64
|
||||||
|
LogIndex uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -194,7 +197,9 @@ func (r *raft) Servers() ([]Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *raft) Stats() Stats {
|
func (r *raft) Stats() Stats {
|
||||||
stats := Stats{}
|
stats := Stats{
|
||||||
|
Address: r.raftAddress,
|
||||||
|
}
|
||||||
|
|
||||||
s := r.raft.Stats()
|
s := r.raft.Stats()
|
||||||
|
|
||||||
@@ -219,6 +224,14 @@ func (r *raft) Stats() Stats {
|
|||||||
stats.NumPeers = x
|
stats.NumPeers = x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if x, err := strconv.ParseUint(s["last_log_term"], 10, 64); err == nil {
|
||||||
|
stats.LogTerm = x
|
||||||
|
}
|
||||||
|
|
||||||
|
if x, err := strconv.ParseUint(s["last_log_index"], 10, 64); err == nil {
|
||||||
|
stats.LogIndex = x
|
||||||
|
}
|
||||||
|
|
||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
91
docs/docs.go
91
docs/docs.go
@@ -4437,12 +4437,6 @@ const docTemplate = `{
|
|||||||
"address": {
|
"address": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"cluster_api_address": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"core_api_address": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"degraded": {
|
"degraded": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
@@ -4452,6 +4446,12 @@ const docTemplate = `{
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"leader": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
@@ -4500,31 +4500,65 @@ const docTemplate = `{
|
|||||||
"address": {
|
"address": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"core": {
|
||||||
|
"$ref": "#/definitions/api.ClusterNodeCore"
|
||||||
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
|
"description": "RFC 3339",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"last_contact": {
|
"last_contact_ms": {
|
||||||
"description": "unix timestamp",
|
"type": "number"
|
||||||
"type": "integer"
|
|
||||||
},
|
},
|
||||||
"latency_ms": {
|
"latency_ms": {
|
||||||
"description": "milliseconds",
|
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"leader": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"resources": {
|
"resources": {
|
||||||
"$ref": "#/definitions/api.ClusterNodeResources"
|
"$ref": "#/definitions/api.ClusterNodeResources"
|
||||||
},
|
},
|
||||||
"state": {
|
"status": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"uptime_seconds": {
|
"uptime_seconds": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"voter": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"api.ClusterNodeCore": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"last_contact_ms": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"latency_ms": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4580,43 +4614,20 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"api.ClusterRaft": {
|
"api.ClusterRaft": {
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"server": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/api.ClusterRaftServer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"stats": {
|
|
||||||
"$ref": "#/definitions/api.ClusterRaftStats"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"api.ClusterRaftServer": {
|
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"address": {
|
"address": {
|
||||||
"description": "raft address",
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"leader": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"voter": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"api.ClusterRaftStats": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"last_contact_ms": {
|
"last_contact_ms": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"log_index": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"log_term": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"num_peers": {
|
"num_peers": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
@@ -4429,12 +4429,6 @@
|
|||||||
"address": {
|
"address": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"cluster_api_address": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"core_api_address": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"degraded": {
|
"degraded": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
@@ -4444,6 +4438,12 @@
|
|||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"leader": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
@@ -4492,31 +4492,65 @@
|
|||||||
"address": {
|
"address": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"core": {
|
||||||
|
"$ref": "#/definitions/api.ClusterNodeCore"
|
||||||
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
|
"description": "RFC 3339",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"last_contact": {
|
"last_contact_ms": {
|
||||||
"description": "unix timestamp",
|
"type": "number"
|
||||||
"type": "integer"
|
|
||||||
},
|
},
|
||||||
"latency_ms": {
|
"latency_ms": {
|
||||||
"description": "milliseconds",
|
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"leader": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"resources": {
|
"resources": {
|
||||||
"$ref": "#/definitions/api.ClusterNodeResources"
|
"$ref": "#/definitions/api.ClusterNodeResources"
|
||||||
},
|
},
|
||||||
"state": {
|
"status": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"uptime_seconds": {
|
"uptime_seconds": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"voter": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"api.ClusterNodeCore": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"last_contact_ms": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"latency_ms": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4572,43 +4606,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"api.ClusterRaft": {
|
"api.ClusterRaft": {
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"server": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/api.ClusterRaftServer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"stats": {
|
|
||||||
"$ref": "#/definitions/api.ClusterRaftStats"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"api.ClusterRaftServer": {
|
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"address": {
|
"address": {
|
||||||
"description": "raft address",
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"leader": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"voter": {
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"api.ClusterRaftStats": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"last_contact_ms": {
|
"last_contact_ms": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"log_index": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"log_term": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"num_peers": {
|
"num_peers": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
@@ -69,16 +69,16 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
address:
|
address:
|
||||||
type: string
|
type: string
|
||||||
cluster_api_address:
|
|
||||||
type: string
|
|
||||||
core_api_address:
|
|
||||||
type: string
|
|
||||||
degraded:
|
degraded:
|
||||||
type: boolean
|
type: boolean
|
||||||
degraded_error:
|
degraded_error:
|
||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
|
leader:
|
||||||
|
type: boolean
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
nodes:
|
nodes:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/api.ClusterNode'
|
$ref: '#/definitions/api.ClusterNode'
|
||||||
@@ -110,24 +110,46 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
address:
|
address:
|
||||||
type: string
|
type: string
|
||||||
|
core:
|
||||||
|
$ref: '#/definitions/api.ClusterNodeCore'
|
||||||
created_at:
|
created_at:
|
||||||
|
description: RFC 3339
|
||||||
|
type: string
|
||||||
|
error:
|
||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
last_contact:
|
last_contact_ms:
|
||||||
description: unix timestamp
|
|
||||||
type: integer
|
|
||||||
latency_ms:
|
|
||||||
description: milliseconds
|
|
||||||
type: number
|
type: number
|
||||||
|
latency_ms:
|
||||||
|
type: number
|
||||||
|
leader:
|
||||||
|
type: boolean
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
resources:
|
resources:
|
||||||
$ref: '#/definitions/api.ClusterNodeResources'
|
$ref: '#/definitions/api.ClusterNodeResources'
|
||||||
state:
|
status:
|
||||||
type: string
|
type: string
|
||||||
uptime_seconds:
|
uptime_seconds:
|
||||||
type: integer
|
type: integer
|
||||||
|
version:
|
||||||
|
type: string
|
||||||
|
voter:
|
||||||
|
type: boolean
|
||||||
|
type: object
|
||||||
|
api.ClusterNodeCore:
|
||||||
|
properties:
|
||||||
|
address:
|
||||||
|
type: string
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
last_contact_ms:
|
||||||
|
type: number
|
||||||
|
latency_ms:
|
||||||
|
type: number
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
type: object
|
type: object
|
||||||
api.ClusterNodeFiles:
|
api.ClusterNodeFiles:
|
||||||
properties:
|
properties:
|
||||||
@@ -165,30 +187,15 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
api.ClusterRaft:
|
api.ClusterRaft:
|
||||||
properties:
|
|
||||||
server:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/api.ClusterRaftServer'
|
|
||||||
type: array
|
|
||||||
stats:
|
|
||||||
$ref: '#/definitions/api.ClusterRaftStats'
|
|
||||||
type: object
|
|
||||||
api.ClusterRaftServer:
|
|
||||||
properties:
|
properties:
|
||||||
address:
|
address:
|
||||||
description: raft address
|
|
||||||
type: string
|
type: string
|
||||||
id:
|
|
||||||
type: string
|
|
||||||
leader:
|
|
||||||
type: boolean
|
|
||||||
voter:
|
|
||||||
type: boolean
|
|
||||||
type: object
|
|
||||||
api.ClusterRaftStats:
|
|
||||||
properties:
|
|
||||||
last_contact_ms:
|
last_contact_ms:
|
||||||
type: number
|
type: number
|
||||||
|
log_index:
|
||||||
|
type: integer
|
||||||
|
log_term:
|
||||||
|
type: integer
|
||||||
num_peers:
|
num_peers:
|
||||||
type: integer
|
type: integer
|
||||||
state:
|
state:
|
||||||
|
@@ -3,32 +3,31 @@ package api
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/cluster/proxy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ClusterNode struct {
|
type ClusterNode struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
Voter bool `json:"voter"`
|
||||||
|
Leader bool `json:"leader"`
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"` // RFC 3339
|
||||||
Uptime int64 `json:"uptime_seconds"`
|
Uptime int64 `json:"uptime_seconds"`
|
||||||
LastContact int64 `json:"last_contact"` // unix timestamp
|
LastContact float64 `json:"last_contact_ms"`
|
||||||
Latency float64 `json:"latency_ms"` // milliseconds
|
Latency float64 `json:"latency_ms"`
|
||||||
State string `json:"state"`
|
Core ClusterNodeCore `json:"core"`
|
||||||
Resources ClusterNodeResources `json:"resources"`
|
Resources ClusterNodeResources `json:"resources"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *ClusterNode) Marshal(about proxy.NodeAbout) {
|
type ClusterNodeCore struct {
|
||||||
n.ID = about.ID
|
Address string `json:"address"`
|
||||||
n.Name = about.Name
|
Status string `json:"status"`
|
||||||
n.Address = about.Address
|
Error string `json:"error"`
|
||||||
n.CreatedAt = about.CreatedAt.Format(time.RFC3339)
|
LastContact float64 `json:"last_contact_ms"`
|
||||||
n.Uptime = int64(about.Uptime.Seconds())
|
Latency float64 `json:"latency_ms"`
|
||||||
n.LastContact = about.LastContact.Unix()
|
|
||||||
n.Latency = about.Latency.Seconds() * 1000
|
|
||||||
n.State = about.State
|
|
||||||
n.Resources = ClusterNodeResources(about.Resources)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterNodeResources struct {
|
type ClusterNodeResources struct {
|
||||||
@@ -40,39 +39,30 @@ type ClusterNodeResources struct {
|
|||||||
MemLimit uint64 `json:"memory_limit_bytes"` // bytes
|
MemLimit uint64 `json:"memory_limit_bytes"` // bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterNodeFiles struct {
|
type ClusterRaft struct {
|
||||||
LastUpdate int64 `json:"last_update"` // unix timestamp
|
Address string `json:"address"`
|
||||||
Files map[string][]string `json:"files"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClusterRaftServer struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Address string `json:"address"` // raft address
|
|
||||||
Voter bool `json:"voter"`
|
|
||||||
Leader bool `json:"leader"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClusterRaftStats struct {
|
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
LastContact float64 `json:"last_contact_ms"`
|
LastContact float64 `json:"last_contact_ms"`
|
||||||
NumPeers uint64 `json:"num_peers"`
|
NumPeers uint64 `json:"num_peers"`
|
||||||
}
|
LogTerm uint64 `json:"log_term"`
|
||||||
|
LogIndex uint64 `json:"log_index"`
|
||||||
type ClusterRaft struct {
|
|
||||||
Server []ClusterRaftServer `json:"server"`
|
|
||||||
Stats ClusterRaftStats `json:"stats"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterAbout struct {
|
type ClusterAbout struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Address string `json:"address"`
|
Name string `json:"name"`
|
||||||
ClusterAPIAddress string `json:"cluster_api_address"`
|
Leader bool `json:"leader"`
|
||||||
CoreAPIAddress string `json:"core_api_address"`
|
Address string `json:"address"`
|
||||||
Raft ClusterRaft `json:"raft"`
|
Raft ClusterRaft `json:"raft"`
|
||||||
Nodes []ClusterNode `json:"nodes"`
|
Nodes []ClusterNode `json:"nodes"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Degraded bool `json:"degraded"`
|
Degraded bool `json:"degraded"`
|
||||||
DegradedErr string `json:"degraded_error"`
|
DegradedErr string `json:"degraded_error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClusterNodeFiles struct {
|
||||||
|
LastUpdate int64 `json:"last_update"` // unix timestamp
|
||||||
|
Files map[string][]string `json:"files"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClusterProcess struct {
|
type ClusterProcess struct {
|
||||||
|
@@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/datarhei/core/v16/cluster"
|
"github.com/datarhei/core/v16/cluster"
|
||||||
"github.com/datarhei/core/v16/cluster/proxy"
|
"github.com/datarhei/core/v16/cluster/proxy"
|
||||||
@@ -56,18 +57,19 @@ func (h *ClusterHandler) About(c echo.Context) error {
|
|||||||
state, _ := h.cluster.About()
|
state, _ := h.cluster.About()
|
||||||
|
|
||||||
about := api.ClusterAbout{
|
about := api.ClusterAbout{
|
||||||
ID: state.ID,
|
ID: state.ID,
|
||||||
Address: state.Address,
|
Name: state.Name,
|
||||||
ClusterAPIAddress: state.ClusterAPIAddress,
|
Leader: state.Leader,
|
||||||
CoreAPIAddress: state.CoreAPIAddress,
|
Address: state.Address,
|
||||||
Raft: api.ClusterRaft{
|
Raft: api.ClusterRaft{
|
||||||
Server: []api.ClusterRaftServer{},
|
Address: state.Raft.Address,
|
||||||
Stats: api.ClusterRaftStats{
|
State: state.Raft.State,
|
||||||
State: state.Raft.Stats.State,
|
LastContact: state.Raft.LastContact.Seconds() * 1000,
|
||||||
LastContact: state.Raft.Stats.LastContact.Seconds() * 1000,
|
NumPeers: state.Raft.NumPeers,
|
||||||
NumPeers: state.Raft.Stats.NumPeers,
|
LogTerm: state.Raft.LogTerm,
|
||||||
},
|
LogIndex: state.Raft.LogIndex,
|
||||||
},
|
},
|
||||||
|
Nodes: []api.ClusterNode{},
|
||||||
Version: state.Version.String(),
|
Version: state.Version.String(),
|
||||||
Degraded: state.Degraded,
|
Degraded: state.Degraded,
|
||||||
}
|
}
|
||||||
@@ -76,25 +78,53 @@ func (h *ClusterHandler) About(c echo.Context) error {
|
|||||||
about.DegradedErr = state.DegradedErr.Error()
|
about.DegradedErr = state.DegradedErr.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range state.Raft.Server {
|
|
||||||
about.Raft.Server = append(about.Raft.Server, api.ClusterRaftServer{
|
|
||||||
ID: n.ID,
|
|
||||||
Address: n.Address,
|
|
||||||
Voter: n.Voter,
|
|
||||||
Leader: n.Leader,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, node := range state.Nodes {
|
for _, node := range state.Nodes {
|
||||||
n := api.ClusterNode{}
|
about.Nodes = append(about.Nodes, h.marshalClusterNode(node))
|
||||||
n.Marshal(node)
|
|
||||||
|
|
||||||
about.Nodes = append(about.Nodes, n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, about)
|
return c.JSON(http.StatusOK, about)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *ClusterHandler) marshalClusterNode(node cluster.ClusterNode) api.ClusterNode {
|
||||||
|
n := api.ClusterNode{
|
||||||
|
ID: node.ID,
|
||||||
|
Name: node.Name,
|
||||||
|
Version: node.Version,
|
||||||
|
Status: node.Status,
|
||||||
|
Voter: node.Voter,
|
||||||
|
Leader: node.Leader,
|
||||||
|
Address: node.Address,
|
||||||
|
CreatedAt: node.CreatedAt.Format(time.RFC3339),
|
||||||
|
Uptime: int64(node.Uptime.Seconds()),
|
||||||
|
LastContact: node.LastContact.Seconds() * 1000,
|
||||||
|
Latency: node.Latency.Seconds() * 1000,
|
||||||
|
Core: api.ClusterNodeCore{
|
||||||
|
Address: node.Core.Address,
|
||||||
|
Status: node.Core.Status,
|
||||||
|
LastContact: node.Core.LastContact.Seconds() * 1000,
|
||||||
|
Latency: node.Core.Latency.Seconds() * 1000,
|
||||||
|
},
|
||||||
|
Resources: api.ClusterNodeResources{
|
||||||
|
IsThrottling: node.Resources.IsThrottling,
|
||||||
|
NCPU: node.Resources.NCPU,
|
||||||
|
CPU: node.Resources.CPU,
|
||||||
|
CPULimit: node.Resources.CPULimit,
|
||||||
|
Mem: node.Resources.Mem,
|
||||||
|
MemLimit: node.Resources.MemLimit,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Error != nil {
|
||||||
|
n.Error = node.Error.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Core.Error != nil {
|
||||||
|
n.Core.Error = node.Core.Error.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
// Healthy returns whether the cluster is healthy
|
// Healthy returns whether the cluster is healthy
|
||||||
// @Summary Whether the cluster is healthy
|
// @Summary Whether the cluster is healthy
|
||||||
// @Description Whether the cluster is healthy
|
// @Description Whether the cluster is healthy
|
||||||
|
@@ -24,16 +24,12 @@ import (
|
|||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /api/v3/cluster/node [get]
|
// @Router /api/v3/cluster/node [get]
|
||||||
func (h *ClusterHandler) GetNodes(c echo.Context) error {
|
func (h *ClusterHandler) GetNodes(c echo.Context) error {
|
||||||
nodes := h.proxy.ListNodes()
|
about, _ := h.cluster.About()
|
||||||
|
|
||||||
list := []api.ClusterNode{}
|
list := []api.ClusterNode{}
|
||||||
|
|
||||||
for _, node := range nodes {
|
for _, node := range about.Nodes {
|
||||||
about := node.About()
|
list = append(list, h.marshalClusterNode(node))
|
||||||
n := api.ClusterNode{}
|
|
||||||
n.Marshal(about)
|
|
||||||
|
|
||||||
list = append(list, n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, list)
|
return c.JSON(http.StatusOK, list)
|
||||||
@@ -53,26 +49,17 @@ func (h *ClusterHandler) GetNodes(c echo.Context) error {
|
|||||||
func (h *ClusterHandler) GetNode(c echo.Context) error {
|
func (h *ClusterHandler) GetNode(c echo.Context) error {
|
||||||
id := util.PathParam(c, "id")
|
id := util.PathParam(c, "id")
|
||||||
|
|
||||||
peer, err := h.proxy.GetNodeReader(id)
|
about, _ := h.cluster.About()
|
||||||
if err != nil {
|
|
||||||
return api.Err(http.StatusNotFound, "", "node not found: %s", err.Error())
|
for _, node := range about.Nodes {
|
||||||
|
if node.ID != id {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, h.marshalClusterNode(node))
|
||||||
}
|
}
|
||||||
|
|
||||||
about := peer.About()
|
return api.Err(http.StatusNotFound, "", "node not found")
|
||||||
|
|
||||||
node := api.ClusterNode{
|
|
||||||
ID: about.ID,
|
|
||||||
Name: about.Name,
|
|
||||||
Address: about.Address,
|
|
||||||
CreatedAt: about.CreatedAt.Format(time.RFC3339),
|
|
||||||
Uptime: int64(about.Uptime.Seconds()),
|
|
||||||
LastContact: about.LastContact.Unix(),
|
|
||||||
Latency: about.Latency.Seconds() * 1000,
|
|
||||||
State: about.State,
|
|
||||||
Resources: api.ClusterNodeResources(about.Resources),
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, node)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNodeVersion returns the proxy node version with the given ID
|
// GetNodeVersion returns the proxy node version with the given ID
|
||||||
|
Reference in New Issue
Block a user