Skip to content

Commit

Permalink
hello world app with CI initial test-run
Browse files Browse the repository at this point in the history
  • Loading branch information
cherrymu committed Aug 20, 2024
1 parent b2bea73 commit c7be225
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 0 deletions.
57 changes: 57 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: CI Pipeline

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: 1.22

- name: Install dependencies
run: go mod tidy

- name: Build the application
run: go build -v ./...

- name: Run unit tests
run: go test -v ./...

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/hello-world-server:latest

- name: Install Cosign (Container Signing Tool)
run: |
curl -LO https://github.com/sigstore/cosign/releases/download/v2.0.0/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
- name: Sign Docker image
run: cosign sign --key ${{ secrets.COSIGN_PRIVATE_KEY }} ${{ secrets.DOCKER_USERNAME }}/hello-world-server:latest
30 changes: 30 additions & 0 deletions hello-world/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Use the official Golang image for building the binary
FROM golang:1.22-alpine AS builder

# Set the working directory inside the container
WORKDIR /app

# Copy the Go module files and download dependencies
COPY go.mod ./
RUN go mod download

# Copy the rest of the source code
COPY . .

# Build the Go binary
RUN go build -o hello-world

# Use a minimal base image to run the application
FROM alpine:3.17

# Set the working directory inside the container
WORKDIR /app

# Copy the built binary from the builder stage
COPY --from=builder /app/hello-world .

# Expose the port that the application will run on
EXPOSE 8080

# Run the Go application
CMD ["./hello-world"]
17 changes: 17 additions & 0 deletions hello-world/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module hello-world

go 1.22.1

require github.com/prometheus/client_golang v1.20.1

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
golang.org/x/sys v0.22.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
)
24 changes: 24 additions & 0 deletions hello-world/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/prometheus/client_golang v1.20.1 h1:IMJXHOD6eARkQpxo8KkhgEVFlBNm+nkrFUyGlIu7Na8=
github.com/prometheus/client_golang v1.20.1/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
17 changes: 17 additions & 0 deletions hello-world/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// this file will contain handlers needed for the server

package main

import (
"fmt"
"log"
"net/http"
)

// helloHandler returns a 200 OK with a "Hello, World!" message.
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "Hello, World!")
log.Println("Serving hello world app")

}
Binary file added hello-world/hello-world
Binary file not shown.
20 changes: 20 additions & 0 deletions hello-world/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// create main entry point for our application

package main

import (
"log"
"net/http"
)

func main() {
// Register the HTTP handlers
http.HandleFunc("/", helloHandler)
http.HandleFunc("/metrics", telemetryHandler)

// Start the HTTP server on port 8080
log.Println("Starting server on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("Could not start server: %s\n", err.Error())
}
}
50 changes: 50 additions & 0 deletions hello-world/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

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

// simple test case to test the helloHandler function

func TestHelloHandler(t *testing.T) {
req, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatalf("Could not create request: %v", err)
}

rec := httptest.NewRecorder()
helloHandler(rec, req)

if status := rec.Code; status != http.StatusOK {
t.Errorf("Expected status code %v, got %v", http.StatusOK, status)
}

expected := "Hello, World!\n"
if strings.TrimSpace(rec.Body.String()) != strings.TrimSpace(expected) {
t.Errorf("Expected body to be '%v', got '%v'", expected, rec.Body.String())
}
}

// simple test case to test the telemetryHandler function

func TestTelemetryHandler(t *testing.T) {
req, err := http.NewRequest("GET", "/metrics", nil)
if err != nil {
t.Fatalf("Could not create request: %v", err)
}

rec := httptest.NewRecorder()
telemetryHandler(rec, req)

if status := rec.Code; status != http.StatusOK {
t.Errorf("Expected status code %v, got %v", http.StatusOK, status)
}

// Simple check to ensure the output contains expected metrics name.
if !strings.Contains(rec.Body.String(), "http_requests_total") {
t.Errorf("Expected body to contain 'http_requests_total' metric, got '%v'", rec.Body.String())
}
}
35 changes: 35 additions & 0 deletions hello-world/telemetry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// This file handles telemetry and exposes metrics using the prometheus client library.

package main

import (
"log"
"net/http"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Define a simple counter metric
var (
httpRequestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total number of HTTP requests",
},
[]string{"path"},
)
)

func init() {
// Register the metric with Prometheus's default registry.
prometheus.MustRegister(httpRequestsTotal)
}

// telemetryHandler exposes Prometheus metrics.
func telemetryHandler(w http.ResponseWriter, r *http.Request) {
httpRequestsTotal.WithLabelValues(r.URL.Path).Inc()
promhttp.Handler().ServeHTTP(w, r)
log.Println("Serving basic metrics for our app")

}

0 comments on commit c7be225

Please sign in to comment.