Merge pull request 'Prometheus Metrics' (#2) from prometheus-metrics into master

Reviewed-on: #2
This commit is contained in:
Arti Zirk 2023-08-10 14:55:47 +00:00
commit 24ca04b2aa
5 changed files with 134 additions and 41 deletions

1
.env
View File

@ -1,3 +1,4 @@
KDOORPI_DOOR=workshop
KDOORPI_API_ALLOWED=http://127.0.0.1:3333/allowed
KDOORPI_API_LONGPOLL=http://127.0.0.1:3333/longpoll
KDOORPI_API_SWIPE=http://127.0.0.1:3333/cardswipe

View File

@ -1,4 +1,13 @@
dev:
GOOS=linux GOARCH=arm64 go build .
.PHONY: all build build_arm64 dev
all: build
build:
go build -ldflags="-X main.Version=`git describe --dirty`" .
build_arm64:
GOOS=linux GOARCH=arm64 go build -ldflags="-X main.Version=`git describe --dirty`" .
dev: build_arm64
scp godoor workshopdoor:/tmp/
ssh workshopdoor 'mv -f /tmp/godoor ~/ && sudo systemctl restart godoor'

20
go.mod
View File

@ -3,8 +3,20 @@ module godoor
go 1.18
require (
github.com/joho/godotenv v1.5.1 // indirect
github.com/warthog618/gpiod v0.8.0 // indirect
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
github.com/joho/godotenv v1.5.1
github.com/prometheus/client_golang v1.16.0
github.com/warthog618/gpiod v0.8.0
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29
)
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
golang.org/x/sys v0.8.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
)

38
go.sum
View File

@ -1,10 +1,40 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/pilebones/go-udev v0.0.0-20180820235104-043677e09b13 h1:Y+ynP+0QIjUejN2tsuIlWOJG1CThJy6amRuWlBL94Vg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/warthog618/gpiod v0.8.0 h1:qxH9XVvWHpTxzWFSndBcujFyNH5zVRzHM63tcmm85o4=
github.com/warthog618/gpiod v0.8.0/go.mod h1:a7Csa+IJtDBZ39++zC/6Srjo01qWejt/5velrDWuNkY=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

103
godoor.go
View File

