Migrate dtests to use pytest and python3 Patch by Michael Kjellman; Reviewed by Ariel Weisberg for CASSANDRA-14134
Project: http://git-wip-us.apache.org/repos/asf/cassandra-dtest/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra-dtest/commit/49b2dda4 Tree: http://git-wip-us.apache.org/repos/asf/cassandra-dtest/tree/49b2dda4 Diff: http://git-wip-us.apache.org/repos/asf/cassandra-dtest/diff/49b2dda4 Branch: refs/heads/master Commit: 49b2dda4e6643d2b18376d504b5fea4c0b3354a7 Parents: f4eda3a Author: Michael Kjellman <kjell...@apple.com> Authored: Thu Jan 25 15:56:18 2018 -0500 Committer: Ariel Weisberg <aweisb...@apple.com> Committed: Mon Jan 29 15:37:06 2018 -0500 ---------------------------------------------------------------------- CONTRIBUTING.md | 2 +- INSTALL.md | 134 - README.md | 224 +- auth_test.py | 323 +- batch_test.py | 130 +- bin/collect_known_failures.py | 58 - bootstrap_test.py | 193 +- cassandra-thrift/v11/Cassandra.py | 336 +- cassandra-thrift/v11/constants.py | 2 +- cassandra-thrift/v11/ttypes.py | 124 +- cdc_test.py | 137 +- cfid_test.py | 13 +- commitlog_test.py | 250 +- compaction_test.py | 198 +- compression_test.py | 50 +- concurrent_schema_changes_test.py | 118 +- configuration_test.py | 69 +- conftest.py | 489 + consistency_test.py | 191 +- consistent_bootstrap_test.py | 74 +- counter_test.py | 417 + counter_tests.py | 414 - cql_prepared_test.py | 8 +- cql_test.py | 1503 +++ cql_tests.py | 1501 --- cql_tracing_test.py | 81 +- cqlsh_tests/cqlsh_copy_tests.py | 603 +- cqlsh_tests/cqlsh_tests.py | 325 +- cqlsh_tests/cqlsh_tools.py | 3 +- delete_insert_test.py | 17 +- deletion_test.py | 20 +- disk_balance_test.py | 117 +- dtest.py | 986 +- dtest_setup.py | 498 + dtest_setup_overrides.py | 3 + global_row_key_cache_test.py | 48 +- hintedhandoff_test.py | 58 +- internode_ssl_test.py | 16 +- jmx_auth_test.py | 19 +- jmx_test.py | 158 +- json_test.py | 91 +- json_tools_test.py | 56 +- largecolumn_test.py | 30 +- materialized_views_test.py | 796 +- meta_tests/assertion_test.py | 12 +- meta_tests/utils_test/funcutils_test.py | 8 +- meta_tests/utils_test/metadata_wrapper_test.py | 49 +- metadata_test.py | 68 + metadata_tests.py | 65 - mixed_version_test.py | 28 +- multidc_putget_test.py | 8 +- native_transport_ssl_test.py | 32 +- nodetool_test.py | 107 +- offline_tools_test.py | 216 +- paging_test.py | 1408 ++- paxos_test.py | 195 + paxos_tests.py | 192 - pending_range_test.py | 26 +- plugins/assert_tools.py | 138 + plugins/dtestcollect.py | 92 - plugins/dtestconfig.py | 42 - plugins/dtesttag.py | 45 - plugins/dtestxunit.py | 348 - prepared_statements_test.py | 15 +- pushed_notifications_test.py | 174 +- putget_test.py | 52 +- pytest.ini | 5 + range_ghost_test.py | 9 +- read_failures_test.py | 37 +- read_repair_test.py | 226 +- rebuild_test.py | 116 +- repair_tests/deprecated_repair_test.py | 127 +- repair_tests/incremental_repair_test.py | 404 +- repair_tests/preview_repair_test.py | 32 +- repair_tests/repair_test.py | 380 +- replace_address_test.py | 216 +- replication_test.py | 122 +- requirements.txt | 19 +- run_dtests.py | 486 +- schema_metadata_test.py | 457 +- schema_test.py | 30 +- scrub_test.py | 85 +- secondary_indexes_test.py | 282 +- snapshot_test.py | 146 +- snitch_test.py | 41 +- sslnodetonode_test.py | 49 +- sstable_generation_loading_test.py | 114 +- sstablesplit_test.py | 48 +- sstableutil_test.py | 63 +- stress_tool_test.py | 13 +- super_column_cache_test.py | 66 +- super_counter_test.py | 39 +- system_keyspaces_test.py | 8 +- thrift_bindings/thrift010/Cassandra-remote | 425 + thrift_bindings/thrift010/Cassandra.py | 10961 ++++++++++++++++++ thrift_bindings/thrift010/__init__.py | 1 + thrift_bindings/thrift010/constants.py | 13 + thrift_bindings/thrift010/ttypes.py | 4218 +++++++ thrift_bindings/v22/Cassandra-remote | 396 - thrift_bindings/v22/Cassandra.py | 10506 ----------------- thrift_bindings/v22/__init__.py | 1 - thrift_bindings/v22/constants.py | 14 - thrift_bindings/v22/ttypes.py | 4219 ------- thrift_hsha_test.py | 51 +- thrift_test.py | 2649 +++++ thrift_tests.py | 2679 ----- token_generator_test.py | 53 +- tools/assertions.py | 100 +- tools/context.py | 4 +- tools/data.py | 42 +- tools/datahelp.py | 20 +- tools/decorators.py | 106 - tools/files.py | 5 +- tools/git.py | 14 +- tools/hacks.py | 30 +- tools/intervention.py | 8 +- tools/jmxutils.py | 32 +- tools/metadata_wrapper.py | 4 +- tools/misc.py | 56 +- tools/paging.py | 7 +- topology_test.py | 136 +- ttl_test.py | 87 +- udtencoding_test.py | 5 +- upgrade_crc_check_chance_test.py | 55 +- upgrade_internal_auth_test.py | 64 +- upgrade_tests/bootstrap_upgrade_test.py | 15 +- upgrade_tests/compatibility_flag_test.py | 42 +- upgrade_tests/cql_tests.py | 841 +- upgrade_tests/paging_test.py | 456 +- upgrade_tests/regression_test.py | 42 +- upgrade_tests/repair_test.py | 15 +- upgrade_tests/storage_engine_upgrade_test.py | 64 +- upgrade_tests/thrift_upgrade_test.py | 130 +- upgrade_tests/upgrade_base.py | 74 +- upgrade_tests/upgrade_compact_storage.py | 84 +- upgrade_tests/upgrade_manifest.py | 16 +- upgrade_tests/upgrade_schema_agreement_test.py | 68 +- upgrade_tests/upgrade_supercolumns_test.py | 67 +- upgrade_tests/upgrade_through_versions_test.py | 179 +- user_functions_test.py | 47 +- user_types_test.py | 87 +- wide_rows_test.py | 19 +- write_failures_test.py | 49 +- 143 files changed, 28963 insertions(+), 28116 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra-dtest/blob/49b2dda4/CONTRIBUTING.md ---------------------------------------------------------------------- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b228c0..6131160 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,7 @@ Description | Brief description of the test ```python -def example_test(self): +def test_example(self): """ Demonstrates the expected syntax for a test plan. Parsed by Doxygen. @jira_ticket CASSANDRA-0000 http://git-wip-us.apache.org/repos/asf/cassandra-dtest/blob/49b2dda4/INSTALL.md ---------------------------------------------------------------------- diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index b37bf60..0000000 --- a/INSTALL.md +++ /dev/null @@ -1,134 +0,0 @@ -Setup instructions for cassandra-dtest -====================================== - -These are instructions for setting up dtests on a fresh install of Ubuntu Linux 12.04 LTS. If you use something else, you'll need to adapt these for your particular situation (or better yet, append to this file with your platform's requirements and send a pull request.) - -## Prerequisite Software: -* Update software repositories: - - sudo apt-get update - -* python - - sudo apt-get install python python-setuptools python-dev python-pip - -* git - - sudo apt-get install git - -* windows - - python: https://www.python.org/downloads/ - git:https://msysgit.github.io/ - gnuwin32: http://gnuwin32.sourceforge.net/ - apache ant: https://ant.apache.org/bindownload.cgi - -## Install Oracle Java 8: -* java and misc tools: - - sudo apt-get install software-properties-common - sudo add-apt-repository ppa:webupd8team/java - sudo apt-get update - sudo apt-get install oracle-java8-installer - - Windows: http://www.oracle.com/technetwork/java/javase/downloads/index.html - -* Ensure that java is a HotSpot 1.8.x version: - - # java -version - java version "1.8.0_73" - Java(TM) SE Runtime Environment (build 1.8.0_73) - Java HotSpot(TM) 64-Bit Server VM (build 24.0-b56, mixed mode) - -* install ant - - sudo apt-get install ant - -## Create a git directory for holding several projects we'll use: - - mkdir -p ~/git/cstar - -## Install companion tools / libraries: -It's best to download the git source tree for these libraries as you -will often need to modify them in some fashion at some later point: - -* ccm: - - cd ~/git/cstar - git clone git://github.com/pcmanus/ccm.git - sudo apt-get install libyaml-dev - sudo pip install -e ccm - sudo pip install pyyaml - -* python-driver - - cd ~/git/cstar - Cassandra 2.x: - sudo pip install cassandra-driver - Cassandra 3.x (requires latest python-driver): - sudo pip install git+git://github.com/datastax/python-driver@cassandra-test # install dedicated test branch for new Cassandra features - sudo pip install --pre cassandra-driver # fallback driver for new features - For more instructions on how to install the python-driver, - see http://datastax.github.io/python-driver/installation.html - -* cql - - sudo pip install cql - -* cassandra-dtest - - cd ~/git/cstar - git clone git://github.com/apache/cassandra-dtest.git - -* nose - - sudo apt-get install python-nose - -* flaky - - sudo pip install flaky - -* cassandra - - cd ~/git/cstar - git clone http://git-wip-us.apache.org/repos/asf/cassandra.git - cd cassandra - ant clean jar - - Optionally, you can self-check cassandra at this point by running - it's unit tests: - - ant test - - Note: you may need to install ant-optional to get junit working: - - sudo apt-get install ant-optional - -## Setup and run dtests -* Install current python dependencies: - - sudo pip install decorator - -* Set CASSANDRA_DIR environment variable. - Set the variable in your ~/.bashrc file once so that you don't have to keep setting it everytime you run dtests: - - export CASSANDRA_DIR=~/git/cstar/cassandra - -* Run the full dtest suite (takes multiple hours, depending on your hardware): - - cd ~/git/cstar/cassandra-dtest - nosetests - -* Run the full dtest suite, retrying tests decorated with `flaky` (see [the `flaky` plugin](https://github.com/box/flaky) for more documentation): - - cd ~/git/cstar/cassandra-dtest - nosetests --with-flaky - -* Run a single dtest, printing debug info, stopping at the first error encountered (if any): - - cd ~/git/cstar/cassandra-dtest - PRINT_DEBUG=true nosetests -x -s -v putget_test.py - -* Some tests will not run with vnodes enabled (you'll see a "SKIP: Test disabled for vnodes" message in that case). Use the provided runner script instead: - - ./run_dtests.py --vnodes false --nose-options "-x -s -v" topology_test.py:TestTopology.movement_test http://git-wip-us.apache.org/repos/asf/cassandra-dtest/blob/49b2dda4/README.md ---------------------------------------------------------------------- diff --git a/README.md b/README.md index be595bd..7519925 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,82 @@ -Cassandra Distributed Tests -=========================== +Cassandra Distributed Tests (DTests) +==================================== -Tests for [Apache Cassandra](http://apache.cassandra.org) clusters. +Cassandra Distributed Tests (or better known as "DTests") are a set of Python-based +tests for [Apache Cassandra](http://apache.cassandra.org) clusters. DTests aim to +test functionality that requires multiple Cassandra instances. Functionality that +of code that can be tested in isolation should ideally be a unit test (which can be +found in the actual Cassandra repository). -Prerequisites +Setup and Prerequisites ------------ -An up to date copy of ccm should be installed for starting and stopping Cassandra. -The tests are run using nosetests. -These tests require the datastax python driver. -A few tests still require the deprecated python CQL over thrift driver. +Some environmental setup is required before you can start running DTests. + +### Native Dependencies +DTests requires the following native dependencies: + * Python 3 + * PIP for Python 3 + * libev + * git + * JDK 8 (Java) + +#### Linux +1. ``apt-get install git-core python3 python3-pip python3-dev libev4 libev-dev`` +2. (Optional - solves warning: "jemalloc shared library could not be preloaded to speed up memory allocations"): +``apt-get install -y --no-install-recommends libjemalloc1`` + +#### Mac +On Mac, the easiest path is to install the latest [Xcode and Command Line Utilities](https://developer.apple.com) to +bootstrap your development environment and then use [Homebrew](https://brew.sh) + +1. (Optional) Make sure brew is in a good state on your system ``brew doctor`` +2. ``brew install python3 libev`` + +### Python Dependencies +There are multiple external Python dependencies required to run DTests. +The current Python depenendcy list is maintained in a file named +[requirements.txt](https://github.com/apache/cassandra-dtest/blob/master/requirements.txt) +in the root of the cassandra-dtest repository. + +The easiest way to install these dependencies is with pip and virtualenv. + +**Note**: While virtualenv isn't strictly required, using virtualenv is almost always the quickest +path to success as it provides common base setup across various configurations. + +1. Install virtualenv: ``pip install virtualenv`` +2. Create a new virtualenv: ``virtualenv --python=python3 --no-site-packages ~/dtest`` +3. Switch/Activate the new virtualenv: ``source ~/dtest/bin/activate`` +4. Install remaining DTest Python dependencies: ``pip install -r /path/to/cassandra-dtest/requirements.txt`` - * [ccm](https://github.com/pcmanus/ccm) - * [nosetests](http://readthedocs.org/docs/nose/en/latest/) - * [Python Driver](http://datastax.github.io/python-driver/installation.html) - * [CQL over Thrift Driver](http://code.google.com/a/apache-extras.org/p/cassandra-dbapi2/) Usage ----- -The tests are run by nosetests. The only thing the framework needs to know is -the location of the (compiled) sources for Cassandra. There are two options: +The tests are executed by the pytest framework. For convenience, a wrapper ``run_dtests.py`` +is included with the intent to make starting execution of the dtests with sane defaults as easy +as possible. Most users will most likely find that invoking the tests directly using ``pytest`` +ultimately works the best and provides the most flexibility. + +Pytest has a great [Usage and Invocations](https://docs.pytest.org/en/latest/usage.html) document which is a great place to start for basic invocation options when using pytest. + +At minimum, + + The only thing the framework needs to know is +the location of the (compiled (hint: ``ant clean jar``)) sources for Cassandra. There are two options: Use existing sources: - CASSANDRA_DIR=~/path/to/cassandra nosetests + pytest --cassandra-dir=~/path/to/cassandra Use ccm ability to download/compile released sources from archives.apache.org: - CASSANDRA_VERSION=1.0.0 nosetests + pytest --cassandra-version=1.0.0 A convenient option if tests are regularly run against the same existing -directory is to set a `default_dir` in `~/.cassandra-dtest`. Create the file and -set it to something like: +directory is to set a `cassandra_dir` in `~/path/to/cassandra-dtest/pytest.ini`: - [main] - default_dir=~/path/to/cassandra + [pytest] + cassandra_dir=~/path/to/cassandra The tests will use this directory by default, avoiding the need for any environment variable (that still will have precedence if given though). @@ -43,6 +84,11 @@ environment variable (that still will have precedence if given though). Existing tests are probably the best place to start to look at how to write tests. +The ``run_dtests.py`` included script is simply a wrapper to make starting the dtests +with sane defaults as simple as possible. If you just want to run the tests and do nothing more, +this is most likely the most easy place to start; however, anyone attempting to do active development + and testing will find invoking pytest directly to be likely the best option. + Each test spawns a new fresh cluster and tears it down after the test. If a test fails, the logs for the node are saved in a `logs/<timestamp>` directory for analysis (it's not perfect but has been good enough so far, I'm open to @@ -64,3 +110,141 @@ Writing Tests - If you're using JMX via [the `tools.jmxutils` module](tools/jmxutils.py), make sure to call `remove_perf_disable_shared_mem` on the node or nodes you want to query with JMX _before starting the nodes_. `remove_perf_disable_shared_mem` disables a JVM option that's incompatible with JMX (see [this JMX ticket](https://github.com/rhuss/jolokia/issues/198)). It works by performing a string replacement in the node's Cassandra startup script, so changes will only propagate to the node at startup time. If you'd like to know what to expect during a code review, please see the included [CONTRIBUTING file](CONTRIBUTING.md). + +Debugging Tests +------------- +Some general tips for debugging dtest/pytest tests + +#### pytest.set_trace() +If there is an unexpected value being asserted on and you'd like to inspect the state of all the tests variables just before a paricular assert, add ``pytest.set_trace()`` right before the problematic code. The next time you execute the test, when that line of code is reached pytest will drop you into an interactive python debugger (pdb). From there you can use standard python options to inspect various methods and variables for debugging. + +#### Hung tests/hung pytest framework +Debugging hung tests can be very difficult but thanks to improvements in Python 3 it's now pretty painless to get a python thread dump of all the threads currently running in the pytest process. + +```python +import faulthandler +faulthandler.enable() +``` +Adding the above code will install a signal handler into your process. When the process recieves a *SIGABRT* signal, python will dump python thread dumps for all running threads in the process. DTests installs this by default with the install_debugging_signal_handler fixture. + +The following is an example of what you might see if you send a *SIGABRT* signal to the pytest process while in a hung state during the test teardown phase after the successful completion of the actual dtest. + +```bash +(env) cassandra-dtest vcooluser$ kill -SIGABRT 24142 + +Fatal Python error: Aborted + +Thread 0x000070000f739000 (most recent call first): + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 295 in wait + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 551 in wait + File "/Users/mkjellman/src/cassandra-dtest/tools/data.py", line 31 in query_c1c2 + File "/Users/mkjellman/src/cassandra-dtest/bootstrap_test.py", line 91 in <lambda> + File "/Users/mkjellman/src/cassandra-dtest/dtest.py", line 245 in run + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 916 in _bootstrap_inner + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 884 in _bootstrap + +Thread 0x000070000e32d000 (most recent call first): + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncore.py", line 183 in poll2 + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncore.py", line 207 in loop + File "/Users/mkjellman/env3/src/cassandra-driver/cassandra/io/asyncorereactor.py", line 119 in loop + File "/Users/mkjellman/env3/src/cassandra-driver/cassandra/io/asyncorereactor.py", line 258 in _run_loop + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 864 in run + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 916 in _bootstrap_inner + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 884 in _bootstrap + +Current thread 0x00007fffa00dd340 (most recent call first): + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 1072 in _wait_for_tstate_lock + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 1056 in join + File "/Users/mkjellman/src/cassandra-dtest/dtest.py", line 253 in stop + File "/Users/mkjellman/src/cassandra-dtest/dtest.py", line 580 in tearDown + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/case.py", line 608 in run + File "/usr/local/Cellar/python3/3.6.3/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/case.py", line 653 in __call__ + File "/Users/mkjellman/env3/lib/python3.6/site-packages/_pytest/unittest.py", line 174 in runtest + File "/Users/mkjellman/env3/lib/python3.6/site-packages/_pytest/runner.py", line 107 in pytest_runtest_call + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/callers.py", line 180 in _multicall + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 216 in <lambda> + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 222 in _hookexec + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 617 in __call__ + File "/Users/mkjellman/env3/lib/python3.6/site-packages/flaky/flaky_pytest_plugin.py", line 273 in <lambda> + File "/Users/mkjellman/env3/lib/python3.6/site-packages/_pytest/runner.py", line 191 in __init__ + File "/Users/mkjellman/env3/lib/python3.6/site-packages/flaky/flaky_pytest_plugin.py", line 274 in call_runtest_hook + File "/Users/mkjellman/env3/lib/python3.6/site-packages/flaky/flaky_pytest_plugin.py", line 118 in call_and_report + File "/Users/mkjellman/env3/lib/python3.6/site-packages/_pytest/runner.py", line 77 in runtestprotocol + File "/Users/mkjellman/env3/lib/python3.6/site-packages/_pytest/runner.py", line 63 in pytest_runtest_protocol + File "/Users/mkjellman/env3/lib/python3.6/site-packages/flaky/flaky_pytest_plugin.py", line 81 in pytest_runtest_protocol + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/callers.py", line 180 in _multicall + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 216 in <lambda> + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 222 in _hookexec + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 617 in __call__ + File "/Users/mkjellman/env3/lib/python3.6/site-packages/_pytest/main.py", line 164 in pytest_runtestloop + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/callers.py", line 180 in _multicall + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 216 in <lambda> + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 222 in _hookexec + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 617 in __call__ + File "/Users/mkjellman/env3/lib/python3.6/site-packages/_pytest/main.py", line 141 in _main + File "/Users/mkjellman/env3/lib/python3.6/site-packages/_pytest/main.py", line 103 in wrap_session + File "/Users/mkjellman/env3/lib/python3.6/site-packages/_pytest/main.py", line 134 in pytest_cmdline_main + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/callers.py", line 180 in _multicall + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 216 in <lambda> + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 222 in _hookexec + File "/Users/mkjellman/env3/lib/python3.6/site-packages/pluggy/__init__.py", line 617 in __call__ + File "/Users/mkjellman/env3/lib/python3.6/site-packages/_pytest/config.py", line 59 in main + File "/Users/mkjellman/env3/bin/pytest", line 11 in <module> +Abort trap: 6 +``` + +#### Debugging Issues with Fixtures and Test Setup/Teardown +pytest can appear to be doing "magic" more often than not. One place it may be hard to follow what actual code will get executed by normal code inspection alone is determining which fixtures will run for a given test and in what order. pytest provides a ``--setup-plan`` command line argument. When pytest is invoked with this argument it will print a execution plan including all fixtures and tests that actually running the test will invoke. The below is an example for the current execution plan pytest generates for dtest *auth_test.py::TestAuthRoles::test_create_drop_role* + +```bash +(env3) Michaels-MacBook-Pro:cassandra-dtest mkjellman$ pytest --cassandra-dir=/Users/mkjellman/src/mkjellman-oss-github-cassandra-trunk auth_test.py::TestAuthRoles::test_create_drop_role --setup-plan +====================================================================== test session starts ====================================================================== +platform darwin -- Python 3.6.3, pytest-3.3.0, py-1.5.2, pluggy-0.6.0 +rootdir: /Users/mkjellman/src/cassandra-dtest, inifile: pytest.ini +plugins: timeout-1.2.1, raisesregexp-2.1, nose2pytest-1.0.8, flaky-3.4.0 +collected 1 item + +auth_test.py +SETUP S install_debugging_signal_handler + SETUP C fixture_logging_setup + SETUP F fixture_dtest_setup_overrides + SETUP F fixture_log_test_name_and_date + SETUP F fixture_maybe_skip_tests_requiring_novnodes + SETUP F parse_dtest_config + SETUP F fixture_dtest_setup (fixtures used: fixture_dtest_setup_overrides, fixture_logging_setup, parse_dtest_config) + SETUP F fixture_since (fixtures used: fixture_dtest_setup) + SETUP F fixture_dtest_config (fixtures used: fixture_logging_setup) + SETUP F set_dtest_setup_on_function (fixtures used: fixture_dtest_config, fixture_dtest_setup) + auth_test.py::TestAuthRoles::()::test_create_drop_role (fixtures used: fixture_dtest_config, fixture_dtest_setup, fixture_dtest_setup_overrides, fixture_log_test_name_and_date, fixture_logging_setup, fixture_maybe_skip_tests_requiring_novnodes, fixture_since, install_debugging_signal_handler, parse_dtest_config, set_dtest_setup_on_function) + TEARDOWN F set_dtest_setup_on_function + TEARDOWN F fixture_dtest_config + TEARDOWN F fixture_since + TEARDOWN F fixture_dtest_setup + TEARDOWN F parse_dtest_config + TEARDOWN F fixture_maybe_skip_tests_requiring_novnodes + TEARDOWN F fixture_log_test_name_and_date + TEARDOWN F fixture_dtest_setup_overrides + TEARDOWN C fixture_logging_setup +TEARDOWN S install_debugging_signal_handler +===Flaky Test Report=== + + +===End Flaky Test Report=== + +====================================================================== 0 tests deselected ======================================================================= +================================================================= no tests ran in 0.12 seconds ================================================================== +``` + +#### Instances Failing to Start (Unclean Test Teardown) +Getting into a state (especially while writing new tests or debugging problamatic ones) where pytest/dtest fails to fully tear-down all local C* instancse that were started. You can use this handy one liner to kill all C* instances in one go: + +```bash +ps aux | grep -ie CassandraDaemon | grep java | awk '{print $2}' | xargs kill +``` + +Links +------- + * [ccm](https://github.com/pcmanus/ccm) + * [pytest](https://docs.pytest.org/) + * [Python Driver](http://datastax.github.io/python-driver/installation.html) + * [CQL over Thrift Driver](http://code.google.com/a/apache-extras.org/p/cassandra-dbapi2/) http://git-wip-us.apache.org/repos/asf/cassandra-dtest/blob/49b2dda4/auth_test.py ---------------------------------------------------------------------- diff --git a/auth_test.py b/auth_test.py index 2749209..34f7212 100644 --- a/auth_test.py +++ b/auth_test.py @@ -2,34 +2,39 @@ import time from collections import namedtuple from datetime import datetime, timedelta from distutils.version import LooseVersion - -from nose.tools import assert_regexp_matches +import re +import pytest +import logging from cassandra import AuthenticationFailed, InvalidRequest, Unauthorized from cassandra.cluster import NoHostAvailable from cassandra.protocol import ServerError, SyntaxException -from dtest import CASSANDRA_VERSION_FROM_BUILD, Tester, debug +from dtest_setup_overrides import DTestSetupOverrides +from dtest import CASSANDRA_VERSION_FROM_BUILD, Tester from tools.assertions import (assert_all, assert_exception, assert_invalid, assert_length_equal, assert_one, assert_unauthorized) -from tools.decorators import since from tools.jmxutils import (JolokiaAgent, make_mbean, remove_perf_disable_shared_mem) from tools.metadata_wrapper import UpdatingKeyspaceMetadataWrapper from tools.misc import ImmutableMapping +since = pytest.mark.since +logger = logging.getLogger(__name__) class TestAuth(Tester): - ignore_log_patterns = ( - # This one occurs if we do a non-rolling upgrade, the node - # it's trying to send the migration to hasn't started yet, - # and when it does, it gets replayed and everything is fine. - r'Can\'t send migration request: node.*is down', - ) + @pytest.fixture(autouse=True) + def fixture_add_additional_log_patterns(self, fixture_dtest_setup): + fixture_dtest_setup.ignore_log_patterns = ( + # This one occurs if we do a non-rolling upgrade, the node + # it's trying to send the migration to hasn't started yet, + # and when it does, it gets replayed and everything is fine. + r'Can\'t send migration request: node.*is down', + ) - def system_auth_ks_is_alterable_test(self): + def test_system_auth_ks_is_alterable(self): """ * Launch a three node cluster * Verify the default RF of system_auth is 1 @@ -41,7 +46,7 @@ class TestAuth(Tester): @jira_ticket CASSANDRA-10655 """ self.prepare(nodes=3) - debug("nodes started") + logger.debug("nodes started") session = self.get_session(user='cassandra', password='cassandra') auth_metadata = UpdatingKeyspaceMetadataWrapper( @@ -49,39 +54,39 @@ class TestAuth(Tester): ks_name='system_auth', max_schema_agreement_wait=30 # 3x the default of 10 ) - self.assertEquals(1, auth_metadata.replication_strategy.replication_factor) + assert 1 == auth_metadata.replication_strategy.replication_factor session.execute(""" ALTER KEYSPACE system_auth WITH replication = {'class':'SimpleStrategy', 'replication_factor':3}; """) - self.assertEquals(3, auth_metadata.replication_strategy.replication_factor) + assert 3 == auth_metadata.replication_strategy.replication_factor # Run repair to workaround read repair issues caused by CASSANDRA-10655 - debug("Repairing before altering RF") + logger.debug("Repairing before altering RF") self.cluster.repair() - debug("Shutting down client session") + logger.debug("Shutting down client session") session.shutdown() # make sure schema change is persistent - debug("Stopping cluster..") + logger.debug("Stopping cluster..") self.cluster.stop() - debug("Restarting cluster..") + logger.debug("Restarting cluster..") self.cluster.start(wait_other_notice=True) # check each node directly for i in range(3): - debug('Checking node: {i}'.format(i=i)) + logger.debug('Checking node: {i}'.format(i=i)) node = self.cluster.nodelist()[i] exclusive_auth_metadata = UpdatingKeyspaceMetadataWrapper( cluster=self.patient_exclusive_cql_connection(node, user='cassandra', password='cassandra').cluster, ks_name='system_auth' ) - self.assertEquals(3, exclusive_auth_metadata.replication_strategy.replication_factor) + assert 3 == exclusive_auth_metadata.replication_strategy.replication_factor - def login_test(self): + def test_login(self): """ * Launch a one node cluster * Connect as the default user/password @@ -94,15 +99,15 @@ class TestAuth(Tester): try: self.get_session(user='cassandra', password='badpassword') except NoHostAvailable as e: - self.assertIsInstance(e.errors.values()[0], AuthenticationFailed) + assert isinstance(list(e.errors.values())[0], AuthenticationFailed) try: self.get_session(user='doesntexist', password='doesntmatter') except NoHostAvailable as e: - self.assertIsInstance(e.errors.values()[0], AuthenticationFailed) + assert isinstance(list(e.errors.values())[0], AuthenticationFailed) # from 2.2 role creation is granted by CREATE_ROLE permissions, not superuser status @since('1.2', max_version='2.1.x') - def only_superuser_can_create_users_test(self): + def test_only_superuser_can_create_users(self): """ * Launch a one node cluster * Connect as the default superuser @@ -119,7 +124,7 @@ class TestAuth(Tester): assert_unauthorized(jackob, "CREATE USER james WITH PASSWORD '54321' NOSUPERUSER", 'Only superusers are allowed to perform CREATE (\[ROLE\|USER\]|USER) queries', ) @since('1.2', max_version='2.1.x') - def password_authenticator_create_user_requires_password_test(self): + def test_password_authenticator_create_user_requires_password(self): """ * Launch a one node cluster * Connect as the default superuser @@ -132,7 +137,7 @@ class TestAuth(Tester): assert_invalid(session, "CREATE USER jackob NOSUPERUSER", 'PasswordAuthenticator requires PASSWORD option') session.execute("CREATE USER jackob WITH PASSWORD '12345' NOSUPERUSER") - def cant_create_existing_user_test(self): + def test_cant_create_existing_user(self): """ * Launch a one node cluster * Connect as the default superuser @@ -145,7 +150,7 @@ class TestAuth(Tester): session.execute("CREATE USER 'ja...@example.com' WITH PASSWORD '12345' NOSUPERUSER") assert_invalid(session, "CREATE USER 'ja...@example.com' WITH PASSWORD '12345' NOSUPERUSER", 'ja...@example.com already exists') - def list_users_test(self): + def test_list_users(self): """ * Launch a one node cluster * Connect as the default superuser @@ -163,30 +168,30 @@ class TestAuth(Tester): session.execute("CREATE USER dave WITH PASSWORD '12345' SUPERUSER") rows = list(session.execute("LIST USERS")) - self.assertEqual(5, len(rows)) + assert 5 == len(rows) # {username: isSuperuser} dict. users = dict([(r[0], r[1]) for r in rows]) - self.assertTrue(users['cassandra']) - self.assertFalse(users['alex']) - self.assertTrue(users['bob']) - self.assertFalse(users['cathy']) - self.assertTrue(users['dave']) + assert users['cassandra'] + assert not users['alex'] + assert users['bob'] + assert not users['cathy'] + assert users['dave'] self.get_session(user='dave', password='12345') rows = list(session.execute("LIST USERS")) - self.assertEqual(5, len(rows)) + assert 5 == len(rows) # {username: isSuperuser} dict. users = dict([(r[0], r[1]) for r in rows]) - self.assertTrue(users['cassandra']) - self.assertFalse(users['alex']) - self.assertTrue(users['bob']) - self.assertFalse(users['cathy']) - self.assertTrue(users['dave']) + assert users['cassandra'] + assert not users['alex'] + assert users['bob'] + assert not users['cathy'] + assert users['dave'] @since('2.2') - def handle_corrupt_role_data_test(self): + def test_handle_corrupt_role_data(self): """ * Launch a one node cluster * Connect as the default superuser @@ -208,14 +213,15 @@ class TestAuth(Tester): session.execute("UPDATE system_auth.roles SET is_superuser=null WHERE role='bob'") - self.ignore_log_patterns = list(self.ignore_log_patterns) + [r'Invalid metadata has been detected for role bob'] + self.fixture_dtest_setup.ignore_log_patterns = list(self.fixture_dtest_setup.ignore_log_patterns) + [ + r'Invalid metadata has been detected for role bob'] assert_exception(session, "LIST USERS", "Invalid metadata has been detected for role", expected=(ServerError)) try: self.get_session(user='bob', password='12345') except NoHostAvailable as e: - self.assertIsInstance(e.errors.values()[0], AuthenticationFailed) + assert isinstance(list(e.errors.values())[0], AuthenticationFailed) - def user_cant_drop_themselves_test(self): + def test_user_cant_drop_themselves(self): """ * Launch a one node cluster * Connect as the default superuser @@ -229,7 +235,7 @@ class TestAuth(Tester): # from 2.2 role deletion is granted by DROP_ROLE permissions, not superuser status @since('1.2', max_version='2.1.x') - def only_superusers_can_drop_users_test(self): + def test_only_superusers_can_drop_users(self): """ * Launch a one node cluster * Connect as the default superuser @@ -245,19 +251,19 @@ class TestAuth(Tester): cassandra.execute("CREATE USER cathy WITH PASSWORD '12345' NOSUPERUSER") cassandra.execute("CREATE USER dave WITH PASSWORD '12345' NOSUPERUSER") rows = list(cassandra.execute("LIST USERS")) - self.assertEqual(3, len(rows)) + assert 3 == len(rows) cathy = self.get_session(user='cathy', password='12345') assert_unauthorized(cathy, 'DROP USER dave', 'Only superusers are allowed to perform DROP (\[ROLE\|USER\]|USER) queries') rows = list(cassandra.execute("LIST USERS")) - self.assertEqual(3, len(rows)) + assert 3 == len(rows) cassandra.execute('DROP USER dave') rows = list(cassandra.execute("LIST USERS")) - self.assertEqual(2, len(rows)) + assert 2 == len(rows) - def dropping_nonexistent_user_throws_exception_test(self): + def test_dropping_nonexistent_user_throws_exception(self): """ * Launch a one node cluster * Connect as the default superuser @@ -268,7 +274,7 @@ class TestAuth(Tester): session = self.get_session(user='cassandra', password='cassandra') assert_invalid(session, 'DROP USER nonexistent', "nonexistent doesn't exist") - def drop_user_case_sensitive_test(self): + def test_drop_user_case_sensitive(self): """ * Launch a one node cluster * Connect as the default superuser @@ -284,7 +290,7 @@ class TestAuth(Tester): cassandra.execute("DROP USER Test") rows = [x[0] for x in list(cassandra.execute("LIST USERS"))] - self.assertItemsEqual(rows, ['cassandra']) + assert rows == ['cassandra'] cassandra.execute("CREATE USER test WITH PASSWORD '12345'") @@ -293,9 +299,9 @@ class TestAuth(Tester): cassandra.execute("DROP USER test") rows = [x[0] for x in list(cassandra.execute("LIST USERS"))] - self.assertItemsEqual(rows, ['cassandra']) + assert rows == ['cassandra'] - def alter_user_case_sensitive_test(self): + def test_alter_user_case_sensitive(self): """ * Launch a one node cluster * Connect as the default superuser @@ -315,7 +321,7 @@ class TestAuth(Tester): assert_invalid(cassandra, "ALTER USER TEST WITH PASSWORD '12345'") cassandra.execute("ALTER USER test WITH PASSWORD '54321'") - def regular_users_can_alter_their_passwords_only_test(self): + def test_regular_users_can_alter_their_passwords_only(self): """ * Launch a one node cluster * Connect as the default superuser @@ -336,7 +342,7 @@ class TestAuth(Tester): assert_unauthorized(cathy, "ALTER USER bob WITH PASSWORD 'cantchangeit'", "You aren't allowed to alter this user|User cathy does not have sufficient privileges to perform the requested operation") - def users_cant_alter_their_superuser_status_test(self): + def test_users_cant_alter_their_superuser_status(self): """ * Launch a one node cluster * Connect as the default superuser @@ -347,7 +353,7 @@ class TestAuth(Tester): session = self.get_session(user='cassandra', password='cassandra') assert_unauthorized(session, "ALTER USER cassandra NOSUPERUSER", "You aren't allowed to alter your own superuser status") - def only_superuser_alters_superuser_status_test(self): + def test_only_superuser_alters_superuser_status(self): """ * Launch a one node cluster * Connect as the default superuser @@ -366,7 +372,7 @@ class TestAuth(Tester): cassandra.execute("ALTER USER cathy SUPERUSER") - def altering_nonexistent_user_throws_exception_test(self): + def test_altering_nonexistent_user_throws_exception(self): """ * Launch a one node cluster * Connect as the default superuser @@ -377,7 +383,7 @@ class TestAuth(Tester): session = self.get_session(user='cassandra', password='cassandra') assert_invalid(session, "ALTER USER nonexistent WITH PASSWORD 'doesn''tmatter'", "nonexistent doesn't exist") - def conditional_create_drop_user_test(self): + def test_conditional_create_drop_user(self): """ * Launch a one node cluster * Connect as the default superuser @@ -404,7 +410,7 @@ class TestAuth(Tester): session.execute("DROP USER IF EXISTS aleksey") assert_one(session, "LIST USERS", ['cassandra', True]) - def create_ks_auth_test(self): + def test_create_ks_auth(self): """ * Launch a one node cluster * Connect as the default superuser @@ -427,7 +433,7 @@ class TestAuth(Tester): cassandra.execute("GRANT CREATE ON ALL KEYSPACES TO cathy") cathy.execute("""CREATE KEYSPACE ks WITH replication = {'class':'SimpleStrategy', 'replication_factor':1}""") - def create_cf_auth_test(self): + def test_create_cf_auth(self): """ * Launch a one node cluster * Connect as the default superuser @@ -450,7 +456,7 @@ class TestAuth(Tester): cassandra.execute("GRANT CREATE ON KEYSPACE ks TO cathy") cathy.execute("CREATE TABLE ks.cf (id int primary key)") - def alter_ks_auth_test(self): + def test_alter_ks_auth(self): """ * Launch a one node cluster * Connect as the default superuser @@ -474,7 +480,7 @@ class TestAuth(Tester): cassandra.execute("GRANT ALTER ON KEYSPACE ks TO cathy") cathy.execute("ALTER KEYSPACE ks WITH replication = {'class':'SimpleStrategy', 'replication_factor':2}") - def alter_cf_auth_test(self): + def test_alter_cf_auth(self): """ * Launch a one node cluster * Connect as the default superuser @@ -515,7 +521,7 @@ class TestAuth(Tester): cathy.execute("DROP INDEX cf_val_idx") @since('3.0') - def materialized_views_auth_test(self): + def test_materialized_views_auth(self): """ * Launch a one node cluster * Connect as the default superuser @@ -562,7 +568,7 @@ class TestAuth(Tester): cassandra.execute("GRANT ALTER ON ks.cf TO cathy") cathy.execute("DROP MATERIALIZED VIEW mv1") - def drop_ks_auth_test(self): + def test_drop_ks_auth(self): """ * Launch a one node cluster * Connect as the default superuser @@ -584,7 +590,7 @@ class TestAuth(Tester): cassandra.execute("GRANT DROP ON KEYSPACE ks TO cathy") cathy.execute("DROP KEYSPACE ks") - def drop_cf_auth_test(self): + def test_drop_cf_auth(self): """ * Launch a one node cluster * Connect as the default superuser @@ -607,7 +613,7 @@ class TestAuth(Tester): cassandra.execute("GRANT DROP ON ks.cf TO cathy") cathy.execute("DROP TABLE ks.cf") - def modify_and_select_auth_test(self): + def test_modify_and_select_auth(self): """ * Launch a one node cluster * Connect as the default superuser @@ -631,7 +637,7 @@ class TestAuth(Tester): cassandra.execute("GRANT SELECT ON ks.cf TO cathy") rows = list(cathy.execute("SELECT * FROM ks.cf")) - self.assertEquals(0, len(rows)) + assert 0 == len(rows) assert_unauthorized(cathy, "INSERT INTO ks.cf (id, val) VALUES (0, 0)", "User cathy has no MODIFY permission on <table ks.cf> or any of its parents") @@ -645,17 +651,17 @@ class TestAuth(Tester): cathy.execute("INSERT INTO ks.cf (id, val) VALUES (0, 0)") cathy.execute("UPDATE ks.cf SET val = 1 WHERE id = 1") rows = list(cathy.execute("SELECT * FROM ks.cf")) - self.assertEquals(2, len(rows)) + assert 2 == len(rows) cathy.execute("DELETE FROM ks.cf WHERE id = 1") rows = list(cathy.execute("SELECT * FROM ks.cf")) - self.assertEquals(1, len(rows)) + assert 1 == len(rows) rows = list(cathy.execute("TRUNCATE ks.cf")) - self.assertItemsEqual(rows, []) + assert rows == [] @since('2.2') - def grant_revoke_without_ks_specified_test(self): + def test_grant_revoke_without_ks_specified(self): """ * Launch a one node cluster * Connect as the default superuser @@ -687,7 +693,7 @@ class TestAuth(Tester): cathy.execute("GRANT SELECT ON cf TO bob") bob.execute("SELECT * FROM ks.cf") - def grant_revoke_auth_test(self): + def test_grant_revoke_auth(self): """ * Launch a one node cluster * Connect as the default superuser @@ -725,7 +731,7 @@ class TestAuth(Tester): bob = self.get_session(user='bob', password='12345') bob.execute("SELECT * FROM ks.cf") - def grant_revoke_nonexistent_user_or_ks_test(self): + def test_grant_revoke_nonexistent_user_or_ks(self): """ * Launch a one node cluster * Connect as the default superuser @@ -747,7 +753,7 @@ class TestAuth(Tester): assert_invalid(cassandra, "REVOKE ALL ON KEYSPACE ks FROM nonexistent", "(User|Role) nonexistent doesn't exist") - def grant_revoke_cleanup_test(self): + def test_grant_revoke_cleanup(self): """ * Launch a one node cluster * Connect as the default superuser @@ -771,7 +777,7 @@ class TestAuth(Tester): cathy = self.get_session(user='cathy', password='12345') cathy.execute("INSERT INTO ks.cf (id, val) VALUES (0, 0)") rows = list(cathy.execute("SELECT * FROM ks.cf")) - self.assertEquals(1, len(rows)) + assert 1 == len(rows) # drop and recreate the user, make sure permissions are gone cassandra.execute("DROP USER cathy") @@ -785,7 +791,7 @@ class TestAuth(Tester): cassandra.execute("GRANT ALL ON ks.cf TO cathy") cathy.execute("INSERT INTO ks.cf (id, val) VALUES (0, 0)") rows = list(cathy.execute("SELECT * FROM ks.cf")) - self.assertEqual(1, len(rows)) + assert 1 == len(rows) # drop and recreate the keyspace, make sure permissions are gone cassandra.execute("DROP KEYSPACE ks") @@ -796,7 +802,7 @@ class TestAuth(Tester): assert_unauthorized(cathy, "SELECT * FROM ks.cf", "User cathy has no SELECT permission on <table ks.cf> or any of its parents") - def permissions_caching_test(self): + def test_permissions_caching(self): """ * Launch a one node cluster, with a 2s permission cache * Connect as the default superuser @@ -828,7 +834,7 @@ class TestAuth(Tester): if attempt > 3: self.fail("Unable to verify cache expiry in 3 attempts, failing") - debug("Attempting to verify cache expiry, attempt #{i}".format(i=attempt)) + logger.debug("Attempting to verify cache expiry, attempt #{i}".format(i=attempt)) # grant SELECT to cathy cassandra.execute("GRANT SELECT ON ks.cf TO cathy") grant_time = datetime.now() @@ -849,7 +855,7 @@ class TestAuth(Tester): # legit failure self.fail("Expecting query to raise an exception, but nothing was raised.") except Unauthorized as e: - assert_regexp_matches(str(e), "User cathy has no SELECT permission on <table ks.cf> or any of its parents") + assert re.search("User cathy has no SELECT permission on <table ks.cf> or any of its parents", str(e)) check_caching() @@ -871,16 +877,16 @@ class TestAuth(Tester): try: for c in cathys: rows = list(c.execute("SELECT * FROM ks.cf")) - self.assertEqual(0, len(rows)) + assert 0 == len(rows) success = True except Unauthorized: pass cnt += 1 time.sleep(0.1) - self.assertTrue(success) + assert success - def list_permissions_test(self): + def test_list_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -956,7 +962,7 @@ class TestAuth(Tester): assert_unauthorized(bob, "LIST ALL PERMISSIONS OF cathy", "You are not authorized to view cathy's permissions") - def type_auth_test(self): + def test_type_auth(self): """ * Launch a one node cluster * Connect as the default superuser @@ -983,7 +989,7 @@ class TestAuth(Tester): cassandra.execute("GRANT DROP ON KEYSPACE ks TO cathy") cathy.execute("DROP TYPE ks.address") - def restart_node_doesnt_lose_auth_data_test(self): + def test_restart_node_doesnt_lose_auth_data(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1018,13 +1024,12 @@ class TestAuth(Tester): philip.execute("SELECT * FROM ks.cf") @since('3.10') - def auth_metrics_test(self): + def test_auth_metrics(self): """ Success and failure metrics were added to the authentication procedure so as to estimate the percentage of authentication attempts that failed. @jira_ticket CASSANDRA-10635 """ - cluster = self.cluster config = {'authenticator': 'org.apache.cassandra.auth.PasswordAuthenticator', 'authorizer': 'org.apache.cassandra.auth.CassandraAuthorizer', @@ -1042,13 +1047,13 @@ class TestAuth(Tester): failure = jmx.read_attribute( make_mbean('metrics', type='Client', name='AuthFailure'), 'Count') - self.assertEqual(0, success) - self.assertEqual(0, failure) + assert 0 == success + assert 0 == failure try: self.get_session(user='cassandra', password='wrong_password') except NoHostAvailable as e: - self.assertIsInstance(e.errors.values()[0], AuthenticationFailed) + assert isinstance(list(e.errors.values())[0], AuthenticationFailed) self.get_session(user='cassandra', password='cassandra') @@ -1057,8 +1062,8 @@ class TestAuth(Tester): failure = jmx.read_attribute( make_mbean('metrics', type='Client', name='AuthFailure'), 'Count') - self.assertGreater(success, 0) - self.assertGreater(failure, 0) + assert success > 0 + assert failure > 0 def prepare(self, nodes=1, permissions_validity=0): """ @@ -1073,7 +1078,7 @@ class TestAuth(Tester): self.cluster.populate(nodes).start() n = self.cluster.wait_for_any_log('Created default superuser', 25) - debug("Default role created by " + n.name) + logger.debug("Default role created by " + n.name) def get_session(self, node_idx=0, user=None, password=None): """ @@ -1097,7 +1102,7 @@ class TestAuth(Tester): """ rows = session.execute(query) perms = [(str(r.username), str(r.resource), str(r.permission)) for r in rows] - self.assertEqual(sorted(expected), sorted(perms)) + assert sorted(expected) == sorted(perms) def data_resource_creator_permissions(creator, resource): @@ -1134,16 +1139,21 @@ cassandra_role = Role('cassandra', True, True, {}) @since('2.2') class TestAuthRoles(Tester): - """ - @jira_ticket CASSANDRA-7653 - """ - if CASSANDRA_VERSION_FROM_BUILD >= '3.0': - cluster_options = ImmutableMapping({'enable_user_defined_functions': 'true', - 'enable_scripted_user_defined_functions': 'true'}) - else: - cluster_options = ImmutableMapping({'enable_user_defined_functions': 'true'}) - def create_drop_role_test(self): + @pytest.fixture(scope='function', autouse=True) + def fixture_dtest_setup_overrides(self): + """ + @jira_ticket CASSANDRA-7653 + """ + dtest_setup_overrides = DTestSetupOverrides() + if CASSANDRA_VERSION_FROM_BUILD >= '3.0': + dtest_setup_overrides.cluster_options = ImmutableMapping({'enable_user_defined_functions': 'true', + 'enable_scripted_user_defined_functions': 'true'}) + else: + dtest_setup_overrides.cluster_options.cluster_options = ImmutableMapping({'enable_user_defined_functions': 'true'}) + return dtest_setup_overrides + + def test_create_drop_role(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1160,7 +1170,7 @@ class TestAuthRoles(Tester): cassandra.execute("DROP ROLE role1") assert_one(cassandra, "LIST ROLES", list(cassandra_role)) - def conditional_create_drop_role_test(self): + def test_conditional_create_drop_role(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1181,7 +1191,7 @@ class TestAuthRoles(Tester): cassandra.execute("DROP ROLE IF EXISTS role1") assert_one(cassandra, "LIST ROLES", list(cassandra_role)) - def create_drop_role_validation_test(self): + def test_create_drop_role_validation(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1212,7 +1222,7 @@ class TestAuthRoles(Tester): cassandra.execute("DROP ROLE role1") assert_invalid(cassandra, "DROP ROLE role1", "role1 doesn't exist") - def role_admin_validation_test(self): + def test_role_admin_validation(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1275,7 +1285,7 @@ class TestAuthRoles(Tester): assert_unauthorized(mike, "CREATE ROLE role3 WITH LOGIN = false", "User mike does not have sufficient privileges to perform the requested operation") - def creator_of_db_resource_granted_all_permissions_test(self): + def test_creator_of_db_resource_granted_all_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1317,7 +1327,7 @@ class TestAuthRoles(Tester): cassandra, "LIST ALL PERMISSIONS") - def create_and_grant_roles_with_superuser_status_test(self): + def test_create_and_grant_roles_with_superuser_status(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1347,7 +1357,7 @@ class TestAuthRoles(Tester): ['non_superuser', False, False, {}], ['role1', False, False, {}]]) - def drop_and_revoke_roles_with_superuser_status_test(self): + def test_drop_and_revoke_roles_with_superuser_status(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1374,7 +1384,7 @@ class TestAuthRoles(Tester): mike.execute("DROP ROLE non_superuser") mike.execute("DROP ROLE role1") - def drop_role_removes_memberships_test(self): + def test_drop_role_removes_memberships(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1406,7 +1416,7 @@ class TestAuthRoles(Tester): assert_one(cassandra, "LIST ROLES OF mike", list(mike_role)) assert_all(cassandra, "LIST ROLES", [list(cassandra_role), list(mike_role), list(role2_role)]) - def drop_role_revokes_permissions_granted_on_it_test(self): + def test_drop_role_revokes_permissions_granted_on_it(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1431,9 +1441,9 @@ class TestAuthRoles(Tester): cassandra.execute("DROP ROLE role1") cassandra.execute("DROP ROLE role2") - self.assertItemsEqual(list(cassandra.execute("LIST ALL PERMISSIONS OF mike")), []) + assert list(cassandra.execute("LIST ALL PERMISSIONS OF mike")) == [] - def grant_revoke_roles_test(self): + def test_grant_revoke_roles(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1460,7 +1470,7 @@ class TestAuthRoles(Tester): cassandra.execute("REVOKE role1 FROM role2") assert_one(cassandra, "LIST ROLES OF role2", list(role2_role)) - def grant_revoke_role_validation_test(self): + def test_grant_revoke_role_validation(self): """ * Launch a one node cluster * Connect as the default superusers @@ -1501,7 +1511,7 @@ class TestAuthRoles(Tester): cassandra.execute("REVOKE role1 FROM john") mike.execute("REVOKE role2 from john") - def list_roles_test(self): + def test_list_roles(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1540,7 +1550,7 @@ class TestAuthRoles(Tester): cassandra.execute("GRANT DESCRIBE ON ALL ROLES TO mike") assert_all(mike, "LIST ROLES", [list(cassandra_role), list(mike_role), list(role1_role), list(role2_role)]) - def grant_revoke_permissions_test(self): + def test_grant_revoke_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1578,7 +1588,7 @@ class TestAuthRoles(Tester): "INSERT INTO ks.cf (id, val) VALUES (0, 0)", "mike has no MODIFY permission on <table ks.cf> or any of its parents") - def filter_granted_permissions_by_resource_type_test(self): + def test_filter_granted_permissions_by_resource_type(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1694,7 +1704,7 @@ class TestAuthRoles(Tester): "LIST ALL PERMISSIONS OF mike") cassandra.execute("REVOKE ALL ON FUNCTION ks.agg_func(int) FROM mike") - def list_permissions_test(self): + def test_list_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1760,7 +1770,7 @@ class TestAuthRoles(Tester): "LIST ALTER PERMISSION ON ROLE role1 OF role2") # make sure ALTER on role2 is excluded properly when OF is for another role cassandra.execute("CREATE ROLE role3 WITH SUPERUSER = false AND LOGIN = false") - self.assertItemsEqual(list(cassandra.execute("LIST ALTER PERMISSION ON ROLE role1 OF role3")), []) + assert list(cassandra.execute("LIST ALTER PERMISSION ON ROLE role1 OF role3")) == [] # now check users can list their own permissions mike = self.get_session(user='mike', password='12345') @@ -1771,7 +1781,7 @@ class TestAuthRoles(Tester): mike, "LIST ALL PERMISSIONS OF mike") - def list_permissions_validation_test(self): + def test_list_permissions_validation(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1815,7 +1825,7 @@ class TestAuthRoles(Tester): "LIST ALL PERMISSIONS OF john", "You are not authorized to view john's permissions") - def role_caching_authenticated_user_test(self): + def test_role_caching_authenticated_user(self): """ This test is to show that the role caching in AuthenticatedUser works correctly and revokes the roles from a logged in user @@ -1853,7 +1863,7 @@ class TestAuthRoles(Tester): except Unauthorized as e: unauthorized = e - self.assertIsNotNone(unauthorized) + assert unauthorized is not None def drop_non_existent_role_should_not_update_cache(self): """ @@ -1883,7 +1893,7 @@ class TestAuthRoles(Tester): mike = self.get_session(user='mike', password='12345') mike.execute("SELECT * FROM ks.cf") - def prevent_circular_grants_test(self): + def test_prevent_circular_grants(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1906,7 +1916,7 @@ class TestAuthRoles(Tester): "mike is a member of role2", InvalidRequest) - def create_user_as_alias_for_create_role_test(self): + def test_create_user_as_alias_for_create_role(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1920,7 +1930,7 @@ class TestAuthRoles(Tester): cassandra.execute("CREATE USER super_user WITH PASSWORD '12345' SUPERUSER") assert_one(cassandra, "LIST ROLES OF super_user", ["super_user", True, True, {}]) - def role_name_test(self): + def test_role_name(self): """ Simple test to verify the behaviour of quoting when creating roles & users * Launch a one node cluster @@ -1959,7 +1969,7 @@ class TestAuthRoles(Tester): self.get_session(user='USER2', password='12345') self.assert_unauthenticated('User2', '12345') - def role_requires_login_privilege_to_authenticate_test(self): + def test_role_requires_login_privilege_to_authenticate(self): """ * Launch a one node cluster * Connect as the default superuser @@ -1982,7 +1992,7 @@ class TestAuthRoles(Tester): assert_one(cassandra, "LIST ROLES OF mike", ["mike", False, True, {}]) self.get_session(user='mike', password='12345') - def roles_do_not_inherit_login_privilege_test(self): + def test_roles_do_not_inherit_login_privilege(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2002,7 +2012,7 @@ class TestAuthRoles(Tester): self.assert_login_not_allowed("mike", "12345") - def role_requires_password_to_login_test(self): + def test_role_requires_password_to_login(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2021,7 +2031,7 @@ class TestAuthRoles(Tester): cassandra.execute("ALTER ROLE mike WITH PASSWORD = '12345'") self.get_session(user='mike', password='12345') - def superuser_status_is_inherited_test(self): + def test_superuser_status_is_inherited(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2048,7 +2058,7 @@ class TestAuthRoles(Tester): ["db_admin", True, False, {}], list(mike_role)]) - def list_users_considers_inherited_superuser_status_test(self): + def test_list_users_considers_inherited_superuser_status(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2065,7 +2075,7 @@ class TestAuthRoles(Tester): ["mike", True]]) # UDF permissions tests # TODO move to separate fixture & refactor this + auth_test.py - def grant_revoke_udf_permissions_test(self): + def test_grant_revoke_udf_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2111,7 +2121,7 @@ class TestAuthRoles(Tester): cassandra.execute("REVOKE EXECUTE PERMISSION ON ALL FUNCTIONS FROM mike") self.assert_no_permissions(cassandra, "LIST ALL PERMISSIONS OF mike") - def grant_revoke_are_idempotent_test(self): + def test_grant_revoke_are_idempotent(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2135,7 +2145,7 @@ class TestAuthRoles(Tester): cassandra.execute("REVOKE EXECUTE ON FUNCTION ks.plus_one(int) FROM mike") self.assert_no_permissions(cassandra, "LIST ALL PERMISSIONS OF mike") - def function_resource_hierarchy_permissions_test(self): + def test_function_resource_hierarchy_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2197,7 +2207,7 @@ class TestAuthRoles(Tester): mike.execute(select_one) mike.execute(select_two) - def udf_permissions_validation_test(self): + def test_udf_permissions_validation(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2262,7 +2272,7 @@ class TestAuthRoles(Tester): cassandra.execute("GRANT CREATE ON ALL FUNCTIONS IN KEYSPACE ks TO mike") mike.execute(cql) - def drop_role_cleans_up_udf_permissions_test(self): + def test_drop_role_cleans_up_udf_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2291,7 +2301,7 @@ class TestAuthRoles(Tester): cassandra.execute("CREATE ROLE mike WITH PASSWORD = '12345' AND LOGIN = true") self.assert_no_permissions(cassandra, "LIST ALL PERMISSIONS OF mike") - def drop_function_and_keyspace_cleans_up_udf_permissions_test(self): + def test_drop_function_and_keyspace_cleans_up_udf_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2323,7 +2333,7 @@ class TestAuthRoles(Tester): cassandra.execute("DROP KEYSPACE ks") self.assert_no_permissions(cassandra, "LIST ALL PERMISSIONS OF mike") - def udf_with_overloads_permissions_test(self): + def test_udf_with_overloads_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2367,7 +2377,7 @@ class TestAuthRoles(Tester): cassandra.execute("DROP FUNCTION ks.plus_one(int)") self.assert_no_permissions(cassandra, "LIST ALL PERMISSIONS OF mike") - def drop_keyspace_cleans_up_function_level_permissions_test(self): + def test_drop_keyspace_cleans_up_function_level_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2393,31 +2403,31 @@ class TestAuthRoles(Tester): cassandra.execute("DROP KEYSPACE ks") self.assert_no_permissions(cassandra, "LIST ALL PERMISSIONS OF mike") - def udf_permissions_in_selection_test(self): + def test_udf_permissions_in_selection(self): """ Verify EXECUTE permission works in a SELECT when UDF is one of the columns requested """ self.verify_udf_permissions("SELECT k, v, ks.plus_one(v) FROM ks.t1 WHERE k = 1") - def udf_permissions_in_select_where_clause_test(self): + def test_udf_permissions_in_select_where_clause(self): """ Verify EXECUTE permission works in a SELECT when UDF is in the WHERE clause """ self.verify_udf_permissions("SELECT k, v FROM ks.t1 WHERE k = ks.plus_one(0)") - def udf_permissions_in_insert_test(self): + def test_udf_permissions_in_insert(self): """ Verify EXECUTE permission works in an INSERT when UDF is in the VALUES """ self.verify_udf_permissions("INSERT INTO ks.t1 (k, v) VALUES (1, ks.plus_one(1))") - def udf_permissions_in_update_test(self): + def test_udf_permissions_in_update(self): """ Verify EXECUTE permission works in an UPDATE when UDF is in the SET and WHERE clauses """ self.verify_udf_permissions("UPDATE ks.t1 SET v = ks.plus_one(2) WHERE k = ks.plus_one(0)") - def udf_permissions_in_delete_test(self): + def test_udf_permissions_in_delete(self): """ Verify EXECUTE permission works in a DELETE when UDF is in the WHERE clause """ @@ -2447,7 +2457,7 @@ class TestAuthRoles(Tester): cassandra.execute("GRANT EXECUTE ON FUNCTION ks.plus_one(int) TO mike") return mike.execute(cql) - def inheritence_of_udf_permissions_test(self): + def test_inheritence_of_udf_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2471,7 +2481,7 @@ class TestAuthRoles(Tester): cassandra.execute("GRANT function_user TO mike") assert_one(mike, select, [1, 1, 2]) - def builtin_functions_require_no_special_permissions_test(self): + def test_builtin_functions_require_no_special_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2486,7 +2496,7 @@ class TestAuthRoles(Tester): cassandra.execute("GRANT ALL PERMISSIONS ON ks.t1 TO mike") assert_one(mike, "SELECT * from ks.t1 WHERE k=blobasint(intasblob(1))", [1, 1]) - def disallow_grant_revoke_on_builtin_functions_test(self): + def test_disallow_grant_revoke_on_builtin_functions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2510,7 +2520,7 @@ class TestAuthRoles(Tester): "Altering permissions on builtin functions is not supported", InvalidRequest) - def disallow_grant_execute_on_non_function_resources_test(self): + def test_disallow_grant_execute_on_non_function_resources(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2540,7 +2550,7 @@ class TestAuthRoles(Tester): "Resource type RoleResource does not support any of the requested permissions", SyntaxException) - def aggregate_function_permissions_test(self): + def test_aggregate_function_permissions(self): """ * Launch a one node cluster * Connect as the default superuser @@ -2609,9 +2619,9 @@ class TestAuthRoles(Tester): cassandra.execute("DROP AGGREGATE ks.simple_aggregate(int)") all_perms = list(cassandra.execute("LIST ALL PERMISSIONS OF mike")) for p in agg_perms: - self.assertFalse(p in all_perms, msg="Perm {p} found, but should be removed".format(p=p)) + assert not p in all_perms, "Perm {p} found, but should be removed".format(p=p) - def ignore_invalid_roles_test(self): + def test_ignore_invalid_roles(self): """ The system_auth.roles table includes a set of roles of which each role is a member. If that list were to get out of sync, so that it indicated @@ -2619,7 +2629,6 @@ class TestAuthRoles(Tester): table, then the result of LIST ROLES OF roleA should not include roleB @jira_ticket CASSANDRA-9551 """ - self.prepare() cassandra = self.get_session(user='cassandra', password='cassandra') cassandra.execute("CREATE ROLE mike WITH LOGIN = true") @@ -2632,10 +2641,10 @@ class TestAuthRoles(Tester): session.execute("CREATE TABLE ks.t1 (k int PRIMARY KEY, v int)") def assert_unauthenticated(self, user, password): - with self.assertRaises(NoHostAvailable) as response: + with pytest.raises(NoHostAvailable) as response: node = self.cluster.nodelist()[0] self.cql_connection(node, user=user, password=password) - host, error = response.exception.errors.popitem() + host, error = response._excinfo[1].errors.popitem() message = "Provided username {user} and/or password are incorrect".format(user=user)\ if node.cluster.version() >= LooseVersion('3.10') \ @@ -2644,19 +2653,19 @@ class TestAuthRoles(Tester): '[Bad credentials] message="{message}"'.format(host=host, message=message) assert isinstance(error, AuthenticationFailed), "Expected AuthenticationFailed, got {error}".format(error=error) - self.assertIn(pattern, error.message) + assert pattern in repr(error) def assert_login_not_allowed(self, user, password): - with self.assertRaises(NoHostAvailable) as response: + with pytest.raises(NoHostAvailable) as response: node = self.cluster.nodelist()[0] self.cql_connection(node, user=user, password=password) - host, error = response.exception.errors.popitem() + host, error = response._excinfo[1].errors.popitem() pattern = 'Failed to authenticate to {host}: Error from server: code=0100 ' \ '[Bad credentials] message="{user} is not permitted to log in"'.format(host=host, user=user) assert isinstance(error, AuthenticationFailed), "Expected AuthenticationFailed, got {error}".format(error=error) - self.assertIn(pattern, error.message) + assert pattern in repr(error) def get_session(self, node_idx=0, user=None, password=None): """ @@ -2684,10 +2693,10 @@ class TestAuthRoles(Tester): def assert_permissions_listed(self, expected, session, query): rows = session.execute(query) perms = [(str(r.role), str(r.resource), str(r.permission)) for r in rows] - self.assertEqual(sorted(expected), sorted(perms)) + assert sorted(expected) == sorted(perms) def assert_no_permissions(self, session, query): - self.assertItemsEqual(list(session.execute(query)), []) + assert list(session.execute(query)) == [] def role_creator_permissions(creator, role): --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org