diff --git a/build.sh b/build.sh index d19659e..a693218 100755 --- a/build.sh +++ b/build.sh @@ -1,70 +1,43 @@ #!/bin/bash -# Default architecture -DEFAULT_ARCH="linux/amd64" +ARCHITECTURES=("linux/amd64" "linux/arm64" "darwin/amd64" "windows/amd64") +PROJECT_NAME=$(basename "$PWD") -# Supported architectures (adjust as needed) -ARCHITECTURES=("linux/amd64" "linux/arm64" "linux/arm/v7" "darwin/amd64" "darwin/arm64") - -# Ensure all necessary directories exist and go modules are ready prepare_build() { - # Create necessary directories if they don't exist - mkdir -p dist - mkdir -p build_logs - - # Initialize go modules if go.mod does not exist + mkdir -p dist build_logs if [ ! -f go.mod ]; then - echo "Initializing Go modules" - go mod init yourmodule # Replace 'yourmodule' with your actual module name or path + go mod init "$PROJECT_NAME" fi - - # Fetch and ensure all dependencies are up to date - echo "Checking dependencies..." go mod tidy } -# Build function build_binary() { - os=$1 - arch=$2 - output_name="oculus" - - if [[ "$os/$arch" != "$DEFAULT_ARCH" ]]; then - output_name="${output_name}_${os}_${arch}" - fi - - output_name="dist/${output_name}" - - # Dynamic Linking - echo "Building dynamically linked for ${os}/${arch} -> ${output_name}" - GOOS=${os} GOARCH=${arch} go build -o ${output_name} main.go 2>build_logs/${os}_${arch}_build.log - if [ $? -eq 0 ]; then - echo "Successfully built ${output_name}" + 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 + if [ $? -ne 0 ]; then + echo "Build failed for $os/$arch" >> build_logs/error.log else - echo "Failed to build ${output_name}. Check build_logs/${os}_${arch}_build.log for errors." + echo "Build succeeded for $os/$arch" fi - - # Static Linking - if [[ "$os" == "linux" ]]; then # Typically, static linking is most relevant for Linux environments - static_output_name="${output_name}_static" - echo "Building statically linked for ${os}/${arch} -> ${static_output_name}" - CGO_ENABLED=0 GOOS=${os} GOARCH=${arch} go build -a -ldflags '-extldflags "-static"' -o ${static_output_name} main.go 2>build_logs/${os}_${arch}_static_build.log - if [ $? -eq 0 ]; then - echo "Successfully built ${static_output_name}" + 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 + if [ $? -ne 0 ]; then + echo "Static build failed for $os/$arch" >> build_logs/error.log else - echo "Failed to build ${static_output_name}. Check build_logs/${os}_${arch}_static_build.log for errors." + echo "Static build succeeded for $os/$arch" fi fi } -# Main Build Process -prepare_build -for arch in "${ARCHITECTURES[@]}"; do - IFS='/' read -r -a parts <<< "$arch" # Split architecture string - os=${parts[0]} - arch=${parts[1]} - build_binary $os $arch -done - -echo "Build process completed." +main() { + prepare_build + for arch in "${ARCHITECTURES[@]}"; do + IFS="/" read -r os arch <<< "$arch" + build_binary $os $arch + done + echo "Build process completed." +} +main diff --git a/build_logs/darwin_amd64.log b/build_logs/darwin_amd64.log new file mode 100644 index 0000000..bc13b0d --- /dev/null +++ b/build_logs/darwin_amd64.log @@ -0,0 +1,3 @@ +# oculus +./main.go:38:67: undefined: types.ContainerListOptions +./main.go:119:4: savedDiffs declared and not used diff --git a/build_logs/darwin_amd64_build.log b/build_logs/darwin_amd64_build.log deleted file mode 100644 index f925358..0000000 --- a/build_logs/darwin_amd64_build.log +++ /dev/null @@ -1,2 +0,0 @@ -# command-line-arguments -./main.go:34:67: undefined: types.ContainerListOptions diff --git a/build_logs/darwin_arm64_build.log b/build_logs/darwin_arm64_build.log deleted file mode 100644 index f925358..0000000 --- a/build_logs/darwin_arm64_build.log +++ /dev/null @@ -1,2 +0,0 @@ -# command-line-arguments -./main.go:34:67: undefined: types.ContainerListOptions diff --git a/build_logs/error.log b/build_logs/error.log new file mode 100644 index 0000000..00c90ae --- /dev/null +++ b/build_logs/error.log @@ -0,0 +1,6 @@ +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 windows/amd64 diff --git a/build_logs/linux_amd64.log b/build_logs/linux_amd64.log new file mode 100644 index 0000000..bc13b0d --- /dev/null +++ b/build_logs/linux_amd64.log @@ -0,0 +1,3 @@ +# oculus +./main.go:38:67: undefined: types.ContainerListOptions +./main.go:119:4: savedDiffs declared and not used diff --git a/build_logs/linux_amd64_build.log b/build_logs/linux_amd64_build.log deleted file mode 100644 index f925358..0000000 --- a/build_logs/linux_amd64_build.log +++ /dev/null @@ -1,2 +0,0 @@ -# command-line-arguments -./main.go:34:67: undefined: types.ContainerListOptions diff --git a/build_logs/linux_amd64_static.log b/build_logs/linux_amd64_static.log new file mode 100644 index 0000000..bc13b0d --- /dev/null +++ b/build_logs/linux_amd64_static.log @@ -0,0 +1,3 @@ +# oculus +./main.go:38:67: undefined: types.ContainerListOptions +./main.go:119:4: savedDiffs declared and not used diff --git a/build_logs/linux_amd64_static_build.log b/build_logs/linux_amd64_static_build.log deleted file mode 100644 index f925358..0000000 --- a/build_logs/linux_amd64_static_build.log +++ /dev/null @@ -1,2 +0,0 @@ -# command-line-arguments -./main.go:34:67: undefined: types.ContainerListOptions diff --git a/build_logs/linux_arm64.log b/build_logs/linux_arm64.log new file mode 100644 index 0000000..bc13b0d --- /dev/null +++ b/build_logs/linux_arm64.log @@ -0,0 +1,3 @@ +# oculus +./main.go:38:67: undefined: types.ContainerListOptions +./main.go:119:4: savedDiffs declared and not used diff --git a/build_logs/linux_arm64_build.log b/build_logs/linux_arm64_build.log deleted file mode 100644 index f925358..0000000 --- a/build_logs/linux_arm64_build.log +++ /dev/null @@ -1,2 +0,0 @@ -# command-line-arguments -./main.go:34:67: undefined: types.ContainerListOptions diff --git a/build_logs/linux_arm64_static.log b/build_logs/linux_arm64_static.log new file mode 100644 index 0000000..bc13b0d --- /dev/null +++ b/build_logs/linux_arm64_static.log @@ -0,0 +1,3 @@ +# oculus +./main.go:38:67: undefined: types.ContainerListOptions +./main.go:119:4: savedDiffs declared and not used diff --git a/build_logs/linux_arm64_static_build.log b/build_logs/linux_arm64_static_build.log deleted file mode 100644 index f925358..0000000 --- a/build_logs/linux_arm64_static_build.log +++ /dev/null @@ -1,2 +0,0 @@ -# command-line-arguments -./main.go:34:67: undefined: types.ContainerListOptions diff --git a/build_logs/linux_arm_build.log b/build_logs/linux_arm_build.log deleted file mode 100644 index f925358..0000000 --- a/build_logs/linux_arm_build.log +++ /dev/null @@ -1,2 +0,0 @@ -# command-line-arguments -./main.go:34:67: undefined: types.ContainerListOptions diff --git a/build_logs/linux_arm_static_build.log b/build_logs/linux_arm_static_build.log deleted file mode 100644 index f925358..0000000 --- a/build_logs/linux_arm_static_build.log +++ /dev/null @@ -1,2 +0,0 @@ -# command-line-arguments -./main.go:34:67: undefined: types.ContainerListOptions diff --git a/build_logs/windows_amd64.log b/build_logs/windows_amd64.log new file mode 100644 index 0000000..bc13b0d --- /dev/null +++ b/build_logs/windows_amd64.log @@ -0,0 +1,3 @@ +# oculus +./main.go:38:67: undefined: types.ContainerListOptions +./main.go:119:4: savedDiffs declared and not used diff --git a/go.mod b/go.mod index 459fb60..e4a297f 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module yourmodule +module oculus go 1.21.1 diff --git a/install.sh b/install.sh index 55c2b41..84006bc 100644 --- a/install.sh +++ b/install.sh @@ -4,12 +4,8 @@ INSTALL_DIR="/usr/local/bin" BINARY_NAME="oculus" BASE_URL="https://git.nixc.us/colin/Oculus/raw/branch/main/dist" -declare -A binaries -binaries["linux/amd64"]="oculus_linux_amd64" -binaries["linux/arm64"]="oculus_linux_arm64" -binaries["linux/arm/v7"]="oculus_linux_arm" -binaries["darwin/amd64"]="oculus_darwin_amd64" -binaries["darwin/arm64"]="oculus_darwin_arm64" +# Supported architectures +ARCHITECTURES=("linux/amd64" "linux/arm64" "linux/arm/v7" "darwin/amd64" "darwin/arm64") OS="$(uname -s | tr '[:upper:]' '[:lower:]')" ARCH="$(uname -m)" @@ -21,14 +17,7 @@ case $ARCH in *) echo "Unsupported architecture: $ARCH"; exit 1 ;; esac -KEY="${OS}/${ARCH}" - -if [[ -z "${binaries[$KEY]}" ]]; then - echo "No pre-built binary for your system architecture ($KEY)." - exit 1 -fi - -BINARY_URL="${BASE_URL}/${binaries[$KEY]}" +BINARY_URL="${BASE_URL}/oculus_${OS}_${ARCH}" echo "Downloading and installing $BINARY_NAME from $BINARY_URL..." diff --git a/main.go b/main.go index c09450b..b64155f 100644 --- a/main.go +++ b/main.go @@ -4,12 +4,13 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "log" "os" "os/exec" + "os/signal" "path/filepath" "strings" + "syscall" "time" "github.com/docker/docker/api/types" @@ -22,14 +23,17 @@ const ( logDir = "/log" ) -// ContainerDiff represents the state of a container type ContainerDiff struct { ID string Image string Labels map[string]string } -// getRunningContainers fetches the list of running containers with relevant labels +type FileChange struct { + Path string + Kind string +} + func getRunningContainers(cli *client.Client) ([]ContainerDiff, error) { containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{}) if err != nil { @@ -38,7 +42,7 @@ func getRunningContainers(cli *client.Client) ([]ContainerDiff, error) { var diffs []ContainerDiff for _, container := range containers { - if _, ok := container.Labels["oculus.containerid"]; ok { + if _, ok := container.Labels["oculus.cname"]; ok { diffs = append(diffs, ContainerDiff{ ID: container.ID, Image: container.Image, @@ -50,29 +54,26 @@ func getRunningContainers(cli *client.Client) ([]ContainerDiff, error) { return diffs, nil } -// saveDiffs writes the container diffs to a file -func saveDiffs(diffs []ContainerDiff, filePath string) error { +func saveDiffs(diffs []FileChange, filePath string) error { data, err := json.Marshal(diffs) if err != nil { return err } - return ioutil.WriteFile(filePath, data, 0644) + return os.WriteFile(filePath, data, 0644) } -// loadDiffs reads the container diffs from a file -func loadDiffs(filePath string) ([]ContainerDiff, error) { - data, err := ioutil.ReadFile(filePath) +func loadDiffs(filePath string) ([]FileChange, error) { + data, err := os.ReadFile(filePath) if err != nil { return nil, err } - var diffs []ContainerDiff + var diffs []FileChange err = json.Unmarshal(data, &diffs) return diffs, err } -// shouldIgnore checks if the change should be ignored based on the ignore list func shouldIgnore(change string, ignoreList []string) bool { for _, ignore := range ignoreList { if strings.Contains(change, ignore) { @@ -82,7 +83,6 @@ func shouldIgnore(change string, ignoreList []string) bool { return false } -// logDetection logs detection events to a file and sends to Go Glitch func logDetection(containerID, message string) { logFilePath := filepath.Join(logDir, fmt.Sprintf("%s.log", containerID)) f, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) @@ -97,7 +97,6 @@ func logDetection(containerID, message string) { sendToGlitchtip(logFilePath) } -// sendToGlitchtip sends the log file content to Go Glitch func sendToGlitchtip(logFilePath string) { if dsn := os.Getenv("GLITCHTIP_DSN"); dsn != "" { cmd := exec.Command("go-glitch", logFilePath) @@ -108,70 +107,66 @@ func sendToGlitchtip(logFilePath string) { } } -// profileContainers logs the current state of containers to a file -func profileContainers(cli *client.Client) { - diffs, err := getRunningContainers(cli) - if err != nil { - log.Fatalf("Error profiling containers: %v", err) - } - - for _, diff := range diffs { - if diff.Labels["oculus.mode"] == "profile" { - filePath := fmt.Sprintf("%s.json", diff.Labels["oculus.containerid"]) - err := saveDiffs([]ContainerDiff{diff}, filePath) - if err != nil { - log.Fatalf("Error saving diffs: %v", err) - } - } - } - - log.Println("Profiled current containers and saved to file") -} - -// compareContainers compares the current state of containers with the saved state -func compareContainers(cli *client.Client) { +func compareContainers(cli *client.Client, mode string) { currentDiffs, err := getRunningContainers(cli) if err != nil { log.Fatalf("Error getting current containers: %v", err) } for _, current := range currentDiffs { - if current.Labels["oculus.mode"] == "monitor" { - filePath := fmt.Sprintf("%s.json", current.Labels["oculus.containerid"]) + if current.Labels["oculus.mode"] == mode { + filePath := filepath.Join(logDir, fmt.Sprintf("%s.json", current.Labels["oculus.cname"])) savedDiffs, err := loadDiffs(filePath) if err != nil { log.Printf("Error loading saved diffs for %s: %v", current.ID, err) - continue + savedDiffs = []FileChange{} // Initialize to an empty slice } ignoreList := strings.Split(current.Labels["oculus.ignorelist"], ",") - if len(savedDiffs) > 0 { - saved := savedDiffs[0] - if current.Image != saved.Image && !shouldIgnore(current.Image, ignoreList) { - message := fmt.Sprintf("Container %s changed: Image %s -> %s", current.ID, saved.Image, current.Image) - logDetection(current.Labels["oculus.containerid"], message) - runNotifyScript(filepath.Join(logDir, fmt.Sprintf("%s.log", current.Labels["oculus.containerid"]))) + currentChanges, err := cli.ContainerDiff(context.Background(), current.ID) + if err != nil { + log.Printf("Error getting container diff for %s: %v", current.ID, err) + continue + } + + var changes []FileChange + for _, change := range currentChanges { + path := change.Path + kind := "" + switch change.Kind { + case 0: + kind = "Modified" + case 1: + kind = "Added" + case 2: + kind = "Deleted" } - if current.Labels["oculus.ignorelist"] != saved.Labels["oculus.ignorelist"] { - message := fmt.Sprintf("Container %s ignore list changed: %s -> %s", current.ID, saved.Labels["oculus.ignorelist"], current.Labels["oculus.ignorelist"]) - if !shouldIgnore(message, ignoreList) { - logDetection(current.Labels["oculus.containerid"], message) - runNotifyScript(filepath.Join(logDir, fmt.Sprintf("%s.log", current.Labels["oculus.containerid"]))) - } + + if !shouldIgnore(path, ignoreList) { + changes = append(changes, FileChange{ + Path: path, + Kind: kind, + }) } - } else { - message := fmt.Sprintf("New container detected: ID %s, Image %s", current.ID, current.Image) - if !shouldIgnore(current.Image, ignoreList) { - logDetection(current.Labels["oculus.containerid"], message) - runNotifyScript(filepath.Join(logDir, fmt.Sprintf("%s.log", current.Labels["oculus.containerid"]))) + } + + if mode == "monitor" { + err = saveDiffs(changes, filePath) + if err != nil { + log.Fatalf("Error saving diffs: %v", err) + } + } else if mode == "report" { + for _, change := range changes { + message := fmt.Sprintf("Container %s changed: %s %s", current.ID, change.Kind, change.Path) + logDetection(current.Labels["oculus.cname"], message) + runNotifyScript(filepath.Join(logDir, fmt.Sprintf("%s.log", current.Labels["oculus.cname"]))) } } } } } -// runNotifyScript executes the notification script with the container details func runNotifyScript(logFilePath string) { cmd := exec.Command(notifyScript, logFilePath) err := cmd.Run() @@ -180,7 +175,6 @@ func runNotifyScript(logFilePath string) { } } -// watchDockerEvents listens for Docker events and triggers comparisons func watchDockerEvents(cli *client.Client) { eventFilter := filters.NewArgs() eventFilter.Add("type", "container") @@ -196,7 +190,7 @@ func watchDockerEvents(cli *client.Client) { select { case event := <-messages: log.Printf("Docker event received: %s for container %s", event.Action, event.ID) - compareContainers(cli) + compareContainers(cli, "monitor") case err := <-errs: log.Printf("Error watching Docker events: %v", err) return @@ -204,7 +198,6 @@ func watchDockerEvents(cli *client.Client) { } } -// monitorContainers sets up monitoring for containers based on the specified interval func monitorContainers(cli *client.Client, interval time.Duration) { ticker := time.NewTicker(interval) defer ticker.Stop() @@ -212,7 +205,19 @@ func monitorContainers(cli *client.Client, interval time.Duration) { for { select { case <-ticker.C: - compareContainers(cli) + compareContainers(cli, "monitor") + } + } +} + +func reportContainers(cli *client.Client, interval time.Duration) { + ticker := time.NewTicker(interval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + compareContainers(cli, "report") } } } @@ -232,16 +237,24 @@ func main() { } for _, container := range containers { - if container.Labels["oculus.mode"] == "monitor" { + if mode, ok := container.Labels["oculus.mode"]; ok { intervalStr := container.Labels["oculus.interval"] interval, err := time.ParseDuration(intervalStr) if err != nil { log.Fatalf("Invalid interval format for container %s: %v", container.ID, err) } - go monitorContainers(cli, interval) + + if mode == "monitor" { + go monitorContainers(cli, interval) + } else if mode == "report" { + go reportContainers(cli, interval) + } } } - // Keep the program running - select {} + // Graceful shutdown handling + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + <-sigs + log.Println("Shutting down...") }