Skip to content

Commit

Permalink
Replace mongo shell with mongosh
Browse files Browse the repository at this point in the history
Needed changes:

  - Use stderr for errors instead of stdout. The `mongo` command from
    the older client package put its errors on stout and exited with
    zero as exit code. Mongosh, by contrast, puts errors on stderr
    instead of stdout and exits with a non-zero exit code.

    This means we need to pass the underlying exception to the caller so
    it can deal with it appropriately.

  - Replace old functions:
    - `printjson` has become `EJSON.stringify`.

    - `rs.secondaryOk()` and `rs.slaveOk()` have become
      `db.getMongo().setReadPref("primaryPreferred")`.

  - `db` is now an object instead of the database name, we need to call
    `getName()` on it to get the database name.

  - Don't throw an exception from the `if (authRequired()) {` block, as
    that causes the `mongosh` command to stop processing further
    commands and exit with a non-zero exit code.
  • Loading branch information
Johan De Wit authored and stevenpost committed Mar 22, 2024
1 parent 30a6905 commit 2843038
Show file tree
Hide file tree
Showing 23 changed files with 126 additions and 119 deletions.
9 changes: 9 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ The following parameters are available in the `mongodb::globals` class:

* [`server_package_name`](#-mongodb--globals--server_package_name)
* [`client_package_name`](#-mongodb--globals--client_package_name)
* [`client_version`](#-mongodb--globals--client_version)
* [`mongod_service_manage`](#-mongodb--globals--mongod_service_manage)
* [`service_enable`](#-mongodb--globals--service_enable)
* [`service_ensure`](#-mongodb--globals--service_ensure)
Expand Down Expand Up @@ -168,6 +169,14 @@ If not specified, the module will use whatever service name is the default for y

Default value: `undef`

##### <a name="-mongodb--globals--client_version"></a>`client_version`

Data type: `Any`

Version of the client package to install.

Default value: `undef`

##### <a name="-mongodb--globals--mongod_service_manage"></a>`mongod_service_manage`

Data type: `Any`
Expand Down
8 changes: 4 additions & 4 deletions lib/facter/is_master.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,17 @@ def get_options_from_config(file)

Facter.add('mongodb_is_master') do
setcode do
if %w[mongo mongod].all? { |m| Facter::Util::Resolution.which m }
if %w[mongosh mongod].all? { |m| Facter::Util::Resolution.which m }
file = mongod_conf_file
if file
options = get_options_from_config(file)
e = File.exist?('/root/.mongorc.js') ? 'load(\'/root/.mongorc.js\'); ' : ''
e = File.exist?('/root/.mongoshrc.js') ? 'load(\'/root/.mongoshrc.js\'); ' : ''

# Check if the mongodb server is responding:
Facter::Core::Execution.exec("mongo --quiet #{options} --eval \"#{e}printjson(db.adminCommand({ ping: 1 }))\"")
Facter::Core::Execution.exec("mongosh --quiet #{options} --eval \"#{e}EJSON.stringify(db.adminCommand({ ping: 1 }))\"")

if $CHILD_STATUS.success?
Facter::Core::Execution.exec("mongo --quiet #{options} --eval \"#{e}db.isMaster().ismaster\"")
Facter::Core::Execution.exec("mongosh --quiet #{options} --eval \"#{e}db.isMaster().ismaster\"")
else
'not_responding'
end
Expand Down
24 changes: 12 additions & 12 deletions lib/puppet/provider/mongodb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
class Puppet::Provider::Mongodb < Puppet::Provider
# Without initvars commands won't work.
initvars
commands mongo: 'mongo'
commands mongosh: 'mongosh'

# Optional defaults file
def self.mongorc_file
"load('#{Facter.value(:root_home)}/.mongorc.js'); " if File.file?("#{Facter.value(:root_home)}/.mongorc.js")
def self.mongoshrc_file
"load('#{Facter.value(:root_home)}/.mongoshrc.js'); " if File.file?("#{Facter.value(:root_home)}/.mongoshrc.js")
end

def mongorc_file
self.class.mongorc_file
def mongoshrc_file
self.class.mongoshrc_file
end

def self.mongod_conf_file
Expand Down Expand Up @@ -74,7 +74,7 @@ def self.tls_invalid_hostnames(config = nil)
config['tlsallowInvalidHostnames']
end

def self.mongo_cmd(db, host, cmd)
def self.mongosh_cmd(db, host, cmd)
config = mongo_conf

args = [db, '--quiet', '--host', host]
Expand All @@ -101,7 +101,7 @@ def self.mongo_cmd(db, host, cmd)
end

args += ['--eval', cmd]
mongo(args)
mongosh(args)
end

def self.conn_string
Expand Down Expand Up @@ -138,7 +138,7 @@ def self.conn_string
def self.db_ismaster
cmd_ismaster = 'db.isMaster().ismaster'
db = 'admin'
res = mongo_cmd(db, conn_string, cmd_ismaster).to_s.split(%r{\n}).last.chomp
res = mongosh_cmd(db, conn_string, cmd_ismaster).to_s.split(%r{\n}).last.chomp
res.eql?('true')
end

Expand All @@ -155,14 +155,14 @@ def self.auth_enabled(config = nil)
def self.mongo_eval(cmd, db = 'admin', retries = 10, host = nil)
retry_count = retries
retry_sleep = 3
cmd = mongorc_file + cmd if mongorc_file
cmd = mongoshrc_file + cmd if mongoshrc_file

out = nil
begin
out = if host
mongo_cmd(db, host, cmd)
mongosh_cmd(db, host, cmd)
else
mongo_cmd(db, conn_string, cmd)
mongosh_cmd(db, conn_string, cmd)
end
rescue StandardError => e
retry_count -= 1
Expand All @@ -173,7 +173,7 @@ def self.mongo_eval(cmd, db = 'admin', retries = 10, host = nil)
end
end

raise Puppet::ExecutionFailure, "Could not evaluate MongoDB shell command: #{cmd}" unless out
raise Puppet::ExecutionFailure, "Could not evaluate MongoDB shell command: #{cmd}, with: #{e.message}" unless out

Puppet::Util::MongodbOutput.sanitize(out)
end
Expand Down
4 changes: 2 additions & 2 deletions lib/puppet/provider/mongodb_database/mongodb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
def self.instances
require 'json'

pre_cmd = 'try { rs.secondaryOk() } catch (err) { rs.slaveOk() }'
dbs = JSON.parse mongo_eval("#{pre_cmd};printjson(db.getMongo().getDBs())")
pre_cmd = 'db.getMongo().setReadPref("primaryPreferred")'
dbs = JSON.parse mongo_eval("#{pre_cmd};EJSON.stringify(db.getMongo().getDBs())")

dbs['databases'].map do |db|
new(name: db['name'],
Expand Down
51 changes: 26 additions & 25 deletions lib/puppet/provider/mongodb_replset/mongo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,31 +153,32 @@ def get_hosts_status(members)
members.select do |member|
host = member['host']
Puppet.debug "Checking replicaset member #{host} ..."
status = rs_status(host)
raise Puppet::Error, "Can't configure replicaset #{name}, host #{host} is not supposed to be part of a replicaset." if status.key?('errmsg') && status['errmsg'] == 'not running with --replSet'

if auth_enabled && status.key?('errmsg') && (status['errmsg'].include?('unauthorized') || status['errmsg'].include?('not authorized') || status['errmsg'].include?('requires authentication'))
Puppet.warning "Host #{host} is available, but you are unauthorized because of authentication is enabled: #{auth_enabled}"
alive.push(member)
end

if status.key?('errmsg') && status['errmsg'].include?('no replset config has been received')
Puppet.debug 'Mongo v4 rs.status() RS not initialized output'
alive.push(member)
end

if status.key?('set')
raise Puppet::Error, "Can't configure replicaset #{name}, host #{host} is already part of another replicaset." if status['set'] != name

# This node is alive and supposed to be a member of our set
Puppet.debug "Host #{host} is available for replset #{status['set']}"
alive.push(member)
elsif status.key?('info')
Puppet.debug "Host #{host} is alive but unconfigured: #{status['info']}"
alive.push(member)
begin
status = rs_status(host)

if status.key?('set')
raise Puppet::Error, "Can't configure replicaset #{name}, host #{host} is already part of another replicaset." if status['set'] != name

# This node is alive and supposed to be a member of our set
Puppet.debug "Host #{host} is available for replset #{status['set']}"
alive.push(member)
elsif status.key?('info')
Puppet.debug "Host #{host} is alive but unconfigured: #{status['info']}"
alive.push(member)
end
rescue Puppet::ExecutionFailure => e
raise Puppet::Error, "Can't configure replicaset #{name}, host #{host} is not supposed to be part of a replicaset." if e.message =~ %r{not running with --replSet}

if auth_enabled && (e.message.include?('unauthorized') || e.message.include?('not authorized') || e.message.include?('requires authentication'))
Puppet.warning "Host #{host} is available, but you are unauthorized because of authentication is enabled: #{auth_enabled}"
alive.push(member)
elsif e.message.include?('no replset config has been received')
Puppet.debug 'Mongo v4 rs.status() RS not initialized output'
alive.push(member)
else
Puppet.warning "Can't connect to replicaset member #{host}."
end
end
rescue Puppet::ExecutionFailure
Puppet.warning "Can't connect to replicaset member #{host}."
end
alive.uniq!
dead = members - alive
Expand Down Expand Up @@ -383,7 +384,7 @@ def mongo_command(command, host, retries = 4)

def self.mongo_command(command, host = nil, retries = 4)
begin
output = mongo_eval("printjson(#{command})", 'admin', retries, host)
output = mongo_eval("EJSON.stringify(#{command})", 'admin', retries, host)
rescue Puppet::ExecutionFailure => e
Puppet.debug "Got an exception: #{e}"
raise
Expand Down
6 changes: 3 additions & 3 deletions lib/puppet/provider/mongodb_shard/mongo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

mk_resource_methods

commands mongo: 'mongo'
commands mongosh: 'mongosh'

def initialize(value = {})
super(value)
Expand Down Expand Up @@ -152,8 +152,8 @@ def self.mongo_command(command, host = nil, _retries = 4)
args = []
args << '--quiet'
args << ['--host', host] if host
args << ['--eval', "printjson(#{command})"]
output = mongo(args.flatten)
args << ['--eval', "EJSON.stringify(#{command})"]
output = mongosh(args.flatten)
rescue Puppet::ExecutionFailure => e
raise unless e =~ %r{Error: couldn't connect to server} && wait <= (2**max_wait)

Expand Down
7 changes: 3 additions & 4 deletions lib/puppet/provider/mongodb_user/mongodb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ def self.instances
require 'json'

if db_ismaster
script = 'printjson(db.system.users.find().toArray())'
script = 'EJSON.stringify(db.system.users.find().toArray())'
# A hack to prevent prefetching failures until admin user is created
script = "try {#{script}} catch (e) { if (e.message.match(/not authorized on admin/)) { 'not authorized on admin' } else {throw e}}" if auth_enabled
script = "try {#{script}} catch (e) { if (e.message.match(/requires authentication/) || e.message.match(/not authorized on admin/)) { 'not authorized on admin' } else {throw e}}" if auth_enabled

out = mongo_eval(script)

return [] if auth_enabled && out.include?('not authorized on admin')
return [] if auth_enabled && (out.include?('requires authentication') || out.include?('not authorized on admin'))

users = JSON.parse out

Expand Down
14 changes: 4 additions & 10 deletions manifests/client/params.pp
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,10 @@
# @api private
#
class mongodb::client::params inherits mongodb::globals {
$package_ensure = pick($mongodb::globals::version, 'present')
$package_ensure = pick($mongodb::globals::client_version, 'present')
$manage_package = pick($mongodb::globals::manage_package, $mongodb::globals::manage_package_repo, false)

if $manage_package {
$package_name = "mongodb-${mongodb::globals::edition}-shell"
} else {
$package_name = $facts['os']['family'] ? {
'Debian' => 'mongodb-clients',
'Redhat' => "mongodb-${mongodb::globals::edition}-shell",
default => 'mongodb',
}
}
# the new mongosh package is the same for all distros.
# and it follows its own versioning
$package_name = 'mongodb-mongosh'
}
3 changes: 3 additions & 0 deletions manifests/globals.pp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
# This setting can be used to specify the name of the client package that should be installed.
# If not specified, the module will use whatever service name is the default for your OS distro.
#
# @param client_version
# Version of the client package to install.
# @param mongod_service_manage
# This setting can be used to override the default management of the mongod service.
# By default the module will manage the mongod process.
Expand Down Expand Up @@ -118,6 +120,7 @@
class mongodb::globals (
$server_package_name = undef,
$client_package_name = undef,
$client_version = undef,

$mongod_service_manage = undef,
$service_enable = undef,
Expand Down
2 changes: 1 addition & 1 deletion manifests/params.pp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
]
$handle_creds = true
$store_creds = false
$rcfile = "${facts['root_home']}/.mongorc.js"
$rcfile = "${facts['root_home']}/.mongoshrc.js"
$dbpath_fix = false

$manage_package = pick($mongodb::globals::manage_package, $mongodb::globals::manage_package_repo, false)
Expand Down
2 changes: 1 addition & 1 deletion manifests/server/config.pp
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
if $handle_creds {
file { $rcfile:
ensure => file,
content => template('mongodb/mongorc.js.erb'),
content => template('mongodb/mongoshrc.js.erb'),
owner => 'root',
group => 'root',
mode => '0600',
Expand Down
8 changes: 4 additions & 4 deletions spec/acceptance/database_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class { 'mongodb::server': }
end

it 'creates the databases' do
shell("mongo testdb1 --eval 'printjson(db.getMongo().getDBs())'")
shell("mongo testdb2 --eval 'printjson(db.getMongo().getDBs())'")
shell("mongosh testdb1 --eval 'EJSON.stringify(db.getMongo().getDBs())'")
shell("mongosh testdb2 --eval 'EJSON.stringify(db.getMongo().getDBs())'")
end
end

Expand All @@ -50,8 +50,8 @@ class { 'mongodb::server':
end

it 'creates the database' do
shell("mongo testdb1 --port 27018 --eval 'printjson(db.getMongo().getDBs())'")
shell("mongo testdb2 --port 27018 --eval 'printjson(db.getMongo().getDBs())'")
shell("mongosh testdb1 --port 27018 --eval 'EJSON.stringify(db.getMongo().getDBs())'")
shell("mongosh testdb2 --port 27018 --eval 'EJSON.stringify(db.getMongo().getDBs())'")
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/acceptance/mongos_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class { 'mongodb::server':
it { is_expected.to be_listening }
end

describe command('mongo --version') do
describe command('mongod --version') do
its(:exit_status) { is_expected.to eq 0 }
end
end
Expand Down
Loading

0 comments on commit 2843038

Please sign in to comment.