Skip to content

Commit

Permalink
working version
Browse files Browse the repository at this point in the history
  • Loading branch information
e-asphyx committed Jun 13, 2024
1 parent 3c49b37 commit 47d94b4
Show file tree
Hide file tree
Showing 8 changed files with 318 additions and 42 deletions.
20 changes: 20 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"time"

tz "github.com/ecadlabs/gotez/v2"
)

type Config struct {
Listen string `yaml:"listen"`
URL string `yaml:"url"`
ChainID *tz.ChainID `yaml:"chain_id"`
Timeout time.Duration `yaml:"timeout"`
Tolerance time.Duration `yaml:"tolerance"`
ReconnectDelay time.Duration `yaml:"reconnect_delay"`
UseTimestamps bool `yaml:"use_timestamps"`
CheckBlockDelay bool `yaml:"check_block_delay"`
CheckBootstrapped bool `yaml:"check_bootstrapped"`
CheckSyncState bool `yaml:"check_sync_state"`
}
3 changes: 3 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
listen: :8080
url: http://ghostnet.ecadinfra.com:8080
chain_id: NetXnHfVqm9iesp
14 changes: 9 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ module github.com/ecadlabs/octez-ecad-sc

go 1.22.2

require github.com/ecadlabs/gotez/v2 v2.0.6
require (
github.com/ecadlabs/gotez/v2 v2.0.7
github.com/gorilla/mux v1.8.1
github.com/sirupsen/logrus v1.9.3
golang.org/x/sys v0.21.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/ecadlabs/pretty v0.0.0-20230412124801-f948fc689a04 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
)
16 changes: 9 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,32 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ecadlabs/gotez/v2 v2.0.6 h1:P7eQ2G+SO1tTV4NHnkdNlrOHxKDo1iF9m34HTLfS3b8=
github.com/ecadlabs/gotez/v2 v2.0.6/go.mod h1:QypK0m1eDPmB9R7Uvgmsfm+JS7Z5Y6dIbIq1tMVYayU=
github.com/ecadlabs/gotez/v2 v2.0.7 h1:D0gDFn+XeyoiQRhBt41tE/UEHoWoTwYo5UUGOlccOx8=
github.com/ecadlabs/gotez/v2 v2.0.7/go.mod h1:QypK0m1eDPmB9R7Uvgmsfm+JS7Z5Y6dIbIq1tMVYayU=
github.com/ecadlabs/pretty v0.0.0-20230412124801-f948fc689a04 h1:7WdblGykGxtGGtchW4kzTaJJO8Fm+JKhLzhttOOWr9k=
github.com/ecadlabs/pretty v0.0.0-20230412124801-f948fc689a04/go.mod h1:VApUlocsLMpp4hUXHxTTIlosebnwo0BM6e1hy78qTPM=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
62 changes: 62 additions & 0 deletions health_checker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package main

import (
"context"
"time"

tz "github.com/ecadlabs/gotez/v2"
"github.com/ecadlabs/gotez/v2/client"
log "github.com/sirupsen/logrus"
)

type HealthChecker struct {
Monitor *HeadMonitor
Client *client.Client
ChainID *tz.ChainID
Timeout time.Duration

CheckBlockDelay bool
CheckBootstrapped bool
CheckSyncState bool
}

type HealthStatus struct {
IsBootstrapped bool `json:"bootstrapped"`
IsSynced bool `json:"synced"`
BlockDelayOk bool `json:"block_delay_ok"`
}

func (h *HealthChecker) HealthStatus(ctx context.Context) (*HealthStatus, bool, error) {
var status HealthStatus
ok := true
if h.CheckBootstrapped || h.CheckSyncState {
c, cancel := context.WithTimeout(ctx, h.Timeout)
defer cancel()
resp, err := h.Client.IsBootstrapped(c, h.ChainID)
if err != nil {
return nil, false, err
}
if h.CheckBootstrapped {
status.IsBootstrapped = resp.Bootstrapped
ok = ok && status.IsBootstrapped
}
if h.CheckSyncState {
status.IsSynced = resp.SyncState == client.SyncStateSynced
ok = ok && status.IsSynced
}
}
if h.CheckBlockDelay {
status.BlockDelayOk = h.Monitor.Status()
ok = ok && status.BlockDelayOk
}

if !ok {
log.WithFields(log.Fields{
"chain_id": h.ChainID,
"bootstrapped": status.IsBootstrapped,
"synced": status.IsSynced,
"block_delay_ok": status.BlockDelayOk,
}).Warn("Chain health is not ok")
}
return &status, ok, nil
}
95 changes: 95 additions & 0 deletions logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package main

