Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the mongosh command instead of the old mongo command #703

Merged
merged 10 commits into from
Mar 29, 2024
16 changes: 13 additions & 3 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Data type: `String[1]`

Used to ensure that the package is installed, or that the package is absent/purged

Default value: `pick($mongodb::globals::version, 'present')`
Default value: `pick($mongodb::globals::client_version, 'present')`

##### <a name="-mongodb--client--package_name"></a>`package_name`

Expand All @@ -73,7 +73,7 @@ Data type: `String[1]`
This setting can be used to specify the name of the package that should be installed.
If not specified, the module will use whatever service name is the default for your OS distro.

Default value: `"mongodb-${mongodb::globals::edition}-shell"`
Default value: `'mongodb-mongosh'`

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

Expand Down Expand Up @@ -135,6 +135,7 @@ class {'mongodb::globals':
The following parameters are available in the `mongodb::globals` class:

* [`version`](#-mongodb--globals--version)
* [`client_version`](#-mongodb--globals--client_version)
* [`manage_package_repo`](#-mongodb--globals--manage_package_repo)
* [`repo_version`](#-mongodb--globals--repo_version)
* [`use_enterprise_repo`](#-mongodb--globals--use_enterprise_repo)
Expand All @@ -153,6 +154,15 @@ If not specified, the module will ensure packages with `present`.

Default value: `undef`

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

Data type: `Optional[String[1]]`

The version of MongoDB Shell to install/manage.
If not specified, the module will ensure packages with `present`.

Default value: `undef`

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

Data type: `Boolean`
Expand Down Expand Up @@ -967,7 +977,7 @@ Data type: `String`

The path to the custom mongosh rc file.

Default value: `"${facts['root_home']}/.mongorc.js"`
Default value: `"${facts['root_home']}/.mongoshrc.js"`

##### <a name="-mongodb--server--service_manage"></a>`service_manage`

Expand Down
7 changes: 3 additions & 4 deletions lib/facter/is_master.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,16 @@ 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\'); ' : ''

# 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 \"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 \"db.isMaster().ismaster\"")
else
'not_responding'
end
Expand Down
6 changes: 3 additions & 3 deletions lib/facter/mongodb_version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

Facter.add(:mongodb_version) do
setcode do
if Facter::Core::Execution.which('mongo')
mongodb_version = Facter::Core::Execution.execute('mongo --version 2>&1')
%r{MongoDB shell version:?\s+v?([\w.]+)}.match(mongodb_version)[1]
if Facter::Core::Execution.which('mongod')
mongodb_version = Facter::Core::Execution.execute('mongod --version 2>&1')
%r{^db version:?\s+v?([\w.]+)}.match(mongodb_version)[1]
end
end
end
33 changes: 18 additions & 15 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 All @@ -111,9 +111,9 @@ def self.conn_string
first_ip_in_list = bindip.split(',').first
ip_real = case first_ip_in_list
when '0.0.0.0'
Facter.value(:fqdn)
'127.0.0.1'
when %r{\[?::0\]?}
Facter.value(:fqdn)
'::1'
else
first_ip_in_list
end
Expand All @@ -135,11 +135,14 @@ def self.conn_string
"#{ip_real}:#{port_real}"
end

def conn_string
self.class.conn_string
end

def self.db_ismaster
cmd_ismaster = 'db.isMaster().ismaster'
cmd_ismaster = mongorc_file + cmd_ismaster if mongorc_file
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 @@ -156,14 +159,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 @@ -174,7 +177,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
6 changes: 3 additions & 3 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 All @@ -29,7 +29,7 @@ def self.prefetch(resources)

def create
if db_ismaster
out = mongo_eval('db.dummyData.insert({"created_by_puppet": 1})', @resource[:name])
out = mongo_eval('db.dummyData.insertOne({"created_by_puppet": 1})', @resource[:name])
raise "Failed to create DB '#{@resource[:name]}'\n#{out}" if %r{writeError} =~ out
else
Puppet.warning 'Database creation is available only from master host'
Expand Down
55 changes: 30 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,36 @@ 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 = if host.split(':').first == Facter.value(:fqdn)
rs_status(conn_string)
else
rs_status(host)
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)
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 +388,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
2 changes: 1 addition & 1 deletion lib/puppet/type/mongodb_user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def insync?(_is)
%w[mongodb mongod]
end

autorequire(:mongodb_database) do
autobefore(:mongodb_database) do
self[:database]
end

Expand Down
4 changes: 2 additions & 2 deletions manifests/client.pp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
# If not specified, the module will use whatever service name is the default for your OS distro.
#
class mongodb::client (
String[1] $ensure = pick($mongodb::globals::version, 'present'),
String[1] $package_name = "mongodb-${mongodb::globals::edition}-shell",
String[1] $ensure = pick($mongodb::globals::client_version, 'present'),
String[1] $package_name = 'mongodb-mongosh',
) inherits mongodb::globals {
package { 'mongodb_client':
ensure => $ensure,
Expand Down
5 changes: 5 additions & 0 deletions manifests/globals.pp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@
# The version of MonogDB to install/manage.
# If not specified, the module will ensure packages with `present`.
#
# @param client_version
# The version of MongoDB Shell to install/manage.
# If not specified, the module will ensure packages with `present`.
#
# @param manage_package_repo
# Whether to manage MongoDB software repository.
#
Expand Down Expand Up @@ -71,6 +75,7 @@
#
class mongodb::globals (
Optional[String[1]] $version = undef,
Optional[String[1]] $client_version = undef,
Boolean $manage_package_repo = true,
String[1] $repo_version = '5.0',
Boolean $use_enterprise_repo = false,
Expand Down
2 changes: 1 addition & 1 deletion manifests/server.pp
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@
Optional[Stdlib::Absolutepath] $pidfilepath = undef,
String[4,4] $pidfilemode = '0644',
Boolean $manage_pidfile = false,
String $rcfile = "${facts['root_home']}/.mongorc.js",
String $rcfile = "${facts['root_home']}/.mongoshrc.js",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like this. This only affects where the rc file gets stored, but not from where it is read, that is hardcoded in the provider. See this same commit, lib/puppet/provider/mongodb.rb, line 15.

Boolean $service_manage = true,
Optional[String[1]] $service_provider = undef,
String[1] $service_name = 'mongod',
Expand Down
2 changes: 1 addition & 1 deletion manifests/server/config.pp
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@
if $handle_creds {
file { $rcfile:
ensure => file,
content => template('mongodb/mongorc.js.erb'),
content => template('mongodb/mongoshrc.js.erb'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this really client side config that should be managed in client.pp or client/config.pp?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically this isn't configuration, it is a set of commands that get executed by the client when loading this script. It is required by the provider to set up the server correctly, not configuring the client. Apart from a package install the client doesn't need configuration. So I would leave it in the server/config.pp.
It contains for example the credentials of the admin user, which obviously are specific to that particular server.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have to do anything about this now, as it's the same with mongorc.
But IMO users should be able to manage this file however they like without breaking the provider.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I created an issue for this: #717

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 @@ -29,8 +29,8 @@ class { 'mongodb::globals':
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 Down Expand Up @@ -59,8 +59,8 @@ class { 'mongodb::globals':
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 @@ -52,7 +52,7 @@ class { 'mongodb::globals':
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