diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index aac9d46..e8c5912 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -56,7 +56,7 @@ jobs: GOOS: ${{ matrix.os }} GOARCH: ${{ matrix.arch }} CGO_ENABLED: 0 - LDFLAGS: -s -w -X gh.tarampamp.am/error-pages/internal/appmeta.version=${{ steps.slug.outputs.branch-name-slug }} + LDFLAGS: -s -w -X gh.tarampamp.am/error-pages/internal/appmeta.version=${{ steps.slug.outputs.commit-hash-short }} run: go build -trimpath -ldflags "$LDFLAGS" -o ./error-pages ./cmd/error-pages/ - {if: matrix.os == 'linux', run: ./error-pages --version && ./error-pages -h} - uses: actions/upload-artifact@v4 @@ -66,117 +66,40 @@ jobs: if-no-files-found: error retention-days: 1 -# generate: -# name: Run templates generator -# runs-on: ubuntu-latest -# needs: [build] -# steps: -# - uses: actions/checkout@v4 -# -# - uses: actions/download-artifact@v4 -# with: -# name: error-pages-linux-amd64 -# path: .artifact -# -# - name: Prepare binary file to run -# working-directory: .artifact -# run: mv ./error-pages ./../error-pages && chmod +x ./../error-pages -# -# - name: Run generator -# run: ./error-pages --verbose build --index ./out -# -# - name: Test files creation -# run: | -# test -f ./out/index.html -# test -f ./out/ghost/404.html -# test -f ./out/l7-dark/404.html -# test -f ./out/l7-light/404.html -# test -f ./out/shuffle/404.html -# test -f ./out/noise/404.html -# test -f ./out/hacker-terminal/404.html -# test -f ./out/cats/404.html -# test -f ./out/lost-in-space/404.html -# test -f ./out/app-down/404.html -# test -f ./out/connection/404.html -# test -f ./out/matrix/404.html -# test -f ./out/orient/404.html -# -# docker-image: -# name: Build docker image -# runs-on: ubuntu-latest -# needs: [golangci-lint, go-test, validate-config-file] -# steps: -# - uses: actions/checkout@v4 -# -# - {uses: gacts/github-slug@v1, id: slug} -# -# - uses: docker/build-push-action@v5 # Action page: -# with: -# context: . -# file: Dockerfile -# push: false -# build-args: "APP_VERSION=${{ steps.slug.outputs.branch-name-slug }}@${{ steps.slug.outputs.commit-hash-short }}" -# tags: app:ci -# -# - run: docker save app:ci > ./docker-image.tar -# -# - uses: actions/upload-artifact@v4 -# with: -# name: docker-image -# path: ./docker-image.tar -# retention-days: 1 -# -# scan-docker-image: -# name: Scan the docker image -# runs-on: ubuntu-latest -# needs: [docker-image] -# steps: -# - uses: actions/checkout@v4 # is needed for `upload-sarif` action -# -# - uses: actions/download-artifact@v4 -# with: -# name: docker-image -# path: .artifact -# -# - uses: aquasecurity/trivy-action@0.21.0 # action page: -# with: -# input: .artifact/docker-image.tar -# format: sarif -# severity: MEDIUM,HIGH,CRITICAL -# exit-code: 1 -# output: trivy-results.sarif -# -# - uses: github/codeql-action/upload-sarif@v3 -# if: always() -# continue-on-error: true -# with: {sarif_file: trivy-results.sarif} -# -# poke-docker-image: -# name: Run the docker image -# runs-on: ubuntu-latest -# needs: [docker-image] -# timeout-minutes: 2 -# steps: -# - uses: actions/checkout@v4 -# -# - uses: actions/download-artifact@v4 -# with: -# name: docker-image -# path: .artifact -# -# - working-directory: .artifact -# run: docker load < docker-image.tar -# -# - uses: gacts/install-hurl@v1 -# -# - name: Run container with the app -# run: docker run --rm -d -p "8080:8080/tcp" -e "SHOW_DETAILS=true" -e "PROXY_HTTP_HEADERS=X-Foo,Bar,Baz_blah" --name app app:ci -# -# - name: Wait for container "healthy" state -# run: until [[ "`docker inspect -f {{.State.Health.Status}} app`" == "healthy" ]]; do echo "wait 1 sec.."; sleep 1; done -# -# - run: hurl --color --test --fail-at-end --variable host=127.0.0.1 --variable port=8080 ./test/hurl/*.hurl -# -# - name: Stop the container -# if: always() -# run: docker kill app + generate: + name: Run the templates generator + runs-on: ubuntu-latest + needs: [build] + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: {name: error-pages-linux-amd64, path: .artifact} + - working-directory: .artifact + run: mv ./error-pages ./../error-pages && chmod +x ./../error-pages + - run: mkdir ./out && ./error-pages --log-level=debug build --index --target-dir ./out + - run: | + test -f ./out/index.html + test -f ./out/ghost/404.html + test -f ./out/l7-dark/404.html + test -f ./out/l7-light/404.html + test -f ./out/shuffle/404.html + test -f ./out/noise/404.html + test -f ./out/hacker-terminal/404.html + test -f ./out/cats/404.html + test -f ./out/lost-in-space/404.html + test -f ./out/app-down/404.html + test -f ./out/connection/404.html + test -f ./out/orient/404.html + + docker-image: + name: Build the docker image + runs-on: ubuntu-latest + needs: [golangci-lint, go-test] + steps: + - uses: actions/checkout@v4 + - {uses: gacts/github-slug@v1, id: slug} + - uses: docker/build-push-action@v5 + with: {push: false, build-args: "APP_VERSION=${{ steps.slug.outputs.commit-hash-short }}", tags: app:ci} + - run: docker save app:ci > ./docker-image.tar + - uses: actions/upload-artifact@v4 + with: {name: docker-image, path: ./docker-image.tar, retention-days: 1} diff --git a/Dockerfile b/Dockerfile index 4a72cfe..eb86bb9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,10 +55,11 @@ COPY --from=compile /tmp/error-pages ./bin/error-pages WORKDIR /tmp/rootfs/opt -## generate static error pages (for usage inside another docker images, for example) -#RUN set -x \ -# && ./../bin/error-pages --verbose build --config-file ./error-pages.yml --index ./html \ -# && ls -l ./html +# generate static error pages (for use inside other Docker images, for example) +RUN set -x \ + && mkdir ./html \ + && ./../bin/error-pages build --index --target-dir ./html \ + && ls -l ./html # -✂- and this is the final stage (an empty filesystem is used) ------------------------------------------------------- FROM scratch AS runtime @@ -83,13 +84,8 @@ USER 10001:10001 WORKDIR /opt -#ENV LISTEN_PORT="8080" \ -# TEMPLATE_NAME="ghost" \ -# DEFAULT_ERROR_PAGE="404" \ -# DEFAULT_HTTP_CODE="404" \ -# SHOW_DETAILS="false" \ -# DISABLE_L10N="false" \ -# READ_BUFFER_SIZE="2048" +# to find out which environment variables and CLI arguments are supported by the application, run the app +# with the `--help` flag or refer to the documentation at https://github.com/tarampampam/error-pages#readme # docs: https://docs.docker.com/reference/dockerfile/#healthcheck HEALTHCHECK --interval=10s --start-interval=1s --start-period=5s --timeout=2s CMD [\ diff --git a/Makefile b/Makefile index efb6791..69ac3bd 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,3 @@ lint: ## Run linters .PHONY: gen gen: ## Generate code docker compose run $(DC_RUN_ARGS) develop go generate ./... - -hurl: ## Run integration tests using hurl - docker compose run $(DC_RUN_ARGS) hurl --color --test --fail-at-end --variable host=web --variable port=8080 ./test/hurl/*.hurl diff --git a/README.md b/README.md index 5b67b85..3aa049d 100644 --- a/README.md +++ b/README.md @@ -27,23 +27,23 @@ $ error-pages [GLOBAL FLAGS] serve [COMMAND FLAGS] [ARGUMENTS...] The following flags are supported: -| Name | Description | Default value | Environment variables | -|--------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------:|:---------------------------:| -| `--listen="…"` (`-l`) | the HTTP server will listen on this IP (v4 or v6) address (set 127.0.0.1 for localhost, 0.0.0.0 to listen on all interfaces, or specify a custom IP) | `0.0.0.0` | `LISTEN_ADDR` | -| `--port="…"` (`-p`) | the TPC port number for the HTTP server to listen on (0-65535) | `8080` | `LISTEN_PORT` | -| `--add-template="…"` | to add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* | -| `--disable-template="…"` | disable the specified template by its name (can be found useful to disable the builtit templates and use only custom ones) | `[]` | *none* | -| `--add-http-code="…"` (`--add-code`) | to add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes, unless a more specific code was described previously) | `map[]` | *none* | -| `--json-format="…"` | override the default error page response in JSON format (Go templates are supported; the error page will use this template if the client requests JSON content type) | | `RESPONSE_JSON_FORMAT` | -| `--xml-format="…"` | override the default error page response in XML format (Go templates are supported; the error page will use this template if the client requests XML content type) | | `RESPONSE_XML_FORMAT` | -| `--plaintext-format="…"` | override the default error page response in plain text format (Go templates are supported; the error page will use this template if the client requests PlainText content type or does not specify any) | | `RESPONSE_PLAINTEXT_FORMAT` | -| `--template-name="…"` (`-t`) | name of the template to use for rendering error pages (builtin templates: app-down, cats, connection, ghost, hacker-terminal, l7, lost-in-space, noise, orient, shuffle) | `app-down` | `TEMPLATE_NAME` | -| `--disable-l10n` | disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` | -| `--default-error-page="…"` | the code of the default (index page, when a code is not specified) error page to render | `404` | `DEFAULT_ERROR_PAGE` | -| `--send-same-http-code` | the HTTP response should have the same status code as the requested error page (by default, every response with an error page will have a status code of 200) | `false` | `SEND_SAME_HTTP_CODE` | -| `--show-details` | show request details in the error page response (if supported by the template) | `false` | `SHOW_DETAILS` | -| `--proxy-headers="…"` | listed here HTTP headers will be proxied from the original request to the error page response (comma-separated list) | `X-Request-Id,X-Trace-Id,X-Amzn-Trace-Id` | `PROXY_HTTP_HEADERS` | -| `--rotation-mode="…"` | templates automatic rotation mode (disabled/random-on-startup/random-on-each-request/random-hourly/random-daily) | `disabled` | `TEMPLATES_ROTATION_MODE` | +| Name | Description | Default value | Environment variables | +|--------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------:|:---------------------------:| +| `--listen="…"` (`-l`) | the HTTP server will listen on this IP (v4 or v6) address (set 127.0.0.1 for localhost, 0.0.0.0 to listen on all interfaces, or specify a custom IP) | `0.0.0.0` | `LISTEN_ADDR` | +| `--port="…"` (`-p`) | the TCP port number for the HTTP server to listen on (0-65535) | `8080` | `LISTEN_PORT` | +| `--add-template="…"` | to add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* | +| `--disable-template="…"` | disable the specified template by its name (useful to disable the built-in templates and use only custom ones) | `[]` | *none* | +| `--add-http-code="…"` (`--add-code`) | to add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes unless a more specific code is described previously) | `map[]` | *none* | +| `--json-format="…"` | override the default error page response in JSON format (Go templates are supported; the error page will use this template if the client requests JSON content type) | | `RESPONSE_JSON_FORMAT` | +| `--xml-format="…"` | override the default error page response in XML format (Go templates are supported; the error page will use this template if the client requests XML content type) | | `RESPONSE_XML_FORMAT` | +| `--plaintext-format="…"` | override the default error page response in plain text format (Go templates are supported; the error page will use this template if the client requests plain text content type or does not specify any) | | `RESPONSE_PLAINTEXT_FORMAT` | +| `--template-name="…"` (`-t`) | name of the template to use for rendering error pages (built-in templates: app-down, cats, connection, ghost, hacker-terminal, l7, lost-in-space, noise, orient, shuffle) | `app-down` | `TEMPLATE_NAME` | +| `--disable-l10n` | disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` | +| `--default-error-page="…"` | the code of the default (index page, when a code is not specified) error page to render | `404` | `DEFAULT_ERROR_PAGE` | +| `--send-same-http-code` | the HTTP response should have the same status code as the requested error page (by default, every response with an error page will have a status code of 200) | `false` | `SEND_SAME_HTTP_CODE` | +| `--show-details` | show request details in the error page response (if supported by the template) | `false` | `SHOW_DETAILS` | +| `--proxy-headers="…"` | HTTP headers listed here will be proxied from the original request to the error page response (comma-separated list) | `X-Request-Id,X-Trace-Id,X-Amzn-Trace-Id` | `PROXY_HTTP_HEADERS` | +| `--rotation-mode="…"` | templates automatic rotation mode (disabled/random-on-startup/random-on-each-request/random-hourly/random-daily) | `disabled` | `TEMPLATES_ROTATION_MODE` | ### `build` command (aliases: `b`) @@ -57,14 +57,14 @@ $ error-pages [GLOBAL FLAGS] build [COMMAND FLAGS] [ARGUMENTS...] The following flags are supported: -| Name | Description | Default value | Environment variables | -|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------:|:---------------------:| -| `--add-template="…"` | to add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* | -| `--disable-template="…"` | disable the specified template by its name (can be found useful to disable the builtit templates and use only custom ones) | `[]` | *none* | -| `--add-http-code="…"` (`--add-code`) | to add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes, unless a more specific code was described previously) | `map[]` | *none* | -| `--disable-l10n` | disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` | -| `--index` (`-i`) | generate index.html file with links to all error pages | `false` | *none* | -| `--target-dir="…"` (`--out`, `--dir`, `-o`) | directory to put the built error pages into | `.` | *none* | +| Name | Description | Default value | Environment variables | +|---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------:|:---------------------:| +| `--add-template="…"` | to add a new template, provide the path to the file using this flag (the filename without the extension will be used as the template name) | `[]` | *none* | +| `--disable-template="…"` | disable the specified template by its name (useful to disable the built-in templates and use only custom ones) | `[]` | *none* | +| `--add-http-code="…"` (`--add-code`) | to add a new HTTP status code, provide the code and its message/description using this flag (the format should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, for example, '4**' will cover all 4xx codes unless a more specific code is described previously) | `map[]` | *none* | +| `--disable-l10n` | disable localization of error pages (if the template supports localization) | `false` | `DISABLE_L10N` | +| `--index` (`-i`) | generate index.html file with links to all error pages | `false` | *none* | +| `--target-dir="…"` (`--out`, `--dir`, `-o`) | directory to put the built error pages into | `.` | *none* | ### `healthcheck` command (aliases: `chk`, `health`, `check`) @@ -78,9 +78,9 @@ $ error-pages [GLOBAL FLAGS] healthcheck [COMMAND FLAGS] [ARGUMENTS...] The following flags are supported: -| Name | Description | Default value | Environment variables | -|---------------------|-----------------|:-------------:|:---------------------:| -| `--port="…"` (`-p`) | TCP port number | `8080` | `LISTEN_PORT` | +| Name | Description | Default value | Environment variables | +|---------------------|-----------------------------------------------|:-------------:|:---------------------:| +| `--port="…"` (`-p`) | TCP port number with the HTTP server to check | `8080` | `LISTEN_PORT` | diff --git a/docker-compose.yml b/docker-compose.yml index dede363..b7753c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,12 +15,5 @@ services: watch: [{action: rebuild, path: .}] security_opt: [no-new-privileges:true] - hurl: - image: ghcr.io/orange-opensource/hurl:4.3.0 - volumes: [.:/src:ro] - working_dir: /src - depends_on: {web: {condition: service_healthy}} - security_opt: [no-new-privileges:true] - volumes: tmp-data: {} diff --git a/internal/cli/healthcheck/command.go b/internal/cli/healthcheck/command.go index 8501286..3d5aae4 100644 --- a/internal/cli/healthcheck/command.go +++ b/internal/cli/healthcheck/command.go @@ -18,6 +18,8 @@ type checker interface { func NewCommand(_ *logger.Logger, checker checker) *cli.Command { var portFlag = shared.ListenPortFlag + portFlag.Usage = "TCP port number with the HTTP server to check" + return &cli.Command{ Name: "healthcheck", Aliases: []string{"chk", "health", "check"}, diff --git a/internal/cli/serve/command.go b/internal/cli/serve/command.go index fbf33e0..18a3c13 100644 --- a/internal/cli/serve/command.go +++ b/internal/cli/serve/command.go @@ -45,31 +45,35 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy addCodeFlag = shared.AddHTTPCodesFlag disableL10nFlag = shared.DisableL10nFlag jsonFormatFlag = cli.StringFlag{ - Name: "json-format", - Usage: "override the default error page response in JSON format (Go templates are supported; the error page will use this template if the client requests JSON content type)", + Name: "json-format", + Usage: "override the default error page response in JSON format (Go templates are supported; the error " + + "page will use this template if the client requests JSON content type)", Sources: env("RESPONSE_JSON_FORMAT"), OnlyOnce: true, Config: trim, } xmlFormatFlag = cli.StringFlag{ - Name: "xml-format", - Usage: "override the default error page response in XML format (Go templates are supported; the error page will use this template if the client requests XML content type)", + Name: "xml-format", + Usage: "override the default error page response in XML format (Go templates are supported; the error " + + "page will use this template if the client requests XML content type)", Sources: env("RESPONSE_XML_FORMAT"), OnlyOnce: true, Config: trim, } plainTextFormatFlag = cli.StringFlag{ - Name: "plaintext-format", - Usage: "override the default error page response in plain text format (Go templates are supported; the error page will use this template if the client requests PlainText content type or does not specify any)", + Name: "plaintext-format", + Usage: "override the default error page response in plain text format (Go templates are supported; the " + + "error page will use this template if the client requests plain text content type or does not specify any)", Sources: env("RESPONSE_PLAINTEXT_FORMAT"), OnlyOnce: true, Config: trim, } templateNameFlag = cli.StringFlag{ - Name: "template-name", - Aliases: []string{"t"}, - Value: cfg.TemplateName, - Usage: "name of the template to use for rendering error pages (builtin templates: " + strings.Join(cfg.Templates.Names(), ", ") + ")", + Name: "template-name", + Aliases: []string{"t"}, + Value: cfg.TemplateName, + Usage: "name of the template to use for rendering error pages (built-in templates: " + + strings.Join(cfg.Templates.Names(), ", ") + ")", Sources: env("TEMPLATE_NAME"), OnlyOnce: true, Config: trim, @@ -105,7 +109,7 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy } proxyHeadersListFlag = cli.StringFlag{ Name: "proxy-headers", - Usage: "listed here HTTP headers will be proxied from the original request to the error page response " + + Usage: "HTTP headers listed here will be proxied from the original request to the error page response " + "(comma-separated list)", Value: strings.Join(cfg.ProxyHeaders, ","), Sources: env("PROXY_HTTP_HEADERS"), @@ -138,8 +142,10 @@ func NewCommand(log *logger.Logger) *cli.Command { //nolint:funlen,gocognit,gocy } ) - addrFlag.Usage = "the HTTP server will listen on this IP (v4 or v6) address (set 127.0.0.1 for localhost, 0.0.0.0 to listen on all interfaces, or specify a custom IP)" - portFlag.Usage = "the TPC port number for the HTTP server to listen on (0-65535)" + // override some flag usage messages + addrFlag.Usage = "the HTTP server will listen on this IP (v4 or v6) address (set 127.0.0.1 for localhost, " + + "0.0.0.0 to listen on all interfaces, or specify a custom IP)" + portFlag.Usage = "the TCP port number for the HTTP server to listen on (0-65535)" disableL10nFlag.Value = cfg.L10n.Disable // set the default value depending on the configuration diff --git a/internal/cli/shared/flags.go b/internal/cli/shared/flags.go index 9aa03ee..e69fc9c 100644 --- a/internal/cli/shared/flags.go +++ b/internal/cli/shared/flags.go @@ -73,7 +73,7 @@ var AddTemplatesFlag = cli.StringSliceFlag{ var DisableTemplateNamesFlag = cli.StringSliceFlag{ Name: "disable-template", - Usage: "disable the specified template by its name (can be found useful to disable the builtit templates and use only custom ones)", + Usage: "disable the specified template by its name (useful to disable the built-in templates and use only custom ones)", Config: cli.StringConfig{TrimSpace: true}, } @@ -81,8 +81,8 @@ var AddHTTPCodesFlag = cli.StringMapFlag{ Name: "add-http-code", Aliases: []string{"add-code"}, Usage: "to add a new HTTP status code, provide the code and its message/description using this flag (the format " + - "should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at once, " + - "for example, '4**' will cover all 4xx codes, unless a more specific code was described previously)", + "should be '%code%=%message%/%description%'; the code may contain a wildcard '*' to cover multiple codes at " + + "once, for example, '4**' will cover all 4xx codes unless a more specific code is described previously)", Config: cli.StringConfig{TrimSpace: true}, Validator: func(codes map[string]string) error { for code, msgAndDesc := range codes {