1
0
Fork 0
mirror of https://github.com/eosswedenorg/eth-healthcheck synced 2026-06-16 05:04:55 +02:00

Inital Commit

This commit is contained in:
Henrik Hautakoski 2021-08-13 12:59:22 +02:00
commit 0d27205c7c
15 changed files with 472 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

21
LICENSE Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,3 @@
module internal/haproxy
go 1.14

15
src/haproxy/types.go Normal file
View 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
View 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
View file

@ -0,0 +1,3 @@
module internal/tcp_server
go 1.14

56
src/tcp_server/server.go Normal file
View 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
View 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)
}