mirror of
https://github.com/eosswedenorg/antelope-api-healthcheck
synced 2026-07-03 11:53:43 +02:00
Initial commit
This commit is contained in:
commit
33c8439604
9 changed files with 270 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
build/
|
||||||
31
Makefile
Normal file
31
Makefile
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
GO = go
|
||||||
|
GOCCFLAGS = -v
|
||||||
|
GOLDFLAGS =
|
||||||
|
PREFIX = /usr/local
|
||||||
|
|
||||||
|
PROGRAM_NAME=eos-api-healthcheck
|
||||||
|
SOURCES=server.go
|
||||||
|
DEPENDANCIES= github.com/firstrow/tcp_server \
|
||||||
|
github.com/liamylian/jsontime/v2 \
|
||||||
|
github.com/imroc/req
|
||||||
|
|
||||||
|
all: build
|
||||||
|
build: build/$(PROGRAM_NAME)
|
||||||
|
|
||||||
|
build/$(PROGRAM_NAME) : $(SOURCES)
|
||||||
|
$(GO) build -o $@ $(GOCCFLAGS) $(GOLDFLAGS) $<
|
||||||
|
|
||||||
|
deps:
|
||||||
|
$(GO) get $(DEPENDANCIES)
|
||||||
|
|
||||||
|
package_deb: build
|
||||||
|
export PACKAGE_NAME="$(PROGRAM_NAME)" \
|
||||||
|
export PACKAGE_VERSION="0.1.0" \
|
||||||
|
export PACKAGE_PREFIX=$(PREFIX:/%=%) \
|
||||||
|
export PACKAGE_PROGRAM="build/$(PROGRAM_NAME)" \
|
||||||
|
&& ./scripts/build_deb.sh
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(GO) clean
|
||||||
|
$(RM) -rf build/
|
||||||
44
eosapi/functions.go
Normal file
44
eosapi/functions.go
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
|
||||||
|
package eosapi;
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
"io/ioutil"
|
||||||
|
"github.com/imroc/req"
|
||||||
|
"github.com/liamylian/jsontime/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var json = v2.ConfigWithCustomTimeFormat;
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
// EOS Api does not specify timezone in timestamps (they are always UTC tho).
|
||||||
|
v2.SetDefaultTimeFormat("2006-01-02T15:04:05", time.UTC);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInfo - Fetches get_info from API
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
func GetInfo(host string, port int) (Info, error) {
|
||||||
|
|
||||||
|
var info Info;
|
||||||
|
|
||||||
|
// Format url.
|
||||||
|
url := fmt.Sprintf("http://%s:%d/v1/chain/get_info", host, port);
|
||||||
|
|
||||||
|
// Go's net.http (that `req` uses) sends the port in the host header.
|
||||||
|
// nodeos api does not like that, so we need to provide our
|
||||||
|
// own Host header with just the host.
|
||||||
|
headers := req.Header{
|
||||||
|
"Host": host,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send HTTP Get request.
|
||||||
|
r, err := req.Get(url, headers)
|
||||||
|
resp := r.Response()
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body);
|
||||||
|
|
||||||
|
// Parse json
|
||||||
|
err = json.Unmarshal(body, &info);
|
||||||
|
return info, err;
|
||||||
|
}
|
||||||
11
eosapi/types.go
Normal file
11
eosapi/types.go
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
package eosapi;
|
||||||
|
|
||||||
|
import "time";
|
||||||
|
|
||||||
|
// get_info format (not all fields).
|
||||||
|
type Info struct {
|
||||||
|
ServerVersion string `json:"server_version"`
|
||||||
|
HeadBlockNum int64 `json:"head_block_num"`
|
||||||
|
HeadBlockTime time.Time `json:"head_block_time"`
|
||||||
|
}
|
||||||
15
haproxy/types.go
Normal file
15
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"
|
||||||
|
)
|
||||||
2
scripts/.gitignore
vendored
Normal file
2
scripts/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pack/
|
||||||
|
*.deb
|
||||||
41
scripts/build_deb.sh
Executable file
41
scripts/build_deb.sh
Executable file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||||
|
|
||||||
|
PACKAGE_DESCRIPTION="HAproxy healthcheck program for EOS API."
|
||||||
|
PACKAGE_TMPDIR="pack"
|
||||||
|
|
||||||
|
# Default to 1 if no release is set.
|
||||||
|
if [[ -z $RELEASE ]]; then
|
||||||
|
RELEASE="1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PACKAGE_FULLNAME="${PACKAGE_NAME}_${PACKAGE_VERSION}-${RELEASE}_amd64"
|
||||||
|
|
||||||
|
rm -fr ${BASE_DIR}/${PACKAGE_TMPDIR}
|
||||||
|
|
||||||
|
# Create debian files.
|
||||||
|
mkdir -p ${BASE_DIR}/${PACKAGE_TMPDIR}/DEBIAN
|
||||||
|
echo "Package: ${PACKAGE_NAME}
|
||||||
|
Version: ${PACKAGE_VERSION}-${RELEASE}
|
||||||
|
Section: introspection
|
||||||
|
Priority: optional
|
||||||
|
Architecture: amd64
|
||||||
|
Homepage: https://github.com/eosswedenorg/eos-api-healthcheck
|
||||||
|
Maintainer: Henrik Hautakoski <henrik@eossweden.org>
|
||||||
|
Description: ${PACKAGE_DESCRIPTION}" &> ${BASE_DIR}/${PACKAGE_TMPDIR}/DEBIAN/control
|
||||||
|
|
||||||
|
cat ${BASE_DIR}/${PACKAGE_TMPDIR}/DEBIAN/control
|
||||||
|
|
||||||
|
# Create service file
|
||||||
|
mkdir -p ${BASE_DIR}/${PACKAGE_TMPDIR}/etc/systemd/system
|
||||||
|
cat ${BASE_DIR}/template.service \
|
||||||
|
| sed "s~{{ DESCRIPTION }}~${PACKAGE_DESCRIPTION}~" \
|
||||||
|
| sed "s~{{ PROGRAM }}~/${PACKAGE_PREFIX}/bin/${PACKAGE_NAME}~" \
|
||||||
|
> ${BASE_DIR}/${PACKAGE_TMPDIR}/etc/systemd/system/${PACKAGE_NAME}.service
|
||||||
|
|
||||||
|
# Copy program
|
||||||
|
mkdir -p ${BASE_DIR}/${PACKAGE_TMPDIR}/${PACKAGE_PREFIX}/bin
|
||||||
|
cp ${BASE_DIR}/../${PACKAGE_PROGRAM} ${BASE_DIR}/${PACKAGE_TMPDIR}/${PACKAGE_PREFIX}/bin/${PACKAGE_NAME}
|
||||||
|
|
||||||
|
fakeroot dpkg-deb --build ${BASE_DIR}/${PACKAGE_TMPDIR} ${BASE_DIR}/${PACKAGE_FULLNAME}.deb
|
||||||
20
scripts/template.service
Normal file
20
scripts/template.service
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
[Unit]
|
||||||
|
Description={{ DESCRIPTION }}
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
# Another Type: forking
|
||||||
|
#User=nanodano
|
||||||
|
#WorkingDirectory=/home/nanodano
|
||||||
|
ExecStart={{ PROGRAM }}
|
||||||
|
Restart=on-failure
|
||||||
|
# Other restart options: always, on-abort, etc
|
||||||
|
|
||||||
|
# The install section is needed to use
|
||||||
|
# `systemctl enable` to start on boot
|
||||||
|
# For a user service that you want to enable
|
||||||
|
# and start automatically, use `default.target`
|
||||||
|
# For system level services, use `multi-user.target`
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
105
server.go
Normal file
105
server.go
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
"strings"
|
||||||
|
"strconv"
|
||||||
|
"./haproxy"
|
||||||
|
"./eosapi"
|
||||||
|
"github.com/firstrow/tcp_server"
|
||||||
|
)
|
||||||
|
|
||||||
|
// check_api - Validates head block time.
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
func check_api(host string, port int) (haproxy.HealthCheckStatus, string) {
|
||||||
|
|
||||||
|
info, err := eosapi.GetInfo(host, port)
|
||||||
|
if err != nil {
|
||||||
|
msg := fmt.Sprintf("%s", err);
|
||||||
|
return haproxy.HealthCheckFailed, msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate head block.
|
||||||
|
now := time.Now().In(time.UTC)
|
||||||
|
diff := now.Sub(info.HeadBlockTime).Seconds()
|
||||||
|
|
||||||
|
if diff > 10.0 {
|
||||||
|
return haproxy.HealthCheckDown,
|
||||||
|
fmt.Sprintf("Taking offline because head block is lagging %.0f seconds", diff)
|
||||||
|
} else if diff < -10.0 {
|
||||||
|
return haproxy.HealthCheckDown,
|
||||||
|
fmt.Sprintf("Taking offline because head block is %.0f seconds into the future", diff)
|
||||||
|
}
|
||||||
|
return haproxy.HealthCheckUp, "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
// argv_listen_addr
|
||||||
|
// Parse listen address from command line.
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
func argv_listen_addr() string {
|
||||||
|
|
||||||
|
var addr string
|
||||||
|
|
||||||
|
argv := os.Args[1:]
|
||||||
|
if len(argv) > 0 {
|
||||||
|
addr = argv[0]
|
||||||
|
} else {
|
||||||
|
addr = "127.0.0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
addr += ":"
|
||||||
|
if len(argv) > 1 {
|
||||||
|
addr += argv[1]
|
||||||
|
} else {
|
||||||
|
addr += "1337"
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// main
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
server := tcp_server.New(argv_listen_addr())
|
||||||
|
|
||||||
|
// TCP Client connect.
|
||||||
|
server.OnNewClient(func(c *tcp_server.Client) {
|
||||||
|
fmt.Println("# Client connected")
|
||||||
|
});
|
||||||
|
|
||||||
|
// TCP Client sends message.
|
||||||
|
server.OnNewMessage(func(c *tcp_server.Client, message string) {
|
||||||
|
var host string
|
||||||
|
var port int = 80
|
||||||
|
|
||||||
|
// Parse host + port.
|
||||||
|
split := strings.Split(strings.TrimSpace(message), ":")
|
||||||
|
|
||||||
|
host = split[0]
|
||||||
|
if len(split) > 1 {
|
||||||
|
p, err := strconv.ParseInt(split[1], 10, 32)
|
||||||
|
if err == nil {
|
||||||
|
port = int(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check api.
|
||||||
|
status, msg := check_api(host, port)
|
||||||
|
|
||||||
|
fmt.Printf("API HealthCheck: %s, %s\n", status, msg)
|
||||||
|
|
||||||
|
// Report status to HAproxy
|
||||||
|
c.Send(fmt.Sprintln(status))
|
||||||
|
c.Close()
|
||||||
|
});
|
||||||
|
|
||||||
|
// TCP Client disconnect.
|
||||||
|
server.OnClientConnectionClosed(func(c *tcp_server.Client, err error) {
|
||||||
|
fmt.Println("# Client disconnected")
|
||||||
|
});
|
||||||
|
|
||||||
|
server.Listen()
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue