Skip to content

Commit 3e26a2a

Browse files
authored
Feature/refactor mongo (#661)
- add standalone mongo option - add cluster node add or removal option - add cluster health check - add readable error messages - add manual in readme - add todo items in readme - remove mongodb container networks from myconext, manage and oidcng
1 parent 9821d28 commit 3e26a2a

18 files changed

Lines changed: 507 additions & 139 deletions

File tree

environments/template/group_vars/mongo_servers.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
replica_set_name: my_mongo_cluster
2+
mongo_replica_set_name: my_mongo_cluster
33

44
mongo_cluster_members:
55
- host: "mongo3.example.com:{{ mongo_port }}" # arbiter first or change mongo_arbiter_index

environments/template/secrets/secret_example.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mongo_passwords:
1313
oidcng: secret
1414
myconext: secret
1515

16-
mongo_admin_password: secret
16+
mongo_admin_password: secret # this works for first time install, if you change it later you will have to do it manually
1717
mongo_ca_passphrase: secret
1818

1919
engine_api_metadata_push_password: secret

roles/manage/tasks/main.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,8 @@
9999
ansible.builtin.set_fact:
100100
manage_docker_networks:
101101
- name: "loadbalancer"
102-
- name: "openconext_mongodb"
103102
- name: "openconext_mariadb"
104-
when: mongodb_in_docker | default(false) | bool
103+
when: mariadb_in_docker | default(false) | bool
105104

106105
- name: Create and start the server container
107106
community.docker.docker_container:

roles/mongo/README.md

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,34 @@ Set the mongo_cluster_private_key variable encrypted in host_vars
1414

1515
Please review the official Mongo documentation for more information.
1616

17+
# Mongo deployment
18+
19+
To avoid surprisesyou can enable or disable cluster configuration with the boolean option mongo_configure_cluster. The role willonly initiate or reconfigure cluster if this is true (safest option is to use -e mongo_configure_cluster=true with your deployment when cluster configuration is necessary).
20+
Another issue is the serial value, it is safest to set it to 1 in your playbook, if it is higher multiple mongo nodes can will be restarted at once and it can break your cluster. However when you want to intialise a new cluster you need to run the tasks parallel and serial needs to be as high as the amount of nodes. We handled this with a variable serial with the name serial_number in our playbook with a default 1. If cluster initialisation or reconfiguration is necessary use -e "serial_number=<AMOUNT_OF_CLUSTERMEMBERS>"
21+
22+
Another option is creating separate playbooks for cluster adjustments/creation and for configuring non cluster related mongo settings.
23+
24+
25+
See also https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_strategies.html#setting-the-batch-size-with-serial
26+
27+
# Cluster reconfiguration
28+
29+
Warning: the cluster reconfiguration option in the mongodb_replicationset module is experimental. and you can only add or remove one node at a time.
30+
1731
# Todo
18-
- [ ] Add the possibility for adding and removing cluster members
19-
- [ ] Add the possibility for a standalone mongo server
32+
- [x] Check mongo_replication_roles and give a clear fail message when not set
33+
- [ ] Add option to change the already existing admin user, for now change the password manually and change it in the ansible config accordingly
34+
- [x] Add the possibility for adding and removing cluster members
35+
- [x] Add the possibility for a standalone mongo server
36+
- [x] Cluster changes can be enabled or disabled
37+
- [ ] Reconfigure cluster always reports changed
38+
- [ ] Initialise cluster always reports changed
39+
- [ ] check mode for writeconcern change tasks does not report change () same for any other mongodb_shell task "remote module (community.mongodb.mongodb_shell) does not support check mode"}
40+
- [X] Clearer error messaging for even number of votes
41+
- [X] Role refuses to add users when a new cluster is built (3 nodes) (cannot add users on a broken cluster)
42+
- [X] it would be helpfull if role (for example primary) is not defined in host_vars but in the mongo_cluster_members array
43+
- [X] removing primary from the cluster will not work but the error is unclear, this is related to the todo above
44+
- [ ] is it necessary to make votes configurable?
45+
- [X] preflight check are cluster members in the inventory and monog_servers group
46+
- [ ] Standalone mongo also requires cluster certificates, not logical although it doens't hurt
47+
- [ ] Changes to mongo users are executed but not reported as an ansible change

roles/mongo/defaults/main.yml

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,37 @@ mongo_servers: [] # Set this in group_vars
1313
# Not all mongo servers in the inventory are cluster members, so we use a separate list for this.
1414
# Set this in group_vars of your environment(s). The arbiter should go first, or change the mongo_arbiter_index.
1515
# mongo_cluster_members:
16-
# - host: "mongoarbiter.example.com:27017"
16+
# - host: "mongoarbiter.example.com"
1717
# priority: 1 # can vote, cannot become primary
18-
# - host: "mongo2.example.com:27017"
18+
# port: 27017
19+
# - host: "mongo2.example.com"
1920
# priority: 2
20-
# - host: "mongo1.example.com:27017"
21+
# port: 27017
22+
# - host: "mongo1.example.com"
2123
# priority: 3
22-
# mongo_arbiter_index: 0
23-
24-
# The replication role
25-
# mongo_replication_role: # Set this in host_vars, it can have the values: "primary", "secondary" or arbiter
24+
# port: 27017
2625

2726
# Todo: there is a link between mongo_replication_role and priority (arbiter is priority 1, primary the highest) so
2827
# setting them separately is not ideal.
2928

3029
# The port for mongo server
31-
mongod_port: 27017
30+
mongo_port: 27017
3231

3332
# The password for admin user
34-
mongo_admin_pass: "{{ mongo_admin_password }}" # Set this in secrets
33+
# mongo_admin_password: # set this in secrets
34+
35+
# Are we using a cluster?
36+
mongo_mode: "cluster" # cluster or standalone
3537

3638
# The name of the replication set
37-
replica_set_name: "{{ instance_name }}" # Set this in group_vars
39+
mongo_replica_set_name: "{{ instance_name }}" # Set this in group_vars
3840

3941
# Add a database
4042
mongo:
4143
users:
42-
- { name: managerw, db_name: metadata, password: "{{ mongo_passwords.manage }}" }
43-
- { name: oidcsrw, db_name: oidc, password: "{{ mongo_passwords.oidcng }}" }
44-
- { name: myconextrw, db_name: myconext, password: "{{ mongo_passwords.myconext }}" }
44+
- { name: managerw, db_name: metadata, password: "{{ mongo_passwords.manage }}", role: "readWrite" }
45+
- { name: oidcsrw, db_name: oidc, password: "{{ mongo_passwords.oidcng }}", role: "readWrite"}
46+
- { name: myconextrw, db_name: myconext, password: "{{ mongo_passwords.myconext }}", role: "readWrite" }
4547

4648
# Listen on all addresses by default
4749
mongo_bind_listen_address: "0.0.0.0"
@@ -53,3 +55,31 @@ mongo_pki_dir: "/etc/pki/mongo"
5355

5456
# Users and groups
5557
mongo_group: "mongod"
58+
59+
# Paths
60+
mongo_config_file: "/etc/mongod.conf"
61+
mongo_data_path: "/var/lib/mongo"
62+
mongo_pymongo_version: 4.16.0
63+
64+
# cluster members
65+
# set in group_vars
66+
# mongo_cluster_members:
67+
# - host: mongo1.example.com
68+
# priority: 3
69+
# votes: 1
70+
# port: 27017
71+
# - host: mongo2.example.com
72+
# priority: 2
73+
# votes: 1
74+
# port: 27017
75+
# - host: mongo3.example.com
76+
# priority: 1
77+
# votes: 1
78+
# port: 27017
79+
# arbiterOnly: true
80+
81+
mongo_cluster_write_concern: "majority"
82+
mongo_cluster_write_timeout: 5000
83+
84+
# to avoid surprises only initiate or reconfigure cluster if this is true (safest option is to use -e mongo_configure_cluster=true with your deployment when cluster configuration is necessary)
85+
mongo_configure_cluster: false
Lines changed: 178 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,191 @@
11
---
2-
# todo this weorks only for new deployments
3-
# rewrite so mongo config can be changed and cluster members can be added or removed
4-
- name: Check if hosts are in clustered
5-
ansible.builtin.command: mongosh --port {{ mongod_port }} --quiet --eval 'db.isMaster().hosts'
6-
register: check_cluster
7-
changed_when: false
8-
check_mode: false
9-
10-
- name: Debug check_cluster variable
2+
# In this task file the cluster is configured
3+
4+
# priority moet matchen met replication role, of replication role uit cluster mebers halen?
5+
# todo write concern zetten
6+
7+
# Do some preflight checks
8+
- name: Check some cluster related variables
9+
when: mongo_mode == "cluster"
10+
block:
11+
- name: Fail on undefined mongo_replica_set_name
12+
when: mongo_replica_set_name is not defined
13+
ansible.builtin.fail:
14+
msg: "Something is wrong, mongo_mode was set to cluster but mongo_replica_set_name is undefined."
15+
16+
- name: Debug replica settings
1117
ansible.builtin.debug:
12-
msg: "{{ check_cluster }}"
18+
msg: "Replica set name {{ mongo_replica_set_name }}"
1319
verbosity: 2
1420

