Skip to content

Commit

Permalink
Refactor acceptance tests with robot framework
Browse files Browse the repository at this point in the history
Signed-off-by: Josh Dolitsky <josh@dolit.ski>
  • Loading branch information
jdolitsky committed Jul 26, 2021
1 parent 6961f15 commit 6271d56
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 117 deletions.
134 changes: 17 additions & 117 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,132 +23,32 @@ jobs:
- name: Unit tests
run: |
make test
- name: Integration setup
- name: Acceptance tests
run: |
docker rm -f registry || true
docker run --rm -d -p 5000:5000 --name registry registry
- name: Test binary on existing remote image
run: |
bin/docker-credential-magician ghcr.io/oras-project/oras:v0.12.0 \
-t "localhost:5000/oras-project/oras:v0.12.0.magic"
docker run --rm localhost:5000/oras-project/oras:v0.12.0.magic -h
docker run --rm --entrypoint docker-credential-acr-env \
localhost:5000/oras-project/oras:v0.12.0.magic -h
docker run --rm --entrypoint docker-credential-ecr-login \
localhost:5000/oras-project/oras:v0.12.0.magic -v
docker run --rm --entrypoint docker-credential-gcr \
localhost:5000/oras-project/oras:v0.12.0.magic help
bin/docker-credential-magician ghcr.io/bloodorangeio/helm:hip-6 \
-t "localhost:5000/bloodorangeio/helm:hip-6.magic"
docker run --rm localhost:5000/bloodorangeio/helm:hip-6.magic -h
- name: Integration test (Google)
env:
GOOGLE_REGISTRY_ENDPOINT: ${{ secrets.GOOGLE_REGISTRY_ENDPOINT }}
GOOGLE_APPLICATION_CREDENTIALS_BASE64: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_BASE64 }}
run: |
echo "${GOOGLE_APPLICATION_CREDENTIALS_BASE64}" | base64 -d > sa.json
docker run --rm --entrypoint sh \
-v ${PWD}/testdata/helm/nginx-9.3.6.tgz:/workspace/nginx-9.3.6.tgz \
-v ${PWD}/sa.json:/sa.json \
-e GOOGLE_REGISTRY_ENDPOINT="${GOOGLE_REGISTRY_ENDPOINT}" \
-e GOOGLE_APPLICATION_CREDENTIALS=/sa.json \
localhost:5000/oras-project/oras:v0.12.0.magic -c \
'echo "{}" > config.json && \
oras push --manifest-config config.json:application/vnd.cncf.helm.config.v1+json \
${GOOGLE_REGISTRY_ENDPOINT}/docker-credential-magic/demo/nginx:9.3.6 \
nginx-9.3.6.tgz:application/tar+gzip'
rm -rf tmp/
mkdir tmp/
docker run --rm --entrypoint sh \
-v ${PWD}/tmp:/workspace \
-v ${PWD}/sa.json:/sa.json \
-e GOOGLE_REGISTRY_ENDPOINT="${GOOGLE_REGISTRY_ENDPOINT}" \
-e GOOGLE_APPLICATION_CREDENTIALS=/sa.json \
localhost:5000/oras-project/oras:v0.12.0.magic -c \
'oras pull -a \
${GOOGLE_REGISTRY_ENDPOINT}/docker-credential-magic/demo/nginx:9.3.6'
ls -la tmp/ | grep nginx-9.3.6.tgz
- name: Integration test (Amazon)
env:
AWS_REGISTRY_ENDPOINT: ${{ secrets.AWS_REGISTRY_ENDPOINT }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
docker run --rm --entrypoint sh \
-v ${PWD}/testdata/helm/nginx-9.3.6.tgz:/workspace/nginx-9.3.6.tgz \
-e AWS_REGISTRY_ENDPOINT="${AWS_REGISTRY_ENDPOINT}" \
-e AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}" \
-e AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID}" \
-e AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY}" \
localhost:5000/oras-project/oras:v0.12.0.magic -c \
'echo "{}" > config.json && \
oras push --manifest-config config.json:application/vnd.cncf.helm.config.v1+json \
${AWS_REGISTRY_ENDPOINT}/docker-credential-magic/demo/nginx:9.3.6 \
nginx-9.3.6.tgz:application/tar+gzip'
rm -rf tmp/
mkdir tmp/
docker run --rm --entrypoint sh \
-v ${PWD}/tmp:/workspace \
-e AWS_REGISTRY_ENDPOINT="${AWS_REGISTRY_ENDPOINT}" \
-e AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}" \
-e AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID}" \
-e AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY}" \
localhost:5000/oras-project/oras:v0.12.0.magic -c \
'oras pull -a \
${AWS_REGISTRY_ENDPOINT}/docker-credential-magic/demo/nginx:9.3.6'
ls -la tmp/ | grep nginx-9.3.6.tgz
- name: Integration test (Microsoft)
make acceptance
env:
AZURE_REGISTRY_ENDPOINT: ${{ secrets.AZURE_REGISTRY_ENDPOINT }}
AZURE_REGISTRY_NAMESPACE: ${{ secrets.AZURE_REGISTRY_ENDPOINT }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
run: |
mkdir -p .config/helm/
echo "{\"credsStore\": \"magic\"}" > .config/helm/registry.json
docker run --rm \
-v ${PWD}/testdata/helm/nginx-9.3.6.tgz:/nginx-9.3.6.tgz \
-v ${PWD}/.config:/.config \
-e AZURE_REGISTRY_ENDPOINT="${AZURE_REGISTRY_ENDPOINT}" \
-e AZURE_CLIENT_ID="${AZURE_CLIENT_ID}" \
-e AZURE_CLIENT_SECRET="${AZURE_CLIENT_SECRET}" \
-e AZURE_TENANT_ID="${AZURE_TENANT_ID}" \
localhost:5000/bloodorangeio/helm:hip-6.magic \
push --debug /nginx-9.3.6.tgz \
oci://${AZURE_REGISTRY_ENDPOINT}/docker-credential-magic/demo
rm -rf tmp/
mkdir tmp/
docker run --rm \
-v ${PWD}/tmp:/tmp \
-v ${PWD}/.config:/.config \
-w /tmp \
-e AZURE_REGISTRY_ENDPOINT="${AZURE_REGISTRY_ENDPOINT}" \
-e AZURE_CLIENT_ID="${AZURE_CLIENT_ID}" \
-e AZURE_CLIENT_SECRET="${AZURE_CLIENT_SECRET}" \
-e AZURE_TENANT_ID="${AZURE_TENANT_ID}" \
localhost:5000/bloodorangeio/helm:hip-6.magic \
pull --debug \
oci://${AZURE_REGISTRY_ENDPOINT}/docker-credential-magic/demo/nginx \
--version 9.3.6
ls -la tmp/ | grep nginx-9.3.6.tgz
- name: Integration test (Bundle Bar / Anonymous pull)
run: |
rm -rf tmp/
mkdir tmp/
docker run --rm --entrypoint sh \
-v ${PWD}/tmp:/tmp \
-w /tmp \
localhost:5000/oras-project/oras:v0.12.0.magic -c \
'oras pull bundle.bar/u/jdolitsky/oras/greetings:v1'
ls -la tmp/ | grep greetings.txt
- name: Integration teardown
if: always()
run: |
docker rm -f registry || true
AWS_REGISTRY_ENDPOINT: ${{ secrets.AWS_REGISTRY_ENDPOINT }}
AWS_REGISTRY_NAMESPACE: ${{ secrets.AZURE_REGISTRY_NAMESPACE }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GOOGLE_REGISTRY_ENDPOINT: ${{ secrets.GOOGLE_REGISTRY_ENDPOINT }}
GOOGLE_REGISTRY_NAMESPACE: ${{ secrets.GOOGLE_REGISTRY_NAMESPACE }}
GOOGLE_APPLICATION_CREDENTIALS_BASE64: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_BASE64 }}
- name: Upload coverage report
uses: actions/upload-artifact@v2
if: always()
with:
name: magic-coverage-report-${{ github.sha }}
path: .cover/coverage.html
- name: Upload acceptance report
uses: actions/upload-artifact@v2
if: always()
with:
name: magic-acceptance-report-${{ github.sha }}
path: .robot/
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ tmp/
.docker/
*.tar.gz
.cover/
.venv/
.robot/
acceptance/lib/__pycache__/
test.env
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ run:
test: vendor
scripts/test.sh

