From 0ff7c19952cbdf1bf267b176da8e211298501174 Mon Sep 17 00:00:00 2001 From: Ishwar Kanse Date: Wed, 30 Apr 2025 13:18:49 +0530 Subject: [PATCH] Update Golang test app to generate telemetry data --- tests/test-e2e-apps/golang/Dockerfile | 30 +++++++-- tests/test-e2e-apps/golang/main.go | 97 +++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 11 deletions(-) diff --git a/tests/test-e2e-apps/golang/Dockerfile b/tests/test-e2e-apps/golang/Dockerfile index 6964ccd0e1..9b29261344 100644 --- a/tests/test-e2e-apps/golang/Dockerfile +++ b/tests/test-e2e-apps/golang/Dockerfile @@ -1,8 +1,30 @@ -FROM golang:1.23-alpine as builder +# Stage 1: Build the Go application +FROM golang:1.23.0-alpine AS builder +# Set the working directory inside the container for the build stage. +WORKDIR /app + +# Copy the Go source file into the working directory inside the container. COPY main.go . -RUN CGO_ENABLED=0 GOOS=linux GO111MODULE=on go build -o app main.go +# Build the Go application. +# - CGO_ENABLED=0: Disables CGO, which is necessary to build a statically linked binary +# that can run in a minimal 'scratch' image without external C libraries. +# - GOOS=linux: Explicitly sets the target operating system to Linux, which is standard for containers. +# -o /app/rolldice: Specifies the output path and name for the compiled executable inside this builder stage. +# main.go: The source file to compile. +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /app/rolldice main.go + +# Stage 2: Create the final minimal production image FROM scratch -COPY --from=builder /go/app . -ENTRYPOINT ["./app"] + +# Set the working directory for the final image. +WORKDIR / + +# Copy only the compiled binary from the 'builder' stage (/app/rolldice) +# into the root directory of the final 'scratch' image. +COPY --from=builder /app/rolldice /rolldice + +# Set the command to run when the container starts. +# This executes the compiled Go binary we copied into the image. +ENTRYPOINT ["/rolldice"] diff --git a/tests/test-e2e-apps/golang/main.go b/tests/test-e2e-apps/golang/main.go index a911a8f5f5..76f43aa544 100644 --- a/tests/test-e2e-apps/golang/main.go +++ b/tests/test-e2e-apps/golang/main.go @@ -4,23 +4,106 @@ package main import ( + "crypto/rand" "fmt" + "log" + "math/big" "net/http" + "os" + "strconv" "time" ) +// getRandomNumber generates a random integer between min and max (inclusive). +func getRandomNumber(min, max int) (int, error) { + if min > max { + return 0, fmt.Errorf("min (%d) cannot be greater than max (%d)", min, max) + } + + // Calculate the range size. + rangeSize := big.NewInt(int64(max - min + 1)) + + // Generate a random number n, where 0 <= n < rangeSize. + n, err := rand.Int(rand.Reader, rangeSize) + if err != nil { + // Return an error if random number generation fails + return 0, fmt.Errorf("failed to generate random number: %w", err) + } + + // Convert the big.Int result back to a regular int. + // Add min to shift the range from [0, rangeSize) to [min, max]. + // n.Int64() is safe here because rangeSize fits in int64 if max-min+1 does. + result := int(n.Int64()) + min + return result, nil +} + +// rollDiceHandler handles requests to the /rolldice endpoint. +func rollDiceHandler(w http.ResponseWriter, r *http.Request) { + // Ensure we only handle GET requests for this endpoint + if r.Method != http.MethodGet { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + + // Generate a random number between 1 and 6 + diceRoll, err := getRandomNumber(1, 6) + if err != nil { + // Log the error and return an internal server error if generation failed + log.Printf("Error generating random number: %v", err) + http.Error(w, "Internal Server Error", http.StatusInternalServerError) + return + } + + // Convert the integer result to a string + responseString := strconv.Itoa(diceRoll) + + // Set the content type header to text/plain + w.Header().Set("Content-Type", "text/plain") + + // Write the string response back to the client + _, writeErr := fmt.Fprint(w, responseString) + if writeErr != nil { + // Log the error if writing the response failed (response may be partially sent) + log.Printf("Error writing response: %v", writeErr) + // Attempt to send an error, though headers might already be sent + // http.Error(w, "Internal Server Error", http.StatusInternalServerError) // Avoid sending second error + } + log.Printf("Rolled a %d for request from %s", diceRoll, r.RemoteAddr) // Optional: Log the roll +} + func main() { - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { - fmt.Println("Hi") - }) + // Get the port from the environment variable, default to "8095" if not set + port := os.Getenv("PORT") + if port == "" { + port = "8095" + } + + // Register the handler function for the "/rolldice" path with the DefaultServeMux. + http.HandleFunc("/rolldice", rollDiceHandler) + + // Construct the server address string (e.g., ":8095") + serverAddr := ":" + port + // Configure the HTTP server with timeouts server := &http.Server{ - Addr: ":8080", - ReadHeaderTimeout: 3 * time.Second, + Addr: serverAddr, + // Use DefaultServeMux by leaving Handler nil, which includes our /rolldice handler + Handler: nil, + ReadTimeout: 10 * time.Second, // Max time to read entire request, including body + WriteTimeout: 10 * time.Second, // Max time to write response + IdleTimeout: 60 * time.Second, // Max time for connections using TCP Keep-Alive } + // Print a message indicating the server is starting + log.Printf("Listening for requests on http://localhost:%s", port) + + // Start the configured HTTP server. + // This ListenAndServe is now called on our configured server instance. err := server.ListenAndServe() - if err != nil { - panic(err) + if err != nil && err != http.ErrServerClosed { + // Log any fatal errors encountered during server startup, ignore ErrServerClosed + log.Fatalf("Server failed to start or unexpectedly closed: %v", err) + } else if err == http.ErrServerClosed { + log.Println("Server shut down gracefully") } }