From cf072888e7821fe9bcb68fb56b02283fc749546f Mon Sep 17 00:00:00 2001 From: Erick Daniszewski Date: Wed, 10 Jul 2019 18:16:34 -0400 Subject: [PATCH] deprecate the --kube-disable flag (#117) --- README.md | 101 ++---------------------- docs/conf.py | 3 +- docs/source/kubetest.rst | 2 + docs/usage.rst | 6 +- docs/writing_tests.rst | 2 +- kubetest/__init__.py | 2 +- kubetest/plugin.py | 164 ++++++++++++++++++--------------------- tox.ini | 2 +- 8 files changed, 92 insertions(+), 190 deletions(-) diff --git a/README.md b/README.md index 476158a..aa1fdfe 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ pod or deployment and inspect the aftermath). For more information, see the [kubetest documentation][kubetest-docs]. ## Installation + This plugin can be installed with `pip` ``` @@ -41,109 +42,19 @@ recommended to only install `kubetest` in a virtual environment or other managed such as a CI pipeline, where you can assure that cluster access and configuration are available. -## Usage -Once installed, the following `pytest` command-line parameters become available: - -``` -pytest \ - [--kube-config ] \ - [--kube-error-log-lines ] \ - [--kube-log-level ] \ - [--kube-disable] -``` - -- **`--kube-config`**: The path to the config file to use for your cluster. If not specified, - it defaults to the same config that `kubectl` uses at `~/.kube/config` -- **`--kube-error-log-lines`**: Set the number of lines to tail from the container logs for - a test namespace when a test fails. By default this is set to 50. If you want to show all - container logs, set this to -1. If you do not wish to show container logs, set this to 0. -- **`--kube-log-level`**: Set the logging level for kubetest. The default log level is *warning*. - Setting the log level to *info* will provide logging for kubetest actions. Setting the log level - to *debug* will log out the Kubernetes object state for various actions as well. -- **`--kube-disable`**: Disable kubetest from running initial cluster configuration. - -Note that kubetest expects a cluster to be available and requires some form of configuration -in order to interface with that cluster. - -## Pytest Integration -Below, a brief overview is given for the various components of kubetest exposed via pytest. -For more detailed information, see the [kubetest documentation][kubetest-docs]. - -### Fixtures - -#### [`kube`](https://kubetest.readthedocs.io/en/latest/fixtures.html#kube) -The `kube` fixture is the "entrypoint" into using kubetest. It provides a basic API for -managing your cluster. - -```python -def test_deployment(kube): - """Example test case for creating and deleting a deployment.""" - - d = kube.load_deployment('path/to/deployment.yaml') - - d.create() - d.wait_until_ready(timeout=10) - - # test some deployment state - - d.delete() - d.wait_until_deleted(timeout=10) -``` - -The above example shows a simplified test case using kubetest to load a deployment -from file, create it on the cluster, wait until it is in the ready state, delete the -deployment, and then wait until it is deleted. - -The two final steps - `delete` and `wait_until_deleted` can be useful when testing -a failure scenario, but does not need to be specified at the end of a test case as -a means of cluster cleanup. Because each test will run in its own namespace, once the -test completes, the namespace will be deleted from the cluster, which will in turn -delete all objects in that namespace, cleaning out all test artifacts. - -### Markers -To see all markers, run `pytest --kube-disable --markers` with kubetest installed. -This will list the kubetest-provided markers along with a detailed description of -what they do. - -#### [`applymanifests`](https://kubetest.readthedocs.io/en/latest/markers.html#apply-manifests) -Allows you to specify manifest directories or files that should be used for the test -case. This will automatically load the manifest and create the API object on the cluster. - -*Example:* -```python -@pytest.mark.applymanifests('manifests') -def test_something(kube): - ... -``` +## Documentation -#### [`clusterrolebinding`](https://kubetest.readthedocs.io/en/latest/markers.html#cluster-role-binding) -Allows you to specify different [cluster roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) -that should be applied to the cluster for the test case. - -*Example:* -```python -@pytest.mark.clusterrolebinding('cluster-admin') -def test_something(kube): - ... -``` - -#### [`rolebinding`](https://kubetest.readthedocs.io/en/latest/markers.html#role-binding) -Allows you to specify different [roles](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) -that should be applied to the cluster for the test namespace of the test case. - -*Example:* -```python -@pytest.mark.rolebinding('custom-role') -def test_something(kube): - ... -``` +See the [kubetest documentation page][kubetest-docs] for details on command line usage, +available fixtures and markers, and general pytest integration. ## Feedback + Feedback for kubetest is greatly appreciated! If you experience any issues, find the documentation unclear, have feature requests, or just have questions about it, we'd love to know. Feel free to open an issue for any feedback you may have. ## License + kubetest is released under the [GPL-3.0](LICENSE) license. diff --git a/docs/conf.py b/docs/conf.py index f5d4d17..c826021 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -12,6 +12,7 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # +from kubetest import __version__ import os import sys import datetime @@ -19,8 +20,6 @@ sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('.')) -from kubetest import __version__ - # -- Project information ----------------------------------------------------- diff --git a/docs/source/kubetest.rst b/docs/source/kubetest.rst index 593ac62..a0b3de2 100644 --- a/docs/source/kubetest.rst +++ b/docs/source/kubetest.rst @@ -8,6 +8,7 @@ kubetest.client module ---------------------- .. automodule:: kubetest.client + :noindex: :members: :undoc-members: :show-inheritance: @@ -32,6 +33,7 @@ kubetest.objects module ----------------------- .. automodule:: kubetest.objects + :noindex: :members: :undoc-members: :show-inheritance: diff --git a/docs/usage.rst b/docs/usage.rst index 5289b32..cf8ec12 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -25,7 +25,11 @@ Once installed (see :ref:`installation`), the following pytest command-line opti Specifies the context to use in the kubeconfig. If not specified, it will use the current context, as set in the kubeconfig. -- ``--kube-disable`` +- ``--kube-disable`` **DEPRECATED** + + .. note:: + *v0.2.0*: This flag no longer does anything. It will be removed in the next + major release. Disable kubetest from running. This can be useful when running pytest when no backing cluster is needed (e.g. to view the registered markers via ``pytest --markers``). diff --git a/docs/writing_tests.rst b/docs/writing_tests.rst index 9a9aab4..0c06f64 100644 --- a/docs/writing_tests.rst +++ b/docs/writing_tests.rst @@ -17,7 +17,7 @@ kubetest. If you wish to specify a different config file, you can pass it in via the ``--kube-config`` flag. See :ref:`command_line_usage` for more details. -You can also write a ``kubeconfig`` fixture_ that provides the path to the +You can also write a ``kubeconfig`` fixture which provides the path to the config path. This may be useful in case your cluster is generated as part of the tests. diff --git a/kubetest/__init__.py b/kubetest/__init__.py index 86faec1..9270c88 100644 --- a/kubetest/__init__.py +++ b/kubetest/__init__.py @@ -1,7 +1,7 @@ """kubetest -- a Kubernetes integration test framework in Python.""" __title__ = 'kubetest' -__version__ = '0.1.2' +__version__ = '0.2.0' __description__ = 'A Kubernetes integration test framework in Python.' __author__ = 'Vapor IO' __author_email__ = 'vapor@vapor.io' diff --git a/kubetest/plugin.py b/kubetest/plugin.py index d638ac1..5a8a4eb 100644 --- a/kubetest/plugin.py +++ b/kubetest/plugin.py @@ -5,8 +5,8 @@ the state of the cluster. """ -# import os import logging +import warnings import kubernetes import pytest @@ -55,7 +55,7 @@ def pytest_addoption(parser): '--kube-disable', action='store_true', default=False, - help='disable automatic configuration with the kubeconfig file' + help='[DEPRECATED] disable automatic configuration with the kubeconfig file' ) # FIXME (etd) - this was an attempt to fix occasional permissions errors @@ -99,20 +99,18 @@ def pytest_report_header(config): See Also: https://docs.pytest.org/en/latest/reference.html#_pytest.hookspec.pytest_report_header """ - disabled = config.getoption('kube_disable') - if not disabled: - config_file = config.getoption('kube_config') - if config_file is None: - config_file = 'default' + config_file = config.getoption('kube_config') + if config_file is None: + config_file = 'default' - context = config.getoption('kube_context') - if context is None: - context = 'current context' + context = config.getoption('kube_context') + if context is None: + context = 'current context' - return [ - 'kubetest config file: {}'.format(config_file), - 'kubetest context: {}'.format(context), - ] + return [ + 'kubetest config file: {}'.format(config_file), + 'kubetest context: {}'.format(context), + ] def pytest_configure(config): @@ -155,20 +153,28 @@ def pytest_sessionstart(session): logger = logging.getLogger('kubetest') logger.setLevel(level) - # Configure kubetest with the kubernetes config, if not disabled. - disabled = session.config.getoption('kube_disable') - if not disabled: - # # Set the GOOGLE_APPLICATION_CREDENTIALS environment variable. For more, see: - # # https://cloud.google.com/docs/authentication/production - # gke_creds = config.getoption('google_application_credentials') - # if gke_creds is not None: - # # If application credentials already exist, store them so they can be - # # reset after testing. - # old_creds = os.environ.get(GOOGLE_APPLICATION_CREDENTIALS) - # if old_creds: - # os.environ['OLD_' + GOOGLE_APPLICATION_CREDENTIALS] = old_creds - # os.environ[GOOGLE_APPLICATION_CREDENTIALS] = gke_creds - pass + # Check for configuration deprecations + if session.config.getoption('kube_disable'): + warnings.warn( + '--kube-disable flag is deprecated (v0.2.0) and is no longer functional. ' + 'To disable the plugin for a project, see: ' + 'https://docs.pytest.org/en/latest/plugins.html', + ) + + # # Configure kubetest with the kubernetes config, if not disabled. + # disabled = session.config.getoption('kube_disable') + # if not disabled: + # # # Set the GOOGLE_APPLICATION_CREDENTIALS environment variable. For more, see: + # # # https://cloud.google.com/docs/authentication/production + # # gke_creds = config.getoption('google_application_credentials') + # # if gke_creds is not None: + # # # If application credentials already exist, store them so they can be + # # # reset after testing. + # # old_creds = os.environ.get(GOOGLE_APPLICATION_CREDENTIALS) + # # if old_creds: + # # os.environ['OLD_' + GOOGLE_APPLICATION_CREDENTIALS] = old_creds + # # os.environ[GOOGLE_APPLICATION_CREDENTIALS] = gke_creds + # pass # def pytest_sessionfinish(session): @@ -203,37 +209,30 @@ def pytest_runtest_setup(item): test_name=item.name, ) - # FIXME (etd) - not sure this is really what we want to do. does it make sense - # to entirely disable the plugin just be specifying the disable flag? probably.. - # but there must be a better way than adding this check (perhaps unregistering the - # plugin in the pytest_configure hook?) - disabled = item.config.getoption('kube_disable') - if not disabled: - - # Note: These markers are not applied right now, meaning that the resource(s) - # which they reference are not added to the cluster yet. They are just - # registered with the test case so they can be applied to the cluster on - # test case setup. - # - # At this point, the config is not loaded, so there is nothing that could be - # added to the cluster. It is safe to skip the teardown (which cleans up the - # test namespace) since nothing could be added to the namespace yet. - try: - # Register test case state based on markers on the test case. - test_case.register_rolebindings( - *markers.rolebindings_from_marker(item, test_case.ns) - ) - test_case.register_clusterrolebindings( - *markers.clusterrolebindings_from_marker(item, test_case.ns) - ) + # Note: These markers are not applied right now, meaning that the resource(s) + # which they reference are not added to the cluster yet. They are just + # registered with the test case so they can be applied to the cluster on + # test case setup. + # + # At this point, the config is not loaded, so there is nothing that could be + # added to the cluster. It is safe to skip the teardown (which cleans up the + # test namespace) since nothing could be added to the namespace yet. + try: + # Register test case state based on markers on the test case. + test_case.register_rolebindings( + *markers.rolebindings_from_marker(item, test_case.ns) + ) + test_case.register_clusterrolebindings( + *markers.clusterrolebindings_from_marker(item, test_case.ns) + ) - # Apply manifests for the test case, if any are specified. - markers.apply_manifests_from_marker(item, test_case) - markers.apply_manifest_from_marker(item, test_case) + # Apply manifests for the test case, if any are specified. + markers.apply_manifests_from_marker(item, test_case) + markers.apply_manifest_from_marker(item, test_case) - except Exception as e: - test_case._pt_setup_failed = True - raise e + except Exception as e: + test_case._pt_setup_failed = True + raise e def pytest_runtest_teardown(item): @@ -242,9 +241,7 @@ def pytest_runtest_teardown(item): See Also: https://docs.pytest.org/en/latest/reference.html#_pytest.hookspec.pytest_runtest_teardown """ - disabled = item.config.getoption('kube_disable') - if not disabled: - manager.teardown(item.nodeid) + manager.teardown(item.nodeid) def pytest_runtest_makereport(item, call): @@ -255,23 +252,21 @@ def pytest_runtest_makereport(item, call): See Also: https://docs.pytest.org/en/latest/reference.html#_pytest.hookspec.pytest_runtest_makereport """ - disabled = item.config.getoption('kube_disable') - if not disabled: - if call.when == 'call': - if call.excinfo is not None: - tail_lines = item.config.getoption('kube_error_log_lines') - if tail_lines != 0: - test_case = manager.get_test(item.nodeid) - logs = test_case.yield_container_logs( - tail_lines=tail_lines + if call.when == 'call': + if call.excinfo is not None: + tail_lines = item.config.getoption('kube_error_log_lines') + if tail_lines != 0: + test_case = manager.get_test(item.nodeid) + logs = test_case.yield_container_logs( + tail_lines=tail_lines + ) + for container_log in logs: + # Add a report section to the test output + item.add_report_section( + when=call.when, + key='kubernetes container logs', + content=container_log ) - for container_log in logs: - # Add a report section to the test output - item.add_report_section( - when=call.when, - key='kubernetes container logs', - content=container_log - ) def pytest_keyboard_interrupt(): @@ -331,21 +326,12 @@ def kubeconfig(request): def kube(kubeconfig, request): """Return a client for managing a Kubernetes cluster for testing.""" - # FIXME (etd): This is a temporary patch which will cause tests to fail when - # --kube-disable is set. The semantics around kube-disable are not clean and - # need to be revisited. Until then, just cause failures and log the error - # so it is actually clear why test setup/run/teardown isn't working correctly. - # • https://github.com/vapor-ware/kubetest/issues/110 - # • https://github.com/vapor-ware/kubetest/issues/111 - if request.session.config.getoption('kube_disable'): - log.error('Unable to get kubetest client, --kube-disable flag is set') - return None - test_case = manager.get_test(request.node.nodeid) if test_case is None: - logging.getLogger('kubetest').warning( - 'No kubetest test client found for test using the "kube" fixture. ' - '(are you running with the --kube-disable flag?)' + log.error( + 'No kubetest client found for test using the "kube" fixture. ({})'.format( + request.node.nodeid, + ) ) return None diff --git a/tox.ini b/tox.ini index 8654d05..b1fad8b 100644 --- a/tox.ini +++ b/tox.ini @@ -17,7 +17,7 @@ commands= ; loaded prior to running coverage against it. this means that ; lines that were hit were not marked as hit, since they were hit ; before the tests were started (e.g. on plugin import). - coverage run --parallel --source kubetest -m pytest -s --kube-disable {posargs:tests} + coverage run --parallel --source kubetest -m pytest -p no:kubetest -s {posargs:tests} coverage combine coverage html coverage report