mirror of
https://github.com/eosswedenorg/eth-healthcheck
synced 2026-06-16 05:04:55 +02:00
Inital Commit
This commit is contained in:
commit
0d27205c7c
15 changed files with 472 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
build/
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 EOS Sw/eden
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
25
Makefile
Normal file
25
Makefile
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
GO = go
|
||||
GOCCFLAGS = -v
|
||||
GOLDFLAGS = -ldflags="-s -w"
|
||||
PREFIX = /usr/local
|
||||
|
||||
PROGRAM_NAME=eth-healthcheck
|
||||
SOURCES=src/cmd/main.go
|
||||
|
||||
all: build
|
||||
build: build/$(PROGRAM_NAME)
|
||||
|
||||
build/$(PROGRAM_NAME) : $(SOURCES)
|
||||
$(GO) build -o $@ $(GOCCFLAGS) $(GOLDFLAGS) $^
|
||||
|
||||
pkg_info :
|
||||
echo PACKAGE_NAME=\"$(PROGRAM_NAME)\" "\n"\
|
||||
PACKAGE_DESCRIPTION=\"HAproxy healthcheck program for Etherium nodes.\" "\n"\
|
||||
PACKAGE_VERSION=\"0.1.0\" "\n"\
|
||||
PACKAGE_PREFIX=\"$(PREFIX:/%=%)\" "\n"\
|
||||
PACKAGE_PROGRAM=\"$(PROGRAM_NAME)\" > build/pkg_info
|
||||
|
||||
package : build pkg_info
|
||||
|
||||
package_deb: package
|
||||
./scripts/pkg.sh deb $(realpath build)
|
||||
79
compile.sh
Executable file
79
compile.sh
Executable file
|
|
@ -0,0 +1,79 @@
|
|||
#!/bin/bash
|
||||
|
||||
SYSTEMS=( windows linux freebsd )
|
||||
|
||||
ARCHS=( 386 amd64 amd64p32 arm arm64 ppc ppc64 )
|
||||
|
||||
function usage() {
|
||||
echo "Usage: ${0##*/} [ -h|--help ] [ --target <system> ] [ -a|--arch <arch> ] [ -p|--package ]"
|
||||
echo ""
|
||||
echo " Valid systems:"
|
||||
for i in "${SYSTEMS[@]}"; do
|
||||
echo " * ${i}"
|
||||
done
|
||||
echo ""
|
||||
echo " Valid architectures:"
|
||||
for i in "${ARCHS[@]}"; do
|
||||
echo " * ${i}"
|
||||
done
|
||||
echo ""
|
||||
exit 1
|
||||
}
|
||||
|
||||
options=$(getopt -n "${0##*/}" -o "ht:a:p" -l "help,target:,arch:,package" -- "$@")
|
||||
|
||||
[ $? -eq 0 ] || usage
|
||||
|
||||
eval set -- "$options"
|
||||
|
||||
MAKE_TARGET="all"
|
||||
|
||||
while true; do
|
||||
|
||||
case $1 in
|
||||
-p|--package)
|
||||
MAKE_TARGET="package_deb"
|
||||
;;
|
||||
-t|--target)
|
||||
shift
|
||||
REGEX=$(echo "${SYSTEMS[@]}" | sed 's/[[:space:]]/|/g')
|
||||
[[ ! "$1" =~ ^($REGEX)$ ]] && {
|
||||
echo "Incorrect system '$1' provided"
|
||||
usage
|
||||
}
|
||||
export GOOS=$1
|
||||
;;
|
||||
-a|--arch)
|
||||
shift
|
||||
REGEX=$(echo "${ARCHS[@]}" | sed 's/[[:space:]]/|/g')
|
||||
[[ ! "$1" =~ ^($REGEX)$ ]] && {
|
||||
echo "Incorrect architecture '$1' provided"
|
||||
usage
|
||||
}
|
||||
export GOARCH=$1
|
||||
;;
|
||||
-h|--help) usage ;;
|
||||
--) shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
MESSAGE=""
|
||||
if [ ! -z "${GOOS}" ]; then
|
||||
# Hack to select the right package :)
|
||||
if [ "${MAKE_TARGET}" == "package_deb" ] && [ "${GOOS}" == "freebsd" ]; then
|
||||
MAKE_TARGET="package_freebsd"
|
||||
fi
|
||||
MESSAGE="[\e[34m::\e[0m] Crosscompiling for: ${GOOS}"
|
||||
fi
|
||||
|
||||
if [ ! -z "${GOARCH}" ]; then
|
||||
MESSAGE="${MESSAGE} (${GOARCH})"
|
||||
fi
|
||||
|
||||
|
||||
[ ! -z "${MESSAGE}" ] && echo -e "" $MESSAGE
|
||||
|
||||
make -B ${MAKE_TARGET}
|
||||
17
go.mod
Normal file
17
go.mod
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
module eosswedenorg/eth-healthcheck
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/go-stack/stack v1.8.0
|
||||
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
github.com/mattn/go-isatty v0.0.13
|
||||
github.com/onrik/ethrpc v1.0.0
|
||||
github.com/pborman/getopt/v2 v2.1.0
|
||||
internal/haproxy v1.0.0
|
||||
internal/tcp_server v1.0.0
|
||||
)
|
||||
|
||||
replace internal/haproxy => ./src/haproxy
|
||||
replace internal/tcp_server => ./src/tcp_server
|
||||
16
go.sum
Normal file
16
go.sum
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac h1:n1DqxAo4oWPMvH1+v+DLYlMCecgumhhgnxAPdqDIFHI=
|
||||
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/onrik/ethrpc v1.0.0 h1:caG0U/Y1op32mntqmDnwvzue4Tg37cSA2EU8PMIMSjM=
|
||||
github.com/onrik/ethrpc v1.0.0/go.mod h1:RoqOlDiBBs1qYamkcYhxMgkPijxu5R8t55mgUiy4le8=
|
||||
github.com/pborman/getopt/v2 v2.1.0 h1:eNfR+r+dWLdWmV8g5OlpyrTYHkhVNxHBdN2cCrJmOEA=
|
||||
github.com/pborman/getopt/v2 v2.1.0/go.mod h1:4NtW75ny4eBw9fO1bhtNdYTlZKYX5/tBLtsOpwKIKd0=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
36
scripts/pkg.sh
Executable file
36
scripts/pkg.sh
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/bash
|
||||
|
||||
############################
|
||||
# Exported variables. #
|
||||
############################
|
||||
|
||||
export BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "$0 <pkg_type> <build_dir>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PKG_TYPE=$1
|
||||
BUILD_DIR=$2
|
||||
|
||||
PKG_SCRIPT="${BASE_DIR}/pkg_${PKG_TYPE}.sh"
|
||||
|
||||
# Check and call script
|
||||
if [ ! -x $PKG_SCRIPT ]; then
|
||||
echo "$PKG_SCRIPT not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Info
|
||||
set -o allexport
|
||||
source ${BUILD_DIR}/pkg_info
|
||||
set +o allexport
|
||||
|
||||
# Directories.
|
||||
export PACKAGE_BINDIR=${PACKAGE_PREFIX}/bin
|
||||
export PACKAGE_SHAREDIR=${PACKAGE_PREFIX}/share/${PACKAGE_NAME}
|
||||
export PACKAGE_TMPDIR="${BUILD_DIR}/pkg_${PKG_TYPE}"
|
||||
export BUILD_DIR
|
||||
|
||||
$PKG_SCRIPT
|
||||
46
scripts/pkg_deb.sh
Executable file
46
scripts/pkg_deb.sh
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
PACKAGE_ARCH=amd64
|
||||
|
||||
if [[ -f /etc/upstream-release/lsb-release ]]; then
|
||||
source /etc/upstream-release/lsb-release
|
||||
elif [[ -f /etc/lsb-release ]]; then
|
||||
source /etc/lsb-release
|
||||
else
|
||||
echo "ERROR: could not determine debian release."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DISTRIB_ID=$(echo $DISTRIB_ID | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Default to 1 if no release is set.
|
||||
if [[ -z $RELEASE ]]; then
|
||||
RELEASE="1"
|
||||
fi
|
||||
|
||||
PACKAGE_FULLNAME="${PACKAGE_NAME}_${PACKAGE_VERSION}-${RELEASE}-${DISTRIB_ID}-${DISTRIB_RELEASE}_${PACKAGE_ARCH}"
|
||||
|
||||
rm -fr ${PACKAGE_TMPDIR}
|
||||
|
||||
# Create debian files.
|
||||
mkdir -p ${PACKAGE_TMPDIR}/DEBIAN
|
||||
echo "Package: ${PACKAGE_NAME}
|
||||
Version: ${PACKAGE_VERSION}-${RELEASE}
|
||||
Section: introspection
|
||||
Priority: optional
|
||||
Architecture: ${PACKAGE_ARCH}
|
||||
Homepage: https://github.com/eosswedenorg/eth-healthcheck
|
||||
Maintainer: Henrik Hautakoski <henrik@eossweden.org>
|
||||
Description: ${PACKAGE_DESCRIPTION}" &> ${PACKAGE_TMPDIR}/DEBIAN/control
|
||||
|
||||
cat ${PACKAGE_TMPDIR}/DEBIAN/control
|
||||
|
||||
# Copy program
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_BINDIR}
|
||||
cp ${BUILD_DIR}/${PACKAGE_PROGRAM} ${PACKAGE_TMPDIR}/${PACKAGE_BINDIR}/${PACKAGE_NAME}
|
||||
|
||||
# Copy files.
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_SHAREDIR}
|
||||
cp ${BASE_DIR}/../LICENSE ${PACKAGE_TMPDIR}/${PACKAGE_SHAREDIR}
|
||||
|
||||
fakeroot dpkg-deb --build ${PACKAGE_TMPDIR} ${BUILD_DIR}/${PACKAGE_FULLNAME}.deb
|
||||
91
src/cmd/main.go
Normal file
91
src/cmd/main.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
log "github.com/inconshreveable/log15"
|
||||
"github.com/pborman/getopt/v2"
|
||||
"github.com/onrik/ethrpc"
|
||||
"internal/haproxy"
|
||||
"internal/tcp_server"
|
||||
)
|
||||
|
||||
var logger log.Logger
|
||||
|
||||
func onConnect(c *tcp_server.Client) {
|
||||
logger.Info("Client connected", "addr", c.Addr.String())
|
||||
}
|
||||
|
||||
func onTcpMessage(c *tcp_server.Client, message string) {
|
||||
|
||||
status := haproxy.HealthCheckDown
|
||||
client := ethrpc.New(strings.TrimSpace(message))
|
||||
|
||||
resp, err := client.EthSyncing()
|
||||
if err == nil {
|
||||
if resp.IsSyncing == false {
|
||||
status = haproxy.HealthCheckUp
|
||||
}
|
||||
} else {
|
||||
logger.Warn(err.Error())
|
||||
}
|
||||
|
||||
logger.Info("Node status", "status", status)
|
||||
|
||||
// Report status to HAproxy
|
||||
c.WriteString(fmt.Sprintln(status))
|
||||
c.Close()
|
||||
}
|
||||
|
||||
func onDisconnect(c *tcp_server.Client, err error) {
|
||||
if err == nil {
|
||||
logger.Info("Client disconnected", "addr", c.Addr.String())
|
||||
} else {
|
||||
logger.Warn("Client disconnected", "addr", c.Addr.String(), "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func argv_listen_addr() string {
|
||||
|
||||
var addr string
|
||||
|
||||
argv := getopt.Args()
|
||||
if len(argv) > 0 {
|
||||
addr = argv[0]
|
||||
} else {
|
||||
addr = "127.0.0.1"
|
||||
}
|
||||
|
||||
addr += ":"
|
||||
if len(argv) > 1 {
|
||||
addr += argv[1]
|
||||
} else {
|
||||
addr += "1301"
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
logger = log.New()
|
||||
|
||||
getopt.Parse()
|
||||
|
||||
addr := argv_listen_addr()
|
||||
server := tcp_server.New(argv_listen_addr())
|
||||
|
||||
logger.Info(fmt.Sprintf("Listening on: %s", addr))
|
||||
|
||||
// TCP Client sends message.
|
||||
server.OnConnect(onConnect)
|
||||
server.OnMessage(onTcpMessage)
|
||||
server.OnDisconnect(onDisconnect)
|
||||
|
||||
err := server.Listen()
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
3
src/haproxy/go.mod
Normal file
3
src/haproxy/go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module internal/haproxy
|
||||
|
||||
go 1.14
|
||||
15
src/haproxy/types.go
Normal file
15
src/haproxy/types.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
package haproxy
|
||||
|
||||
// All supported health check values for HAproxy.
|
||||
// See https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#5.2-agent-check
|
||||
type HealthCheckStatus string
|
||||
const (
|
||||
HealthCheckUp = "up"
|
||||
HealthCheckDown = "down"
|
||||
HealthCheckMaint = "maint"
|
||||
HealthCheckReady = "ready"
|
||||
HealthCheckDrain = "drain"
|
||||
HealthCheckFailed = "failed"
|
||||
HealthCheckStopped = "Stopped"
|
||||
)
|
||||
44
src/tcp_server/client.go
Normal file
44
src/tcp_server/client.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
package tcp_server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Read client messages.
|
||||
func (c *Client) read() {
|
||||
reader := bufio.NewReader(c.conn)
|
||||
for {
|
||||
message, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
c.conn.Close()
|
||||
c.Server.onDisconnect(c, err)
|
||||
return
|
||||
}
|
||||
c.Server.onMessage(c, message)
|
||||
}
|
||||
}
|
||||
|
||||
// Write string to client.
|
||||
func (c *Client) WriteString(message string) error {
|
||||
return c.Write([]byte(message))
|
||||
}
|
||||
|
||||
// Write bytes to client
|
||||
func (c *Client) Write(b []byte) error {
|
||||
_, err := c.conn.Write(b)
|
||||
if err != nil {
|
||||
c.conn.Close()
|
||||
c.Server.onDisconnect(c, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) Conn() net.Conn {
|
||||
return c.conn
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
3
src/tcp_server/go.mod
Normal file
3
src/tcp_server/go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module internal/tcp_server
|
||||
|
||||
go 1.14
|
||||
56
src/tcp_server/server.go
Normal file
56
src/tcp_server/server.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
package tcp_server
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func New(address string) *server {
|
||||
|
||||
server := &server{
|
||||
address: address,
|
||||
}
|
||||
|
||||
server.OnConnect(func(c *Client) {})
|
||||
server.OnMessage(func(c *Client, message string) {})
|
||||
server.OnDisconnect(func(c *Client, err error) {})
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
// Called when a client connects
|
||||
func (s *server) OnConnect(callback func(c *Client)) {
|
||||
s.onConnect = callback
|
||||
}
|
||||
|
||||
// Called the server gets a message from a client.
|
||||
func (s *server) OnMessage(callback func(c *Client, message string)) {
|
||||
s.onMessage = callback
|
||||
}
|
||||
|
||||
// Called when a connection is closed.
|
||||
func (s *server) OnDisconnect(callback func(c *Client, err error)) {
|
||||
s.onDisconnect = callback
|
||||
}
|
||||
|
||||
func (s *server) Listen() error {
|
||||
|
||||
sock, err := net.Listen("tcp", s.address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sock.Close()
|
||||
|
||||
for {
|
||||
conn, _ := sock.Accept()
|
||||
c := &Client{
|
||||
conn: conn,
|
||||
Server: s,
|
||||
Addr: conn.RemoteAddr(),
|
||||
}
|
||||
s.onConnect(c)
|
||||
go c.read()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
19
src/tcp_server/types.go
Normal file
19
src/tcp_server/types.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
package tcp_server
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
conn net.Conn
|
||||
Addr net.Addr
|
||||
Server *server
|
||||
}
|
||||
|
||||
type server struct {
|
||||
address string
|
||||
onConnect func(c *Client)
|
||||
onDisconnect func(c *Client, err error)
|
||||
onMessage func(c *Client, message string)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue