Skip to content

Commit

Permalink
keepalive
Browse files Browse the repository at this point in the history
  • Loading branch information
mike-callahan committed Aug 1, 2024
1 parent 5a9bf9b commit 84ba26b
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 0 deletions.
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM golang:1.21.12

WORKDIR /usr/src/app

# pre-copy/cache go.mod for pre-downloading dependencies and only redownloading them in subsequent builds if they change
COPY go.mod ./

COPY . .
RUN go build -v -o /usr/bin/k8s-keepalive ./...

EXPOSE 5000

CMD ["/usr/bin/k8s-keepalive"]
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/mike-callahan/k8s-keepalive

go 1.21.12
40 changes: 40 additions & 0 deletions k8s-keepalive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"context"
"errors"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)

func main() {
log.Println("Starting keepalive service...")
handler := http.HandlerFunc(HTTPProbe)
server := &http.Server{
Addr: ":5000",
Handler: handler,
}

go func() {
if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("HTTP server error: %v", err)
}
log.Println("Stopped serving new connections.")
}()

sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan

shutdownCtx, shutdownRelease := context.WithTimeout(context.Background(), 10*time.Second)
defer shutdownRelease()

if err := server.Shutdown(shutdownCtx); err != nil {
log.Fatalf("HTTP shutdown error: %v", err)
}
log.Println("Graceful shutdown complete.")
}
60 changes: 60 additions & 0 deletions web.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import (
"errors"
"fmt"
"net/http"
"strconv"
"strings"
)

// Check if the status code passed by the user is valid
func IsValidHTTPStatusCode(code int) bool {
return code >= 100 && code <= 599
}

// Respond to k8s health probes
// Additional functionality of allowing user to pass arbitrary status codes for testing
func HTTPProbe(w http.ResponseWriter, r *http.Request) {
// Error if the request isn't Get or Post
if r.Method != http.MethodGet && r.Method != http.MethodPost {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, "Only GET and POST requests are allowed")
return
}

// Send 200 for /
path := strings.TrimPrefix(r.URL.Path, "/")
if path == "" || path == "healthz" {
// Default response
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Hello! Pass a status code in the URL path (e.g., /200 or /404)")
return
}

statusCode, _ := strconv.Atoi(path)
_, err := strconv.Atoi(path)

// Error if string can't be converted to integer
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, "That is not a valid status code or integer.")
return
}

// Error if integer is out of validity range
err = nil
if IsValidHTTPStatusCode(statusCode) == false {
err = errors.New("That integer is not a valid status code")
}

if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintln(w, fmt.Errorf("Error processing data: %w", err))
return
}

// Write value back to header and body
w.WriteHeader(statusCode)
fmt.Fprintf(w, "%d", statusCode)
}
83 changes: 83 additions & 0 deletions web_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package main

import (
"net/http"
"net/http/httptest"
"testing"
)

func TestHTTPProbe(t *testing.T) {
t.Run("Return default status", func(t *testing.T) {
request, _ := http.NewRequest(http.MethodGet, "/", nil)
response := httptest.NewRecorder()

HTTPProbe(response, request)

got := response.Result().StatusCode
want := 200

if got != want {
t.Errorf("got %q, want %q", got, want)
}

})
t.Run("Return status 200", func(t *testing.T) {
request, _ := http.NewRequest(http.MethodGet, "/200", nil)
response := httptest.NewRecorder()

HTTPProbe(response, request)

got := response.Result().StatusCode
want := 200

if got != want {
t.Errorf("got %q, want %q", got, want)
}

})

t.Run("Return status 404", func(t *testing.T) {
request, _ := http.NewRequest(http.MethodGet, "/404", nil)
response := httptest.NewRecorder()

HTTPProbe(response, request)

got := response.Result().StatusCode
want := 404

if got != want {
t.Errorf("got %q, want %q", got, want)
}

})

t.Run("Return status non-status", func(t *testing.T) {
request, _ := http.NewRequest(http.MethodGet, "/asdf", nil)
response := httptest.NewRecorder()

HTTPProbe(response, request)

got := response.Result().StatusCode
want := 400

if got != want {
t.Errorf("got %d, want %d", got, want)
}

})

t.Run("PATCH method", func(t *testing.T) {
request, _ := http.NewRequest(http.MethodPatch, "/asdf", nil)
response := httptest.NewRecorder()

HTTPProbe(response, request)

got := response.Result().StatusCode
want := 400

if got != want {
t.Errorf("got %d, want %d", got, want)
}

})
}

0 comments on commit 84ba26b

Please sign in to comment.