Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 111 additions & 50 deletions server/coverage_server.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
package main

// All imports are aliased with a "_cov" prefix and
// all top-level identifiers use a "_cov" prefix to avoid name
// collisions with identifiers declared by the host package (e.g. many
// projects declare ``var log = …`` at the package level).
//
// Multiple containers in a pod may each start a coverage server. The server
// tries ports starting at _covDefaultPort (or COVERAGE_PORT) and increments
// up to _covMaxRetries times until it finds a free port.
//
// Clients can identify a coverage server by sending a HEAD request to any
// endpoint: the response will include the headers:
// X-Art-Coverage-Server: 1
// X-Art-Coverage-Pid: <pid>
// X-Art-Coverage-Binary: <binary-name>

import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"runtime/coverage"
"time"
_covBytes "bytes"
_covBase64 "encoding/base64"
_covJSON "encoding/json"
_covFmt "fmt"
_covLog "log"
_covNet "net"
_covHTTP "net/http"
_covOS "os"
_covPath "path/filepath"
_covRuntime "runtime/coverage"
_covStrconv "strconv"
_covTime "time"
)

const (
_covDefaultPort = 53700 // Starting port for the coverage server
_covMaxRetries = 50 // Maximum number of ports to try
)

// CoverageResponse represents the JSON response from the coverage endpoint
type CoverageResponse struct {
// _covResponse represents the JSON response from the coverage endpoint
type _covResponse struct {
MetaFilename string `json:"meta_filename"`
MetaData string `json:"meta_data"` // base64 encoded
CountersFilename string `json:"counters_filename"`
Expand All @@ -23,51 +46,89 @@ type CoverageResponse struct {

func init() {
// Start coverage server in a separate goroutine
go startCoverageServer()
go _covStartServer()
}

// _covIdentityMiddleware wraps a handler to add identification headers to
// every response (including HEAD requests) so that clients can confirm they
// are talking to a coverage server rather than an unrelated process.
func _covIdentityMiddleware(next _covHTTP.Handler) _covHTTP.Handler {
pid := _covStrconv.Itoa(_covOS.Getpid())
exe := "unknown"
if exePath, err := _covOS.Executable(); err == nil {
exe = _covPath.Base(exePath)
}
return _covHTTP.HandlerFunc(func(w _covHTTP.ResponseWriter, r *_covHTTP.Request) {
w.Header().Set("X-Art-Coverage-Server", "1")
w.Header().Set("X-Art-Coverage-Pid", pid)
w.Header().Set("X-Art-Coverage-Binary", exe)
next.ServeHTTP(w, r)
})
}

// startCoverageServer starts a dedicated HTTP server for coverage collection
func startCoverageServer() {
// Get coverage port from environment variable, default to 9095
coveragePort := os.Getenv("COVERAGE_PORT")
if coveragePort == "" {
coveragePort = "9095"
// _covStartServer starts a dedicated HTTP server for coverage collection.
// It tries successive ports starting from COVERAGE_PORT (default 53700)
// until one is available or _covMaxRetries attempts are exhausted.
func _covStartServer() {
startPort := _covDefaultPort
if envPort := _covOS.Getenv("COVERAGE_PORT"); envPort != "" {
if p, err := _covStrconv.Atoi(envPort); err == nil && p > 0 {
startPort = p
}
}

// Create a new ServeMux for the coverage server (isolated from main app)
mux := http.NewServeMux()
mux.HandleFunc("/coverage", CoverageHandler)
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "coverage server healthy")
mux := _covHTTP.NewServeMux()
mux.HandleFunc("/coverage", _covHandler)
mux.HandleFunc("/health", func(w _covHTTP.ResponseWriter, r *_covHTTP.Request) {
w.WriteHeader(_covHTTP.StatusOK)
_covFmt.Fprintf(w, "coverage server healthy")
})

addr := ":" + coveragePort
log.Printf("[COVERAGE] Starting coverage server on %s", addr)
log.Printf("[COVERAGE] Endpoints: GET %s/coverage, GET %s/health", addr, addr)
// Wrap with identity middleware so HEAD requests get recognition headers
handler := _covIdentityMiddleware(mux)

for attempt := 0; attempt < _covMaxRetries; attempt++ {
port := startPort + attempt
addr := _covFmt.Sprintf(":%d", port)

// Try to bind the port before committing to ListenAndServe so we
// can detect "address already in use" and try the next port.
ln, err := _covNet.Listen("tcp", addr)
if err != nil {
_covLog.Printf("[COVERAGE] Port %d unavailable: %v; trying next", port, err)
continue
}

// Start the server (this will block, but we're in a goroutine)
if err := http.ListenAndServe(addr, mux); err != nil {
log.Printf("[COVERAGE] ERROR: Coverage server failed: %v", err)
_covLog.Printf("[COVERAGE] Starting coverage server on port %d (pid %d)", port, _covOS.Getpid())
_covLog.Printf("[COVERAGE] Endpoints: GET %s/coverage, GET %s/health, HEAD %s/*", addr, addr, addr)

// Serve on the already-bound listener (this blocks)
if err := _covHTTP.Serve(ln, handler); err != nil {
_covLog.Printf("[COVERAGE] ERROR: Coverage server on port %d failed: %v", port, err)
}
return
}

_covLog.Printf("[COVERAGE] ERROR: Could not bind any port in range %d–%d", startPort, startPort+_covMaxRetries-1)
}

// CoverageHandler collects coverage data and returns it via HTTP as JSON
func CoverageHandler(w http.ResponseWriter, r *http.Request) {
log.Println("[COVERAGE] Collecting coverage data...")
// _covHandler collects coverage data and returns it via HTTP as JSON
func _covHandler(w _covHTTP.ResponseWriter, r *_covHTTP.Request) {
_covLog.Println("[COVERAGE] Collecting coverage data...")

// Collect metadata
var metaBuf bytes.Buffer
if err := coverage.WriteMeta(&metaBuf); err != nil {
http.Error(w, fmt.Sprintf("Failed to collect metadata: %v", err), http.StatusInternalServerError)
var metaBuf _covBytes.Buffer
if err := _covRuntime.WriteMeta(&metaBuf); err != nil {
_covHTTP.Error(w, _covFmt.Sprintf("Failed to collect metadata: %v", err), _covHTTP.StatusInternalServerError)
return
}
metaData := metaBuf.Bytes()

// Collect counters
var counterBuf bytes.Buffer
if err := coverage.WriteCounters(&counterBuf); err != nil {
http.Error(w, fmt.Sprintf("Failed to collect counters: %v", err), http.StatusInternalServerError)
var counterBuf _covBytes.Buffer
if err := _covRuntime.WriteCounters(&counterBuf); err != nil {
_covHTTP.Error(w, _covFmt.Sprintf("Failed to collect counters: %v", err), _covHTTP.StatusInternalServerError)
return
}
counterData := counterBuf.Bytes()
Expand All @@ -76,34 +137,34 @@ func CoverageHandler(w http.ResponseWriter, r *http.Request) {
var hash string
if len(metaData) >= 32 {
hashBytes := metaData[16:32]
hash = fmt.Sprintf("%x", hashBytes)
hash = _covFmt.Sprintf("%x", hashBytes)
} else {
hash = "unknown"
}

// Generate proper filenames
timestamp := time.Now().UnixNano()
metaFilename := fmt.Sprintf("covmeta.%s", hash)
counterFilename := fmt.Sprintf("covcounters.%s.%d.%d", hash, os.Getpid(), timestamp)
timestamp := _covTime.Now().UnixNano()
metaFilename := _covFmt.Sprintf("covmeta.%s", hash)
counterFilename := _covFmt.Sprintf("covcounters.%s.%d.%d", hash, _covOS.Getpid(), timestamp)

log.Printf("[COVERAGE] Collected %d bytes metadata, %d bytes counters",
_covLog.Printf("[COVERAGE] Collected %d bytes metadata, %d bytes counters",
len(metaData), len(counterData))

// Return coverage data as JSON
response := CoverageResponse{
response := _covResponse{
MetaFilename: metaFilename,
MetaData: base64.StdEncoding.EncodeToString(metaData),
MetaData: _covBase64.StdEncoding.EncodeToString(metaData),
CountersFilename: counterFilename,
CountersData: base64.StdEncoding.EncodeToString(counterData),
CountersData: _covBase64.StdEncoding.EncodeToString(counterData),
Timestamp: timestamp,
}

w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(response); err != nil {
log.Printf("[COVERAGE] Error encoding response: %v", err)
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
if err := _covJSON.NewEncoder(w).Encode(response); err != nil {
_covLog.Printf("[COVERAGE] Error encoding response: %v", err)
_covHTTP.Error(w, "Failed to encode response", _covHTTP.StatusInternalServerError)
return
}

log.Println("[COVERAGE] Coverage data sent successfully")
_covLog.Println("[COVERAGE] Coverage data sent successfully")
}
Loading