blob: bacbc5d4c64d533e2eb6eda6ade45383474a8213 [file] [log] [blame]
package main
import (
"context"
"flag"
"net/http"
"sync"
"time"
"code.hackerspace.pl/hscloud/go/mirko"
"github.com/golang/glog"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
pb "code.hackerspace.pl/hscloud/personal/q3k/shipstuck/proto"
)
type vessel struct {
Speed float64 `json:"ss"`
}
// get retrieves the current status of the ship - returns true if stack, false
// otherwise.
func get(ctx context.Context) (shipState, error) {
// 2021/03/29/ 17:23 UTC+2 it's been freed!
return shipStateFreed, nil
}
type shipState string
const (
shipStateUnknown shipState = "UNKNOWN"
shipStateStuck shipState = "STUCK"
shipStateFreed shipState = "FREED"
shipStateTowed shipState = "TOWED"
)
type service struct {
lastStateMu sync.RWMutex
lastState shipState
lastStateTime time.Time
}
func (s *service) worker(ctx context.Context) {
update := func() {
state := shipStateUnknown
// shitty back off, good enough.
retries := 10
for {
stuck, err := get(ctx)
if err != nil {
glog.Warningf("get: %v", err)
if retries > 0 {
time.Sleep(60 * time.Second)
retries -= 1
} else {
glog.Errorf("giving up on get")
break
}
} else {
state = stuck
break
}
}
glog.Infof("New state: %v", state)
s.lastStateMu.Lock()
s.lastState = state
s.lastStateTime = time.Now()
s.lastStateMu.Unlock()
}
update()
ticker := time.NewTicker(15 * 60 * time.Second)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
update()
}
}
}
func timeMust(t time.Time, err error) time.Time {
if err != nil {
panic(err)
}
return t
}
var (
timeStuck = timeMust(time.Parse(
"At 15:04 Eastern European Time (-0700) on 2 January 2006",
"At 07:40 Eastern European Time (+0200) on 23 March 2021",
))
)
func (s *service) Status(ctx context.Context, req *pb.StatusRequest) (*pb.StatusResponse, error) {
s.lastStateMu.RLock()
state := s.lastState
lastChecked := s.lastStateTime
s.lastStateMu.RUnlock()
res := &pb.StatusResponse{
LastChecked: lastChecked.UnixNano(),
}
switch state {
case shipStateUnknown:
res.Current = pb.StatusResponse_STUCKNESS_UNKNOWN
case shipStateStuck:
res.Current = pb.StatusResponse_STUCKNESS_STUCK
res.Elapsed = time.Since(timeStuck).Nanoseconds()
case shipStateFreed:
res.Current = pb.StatusResponse_STUCKNESS_FREE
case shipStateTowed:
res.Current = pb.StatusResponse_STUCKNESS_TOWED
res.Elapsed = time.Since(timeStuck).Nanoseconds()
}
return res, nil
}
var (
flagPublicAddress string
)
func main() {
flag.StringVar(&flagPublicAddress, "public_address", "127.0.0.1:8080", "Public HTTP/JSON listen address")
flag.Parse()
m := mirko.New()
if err := m.Listen(); err != nil {
glog.Exitf("Listen(): %v", err)
}
s := &service{}
pb.RegisterShipStuckServer(m.GRPC(), s)
publicMux := runtime.NewServeMux()
publicSrv := http.Server{
Addr: flagPublicAddress,
Handler: publicMux,
}
go func() {
glog.Infof("REST listening on %s", flagPublicAddress)
if err := publicSrv.ListenAndServe(); err != nil {
glog.Exitf("public ListenAndServe: %v", err)
}
}()
if err := pb.RegisterShipStuckHandlerServer(m.Context(), publicMux, s); err != nil {
glog.Exitf("RegisterShipStuckHandlerSerever: %v", err)
}
go s.worker(m.Context())
if err := m.Serve(); err != nil {
glog.Exitf("Serve(): %v", err)
}
<-m.Done()
}