Salt: mongodb_user not creating users

Created on 2 Dec 2013  路  14Comments  路  Source: saltstack/salt

This has been an ongoing issue. I posted about it in the mailing list here:
https://groups.google.com/forum/#!searchin/salt-users/gpgkeys/salt-users/24E_ahS8OC0/Cup3NIy3Fb8J

Basically, I have a salt state file which installs MongoDB and attempts to configure users. It installed MongoDB just fine, but throws an exception the first time when it attempts to set up the user.

Here is the state file I am using:

mongodb:
  pkgrepo.managed:
    - name: deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen
    - keyid: 7F0CEB10
    - keyserver: keyserver.ubuntu.com
    - file: /etc/apt/sources.list.d/mongodb.list
    - refresh: True

  pkg.installed:
    - name: mongodb-10gen
    - version: 2.4.8
    - refresh: True
    - require:
      - pkgrepo: mongodb

  file.managed:
    - name: /etc/mongodb.conf
    - source: salt://mongodb/mongodb.conf
    - require:
      - pkg: mongodb

  service.running:
    - enable: True
    - require:
      - pkg: mongodb
      - file: /etc/mongodb.conf
    - watch:
      - file: /etc/mongodb.conf

pip:
  pkg.installed:
    - pkgs:
      - python-pip
      - python-dev
      - build-essential

pymongo:
  pip.installed:
    - require:
      - pkg: pip
      - pkg: mongodb

mongo_root:
  mongodb_user.present:
    - name: root
    - passwd: PASSWORD
    - host: localhost
    - port: 27017
    - database: admin
    - require:
      - service: mongodb
      - pip: pymongo

And here is the error I get when the last state, mongodb_user, runs:

 State: - mongodb_user
    Name:      root
    Function:  present
        Result:    False
        Comment:   An exception occurred in this state: Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.7/salt/state.py", line 1280, in call
    ret = self.states[cdata['full']](*cdata['args'])
  File "/usr/lib/pymodules/python2.7/salt/states/mongodb_user.py", line 45, in present
    if __salt__['mongodb.user_exists'](name, user, password, host, port, database):
  File "/usr/lib/pymodules/python2.7/salt/modules/mongodb.py", line 186, in user_exists
    if name == dict(user).get('user'):
ValueError: dictionary update sequence element #0 has length 1; 2 is required

There is this additional error in the minion log:

2013-12-01 20:56:15,918 [salt.loaded.int.module.mongodb][ERROR   ] Error connecting to database admin

The strange part is, it will eventually work. If I re-run the state again, it completes successfully:

sudo salt-call --local state.sls mongodb

Sometimes it works on the 2nd run, sometimes it takes a 3rd. I have not yet been able to determine what is changing on the 2nd run. Perhaps Mongod is not started properly yet? I did try adding a 120 second sleep command in case there was such a race condition happening, but it did not alleviate the problem.

I am working around it by setting up the users with a JS script run with mongo via cmd.run.

mongo localhost/admin --quiet /var/lib/mongodb/userCreate.js:
  cmd:
    - run
    - user: mongodb
    - group: mongodb
    - require:
      - service: mongodb

/var/lib/mongodb/userCreate.js is just:

db.addUser("root", "PASSWORD");

This is running the latest salt, on Vagrant Ubuntu 12.04.

Salt: 0.17.2
Python: 2.7.3 (default, Sep 26 2013, 20:03:06)
Jinja2: 2.6
M2Crypto: 0.21.1
msgpack-python: 0.1.10
msgpack-pure: Not Installed
pycrypto: 2.4.1
PyYAML: 3.10
PyZMQ: 13.0.0
ZMQ: 3.2.2
Bug

Most helpful comment

@basepi Please consider reopening this bug.

The problem is _to_dict returns a python list with a python dictionary inside (called from here). When roles, is used in the mdb.eval("db.grantRolesToUser('{0}', {1})".format(name, roles)) roles is really roles.__str__(). This is a problem because after string formatting, the result contains unicode markup which db.grantRolesToUser(...) doesn't like.

