blob: bacbc5d4c64d533e2eb6eda6ade45383474a8213 [file] [log] [blame]
Serge Bazanski3d116b22021-03-27 15:43:18 +00001package main
2
3import (
4 "context"
Serge Bazanski3d116b22021-03-27 15:43:18 +00005 "flag"
Serge Bazanski3d116b22021-03-27 15:43:18 +00006 "net/http"
7 "sync"
8 "time"
9
10 "code.hackerspace.pl/hscloud/go/mirko"
11 "github.com/golang/glog"
12 "github.com/grpc-ecosystem/grpc-gateway/runtime"
13
14 pb "code.hackerspace.pl/hscloud/personal/q3k/shipstuck/proto"
15)
16
17type vessel struct {
Serge Bazanski5c1ab3c2021-03-27 15:57:16 +000018 Speed float64 `json:"ss"`
Serge Bazanski3d116b22021-03-27 15:43:18 +000019}
20
21// get retrieves the current status of the ship - returns true if stack, false
22// otherwise.
Serge Bazanskia4ae66b2021-03-29 13:26:37 +000023func get(ctx context.Context) (shipState, error) {
Serge Bazanski3df9d0c2021-03-29 14:23:37 +000024 // 2021/03/29/ 17:23 UTC+2 it's been freed!
25 return shipStateFreed, nil
Serge Bazanski3d116b22021-03-27 15:43:18 +000026}
27
28type shipState string
29
30const (
31 shipStateUnknown shipState = "UNKNOWN"
32 shipStateStuck shipState = "STUCK"
33 shipStateFreed shipState = "FREED"
Serge Bazanskia4ae66b2021-03-29 13:26:37 +000034 shipStateTowed shipState = "TOWED"
Serge Bazanski3d116b22021-03-27 15:43:18 +000035)
36
37type service struct {
38 lastStateMu sync.RWMutex
39 lastState shipState
40 lastStateTime time.Time
41}
42
43func (s *service) worker(ctx context.Context) {
44 update := func() {
45 state := shipStateUnknown
46 // shitty back off, good enough.
47 retries := 10
48 for {
49 stuck, err := get(ctx)
50 if err != nil {
51 glog.Warningf("get: %v", err)
52 if retries > 0 {
53 time.Sleep(60 * time.Second)
54 retries -= 1
55 } else {
56 glog.Errorf("giving up on get")
57 break
58 }
59 } else {
Serge Bazanskia4ae66b2021-03-29 13:26:37 +000060 state = stuck
Serge Bazanski3d116b22021-03-27 15:43:18 +000061 break
62 }
63 }
64
65 glog.Infof("New state: %v", state)
66 s.lastStateMu.Lock()
67 s.lastState = state
68 s.lastStateTime = time.Now()
69 s.lastStateMu.Unlock()
70 }
71
72 update()
73 ticker := time.NewTicker(15 * 60 * time.Second)
74 for {
75 select {
76 case <-ctx.Done():
77 return
78 case <-ticker.C:
79 update()
80 }
81 }
82}
83
84func timeMust(t time.Time, err error) time.Time {
85 if err != nil {
86 panic(err)
87 }
88 return t
89}
90
91var (
92 timeStuck = timeMust(time.Parse(
Serge Bazanski5da04942021-03-27 16:03:45 +000093 "At 15:04 Eastern European Time (-0700) on 2 January 2006",
94 "At 07:40 Eastern European Time (+0200) on 23 March 2021",
Serge Bazanski3d116b22021-03-27 15:43:18 +000095 ))
96)
97
98func (s *service) Status(ctx context.Context, req *pb.StatusRequest) (*pb.StatusResponse, error) {
99 s.lastStateMu.RLock()
100 state := s.lastState
101 lastChecked := s.lastStateTime
102 s.lastStateMu.RUnlock()
103
104 res := &pb.StatusResponse{
105 LastChecked: lastChecked.UnixNano(),
106 }
107 switch state {
108 case shipStateUnknown:
109 res.Current = pb.StatusResponse_STUCKNESS_UNKNOWN
110 case shipStateStuck:
111 res.Current = pb.StatusResponse_STUCKNESS_STUCK
112 res.Elapsed = time.Since(timeStuck).Nanoseconds()
113 case shipStateFreed:
114 res.Current = pb.StatusResponse_STUCKNESS_FREE
Serge Bazanskia4ae66b2021-03-29 13:26:37 +0000115 case shipStateTowed:
116 res.Current = pb.StatusResponse_STUCKNESS_TOWED
117 res.Elapsed = time.Since(timeStuck).Nanoseconds()
Serge Bazanski3d116b22021-03-27 15:43:18 +0000118 }
119
120 return res, nil
121}
122
123var (
124 flagPublicAddress string
125)
126
127func main() {
128 flag.StringVar(&flagPublicAddress, "public_address", "127.0.0.1:8080", "Public HTTP/JSON listen address")
129 flag.Parse()
130 m := mirko.New()
131 if err := m.Listen(); err != nil {
132 glog.Exitf("Listen(): %v", err)
133 }
134
135 s := &service{}
136 pb.RegisterShipStuckServer(m.GRPC(), s)
137
138 publicMux := runtime.NewServeMux()
139 publicSrv := http.Server{
140 Addr: flagPublicAddress,
141 Handler: publicMux,
142 }
143 go func() {
144 glog.Infof("REST listening on %s", flagPublicAddress)
145 if err := publicSrv.ListenAndServe(); err != nil {
146 glog.Exitf("public ListenAndServe: %v", err)
147 }
148 }()
149 if err := pb.RegisterShipStuckHandlerServer(m.Context(), publicMux, s); err != nil {
150 glog.Exitf("RegisterShipStuckHandlerSerever: %v", err)
151 }
152
153 go s.worker(m.Context())
154
155 if err := m.Serve(); err != nil {
156 glog.Exitf("Serve(): %v", err)
157 }
158
159 <-m.Done()
160}