Skip to content

Commit

Permalink
Added optional NODE env var
Browse files Browse the repository at this point in the history
Signed-off-by: Philip Schmid <[email protected]>
  • Loading branch information
PhilipSchmid committed Jun 10, 2024
1 parent 2c319ab commit 436c634
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 9 deletions.
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

![Build and Push Docker Image](https://github.com/philipschmid/echo-app/actions/workflows/build.yaml/badge.svg) ![Go Syntax and Format Check](https://github.com/philipschmid/echo-app/actions/workflows/test.yaml/badge.svg)

Tiny golang app which returns a timestamp, a customizable message, the hostname, the request source IP, and optionally the HTTP request headers.
Tiny golang app which returns a timestamp, a customizable message, the hostname, the request source IP, the node name (if set), and optionally the HTTP request headers.

## Configuration Options
- `MESSAGE`: A customizable message to be returned in the JSON response. If not set, no message will be displayed.
- `NODE`: The name of the node where the app is running. This is typically used in a Kubernetes environment.
- `PORT`: The port number on which the server listens. Default is `8080`.
- `PRINT_HTTP_REQUEST_HEADERS`: Set to `true` to include HTTP request headers in the JSON response. By default, headers are not included.

Expand All @@ -15,6 +16,8 @@ Shell 1 (server):
docker run -it -p 8080:8080 ghcr.io/philipschmid/echo-app:main
# Optionally with a customized message:
docker run -it -p 8080:8080 -e MESSAGE="demo-env" ghcr.io/philipschmid/echo-app:main
# Optionally with a node name:
docker run -it -p 8080:8080 -e NODE="k8s-node-1" ghcr.io/philipschmid/echo-app:main
# Optionally include HTTP request headers in the response:
docker run -it -p 8080:8080 -e PRINT_HTTP_REQUEST_HEADERS="true" ghcr.io/philipschmid/echo-app:main
```
Expand All @@ -27,16 +30,16 @@ curl http://localhost:8080/
You should see a similar client output like this:
```json
{"timestamp":"2024-05-28T19:50:10.289Z","hostname":"83ff0b127ed6","source_ip":"192.168.65.1"}
{"timestamp":"2024-05-28T19:50:35.022Z","message":"Hello World!","hostname":"4495529ebd32","source_ip":"192.168.65.1"}
{"timestamp":"2024-05-28T19:50:35.022Z","message":"Hello World!","hostname":"4495529ebd32","source_ip":"192.168.65.1","node":"k8s-node-1"}
```

If `PRINT_HTTP_REQUEST_HEADERS` is set to `true`, the response will also include the request headers:
```json
{"timestamp":"2024-05-28T20:21:23.363Z","hostname":"3f96391b04f2","source_ip":"192.168.65.1","headers":{"Accept":["*/*"],"User-Agent":["curl/8.6.0"]}}
{"timestamp":"2024-05-28T20:21:23.363Z","hostname":"3f96391b04f2","source_ip":"192.168.65.1","node":"k8s-node-1","headers":{"Accept":["*/*"],"User-Agent":["curl/8.6.0"]}}
```

## Kubernetes
Apply the following manifests:
Apply the following manifests to deploy the echo-app with the `NODE` environment variable set to the name of the Kubernetes node using the Downward API:
```yaml
---
apiVersion: v1
Expand Down Expand Up @@ -91,6 +94,11 @@ spec:
configMapKeyRef:
name: echo-app-config
key: PRINT_HTTP_REQUEST_HEADERS
# Add the NODE environment variable using the downward API
- name: NODE
valueFrom:
fieldRef:
fieldPath: spec.nodeName
---
apiVersion: v1
kind: Service
Expand All @@ -111,5 +119,16 @@ Shell 2 (client):
kubectl run netshoot --rm -it --image=nicolaka/netshoot -- curl http://echo-app-service:8080
```

You should see a similar client output like this:
```json
{"timestamp":"2024-05-28T19:50:35.022Z","message":"demo-env","hostname":"echo-app-deployment-5d8f8b8b8b-9t4kq","source_ip":"10.1.0.1","node":"k8s-node-1"}
```

If `PRINT_HTTP_REQUEST_HEADERS` is set to `true`, the response will also include the request headers:
```json
{"timestamp":"2024-05-28T20:21:23.363Z","message":"demo-env","hostname":"echo-app-deployment-5d8f8b8b8b-9t4kq","source_ip":"10.1.0.1","node":"k8s-node-1","headers":{"Accept":["*/*"],"User-Agent":["curl/8.6.0"]}}
```

## Credit
Basic idea (& source code) is taken from https://cloud.google.com/kubernetes-engine/docs/samples/container-hello-app.
Basic idea (& source code) is taken from https://cloud.google.com/kubernetes-engine/docs/samples/container-hello-app.
```
36 changes: 32 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ import (
type Response struct {
Timestamp string `json:"timestamp"`
Message *string `json:"message,omitempty"`
Hostname string `json:"hostname"`
SourceIP string `json:"source_ip"`
Hostname string `json:"hostname"`
Node *string `json:"node,omitempty"` // Optional field to include node name
Headers map[string][]string `json:"headers,omitempty"` // Optional field to include headers
}

func main() {
// Get MESSAGE and PRINT_HTTP_REQUEST_HEADERS environment variables
messagePtr := getMessagePtr()
nodePtr := getNodePtr()
printHeaders := getPrintHeadersSetting()

// Prepare the message log
Expand All @@ -30,9 +32,25 @@ func main() {
messageLog = "MESSAGE environment variable set to: " + *messagePtr
}

// Prepare the node log
nodeLog := "No NODE environment variable set"
if nodePtr != nil {
nodeLog = "NODE environment variable set to: " + *nodePtr
}

// Print optional configs on multiple lines
log.Println("Server configuration:")
log.Printf(" %s\n", messageLog)
log.Printf(" %s\n", nodeLog)
if printHeaders {
log.Println(" PRINT_HTTP_REQUEST_HEADERS is enabled")
} else {
log.Println(" PRINT_HTTP_REQUEST_HEADERS is disabled")
}

// Register hello function to handle all requests
mux := http.NewServeMux()
mux.HandleFunc("/", hello(messagePtr, printHeaders)) // Pass message pointer and printHeaders to the hello function
mux.HandleFunc("/", hello(messagePtr, nodePtr, printHeaders)) // Pass message and node pointers and printHeaders to the hello function

// Use PORT environment variable, or default to 8080
port := os.Getenv("PORT")
Expand All @@ -41,12 +59,12 @@ func main() {
}

// Start the web server on port and accept requests
log.Printf("Server listening on port %s (%s)", port, messageLog)
log.Printf("Server listening on port %s\n", port)
log.Fatal(http.ListenAndServe(":"+port, mux))
}

// hello returns a http.HandlerFunc that uses the provided message pointer and printHeaders flag.
func hello(messagePtr *string, printHeaders bool) http.HandlerFunc {
func hello(messagePtr *string, nodePtr *string, printHeaders bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Get the IP address without the port number
ip, _, err := net.SplitHostPort(r.RemoteAddr)
Expand All @@ -68,6 +86,7 @@ func hello(messagePtr *string, printHeaders bool) http.HandlerFunc {
Timestamp: timestamp,
Message: messagePtr,
Hostname: host,
Node: nodePtr,
SourceIP: ip,
}

Expand Down Expand Up @@ -100,6 +119,15 @@ func getMessagePtr() *string {
return &message
}

// getNodePtr gets the NODE environment variable and returns a pointer to it, or nil if it's not set.
func getNodePtr() *string {
node := os.Getenv("NODE")
if node == "" {
return nil
}
return &node
}

// getPrintHeadersSetting checks the PRINT_HTTP_REQUEST_HEADERS environment variable.
func getPrintHeadersSetting() bool {
return strings.ToLower(os.Getenv("PRINT_HTTP_REQUEST_HEADERS")) == "true"
Expand Down

0 comments on commit 436c634

Please sign in to comment.