Openstatus www.openstatus.dev
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(checker): http cleanup (#547)

* feat(checker): http cleanup

* feat(chan): close channel and send cancellation

authored by

Arthur EICHELBERGER and committed by
GitHub
e96ff6d1 a69302bc

+238 -106
+28 -1
apps/checker/go.mod
··· 3 3 go 1.21.4 4 4 5 5 require ( 6 + github.com/gin-gonic/gin v1.9.1 6 7 github.com/go-chi/chi/v5 v5.0.10 7 - github.com/google/uuid v1.4.0 8 + ) 9 + 10 + require ( 11 + github.com/bytedance/sonic v1.9.1 // indirect 12 + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 13 + github.com/gabriel-vasile/mimetype v1.4.2 // indirect 14 + github.com/gin-contrib/sse v0.1.0 // indirect 15 + github.com/go-playground/locales v0.14.1 // indirect 16 + github.com/go-playground/universal-translator v0.18.1 // indirect 17 + github.com/go-playground/validator/v10 v10.14.0 // indirect 18 + github.com/goccy/go-json v0.10.2 // indirect 19 + github.com/json-iterator/go v1.1.12 // indirect 20 + github.com/klauspost/cpuid/v2 v2.2.4 // indirect 21 + github.com/leodido/go-urn v1.2.4 // indirect 22 + github.com/mattn/go-isatty v0.0.19 // indirect 23 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 24 + github.com/modern-go/reflect2 v1.0.2 // indirect 25 + github.com/pelletier/go-toml/v2 v2.0.8 // indirect 26 + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 27 + github.com/ugorji/go/codec v1.2.11 // indirect 28 + golang.org/x/arch v0.3.0 // indirect 29 + golang.org/x/crypto v0.9.0 // indirect 30 + golang.org/x/net v0.10.0 // indirect 31 + golang.org/x/sys v0.8.0 // indirect 32 + golang.org/x/text v0.9.0 // indirect 33 + google.golang.org/protobuf v1.30.0 // indirect 34 + gopkg.in/yaml.v3 v3.0.1 // indirect 8 35 )
+86 -2
apps/checker/go.sum
··· 1 + github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 2 + github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 3 + github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 4 + github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 5 + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 6 + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 7 + github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 + github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 + github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 + github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 11 + github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 12 + github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 13 + github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 14 + github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 15 + github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 1 16 github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= 2 17 github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= 3 - github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= 4 - github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 18 + github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 19 + github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 20 + github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 21 + github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 22 + github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 23 + github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 24 + github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 25 + github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 26 + github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 27 + github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 28 + github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 29 + github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 30 + github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 31 + github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 32 + github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 33 + github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 34 + github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 35 + github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 36 + github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 37 + github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 38 + github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 39 + github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 40 + github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 41 + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 42 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 43 + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 44 + github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 45 + github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 46 + github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 47 + github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 48 + github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 49 + github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 50 + github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 51 + github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 52 + github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 53 + github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 54 + github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 55 + github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 56 + github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 57 + github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 58 + github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 59 + github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= 60 + github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 61 + github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 62 + github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 63 + github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 64 + github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 65 + golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 66 + golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 67 + golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 68 + golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 69 + golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 70 + golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 71 + golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 72 + golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 73 + golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 74 + golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 75 + golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 76 + golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 77 + golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 78 + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 79 + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 80 + google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 81 + google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 82 + google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 83 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 84 + gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 85 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 86 + gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 87 + gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 88 + rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
+86 -85
apps/checker/main.go
··· 1 1 package main 2 2 3 3 import ( 4 - "encoding/json" 4 + "context" 5 5 "fmt" 6 6 "net/http" 7 7 "os" 8 + "os/signal" 8 9 "strconv" 10 + "syscall" 9 11 10 - "github.com/go-chi/chi/v5" 11 - "github.com/go-chi/chi/v5/middleware" 12 + "github.com/gin-gonic/gin" 13 + "github.com/openstatushq/openstatus/apps/checker/request" 12 14 ) 13 15 14 - type InputData struct { 15 - WorkspaceId string `json:"workspaceId"` 16 - Url string `json:"url"` 17 - MonitorId string `json:"monitorId"` 18 - Method string `json:"method"` 19 - CronTimestamp int64 `json:"cronTimestamp"` 20 - Body string `json:"body"` 21 - Headers []struct { 22 - Key string `json:"key"` 23 - Value string `json:"value"` 24 - } `json:"headers,omitempty"` 25 - PagesIds []string `json:"pagesIds"` 26 - Status string `json:"status"` 27 - } 28 - 29 16 func main() { 30 - r := chi.NewRouter() 31 - r.Use(middleware.Logger) 17 + ctx, cancel := context.WithCancel(context.Background()) 18 + defer cancel() 32 19 33 - // That's the new checker sending to the correct ingest endpoint 34 - r.Post("/checker", func(w http.ResponseWriter, r *http.Request) { 35 - if r.Header.Get("Authorization") != "Basic "+os.Getenv("CRON_SECRET") { 36 - http.Error(w, "Unauthorized", 401) 37 - return 38 - } 39 - i, err := strconv.Atoi(r.Header.Get("X-CloudTasks-TaskRetryCount")) 40 - if err != nil { 41 - http.Error(w, "Something went whont", 400) 42 - return 43 - } 44 - // If something went wrong we only try it twice 45 - if i > 1 { 46 - w.WriteHeader(http.StatusOK) 47 - w.Write([]byte("Ok")) 20 + done := make(chan os.Signal, 1) 21 + signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) 22 + 23 + go func() { 24 + <-done 25 + cancel() 26 + }() 27 + 28 + // environment variables. 29 + flyRegion := env("FLY_REGION", "local") 30 + cronSecret := env("CRON_SECRET", "") 31 + 32 + httpClient := &http.Client{} 33 + defer httpClient.CloseIdleConnections() 34 + 35 + router := gin.New() 36 + router.POST("/checker", func(c *gin.Context) { 37 + ctx := c.Request.Context() 38 + _ = ctx // TODO: use ctx. 39 + 40 + if c.GetHeader("Authorization") != fmt.Sprintf("Basic %s", cronSecret) { 41 + c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) 48 42 return 49 43 } 50 44 51 - if r.Body == nil { 52 - http.Error(w, "Please send a request body", 400) 45 + switch i, err := strconv.Atoi(c.GetHeader("X-CloudTasks-TaskRetryCount")); { 46 + case err != nil: 47 + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid retry count"}) 48 + return 49 + case i > 1: 50 + // Why would that be OK? 51 + c.JSON(http.StatusOK, gin.H{"message": "ok"}) 53 52 return 54 53 } 55 - var u InputData 56 - region := os.Getenv("FLY_REGION") 57 54 58 - err = json.NewDecoder(r.Body).Decode(&u) 59 - 60 - fmt.Printf("🚀 Start checker for %+v \n", u) 61 - 62 - if err != nil { 63 - w.Write([]byte("Ok")) 64 - w.WriteHeader(200) 55 + var req request.CheckerRequest 56 + if err := c.ShouldBindJSON(&req); err != nil { 57 + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"}) 65 58 return 66 59 } 67 60 68 - client := &http.Client{} 69 - defer client.CloseIdleConnections() 70 - 71 - response, error := ping(client, u) 72 - 61 + response, error := ping(httpClient, req) 73 62 if error != nil { 74 63 // Add one more retry 75 - response, error = ping(client, u) 64 + response, error = ping(httpClient, req) 76 65 if error != nil { 77 66 sendToTinybirdNew(PingData{ 78 - Url: u.Url, 79 - Region: region, 67 + URL: req.URL, 68 + Region: flyRegion, 80 69 Message: error.Error(), 81 - CronTimestamp: u.CronTimestamp, 82 - Timestamp: u.CronTimestamp, 83 - MonitorId: u.MonitorId, 84 - WorkspaceId: u.WorkspaceId, 70 + CronTimestamp: req.CronTimestamp, 71 + Timestamp: req.CronTimestamp, 72 + MonitorID: req.MonitorID, 73 + WorkspaceID: req.WorkspaceID, 85 74 }) 86 - if u.Status == "active" { 75 + if req.Status == "active" { 87 76 updateStatus(UpdateData{ 88 - MonitorId: u.MonitorId, 77 + MonitorId: req.MonitorID, 89 78 Status: "error", 90 79 Message: error.Error(), 91 - Region: region, 80 + Region: flyRegion, 92 81 }) 93 82 } 94 - w.Write([]byte("Ok")) 95 - w.WriteHeader(200) 83 + 84 + c.JSON(http.StatusOK, gin.H{"message": "ok"}) 96 85 return 97 86 } 98 87 } 99 88 100 89 if response.StatusCode < 200 || response.StatusCode >= 300 { 101 90 // Add one more retry 102 - response, error = ping(client, u) 103 - if response.StatusCode < 200 || response.StatusCode >= 300 && u.Status == "active" { 91 + response, error = ping(httpClient, req) 92 + if response.StatusCode < 200 || response.StatusCode >= 300 && req.Status == "active" { 104 93 // If the status code is not within the 200 range, we update the status to error 105 94 updateStatus(UpdateData{ 106 - MonitorId: u.MonitorId, 95 + MonitorId: req.MonitorID, 107 96 Status: "error", 108 97 StatusCode: response.StatusCode, 109 - Region: region, 98 + Region: flyRegion, 110 99 }) 111 100 } 112 101 } 113 102 114 103 // If the status was error and the status code is within the 200 range, we update the status to active 115 - if u.Status == "error" && response.StatusCode >= 200 && response.StatusCode < 300 { 104 + if req.Status == "error" && response.StatusCode >= 200 && response.StatusCode < 300 { 116 105 // If the status was error, we update it to active 117 106 updateStatus(UpdateData{ 118 - MonitorId: u.MonitorId, 107 + MonitorId: req.MonitorID, 119 108 Status: "active", 120 - Region: region, 109 + Region: flyRegion, 121 110 StatusCode: response.StatusCode, 122 111 }) 123 112 } 124 113 // We send the data to Tinybird 125 114 sendToTinybirdNew(response) 126 115 127 - fmt.Printf("⏱️ End checker for %v with latency %d and statusCode %d", u, response.Latency, response.StatusCode) 128 - w.Write([]byte("Ok")) 129 - w.WriteHeader(200) 116 + c.JSON(http.StatusOK, gin.H{"message": "ok"}) 117 + return 118 + }) 119 + 120 + router.GET("/ping", func(c *gin.Context) { 121 + c.JSON(http.StatusOK, gin.H{"message": "pong", "fly_region": flyRegion}) 130 122 return 131 123 }) 132 124 133 - r.Get("/ping", func(w http.ResponseWriter, r *http.Request) { 134 - data := struct { 135 - Ping string `json:"ping"` 136 - FlyRegion string `json:"fly_region"` 137 - }{ 138 - Ping: "pong", 139 - FlyRegion: os.Getenv("FLY_REGION"), 125 + httpServer := &http.Server{ 126 + Addr: fmt.Sprintf("0.0.0.0:%s", env("PORT", "8080")), 127 + Handler: router, 128 + } 129 + 130 + go func() { 131 + if err := httpServer.ListenAndServe(); err != nil { 132 + // Add structured logs. 133 + cancel() 140 134 } 135 + }() 141 136 142 - w.Header().Set("Content-Type", "application/json") 143 - w.WriteHeader(http.StatusOK) 144 - json.NewEncoder(w).Encode(data) 137 + <-ctx.Done() 138 + if err := httpServer.Shutdown(ctx); err != nil { 139 + // Add structured logs. 145 140 return 146 - }) 141 + } 142 + } 147 143 148 - http.ListenAndServe(":8080", r) 144 + func env(key, fallback string) string { 145 + if value, ok := os.LookupEnv(key); ok { 146 + return value 147 + } 148 + 149 + return fallback 149 150 }
+16 -14
apps/checker/ping.go
··· 9 9 "net/url" 10 10 "os" 11 11 "time" 12 + 13 + "github.com/openstatushq/openstatus/apps/checker/request" 12 14 ) 13 15 14 16 type PingData struct { 15 - WorkspaceId string `json:"workspaceId"` 16 - MonitorId string `json:"monitorId"` 17 + WorkspaceID string `json:"workspaceId"` 18 + MonitorID string `json:"monitorId"` 17 19 Timestamp int64 `json:"timestamp"` 18 20 StatusCode int `json:"statusCode,omitempty"` 19 21 Latency int64 `json:"latency"` 20 22 CronTimestamp int64 `json:"cronTimestamp"` 21 - Url string `json:"url"` 23 + URL string `json:"url"` 22 24 Region string `json:"region"` 23 25 Message string `json:"message,omitempty"` 24 26 } ··· 63 65 64 66 } 65 67 66 - func ping(client *http.Client, inputData InputData) (PingData, error) { 68 + func ping(client *http.Client, inputData request.CheckerRequest) (PingData, error) { 67 69 68 70 region := os.Getenv("FLY_REGION") 69 - request, err := http.NewRequest(inputData.Method, inputData.Url, bytes.NewReader([]byte(inputData.Body))) 70 - 71 + request, err := http.NewRequest(inputData.Method, inputData.URL, bytes.NewReader([]byte(inputData.Body))) 71 72 if err != nil { 72 73 return PingData{}, fmt.Errorf("Unable to create request: %w", err) 73 74 } 75 + 74 76 request.Header.Set("User-Agent", "OpenStatus/1.0") 75 77 76 78 // Setting headers ··· 89 91 if urlErr.Timeout() { 90 92 return PingData{ 91 93 Latency: latency, 92 - MonitorId: inputData.MonitorId, 94 + MonitorID: inputData.MonitorID, 93 95 Region: region, 94 - WorkspaceId: inputData.WorkspaceId, 96 + WorkspaceID: inputData.WorkspaceID, 95 97 Timestamp: time.Now().UTC().UnixMilli(), 96 - Url: inputData.Url, 98 + URL: inputData.URL, 97 99 Message: fmt.Sprintf("Timeout after %d ms", latency), 98 100 }, nil 99 101 } 100 102 } 101 103 102 - return PingData{}, fmt.Errorf("Error with monitor %s: %w", inputData.Url, err) 104 + return PingData{}, fmt.Errorf("Error with monitor %s: %w", inputData.URL, err) 103 105 } 104 106 defer response.Body.Close() 105 107 106 108 _, err = io.ReadAll(response.Body) 107 109 108 110 if err != nil { 109 - return PingData{}, fmt.Errorf("Error while reading body from %s: %w", inputData.Url, err) 111 + return PingData{}, fmt.Errorf("Error while reading body from %s: %w", inputData.URL, err) 110 112 } 111 113 return PingData{ 112 114 Latency: latency, 113 115 StatusCode: response.StatusCode, 114 - MonitorId: inputData.MonitorId, 116 + MonitorID: inputData.MonitorID, 115 117 Region: region, 116 - WorkspaceId: inputData.WorkspaceId, 118 + WorkspaceID: inputData.WorkspaceID, 117 119 Timestamp: time.Now().UTC().UnixMilli(), 118 120 CronTimestamp: inputData.CronTimestamp, 119 - Url: inputData.Url, 121 + URL: inputData.URL, 120 122 }, nil 121 123 }
+6 -4
apps/checker/ping_test.go
··· 3 3 import ( 4 4 "net/http" 5 5 "testing" 6 + 7 + "github.com/openstatushq/openstatus/apps/checker/request" 6 8 ) 7 9 8 10 func Test_ping(t *testing.T) { 9 11 type args struct { 10 12 client *http.Client 11 - inputData InputData 13 + inputData request.CheckerRequest 12 14 } 13 15 tests := []struct { 14 16 name string ··· 16 18 want PingData 17 19 wantErr bool 18 20 }{ 19 - {name: "200", args: args{client: &http.Client{}, inputData: InputData{Url: "https://openstat.us", CronTimestamp: 1}}, want: PingData{Url: "https://openstat.us", StatusCode: 200}, wantErr: false}, 20 - {name: "500", args: args{client: &http.Client{}, inputData: InputData{Url: "https://openstat.us/500", CronTimestamp: 1}}, want: PingData{Url: "https://openstat.us/500", StatusCode: 500}, wantErr: false}, 21 - {name: "500", args: args{client: &http.Client{}, inputData: InputData{Url: "https://somethingthatwillfail.ed", CronTimestamp: 1}}, want: PingData{Url: "https://openstat.us/500", StatusCode: 0}, wantErr: true}, 21 + {name: "200", args: args{client: &http.Client{}, inputData: request.CheckerRequest{URL: "https://openstat.us", CronTimestamp: 1}}, want: PingData{URL: "https://openstat.us", StatusCode: 200}, wantErr: false}, 22 + {name: "500", args: args{client: &http.Client{}, inputData: request.CheckerRequest{URL: "https://openstat.us/500", CronTimestamp: 1}}, want: PingData{URL: "https://openstat.us/500", StatusCode: 500}, wantErr: false}, 23 + {name: "500", args: args{client: &http.Client{}, inputData: request.CheckerRequest{URL: "https://somethingthatwillfail.ed", CronTimestamp: 1}}, want: PingData{URL: "https://openstat.us/500", StatusCode: 0}, wantErr: true}, 22 24 23 25 // TODO: Add test cases. 24 26 }
+16
apps/checker/request/request.go
··· 1 + package request 2 + 3 + type CheckerRequest struct { 4 + WorkspaceID string `json:"workspaceId"` 5 + URL string `json:"url"` 6 + MonitorID string `json:"monitorId"` 7 + Method string `json:"method"` 8 + CronTimestamp int64 `json:"cronTimestamp"` 9 + Body string `json:"body"` 10 + Headers []struct { 11 + Key string `json:"key"` 12 + Value string `json:"value"` 13 + } `json:"headers,omitempty"` 14 + PagesIDs []string `json:"pagesIds"` 15 + Status string `json:"status"` 16 + }