15-
- name: Debug mongo_cluster_members variable
21+
# Loop over cluster members and check their presence in mong_servers group and their mode (not standalone)
22+
23+
- name: Check if mongo_cluster_members exist in inventory group
24+
ansible.builtin.assert:
25+
that:
26+
- item.host in groups['mongo_servers']
27+
fail_msg: "Server '{{ item.host }}' is not in the mongo_servers inventory group"
28+
success_msg: "Server '{{ item.host }}' found in mongo_servers inventory group"
29+
run_once: true
30+
loop: "{{ mongo_cluster_members }}"
31+
32+
# Loop over cluster members and check for primary
33+
34+
- name: Set primary host fact
35+
ansible.builtin.set_fact:
36+
mongo_primary_host: "{{ (mongo_cluster_members | max(attribute='priority')).host }}"
37+
38+
- name: Debug primary settings
1639
ansible.builtin.debug:
17-
msg: "{{ mongo_cluster_members }}"
40+
msg: "Primary is {{ mongo_primary_host }}"
1841
verbosity: 2
1942

20-
- name: Debug mongo_replication_role variable
43+
# What is the replication role of the current host
44+
- name: Debug replication role settings
2145
ansible.builtin.debug:
22-
msg: "{{ mongo_replication_role }}"
46+
msg: "This nodes replication role is {{ mongo_replication_role }}"
2347
verbosity: 2
2448

25-
- name: Initial cluster initialisation
26-
community.mongodb.mongodb_replicaset:
27-
login_host: localhost
28-
login_user: admin
29-
login_port: "{{ mongod_port }}"
30-
login_password: "{{ mongo_admin_password }}"
31-
replica_set: "{{ replica_set_name }}"
32-
members: "{{ mongo_cluster_members }}"
33-
arbiter_at_index: "{{ mongo_arbiter_index | default(0) }}"
34-
validate: false
35-
run_once: true
36-
when: mongo_replication_role == 'primary'
49+
# Cannot initialise a cluster without starting.......
50+
- name: Enable and start mongod
51+
ansible.builtin.service:
52+
name: mongod.service
53+
enabled: true
54+
state: started
3755

38-
- name: Wait until cluster health is ok
39-
community.mongodb.mongodb_status:
40-
login_user: admin
41-
login_password: "{{ mongo_admin_password }}"
42-
login_database: admin
43-
login_port: "{{ mongod_port }}"
44-
validate: default
45-
poll: 5
46-
interval: 12
47-
replica_set: "{{ replica_set_name }}"
56+
# Initialise cluster block
57+
- name: Initialise or reconfigure cluster block
4858
when: mongo_replication_role == 'primary'
59+
block:
60+
- name: Check if replica set is already initialised
61+
community.mongodb.mongodb_shell:
62+
login_host: localhost
63+
login_user: admin
64+
login_port: "{{ mongo_port }}"
65+
login_password: "{{ mongo_admin_password }}"
66+
eval: "rs.status().ok"
67+
db: admin
68+
register: rs_already_init
69+
ignore_errors: true
4970

