autogits/obs-status-service/rabbitmq.go

202 lines
5.0 KiB
Go
Raw Normal View History

2024-08-16 17:17:26 +02:00
package main
import (
"crypto/tls"
"encoding/json"
"log"
2024-08-19 17:14:20 +02:00
"os"
2024-08-16 17:17:26 +02:00
"strings"
"sync"
"time"
_ "github.com/mattn/go-sqlite3"
rabbitmq "github.com/rabbitmq/amqp091-go"
"src.opensuse.org/autogits/common"
"src.opensuse.org/autogits/common/gitea-generated/models"
)
type BuildNotification struct {
BuildSuccess bool
Project, Package, Repository, Arch, Release, Rev, Buildtype, Workerid string
Starttime, Endtime, Readytime string
}
2024-08-19 17:14:20 +02:00
var out *os.File
2024-08-16 17:17:26 +02:00
var obsNotifications map[string]*BuildNotification // Project/Package/Repositry/Arch as key
var notificationMutex sync.RWMutex
var notificationChannels map[string][]chan *BuildNotification
func getProjectBuildStatus(project string) []*BuildNotification {
notificationMutex.RLock()
defer notificationMutex.RUnlock()
data := make([]*BuildNotification, 0, 4)
for _, val := range obsNotifications {
if val.Package == project {
data = append(data, val)
}
}
return data
}
func addProjectWatcher(meta *common.ProjectMeta, pr *models.PullReview) {
}
func addObsNotificationToCache(notification *BuildNotification) {
key := strings.Join(
[]string{
notification.Project,
notification.Package,
notification.Repository,
notification.Arch,
},
"/",
)
notificationMutex.Lock()
obsNotifications[key] = notification
chns, ok := notificationChannels[notification.Project]
notificationMutex.Unlock()
if ok {
for _, ch := range chns {
ch <- notification
}
}
}
func processObsMessage(msg *rabbitmq.Delivery) {
2024-08-19 17:14:20 +02:00
out.Write([]byte(msg.Timestamp.String()))
out.Write([]byte("\n"))
2024-08-19 17:33:52 +02:00
out.Write([]byte(msg.RoutingKey))
out.Write([]byte("\n"))
2024-08-19 17:14:20 +02:00
out.Write(msg.Body)
out.Write([]byte("\n--------------------------\n"))
return
2024-08-16 17:17:26 +02:00
key := strings.SplitN(msg.RoutingKey, ".", 4)
if len(key) != 4 || len(key[3]) < 7 || key[3][:6] != "build_" {
return
}
buildSuccess := false
switch key[3][6:] {
case "success", "unchanged":
buildSuccess = true
case "fail":
buildSuccess = false
default:
log.Printf("unknown build_ logging message: %s\n", msg.RoutingKey)
return
}
notification := &BuildNotification{
BuildSuccess: buildSuccess,
}
err := json.Unmarshal(msg.Body, notification)
if err != nil {
log.Printf("Cannot unmarshall json object: %s\n", msg.Body)
return
}
log.Printf("%v\n", notification)
addObsNotificationToCache(notification)
}
func failOnError(err error, msg string) {
if err != nil {
log.Panicf("%s: %s", err, msg)
}
}
func ProcessingObsMessages(host, username, password, queueName string) {
defer func() {
if r := recover(); r != nil {
log.Print("recovering... reconnecting...\n")
time.Sleep(5 * time.Second)
go ProcessingObsMessages(host, username, password, queueName)
}
}()
2024-08-19 17:14:20 +02:00
var err error
2024-08-19 17:37:35 +02:00
out, err = os.OpenFile("messages.txt", os.O_WRONLY | os.O_APPEND, 0644)
2024-08-19 17:14:20 +02:00
if err != nil {
log.Printf("Cannot open message.txt: %v", err)
return
}
2024-08-16 17:17:26 +02:00
if obsNotifications == nil {
obsNotifications = make(map[string]*BuildNotification)
// notificationChannels = make(map[string]chan *BuildNotification)
}
auth := ""
if len(username) > 0 && len(password) > 0 {
auth = username + ":" + password + "@"
}
connection, err := rabbitmq.DialTLS("amqps://"+auth+host, &tls.Config{
ServerName: host,
})
failOnError(err, "Cannot connect to rabbit.opensuse.org")
defer connection.Close()
ch, err := connection.Channel()
failOnError(err, "Cannot create a channel")
defer ch.Close()
err = ch.ExchangeDeclarePassive("pubsub", "topic", true, false, false, false, nil)
failOnError(err, "Cannot find pubsub exchange")
var q rabbitmq.Queue
if len(queueName) == 0 {
q, err = ch.QueueDeclare("", false, true, true, false, nil)
} else {
q, err = ch.QueueDeclarePassive(queueName, true, false, true, false, nil)
if err != nil {
log.Printf("queue not found .. trying to create it: %v\n", err)
if ch.IsClosed() {
ch, err = connection.Channel()
failOnError(err, "Channel cannot be re-opened")
}
q, err = ch.QueueDeclare(queueName, true, false, true, false, nil)
if err != nil {
log.Printf("can't create persistent queue ... falling back to temporaty queue: %v\n", err)
if ch.IsClosed() {
ch, err = connection.Channel()
failOnError(err, "Channel cannot be re-opened")
}
q, err = ch.QueueDeclare("", false, true, true, false, nil)
}
}
}
failOnError(err, "Cannot declare queue")
log.Printf("queue: %s:%d", q.Name, q.Consumers)
2024-08-19 17:14:20 +02:00
err = ch.QueueBind(q.Name, "*.obs.*.*", "pubsub", false, nil)
2024-08-16 17:17:26 +02:00
failOnError(err, "Cannot bind queue to exchange")
msgs, err := ch.Consume(q.Name, "", true, true, false, false, nil)
failOnError(err, "Cannot start consumer")
log.Printf("queue: %s:%d", q.Name, q.Consumers)
for {
msg, ok := <-msgs
if !ok {
log.Printf("channel/connection closed?\n")
if connection.IsClosed() {
// reconnect
log.Printf("reconnecting...")
time.Sleep(5 * time.Second)
go ProcessingObsMessages(host, username, password, queueName)
}
return
}
processObsMessage(&msg)
}
}