From 1cea32477f456120249ba7c6321502b292e2792c Mon Sep 17 00:00:00 2001 From: Wayne Werner Date: Tue, 8 Feb 2022 16:26:45 -0600 Subject: [PATCH] Add tests + ssl arg for mongodb_user state These tests are not *super* meaningful but they do at least ensure that ssl the arg correctly gets passed into the MongoClient. Still left to do is add tests + ssl arg for the mongodb_database state, the mongodb module, as well as updating all of the documentation for these modules. We also want to add a few functional tests that ensure that the SSL argument is correctly used with an actual mongodb server. --- salt/modules/mongodb.py | 406 ++++++++++++++++-- salt/states/mongodb_database.py | 1 + salt/states/mongodb_user.py | 46 +- .../unit/states/test_mongodb_database.py | 3 + .../pytests/unit/states/test_mongodb_user.py | 98 ++++- 5 files changed, 498 insertions(+), 56 deletions(-) diff --git a/salt/modules/mongodb.py b/salt/modules/mongodb.py index 72b0ff00747..1dd92d8aa07 100644 --- a/salt/modules/mongodb.py +++ b/salt/modules/mongodb.py @@ -45,11 +45,30 @@ def __virtual__(): def _connect( - user=None, password=None, host=None, port=None, database="admin", authdb=None + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + ssl=False, ): """ - Returns a tuple of (user, host, port) with config, pillar, or default - values assigned to missing values. + Connect to a mongodb database with pymongo.MongoClient. If user and + password are provided, use those to authenticate with the database. If + any of user, password, host, or port are not provided then fall back to + config options: + + - mongodb.user + - mongodb.password + - mongodb.host + - mongodb.port + + By default use the admin database for the database and authdb. + + If ``ssl`` is True, connect to mongodb over SSL. + + If connection fails for any reason, log error and return False. """ if not user: user = __salt__["config.option"]("mongodb.user") @@ -63,7 +82,7 @@ def _connect( authdb = database try: - conn = pymongo.MongoClient(host=host, port=port) + conn = pymongo.MongoClient(host=host, port=port, ssl=bool(ssl)) mdb = pymongo.database.Database(conn, database) if user and password: mdb.authenticate(user, password, source=authdb) @@ -88,7 +107,7 @@ def _to_dict(objects): return objects -def db_list(user=None, password=None, host=None, port=None, authdb=None): +def db_list(user=None, password=None, host=None, port=None, authdb=None, ssl=False): """ List all MongoDB databases @@ -107,13 +126,18 @@ def db_list(user=None, password=None, host=None, port=None, authdb=None): authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash salt '*' mongodb.db_list """ - conn = _connect(user, password, host, port, authdb=authdb) + conn = _connect(user, password, host, port, authdb=authdb, ssl=ssl) if not conn: return "Failed to connect to mongo database" @@ -125,7 +149,9 @@ def db_list(user=None, password=None, host=None, port=None, authdb=None): return str(err) -def db_exists(name, user=None, password=None, host=None, port=None, authdb=None): +def db_exists( + name, user=None, password=None, host=None, port=None, authdb=None, ssl=False +): """ Checks if a database exists in MongoDB @@ -147,13 +173,18 @@ def db_exists(name, user=None, password=None, host=None, port=None, authdb=None) authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash salt '*' mongodb.db_exists """ - dbs = db_list(user, password, host, port, authdb=authdb) + dbs = db_list(user, password, host, port, authdb=authdb, ssl=ssl) if isinstance(dbs, str): return False @@ -161,7 +192,9 @@ def db_exists(name, user=None, password=None, host=None, port=None, authdb=None) return name in dbs -def db_remove(name, user=None, password=None, host=None, port=None, authdb=None): +def db_remove( + name, user=None, password=None, host=None, port=None, authdb=None, ssl=False +): """ Remove a MongoDB database @@ -183,13 +216,18 @@ def db_remove(name, user=None, password=None, host=None, port=None, authdb=None) authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash salt '*' mongodb.db_remove """ - conn = _connect(user, password, host, port, authdb=authdb) + conn = _connect(user, password, host, port, authdb=authdb, ssl=ssl) if not conn: return "Failed to connect to mongo database" @@ -208,7 +246,13 @@ def _version(mdb): def version( - user=None, password=None, host=None, port=None, database="admin", authdb=None + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + ssl=False, ): """ Get MongoDB instance version @@ -228,13 +272,18 @@ def version( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash salt '*' mongodb.version """ - conn = _connect(user, password, host, port, authdb=authdb) + conn = _connect(user, password, host, port, authdb=authdb, ssl=ssl) if not conn: err_msg = "Failed to connect to MongoDB database {}:{}".format(host, port) log.error(err_msg) @@ -249,10 +298,17 @@ def version( def user_find( - name, user=None, password=None, host=None, port=None, database="admin", authdb=None + name, + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + ssl=False, ): """ - Get single user from MongoDB + Get single user from MongoDB. name The name of the user to find. @@ -275,13 +331,18 @@ def user_find( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash - salt '*' mongodb.user_find + salt '*' mongodb.user_find """ - conn = _connect(user, password, host, port, authdb=authdb) + conn = _connect(user, password, host, port, authdb=authdb, ssl=ssl) if not conn: err_msg = "Failed to connect to MongoDB database {}:{}".format(host, port) log.error(err_msg) @@ -296,10 +357,16 @@ def user_find( def user_list( - user=None, password=None, host=None, port=None, database="admin", authdb=None + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + ssl=False, ): """ - List users of a MongoDB database + List users of a MongoDB database. user The user to connect to MongoDB as. Default is None. @@ -319,13 +386,18 @@ def user_list( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash salt '*' mongodb.user_list """ - conn = _connect(user, password, host, port, authdb=authdb) + conn = _connect(user, password, host, port, authdb=authdb, ssl=ssl) if not conn: return "Failed to connect to mongo database" @@ -352,10 +424,17 @@ def user_list( def user_exists( - name, user=None, password=None, host=None, port=None, database="admin", authdb=None + name, + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + ssl=False, ): """ - Checks if a user exists in MongoDB + Checks if a user exists in MongoDB. user The user to connect to MongoDB as. Default is None. @@ -375,13 +454,18 @@ def user_exists( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash - salt '*' mongodb.user_exists + salt '*' mongodb.user_exists """ - users = user_list(user, password, host, port, database, authdb) + users = user_list(user, password, host, port, database, authdb, ssl=ssl) if isinstance(users, str): return "Failed to connect to mongo database" @@ -403,9 +487,10 @@ def user_create( database="admin", authdb=None, roles=None, + ssl=False, ): """ - Create a MongoDB user + Create a MongoDB user. name The name of the user to create. @@ -434,13 +519,18 @@ def user_create( roles The roles that should be associated with the user. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash salt '*' mongodb.user_create """ - conn = _connect(user, password, host, port, authdb=authdb) + conn = _connect(user, password, host, port, authdb=authdb, ssl=ssl) if not conn: return "Failed to connect to mongo database" @@ -460,10 +550,17 @@ def user_create( def user_remove( - name, user=None, password=None, host=None, port=None, database="admin", authdb=None + name, + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + ssl=False, ): """ - Remove a MongoDB user + Remove a MongoDB user. name The name of the user that should be removed. @@ -483,13 +580,18 @@ def user_remove( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash salt '*' mongodb.user_remove """ - conn = _connect(user, password, host, port) + conn = _connect(user, password, host, port, ssl=ssl) if not conn: return "Failed to connect to mongo database" @@ -503,8 +605,9 @@ def user_remove( return True +# TODO: Add SSL arg + docs -W. Werner, 2022-02-08 def user_roles_exists( - name, roles, database, user=None, password=None, host=None, port=None, authdb=None + name, roles, database, user=None, password=None, host=None, port=None, authdb=None, ssl=False ): """ Checks if a user of a MongoDB database has specified roles @@ -548,7 +651,7 @@ def user_roles_exists( except Exception: # pylint: disable=broad-except return "Roles provided in wrong format" - users = user_list(user, password, host, port, database, authdb) + users = user_list(user, password, host, port, database, authdb, ssl) if isinstance(users, str): return "Failed to connect to mongo database" @@ -566,8 +669,9 @@ def user_roles_exists( return False +# TODO: Add SSL arg + docs -W. Werner, 2022-02-08 def user_grant_roles( - name, roles, database, user=None, password=None, host=None, port=None, authdb=None + name, roles, database, user=None, password=None, host=None, port=None, authdb=None, ssl=False ): """ Grant one or many roles to a MongoDB user @@ -596,6 +700,11 @@ def user_grant_roles( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Examples: .. code-block:: bash @@ -606,7 +715,7 @@ def user_grant_roles( salt '*' mongodb.user_grant_roles janedoe '[{"role": "readWrite", "db": "dbname" }, {"role": "read", "db": "otherdb"}]' dbname admin adminpwd localhost 27017 """ - conn = _connect(user, password, host, port, authdb=authdb) + conn = _connect(user, password, host, port, authdb=authdb, ssl=ssl) if not conn: return "Failed to connect to mongo database" @@ -628,8 +737,9 @@ def user_grant_roles( return True +# TODO: Add SSL arg + docs -W. Werner, 2022-02-08 def user_revoke_roles( - name, roles, database, user=None, password=None, host=None, port=None, authdb=None + name, roles, database, user=None, password=None, host=None, port=None, authdb=None, ssl=False ): """ Revoke one or many roles to a MongoDB user @@ -655,6 +765,11 @@ def user_revoke_roles( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Examples: .. code-block:: bash @@ -665,7 +780,7 @@ def user_revoke_roles( salt '*' mongodb.user_revoke_roles janedoe '[{"role": "readWrite", "db": "dbname" }, {"role": "read", "db": "otherdb"}]' dbname admin adminpwd localhost 27017 """ - conn = _connect(user, password, host, port, authdb=authdb) + conn = _connect(user, password, host, port, authdb=authdb, ssl=ssl) if not conn: return "Failed to connect to mongo database" @@ -695,6 +810,7 @@ def collection_create( port=None, database="admin", authdb=None, + ssl=False, ): """ .. versionadded:: 3006.0 @@ -719,6 +835,9 @@ def collection_create( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + CLI Example: .. code-block:: bash @@ -726,7 +845,7 @@ def collection_create( salt '*' mongodb.collection_create mycollection """ - conn = _connect(user, password, host, port, database, authdb) + conn = _connect(user, password, host, port, database, authdb, ssl) if not conn: return "Failed to connect to mongo database" @@ -750,6 +869,177 @@ def collection_drop( port=None, database="admin", authdb=None, + ssl=False, +): + """ + .. versionadded:: 3006.0 + + Drop a collection in the specified database. + + collection + The name of the collection to drop. + + user + The user to connect to MongoDB as. Default is None. + + password + The password to use to connect to MongoDB as. Default is None. + + host + The host where MongoDB is running. Default is None. + + port + The host where MongoDB is running. Default is None. + + authdb + The MongoDB database to use for authentication. Default is None. + + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + CLI Example: + + .. code-block:: bash + + salt '*' mongodb.collection_drop mycollection + + """ + conn = _connect(user, password, host, port, database, authdb, ssl) + if not conn: + return "Failed to connect to mongo database" + + try: + log.info("Dropping %s.%s", database, collection) + mdb = pymongo.database.Database(conn, database) + mdb.drop_collection(collection) + except pymongo.errors.PyMongoError as err: + log.error( + "Creating collection %r.%r failed with error %s", database, collection, err + ) + return err + return True + + +def collections_list( + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + ssl=False, +): + """ + .. versionadded:: 3006.0 + + List the collections available in the specified database. + + user + The user to connect to MongoDB as. Default is None. + + password + The password to use to connect to MongoDB as. Default is None. + + host + The host where MongoDB is running. Default is None. + + port + The host where MongoDB is running. Default is None. + + authdb + The MongoDB database to use for authentication. Default is None. + + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + CLI Example: + + .. code-block:: bash + + salt '*' mongodb.collections_list mycollection + + """ + conn = _connect(user, password, host, port, database, authdb, ssl) + if not conn: + return "Failed to connect to mongo database" + + try: + mdb = pymongo.database.Database(conn, database) + ret = mdb.list_collection_names() + except pymongo.errors.PyMongoError as err: + log.error("Listing collections failed with error %s", err) + return err + return ret + + +def collection_create( + collection, + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + ssl=False, +): + """ + .. versionadded:: 3006.0 + + Create a collection in the specified database. + + collection + The name of the collection to create. + + user + The user to connect to MongoDB as. Default is None. + + password + The password to use to connect to MongoDB as. Default is None. + + host + The host where MongoDB is running. Default is None. + + port + The host where MongoDB is running. Default is None. + + authdb + The MongoDB database to use for authentication. Default is None. + + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + CLI Example: + + .. code-block:: bash + + salt '*' mongodb.collection_create mycollection + + """ + conn = _connect(user, password, host, port, database, authdb, ssl) + if not conn: + return "Failed to connect to mongo database" + + try: + log.info("Creating %s.%s", database, collection) + mdb = pymongo.database.Database(conn, database) + mdb.create_collection(collection) + except pymongo.errors.PyMongoError as err: + log.error( + "Creating collection %r.%r failed with error %s", database, collection, err + ) + return err + return True + + +def collection_drop( + collection, + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + ssl=False, ): """ .. versionadded:: 3006.0 @@ -781,7 +1071,7 @@ def collection_drop( salt '*' mongodb.collection_drop mycollection """ - conn = _connect(user, password, host, port, database, authdb) + conn = _connect(user, password, host, port, database, authdb, ssl) if not conn: return "Failed to connect to mongo database" @@ -804,6 +1094,7 @@ def collections_list( port=None, database="admin", authdb=None, + ssl=False, ): """ .. versionadded:: 3006.0 @@ -825,6 +1116,9 @@ def collections_list( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + CLI Example: .. code-block:: bash @@ -832,7 +1126,7 @@ def collections_list( salt '*' mongodb.collections_list mycollection """ - conn = _connect(user, password, host, port, database, authdb) + conn = _connect(user, password, host, port, database, authdb, ssl) if not conn: return "Failed to connect to mongo database" @@ -845,6 +1139,7 @@ def collections_list( return ret +# TODO: Add SSL arg + docs -W. Werner, 2022-02-08 def insert( objects, collection, @@ -854,6 +1149,7 @@ def insert( port=None, database="admin", authdb=None, + ssl=False, ): """ Insert an object or list of objects into a collection @@ -879,6 +1175,11 @@ def insert( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash @@ -886,7 +1187,7 @@ def insert( salt '*' mongodb.insert '[{"foo": "FOO", "bar": "BAR"}, {"foo": "BAZ", "bar": "BAM"}]' mycollection """ - conn = _connect(user, password, host, port, database, authdb) + conn = _connect(user, password, host, port, database, authdb, ssl) if not conn: return "Failed to connect to mongo database" @@ -906,6 +1207,7 @@ def insert( return err +# TODO: Add SSL arg + docs -W. Werner, 2022-02-08 def update_one( objects, collection, @@ -915,6 +1217,7 @@ def update_one( port=None, database="admin", authdb=None, + ssl=False ): """ Update an object into a collection @@ -943,6 +1246,11 @@ def update_one( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash @@ -950,7 +1258,7 @@ def update_one( salt '*' mongodb.update_one '{"_id": "my_minion"} {"bar": "BAR"}' mycollection """ - conn = _connect(user, password, host, port, database, authdb) + conn = _connect(user, password, host, port, database, authdb, ssl) if not conn: return "Failed to connect to mongo database" @@ -979,7 +1287,7 @@ def update_one( _update_doc = document[1] # need a string to perform the test, so using objs[0] - test_f = find(collection, objs[0], user, password, host, port, database, authdb) + test_f = find(collection, objs[0], user, password, host, port, database, authdb, ssl) if not isinstance(test_f, list): return "The find result is not well formatted. An error appears; cannot update." elif not test_f: @@ -999,6 +1307,7 @@ def update_one( return err +# TODO: Add SSL arg + docs -W. Werner, 2022-02-08 def find( collection, query=None, @@ -1008,6 +1317,7 @@ def find( port=None, database="admin", authdb=None, + ssl=False, ): """ Find an object or list of objects in a collection @@ -1033,6 +1343,11 @@ def find( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash @@ -1040,7 +1355,7 @@ def find( salt '*' mongodb.find mycollection '[{"foo": "FOO", "bar": "BAR"}]' """ - conn = _connect(user, password, host, port, database, authdb) + conn = _connect(user, password, host, port, database, authdb, ssl) if not conn: return "Failed to connect to mongo database" @@ -1068,6 +1383,7 @@ def find( return err +# TODO: Add SSL arg + docs -W. Werner, 2022-02-08 def remove( collection, query=None, @@ -1078,6 +1394,7 @@ def remove( database="admin", w=1, authdb=None, + ssl=False, ): """ Remove an object or list of objects from a collection @@ -1109,6 +1426,11 @@ def remove( authdb The MongoDB database to use for authentication. Default is None. + ssl + Whether or not to connect to MongoDB over SSL. Default ``False``. + + .. versionadded:: 3006 + CLI Example: .. code-block:: bash @@ -1116,7 +1438,7 @@ def remove( salt '*' mongodb.remove mycollection '[{"foo": "FOO", "bar": "BAR"}, {"foo": "BAZ", "bar": "BAM"}]' """ - conn = _connect(user, password, host, port, database, authdb) + conn = _connect(user, password, host, port, database, authdb, ssl) if not conn: return "Failed to connect to mongo database" diff --git a/salt/states/mongodb_database.py b/salt/states/mongodb_database.py index 4df6c4297ed..f81d500a369 100644 --- a/salt/states/mongodb_database.py +++ b/salt/states/mongodb_database.py @@ -18,6 +18,7 @@ def __virtual__(): return (False, "mongodb module could not be loaded") +# TODO: Add SSL arg + docs -W. Werner, 2022-02-08 def absent(name, user=None, password=None, host=None, port=None, authdb=None): """ Ensure that the named database is absent. Note that creation doesn't make diff --git a/salt/states/mongodb_user.py b/salt/states/mongodb_user.py index a83cd9b5686..bf2ced35885 100644 --- a/salt/states/mongodb_user.py +++ b/salt/states/mongodb_user.py @@ -21,10 +21,11 @@ def present( database="admin", user=None, password=None, - host="localhost", - port=27017, + host=None, + port=None, authdb=None, roles=None, + ssl=False, ): """ Ensure that the user is present with the specified properties @@ -59,6 +60,9 @@ def present( roles The roles assigned to user specified with the ``name`` parameter + ssl + Whether or not to use SSL to connect to mongodb. Default False. + Example: .. code-block:: yaml @@ -90,15 +94,22 @@ def present( # Check for valid port try: - port = int(port) + port = int(port or __salt__["config.option"]("mongodb.port")) except TypeError: ret["result"] = False - ret["comment"] = "Port ({}) is not an integer.".format(port) + ret["comment"] = "Port ({!r}) is not an integer.".format(port) return ret # check if user exists users = __salt__["mongodb.user_find"]( - name, user, password, host, port, database, authdb + name, + user, + password, + host, + port, + database, + authdb, + ssl=ssl, ) if len(users) > 0: # check for errors returned in users e.g. @@ -140,6 +151,7 @@ def present( database=database, authdb=authdb, roles=roles, + ssl=ssl, ) return ret @@ -165,6 +177,7 @@ def present( database=database, authdb=authdb, roles=roles, + ssl=ssl, ): ret["comment"] = "User {} has been created".format(name) ret["changes"][name] = "Present" @@ -176,7 +189,14 @@ def present( def absent( - name, user=None, password=None, host=None, port=None, database="admin", authdb=None + name, + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + ssl=False, ): """ Ensure that the named user is absent @@ -202,12 +222,15 @@ def absent( authdb The database in which to authenticate + + ssl + Whether or not to use SSL to connect to mongodb. Default False. """ ret = {"name": name, "changes": {}, "result": True, "comment": ""} # check if user exists and remove it user_exists = __salt__["mongodb.user_exists"]( - name, user, password, host, port, database=database, authdb=authdb + name, user, password, host, port, database=database, authdb=authdb, ssl=ssl ) if user_exists is True: if __opts__["test"]: @@ -215,7 +238,14 @@ def absent( ret["comment"] = "User {} is present and needs to be removed".format(name) return ret if __salt__["mongodb.user_remove"]( - name, user, password, host, port, database=database, authdb=authdb + name, + user, + password, + host, + port, + database=database, + authdb=authdb, + ssl=ssl, ): ret["comment"] = "User {} has been removed".format(name) ret["changes"][name] = "Absent" diff --git a/tests/pytests/unit/states/test_mongodb_database.py b/tests/pytests/unit/states/test_mongodb_database.py index e82bb159d53..e53fce50d9d 100644 --- a/tests/pytests/unit/states/test_mongodb_database.py +++ b/tests/pytests/unit/states/test_mongodb_database.py @@ -40,3 +40,6 @@ def test_absent(): comt = "Database {} is not present".format(name) ret.update({"comment": comt, "changes": {}}) assert mongodb_database.absent(name) == ret + + +# TODO: Add test to ensure that when remove is called that it passes the SSL args on as expected -W. Werner, 2022-02-08 diff --git a/tests/pytests/unit/states/test_mongodb_user.py b/tests/pytests/unit/states/test_mongodb_user.py index cc1524c85c5..f35d24f442d 100644 --- a/tests/pytests/unit/states/test_mongodb_user.py +++ b/tests/pytests/unit/states/test_mongodb_user.py @@ -4,13 +4,35 @@ import pytest +import salt.modules.mongodb import salt.states.mongodb_user as mongodb_user -from tests.support.mock import MagicMock, patch +from tests.support.mock import MagicMock, call, patch @pytest.fixture def configure_loader_modules(): - return {mongodb_user: {"__opts__": {"test": True}}} + salt.modules.mongodb.pymongo = MagicMock() + salt.modules.mongodb.pymongo.errors.PyMongoError = Exception + salt.modules.mongodb.HAS_MONGODB = True + fake_config = { + "mongodb.host": "example.com", + "mongodb.port": 42, + } + fake_salt = { + "mongodb.user_exists": salt.modules.mongodb.user_exists, + "mongodb.user_find": salt.modules.mongodb.user_find, + "mongodb.user_create": salt.modules.mongodb.user_create, + "mongodb.user_remove": salt.modules.mongodb.user_remove, + "config.option": fake_config.get, + } + with patch("salt.modules.mongodb._version", autospec=True, return_value=4): + yield { + mongodb_user: { + "__opts__": {"test": True}, + "__salt__": fake_salt, + }, + salt.modules.mongodb: {"__salt__": fake_salt}, + } def test_present(): @@ -20,11 +42,10 @@ def test_present(): name = "myapp" passwd = "password-of-myapp" - ret = {"name": name, "result": False, "comment": "", "changes": {}} + comt = "Port ({1, 2, 3}) is not an integer." + ret = {"name": name, "result": False, "comment": comt, "changes": {}} - comt = "Port ({}) is not an integer." - ret.update({"comment": comt}) - assert mongodb_user.present(name, passwd, port={}) == ret + assert mongodb_user.present(name, passwd, port={1, 2, 3}) == ret mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=[]) @@ -74,3 +95,68 @@ def test_absent(): comt = "User {} is not present".format(name) ret.update({"comment": comt, "result": True, "changes": {}}) assert mongodb_user.absent(name) == ret + + +@pytest.mark.parametrize( + "expected_ssl, absent_kwargs", + [ + (True, {"name": "mr_fnord", "ssl": True}), + (False, {"name": "mr_fnord", "ssl": False}), + (False, {"name": "mr_fnord", "ssl": None}), + (False, {"name": "mr_fnord"}), + ], +) +def test_when_absent_is_called_it_should_pass_the_correct_ssl_argument_to_MongoClient( + expected_ssl, absent_kwargs +): + with patch.dict(mongodb_user.__opts__, {"test": False}), patch( + "salt.modules.mongodb._LooseVersion", autospec=True, return_value=4 + ): + salt.modules.mongodb.pymongo.database.Database.return_value.command.return_value = { + "users": [ + { + "user": absent_kwargs["name"], + "roles": [{"db": "kaiser"}, {"db": "dinner"}], + } + ] + } + mongodb_user.absent(**absent_kwargs) + salt.modules.mongodb.pymongo.MongoClient.assert_has_calls( + [ + call(host="example.com", port=42, ssl=expected_ssl), + call().__bool__(), + call(host="example.com", port=42, ssl=expected_ssl), + call().__bool__(), + ] + ) + + +@pytest.mark.parametrize( + "expected_ssl, present_kwargs", + [ + (True, {"name": "mr_fnord", "ssl": True}), + (False, {"name": "mr_fnord", "ssl": False}), + (False, {"name": "mr_fnord", "ssl": None}), + (False, {"name": "mr_fnord"}), + ], +) +@pytest.mark.parametrize( + "users", + [[], [{"roles": [{"db": "kaiser"}, {"db": "fnord"}]}]], +) +def test_when_present_is_called_it_should_pass_the_correct_ssl_argument_to_MongoClient( + expected_ssl, present_kwargs, users +): + with patch.dict(mongodb_user.__opts__, {"test": False}): + salt.modules.mongodb.pymongo.database.Database.return_value.command.return_value = { + "users": users + } + mongodb_user.present(passwd="fnord", **present_kwargs) + salt.modules.mongodb.pymongo.MongoClient.assert_has_calls( + [ + call(host="example.com", port=42, ssl=expected_ssl), + call().__bool__(), + call(host="example.com", port=42, ssl=expected_ssl), + call().__bool__(), + ] + )