Skip to content

Commit 2221b12

Browse files
authored
sentry impl
1 parent 53d1c95 commit 2221b12

File tree

6 files changed

+144
-8
lines changed

6 files changed

+144
-8
lines changed

.devcontainer/devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"ghcr.io/devcontainers/features/azure-cli:1": {},
66
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
77
"ghcr.io/devcontainers/features/github-cli:1": {},
8-
"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {}
8+
"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {},
99
},
1010
"customizations": {
1111
"vscode": {

example.env

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
# VALID_TELEGRAM_GROUPS=
55
# VALID_TELEGRAM_SENDERS=
66

7+
# JARVIS_CLOUD_BASE_ADDR
8+
79
# TRUSTED_ACTORS=
810

9-
# GIN_MODE=release
11+
# RELEASE=
1012

1113
# AUGUST_API_KEY=
1214
# AUGUST_INSTALLID=
@@ -15,6 +17,3 @@
1517
# AUGUST_ID=
1618
# AUGUST_LOCK_ID=
1719

18-
# DOMAIN=
19-
# PROTOCOL=
20-

server/cloudRouter.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ func CloudRouter(bot *tgbotapi.BotAPI) *gin.Engine {
2121
router.GET("/health", health)
2222
router.GET("/whoami", Auth(), whoami)
2323

24+
router.POST("/heartbeat", Auth(), sentryHeartbeatHandler)
25+
2426
// Knocks
2527
// router.POST("/welcome/:invite_code", welcome)
2628
// router.POST("/trustedknock", Auth(), trustedKnock)
@@ -51,6 +53,35 @@ func whoami(c *gin.Context) {
5153
c.String(http.StatusOK, authenticatedUser)
5254
}
5355

56+
func sentryHeartbeatHandler(c *gin.Context) {
57+
// Protected by 'TrustedHmacAuthentication' middleware
58+
authenticatedUser := c.MustGet("authenticatedUser").(string)
59+
60+
// Set no cache headers
61+
c.Header("Cache-Control", "no-cache, no-store, no-transform, must-revalidate, private, max-age=0")
62+
c.Header("Pragma", "no-cache")
63+
c.Header("Expires", "0")
64+
c.Header("X-Accel-Expires", "0")
65+
66+
// Parse JSON
67+
var heartbeat Heartbeat
68+
if err := c.ShouldBindJSON(&heartbeat); err != nil {
69+
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
70+
return
71+
}
72+
73+
// TEMP: Send to Telegram
74+
bot := c.MustGet(BOT_CONTEXT).(*BotExtended)
75+
bot.SendMessageToPrimaryTelegramGroup(fmt.Sprintf("Heartbeat from '%s' on '%s' at %d", authenticatedUser, heartbeat.HostName, heartbeat.Timestamp))
76+
77+
// Accept if we have not aborted.
78+
if !c.IsAborted() {
79+
var response HeartbeatResponse
80+
response.accepted = true
81+
c.JSON(http.StatusAccepted, response)
82+
}
83+
}
84+
5485
func trustedKnock(c *gin.Context) {
5586
// Protected by 'TrustedHmacAuthentication' middleware
5687
authenticatedUser := c.MustGet("authenticatedUser").(string)

server/nodeRouter.go renamed to server/fieldNodeRouter.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"github.com/warthog618/modem/serial"
1414
)
1515

16-
func NodeRouter() *gin.Engine {
16+
func FieldNodeRouter() *gin.Engine {
1717
router := gin.Default()
1818

1919
// Static

server/main.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"log"
66
"os"
7+
"strings"
78

89
"github.com/gin-gonic/gin"
910
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
@@ -36,6 +37,8 @@ func main() {
3637
log.Printf("Starting cloud")
3738
bot := SetupTelegram()
3839
initializeCloud(bot, mode)
40+
case "sentry":
41+
initializeSentry()
3942
// case "home":
4043
// log.Printf("Starting home")
4144
// bot := SetupTelegram()
@@ -65,14 +68,37 @@ func initializeCloud(bot *tgbotapi.BotAPI, mode string) {
6568
router.Run(fmt.Sprintf(":%s", PORT))
6669
}
6770

71+
func initializeSentry() {
72+
jarvisCloudBaseAddr := os.Getenv("JARVIS_CLOUD_BASE_ADDR")
73+
trustedActors, err := GetTrustedActors()
74+
if err != nil {
75+
log.Fatalf("Failed to retrieve trusted actors: %s\n", err)
76+
}
77+
78+
primaryActor := trustedActors[0]
79+
80+
// Check jarvisCloudBaseAddr contains protocol and no trailing slash (eg: https://example.com)
81+
if !strings.HasPrefix(jarvisCloudBaseAddr, "http") || jarvisCloudBaseAddr[len(jarvisCloudBaseAddr)-1:] == "/" {
82+
log.Fatalf("Misformed cloud base addr. Got: %s", jarvisCloudBaseAddr)
83+
}
84+
85+
sentry := Sentry{
86+
CloudBaseAddr: jarvisCloudBaseAddr,
87+
Actor: primaryActor,
88+
}
89+
90+
sentry.DoHeartbeat()
91+
92+
// TODO: sentry.Cron()
93+
}
94+
6895
func determineMode() string {
69-
DEFAULT_MODE := "cloud"
7096
if len(os.Args) >= 2 {
7197
return os.Args[1]
7298
}
7399
mode := os.Getenv("JARVIS_MODE")
74100
if mode != "" {
75101
return mode
76102
}
77-
return DEFAULT_MODE
103+
return mode
78104
}

server/sentry.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"log"
8+
"net/http"
9+
"os"
10+
"time"
11+
)
12+
13+
type Sentry struct {
14+
CloudBaseAddr string
15+
Actor Actor
16+
}
17+
18+
// Heartbeat object definition (JSON)
19+
type Heartbeat struct {
20+
Id string `json:"id"`
21+
Timestamp int64 `json:"timestamp"`
22+
HostName string `json:"hostname"`
23+
}
24+
25+
type HeartbeatResponse struct {
26+
accepted bool
27+
}
28+
29+
func (s Sentry) DoHeartbeat() {
30+
hostName, err := os.Hostname()
31+
if err != nil {
32+
hostName = "(unknown)"
33+
}
34+
35+
values := Heartbeat{
36+
Id: s.Actor.name,
37+
Timestamp: time.Now().Unix(),
38+
HostName: hostName,
39+
}
40+
s.sendHeartbeat(values)
41+
}
42+
43+
func (s Sentry) sendHeartbeat(values Heartbeat) {
44+
45+
json_data, err := json.Marshal(values)
46+
if err != nil {
47+
fmt.Println(err.Error())
48+
}
49+
50+
// Create new POST request object
51+
req, err := http.NewRequest("POST", s.CloudBaseAddr+"/heartbeat", bytes.NewBuffer(json_data))
52+
if err != nil {
53+
fmt.Println(err.Error())
54+
}
55+
56+
auth, nonce, err := GenerateAuthHeaderForPrimaryActor()
57+
if err != nil {
58+
fmt.Println(err.Error())
59+
}
60+
61+
// Add headers
62+
req.Header.Set("Content-Type", "application/json")
63+
req.Header.Set("Authorization", auth)
64+
req.Header.Set("X-Jarvis-Timestamp", nonce)
65+
66+
// Send request
67+
client := &http.Client{}
68+
resp, err := client.Do(req)
69+
if err != nil {
70+
fmt.Println(err.Error())
71+
}
72+
73+
// Close request
74+
defer resp.Body.Close()
75+
76+
// Parse response
77+
var res map[string]interface{}
78+
json.NewDecoder(resp.Body).Decode(&res)
79+
log.Println(res["json"])
80+
}

0 commit comments

Comments
 (0)