diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 20876f6..59e6723 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,9 +1,10 @@ -name: Build Docker Image +name: Build Docker Images on: push: branches: - main + - bucket-tester-app-image env: REGISTRY: ghcr.io DOCKER_USERNAME: fczuardi @@ -14,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Login to Docker Hub uses: docker/login-action@v3 @@ -25,14 +26,35 @@ jobs: - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha - - name: Build and push Docker image + - name: Build and push (main) Docker image uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + + - name: Pre-build webapp + run: | + wget --quiet https://github.com/belitre/gotpl/releases/download/v0.7/gotpl-v0.7-linux-amd64.zip && unzip gotpl-v0.7-linux-amd64.zip + linux-amd64/gotpl -f example.config.yaml src/templates/nginx.conf --output . + + - name: Build and push (webapp) Docker image + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: . + push: true + file: webapp.Dockerfile + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:webapp + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:webapp-${{github.sha}} diff --git a/.gitignore b/.gitignore index 6911429..aac405f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ config.yaml results venv mgc +nginx.conf +!src/templates/nginx.conf diff --git a/Dockerfile b/Dockerfile index 3743f71..ee14d7b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,6 +35,7 @@ WORKDIR /app COPY src /app/src COPY vendor /app/vendor COPY justfile /app/justfile +COPY run_tests.sh /app/run_tests.sh COPY requirements.txt /app/requirements.txt COPY LICENSE /app/LICENSE RUN pip install --no-cache-dir --no-dependencies -r requirements.txt diff --git a/README.md b/README.md index 6c8bf2f..330f8d5 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,15 @@ vim config.yaml #edit with your remotes ``` ### Run tests using a pre-made docker image -**object-storage-tests** is available as two -[docker images](https://hub.docker.com/r/fczuardi/object-storage-tests): - - tag `latest` is the [main tests runner][Dockerfile] with its commands and +**object-storage-tests** is available as three +[docker images](https://github.com/marmotitude/object-storage-tests/pkgs/container/object-storage-tests/versions?filters%5Bversion_type%5D=tagged): + - tag `main` is the [main tests runner][Dockerfile] with its commands and requirements. - tag `devshell` is a [shell for developers][devshell.Dockerfile] to use it interactively and make contributions. + - tag `webapp` is the same as main with a [webserver exposed on port 5000][webapp.Dockerfile] that +serves html reports from the `results` folder, this image have a `run_tests.sh` script that updates +the folder with a new report. If you are on a machine with podman installed, you can use `just run ` to execute a command from within the main test runner image (tag latest). For example: diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..e933ddc --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,42 @@ +#!/bin/sh +set +x + +# Check if CONFIG_YAML_CONTENT is present +if [ -z "$CONFIG_YAML_CONTENT" ]; then + echo "Error: CONFIG_YAML_CONTENT environment variable is not set." + printf ":-(\n\nError: CONFIG_YAML_CONTENT environment variable is not set. $(date)" > /app/results/smile.txt + exit 1 +fi + +# create config.yaml file from env var +echo "$CONFIG_YAML_CONTENT" > /app/config.yaml + +# create ~/.aws config files +just _setup + +# space separated list of remote names +remotes=$(dasel -f /app/config.yaml -r yaml -s '.remotes.all().key()' | grep -v '\-second$' | tr '\n' ' ') + +echo "Remotes: $remotes" +date +for remote in $remotes; do + echo "Executing AWS CLI command for profile $remote" + echo "$remote" >> /app/results/smile.txt + aws s3api list-buckets --cli-connect-timeout 2 --cli-read-timeout 2 --profile "$remote" >> /app/results/smile.txt +done + +# # run a small test (delete-objects) on all remotes to assure that the reports are being written in the correct place +# just group-test delete-objects _all "$remotes" + +# asure that results folder is readable +chmod -R 755 results + +# Create a cron job that runs three times a day +echo "50 0,8,16 * * * /app/run_tests.sh" > /etc/crontabs/root + +# Start cron +crond -f & + +# Start Nginx +exec nginx -g 'daemon off;' + diff --git a/example.config.yaml b/example.config.yaml index a7cb6c2..e20f350 100644 --- a/example.config.yaml +++ b/example.config.yaml @@ -1,4 +1,4 @@ -current_remote: "example-mgc" +# credentials and endpoints for multiple object storage providers remotes: aws-east-1: s3: @@ -32,3 +32,15 @@ remotes: region: us-east-1 access_key: "test:tester" secret_key: "testing" + +# TODO: mgc tool supports only one remote / profile, this config will be deprecated in the future +current_remote: "example-mgc" + +# config for a webserver to expose results in a website, see webapp.Dockerfile +webapp: + nginx: + listen: 5000 + server_name: localhost + locations: + "/": + root: "/app/results" diff --git a/justfile b/justfile index dff6fcf..d435ba6 100644 --- a/justfile +++ b/justfile @@ -3,8 +3,10 @@ date := `date +%Y%m%d-%H%M%S` config_file := env_var_or_default("CONFIG_PATH", "./") + "config.yaml" results_prefix := "results" -rclone_conf_exists := path_exists(env_var("HOME") + "/.config/rclone") -aws_conf_exists := path_exists(env_var("HOME") + "/.aws") +rclone_conf_path := env_var("HOME") + "/.config/rclone" +rclone_conf_exists := path_exists(rclone_conf_path) +aws_conf_path := env_var("HOME") + "/.aws" +aws_conf_exists := path_exists(aws_conf_path) distroboxrc_cm := `dasel -f ~/.distroboxrc \ -s container_manager -r toml 2>/dev/null || true` fallback_cm := if distroboxrc_cm == "" { "podman" } else { distroboxrc_cm } @@ -17,6 +19,7 @@ k6_iterations := "1" # OCI main_image := "ghcr.io/marmotitude/object-storage-tests:main" +webapp_image := "ghcr.io/marmotitude/object-storage-tests:webapp" devshell_image := "docker.io/fczuardi/object-storage-tests:devshell" distrobox_name := "devshell-obj" @@ -128,6 +131,16 @@ build builder=oci_manager: build-dev builder=oci_manager: {{builder}} build --rm -t {{devshell_image}} -f ./devshell.Dockerfile . +# Build webapp image. +build-webapp builder=oci_manager: + # write ./nginx.conf to be copied by Dockerfile + gotpl -f config.yaml src/templates/nginx.conf --output . + {{builder}} build --no-cache --rm -t {{webapp_image}} -f ./webapp.Dockerfile . + rm ./nginx.conf + +# Launch webapp container +run-webapp: + docker run -e CONFIG_YAML_CONTENT="$(cat ./small-config.yaml)" -p 5000:5000 {{webapp_image}} # Private recipes #---------------- @@ -153,9 +166,9 @@ _setup-aws: {{ if aws_conf_exists == "false" { "just __setup-aws" } else { "" } }} __setup-aws: @echo "writing ~/.aws…" - @mkdir -p ~/.aws - gotpl src/templates/aws/config -f {{config_file}} -o ~/.aws - gotpl src/templates/aws/credentials -f {{config_file}} -o ~/.aws + @mkdir -p {{aws_conf_path}} + gotpl src/templates/aws/config -f {{config_file}} -o {{aws_conf_path}} + gotpl src/templates/aws/credentials -f {{config_file}} -o {{aws_conf_path}} _setup-mgc: @echo "writing ~/.config/mgc…" diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..e8a9339 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,17 @@ +#!/bin/sh +set +x + +# cron job starts on /root and is run by user root +cd /app + +touch results/debug_last_test_start + +# space separated list of remote names +remotes=$(dasel -f /app/config.yaml -r yaml -s '.remotes.all().key()' | grep -v '\-second$' | tr '\n' ' ') + +just group-test index-s3 _all "$remotes" > results/debug_last_test.txt 2>&1 + +# asure that results folder is readable +chmod -R 755 results + +printf ":-)\n\nLast test ended on $(date)" > results/smile.txt diff --git a/src/k6/index-s3.js b/src/k6/index-s3.js index d299a22..52d4a34 100644 --- a/src/k6/index-s3.js +++ b/src/k6/index-s3.js @@ -99,6 +99,7 @@ export default function ({ mgcData, rcloneData, awsCliCreateBucketData, + awsCliDeleteObjectsData, }) { describe("Run k6-jslib-aws buckets test", async (_t) => { await k6JsLibBucketsTest(k6JsLibBucketsData); @@ -137,7 +138,8 @@ export function teardown({ boto3Data, mgcData, rcloneData, - awsCliCreateBucketData + awsCliCreateBucketData, + awsCliDeleteObjectsData }) { describe("Teardown k6-jslib-aws objects test", (_t) => { k6JsLibObjectsTeardown(k6JsLibObjectsData); diff --git a/src/templates/nginx.conf b/src/templates/nginx.conf new file mode 100644 index 0000000..811f718 --- /dev/null +++ b/src/templates/nginx.conf @@ -0,0 +1,26 @@ +# Set number of worker processes automatically based on number of CPU cores. +worker_processes auto; + +events { + # The maximum number of simultaneous connections that can be opened by + # a worker process. + worker_connections 1024; +} + +http { + charset utf-8; + + server { + listen {{ .webapp.nginx.listen }}; + server_name {{ .webapp.nginx.server_name }}; + + {{- range $location_name, $location := .webapp.nginx.locations}} + location {{ $location_name }} { + root {{ $location.root }}; + index index.html; + autoindex on; + autoindex_localtime on; + } + {{- end}} + } +} diff --git a/webapp.Dockerfile b/webapp.Dockerfile new file mode 100644 index 0000000..0788039 --- /dev/null +++ b/webapp.Dockerfile @@ -0,0 +1,47 @@ +# Use main image as base +FROM ghcr.io/marmotitude/object-storage-tests:main + +# Install Nginx and cron +RUN apk update && \ + apk add --no-cache nginx dcron tzdata + +# Set the timezone to America/Sao_Paulo +RUN ln -sf /usr/share/zoneinfo/America/Sao_Paulo /etc/localtime && \ + echo "America/Sao_Paulo" > /etc/timezone + +# Keep a backup of alpine's default nginx config +RUN mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.orig + +# Copy our custom Nginx configuration +COPY nginx.conf /etc/nginx/nginx.conf + +# Set the working directory to /app +WORKDIR /app + +# Create the directory for the results +RUN mkdir -p results + +# Set ownership and permissions for Nginx to read from /app/results +RUN chown -R root:root results && \ + chmod -R 755 results + +# Write dummy file to results directory with a smile and timestamp +RUN printf ":-)\n\nBuilt on $(date) by:$(hostname)" > results/smile.txt + +# Copy the shell script that runs desired tests outputing to results folder +# this script will be executed by an external scheduler periodically +COPY run_tests.sh /app/run_tests.sh + +# Expose port 5000 +EXPOSE 5000 + +COPY justfile /app/justfile + +# Copy the entrypoint script +COPY entrypoint.sh /app/entrypoint.sh + +# Make the script executable +RUN chmod +x /app/entrypoint.sh + +# Set the ENTRYPOINT to the script +ENTRYPOINT ["/app/entrypoint.sh"]