-
Notifications
You must be signed in to change notification settings - Fork 185
Integration testing via pytest-docker #315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
zachmoody
merged 27 commits into
netbox-community:master
from
raddessi:master.pytest-docker
Jan 27, 2021
Merged
Changes from 5 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
a4d8f18
Added some very basic integration testing support
raddessi 9307060
Cleaned up a little
raddessi 71b3bb9
Fixed fixture requirements
raddessi d6a1cae
Fixed another typo
raddessi e2511a5
Re-added the requests module import I mistakenly removed
raddessi 7a5c398
Use safe_load to avoid warnings re: load()
bb25ebf
Added a few features
raddessi 9817a5c
Silenced stdout messages on cleanup
raddessi c659eed
Added a little more documentation
raddessi 4ab1f2a
First stab at a device type import
raddessi bffc707
Added faker package
raddessi 0de9f18
Fixed some docs
raddessi 3097a76
Added cleanup steps for docker networks and volumes
raddessi fe074cf
Fixed name collision with faker module
raddessi 6dc84f2
Updated FIXME comment
raddessi e2e854a
Updated comments
raddessi a40a77c
Updated comments in tests
raddessi e09bfc6
Updated black version in gh workflow
raddessi 8112544
Renamed gh workflow files to match upstream
raddessi a05ce6b
Merge branch 'master' into master.pytest-docker
raddessi 25525c4
Fixed integration test
raddessi bd3358d
Removed a dead comment
raddessi 93c6b19
Reverted accidental change to gh workflow
raddessi 3a722d7
Reverted black version update, many files would be changed
raddessi fce6027
Multiple updates
raddessi f903af5
Merge branch 'master.pytest-docker' of https://github.com/raddessi/py…
raddessi 3de7129
Blacked again with the matching version
raddessi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -96,4 +96,7 @@ ENV/ | |
| /site | ||
|
|
||
| # mypy | ||
| .mypy_cache/ | ||
| .mypy_cache/ | ||
|
|
||
| # Other git repos checked out locally | ||
| .netbox-docker/ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| import os | ||
| import pytest | ||
|
|
||
| import subprocess as subp | ||
| import yaml | ||
| from packaging import version | ||
| import time | ||
|
|
||
| import pynetbox | ||
| import requests | ||
|
|
||
|
|
||
| def netbox_integration_versions(): | ||
| versions = [] | ||
| for tag in ["v2.9.11", "v2.10.2"]: | ||
| versions.append( | ||
| { | ||
| "tag": tag, | ||
| "docker_tag": tag.replace(".", "_"), | ||
| "version": version.Version(tag), | ||
| } | ||
| ) | ||
|
|
||
| return versions | ||
|
|
||
|
|
||
| @pytest.fixture(scope="session") | ||
| def git_toplevel(): | ||
| return ( | ||
| subp.check_output(["git", "rev-parse", "--show-toplevel"]) | ||
| .decode("utf-8") | ||
| .splitlines()[0] | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture(scope="session") | ||
| def netbox_docker_repo_fpath(git_toplevel): | ||
| repo_fpath = "/".join([git_toplevel, ".netbox-docker"]) | ||
| if os.path.isdir(repo_fpath): | ||
| subp.check_call( | ||
| ["git", "fetch",], cwd=repo_fpath, | ||
| ) | ||
| subp.check_call( | ||
| ["git", "pull",], cwd=repo_fpath, | ||
| ) | ||
| else: | ||
| subp.check_call( | ||
| [ | ||
| "git", | ||
| "clone", | ||
| "https://github.com/netbox-community/netbox-docker", | ||
| repo_fpath, | ||
| ] | ||
| ) | ||
|
|
||
| return repo_fpath | ||
|
|
||
|
|
||
| @pytest.fixture(scope="session") | ||
| def docker_compose_project_name(pytestconfig): | ||
| """Return a consistent project name so we can kill stale containers.""" | ||
| return "pytest_pynetbox_%s" % int(time.time()) | ||
|
|
||
|
|
||
| @pytest.fixture(scope="session") | ||
| def docker_compose_file(pytestconfig, netbox_docker_repo_fpath): | ||
| """Return paths to the compose files needed to create test containers. | ||
|
|
||
| We can create container sets for multiple versions of netbox here by returning a | ||
| list of paths to multiple compose files. | ||
| """ | ||
raddessi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| compose_files = [] | ||
| compose_source_fpath = os.path.join(netbox_docker_repo_fpath, "docker-compose.yml") | ||
|
|
||
| for netbox_integration_version in netbox_integration_versions(): | ||
| # load the compose file yaml | ||
| compose_data = yaml.load(open(compose_source_fpath, "r").read()) | ||
|
|
||
| # add the custom network for this version | ||
| compose_data["networks"] = { | ||
| "pytest_pynetbox_" | ||
| + netbox_integration_version["docker_tag"]: { | ||
| "name": "pytest_pynetbox_" + netbox_integration_version["docker_tag"], | ||
| } | ||
| } | ||
|
|
||
| # keep track of what each container's name was originally so we can add named | ||
| # links back to them from the other containers within their network | ||
| original_service_names = {} | ||
raddessi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # prepend the netbox version to each of the service names and anything else | ||
| # needed to make the continers unique to the netbox version | ||
| new_services = {} | ||
| for service_name in compose_data["services"].keys(): | ||
| new_service_name = "netbox_%s_%s" % ( | ||
| netbox_integration_version["docker_tag"], | ||
| service_name, | ||
| ) | ||
| new_services[new_service_name] = compose_data["services"][service_name] | ||
| original_service_names[new_service_name] = service_name | ||
|
|
||
| if service_name in ["netbox", "netbox-worker"]: | ||
| # set the netbox image version | ||
| new_services[new_service_name]["image"] = ( | ||
| "netboxcommunity/netbox:%s" % netbox_integration_version["tag"] | ||
| ) | ||
|
|
||
| # set the network and an alias to the proper short name of the container | ||
| # within that network | ||
| new_services[new_service_name]["networks"] = { | ||
| "pytest_pynetbox_" | ||
| + netbox_integration_version["docker_tag"]: {"aliases": [service_name]} | ||
| } | ||
|
|
||
| # fix the naming of any dependencies | ||
| if "depends_on" in new_services[new_service_name]: | ||
| new_service_dependencies = [] | ||
| for dependent_service_name in new_services[new_service_name][ | ||
| "depends_on" | ||
| ]: | ||
| new_service_dependencies.append( | ||
| "netbox_%s_%s" | ||
| % ( | ||
| netbox_integration_version["docker_tag"], | ||
| dependent_service_name, | ||
| ) | ||
| ) | ||
| new_services[new_service_name]["depends_on"] = new_service_dependencies | ||
|
|
||
| # make any internal named volumes unique to the netbox version | ||
| if "volumes" in new_services[new_service_name]: | ||
| new_volumes = [] | ||
| for volume_config in new_services[new_service_name]["volumes"]: | ||
| source = volume_config.split(":")[0] | ||
| if "/" in source: | ||
| new_volumes.append(volume_config) | ||
| else: | ||
| new_volumes.append( | ||
| "pytest_pynetbox_" | ||
| + netbox_integration_version["docker_tag"] | ||
| + "_" | ||
| + volume_config | ||
| ) | ||
| new_services[new_service_name]["volumes"] = new_volumes | ||
|
|
||
| # replace the servives config with the renamed versions | ||
| compose_data["services"] = new_services | ||
|
|
||
| # prepend local volume names | ||
| new_volumes = {} | ||
| for volume_name, volume_config in compose_data["volumes"].items(): | ||
| new_volumes[ | ||
| "pytest_pynetbox_" | ||
| + netbox_integration_version["docker_tag"] | ||
| + "_" | ||
| + volume_name | ||
| ] = volume_config | ||
| compose_data["volumes"] = new_volumes | ||
|
|
||
| compose_output_fpath = os.path.join( | ||
| netbox_docker_repo_fpath, | ||
| "docker-compose_%s.yml" % netbox_integration_version["tag"], | ||
| ) | ||
| with open(compose_output_fpath, "w") as fdesc: | ||
| fdesc.write(yaml.dump(compose_data)) | ||
|
|
||
| compose_files.append(compose_output_fpath) | ||
|
|
||
| return compose_files | ||
|
|
||
|
|
||
| def is_responsive(url): | ||
| try: | ||
| response = requests.get(url) | ||
| if response.status_code == 200: | ||
| return True | ||
| except ConnectionError: | ||
| return False | ||
|
|
||
|
|
||
| def id_netbox_service(fixture_value): | ||
| """Create and ID representation for a netbox service fixture param. | ||
|
|
||
| Returns: | ||
| str: Identifiable representation of the service, as best we can | ||
|
|
||
| """ | ||
| return "netbox %s" % fixture_value["tag"] | ||
|
|
||
|
|
||
| @pytest.fixture( | ||
| scope="session", params=netbox_integration_versions(), ids=id_netbox_service | ||
| ) | ||
| def netbox_service(docker_ip, docker_services, request): | ||
| """Ensure that HTTP service is up and responsive.""" | ||
| netbox_integration_version = request.param | ||
|
|
||
| # `port_for` takes a container port and returns the corresponding host port | ||
| port = docker_services.port_for( | ||
| "netbox_%s_nginx" % netbox_integration_version["docker_tag"], 8080 | ||
| ) | ||
| url = "http://{}:{}".format(docker_ip, port) | ||
| docker_services.wait_until_responsive( | ||
| timeout=300.0, pause=1, check=lambda: is_responsive(url) | ||
| ) | ||
| return { | ||
| "url": url, | ||
| "netbox_version": netbox_integration_version, | ||
| "api": pynetbox.api(url, token="0123456789abcdef0123456789abcdef01234567"), | ||
| } | ||
raddessi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| """Tests for the pytest-docker integration.""" | ||
|
|
||
| import pytest | ||
| import requests | ||
raddessi marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| from packaging import version | ||
|
|
||
|
|
||
| def test_netbox_version(netbox_service): | ||
| """Verify the reported version of netbox matches what we should have spun up.""" | ||
| reported_version = version.Version(netbox_service["api"].version) | ||
|
|
||
| assert reported_version.major == netbox_service["netbox_version"]["version"].major | ||
| assert reported_version.minor == netbox_service["netbox_version"]["version"].minor | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.