Skip to content

Commit

Permalink
Merge pull request #1571 from ProgrammeVitam/cp70_bug_12074_fix_mongo…
Browse files Browse the repository at this point in the history
…_writablePrimary

CP 7.0 - Bug #12074: Fix mongo isWritablePrimary check.
  • Loading branch information
GiooDev authored Dec 8, 2023
2 parents 028dfc5 + 9d0d75d commit 5b14080
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 67 deletions.
1 change: 1 addition & 0 deletions deployment/environments/hosts-ui.example
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ hosts_vitamui_mongod
# - mongo_rs_bootstrap=true (default: false); mandatory for 1 node of the shard, some init commands will be executed on it
# - mongo_cluster_name=mongo-vitamui (default: mongo-vitamui)
# - mongo_shard_id=0 (default: 0)
# - mongo_arbiter=true ; the node will be only an arbiter, it will not store data ; do not add this parameter on a mongo_rs_bootstrap node, maximum 1 node per shard
# - mongod_memory=x (default: unset); this will force the wiredtiger cache size to x (unit is GB)
# - is_small=true (default: false); this will force the priority for this server to be lower when electing master ; hardware can be downgraded for this machine
# - mongo_express_enabled=true to deploy mongo_express (default: false)
Expand Down
1 change: 1 addition & 0 deletions deployment/environments/hosts.local
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ hosts_vitamui_mongod
# - mongo_rs_bootstrap=true (default: false); mandatory for 1 node of the shard, some init commands will be executed on it
# - mongo_cluster_name=mongo-vitamui (default: mongo-vitamui)
# - mongo_shard_id=0 (default: 0)
# - mongo_arbiter=true ; the node will be only an arbiter, it will not store data ; do not add this parameter on a mongo_rs_bootstrap node, maximum 1 node per shard
# - mongod_memory=x (default: unset); this will force the wiredtiger cache size to x (unit is GB)
# - is_small=true (default: false); this will force the priority for this server to be lower when electing master ; hardware can be downgraded for this machine
# - mongo_express_enabled=true to deploy mongo_express (default: false)
Expand Down
4 changes: 2 additions & 2 deletions deployment/roles/mongo/tasks/check_auth.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
set_fact:
mongo_credentials: ""
mongo_no_auth: true
when: "mongo_authent_enabled.rc != 0"
when: mongo_authent_enabled.rc != 0
tags: update_mongodb_configuration

# When authentication is required, we set mongodb admin credentials
- name: Set mongodb authentication credentials
set_fact:
mongo_credentials: "-u {{ mongodb.admin.user }} -p {{ mongodb.admin.password }}"
mongo_no_auth: false
when: "mongo_authent_enabled.rc == 0"
when: mongo_authent_enabled.rc == 0
no_log: "{{ hide_passwords_during_deploy }}"
tags: update_mongodb_configuration

18 changes: 14 additions & 4 deletions deployment/roles/mongo/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,17 @@

- block:

- fail:
msg: "ERROR: mongo_rs_bootstrap node can't be mongo_arbiter !"
when: mongo_arbiter | default(false) | bool == true
tags: update_mongodb_configuration

- name: Wait for the service port to be open on all members of the replica
wait_for:
host: "{{ hostvars[item]['ip_service'] }}"
port: "{{ mongodb.mongod_port }}"
timeout: "{{ vitamui_defaults.services.start_timeout }}"
with_items:
- "{{ groups['hosts_vitamui_mongod'] }}"
with_items: "{{ groups['hosts_vitamui_mongod'] }}"
tags: update_mongodb_configuration

- name: Copy the script which initiate the replica set
Expand All @@ -164,12 +168,18 @@
tags: update_mongodb_configuration

- name: Initiate the replica set
command: "mongo --host {{ ip_service }} --port {{ mongodb.mongod_port }} {{ vitamui_defaults.folder.root_path | default('/vitamui') }}/app/mongod/init-replica.js"
command: "mongosh --host {{ ip_service }} --port {{ mongodb.mongod_port }} --quiet --file {{ vitamui_defaults.folder.root_path | default('/vitamui') }}/app/mongod/init-replica.js"
tags: update_mongodb_configuration

- import_tasks: check_auth.yml

# Create the local shard admin user
- name: Compute list of mongo_nodes
set_fact:
mongo_nodes: "{{ mongo_nodes | default([]) + [ hostvars[item]['ip_service'] + ':' + mongodb.mongod_port | string ] }}"
loop: "{{ groups['hosts_vitamui_mongod'] }}"
when: hostvars[item]['mongo_arbiter'] | default(false) | bool == false

