From 8dfe488cdf79076f2f7762aa867abcba2de0658a Mon Sep 17 00:00:00 2001 From: Zach <3724288+zedkipp@users.noreply.github.com> Date: Fri, 6 Feb 2026 16:55:33 -0700 Subject: [PATCH] feat: add mock telemetry server for local development (#21932) Adds a standalone command that acts as a mock telemetry server, receiving snapshots and printing them as a JSON stream to stdout. Useful for local development testing with scripts/develop.sh by setting CODER_TELEMETRY_ENABLE and CODER_TELEMETRY_URL environment variabless. --- codersdk/deployment.go | 2 + scripts/telemetry-server/main.go | 74 ++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 scripts/telemetry-server/main.go diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 6b450058e6..c7c60ae1db 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -2380,6 +2380,8 @@ func (c *DeploymentValues) Options() serpent.OptionSet { Group: &deploymentGroupTelemetry, UseInstead: []serpent.Option{telemetryEnable}, }, + // For local development testing, see scripts/telemetry-server which + // provides a mock server that prints received telemetry as JSON. { Name: "Telemetry URL", Description: "URL to send telemetry.", diff --git a/scripts/telemetry-server/main.go b/scripts/telemetry-server/main.go new file mode 100644 index 0000000000..4fccf592b3 --- /dev/null +++ b/scripts/telemetry-server/main.go @@ -0,0 +1,74 @@ +// telemetry-server is a standalone HTTP server that receives telemetry +// snapshots and prints them as a JSON stream to stdout. This is useful for +// local development. Test with scripts/develop.sh by setting: +// +// CODER_TELEMETRY_ENABLE=true CODER_TELEMETRY_URL=http://127.0.0.1:8081 +// +// Usage: +// +// go run ./scripts/telemetry-server [--port 8081] +package main + +import ( + "encoding/json" + "errors" + "flag" + "fmt" + "io" + "net" + "net/http" + "os" + "os/signal" + "time" +) + +func main() { + port := flag.String("port", "8081", "Port to listen on") + flag.Parse() + + enc := json.NewEncoder(os.Stdout) + + mux := http.NewServeMux() + + handleTelemetry := func(telemetryType string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + output := map[string]any{ + "type": telemetryType, + "version": r.Header.Get("X-Telemetry-Version"), + "data": json.RawMessage(body), + } + _ = enc.Encode(output) + + w.WriteHeader(http.StatusAccepted) + } + } + + mux.HandleFunc("POST /snapshot", handleTelemetry("snapshot")) + mux.HandleFunc("POST /deployment", handleTelemetry("deployment")) + + addr := net.JoinHostPort("127.0.0.1", *port) + server := &http.Server{ + Addr: addr, + Handler: mux, + ReadHeaderTimeout: 10 * time.Second, + } + + go func() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + <-c + _ = server.Close() + }() + + _, _ = fmt.Fprintf(os.Stdout, "Mock telemetry server listening on %s\n", addr) + if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + _, _ = fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } +}