Skip to content

Commit

Permalink
[tests] use shellspec
Browse files Browse the repository at this point in the history
  • Loading branch information
shizunge committed Jan 31, 2024
1 parent 4661978 commit fcd8d8d
Show file tree
Hide file tree
Showing 25 changed files with 1,946 additions and 1,631 deletions.
70 changes: 21 additions & 49 deletions .github/workflows/on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,58 +29,30 @@ jobs:
fail-fast: false
matrix:
test_suit:
- test_new_image_
- test_multiple_services_
- test_SERVICES_
- test_jobs_
- test_MANIFEST_
- test_no_running_tasks_
- test_rollback_
- test_options_
- test_CLEANUP_IMAGES_
- test_login_
- gantry_cleanup_images_spec.sh
- gantry_entrypoint.sh
- gantry_job_spec.sh
- gantry_login_spec.sh
- gantry_manifest_spec.sh
- gantry_multiple_services_spec.sh
- gantry_no_running_tasks_spec.sh
- gantry_options_spec.sh
- gantry_rollback_spec.sh
- gantry_simple_spec.sh
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/[email protected]
- name: Login to GitHub Container Registry
uses: docker/[email protected]
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}
- name: Install shellspec
run: |
mkdir -p ~/shellspec
cd ~/shellspec
git clone https://github.com/shellspec/shellspec.git
ln -s ~/shellspec/shellspec/shellspec /usr/local/bin/shellspec
echo -n "shellspec version: "
shellspec --version
- name: Checkout Code
uses: actions/checkout@v4
- name: Run tests
run: |
export GANTRY_TEST_ENABLE_TESTS="${{ matrix.test_suit }}"
./tests/run_all_tests.sh "${{ github.repository }}-test" "ghcr.io" "${{ github.repository_owner }}" "${{ github.token }}"
bash shellspec --pattern tests/${{ matrix.test_suit }}
clean-ghcr-tests:
name: Delete old tests container images
runs-on: ubuntu-latest
needs:
- tests
steps:
- name: Delete old test images
uses: snok/container-retention-policy@v2
with:
image-names: "${{ github.repository }}-test"
# leave some margins in case multiple actions run in parallel
cut-off: 5 minutes ago UTC
account-type: personal
token: ${{ secrets.TOKEN_DELETE_GHCR_IMAGES }}
keep-at-least: 0
skip-tags: latest, development
filter-tags: "for-test-*"
dry-run: False
- name: Delete untagged images
uses: snok/container-retention-policy@v2
with:
image-names: "${{ github.repository }}-test"
cut-off: 1 second ago UTC
account-type: personal
token: ${{ secrets.TOKEN_DELETE_GHCR_IMAGES }}
keep-at-least: 0
untagged-only: True
skip-tags: latest, development
dry-run: False
79 changes: 21 additions & 58 deletions .github/workflows/on-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,32 @@ jobs:
fail-fast: false
matrix:
test_suit:
- test_new_image_
- test_multiple_services_
- test_SERVICES_
- test_jobs_
- test_MANIFEST_
- test_no_running_tasks_
- test_rollback_
- test_options_
- test_CLEANUP_IMAGES_
- test_login_
- gantry_cleanup_images_spec.sh
- gantry_entrypoint.sh
- gantry_job_spec.sh
- gantry_login_spec.sh
- gantry_manifest_spec.sh
- gantry_multiple_services_spec.sh
- gantry_no_running_tasks_spec.sh
- gantry_options_spec.sh
- gantry_rollback_spec.sh
- gantry_simple_spec.sh
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/[email protected]
- name: Login to GitHub Container Registry
uses: docker/[email protected]
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}
- name: Install shellspec
run: |
mkdir -p ~/shellspec
cd ~/shellspec
git clone https://github.com/shellspec/shellspec.git
ln -s ~/shellspec/shellspec/shellspec /usr/local/bin/shellspec
echo -n "shellspec version: "
shellspec --version
- name: Checkout Code
uses: actions/checkout@v4
- name: Run tests
run: |
export GANTRY_TEST_ENABLE_TESTS="${{ matrix.test_suit }}"
./tests/run_all_tests.sh "${{ github.repository }}-test" "ghcr.io" "${{ github.repository_owner }}" "${{ github.token }}"
bash shellspec --pattern tests/${{ matrix.test_suit }}
build_and_push:
name: Build and push Docker image
Expand Down Expand Up @@ -106,42 +107,4 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
provenance: false
# - name: Post push tests
# run: |
# echo "Start running tests";
# TAG=$(echo "${{ steps.meta.outputs.tags }}" | grep "ghcr.io" | grep "dev-" | tr '\n' ' ' | cut -f1 -d ' ')
# echo "Using tag ${TAG}";
# export GANTRY_TEST_CONTAINER_REPO_TAG="${TAG}";
# export GANTRY_TEST_ENABLE_TESTS="test_new_image_";
# ./tests/run_all_tests.sh "${{ github.repository }}-test" "ghcr.io" "${{ github.repository_owner }}" "${{ github.token }}";
# echo "Done running tests";

