From 6ac5e3ebfe3be24731e41113ae00741e814dd1e9 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Mon, 29 Dec 2025 23:37:16 +0000 Subject: [PATCH] chore: Clean up build scripts and remove unused Docker agent entry point --- Dockerfile | 65 ++--- Makefile | 5 +- cmd/pulse-docker-agent/main.go | 365 -------------------------- cmd/pulse-docker-agent/main_test.go | 394 ---------------------------- scripts/build-release.sh | 33 +-- 5 files changed, 19 insertions(+), 843 deletions(-) delete mode 100644 cmd/pulse-docker-agent/main.go delete mode 100644 cmd/pulse-docker-agent/main_test.go diff --git a/Dockerfile b/Dockerfile index d9a65db11..abec9e0a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -67,42 +67,7 @@ RUN --mount=type=cache,id=pulse-go-mod,target=/go/pkg/mod \ -trimpath \ -o pulse-linux-arm64 ./cmd/pulse -# Build docker-agent binaries (optional cross-arch builds controlled by BUILD_AGENT) -RUN --mount=type=cache,id=pulse-go-mod,target=/go/pkg/mod \ - --mount=type=cache,id=pulse-go-build,target=/root/.cache/go-build \ - VERSION="v$(cat VERSION | tr -d '\n')" && \ - if [ "${BUILD_AGENT:-1}" = "1" ]; then \ - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ - -ldflags="-s -w -X github.com/rcourtman/pulse-go-rewrite/internal/dockeragent.Version=${VERSION}" \ - -trimpath \ - -o pulse-docker-agent-linux-amd64 ./cmd/pulse-docker-agent && \ - CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build \ - -ldflags="-s -w -X github.com/rcourtman/pulse-go-rewrite/internal/dockeragent.Version=${VERSION}" \ - -trimpath \ - -o pulse-docker-agent-linux-arm64 ./cmd/pulse-docker-agent && \ - CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build \ - -ldflags="-s -w -X github.com/rcourtman/pulse-go-rewrite/internal/dockeragent.Version=${VERSION}" \ - -trimpath \ - -o pulse-docker-agent-linux-armv7 ./cmd/pulse-docker-agent && \ - CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build \ - -ldflags="-s -w -X github.com/rcourtman/pulse-go-rewrite/internal/dockeragent.Version=${VERSION}" \ - -trimpath \ - -o pulse-docker-agent-linux-armv6 ./cmd/pulse-docker-agent && \ - CGO_ENABLED=0 GOOS=linux GOARCH=386 go build \ - -ldflags="-s -w -X github.com/rcourtman/pulse-go-rewrite/internal/dockeragent.Version=${VERSION}" \ - -trimpath \ - -o pulse-docker-agent-linux-386 ./cmd/pulse-docker-agent; \ - else \ - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ - -ldflags="-s -w -X github.com/rcourtman/pulse-go-rewrite/internal/dockeragent.Version=${VERSION}" \ - -trimpath \ - -o pulse-docker-agent-linux-amd64 ./cmd/pulse-docker-agent && \ - cp pulse-docker-agent-linux-amd64 pulse-docker-agent-linux-arm64 && \ - cp pulse-docker-agent-linux-amd64 pulse-docker-agent-linux-armv7 && \ - cp pulse-docker-agent-linux-amd64 pulse-docker-agent-linux-armv6 && \ - cp pulse-docker-agent-linux-amd64 pulse-docker-agent-linux-386; \ - fi && \ - cp pulse-docker-agent-linux-amd64 pulse-docker-agent + # Build host-agent binaries for all platforms (for download endpoint) RUN --mount=type=cache,id=pulse-go-mod,target=/go/pkg/mod \ @@ -233,20 +198,25 @@ RUN apk --no-cache add ca-certificates tzdata WORKDIR /app -# Copy all agent binaries first -COPY --from=backend-builder /app/pulse-docker-agent-linux-* /tmp/ +# Copy all unified agent binaries first +COPY --from=backend-builder /app/pulse-agent-linux-* /tmp/ # Select the appropriate architecture binary # Docker buildx automatically sets TARGETARCH (amd64, arm64, arm) and TARGETVARIANT (v7) RUN if [ "$TARGETARCH" = "arm64" ]; then \ - cp /tmp/pulse-docker-agent-linux-arm64 /usr/local/bin/pulse-docker-agent; \ + cp /tmp/pulse-agent-linux-arm64 /usr/local/bin/pulse-agent; \ elif [ "$TARGETARCH" = "arm" ]; then \ - cp /tmp/pulse-docker-agent-linux-armv7 /usr/local/bin/pulse-docker-agent; \ + cp /tmp/pulse-agent-linux-armv7 /usr/local/bin/pulse-agent; \ else \ - cp /tmp/pulse-docker-agent-linux-amd64 /usr/local/bin/pulse-docker-agent; \ + cp /tmp/pulse-agent-linux-amd64 /usr/local/bin/pulse-agent; \ fi && \ - chmod +x /usr/local/bin/pulse-docker-agent && \ - rm -rf /tmp/pulse-docker-agent-* + chmod +x /usr/local/bin/pulse-agent && \ + rm -rf /tmp/pulse-agent-* + +# Create shim for pulse-docker-agent to maintain backward compatibility +RUN echo '#!/bin/sh' > /usr/local/bin/pulse-docker-agent && \ + echo 'exec /usr/local/bin/pulse-agent --enable-docker "$@"' >> /usr/local/bin/pulse-docker-agent && \ + chmod +x /usr/local/bin/pulse-docker-agent COPY --from=backend-builder /app/VERSION /VERSION @@ -274,7 +244,7 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \ chmod +x ./pulse && \ rm -rf /tmp/pulse-linux-* -COPY --from=backend-builder /app/pulse-docker-agent . + # Copy VERSION file COPY --from=backend-builder /app/VERSION . @@ -309,12 +279,7 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \ fi # Docker agent binaries (all architectures) -COPY --from=backend-builder /app/pulse-docker-agent-linux-amd64 /opt/pulse/bin/ -COPY --from=backend-builder /app/pulse-docker-agent-linux-arm64 /opt/pulse/bin/ -COPY --from=backend-builder /app/pulse-docker-agent-linux-armv7 /opt/pulse/bin/ -COPY --from=backend-builder /app/pulse-docker-agent-linux-armv6 /opt/pulse/bin/ -COPY --from=backend-builder /app/pulse-docker-agent-linux-386 /opt/pulse/bin/ -COPY --from=backend-builder /app/pulse-docker-agent /opt/pulse/bin/pulse-docker-agent + # Host agent binaries (all platforms and architectures) COPY --from=backend-builder /app/pulse-host-agent-linux-amd64 /opt/pulse/bin/ diff --git a/Makefile b/Makefile index 4197d6c71..e2a92f7c2 100644 --- a/Makefile +++ b/Makefile @@ -87,10 +87,7 @@ build-agents: @echo "Building agent binaries for all platforms..." @mkdir -p bin @VERSION=$$(cat VERSION | tr -d '\n') && \ - echo "Building docker agent binaries (version: v$$VERSION)..." && \ - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X github.com/rcourtman/pulse-go-rewrite/internal/dockeragent.Version=v$$VERSION" -trimpath -o bin/pulse-docker-agent-linux-amd64 ./cmd/pulse-docker-agent && \ - CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w -X github.com/rcourtman/pulse-go-rewrite/internal/dockeragent.Version=v$$VERSION" -trimpath -o bin/pulse-docker-agent-linux-arm64 ./cmd/pulse-docker-agent && \ - CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-s -w -X github.com/rcourtman/pulse-go-rewrite/internal/dockeragent.Version=v$$VERSION" -trimpath -o bin/pulse-docker-agent-linux-armv7 ./cmd/pulse-docker-agent && \ + echo "Building host agent binaries..." && \ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -trimpath -o bin/pulse-host-agent-linux-amd64 ./cmd/pulse-host-agent && \ CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -trimpath -o bin/pulse-host-agent-linux-arm64 ./cmd/pulse-host-agent && \ diff --git a/cmd/pulse-docker-agent/main.go b/cmd/pulse-docker-agent/main.go deleted file mode 100644 index 60b9f99c4..000000000 --- a/cmd/pulse-docker-agent/main.go +++ /dev/null @@ -1,365 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "os" - "os/signal" - "strings" - "syscall" - "time" - - "github.com/rcourtman/pulse-go-rewrite/internal/dockeragent" - "github.com/rcourtman/pulse-go-rewrite/internal/utils" - "github.com/rs/zerolog" -) - -type stringFlagList []string - -func (l *stringFlagList) String() string { - return strings.Join(*l, ",") -} - -func (l *stringFlagList) Set(value string) error { - *l = append(*l, value) - return nil -} - -func (l stringFlagList) Values() []string { - if len(l) == 0 { - return nil - } - return append([]string(nil), l...) -} - -func main() { - // Handle --version flag early before other config parsing - versionFlag := false - for _, arg := range os.Args[1:] { - if arg == "--version" || arg == "-version" || arg == "version" { - versionFlag = true - break - } - } - - if versionFlag { - fmt.Printf("pulse-docker-agent version %s\n", dockeragent.Version) - os.Exit(0) - } - - cfg := loadConfig() - - zerolog.SetGlobalLevel(cfg.LogLevel) - - logger := zerolog.New(os.Stdout).Level(cfg.LogLevel).With().Timestamp().Logger() - cfg.Logger = &logger - - // Deprecation warning - logger.Warn().Msg("pulse-docker-agent is DEPRECATED and will be removed in a future release") - logger.Warn().Msg("Please migrate to the unified 'pulse-agent' with --enable-docker flag") - logger.Warn().Msg("Example: pulse-agent --url --token --enable-docker") - logger.Warn().Msg("") - - agent, err := dockeragent.New(cfg) - if err != nil { - logger.Fatal().Err(err).Msg("Failed to create docker agent") - } - defer agent.Close() - - ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) - defer cancel() - - logger.Info().Str("pulse_url", cfg.PulseURL).Dur("interval", cfg.Interval).Msg("Starting Pulse Docker agent") - - if err := agent.Run(ctx); err != nil && err != context.Canceled { - logger.Fatal().Err(err).Msg("Agent terminated with error") - } - - logger.Info().Msg("Agent stopped") -} - -func loadConfig() dockeragent.Config { - envURL := utils.GetenvTrim("PULSE_URL") - envToken := utils.GetenvTrim("PULSE_TOKEN") - envInterval := utils.GetenvTrim("PULSE_INTERVAL") - envHostname := utils.GetenvTrim("PULSE_HOSTNAME") - envAgentID := utils.GetenvTrim("PULSE_AGENT_ID") - envInsecure := utils.GetenvTrim("PULSE_INSECURE_SKIP_VERIFY") - envNoAutoUpdate := utils.GetenvTrim("PULSE_NO_AUTO_UPDATE") - envTargets := utils.GetenvTrim("PULSE_TARGETS") - envRuntime := utils.GetenvTrim("PULSE_RUNTIME") - envContainerStates := utils.GetenvTrim("PULSE_CONTAINER_STATES") - envSwarmScope := utils.GetenvTrim("PULSE_SWARM_SCOPE") - envSwarmServices := utils.GetenvTrim("PULSE_SWARM_SERVICES") - envSwarmTasks := utils.GetenvTrim("PULSE_SWARM_TASKS") - envIncludeContainers := utils.GetenvTrim("PULSE_INCLUDE_CONTAINERS") - envCollectDisk := utils.GetenvTrim("PULSE_COLLECT_DISK") - envLogLevel := utils.GetenvTrim("LOG_LEVEL") - - defaultInterval := 30 * time.Second - if envInterval != "" { - if parsed, err := time.ParseDuration(envInterval); err == nil { - defaultInterval = parsed - } - } - - swarmScopeDefault := envSwarmScope - if swarmScopeDefault == "" { - swarmScopeDefault = "node" - } - - includeServicesDefault := true - if envSwarmServices != "" { - includeServicesDefault = utils.ParseBool(envSwarmServices) - } - - includeTasksDefault := true - if envSwarmTasks != "" { - includeTasksDefault = utils.ParseBool(envSwarmTasks) - } - - includeContainersDefault := true - if envIncludeContainers != "" { - includeContainersDefault = utils.ParseBool(envIncludeContainers) - } - - collectDiskDefault := true - if envCollectDisk != "" { - collectDiskDefault = utils.ParseBool(envCollectDisk) - } - - logLevelDefault := "info" - if envLogLevel != "" { - logLevelDefault = envLogLevel - } - - urlFlag := flag.String("url", envURL, "Pulse server URL (e.g. http://pulse:7655)") - tokenFlag := flag.String("token", envToken, "Pulse API token (required)") - intervalFlag := flag.Duration("interval", defaultInterval, "Reporting interval (e.g. 30s)") - hostnameFlag := flag.String("hostname", envHostname, "Override hostname reported to Pulse") - agentIDFlag := flag.String("agent-id", envAgentID, "Override agent identifier") - insecureFlag := flag.Bool("insecure", utils.ParseBool(envInsecure), "Skip TLS certificate verification") - noAutoUpdateFlag := flag.Bool("no-auto-update", utils.ParseBool(envNoAutoUpdate), "Disable automatic agent updates") - runtimeFlag := flag.String("runtime", envRuntime, "Container runtime to expect (auto, docker, podman)") - logLevelFlag := flag.String("log-level", logLevelDefault, "Log level: debug, info, warn, error") - var targetFlags stringFlagList - flag.Var(&targetFlags, "target", "Pulse target in url|token[|insecure] format. Repeat to send to multiple Pulse instances") - var containerStateFlags stringFlagList - flag.Var(&containerStateFlags, "container-state", "Only include containers whose status matches this value (repeat to allow multiple). Allowed values: created,running,restarting,removing,paused,exited,dead.") - swarmScopeFlag := flag.String("swarm-scope", strings.ToLower(strings.TrimSpace(swarmScopeDefault)), "Swarm data scope to collect: node, cluster, or auto") - includeServicesFlag := flag.Bool("swarm-services", includeServicesDefault, "Include Swarm service summaries in reports") - includeTasksFlag := flag.Bool("swarm-tasks", includeTasksDefault, "Include Swarm tasks in reports") - includeContainersFlag := flag.Bool("include-containers", includeContainersDefault, "Include per-container metrics in reports") - collectDiskFlag := flag.Bool("collect-disk", collectDiskDefault, "Collect per-container disk usage, block IO, and mount details in reports") - - flag.Parse() - - // Check for common mistakes with unparsed arguments - unparsedArgs := flag.Args() - if len(unparsedArgs) > 0 { - // Check if any look like flags with single dash - for _, arg := range unparsedArgs { - if strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") { - fmt.Fprintf(os.Stderr, "error: unrecognized argument %q\n", arg) - fmt.Fprintln(os.Stderr, "note: flags must use double dashes (e.g., --token, not -token)") - fmt.Fprintln(os.Stderr, "\nUsage:") - flag.Usage() - os.Exit(1) - } - } - fmt.Fprintf(os.Stderr, "error: unexpected arguments: %v\n", unparsedArgs) - flag.Usage() - os.Exit(1) - } - - pulseURL := *urlFlag - urlFromEnvOrFlag := envURL != "" || *urlFlag != envURL - if pulseURL == "" { - pulseURL = "http://localhost:7655" - } - - targets := make([]dockeragent.TargetConfig, 0) - - if len(targetFlags) > 0 { - parsedTargets, err := parseTargetSpecs(targetFlags.Values()) - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - targets = append(targets, parsedTargets...) - } - - if envTargets != "" { - envTargetSpecs := splitTargetSpecs(envTargets) - if len(envTargetSpecs) > 0 { - parsedTargets, err := parseTargetSpecs(envTargetSpecs) - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - targets = append(targets, parsedTargets...) - } - } - - token := strings.TrimSpace(*tokenFlag) - if token == "" && len(targets) == 0 { - fmt.Fprintln(os.Stderr, "error: PULSE_TOKEN, --token, or at least one --target/PULSE_TARGETS entry must be provided") - fmt.Fprintln(os.Stderr, "\nExample usage:") - fmt.Fprintln(os.Stderr, " pulse-docker-agent --url http://pulse.example.com:7655 --token ") - fmt.Fprintln(os.Stderr, "\nOr set environment variables:") - fmt.Fprintln(os.Stderr, " export PULSE_URL=http://pulse.example.com:7655") - fmt.Fprintln(os.Stderr, " export PULSE_TOKEN=") - fmt.Fprintln(os.Stderr, " pulse-docker-agent") - os.Exit(1) - } - - // Warn if using default localhost URL without explicit configuration - if !urlFromEnvOrFlag && len(targets) == 0 && token != "" { - fmt.Fprintln(os.Stderr, "warning: no --url or PULSE_URL provided, defaulting to http://localhost:7655") - fmt.Fprintln(os.Stderr, "note: if your Pulse server is not on localhost, specify --url http://your-pulse-server:7655") - fmt.Fprintln(os.Stderr, "") - } - - interval := *intervalFlag - if interval <= 0 { - interval = 30 * time.Second - } - - logLevel, err := parseLogLevel(*logLevelFlag) - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - - containerStates := make([]string, 0) - if len(containerStateFlags) > 0 { - containerStates = append(containerStates, containerStateFlags.Values()...) - } - if envContainerStates != "" { - containerStates = append(containerStates, splitStringList(envContainerStates)...) - } - - return dockeragent.Config{ - PulseURL: pulseURL, - APIToken: token, - Interval: interval, - HostnameOverride: strings.TrimSpace(*hostnameFlag), - AgentID: strings.TrimSpace(*agentIDFlag), - InsecureSkipVerify: *insecureFlag, - DisableAutoUpdate: *noAutoUpdateFlag, - Targets: targets, - ContainerStates: containerStates, - SwarmScope: strings.ToLower(strings.TrimSpace(*swarmScopeFlag)), - Runtime: strings.ToLower(strings.TrimSpace(*runtimeFlag)), - IncludeServices: *includeServicesFlag, - IncludeTasks: *includeTasksFlag, - IncludeContainers: *includeContainersFlag, - CollectDiskMetrics: *collectDiskFlag, - LogLevel: logLevel, - } -} - -func parseLogLevel(value string) (zerolog.Level, error) { - normalized := strings.ToLower(strings.TrimSpace(value)) - if normalized == "" { - return zerolog.InfoLevel, nil - } - - level, err := zerolog.ParseLevel(normalized) - if err != nil { - return zerolog.InfoLevel, fmt.Errorf("invalid log level %q: must be debug, info, warn, or error", value) - } - - return level, nil -} - -func parseTargetSpecs(specs []string) ([]dockeragent.TargetConfig, error) { - targets := make([]dockeragent.TargetConfig, 0, len(specs)) - for _, spec := range specs { - spec = strings.TrimSpace(spec) - if spec == "" { - continue - } - target, err := parseTargetSpec(spec) - if err != nil { - return nil, err - } - targets = append(targets, target) - } - return targets, nil -} - -func parseTargetSpec(spec string) (dockeragent.TargetConfig, error) { - parts := strings.Split(spec, "|") - if len(parts) < 2 { - return dockeragent.TargetConfig{}, fmt.Errorf("invalid target %q: expected format url|token[|insecure]", spec) - } - - url := strings.TrimSpace(parts[0]) - token := strings.TrimSpace(parts[1]) - if url == "" { - return dockeragent.TargetConfig{}, fmt.Errorf("invalid target %q: URL is required", spec) - } - if token == "" { - return dockeragent.TargetConfig{}, fmt.Errorf("invalid target %q: token is required", spec) - } - - insecure := false - if len(parts) >= 3 { - switch strings.ToLower(strings.TrimSpace(parts[2])) { - case "1", "true", "yes", "y", "on": - insecure = true - case "", "0", "false", "no", "n", "off": - insecure = false - default: - return dockeragent.TargetConfig{}, fmt.Errorf("invalid target %q: insecure flag must be true/false", spec) - } - } - - return dockeragent.TargetConfig{ - URL: url, - Token: token, - InsecureSkipVerify: insecure, - }, nil -} - -func splitTargetSpecs(value string) []string { - if value == "" { - return nil - } - - normalized := strings.ReplaceAll(value, "\n", ";") - raw := strings.Split(normalized, ";") - result := make([]string, 0, len(raw)) - for _, item := range raw { - if trimmed := strings.TrimSpace(item); trimmed != "" { - result = append(result, trimmed) - } - } - return result -} - -func splitStringList(value string) []string { - if value == "" { - return nil - } - - items := strings.FieldsFunc(value, func(r rune) bool { - switch r { - case ',', ';', '\n', '\r': - return true - default: - return false - } - }) - - result := make([]string, 0, len(items)) - for _, item := range items { - if trimmed := strings.TrimSpace(item); trimmed != "" { - result = append(result, trimmed) - } - } - - return result -} diff --git a/cmd/pulse-docker-agent/main_test.go b/cmd/pulse-docker-agent/main_test.go deleted file mode 100644 index fd64d4e41..000000000 --- a/cmd/pulse-docker-agent/main_test.go +++ /dev/null @@ -1,394 +0,0 @@ -package main - -import ( - "reflect" - "strings" - "testing" - - "github.com/rcourtman/pulse-go-rewrite/internal/dockeragent" - "github.com/rs/zerolog" -) - -func TestParseTargetSpec(t *testing.T) { - target, err := parseTargetSpec("https://pulse.example.com|abc123|true") - if err != nil { - t.Fatalf("parseTargetSpec returned error: %v", err) - } - - if target.URL != "https://pulse.example.com" { - t.Fatalf("expected URL https://pulse.example.com, got %q", target.URL) - } - if target.Token != "abc123" { - t.Fatalf("expected token abc123, got %q", target.Token) - } - if !target.InsecureSkipVerify { - t.Fatalf("expected insecure flag true") - } -} - -func TestParseTargetSpecDefaults(t *testing.T) { - target, err := parseTargetSpec(" https://pulse.example.com | token456 ") - if err != nil { - t.Fatalf("parseTargetSpec returned error: %v", err) - } - - if target.URL != "https://pulse.example.com" { - t.Fatalf("expected URL https://pulse.example.com, got %q", target.URL) - } - if target.Token != "token456" { - t.Fatalf("expected token token456, got %q", target.Token) - } - if target.InsecureSkipVerify { - t.Fatalf("expected insecure flag false") - } -} - -func TestParseTargetSpecInvalid(t *testing.T) { - if _, err := parseTargetSpec("https://pulse.example.com"); err == nil { - t.Fatalf("expected error for missing token") - } - if _, err := parseTargetSpec("https://pulse.example.com|token|maybe"); err == nil { - t.Fatalf("expected error for invalid insecure flag") - } -} - -func TestParseTargetSpecsSkipsBlanks(t *testing.T) { - specs, err := parseTargetSpecs([]string{"https://a|tokenA", " ", "\n", "https://b|tokenB|true"}) - if err != nil { - t.Fatalf("parseTargetSpecs returned error: %v", err) - } - - if len(specs) != 2 { - t.Fatalf("expected 2 targets, got %d", len(specs)) - } - - expected := []dockeragent.TargetConfig{ - {URL: "https://a", Token: "tokenA", InsecureSkipVerify: false}, - {URL: "https://b", Token: "tokenB", InsecureSkipVerify: true}, - } - - for i, target := range specs { - if target != expected[i] { - t.Fatalf("target %d mismatch: expected %+v, got %+v", i, expected[i], target) - } - } -} - -func TestSplitTargetSpecs(t *testing.T) { - values := splitTargetSpecs("https://a|tokenA;https://b|tokenB\nhttps://c|tokenC") - expected := []string{"https://a|tokenA", "https://b|tokenB", "https://c|tokenC"} - - if len(values) != len(expected) { - t.Fatalf("expected %d values, got %d", len(expected), len(values)) - } - - for i, v := range values { - if v != expected[i] { - t.Fatalf("value %d mismatch: expected %q, got %q", i, expected[i], v) - } - } -} - -func TestParseLogLevel(t *testing.T) { - tests := []struct { - name string - input string - wantLevel zerolog.Level - wantErr bool - errSubstr string - }{ - // Valid levels - { - name: "debug level", - input: "debug", - wantLevel: zerolog.DebugLevel, - }, - { - name: "info level", - input: "info", - wantLevel: zerolog.InfoLevel, - }, - { - name: "warn level", - input: "warn", - wantLevel: zerolog.WarnLevel, - }, - { - name: "error level", - input: "error", - wantLevel: zerolog.ErrorLevel, - }, - { - name: "trace level", - input: "trace", - wantLevel: zerolog.TraceLevel, - }, - - // Case insensitivity - { - name: "uppercase DEBUG", - input: "DEBUG", - wantLevel: zerolog.DebugLevel, - }, - { - name: "mixed case Info", - input: "Info", - wantLevel: zerolog.InfoLevel, - }, - { - name: "uppercase WARN", - input: "WARN", - wantLevel: zerolog.WarnLevel, - }, - - // Whitespace handling - { - name: "leading whitespace", - input: " debug", - wantLevel: zerolog.DebugLevel, - }, - { - name: "trailing whitespace", - input: "warn ", - wantLevel: zerolog.WarnLevel, - }, - { - name: "both whitespace", - input: " error ", - wantLevel: zerolog.ErrorLevel, - }, - { - name: "tabs", - input: "\tinfo\t", - wantLevel: zerolog.InfoLevel, - }, - - // Empty string defaults to info - { - name: "empty string defaults to info", - input: "", - wantLevel: zerolog.InfoLevel, - }, - { - name: "whitespace only defaults to info", - input: " ", - wantLevel: zerolog.InfoLevel, - }, - { - name: "tabs only defaults to info", - input: "\t\t", - wantLevel: zerolog.InfoLevel, - }, - - // Invalid levels - { - name: "invalid level returns error", - input: "invalid", - wantLevel: zerolog.InfoLevel, - wantErr: true, - errSubstr: "invalid log level", - }, - { - name: "typo returns error", - input: "debuf", - wantLevel: zerolog.InfoLevel, - wantErr: true, - errSubstr: "must be debug, info, warn, or error", - }, - { - name: "numeric 1 maps to info level", - input: "1", - wantLevel: zerolog.InfoLevel, - }, - { - name: "numeric 0 maps to debug level", - input: "0", - wantLevel: zerolog.DebugLevel, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - level, err := parseLogLevel(tt.input) - - if tt.wantErr { - if err == nil { - t.Fatalf("expected error, got nil") - } - if tt.errSubstr != "" && !strings.Contains(err.Error(), tt.errSubstr) { - t.Fatalf("expected error containing %q, got %q", tt.errSubstr, err.Error()) - } - } else { - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - } - - if level != tt.wantLevel { - t.Fatalf("expected level %v, got %v", tt.wantLevel, level) - } - }) - } -} - -func TestSplitStringList(t *testing.T) { - tests := []struct { - name string - input string - want []string - }{ - // Empty input - { - name: "empty string returns nil", - input: "", - want: nil, - }, - - // Single item - { - name: "single item", - input: "foo", - want: []string{"foo"}, - }, - { - name: "single item with whitespace", - input: " foo ", - want: []string{"foo"}, - }, - - // Comma delimiter - { - name: "comma separated", - input: "foo,bar,baz", - want: []string{"foo", "bar", "baz"}, - }, - { - name: "comma with spaces", - input: "foo, bar, baz", - want: []string{"foo", "bar", "baz"}, - }, - { - name: "comma with extra spaces", - input: " foo , bar , baz ", - want: []string{"foo", "bar", "baz"}, - }, - - // Semicolon delimiter - { - name: "semicolon separated", - input: "foo;bar;baz", - want: []string{"foo", "bar", "baz"}, - }, - { - name: "semicolon with spaces", - input: "foo; bar; baz", - want: []string{"foo", "bar", "baz"}, - }, - - // Newline delimiter - { - name: "newline separated", - input: "foo\nbar\nbaz", - want: []string{"foo", "bar", "baz"}, - }, - { - name: "newline with spaces", - input: "foo \n bar \n baz", - want: []string{"foo", "bar", "baz"}, - }, - - // Carriage return delimiter - { - name: "carriage return separated", - input: "foo\rbar\rbaz", - want: []string{"foo", "bar", "baz"}, - }, - { - name: "CRLF (Windows line ending)", - input: "foo\r\nbar\r\nbaz", - want: []string{"foo", "bar", "baz"}, - }, - - // Mixed delimiters - { - name: "mixed comma and semicolon", - input: "foo,bar;baz", - want: []string{"foo", "bar", "baz"}, - }, - { - name: "mixed all delimiters", - input: "a,b;c\nd\re", - want: []string{"a", "b", "c", "d", "e"}, - }, - { - name: "mixed with spaces", - input: "a , b ; c \n d \r e", - want: []string{"a", "b", "c", "d", "e"}, - }, - - // Consecutive delimiters (should be filtered) - { - name: "double comma", - input: "foo,,bar", - want: []string{"foo", "bar"}, - }, - { - name: "multiple consecutive delimiters", - input: "foo,,,bar;;;baz", - want: []string{"foo", "bar", "baz"}, - }, - { - name: "trailing delimiter", - input: "foo,bar,", - want: []string{"foo", "bar"}, - }, - { - name: "leading delimiter", - input: ",foo,bar", - want: []string{"foo", "bar"}, - }, - { - name: "only delimiters returns empty slice", - input: ",;,;", - want: []string{}, - }, - - // Whitespace-only items filtered - { - name: "whitespace between delimiters filtered", - input: "foo, ,bar", - want: []string{"foo", "bar"}, - }, - { - name: "tabs between delimiters filtered", - input: "foo,\t\t,bar", - want: []string{"foo", "bar"}, - }, - - // Real-world examples - { - name: "network names list", - input: "bridge, host, none", - want: []string{"bridge", "host", "none"}, - }, - { - name: "container IDs", - input: "abc123;def456;ghi789", - want: []string{"abc123", "def456", "ghi789"}, - }, - { - name: "multiline config", - input: "web\napi\nworker", - want: []string{"web", "api", "worker"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := splitStringList(tt.input) - if !reflect.DeepEqual(got, tt.want) { - t.Fatalf("expected %v, got %v", tt.want, got) - } - }) - } -} diff --git a/scripts/build-release.sh b/scripts/build-release.sh index 68c4fb895..58ce460b2 100755 --- a/scripts/build-release.sh +++ b/scripts/build-release.sh @@ -113,12 +113,7 @@ for build_name in "${build_order[@]}"; do -o "$BUILD_DIR/pulse-$build_name" \ ./cmd/pulse - # Build docker agent binary - env $build_env go build \ - -ldflags="-s -w -X github.com/rcourtman/pulse-go-rewrite/internal/dockeragent.Version=v${VERSION}" \ - -trimpath \ - -o "$BUILD_DIR/pulse-docker-agent-$build_name" \ - ./cmd/pulse-docker-agent + # Build temperature proxy binary env $build_env go build \ @@ -140,7 +135,7 @@ for build_name in "${build_order[@]}"; do # Copy architecture-specific runtime binaries cp "$BUILD_DIR/pulse-$build_name" "$staging_dir/bin/pulse" - cp "$BUILD_DIR/pulse-docker-agent-$build_name" "$staging_dir/bin/pulse-docker-agent" + cp "$BUILD_DIR/pulse-host-agent-$build_name" "$staging_dir/bin/pulse-host-agent" cp "$BUILD_DIR/pulse-sensor-proxy-$build_name" "$staging_dir/bin/pulse-sensor-proxy" @@ -201,7 +196,7 @@ mkdir -p "$universal_dir/scripts" # Copy all binaries to bin/ directory to maintain consistent structure for build_name in "${build_order[@]}"; do cp "$BUILD_DIR/pulse-$build_name" "$universal_dir/bin/pulse-${build_name}" - cp "$BUILD_DIR/pulse-docker-agent-$build_name" "$universal_dir/bin/pulse-docker-agent-${build_name}" + cp "$BUILD_DIR/pulse-host-agent-$build_name" "$universal_dir/bin/pulse-host-agent-${build_name}" cp "$BUILD_DIR/pulse-agent-$build_name" "$universal_dir/bin/pulse-agent-${build_name}" cp "$BUILD_DIR/pulse-sensor-proxy-$build_name" "$universal_dir/bin/pulse-sensor-proxy-${build_name}" @@ -243,28 +238,6 @@ esac EOF chmod +x "$universal_dir/bin/pulse" -cat > "$universal_dir/bin/pulse-docker-agent" << 'EOF' -#!/bin/sh -# Auto-detect architecture and run appropriate pulse-docker-agent binary - -ARCH=$(uname -m) -case "$ARCH" in - x86_64|amd64) - exec "$(dirname "$0")/pulse-docker-agent-linux-amd64" "$@" - ;; - aarch64|arm64) - exec "$(dirname "$0")/pulse-docker-agent-linux-arm64" "$@" - ;; - armv7l|armhf) - exec "$(dirname "$0")/pulse-docker-agent-linux-armv7" "$@" - ;; - *) - echo "Unsupported architecture: $ARCH" >&2 - exit 1 - ;; -esac -EOF -chmod +x "$universal_dir/bin/pulse-docker-agent" cat > "$universal_dir/bin/pulse-sensor-proxy" << 'EOF' #!/bin/sh