diff --git a/.travis.yml b/.travis.yml index 1ba319b..0ac1e0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ -language: python -services: - - docker +language: go +go: +- 1.8 +- 1.9 +- master script: - - docker build -t infrastructureascode/hello-world . +- make all diff --git a/Dockerfile b/Dockerfile index 624e450..20eeb01 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,14 @@ -FROM alpine +# Copied from https://github.com/prometheus/client_golang/blob/master/examples/simple/Dockerfile -RUN apk update && \ - apk upgrade && \ - apk add nginx && \ - mkdir -p /run/nginx +FROM golang:1.9.2 AS builder +WORKDIR /go/src/github.com/infrastructure-as-code/docker-hello-world +ENV GIN_MODE debug +COPY Makefile *.go ./ +RUN make all -COPY default.conf /etc/nginx/conf.d/default.conf -EXPOSE 80 -CMD nginx -g "daemon off;" +FROM scratch +LABEL maintainer "Vince Tse " +COPY --from=builder /go/src/github.com/infrastructure-as-code/docker-hello-world/hello_world . +ENV GIN_MODE release +EXPOSE 8080 +ENTRYPOINT ["/hello_world"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2edc088 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +all: deps test + CGO_ENABLED=0 go build -a -o hello_world + +test: + GIN_MODE=debug go test + +deps: + go get -d + go get github.com/stretchr/testify diff --git a/README.md b/README.md index 866594c..c451f27 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ +[![Build Status](https://travis-ci.org/infrastructure-as-code/docker-hello-world.svg?branch=master)](https://travis-ci.org/infrastructure-as-code/docker-hello-world) + # infrastructureascode/hello-world -A Docker "Hello World" web server inspired by the [tutum/hello-world](https://hub.docker.com/r/tutum/hello-world/) image which seems to have become unmaintained, and returns HTTP/400 more often than it should. +A [Prometheus](https://prometheus.io/)-instrumented Docker "Hello World" web server. ## Features -1. Always returns a HTTP 200 status code and a "Hello, world!" message. -1. Has a health check endpoint, `/health`, that returns an empty response. +1. Always returns a HTTP 200 status code and a "Hello, World!" message at the `/` path. +1. Has a metrics endpoint at `/metrics` that returns Prometheus metrics. +1. Has a health check endpoint, `/health`, that returns an empty response and a HTTP 200 response. ## Building ``` -docker build --rm infrastructureascode/hello-world . +docker build --rm -t infrastructureascode/hello-world . ``` ## Usage @@ -20,7 +23,7 @@ docker build --rm infrastructureascode/hello-world . docker run \ --detach \ --name hello-world \ - --publish 8000:80 \ + --publish 8000:8080 \ infrastructureascode/hello-world # curl the container @@ -28,4 +31,7 @@ curl http://0.0.0.0:8000/ # curl the health check endpoint which returns an empty response curl http://0.0.0.0:8000/health + +# curl Prometheus metrics +curl http://0.0.0.0:8000/metrics ``` diff --git a/default.conf b/default.conf deleted file mode 100644 index 3d4e294..0000000 --- a/default.conf +++ /dev/null @@ -1,27 +0,0 @@ -# This is a default site configuration which will simply return 404, preventing -# chance access to any other virtualhost. - -server { - listen 80 default_server; - listen [::]:80 default_server; - - # get real client IP address - real_ip_header X-Forwarded-For; - access_log /dev/stdout main; - default_type 'text/plain'; - - # Everything is a 200 - location / { - return 200 'Hello, world!'; - } - - # Health-check - location /health { - return 200; - } - - # You may need this to prevent return 404 recursion. - location = /404.html { - internal; - } -} diff --git a/hello_world.go b/hello_world.go new file mode 100644 index 0000000..2b8cd01 --- /dev/null +++ b/hello_world.go @@ -0,0 +1,43 @@ +package main + +import ( + "net/http" + "os" + + "github.com/gin-gonic/gin" + "github.com/zsais/go-gin-prometheus" +) + +var hostname = getHostname() + +func getHostname() string { + var name, err = os.Hostname() + if err != nil { + panic(err) + } + return name +} + +func helloFunc(c *gin.Context) { + c.Writer.Header().Set("X-Hostname", hostname) + c.String(http.StatusOK, "Hello, World!") +} + +func healthFunc(c *gin.Context) { + c.Writer.Header().Set("X-Hostname", hostname) + c.String(http.StatusOK, "") +} + +func setupRouter() *gin.Engine { + router := gin.Default() + ginprom := ginprometheus.NewPrometheus("gin") + ginprom.Use(router) + router.GET("/", helloFunc) + router.GET("/health", healthFunc) + return router +} + +func main() { + router := setupRouter() + router.Run() +} diff --git a/hello_world_test.go b/hello_world_test.go new file mode 100644 index 0000000..3b430d7 --- /dev/null +++ b/hello_world_test.go @@ -0,0 +1,42 @@ + +package main + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDefaultRoute(t *testing.T) { + router := setupRouter() + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/", nil) + router.ServeHTTP(w, req) + + assert.Equal(t, 200, w.Code) + assert.Equal(t, "Hello, World!", w.Body.String()) +} + +func TestHealthRoute(t *testing.T) { + router := setupRouter() + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/health", nil) + router.ServeHTTP(w, req) + + assert.Equal(t, 200, w.Code) + assert.Equal(t, "", w.Body.String()) +} + +func TestMetricsRoute(t *testing.T) { + router := setupRouter() + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/metrics", nil) + router.ServeHTTP(w, req) + + assert.Equal(t, 200, w.Code) +}