Rush has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/403202 )

Change subject: wip: rabbitmq: handling users and initial setup
......................................................................

wip: rabbitmq: handling users and initial setup

A few inconsistencies and issues across our stuff:

* labtestcontrol2001 did not have the management
  plugin enabled which made the cleanup cron invalid
* the queuecleanup script did not handle a queue
  not being present yet and they are created dynamically
  (this is not by necessarily an errant state)
* queuecleanup script had hard coded credentials
* There has been no way to manage rabbitmq users and
  this has largely been done adhoc and outside of
  Puppets purview resulting in inconsistencies
* Rabbitmq can run on the non-primary control node
  and really needs to in order to manage rabbit
  internals as rabbit has a local user database
  and settings.
* guest account had been left in use on installs
* header docs for rabbit/init.pp were confusing

Change-Id: I1870faa07e49603bd7eff9f38cc1202519aeb467
---
M modules/profile/manifests/openstack/base/rabbitmq.pp
M modules/rabbitmq/files/drain_queue
A modules/rabbitmq/files/rabbit_random_guest.sh
M modules/rabbitmq/manifests/cleanup.pp
M modules/rabbitmq/manifests/init.pp
A modules/rabbitmq/manifests/user.pp
6 files changed, 142 insertions(+), 27 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/operations/puppet 
refs/changes/02/403202/1

diff --git a/modules/profile/manifests/openstack/base/rabbitmq.pp 
b/modules/profile/manifests/openstack/base/rabbitmq.pp
index efd0127..ffececf 100644
--- a/modules/profile/manifests/openstack/base/rabbitmq.pp
+++ b/modules/profile/manifests/openstack/base/rabbitmq.pp
@@ -10,7 +10,6 @@
 ){
 
     class { '::rabbitmq':
-        running      => $::fqdn == $nova_controller,
         file_handles => $file_handles,
     }
     contain '::rabbitmq'
diff --git a/modules/rabbitmq/files/drain_queue 
b/modules/rabbitmq/files/drain_queue
index bd14e4e..4b98dbf 100644
--- a/modules/rabbitmq/files/drain_queue
+++ b/modules/rabbitmq/files/drain_queue
@@ -28,17 +28,21 @@
 import urllib.parse
 
 
+def eprint(*args, **kwargs):
+    print(*args, file=sys.stderr, **kwargs)
+
 def die(s):
-    print('*** {}'.format(s), file=sys.stderr)
+    eprint('*** {}'.format(s))
     exit(1)
 
-
-def http_req(verb, path, body=None):
+def http_req(username, password, verb, path, body=None):
     path = '/api%s' % path
     conn = http.client.HTTPConnection('localhost', 15672)
+    credentials = '{}:{}'.format(username, password)
+    b = credentials.encode()
     headers = {
         'Authorization': 'Basic {}'.format(
-            base64.b64encode(b'guest:guest').decode('ascii')),
+            base64.b64encode(b).decode('ascii')),
     }
     if body:
         headers['Content-Type'] = 'application/json'
@@ -53,11 +57,20 @@
     if resp.status == 401:
         die('Access refused: {}'.format(path))
     if resp.status == 404:
-        die('Not found: {}'.format(path))
+        # Rabbitmq manages queues dynamically so
+        # the existence of a queue may depend on a message
+        # ever needing to be delivered to it.  Even
+        # necessary queues are often created on-demand.
+        eprint('Queue not found!')
+        return json.dumps('')
     if resp.status == 301:
         url = urllib.parse.urlparse(resp.getheader('location'))
         [host, port] = url.netloc.split(':')
