Chore: Update 2.8 disabled networking policie checks - not needed. upload is perfect

This commit is contained in:
Alexander Renz 2025-04-05 14:36:12 +02:00
parent ccf04ede06
commit a0a117dc11
3 changed files with 320 additions and 223 deletions

View File

@ -384,18 +384,11 @@ func createAndMountISO(size, mountpoint, charset string) error {
var dialer = &net.Dialer{ var dialer = &net.Dialer{
DualStack: true, DualStack: true,
Timeout: 5 * time.Second, Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second, // Added keep-alive for better network change handling
} }
var dualStackClient = &http.Client{ var dualStackClient = &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
DialContext: dialer.DialContext, DialContext: dialer.DialContext,
ForceAttemptHTTP2: true, // Enforce HTTP/2
IdleConnTimeout: 90 * time.Second, // Longer idle connections
DisableKeepAlives: false, // Ensure keep-alives are enabled
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
// ...existing code...
}, },
} }
@ -542,6 +535,10 @@ func main() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
if conf.Server.NetworkEvents {
go monitorNetwork(ctx)
go handleNetworkEvents(ctx)
}
go updateSystemMetrics(ctx) go updateSystemMetrics(ctx)
if conf.ClamAV.ClamAVEnabled { if conf.ClamAV.ClamAVEnabled {
@ -637,6 +634,9 @@ func main() {
log.Fatalf("Server failed: %v", err) log.Fatalf("Server failed: %v", err)
} }
} else { } else {
if conf.Server.ListenPort == "0.0.0.0" {
log.Info("Binding to 0.0.0.0. Any net/http logs you see are normal for this universal address.")
}
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server failed: %v", err) log.Fatalf("Server failed: %v", err)
} }
@ -733,7 +733,7 @@ uploadqueuesize = 50
# Add file-specific configurations here # Add file-specific configurations here
[build] [build]
version = "2.8-Stable" version = "2.7-Stable"
`) `)
} }
@ -1700,7 +1700,26 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
log.WithFields(logrus.Fields{"method": r.Method, "url": r.URL.String(), "remote": clientIP}).Info("Incoming request") log.WithFields(logrus.Fields{"method": r.Method, "url": r.URL.String(), "remote": clientIP}).Info("Incoming request")
// Log the requested URL for debugging
log.Infof("handleRequest: Received URL path: %s", r.URL.String())
p := r.URL.Path p := r.URL.Path
fileStorePath := strings.TrimPrefix(p, "/")
if fileStorePath == "" || fileStorePath == "/" {
log.WithField("path", fileStorePath).Warn("No file specified in URL")
// Updated to return 404 with a clear message instead of forbidden.
http.Error(w, "File not specified in URL. Please include the file path after the host.", http.StatusNotFound)
flushLogMessages()
return
}
// NEW: Compute absolute file path from storage path and fileStorePath.
absFilename, err := sanitizeFilePath(conf.Server.StoragePath, fileStorePath)
if err != nil {
log.WithError(err).Warn("Invalid file path")
http.Error(w, "Invalid file path", http.StatusBadRequest)
return
}
a, err := url.ParseQuery(r.URL.RawQuery) a, err := url.ParseQuery(r.URL.RawQuery)
if err != nil { if err != nil {
log.Warn("Failed to parse query parameters") log.Warn("Failed to parse query parameters")
@ -1708,26 +1727,6 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
return return
} }
fileStorePath := strings.TrimPrefix(p, "/")
if fileStorePath == "" || fileStorePath == "/" {
log.WithFields(logrus.Fields{
"event": "AccessAttempt",
"severity": "warning",
}).Warn("Access to root directory is forbidden")
http.Error(w, "Forbidden", http.StatusForbidden)
flushLogMessages()
return
} else if fileStorePath[0] == '/' {
fileStorePath = fileStorePath[1:]
}
absFilename, err := sanitizeFilePath(conf.Server.StoragePath, fileStorePath)
if err != nil {
log.WithFields(logrus.Fields{"file": fileStorePath, "error": err}).Warn("Invalid file path")
http.Error(w, "Invalid file path", http.StatusBadRequest)
return
}
switch r.Method { switch r.Method {
case http.MethodPut: case http.MethodPut:
handleUpload(w, r, absFilename, fileStorePath, a) handleUpload(w, r, absFilename, fileStorePath, a)
@ -1745,14 +1744,6 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
// handleUpload handles PUT requests for file uploads // handleUpload handles PUT requests for file uploads
func handleUpload(w http.ResponseWriter, r *http.Request, absFilename, fileStorePath string, a url.Values) { func handleUpload(w http.ResponseWriter, r *http.Request, absFilename, fileStorePath string, a url.Values) {
clientIP := getOriginalClientIP(r)
parsedIP := net.ParseIP(clientIP)
if parsedIP == nil {
log.Warnf("Invalid client IP address: %s", clientIP)
} else {
log.Infof("Handling upload from IP: %s (%s)", parsedIP.String(), detectIPVersion(parsedIP.String()))
}
log.Infof("Using storage path: %s", conf.Server.StoragePath) log.Infof("Using storage path: %s", conf.Server.StoragePath)
// HMAC validation // HMAC validation
@ -1860,7 +1851,6 @@ func handleUpload(w http.ResponseWriter, r *http.Request, absFilename, fileStore
} }
// Respond with 201 Created immediately // Respond with 201 Created immediately
w.Header().Set("Content-Type", "text/plain") // Ensure correct interpretation
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
if f, ok := w.(http.Flusher); ok { if f, ok := w.(http.Flusher); ok {
f.Flush() f.Flush()
@ -1965,14 +1955,7 @@ func handleDownload(w http.ResponseWriter, r *http.Request, absFilename, fileSto
} else { } else {
startTime := time.Now() startTime := time.Now()
log.Infof("Initiating download for file: %s", absFilename) log.Infof("Initiating download for file: %s", absFilename)
f, err := os.Open(absFilename) http.ServeFile(w, r, absFilename)
if err != nil {
log.Errorf("Couldn't open file: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
defer f.Close()
http.ServeContent(w, r, filepath.Base(absFilename), fileInfo.ModTime(), f)
downloadDuration.Observe(time.Since(startTime).Seconds()) downloadDuration.Observe(time.Since(startTime).Seconds())
downloadSizeBytes.Observe(float64(fileInfo.Size())) downloadSizeBytes.Observe(float64(fileInfo.Size()))
downloadsTotal.Inc() downloadsTotal.Inc()
@ -2172,6 +2155,73 @@ func getFileInfo(absFilename string) (os.FileInfo, error) {
return fileInfo, nil return fileInfo, nil
} }
func monitorNetwork(ctx context.Context) {
currentIP := getCurrentIPAddress()
for {
select {
case <-ctx.Done():
log.Info("Stopping network monitor.")
return
case <-time.After(10 * time.Second):
newIP := getCurrentIPAddress()
if newIP != currentIP && newIP != "" {
currentIP = newIP
select {
case networkEvents <- NetworkEvent{Type: "IP_CHANGE", Details: currentIP}:
log.WithField("new_ip", currentIP).Info("Queued IP_CHANGE event")
default:
log.Warn("Network event channel full. Dropping IP_CHANGE event.")
}
}
}
}
}
func handleNetworkEvents(ctx context.Context) {
for {
select {
case <-ctx.Done():
log.Info("Stopping network event handler.")
return
case event, ok := <-networkEvents:
if !ok {
log.Info("Network events channel closed.")
return
}
switch event.Type {
case "IP_CHANGE":
log.WithField("new_ip", event.Details).Info("Network change detected")
}
}
}
}
func getCurrentIPAddress() string {
interfaces, err := net.Interfaces()
if err != nil {
log.WithError(err).Error("Failed to get network interfaces")
return ""
}
for _, iface := range interfaces {
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
continue
}
addrs, err := iface.Addrs()
if err != nil {
log.WithError(err).Errorf("Failed to get addresses for interface %s", iface.Name)
continue
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && ipnet.IP.IsGlobalUnicast() && ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
return ""
}
func setupGracefulShutdown(server *http.Server, cancel context.CancelFunc) { func setupGracefulShutdown(server *http.Server, cancel context.CancelFunc) {
quit := make(chan os.Signal, 1) quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
@ -2757,23 +2807,13 @@ func detectIPVersion(ip string) string {
} }
func getOriginalClientIP(r *http.Request) string { func getOriginalClientIP(r *http.Request) string {
if xff := r.Header.Get("X-Forwarded-For"); xff != "" { if ip := r.Header.Get("X-Forwarded-For"); ip != "" {
parts := strings.Split(xff, ",") parts := strings.Split(ip, ",")
for _, part := range parts { return strings.TrimSpace(parts[0])
ip := strings.TrimSpace(part)
if net.ParseIP(ip) != nil {
return ip
} }
if ip := r.Header.Get("X-Real-IP"); ip != "" {
return strings.TrimSpace(ip)
} }
} host, _, _ := net.SplitHostPort(r.RemoteAddr)
if rip := r.Header.Get("X-Real-IP"); rip != "" {
if net.ParseIP(rip) != nil {
return strings.TrimSpace(rip)
}
}
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err == nil && host != "" && net.ParseIP(host) != nil {
return host return host
}
return ""
} }

View File

@ -27,8 +27,8 @@
"overrides": [] "overrides": []
}, },
"gridPos": { "gridPos": {
"h": 6, "h": 7,
"w": 24, "w": 3,
"x": 0, "x": 0,
"y": 0 "y": 0
}, },
@ -42,7 +42,7 @@
"content": "<div style=\"text-align: center; background-color: transparent; padding: 20px;\">\n <h3 style=\"color: white; font-family: 'Arial', sans-serif; font-weight: bold;\">HMAC Dashboard</h3>\n <img src=\"https://git.uuxo.net/uuxo/hmac-file-server/raw/branch/main/dashboard/hmac_icon.png\" alt=\"HMAC Icon\" style=\"width: 50px; height: 50px; display: block; margin: 10px auto;\">\n <p style=\"font-family: 'Verdana', sans-serif; color: white;\">\n This dashboard monitors <strong style=\"color: #FF5733;\">key metrics</strong> for the \n <span style=\"font-style: italic; color: #007BFF;\">HMAC File Server</span>.\n </p>\n</div>\n", "content": "<div style=\"text-align: center; background-color: transparent; padding: 20px;\">\n <h3 style=\"color: white; font-family: 'Arial', sans-serif; font-weight: bold;\">HMAC Dashboard</h3>\n <img src=\"https://git.uuxo.net/uuxo/hmac-file-server/raw/branch/main/dashboard/hmac_icon.png\" alt=\"HMAC Icon\" style=\"width: 50px; height: 50px; display: block; margin: 10px auto;\">\n <p style=\"font-family: 'Verdana', sans-serif; color: white;\">\n This dashboard monitors <strong style=\"color: #FF5733;\">key metrics</strong> for the \n <span style=\"font-style: italic; color: #007BFF;\">HMAC File Server</span>.\n </p>\n</div>\n",
"mode": "html" "mode": "html"
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"title": "HMAC Dashboard", "title": "HMAC Dashboard",
"type": "text" "type": "text"
}, },
@ -77,8 +77,8 @@
"gridPos": { "gridPos": {
"h": 7, "h": 7,
"w": 6, "w": 6,
"x": 0, "x": 3,
"y": 6 "y": 0
}, },
"id": 14, "id": 14,
"options": { "options": {
@ -105,7 +105,7 @@
"sizing": "auto", "sizing": "auto",
"valueMode": "color" "valueMode": "color"
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -143,8 +143,8 @@
"gridPos": { "gridPos": {
"h": 7, "h": 7,
"w": 6, "w": 6,
"x": 6, "x": 9,
"y": 6 "y": 0
}, },
"id": 18, "id": 18,
"options": { "options": {
@ -164,7 +164,7 @@
"textMode": "auto", "textMode": "auto",
"wideLayout": true "wideLayout": true
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -203,9 +203,9 @@
}, },
"gridPos": { "gridPos": {
"h": 7, "h": 7,
"w": 4, "w": 5,
"x": 12, "x": 15,
"y": 6 "y": 0
}, },
"id": 10, "id": 10,
"options": { "options": {
@ -225,7 +225,7 @@
"textMode": "value", "textMode": "value",
"wideLayout": true "wideLayout": true
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -262,8 +262,8 @@
"gridPos": { "gridPos": {
"h": 7, "h": 7,
"w": 4, "w": 4,
"x": 16, "x": 20,
"y": 6 "y": 0
}, },
"id": 17, "id": 17,
"options": { "options": {
@ -283,7 +283,7 @@
"textMode": "auto", "textMode": "auto",
"wideLayout": true "wideLayout": true
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -297,77 +297,6 @@
"title": "HMAC GoRoutines", "title": "HMAC GoRoutines",
"type": "stat" "type": "stat"
}, },
{
"datasource": {
"default": true,
"type": "prometheus"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "s"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 4,
"x": 20,
"y": 6
},
"id": 15,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"percentChangeColorMode": "standard",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "11.5.2",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "bduehd5vqv1moa"
},
"editorMode": "code",
"expr": "hmac_file_server_upload_duration_seconds_sum + hmac_file_server_download_duration_seconds_sum",
"hide": false,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "B"
}
],
"title": "HMAC Up/Down Duration",
"type": "stat"
},
{ {
"datasource": { "datasource": {
"default": true, "default": true,
@ -397,7 +326,7 @@
"h": 7, "h": 7,
"w": 5, "w": 5,
"x": 0, "x": 0,
"y": 13 "y": 7
}, },
"id": 11, "id": 11,
"options": { "options": {
@ -417,7 +346,7 @@
"textMode": "value", "textMode": "value",
"wideLayout": true "wideLayout": true
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -460,7 +389,7 @@
"h": 7, "h": 7,
"w": 5, "w": 5,
"x": 5, "x": 5,
"y": 13 "y": 7
}, },
"id": 12, "id": 12,
"options": { "options": {
@ -480,7 +409,7 @@
"textMode": "value", "textMode": "value",
"wideLayout": true "wideLayout": true
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -493,6 +422,77 @@
"title": "HMAC Downloads", "title": "HMAC Downloads",
"type": "stat" "type": "stat"
}, },
{
"datasource": {
"default": true,
"type": "prometheus"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "s"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 3,
"x": 10,
"y": 7
},
"id": 15,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"percentChangeColorMode": "standard",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "11.4.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "bduehd5vqv1moa"
},
"editorMode": "code",
"expr": "hmac_file_server_upload_duration_seconds_sum + hmac_file_server_download_duration_seconds_sum",
"hide": false,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "B"
}
],
"title": "HMAC Up/Down Duration",
"type": "stat"
},
{ {
"datasource": { "datasource": {
"default": true, "default": true,
@ -519,9 +519,9 @@
}, },
"gridPos": { "gridPos": {
"h": 7, "h": 7,
"w": 5, "w": 3,
"x": 10, "x": 13,
"y": 13 "y": 7
}, },
"id": 13, "id": 13,
"options": { "options": {
@ -541,7 +541,7 @@
"textMode": "auto", "textMode": "auto",
"wideLayout": true "wideLayout": true
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -580,9 +580,9 @@
}, },
"gridPos": { "gridPos": {
"h": 7, "h": 7,
"w": 5, "w": 3,
"x": 15, "x": 16,
"y": 13 "y": 7
}, },
"id": 2, "id": 2,
"options": { "options": {
@ -602,7 +602,7 @@
"textMode": "auto", "textMode": "auto",
"wideLayout": true "wideLayout": true
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -641,9 +641,9 @@
}, },
"gridPos": { "gridPos": {
"h": 7, "h": 7,
"w": 4, "w": 5,
"x": 20, "x": 19,
"y": 13 "y": 7
}, },
"id": 21, "id": 21,
"options": { "options": {
@ -663,7 +663,7 @@
"textMode": "value", "textMode": "value",
"wideLayout": true "wideLayout": true
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -704,7 +704,7 @@
"h": 7, "h": 7,
"w": 3, "w": 3,
"x": 0, "x": 0,
"y": 20 "y": 14
}, },
"id": 19, "id": 19,
"options": { "options": {
@ -724,7 +724,7 @@
"textMode": "auto", "textMode": "auto",
"wideLayout": true "wideLayout": true
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -746,7 +746,21 @@
"fieldConfig": { "fieldConfig": {
"defaults": { "defaults": {
"color": { "color": {
"mode": "thresholds" "mode": "palette-classic"
},
"custom": {
"fillOpacity": 80,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineWidth": 1,
"stacking": {
"group": "A",
"mode": "none"
}
}, },
"fieldMinMax": false, "fieldMinMax": false,
"mappings": [], "mappings": [],
@ -762,7 +776,8 @@
"value": 80 "value": 80
} }
] ]
} },
"unit": "files"
}, },
"overrides": [] "overrides": []
}, },
@ -770,42 +785,37 @@
"h": 7, "h": 7,
"w": 7, "w": 7,
"x": 3, "x": 3,
"y": 20 "y": 14
}, },
"id": 22, "id": 22,
"options": { "options": {
"colorMode": "value", "legend": {
"graphMode": "area", "calcs": [],
"justifyMode": "auto", "displayMode": "list",
"orientation": "auto", "placement": "bottom",
"percentChangeColorMode": "standard", "showLegend": true
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
}, },
"showPercentChange": false, "tooltip": {
"textMode": "auto", "mode": "single",
"wideLayout": true "sort": "none"
}
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
"exemplar": false, "exemplar": false,
"expr": "hmac_active_connections_total", "expr": "increase(hmac_file_server_clamav_scans_total[24h])",
"format": "time_series", "format": "time_series",
"instant": false, "instant": true,
"interval": "", "interval": "",
"legendFormat": "__auto", "legendFormat": "__auto",
"range": true, "range": true,
"refId": "A" "refId": "A"
} }
], ],
"title": "HMAC Active Connection(s)", "title": "HMAC ClamAV San (24h)",
"type": "stat" "type": "histogram"
}, },
{ {
"datasource": { "datasource": {
@ -818,36 +828,17 @@
"mode": "palette-classic" "mode": "palette-classic"
}, },
"custom": { "custom": {
"axisBorderShow": false, "fillOpacity": 80,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none", "gradientMode": "none",
"hideFrom": { "hideFrom": {
"legend": false, "legend": false,
"tooltip": false, "tooltip": false,
"viz": false "viz": false
}, },
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1, "lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": { "stacking": {
"group": "A", "group": "A",
"mode": "none" "mode": "none"
},
"thresholdsStyle": {
"mode": "off"
} }
}, },
"fieldMinMax": false, "fieldMinMax": false,
@ -873,7 +864,7 @@
"h": 7, "h": 7,
"w": 7, "w": 7,
"x": 10, "x": 10,
"y": 20 "y": 14
}, },
"id": 23, "id": 23,
"options": { "options": {
@ -884,17 +875,16 @@
"showLegend": true "showLegend": true
}, },
"tooltip": { "tooltip": {
"hideZeros": false,
"mode": "single", "mode": "single",
"sort": "none" "sort": "none"
} }
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
"exemplar": false, "exemplar": false,
"expr": "hmac_deduplication_errors_total", "expr": "increase(hmac_file_server_clamav_errors_total[24h])",
"format": "time_series", "format": "time_series",
"instant": true, "instant": true,
"interval": "", "interval": "",
@ -903,8 +893,8 @@
"refId": "A" "refId": "A"
} }
], ],
"title": "HMAC Duplication Error", "title": "HMAC ClamAV SanError(s) (24h)",
"type": "timeseries" "type": "histogram"
}, },
{ {
"datasource": { "datasource": {
@ -951,7 +941,7 @@
"h": 7, "h": 7,
"w": 7, "w": 7,
"x": 17, "x": 17,
"y": 20 "y": 14
}, },
"id": 16, "id": 16,
"options": { "options": {
@ -962,12 +952,11 @@
"showLegend": true "showLegend": true
}, },
"tooltip": { "tooltip": {
"hideZeros": false,
"mode": "single", "mode": "single",
"sort": "none" "sort": "none"
} }
}, },
"pluginVersion": "11.5.2", "pluginVersion": "11.4.0",
"targets": [ "targets": [
{ {
"editorMode": "code", "editorMode": "code",
@ -986,20 +975,19 @@
} }
], ],
"preload": false, "preload": false,
"refresh": "10s",
"schemaVersion": 40, "schemaVersion": 40,
"tags": [], "tags": [],
"templating": { "templating": {
"list": [] "list": []
}, },
"time": { "time": {
"from": "now-5m", "from": "now-24h",
"to": "now" "to": "now"
}, },
"timepicker": {}, "timepicker": {},
"timezone": "", "timezone": "",
"title": "HMAC File Server Metrics", "title": "HMAC File Server Metrics",
"uid": "de0ye5t0hzq4ge", "uid": "de0ye5t0hzq4ge",
"version": 158, "version": 153,
"weekStart": "" "weekStart": ""
} }

69
lib/maps/iter.go Normal file
View File

@ -0,0 +1,69 @@
package maps
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
type Seq2[K comparable, V any] func(yield func(K, V) bool)
type Seq[K any] func(yield func(K) bool)
func All[Map ~map[K]V, K comparable, V any](m Map) Seq2[K, V] {
return func(yield func(K, V) bool) {
for k, v := range m {
if !yield(k, v) {
return
}
}
}
}
// All returns an iterator over key-value pairs from m.
// The iteration order is not specified and is not guaranteed
// to be the same from one call to the next.
func Insert[Map ~map[K]V, K comparable, V any](m Map, seq Seq2[K, V]) {
seq(func(k K, v V) bool {
m[k] = v
return true
})
}
// Insert adds the key-value pairs from seq to m.
// If a key in seq already exists in m, its value will be overwritten.
func Collect[K comparable, V any](seq Seq2[K, V]) map[K]V {
m := make(map[K]V)
Insert(m, seq)
return m
}
// Collect collects key-value pairs from seq into a new map
// and returns it.
func Keys[Map ~map[K]V, K comparable, V any](m Map) Seq[K] {
return func(yield func(K) bool) {
for k := range m {
if !yield(k) {
return
}
}
}
}
// Keys returns an iterator over keys in m.
// The iteration order is not specified and is not guaranteed
// to be the same from one call to the next.
func Values[Map ~map[K]V, K comparable, V any](m Map) Seq[V] {
return func(yield func(V) bool) {
for _, v := range m {
if !yield(v) {
return
}
}
}
}
// Values returns an iterator over values in m.
// The iteration order is not specified and is not guaranteed
// to be the same from one call to the next.