clean-ghcr-tests:
name: Delete old tests container images
runs-on: ubuntu-latest
needs:
- tests
steps:
- name: Delete old test images
uses: snok/container-retention-policy@v2
with:
image-names: "${{ github.repository }}-test"
# leave some margins in case multiple actions run in parallel
cut-off: 5 minutes ago UTC
account-type: personal
token: ${{ secrets.TOKEN_DELETE_GHCR_IMAGES }}
keep-at-least: 0
skip-tags: latest, development
filter-tags: "for-test-*"
dry-run: False
- name: Delete untagged images
uses: snok/container-retention-policy@v2
with:
image-names: "${{ github.repository }}-test"
cut-off: 1 second ago UTC
account-type: personal
token: ${{ secrets.TOKEN_DELETE_GHCR_IMAGES }}
keep-at-least: 0
untagged-only: True
skip-tags: latest, development
dry-run: False
79 changes: 21 additions & 58 deletions .github/workflows/on-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,32 @@ jobs:
fail-fast: false
matrix:
test_suit:
- test_new_image_
- test_multiple_services_
- test_SERVICES_
- test_jobs_
- test_MANIFEST_
- test_no_running_tasks_
- test_rollback_
- test_options_
- test_CLEANUP_IMAGES_
- test_login_
- gantry_cleanup_images_spec.sh
- gantry_entrypoint.sh
- gantry_job_spec.sh
- gantry_login_spec.sh
- gantry_manifest_spec.sh
- gantry_multiple_services_spec.sh
- gantry_no_running_tasks_spec.sh
- gantry_options_spec.sh
- gantry_rollback_spec.sh
- gantry_simple_spec.sh
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/[email protected]
- name: Login to GitHub Container Registry
uses: docker/[email protected]
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}
- name: Install shellspec
run: |
mkdir -p ~/shellspec
cd ~/shellspec
git clone https://github.com/shellspec/shellspec.git
ln -s ~/shellspec/shellspec/shellspec /usr/local/bin/shellspec
echo -n "shellspec version: "
shellspec --version
- name: Checkout Code
uses: actions/checkout@v4
- name: Run tests
run: |
export GANTRY_TEST_ENABLE_TESTS="${{ matrix.test_suit }}"
./tests/run_all_tests.sh "${{ github.repository }}-test" "ghcr.io" "${{ github.repository_owner }}" "${{ github.token }}"
bash shellspec --pattern tests/${{ matrix.test_suit }}
build_and_push:
name: Build and push Docker image
Expand Down Expand Up @@ -99,42 +100,4 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
provenance: false
# - name: Post push tests
# run: |
# echo "Start running tests";
# TAG=$(echo "${{ steps.meta.outputs.tags }}" | grep "ghcr.io" | tr '\n' ' ' | cut -f1 -d ' ')
# echo "Using tag ${TAG}";
# export GANTRY_TEST_CONTAINER_REPO_TAG="${TAG}";
# export GANTRY_TEST_ENABLE_TESTS="test_new_image_";
# ./tests/run_all_tests.sh "${{ github.repository }}-test" "ghcr.io" "${{ github.repository_owner }}" "${{ github.token }}";
# echo "Done running tests";

clean-ghcr-tests:
name: Delete old tests container images
runs-on: ubuntu-latest
needs:
- tests
steps:
- name: Delete old test images
uses: snok/container-retention-policy@v2
with:
image-names: "${{ github.repository }}-test"
# leave some margins in case multiple actions run in parallel
cut-off: 5 minutes ago UTC
account-type: personal
token: ${{ secrets.TOKEN_DELETE_GHCR_IMAGES }}
keep-at-least: 0
skip-tags: latest, development
filter-tags: "for-test-*"
dry-run: False
- name: Delete untagged images
uses: snok/container-retention-policy@v2
with:
image-names: "${{ github.repository }}-test"
cut-off: 1 second ago UTC
account-type: personal
token: ${{ secrets.TOKEN_DELETE_GHCR_IMAGES }}
keep-at-least: 0
untagged-only: True
skip-tags: latest, development
dry-run: False
10 changes: 10 additions & 0 deletions .shellspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--helperdir tests
--require spec_helper
--require spec_gantry_test_helper
--default-path tests

