refactoring

This commit is contained in:
milarin 2023-07-19 17:35:51 +02:00
parent 919aa659cb
commit cb72be6aca
6 changed files with 91 additions and 88 deletions

2
.gitignore vendored
View File

@ -1 +1,3 @@
raidcheck raidcheck
.env
mdstat

View File

@ -2,8 +2,6 @@ package main
import ( import (
"reflect" "reflect"
"git.tordarus.net/Tordarus/dockerhealth"
) )
func filterChanges(in <-chan RaidState) <-chan RaidState { func filterChanges(in <-chan RaidState) <-chan RaidState {
@ -14,17 +12,12 @@ func filterChanges(in <-chan RaidState) <-chan RaidState {
currentStates := map[string]RaidState{} currentStates := map[string]RaidState{}
first := true
for state := range in { for state := range in {
if oldState, ok := currentStates[state.Name]; !ok || !reflect.DeepEqual(oldState.DevicesUp, state.DevicesUp) { oldState, ok := currentStates[state.Name]
dockerhealth.Healthy = first if !ok || !reflect.DeepEqual(oldState.DevicesUp, state.DevicesUp) {
currentStates[state.Name] = state currentStates[state.Name] = state
out <- state out <- state
} else {
dockerhealth.Healthy = true
} }
first = false
} }
}(in, out) }(in, out)

6
go.mod
View File

@ -1,9 +1,9 @@
module git.tordarus.net/Tordarus/raidcheck module git.milar.in/milarin/raidcheck
go 1.17 go 1.17
require ( require (
git.tordarus.net/Tordarus/adverr v0.2.0 git.milar.in/milarin/adverr v1.1.0
git.tordarus.net/Tordarus/dockerhealth v0.0.1 git.milar.in/milarin/envvars/v2 v2.0.0
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
) )

6
go.sum
View File

@ -1,5 +1,7 @@
git.tordarus.net/Tordarus/adverr v0.2.0 h1:kLYjR2/Vb2GHiSAMvAv+WPNaHR9BRphKanf8H/pCZdA= git.milar.in/milarin/adverr v1.1.0 h1:jD9WnOvs40lfMhvqQ7cllOaRJNBMWr1f07/s9jAadp0=
git.tordarus.net/Tordarus/adverr v0.2.0/go.mod h1:XRf0+7nhOkIEr0gi9DUG4RvV2KaOFB0fYPDaR1KLenw= git.milar.in/milarin/adverr v1.1.0/go.mod h1:joU9sBb7ySyNv4SpTXB0Z4o1mjXsArBw4N27wjgzj9E=
git.milar.in/milarin/envvars/v2 v2.0.0 h1:DWRQCWaHqzDD8NGpSgv5tYLuF9A/dVFPAtTvz3oiIqE=
git.milar.in/milarin/envvars/v2 v2.0.0/go.mod h1:HkdEi+gG2lJSmVq547bTlQV4qQ0hO333bE8IrE0B9yY=
git.tordarus.net/Tordarus/dockerhealth v0.0.1 h1:rxkwmCW5PDe9gbnXap7d3n5rK1Qyr6xpmJPZFom/ZXc= git.tordarus.net/Tordarus/dockerhealth v0.0.1 h1:rxkwmCW5PDe9gbnXap7d3n5rK1Qyr6xpmJPZFom/ZXc=
git.tordarus.net/Tordarus/dockerhealth v0.0.1/go.mod h1:U0IPsBJHAjqWgNyehWwGfYqTwPJgqBNhzk/eBpaZsnE= git.tordarus.net/Tordarus/dockerhealth v0.0.1/go.mod h1:U0IPsBJHAjqWgNyehWwGfYqTwPJgqBNhzk/eBpaZsnE=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=

41
main.go
View File

@ -3,56 +3,47 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"strconv"
"time" "time"
"git.tordarus.net/Tordarus/adverr" "git.milar.in/milarin/adverr"
"git.tordarus.net/Tordarus/dockerhealth" "git.milar.in/milarin/envvars/v2"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
) )
var ( var (
interval = 1 * time.Minute MdstatFile = envvars.String("MDSTAT_FILE", "/proc/mdstat")
telegramToken string PollInterval = envvars.Duration("POLL_INTERVAL", time.Minute)
chatID int64 BotToken = envvars.String("TELEGRAM_API_TOKEN", "")
ChatID = envvars.Int64("TELEGRAM_CHAT_ID", 0)
) )
func main() { func main() {
if i, ok := os.LookupEnv("POLL_INTERVAL"); ok { if BotToken == "" {
if interval2, err := time.ParseDuration(i); err == nil { fmt.Fprintln(os.Stderr, "$TELEGRAM_API_TOKEN not set")
interval = interval2 os.Exit(1)
}
} }
if tt, ok := os.LookupEnv("TELEGRAM_API_TOKEN"); ok { if ChatID == 0 {
telegramToken = tt fmt.Fprintln(os.Stderr, "$TELEGRAM_CHAT_ID not set")
os.Exit(1)
} }
if id, ok := os.LookupEnv("TELEGRAM_CHAT_ID"); ok { bot, err := tgbotapi.NewBotAPI(BotToken)
if chatId2, err := strconv.Atoi(id); err == nil {
chatID = int64(chatId2)
}
}
bot, err := tgbotapi.NewBotAPI(telegramToken)
if err != nil { if err != nil {
adverr.Fatalln(err, 1) adverr.Fatalln(err, 1)
} }
for state := range filterChanges(mdstat()) { for state := range filterChanges(mdstat(MdstatFile, PollInterval)) {
str := state.String() str := state.String()
fmt.Println(str) fmt.Println(str)
message := tgbotapi.NewMessage(chatID, fmt.Sprintf("Der Zustand des RAID Systems %s hat sich verändert:\n\n%s", state.Name, str)) message := tgbotapi.NewMessage(ChatID, fmt.Sprintf("Der Zustand des RAID Systems %s hat sich verändert:\n\n%s", state.Name, str))
message.Entities = append(message.Entities, tgbotapi.MessageEntity{ message.Entities = append(message.Entities, tgbotapi.MessageEntity{
Type: "code", Type: "code",
Offset: len("Der Zustand des RAID Systems %s hat sich verändert:\n\n"), Offset: len("Der Zustand des RAID Systems %s hat sich verändert:\n\n"),
Length: len(str), Length: len(str),
}) })
_, err := bot.Send(message) if _, err := bot.Send(message); err != nil {
if err != nil {
adverr.Println(err) adverr.Println(err)
} }
} }
dockerhealth.Healthy = false
} }