-        return http_req(verb, url.path + '?' + url.query, body)
+        return http_req(username,
+                        password,
+                        verb,
+                        url.path + '?' + url.query,
+                        body)
     if resp.status < 200 or resp.status > 400:
         raise Exception(
             'Received {:d} {} for path {}\n{}'.format(
@@ -65,12 +78,19 @@
     return resp_body
 
 
-def http_json(verb, path, body=None):
-    return json.loads(http_req(verb, path, body))
+def http_json(username, password, verb, path, body=None):
+    return json.loads(http_req(username, password, verb, path, body))
 
 
-def message_count(queue):
-    return http_json('GET', '/queues/%2F/{}'.format(queue))['messages_ready']
+def message_count(username, password, queue):
+    out = http_json(username,
+                     password,
+                     'GET',
+                     '/queues/%2F/{}'.format(queue))
+    if not out:
+        return None
+    return out['messages_ready']
+
 
 
 def main():
@@ -86,14 +106,29 @@
     parser.add_argument(
         'queue', metavar='QUEUE', nargs=1,
         help='queue to read messages from')
+    parser.add_argument(
+        '--username', default='drainqueue',
+        help='username to connect to rabbitmq')
+    parser.add_argument(
+        '--password', default='',
+        help='password to connect to rabbitmq')
+
     args = parser.parse_args()
     queue = args.queue[0]
+    username = args.username
+    password = args.password
 
     if args.silent:
-        http_req('DELETE', '/queues/%2F/{}/contents'.format(queue))
+        http_req(username,
+                 password,
+                 'DELETE',
+                 '/queues/%2F/{}/contents'.format(queue))
 
     else:
-        ready = message_count(queue)
+        ready = message_count(username, password, queue)
+        if ready is None:
+            return
+        print("message count is {}".format(ready))
         while ready > 0:
             msgs = http_json(
                 'POST',
@@ -113,7 +148,7 @@
             if args.requeue:
                 ready = 0  # prevent infinte loops
             else:
-                ready = message_count(queue)
+                ready = message_count(username, password, queue)
 
 
 if __name__ == '__main__':
diff --git a/modules/rabbitmq/files/rabbit_random_guest.sh 
b/modules/rabbitmq/files/rabbit_random_guest.sh
new file mode 100644
index 0000000..40527ca
--- /dev/null
+++ b/modules/rabbitmq/files/rabbit_random_guest.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+# A guest user with a default 'guest' password will
+# automatically be created for the RabbitMQ service.
+# It should be never used
+
+# recommendations seems to be to set to unknown
+# value rather than remove the default account
+# which may be handwaving based on older versions
+# of rabbitmq not handling the missing builtin well
+RNDM=$(date +%s | sha256sum | base64 | head -c 32 ; echo)
+/usr/sbin/rabbitmqctl change_password guest $RNDM
+/usr/sbin/rabbitmqctl list_users
diff --git a/modules/rabbitmq/manifests/cleanup.pp 
b/modules/rabbitmq/manifests/cleanup.pp
index ac2afa0..bd5b44c 100644
--- a/modules/rabbitmq/manifests/cleanup.pp
+++ b/modules/rabbitmq/manifests/cleanup.pp
@@ -2,11 +2,20 @@
 # that are not being consumed and this becomes costly
 # over time.
 
+# Enabled is only for active cron, all other
+# components are setup if the class is included.
+
 class rabbitmq::cleanup(
     $enabled=false,
+    $username='drainqueue',
+    $password,
     ) {
 
     require rabbitmq
+
+    if ! ($password) {
+       fail("password must be set for ${name}")
+    }
 
     if ($enabled) {
         $ensure = 'present'
@@ -15,11 +24,24 @@
         $ensure = 'absent'
     }
 
+    file { '/usr/local/sbin/drain_queue':
+        ensure => 'present',
+        owner  => 'root',
+        group  => 'root',
+        mode   => '0655',
+        source => 'puppet:///modules/rabbitmq/drain_queue',
+    }
+
+    rabbitmq::user{'drainqueue':
+        username => $username,
+        password => $password,
+    }
+
     # These logfiles will be rotated by an already-existing wildcard logrotate 
rule for rabbit
     cron { 'drain and log rabbit notifications.error queue':
             ensure  => $ensure,
             user    => 'root',
             minute  => '35',
-            command => '/usr/local/sbin/drain_queue notifications.error >> 
/var/log/rabbitmq/notifications_error.log 2>&1',
+            command => "/usr/local/sbin/drain_queue notifications.error  
--password ${password}>> /var/log/rabbitmq/notifications_error.log 2>&1",
     }
 }
diff --git a/modules/rabbitmq/manifests/init.pp 
b/modules/rabbitmq/manifests/init.pp
index 3e905ff..16dadb0 100644
--- a/modules/rabbitmq/manifests/init.pp
+++ b/modules/rabbitmq/manifests/init.pp
@@ -2,22 +2,21 @@
 #
 # User MAC's are not handled by Puppet
 #
-# Changing a user password
-#  rabbitmqctl change_password <user> <password>
 # Adding a user
 #  rabbitmqctl add_user <user> <password>
-#
-# Creating user "<user>" ...
+# Changing a user password
 #  rabbitmqctl change_password <user> <password>
-#  rabbitmqctl set_user_tags <user> administrator
+#
 # Setting tags for user "<user>" to [administrator] ...
+#  rabbitmqctl set_user_tags <user> administrator
+# All permissions example
 #  rabbitmqctl set_permissions -p / <user> ".*" ".*" ".*"
 #
 # The management plugin may be desired
 #  rabbitmq-plugins enable rabbitmq_management
 
 class rabbitmq(
-    $running = false,
+    $running = true,
     $file_handles = '1024',
     ) {
 
@@ -53,12 +52,31 @@
         require => Package['rabbitmq-server'],
     }
 
-    file { '/usr/local/sbin/drain_queue':
-        ensure => 'present',
-        owner  => 'root',
-        group  => 'root',
-        mode   => '0655',
-        source => 'puppet:///modules/rabbitmq/drain_queue',
+    file {'/usr/local/sbin/rabbit_random_guest':
+        ensure  => 'present',
+        owner   => 'root',
+        group   => 'root',
+        mode    => '0655',
+        source  => 'puppet:///modules/rabbitmq/random_guesh.sh',
+        require => Package['rabbitmq-server'],
+    }
+
+    exec { 'invalidate_rabbitmq_guest_account':
+        command     => 'rabbit_random_guest'
+        path        => ['/usr/local/sbin/'],
+        subscribe   => File['/usr/local/sbin/random_guest'],
+        refreshonly => true,
+    }
+
+    # this appears to be idempotent
+    # https://www.rabbitmq.com/management.html
+    # Needed for https://www.rabbitmq.com/management-cli.html
+    # rabbitmq-plugins -E list | egrep '\[E\*]\srabbitmq_management
+    exec { 'enable_management_plugin':
+        command     => 'rabbitmq-plugins enable rabbitmq_management'
+        path        => ['/usr/sbin/'],
+        subscribe   => Package['rabbitmq-server'],
+        refreshonly => true,
     }
 
     service { 'rabbitmq-server':
diff --git a/modules/rabbitmq/manifests/user.pp 
b/modules/rabbitmq/manifests/user.pp
new file mode 100644
index 0000000..5eb2e43
--- /dev/null
+++ b/modules/rabbitmq/manifests/user.pp
@@ -0,0 +1,29 @@
+class rabbitmq::user(
+    $ensure='present',
+    $username,
+    $password,
+    $permissions='".*" ".*" ".*"',
+    ), {
+
+    if ($ensure == 'present') {
+        exec {'rabbit_user_create':
+            command => "rabbitmqctl add_user ${user} ${password}",
+            path    => ['/usr/sbin/'],
+            unless  => "rabbitmqctl list_users | grep --quiet ${USER}",
+            notify  => Exec['rabbit_user_setup_perms'],
+        }
+
+        exec {'rabbit_user_setup_perms':
+            command     => "rabbitmqctl set_permissions ${permissions}",
+            path        => ['/usr/sbin/'],
+            refreshonly => true,
+        }
+    }
+
+    if ($ensure == 'absent') {
+        exec {'rabbit_user_removal':
+            command => "rabbitmqctl delete_user ${user}",
+            onlyif  => "rabbitmqctl list_users | grep --quiet ${USER}",
+        }
+    }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/403202
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I1870faa07e49603bd7eff9f38cc1202519aeb467
Gerrit-PatchSet: 1
Gerrit-Project: operations/puppet
Gerrit-Branch: production
Gerrit-Owner: Rush <r...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to