## kcov (coverage) options
--kcov-options "--include-path=src --path-strip-level=1"
--kcov-options "--include-pattern=.sh"
# --kcov-options "--exclude-pattern=/.shellspec,/spec/,/coverage/,/report/,/tests,/.shellcheckrc"

41 changes: 10 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Gantry - Docker service updater

[*Gantry*](https://github.com/shizunge/gantry) is a tool to update docker swarm services, [enhanced Shepherd](docs/migration.md).
[*Gantry*](https://github.com/shizunge/gantry) updates docker swarm services to newer images.

## Usage

*Gantry* automatically runs `docker service update` command to update selected services to newer images with the same tag. It is inspired by but [enhanced Shepherd](docs/migration.md).

*Gantry* is released as a container [image](https://hub.docker.com/r/shizunge/gantry). You can create a docker service and run it on a swarm manager node.

```
Expand All @@ -30,9 +32,9 @@ You can configure the most behaviors of *Gantry* via environment variables.
|-----------------------|---------|------------|
| GANTRY_LOG_LEVEL | INFO | Control how many logs generated by *Gantry*. Valid values are `NONE`, `ERROR`, `WARN`, `INFO`, `DEBUG` (case sensitive). |
| GANTRY_NODE_NAME | | Add node name to logs. |
| GANTRY_POST_RUN_CMD | | Command(s) to eval after each updating iteration. |
| GANTRY_PRE_RUN_CMD | | Command(s) to eval before each updating iteration. |
| GANTRY_SLEEP_SECONDS | 0 | Interval between two updates. Set it to 0 to run *Gantry* once and then exit. Sleep time will exclude the time spent on updating services. |
| GANTRY_POST_RUN_CMD | | Command(s) to `eval` after each updating iteration. |
| GANTRY_PRE_RUN_CMD | | Command(s) to `eval` before each updating iteration. |
| GANTRY_SLEEP_SECONDS | 0 | Interval between two updates. Set it to 0 to run *Gantry* once and then exit. When this is a non-zero value, after an updating, *Gantry* will sleep until the next scheduled update. The actual sleep time is this value minus time spent on updating services. |
| TZ | | Set timezone for time in logs. |

*Gantry* bases on Docker command line, [environment variables](https://docs.docker.com/engine/reference/commandline/cli/#environment-variables) for Docker command line also works for *Gantry*.
Expand All @@ -56,9 +58,9 @@ You can configure the most behaviors of *Gantry* via environment variables.
| Environment Variable | Default | Description |
|-----------------------|---------|-------------|
| GANTRY_SERVICES_EXCLUDED | | A space separated list of services names that are excluded from updating. |
| GANTRY_SERVICES_EXCLUDED_FILTERS | | A space separated list of [filters](https://docs.docker.com/engine/reference/commandline/service_ls/#filter). Exclude services which match the given filters from updating. |
| GANTRY_SERVICES_FILTERS | | A space separated list of [filters](https://docs.docker.com/engine/reference/commandline/service_ls/#filter) that are accepted by `docker service ls --filter` to select services to update. |
| GANTRY_SERVICES_SELF | | This is optional. *Gantry* will try to find the service name of itself automatically, and update itself firstly. The manifest inspection will be always performed on the *Gantry* service to avoid an infinity loop of updating itself. User can use this to ask *Gantry* to update another service firstly or in case *Gantry* fails to find the service name of itself. |
| GANTRY_SERVICES_EXCLUDED_FILTERS | | A space separated list of [filters](https://docs.docker.com/engine/reference/commandline/service_ls/#filter), e.g. `label=project=project-a`. Exclude services which match the given filters from updating. Note that multiple filters will be logical **ANDED**. |
| GANTRY_SERVICES_FILTERS | | A space separated list of [filters](https://docs.docker.com/engine/reference/commandline/service_ls/#filter) that are accepted by `docker service ls --filter` to select services to update, e.g. `label=project=project-a`. Note that multiple filters will be logical **ANDED**. |
| GANTRY_SERVICES_SELF | | This is optional. When running as a docker service, *Gantry* will try to find the service name of itself automatically, and update itself firstly. The manifest inspection will be always performed on the *Gantry* service to avoid an infinity loop of updating itself. User can use this to ask *Gantry* to update another service firstly. |

### To check if new images are available

Expand Down Expand Up @@ -128,30 +130,7 @@ To run `shellcheck` locally:
shellcheck src/*.sh tests/*.sh
```

Majority of the configuration options are covered by end-to-end tests. It would be a good enhancement to generate coverage metrics that are missing today.

You can also run the tests locally. During testing, the tests will generate some temporary images, create services and run *Gantry* to update the services. The tests need to push those temporary images to a registry. Therefore you need to prepare a container repository before starting a test, and login to the registry via `docekr login`.

To test *Gantry* scripts, and run all the tests locally:
```
./tests/run_all_tests.sh <repository-name> [registry]
```

If you want to test a container image of *Gantry*, you need to specify the image of *Gantry* via the environment variable `GANTRY_TEST_CONTAINER_REPO_TAG`.
```
export GANTRY_TEST_CONTAINER_REPO_TAG=<gantry image>:<tag>
./tests/run_all_tests.sh <repository-name> [registry]
```

> NOTE: `GANTRY_TEST_CONTAINER_REPO_TAG` specifies the container image of *Gantry* under test.
> On the other hand, what is passed to the `run_all_tests.sh` as CLI arguments is the repository that holds temporary images generated during the tests.
You can select tests by populating test names to environment variable `GANTRY_TEST_ENABLE_TESTS`. The item in that space separated list could be a regexp that `grep -P` accepts. For example:
```
# Optionally set GANTRY_TEST_CONTAINER_REPO_TAG
export GANTRY_TEST_ENABLE_TESTS="test_new_image$"
./tests/run_all_tests.sh <repository-name> [registry]
```
Majority of the configuration options are covered by end-to-end tests. See [tests](./tests/README.md) folder about how to run tests.

## Contacts

Expand Down
4 changes: 2 additions & 2 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ At the end of updating, *Gantry* optionally removes the old images.

### How to filters multiple services by name?

You can set multiple filters. However filters are **ANDED**. So multiple filters on different names will not work.
It will not work by setting multiple filters with different names, because filters are logical **ANDED**.

To filter multiple services, you can set a label on each service then let *Gantry* filter on that label. Or you can run multiple *Gantry* instances.

Expand Down Expand Up @@ -48,6 +48,6 @@ You can switch back to use [`docker manifest inspect`](https://docs.docker.com/e

### I logged in my Docker Hub account, but the Docker Hub rate reported seems incorrect.

When checking Docker Hub rate, *Gantry* reads the docker hub credential only from `GANTRY_REGISTRY_PASSWORD` and `GANTRY_REGISTRY_USER`, or their `_FILE` variants. `GANTRY_REGISTRY_HOST` or its `_FILE` variant must be either empty or `docker.io`.
When checking Docker Hub rate, *Gantry* reads the Docker Hub credential only from `GANTRY_REGISTRY_PASSWORD` and `GANTRY_REGISTRY_USER`, or their `_FILE` variants. `GANTRY_REGISTRY_HOST` or its `_FILE` variant must be either empty or `docker.io`.

If you need to login to multiple registries, you can use `GANTRY_REGISTRY_CONFIGS_FILE` together with `GANTRY_REGISTRY_PASSWORD` and `GANTRY_REGISTRY_USER`. Credentials in `GANTRY_REGISTRY_CONFIGS_FILE` will be used for services updating, but they won't be used for checking Docker Hub rate. See [Authentication](../README.md#authentication) for more information.
7 changes: 5 additions & 2 deletions docs/migration.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
## Migration from shepherd

*Gantry* started to fix the following problems I found in [*shepherd*](https://github.com/containrrr/shepherd), then it became refactored and totally rewritten, with abundant tests.
*Gantry* started to fix the following problems I found in [*shepherd*](https://github.com/containrrr/shepherd), then it became refactored and totally rewritten, with [abundant tests](../tests/README.md).

* `docker manifest` CLI failed to get the image meta data for some registries.
* High usage of docker hub rate. Getting manifest and then pulling the image double the usage.
* Removing images related: Failure of removing old images will exit and block subsequent updating. `docker rmi` only works for the current host.
* Unnecessary `docker service update` commands slow down the overall process.
* Removing images related
* Failure of removing old images will exit and block subsequent updating.
* `docker rmi` only works for the current host.
* `docker service update` CLI hangs when updating services without running tasks.
* Other UX issues when running it as a script outside the provided container.

Expand Down
Loading

0 comments on commit fcd8d8d

Please sign in to comment.