1# test code for the mongodb_shard module
2# (c) 2019,  Rhys Campbell <rhys.james.campbell@googlemail.com>
3
4# This file is part of Ansible
5#
6# Ansible is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# Ansible is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
18
19# ============================================================
20
21- name: Ensure tests home exists
22  file:
23    path: "{{ remote_tmp_dir }}/tests"
24    state: directory
25
26- include_tasks: mongod_teardown.yml
27
28- set_fact:
29    current_replicaset: "{{ mongodb_replicaset1 }}"
30
31- set_fact:
32    mongodb_nodes: [ 3001, 3002, 3003 ]
33
34- include_tasks: mongod_replicaset.yml
35
36- set_fact:
37    current_replicaset: "{{ mongodb_replicaset2 }}"
38
39- set_fact:
40    mongodb_nodes: [ 3004, 3005, 3006 ]
41
42- include_tasks: mongod_replicaset.yml
43
44- name: Launch cfg server
45  command: mongod --configsvr --port 4000 --dbpath {{ remote_tmp_dir }}/config --logpath {{ remote_tmp_dir }}/config.log --smallfiles --replSet "{{ configsrv_replicaset }}" --fork
46
47- name: Create replicaset1 with module
48  mongodb_replicaset:
49    login_host: "localhost"
50    login_port: 3001
51    login_database: "admin"
52    replica_set: "{{ mongodb_replicaset1 }}"
53    members:
54     - "localhost:3001"
55     - "localhost:3002"
56     - "localhost:3003"
57
58- name: Create replicaset2 with module
59  mongodb_replicaset:
60    login_host: "localhost"
61    login_port: 3004
62    login_database: "admin"
63    replica_set: "{{ mongodb_replicaset2 }}"
64    members:
65     - "localhost:3004"
66     - "localhost:3005"
67     - "localhost:3006"
68
69- name: Create config srv replicaset with module
70  mongodb_replicaset:
71    login_host: "localhost"
72    login_port: 4000
73    login_database: "admin"
74    replica_set: "{{ configsrv_replicaset }}"
75    validate: no
76    members:
77     - "localhost:4000"
78
79- name: Get config server replset mongo_output
80  command: mongo admin --port 4000 --eval "rs.status();"
81  register: cfg_replset_output
82
83- name: Assert that replset is a config server
84  assert:
85    that:
86      - "'\"configsvr\" : true' in cfg_replset_output.stdout"
87      - "'\"set\" : \"{{ configsrv_replicaset }}\"' in cfg_replset_output.stdout"
88
89- name: Launch mongos
90  command: mongos --configdb "{{ configsrv_replicaset }}/localhost:4000" --logpath "{{ remote_tmp_dir }}/tests/mongos.log" --port 27017 --fork
91
92- name: Ensure is_primary script exists on host
93  copy:
94    src: js/is_primary.js
95    dest: "{{ remote_tmp_dir }}/tests/is_primary.js"
96
97- name: Ensure host reaches primary before proceeding 3001
98  command: mongo admin --port 3001 "{{ remote_tmp_dir }}/tests/is_primary.js"
99
100- name: Ensure host reaches primary before proceeding 3004
101  command: mongo admin --port 3004 "{{ remote_tmp_dir }}/tests/is_primary.js"
102
103- name: Add shard 1
104  mongodb_shard:
105    login_user: admin
106    login_password: admin
107    shard: "{{ mongodb_replicaset1 }}/localhost:3001"
108    state: present
109
110- name: Add shard 2
111  mongodb_shard:
112    login_user: admin
113    login_password: admin
114    shard: "{{ mongodb_replicaset2 }}/localhost:3004"
115    state: present
116
117- name: Get replicaset info
118  command: mongo admin --eval "sh.status()" --port 27017
119  register: mongo_output
120
121- name: Assert shard name is in mongo_output
122  assert:
123    that:
124      - "mongo_output.changed == true"
125      - "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
126      - "'{{ mongodb_replicaset2 }}/localhost:3004,localhost:3005,localhost:3006' in mongo_output.stdout"
127      - "'balancer' in mongo_output.stdout"
128
129- name: Remove shard 2
130  mongodb_shard:
131    login_user: admin
132    login_password: admin
133    shard: "{{ mongodb_replicaset2 }}"
134    state: absent
135
136- name: Get replicaset info
137  command: mongo admin --eval "sh.status()" --port 27017
138  register: mongo_output
139
140- name: Assert shard 2 is draining
141  assert:
142    that:
143      - "mongo_output.changed == true"
144      - "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
145      - "'\"draining\" : true' in mongo_output.stdout"
146      - "'balancer' in mongo_output.stdout"
147
148- name: Run remove command again to finalize shard removal
149  mongodb_shard:
150    login_user: admin
151    login_password: admin
152    shard: "{{ mongodb_replicaset2 }}"
153    state: absent
154
155- name: Get replicaset info
156  command: mongo admin --eval "sh.status()" --port 27017
157  register: mongo_output
158
159- name: Assert shard 2 is not present
160  assert:
161    that:
162      - "mongo_output.changed == true"
163      - "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
164      - "'{{ mongodb_replicaset2 }}/localhost:3004,localhost:3005,localhost:3006' not in mongo_output.stdout"
165      - "'balancer' in mongo_output.stdout"
166
167# Repeat of above with auth enabled
168- include_tasks: mongod_teardown.yml
169
170- set_fact:
171    current_replicaset: "{{ mongodb_replicaset1 }}"
172
173- set_fact:
174    mongodb_nodes: [ 3001, 3002, 3003 ]
175
176- include_tasks: mongod_replicaset.yml
177
178- set_fact:
179    current_replicaset: "{{ mongodb_replicaset2 }}"
180
181- set_fact:
182    mongodb_nodes: [ 3004, 3005, 3006 ]
183
184- include_tasks: mongod_replicaset.yml
185
186- name: Create replicaset1 with module
187  mongodb_replicaset:
188    login_host: "localhost"
189    login_port: 3001
190    login_database: "admin"
191    replica_set: "{{ mongodb_replicaset1 }}"
192    members:
193     - "localhost:3001"
194     - "localhost:3002"
195     - "localhost:3003"
196
197- name: Create replicaset2 with module
198  mongodb_replicaset:
199    login_host: "localhost"
200    login_port: 3004
201    login_database: "admin"
202    replica_set: "{{ mongodb_replicaset2 }}"
203    members:
204     - "localhost:3004"
205     - "localhost:3005"
206     - "localhost:3006"
207
208- name: Launch cfg server
209  command: mongod --configsvr --port 4000 --dbpath {{ remote_tmp_dir }}/config --logpath {{ remote_tmp_dir }}/config.log --smallfiles --replSet "{{ configsrv_replicaset }}" --fork
210
211- name: Create config srv replicaset with module
212  mongodb_replicaset:
213    login_port: 4000
214    login_database: "admin"
215    replica_set: "{{ configsrv_replicaset }}"
216    validate: no
217    members:
218     - "localhost:4000"
219
220- name: Ensure host reaches primary before proceeding 3001
221  command: mongo admin --port 3001 "{{ remote_tmp_dir }}/tests/is_primary.js"
222
223- name: Ensure host reaches primary before proceeding 3004
224  command: mongo admin --port 3004 "{{ remote_tmp_dir }}/tests/is_primary.js"
225
226- name: Ensure host reaches primary before proceeding 4000
227  command: mongo admin --port 4000 "{{ remote_tmp_dir }}/tests/is_primary.js"
228
229- name: Add mongodb admin user to each shard 3.4+
230  mongodb_user:
231    login_host: localhost
232    login_port: "{{ item.port }}"
233    replica_set: "{{ item.rs }}"
234    database: admin
235    name: "{{ mongodb_admin_user }}"
236    password: "{{ mongodb_admin_password }}"
237    roles: ["root"]
238    state: present
239  register: mongo_admin_user
240  with_items:
241    - { "port": 3001, "rs": "{{ mongodb_replicaset1 }}" }
242    - { "port": 3004, "rs": "{{ mongodb_replicaset2 }}" }
243    - { "port": 4000, "rs": "{{ configsrv_replicaset }}" }
244  when: mongodb_version not in ["3.2", "4.0"] and test_mongo_auth == True
245
246- name: Add mongodb admin user to each shard 3.2 ^ 4.0
247  mongodb_user:
248    login_host: localhost
249    login_port: "{{ item.port }}"
250    replica_set: "{{ item.rs }}"
251    database: admin
252    name: "{{ mongodb_admin_user }}"
253    password: "{{ mongodb_admin_password }}"
254    roles: ["root"]
255    state: present
256  register: mongo_admin_user
257  with_items:
258    - { "port": 3001, "rs": "{{ mongodb_replicaset1 }}" }
259    - { "port": 3004, "rs": "{{ mongodb_replicaset2 }}" }
260  when: mongodb_version not in ["3.2", "4.0"] and test_mongo_auth == True
261
262# mongodb_user throws an error when creating a user on 3.2 (also on 4.0 with Ubuntu 18.04)
263# 'majority' is the only valid write concern when writing to config server replica sets
264- name: Copy create_user_root_3.2.js.j2 template to host
265  template:
266    src: create_user_root_3.2.js.j2
267    dest: /root/create_user_root_3.2.js
268  when: mongodb_version in ["3.2", "4.0"]
269
270- name: Copy script to host
271  template:
272    src: files/bash/ensure_primary.sh.j2
273    dest: /root/ensure_primary.sh
274
275- name: Execute script for 3001
276  script: /root/ensure_primary.sh 3001 0
277# We do this here because sometimes 3004 instance seems to be demoted
278- name: Execute script for 3004
279  script: /root/ensure_primary.sh 3004 0
280
281- name: Create admin user on 3.2 and 4.0 config replset
282  shell: mongo admin --port {{ item }} /root/create_user_root_3.2.js
283  with_items:
284    - 4000
285    - 3001
286    - 3004
287  when: mongodb_version in ["3.2", "4.0"]
288
289- name: Murder all mongod processes
290  shell: pkill -{{ kill_signal }} mongod || true;
291
292- name: Wait for ports to get out of TIME_WAIT
293  wait_for:
294    port: '{{ item }}'
295    state: drained
296  with_sequence: start=3001 end=3006
297
298- set_fact:
299    current_replicaset: "{{ mongodb_replicaset1 }}"
300
301- set_fact:
302    mongodb_nodes: [ 3001, 3002, 3003 ]
303
304- set_fact:
305    mongod_auth: true
306
307- include_tasks: mongod_replicaset.yml
308
309- set_fact:
310    current_replicaset: "{{ mongodb_replicaset2 }}"
311
312- set_fact:
313    mongodb_nodes: [ 3004, 3005, 3006 ]
314
315- set_fact:
316    mongod_auth: true
317
318- include_tasks: mongod_replicaset.yml
319
320- name: Launch cfg server with auth
321  command: mongod --configsvr --port 4000 --dbpath {{ remote_tmp_dir }}/config --logpath {{ remote_tmp_dir }}/config.log --smallfiles --replSet "{{ configsrv_replicaset }}" --fork --auth --keyFile {{ remote_tmp_dir }}/my.key
322
323- name: Execute script for 3001
324  script: /root/ensure_primary.sh 3001 1
325
326- name: Execute script for 3004
327  script: /root/ensure_primary.sh 3004 1
328
329- name: Ensure host reaches primary before proceeding 3001
330  command: mongo admin --port 3001 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} "{{ remote_tmp_dir }}/tests/is_primary.js"
331
332- name: Ensure host reaches primary before proceeding 3004
333  command: mongo admin --port 3004 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} "{{ remote_tmp_dir }}/tests/is_primary.js"
334
335- name: Launch mongos
336  command: mongos --configdb "{{ configsrv_replicaset }}/localhost:4000" --logpath "{{ remote_tmp_dir }}/mongos.log" --port 27017 --fork --keyFile {{ remote_tmp_dir }}/my.key
337
338- name: Wait for mongos to become active
339  wait_for:
340    host: localhost
341    port: 4000
342    delay: 1
343
344- name: Add shard 1
345  mongodb_shard:
346    login_user: "{{ mongodb_admin_user }}"
347    login_password: "{{ mongodb_admin_password }}"
348    shard: "{{ mongodb_replicaset1 }}/localhost:3001"
349    state: present
350
351- name: Add shard 2
352  mongodb_shard:
353    login_user: "{{ mongodb_admin_user }}"
354    login_password: "{{ mongodb_admin_password }}"
355    shard: "{{ mongodb_replicaset2 }}/localhost:3004"
356    state: present
357
358- name: Test with bad password
359  mongodb_shard:
360    login_user: "{{ mongodb_admin_user }}"
361    login_password: XXXXXXXXXXXX
362    shard: "{{ mongodb_replicaset2 }}/localhost:3004"
363    state: present
364  register: mongodb_shard_bad_pw
365  ignore_errors: True
366
367- name: Assert login failed
368  assert:
369    that:
370      - "mongodb_shard_bad_pw.changed == False"
371      - "'unable to connect to database: Authentication failed.' == mongodb_shard_bad_pw.msg"
372
373- name: Get replicaset info
374  command: mongo admin --eval "sh.status()" --port 27017 -u "{{ mongodb_admin_user }}" -p "{{ mongodb_admin_password }}"
375  register: mongo_output
376
377- name: Assert shard name is in mongo_output
378  assert:
379    that:
380      - "mongo_output.changed == true"
381      - "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
382      - "'{{ mongodb_replicaset2 }}/localhost:3004,localhost:3005,localhost:3006' in mongo_output.stdout"
383      - "'balancer' in mongo_output.stdout"
384
385- name: Remove shard 2
386  mongodb_shard:
387    login_user: "{{ mongodb_admin_user }}"
388    login_password: "{{ mongodb_admin_password }}"
389    shard: "{{ mongodb_replicaset2 }}"
390    state: absent
391
392- name: Get replicaset info
393  command: mongo admin --eval "sh.status()" --port 27017 -u "{{ mongodb_admin_user }}" -p "{{ mongodb_admin_password }}"
394  register: mongo_output
395
396- name: Assert shard 2 is draining
397  assert:
398    that:
399      - "mongo_output.changed == true"
400      - "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
401      - "'\"draining\" : true' in mongo_output.stdout"
402      - "'balancer' in mongo_output.stdout"
403
404- name: Run remove command again to finalize shard removal
405  mongodb_shard:
406    login_user: "{{ mongodb_admin_user }}"
407    login_password: "{{ mongodb_admin_password }}"
408    shard: "{{ mongodb_replicaset2 }}"
409    state: absent
410
411- name: Get replicaset info
412  command: mongo admin --eval "sh.status()" --port 27017 -u "{{ mongodb_admin_user }}" -p "{{ mongodb_admin_password }}"
413  register: mongo_output
414
415- name: Assert shard 2 is not present
416  assert:
417    that:
418      - "mongo_output.changed == true"
419      - "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
420      - "'{{ mongodb_replicaset2 }}/localhost:3004,localhost:3005,localhost:3006' not in mongo_output.stdout"
421      - "'balancer' in mongo_output.stdout"
422
423# TODO - Readd this test once we support serverSelectionTimeoutMS / connectTimeoutMS
424#- name: Run test with unknown host
425#  mongodb_shard:
426#    login_user: "{{ mongodb_admin_user }}"
427#    login_password: "{{ mongodb_admin_password }}"
428#    login_host: "idonotexist"
429#    shard: "{{ mongodb_replicaset2 }}"
430#    state: absent
431#  ignore_errors: True
432#  register: host_does_not_exist
433
434#- name: Assert that "Name or service not known" is in error
435#  assert:
436#    that:
437#      - "host_does_not_exist.changed == False"
438#      - "'unable to connect to database: idonotexist:27017: [Errno -2] Name or service not known' == host_does_not_exist.msg"
439
440# Final clean up to prevent "directory not empty" error
441- include_tasks: mongod_teardown.yml
442