- name: Copy the script which create the local users
template:
src: local-user.js.j2
Expand All @@ -180,7 +190,7 @@
tags: update_mongodb_configuration

- name: Create the local shard user
command: "mongo --host {{ ip_service }}:{{ mongodb.mongod_port }} {{ mongo_credentials }} {{ vitamui_defaults.folder.root_path | default('/vitamui') }}/app/mongod/local-user.js"
command: "mongosh --host {{ mongo_nodes | join(',') }} {{ mongo_credentials }} --quiet --file {{ vitamui_defaults.folder.root_path | default('/vitamui') }}/app/mongod/local-user.js"
tags: update_mongodb_configuration

when: mongo_rs_bootstrap | default(false) | bool == true
102 changes: 48 additions & 54 deletions deployment/roles/mongo/templates/init-replica.js.j2
Original file line number Diff line number Diff line change
Expand Up @@ -10,44 +10,59 @@

function checkExistingReplicaSet() {
print("INFO: checkExistingReplicaSet");
var rsStatus = rs.status();
try {
var rsStatus = rs.status();

if(!rsStatus.ok) {
if (!rsStatus.ok) {
printjson(rsStatus);
throw "ERROR : Cannot check mongod replica set status";
}

print("INFO : Exiting mongod replica set found");
return true;

if(rsStatus.codeName === "NotYetInitialized") {
} catch (error) {
if (error.message && error.message.includes("no replset config has been received")) {
print("INFO : No mongod replica set found");
return false;
}

printjson(rsStatus);
throw "ERROR : Cannot check mongod replica set status";
print("ERROR : Cannot check mongod replica set status");
throw error;
}

print("INFO : Exiting mongod replica set found");

return true;
}

function checkExistingReplicaSetMembers() {
print("INFO: checkExistingReplicaSetMembers");
var rsConfig = rs.config();

let targetMembers = buildTargetReplicaSetMemberConfiguration();
let rsConfigMembers = rsConfig.members;
try {
var rsConfig = rs.config();

let targetMembers = buildTargetReplicaSetMemberConfiguration();
let rsConfigMembers = rsConfig.members;

for (const targetMember of targetMembers) {
let rsConfigMember = rsConfigMembers.find(member => member.host === targetMember.host);
if (rsConfigMember) {
if(rsConfigMember.arbiterOnly !== targetMember.arbiterOnly) {
printjson(rsConfig);
throw "ERROR : Host " + targetMember.host + " expected " +
(targetMember.arbiterOnly? "" : "NOT ") + "to be arbiter (mongo_arbiter=true)";
}

print("INFO : host " + targetMember.host + " found in mongod replica set");
} else {
printjson(rsConfig);
throw "ERROR : Host " + targetMember.host + " not found in mongod replica set";
}
}

for (const targetMember of targetMembers) {
let rsConfigMember = rsConfigMembers.find(member => member.host === targetMember.host);
if(rsConfigMember) {
print("INFO : host " + targetMember.host + " found in mongod replica set");
} else {
if (targetMembers.length !== rsConfigMembers.length) {
printjson(rsConfig);
throw "ERROR : Host " + targetMember.host + " not found in mongod replica set";
throw "ERROR : Found unexpected / unknown mongod ReplicaSet members";
}
}

if(targetMembers.length != rsConfigMembers.length) {
printjson(rsConfig);
throw "ERROR : Found unexpected / unknown mongod ReplicaSet members";
} catch (error) {
print("ERROR : Cannot check mongod replica set members");
throw error;
}
}

Expand All @@ -59,7 +74,12 @@ function buildTargetReplicaSetMemberConfiguration() {
members.push({
_id: idCpt++,
host: "{{ hostvars[host]['ip_service'] }}:{{ mongodb.mongod_port }}",
{% if (hostvars[host]['mongo_rs_bootstrap'] | default(false) | bool != true) and (hostvars[host]['mongo_arbiter'] | default(false) | bool == true) %}
arbiterOnly: true
{% else %}
arbiterOnly: false,
priority: {{ '1' if (hostvars[host]['is_small'] | default(false) | bool == true) else '10' }}
{% endif %}
});
{% endfor %}
return members;
Expand All @@ -76,7 +96,7 @@ function initReplicaSetPrimary() {
}
)

if(!rsInit.ok) {
if (!rsInit.ok) {
printjson(rsInit);
throw "ERROR : Cannot initialize mongod replica set";
}
Expand All @@ -89,12 +109,12 @@ function waitForReplicaSetPrimaryElection() {

status = rs.status();

if(!status.ok) {
if (!status.ok) {
printjson(status);
throw "ERROR : Cannot get mongod replica set status";
}

if(status.myState === 1) {
if (status.members.find(member => member.stateStr === 'PRIMARY')) {
print("OK : mongod replica set elected primary node");
return;
}
Expand All @@ -107,38 +127,12 @@ function waitForReplicaSetPrimaryElection() {
throw "ERROR : Timeout - NO PRIMARY NODE FOUND";
}

function waitForWritablePrimary() {
print("INFO: waitForWritablePrimary");
var instanceStatus;
for (let i = 0; i < 60; i++) {

instanceStatus = db.hello();

if(!instanceStatus.ok) {
printjson(instanceStatus);
throw "ERROR : Cannot get db instance status";
}

if(instanceStatus.isWritablePrimary) {
print("OK : Primary node " + instanceStatus.primary + " is writable");
return;
}

// Retry later
sleep(1000);
}

printjson(instanceStatus);
throw "ERROR : Timeout - NO PRIMARY NODE FOUND";
}

if(checkExistingReplicaSet()) {
if (checkExistingReplicaSet()) {
checkExistingReplicaSetMembers();
print("INFO : mongod replica set already configured.");
} else {
print("INFO : Initializing mongod replica set");
initReplicaSetPrimary();
waitForReplicaSetPrimaryElection();
waitForWritablePrimary();
print("INFO : mongod replica set intitialization OK");
}
31 changes: 28 additions & 3 deletions deployment/roles/mongo/templates/local-user.js.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
function waitForWritablePrimary() {
print("INFO: waitForWritablePrimary");
var instanceStatus;
for (let i = 0; i < 60; i++) {

instanceStatus = db.hello();

if (!instanceStatus.ok) {
printjson(instanceStatus);
throw "ERROR : Cannot get db instance status";
}

if (instanceStatus.isWritablePrimary) {
print("OK : Primary node " + instanceStatus.primary + " is writable");
return;
}

// Retry later
sleep(1000);
}

printjson(instanceStatus);
throw "ERROR : Timeout - NO PRIMARY NODE FOUND";
}

waitForWritablePrimary();

admin = db.getSiblingDB("admin")

if (! admin.getUser("{{ mongodb.localadmin.user }}")) {
if (!admin.getUser("{{ mongodb.localadmin.user }}")) {
admin.createUser(
{
user: "{{ mongodb.localadmin.user }}",
Expand All @@ -17,8 +43,7 @@ if (! admin.getUser("{{ mongodb.localadmin.user }}")) {
]
}
)
}
else {
} else {
admin.updateUser(
"{{ mongodb.localadmin.user }}",
{
Expand Down
5 changes: 2 additions & 3 deletions deployment/roles/mongo_init/tasks/check_auth.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,19 @@
- name: Set mongo_no_auth fact to true
set_fact:
mongo_no_auth: true
when: "mongo_authent_enabled.rc != 0"
when: mongo_authent_enabled.rc != 0
when: mongodb.docker is not defined or not mongodb.docker.enable

- block:
- name: Load script in database (docker)
shell: "docker exec --tty {{ mongodb.docker.image_name }} /bin/bash -c \"mongo --host mongodb://{{ mongod_uri }}/admin?replicaSet={{ mongod_replicaset_name }} -u {{ mongodb.admin.user }} -p {{ mongodb.admin.password }} --quiet --eval 'db.help()'\""

failed_when: false
register: mongo_authent_enabled

- name: Set mongo_no_auth fact to true
set_fact:
mongo_no_auth: true
when: "mongo_authent_enabled.rc != 0"
when: mongo_authent_enabled.rc != 0
when: mongodb.docker is defined and mongodb.docker.enable

# When authentication is required, we set mongodb admin credentials
Expand Down
3 changes: 2 additions & 1 deletion deployment/roles/mongo_init/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
set_fact:
mongo_nodes: "{{ mongo_nodes | default([]) + [ hostvars[item]['ip_service'] + ':'+ mongodb.mongod_port | string ] }}"
loop: "{{ groups['hosts_vitamui_mongod'] }}"
when: hostvars[item]['mongo_arbiter'] | default(false) | bool == false

- name: Set Mongo URI
set_fact:
mongod_uri: "{{ mongo_nodes| join(',') }}"
mongod_uri: "{{ mongo_nodes | join(',') }}"

- name: Set mongod_output_dir_entry_point
set_fact:
Expand Down

0 comments on commit 5b14080

Please sign in to comment.