mirror of
https://github.com/eosswedenorg/antelope-api-healthcheck
synced 2026-06-20 09:56:49 +02:00
Compare commits
255 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 77f3fbde85 | |||
| efa0e71f03 | |||
| d37e4c28e7 | |||
| e331b9e2e5 | |||
|
|
067aa54095 | ||
|
|
e2d6f8f2cd | ||
| bc5e9c13eb | |||
| 8e45579eb9 | |||
|
|
c008a3ec1e | ||
|
|
40fb0349ed | ||
| 5ef28b0a06 | |||
|
|
65d3359f1b | ||
| 79e53ff89e | |||
|
|
ee40a7066c | ||
| 84888c7bed | |||
|
|
5e73f6799f | ||
| fa9e87a90f | |||
|
|
828e38797a | ||
| 8a0ab6bb7b | |||
|
|
65fd7cff9c | ||
| a024913244 | |||
| b98164116e | |||
| 4e56ee60fc | |||
| eaeec1645d | |||
| e461d20125 | |||
|
|
18a3a8a3d5 | ||
| 61724eb8d2 | |||
|
|
6fe3d2572a | ||
| 9eda495d4e | |||
|
|
75b8a7bab1 | ||
| 3b497afd76 | |||
| dd76ae2b19 | |||
| d253128d6d | |||
|
|
2299331321 | ||
| 232aa7df65 | |||
|
|
2d6e768e2a | ||
| 80ba31050e | |||
|
|
5dc14716c5 | ||
| c8dd655e36 | |||
| 10f3a4d4be | |||
| 995f6a8629 | |||
|
|
c53cb4fe1f | ||
| 824ae9ca9d | |||
| f377538f4c | |||
| 92e7b89f81 | |||
|
|
320b54cce8 | ||
| 9a34d2647d | |||
| 37c8bbc357 | |||
| 528461ebe4 | |||
| ad596c15ad | |||
| 8ef31fc4e7 | |||
| b8b61d5064 | |||
|
|
03ec91a675 | ||
|
|
b8b44ecf24 | ||
|
|
df0f8b0c43 | ||
| 7f68732eae | |||
| 6b0776937a | |||
| cf2aef149a | |||
| 4b15a56804 | |||
| 8deb0b9bf3 | |||
| a632ba914f | |||
| 5bf880b0d0 | |||
| 318d14f44b | |||
| 9814f07612 | |||
| 9b28e45295 | |||
| 228d7ae731 | |||
| adf9508197 | |||
| 550cabc64b | |||
| d73187ec38 | |||
| 6030981ba5 | |||
| 6b561bdf25 | |||
| 0448ea4781 | |||
| afd79110f9 | |||
| 59399c7cf4 | |||
| f0ab79ef06 | |||
| 919599f86d | |||
| d67864dadf | |||
| dbf59d1305 | |||
| b815bcee44 | |||
| 12bd16399b | |||
| 83fd4826d7 | |||
| 5ed22bfdc6 | |||
| df1476355f | |||
| 4cdae2d3f1 | |||
| aff3737f67 | |||
| 9456b8acbb | |||
| fba9402584 | |||
| 755438408b | |||
| a811f82028 | |||
| 46fe9b3e47 | |||
| b9b1e71bcd | |||
| 1f6811c3b6 | |||
| 852a17bf9e | |||
| 32178dd47f | |||
| dcf035ec6c | |||
| 29ed2d790b | |||
| 62d039deb2 | |||
| a73f1eae8b | |||
| 1f22435dfa | |||
| 1db04c0e76 | |||
| be7a246317 | |||
| d365b5deb1 | |||
| a6b0575cf7 | |||
| 28d4ab3437 | |||
| d752839f0b | |||
| 86e87d8245 | |||
| 1fc8c927aa | |||
| 2caad14e44 | |||
| 64d46ed460 | |||
| 142465ac16 | |||
| 4f77e23797 | |||
| c19dc9a63b | |||
| f0db33f9b1 | |||
| a771b25415 | |||
| 22b10006d7 | |||
| 005ad9fcd0 | |||
| 61cb49f8be | |||
| e0a7a10662 | |||
| 881e7cd760 | |||
| 9109bef803 | |||
| 0f5fdedd91 | |||
| c89da02738 | |||
| d2b8e7d0dc | |||
| bcd9a93a35 | |||
| c48162742f | |||
| a768d89909 | |||
| 2daa5801c1 | |||
| 34814dae05 | |||
| 72bdde0457 | |||
| 8feca959d4 | |||
| 1fb48800a1 | |||
| 2b0b32b5ab | |||
| 1b1f601678 | |||
| e9976fbbee | |||
| bcc704c4c0 | |||
| adb1ad3c6d | |||
| b0e5b455ca | |||
| b09e5ba463 | |||
| a17e2929e3 | |||
| 9102240837 | |||
| 0b463e5111 | |||
| d8d872adae | |||
| 6448aeb0f7 | |||
| c27abb5ed9 | |||
| cb8eec6ff6 | |||
| c168e263df | |||
| 15ff583035 | |||
| b41fb21f6a | |||
| d8c8c14edc | |||
| 9fcffe375a | |||
| 7b78d2632b | |||
| 39927a2441 | |||
| 6e8ffe718e | |||
| 0081f86f0d | |||
| 8079962be8 | |||
| 8d2c1c8fa3 | |||
| 3ab2db7cb3 | |||
| 7f387680ec | |||
| aa98134a1e | |||
| 3568e7c908 | |||
| 818cf7e7e5 | |||
| 97d9e662ce | |||
| 76e50d1801 | |||
| 311d9b34cb | |||
| 704965b9cc | |||
| da28a68943 | |||
| e36913cc92 | |||
| 7fe06a144a | |||
| 822bfaa16e | |||
| da87724920 | |||
| 35b81cdcdc | |||
| 3e9bf2aa75 | |||
| f5ff3480e0 | |||
| 8f81047e02 | |||
| ce1ad75649 | |||
| 9558aea0a8 | |||
| c2cc60dbfb | |||
| f77edfe321 | |||
| bc6f857c1e | |||
| 0349999540 | |||
| edb063722b | |||
| bda40bf1d5 | |||
| 11c1e8afcd | |||
| 566071c997 | |||
| ad5830ed34 | |||
| 0f4f4ad994 | |||
| d0d32ed740 | |||
| 6de7ca549f | |||
| 22255b9758 | |||
| 843d30bb3d | |||
| c94f274da3 | |||
| 47077aa84c | |||
| da4694eb6b | |||
| e2d3e3e59f | |||
| 189b4f2207 | |||
| d2db6ea4b3 | |||
| 4b3ae5431c | |||
| b67b5d6b7e | |||
| 53bc1cfbd8 | |||
| 83edcac967 | |||
| 08e7041246 | |||
| d33abd05f6 | |||
| e44a5ab86e | |||
| fe1beaa616 | |||
| c2e0bef26e | |||
| bd412bb0eb | |||
| 8b86a5d34b | |||
| 44c1d690ef | |||
| 2832b50fe7 | |||
| b2c6589408 | |||
| 14d833e174 | |||
| 0e7ed54c1a | |||
| 4653790df4 | |||
| d4cb1a92f9 | |||
| 6907b3ce75 | |||
| def3b41842 | |||
| 4dd19df65b | |||
| 1577707b7d | |||
| 7043d67945 | |||
| 6f8a3db85e | |||
| 792632d0e5 | |||
| fd04b3364f | |||
| 0001c28ca7 | |||
| 1f2252b2b5 | |||
| 787adca5bd | |||
| 4a7d74594e | |||
| f63723a6f8 | |||
| f4a39783b1 | |||
| 3fce1f558b | |||
| c8361f6a44 | |||
| f98e2ed794 | |||
| e5ddffbc5a | |||
| d3d0fb11d7 | |||
| c8a128b657 | |||
| ec705687bc | |||
| ec44e35488 | |||
| fa6c0bf8a1 | |||
| 816a38acae | |||
| a550f67dca | |||
| 7f45b746c1 | |||
| bfb74e8e48 | |||
| 47ec8ae86e | |||
| b8bc300c70 | |||
| 838f199b30 | |||
| 43b3b36d09 | |||
| f16dced21c | |||
| 513beee79a | |||
| ccd6cb46d2 | |||
| 589e2ad679 | |||
| 14c4e3764c | |||
| 890ea537c6 | |||
| 83a2ddf2ed | |||
| da8a53aa3c | |||
| 5d27bf0ad2 | |||
| 303995ffda |
64 changed files with 3050 additions and 959 deletions
78
.github/workflows/release.yml
vendored
78
.github/workflows/release.yml
vendored
|
|
@ -7,19 +7,26 @@ on:
|
|||
jobs:
|
||||
cross-compile:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ linux, freebsd ]
|
||||
arch: [ 386, amd64, arm, arm64 ]
|
||||
name: Crosscompile - ${{matrix.os}}-${{matrix.arch}}
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: compile
|
||||
id: compile
|
||||
run: |
|
||||
./compile.sh --target ${{matrix.os}} -a ${{matrix.arch}}
|
||||
FILE=$(find build -type f | head -1)
|
||||
echo "::set-output name=version::$(sed -n 's/.*PROGRAM_VERSION\s*=\s*//p' Makefile)"
|
||||
echo "::set-output name=filename::$FILE"
|
||||
echo "::set-output name=name::$(basename $FILE)"
|
||||
echo "::set-output name=mime::$(file -bi $FILE)"
|
||||
|
|
@ -30,56 +37,93 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_name: ${{ steps.compile.outputs.name }}-${{matrix.os}}-${{matrix.arch}}
|
||||
asset_name: ${{ steps.compile.outputs.name }}-${{steps.compile.outputs.version}}-${{matrix.os}}-${{matrix.arch}}
|
||||
asset_path: ${{ steps.compile.outputs.filename }}
|
||||
asset_content_type: ${{ steps.compile.outputs.mime }}
|
||||
|
||||
package-ubuntu:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-18.04, ubuntu-20.04 ]
|
||||
os: [ ubuntu-20.04 ]
|
||||
name: Package - ${{matrix.os}}
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: compile
|
||||
run: ./compile.sh
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install build-essential fakeroot debhelper
|
||||
|
||||
- name: Package
|
||||
id: package
|
||||
run: |
|
||||
./package.sh
|
||||
FILE=$(ls build/*.deb | head -1)
|
||||
echo "::set-output name=filename::$FILE"
|
||||
echo "::set-output name=name::$(basename $FILE)"
|
||||
make package_debian
|
||||
DEB_FILE=$(ls ../*.deb | head -1)
|
||||
echo "::set-output name=deb_filename::$DEB_FILE"
|
||||
echo "::set-output name=deb_name::$(basename $DEB_FILE)"
|
||||
CH_FILE=$(ls ../*.changes | head -1)
|
||||
echo "::set-output name=ch_filename::$CH_FILE"
|
||||
echo "::set-output name=ch_name::$(basename $CH_FILE)"
|
||||
INFO_FILE=$(ls ../*.buildinfo | head -1)
|
||||
echo "::set-output name=info_filename::$INFO_FILE"
|
||||
echo "::set-output name=info_name::$(basename $INFO_FILE)"
|
||||
|
||||
- name: Upload
|
||||
- name: Upload (package)
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_name: ${{ steps.package.outputs.name }}
|
||||
asset_path: ${{ steps.package.outputs.filename }}
|
||||
asset_name: ${{ steps.package.outputs.deb_name }}
|
||||
asset_path: ${{ steps.package.outputs.deb_filename }}
|
||||
asset_content_type: application/x-deb
|
||||
|
||||
- name: Upload (.changes)
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_name: ${{ steps.package.outputs.ch_name }}
|
||||
asset_path: ${{ steps.package.outputs.ch_filename }}
|
||||
asset_content_type: text/plain
|
||||
|
||||
- name: Upload (buildinfo)
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ github.event.release.upload_url }}
|
||||
asset_name: ${{ steps.package.outputs.info_name }}
|
||||
asset_path: ${{ steps.package.outputs.info_filename }}
|
||||
asset_content_type: text/plain
|
||||
|
||||
package-freebsd:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [ 386, amd64, arm, arm64 ]
|
||||
name: Package - FreeBSD (${{matrix.arch}})
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: compile
|
||||
run: ./compile.sh --target=freebsd -a ${{matrix.arch}}
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Package
|
||||
id: package
|
||||
run: |
|
||||
./package.sh
|
||||
GOARCH=${{matrix.arch}} make package_freebsd
|
||||
FILE=$(ls build/*.tar.gz | head -1)
|
||||
echo "::set-output name=filename::$FILE"
|
||||
echo "::set-output name=name::$(basename $FILE)"
|
||||
|
|
|
|||
40
.github/workflows/test.yml
vendored
Normal file
40
.github/workflows/test.yml
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
name: Test
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
cross-compile:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ linux, freebsd ]
|
||||
arch: [ 386, amd64, arm, arm64 ]
|
||||
name: Crosscompile - ${{matrix.os}}-${{matrix.arch}}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: compile
|
||||
id: compile
|
||||
run: |
|
||||
./compile.sh --target ${{matrix.os}} -a ${{matrix.arch}}
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1 +1,3 @@
|
|||
*.swp
|
||||
.vscode/
|
||||
build/
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020-2022 Sw/eden
|
||||
Copyright (c) 2020-2023 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
|
||||
|
|
|
|||
53
Makefile
53
Makefile
|
|
@ -1,32 +1,47 @@
|
|||
|
||||
GO = go
|
||||
GOCCFLAGS = -v
|
||||
GOLDFLAGS = -ldflags="-s -w"
|
||||
PREFIX = /usr/local
|
||||
PROGRAM_NAME = antelope-api-healthcheck
|
||||
export PROGRAM_VERSION = 1.4.6
|
||||
|
||||
PROGRAM_NAME=eosio-api-healthcheck
|
||||
SOURCES=src/main.go src/server.go
|
||||
GO = go
|
||||
PREFIX = /usr/local
|
||||
export GOOS = $(shell $(GO) env GOOS)
|
||||
export GOARCH = $(shell $(GO) env GOARCH)
|
||||
GOBUILDFLAGS = -v -ldflags='-v -s -w -X main.VersionString=$(PROGRAM_VERSION)'
|
||||
|
||||
.PHONY: all build/$(PROGRAM_NAME) clean
|
||||
DPKG_BUILDPACKAGE = dpkg-buildpackage
|
||||
DPKG_BUILDPACKAGE_FLAGS = -b -uc
|
||||
|
||||
.PHONY: all build/$(PROGRAM_NAME) build/antelope-v1-mock-server clean package_debian
|
||||
all: build
|
||||
build: build/$(PROGRAM_NAME)
|
||||
|
||||
build/$(PROGRAM_NAME) : $(SOURCES)
|
||||
$(GO) build -o $@ $(GOCCFLAGS) $(GOLDFLAGS) $^
|
||||
$(GO) env > build/.buildinfo
|
||||
$(GO) build -o $@ $(GOBUILDFLAGS) cmd/antelope-api-healtcheck/main.go
|
||||
|
||||
info-file :
|
||||
echo PACKAGE_NAME=\"$(PROGRAM_NAME)\" "\n"\
|
||||
PACKAGE_DESCRIPTION=\"HAproxy healthcheck program for EOSIO API.\" "\n"\
|
||||
PACKAGE_VERSION=\"1.2.2\" "\n"\
|
||||
PACKAGE_PREFIX=\"$(PREFIX:/%=%)\" "\n"\
|
||||
PACKAGE_PROGRAM=\"build/$(PROGRAM_NAME)\" > scripts/pkg_info
|
||||
build/antelope-v1-mock-server:
|
||||
$(GO) build -o $@ $(GOBUILDFLAGS) cmd/antelope-v1-mock-server/main.go
|
||||
|
||||
package_deb: info-file
|
||||
./scripts/build.sh deb $(realpath build)
|
||||
test-utils: build/antelope-v1-mock-server
|
||||
|
||||
package_freebsd: info-file
|
||||
./scripts/build.sh freebsd $(realpath build)
|
||||
test:
|
||||
$(GO) test -v ./...
|
||||
|
||||
coverage:
|
||||
$(GO) test -cover -v ./...
|
||||
|
||||
install: build
|
||||
PREFIX=$(PREFIX) DESTDIR=$(DESTDIR) scripts/install.sh $(GOOS)
|
||||
|
||||
package:
|
||||
PKGROOT=$(DESTDIR) BUILDDIR=$(realpath build) scripts/package.sh $(PKGTYPE)
|
||||
|
||||
package_debian:
|
||||
$(DPKG_BUILDPACKAGE) $(DPKG_BUILDPACKAGE_FLAGS)
|
||||
|
||||
package_freebsd: PKGTYPE = freebsd
|
||||
package_freebsd: GOOS = freebsd
|
||||
package_freebsd: DESTDIR = build/freebsdroot
|
||||
package_freebsd: install package
|
||||
|
||||
clean:
|
||||
$(GO) clean
|
||||
|
|
|
|||
38
README.md
38
README.md
|
|
@ -1,15 +1,18 @@
|
|||
# EOSIO API Healthcheck for HAProxy
|
||||
# Antelope API Healthcheck for HAProxy
|
||||
|
||||
This program implements EOSIO healthcheck for HAProxy over TCP.
|
||||
[](https://github.com/eosswedenorg/antelope-api-healthcheck/actions/workflows/test.yml)
|
||||
[](https://goreportcard.com/report/github.com/eosswedenorg/antelope-api-healthcheck)
|
||||
|
||||
This program implements Antelope healthcheck for HAProxy over TCP.
|
||||
|
||||
## Compiling
|
||||
|
||||
You will need go-lang version `1.14` or later to compile the source.
|
||||
You will need golang version `1.16` or later to compile the source.
|
||||
|
||||
compile with `compile.sh` script
|
||||
|
||||
```sh
|
||||
$ ./compile.sh
|
||||
./compile.sh
|
||||
```
|
||||
|
||||
Execute `./compile.sh --help` to see all available flags to crosscompile for different systems/architectures.
|
||||
|
|
@ -27,32 +30,31 @@ The protocol is simple and has 4 rules.
|
|||
3. Each parameter inside a `Request` is separated by `|`
|
||||
4. Each response contains exactly one `status code` (see below)
|
||||
|
||||
|
||||
### Request
|
||||
|
||||
The following parameters are supported in a request and are ordered from
|
||||
first to last below:
|
||||
|
||||
| # | Name | Required | Description |
|
||||
| - | ---------- | ----------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| 1 | api | Yes | Type of API to check against, `v1` = standard, `v2` = Hyperion, `contract` = eosio-contract-api |
|
||||
| 2 | url | Yes (port default `80`) | http url to the api. `http(s)://<ip-or-domain>(:<port>)` |
|
||||
| 3 | num_blocks | No (default `10`) | Number of blocks the api can drift before reported `down` |
|
||||
| 4 | host | No (default from `url`) | Value to send in the `HTTP Host Header` to the API |
|
||||
| # | Name | Required | Description |
|
||||
| - | ---------- | ------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| 1 | api | Yes | Type of API to check against,`v1` = standard, `v2` = Hyperion, `atomic` = atomicassets |
|
||||
| 2 | url | Yes (port default `80`) | http url to the api.`http(s)://<ip-or-domain>(:<port>)` |
|
||||
| 3 | num_blocks | No (default `10`) | Number of blocks the api can drift before reported `down` |
|
||||
| 4 | host | No (default from `url`) | Value to send in the `HTTP Host Header` to the API |
|
||||
|
||||
### Response
|
||||
|
||||
The api can respond with exactly one `status code`.
|
||||
See [HAproxy documentation](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#5.2-agent-check) for more information
|
||||
|
||||
| code | Description |
|
||||
| --------- | ---------------------------------------------------------- |
|
||||
| `up` | Api is healthy |
|
||||
| `down` | Api is not healthy |
|
||||
| `failed` | The program failed to read the status from the api. |
|
||||
| `maint` | Api is set in maintenance mode (not used) |
|
||||
| code | Description |
|
||||
| ----------- | ------------------------------------------------------------ |
|
||||
| `up` | Api is healthy |
|
||||
| `down` | Api is not healthy |
|
||||
| `fail` | The program failed to read the status from the api. |
|
||||
| `maint` | Api is set in maintenance mode (not used) |
|
||||
| `ready` | Api is ready again after being in `maint` state (not used) |
|
||||
| `stopped` | Api has been stopped manually (not used) |
|
||||
| `stopped` | Api has been stopped manually (not used) |
|
||||
|
||||
## Author
|
||||
|
||||
|
|
|
|||
208
cmd/antelope-api-healtcheck/main.go
Normal file
208
cmd/antelope-api-healtcheck/main.go
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/eosswedenorg-go/pid"
|
||||
"github.com/eosswedenorg/antelope-api-healthcheck/internal/server"
|
||||
"github.com/eosswedenorg/antelope-api-healthcheck/internal/utils"
|
||||
log "github.com/inconshreveable/log15"
|
||||
"github.com/pborman/getopt/v2"
|
||||
)
|
||||
|
||||
// Command line flags
|
||||
// ---------------------------------------------------------
|
||||
|
||||
var (
|
||||
logFile string
|
||||
pidFile string
|
||||
)
|
||||
|
||||
// Global variables
|
||||
// ---------------------------------------------------------
|
||||
|
||||
// Version string, should be updated by the go linker (by passing "-X main.VersionString=value" to the linker)
|
||||
// see: https://pkg.go.dev/cmd/link
|
||||
var VersionString string = "-"
|
||||
|
||||
// File descriptor to the current log file.
|
||||
var logfd *os.File
|
||||
|
||||
var (
|
||||
logfmt log.Format
|
||||
logger log.Logger
|
||||
|
||||
// TCP Server
|
||||
srv *server.Server
|
||||
)
|
||||
|
||||
// argv_listen_addr
|
||||
// Parse listen address from command line.
|
||||
//
|
||||
// ---------------------------------------------------------
|
||||
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 += "1337"
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
func setLogFile() {
|
||||
// Open file
|
||||
fd, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
|
||||
// Try close if old descriptor is defined.
|
||||
if logfd != nil {
|
||||
if err = logfd.Close(); err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Update variable and set log writer.
|
||||
logfd = fd
|
||||
logger.SetHandler(log.StreamHandler(logfd, logfmt))
|
||||
}
|
||||
|
||||
// signalEventLoop()
|
||||
// Initialize event channel for OS signals
|
||||
// and runs an event loop.
|
||||
//
|
||||
// ---------------------------------------------------------
|
||||
func signalEventLoop() {
|
||||
// Setup a channel
|
||||
sig_ch := make(chan os.Signal, 1)
|
||||
|
||||
// subscribe to SIGHUP signal.
|
||||
signal.Notify(sig_ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// Event loop
|
||||
var run bool = true
|
||||
for run {
|
||||
// Block until we get a signal.
|
||||
sig := <-sig_ch
|
||||
|
||||
l := logger.New("signal", sig)
|
||||
|
||||
switch sig {
|
||||
case syscall.SIGINT, syscall.SIGTERM:
|
||||
l.Info("Program was asked to terminate.")
|
||||
run = false
|
||||
|
||||
// Tell the server to close.
|
||||
err := srv.Close()
|
||||
if err != nil {
|
||||
l.Error("Failed to close server", "error", err)
|
||||
}
|
||||
// SIGHUP is sent when logfile is rotated.
|
||||
case syscall.SIGHUP:
|
||||
msg := "Logfile was rotated: "
|
||||
|
||||
if logfd != nil {
|
||||
setLogFile()
|
||||
msg += "Filedescriptor was updated"
|
||||
} else {
|
||||
msg += "No Filedescriptor to update (most likely uses standard out/err streams)"
|
||||
}
|
||||
|
||||
l.Info(msg)
|
||||
default:
|
||||
l.Warn("Unknown signal")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// main
|
||||
//
|
||||
// ---------------------------------------------------------
|
||||
func main() {
|
||||
var version bool
|
||||
var usage bool
|
||||
var logFormatter *string
|
||||
// Set default timeout to 2 sec
|
||||
// as haproxy "inter" parameter to healthcheck is set to 2s per default.
|
||||
req_timeout := time.Second * 2
|
||||
|
||||
logger = log.Root()
|
||||
|
||||
// Command line parsing
|
||||
getopt.SetParameters("[ip] [port]")
|
||||
getopt.FlagLong(&usage, "help", 'h', "Print this help text")
|
||||
getopt.FlagLong(&version, "version", 'v', "Print version")
|
||||
getopt.FlagLong(&logFile, "log", 'l', "Path to log file", "file")
|
||||
getopt.FlagLong(&pidFile, "pid", 'p', "Path to pid file", "file")
|
||||
getopt.FlagLong(&req_timeout, "timeout", 't', "Set the maximum time before a request times out, valid prefixes are 's','ms','us'", "duration")
|
||||
logFormatter = getopt.EnumLong("log-format", 0, []string{"term", "logfmt", "json", "json-pretty"}, "", "Log format to use: term,logfmt,json,json-pretty")
|
||||
|
||||
getopt.Parse()
|
||||
|
||||
if usage {
|
||||
getopt.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
if version {
|
||||
fmt.Printf("Version: %s\n", VersionString)
|
||||
return
|
||||
}
|
||||
|
||||
logfmt = utils.ParseLogFormatter(*logFormatter)
|
||||
|
||||
// Open logfile.
|
||||
if len(logFile) > 0 {
|
||||
setLogFile()
|
||||
} else {
|
||||
logger.SetHandler(log.StreamHandler(os.Stdout, logfmt))
|
||||
}
|
||||
|
||||
logger.Info("Process is starting", "pid", pid.Get())
|
||||
|
||||
if len(pidFile) > 0 {
|
||||
logger.Info("Writing pidfile", "file", pidFile)
|
||||
err := pid.Save(pidFile)
|
||||
if err != nil {
|
||||
logger.Error("Failed to write pidfile", "msg", err)
|
||||
}
|
||||
}
|
||||
|
||||
if req_timeout.Seconds() < 2 {
|
||||
// Dont alow anything below 2 seconds. that is abit aggressive.
|
||||
logger.Warn("Request timeout is less than the minimum. Setting it to 2 seconds", "req_timeout", req_timeout)
|
||||
req_timeout = time.Second * 2
|
||||
} else if req_timeout.Minutes() > 1.0 {
|
||||
// Anything more than 1 min is too long :)
|
||||
logger.Warn("Request timeout is more than the maximum. Setting it to 1 minute", "req_timeout", req_timeout)
|
||||
req_timeout = time.Minute
|
||||
}
|
||||
|
||||
// Create server
|
||||
srv = server.New(argv_listen_addr(), server.WithTick(time.Second*10), server.WithTimeout(req_timeout))
|
||||
|
||||
// Run signal event loop in its own goroutine
|
||||
go signalEventLoop()
|
||||
|
||||
// Run server
|
||||
if err := srv.Run(); err != nil {
|
||||
logger.Error("Server error", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
78
cmd/antelope-v1-mock-server/main.go
Normal file
78
cmd/antelope-v1-mock-server/main.go
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/eosswedenorg-go/leapapi"
|
||||
)
|
||||
|
||||
var (
|
||||
listen_host = flag.String("h,host", "localhost", "Host to listen on.")
|
||||
listen_port = flag.Int("p", 3333, "Port to listen to.")
|
||||
delay = flag.Int("d", 0, "Delays responses randomly between 0 and int seconds.")
|
||||
)
|
||||
|
||||
func getInfo(w http.ResponseWriter, r *http.Request) {
|
||||
current_time := time.Now()
|
||||
|
||||
info := leapapi.Info{
|
||||
ServerVersion: "c83ea9c2",
|
||||
ServerVersionString: "0.0.0-debug",
|
||||
ServerFullVersionString: "0.0.0-debug-c83ea9c21f60670a00627319ebbd233e6bb4f84904dbcfc894242ba38b2761d4",
|
||||
HeadBlockNum: 1000,
|
||||
HeadBlockID: "168d2cf232ca78e94d57a86301e35f110b6016358e05d49ab822df0a8aa988ea",
|
||||
HeadBlockTime: current_time.UTC(),
|
||||
ChainID: "1045fa26e1c5be590ae6114e73331152671f13c87eee60a2171387dcbc44da88",
|
||||
HeadBlockProducer: "debugproducer",
|
||||
LastIrreversableBlockNum: 900,
|
||||
LastIrreversableBlockID: "5149254b9b6fd61a02403ebe3b45ade57642ed473295f33e2184e56966370a1f",
|
||||
LastIrreversableBlockTime: current_time.Add(time.Second * -5).UTC(),
|
||||
VirtualBlockCPULimit: 4000,
|
||||
VirtualBlockNETLimit: 5000,
|
||||
BlockCPULimit: 8000,
|
||||
BlockNETLimit: 2000,
|
||||
TotalCPUWeight: 60488453825414473,
|
||||
TotalNETWeight: 101764028077814346,
|
||||
ForkDBHeadBlockID: "7544799d7c2f511368cb94adc65223e1e2cc4cf9639ba07eef2421486a8dbfe5",
|
||||
ForkDBHeadBlockNum: 100,
|
||||
}
|
||||
|
||||
if *delay > 0 {
|
||||
sleep_for := rand.Intn(*delay)
|
||||
time.Sleep(time.Second * time.Duration(sleep_for))
|
||||
}
|
||||
|
||||
payload, err := leapapi.Json().Marshal(&info)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(time.Now().Unix())
|
||||
|
||||
flag.Parse()
|
||||
|
||||
http.HandleFunc("/v1/chain/get_info", getInfo)
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", *listen_host, *listen_port)
|
||||
|
||||
fmt.Println("Listening on:", addr)
|
||||
|
||||
err := http.ListenAndServe(addr, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
7
debian/.gitignore
vendored
Normal file
7
debian/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.debhelper/*
|
||||
*.debhelper
|
||||
*.log
|
||||
*-stamp
|
||||
*.substvars
|
||||
files
|
||||
pkgroot/*
|
||||
192
debian/changelog
vendored
Normal file
192
debian/changelog
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
antelope-api-healthcheck (1.4.6) unstable; urgency=medium
|
||||
|
||||
* Go Packages
|
||||
- Update github.com/panjf2000/gnet/v2 from 2.2.9 to 2.3.1
|
||||
- Update github.com/stretchr/testify from 1.8.3 to 1.8.4
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Tue, 25 Jul 2023 17:28:29 +0200
|
||||
|
||||
antelope-api-healthcheck (1.4.5) unstable; urgency=medium
|
||||
|
||||
Maintenance release, dependancies updates.
|
||||
|
||||
* Go Packages
|
||||
- Update github.com/panjf2000/gnet/v2 from 2.2.6 to 2.2.9
|
||||
- Update github.com/stretchr/testify from 1.8.2 to 1.8.3
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Tue, 23 May 2023 17:19:42 +0200
|
||||
|
||||
antelope-api-healthcheck (1.4.4) unstable; urgency=medium
|
||||
|
||||
[ Security ]
|
||||
* CVE-2022-41723 - Uncontrolled Resource Consumption
|
||||
Fixed by updating golang.org/x/net to patched version 0.7.0
|
||||
|
||||
[ Misc ]
|
||||
|
||||
* Go Packages
|
||||
- Update golang.org/x/net from 0.4.0 to 0.7.0
|
||||
- Update github.com/eosswedenorg-go/atomicasset from v0.1.1-0.20230206134606-4577244fa67a to v0.1.1
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Tue, 04 Apr 2023 07:04:24 +0200
|
||||
|
||||
antelope-api-healthcheck (1.4.3) unstable; urgency=medium
|
||||
|
||||
[ Misc ]
|
||||
|
||||
* Typo fix.
|
||||
|
||||
* Go Packages
|
||||
- github.com/stretchr/testify from 1.8.1 to 1.8.2
|
||||
- github.com/inconshreveable/log15 from 0.0.0-20221122034931-555555054819 to 3.0.0-testing.5+incompatible
|
||||
- github.com/panjf2000/gnet/v2 from 2.2.4 to 2.2.6
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Tue, 04 Apr 2023 06:07:05 +0200
|
||||
|
||||
antelope-api-healthcheck (1.4.2) unstable; urgency=medium
|
||||
|
||||
[ Improvements ]
|
||||
* API Check
|
||||
- Default value for api timeout changed from 30s to 2s.
|
||||
This makes sense because haproxy "inter" parameter is set to 2s per default.
|
||||
|
||||
* Linux install script
|
||||
- Now installs `syslog-ng` configuration file.
|
||||
|
||||
* Misc
|
||||
- Program now exits with correct status code "1" when an error occures.
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Tue, 14 Feb 2023 09:11:43 +0100
|
||||
|
||||
antelope-api-healthcheck (1.4.1) unstable; urgency=medium
|
||||
|
||||
[ Bugfixes ]
|
||||
|
||||
* Fixing a bug where TCP connections were not closed after response was written.
|
||||
|
||||
[ Improvements ]
|
||||
|
||||
* API Check
|
||||
- timeouts can now be configurable.
|
||||
- "duration" and "duration_us" fields added to log row, these
|
||||
values represents the api check duration.
|
||||
|
||||
* CLI
|
||||
- new parameter `-t`, `--timeout` that specify the maximum duration of api checks.
|
||||
|
||||
[ Misc ]
|
||||
|
||||
* Go Packages
|
||||
- Update github.com/panjf2000/gnet/v2 to v2.2.4
|
||||
- Update github.com/eosswedenorg-go/leapapi to v0.2.3
|
||||
- Update github.com/eosswedenorg-go/atomicassets to v0.1.1-0.20230206134606-4577244fa67a
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Tue, 07 Feb 2023 09:29:40 +0100
|
||||
|
||||
antelope-api-healthcheck (1.4.0) unstable; urgency=medium
|
||||
|
||||
* Using github.com/panjf2000/gnet as tcp server library instead of github.com/eosswedenorg-go/tcp_server
|
||||
|
||||
[ Misc ]
|
||||
|
||||
* Go Packages
|
||||
- Update github.com/eosswedenorg-go/haproxy to v1.0.1
|
||||
- Update github.com/eosswedenorg-go/atomicasset to v0.1.0
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Wed, 04 Jan 2023 14:03:41 +0100
|
||||
|
||||
antelope-api-healthcheck (1.3.1) unstable; urgency=medium
|
||||
|
||||
* Upgraded to github.com/eosswedenorg-go/leapapi@v0.2.1
|
||||
That contains a bugfix for HTTP Host Header being sent without port.
|
||||
This caused some antelope api's with `http-validate-host` enabled
|
||||
to respond with `400 Bad Request` as the header and url did not match.
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Mon, 12 Dec 2022 16:19:26 +0100
|
||||
|
||||
antelope-api-healthcheck (1.3.0) unstable; urgency=medium
|
||||
|
||||
Project and binary renamed from eosio-api-healthcheck to antelope-api-healthcheck
|
||||
|
||||
[ BREAKING Change: Config files ]
|
||||
|
||||
* Debian /etc/sysconfig/eosio-api-healthcheck
|
||||
- File renamed to /etc/sysconfig/antelope-api-healthcheck
|
||||
- EOSIO_API_HEALTCHECK_OPTS changed to ANTELOPE_API_HEALTCHECK_OPTS
|
||||
|
||||
* FreeBSD /etc/rc.conf
|
||||
- eosio_api_healthcheck_args changed to antelope_api_healthcheck_args
|
||||
- eosio_api_healthcheck_logfile changed to antelope_api_healthcheck_logfile
|
||||
|
||||
[ BREAKING Change: API ]
|
||||
|
||||
* Requests using "contract" as api is invalid from
|
||||
this release and should use "atomic" instead.
|
||||
|
||||
For example: "contract|https://api.domain.com" should be changed to "atomic|https://api.domain.com"
|
||||
|
||||
[ Misc ]
|
||||
|
||||
* Go Packages
|
||||
- Upgrade github.com/eosswedenorg-go/eosapi to github.com/eosswedenorg-go/leapapi v0.2.0
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Fri, 25 Nov 2022 12:40:02 +0100
|
||||
|
||||
eosio-api-healthcheck (1.2.6) unstable; urgency=medium
|
||||
|
||||
* Go Packages
|
||||
- Upgrade github.com/stretchr/testify to v1.8.1
|
||||
- Upgrade github.com/eosswedenorg-go/eosapi to v0.1.3
|
||||
- Upgrade github.com/inconshreveable/log15 to v0.0.0-20221122034931-555555054819
|
||||
- Upgrade github.com/eosswedenorg-go/tcp_server to v0.2.1
|
||||
|
||||
* Eosio v1/v2 API's
|
||||
- HTTP Errors are now handled by `eosapi`
|
||||
This changes the log message abit and will report "Fail" instead of "Down" to haproxy.
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Thu, 24 Nov 2022 14:57:00 +0100
|
||||
|
||||
eosio-api-healthcheck (1.2.5) unstable; urgency=medium
|
||||
|
||||
* Logging
|
||||
- Add runtime support via "--log-format" cli flag to change the logging format.
|
||||
Supported formats are: term, logfmt, json, json-pretty
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Fri, 21 Oct 2022 13:42:23 +0200
|
||||
|
||||
eosio-api-healthcheck (1.2.4) unstable; urgency=medium
|
||||
|
||||
* Go Version
|
||||
- Project now requires golang v1.16 or later to build.
|
||||
|
||||
* Go Packages
|
||||
- Upgrade github.com/eosswedenorg-go/pid to v1.0.1
|
||||
- Upgrade github.com/mattn/go-colorable to v0.1.13
|
||||
- Upgrade github.com/eosswedenorg-go/eosapi to v0.1.1
|
||||
- Upgrade github.com/eosswedenorg-go/eos-contract-api-client to v0.0.0-20220221105418-dc591fcc0dc5
|
||||
- Upgrade github.com/eosswedenorg-go/tcp_server to v0.2.0
|
||||
|
||||
* Testing
|
||||
- Improve unit tests to catch more bugs.
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Thu, 13 Oct 2022 14:34:04 +0200
|
||||
|
||||
eosio-api-healthcheck (1.2.3) bionic; urgency=medium
|
||||
|
||||
* Signal handling
|
||||
- Act on SIGINT (user input CTRL+C for example)
|
||||
and gracefully shutdown the program.
|
||||
- Act on SIGTERM (sent by systemd for example)
|
||||
and gracefully shutdown the program
|
||||
* Build system
|
||||
- Separate install and packaging from build script into it's own scripts.
|
||||
- Remove the debian package script and use "The debian standard"
|
||||
(and debhelper) to build packages.
|
||||
Files stored under the debian directory defines how the program is
|
||||
built and package The `dpkg-buildpackage` script (and other tools)
|
||||
uses these files to create a package.
|
||||
* Testing: Adding test cases and make github automatically runs test on push.
|
||||
* Go packages
|
||||
- Upgrade to v0.1.1 for github.com/eosswedenorg-go/tcp_server
|
||||
|
||||
-- Henrik Hautakoski <henrik@eossweden.org> Mon, 22 Aug 2022 13:31:39 +0200
|
||||
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
10
|
||||
19
debian/control
vendored
Normal file
19
debian/control
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Source: antelope-api-healthcheck
|
||||
Section: introspection
|
||||
Build-Depends:
|
||||
debhelper (>= 11)
|
||||
Standards-Version: 4.5.0
|
||||
Vcs-Git: https://github.com/eosswedenorg/antelope-api-healthcheck.git
|
||||
Vcs-Browser: https://github.com/eosswedenorg/antelope-api-healthcheck
|
||||
Priority: optional
|
||||
Maintainer: Henrik Hautakoski <henrik@eossweden.org>
|
||||
|
||||
Package: antelope-api-healthcheck
|
||||
Section: introspection
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Homepage: https://github.com/eosswedenorg/antelope-api-healthcheck
|
||||
Description: HAproxy healthcheck program for Leap API.
|
||||
This package provides all the files needed to
|
||||
run the antelope-api-healthcheck TCP Server
|
||||
27
debian/copyright
vendored
Normal file
27
debian/copyright
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: antelope-api-healthcheck
|
||||
Upstream-Contact: Henrik Hautakoski <henrik@eossweden.org>
|
||||
Source: https://github.com/eosswedenorg/antelope-api-healthcheck.git
|
||||
|
||||
Files: *
|
||||
Copyright: 2020-2023 Sw/eden
|
||||
License: MIT
|
||||
|
||||
License: MIT
|
||||
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.
|
||||
15
debian/rules
vendored
Executable file
15
debian/rules
vendored
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
include /usr/share/dpkg/default.mk
|
||||
|
||||
INSTALL_FLAGS := PREFIX=/usr GOOS=linux GOBUILDFLAGS="-v --buildmode=pie -ldflags=\"-v -s -w -X main.VersionString=$(DEB_VERSION)\""
|
||||
|
||||
%:
|
||||
dh $@ -P debian/pkgroot
|
||||
|
||||
override_dh_auto_build:
|
||||
override_dh_auto_install:
|
||||
dh_auto_install --buildsystem=makefile -- $(INSTALL_FLAGS)
|
||||
|
||||
override_dh_usrlocal:
|
||||
override_dh_gconf:
|
||||
1
debian/source/format
vendored
Normal file
1
debian/source/format
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
3.0 (native)
|
||||
64
go.mod
64
go.mod
|
|
@ -1,15 +1,59 @@
|
|||
module github.com/eosswedenorg/eosio-api-healthcheck
|
||||
module github.com/eosswedenorg/antelope-api-healthcheck
|
||||
|
||||
go 1.14
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/eosswedenorg-go/eos-contract-api-client v0.0.0-20220221105418-dc591fcc0dc5
|
||||
github.com/eosswedenorg-go/eosapi v0.1.0
|
||||
github.com/eosswedenorg-go/haproxy v0.1.1
|
||||
github.com/eosswedenorg-go/pid v1.0.0
|
||||
github.com/eosswedenorg-go/tcp_server v0.1.0
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/eosswedenorg-go/atomicasset v0.1.2
|
||||
github.com/eosswedenorg-go/haproxy v1.0.1
|
||||
github.com/eosswedenorg-go/leapapi v0.2.3
|
||||
github.com/eosswedenorg-go/pid v1.0.1
|
||||
github.com/inconshreveable/log15 v3.0.0-testing.5+incompatible
|
||||
github.com/panjf2000/gnet/v2 v2.3.1
|
||||
github.com/pborman/getopt/v2 v2.1.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/eosswedenorg-go/unixtime v0.1.1 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/pprof v0.0.0-20221219190121-3cb0bae90811 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/imroc/req/v3 v3.33.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/liamylian/jsontime/v2 v2.0.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.6.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 // indirect
|
||||
github.com/quic-go/quic-go v0.32.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.10.0 // indirect
|
||||
github.com/sonh/qs v0.6.2 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/net v0.23.0 // indirect
|
||||
golang.org/x/sync v0.2.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/term v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
gopkg.in/guregu/null.v4 v4.0.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
|
|||
175
go.sum
175
go.sum
|
|
@ -1,51 +1,170 @@
|
|||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eosswedenorg-go/eos-contract-api-client v0.0.0-20220221105418-dc591fcc0dc5 h1:Xd6K6u8Bt1s9Szh3TydkkdwOEWBh4ThkHU0rbiO5BAI=
|
||||
github.com/eosswedenorg-go/eos-contract-api-client v0.0.0-20220221105418-dc591fcc0dc5/go.mod h1:o/H/XtSXtaNe2ooXIAcp9vApmy2pY5YyeSl3yWgEnLw=
|
||||
github.com/eosswedenorg-go/eosapi v0.1.0 h1:SnVMx1QGPBZoQknjnAiGzjL6hVfrXPLrOdxoUstYUrk=
|
||||
github.com/eosswedenorg-go/eosapi v0.1.0/go.mod h1:7VrkU30cSqRtGDE6bXygWqMcEhCyWOaC9yVA34QIQzM=
|
||||
github.com/eosswedenorg-go/haproxy v0.1.0 h1:E/9Ptt5upbVoSpkiXwffjapDF0FRLeFePTCAkvA0STQ=
|
||||
github.com/eosswedenorg-go/haproxy v0.1.0/go.mod h1:WnDKkwYbgrpuKUOtUtWlGCgj43DMV+r4VKGdWSTYQeA=
|
||||
github.com/eosswedenorg-go/haproxy v0.1.1 h1:ClOhoK3uGn70KzGCKHdYNvHoU72whfNq3LbqT/lWlOY=
|
||||
github.com/eosswedenorg-go/haproxy v0.1.1/go.mod h1:WnDKkwYbgrpuKUOtUtWlGCgj43DMV+r4VKGdWSTYQeA=
|
||||
github.com/eosswedenorg-go/pid v1.0.0 h1:k1ra19cgWBHnX5gWQq+eUhNHIjT7hdhsYGp+Ovfvd2U=
|
||||
github.com/eosswedenorg-go/pid v1.0.0/go.mod h1:c9nVBDmo8pa3dVjrZjDUN4/DT1ChmfuCWXt6lHsagRc=
|
||||
github.com/eosswedenorg-go/tcp_server v0.1.0 h1:nbXJK4sPSGn3zGPGWR2vdmj+s/Ywp58k7dZ9fuYNZlI=
|
||||
github.com/eosswedenorg-go/tcp_server v0.1.0/go.mod h1:PZd1z/KzNlEpmKtW0Zu9dHJLs5PAMTNR3jPbMGWhZIw=
|
||||
github.com/eosswedenorg-go/atomicasset v0.1.2 h1:bvz5tVVyP8EHSQo9CPFZjdl1lICBisfoRwJv/SURMCs=
|
||||
github.com/eosswedenorg-go/atomicasset v0.1.2/go.mod h1:iRs02PLQmJAFBlukjrReSwti+Ga2Rp2wtnBGQElMVg4=
|
||||
github.com/eosswedenorg-go/haproxy v1.0.1 h1:N9tyQSvEDG9Fq+gBP0b7A8R5iZAqYCms2K6Nvqq8TfE=
|
||||
github.com/eosswedenorg-go/haproxy v1.0.1/go.mod h1:rBXDRd72ifA/IvsZUpW8Q4gR5rbV/4DAuKC/lqDrwWQ=
|
||||
github.com/eosswedenorg-go/leapapi v0.2.3 h1:2qGlP8wzZJCvjM9ol9t2uroCPMpV+wxF7lNl2NdG87M=
|
||||
github.com/eosswedenorg-go/leapapi v0.2.3/go.mod h1:hyfjHswFjcnWMqOb+cYvTrT6l34S80GrFvfOdcYugnM=
|
||||
github.com/eosswedenorg-go/pid v1.0.1 h1:W4AEnnNwb041SpNR1uTZ/KbJ0OTA5eqiqIR1Q5Ah6A0=
|
||||
github.com/eosswedenorg-go/pid v1.0.1/go.mod h1:wiOB/JXGt4YA3+T0j0xmCGSc3Jxzb7Ti/Ftli1fgWu4=
|
||||
github.com/eosswedenorg-go/unixtime v0.1.1 h1:fTNxDtQOKncv/zAc3TzwLQLA/YBVM5nlbsFWVEyMkds=
|
||||
github.com/eosswedenorg-go/unixtime v0.1.1/go.mod h1:knU247oYvgCQD9MLYBdpi7qD1pTRgkSAWr9jbUx3S6c=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/imroc/req v0.3.2 h1:M/JkeU6RPmX+WYvT2vaaOL0K+q8ufL5LxwvJc4xeB4o=
|
||||
github.com/imroc/req v0.3.2/go.mod h1:F+NZ+2EFSo6EFXdeIbpfE9hcC233id70kf0byW97Caw=
|
||||
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/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/google/pprof v0.0.0-20221219190121-3cb0bae90811 h1:wORs2YN3R3ona/CXYuTvLM31QlgoNKHvlCNuArCDDCU=
|
||||
github.com/google/pprof v0.0.0-20221219190121-3cb0bae90811/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/imroc/req/v3 v3.7.6/go.mod h1:3JIicOKEDHfCSYYNLb/ObZNpx64EV5y40VlHMwhUCzU=
|
||||
github.com/imroc/req/v3 v3.33.2 h1:mqphLIo++p+IPYdjgP/Wd5rqXUjKvuEIst2U+EsLIwQ=
|
||||
github.com/imroc/req/v3 v3.33.2/go.mod h1:cZ+7C3L/AYOr4tLGG16hZF90F1WzAdAdzt1xFSlizXY=
|
||||
github.com/inconshreveable/log15 v3.0.0-testing.5+incompatible h1:VryeOTiaZfAzwx8xBcID1KlJCeoWSIpsNbSk+/D2LNk=
|
||||
github.com/inconshreveable/log15 v3.0.0-testing.5+incompatible/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/liamylian/jsontime/v2 v2.0.0 h1:3if2kDW/boymUdO+4Qj/m4uaXMBSF6np9KEgg90cwH0=
|
||||
github.com/liamylian/jsontime/v2 v2.0.0/go.mod h1:UHp1oAPqCBfspokvGmaGe0IAl2IgOpgOgDaKPcvcGGY=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q=
|
||||
github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
|
||||
github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
|
||||
github.com/panjf2000/ants/v2 v2.8.1 h1:C+n/f++aiW8kHCExKlpX6X+okmxKXP7DWLutxuAPuwQ=
|
||||
github.com/panjf2000/gnet/v2 v2.3.1 h1:J7vHkNxwsevVIw3u/6LCXgcnpGBk5iKqhQ2RMblGodc=
|
||||
github.com/panjf2000/gnet/v2 v2.3.1/go.mod h1:Ik5lTy2nmBg9Uvjfcf2KRYs+EXVNOLyxPHpFOFlqu+M=
|
||||
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=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
|
||||
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
|
||||
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/sonh/qs v0.6.2 h1:ao0XNj2hXNdLW9Xk0L8zzEj1s3DjI7bDdG5QU8WUyxY=
|
||||
github.com/sonh/qs v0.6.2/go.mod h1:ywKyX7vSo9R5dfgEQSCZ75tFzNkVUJZyK3/W6qGeHMQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 h1:foEbQz/B0Oz6YIqu/69kfXPYeFQAuuMYFkjaqXzl5Wo=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 h1:5oN1Pz/eDhCpbMbLstvIPa0b/BEQo6g6nwV3pLjfM6w=
|
||||
golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg=
|
||||
gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
72
internal/api/antelope_v1.go
Normal file
72
internal/api/antelope_v1.go
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/eosswedenorg-go/leapapi"
|
||||
"github.com/eosswedenorg/antelope-api-healthcheck/internal/utils"
|
||||
)
|
||||
|
||||
type AntelopeV1 struct {
|
||||
utils.Time
|
||||
client leapapi.Client
|
||||
block_time float64
|
||||
}
|
||||
|
||||
func AntelopeV1Factory(args ApiArguments) ApiInterface {
|
||||
return NewAntelopeV1(args.Url, args.Host, float64(args.NumBlocks/2))
|
||||
}
|
||||
|
||||
func NewAntelopeV1(url string, host string, block_time float64) AntelopeV1 {
|
||||
api := AntelopeV1{
|
||||
client: *leapapi.New(url),
|
||||
block_time: block_time,
|
||||
}
|
||||
|
||||
api.client.Host = host
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
func (e AntelopeV1) LogInfo() LogParams {
|
||||
p := LogParams{
|
||||
"type", "antelope-v1",
|
||||
"url", e.client.Url,
|
||||
}
|
||||
|
||||
if len(e.client.Host) > 0 {
|
||||
p.Add("host", e.client.Host)
|
||||
}
|
||||
|
||||
p.Add("block_time", e.block_time)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (e AntelopeV1) Call(ctx context.Context) (agentcheck.Response, string) {
|
||||
info, err := e.client.GetInfo(ctx)
|
||||
if err != nil {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
return resp, err.Error()
|
||||
}
|
||||
|
||||
// Validate head block.
|
||||
diff := e.GetTime().Sub(info.HeadBlockTime).Seconds()
|
||||
|
||||
if diff > e.block_time {
|
||||
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
|
||||
msg := "Taking offline because head block is lagging %.0f seconds"
|
||||
return resp, fmt.Sprintf(msg, diff)
|
||||
} else if diff < -e.block_time {
|
||||
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
|
||||
msg := "Taking offline because head block is %.0f seconds into the future"
|
||||
return resp, fmt.Sprintf(msg, diff)
|
||||
}
|
||||
return agentcheck.NewStatusResponse(agentcheck.Up), "OK"
|
||||
}
|
||||
171
internal/api/antelope_v1_test.go
Normal file
171
internal/api/antelope_v1_test.go
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAntelopeV1_Factory(t *testing.T) {
|
||||
api := AntelopeV1Factory(ApiArguments{
|
||||
Url: "https://api.v1.example.com",
|
||||
Host: "host.example.com",
|
||||
NumBlocks: 120,
|
||||
})
|
||||
|
||||
expected := NewAntelopeV1("https://api.v1.example.com", "host.example.com", 60)
|
||||
|
||||
assert.IsType(t, expected, api)
|
||||
assert.Equal(t, expected.client.Url, api.(AntelopeV1).client.Url)
|
||||
assert.Equal(t, expected.client.Host, api.(AntelopeV1).client.Host)
|
||||
assert.Equal(t, expected.block_time, api.(AntelopeV1).block_time)
|
||||
}
|
||||
|
||||
func TestAntelopeV1_LogInfo(t *testing.T) {
|
||||
api := NewAntelopeV1("https://api.v1.example.com", "host.example.com", 120)
|
||||
|
||||
expected := LogParams{"type", "antelope-v1", "url", "https://api.v1.example.com", "host", "host.example.com", "block_time", float64(120)}
|
||||
|
||||
assert.Equal(t, expected, api.LogInfo())
|
||||
}
|
||||
|
||||
func TestAntelopeV1_SetTime(t *testing.T) {
|
||||
expected := time.Date(2022, 2, 24, 13, 38, 0, 0, time.UTC)
|
||||
|
||||
api := NewAntelopeV1("", "", 60)
|
||||
// Assert that time is NOW (+-10 seconds)
|
||||
assert.InDelta(t, api.GetTime().Unix(), time.Now().In(time.UTC).Unix(), float64(10))
|
||||
|
||||
api.SetTime(expected)
|
||||
assert.Equal(t, expected, api.GetTime())
|
||||
}
|
||||
|
||||
func TestAntelopeV1_JsonFailure(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
_, err := res.Write([]byte(`!//{invalid-json}!##`))
|
||||
assert.NoError(t, err)
|
||||
}))
|
||||
|
||||
api := NewAntelopeV1(srv.URL, "", 120)
|
||||
check, _ := api.Call(context.Background())
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV1_HTTP500Failed(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
res.WriteHeader(500)
|
||||
_, err := res.Write([]byte(`{}`))
|
||||
assert.NoError(t, err)
|
||||
}))
|
||||
|
||||
api := NewAntelopeV1(srv.URL, "", 120)
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "server returned HTTP 500 Internal Server Error", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV1_LaggingUp(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v1/chain/get_info" {
|
||||
info := `{
|
||||
"server_version": "8f613ec9",
|
||||
"head_block_num": 7272812,
|
||||
"head_block_time": "2022-02-24T13:37:00"
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV1(srv.URL, "", 60)
|
||||
api.SetTime(time.Date(2022, 2, 24, 13, 38, 0, 0, time.UTC))
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "OK", status)
|
||||
|
||||
expected := agentcheck.NewStatusResponse(agentcheck.Up)
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV1_LaggingDown(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v1/chain/get_info" {
|
||||
info := `{
|
||||
"server_version": "9a607cce",
|
||||
"head_block_num": 87263,
|
||||
"head_block_time": "2018-01-01T13:37:01"
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV1(srv.URL, "", 60)
|
||||
api.SetTime(time.Date(2018, time.January, 1, 13, 38, 2, 0, time.UTC))
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Taking offline because head block is lagging 61 seconds", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV1_TimeInFutureUP(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v1/chain/get_info" {
|
||||
info := `{
|
||||
"server_version": "d1bec8d3",
|
||||
"head_block_num": 548847,
|
||||
"head_block_time": "2020-09-22T09:32:00"
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV1(srv.URL, "", 120)
|
||||
api.SetTime(time.Date(2020, 9, 22, 9, 30, 0, 0, time.UTC))
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "OK", status)
|
||||
|
||||
expected := agentcheck.NewStatusResponse(agentcheck.Up)
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV1_TimeInFutureDown(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v1/chain/get_info" {
|
||||
info := `{
|
||||
"server_version": "c879d231",
|
||||
"head_block_num": 2637621,
|
||||
"head_block_time": "2019-04-14T12:02:01"
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV1(srv.URL, "", 120)
|
||||
api.SetTime(time.Date(2019, time.April, 14, 12, 0, 0, 0, time.UTC))
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Taking offline because head block is -121 seconds into the future", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
85
internal/api/antelope_v2.go
Normal file
85
internal/api/antelope_v2.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/eosswedenorg-go/leapapi"
|
||||
"github.com/eosswedenorg/antelope-api-healthcheck/internal/utils"
|
||||
)
|
||||
|
||||
type AntelopeV2 struct {
|
||||
client leapapi.Client
|
||||
offset int64
|
||||
}
|
||||
|
||||
func AntelopeV2Factory(args ApiArguments) ApiInterface {
|
||||
return NewAntelopeV2(args.Url, args.Host, int64(args.NumBlocks))
|
||||
}
|
||||
|
||||
func NewAntelopeV2(url string, host string, offset int64) AntelopeV2 {
|
||||
api := AntelopeV2{
|
||||
client: *leapapi.New(url),
|
||||
offset: offset,
|
||||
}
|
||||
|
||||
api.client.Host = host
|
||||
|
||||
return api
|
||||
}
|
||||
|
||||
func (e AntelopeV2) LogInfo() LogParams {
|
||||
p := LogParams{
|
||||
"type", "antelope-v2",
|
||||
"url", e.client.Url,
|
||||
}
|
||||
|
||||
if len(e.client.Host) > 0 {
|
||||
p.Add("host", e.client.Host)
|
||||
}
|
||||
|
||||
p.Add("offset", e.offset)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (e AntelopeV2) Call(ctx context.Context) (agentcheck.Response, string) {
|
||||
health, err := e.client.GetHealth(ctx)
|
||||
if err != nil {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
return resp, err.Error()
|
||||
}
|
||||
|
||||
// Fetch elasticsearch and nodeos block numbers from json.
|
||||
var es_block int64 = 0
|
||||
var node_block int64 = 0
|
||||
|
||||
for _, v := range health.Health {
|
||||
if v.Name == "Elasticsearch" {
|
||||
es_block = utils.JsonGetInt64(v.Data["last_indexed_block"])
|
||||
} else if v.Name == "NodeosRPC" {
|
||||
node_block = utils.JsonGetInt64(v.Data["head_block_num"])
|
||||
}
|
||||
}
|
||||
|
||||
// Error out if ether or both are zero.
|
||||
if es_block == 0 || node_block == 0 {
|
||||
msg := fmt.Sprintf("Failed to get Elasticsearch and/or nodeos "+
|
||||
"block numbers (es: %d, eos: %d)", es_block, node_block)
|
||||
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
return resp, msg
|
||||
}
|
||||
|
||||
// Check if ES is behind or in the future.
|
||||
diff := node_block - es_block
|
||||
if diff > e.offset {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
return resp, fmt.Sprintf("Taking offline because Elastic is %d blocks behind", diff)
|
||||
} else if diff < -e.offset {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
return resp, fmt.Sprintf("Taking offline because Elastic is %d blocks into the future", -1*diff)
|
||||
}
|
||||
return agentcheck.NewStatusResponse(agentcheck.Up), "OK"
|
||||
}
|
||||
382
internal/api/antelope_v2_test.go
Normal file
382
internal/api/antelope_v2_test.go
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAntelopeV2_Factory(t *testing.T) {
|
||||
api := AntelopeV2Factory(ApiArguments{
|
||||
Url: "https://api.v2.example.com",
|
||||
Host: "host.example.com",
|
||||
NumBlocks: 120,
|
||||
})
|
||||
|
||||
expected := NewAntelopeV2("https://api.v2.example.com", "host.example.com", 120)
|
||||
|
||||
assert.IsType(t, expected, api)
|
||||
assert.Equal(t, expected.client.Url, api.(AntelopeV2).client.Url)
|
||||
assert.Equal(t, expected.client.Host, api.(AntelopeV2).client.Host)
|
||||
assert.Equal(t, expected.offset, api.(AntelopeV2).offset)
|
||||
}
|
||||
|
||||
func TestAntelopeV2_LogInfo(t *testing.T) {
|
||||
api := NewAntelopeV2("https://api.v2.example.com", "host.example.com", 120)
|
||||
|
||||
expected := LogParams{"type", "antelope-v2", "url", "https://api.v2.example.com", "host", "host.example.com", "offset", int64(120)}
|
||||
|
||||
assert.Equal(t, expected, api.LogInfo())
|
||||
}
|
||||
|
||||
func TestAntelopeV2_JsonFailure(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
_, err := res.Write([]byte(`!//{invalid-json}!##`))
|
||||
assert.NoError(t, err)
|
||||
}))
|
||||
|
||||
api := NewAntelopeV2(srv.URL, "", 120)
|
||||
check, _ := api.Call(context.Background())
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV2_HTTP500Failed(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
res.WriteHeader(500)
|
||||
_, err := res.Write([]byte(`{}`))
|
||||
assert.NoError(t, err)
|
||||
}))
|
||||
|
||||
api := NewAntelopeV2(srv.URL, "", 120)
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "server returned HTTP 500 Internal Server Error", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV2_LaggingUp(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v2/health" {
|
||||
info := `{
|
||||
"version": "1.0",
|
||||
"version_hash": "028d5a34463884fcbe2ecfd3c0fcb3b5d4d538f4fd64803c1ef7209c85f2f266",
|
||||
"host": "api.test.com:443",
|
||||
"health": [
|
||||
{
|
||||
"service": "NodeosRPC",
|
||||
"status": "OK",
|
||||
"service_data": {
|
||||
"head_block_num": 263148621,
|
||||
"head_block_time": "2022-08-17T14:16:36.000",
|
||||
"time_offset": 190,
|
||||
"last_irreversible_block": 263148296,
|
||||
"chain_id": "f8c74ccb7f9dea6f26a6d7f786809ddd1bce9fada3867f567dd83691b5348534"
|
||||
},
|
||||
"time": 1642174781678
|
||||
},
|
||||
{
|
||||
"service": "Elasticsearch",
|
||||
"status": "OK",
|
||||
"service_data": {
|
||||
"last_indexed_block": 263148121,
|
||||
"total_indexed_blocks": 263148121,
|
||||
"active_shards": "100.0%"
|
||||
},
|
||||
"time": 1642174781736
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV2(srv.URL, "", 500)
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "OK", status)
|
||||
|
||||
expected := agentcheck.NewStatusResponse(agentcheck.Up)
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV2_LaggingDown(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v2/health" {
|
||||
info := `{
|
||||
"version": "1.0",
|
||||
"version_hash": "028d5a34463884fcbe2ecfd3c0fcb3b5d4d538f4fd64803c1ef7209c85f2f266",
|
||||
"host": "api.test.com:443",
|
||||
"health": [
|
||||
{
|
||||
"service": "NodeosRPC",
|
||||
"status": "OK",
|
||||
"service_data": {
|
||||
"head_block_num": 263148621,
|
||||
"head_block_time": "2022-08-17T14:16:36.000",
|
||||
"time_offset": 190,
|
||||
"last_irreversible_block": 263148296,
|
||||
"chain_id": "f8c74ccb7f9dea6f26a6d7f786809ddd1bce9fada3867f567dd83691b5348534"
|
||||
},
|
||||
"time": 1642174781678
|
||||
},
|
||||
{
|
||||
"service": "Elasticsearch",
|
||||
"status": "OK",
|
||||
"service_data": {
|
||||
"last_indexed_block": 263148121,
|
||||
"total_indexed_blocks": 263148121,
|
||||
"active_shards": "100.0%"
|
||||
},
|
||||
"time": 1642174781736
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV2(srv.URL, "", 499)
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Taking offline because Elastic is 500 blocks behind", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV2_LaggingESInFutureUP(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v2/health" {
|
||||
info := `{
|
||||
"version": "1.0",
|
||||
"version_hash": "028d5a34463884fcbe2ecfd3c0fcb3b5d4d538f4fd64803c1ef7209c85f2f266",
|
||||
"host": "api.test.com:443",
|
||||
"health": [
|
||||
{
|
||||
"service": "NodeosRPC",
|
||||
"status": "OK",
|
||||
"service_data": {
|
||||
"head_block_num": 263148621,
|
||||
"head_block_time": "2022-08-17T14:16:36.000",
|
||||
"time_offset": 190,
|
||||
"last_irreversible_block": 263148296,
|
||||
"chain_id": "f8c74ccb7f9dea6f26a6d7f786809ddd1bce9fada3867f567dd83691b5348534"
|
||||
},
|
||||
"time": 1642174781678
|
||||
},
|
||||
{
|
||||
"service": "Elasticsearch",
|
||||
"status": "OK",
|
||||
"service_data": {
|
||||
"last_indexed_block": 263148821,
|
||||
"total_indexed_blocks": 263148821,
|
||||
"active_shards": "100.0%"
|
||||
},
|
||||
"time": 1642174781736
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV2(srv.URL, "", 200)
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "OK", status)
|
||||
|
||||
expected := agentcheck.NewStatusResponse(agentcheck.Up)
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV2_LaggingESInFutureDown(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v2/health" {
|
||||
info := `{
|
||||
"version": "1.0",
|
||||
"version_hash": "028d5a34463884fcbe2ecfd3c0fcb3b5d4d538f4fd64803c1ef7209c85f2f266",
|
||||
"host": "api.test.com:443",
|
||||
"health": [
|
||||
{
|
||||
"service": "NodeosRPC",
|
||||
"status": "OK",
|
||||
"service_data": {
|
||||
"head_block_num": 263148621,
|
||||
"head_block_time": "2022-08-17T14:16:36.000",
|
||||
"time_offset": 190,
|
||||
"last_irreversible_block": 263148296,
|
||||
"chain_id": "f8c74ccb7f9dea6f26a6d7f786809ddd1bce9fada3867f567dd83691b5348534"
|
||||
},
|
||||
"time": 1642174781678
|
||||
},
|
||||
{
|
||||
"service": "Elasticsearch",
|
||||
"status": "OK",
|
||||
"service_data": {
|
||||
"last_indexed_block": 263148822,
|
||||
"total_indexed_blocks": 263148822,
|
||||
"active_shards": "100.0%"
|
||||
},
|
||||
"time": 1642174781736
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV2(srv.URL, "", 200)
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Taking offline because Elastic is 201 blocks into the future", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV2_ElasticsFailed(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v2/health" {
|
||||
info := `{
|
||||
"version": "1.0",
|
||||
"version_hash": "028d5a34463884fcbe2ecfd3c0fcb3b5d4d538f4fd64803c1ef7209c85f2f266",
|
||||
"host": "api.test.com:443",
|
||||
"health": [
|
||||
{
|
||||
"service": "NodeosRPC",
|
||||
"status": "OK",
|
||||
"service_data": {
|
||||
"head_block_num": 263148621,
|
||||
"head_block_time": "2022-08-17T14:16:36.000",
|
||||
"time_offset": 190,
|
||||
"last_irreversible_block": 263148296,
|
||||
"chain_id": "f8c74ccb7f9dea6f26a6d7f786809ddd1bce9fada3867f567dd83691b5348534"
|
||||
},
|
||||
"time": 1660745796190
|
||||
},
|
||||
{
|
||||
"service": "Elasticsearch",
|
||||
"status": "DOWN",
|
||||
"service_data": {
|
||||
"last_indexed_block": 0,
|
||||
"total_indexed_blocks": 0,
|
||||
"active_shards": "0.0%"
|
||||
},
|
||||
"time": 1660745796204
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV2(srv.URL, "", 500)
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Failed to get Elasticsearch and/or nodeos block numbers (es: 0, eos: 263148621)", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV2_NodeosRPCFailed(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v2/health" {
|
||||
info := `{
|
||||
"version": "1.0",
|
||||
"version_hash": "028d5a34463884fcbe2ecfd3c0fcb3b5d4d538f4fd64803c1ef7209c85f2f266",
|
||||
"host": "api.test.com:443",
|
||||
"health": [
|
||||
{
|
||||
"service": "NodeosRPC",
|
||||
"status": "DOWN",
|
||||
"service_data": {
|
||||
"head_block_num": 0,
|
||||
"head_block_time": "",
|
||||
"time_offset": 0,
|
||||
"last_irreversible_block": 0,
|
||||
"chain_id": ""
|
||||
},
|
||||
"time": 1642174781678
|
||||
},
|
||||
{
|
||||
"service": "Elasticsearch",
|
||||
"status": "DOWN",
|
||||
"service_data": {
|
||||
"last_indexed_block": 263148121,
|
||||
"total_indexed_blocks": 263148121,
|
||||
"active_shards": "100.0%"
|
||||
},
|
||||
"time": 1642174781736
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV2(srv.URL, "", 500)
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Failed to get Elasticsearch and/or nodeos block numbers (es: 263148121, eos: 0)", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAntelopeV2_ElasticsNodeosRPCFailed(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/v2/health" {
|
||||
info := `{
|
||||
"version": "1.0",
|
||||
"version_hash": "028d5a34463884fcbe2ecfd3c0fcb3b5d4d538f4fd64803c1ef7209c85f2f266",
|
||||
"host": "api.test.com:443",
|
||||
"health": [
|
||||
{
|
||||
"service": "NodeosRPC",
|
||||
"status": "DOWN",
|
||||
"service_data": {},
|
||||
"time": 1642174781678
|
||||
},
|
||||
{
|
||||
"service": "Elasticsearch",
|
||||
"status": "DOWN",
|
||||
"service_data": {},
|
||||
"time": 1642174781736
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
_, err := res.Write([]byte(info))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAntelopeV2(srv.URL, "", 500)
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Failed to get Elasticsearch and/or nodeos block numbers (es: 0, eos: 0)", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
91
internal/api/atomicasset.go
Normal file
91
internal/api/atomicasset.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/eosswedenorg-go/atomicasset"
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/eosswedenorg/antelope-api-healthcheck/internal/utils"
|
||||
)
|
||||
|
||||
type AtomicAsset struct {
|
||||
utils.Time
|
||||
|
||||
url string
|
||||
host string
|
||||
block_time float64
|
||||
}
|
||||
|
||||
func AtomicAssetFactory(args ApiArguments) ApiInterface {
|
||||
return NewAtomicAsset(args.Url, args.Host, float64(args.NumBlocks/2))
|
||||
}
|
||||
|
||||
func NewAtomicAsset(url string, host string, block_time float64) AtomicAsset {
|
||||
return AtomicAsset{
|
||||
url: url,
|
||||
host: host,
|
||||
block_time: block_time,
|
||||
}
|
||||
}
|
||||
|
||||
func (e AtomicAsset) LogInfo() LogParams {
|
||||
p := LogParams{
|
||||
"type", "atomicasset",
|
||||
"url", e.url,
|
||||
"block_time", e.block_time,
|
||||
}
|
||||
|
||||
if len(e.host) > 0 {
|
||||
p.Add("host", e.host)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (e AtomicAsset) Call(ctx context.Context) (agentcheck.Response, string) {
|
||||
client := atomicasset.NewWithContext(e.url, ctx)
|
||||
client.Host = e.host
|
||||
|
||||
h, err := client.GetHealth()
|
||||
if err != nil {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
return resp, err.Error()
|
||||
}
|
||||
|
||||
// Check HTTP Status Code
|
||||
if h.HTTPStatusCode > 299 {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
msg := "Taking offline because %v was received from backend"
|
||||
return resp, fmt.Sprintf(msg, h.HTTPStatusCode)
|
||||
}
|
||||
|
||||
// Check postgres
|
||||
if h.Data.Postgres.Status != "OK" {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
msg := "Taking offline because Postgres reported '%s'"
|
||||
return resp, fmt.Sprintf(msg, h.Data.Postgres.Status)
|
||||
}
|
||||
|
||||
// Check redis
|
||||
if h.Data.Redis.Status != "OK" {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
msg := "Taking offline because Redis reported '%s'"
|
||||
return resp, fmt.Sprintf(msg, h.Data.Redis.Status)
|
||||
}
|
||||
|
||||
// Validate head block.
|
||||
diff := e.GetTime().Sub(h.Data.Chain.HeadTime.Time()).Seconds()
|
||||
|
||||
if diff > e.block_time {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
msg := "Taking offline because head block is lagging %.0f seconds"
|
||||
return resp, fmt.Sprintf(msg, diff)
|
||||
} else if diff < -e.block_time {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
msg := "Taking offline because head block is %.0f seconds into the future"
|
||||
return resp, fmt.Sprintf(msg, diff)
|
||||
}
|
||||
|
||||
return agentcheck.NewStatusResponse(agentcheck.Up), "OK"
|
||||
}
|
||||
316
internal/api/atomicasset_test.go
Normal file
316
internal/api/atomicasset_test.go
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAtomicAsset_Factory(t *testing.T) {
|
||||
api := AtomicAssetFactory(ApiArguments{
|
||||
Url: "https://atomic.example.com",
|
||||
NumBlocks: 120,
|
||||
})
|
||||
|
||||
expected := NewAtomicAsset("https://atomic.example.com", "", 60)
|
||||
|
||||
assert.IsType(t, expected, api)
|
||||
assert.Equal(t, expected.url, api.(AtomicAsset).url)
|
||||
assert.Equal(t, expected.block_time, api.(AtomicAsset).block_time)
|
||||
}
|
||||
|
||||
func TestAtomicAsset_LogInfo(t *testing.T) {
|
||||
api := NewAtomicAsset("https://atomic.example.com", "", 120)
|
||||
|
||||
expected := LogParams{"type", "atomicasset", "url", "https://atomic.example.com", "block_time", float64(120)}
|
||||
|
||||
assert.Equal(t, expected, api.LogInfo())
|
||||
}
|
||||
|
||||
func TestAtomicAsset_LogInfoWithHost(t *testing.T) {
|
||||
api := NewAtomicAsset("https://atomic.example.com", "some.other.host", 120)
|
||||
|
||||
expected := LogParams{"type", "atomicasset", "url", "https://atomic.example.com", "block_time", float64(120), "host", "some.other.host"}
|
||||
|
||||
assert.Equal(t, expected, api.LogInfo())
|
||||
}
|
||||
|
||||
func TestAtomicAsset_SetTime(t *testing.T) {
|
||||
expected := time.Date(2019, 3, 18, 20, 29, 32, 0, time.UTC)
|
||||
|
||||
api := NewAtomicAsset("", "", 60)
|
||||
// Assert that time is NOW (+-10 seconds)
|
||||
assert.InDelta(t, api.GetTime().Unix(), time.Now().In(time.UTC).Unix(), float64(10))
|
||||
|
||||
api.SetTime(expected)
|
||||
assert.Equal(t, expected, api.GetTime())
|
||||
}
|
||||
|
||||
func TestAtomicAsset_JsonFailure(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
_, err := res.Write([]byte(`!//{invalid-json}!##`))
|
||||
assert.NoError(t, err)
|
||||
}))
|
||||
|
||||
api := NewAtomicAsset(srv.URL, "", 120)
|
||||
check, _ := api.Call(context.Background())
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAtomicAsset_HTTP500Down(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
res.Header().Add("Content-type", "application/json; charset=utf-8")
|
||||
res.WriteHeader(500)
|
||||
_, err := res.Write([]byte(`{}`))
|
||||
assert.NoError(t, err)
|
||||
}))
|
||||
|
||||
api := NewAtomicAsset(srv.URL, "", 120)
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Taking offline because 500 was received from backend", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAtomicAsset_LaggingUp(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/health" {
|
||||
payload := `{
|
||||
"success":true,
|
||||
"data":{
|
||||
"version":"1.0.0",
|
||||
"postgres":{
|
||||
"status":"OK"
|
||||
},
|
||||
"redis":{
|
||||
"status":"OK"
|
||||
},
|
||||
"chain":{
|
||||
"status":"OK",
|
||||
"head_block":2173612361,
|
||||
"head_time":1759953927000
|
||||
}
|
||||
},
|
||||
"query_time":1759953929542
|
||||
}`
|
||||
|
||||
res.Header().Add("Content-type", "application/json; charset=utf-8")
|
||||
_, err := res.Write([]byte(payload))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAtomicAsset(srv.URL, "", 120)
|
||||
api.SetTime(time.Date(2025, 10, 8, 20, 7, 27, 0, time.UTC))
|
||||
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "OK", status)
|
||||
|
||||
expected := agentcheck.NewStatusResponse(agentcheck.Up)
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAtomicAsset_LaggingDown(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/health" {
|
||||
payload := `{
|
||||
"success":true,
|
||||
"data":{
|
||||
"version":"1.0.0",
|
||||
"postgres":{
|
||||
"status":"OK"
|
||||
},
|
||||
"redis":{
|
||||
"status":"OK"
|
||||
},
|
||||
"chain":{
|
||||
"status":"OK",
|
||||
"head_block":213671263812,
|
||||
"head_time":1533451894000
|
||||
}
|
||||
},
|
||||
"query_time":1533451895542
|
||||
}`
|
||||
|
||||
res.Header().Add("Content-type", "application/json; charset=utf-8")
|
||||
_, err := res.Write([]byte(payload))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAtomicAsset(srv.URL, "", 120)
|
||||
api.SetTime(time.Date(2018, 8, 5, 6, 53, 35, 0, time.UTC))
|
||||
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Taking offline because head block is lagging 121 seconds", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAtomicAsset_InFutureUp(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/health" {
|
||||
payload := `{
|
||||
"success":true,
|
||||
"data":{
|
||||
"version":"1.0.0",
|
||||
"postgres":{
|
||||
"status":"OK"
|
||||
},
|
||||
"redis":{
|
||||
"status":"OK"
|
||||
},
|
||||
"chain":{
|
||||
"status":"OK",
|
||||
"head_block":213671263812,
|
||||
"head_time":1728954676500
|
||||
}
|
||||
},
|
||||
"query_time":1728954678231
|
||||
}`
|
||||
|
||||
res.Header().Add("Content-type", "application/json; charset=utf-8")
|
||||
_, err := res.Write([]byte(payload))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAtomicAsset(srv.URL, "", 120)
|
||||
api.SetTime(time.Date(2024, 10, 15, 1, 9, 16, 500*int(time.Millisecond), time.UTC))
|
||||
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "OK", status)
|
||||
|
||||
expected := agentcheck.NewStatusResponse(agentcheck.Up)
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAtomicAsset_InFutureDown(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/health" {
|
||||
payload := `{
|
||||
"success":true,
|
||||
"data":{
|
||||
"version":"1.0.0",
|
||||
"postgres":{
|
||||
"status":"OK"
|
||||
},
|
||||
"redis":{
|
||||
"status":"OK"
|
||||
},
|
||||
"chain":{
|
||||
"status":"OK",
|
||||
"head_block":213671263812,
|
||||
"head_time":1041122824500
|
||||
}
|
||||
},
|
||||
"query_time":1041122832231
|
||||
}`
|
||||
|
||||
res.Header().Add("Content-type", "application/json; charset=utf-8")
|
||||
_, err := res.Write([]byte(payload))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAtomicAsset(srv.URL, "", 120)
|
||||
api.SetTime(time.Date(2002, 12, 29, 0, 45, 3, 500*int(time.Millisecond), time.UTC))
|
||||
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Taking offline because head block is -121 seconds into the future", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAtomicAsset_RedisDown(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/health" {
|
||||
payload := `{
|
||||
"success":true,
|
||||
"data":{
|
||||
"version":"1.0.0",
|
||||
"postgres":{
|
||||
"status":"OK"
|
||||
},
|
||||
"redis":{
|
||||
"status":"DOWN"
|
||||
},
|
||||
"chain":{
|
||||
"status":"OK",
|
||||
"head_block":213671263812,
|
||||
"head_time":1426072770500
|
||||
}
|
||||
},
|
||||
"query_time":1426072775872
|
||||
}`
|
||||
|
||||
res.Header().Add("Content-type", "application/json; charset=utf-8")
|
||||
_, err := res.Write([]byte(payload))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAtomicAsset(srv.URL, "", 120)
|
||||
api.SetTime(time.Date(2015, 3, 11, 11, 19, 30, 500*int(time.Millisecond), time.UTC))
|
||||
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Taking offline because Redis reported 'DOWN'", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
|
||||
func TestAtomicAsset_PostgresDown(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.String() == "/health" {
|
||||
payload := `{
|
||||
"success":true,
|
||||
"data":{
|
||||
"version":"1.0.0",
|
||||
"postgres":{
|
||||
"status":"DOWN"
|
||||
},
|
||||
"redis":{
|
||||
"status":"OK"
|
||||
},
|
||||
"chain":{
|
||||
"status":"OK",
|
||||
"head_block":213671263812,
|
||||
"head_time":1562868371500
|
||||
}
|
||||
},
|
||||
"query_time":156286837143
|
||||
}`
|
||||
|
||||
res.Header().Add("Content-type", "application/json; charset=utf-8")
|
||||
_, err := res.Write([]byte(payload))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}))
|
||||
|
||||
api := NewAtomicAsset(srv.URL, "", 120)
|
||||
api.SetTime(time.Date(2019, 7, 11, 18, 6, 11, 500*int(time.Millisecond), time.UTC))
|
||||
|
||||
check, status := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, "Taking offline because Postgres reported 'DOWN'", status)
|
||||
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
assert.Equal(t, expected, check)
|
||||
}
|
||||
47
internal/api/debug.go
Normal file
47
internal/api/debug.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
)
|
||||
|
||||
type DebugApi struct {
|
||||
response agentcheck.Response
|
||||
}
|
||||
|
||||
func parseResponse(resp string) agentcheck.Response {
|
||||
parts := strings.SplitN(resp, "#", 2)
|
||||
|
||||
// Status with message
|
||||
if len(parts) > 1 {
|
||||
rtype := agentcheck.StatusMessageResponseType(parts[0])
|
||||
return agentcheck.NewStatusMessageResponse(rtype, parts[1])
|
||||
}
|
||||
|
||||
// Only status.
|
||||
rtype := agentcheck.StatusResponseType(resp)
|
||||
return agentcheck.NewStatusResponse(rtype)
|
||||
}
|
||||
|
||||
func DebugApiFactory(args ApiArguments) ApiInterface {
|
||||
return NewDebugApi(args.Url)
|
||||
}
|
||||
|
||||
func NewDebugApi(response string) DebugApi {
|
||||
return DebugApi{
|
||||
response: parseResponse(response),
|
||||
}
|
||||
}
|
||||
|
||||
func (d DebugApi) LogInfo() LogParams {
|
||||
return LogParams{
|
||||
"type", "Debug",
|
||||
"response", strings.TrimSpace(d.response.String()),
|
||||
}
|
||||
}
|
||||
|
||||
func (d DebugApi) Call(_ context.Context) (agentcheck.Response, string) {
|
||||
return d.response, ""
|
||||
}
|
||||
67
internal/api/debug_test.go
Normal file
67
internal/api/debug_test.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDebugApi_Factory(t *testing.T) {
|
||||
api := DebugApiFactory(ApiArguments{
|
||||
Url: "up",
|
||||
Host: "host",
|
||||
NumBlocks: 40,
|
||||
})
|
||||
|
||||
assert.IsType(t, DebugApi{}, api)
|
||||
assert.Equal(t, api.(DebugApi).response, agentcheck.NewStatusResponse(agentcheck.Up))
|
||||
}
|
||||
|
||||
func TestNewDebugApi(t *testing.T) {
|
||||
type args struct {
|
||||
response string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want DebugApi
|
||||
}{
|
||||
{"Up", args{"up"}, DebugApi{response: agentcheck.NewStatusResponse(agentcheck.Up)}},
|
||||
{"Down", args{"down"}, DebugApi{response: agentcheck.NewStatusResponse("down")}},
|
||||
{"DownMessage", args{"down#some message"}, DebugApi{response: agentcheck.NewStatusMessageResponse(agentcheck.Down, "some message")}},
|
||||
{"Ready", args{"ready"}, DebugApi{response: agentcheck.NewStatusResponse(agentcheck.Ready)}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := NewDebugApi(tt.args.response); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("NewDebugApi() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDebugApi_LogInfo(t *testing.T) {
|
||||
expected := LogParams{"type", "Debug", "response", "up"}
|
||||
|
||||
api := DebugApi{
|
||||
response: agentcheck.NewStatusResponse(agentcheck.Up),
|
||||
}
|
||||
|
||||
assert.Equal(t, api.LogInfo(), expected)
|
||||
}
|
||||
|
||||
func TestDebugApi_Call(t *testing.T) {
|
||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Stopped, "message")
|
||||
|
||||
api := DebugApi{
|
||||
response: expected,
|
||||
}
|
||||
|
||||
response, msg := api.Call(context.Background())
|
||||
|
||||
assert.Equal(t, response, expected)
|
||||
assert.Equal(t, msg, "")
|
||||
}
|
||||
33
internal/api/interface.go
Normal file
33
internal/api/interface.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
)
|
||||
|
||||
/**
|
||||
* Generic struct that is passed to factory functions
|
||||
* to configure the API request.
|
||||
*/
|
||||
type ApiArguments struct {
|
||||
Url string
|
||||
Host string
|
||||
NumBlocks int
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory function
|
||||
*
|
||||
* Each API must implement this function and process `args`
|
||||
* returing a instance of it's implementation of the ApiInterface
|
||||
*/
|
||||
type Factory func(args ApiArguments) ApiInterface
|
||||
|
||||
type ApiInterface interface {
|
||||
// Returns Logging information
|
||||
LogInfo() LogParams
|
||||
|
||||
// Call api and validate it's status.
|
||||
Call(ctx context.Context) (agentcheck.Response, string)
|
||||
}
|
||||
13
internal/api/log_params.go
Normal file
13
internal/api/log_params.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package api
|
||||
|
||||
type LogParams []interface{}
|
||||
|
||||
func (p *LogParams) Add(field string, value interface{}) {
|
||||
*p = append(*p, field, value)
|
||||
}
|
||||
|
||||
// Syntactic sugar for append(p, other...)
|
||||
// Returns a new instance of LogParams with all values from both p and other
|
||||
func (p LogParams) Combine(other LogParams) LogParams {
|
||||
return append(p, other...)
|
||||
}
|
||||
47
internal/api/log_params_test.go
Normal file
47
internal/api/log_params_test.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLogParams(t *testing.T) {
|
||||
type test_struct struct {
|
||||
First string
|
||||
Second int
|
||||
}
|
||||
|
||||
p := LogParams{}
|
||||
|
||||
p.Add("one", 1)
|
||||
p.Add("string", "str")
|
||||
p.Add("struct", test_struct{First: "first_string", Second: 1234})
|
||||
|
||||
expected := []interface{}([]interface{}{
|
||||
"one", 1,
|
||||
"string", "str",
|
||||
"struct",
|
||||
test_struct{
|
||||
First: "first_string",
|
||||
Second: 1234,
|
||||
},
|
||||
})
|
||||
|
||||
assert.ElementsMatch(t, expected, p)
|
||||
}
|
||||
|
||||
func TestLogParams_Combine(t *testing.T) {
|
||||
a := LogParams{"one", 1, "string1", "str1"}
|
||||
|
||||
b := LogParams{"two", 2, "string2", "str2"}
|
||||
|
||||
expected := LogParams{
|
||||
"one", 1,
|
||||
"string1", "str1",
|
||||
"two", 2,
|
||||
"string2", "str2",
|
||||
}
|
||||
|
||||
assert.Equal(t, expected, a.Combine(b))
|
||||
}
|
||||
20
internal/api/make.go
Normal file
20
internal/api/make.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Make(name string, args ApiArguments) (ApiInterface, error) {
|
||||
factories := map[string]Factory{
|
||||
"v1": AntelopeV1Factory,
|
||||
"v2": AntelopeV2Factory,
|
||||
"atomic": AtomicAssetFactory,
|
||||
"debug": DebugApiFactory,
|
||||
}
|
||||
|
||||
if factory, ok := factories[name]; ok {
|
||||
return factory(args), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("invalid API '%s'", name)
|
||||
}
|
||||
37
internal/api/make_test.go
Normal file
37
internal/api/make_test.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMakeV1(t *testing.T) {
|
||||
api, err := Make("v1", ApiArguments{})
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, AntelopeV1{}, api)
|
||||
}
|
||||
|
||||
func TestMakeV2(t *testing.T) {
|
||||
api, err := Make("v2", ApiArguments{})
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, AntelopeV2{}, api)
|
||||
}
|
||||
|
||||
func TestMakeAtomic(t *testing.T) {
|
||||
api, err := Make("atomic", ApiArguments{})
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, AtomicAsset{}, api)
|
||||
}
|
||||
|
||||
func TestMakeDebug(t *testing.T) {
|
||||
api, err := Make("debug", ApiArguments{})
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, DebugApi{}, api)
|
||||
}
|
||||
|
||||
func TestMakeInvalid(t *testing.T) {
|
||||
api, err := Make("invalid", ApiArguments{})
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, api)
|
||||
}
|
||||
45
internal/server/parse_request.go
Normal file
45
internal/server/parse_request.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/eosswedenorg/antelope-api-healthcheck/internal/api"
|
||||
)
|
||||
|
||||
func ParseArguments(args []string) api.ApiArguments {
|
||||
a := api.ApiArguments{
|
||||
NumBlocks: 10,
|
||||
}
|
||||
|
||||
// 1. url (scheme + ip/domain + port)
|
||||
a.Url = args[0]
|
||||
|
||||
// 2. num blocks
|
||||
if len(args) > 1 {
|
||||
num, err := strconv.ParseInt(args[1], 10, 32)
|
||||
if err == nil {
|
||||
a.NumBlocks = int(num)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Host
|
||||
if len(args) > 2 {
|
||||
a.Host = args[2]
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func ParseRequest(request string) (api.ApiInterface, error) {
|
||||
p := strings.Split(strings.TrimSpace(request), "|")
|
||||
|
||||
if len(p) < 2 {
|
||||
return nil, fmt.Errorf("invalid number of parameters in agent request")
|
||||
}
|
||||
|
||||
a := ParseArguments(p[1:])
|
||||
|
||||
return api.Make(p[0], a)
|
||||
}
|
||||
112
internal/server/parse_request_test.go
Normal file
112
internal/server/parse_request_test.go
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/eosswedenorg/antelope-api-healthcheck/internal/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseRequest_WithInvalidApi(t *testing.T) {
|
||||
api, err := ParseRequest("invalid|http://api.example.com")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err.Error(), "invalid API 'invalid'")
|
||||
assert.Nil(t, api)
|
||||
}
|
||||
|
||||
func TestParseRequest_WithInvalidParams(t *testing.T) {
|
||||
api, err := ParseRequest("v1")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err.Error(), "invalid number of parameters in agent request")
|
||||
assert.Nil(t, api)
|
||||
}
|
||||
|
||||
// AntelopeV1
|
||||
// --------------------------------
|
||||
|
||||
func TestParseRequest_AntelopeV1(t *testing.T) {
|
||||
expected := api.NewAntelopeV1("http://api.example.com", "", 5)
|
||||
|
||||
api, err := ParseRequest("v1|http://api.example.com")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
||||
}
|
||||
|
||||
func TestParseRequest_AntelopeV1WithBlockNumber(t *testing.T) {
|
||||
expected := api.NewAntelopeV1("http://api.example.com", "", 1000)
|
||||
|
||||
api, err := ParseRequest("v1|http://api.example.com|2000")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
||||
}
|
||||
|
||||
func TestParseRequest_AntelopeV1Full(t *testing.T) {
|
||||
expected := api.NewAntelopeV1("http://api.example.com", "http://host.example.com", 500)
|
||||
|
||||
api, err := ParseRequest("v1|http://api.example.com|1000|http://host.example.com")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
||||
}
|
||||
|
||||
// AntelopeV2
|
||||
// --------------------------------
|
||||
|
||||
func TestParseRequest_AntelopeV2(t *testing.T) {
|
||||
expected := api.NewAntelopeV2("http://api.v2.example.com", "", 10)
|
||||
|
||||
api, err := ParseRequest("v2|http://api.v2.example.com")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
||||
}
|
||||
|
||||
func TestParseRequest_AntelopeV2WithOffset(t *testing.T) {
|
||||
expected := api.NewAntelopeV2("http://api.v2.example.com", "", 1000)
|
||||
|
||||
api, err := ParseRequest("v2|http://api.v2.example.com|1000")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
||||
}
|
||||
|
||||
func TestParseRequest_AntelopeV2Full(t *testing.T) {
|
||||
expected := api.NewAntelopeV2("http://api.v2.example.com", "http://host.example.com", 1000)
|
||||
|
||||
api, err := ParseRequest("v2|http://api.v2.example.com|1000|http://host.example.com")
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
||||
}
|
||||
|
||||
// AtomicAsset
|
||||
// --------------------------------
|
||||
|
||||
func TestParseRequest_AtomicAsset(t *testing.T) {
|
||||
expected := api.NewAtomicAsset("http://api.atomicassets.io", "", 5)
|
||||
|
||||
api, err := ParseRequest("atomic|http://api.atomicassets.io")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
||||
}
|
||||
|
||||
func TestParseRequest_AtomicAssetWithBlockTime(t *testing.T) {
|
||||
expected := api.NewAtomicAsset("http://api.atomicassets.io", "", 256)
|
||||
|
||||
api, err := ParseRequest("atomic|http://api.atomicassets.io|512")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
||||
}
|
||||
|
||||
func TestParseRequest_AtomicAssetWithHost(t *testing.T) {
|
||||
expected := api.NewAtomicAsset("http://api.atomicassets.io", "some.other.host", 256)
|
||||
|
||||
api, err := ParseRequest("atomic|http://api.atomicassets.io|512|some.other.host")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
||||
}
|
||||
|
||||
func TestParseRequest_DebugApi(t *testing.T) {
|
||||
expected := api.NewDebugApi("some_api_call")
|
||||
|
||||
api, err := ParseRequest("debug|some_api_call")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
||||
}
|
||||
176
internal/server/server.go
Normal file
176
internal/server/server.go
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/eosswedenorg/antelope-api-healthcheck/internal/api"
|
||||
log "github.com/inconshreveable/log15"
|
||||
"github.com/panjf2000/gnet/v2"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
gnet.BuiltinEventEngine
|
||||
|
||||
eng gnet.Engine
|
||||
|
||||
// Address to bind to.
|
||||
addr string
|
||||
|
||||
// Number of connections between each OnTick()
|
||||
num_conn uint64
|
||||
|
||||
// Time between each call to OnTick()
|
||||
tick_interval time.Duration
|
||||
|
||||
// API Check timeout
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
type Option func(*Server)
|
||||
|
||||
func New(addr string, options ...Option) *Server {
|
||||
s := &Server{
|
||||
addr: fmt.Sprintf("tcp://%s", addr),
|
||||
}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(s)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func WithTick(interval time.Duration) Option {
|
||||
return func(s *Server) {
|
||||
s.tick_interval = interval
|
||||
}
|
||||
}
|
||||
|
||||
func WithTimeout(duration time.Duration) Option {
|
||||
return func(s *Server) {
|
||||
s.timeout = duration
|
||||
}
|
||||
}
|
||||
|
||||
// OnBoot callback function
|
||||
//
|
||||
// ---------------------------------------------------------
|
||||
func (s *Server) OnBoot(eng gnet.Engine) gnet.Action {
|
||||
s.eng = eng
|
||||
log.Info("Server started", "addr", s.addr)
|
||||
return gnet.None
|
||||
}
|
||||
|
||||
func (s *Server) OnOpen(c gnet.Conn) ([]byte, gnet.Action) {
|
||||
atomic.AddUint64(&s.num_conn, 1)
|
||||
return nil, gnet.None
|
||||
}
|
||||
|
||||
// OnClose callback function
|
||||
//
|
||||
// ---------------------------------------------------------
|
||||
func (s *Server) OnClose(c gnet.Conn, err error) gnet.Action {
|
||||
if err != nil {
|
||||
log.Error("TCP Close", "error", err)
|
||||
}
|
||||
return gnet.None
|
||||
}
|
||||
|
||||
// OnShutdown callback function
|
||||
//
|
||||
// ---------------------------------------------------------
|
||||
func (s *Server) OnShutdown(eng gnet.Engine) {
|
||||
log.Info("Server shutdown")
|
||||
}
|
||||
|
||||
// OnTick callback function
|
||||
//
|
||||
// ---------------------------------------------------------
|
||||
func (s *Server) OnTick() (time.Duration, gnet.Action) {
|
||||
log.Info("Server info", log.Ctx{
|
||||
"connections": atomic.LoadUint64(&s.num_conn),
|
||||
"current_connections": s.eng.CountConnections(),
|
||||
})
|
||||
atomic.StoreUint64(&s.num_conn, 0)
|
||||
return s.tick_interval, gnet.None
|
||||
}
|
||||
|
||||
// OnTraffic callback function
|
||||
//
|
||||
// ---------------------------------------------------------
|
||||
func (s *Server) OnTraffic(c gnet.Conn) gnet.Action {
|
||||
logger := log.Root()
|
||||
|
||||
req, err := c.Next(-1)
|
||||
if err != nil {
|
||||
logger.Error("Read", "message", err)
|
||||
return gnet.Close
|
||||
}
|
||||
|
||||
// Check api.
|
||||
// -------------------
|
||||
healthCheckApi, err := ParseRequest(string(req))
|
||||
if err != nil {
|
||||
logger.Warn("Agent request error", "message", err)
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||
|
||||
_, err = c.Write([]byte(resp.String()))
|
||||
if err != nil {
|
||||
logger.Error("Write", "message", err)
|
||||
}
|
||||
|
||||
return gnet.Close
|
||||
}
|
||||
|
||||
// gnet library does not like blocking calls.
|
||||
// as we do a blocking http call here, we need to wrap it in a goroutine.
|
||||
go func() {
|
||||
ctx := context.Background()
|
||||
if s.timeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, s.timeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
t := time.Now()
|
||||
status, msg := healthCheckApi.Call(ctx)
|
||||
req_time := time.Since(t)
|
||||
|
||||
params := api.LogParams{}
|
||||
params.Add("status", strings.TrimSpace(status.String()))
|
||||
params.Add("duration", req_time)
|
||||
params.Add("duration_us", req_time.Microseconds())
|
||||
|
||||
if msg != "OK" && len(msg) > 0 {
|
||||
params.Add("error", msg)
|
||||
}
|
||||
|
||||
logger.Info("API Check", params.Combine(healthCheckApi.LogInfo())...)
|
||||
// Report status to HAproxy
|
||||
err = c.AsyncWrite([]byte(status.String()), func(c gnet.Conn, err error) error {
|
||||
return c.Close()
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Error("Write", "message", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return gnet.None
|
||||
}
|
||||
|
||||
func (s *Server) Close() error {
|
||||
return s.eng.Stop(context.Background())
|
||||
}
|
||||
|
||||
// Run the server event loop.
|
||||
//
|
||||
// ---------------------------------------------------------
|
||||
func (s *Server) Run() error {
|
||||
return gnet.Run(s, s.addr, gnet.WithMulticore(true), gnet.WithTicker(true))
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package utils
|
||||
|
||||
// JsonGetInt64
|
||||
|
|
@ -8,10 +7,10 @@ package utils
|
|||
// if the type assertion fails, the function defaults 0 (zero).
|
||||
// ---------------------------------------------------------
|
||||
|
||||
func JsonGetInt64(input interface{}) (int64) {
|
||||
v, res := input.(float64)
|
||||
if res {
|
||||
return (int64) (v)
|
||||
}
|
||||
return 0
|
||||
func JsonGetInt64(input interface{}) int64 {
|
||||
v, res := input.(float64)
|
||||
if res {
|
||||
return (int64)(v)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
22
internal/utils/json_test.go
Normal file
22
internal/utils/json_test.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package utils
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestJson_GetInt64(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input interface{}
|
||||
want int64
|
||||
}{
|
||||
{"String", "test", 0},
|
||||
{"Int", 1234, 0},
|
||||
{"Float", float64(1234), 1234},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := JsonGetInt64(tt.input); got != tt.want {
|
||||
t.Errorf("JsonGetInt64() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
18
internal/utils/parse_log_formatter.go
Normal file
18
internal/utils/parse_log_formatter.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
log "github.com/inconshreveable/log15"
|
||||
)
|
||||
|
||||
func ParseLogFormatter(name string) log.Format {
|
||||
switch name {
|
||||
case "logfmt":
|
||||
return log.LogfmtFormat()
|
||||
case "json":
|
||||
return log.JsonFormat()
|
||||
case "json-pretty":
|
||||
return log.JsonFormatEx(true, true)
|
||||
default:
|
||||
return log.TerminalFormat()
|
||||
}
|
||||
}
|
||||
29
internal/utils/parse_log_formatter_test.go
Normal file
29
internal/utils/parse_log_formatter_test.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
log "github.com/inconshreveable/log15"
|
||||
)
|
||||
|
||||
func TestParseLogFormatter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
arg string
|
||||
want log.Format
|
||||
}{
|
||||
{"Default", "", log.TerminalFormat()},
|
||||
{"LogFmt", "logfmt", log.LogfmtFormat()},
|
||||
{"Json", "json", log.JsonFormat()},
|
||||
{"JsonPretty", "json-pretty", log.JsonFormat()},
|
||||
{"Unknown", "unknown", log.TerminalFormat()},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ParseLogFormatter(tt.arg); reflect.ValueOf(got).Pointer() != reflect.ValueOf(tt.want).Pointer() {
|
||||
t.Errorf("parseLogFormatter() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
20
internal/utils/time.go
Normal file
20
internal/utils/time.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Time struct {
|
||||
ts time.Time
|
||||
}
|
||||
|
||||
func (t *Time) SetTime(value time.Time) {
|
||||
t.ts = value
|
||||
}
|
||||
|
||||
func (t Time) GetTime() time.Time {
|
||||
if !t.ts.IsZero() {
|
||||
return t.ts
|
||||
}
|
||||
return time.Now().In(time.UTC)
|
||||
}
|
||||
24
internal/utils/time_test.go
Normal file
24
internal/utils/time_test.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTime_GetTimeWithDefaultValue(t *testing.T) {
|
||||
var ts Time
|
||||
|
||||
// Assert that time is NOW (+-10 seconds)
|
||||
assert.InDelta(t, ts.GetTime().Unix(), time.Now().In(time.UTC).Unix(), float64(10))
|
||||
}
|
||||
|
||||
func TestTime_GetTimeWithSetTime(t *testing.T) {
|
||||
var ts Time
|
||||
|
||||
expected := time.Unix(1048722042, 500)
|
||||
ts.SetTime(expected)
|
||||
|
||||
assert.Equal(t, expected, ts.GetTime())
|
||||
}
|
||||
17
package.sh
17
package.sh
|
|
@ -1,17 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
BUILD_INFO=build/.buildinfo
|
||||
|
||||
if [ ! -f "${BUILD_INFO}" ]; then
|
||||
echo "Could not find '${BUILD_INFO}' file, You need to compile first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TYPE=$(cat "${BUILD_INFO}" | sed -n 's/^GOOS=\"\(.*\)\"/\1/p')
|
||||
if [ "$TYPE" == "freebsd" ]; then
|
||||
MAKE_TARGET="package_freebsd"
|
||||
else
|
||||
MAKE_TARGET="package_deb"
|
||||
fi
|
||||
|
||||
make -B ${MAKE_TARGET}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
############################
|
||||
# Read cli args #
|
||||
############################
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "$0 <pkg_type> <build_dir>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PKG_TYPE=$1
|
||||
BUILD_DIR=$2
|
||||
|
||||
############################
|
||||
# Exported variables. #
|
||||
############################
|
||||
|
||||
export BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
export TEMPLATE_DIR=${BASE_DIR}/templates
|
||||
|
||||
set -o allexport
|
||||
# Package info
|
||||
source ${BASE_DIR}/pkg_info
|
||||
# Build info
|
||||
source ${BUILD_DIR}/.buildinfo
|
||||
set +o allexport
|
||||
|
||||
# Directories.
|
||||
export PACKAGE_BINDIR=${PACKAGE_PREFIX}/bin
|
||||
export PACKAGE_ETCDIR=etc/${PACKAGE_NAME}
|
||||
export PACKAGE_LOGDIR=/var/log
|
||||
export PACKAGE_LOGFILE=${PACKAGE_LOGDIR}/${PACKAGE_NAME}.log
|
||||
export PACKAGE_SHAREDIR=${PACKAGE_PREFIX}/share/${PACKAGE_NAME}
|
||||
export PACKAGE_TMPDIR="${BUILD_DIR}/pkg_${PKG_TYPE}"
|
||||
export BUILD_DIR
|
||||
|
||||
############################
|
||||
# Run script #
|
||||
############################
|
||||
|
||||
PKG_SCRIPT="${BASE_DIR}/build_${PKG_TYPE}.sh"
|
||||
|
||||
# Check and call script
|
||||
if [ ! -x $PKG_SCRIPT ]; then
|
||||
echo "$PKG_SCRIPT not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "[\e[34m::\e[0m] Building package for: ${PKG_TYPE}"
|
||||
|
||||
$PKG_SCRIPT
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
PACKAGE_SYSUNITDIR=etc/systemd/system
|
||||
PACKAGE_RSYSLOGDIR=etc/rsyslog.d
|
||||
PACKAGE_LOGROTATEDIR=etc/logrotate.d
|
||||
|
||||
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}_amd64"
|
||||
|
||||
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: amd64
|
||||
Homepage: https://github.com/eosswedenorg/eos-api-healthcheck
|
||||
Maintainer: Henrik Hautakoski <henrik@eossweden.org>
|
||||
Description: ${PACKAGE_DESCRIPTION}" &> ${PACKAGE_TMPDIR}/DEBIAN/control
|
||||
|
||||
cat ${PACKAGE_TMPDIR}/DEBIAN/control
|
||||
|
||||
# Create service file
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_SYSUNITDIR}
|
||||
cat ${TEMPLATE_DIR}/sysunit.service \
|
||||
| sed "s~{{ PACKAGE_NAME }}~${PACKAGE_NAME}~" \
|
||||
| sed "s~{{ DESCRIPTION }}~${PACKAGE_DESCRIPTION}~" \
|
||||
| sed "s~{{ PROGRAM }}~/${PACKAGE_PREFIX}/bin/${PACKAGE_NAME}~" \
|
||||
> ${PACKAGE_TMPDIR}/${PACKAGE_SYSUNITDIR}/${PACKAGE_NAME}.service
|
||||
|
||||
# Create rsyslog file
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_RSYSLOGDIR}
|
||||
cat ${TEMPLATE_DIR}/rsyslog.conf \
|
||||
| sed "s~{{ PROGRAM }}~${PACKAGE_NAME}~" \
|
||||
| sed "s~{{ LOG_FILE }}~${PACKAGE_LOGFILE}~" \
|
||||
> ${PACKAGE_TMPDIR}/${PACKAGE_RSYSLOGDIR}/49-${PACKAGE_NAME}.conf
|
||||
|
||||
# Create logrotate file
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_LOGROTATEDIR}
|
||||
cat ${TEMPLATE_DIR}/logrotate.conf \
|
||||
| sed "s~{{ LOG_FILE }}~${PACKAGE_LOGFILE}~" \
|
||||
> ${PACKAGE_TMPDIR}/${PACKAGE_LOGROTATEDIR}/${PACKAGE_NAME}.conf
|
||||
chmod 644 ${PACKAGE_TMPDIR}/${PACKAGE_LOGROTATEDIR}/${PACKAGE_NAME}.conf
|
||||
|
||||
# Cerate config file
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_ETCDIR}
|
||||
cat ${TEMPLATE_DIR}/config \
|
||||
| sed "s~{{ PACKAGE_NAME }}~${PACKAGE_NAME}~" \
|
||||
> ${PACKAGE_TMPDIR}/${PACKAGE_ETCDIR}/env
|
||||
|
||||
# Copy program
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_BINDIR}
|
||||
cp ${BASE_DIR}/../${PACKAGE_PROGRAM} ${PACKAGE_TMPDIR}/${PACKAGE_BINDIR}/${PACKAGE_NAME}
|
||||
|
||||
# Copy files.
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_SHAREDIR}
|
||||
cp ${BASE_DIR}/../README.md ${PACKAGE_TMPDIR}/${PACKAGE_SHAREDIR}
|
||||
|
||||
fakeroot dpkg-deb --build ${PACKAGE_TMPDIR} ${BUILD_DIR}/${PACKAGE_FULLNAME}.deb
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Simple script to create a tar archive for FreeBSD
|
||||
|
||||
PACKAGE_TMPDIR="${PACKAGE_TMPDIR}/freebsd"
|
||||
PACKAGE_RCDIR=/etc/rc.d
|
||||
PACKAGE_NEWSYSLOGDIR=etc/newsyslog.conf.d
|
||||
|
||||
# Common variables
|
||||
PID_FILE=/var/run/${PACKAGE_NAME}.pid
|
||||
|
||||
############################
|
||||
# Create rc file #
|
||||
############################
|
||||
|
||||
# rc does not like "-" in the filename.
|
||||
RC_NAME=$(echo ${PACKAGE_NAME} | sed "s~-~_~g")
|
||||
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_RCDIR}
|
||||
cat ${TEMPLATE_DIR}/rc.conf \
|
||||
| sed "s~{{ RC_NAME }}~${RC_NAME}~g" \
|
||||
| sed "s~{{ PID_FILE }}~${PID_FILE}~g" \
|
||||
| sed "s~{{ LOG_FILE }}~${PACKAGE_LOGFILE}~" \
|
||||
| sed "s~{{ DESCRIPTION }}~${PACKAGE_DESCRIPTION}~" \
|
||||
| sed "s~{{ PROGRAM }}~/${PACKAGE_BINDIR}/${PACKAGE_NAME}~" \
|
||||
> ${PACKAGE_TMPDIR}/${PACKAGE_RCDIR}/${RC_NAME}
|
||||
|
||||
# Must be executable.
|
||||
chmod 755 ${PACKAGE_TMPDIR}/${PACKAGE_RCDIR}/${RC_NAME}
|
||||
|
||||
############################
|
||||
# Create newsyslog config #
|
||||
############################
|
||||
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_NEWSYSLOGDIR}
|
||||
cat ${TEMPLATE_DIR}/newsyslog.conf \
|
||||
| sed "s~{{ LOG_FILE }}~${PACKAGE_LOGFILE}~" \
|
||||
| sed "s~{{ PID_FILE }}~${PID_FILE}~g" \
|
||||
> ${PACKAGE_TMPDIR}/${PACKAGE_NEWSYSLOGDIR}/${PACKAGE_NAME}.conf
|
||||
|
||||
|
||||
############################
|
||||
# Copy binary #
|
||||
############################
|
||||
|
||||
mkdir -p ${PACKAGE_TMPDIR}/${PACKAGE_BINDIR}
|
||||
cp ${BASE_DIR}/../${PACKAGE_PROGRAM} ${PACKAGE_TMPDIR}/${PACKAGE_BINDIR}
|
||||
|
||||
############################
|
||||
# Create archive #
|
||||
############################
|
||||
|
||||
TAR_FILENAME="${PACKAGE_NAME}-${PACKAGE_VERSION}-freebsd-${GOARCH}.tar.gz"
|
||||
|
||||
tar -C ${PACKAGE_TMPDIR} --owner root --group root -zcvf ${BUILD_DIR}/${TAR_FILENAME} .
|
||||
5
scripts/functions/log_install.sh
Normal file
5
scripts/functions/log_install.sh
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
log_install() {
|
||||
echo -e "[\e[34m::\e[0m] Installing: \e[32m${1}\e[0m"
|
||||
}
|
||||
5
scripts/info.sh
Normal file
5
scripts/info.sh
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Info
|
||||
PROGRAM_NAME=antelope-api-healthcheck
|
||||
PROGRAM_DESCRIPTION="HAproxy healthcheck program for Antelope Leap API."
|
||||
61
scripts/install.sh
Executable file
61
scripts/install.sh
Executable file
|
|
@ -0,0 +1,61 @@
|
|||
#!/bin/bash
|
||||
|
||||
############################
|
||||
# Read cli args #
|
||||
############################
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "$0 <type>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SYSTEM_TYPE=$1
|
||||
|
||||
if [ $# -gt 1 ]; then
|
||||
PREFIX=$2
|
||||
fi
|
||||
|
||||
if [ $# -gt 2 ]; then
|
||||
DESTDIR=$3
|
||||
fi
|
||||
|
||||
############################
|
||||
# Exported variables. #
|
||||
############################
|
||||
|
||||
export BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
export TEMPLATE_DIR=${BASE_DIR}/templates
|
||||
export BUILD_DIR=${BASE_DIR}/../build
|
||||
|
||||
# Export info variables
|
||||
set -o allexport
|
||||
source ${BASE_DIR}/info.sh
|
||||
set +o allexport
|
||||
|
||||
# Directories.
|
||||
export DESTDIR
|
||||
export PREFIX
|
||||
export BINDIR=${PREFIX}/bin
|
||||
export ETCDIR=/etc/${PROGRAM_NAME}
|
||||
export LOGDIR=/var/log
|
||||
export LOGFILE=${LOGDIR}/${PROGRAM_NAME}.log
|
||||
export SHAREDIR=${PREFIX}/share/${PROGRAM_NAME}
|
||||
|
||||
############################
|
||||
# Run script #
|
||||
############################
|
||||
|
||||
SCRIPT="${BASE_DIR}/install_${SYSTEM_TYPE}.sh"
|
||||
|
||||
# Check and call script
|
||||
if [ ! -x $SCRIPT ]; then
|
||||
echo "$SCRIPT not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "[\e[34m::\e[0m] Installing for system: \e[32m${SYSTEM_TYPE}\e[0m"
|
||||
if [ -n "$DESTDIR" ]; then
|
||||
echo -e "[\e[34m::\e[0m] Installing with root: \e[32m${DESTDIR}\e[0m"
|
||||
fi
|
||||
|
||||
bash $SCRIPT
|
||||
49
scripts/install_freebsd.sh
Executable file
49
scripts/install_freebsd.sh
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
#!/bin/bash
|
||||
# Simple script to install program files on FreeBSD systems
|
||||
|
||||
source ${BASE_DIR}/functions/log_install.sh
|
||||
|
||||
RCDIR=${DESTDIR}/etc/rc.d
|
||||
NEWSYSLOGDIR=${DESTDIR}/etc/newsyslog.conf.d
|
||||
|
||||
# Common variables
|
||||
PID_FILE=/var/run/${PROGRAM_NAME}.pid
|
||||
# rc does not like "-" in the filename.
|
||||
RC_NAME=$(echo ${PROGRAM_NAME} | sed "s~-~_~g")
|
||||
|
||||
############################
|
||||
# Create rc file #
|
||||
############################
|
||||
|
||||
log_install ${RCDIR}/${RC_NAME}
|
||||
mkdir -p ${RCDIR}
|
||||
cat ${TEMPLATE_DIR}/rc.conf \
|
||||
| sed "s~{{ RC_NAME }}~${RC_NAME}~g" \
|
||||
| sed "s~{{ PID_FILE }}~${PID_FILE}~g" \
|
||||
| sed "s~{{ LOG_FILE }}~${LOGFILE}~" \
|
||||
| sed "s~{{ PROGRAM_DESCRIPTION }}~${PROGRAM_DESCRIPTION}~" \
|
||||
| sed "s~{{ PROGRAM }}~${BINDIR}/${PROGRAM_NAME}~" \
|
||||
> ${RCDIR}/${RC_NAME}
|
||||
|
||||
# Must be executable.
|
||||
chmod 755 ${RCDIR}/${RC_NAME}
|
||||
|
||||
############################
|
||||
# Create newsyslog config #
|
||||
############################
|
||||
|
||||
log_install ${NEWSYSLOGDIR}/${PROGRAM_NAME}.conf
|
||||
mkdir -p ${NEWSYSLOGDIR}
|
||||
cat ${TEMPLATE_DIR}/newsyslog.conf \
|
||||
| sed "s~{{ LOG_FILE }}~${LOGFILE}~" \
|
||||
| sed "s~{{ PID_FILE }}~${PID_FILE}~g" \
|
||||
> ${NEWSYSLOGDIR}/${PROGRAM_NAME}.conf
|
||||
|
||||
|
||||
############################
|
||||
# Copy binary #
|
||||
############################
|
||||
|
||||
log_install ${DESTDIR}${BINDIR}/${PROGRAM_NAME}
|
||||
mkdir -p ${DESTDIR}/${BINDIR}
|
||||
cp ${BUILD_DIR}/${PROGRAM_NAME} ${DESTDIR}${BINDIR}/${PROGRAM_NAME}
|
||||
64
scripts/install_linux.sh
Executable file
64
scripts/install_linux.sh
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
#!/bin/bash
|
||||
# Script to install program files on linux systems
|
||||
|
||||
source ${BASE_DIR}/functions/log_install.sh
|
||||
|
||||
SYSTEMDDIR=${DESTDIR}/lib/systemd/system
|
||||
SYSTEMDLINKDIR=${DESTDIR}/etc/systemd/system
|
||||
RSYSLOGDIR=${DESTDIR}/etc/rsyslog.d
|
||||
SYSLOG_NG_DIR=${DESTDIR}/etc/syslog-ng/conf.d
|
||||
LOGROTATEDIR=${DESTDIR}/etc/logrotate.d
|
||||
|
||||
# Create service file
|
||||
log_install ${SYSTEMDDIR}/${PROGRAM_NAME}.service
|
||||
mkdir -p ${SYSTEMDDIR}
|
||||
cat ${TEMPLATE_DIR}/sysunit.service \
|
||||
| sed "s~{{ PROGRAM_NAME }}~${PROGRAM_NAME}~" \
|
||||
| sed "s~{{ PROGRAM_DESCRIPTION }}~${PROGRAM_DESCRIPTION}~" \
|
||||
| sed "s~{{ PROGRAM }}~${BINDIR}/${PROGRAM_NAME}~" \
|
||||
> ${SYSTEMDDIR}/${PROGRAM_NAME}.service
|
||||
|
||||
# Create systemd symlink
|
||||
log_install ${SYSTEMDLINKDIR}/${PROGRAM_NAME}.service
|
||||
mkdir -p ${SYSTEMDLINKDIR}
|
||||
ln -s -T /lib/systemd/system/${PROGRAM_NAME}.service ${SYSTEMDLINKDIR}/${PROGRAM_NAME}.service
|
||||
|
||||
# Create systemd/init.d config file
|
||||
log_install ${DESTDIR}/etc/sysconfig/${PROGRAM_NAME}
|
||||
mkdir -p ${DESTDIR}/etc/sysconfig
|
||||
cat ${TEMPLATE_DIR}/config \
|
||||
| sed "s~{{ PROGRAM_NAME }}~${PROGRAM_NAME}~" \
|
||||
> ${DESTDIR}/etc/sysconfig/${PROGRAM_NAME}
|
||||
|
||||
# Create rsyslog file
|
||||
log_install ${RSYSLOGDIR}/49-${PROGRAM_NAME}.conf
|
||||
mkdir -p ${RSYSLOGDIR}
|
||||
cat ${TEMPLATE_DIR}/rsyslog.conf \
|
||||
| sed "s~{{ PROGRAM }}~${PROGRAM_NAME}~" \
|
||||
| sed "s~{{ LOG_FILE }}~${LOGFILE}~" \
|
||||
> ${RSYSLOGDIR}/49-${PROGRAM_NAME}.conf
|
||||
|
||||
# Create syslog-ng file
|
||||
log_install ${SYSLOG_NG_DIR}/${PROGRAM_NAME}.conf
|
||||
mkdir -p ${SYSLOG_NG_DIR}
|
||||
cat ${TEMPLATE_DIR}/syslog-ng.conf \
|
||||
| sed "s~{{ PROGRAM }}~${PROGRAM_NAME}~" \
|
||||
| sed "s~{{ LOG_FILE }}~${LOGFILE}~" \
|
||||
> ${SYSLOG_NG_DIR}/${PROGRAM_NAME}.conf
|
||||
|
||||
# Create logrotate file
|
||||
log_install ${LOGROTATEDIR}/${PROGRAM_NAME}.conf
|
||||
mkdir -p ${LOGROTATEDIR}
|
||||
cat ${TEMPLATE_DIR}/logrotate.conf \
|
||||
| sed "s~{{ LOG_FILE }}~${LOGFILE}~" \
|
||||
> ${LOGROTATEDIR}/${PROGRAM_NAME}.conf
|
||||
chmod 644 ${LOGROTATEDIR}/${PROGRAM_NAME}.conf
|
||||
|
||||
# Copy program
|
||||
log_install ${DESTDIR}${SHAREDIR}
|
||||
mkdir -p ${DESTDIR}/${BINDIR}
|
||||
cp ${BUILD_DIR}/${PROGRAM_NAME} ${DESTDIR}${BINDIR}/${PROGRAM_NAME}
|
||||
|
||||
# Copy files.
|
||||
mkdir -p ${DESTDIR}${SHAREDIR}
|
||||
cp ${BASE_DIR}/../README.md ${DESTDIR}${SHAREDIR}
|
||||
38
scripts/package.sh
Executable file
38
scripts/package.sh
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/bash
|
||||
|
||||
############################
|
||||
# Read cli args #
|
||||
############################
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "$0 <type>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PKG_TYPE=$1
|
||||
|
||||
# Setup vars
|
||||
export BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
# export
|
||||
export PKGROOT
|
||||
export BUILDDIR
|
||||
|
||||
############################
|
||||
# Run script #
|
||||
############################
|
||||
|
||||
SCRIPT="${BASE_DIR}/package_${PKG_TYPE}.sh"
|
||||
|
||||
# Check and call script
|
||||
if [ ! -x $SCRIPT ]; then
|
||||
echo "$SCRIPT not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Export info variables
|
||||
set -o allexport
|
||||
source ${BASE_DIR}/info.sh
|
||||
set +o allexport
|
||||
|
||||
bash $SCRIPT
|
||||
7
scripts/package_freebsd.sh
Executable file
7
scripts/package_freebsd.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
PROGRAM_ARCH=$(go env GOARCH)
|
||||
TAR_FILENAME="${PROGRAM_NAME}-${PROGRAM_VERSION}-freebsd-${PROGRAM_ARCH}.tar.gz"
|
||||
|
||||
echo "Create archive: ${BUILDDIR}/${TAR_FILENAME}"
|
||||
tar -C ${PKGROOT} --owner root --group root -zcvf ${BUILDDIR}/${TAR_FILENAME} .
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# Default settings for {{ PACKAGE_NAME }}
|
||||
# Default settings for {{ PROGRAM_NAME }}
|
||||
#
|
||||
# This is sourced by the systemd unit file.
|
||||
# This is sourced by the systemd unit file or init.d
|
||||
|
||||
# Command line flags to pass to {{ PACKAGE_NAME }}
|
||||
# First is IP to listen to, second is port.
|
||||
#OPTS="127.0.0.1 1337"
|
||||
# Command line flags to pass to {{ PROGRAM_NAME }}
|
||||
# Positional agruments are IP to listen to, then port number.
|
||||
ANTELOPE_API_HEALTCHECK_OPTS="--log-format=logfmt 127.0.0.1 1337"
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Add the following lines to /etc/rc.conf to configure eosio_api_healthcheck:
|
||||
# Add the following lines to /etc/rc.conf to configure antelope_api_healthcheck:
|
||||
#
|
||||
# eosio_api_healthcheck_args : arguments to the command.
|
||||
# antelope_api_healthcheck_args : arguments to the command.
|
||||
#
|
||||
# eosio_api_healthcheck_logfile : file to log to (default /var/log/${name}.log)
|
||||
# antelope_api_healthcheck_logfile : file to log to (default /var/log/${name}.log)
|
||||
#
|
||||
|
||||
# PROVIDE: {{ RC_NAME }}
|
||||
|
|
@ -14,15 +14,15 @@
|
|||
. /etc/rc.subr
|
||||
|
||||
name="{{ RC_NAME }}"
|
||||
desc="{{ DESCRIPTION }}"
|
||||
logfile="${eosio_api_healthcheck_logfile:-{{ LOG_FILE }}}"
|
||||
desc="{{ PROGRAM_DESCRIPTION }}"
|
||||
logfile="${antelope_api_healthcheck_logfile:-{{ LOG_FILE }}}"
|
||||
pidfile="{{ PID_FILE }}"
|
||||
command="{{ PROGRAM }}"
|
||||
command_args="-p ${pidfile} -l ${logfile} ${eosio_api_healthcheck_args}"
|
||||
command_args="-p ${pidfile} -l ${logfile} ${antelope_api_healthcheck_args}"
|
||||
|
||||
start_cmd="${name}_start"
|
||||
|
||||
eosio_api_healthcheck_start()
|
||||
antelope_api_healthcheck_start()
|
||||
{
|
||||
echo "Starting ${name}"
|
||||
${command} ${command_args} 2>&1 &
|
||||
|
|
|
|||
3
scripts/templates/syslog-ng.conf
Normal file
3
scripts/templates/syslog-ng.conf
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
filter f_api-healthcheck { program("{{ PROGRAM }}"); };
|
||||
destination d_api-healthcheck { file("{{ LOG_FILE }}"); };
|
||||
log { source(s_src); filter(f_api-healthcheck); destination(d_api-healthcheck); };
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
[Unit]
|
||||
Description={{ DESCRIPTION }}
|
||||
Description={{ PROGRAM_DESCRIPTION }}
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
EnvironmentFile=-/etc/{{ PACKAGE_NAME }}/env
|
||||
EnvironmentFile=-/etc/sysconfig/{{ PROGRAM_NAME }}
|
||||
Type=simple
|
||||
ExecStart={{ PROGRAM }} $OPTS
|
||||
ExecStart={{ PROGRAM }} $ANTELOPE_API_HEALTCHECK_OPTS
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,68 @@
|
|||
#!/bin/bash
|
||||
# Simple script to make it easy to update the version number for the program.
|
||||
#
|
||||
# Debian
|
||||
# ----------------------------
|
||||
# For releasing debian packages, there must be a name and email associated with the version.
|
||||
# You can pass "-n|--name" and "-e|--email" as parameters to this script.
|
||||
#
|
||||
# You can if you want set the following enviroment variables in your shell to have your name and email be inserted without cli flags.
|
||||
# DEB_MAINT_NAME
|
||||
# DEB_MAINT_EMAIL
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "$0 <version>"
|
||||
function usage() {
|
||||
echo "Usage: ${0##*/} [ -h|--help ] [ -n|--name <value> ] [ -e|--email <value> ] [ --nodebchanges ] <version>"
|
||||
exit 1
|
||||
}
|
||||
|
||||
eval set -- "$(getopt -n "${0##*/}" -o "hn:e:" -l "help,name:,email:,nodebchanges" -- "$@")"
|
||||
|
||||
WRITE_DEBCHANGES=1
|
||||
while true; do
|
||||
|
||||
case $1 in
|
||||
-n|--name)
|
||||
shift
|
||||
DEB_MAINT_NAME=$1
|
||||
;;
|
||||
-e|--email)
|
||||
shift
|
||||
DEB_MAINT_EMAIL=$1
|
||||
;;
|
||||
--nodebchanges)
|
||||
WRITE_DEBCHANGES=0
|
||||
;;
|
||||
-h|--help) usage ;;
|
||||
--) shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[ $# -gt 0 ] || [ $? -eq 0 ] || usage
|
||||
|
||||
VERSION=$@
|
||||
|
||||
if [ ${WRITE_DEBCHANGES} -ne 0 ]; then
|
||||
# Update debian changelog
|
||||
ex debian/changelog <<EOF
|
||||
1 insert
|
||||
antelope-api-healthcheck (${VERSION}) unstable; urgency=medium
|
||||
|
||||
*
|
||||
|
||||
-- ${DEB_MAINT_NAME} <${DEB_MAINT_EMAIL}> $(date -R)
|
||||
|
||||
.
|
||||
xit
|
||||
EOF
|
||||
echo -e "[\e[34m::\e[0m] Inserted template in \e[1mdebian/changelog\e[0m. \e[33mMake sure you edit this file with the actual changes!\e[0m"
|
||||
else :
|
||||
echo -e "[\e[33m::\e[0m] Skipping \e[1mdebian/changelog\e[0m."
|
||||
fi
|
||||
|
||||
sed -i "s:PACKAGE_VERSION=\\\\\"\(.*\)\\\\\":PACKAGE_VERSION=\\\\\"$1\\\\\":g" Makefile
|
||||
sed -i "s~\print(\"Version:\ v\(.*\)\\\n\")~print(\\\"Version:\ v$1\\\n\\\")~g" src/main.go
|
||||
|
||||
# Update Makefile
|
||||
sed -i "s:PROGRAM_VERSION\(\s*\)=\(\s*\)\(.*\):PROGRAM_VERSION\1=\2$VERSION:g" Makefile
|
||||
echo -e "[\e[34m::\e[0m] Set PROGRAM_VERSION=\e[34m$VERSION\e[0m in \e[1mMakefile\e[0m"
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
contract_api "github.com/eosswedenorg-go/eos-contract-api-client"
|
||||
)
|
||||
|
||||
type EosioContract struct {
|
||||
client contract_api.Client
|
||||
block_time float64
|
||||
}
|
||||
|
||||
func NewEosioContract(url string, block_time float64) EosioContract {
|
||||
return EosioContract{
|
||||
client: contract_api.Client{
|
||||
Url: url,
|
||||
},
|
||||
block_time: block_time,
|
||||
}
|
||||
}
|
||||
|
||||
func (e EosioContract) LogInfo() LogParams {
|
||||
return LogParams{
|
||||
"type", "eosio-contract",
|
||||
"url", e.client.Url,
|
||||
"block_time", e.block_time,
|
||||
}
|
||||
}
|
||||
|
||||
// check_api - Validates head block time.
|
||||
// ---------------------------------------------------------
|
||||
func (e EosioContract) Call() (agentcheck.Response, string) {
|
||||
|
||||
h, err := e.client.GetHealth()
|
||||
if err != nil {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
||||
return resp, err.Error()
|
||||
}
|
||||
|
||||
// Check HTTP Status Code
|
||||
if h.HTTPStatusCode > 299 {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
|
||||
msg := "Taking offline because %v was received from backend"
|
||||
return resp, fmt.Sprintf(msg, h.HTTPStatusCode)
|
||||
}
|
||||
|
||||
// Check postgres
|
||||
if h.Data.Postgres.Status != "OK" {
|
||||
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down,
|
||||
fmt.Sprintf("Postgres: %s", h.Data.Postgres.Status))
|
||||
|
||||
msg := "Taking offline because Postgres reported '%s'"
|
||||
return resp, fmt.Sprintf(msg, h.Data.Postgres.Status)
|
||||
}
|
||||
|
||||
// Check redis
|
||||
if h.Data.Redis.Status != "OK" {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
|
||||
msg := "Taking offline because Redis reported '%s'"
|
||||
return resp, fmt.Sprintf(msg, h.Data.Redis.Status)
|
||||
}
|
||||
|
||||
// Validate head block.
|
||||
now := time.Now().In(time.UTC)
|
||||
diff := now.Sub(h.Data.Chain.HeadTime).Seconds()
|
||||
|
||||
if diff > e.block_time {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
|
||||
msg := "Taking offline because head block is lagging %.0f seconds"
|
||||
return resp, fmt.Sprintf(msg, diff)
|
||||
} else if diff < -e.block_time {
|
||||
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
|
||||
msg := "Taking offline because head block is %.0f seconds into the future"
|
||||
return resp, fmt.Sprintf(msg, diff)
|
||||
}
|
||||
|
||||
return agentcheck.NewStatusResponse(agentcheck.Up), "OK"
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
)
|
||||
|
||||
type ApiInterface interface {
|
||||
|
||||
// Returns Logging information
|
||||
LogInfo() LogParams
|
||||
|
||||
// Call api and validate it's status.
|
||||
Call() (agentcheck.Response, string)
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
package api
|
||||
|
||||
type LogParams []interface{}
|
||||
|
||||
func (p *LogParams) Add(field string, value interface{}) {
|
||||
*p = append(*p, field, value)
|
||||
}
|
||||
|
||||
func (p LogParams) ToSlice() []interface{} {
|
||||
return []interface{}(p)
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
)
|
||||
|
||||
type TestApi struct {
|
||||
response agentcheck.Response
|
||||
}
|
||||
|
||||
func parseResponse(resp string) (agentcheck.Response, error) {
|
||||
|
||||
parts := strings.SplitN(resp, "#", 2)
|
||||
|
||||
// Status with message
|
||||
if len(parts) > 1 {
|
||||
rtype := agentcheck.StatusMessageResponseType(parts[0])
|
||||
return agentcheck.NewStatusMessageResponse(rtype, parts[1]), nil
|
||||
}
|
||||
|
||||
// Only status.
|
||||
rtype := agentcheck.StatusResponseType(parts[0])
|
||||
return agentcheck.NewStatusResponse(rtype), nil
|
||||
}
|
||||
|
||||
func NewTestApi(response string) TestApi {
|
||||
|
||||
resp, _ := parseResponse(response)
|
||||
|
||||
return TestApi{
|
||||
response: resp,
|
||||
}
|
||||
}
|
||||
|
||||
func (t TestApi) LogInfo() LogParams {
|
||||
return LogParams{
|
||||
"type", "TestApi",
|
||||
"response", t.response,
|
||||
}
|
||||
}
|
||||
|
||||
func (t TestApi) Call() (agentcheck.Response, string) {
|
||||
return t.response, ""
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/eosswedenorg-go/eosapi"
|
||||
)
|
||||
|
||||
type EosioV1 struct {
|
||||
params eosapi.ReqParams
|
||||
block_time float64
|
||||
}
|
||||
|
||||
func NewEosioV1(params eosapi.ReqParams, block_time float64) EosioV1 {
|
||||
return EosioV1{
|
||||
params: params,
|
||||
block_time: block_time,
|
||||
}
|
||||
}
|
||||
|
||||
func (e EosioV1) LogInfo() LogParams {
|
||||
p := LogParams{
|
||||
"type", "eosio-v1",
|
||||
"url", e.params.Url,
|
||||
}
|
||||
|
||||
if len(e.params.Host) > 0 {
|
||||
p.Add("host", e.params.Host)
|
||||
}
|
||||
|
||||
p.Add("block_time", e.block_time)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (e EosioV1) Call() (agentcheck.Response, string) {
|
||||
|
||||
info, err := eosapi.GetInfo(e.params)
|
||||
if err != nil {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
||||
return resp, err.Error()
|
||||
}
|
||||
|
||||
// Check HTTP Status Code
|
||||
if info.HTTPStatusCode > 299 {
|
||||
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
|
||||
msg := "Taking offline because %v was received from backend"
|
||||
return resp, fmt.Sprintf(msg, info.HTTPStatusCode)
|
||||
}
|
||||
|
||||
// Validate head block.
|
||||
now := time.Now().In(time.UTC)
|
||||
diff := now.Sub(info.HeadBlockTime).Seconds()
|
||||
|
||||
if diff > e.block_time {
|
||||
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
|
||||
msg := "Taking offline because head block is lagging %.0f seconds"
|
||||
return resp, fmt.Sprintf(msg, diff)
|
||||
} else if diff < -e.block_time {
|
||||
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
|
||||
msg := "Taking offline because head block is %.0f seconds into the future"
|
||||
return resp, fmt.Sprintf(msg, diff)
|
||||
}
|
||||
return agentcheck.NewStatusResponse(agentcheck.Up), "OK"
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/eosswedenorg/eosio-api-healthcheck/src/utils"
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/eosswedenorg-go/eosapi"
|
||||
)
|
||||
|
||||
type EosioV2 struct {
|
||||
params eosapi.ReqParams
|
||||
offset int64
|
||||
}
|
||||
|
||||
func NewEosioV2(params eosapi.ReqParams, offset int64) EosioV2 {
|
||||
return EosioV2{
|
||||
params: params,
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
func (e EosioV2) LogInfo() LogParams {
|
||||
p := LogParams{
|
||||
"type", "eosio-v2",
|
||||
"url", e.params.Url,
|
||||
}
|
||||
|
||||
if len(e.params.Host) > 0 {
|
||||
p.Add("host", e.params.Host)
|
||||
}
|
||||
|
||||
p.Add("offset", e.offset)
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (e EosioV2) Call() (agentcheck.Response, string) {
|
||||
|
||||
health, err := eosapi.GetHealth(e.params)
|
||||
if err != nil {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
||||
return resp, err.Error()
|
||||
}
|
||||
|
||||
// Check HTTP Status Code
|
||||
if health.HTTPStatusCode > 299 {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
return resp, fmt.Sprintf("Taking offline because %v was received from backend", health.HTTPStatusCode)
|
||||
}
|
||||
|
||||
// Fetch elasticsearch and nodeos block numbers from json.
|
||||
var es_block int64 = 0
|
||||
var node_block int64 = 0
|
||||
|
||||
for _, v := range health.Health {
|
||||
if v.Name == "Elasticsearch" {
|
||||
es_block = utils.JsonGetInt64(v.Data["last_indexed_block"])
|
||||
} else if v.Name == "NodeosRPC" {
|
||||
node_block = utils.JsonGetInt64(v.Data["head_block_num"])
|
||||
}
|
||||
}
|
||||
|
||||
// Error out if ether or both are zero.
|
||||
if es_block == 0 || node_block == 0 {
|
||||
msg := fmt.Sprintf("Failed to get Elasticsearch and/or nodeos " +
|
||||
"block numbers (es: %d, eos: %d)", es_block, node_block)
|
||||
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
||||
return resp, msg
|
||||
}
|
||||
|
||||
// Check if ES is behind or in the future.
|
||||
diff := node_block - es_block;
|
||||
if diff > e.offset {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
return resp, fmt.Sprintf("Taking offline because Elastic is %d blocks behind", diff)
|
||||
} else if diff < -e.offset {
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
||||
return resp, fmt.Sprintf("Taking offline because Elastic is %d blocks into the future", -1 * diff)
|
||||
}
|
||||
return agentcheck.NewStatusResponse(agentcheck.Up), "OK"
|
||||
}
|
||||
161
src/main.go
161
src/main.go
|
|
@ -1,161 +0,0 @@
|
|||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
log "github.com/inconshreveable/log15"
|
||||
"github.com/eosswedenorg-go/pid"
|
||||
"github.com/pborman/getopt/v2"
|
||||
)
|
||||
|
||||
// Command line flags
|
||||
// ---------------------------------------------------------
|
||||
|
||||
var logFile string
|
||||
var pidFile string
|
||||
|
||||
// Global variables
|
||||
// ---------------------------------------------------------
|
||||
|
||||
// File descriptor to the current log file.
|
||||
var logfd *os.File
|
||||
|
||||
var logger log.Logger
|
||||
|
||||
// argv_listen_addr
|
||||
// Parse listen address from command line.
|
||||
// ---------------------------------------------------------
|
||||
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 += "1337"
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
||||
|
||||
func setLogFile() {
|
||||
|
||||
// Open file
|
||||
fd, err := os.OpenFile(logFile, os.O_APPEND | os.O_CREATE | os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
|
||||
// Try close if old descriptor is defined.
|
||||
if logfd != nil {
|
||||
if err = logfd.Close(); err != nil {
|
||||
logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Update variable and set log writer.
|
||||
logfd = fd
|
||||
logger.SetHandler(log.StreamHandler(logfd, log.LogfmtFormat()))
|
||||
}
|
||||
|
||||
// signalEventLoop()
|
||||
// Initialize event channel for OS signals
|
||||
// and runs an event loop in a separate thread.
|
||||
// ---------------------------------------------------------
|
||||
func signalEventLoop() {
|
||||
|
||||
// Setup a channel
|
||||
sig_ch := make(chan os.Signal, 1)
|
||||
|
||||
// subscribe to SIGHUP signal.
|
||||
signal.Notify(sig_ch, syscall.SIGHUP)
|
||||
|
||||
// Event loop (runs in a seperate thread)
|
||||
go func() {
|
||||
for {
|
||||
// Block until we get a signal.
|
||||
sig := <- sig_ch
|
||||
|
||||
switch sig {
|
||||
// SIGHUP is sent when logfile is rotated.
|
||||
case syscall.SIGHUP :
|
||||
msg := "SIGHUP (Logfile was rotated): "
|
||||
|
||||
if logfd != nil {
|
||||
setLogFile()
|
||||
msg += "Filedescriptor was updated"
|
||||
} else {
|
||||
msg += "No Filedescriptor to update (most likely uses standard out/err streams)"
|
||||
}
|
||||
|
||||
logger.Info(msg)
|
||||
default:
|
||||
logger.Warn("Unknown signal", "signal", sig)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// main
|
||||
// ---------------------------------------------------------
|
||||
func main() {
|
||||
|
||||
var version bool
|
||||
var usage bool
|
||||
var addr string;
|
||||
|
||||
logger = log.New()
|
||||
|
||||
// Command line parsing
|
||||
getopt.SetParameters("[ip] [port]")
|
||||
getopt.FlagLong(&usage, "help", 'h', "Print this help text")
|
||||
getopt.FlagLong(&version, "version", 'v', "Print version")
|
||||
getopt.FlagLong(&logFile, "log", 'l', "Path to log file", "file")
|
||||
getopt.FlagLong(&pidFile, "pid", 'p', "Path to pid file", "file")
|
||||
getopt.Parse()
|
||||
|
||||
if usage {
|
||||
getopt.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
if version {
|
||||
print("Version: v1.2.2\n")
|
||||
return;
|
||||
}
|
||||
|
||||
// Open logfile.
|
||||
if len(logFile) > 0 {
|
||||
setLogFile()
|
||||
}
|
||||
|
||||
logger.Info("Process is starting", "pid", pid.Get())
|
||||
|
||||
if len(pidFile) > 0 {
|
||||
logger.Info("Writing pidfile", "file", pidFile)
|
||||
err := pid.Save(pidFile)
|
||||
if err != nil {
|
||||
logger.Error("Failed to write pidfile", "msg", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Run the signal event loop.
|
||||
signalEventLoop()
|
||||
|
||||
addr = argv_listen_addr()
|
||||
|
||||
logger.Info("TCP Server started", "addr", addr)
|
||||
|
||||
// Start listening to TCP Connections
|
||||
spawnTcpServer(addr);
|
||||
}
|
||||
155
src/server.go
155
src/server.go
|
|
@ -1,155 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"strconv"
|
||||
"github.com/eosswedenorg/eosio-api-healthcheck/src/utils"
|
||||
"github.com/eosswedenorg/eosio-api-healthcheck/src/api"
|
||||
"github.com/eosswedenorg-go/eosapi"
|
||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||
"github.com/eosswedenorg-go/tcp_server"
|
||||
)
|
||||
|
||||
type arguments struct {
|
||||
api string
|
||||
url string
|
||||
host string
|
||||
num_blocks int
|
||||
}
|
||||
|
||||
func createApi(a *arguments) (api.ApiInterface, error) {
|
||||
|
||||
switch a.api {
|
||||
case "v1":
|
||||
return api.NewEosioV1(eosapi.ReqParams{Url: a.url, Host: a.host}, float64(a.num_blocks / 2)), nil
|
||||
case "v2":
|
||||
return api.NewEosioV2(eosapi.ReqParams{Url: a.url, Host: a.host}, int64(a.num_blocks)), nil
|
||||
case "contract":
|
||||
return api.NewEosioContract(a.url, float64(a.num_blocks / 2)), nil
|
||||
case "test":
|
||||
return api.NewTestApi(a.url), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Invalid API '%s'", a.api)
|
||||
}
|
||||
|
||||
// onTcpMessage callback function
|
||||
// ---------------------------------------------------------
|
||||
|
||||
func onTcpMessage(c *tcp_server.Client, args string) {
|
||||
|
||||
a := arguments{
|
||||
api: "v1",
|
||||
num_blocks: 10,
|
||||
}
|
||||
|
||||
// Parse arguments.
|
||||
// -------------------
|
||||
split := strings.Split(strings.TrimSpace(args), "|")
|
||||
|
||||
if len(split) < 2 {
|
||||
msg := "Invalid number of parameters in agent request"
|
||||
|
||||
logger.Warn("Agent request error", "message", msg, "args", split)
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
||||
|
||||
c.WriteString(resp.String())
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Old format: <url> <num_blocks> <api_version> <host>
|
||||
if utils.IsUrl(split[0]) {
|
||||
|
||||
logger.Warn("Deprecated format. Please change to the new format: <api>|<url>[|<num_blocks>|<host>]")
|
||||
|
||||
// 1. url (scheme + ip/domain + port)
|
||||
a.url = split[0]
|
||||
|
||||
// 2. num blocks
|
||||
if len(split) > 1 {
|
||||
num, err := strconv.ParseInt(split[1], 10, 32)
|
||||
if err == nil {
|
||||
a.num_blocks = int(num)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. api_version
|
||||
if len(split) > 2 {
|
||||
a.api = split[2]
|
||||
}
|
||||
|
||||
// 4. Host
|
||||
if len(split) > 3 {
|
||||
a.host = split[3]
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if len(split) < 2 {
|
||||
msg := "Invalid number of parameters in agent request"
|
||||
|
||||
logger.Warn("Agent request error", "message", msg, "args", split)
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
||||
|
||||
c.WriteString(resp.String())
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// 1. Api
|
||||
a.api = split[0]
|
||||
|
||||
// 2. url (scheme + ip/domain + port)
|
||||
a.url = split[1]
|
||||
|
||||
// 3. num blocks
|
||||
if len(split) > 2 {
|
||||
num, err := strconv.ParseInt(split[2], 10, 32)
|
||||
if err == nil {
|
||||
a.num_blocks = int(num)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Host
|
||||
if len(split) > 3 {
|
||||
a.host = split[3]
|
||||
}
|
||||
}
|
||||
|
||||
// Check api.
|
||||
// -------------------
|
||||
healthCheckApi, err := createApi(&a)
|
||||
if err != nil {
|
||||
logger.Warn("Agent request error", "message", err)
|
||||
resp := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
||||
|
||||
c.WriteString(resp.String())
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
|
||||
status, msg := healthCheckApi.Call()
|
||||
|
||||
logger.Info("API Check", append([]interface{}{
|
||||
"status", strings.TrimSpace(status.String())},
|
||||
healthCheckApi.LogInfo().ToSlice()...)...)
|
||||
|
||||
if msg != "OK" && len(msg) > 0 {
|
||||
logger.Warn("API Check Failed", "message", msg)
|
||||
}
|
||||
|
||||
// Report status to HAproxy
|
||||
c.WriteString(status.String())
|
||||
c.Close()
|
||||
}
|
||||
|
||||
// spawnTcpServer
|
||||
// ---------------------------------------------------------
|
||||
|
||||
func spawnTcpServer(addr string) {
|
||||
server := tcp_server.New(addr)
|
||||
server.OnMessage(onTcpMessage)
|
||||
server.Listen()
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
|
||||
package utils
|
||||
|
||||
import "net/url"
|
||||
|
||||
func IsUrl(str string) bool {
|
||||
u, err := url.Parse(str)
|
||||
return err == nil && u.Scheme != "" && u.Host != ""
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue