Skip to content

Commit

Permalink
🔥 to the moon (#494)
Browse files Browse the repository at this point in the history
  • Loading branch information
thibaultleouay authored Nov 30, 2023
1 parent 8220930 commit a4a5398
Show file tree
Hide file tree
Showing 9 changed files with 266 additions and 26 deletions.
21 changes: 21 additions & 0 deletions apps/checker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM golang:1.21 AS builder

WORKDIR /go/src/github.com/openstatushq/openstatus/apps/checker

COPY go.mod .
COPY go.sum .

RUN go mod download

COPY . .

ARG VERSION

RUN go build -o openstatus-checker .

FROM golang:1.21

COPY --from=builder /go/src/github.com/openstatushq/openstatus/apps/checker/openstatus-checker /usr/local/bin/openstatus-checker


CMD ["/usr/local/bin/openstatus-checker"]
30 changes: 30 additions & 0 deletions apps/checker/fly.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# fly.toml app configuration file generated for openstatus-checker on 2023-11-30T20:23:20+01:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#

app = "openstatus-checker"
primary_region = "ams"

[build]
dockerfile = "./Dockerfile"

[deploy]
strategy = "canary"


[env]
PORT = "8080"

[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
processes = ["app"]

[[vm]]
cpu_kind = "shared"
cpus = 2
memory_mb = 512
8 changes: 8 additions & 0 deletions apps/checker/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/openstatushq/openstatus/apps/checker

go 1.21.4

require (
github.com/go-chi/chi/v5 v5.0.10
github.com/google/uuid v1.4.0
)
4 changes: 4 additions & 0 deletions apps/checker/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
124 changes: 124 additions & 0 deletions apps/checker/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"time"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)

type InputData struct {
WorkspaceId string `json:"workspaceId"`
Url string `json:"url"`
MonitorId string `json:"monitorId"`
Method string `json:"method"`
CronTimestamp int64 `json:"cronTimestamp"`
Body string `json:"body"`
Headers []struct {
Key string `json:"key"`
Value string `json:"value"`
} `json:"headers,omitempty"`
PagesIds []string `json:"pagesIds"`
Status string `json:"status"`
}

func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Post("/", func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Authorization") != "Basic "+ os.Getenv("CRON_SECRET") {
http.Error(w, "Unauthorized", 401)
return
}
region := os.Getenv("FLY_REGION")
if r.Body == nil {
http.Error(w, "Please send a request body", 400)
return
}
var u InputData

err := json.NewDecoder(r.Body).Decode(&u)

fmt.Printf("Start checker for %+v", u)

if err != nil {
http.Error(w, err.Error(), 400)
return
}
request, error := http.NewRequest(u.Method, u.Url, bytes.NewReader([]byte(u.Body)))

// Setting headers
for _, header := range u.Headers {
fmt.Printf("%+v", header)
if header.Key != "" && header.Value != "" {
request.Header.Set(header.Key, header.Value)
}
}

if error != nil {
fmt.Println(error)
}

client := &http.Client{}
start := time.Now().UTC().UnixMilli()
response, error := client.Do(request)
end := time.Now().UTC().UnixMilli()

// Retry if error
if error != nil {
response, error = client.Do(request)
end = time.Now().UTC().UnixMilli()
}

latency := end - start
fmt.Println("🚀 Checked url: %v with latency %v in region %v ", u.Url, latency, region)
fmt.Printf("Response %+v for %+v", response, u)
if error != nil {
tiny((PingData{
Latency: (latency),
MonitorId: u.MonitorId,
Region: region,
WorkspaceId: u.WorkspaceId,
Timestamp: time.Now().UTC().UnixMilli(),
Url: u.Url,
Message: error.Error(),
}))
} else {
tiny((PingData{
Latency: (latency),
MonitorId: u.MonitorId,
Region: region,
WorkspaceId: u.WorkspaceId,
StatusCode: int16(response.StatusCode),
Timestamp: time.Now().UTC().UnixMilli(),
Url: u.Url,
}))
}

fmt.Printf("End checker for %+v", u)

w.Write([]byte("Ok"))
w.WriteHeader(200)
})

r.Get("/ping", func(w http.ResponseWriter, r *http.Request) {
data := struct {
Ping string `json:"ping"`
FlyRegion string `json:"fly_region"`
}{
Ping: "pong",
FlyRegion: os.Getenv("FLY_REGION"),
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(data)
})

http.ListenAndServe(":8080", r)
}
45 changes: 45 additions & 0 deletions apps/checker/ping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)

