├── .codeclimate.yml ├── Dockerfile ├── LICENSE ├── README.md ├── codefresh.yml └── healthcheck.go /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | fixme: 4 | enabled: true 5 | gofmt: 6 | enabled: true 7 | golint: 8 | enabled: true 9 | ratings: 10 | paths: 11 | - "**.go" 12 | exclude_paths: [] 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.9-alpine as build 2 | WORKDIR /go/src 3 | ENV CGO_ENABLED=0 4 | ENV GO_PATH=/go/src 5 | COPY . /go/src 6 | RUN go build -a --installsuffix cgo --ldflags=-s -o healthcheck 7 | 8 | FROM scratch 9 | COPY --from=build /go/src/healthcheck / 10 | CMD ["/healthcheck"] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 wtfcoderz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # static-healthcheck 2 | [![Go Report Card](https://goreportcard.com/badge/wtfcoderz/static-healthcheck)](http://goreportcard.com/report/wtfcoderz/static-healthcheck) 3 | [![Codefresh build status]( https://g.codefresh.io/api/badges/build?repoOwner=wtfcoderz&repoName=static-healthcheck&branch=master&pipelineName=static-healthcheck&accountName=wtfkr0&type=cf-1)]( https://g.codefresh.io/repositories/wtfcoderz/static-healthcheck/builds?filter=trigger:build;branch:master;service:5a01d6647062b6000195c179~static-healthcheck) 4 | [![](https://images.microbadger.com/badges/image/wtfcoderz/static-healthcheck.svg)](https://microbadger.com/images/wtfcoderz/static-healthcheck) 5 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/wtfcoderz/static-healthcheck/blob/master/LICENSE) 6 | [![Docker Automated buil](https://img.shields.io/badge/docker--hub-automatic--build-blue.svg)](https://hub.docker.com/r/wtfcoderz/static-healthcheck/) 7 | 8 | This project was created to add healthcheck in docker images without installing curl or other tools 9 | This is useful for statically compiled images `FROM scratch` for example 10 | 11 | ## Standalone Usage 12 | 13 | ``` 14 | Usage of /healthcheck: 15 | -http value 16 | httpcheck: ex: my.domain.com:80 17 | -tcp value 18 | tcpcheck: ex: my.domain.com:80 19 | ``` 20 | 21 | These flags accept multiple values : 22 | `/healthcheck -tcp my.domain.com:80 -http my2.domain.com:80 -http 127.0.0.1:8080` 23 | 24 | -http do a GET of the provided URI : return OK if response code < 400 25 | -tcp verify that the privided host:port respond 26 | 27 | Return 0 if all checks are OK 28 | Return 1 otherwise 29 | 30 | ## Dockerfile Usage 31 | 32 | In fact, you only need two lines in your existing Dockerfile to add this 33 | ``` 34 | COPY --from=wtfcoderz/static-healthcheck /healthcheck / 35 | HEALTHCHECK --interval=5s --timeout=2s --start-period=1s --retries=2 CMD ["/healthcheck", "-tcp", "127.0.0.1:80"] 36 | ``` 37 | 38 | Dockerfile example for Caddy server with a Healthcheck 39 | ``` 40 | FROM alpine:latest as build 41 | RUN apk add --no-cache curl \ 42 | && curl --insecure --silent --show-error --fail --location \ 43 | --header "Accept: application/tar+gzip, application/x-gzip, application/octet-stream" -o - \ 44 | "https://caddyserver.com/download/linux/amd64?plugins=http.git,http.cgi" \ 45 | | tar --no-same-owner -xz caddy \ 46 | && chmod 0755 /caddy \ 47 | && /caddy -version 48 | 49 | FROM scratch 50 | COPY --from=build /caddy /caddy 51 | COPY --from=wtfcoderz/static-healthcheck /healthcheck / 52 | HEALTHCHECK --interval=5s --timeout=2s --start-period=1s --retries=2 CMD ["/healthcheck", "-tcp", "127.0.0.1:2015"] 53 | COPY Caddyfile /etc/Caddyfile 54 | CMD ["/caddy", "-conf","/etc/Caddyfile","--log","stdout"] 55 | ``` 56 | 57 | 58 | -------------------------------------------------------------------------------- /codefresh.yml: -------------------------------------------------------------------------------- 1 | version: '1.0' 2 | 3 | steps: 4 | 5 | build: 6 | type: build 7 | image_name: ${{CF_REPO_OWNER}}/${{CF_REPO_NAME}} 8 | tag: ${{CF_BRANCH_TAG_NORMALIZED}} 9 | 10 | push: 11 | type: push 12 | candidate: ${{build}} 13 | registry: dockerhub 14 | tag: ${{CF_BRANCH_TAG_NORMALIZED}} 15 | 16 | push-latest: 17 | type: push 18 | candidate: ${{build}} 19 | registry: dockerhub 20 | tag: latest 21 | when: 22 | branch: 23 | only: 24 | - master 25 | -------------------------------------------------------------------------------- /healthcheck.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "net" 7 | "net/http" 8 | "os" 9 | ) 10 | 11 | type arrayFlags []string 12 | 13 | func (i *arrayFlags) String() string { 14 | return "my string representation" 15 | } 16 | 17 | func (i *arrayFlags) Set(value string) error { 18 | *i = append(*i, value) 19 | return nil 20 | } 21 | 22 | var tcpFlags arrayFlags 23 | var httpFlags arrayFlags 24 | 25 | func main() { 26 | flag.Var(&tcpFlags, "tcp", "tcpcheck: ex: my.domain.com:80") 27 | flag.Var(&httpFlags, "http", "httpcheck: ex: my.domain.com:80") 28 | flag.Parse() 29 | 30 | for _, tcpAddr := range tcpFlags { 31 | conn, err := net.Dial("tcp", tcpAddr) 32 | if err != nil { 33 | log.Println("Connection error:", err) 34 | log.Println(tcpAddr, ": Unreachable") 35 | os.Exit(1) 36 | } else { 37 | defer conn.Close() 38 | log.Println(tcpAddr, ": Online") 39 | } 40 | } 41 | 42 | for _, httpURL := range httpFlags { 43 | resp, err := http.Get(httpURL) 44 | if err != nil { 45 | // handle error 46 | log.Println("Connection error:", err) 47 | log.Println(httpURL, ": Unreachable") 48 | os.Exit(1) 49 | } else { 50 | if resp.StatusCode < 400 { 51 | log.Println(httpURL, ": Online - response:", resp.StatusCode) 52 | } else { 53 | log.Println(httpURL, ": Error - response:", resp.StatusCode) 54 | os.Exit(1) 55 | } 56 | } 57 | } 58 | 59 | } 60 | --------------------------------------------------------------------------------