50-
- name: Add the admin user
51-
community.mongodb.mongodb_user:
52-
database: admin
53-
name: admin
54-
password: "{{ mongo_admin_password }}"
55-
login_port: "{{ mongod_port }}"
56-
roles: root
57-
state: present
58-
when: check_cluster.stdout == ""
59-
no_log: true
60-
run_once: true
71+
- name: Debug cluster initialization check
72+
ansible.builtin.debug:
73+
msg: "{{ rs_already_init }}"
74+
verbosity: 2
75+
76+
# This should be possible with community.mongodb.mongodb_replicaset
77+
# But we keep getting authenticatione error so leave it like this for now
78+
- name: Initialise replica set if necessary
79+
community.mongodb.mongodb_shell:
80+
login_host: localhost
81+
login_user: admin
82+
login_port: "{{ mongo_port }}"
83+
login_password: "{{ mongo_admin_password }}"
84+
eval: |
85+
rs.initiate({
86+
_id: "{{ mongo_replica_set_name }}",
87+
members: [
88+
{% for m in mongo_cluster_members %}
89+
{ _id: {{ loop.index0 }}, host: "{{ m.host }}:{{ m.port }}", priority: {{ m.priority }}, votes: {{ m.votes }}{% if m.arbiterOnly is defined and m.arbiterOnly and m.arbiterOnly == true %}, arbiterOnly: true {% endif %} }{{ "," if not loop.last else "" }}
90+
{% endfor %}
91+
]
92+
})
93+
db: admin
94+
when: rs_already_init.failed
95+
register: rs_init
96+
97+
- name: Debug cluster initialization
98+
ansible.builtin.debug:
99+
msg: "{{ rs_init }}"
100+
verbosity: 2
101+
102+
- name: Format members list
103+
ansible.builtin.set_fact:
104+
mongo_cluster_members_formatted: "{{ mongo_cluster_members_formatted | default([]) + [m | combine({'host': m.host ~ ':' ~ (m.port | string)}) | dict2items | rejectattr('key', 'eq', 'port') | list | items2dict] }}"
105+
loop: "{{ mongo_cluster_members }}"
106+
loop_control:
107+
loop_var: m
108+
109+
- name: Debug members list
110+
ansible.builtin.debug:
111+
msg: "{{ mongo_cluster_members }}"
112+
verbosity: 2
113+
114+
- name: Debug formatted members list
115+
ansible.builtin.debug:
116+
msg: "{{ mongo_cluster_members_formatted }}"
117+
verbosity: 2
118+
119+
# Reconfigure cluster
120+
# todo: this always returns changed even when nothing changes
121+
- name: Reconfigure cluster if necessary
122+
community.mongodb.mongodb_replicaset:
123+
login_host: localhost
124+
login_user: admin
125+
login_password: "{{ mongo_admin_password }}"
126+
login_port: "{{ mongo_port }}"
127+
reconfigure: true
128+
replica_set: "{{ mongo_replica_set_name }}"
129+
members: "{{ mongo_cluster_members_formatted }}"
130+
register: rs_reconfigure
131+
132+
- name: Debug cluster reconfiguration
133+
ansible.builtin.debug:
134+
msg: "{{ rs_reconfigure }}"
135+
verbosity: 2
136+
137+
- name: Wait for the replicaset to stabilise
138+
community.mongodb.mongodb_status:
139+
replica_set: "{{ mongo_replica_set_name }}"
140+
login_host: localhost
141+
login_user: admin
142+
login_password: "{{ mongo_admin_password }}"
143+
login_port: "{{ mongo_port }}"
144+
poll: 5
145+
interval: 30
146+
validate: minimal # default fails on even number of servers and although this is not a great situation, it is sometimes the temporary situation because we can onlye add or remove 1 node at a time
147+
148+
# Cluster settings that cannot be changed with mongodb_replicaset
149+
150+
- name: Get current default write concern
151+
community.mongodb.mongodb_shell:
152+
login_host: localhost
153+
login_port: 27017
154+
login_user: admin
155+
login_password: "{{ mongo_admin_password }}"
156+
eval: "db.adminCommand({ getDefaultRWConcern: 1 })"
157+
register: current_write_concern
158+
changed_when: false
159+
160+
- name: Debug write concern check
161+
ansible.builtin.debug:
162+
msg: "{{ current_write_concern.transformed_output.defaultWriteConcern }}"
163+
verbosity: 2
164+
when: current_write_concern.transformed_output.defaultWriteConcern is defined
165+
166+
- name: Set default write concern
167+
when: >
168+
current_write_concern.transformed_output.defaultWriteConcern is defined
169+
and
170+
(current_write_concern.transformed_output.defaultWriteConcern.w | string != mongo_cluster_write_concern | default('majority') | string
171+
or
172+
current_write_concern.transformed_output.defaultWriteConcern.wtimeout | int != mongo_cluster_write_timeout | default(5000) | int)
173+
or current_write_concern.transformed_output.defaultWriteConcern is not defined
174+
block:
175+
- name: "set write concern majority"
176+
when: mongo_cluster_write_concern == "majority"
177+
community.mongodb.mongodb_shell:
178+
login_host: localhost
179+
login_user: admin
180+
login_password: "{{ mongo_admin_password }}"
181+
login_port: "{{ mongo_port }}"
182+
eval: "db.adminCommand({ setDefaultRWConcern: 1, defaultWriteConcern: { w: \"{{ mongo_cluster_write_concern | default('majority') }}\", wtimeout: {{ mongo_cluster_write_timeout | default(5000) }} } })"
183+
# could not get this to work with either majority with quotes or number without quotes so for now an ugly fix
184+
- name: "set write concern numeric"
185+
when: mongo_cluster_write_concern != "majority"
186+
community.mongodb.mongodb_shell:
187+
login_host: localhost
188+
login_user: admin
189+
login_password: "{{ mongo_admin_password }}"
190+
login_port: "{{ mongo_port }}"
191+
eval: "db.adminCommand({ setDefaultRWConcern: 1, defaultWriteConcern: { w: {{ mongo_cluster_write_concern | default('majority') }}, wtimeout: {{ mongo_cluster_write_timeout | default(5000) }} } })"

0 commit comments

Comments
 (0)