View File

@ -16,57 +16,72 @@ var (
deviceRegex = regexp.MustCompile(`^([\w\d]+)\[(\d+)\]$`) deviceRegex = regexp.MustCompile(`^([\w\d]+)\[(\d+)\]$`)
) )
func mdstat() <-chan RaidState { func mdstat(file string, pollInterval time.Duration) <-chan RaidState {
ch := make(chan RaidState, 4) ch := make(chan RaidState, 4)
go parseMdstatOutput(file, pollInterval, ch)
return ch
}
go func(out chan<- RaidState) { func parseMdstatOutput(file string, pollInterval time.Duration, out chan<- RaidState) {
defer close(ch) defer close(out)
for { ticker := time.NewTicker(pollInterval)
data, err := os.ReadFile("/proc/mdstat") for range ticker.C {
data, err := os.ReadFile(file)
if err != nil { if err != nil {
panic(err) panic(err)
} }
lines := strings.Split(string(data), "\n")
lines := strings.Split(string(data), "\n")
state := RaidState{} state := RaidState{}
for _, line := range lines { for _, line := range lines {
if match := firstLineRegex.FindStringSubmatch(line); match != nil { parseLine(&state, line)
state.Name = match[1]
state.State = match[2]
state.Level = match[3]
devices := strings.Split(match[4], " ")
state.Devices = make([]string, len(devices))
for _, device := range devices {
if match := deviceRegex.FindStringSubmatch(device); match != nil {
deviceIndex, _ := strconv.Atoi(match[2])
state.Devices[deviceIndex] = match[1]
}
}
} else if match := secondLineRegex.FindStringSubmatch(line); match != nil {
state.RegisteredDeviceCount, _ = strconv.Atoi(match[1])
state.UsedDeviceCount, _ = strconv.Atoi(match[2])
state.DevicesUp = map[string]bool{}
for i, rn := range match[3] {
state.DevicesUp[state.Devices[i]] = rn == 'U'
}
} else if match := actionLineRegex.FindStringSubmatch(line); match != nil {
state.Action = new(RaidAction)
state.Action.Name = match[1]
state.Action.Progress, _ = strconv.ParseFloat(match[2], 64)
state.Action.Duration = parseFinished(match[3], match[4])
state.Action.DurationFormatted = state.Action.Duration.String()
state.Action.Finished = time.Now().Add(state.Action.Duration)
}
} }
out <- state out <- state
time.Sleep(time.Minute)
} }
}(ch) }
return ch func parseLine(state *RaidState, line string) {
if matches := firstLineRegex.FindStringSubmatch(line); matches != nil {
parseFirstLine(state, matches)
} else if matches := secondLineRegex.FindStringSubmatch(line); matches != nil {
parseSecondLine(state, matches)
} else if match := actionLineRegex.FindStringSubmatch(line); match != nil {
parseActionLine(state, matches)
}
}
func parseFirstLine(state *RaidState, matches []string) {
state.Name = matches[1]
state.State = matches[2]
state.Level = matches[3]
devices := strings.Split(matches[4], " ")
state.Devices = make([]string, len(devices))
for deviceIndex, device := range devices {
if match := deviceRegex.FindStringSubmatch(device); match != nil {
state.Devices[deviceIndex] = match[1]
}
}
}
func parseSecondLine(state *RaidState, matches []string) {
state.RegisteredDeviceCount, _ = strconv.Atoi(matches[1])
state.UsedDeviceCount, _ = strconv.Atoi(matches[2])
state.DevicesUp = map[string]bool{}
for i, rn := range matches[3] {
state.DevicesUp[state.Devices[i]] = rn == 'U'
}
}
func parseActionLine(state *RaidState, matches []string) {
state.Action = new(RaidAction)
state.Action.Name = matches[1]
state.Action.Progress, _ = strconv.ParseFloat(matches[2], 64)
state.Action.Duration = parseFinished(matches[3], matches[4])
state.Action.DurationFormatted = state.Action.Duration.String()
state.Action.Finished = time.Now().Add(state.Action.Duration)
} }
func parseFinished(value, unit string) time.Duration { func parseFinished(value, unit string) time.Duration {