Skip to content

[Chore] Update Golang test app to generate telemetry data #3960

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 1, 2025
Merged
Show file tree
Hide file tree
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
30 changes: 26 additions & 4 deletions tests/test-e2e-apps/golang/Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
97 changes: 90 additions & 7 deletions tests/test-e2e-apps/golang/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
Loading