In [1]: name = 'test'
In [2]: roles = '[{"role": "readWrite", "db": "foo" }]'
In [3]: roles = _to_dict(roles)
In [4]: "db.grantRolesToUser('{0}', {1})".format(name, roles)

Out[4]: 'db.grantRolesToUser(\'test\', [{u"db": u"foo", u"role": u"readWrite"}])'

Should really look like this:

'db.grantRolesToUser(\'test\', [{"role": "readWrite", "db": "foo" }])'

I can submit a pull request with the fix if desired.

All 14 comments

Thanks for the report. We will look into this.

I am running into this issue as well running version 2014.1.10. I have created an admin user in the admin database and I am trying to create a new user in a separate database, passing the username and password for the admin user to authenticate. To create the admin user I had to use the mongo shell.

@blarghmatey Does it work the second time you run the state? That was the behavior @thaddeusmt was seeing.

For me it wasn't working at all. I've since moved on to a different approach, but I will let you know once I revisit that state in a slightly different context.

Same error here on mongodb_user

@arthurlogilab Same behavior as well? It works, but reports an error?

I think I know from where this bug is coming. Its a problem related to the function mongodb.user_exists of the Mongodb module.

That function is used by mongodb_user.present to check if a user is already present or not but it does not handle correctly database connection errors.

If you didn't use correctly the require statement, you can get that error during the first run of the state.

I think I can provide a patch tomorrow, here it is time to sleep in France ;-)

@blueicefield Did you ever get a chance to submit a pull request? I see a few commits, wondered if they were ever submitted/accepted.

@basepi My commits were merged one hour ago.

Awesome!

I'm still getting this bug with 2014.1.13+ds-3 in which version has the above patch been included (I always have trouble finding that information...)

It looks like these were never backported (they are only in the develop branch), and so are slated for the next feature release after 2015.2.0 (codenamed Beryllium).

I realize this is an old issue and is somewhat unrelated to my issue. However, I want to add a follow up about errors I was getting when trying to use the mongodb salt modules user_list function etc., while having mongodb authentication enabled. The not authorized to execute error is posted below.

[root@mnode00 bkeep]# salt-call mongodb.user_list admin password localhost 27017 admin
[ERROR   ] Listing users failed with error: not authorized on admin to execute command { $eval: db.version(), args: [] }
local:
    not authorized on admin to execute command { $eval: db.version(), args: [] }

The solution is to create a new role and grant that role to your user
https://dzone.com/articles/grant-right-to-use-eval-on-mongodb-32

The role to be created

db.createRole({
    role: "executeEval",
    privileges: [{
        resource: {
            anyResource: true
        },
        actions: ["anyAction"]
    }],
    roles: []
})

You can apply this on the cli for the admin user like so
mongo -u admin -p password --quiet --eval "db.createRole({role:'executeEval',privileges:[{resource:{anyResource: true},actions:['anyAction']}],roles:[]})" admin

Grant the role to the user

db.grantRolesToUser('admin',
[{
    role: 'executeEval',
    db: 'admin'
}])

This too can be applied on the cli for the admin user like so
mongo -u admin -p password --quiet --eval "db.grantRolesToUser('admin',[{role:'executeEval',db:'admin'}])" admin

Maybe this will save someone a few google searches.
Regards,
Brandon

@basepi Please consider reopening this bug.

The problem is _to_dict returns a python list with a python dictionary inside (called from here). When roles, is used in the mdb.eval("db.grantRolesToUser('{0}', {1})".format(name, roles)) roles is really roles.__str__(). This is a problem because after string formatting, the result contains unicode markup which db.grantRolesToUser(...) doesn't like.

In [1]: name = 'test'
In [2]: roles = '[{"role": "readWrite", "db": "foo" }]'
In [3]: roles = _to_dict(roles)
In [4]: "db.grantRolesToUser('{0}', {1})".format(name, roles)

Out[4]: 'db.grantRolesToUser(\'test\', [{u"db": u"foo", u"role": u"readWrite"}])'

Should really look like this:

'db.grantRolesToUser(\'test\', [{"role": "readWrite", "db": "foo" }])'

I can submit a pull request with the fix if desired.

Was this page helpful?
0 / 5 - 0 ratings