121 lines
4.1 KiB
Python
121 lines
4.1 KiB
Python
# Copyright 2015 MongoDB, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"),;
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
from __future__ import annotations
|
|
|
|
from collections import namedtuple
|
|
|
|
from mockupdb import OpMsgReply, OpReply
|
|
|
|
from pymongo import ReadPreference
|
|
|
|
__all__ = ["operations", "upgrades"]
|
|
|
|
|
|
Operation = namedtuple("Operation", ["name", "function", "reply", "op_type", "not_master"])
|
|
"""Client operations on MongoDB.
|
|
|
|
Each has a human-readable name, a function that actually executes a test, and
|
|
a type that maps to one of the types in the Server Selection Spec:
|
|
'may-use-secondary', 'must-use-primary', etc.
|
|
|
|
The special type 'always-use-secondary' applies to an operation with an explicit
|
|
read mode, like the operation "command('c', read_preference=SECONDARY)".
|
|
|
|
The not-master response is how a secondary responds to a must-use-primary op,
|
|
or how a recovering member responds to a may-use-secondary op.
|
|
|
|
Example uses:
|
|
|
|
We can use "find_one" to validate that the SlaveOk bit is set when querying a
|
|
standalone, even with mode PRIMARY, but that it isn't set when sent to a mongos
|
|
with mode PRIMARY. Or it can validate that "$readPreference" is included in
|
|
mongos queries except with mode PRIMARY or SECONDARY_PREFERRED (PYTHON-865).
|
|
|
|
We can use "options_old" and "options_new" to test that the driver queries an
|
|
old server's system.namespaces collection, but uses the listCollections command
|
|
on a new server (PYTHON-857).
|
|
|
|
"secondary command" is good to test that the client can direct reads to
|
|
secondaries in a replica set, or select a mongos for secondary reads in a
|
|
sharded cluster (PYTHON-868).
|
|
"""
|
|
|
|
not_master_reply = OpMsgReply(ok=0, errmsg="not master")
|
|
|
|
operations = [
|
|
Operation(
|
|
"find_one",
|
|
lambda client: client.db.collection.find_one(),
|
|
reply={"cursor": {"id": 0, "firstBatch": []}},
|
|
op_type="may-use-secondary",
|
|
not_master=not_master_reply,
|
|
),
|
|
Operation(
|
|
"count_documents",
|
|
lambda client: client.db.collection.count_documents({}),
|
|
reply={"n": 1},
|
|
op_type="may-use-secondary",
|
|
not_master=not_master_reply,
|
|
),
|
|
Operation(
|
|
"estimated_document_count",
|
|
lambda client: client.db.collection.estimated_document_count(),
|
|
reply={"n": 1},
|
|
op_type="may-use-secondary",
|
|
not_master=not_master_reply,
|
|
),
|
|
Operation(
|
|
"aggregate",
|
|
lambda client: client.db.collection.aggregate([]),
|
|
reply={"cursor": {"id": 0, "firstBatch": []}},
|
|
op_type="may-use-secondary",
|
|
not_master=not_master_reply,
|
|
),
|
|
Operation(
|
|
"options",
|
|
lambda client: client.db.collection.options(),
|
|
reply={"cursor": {"id": 0, "firstBatch": []}},
|
|
op_type="must-use-primary",
|
|
not_master=not_master_reply,
|
|
),
|
|
Operation(
|
|
"command",
|
|
lambda client: client.db.command("foo"),
|
|
reply={"ok": 1},
|
|
op_type="must-use-primary", # Ignores client's read preference.
|
|
not_master=not_master_reply,
|
|
),
|
|
Operation(
|
|
"secondary command",
|
|
lambda client: client.db.command("foo", read_preference=ReadPreference.SECONDARY),
|
|
reply={"ok": 1},
|
|
op_type="always-use-secondary",
|
|
not_master=OpReply(ok=0, errmsg="node is recovering"),
|
|
),
|
|
Operation(
|
|
"listIndexes",
|
|
lambda client: client.db.collection.index_information(),
|
|
reply={"cursor": {"id": 0, "firstBatch": []}},
|
|
op_type="must-use-primary",
|
|
not_master=not_master_reply,
|
|
),
|
|
]
|
|
|
|
|
|
_ops_by_name = {op.name: op for op in operations}
|
|
|
|
Upgrade = namedtuple("Upgrade", ["name", "function", "old", "new", "wire_version"])
|
|
|
|
upgrades = []
|