mirror of
https://github.com/eosswedenorg/antelope-api-healthcheck
synced 2026-07-02 11:43:42 +02:00
Compare commits
157 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 |
60 changed files with 2234 additions and 1831 deletions
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
|
|
@ -7,13 +7,19 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
cross-compile:
|
cross-compile:
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ linux, freebsd ]
|
os: [ linux, freebsd ]
|
||||||
arch: [ 386, amd64, arm, arm64 ]
|
arch: [ 386, amd64, arm, arm64 ]
|
||||||
name: Crosscompile - ${{matrix.os}}-${{matrix.arch}}
|
name: Crosscompile - ${{matrix.os}}-${{matrix.arch}}
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
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
|
- name: compile
|
||||||
id: compile
|
id: compile
|
||||||
|
|
@ -37,12 +43,18 @@ jobs:
|
||||||
|
|
||||||
package-ubuntu:
|
package-ubuntu:
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-20.04 ]
|
os: [ ubuntu-20.04 ]
|
||||||
name: Package - ${{matrix.os}}
|
name: Package - ${{matrix.os}}
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.18
|
||||||
|
|
||||||
- name: Install build dependencies
|
- name: Install build dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -95,12 +107,18 @@ jobs:
|
||||||
|
|
||||||
package-freebsd:
|
package-freebsd:
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
arch: [ 386, amd64, arm, arm64 ]
|
arch: [ 386, amd64, arm, arm64 ]
|
||||||
name: Package - FreeBSD (${{matrix.arch}})
|
name: Package - FreeBSD (${{matrix.arch}})
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.18
|
||||||
|
|
||||||
- name: Package
|
- name: Package
|
||||||
id: package
|
id: package
|
||||||
|
|
|
||||||
26
.github/workflows/test.yml
vendored
26
.github/workflows/test.yml
vendored
|
|
@ -5,16 +5,36 @@ on:
|
||||||
- pull_request
|
- pull_request
|
||||||
|
|
||||||
jobs:
|
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:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.16
|
go-version: 1.18
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: go test -v ./...
|
run: go test -v ./...
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
*.swp
|
*.swp
|
||||||
|
.vscode/
|
||||||
build/
|
build/
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
19
Makefile
19
Makefile
|
|
@ -1,24 +1,27 @@
|
||||||
|
|
||||||
PROGRAM_NAME = eosio-api-healthcheck
|
PROGRAM_NAME = antelope-api-healthcheck
|
||||||
export PROGRAM_VERSION = 1.2.5
|
export PROGRAM_VERSION = 1.4.6
|
||||||
|
|
||||||
GO = go
|
GO = go
|
||||||
PREFIX = /usr/local
|
PREFIX = /usr/local
|
||||||
export GOOS = $(shell go env GOOS)
|
export GOOS = $(shell $(GO) env GOOS)
|
||||||
export GOARCH = $(shell go env GOARCH)
|
export GOARCH = $(shell $(GO) env GOARCH)
|
||||||
GOBUILDFLAGS = -v -ldflags='-v -s -w -X main.VersionString=$(PROGRAM_VERSION)'
|
GOBUILDFLAGS = -v -ldflags='-v -s -w -X main.VersionString=$(PROGRAM_VERSION)'
|
||||||
|
|
||||||
DPKG_BUILDPACKAGE = dpkg-buildpackage
|
DPKG_BUILDPACKAGE = dpkg-buildpackage
|
||||||
DPKG_BUILDPACKAGE_FLAGS = -b -uc
|
DPKG_BUILDPACKAGE_FLAGS = -b -uc
|
||||||
|
|
||||||
SOURCES=src/main.go src/server.go src/parse_request.go
|
.PHONY: all build/$(PROGRAM_NAME) build/antelope-v1-mock-server clean package_debian
|
||||||
|
|
||||||
.PHONY: all build/$(PROGRAM_NAME) clean package_debian
|
|
||||||
all: build
|
all: build
|
||||||
build: build/$(PROGRAM_NAME)
|
build: build/$(PROGRAM_NAME)
|
||||||
|
|
||||||
build/$(PROGRAM_NAME) : $(SOURCES)
|
build/$(PROGRAM_NAME) : $(SOURCES)
|
||||||
$(GO) build -o $@ $(GOBUILDFLAGS) $^
|
$(GO) build -o $@ $(GOBUILDFLAGS) cmd/antelope-api-healtcheck/main.go
|
||||||
|
|
||||||
|
build/antelope-v1-mock-server:
|
||||||
|
$(GO) build -o $@ $(GOBUILDFLAGS) cmd/antelope-v1-mock-server/main.go
|
||||||
|
|
||||||
|
test-utils: build/antelope-v1-mock-server
|
||||||
|
|
||||||
test:
|
test:
|
||||||
$(GO) test -v ./...
|
$(GO) test -v ./...
|
||||||
|
|
|
||||||
18
README.md
18
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
|
## Compiling
|
||||||
|
|
||||||
You will need go-lang version `1.16` or later to compile the source.
|
You will need golang version `1.16` or later to compile the source.
|
||||||
|
|
||||||
compile with `compile.sh` script
|
compile with `compile.sh` script
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ ./compile.sh
|
./compile.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Execute `./compile.sh --help` to see all available flags to crosscompile for different systems/architectures.
|
Execute `./compile.sh --help` to see all available flags to crosscompile for different systems/architectures.
|
||||||
|
|
@ -27,15 +30,14 @@ The protocol is simple and has 4 rules.
|
||||||
3. Each parameter inside a `Request` is separated by `|`
|
3. Each parameter inside a `Request` is separated by `|`
|
||||||
4. Each response contains exactly one `status code` (see below)
|
4. Each response contains exactly one `status code` (see below)
|
||||||
|
|
||||||
|
|
||||||
### Request
|
### Request
|
||||||
|
|
||||||
The following parameters are supported in a request and are ordered from
|
The following parameters are supported in a request and are ordered from
|
||||||
first to last below:
|
first to last below:
|
||||||
|
|
||||||
| # | Name | Required | Description |
|
| # | Name | Required | Description |
|
||||||
| - | ---------- | ----------------------- | ----------------------------------------------------------------------------------------------- |
|
| - | ---------- | ------------------------- | -------------------------------------------------------------------------------------------- |
|
||||||
| 1 | api | Yes | Type of API to check against, `v1` = standard, `v2` = Hyperion, `contract` = eosio-contract-api |
|
| 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>)` |
|
| 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` |
|
| 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 |
|
| 4 | host | No (default from `url`) | Value to send in the `HTTP Host Header` to the API |
|
||||||
|
|
@ -46,7 +48,7 @@ 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
|
See [HAproxy documentation](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#5.2-agent-check) for more information
|
||||||
|
|
||||||
| code | Description |
|
| code | Description |
|
||||||
| --------- | ---------------------------------------------------------- |
|
| ----------- | ------------------------------------------------------------ |
|
||||||
| `up` | Api is healthy |
|
| `up` | Api is healthy |
|
||||||
| `down` | Api is not healthy |
|
| `down` | Api is not healthy |
|
||||||
| `fail` | The program failed to read the status from the api. |
|
| `fail` | The program failed to read the status from the api. |
|
||||||
|
|
|
||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
148
debian/changelog
vendored
148
debian/changelog
vendored
|
|
@ -1,3 +1,151 @@
|
||||||
|
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
|
eosio-api-healthcheck (1.2.5) unstable; urgency=medium
|
||||||
|
|
||||||
* Logging
|
* Logging
|
||||||
|
|
|
||||||
14
debian/control
vendored
14
debian/control
vendored
|
|
@ -1,19 +1,19 @@
|
||||||
Source: eosio-api-healthcheck
|
Source: antelope-api-healthcheck
|
||||||
Section: introspection
|
Section: introspection
|
||||||
Build-Depends:
|
Build-Depends:
|
||||||
debhelper (>= 11)
|
debhelper (>= 11)
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Vcs-Git: https://github.com/eosswedenorg/eos-api-healthcheck.git
|
Vcs-Git: https://github.com/eosswedenorg/antelope-api-healthcheck.git
|
||||||
Vcs-Browser: https://github.com/eosswedenorg/eos-api-healthcheck
|
Vcs-Browser: https://github.com/eosswedenorg/antelope-api-healthcheck
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Henrik Hautakoski <henrik@eossweden.org>
|
Maintainer: Henrik Hautakoski <henrik@eossweden.org>
|
||||||
|
|
||||||
Package: eosio-api-healthcheck
|
Package: antelope-api-healthcheck
|
||||||
Section: introspection
|
Section: introspection
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||||
Homepage: https://github.com/eosswedenorg/eos-api-healthcheck
|
Homepage: https://github.com/eosswedenorg/antelope-api-healthcheck
|
||||||
Description: HAproxy healthcheck program for EOSIO API.
|
Description: HAproxy healthcheck program for Leap API.
|
||||||
This package provides all the files needed to
|
This package provides all the files needed to
|
||||||
run the eos-api-healthcheck TCP Server
|
run the antelope-api-healthcheck TCP Server
|
||||||
|
|
|
||||||
6
debian/copyright
vendored
6
debian/copyright
vendored
|
|
@ -1,10 +1,10 @@
|
||||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
Upstream-Name: eosio-api-healthcheck
|
Upstream-Name: antelope-api-healthcheck
|
||||||
Upstream-Contact: Henrik Hautakoski <henrik@eossweden.org>
|
Upstream-Contact: Henrik Hautakoski <henrik@eossweden.org>
|
||||||
Source: https://github.com/eosswedenorg/eos-api-healthcheck.git
|
Source: https://github.com/eosswedenorg/antelope-api-healthcheck.git
|
||||||
|
|
||||||
Files: *
|
Files: *
|
||||||
Copyright: 2020-2022 Sw/eden
|
Copyright: 2020-2023 Sw/eden
|
||||||
License: MIT
|
License: MIT
|
||||||
|
|
||||||
License: MIT
|
License: MIT
|
||||||
|
|
|
||||||
63
go.mod
63
go.mod
|
|
@ -1,16 +1,59 @@
|
||||||
module github.com/eosswedenorg/eosio-api-healthcheck
|
module github.com/eosswedenorg/antelope-api-healthcheck
|
||||||
|
|
||||||
go 1.16
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/eosswedenorg-go/eos-contract-api-client v0.0.0-20221012162219-7bf5d16d1d5f
|
github.com/eosswedenorg-go/atomicasset v0.1.2
|
||||||
github.com/eosswedenorg-go/eosapi v0.1.1
|
github.com/eosswedenorg-go/haproxy v1.0.1
|
||||||
github.com/eosswedenorg-go/haproxy v0.1.1
|
github.com/eosswedenorg-go/leapapi v0.2.3
|
||||||
github.com/eosswedenorg-go/pid v1.0.1
|
github.com/eosswedenorg-go/pid v1.0.1
|
||||||
github.com/eosswedenorg-go/tcp_server v0.2.0
|
github.com/inconshreveable/log15 v3.0.0-testing.5+incompatible
|
||||||
github.com/go-stack/stack v1.8.1 // indirect
|
github.com/panjf2000/gnet/v2 v2.3.1
|
||||||
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
|
||||||
github.com/pborman/getopt/v2 v2.1.0
|
github.com/pborman/getopt/v2 v2.1.0
|
||||||
github.com/stretchr/testify v1.8.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
|
||||||
)
|
)
|
||||||
|
|
|
||||||
362
go.sum
362
go.sum
|
|
@ -1,358 +1,170 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
|
||||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
|
||||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
|
||||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
|
||||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
|
||||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
|
||||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
|
||||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
|
||||||
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
|
|
||||||
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/eosswedenorg-go/atomicasset v0.1.2 h1:bvz5tVVyP8EHSQo9CPFZjdl1lICBisfoRwJv/SURMCs=
|
||||||
github.com/eosswedenorg-go/eos-contract-api-client v0.0.0-20221012162219-7bf5d16d1d5f h1:28cfafllXW0tPZM+8xkwUlpMD5E+4+JLjZ2gNltVtf4=
|
github.com/eosswedenorg-go/atomicasset v0.1.2/go.mod h1:iRs02PLQmJAFBlukjrReSwti+Ga2Rp2wtnBGQElMVg4=
|
||||||
github.com/eosswedenorg-go/eos-contract-api-client v0.0.0-20221012162219-7bf5d16d1d5f/go.mod h1:5MKXTTcCZ3ZPmPAfOKwgVKA5S9BUltN8hY0/U2HqdDE=
|
github.com/eosswedenorg-go/haproxy v1.0.1 h1:N9tyQSvEDG9Fq+gBP0b7A8R5iZAqYCms2K6Nvqq8TfE=
|
||||||
github.com/eosswedenorg-go/eosapi v0.1.1 h1:+7DpTz6om3Xluj0ssY0LU5Tj6WKicV9Zlgr8Cso0dlw=
|
github.com/eosswedenorg-go/haproxy v1.0.1/go.mod h1:rBXDRd72ifA/IvsZUpW8Q4gR5rbV/4DAuKC/lqDrwWQ=
|
||||||
github.com/eosswedenorg-go/eosapi v0.1.1/go.mod h1:5r8ukl/BXbjeydPQNG/eE3+idpuY6XR654nyfcqkZbg=
|
github.com/eosswedenorg-go/leapapi v0.2.3 h1:2qGlP8wzZJCvjM9ol9t2uroCPMpV+wxF7lNl2NdG87M=
|
||||||
github.com/eosswedenorg-go/haproxy v0.1.1 h1:ClOhoK3uGn70KzGCKHdYNvHoU72whfNq3LbqT/lWlOY=
|
github.com/eosswedenorg-go/leapapi v0.2.3/go.mod h1:hyfjHswFjcnWMqOb+cYvTrT6l34S80GrFvfOdcYugnM=
|
||||||
github.com/eosswedenorg-go/haproxy v0.1.1/go.mod h1:WnDKkwYbgrpuKUOtUtWlGCgj43DMV+r4VKGdWSTYQeA=
|
|
||||||
github.com/eosswedenorg-go/pid v1.0.1 h1:W4AEnnNwb041SpNR1uTZ/KbJ0OTA5eqiqIR1Q5Ah6A0=
|
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/pid v1.0.1/go.mod h1:wiOB/JXGt4YA3+T0j0xmCGSc3Jxzb7Ti/Ftli1fgWu4=
|
||||||
github.com/eosswedenorg-go/tcp_server v0.1.1 h1:jNTThJKL+5dbPBGFCAIXQRoKx9YQer3yFp4unF0+iU8=
|
github.com/eosswedenorg-go/unixtime v0.1.1 h1:fTNxDtQOKncv/zAc3TzwLQLA/YBVM5nlbsFWVEyMkds=
|
||||||
github.com/eosswedenorg-go/tcp_server v0.1.1/go.mod h1:JvArWV6imAGj+NkrMJWSWTTe2a+WzqfWZUcZTH9y/+0=
|
github.com/eosswedenorg-go/unixtime v0.1.1/go.mod h1:knU247oYvgCQD9MLYBdpi7qD1pTRgkSAWr9jbUx3S6c=
|
||||||
github.com/eosswedenorg-go/tcp_server v0.2.0 h1:0EHNmdrOGgYuDE+yT6N1xDdZ1Amc18w6tczDnaHdJL8=
|
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||||
github.com/eosswedenorg-go/tcp_server v0.2.0/go.mod h1:3+3QlVIS8UY6eGDP78lg2km6/XFEk/JLbwOr5MfwW/g=
|
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
|
||||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
|
||||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
|
||||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
|
||||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
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-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 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
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/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/pprof v0.0.0-20221219190121-3cb0bae90811 h1:wORs2YN3R3ona/CXYuTvLM31QlgoNKHvlCNuArCDDCU=
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20221219190121-3cb0bae90811/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
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 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
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 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
|
||||||
github.com/imroc/req/v3 v3.7.6/go.mod h1:3JIicOKEDHfCSYYNLb/ObZNpx64EV5y40VlHMwhUCzU=
|
github.com/imroc/req/v3 v3.7.6/go.mod h1:3JIicOKEDHfCSYYNLb/ObZNpx64EV5y40VlHMwhUCzU=
|
||||||
github.com/imroc/req/v3 v3.24.1 h1:tT5MrwwgDQTUAWZqhPnxbsnaPMThCPGqcjiBwmZJJO4=
|
github.com/imroc/req/v3 v3.33.2 h1:mqphLIo++p+IPYdjgP/Wd5rqXUjKvuEIst2U+EsLIwQ=
|
||||||
github.com/imroc/req/v3 v3.24.1/go.mod h1:EluRnkfh8A39BmrCARYhcUrfGyR8qPw+O0BZyTy4j9k=
|
github.com/imroc/req/v3 v3.33.2/go.mod h1:cZ+7C3L/AYOr4tLGG16hZF90F1WzAdAdzt1xFSlizXY=
|
||||||
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac h1:n1DqxAo4oWPMvH1+v+DLYlMCecgumhhgnxAPdqDIFHI=
|
github.com/inconshreveable/log15 v3.0.0-testing.5+incompatible h1:VryeOTiaZfAzwx8xBcID1KlJCeoWSIpsNbSk+/D2LNk=
|
||||||
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
github.com/inconshreveable/log15 v3.0.0-testing.5+incompatible/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
|
||||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
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/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 h1:3if2kDW/boymUdO+4Qj/m4uaXMBSF6np9KEgg90cwH0=
|
||||||
github.com/liamylian/jsontime/v2 v2.0.0/go.mod h1:UHp1oAPqCBfspokvGmaGe0IAl2IgOpgOgDaKPcvcGGY=
|
github.com/liamylian/jsontime/v2 v2.0.0/go.mod h1:UHp1oAPqCBfspokvGmaGe0IAl2IgOpgOgDaKPcvcGGY=
|
||||||
github.com/lucas-clemente/quic-go v0.28.1 h1:Uo0lvVxWg5la9gflIF9lwa39ONq85Xq2D91YNEIslzU=
|
|
||||||
github.com/lucas-clemente/quic-go v0.28.1/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0=
|
|
||||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
|
||||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
|
||||||
github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
|
|
||||||
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
|
|
||||||
github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ=
|
|
||||||
github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
|
|
||||||
github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ=
|
|
||||||
github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
|
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
|
|
||||||
github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
|
|
||||||
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 h1:7m/WlWcSROrcK5NxuXaxYD32BZqe/LEEnBrWcH/cOqQ=
|
|
||||||
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
|
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
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-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 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
|
||||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
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 h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
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 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.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/panjf2000/ants/v2 v2.8.1 h1:C+n/f++aiW8kHCExKlpX6X+okmxKXP7DWLutxuAPuwQ=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
github.com/panjf2000/gnet/v2 v2.3.1 h1:J7vHkNxwsevVIw3u/6LCXgcnpGBk5iKqhQ2RMblGodc=
|
||||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
github.com/panjf2000/gnet/v2 v2.3.1/go.mod h1:Ik5lTy2nmBg9Uvjfcf2KRYs+EXVNOLyxPHpFOFlqu+M=
|
||||||
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
|
|
||||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
|
||||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
|
||||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
|
||||||
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
|
|
||||||
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
|
|
||||||
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
|
||||||
github.com/pborman/getopt/v2 v2.1.0 h1:eNfR+r+dWLdWmV8g5OlpyrTYHkhVNxHBdN2cCrJmOEA=
|
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/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.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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/quic-go/qtls-go1-18 v0.2.0/go.mod h1:moGulGHK7o6O8lSPSZNoOwcLvJKJ85vVNc7oJFD65bc=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/quic-go/qtls-go1-19 v0.2.0/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
github.com/quic-go/qtls-go1-20 v0.1.0 h1:d1PK3ErFy9t7zxKsG3NXBJXZjp/kMLoIb3y/kV54oAI=
|
||||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
github.com/quic-go/qtls-go1-20 v0.1.0/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
github.com/quic-go/quic-go v0.32.0 h1:lY02md31s1JgPiiyfqJijpu/UX/Iun304FI3yUqX7tA=
|
||||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
github.com/quic-go/quic-go v0.32.0/go.mod h1:/fCsKANhQIeD5l76c2JFU+07gVE3KaA0FP+0zMWwfwo=
|
||||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
github.com/sonh/qs v0.6.2 h1:ao0XNj2hXNdLW9Xk0L8zzEj1s3DjI7bDdG5QU8WUyxY=
|
||||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
github.com/sonh/qs v0.6.2/go.mod h1:ywKyX7vSo9R5dfgEQSCZ75tFzNkVUJZyK3/W6qGeHMQ=
|
||||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
|
||||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
|
||||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
|
||||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
|
||||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
|
||||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
|
||||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
|
||||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
|
||||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
|
||||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
|
||||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
|
||||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
|
||||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
|
||||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
|
||||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
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=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
|
||||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 h1:5oN1Pz/eDhCpbMbLstvIPa0b/BEQo6g6nwV3pLjfM6w=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20220111093109-d55c255bac03/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b h1:3ogNYyK4oIQdIKzTu68hQrr4iuVxF3AxKl9Aj/eDrw0=
|
|
||||||
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-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-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
|
||||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
|
||||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
|
||||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
|
||||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
|
||||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
|
||||||
gopkg.in/guregu/null.v4 v4.0.0 h1:1Wm3S1WEA2I26Kq+6vcW+w0gcDo44YKYD7YIEJNHDjg=
|
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/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI=
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
|
||||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
|
||||||
|
|
|
||||||
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"
|
||||||
|
}
|
||||||
|
|
@ -1,55 +1,69 @@
|
||||||
|
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"github.com/stretchr/testify/assert"
|
"testing"
|
||||||
|
|
||||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEosioV2LogInfo(t *testing.T) {
|
func TestAntelopeV2_Factory(t *testing.T) {
|
||||||
|
api := AntelopeV2Factory(ApiArguments{
|
||||||
|
Url: "https://api.v2.example.com",
|
||||||
|
Host: "host.example.com",
|
||||||
|
NumBlocks: 120,
|
||||||
|
})
|
||||||
|
|
||||||
api := NewEosioV2("https://api.v2.example.com", "host.example.com", 120)
|
expected := NewAntelopeV2("https://api.v2.example.com", "host.example.com", 120)
|
||||||
|
|
||||||
expected := LogParams{"type","eosio-v2","url","https://api.v2.example.com","host","host.example.com","offset",int64(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())
|
assert.Equal(t, expected, api.LogInfo())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEosioV2JsonFailure(t *testing.T) {
|
func TestAntelopeV2_JsonFailure(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
_, err := res.Write([]byte(`!//{invalid-json}!##`))
|
||||||
res.Write([]byte(`!//{invalid-json}!##`))
|
assert.NoError(t, err)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
api := NewEosioV2(srv.URL, "", 120)
|
api := NewAntelopeV2(srv.URL, "", 120)
|
||||||
check, _ := api.Call()
|
check, _ := api.Call(context.Background())
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||||
assert.Equal(t, expected, check)
|
assert.Equal(t, expected, check)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEosioV2HTTP500Down(t *testing.T) {
|
func TestAntelopeV2_HTTP500Failed(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
res.WriteHeader(500)
|
res.WriteHeader(500)
|
||||||
res.Write([]byte(`{}`))
|
_, err := res.Write([]byte(`{}`))
|
||||||
|
assert.NoError(t, err)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
api := NewEosioV2(srv.URL, "", 120)
|
api := NewAntelopeV2(srv.URL, "", 120)
|
||||||
check, status := api.Call()
|
check, status := api.Call(context.Background())
|
||||||
|
|
||||||
assert.Equal(t, "Taking offline because 500 was received from backend", status)
|
assert.Equal(t, "server returned HTTP 500 Internal Server Error", status)
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||||
assert.Equal(t, expected, check)
|
assert.Equal(t, expected, check)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEosioV2LaggingUp(t *testing.T) {
|
func TestAntelopeV2_LaggingUp(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
if req.URL.String() == "/v2/health" {
|
if req.URL.String() == "/v2/health" {
|
||||||
info := `{
|
info := `{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
|
|
@ -81,12 +95,13 @@ func TestEosioV2LaggingUp(t *testing.T) {
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
res.Write([]byte(info))
|
_, err := res.Write([]byte(info))
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
api := NewEosioV2(srv.URL, "", 500)
|
api := NewAntelopeV2(srv.URL, "", 500)
|
||||||
check, status := api.Call()
|
check, status := api.Call(context.Background())
|
||||||
|
|
||||||
assert.Equal(t, "OK", status)
|
assert.Equal(t, "OK", status)
|
||||||
|
|
||||||
|
|
@ -94,9 +109,8 @@ func TestEosioV2LaggingUp(t *testing.T) {
|
||||||
assert.Equal(t, expected, check)
|
assert.Equal(t, expected, check)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEosioV2LaggingDown(t *testing.T) {
|
func TestAntelopeV2_LaggingDown(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
if req.URL.String() == "/v2/health" {
|
if req.URL.String() == "/v2/health" {
|
||||||
info := `{
|
info := `{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
|
|
@ -128,12 +142,13 @@ func TestEosioV2LaggingDown(t *testing.T) {
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
res.Write([]byte(info))
|
_, err := res.Write([]byte(info))
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
api := NewEosioV2(srv.URL, "", 499)
|
api := NewAntelopeV2(srv.URL, "", 499)
|
||||||
check, status := api.Call()
|
check, status := api.Call(context.Background())
|
||||||
|
|
||||||
assert.Equal(t, "Taking offline because Elastic is 500 blocks behind", status)
|
assert.Equal(t, "Taking offline because Elastic is 500 blocks behind", status)
|
||||||
|
|
||||||
|
|
@ -141,9 +156,8 @@ func TestEosioV2LaggingDown(t *testing.T) {
|
||||||
assert.Equal(t, expected, check)
|
assert.Equal(t, expected, check)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEosioV2LaggingESInFutureUP(t *testing.T) {
|
func TestAntelopeV2_LaggingESInFutureUP(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
if req.URL.String() == "/v2/health" {
|
if req.URL.String() == "/v2/health" {
|
||||||
info := `{
|
info := `{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
|
|
@ -175,12 +189,13 @@ func TestEosioV2LaggingESInFutureUP(t *testing.T) {
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
res.Write([]byte(info))
|
_, err := res.Write([]byte(info))
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
api := NewEosioV2(srv.URL, "", 200)
|
api := NewAntelopeV2(srv.URL, "", 200)
|
||||||
check, status := api.Call()
|
check, status := api.Call(context.Background())
|
||||||
|
|
||||||
assert.Equal(t, "OK", status)
|
assert.Equal(t, "OK", status)
|
||||||
|
|
||||||
|
|
@ -188,9 +203,8 @@ func TestEosioV2LaggingESInFutureUP(t *testing.T) {
|
||||||
assert.Equal(t, expected, check)
|
assert.Equal(t, expected, check)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEosioV2LaggingESInFutureDown(t *testing.T) {
|
func TestAntelopeV2_LaggingESInFutureDown(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
if req.URL.String() == "/v2/health" {
|
if req.URL.String() == "/v2/health" {
|
||||||
info := `{
|
info := `{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
|
|
@ -222,12 +236,13 @@ func TestEosioV2LaggingESInFutureDown(t *testing.T) {
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
res.Write([]byte(info))
|
_, err := res.Write([]byte(info))
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
api := NewEosioV2(srv.URL, "", 200)
|
api := NewAntelopeV2(srv.URL, "", 200)
|
||||||
check, status := api.Call()
|
check, status := api.Call(context.Background())
|
||||||
|
|
||||||
assert.Equal(t, "Taking offline because Elastic is 201 blocks into the future", status)
|
assert.Equal(t, "Taking offline because Elastic is 201 blocks into the future", status)
|
||||||
|
|
||||||
|
|
@ -235,9 +250,8 @@ func TestEosioV2LaggingESInFutureDown(t *testing.T) {
|
||||||
assert.Equal(t, expected, check)
|
assert.Equal(t, expected, check)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEosioV2ElasticsFailed(t *testing.T) {
|
func TestAntelopeV2_ElasticsFailed(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
if req.URL.String() == "/v2/health" {
|
if req.URL.String() == "/v2/health" {
|
||||||
info := `{
|
info := `{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
|
|
@ -269,22 +283,22 @@ func TestEosioV2ElasticsFailed(t *testing.T) {
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
res.Write([]byte(info))
|
_, err := res.Write([]byte(info))
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
api := NewEosioV2(srv.URL, "", 500)
|
api := NewAntelopeV2(srv.URL, "", 500)
|
||||||
check, status := api.Call()
|
check, status := api.Call(context.Background())
|
||||||
|
|
||||||
assert.Equal(t, "Failed to get Elasticsearch and/or nodeos block numbers (es: 0, eos: 263148621)", status)
|
assert.Equal(t, "Failed to get Elasticsearch and/or nodeos block numbers (es: 0, eos: 263148621)", status)
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||||
assert.Equal(t, expected, check)
|
assert.Equal(t, expected, check)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEosioV2NodeosRPCFailed(t *testing.T) {
|
func TestAntelopeV2_NodeosRPCFailed(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
if req.URL.String() == "/v2/health" {
|
if req.URL.String() == "/v2/health" {
|
||||||
info := `{
|
info := `{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
|
|
@ -316,22 +330,22 @@ func TestEosioV2NodeosRPCFailed(t *testing.T) {
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
res.Write([]byte(info))
|
_, err := res.Write([]byte(info))
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
api := NewEosioV2(srv.URL, "", 500)
|
api := NewAntelopeV2(srv.URL, "", 500)
|
||||||
check, status := api.Call()
|
check, status := api.Call(context.Background())
|
||||||
|
|
||||||
assert.Equal(t, "Failed to get Elasticsearch and/or nodeos block numbers (es: 263148121, eos: 0)", status)
|
assert.Equal(t, "Failed to get Elasticsearch and/or nodeos block numbers (es: 263148121, eos: 0)", status)
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||||
assert.Equal(t, expected, check)
|
assert.Equal(t, expected, check)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEosioV2ElasticsNodeosRPCFailed(t *testing.T) {
|
func TestAntelopeV2_ElasticsNodeosRPCFailed(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
if req.URL.String() == "/v2/health" {
|
if req.URL.String() == "/v2/health" {
|
||||||
info := `{
|
info := `{
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
|
|
@ -353,15 +367,16 @@ func TestEosioV2ElasticsNodeosRPCFailed(t *testing.T) {
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
res.Write([]byte(info))
|
_, err := res.Write([]byte(info))
|
||||||
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
api := NewEosioV2(srv.URL, "", 500)
|
api := NewAntelopeV2(srv.URL, "", 500)
|
||||||
check, status := api.Call()
|
check, status := api.Call(context.Background())
|
||||||
|
|
||||||
assert.Equal(t, "Failed to get Elasticsearch and/or nodeos block numbers (es: 0, eos: 0)", status)
|
assert.Equal(t, "Failed to get Elasticsearch and/or nodeos block numbers (es: 0, eos: 0)", status)
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
expected := agentcheck.NewStatusMessageResponse(agentcheck.Fail, "")
|
||||||
assert.Equal(t, expected, check)
|
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, ""
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,25 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
"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) {
|
func TestNewDebugApi(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
response string
|
response string
|
||||||
|
|
@ -31,7 +44,6 @@ func TestNewDebugApi(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDebugApi_LogInfo(t *testing.T) {
|
func TestDebugApi_LogInfo(t *testing.T) {
|
||||||
|
|
||||||
expected := LogParams{"type", "Debug", "response", "up"}
|
expected := LogParams{"type", "Debug", "response", "up"}
|
||||||
|
|
||||||
api := DebugApi{
|
api := DebugApi{
|
||||||
|
|
@ -42,14 +54,13 @@ func TestDebugApi_LogInfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDebugApi_Call(t *testing.T) {
|
func TestDebugApi_Call(t *testing.T) {
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Stopped, "message")
|
expected := agentcheck.NewStatusMessageResponse(agentcheck.Stopped, "message")
|
||||||
|
|
||||||
api := DebugApi{
|
api := DebugApi{
|
||||||
response: expected,
|
response: expected,
|
||||||
}
|
}
|
||||||
|
|
||||||
response, msg := api.Call()
|
response, msg := api.Call(context.Background())
|
||||||
|
|
||||||
assert.Equal(t, response, expected)
|
assert.Equal(t, response, expected)
|
||||||
assert.Equal(t, msg, "")
|
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
|
package utils
|
||||||
|
|
||||||
// JsonGetInt64
|
// JsonGetInt64
|
||||||
|
|
@ -8,7 +7,7 @@ package utils
|
||||||
// if the type assertion fails, the function defaults 0 (zero).
|
// if the type assertion fails, the function defaults 0 (zero).
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
func JsonGetInt64(input interface{}) (int64) {
|
func JsonGetInt64(input interface{}) int64 {
|
||||||
v, res := input.(float64)
|
v, res := input.(float64)
|
||||||
if res {
|
if res {
|
||||||
return (int64)(v)
|
return (int64)(v)
|
||||||
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())
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Info
|
# Info
|
||||||
PROGRAM_NAME=eosio-api-healthcheck
|
PROGRAM_NAME=antelope-api-healthcheck
|
||||||
PROGRAM_DESCRIPTION="HAproxy healthcheck program for EOSIO API."
|
PROGRAM_DESCRIPTION="HAproxy healthcheck program for Antelope Leap API."
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ source ${BASE_DIR}/functions/log_install.sh
|
||||||
SYSTEMDDIR=${DESTDIR}/lib/systemd/system
|
SYSTEMDDIR=${DESTDIR}/lib/systemd/system
|
||||||
SYSTEMDLINKDIR=${DESTDIR}/etc/systemd/system
|
SYSTEMDLINKDIR=${DESTDIR}/etc/systemd/system
|
||||||
RSYSLOGDIR=${DESTDIR}/etc/rsyslog.d
|
RSYSLOGDIR=${DESTDIR}/etc/rsyslog.d
|
||||||
|
SYSLOG_NG_DIR=${DESTDIR}/etc/syslog-ng/conf.d
|
||||||
LOGROTATEDIR=${DESTDIR}/etc/logrotate.d
|
LOGROTATEDIR=${DESTDIR}/etc/logrotate.d
|
||||||
|
|
||||||
# Create service file
|
# Create service file
|
||||||
|
|
@ -37,6 +38,14 @@ cat ${TEMPLATE_DIR}/rsyslog.conf \
|
||||||
| sed "s~{{ LOG_FILE }}~${LOGFILE}~" \
|
| sed "s~{{ LOG_FILE }}~${LOGFILE}~" \
|
||||||
> ${RSYSLOGDIR}/49-${PROGRAM_NAME}.conf
|
> ${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
|
# Create logrotate file
|
||||||
log_install ${LOGROTATEDIR}/${PROGRAM_NAME}.conf
|
log_install ${LOGROTATEDIR}/${PROGRAM_NAME}.conf
|
||||||
mkdir -p ${LOGROTATEDIR}
|
mkdir -p ${LOGROTATEDIR}
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@
|
||||||
|
|
||||||
# Command line flags to pass to {{ PROGRAM_NAME }}
|
# Command line flags to pass to {{ PROGRAM_NAME }}
|
||||||
# Positional agruments are IP to listen to, then port number.
|
# Positional agruments are IP to listen to, then port number.
|
||||||
EOSIO_API_HEALTCHECK_OPTS="--log-format=logfmt 127.0.0.1 1337"
|
ANTELOPE_API_HEALTCHECK_OPTS="--log-format=logfmt 127.0.0.1 1337"
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
#!/bin/sh
|
#!/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 }}
|
# PROVIDE: {{ RC_NAME }}
|
||||||
|
|
@ -15,14 +15,14 @@
|
||||||
|
|
||||||
name="{{ RC_NAME }}"
|
name="{{ RC_NAME }}"
|
||||||
desc="{{ PROGRAM_DESCRIPTION }}"
|
desc="{{ PROGRAM_DESCRIPTION }}"
|
||||||
logfile="${eosio_api_healthcheck_logfile:-{{ LOG_FILE }}}"
|
logfile="${antelope_api_healthcheck_logfile:-{{ LOG_FILE }}}"
|
||||||
pidfile="{{ PID_FILE }}"
|
pidfile="{{ PID_FILE }}"
|
||||||
command="{{ PROGRAM }}"
|
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"
|
start_cmd="${name}_start"
|
||||||
|
|
||||||
eosio_api_healthcheck_start()
|
antelope_api_healthcheck_start()
|
||||||
{
|
{
|
||||||
echo "Starting ${name}"
|
echo "Starting ${name}"
|
||||||
${command} ${command_args} 2>&1 &
|
${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); };
|
||||||
|
|
@ -5,7 +5,7 @@ After=network.target
|
||||||
[Service]
|
[Service]
|
||||||
EnvironmentFile=-/etc/sysconfig/{{ PROGRAM_NAME }}
|
EnvironmentFile=-/etc/sysconfig/{{ PROGRAM_NAME }}
|
||||||
Type=simple
|
Type=simple
|
||||||
ExecStart={{ PROGRAM }} $EOSIO_API_HEALTCHECK_OPTS
|
ExecStart={{ PROGRAM }} $ANTELOPE_API_HEALTCHECK_OPTS
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ if [ ${WRITE_DEBCHANGES} -ne 0 ]; then
|
||||||
# Update debian changelog
|
# Update debian changelog
|
||||||
ex debian/changelog <<EOF
|
ex debian/changelog <<EOF
|
||||||
1 insert
|
1 insert
|
||||||
eosio-api-healthcheck (${VERSION}) unstable; urgency=medium
|
antelope-api-healthcheck (${VERSION}) unstable; urgency=medium
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DebugApi 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 NewDebugApi(response string) DebugApi {
|
|
||||||
|
|
||||||
resp, _ := parseResponse(response)
|
|
||||||
|
|
||||||
return DebugApi{
|
|
||||||
response: resp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d DebugApi) LogInfo() LogParams {
|
|
||||||
return LogParams{
|
|
||||||
"type", "Debug",
|
|
||||||
"response", strings.TrimSpace(d.response.String()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d DebugApi) Call() (agentcheck.Response, string) {
|
|
||||||
return d.response, ""
|
|
||||||
}
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/eosswedenorg/eosio-api-healthcheck/src/utils"
|
|
||||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
|
||||||
contract_api "github.com/eosswedenorg-go/eos-contract-api-client"
|
|
||||||
)
|
|
||||||
|
|
||||||
type EosioContract struct {
|
|
||||||
utils.Time
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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, "")
|
|
||||||
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).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,297 +0,0 @@
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"testing"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEosioContractLogInfo(t *testing.T) {
|
|
||||||
|
|
||||||
api := NewEosioContract("https://atomic.example.com", 120)
|
|
||||||
|
|
||||||
expected := LogParams{"type","eosio-contract","url","https://atomic.example.com","block_time",float64(120)}
|
|
||||||
|
|
||||||
assert.Equal(t, expected, api.LogInfo())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioContractSetTime(t *testing.T) {
|
|
||||||
|
|
||||||
expected := time.Date(2019, 3, 18, 20, 29, 32, 0, time.UTC)
|
|
||||||
|
|
||||||
api := NewEosioContract("", 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 TestEosioContractJsonFailure(t *testing.T) {
|
|
||||||
|
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
res.Write([]byte(`!//{invalid-json}!##`))
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioContract(srv.URL, 120)
|
|
||||||
check, _ := api.Call()
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioContractHTTP500Down(t *testing.T) {
|
|
||||||
|
|
||||||
var 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)
|
|
||||||
res.Write([]byte(`{}`))
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioContract(srv.URL, 120)
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
assert.Equal(t, "Taking offline because 500 was received from backend", status)
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioContractLaggingUp(t *testing.T) {
|
|
||||||
|
|
||||||
var 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")
|
|
||||||
res.Write([]byte(payload))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioContract(srv.URL, 120)
|
|
||||||
api.SetTime(time.Date(2025, 10, 8, 20, 7, 27, 0, time.UTC))
|
|
||||||
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
assert.Equal(t, "OK", status)
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusResponse(agentcheck.Up)
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioContractLaggingDown(t *testing.T) {
|
|
||||||
|
|
||||||
var 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")
|
|
||||||
res.Write([]byte(payload))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioContract(srv.URL, 120)
|
|
||||||
api.SetTime(time.Date(2018, 8, 5, 6, 53, 35, 0, time.UTC))
|
|
||||||
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
assert.Equal(t, "Taking offline because head block is lagging 121 seconds", status)
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioContractInFutureUp(t *testing.T) {
|
|
||||||
|
|
||||||
var 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")
|
|
||||||
res.Write([]byte(payload))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioContract(srv.URL, 120)
|
|
||||||
api.SetTime(time.Date(2024, 10, 15, 1, 9, 16, 500, time.UTC))
|
|
||||||
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
assert.Equal(t, "OK", status)
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusResponse(agentcheck.Up)
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioContractInFutureDown(t *testing.T) {
|
|
||||||
|
|
||||||
var 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")
|
|
||||||
res.Write([]byte(payload))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioContract(srv.URL, 120)
|
|
||||||
api.SetTime(time.Date(2002, 12, 29, 0, 45, 03, 500, time.UTC))
|
|
||||||
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
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 TestEosioContractRedisDown(t *testing.T) {
|
|
||||||
|
|
||||||
var 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")
|
|
||||||
res.Write([]byte(payload))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioContract(srv.URL, 120)
|
|
||||||
api.SetTime(time.Date(2015, 3, 11, 11, 19, 30, 500, time.UTC))
|
|
||||||
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
assert.Equal(t, "Taking offline because Redis reported 'DOWN'", status)
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioContractPostgresDown(t *testing.T) {
|
|
||||||
|
|
||||||
var 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")
|
|
||||||
res.Write([]byte(payload))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioContract(srv.URL, 120)
|
|
||||||
api.SetTime(time.Date(2019, 7, 11, 18, 6, 11, 500, time.UTC))
|
|
||||||
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
assert.Equal(t, "Taking offline because Postgres reported 'DOWN'", status)
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
@ -1,78 +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 EosioV1 struct {
|
|
||||||
utils.Time
|
|
||||||
client eosapi.Client
|
|
||||||
block_time float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEosioV1(url string, host string, block_time float64) EosioV1 {
|
|
||||||
|
|
||||||
api := EosioV1{
|
|
||||||
client: *eosapi.New(url),
|
|
||||||
block_time: block_time,
|
|
||||||
}
|
|
||||||
|
|
||||||
api.client.Host = host
|
|
||||||
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e EosioV1) LogInfo() LogParams {
|
|
||||||
p := LogParams{
|
|
||||||
"type", "eosio-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 EosioV1) Call() (agentcheck.Response, string) {
|
|
||||||
|
|
||||||
info, err := e.client.GetInfo()
|
|
||||||
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.
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
|
|
||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"testing"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEosioV1LogInfo(t *testing.T) {
|
|
||||||
|
|
||||||
api := NewEosioV1("https://api.v1.example.com", "host.example.com", 120)
|
|
||||||
|
|
||||||
expected := LogParams{"type","eosio-v1","url","https://api.v1.example.com","host","host.example.com","block_time",float64(120)}
|
|
||||||
|
|
||||||
assert.Equal(t, expected, api.LogInfo())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioV1SetTime(t *testing.T) {
|
|
||||||
|
|
||||||
expected := time.Date(2022, 2, 24, 13, 38, 0, 0, time.UTC)
|
|
||||||
|
|
||||||
api := NewEosioV1("", "", 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 TestEosioV1JsonFailure(t *testing.T) {
|
|
||||||
|
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
res.Write([]byte(`!//{invalid-json}!##`))
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioV1(srv.URL, "", 120)
|
|
||||||
check, _ := api.Call()
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Failed, "")
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioV1HTTP500Down(t *testing.T) {
|
|
||||||
|
|
||||||
var srv = httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
||||||
res.WriteHeader(500)
|
|
||||||
res.Write([]byte(`{}`))
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioV1(srv.URL, "", 120)
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
assert.Equal(t, "Taking offline because 500 was received from backend", status)
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioV1LaggingUp(t *testing.T) {
|
|
||||||
|
|
||||||
var 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"
|
|
||||||
}`
|
|
||||||
|
|
||||||
res.Write([]byte(info))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioV1(srv.URL, "", 60)
|
|
||||||
api.SetTime(time.Date(2022, 2, 24, 13, 38, 0, 0, time.UTC))
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
assert.Equal(t, "OK", status)
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusResponse(agentcheck.Up)
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioV1LaggingDown(t *testing.T) {
|
|
||||||
|
|
||||||
var 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"
|
|
||||||
}`
|
|
||||||
|
|
||||||
res.Write([]byte(info))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioV1(srv.URL, "", 60)
|
|
||||||
api.SetTime(time.Date(2018, time.January, 1, 13, 38, 2, 0, time.UTC))
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
assert.Equal(t, "Taking offline because head block is lagging 61 seconds", status)
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusMessageResponse(agentcheck.Down, "")
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEosioV1TimeInFutureUP(t *testing.T) {
|
|
||||||
|
|
||||||
var 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"
|
|
||||||
}`
|
|
||||||
|
|
||||||
res.Write([]byte(info))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioV1(srv.URL, "", 120)
|
|
||||||
api.SetTime(time.Date(2020, 9, 22, 9, 30, 0, 0, time.UTC))
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
assert.Equal(t, "OK", status)
|
|
||||||
|
|
||||||
expected := agentcheck.NewStatusResponse(agentcheck.Up)
|
|
||||||
assert.Equal(t, expected, check)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func TestEosioV1TimeInFutureDown(t *testing.T) {
|
|
||||||
|
|
||||||
var 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"
|
|
||||||
}`
|
|
||||||
|
|
||||||
res.Write([]byte(info))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
api := NewEosioV1(srv.URL, "", 120)
|
|
||||||
api.SetTime(time.Date(2019, time.April, 14, 12, 0, 0, 0, time.UTC))
|
|
||||||
check, status := api.Call()
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
@ -1,88 +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 {
|
|
||||||
client eosapi.Client
|
|
||||||
offset int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEosioV2(url string, host string, offset int64) EosioV2 {
|
|
||||||
|
|
||||||
api := EosioV2{
|
|
||||||
client: *eosapi.New(url),
|
|
||||||
offset: offset,
|
|
||||||
}
|
|
||||||
|
|
||||||
api.client.Host = host
|
|
||||||
|
|
||||||
return api
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e EosioV2) LogInfo() LogParams {
|
|
||||||
p := LogParams{
|
|
||||||
"type", "eosio-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 EosioV2) Call() (agentcheck.Response, string) {
|
|
||||||
|
|
||||||
health, err := e.client.GetHealth()
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
@ -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,34 +0,0 @@
|
||||||
|
|
||||||
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.Equal(t, expected, p.ToSlice())
|
|
||||||
}
|
|
||||||
185
src/main.go
185
src/main.go
|
|
@ -1,185 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"github.com/eosswedenorg/eosio-api-healthcheck/src/utils"
|
|
||||||
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
|
|
||||||
// ---------------------------------------------------------
|
|
||||||
|
|
||||||
// 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 and
|
|
||||||
var VersionString string = "-"
|
|
||||||
|
|
||||||
// File descriptor to the current log file.
|
|
||||||
var logfd *os.File
|
|
||||||
|
|
||||||
var logfmt log.Format
|
|
||||||
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, 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
|
|
||||||
func() {
|
|
||||||
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
|
|
||||||
// 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 addr string
|
|
||||||
var logFormatter *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")
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = argv_listen_addr()
|
|
||||||
|
|
||||||
// Start listening to TCP Connections
|
|
||||||
err := spawnTcpServer(addr)
|
|
||||||
if err == nil {
|
|
||||||
logger.Info("TCP Server started", "addr", addr)
|
|
||||||
|
|
||||||
// Run the signal event loop.
|
|
||||||
signalEventLoop()
|
|
||||||
} else {
|
|
||||||
log.Error("Failed to start tcp server", "error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Info("Shutdown")
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"github.com/eosswedenorg/eosio-api-healthcheck/src/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
type arguments struct {
|
|
||||||
url string
|
|
||||||
host string
|
|
||||||
num_blocks int
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseArguments(args []string) arguments {
|
|
||||||
|
|
||||||
a := arguments{
|
|
||||||
num_blocks: 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.num_blocks = int(num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Host
|
|
||||||
if len(args) > 2 {
|
|
||||||
a.host = args[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseRequest(request string) (api.ApiInterface, error) {
|
|
||||||
|
|
||||||
// Parse arguments.
|
|
||||||
// -------------------
|
|
||||||
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:])
|
|
||||||
|
|
||||||
switch p[0] {
|
|
||||||
case "v1":
|
|
||||||
return api.NewEosioV1(a.url, a.host, float64(a.num_blocks / 2)), nil
|
|
||||||
case "v2":
|
|
||||||
return api.NewEosioV2(a.url, a.host, int64(a.num_blocks)), nil
|
|
||||||
case "contract":
|
|
||||||
return api.NewEosioContract(a.url, float64(a.num_blocks / 2)), nil
|
|
||||||
case "debug":
|
|
||||||
return api.NewDebugApi(a.url), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("invalid API '%s'", p[0])
|
|
||||||
}
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
// "fmt"
|
|
||||||
"testing"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/eosswedenorg/eosio-api-healthcheck/src/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseWithInvalidApi(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 TestParseWithInvalidParams(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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EosioV1
|
|
||||||
// --------------------------------
|
|
||||||
|
|
||||||
func TestParseEosioV1(t *testing.T) {
|
|
||||||
|
|
||||||
expected := api.NewEosioV1("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 TestParseEosioV1WithBlockNumber(t *testing.T) {
|
|
||||||
|
|
||||||
expected := api.NewEosioV1("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 TestParseEosioV1Full(t *testing.T) {
|
|
||||||
|
|
||||||
expected := api.NewEosioV1("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())
|
|
||||||
}
|
|
||||||
|
|
||||||
// EosioV2
|
|
||||||
// --------------------------------
|
|
||||||
|
|
||||||
func TestParseEosioV2(t *testing.T) {
|
|
||||||
|
|
||||||
expected := api.NewEosioV2("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 TestParseEosioV2WithOffset(t *testing.T) {
|
|
||||||
|
|
||||||
expected := api.NewEosioV2("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 TestParseEosioV2Full(t *testing.T) {
|
|
||||||
|
|
||||||
expected := api.NewEosioV2("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())
|
|
||||||
}
|
|
||||||
|
|
||||||
// EosioContract
|
|
||||||
// --------------------------------
|
|
||||||
|
|
||||||
func TestParseEosioContract(t *testing.T) {
|
|
||||||
|
|
||||||
expected := api.NewEosioContract("http://api.contract.example.com", 5)
|
|
||||||
|
|
||||||
api, err := ParseRequest("contract|http://api.contract.example.com")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseEosioContractWithBlockTime(t *testing.T) {
|
|
||||||
|
|
||||||
expected := api.NewEosioContract("http://api.contract.example.com", 256)
|
|
||||||
|
|
||||||
api, err := ParseRequest("contract|http://api.contract.example.com|512")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, expected.LogInfo(), api.LogInfo())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseDebugApi(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())
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"github.com/eosswedenorg-go/haproxy/agentcheck"
|
|
||||||
"github.com/eosswedenorg-go/tcp_server"
|
|
||||||
)
|
|
||||||
|
|
||||||
// onTcpMessage callback function
|
|
||||||
// ---------------------------------------------------------
|
|
||||||
|
|
||||||
func onTcpMessage(c *tcp_server.Client, args string) {
|
|
||||||
|
|
||||||
// Check api.
|
|
||||||
// -------------------
|
|
||||||
healthCheckApi, err := ParseRequest(args)
|
|
||||||
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) error {
|
|
||||||
server := tcp_server.New(addr)
|
|
||||||
server.OnMessage(onTcpMessage)
|
|
||||||
|
|
||||||
err := server.Connect()
|
|
||||||
if err == nil {
|
|
||||||
go server.Listen()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestJsonGetInt64(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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
log "github.com/inconshreveable/log15"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_ParseLogFormatter(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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"testing"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTimeGetTimeWithDefaultValue(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 TestTimeGetTimeWithSetTime(t *testing.T) {
|
|
||||||
|
|
||||||
var ts Time
|
|
||||||
|
|
||||||
expected := time.Unix(1048722042, 500)
|
|
||||||
ts.SetTime(expected)
|
|
||||||
|
|
||||||
assert.Equal(t, expected, ts.GetTime())
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue