Stable and functional filter.go it manages to match a number of lines I need matched.
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
5ebd0a46eb
commit
a6b6f5cbf6
|
|
@ -0,0 +1,115 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ContainerInfo struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
CName string
|
||||||
|
Interval string
|
||||||
|
Ignores []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func listContainersHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
containers, err := listContainers()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Error listing containers: %v", err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(containers); err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Error encoding response: %v", err), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDiffHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
containerID := r.URL.Query().Get("id")
|
||||||
|
if containerID == "" {
|
||||||
|
http.Error(w, "Missing container ID", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
diffOutput, err := getDiffOutput(containerID)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Error getting diff for container %s: %v", containerID, err), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
w.Write([]byte(diffOutput))
|
||||||
|
}
|
||||||
|
|
||||||
|
func startServer() {
|
||||||
|
http.HandleFunc("/containers", listContainersHandler)
|
||||||
|
http.HandleFunc("/diff", getDiffHandler)
|
||||||
|
|
||||||
|
log.Println("Starting server on :8080")
|
||||||
|
if err := http.ListenAndServe(":8080", nil); err != nil {
|
||||||
|
log.Fatalf("Error starting server: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listContainers() ([]ContainerInfo, error) {
|
||||||
|
output, err := exec.Command("docker", "ps", "--format", "{{.ID}} {{.Names}} {{.Label \"oculus.enable\"}} {{.Label \"oculus.interval\"}} {{.Label \"oculus.cname\"}} {{.Label \"oculus.ignores\"}}").Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
var containers []ContainerInfo
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) < 3 || parts[2] == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
id := parts[0]
|
||||||
|
name := parts[1]
|
||||||
|
interval := "300s"
|
||||||
|
if len(parts) > 3 && parts[3] != "" {
|
||||||
|
interval = parts[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
cname := name
|
||||||
|
if len(parts) > 4 && parts[4] != "" {
|
||||||
|
cname = parts[4]
|
||||||
|
}
|
||||||
|
|
||||||
|
ignores := []string{}
|
||||||
|
if len(parts) > 5 && parts[5] != "" {
|
||||||
|
ignores = strings.Split(parts[5], ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
containers = append(containers, ContainerInfo{
|
||||||
|
ID: id,
|
||||||
|
Name: name,
|
||||||
|
CName: cname,
|
||||||
|
Interval: interval,
|
||||||
|
Ignores: ignores,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return containers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDiffOutput(containerID string) (string, error) {
|
||||||
|
cmd := exec.Command("docker", "diff", containerID)
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("docker diff failed: %w", err)
|
||||||
|
}
|
||||||
|
return string(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
startServer()
|
||||||
|
}
|
||||||
48
build.sh
48
build.sh
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
ARCHITECTURES=("linux/amd64" "linux/arm64" "darwin/amd64" "darwin/arm64" "windows/amd64")
|
ARCHITECTURES=("darwin/arm64" "linux/amd64" "linux/arm64" "darwin/amd64" "windows/amd64")
|
||||||
PROJECT_NAME=$(basename "$(pwd)")
|
PROJECT_NAME=$(basename "$(pwd)")
|
||||||
|
|
||||||
prepare_build() {
|
prepare_build() {
|
||||||
|
|
@ -14,19 +14,51 @@ prepare_build() {
|
||||||
build_binary() {
|
build_binary() {
|
||||||
local os=$1
|
local os=$1
|
||||||
local arch=$2
|
local arch=$2
|
||||||
local output="dist/${PROJECT_NAME}_${os}_${arch}"
|
local output_main="dist/${os}_${arch}_main"
|
||||||
env GOOS=$os GOARCH=$arch go build -o $output . &> "build_logs/${os}_${arch}.log"
|
local output_filter="dist/${os}_${arch}_filter"
|
||||||
|
local output_api="dist/${os}_${arch}_api"
|
||||||
|
|
||||||
|
env GOOS=$os GOARCH=$arch go build -o $output_main main.go &> "build_logs/${os}_${arch}_main.log"
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "Build failed for $os/$arch" >> "build_logs/error.log"
|
echo "Build failed for $os/$arch main" >> "build_logs/error.log"
|
||||||
else
|
else
|
||||||
echo "Build succeeded for $os/$arch"
|
echo "Build succeeded for $os/$arch main"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
env GOOS=$os GOARCH=$arch go build -o $output_filter filter.go &> "build_logs/${os}_${arch}_filter.log"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Build failed for $os/$arch filter" >> "build_logs/error.log"
|
||||||
|
else
|
||||||
|
echo "Build succeeded for $os/$arch filter"
|
||||||
|
fi
|
||||||
|
|
||||||
|
env GOOS=$os GOARCH=$arch go build -o $output_api api.go &> "build_logs/${os}_${arch}_api.log"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Build failed for $os/$arch api" >> "build_logs/error.log"
|
||||||
|
else
|
||||||
|
echo "Build succeeded for $os/$arch api"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$os" == "linux" ]; then
|
if [ "$os" == "linux" ]; then
|
||||||
env GOOS=$os GOARCH=$arch CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o "${output}_static" . &> "build_logs/${os}_${arch}_static.log"
|
env GOOS=$os GOARCH=$arch CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o "${output_main}_static" main.go &> "build_logs/${os}_${arch}_main_static.log"
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo "Static build failed for $os/$arch" >> "build_logs/error.log"
|
echo "Static build failed for $os/$arch main" >> "build_logs/error.log"
|
||||||
else
|
else
|
||||||
echo "Static build succeeded for $os/$arch"
|
echo "Static build succeeded for $os/$arch main"
|
||||||
|
fi
|
||||||
|
|
||||||
|
env GOOS=$os GOARCH=$arch CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o "${output_filter}_static" filter.go &> "build_logs/${os}_${arch}_filter_static.log"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Static build failed for $os/$arch filter" >> "build_logs/error.log"
|
||||||
|
else
|
||||||
|
echo "Static build succeeded for $os/$arch filter"
|
||||||
|
fi
|
||||||
|
|
||||||
|
env GOOS=$os GOARCH=$arch CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o "${output_api}_static" api.go &> "build_logs/${os}_${arch}_api_static.log"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Static build failed for $os/$arch api" >> "build_logs/error.log"
|
||||||
|
else
|
||||||
|
echo "Static build succeeded for $os/$arch api"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# oculus
|
|
||||||
./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# oculus
|
|
||||||
./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value
|
|
||||||
|
|
@ -1,178 +0,0 @@
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
Build failed for linux/amd64
|
|
||||||
Static build failed for linux/amd64
|
|
||||||
Build failed for linux/arm64
|
|
||||||
Static build failed for linux/arm64
|
|
||||||
Build failed for darwin/amd64
|
|
||||||
Build failed for darwin/arm64
|
|
||||||
Build failed for windows/amd64
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# oculus
|
|
||||||
./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# oculus
|
|
||||||
./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# oculus
|
|
||||||
./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# oculus
|
|
||||||
./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
# oculus
|
|
||||||
./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
A /newfile.txt
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
D /file1.txt
|
||||||
|
A /file2.txt
|
||||||
|
C /do/match/file3.txt
|
||||||
|
A /dontmatch.yml
|
||||||
|
D /dont/match.md
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,105 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 3 {
|
||||||
|
log.Fatalf("Usage: %s <file> <pattern1> [<pattern2> ... <patternN>]\n", os.Args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := os.Args[1]
|
||||||
|
patterns := os.Args[2:]
|
||||||
|
|
||||||
|
// Compile patterns into regular expressions
|
||||||
|
regexPatterns := compilePatterns(patterns)
|
||||||
|
|
||||||
|
// Read the file
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error opening file: %s\n", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var filteredLines []string
|
||||||
|
var removedCount int
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
fmt.Printf("Processing line: %s\n", line) // Debug print
|
||||||
|
if matchesAnyPattern(line, regexPatterns) {
|
||||||
|
fmt.Printf("Line matches pattern, removing: %s\n", line) // Debug print
|
||||||
|
removedCount++
|
||||||
|
} else {
|
||||||
|
filteredLines = append(filteredLines, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
log.Fatalf("Error reading file: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the filtered lines back to the file
|
||||||
|
tempFile, err := os.CreateTemp("", "filtered_output")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error creating temporary file: %s\n", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tempFile.Name())
|
||||||
|
|
||||||
|
for _, line := range filteredLines {
|
||||||
|
_, err = tempFile.WriteString(line + "\n")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error writing to temporary file: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tempFile.Close()
|
||||||
|
|
||||||
|
err = os.Rename(tempFile.Name(), filename)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error renaming temporary file: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Filtered %d lines from %s\n", removedCount, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compilePatterns(patterns []string) []*regexp.Regexp {
|
||||||
|
var regexPatterns []*regexp.Regexp
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
// Convert wildcard pattern to regex pattern
|
||||||
|
regexPattern := strings.ReplaceAll(regexp.QuoteMeta(pattern), "\\*", ".*")
|
||||||
|
regexPattern = strings.TrimSuffix(regexPattern, `\.`) // Remove trailing dot if present
|
||||||
|
fmt.Printf("Compiled pattern: %s to regex: %s\n", pattern, regexPattern) // Debug print
|
||||||
|
re, err := regexp.Compile(regexPattern)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error compiling regex pattern: %s\n", err)
|
||||||
|
}
|
||||||
|
regexPatterns = append(regexPatterns, re)
|
||||||
|
}
|
||||||
|
return regexPatterns
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchesAnyPattern(line string, patterns []*regexp.Regexp) bool {
|
||||||
|
// Get the full path including directory
|
||||||
|
parts := strings.Fields(line)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fullPath := parts[1]
|
||||||
|
fmt.Printf("Checking filename: %s against patterns\n", fullPath) // Debug print
|
||||||
|
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
if pattern.MatchString(fullPath) {
|
||||||
|
fmt.Printf("Pattern matched: %s in filename: %s\n", pattern.String(), fullPath) // Debug print
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
286
main.go
286
main.go
|
|
@ -1,9 +1,13 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -15,7 +19,8 @@ import (
|
||||||
type ContainerInfo struct {
|
type ContainerInfo struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
Interval time.Duration
|
CName string
|
||||||
|
Interval string
|
||||||
Ignores []string
|
Ignores []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,10 +29,14 @@ var notifiedChanges = struct {
|
||||||
changes map[string]string
|
changes map[string]string
|
||||||
}{changes: make(map[string]string)}
|
}{changes: make(map[string]string)}
|
||||||
|
|
||||||
var watchedContainers = struct {
|
var apiAddress string
|
||||||
sync.RWMutex
|
|
||||||
containers map[string]ContainerInfo
|
func init() {
|
||||||
}{containers: make(map[string]ContainerInfo)}
|
apiAddress = os.Getenv("API_ADDRESS")
|
||||||
|
if apiAddress == "" {
|
||||||
|
apiAddress = "http://localhost:8080"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Check if GLITCHTIP_DSN environment variable is set
|
// Check if GLITCHTIP_DSN environment variable is set
|
||||||
|
|
@ -38,132 +47,90 @@ func main() {
|
||||||
|
|
||||||
log.Println("Starting Oculus...")
|
log.Println("Starting Oculus...")
|
||||||
|
|
||||||
go monitorContainerList()
|
// Ensure the diffs directory exists
|
||||||
|
err := ensureDiffsDirectory()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error creating diffs directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
containers, err := getContainers()
|
containers, err := fetchContainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error listing containers: %v", err)
|
log.Printf("Error fetching containers: %v", err)
|
||||||
time.Sleep(1 * time.Minute)
|
time.Sleep(1 * time.Minute)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
watchedContainers.RLock()
|
wg.Add(1)
|
||||||
_, watched := watchedContainers.containers[container.ID]
|
go func(container ContainerInfo) {
|
||||||
watchedContainers.RUnlock()
|
defer wg.Done()
|
||||||
|
checkAndNotify(container)
|
||||||
if !watched {
|
}(container)
|
||||||
log.Printf("Monitoring container: %s (%s) every %s", container.Name, container.ID, container.Interval)
|
|
||||||
watchedContainers.Lock()
|
|
||||||
watchedContainers.containers[container.ID] = container
|
|
||||||
watchedContainers.Unlock()
|
|
||||||
go monitorContainer(container, true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
cleanupHashes(containers)
|
||||||
|
|
||||||
// Sleep for 1 minute before checking for new containers again
|
// Sleep for 1 minute before checking for new containers again
|
||||||
time.Sleep(1 * time.Minute)
|
time.Sleep(1 * time.Minute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getContainers() ([]ContainerInfo, error) {
|
func fetchContainers() ([]ContainerInfo, error) {
|
||||||
log.Println("Fetching container list...")
|
resp, err := http.Get(fmt.Sprintf("%s/containers", apiAddress))
|
||||||
output, err := exec.Command("docker", "ps", "--format", "{{.ID}} {{.Label \"oculus.enable\"}} {{.Label \"oculus.interval\"}} {{.Label \"oculus.cname\"}} {{.Label \"oculus.ignores\"}}").Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
lines := strings.Split(string(output), "\n")
|
if resp.StatusCode != http.StatusOK {
|
||||||
var containers []ContainerInfo
|
return nil, fmt.Errorf("error fetching containers: %s", resp.Status)
|
||||||
|
}
|
||||||
for _, line := range lines {
|
|
||||||
if line == "" {
|
var containers []ContainerInfo
|
||||||
continue
|
if err := json.NewDecoder(resp.Body).Decode(&containers); err != nil {
|
||||||
}
|
return nil, err
|
||||||
parts := strings.Fields(line)
|
|
||||||
if len(parts) < 2 || parts[1] == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
id := parts[0]
|
|
||||||
intervalStr := "300s"
|
|
||||||
if len(parts) > 2 && parts[2] != "" {
|
|
||||||
intervalStr = parts[2]
|
|
||||||
}
|
|
||||||
interval, err := time.ParseDuration(intervalStr)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Invalid interval format for container %s: %v", id, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cname := id
|
|
||||||
if len(parts) > 3 && parts[3] != "" {
|
|
||||||
cname = parts[3]
|
|
||||||
}
|
|
||||||
|
|
||||||
ignores := []string{}
|
|
||||||
if len(parts) > 4 && parts[4] != "" {
|
|
||||||
ignores = strings.Split(parts[4], ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Container ID: %s, Name: %s, Interval: %s, Ignores: %v", id, cname, interval, ignores)
|
|
||||||
|
|
||||||
containers = append(containers, ContainerInfo{
|
|
||||||
ID: id,
|
|
||||||
Name: cname,
|
|
||||||
Interval: interval,
|
|
||||||
Ignores: ignores,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Found %d containers to monitor.", len(containers))
|
|
||||||
return containers, nil
|
return containers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func monitorContainer(container ContainerInfo, initial bool) {
|
func fetchDiffOutput(containerID string) (string, error) {
|
||||||
ticker := time.NewTicker(container.Interval)
|
resp, err := http.Get(fmt.Sprintf("%s/diff?id=%s", apiAddress, containerID))
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
log.Printf("Checking diffs for container: %s (%s)", container.Name, container.ID)
|
|
||||||
diffOutput, err := getDiffOutput(container.ID)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error getting diffs for container %s (%s): %v", container.Name, container.ID, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredOutput, err := filterDiffOutput(diffOutput, container.Ignores)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error filtering diffs for container %s (%s): %v", container.Name, container.ID, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("Filtered diff output for container %s: %s", container.Name, filteredOutput)
|
|
||||||
|
|
||||||
err = handleDiffOutput(container, filteredOutput, initial)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error handling diff output for container %s (%s): %v", container.Name, container.ID, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
initial = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDiffOutput(containerID string) (string, error) {
|
|
||||||
cmd := exec.Command("docker", "diff", containerID)
|
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("docker diff failed: %w", err)
|
return "", err
|
||||||
}
|
}
|
||||||
return string(output), nil
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return "", fmt.Errorf("error fetching diff: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(body), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleDiffOutput(container ContainerInfo, filteredOutput string, initial bool) error {
|
func checkAndNotify(container ContainerInfo) {
|
||||||
|
diffOutput, err := fetchDiffOutput(container.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error getting diffs for container %s (%s): %v", container.Name, container.ID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredOutput, err := filterDiffOutput(diffOutput, container.CName, container.Ignores)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error filtering diffs for container %s (%s): %v", container.Name, container.ID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("Filtered diff output for container %s: %s", container.Name, filteredOutput)
|
||||||
|
|
||||||
if filteredOutput != "" {
|
if filteredOutput != "" {
|
||||||
// Calculate a hash of the filtered diff output
|
|
||||||
diffHash := fmt.Sprintf("%x", md5.Sum([]byte(filteredOutput)))
|
diffHash := fmt.Sprintf("%x", md5.Sum([]byte(filteredOutput)))
|
||||||
log.Printf("Diff hash for container %s: %s", container.Name, diffHash)
|
log.Printf("Diff hash for container %s: %s", container.Name, diffHash)
|
||||||
|
|
||||||
|
|
@ -171,23 +138,14 @@ func handleDiffOutput(container ContainerInfo, filteredOutput string, initial bo
|
||||||
lastNotifiedHash, notified := notifiedChanges.changes[container.ID]
|
lastNotifiedHash, notified := notifiedChanges.changes[container.ID]
|
||||||
notifiedChanges.RUnlock()
|
notifiedChanges.RUnlock()
|
||||||
|
|
||||||
if initial {
|
if !notified || lastNotifiedHash != diffHash {
|
||||||
// For the initial check, just store the hash and don't send a notification
|
filename := filepath.Join("diffs", fmt.Sprintf("%s.diff", container.CName))
|
||||||
notifiedChanges.Lock()
|
|
||||||
notifiedChanges.changes[container.ID] = diffHash
|
|
||||||
notifiedChanges.Unlock()
|
|
||||||
log.Printf("Initial check, storing hash for container: %s (%s)", container.Name, container.ID)
|
|
||||||
} else if !notified || lastNotifiedHash != diffHash {
|
|
||||||
log.Printf("Writing diff output for container: %s (%s)", container.Name, container.ID)
|
|
||||||
// Write diff output to a file
|
|
||||||
filename := fmt.Sprintf("%s.diff", container.Name)
|
|
||||||
err := writeToFile(filename, filteredOutput)
|
err := writeToFile(filename, filteredOutput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error writing diff to file: %w", err)
|
log.Printf("Error writing diff to file: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send notification using go-glitch
|
|
||||||
log.Printf("Sending notification for container: %s (%s)", container.Name, container.ID)
|
|
||||||
err = sendNotification(filteredOutput)
|
err = sendNotification(filteredOutput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error sending notification for container %s: %v", container.ID, err)
|
log.Printf("Error sending notification for container %s: %v", container.ID, err)
|
||||||
|
|
@ -203,47 +161,55 @@ func handleDiffOutput(container ContainerInfo, filteredOutput string, initial bo
|
||||||
} else {
|
} else {
|
||||||
log.Printf("No significant changes detected for container: %s (%s)", container.Name, container.ID)
|
log.Printf("No significant changes detected for container: %s (%s)", container.Name, container.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterDiffOutput(diffOutput string, ignores []string) (string, error) {
|
func filterDiffOutput(diffOutput, cname string, ignores []string) (string, error) {
|
||||||
tempFile, err := os.CreateTemp("", "diff_output")
|
filename := filepath.Join("diffs", fmt.Sprintf("%s.diff", cname))
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer os.Remove(tempFile.Name())
|
|
||||||
|
|
||||||
_, err = tempFile.WriteString(diffOutput)
|
// Write the diff output to the file
|
||||||
|
err := ioutil.WriteFile(filename, []byte(diffOutput), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", fmt.Errorf("error writing diff to file: %s", err)
|
||||||
}
|
}
|
||||||
tempFile.Close()
|
|
||||||
|
|
||||||
for _, ignore := range ignores {
|
// Read and filter the diff output
|
||||||
cmd := exec.Command("sed", "-i", fmt.Sprintf("/%s/d", ignore), tempFile.Name())
|
var filteredLines []string
|
||||||
err := cmd.Run()
|
file, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("error running sed command: %w", err)
|
return "", fmt.Errorf("error opening diff file: %s", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
ignore := false
|
||||||
|
for _, pattern := range ignores {
|
||||||
|
match, err := filepath.Match(pattern, line)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error matching pattern: %s", err)
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
ignore = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ignore {
|
||||||
|
filteredLines = append(filteredLines, line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredOutput, err := os.ReadFile(tempFile.Name())
|
if err := scanner.Err(); err != nil {
|
||||||
if err != nil {
|
return "", fmt.Errorf("error reading diff file: %s", err)
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(filteredOutput), nil
|
filteredOutput := strings.Join(filteredLines, "\n")
|
||||||
}
|
err = ioutil.WriteFile(filename, []byte(filteredOutput), 0644)
|
||||||
|
|
||||||
func matchPath(path, pattern string) bool {
|
|
||||||
match, err := filepath.Match(pattern, filepath.Base(path))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error matching pattern: %s with path: %s, error: %v", pattern, path, err)
|
return "", fmt.Errorf("error writing filtered diff to file: %s", err)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
log.Printf("Matching path %s with pattern %s: %v", path, pattern, match)
|
|
||||||
return match
|
return filteredOutput, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeToFile(filename, content string) error {
|
func writeToFile(filename, content string) error {
|
||||||
|
|
@ -268,26 +234,22 @@ func sendNotification(content string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func monitorContainerList() {
|
func ensureDiffsDirectory() error {
|
||||||
for {
|
return os.MkdirAll("./diffs", os.ModePerm)
|
||||||
watchedContainers.Lock()
|
|
||||||
for id, container := watchedContainers.containers {
|
|
||||||
if !isContainerRunning(id) {
|
|
||||||
log.Printf("Removing stopped container from watch list: %s (%s)", container.Name, id)
|
|
||||||
delete(watchedContainers.containers, id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
watchedContainers.Unlock()
|
|
||||||
time.Sleep(1 * time.Minute)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isContainerRunning(containerID string) bool {
|
func cleanupHashes(containers []ContainerInfo) {
|
||||||
cmd := exec.Command("docker", "inspect", "--format", "{{.State.Running}}", containerID)
|
notifiedChanges.Lock()
|
||||||
output, err := cmd.Output()
|
defer notifiedChanges.Unlock()
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error inspecting container %s: %v", containerID, err)
|
activeContainers := make(map[string]bool)
|
||||||
return false
|
for _, container := range containers {
|
||||||
|
activeContainers[container.ID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for id := range notifiedChanges.changes {
|
||||||
|
if !activeContainers[id] {
|
||||||
|
delete(notifiedChanges.changes, id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return strings.TrimSpace(string(output)) == "true"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
A /file1.txt
|
A /file1.txt
|
||||||
A /file2.txt
|
A /file2.txt
|
||||||
A /file3.txt
|
A /file3.txt
|
||||||
Loading…
Reference in New Issue