Skip to content

Commit

Permalink
Add docker-compose tests
Browse files Browse the repository at this point in the history
  • Loading branch information
s-westphal committed Jan 18, 2024
1 parent d82432c commit af95aba
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 45 deletions.
76 changes: 32 additions & 44 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -182,49 +182,6 @@ jobs:
path: gcs_upload_dir/
retention-days: 1

build-push-docker-image:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
runs-on: ubuntu-22.04
needs:
- build-centos
- build-ubuntu
- build-osx
- build-windows
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Download installers from GitHub artifacts
id: download
uses: actions/download-artifact@v4
with:
path: ./_artifacts
pattern: '*installer*'
- name: Login to GitHub Container registry
if: ${{ github.event_name == 'push' }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
if: ${{ github.event_name == 'push' }}
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push Docker image
if: ${{ github.event_name == 'push' }}
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

build-push-docker-base-image:
env:
REGISTRY: ghcr.io
Expand Down Expand Up @@ -276,7 +233,7 @@ jobs:
id-token: 'write'
runs-on: ubuntu-22.04
needs:
- build-push-docker
- docker-compose-test
steps:
- uses: actions/checkout@v3
- name: Download installers from GitHub artifacts
Expand Down Expand Up @@ -318,3 +275,34 @@ jobs:
destination: ${{ env.GCS_BUCKET_OPENAPI }}
# Omit `path` (e.g. /home/runner/deploy/) in final GCS path.
parent: false

docker-compose-test:
if: ${{ github.event_name == 'push' }}
permissions:
contents: 'read'
id-token: 'write'
runs-on: ubuntu-22.04
needs:
- build-push-docker-base-image
steps:
- uses: actions/checkout@v3
- name: Start docker-compose stack
shell: bash
run: |
docker-compose pull --include-deps
docker-compose up -d
- name: Test
shell: bash
run: |
docker-compose run \
-v $(pwd):/github_workspace \
-w /github_workspace \
--entrypoint appveyor/e2e_tests/run_docker_compose_e2e_test.sh \
grr-admin-ui
--
$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' grr-linux-client)
- name: Stop docker-compose stack
if: always()
shell: bash
run: |
docker-compose down --volumes
44 changes: 44 additions & 0 deletions appveyor/e2e_tests/docker_compose_client_collection_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env python3

import time

from grr_api_client import api


grrapi = api.InitHttp(api_endpoint="http://localhost:8000",
auth=("admin", "root"))

search_result = grrapi.SearchClients()

result = {}
for client in search_result:
client_id = client.client_id
client_last_seen_at = client.data.last_seen_at
result[client_id] = client_last_seen_at

assert len(result) == 1


flow_args = grrapi.types.CreateFlowArgs("FileFinder")
flow_args.ClearField("paths")
flow_args.paths.append("/client_templates/*")
flow_args.action.action_type = flow_args.action.DOWNLOAD

hunt_runner_args = grrapi.types.CreateHuntRunnerArgs()

hunt = grrapi.CreateHunt(flow_name="FileFinder", flow_args=flow_args,
hunt_runner_args=hunt_runner_args)
hunt = hunt.Start()

# Wait until results are available.
time.sleep(20)

found_files = set([f.payload.stat_entry.pathspec.path for f in hunt.ListResults()])

assert len(found_files) == 4
assert found_files == {
'/client_templates/windows-installers',
'/client_templates/osx-installers',
'/client_templates/centos-installers',
'/client_templates/ubuntu-installers'
}
43 changes: 43 additions & 0 deletions appveyor/e2e_tests/run_docker_compose_e2e_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash
#
# Runs the e2e test in the docker-compose stack.

set -ex

readonly GRR_ADMIN_PASS="root"

readonly CLIENT_IP=${1}

readonly FLAKY_TESTS_ARR=(\
TestCheckRunner.runTest \
)
# Convert array to string (comma-separated).
readonly FLAKY_TESTS="$(IFS=,;echo "${FLAKY_TESTS_ARR[*]}")"

function fatal() {
>&2 echo "Error: ${1}"
exit 1
}

# Install the grr tests
cd /usr/src/grr && pip install -e grr/test && cd -

grr_end_to_end_tests --verbose \
--api_password "${GRR_ADMIN_PASS}" \
--client_ip "${CLIENT_IP}" \
--flow_timeout_secs 240 \
--flow_results_sla_secs 60 \
--skip_tests "${FLAKY_TESTS}" \
2>&1 | tee e2e.log

if [[ ! -z "$(cat e2e.log | grep -F '[ FAIL ]')" ]]; then
fatal 'End-to-end tests failed.'
fi

if [[ -z "$(cat e2e.log | grep -F '[ PASS ]')" ]]; then
fatal "Expected to find at least one passing test in the test log. It is possible no tests actually ran."
fi




28 changes: 28 additions & 0 deletions grr/test/grr_response_test/end_to_end_tests/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,34 @@ def Retry():

return Retry()

def SearchClientByID(self, client_ip):
"""Searches for a client with a given IP via the GRR API and return its id.
Args:
client_ip: Client's IP.
Returns:
The IP of the client.
"""
start_time = time.time()

def DeadlineExceeded():
return time.time() - start_time > self._api_retry_deadline_secs

@retry.When(
requests.ConnectionError,
lambda _: not DeadlineExceeded(),
opts=retry.Opts(
attempts=sys.maxsize, # Limited by deadline.
init_delay=datetime.timedelta(seconds=self._api_retry_period_secs),
),
)
def Retry():
clients = list(self._grr_api.SearchClient(f"ip:{client_ip}"))
return clients[0].client_id if clients else None

return Retry()

def _GetApplicableTests(self, client):
"""Returns all e2e test methods that should be run against the client."""
applicable_tests = {}
Expand Down
13 changes: 12 additions & 1 deletion grr/test/grr_response_test/run_end_to_end_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
_CLIENT_ID = flags.DEFINE_string("client_id", "",
"Id for client to run tests against.")

_CLIENT_IP = flags.DEFINE_string(
"client_ip", "", "IP address of a client to run tests against."
)

_RUN_ONLY_TESTS = flags.DEFINE_list(
"run_only_tests", [],
"(Optional) comma-separated list of tests to run (skipping all others).")
Expand Down Expand Up @@ -84,7 +88,14 @@ def main(argv):
upload_test_binaries=_UPLOAD_TEST_BINARIES.value)
test_runner.Initialize()

results, _ = test_runner.RunTestsAgainstClient(_CLIENT_ID.value)
client_id = _CLIENT_ID.value
if _CLIENT_IP.value and not _CLIENT_ID.value:
client_id = test_runner.SearchClientByID(_CLIENT_IP.value)

if not client_id:
sys.exit(1)

results, _ = test_runner.RunTestsAgainstClient(client_id)
# Exit with a non-0 error code if one of the tests failed.
for r in results.values():
if r.errors or r.failures:
Expand Down

0 comments on commit af95aba

Please sign in to comment.