Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mr-karan/calert
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.1.0
Choose a base ref
...
head repository: mr-karan/calert
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
  • 5 commits
  • 6 files changed
  • 3 contributors

Commits on Mar 13, 2024

  1. feat: make threaded replies optional

    fixes #74
    mr-karan committed Mar 13, 2024
    Copy the full SHA
    a873437 View commit details

Commits on Aug 30, 2024

  1. Copy the full SHA
    0508405 View commit details

Commits on Sep 25, 2024

  1. use log/slog also for http.Server

    Now the remaining non-structured (non-JSON) log messages are from
    chi/middleware.Logger.
    marco-m-pix4d committed Sep 25, 2024
    Copy the full SHA
    0de4bde View commit details

Commits on Oct 8, 2024

  1. Merge pull request #81 from marco-m-pix4d/slog-http-server

    use log/slog also for http.Server
    mr-karan authored Oct 8, 2024
    Copy the full SHA
    9504318 View commit details
  2. Merge pull request #80 from tailsdotcom/feature/multi-arch

    Build multi-arch docker manifest
    mr-karan authored Oct 8, 2024
    Copy the full SHA
    ca63a8e View commit details
Showing with 87 additions and 37 deletions.
  1. +38 −3 .goreleaser.yml
  2. +11 −10 cmd/init.go
  3. +6 −2 cmd/main.go
  4. +2 −0 config.sample.toml
  5. +23 −20 internal/providers/google_chat/google_chat.go
  6. +7 −2 internal/providers/google_chat/message.go
41 changes: 38 additions & 3 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
version: 2

env:
- GO111MODULE=on
- CGO_ENABLED=0
@@ -26,7 +28,7 @@ archives:

dockers:
- # ID of the image, needed if you want to filter by it later on (e.g. on custom publishers).
id: calert
id: calert-amd64

# GOOS of the built binaries/packages that should be used.
goos: linux
@@ -40,8 +42,8 @@ dockers:

# Templates of the Docker image names.
image_templates:
- "ghcr.io/mr-karan/calert:{{ .Tag }}"
- "ghcr.io/mr-karan/calert:latest"
- "ghcr.io/mr-karan/calert:{{ .Tag }}-amd64"
- "ghcr.io/mr-karan/calert:latest-amd64"

# Skips the docker push.
# Could be useful if you also do draft releases.
@@ -82,3 +84,36 @@ dockers:
extra_files:
- config.sample.toml
- static/
- # Make an additional template for arm64
id: calert-arm64
goos: linux
goarch: arm64
ids:
- calert
image_templates:
- "ghcr.io/mr-karan/calert:{{ .Tag }}-arm64"
- "ghcr.io/mr-karan/calert:latest-arm64"
skip_push: false
dockerfile: Dockerfile
use: docker
build_flag_templates:
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
- "--label=org.opencontainers.image.title={{.ProjectName}}"
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
- "--label=org.opencontainers.image.version={{.Version}}"
- "--platform=linux/arm64"
extra_files:
- config.sample.toml
- static/

