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
|
||||
|
||||
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)")
|
||||
|
||||
prepare_build() {
|
||||
|
|
@ -14,19 +14,51 @@ prepare_build() {
|
|||
build_binary() {
|
||||
local os=$1
|
||||
local arch=$2
|
||||
local output="dist/${PROJECT_NAME}_${os}_${arch}"
|
||||
env GOOS=$os GOARCH=$arch go build -o $output . &> "build_logs/${os}_${arch}.log"
|
||||
local output_main="dist/${os}_${arch}_main"
|
||||
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
|
||||
echo "Build failed for $os/$arch" >> "build_logs/error.log"
|
||||
echo "Build failed for $os/$arch main" >> "build_logs/error.log"
|
||||
else
|
||||
echo "Build succeeded for $os/$arch"
|
||||
echo "Build succeeded for $os/$arch main"
|
||||
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
|
||||
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
|
||||
echo "Static build failed for $os/$arch" >> "build_logs/error.log"
|
||||
echo "Static build failed for $os/$arch main" >> "build_logs/error.log"
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
|
@ -15,7 +19,8 @@ import (
|
|||
type ContainerInfo struct {
|
||||
ID string
|
||||
Name string
|
||||
Interval time.Duration
|
||||
CName string
|
||||
Interval string
|
||||
Ignores []string
|
||||
}
|
||||
|
||||
|
|
@ -24,10 +29,14 @@ var notifiedChanges = struct {
|
|||
changes map[string]string
|
||||
}{changes: make(map[string]string)}
|
||||
|
||||
var watchedContainers = struct {
|
||||
sync.RWMutex
|
||||
containers map[string]ContainerInfo
|
||||
}{containers: make(map[string]ContainerInfo)}
|
||||
var apiAddress string
|
||||
|
||||
func init() {
|
||||
apiAddress = os.Getenv("API_ADDRESS")
|
||||
if apiAddress == "" {
|
||||
apiAddress = "http://localhost:8080"
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Check if GLITCHTIP_DSN environment variable is set
|
||||
|
|
@ -38,132 +47,90 @@ func main() {
|
|||
|
||||
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 {
|
||||
containers, err := getContainers()
|
||||
containers, err := fetchContainers()
|
||||
if err != nil {
|
||||
log.Printf("Error listing containers: %v", err)
|
||||
log.Printf("Error fetching containers: %v", err)
|
||||
time.Sleep(1 * time.Minute)
|
||||
continue
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, container := range containers {
|
||||
watchedContainers.RLock()
|
||||
_, watched := watchedContainers.containers[container.ID]
|
||||
watchedContainers.RUnlock()
|
||||
|
||||
if !watched {
|
||||
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.Add(1)
|
||||
go func(container ContainerInfo) {
|
||||
defer wg.Done()
|
||||
checkAndNotify(container)
|
||||
}(container)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
cleanupHashes(containers)
|
||||
|
||||
// Sleep for 1 minute before checking for new containers again
|
||||
time.Sleep(1 * time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
func getContainers() ([]ContainerInfo, error) {
|
||||
log.Println("Fetching container list...")
|
||||
output, err := exec.Command("docker", "ps", "--format", "{{.ID}} {{.Label \"oculus.enable\"}} {{.Label \"oculus.interval\"}} {{.Label \"oculus.cname\"}} {{.Label \"oculus.ignores\"}}").Output()
|
||||
func fetchContainers() ([]ContainerInfo, error) {
|
||||
resp, err := http.Get(fmt.Sprintf("%s/containers", apiAddress))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
var containers []ContainerInfo
|
||||
|
||||
for _, line := range lines {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
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,
|
||||
})
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("error fetching containers: %s", resp.Status)
|
||||
}
|
||||
|
||||
var containers []ContainerInfo
|
||||
if err := json.NewDecoder(resp.Body).Decode(&containers); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("Found %d containers to monitor.", len(containers))
|
||||
return containers, nil
|
||||
}
|
||||
|
||||
func monitorContainer(container ContainerInfo, initial bool) {
|
||||
ticker := time.NewTicker(container.Interval)
|
||||
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()
|
||||
func fetchDiffOutput(containerID string) (string, error) {
|
||||
resp, err := http.Get(fmt.Sprintf("%s/diff?id=%s", apiAddress, containerID))
|
||||
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 != "" {
|
||||
// Calculate a hash of the filtered diff output
|
||||
diffHash := fmt.Sprintf("%x", md5.Sum([]byte(filteredOutput)))
|
||||
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]
|
||||
notifiedChanges.RUnlock()
|
||||
|
||||
if initial {
|
||||
// For the initial check, just store the hash and don't send a notification
|
||||
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)
|
||||
if !notified || lastNotifiedHash != diffHash {
|
||||
filename := filepath.Join("diffs", fmt.Sprintf("%s.diff", container.CName))
|
||||
err := writeToFile(filename, filteredOutput)
|
||||
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)
|
||||
if err != nil {
|
||||
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 {
|
||||
log.Printf("No significant changes detected for container: %s (%s)", container.Name, container.ID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func filterDiffOutput(diffOutput string, ignores []string) (string, error) {
|
||||
tempFile, err := os.CreateTemp("", "diff_output")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
func filterDiffOutput(diffOutput, cname string, ignores []string) (string, error) {
|
||||
filename := filepath.Join("diffs", fmt.Sprintf("%s.diff", cname))
|
||||
|
||||
_, err = tempFile.WriteString(diffOutput)
|
||||
// Write the diff output to the file
|
||||
err := ioutil.WriteFile(filename, []byte(diffOutput), 0644)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", fmt.Errorf("error writing diff to file: %s", err)
|
||||
}
|
||||
tempFile.Close()
|
||||
|
||||
for _, ignore := range ignores {
|
||||
cmd := exec.Command("sed", "-i", fmt.Sprintf("/%s/d", ignore), tempFile.Name())
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error running sed command: %w", err)
|
||||
// Read and filter the diff output
|
||||
var filteredLines []string
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
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 != nil {
|
||||
return "", err
|
||||
if err := scanner.Err(); err != nil {
|
||||
return "", fmt.Errorf("error reading diff file: %s", err)
|
||||
}
|
||||
|
||||
return string(filteredOutput), nil
|
||||
}
|
||||
|
||||
func matchPath(path, pattern string) bool {
|
||||
match, err := filepath.Match(pattern, filepath.Base(path))
|
||||
filteredOutput := strings.Join(filteredLines, "\n")
|
||||
err = ioutil.WriteFile(filename, []byte(filteredOutput), 0644)
|
||||
if err != nil {
|
||||
log.Printf("Error matching pattern: %s with path: %s, error: %v", pattern, path, err)
|
||||
return false
|
||||
return "", fmt.Errorf("error writing filtered diff to file: %s", err)
|
||||
}
|
||||
log.Printf("Matching path %s with pattern %s: %v", path, pattern, match)
|
||||
return match
|
||||
|
||||
return filteredOutput, nil
|
||||
}
|
||||
|
||||
func writeToFile(filename, content string) error {
|
||||
|
|
@ -268,26 +234,22 @@ func sendNotification(content string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func monitorContainerList() {
|
||||
for {
|
||||
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 ensureDiffsDirectory() error {
|
||||
return os.MkdirAll("./diffs", os.ModePerm)
|
||||
}
|
||||
|
||||
func isContainerRunning(containerID string) bool {
|
||||
cmd := exec.Command("docker", "inspect", "--format", "{{.State.Running}}", containerID)
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Printf("Error inspecting container %s: %v", containerID, err)
|
||||
return false
|
||||
func cleanupHashes(containers []ContainerInfo) {
|
||||
notifiedChanges.Lock()
|
||||
defer notifiedChanges.Unlock()
|
||||
|
||||
activeContainers := make(map[string]bool)
|
||||
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 /file2.txt
|
||||
A /file3.txt
|
||||
A /file3.txt
|
||||
Loading…
Reference in New Issue