// Logging middleware inspired by github.com/urfave/negroni

import (
"net/http"
"time"

log "github.com/sirupsen/logrus"
)

func (rw *responseWriter) Status() int {
return rw.status
}

func (rw *responseWriter) WriteHeader(s int) {
rw.status = s
rw.ResponseWriter.WriteHeader(s)
}

func (rw *responseWriter) Write(data []byte) (int, error) {
if rw.status == 0 {
rw.status = http.StatusOK
}

return rw.ResponseWriter.Write(data)
}

var _ http.ResponseWriter = &responseWriter{}
var _ http.ResponseWriter = &responseWriterHijacker{}
var _ http.Hijacker = &responseWriterHijacker{}

// ResponseStatusWriter wraps http.ResponseWriter to save HTTP status code
type ResponseStatusWriter interface {
http.ResponseWriter
Status() int
}

type responseWriter struct {
http.ResponseWriter
status int
}

type responseWriterHijacker struct {
*responseWriter
http.Hijacker
}

func newResponseStatusWriter(w http.ResponseWriter) ResponseStatusWriter {
ret := &responseWriter{
ResponseWriter: w,
}

if h, ok := w.(http.Hijacker); ok {
return &responseWriterHijacker{
responseWriter: ret,
Hijacker: h,
}
}

return ret
}

// Logging is a logrus-enabled logging middleware
type Logging struct {
Logger *log.Logger
}

func (l *Logging) log() *log.Logger {
if l.Logger != nil {
return l.Logger
}
return log.StandardLogger()
}

// Handler wraps provided http.Handler with middleware
func (l *Logging) Handler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
timestamp := time.Now()

rw := newResponseStatusWriter(w)
h.ServeHTTP(rw, r)

fields := log.Fields{
"start_time": timestamp.Format(time.RFC3339),
"duration": time.Since(timestamp),
"status": rw.Status(),
"hostname": r.Host,
"method": r.Method,
"path": r.URL.Path,
}

l.log().WithFields(fields).Println(r.Method + " " + r.URL.Path)
})
}
117 changes: 117 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package main

import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"os/signal"
"time"

"flag"

"github.com/ecadlabs/gotez/v2/client"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"gopkg.in/yaml.v3"
)

const (
defaultListen = ":8080"
defaultTimeout = 30 * time.Second
defaultTolerance = 1 * time.Second
defaultReconnectDelay = 10 * time.Second
)

func main() {
logLevel := flag.String("l", "info", "Log level: [error, warn, info, debug, trace]")
confPath := flag.String("c", "", "Config file path")
flag.Parse()

ll, err := log.ParseLevel(*logLevel)
if err != nil {
log.Fatal(err)
}
log.SetLevel(ll)

conf := Config{
Listen: defaultListen,
Timeout: defaultTimeout,
Tolerance: defaultTolerance,
ReconnectDelay: defaultReconnectDelay,
CheckBlockDelay: true,
CheckBootstrapped: true,
CheckSyncState: true,
}

buf, err := os.ReadFile(*confPath)
if err != nil {
log.Fatal(err)
}
if err := yaml.Unmarshal(buf, &conf); err != nil {
log.Fatal(err)
}

cl := client.Client{
URL: conf.URL,
}

mon := HeadMonitor{
Client: &cl,
ChainID: conf.ChainID,
Timeout: conf.Timeout,
Tolerance: conf.Tolerance,
ReconnectDelay: conf.ReconnectDelay,
UseTimestamps: conf.UseTimestamps,
}
checker := HealthChecker{
Monitor: &mon,
Client: &cl,
ChainID: conf.ChainID,
Timeout: conf.Timeout,
CheckBlockDelay: conf.CheckBlockDelay,
CheckBootstrapped: conf.CheckBootstrapped,
CheckSyncState: conf.CheckSyncState,
}
if conf.CheckBlockDelay {
mon.Start()
defer mon.Stop(context.Background())
}

r := mux.NewRouter()
r.Methods("GET").Path("/health").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
status, ok, err := checker.HealthStatus(r.Context())
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "%v", err)
return
}
var code int
if ok {
code = http.StatusOK
} else {
code = http.StatusInternalServerError
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(code)
json.NewEncoder(w).Encode(status)
})
r.Use((&Logging{}).Handler)

srv := &http.Server{
Handler: r,
Addr: conf.Listen,
}
go func() {
log.Infof("Listening on %s", conf.Listen)
srv.ListenAndServe()
}()

c := make(chan os.Signal, 1)
signal.Notify(c, unix.SIGINT, unix.SIGTERM)
<-c

srv.Shutdown(context.Background())
}
Loading

0 comments on commit 47d94b4

Please sign in to comment.