# Make a manifest with both architectures
docker_manifests:
- name_template: "ghcr.io/mr-karan/calert:{{ .Tag }}"
image_templates:
- "ghcr.io/mr-karan/calert:{{ .Tag }}-amd64"
- "ghcr.io/mr-karan/calert:{{ .Tag }}-arm64"
- name_template: "ghcr.io/mr-karan/calert:latest"
image_templates:
- "ghcr.io/mr-karan/calert:{{ .Tag }}-amd64"
- "ghcr.io/mr-karan/calert:{{ .Tag }}-arm64"
21 changes: 11 additions & 10 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -95,16 +95,17 @@ func initProviders(ko *koanf.Koanf, lo *slog.Logger, metrics *metrics.Manager) (
case "google_chat":
gchat, err := google_chat.NewGoogleChat(
google_chat.GoogleChatOpts{
Log: lo,
Timeout: ko.MustDuration(fmt.Sprintf("%s.timeout", cfgKey)),
MaxIdleConn: ko.MustInt(fmt.Sprintf("%s.max_idle_conns", cfgKey)),
ProxyURL: ko.String(fmt.Sprintf("%s.proxy_url", cfgKey)),
Endpoint: ko.MustString(fmt.Sprintf("%s.endpoint", cfgKey)),
Room: name,
Template: ko.MustString(fmt.Sprintf("%s.template", cfgKey)),
ThreadTTL: ko.MustDuration(fmt.Sprintf("%s.thread_ttl", cfgKey)),
Metrics: metrics,
DryRun: ko.Bool(fmt.Sprintf("%s.dry_run", cfgKey)),
Log: lo,
Timeout: ko.MustDuration(fmt.Sprintf("%s.timeout", cfgKey)),
MaxIdleConn: ko.MustInt(fmt.Sprintf("%s.max_idle_conns", cfgKey)),
ProxyURL: ko.String(fmt.Sprintf("%s.proxy_url", cfgKey)),
Endpoint: ko.MustString(fmt.Sprintf("%s.endpoint", cfgKey)),
Room: name,
Template: ko.MustString(fmt.Sprintf("%s.template", cfgKey)),
ThreadTTL: ko.MustDuration(fmt.Sprintf("%s.thread_ttl", cfgKey)),
ThreadedReplies: ko.Bool(fmt.Sprintf("%s.threaded_replies", cfgKey)),
Metrics: metrics,
DryRun: ko.Bool(fmt.Sprintf("%s.dry_run", cfgKey)),
},
)
if err != nil {
8 changes: 6 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package main

import (
"log/slog"
"net/http"
"os"

"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/mr-karan/calert/internal/metrics"
"github.com/mr-karan/calert/internal/notifier"

"log/slog"
)

var (
@@ -83,6 +82,11 @@ func main() {
// Start HTTP Server.
app.lo.Info("starting http server", "address", ko.MustString("app.address"))
srv := &http.Server{
// http.Server does not support structured logging. The best we can do
// is to use our structured logger at a fixed log level. The "msg" field
// will contain the whole log message, but at least any error log from
// http.Server will be a JSON object, as the rest of the logs.
ErrorLog: slog.NewLogLogger(lo.Handler(), slog.LevelError),
Addr: ko.MustString("app.address"),
ReadTimeout: ko.MustDuration("app.server_timeout"),
WriteTimeout: ko.MustDuration("app.server_timeout"),
2 changes: 2 additions & 0 deletions config.sample.toml
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ timeout = "30s" # Timeout for making requests to Provider.
# proxy_url = "http://internal-squid-proxy.com:3128" # Specify `proxy_url` as your proxy endpoint to route all HTTP requests to the provider via a proxy.
template = "static/message.tmpl" # Path to specify the message template path.
thread_ttl = "12h" # Timeout to keep active alerts in memory. Once this TTL expires, a new thread will be created.
threaded_replies = true # Whether to send threaded replies or not.
dry_run = false

[providers.dev_alerts]
@@ -22,4 +23,5 @@ timeout = "30s"
# proxy_url = "http://internal-squid-proxy.com:3128"
template = "static/message.tmpl"
thread_ttl = "12h"
threaded_replies = false # Whether to send threaded replies or not.
dry_run = false
43 changes: 23 additions & 20 deletions internal/providers/google_chat/google_chat.go
Original file line number Diff line number Diff line change
@@ -17,27 +17,29 @@ import (
)

type GoogleChatManager struct {
lo *slog.Logger
metrics *metrics.Manager
activeAlerts *ActiveAlerts
endpoint string
room string
client *http.Client
msgTmpl *template.Template
dryRun bool
lo *slog.Logger
metrics *metrics.Manager
activeAlerts *ActiveAlerts
endpoint string
room string
client *http.Client
msgTmpl *template.Template
dryRun bool
threadedReplies bool
}

type GoogleChatOpts struct {
Log *slog.Logger
Metrics *metrics.Manager
DryRun bool
MaxIdleConn int
Timeout time.Duration
ProxyURL string
Endpoint string
Room string
Template string
ThreadTTL time.Duration
Log *slog.Logger
Metrics *metrics.Manager
DryRun bool
MaxIdleConn int
Timeout time.Duration
ProxyURL string
Endpoint string
Room string
Template string
ThreadTTL time.Duration
ThreadedReplies bool
}

// NewGoogleChat initializes a Google Chat provider object.
@@ -92,8 +94,9 @@ func NewGoogleChat(opts GoogleChatOpts) (*GoogleChatManager, error) {
lo: opts.Log,
metrics: opts.Metrics,
},
msgTmpl: tmpl,
dryRun: opts.DryRun,
msgTmpl: tmpl,
dryRun: opts.DryRun,
threadedReplies: opts.ThreadedReplies,
}
// Start a background worker to cleanup alerts based on TTL mechanism.
go mgr.activeAlerts.startPruneWorker(1*time.Hour, opts.ThreadTTL)
9 changes: 7 additions & 2 deletions internal/providers/google_chat/message.go
Original file line number Diff line number Diff line change
@@ -66,8 +66,13 @@ func (m *GoogleChatManager) sendMessage(msg ChatMessage, threadKey string) error
return err
}
q := u.Query()
q.Set("threadKey", threadKey)
q.Set("messageReplyOption", "REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD")
// Default behaviour is to start a new thread for every alert.
q.Set("messageReplyOption", "MESSAGE_REPLY_OPTION_UNSPECIFIED")
if m.threadedReplies {
// If threaded replies are enabled, use the threadKey to reply to the same thread.
q.Set("messageReplyOption", "REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD")
q.Set("threadKey", threadKey)
}
u.RawQuery = q.Encode()
endpoint := u.String()