Skip to content

Commit

Permalink
feat: update test suite to use pytest operator
Browse files Browse the repository at this point in the history
* refactor tests to use pytest-operator and move integration test logic from github actions to pytest
* refactor integration CI to use actions-operator to set up environment
* generalize test suite to make it more transferable to other environments/clouds
  • Loading branch information
ca-scribner committed Aug 27, 2021
1 parent 04a3d4e commit 51a184e
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 83 deletions.
123 changes: 45 additions & 78 deletions .github/workflows/integrate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,95 +12,62 @@ jobs:
steps:
- name: Check out code
uses: actions/checkout@v2

- name: Install dependencies
run: |
sudo apt-get install python3-pip tox
pip install tox
- name: Lint code
run: tox -e lint
run: tox -vve lint

unit:
name: Unit Test
runs-on: ubuntu-latest

steps:
- name: Check out code
uses: actions/checkout@v2

- name: Install dependencies
run: |
sudo apt-get install python3-pip tox
- name: Check out code
uses: actions/checkout@v2
- name: Install dependencies
run: |
pip install tox
- name: Run unit tests
run: tox -e unit
- name: Run unit tests
run: tox -vve unit

deploy:
name: Deploy Test
integration:
name: Integration Test (deploy and access)
runs-on: ubuntu-latest

steps:
- name: Check out repo
uses: actions/checkout@v2

- uses: balchua/microk8s-actions@v0.2.2
with:
addons: '["dns", "storage", "rbac"]'

- name: Install dependencies
run: |
set -eux
sudo pip3 install charmcraft==1.0.0
sudo snap install juju --classic
sudo snap install juju-wait --classic
sudo snap install yq
# Avoid race condition with storage taking a long time to initialize
- name: Wait for storage
run: |
sg microk8s -c 'microk8s kubectl rollout status deployment/hostpath-provisioner -n kube-system'
- name: Bootstrap Juju
run: |
set -eux
sg microk8s -c 'juju bootstrap microk8s uk8s'
juju add-model minio
- name: Deploy MinIO
run: |
set -eux
charmcraft build -v
juju deploy ./minio.charm \
--config secret-key=minio-secret-key \
--resource oci-image=$(yq eval '.resources.oci-image.upstream-source' metadata.yaml)
juju wait -wvt 300
- name: Test MinIO
run: |
kubectl run \
--rm \
-i \
--restart=Never \
--command sh \
--image=minio/mc \
-- \
sh -c \
"mc alias set ci http://minio.minio.svc.cluster.local:9000 minio minio-secret-key && mc mb ci/foo && mc rb ci/foo"
- name: Get all
run: kubectl get all -A
if: failure()

- name: Get juju status
run: juju status
if: failure()

- name: Get minio logs
run: kubectl logs --tail 100 -nminio -ljuju-app=minio
if: failure()

- name: Get minio operator logs
run: kubectl logs --tail 100 -nminio -ljuju-operator=minio
if: failure()

- name: Check out code
uses: actions/checkout@v2
- name: Setup operator environment
uses: charmed-kubernetes/actions-operator@main
with:
provider: microk8s

# TODO: Remove once the actions-operator does this automatically
- name: Configure kubectl
run: |
sg microk8s -c "microk8s config > ~/.kube/config"
- name: Build and test
run: |
sg microk8s -c "tox -vve integration"
# On failure, capture debugging resources
- name: Get all
run: kubectl get all -A
if: failure()

- name: Get juju status
run: juju status
if: failure()

# TODO: How can we capture this if using pytest operator? Create our own model and keep it
# after tests end?
# - name: Get minio logs
# run: kubectl logs --tail 100 -nminio -ljuju-app=minio
# if: failure()
#
# - name: Get minio operator logs
# run: kubectl logs --tail 100 -nminio -ljuju-operator=minio
# if: failure()
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
ops==1.1.0
git+https://github.com/juju-solutions/resource-oci-image@1964d748022b762b9dce6e8bb7bdf12835102c72
oci-image
serialized-data-interface<0.4
3 changes: 1 addition & 2 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
pytest
flake8
black==20.8b1
pyyaml
84 changes: 84 additions & 0 deletions tests/integration/test_charm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import logging
from pathlib import Path

import pytest
import yaml
from pytest_operator.plugin import OpsTest

log = logging.getLogger(__name__)

METADATA = yaml.safe_load(Path("./metadata.yaml").read_text())

MINIO_CONFIG = {
"access-key": "minio",
"secret-key": "minio-secret-key",
}

APP_NAME = "minio"
CHARM_ROOT = "."


@pytest.mark.abort_on_fail
async def test_build_and_deploy(ops_test: OpsTest):
built_charm_path = await ops_test.build_charm(CHARM_ROOT)
log.info(f"Built charm {built_charm_path}")

image_path = METADATA["resources"]["oci-image"]["upstream-source"]
resources = {"oci-image": image_path}

await ops_test.model.deploy(
entity_url=built_charm_path,
resources=resources,
config=MINIO_CONFIG,
)
await ops_test.model.wait_for_idle(timeout=60 * 10)


async def test_connect_client_to_server(ops_test: OpsTest):
"""
Tests a deployed MinIO app by trying to connect to it from a pod and do trivial actions with it
"""
# TODO: This presumes we're using microk8s. Fix that.
# could just call kubectl and count on the outside environment aliasing it for us?
# or could pass kubectl path in as a variable?

application = ops_test.model.applications[APP_NAME]
config = await application.get_config()
port = config["port"]["value"]
alias = "ci"
bucket = "testbucket"
# TODO: how do I dynamically retrieve the minio k8s service name?
service_name = APP_NAME
model_name = ops_test.model_name
log.info(f"ops_test.model_name = {ops_test.model_name}")

url = f"http://{service_name}.{model_name}.svc.cluster.local:{port}"

minio_cmd = (
f"mc alias set {alias} {url} {MINIO_CONFIG['access-key']} {MINIO_CONFIG['secret-key']}"
f"&& mc mb {alias}/{bucket}"
f"&& mc rb {alias}/{bucket}"
)

kubectl_cmd = (
"microk8s",
"kubectl",
"run",
"--rm",
"-i",
"--restart=Never",
"--command",
f"--namespace={ops_test.model_name}",
"minio-deployment-test",
"--image=minio/mc",
"--",
"sh",
"-c",
minio_cmd,
)

ret_code, stdout, stderr = await ops_test.run(*kubectl_cmd)

assert (
ret_code == 0
), f"Test returned code {ret_code} with stdout:\n{stdout}\nstderr:\n{stderr}"
File renamed without changes.
12 changes: 10 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,19 @@ deps =
-rtest-requirements.txt
-rrequirements.txt

[testenv:integration]
deps =
{[testenv]deps}
pytest-operator
commands = pytest -v --tb native --show-capture=no --log-cli-level=INFO -s {posargs} {toxinidir}/tests/integration

[testenv:unit]
commands =
pytest -v tests
commands = pytest -v {toxinidir}/tests/unit

[testenv:lint]
deps =
flake8
black==20.8b1
commands =
flake8 {toxinidir}/src {toxinidir}/tests
black --check {toxinidir}/src {toxinidir}/tests
Expand Down

0 comments on commit 51a184e

Please sign in to comment.