mirror of
https://github.com/eosswedenorg/antelope-api-healthcheck
synced 2026-06-18 05:00:03 +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