type PingData struct {
WorkspaceId string `json:"workspaceId"`
MonitorId string `json:"monitorId"`
Timestamp int64 `json:"timestamp"`
StatusCode int16 `json:"statusCode"`
Latency int64 `json:"latency"`
CronTimestamp int64 `json:"cronTimestamp"`
Url string `json:"url"`
Region string `json:"region"`
Message string `json:"message"`
}

func tiny(pingData PingData) {
url := "https://api.tinybird.co/v0/events?name=golang_ping_response__v1"
fmt.Println("URL:>", url)
bearer := "Bearer " + os.Getenv("TINYBIRD_TOKEN")
payloadBuf := new(bytes.Buffer)
json.NewEncoder(payloadBuf).Encode(pingData)
req, err := http.NewRequest("POST", url, payloadBuf)
req.Header.Set("Authorization", bearer)
req.Header.Set("Content-Type", "application/json")

client := &http.Client{Timeout: time.Second * 10}
resp, err := client.Do(req)
if err != nil {
fmt.Println(err)

}
defer resp.Body.Close()

body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
23 changes: 17 additions & 6 deletions apps/server/src/checker/checker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { handleMonitorFailed, handleMonitorRecovered } from "./monitor-handler";
import type { PublishPingType } from "./ping";
import { pingEndpoint, publishPing } from "./ping";
import { getHeaders, publishPing } from "./ping";
import type { Payload } from "./schema";

// we could have a 'retry' parameter to know how often we should retry
Expand Down Expand Up @@ -54,19 +54,30 @@ const run = async (data: Payload, retry: number) => {
let message = undefined;
// We are doing these for wrong urls
try {
startTime = Date.now();
res = await pingEndpoint(data);
endTime = Date.now();
const headers = getHeaders(data);
console.log(`🆕 fetch is about to start for ${JSON.stringify(data)}`);
startTime = performance.now();
res = await fetch(data.url, {
method: data.method,
keepalive: false,
cache: "no-store",
headers,
// Avoid having "TypeError: Request with a GET or HEAD method cannot have a body." error
...(data.method === "POST" && { body: data?.body }),
});

endTime = performance.now();
console.log(`✅ fetch is done for ${JSON.stringify(data)}`);
} catch (e) {
endTime = Date.now();
endTime = performance.now();
message = `${e}`;
console.log(
`🚨 error on pingEndpoint for ${JSON.stringify(data)} error: `,
e,
);
}

const latency = endTime - startTime;
const latency = Number((endTime - startTime).toFixed(0));
if (res?.ok) {
await publishPingRetryPolicy({
payload: data,
Expand Down
19 changes: 1 addition & 18 deletions apps/server/src/checker/ping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { Payload } from "./schema";

const region = env.FLY_REGION;

function getHeaders(data?: Payload) {
export function getHeaders(data?: Payload) {
const customHeaders =
data?.headers?.reduce((o, v) => {
// removes empty keys from the header
Expand All @@ -19,23 +19,6 @@ function getHeaders(data?: Payload) {
};
}

export async function pingEndpoint(data: Payload) {
try {
const res = await fetch(data?.url, {
method: data?.method,
keepalive: false,
cache: "no-store",
headers: getHeaders(data),
// Avoid having "TypeError: Request with a GET or HEAD method cannot have a body." error
...(data.method === "POST" && { body: data?.body }),
});

return res;
} catch (e) {
throw e;
}
}

export type PublishPingType = {
payload: Payload;
latency: number;
Expand Down
18 changes: 16 additions & 2 deletions apps/web/src/app/api/checker/cron/_cron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,24 @@ export const cron = async ({
body: Buffer.from(JSON.stringify(payload)).toString("base64"),
},
};
const newTask: google.cloud.tasks.v2beta3.ITask = {
httpRequest: {
headers: {
"Content-Type": "application/json", // Set content type to ensure compatibility your application's request parsing
...(region !== "auto" && { "fly-prefer-region": region }), // Specify the region you want the request to be sent to
Authorization: `Basic ${env.CRON_SECRET}`,
},
httpMethod: "POST",
url: "https://openstatus-checker.fly.dev",
body: Buffer.from(JSON.stringify(payload)).toString("base64"),
},
};

const request = { parent: parent, task: task };
const [response] = await client.createTask(request);

allResult.push(response);
const requestNew = { parent: parent, task: newTask };
const [responseNew] = await client.createTask(requestNew);
allResult.push(response, responseNew);
}
}
await Promise.all(allResult);
Expand Down

0 comments on commit a4a5398

Please sign in to comment.