.PHONY: acceptance
acceptance:
scripts/acceptance.sh

.PHONY: clean
clean:
rm -rf bin/ tmp/ vendor/ credential-helpers/ \
Expand Down
33 changes: 33 additions & 0 deletions acceptance/lib/Sh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import subprocess


class Sh(object):
def run_command(self, command, detach=False, hide_debug=False):
process = subprocess.Popen(['/bin/bash', '-xc', command],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
if not detach:
stdout = process.communicate()[0].strip().decode()
self.rc = process.returncode
tmp = []
for x in stdout.split('\n'):
is_debug_line = x.startswith('+ ')
if not is_debug_line or not hide_debug:
print(x)
# Remove debug lines that start with "+ "
if not is_debug_line:
tmp.append(x)
self.stdout = '\n'.join(tmp)

def return_code_should_be(self, expected_rc):
if int(expected_rc) != self.rc:
raise AssertionError('Expected return code to be "%s" but was "%s".'
% (expected_rc, self.rc))

def should_pass(self, cmd):
self.run_command(cmd)
self.return_code_should_be(0)

def should_pass_no_output(self, cmd):
self.run_command(cmd, hide_debug=True)
self.return_code_should_be(0)
77 changes: 77 additions & 0 deletions acceptance/registry_providers.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
*** Settings ***
Documentation Tests to verify that docker-credential-magic/magician works properly
... against various commercial registry providers (Docker required).
Library String
Library OperatingSystem
Library lib/Sh.py
Suite Setup Suite Setup
Suite Teardown Suite Teardown

*** Test Cases ***
Azure Container Registry (ACR)
Cleanup envfile
Skip test or add to envfile AZURE_REGISTRY_ENDPOINT REGISTRY_ENDPOINT
Skip test or add to envfile AZURE_REGISTRY_NAMESPACE REGISTRY_NAMESPACE
Skip test or add to envfile AZURE_CLIENT_ID
Skip test or add to envfile AZURE_CLIENT_SECRET
Skip test or add to envfile AZURE_TENANT_ID
Test registry provider integration

Amazon Elastic Container Registry (ECR)
Cleanup envfile
Skip test or add to envfile AWS_REGISTRY_ENDPOINT REGISTRY_ENDPOINT
Skip test or add to envfile AWS_REGISTRY_NAMESPACE REGISTRY_NAMESPACE
Skip test or add to envfile AWS_DEFAULT_REGION
Skip test or add to envfile AWS_ACCESS_KEY_ID
Skip test or add to envfile AWS_SECRET_ACCESS_KEY
Test registry provider integration

Google Container Registry (GCR) / Google Artifact Registry (GAR)
Cleanup envfile
Skip test or add to envfile GOOGLE_REGISTRY_ENDPOINT REGISTRY_ENDPOINT
Skip test or add to envfile GOOGLE_REGISTRY_NAMESPACE REGISTRY_NAMESPACE
Skip test or add to envfile GOOGLE_APPLICATION_CREDENTIALS
Test registry provider integration

*** Keyword ***
Skip test or add to envfile
[Arguments] ${key} ${override_key}=unset
Skip If "%{${key}}" == "" Missing required env var ${key}
IF "${override_key}" != "unset"
Add to envfile "${override_key}" "%{${key}}"
ELSE
Add to envfile "${key}" "%{${key}}"
END

Add to envfile
[Arguments] ${key} ${value}
Should pass no output echo "${key}=${value}" >> test.env

Cleanup envfile
Should pass rm -f test.env || true

Test registry provider integration
Should pass docker run --rm --env-file=test.env --entrypoint %{PUSH_ENTRYPOINT} %{PUSH_MOUNT_FLAGS} %{LOCAL_IMAGE} %{PUSH_ARGS}
Should pass %{POST_PUSH_CMD}
Should pass docker run --rm --env-file=test.env --entrypoint %{PULL_ENTRYPOINT} %{PULL_MOUNT_FLAGS} %{LOCAL_IMAGE} %{PULL_ARGS}
Should pass %{VERIFY_CMD}

Start local test registry
Should pass docker rmi -f %{LOCAL_IMAGE} || true
Should pass docker rm -f %{CONTAINER_NAME} || true
Should pass docker run --rm -d -p %{CONTAINER_PORT}:5000 --name %{CONTAINER_NAME} registry

Stop local test registry
Should pass docker logs %{CONTAINER_NAME}
Should pass docker rm -f %{CONTAINER_NAME}

Mutate remote test image
Should pass docker-credential-magician %{REMOTE_IMAGE} -t %{LOCAL_IMAGE}

Suite Setup
Start local test registry
Mutate remote test image

Suite Teardown
Cleanup envfile
Stop local test registry
62 changes: 62 additions & 0 deletions scripts/acceptance.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env bash

set -ex

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $DIR/../
export PATH="${PWD}/bin:${PATH}"

PY_REQUIRES="robotframework==4.1"

# Default test settings
export REMOTE_IMAGE="${REMOTE_IMAGE:-ghcr.io/oras-project/oras:v0.12.0@sha256:c17f028ecbe9caed88a352d590fbe58abef4b91baf9157e353f7ab7b60cd6fdb}"
export CONTAINER_NAME="${CONTAINER_NAME:-docker-credential-magic-acceptance}"
export CONTAINER_PORT="${CONTAINER_PORT:-5000}"
export LOCAL_IMAGE="${LOCAL_IMAGE:-localhost:${CONTAINER_PORT}/acceptance:magic}"
export PUSH_ENTRYPOINT="${PUSH_ENTRYPOINT:-sh}"
export PUSH_MOUNT_FLAGS="${PUSH_MOUNT_FLAGS:--v ${PWD}/testdata/helm/nginx-9.3.6.tgz:/workspace/nginx-9.3.6.tgz}"
export PUSH_ARGS="${PUSH_ARGS:--c 'echo "{}" > config.json && \
oras push --manifest-config config.json:application/vnd.cncf.helm.config.v1+json \
\${REGISTRY_ENDPOINT}/\${REGISTRY_NAMESPACE}:9.3.6 \
nginx-9.3.6.tgz:application/tar+gzip'}"
export POST_PUSH_CMD="${POST_PUSH_CMD:-rm -rf tmp/ && mkdir tmp/}"
export PULL_ENTRYPOINT="${PULL_ENTRYPOINT:-sh}"
export PULL_MOUNT_FLAGS="${PULL_MOUNT_FLAGS:--v ${PWD}/tmp:/workspace}"
export PULL_ARGS="${PULL_ARGS:--c 'oras pull -a \${REGISTRY_ENDPOINT}/\${REGISTRY_NAMESPACE}:9.3.6'}"
export VERIFY_CMD="${VERIFY_CMD:-ls -la tmp/ | grep nginx-9.3.6.tgz}"

# Special case for Google: if GOOGLE_APPLICATION_CREDENTIALS_BASE64 is set,
# create sa.json and add to the mount args
set +x
if [[ "${GOOGLE_APPLICATION_CREDENTIALS_BASE64}" != "" ]]; then
echo "${GOOGLE_APPLICATION_CREDENTIALS_BASE64}" | base64 -d > sa.json
export PUSH_MOUNT_FLAGS="${PUSH_MOUNT_FLAGS} -v ${PWD}/sa.json:/sa.json"
export PULL_MOUNT_FLAGS="${PULL_MOUNT_FLAGS} -v ${PWD}/sa.json:/sa.json"
export GOOGLE_APPLICATION_CREDENTIALS="/sa.json"
fi
set -x

# Default all provider-specific env vars to empty strings so robot does not complain
set +x
export AZURE_REGISTRY_ENDPOINT="${AZURE_REGISTRY_ENDPOINT}"
export AZURE_REGISTRY_NAMESPACE="${AZURE_REGISTRY_NAMESPACE}"
export AZURE_CLIENT_ID="${AZURE_CLIENT_ID}"
export AZURE_CLIENT_SECRET="${AZURE_CLIENT_SECRET}"
export AZURE_TENANT_ID="${AZURE_TENANT_ID}"
export AWS_REGISTRY_ENDPOINT="${AWS_REGISTRY_ENDPOINT}"
export AWS_REGISTRY_NAMESPACE="${AWS_REGISTRY_NAMESPACE}"
export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}"
export AWS_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID}"
export AWS_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY}"
export GOOGLE_REGISTRY_ENDPOINT="${GOOGLE_REGISTRY_ENDPOINT}"
export GOOGLE_REGISTRY_NAMESPACE="${GOOGLE_REGISTRY_NAMESPACE}"
export GOOGLE_APPLICATION_CREDENTIALS="${GOOGLE_APPLICATION_CREDENTIALS}"
set -x

if [ ! -d .venv/ ]; then
virtualenv -p $(which python3) .venv/
.venv/bin/python .venv/bin/pip install $PY_REQUIRES
fi

rm -rf .robot/
.venv/bin/robot --outputdir=.robot/ acceptance/

0 comments on commit 6271d56

Please sign in to comment.