|
| 1 | +# Copyright 2015 MongoDB, Inc. |
| 2 | +# |
| 3 | +# Licensed under the Apache License, Version 2.0 (the "License"),; |
| 4 | +# you may not use this file except in compliance with the License. |
| 5 | +# You may obtain a copy of the License at |
| 6 | +# |
| 7 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +# |
| 9 | +# Unless required by applicable law or agreed to in writing, software |
| 10 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +# See the License for the specific language governing permissions and |
| 13 | +# limitations under the License. |
| 14 | + |
| 15 | +from collections import namedtuple |
| 16 | + |
| 17 | +from mockupdb import * |
| 18 | +from pymongo import ReadPreference |
| 19 | + |
| 20 | +__all__ = ['operations', 'upgrades'] |
| 21 | + |
| 22 | + |
| 23 | +Operation = namedtuple( |
| 24 | + 'operation', |
| 25 | + ['name', 'function', 'reply', 'op_type', 'not_master']) |
| 26 | +"""Client operations on MongoDB. |
| 27 | +
|
| 28 | +Each has a human-readable name, a function that actually executes a test, and |
| 29 | +a type that maps to one of the types in the Server Selection Spec: |
| 30 | +'may-use-secondary', 'must-use-primary', etc. |
| 31 | +
|
| 32 | +The special type 'always-use-secondary' applies to an operation with an explicit |
| 33 | +read mode, like the operation "command('c', read_preference=SECONDARY)". |
| 34 | +
|
| 35 | +The not-master response is how a secondary responds to a must-use-primary op, |
| 36 | +or how a recovering member responds to a may-use-secondary op. |
| 37 | +
|
| 38 | +Example uses: |
| 39 | +
|
| 40 | +We can use "find_one" to validate that the SlaveOk bit is set when querying a |
| 41 | +standalone, even with mode PRIMARY, but that it isn't set when sent to a mongos |
| 42 | +with mode PRIMARY. Or it can validate that "$readPreference" is included in |
| 43 | +mongos queries except with mode PRIMARY or SECONDARY_PREFERRED (PYTHON-865). |
| 44 | +
|
| 45 | +We can use "options_old" and "options_new" to test that the driver queries an |
| 46 | +old server's system.namespaces collection, but uses the listCollections command |
| 47 | +on a new server (PYTHON-857). |
| 48 | +
|
| 49 | +"secondary command" is good to test that the client can direct reads to |
| 50 | +secondaries in a replica set, or select a mongos for secondary reads in a |
| 51 | +sharded cluster (PYTHON-868). |
| 52 | +""" |
| 53 | + |
| 54 | +not_master_reply_to_query = OpReply( |
| 55 | + {'$err': 'not master'}, |
| 56 | + flags=REPLY_FLAGS['QueryFailure']) |
| 57 | + |
| 58 | +not_master_reply_to_command = OpReply(ok=0, errmsg='not master') |
| 59 | + |
| 60 | +operations = [ |
| 61 | + Operation( |
| 62 | + 'find_one', |
| 63 | + lambda client: client.db.collection.find_one(), |
| 64 | + reply={'cursor': {'id': 0, 'firstBatch': []}}, |
| 65 | + op_type='may-use-secondary', |
| 66 | + not_master=not_master_reply_to_query), |
| 67 | + Operation( |
| 68 | + 'count', |
| 69 | + lambda client: client.db.collection.count_documents({}), |
| 70 | + reply={'n': 1}, |
| 71 | + op_type='may-use-secondary', |
| 72 | + not_master=not_master_reply_to_command), |
| 73 | + Operation( |
| 74 | + 'aggregate', |
| 75 | + lambda client: client.db.collection.aggregate([]), |
| 76 | + reply={'cursor': {'id': 0, 'firstBatch': []}}, |
| 77 | + op_type='may-use-secondary', |
| 78 | + not_master=not_master_reply_to_command), |
| 79 | + Operation( |
| 80 | + 'mapreduce', |
| 81 | + lambda client: client.db.collection.map_reduce( |
| 82 | + 'function() {}', 'function() {}'), |
| 83 | + reply={'result': {'db': 'db', 'collection': 'out_collection'}}, |
| 84 | + op_type='must-use-primary', |
| 85 | + not_master=not_master_reply_to_command), |
| 86 | + Operation( |
| 87 | + 'inline_mapreduce', |
| 88 | + lambda client: client.db.collection.inline_map_reduce( |
| 89 | + 'function() {}', 'function() {}', {'out': {'inline': 1}}), |
| 90 | + reply={'results': []}, |
| 91 | + op_type='may-use-secondary', |
| 92 | + not_master=not_master_reply_to_command), |
| 93 | + Operation( |
| 94 | + 'options', |
| 95 | + lambda client: client.db.collection.options(), |
| 96 | + reply={'cursor': {'id': 0, 'firstBatch': []}}, |
| 97 | + op_type='must-use-primary', |
| 98 | + not_master=not_master_reply_to_command), |
| 99 | + Operation( |
| 100 | + 'command', |
| 101 | + lambda client: client.db.command('foo'), |
| 102 | + reply={'ok': 1}, |
| 103 | + op_type='must-use-primary', # Ignores client's read preference. |
| 104 | + not_master=not_master_reply_to_command), |
| 105 | + Operation( |
| 106 | + 'secondary command', |
| 107 | + lambda client: |
| 108 | + client.db.command('foo', read_preference=ReadPreference.SECONDARY), |
| 109 | + reply={'ok': 1}, |
| 110 | + op_type='always-use-secondary', |
| 111 | + not_master=OpReply(ok=0, errmsg='node is recovering')), |
| 112 | + Operation( |
| 113 | + 'listCollections', |
| 114 | + lambda client: client.db.collection_names(), |
| 115 | + reply={'cursor': {'id': 0, 'firstBatch': []}}, |
| 116 | + op_type='must-use-primary', |
| 117 | + not_master=not_master_reply_to_command), |
| 118 | + Operation( |
| 119 | + 'listIndexes', |
| 120 | + lambda client: client.db.collection.index_information(), |
| 121 | + reply={'cursor': {'id': 0, 'firstBatch': []}}, |
| 122 | + op_type='must-use-primary', |
| 123 | + not_master=not_master_reply_to_command), |
| 124 | +] |
| 125 | + |
| 126 | + |
| 127 | +_ops_by_name = dict([(op.name, op) for op in operations]) |
| 128 | + |
| 129 | +Upgrade = namedtuple('Upgrade', |
| 130 | + ['name', 'function', 'old', 'new', 'wire_version']) |
| 131 | + |
| 132 | +upgrades = [ |
| 133 | + Upgrade('index_information', |
| 134 | + lambda client: client.db.collection.index_information(), |
| 135 | + old=OpQuery(namespace='db.system.indexes'), |
| 136 | + new=Command('listIndexes', 'collection', namespace='db'), |
| 137 | + wire_version=3), |
| 138 | + Upgrade('collection_names', |
| 139 | + lambda client: client.db.collection_names(), |
| 140 | + old=Command('aggregate', 'system.namespaces', namespace='db'), |
| 141 | + new=Command('listCollections', namespace='db'), |
| 142 | + wire_version=3), |
| 143 | + Upgrade('options', |
| 144 | + lambda client: client.db.collection.options(), |
| 145 | + old=Command('aggregate', 'system.namespaces', namespace='db'), |
| 146 | + new=Command('listCollections', namespace='db'), |
| 147 | + wire_version=3), |
| 148 | +] |
0 commit comments