@ -7,11 +7,15 @@ import (
"encoding/json"
"fmt"
"github.com/joho/godotenv"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
"io"
"log"
"net/http"
"os"
"os/signal"
"runtime/debug"
"strconv"
"strings"
"sync"
@ -55,6 +59,7 @@ type Config struct {
wiegandB int
solenoid int
}
prometheusMetricsBind string
}
type KeepDoorOpen struct {
@ -67,22 +72,62 @@ type OpenedTimestamp struct {
Closed *time.Time
}
var Commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings {
if setting.Key == "vcs.revision" {
return setting.Value
}
}
}
return ""
}()
var Version string
var config Config
var globalLock sync.Mutex
var validUids ValidUids
var wiegand Wiegand
var keepDoorOpen KeepDoorOpen
var lastSyncedTimestamp *time.Time
var openDoorTimestamps []OpenedTimestamp
var (
godoorBuildInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "godoor_build_info",
Help: "Build Information",
}, []string{"version", "revision"})
lastSyncTimestamp = promauto.NewGauge(prometheus.GaugeOpts{
Name: "godoor_last_allow_list_sync_timestamp_seconds",
Help: "Last time list of card hashes was pulled from the server",
})
apiFailuresCount = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "godoor_api_request_failures_total",
Help: "HTTP API request failures count",
}, []string{"api", "endpoint"})
nrCardsInAllowList = promauto.NewGauge(prometheus.GaugeOpts{
Name: "godoor_allowed_card_hashes_total",
Help: "Number of card hashes in memory that can open the door",
})
doorOpenedCount = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "godoor_door_opens_total",
Help: "Number of times door was opened",
}, []string{"source"})
cardSwipesCount = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "godoor_swipes_total",
Help: "Number of times a card has been swiped",
}, []string{"status"})
)
func main() {
log.Printf("GoDoor ver: %s (%s)", Version, Commit)
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()
loadConfig()
godoorBuildInfo.WithLabelValues(Version, Commit).Set(1)
go func() {
setup()
}()
@ -113,6 +158,10 @@ func loadConfig() {
config.doorOpenTime = 3
}
_, config.mock = os.LookupEnv("KDOORPI_MOCK_HW")
config.prometheusMetricsBind = os.Getenv("KDOORPI_PROMETHEUS_METRICS_BIND")
if config.prometheusMetricsBind == "" {
config.prometheusMetricsBind = ":3334"
}
config.pins.wiegandA, err = strconv.Atoi(os.Getenv("KDOORPI_PIN_WIEGAND_A"))
if err != nil {
@ -142,16 +191,24 @@ func setup() {
}
log.Println("HW Setup done")
go runHttpServer()
http.DefaultClient.Timeout = 120 * time.Second
go func() {
for {
err := waitEvents()
if err != nil {
apiFailuresCount.WithLabelValues("longpoll", config.api.longpoll).Inc()
log.Printf("LongPoll for events failed: %v", err)
log.Println("Will try to LongPoll again in 120 seconds")
time.Sleep(120 * time.Second)
go reloadTokens()
go func() {
err := reloadTokens()
if err != nil {
apiFailuresCount.WithLabelValues("allowed", config.api.allowed).Inc()
}
}()
}
time.Sleep(1 * time.Second)
}
@ -165,13 +222,12 @@ func setup() {
if err == nil {
break
}
apiFailuresCount.WithLabelValues("allowed", config.api.allowed).Inc()
log.Printf("Initial token population failed. err: %v", err)
log.Println("Retrying in 10 seconds...")
time.Sleep(10 * time.Second)
}
go runHttpServer()
log.Println("Initial token population success")
go cardRunner(wiegand)
@ -180,26 +236,9 @@ func setup() {
}
func runHttpServer() {
http.HandleFunc("/lastsync", func(w http.ResponseWriter, r *http.Request) {
e := json.NewEncoder(w)
e.Encode(map[string]any{
"last_synced": lastSyncedTimestamp,
})
})
http.HandleFunc("/opened", func(w http.ResponseWriter, r *http.Request) {
e := json.NewEncoder(w)
e.Encode(map[string]any{
"open_timestamps": openDoorTimestamps,
})
})
http.HandleFunc("/isopen", func(w http.ResponseWriter, r *http.Request) {
e := json.NewEncoder(w)
open, _ := wiegand.IsDoorOpen()
e.Encode(map[string]any{
"open": open,
})
})
http.ListenAndServe(":3334", nil)
http.Handle("/metrics", promhttp.Handler())
log.Printf("Running prometheus metrics on http://%s/metrics", config.prometheusMetricsBind)
log.Fatal(http.ListenAndServe(config.prometheusMetricsBind, nil))
}
func OpenAndCloseDoor(w Wiegand) error {
@ -232,7 +271,6 @@ func OpenDoor(w Wiegand) error {
return nil
}
w.OpenDoor()
openDoorTimestamps = append(openDoorTimestamps, OpenedTimestamp{Opened: time.Now(), Closed: nil})
return nil
}
@ -242,8 +280,6 @@ func CloseDoor(w Wiegand) error {
return nil
}
w.CloseDoor()
t := time.Now()
openDoorTimestamps[len(openDoorTimestamps)-1].Closed = &t
return nil
}
@ -265,6 +301,7 @@ func cardRunner(w Wiegand) {
go func() {
err := sendSwipeEvent(hashedHex, ok)
if err != nil {
apiFailuresCount.WithLabelValues("swipe", config.api.swipe).Inc()
log.Println("Failed to send swipe event: %v", err)
}
}()
@ -272,10 +309,13 @@ func cardRunner(w Wiegand) {
if ok {
log.Println("Opening door")
err := OpenAndCloseDoor(w)
cardSwipesCount.WithLabelValues("accepted").Inc()
doorOpenedCount.WithLabelValues("card").Inc()
if err != nil {
log.Println("There was an error opening and closing the Door")
}
} else {
cardSwipesCount.WithLabelValues("denied").Inc()
log.Println("Unknown card")
}
@ -333,6 +373,7 @@ func waitEvents() error {
log.Printf("got server data: %q\n", data)
if strings.TrimSpace(data) == config.door {
err := OpenAndCloseDoor(wiegand)
doorOpenedCount.WithLabelValues("api").Inc()
if err != nil {
log.Println("There was an error opening and closing the Door")
}
@ -381,13 +422,13 @@ func reloadTokens() error {
totalCardCount = i
}
log.Printf("Got %d cards from server", totalCardCount)
nrCardsInAllowList.Set(float64(totalCardCount))
if cl.KeepOpenUntil != nil {
updateKeepOpenDoor(*cl.KeepOpenUntil)
}
t := time.Now()
lastSyncedTimestamp = &t
lastSyncTimestamp.SetToCurrentTime()
return nil
}