Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
KEINOS committed Mar 27, 2024
0 parents commit 02a0584
Show file tree
Hide file tree
Showing 12 changed files with 612 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .github/docker-compose-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
services:
imhost:
build: ..
ports:
- "8080:80"
22 changes: 22 additions & 0 deletions .github/workflows/docker-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Docker Tests
on:
workflow_dispatch:
push:
branches:
- main
pull_request:

jobs:
docker-test:
name: Run tests on latest Go version
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4

- run: docker compose -f ./.github/docker-compose-test.yml up --build -d

- run: |
curl -sS http://localhost:8080/ | grep Hello\ from\ host
- run: docker compose -f ./.github/docker-compose-test.yml down --remove-orphans
24 changes: 24 additions & 0 deletions .github/workflows/golang-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Lint Test
on:
workflow_dispatch:
push:
branches:
- main
pull_request:

jobs:
golangci-lint:
name: Run golangci-lint
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: 'stable'

# Linting and static analysis
- uses: golangci/golangci-lint-action@v4
with:
version: latest
22 changes: 22 additions & 0 deletions .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Unit Tests
on:
workflow_dispatch:
push:
branches:
- main
pull_request:

jobs:
go-test:
name: Run tests on latest Go version
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: 'stable'

# Unit tests
- run: go test ./...
27 changes: 27 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
run:
tests: true
build-tags:
- golangci
allow-parallel-runners: true

output:
sort-results: true

linters:
enable-all: true
disable:
# Disable deprecated/abandoned linters
- structcheck
- scopelint
- ifshort
- interfacer
- maligned
- exhaustivestruct
- nosnakecase
- varcheck
- deadcode
- golint
# Allow global variables
- gochecknoglobals
# Disable due to use of external linters
- depguard
35 changes: 35 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -----------------------------------------------------------------------------
# Build Stage
# -----------------------------------------------------------------------------
FROM golang:alpine AS builder

COPY . /go/src/app
ENV CGO_ENABLED 0

WORKDIR /go/src/app

RUN go mod download && \
go mod verify && \
go test -v ./...

RUN go build \
-ldflags="-s -w -extldflags \"-static\"" \
-o /go/bin/mywebapp \
/go/src/app/imhost/main.go

# -----------------------------------------------------------------------------
# Main Stage
# -----------------------------------------------------------------------------
FROM scratch

COPY --from=builder /go/bin/mywebapp /usr/bin/mywebapp
EXPOSE 80/tcp
WORKDIR /tmp
ENTRYPOINT [ "/usr/bin/mywebapp" ]

HEALTHCHECK \
--start-period=15s \
--interval=5m \
--timeout=15s \
--retries=3 \
CMD [ "/usr/bin/mywebapp", "--ping"]
24 changes: 24 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <https://unlicense.org>
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# ImHost

ImHost is a simple Docker image that simply returns the hostname of the container it is running in.

Its aim is to be used for testing and debugging purposes in a Docker orchestration environment. Such as docker-compose, docker-swarm, kubernetes, etc.

## Usage

- Build the image

```bash
docker build -t imhost https://github.com/KEINOS/imhost.git
```

- Run the container (expose the port 80 to 8088)

```bash
docker run --rm -p 8088:80 imhost
```
15 changes: 15 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module github.com/KEINOS/iamhost

go 1.22.1

require (
github.com/stretchr/testify v1.9.0
github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
14 changes: 14 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04 h1:qXafrlZL1WsJW5OokjraLLRURHiw0OzKHD/RNdspp4w=
github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04/go.mod h1:FiwNQxz6hGoNFBC4nIx+CxZhI3nne5RmIOlT/MXcSD4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
125 changes: 125 additions & 0 deletions imhost/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package main

import (
"context"
"fmt"
"log"
"net/http"
"os"
"strings"
"time"

"github.com/pkg/errors"
)

const (
listenPort = "80" // Listen port
srvTimeoutSec = 20 // Server timeout
FAILURE = 1 // Exit code for failure
SUCCESS = 0 // Exit code for success
)

// Monkey patching for testing.
var (
// osExit is a copy of os.Exit that can be mocked in tests.
osExit = os.Exit
// osHostname is a copy of os.Hostname that can be mocked in tests.
osHostname = os.Hostname
)

// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------

//nolint:forbidigo // allow fmt.Print* due to its nature
func main() {
addr := "0.0.0.0:" + listenPort
url := "http://" + addr
handler := new(defaultHandler)

// If the args contain --ping, do a health check and exit
healthCheck(url)

// Start server
fmt.Println("* Starging up server ...")
fmt.Printf("* Listening to: %s\n", url)

log.Fatal(spawnServer(addr, handler))
}

func spawnServer(addr string, handler http.Handler) error {
srv := new(http.Server)

srv.Addr = addr
srv.Handler = handler
srv.ReadHeaderTimeout = srvTimeoutSec * time.Second

return errors.Wrap(srv.ListenAndServe(), "failed to start server")
}

// ----------------------------------------------------------------------------
// Type: defaultHandler (implements http.Handler)
// ----------------------------------------------------------------------------

// defaultHandler is a "catch all" handler that responds with a message
// containing the hostname of the server.
type defaultHandler struct{}

// ServeHTTP writes a message containing the hostname of the server to the
// response writer.
// This is an implementation of the http.Handler interface.
func (h *defaultHandler) ServeHTTP(respWriter http.ResponseWriter, _ *http.Request) {
var respMsg string

status := http.StatusOK

hostname, err := osHostname()
if err != nil {
respMsg = fmt.Sprintf("failed to get hostname. error: %s", err)
status = http.StatusInternalServerError
} else {
respMsg = fmt.Sprintf("Hello from host: %s\n", hostname)
}

respWriter.WriteHeader(status)

if _, err := respWriter.Write([]byte(respMsg)); err != nil {
panic(err)
}
}

// ----------------------------------------------------------------------------
// Functions
// ----------------------------------------------------------------------------

// healthCheck exits if the --ping flag is set. Otherwise it will do nothing.
//
// If the --ping flag is set, it will:
// Make a request to the local server and check if it is running. It will exit
// with a status code of 0 if the server is running, and 1 if it is not.
func healthCheck(rawURL string) {
if !strings.Contains(strings.Join(os.Args, " "), "--ping") {
return
}

ctx, doCancel := context.WithTimeout(context.Background(), srvTimeoutSec*time.Second)

req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil)
if err != nil {
doCancel()
fmt.Fprintln(os.Stderr, err)
osExit(FAILURE)
}

resp, err := http.DefaultClient.Do(req)
if err != nil {
doCancel()
fmt.Fprintln(os.Stderr, err)
osExit(FAILURE)
}

resp.Body.Close()
doCancel()

osExit(SUCCESS)
}
Loading

0 comments on commit 02a0584

Please sign in to comment.