diff --git a/build_logs/darwin_amd64.log b/build_logs/darwin_amd64.log index e69de29..35171e2 100644 --- a/build_logs/darwin_amd64.log +++ b/build_logs/darwin_amd64.log @@ -0,0 +1,2 @@ +# oculus +./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/darwin_arm64.log b/build_logs/darwin_arm64.log index e69de29..35171e2 100644 --- a/build_logs/darwin_arm64.log +++ b/build_logs/darwin_arm64.log @@ -0,0 +1,2 @@ +# oculus +./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/error.log b/build_logs/error.log new file mode 100644 index 0000000..d042852 --- /dev/null +++ b/build_logs/error.log @@ -0,0 +1,178 @@ +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 index e69de29..35171e2 100644 --- a/build_logs/linux_amd64.log +++ b/build_logs/linux_amd64.log @@ -0,0 +1,2 @@ +# oculus +./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/linux_amd64_static.log b/build_logs/linux_amd64_static.log index e69de29..35171e2 100644 --- a/build_logs/linux_amd64_static.log +++ b/build_logs/linux_amd64_static.log @@ -0,0 +1,2 @@ +# 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 index e69de29..35171e2 100644 --- a/build_logs/linux_arm64.log +++ b/build_logs/linux_arm64.log @@ -0,0 +1,2 @@ +# oculus +./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/build_logs/linux_arm64_static.log b/build_logs/linux_arm64_static.log index e69de29..35171e2 100644 --- a/build_logs/linux_arm64_static.log +++ b/build_logs/linux_arm64_static.log @@ -0,0 +1,2 @@ +# 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 index e69de29..35171e2 100644 --- a/build_logs/windows_amd64.log +++ b/build_logs/windows_amd64.log @@ -0,0 +1,2 @@ +# oculus +./main.go:274:21: syntax error: cannot use id, container := watchedContainers.containers as value diff --git a/dist/oculus_darwin_amd64 b/dist/oculus_darwin_amd64 index b4b4b6e..df452bf 100755 Binary files a/dist/oculus_darwin_amd64 and b/dist/oculus_darwin_amd64 differ diff --git a/dist/oculus_darwin_arm64 b/dist/oculus_darwin_arm64 index db7003c..5409eee 100755 Binary files a/dist/oculus_darwin_arm64 and b/dist/oculus_darwin_arm64 differ diff --git a/dist/oculus_linux_amd64 b/dist/oculus_linux_amd64 index 7cf5d0b..017f110 100755 Binary files a/dist/oculus_linux_amd64 and b/dist/oculus_linux_amd64 differ diff --git a/dist/oculus_linux_amd64_static b/dist/oculus_linux_amd64_static index 43b2519..7b5b61c 100755 Binary files a/dist/oculus_linux_amd64_static and b/dist/oculus_linux_amd64_static differ diff --git a/dist/oculus_linux_arm64 b/dist/oculus_linux_arm64 index 18cd7e8..0d25e59 100755 Binary files a/dist/oculus_linux_arm64 and b/dist/oculus_linux_arm64 differ diff --git a/dist/oculus_linux_arm64_static b/dist/oculus_linux_arm64_static index b9ea708..93c0528 100755 Binary files a/dist/oculus_linux_arm64_static and b/dist/oculus_linux_arm64_static differ diff --git a/dist/oculus_windows_amd64 b/dist/oculus_windows_amd64 index d6cf973..4b266a3 100755 Binary files a/dist/oculus_windows_amd64 and b/dist/oculus_windows_amd64 differ diff --git a/main.go b/main.go index b9ef6f9..f42d7b7 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "log" "os" "os/exec" + "path/filepath" "strings" "sync" "time" @@ -23,6 +24,11 @@ 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)} + func main() { // Check if GLITCHTIP_DSN environment variable is set glitchtipDSN := os.Getenv("GLITCHTIP_DSN") @@ -32,6 +38,8 @@ func main() { log.Println("Starting Oculus...") + go monitorContainerList() + for { containers, err := getContainers() if err != nil { @@ -41,8 +49,17 @@ func main() { } for _, container := range containers { - log.Printf("Monitoring container: %s (%s) every %s", container.Name, container.ID, container.Interval) - go monitorContainer(container) + 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) + } } // Sleep for 1 minute before checking for new containers again @@ -90,6 +107,8 @@ func getContainers() ([]ContainerInfo, error) { 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, @@ -102,56 +121,81 @@ func getContainers() ([]ContainerInfo, error) { return containers, nil } -func monitorContainer(container ContainerInfo) { +func monitorContainer(container ContainerInfo, initial bool) { + ticker := time.NewTicker(container.Interval) + defer ticker.Stop() + for { - log.Printf("Checking diffs for container: %s (%s)", container.Name, container.ID) - checkDiff(container) - time.Sleep(container.Interval) + 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 checkDiff(container ContainerInfo) { - cmd := exec.Command("docker", "diff", container.ID) +func getDiffOutput(containerID string) (string, error) { + cmd := exec.Command("docker", "diff", containerID) output, err := cmd.Output() if err != nil { - log.Printf("Error running docker diff for container %s: %v", container.ID, err) - return + return "", fmt.Errorf("docker diff failed: %w", err) } + return string(output), nil +} - diffOutput := string(output) - - for _, ignore := range container.Ignores { - diffOutput = removeIgnoredPaths(diffOutput, ignore) - } - - if diffOutput != "" { - diffHash := fmt.Sprintf("%x", md5.Sum([]byte(diffOutput))) +func handleDiffOutput(container ContainerInfo, filteredOutput string, initial bool) error { + 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) notifiedChanges.RLock() lastNotifiedHash, notified := notifiedChanges.changes[container.ID] notifiedChanges.RUnlock() - if !notified || lastNotifiedHash != diffHash { + 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) - err = writeToFile(filename, diffOutput) + err := writeToFile(filename, filteredOutput) if err != nil { - log.Printf("Error writing diff to file for container %s: %v", container.ID, err) + return fmt.Errorf("error writing diff to file: %w", err) } // Send notification using go-glitch log.Printf("Sending notification for container: %s (%s)", container.Name, container.ID) - cmd = exec.Command("go-glitch") - cmd.Stdin = strings.NewReader(diffOutput) - output, err = cmd.CombinedOutput() + err = sendNotification(filteredOutput) if err != nil { log.Printf("Error sending notification for container %s: %v", container.ID, err) - log.Printf("go-glitch output: %s", output) } else { notifiedChanges.Lock() notifiedChanges.changes[container.ID] = diffHash notifiedChanges.Unlock() + log.Printf("Notification sent and hash updated for container: %s (%s)", container.Name, container.ID) } } else { log.Printf("No new changes detected for container: %s (%s)", container.Name, container.ID) @@ -159,17 +203,47 @@ func checkDiff(container ContainerInfo) { } else { log.Printf("No significant changes detected for container: %s (%s)", container.Name, container.ID) } + + return nil } -func removeIgnoredPaths(diffOutput string, ignore string) string { - lines := strings.Split(diffOutput, "\n") - filteredLines := []string{} - for _, line := range lines { - if !strings.Contains(line, ignore) { - filteredLines = append(filteredLines, line) +func filterDiffOutput(diffOutput string, ignores []string) (string, error) { + tempFile, err := os.CreateTemp("", "diff_output") + if err != nil { + return "", err + } + defer os.Remove(tempFile.Name()) + + _, err = tempFile.WriteString(diffOutput) + if err != nil { + return "", 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) } } - return strings.Join(filteredLines, "\n") + + filteredOutput, err := os.ReadFile(tempFile.Name()) + if err != nil { + return "", err + } + + return string(filteredOutput), nil +} + +func matchPath(path, pattern string) bool { + match, err := filepath.Match(pattern, filepath.Base(path)) + if err != nil { + log.Printf("Error matching pattern: %s with path: %s, error: %v", pattern, path, err) + return false + } + log.Printf("Matching path %s with pattern %s: %v", path, pattern, match) + return match } func writeToFile(filename, content string) error { @@ -182,3 +256,38 @@ func writeToFile(filename, content string) error { _, err = file.WriteString(content) return err } + +func sendNotification(content string) error { + cmd := exec.Command("go-glitch") + cmd.Stdin = strings.NewReader(content) + output, err := cmd.CombinedOutput() + if err != nil { + log.Printf("go-glitch output: %s", output) + return err + } + 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 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 + } + return strings.TrimSpace(string(output)) == "true" +} diff --git a/my_friendly_container.diff b/my_friendly_container.diff deleted file mode 100644 index 138b9b3..0000000 --- a/my_friendly_container.diff +++ /dev/null @@ -1,3 +0,0 @@ -A /newfile2.txt -A /newfile3.txt -A /newfile.txt diff --git a/second_container.diff b/second_container.diff new file mode 100644 index 0000000..e34be8a --- /dev/null +++ b/second_container.diff @@ -0,0 +1,3 @@ +A /file1.txt +A /file2.txt +A /file3.txt