diff --git a/api.go b/api.go new file mode 100644 index 0000000..4b2de5b --- /dev/null +++ b/api.go @@ -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() +} diff --git a/build.sh b/build.sh index 03907e4..5981132 100755 --- a/build.sh +++ b/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 } diff --git a/build_logs/darwin_amd64.log b/build_logs/darwin_amd64.log deleted file mode 100644 index 35171e2..0000000 --- a/build_logs/darwin_amd64.log +++ /dev/null @@ -1,2 +0,0 @@ -# oculus -./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/darwin_amd64_api.log b/build_logs/darwin_amd64_api.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/darwin_amd64_filter.log b/build_logs/darwin_amd64_filter.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/darwin_amd64_main.log b/build_logs/darwin_amd64_main.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/darwin_arm64.log b/build_logs/darwin_arm64.log deleted file mode 100644 index 35171e2..0000000 --- a/build_logs/darwin_arm64.log +++ /dev/null @@ -1,2 +0,0 @@ -# oculus -./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/darwin_arm64_api.log b/build_logs/darwin_arm64_api.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/darwin_arm64_filter.log b/build_logs/darwin_arm64_filter.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/darwin_arm64_main.log b/build_logs/darwin_arm64_main.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/error.log b/build_logs/error.log deleted file mode 100644 index d042852..0000000 --- a/build_logs/error.log +++ /dev/null @@ -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 diff --git a/build_logs/linux_amd64.log b/build_logs/linux_amd64.log deleted file mode 100644 index 35171e2..0000000 --- a/build_logs/linux_amd64.log +++ /dev/null @@ -1,2 +0,0 @@ -# oculus -./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/linux_amd64_api.log b/build_logs/linux_amd64_api.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_amd64_api_static.log b/build_logs/linux_amd64_api_static.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_amd64_filter.log b/build_logs/linux_amd64_filter.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_amd64_filter_static.log b/build_logs/linux_amd64_filter_static.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_amd64_main.log b/build_logs/linux_amd64_main.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_amd64_main_static.log b/build_logs/linux_amd64_main_static.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_amd64_static.log b/build_logs/linux_amd64_static.log deleted file mode 100644 index 35171e2..0000000 --- a/build_logs/linux_amd64_static.log +++ /dev/null @@ -1,2 +0,0 @@ -# oculus -./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/linux_arm64.log b/build_logs/linux_arm64.log deleted file mode 100644 index 35171e2..0000000 --- a/build_logs/linux_arm64.log +++ /dev/null @@ -1,2 +0,0 @@ -# oculus -./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/linux_arm64_api.log b/build_logs/linux_arm64_api.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_arm64_api_static.log b/build_logs/linux_arm64_api_static.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_arm64_filter.log b/build_logs/linux_arm64_filter.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_arm64_filter_static.log b/build_logs/linux_arm64_filter_static.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_arm64_main.log b/build_logs/linux_arm64_main.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_arm64_main_static.log b/build_logs/linux_arm64_main_static.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/linux_arm64_static.log b/build_logs/linux_arm64_static.log deleted file mode 100644 index 35171e2..0000000 --- a/build_logs/linux_arm64_static.log +++ /dev/null @@ -1,2 +0,0 @@ -# oculus -./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/windows_amd64.log b/build_logs/windows_amd64.log deleted file mode 100644 index 35171e2..0000000 --- a/build_logs/windows_amd64.log +++ /dev/null @@ -1,2 +0,0 @@ -# oculus -./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/windows_amd64_api.log b/build_logs/windows_amd64_api.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/windows_amd64_filter.log b/build_logs/windows_amd64_filter.log new file mode 100644 index 0000000..e69de29 diff --git a/build_logs/windows_amd64_main.log b/build_logs/windows_amd64_main.log new file mode 100644 index 0000000..e69de29 diff --git a/diffs/my_friendly_container.diff b/diffs/my_friendly_container.diff new file mode 100644 index 0000000..3a24665 --- /dev/null +++ b/diffs/my_friendly_container.diff @@ -0,0 +1 @@ +A /newfile.txt \ No newline at end of file diff --git a/diffs/second_container.diff b/diffs/second_container.diff new file mode 100644 index 0000000..54c37c0 --- /dev/null +++ b/diffs/second_container.diff @@ -0,0 +1,5 @@ +D /file1.txt +A /file2.txt +C /do/match/file3.txt +A /dontmatch.yml +D /dont/match.md diff --git a/dist/darwin_amd64_api b/dist/darwin_amd64_api new file mode 100755 index 0000000..9c8a042 Binary files /dev/null and b/dist/darwin_amd64_api differ diff --git a/dist/darwin_amd64_filter b/dist/darwin_amd64_filter new file mode 100755 index 0000000..ecba7ad Binary files /dev/null and b/dist/darwin_amd64_filter differ diff --git a/dist/darwin_amd64_main b/dist/darwin_amd64_main new file mode 100755 index 0000000..ffea7da Binary files /dev/null and b/dist/darwin_amd64_main differ diff --git a/dist/darwin_arm64_api b/dist/darwin_arm64_api new file mode 100755 index 0000000..5eecce0 Binary files /dev/null and b/dist/darwin_arm64_api differ diff --git a/dist/darwin_arm64_filter b/dist/darwin_arm64_filter new file mode 100755 index 0000000..b3bd419 Binary files /dev/null and b/dist/darwin_arm64_filter differ diff --git a/dist/darwin_arm64_main b/dist/darwin_arm64_main new file mode 100755 index 0000000..baaea5f Binary files /dev/null and b/dist/darwin_arm64_main differ diff --git a/dist/linux_amd64_api b/dist/linux_amd64_api new file mode 100755 index 0000000..47125f4 Binary files /dev/null and b/dist/linux_amd64_api differ diff --git a/dist/linux_amd64_api_static b/dist/linux_amd64_api_static new file mode 100755 index 0000000..feb3b1b Binary files /dev/null and b/dist/linux_amd64_api_static differ diff --git a/dist/linux_amd64_filter b/dist/linux_amd64_filter new file mode 100755 index 0000000..ab4814e Binary files /dev/null and b/dist/linux_amd64_filter differ diff --git a/dist/linux_amd64_filter_static b/dist/linux_amd64_filter_static new file mode 100755 index 0000000..d0c61e0 Binary files /dev/null and b/dist/linux_amd64_filter_static differ diff --git a/dist/linux_amd64_main b/dist/linux_amd64_main new file mode 100755 index 0000000..37f63a5 Binary files /dev/null and b/dist/linux_amd64_main differ diff --git a/dist/linux_amd64_main_static b/dist/linux_amd64_main_static new file mode 100755 index 0000000..7aa2b21 Binary files /dev/null and b/dist/linux_amd64_main_static differ diff --git a/dist/linux_arm64_api b/dist/linux_arm64_api new file mode 100755 index 0000000..3c4d5e6 Binary files /dev/null and b/dist/linux_arm64_api differ diff --git a/dist/linux_arm64_api_static b/dist/linux_arm64_api_static new file mode 100755 index 0000000..cb10f7b Binary files /dev/null and b/dist/linux_arm64_api_static differ diff --git a/dist/linux_arm64_filter b/dist/linux_arm64_filter new file mode 100755 index 0000000..22eed99 Binary files /dev/null and b/dist/linux_arm64_filter differ diff --git a/dist/linux_arm64_filter_static b/dist/linux_arm64_filter_static new file mode 100755 index 0000000..9a36217 Binary files /dev/null and b/dist/linux_arm64_filter_static differ diff --git a/dist/linux_arm64_main b/dist/linux_arm64_main new file mode 100755 index 0000000..66960b5 Binary files /dev/null and b/dist/linux_arm64_main differ diff --git a/dist/linux_arm64_main_static b/dist/linux_arm64_main_static new file mode 100755 index 0000000..0c6783a Binary files /dev/null and b/dist/linux_arm64_main_static differ diff --git a/dist/oculus_darwin_amd64 b/dist/oculus_darwin_amd64 deleted file mode 100755 index df452bf..0000000 Binary files a/dist/oculus_darwin_amd64 and /dev/null differ diff --git a/dist/oculus_darwin_arm64 b/dist/oculus_darwin_arm64 deleted file mode 100755 index 5409eee..0000000 Binary files a/dist/oculus_darwin_arm64 and /dev/null differ diff --git a/dist/oculus_linux_amd64 b/dist/oculus_linux_amd64 deleted file mode 100755 index 017f110..0000000 Binary files a/dist/oculus_linux_amd64 and /dev/null differ diff --git a/dist/oculus_linux_amd64_static b/dist/oculus_linux_amd64_static deleted file mode 100755 index 7b5b61c..0000000 Binary files a/dist/oculus_linux_amd64_static and /dev/null differ diff --git a/dist/oculus_linux_arm64 b/dist/oculus_linux_arm64 deleted file mode 100755 index 0d25e59..0000000 Binary files a/dist/oculus_linux_arm64 and /dev/null differ diff --git a/dist/oculus_linux_arm64_static b/dist/oculus_linux_arm64_static deleted file mode 100755 index 93c0528..0000000 Binary files a/dist/oculus_linux_arm64_static and /dev/null differ diff --git a/dist/oculus_windows_amd64 b/dist/oculus_windows_amd64 deleted file mode 100755 index 4b266a3..0000000 Binary files a/dist/oculus_windows_amd64 and /dev/null differ diff --git a/dist/windows_amd64_api b/dist/windows_amd64_api new file mode 100755 index 0000000..0754feb Binary files /dev/null and b/dist/windows_amd64_api differ diff --git a/dist/windows_amd64_filter b/dist/windows_amd64_filter new file mode 100755 index 0000000..ff72919 Binary files /dev/null and b/dist/windows_amd64_filter differ diff --git a/dist/windows_amd64_main b/dist/windows_amd64_main new file mode 100755 index 0000000..d488cd2 Binary files /dev/null and b/dist/windows_amd64_main differ diff --git a/filter.go b/filter.go new file mode 100644 index 0000000..df3e3de --- /dev/null +++ b/filter.go @@ -0,0 +1,105 @@ +package main + +import ( + "bufio" + "fmt" + "log" + "os" + "regexp" + "strings" +) + +func main() { + if len(os.Args) < 3 { + log.Fatalf("Usage: %s [ ... ]\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 +} diff --git a/main.go b/main.go index f42d7b7..3664535 100644 --- a/main.go +++ b/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" } diff --git a/second_container.diff b/second_container.diff index e34be8a..00fa6ab 100644 --- a/second_container.diff +++ b/second_container.diff @@ -1,3 +1,3 @@ A /file1.txt A /file2.txt -A /file3.txt +A /file3.txt \ No newline at end of file