diff --git a/.gitignore b/.gitignore index 7a7b6d9dc61a..a42a3ab53f68 100644 --- a/.gitignore +++ b/.gitignore @@ -6,12 +6,15 @@ cover/ debug/ deps/ +debug/ doc/ ebin/ etc/ logs/ plugins/ +PACKAGES/ + rabbit.d # Generated sources files. @@ -43,3 +46,5 @@ rabbitmq-server-*.zip # Tracing tools *-ttb *.ti + +PACKAGES/* diff --git a/.travis.yml b/.travis.yml index 6d9dbc0b7137..da19ebc30273 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ sudo: false +services: + - docker language: erlang notifications: email: @@ -11,14 +13,20 @@ otp_release: - "R16B03-1" - "17.5" - "18.0" +env: + matrix: + - GROUP=1 GROUP=2 # The checkout made by Travis is a "detached HEAD". We switch back # to a tag or a branch. This pleases our git_rmq fetch method in # rabbitmq-components.mk and the proper tag/branch is selected in # dependencies too. -before_script: (test "$TRAVIS_TAG" && git checkout "$TRAVIS_TAG") || (test "$TRAVIS_BRANCH" && git checkout "$TRAVIS_BRANCH") +before_script: + - ([ "${GROUP}" = "1" ] && ((test "$TRAVIS_TAG" && git checkout "$TRAVIS_TAG") || (test "$TRAVIS_BRANCH" && git checkout "$TRAVIS_BRANCH")) || /bin/true) -script: travis_wait make tests +script: + - ([ "${GROUP}" = "1" ] && travis_wait make tests) + - ([ "${GROUP}" = "2" ] && sh ./scripts/travis_test_ocf_ra.sh) cache: apt: true diff --git a/Makefile b/Makefile index 026c05edb468..924160849672 100644 --- a/Makefile +++ b/Makefile @@ -129,7 +129,8 @@ $(TARGETS_IN_RABBITMQ_TEST): $(ERLANG_MK_RECURSIVE_TEST_DEPS_LIST) \ grep -E '^xmlto version 0\.0\.([0-9]|1[1-8])$$' >/dev/null || \ opt='--stringparam man.indent.verbatims=0' ; \ xsltproc --novalid $(DOCS_DIR)/examples-to-end.xsl $< > $<.tmp && \ - (xmlto -o $(DOCS_DIR) $$opt man $< 2>&1 | (grep -qv '^Note: Writing' || :)) && \ + xmlto -vv -o $(DOCS_DIR) $$opt man $< 2>&1 | (grep -v '^Note: Writing' || :) && \ + test -f $@ && \ rm $<.tmp # Use tmp files rather than a pipeline so that we get meaningful errors @@ -336,7 +337,8 @@ SCRIPTS = rabbitmq-defaults \ rabbitmq-env \ rabbitmq-server \ rabbitmqctl \ - rabbitmq-plugins + rabbitmq-plugins \ + cuttlefish WINDOWS_SCRIPTS = rabbitmq-defaults.bat \ rabbitmq-echopid.bat \ @@ -344,7 +346,8 @@ WINDOWS_SCRIPTS = rabbitmq-defaults.bat \ rabbitmq-plugins.bat \ rabbitmq-server.bat \ rabbitmq-service.bat \ - rabbitmqctl.bat + rabbitmqctl.bat \ + cuttlefish UNIX_TO_DOS ?= todos @@ -355,7 +358,7 @@ install: install-erlapp install-scripts install-erlapp: dist $(verbose) mkdir -p $(DESTDIR)$(RMQ_ERLAPP_DIR) - $(inst_verbose) cp -r include ebin plugins LICENSE* INSTALL \ + $(inst_verbose) cp -r include ebin plugins priv LICENSE* INSTALL \ $(DESTDIR)$(RMQ_ERLAPP_DIR) $(verbose) echo "Put your EZs here and use rabbitmq-plugins to enable them." \ > $(DESTDIR)$(RMQ_ERLAPP_DIR)/plugins/README @@ -385,18 +388,18 @@ install-man: manpages $(inst_verbose) sections=$$(ls -1 docs/*.[1-9] \ | sed -E 's/.*\.([1-9])$$/\1/' | uniq | sort); \ for section in $$sections; do \ - mkdir -p $(DESTDIR)$(MANDIR)/man$$section; \ - for manpage in $(DOCS_DIR)/*.$$section; do \ - gzip < $$manpage \ + mkdir -p $(DESTDIR)$(MANDIR)/man$$section; \ + for manpage in $(DOCS_DIR)/*.$$section; do \ + gzip < $$manpage \ > $(DESTDIR)$(MANDIR)/man$$section/$$(basename $$manpage).gz; \ - done; \ - done + done; \ + done install-windows: install-windows-erlapp install-windows-scripts install-windows-docs install-windows-erlapp: dist $(verbose) mkdir -p $(DESTDIR)$(WINDOWS_PREFIX) - $(inst_verbose) cp -r include ebin plugins LICENSE* INSTALL \ + $(inst_verbose) cp -r include ebin plugins priv LICENSE* INSTALL \ $(DESTDIR)$(WINDOWS_PREFIX) $(verbose) echo "Put your EZs here and use rabbitmq-plugins.bat to enable them." \ > $(DESTDIR)$(WINDOWS_PREFIX)/plugins/README.txt @@ -449,3 +452,4 @@ package-rpm-suse package-windows package-standalone-macosx \ package-generic-unix: $(PACKAGES_SOURCE_DIST_FILE) $(verbose) $(MAKE) -C packaging $@ \ SOURCE_DIST_FILE=$(abspath $(PACKAGES_SOURCE_DIST_FILE)) + diff --git a/docs/README-for-packages b/docs/README-for-packages index 35a1523ac3fa..f26889b6408f 100644 --- a/docs/README-for-packages +++ b/docs/README-for-packages @@ -18,3 +18,7 @@ An example configuration file is provided in the same directory as this README. Copy it to /etc/rabbitmq/rabbitmq.config to use it. The RabbitMQ server must be restarted after changing the configuration file. + +An example policy file for HA queues is provided in the same directory +as this README. Copy and chmod +x it to +/usr/local/sbin/set_rabbitmq_policy to use it with the Pacemaker OCF RA. diff --git a/docs/advanced.config.example b/docs/advanced.config.example new file mode 100644 index 000000000000..82a1c000e1a4 --- /dev/null +++ b/docs/advanced.config.example @@ -0,0 +1,109 @@ +[ + + + %% ---------------------------------------------------------------------------- + %% Advanced Erlang Networking/Clustering Options. + %% + %% See http://www.rabbitmq.com/clustering.html for details + %% ---------------------------------------------------------------------------- + %% Sets the net_kernel tick time. + %% Please see http://erlang.org/doc/man/kernel_app.html and + %% http://www.rabbitmq.com/nettick.html for further details. + %% + %% {kernel, [{net_ticktime, 60}]}, + %% ---------------------------------------------------------------------------- + %% RabbitMQ Shovel Plugin + %% + %% See http://www.rabbitmq.com/shovel.html for details + %% ---------------------------------------------------------------------------- + + {rabbitmq_shovel, + [{shovels, + [%% A named shovel worker. + %% {my_first_shovel, + %% [ + + %% List the source broker(s) from which to consume. + %% + %% {sources, + %% [%% URI(s) and pre-declarations for all source broker(s). + %% {brokers, ["amqp://user:password@host.domain/my_vhost"]}, + %% {declarations, []} + %% ]}, + + %% List the destination broker(s) to publish to. + %% {destinations, + %% [%% A singular version of the 'brokers' element. + %% {broker, "amqp://"}, + %% {declarations, []} + %% ]}, + + %% Name of the queue to shovel messages from. + %% + %% {queue, <<"your-queue-name-goes-here">>}, + + %% Optional prefetch count. + %% + %% {prefetch_count, 10}, + + %% when to acknowledge messages: + %% - no_ack: never (auto) + %% - on_publish: after each message is republished + %% - on_confirm: when the destination broker confirms receipt + %% + %% {ack_mode, on_confirm}, + + %% Overwrite fields of the outbound basic.publish. + %% + %% {publish_fields, [{exchange, <<"my_exchange">>}, + %% {routing_key, <<"from_shovel">>}]}, + + %% Static list of basic.properties to set on re-publication. + %% + %% {publish_properties, [{delivery_mode, 2}]}, + + %% The number of seconds to wait before attempting to + %% reconnect in the event of a connection failure. + %% + %% {reconnect_delay, 2.5} + + %% ]} %% End of my_first_shovel + ]} + %% Rather than specifying some values per-shovel, you can specify + %% them for all shovels here. + %% + %% {defaults, [{prefetch_count, 0}, + %% {ack_mode, on_confirm}, + %% {publish_fields, []}, + %% {publish_properties, [{delivery_mode, 2}]}, + %% {reconnect_delay, 2.5}]} + ]}, + + {rabbitmq_auth_backend_ldap, [ + %% + %% Authorisation + %% ============= + %% + + %% The LDAP plugin can perform a variety of queries against your + %% LDAP server to determine questions of authorisation. See + %% http://www.rabbitmq.com/ldap.html#authorisation for more + %% information. + + %% Set the query to use when determining vhost access + %% + %% {vhost_access_query, {in_group, + %% "ou=${vhost}-users,ou=vhosts,dc=example,dc=com"}}, + + %% Set the query to use when determining resource (e.g., queue) access + %% + %% {resource_access_query, {constant, true}}, + + %% Set queries to determine which tags a user has + %% + %% {tag_queries, []} + ]} +]. + + + diff --git a/docs/rabbitmq-server.service.example b/docs/rabbitmq-server.service.example new file mode 100644 index 000000000000..5a8f1cd73e7c --- /dev/null +++ b/docs/rabbitmq-server.service.example @@ -0,0 +1,17 @@ +# systemd unit example +[Unit] +Description=RabbitMQ broker +After=syslog.target network.target + +[Service] +Type=notify +User=rabbitmq +Group=rabbitmq +NotifyAccess=all +TimeoutStartSec=3600 +WorkingDirectory=/var/lib/rabbitmq +ExecStart=/usr/lib/rabbitmq/bin/rabbitmq-server +ExecStop=/usr/lib/rabbitmq/bin/rabbitmqctl stop + +[Install] +WantedBy=multi-user.target diff --git a/docs/rabbitmq.conf.example b/docs/rabbitmq.conf.example new file mode 100644 index 000000000000..7e60c53da4f5 --- /dev/null +++ b/docs/rabbitmq.conf.example @@ -0,0 +1,736 @@ +# ====================================== +# RabbbitMQ broker section +# ====================================== + +## Network Connectivity +## ==================== +## +## By default, RabbitMQ will listen on all interfaces, using +## the standard (reserved) AMQP port. +## +# listeners.tcp.default = 5672 + + +## To listen on a specific interface, provide an IP address with port. +## For example, to listen only on localhost for both IPv4 and IPv6: +## +# IPv4 +# listeners.tcp.local = 127.0.0.1:5672 +# IPv6 +# listeners.tcp.local_v6 = ::1:5672 + +## You can define multiple listeners using listener names +# listeners.tcp.other_port = 5673 +# listeners.tcp.other_ip = 10.10.10.10:5672 + + +## SSL listeners are configured in the same fashion as TCP listeners, +## including the option to control the choice of interface. +## +# listeners.ssl.default = 5671 + +## Number of Erlang processes that will accept connections for the TCP +## and SSL listeners. +## +# num_acceptors.tcp = 10 +# num_acceptors.ssl = 1 + + +## Maximum time for AMQP 0-8/0-9/0-9-1 handshake (after socket connection +## and SSL handshake), in milliseconds. +## +# handshake_timeout = 10000 + +## Set to 'true' to perform reverse DNS lookups when accepting a +## connection. Hostnames will then be shown instead of IP addresses +## in rabbitmqctl and the management plugin. +## +# reverse_dns_lookups = true + +## +## Security / AAA +## ============== +## + +## The default "guest" user is only permitted to access the server +## via a loopback interface (e.g. localhost). +## {loopback_users, [<<"guest">>]}, +## +# loopback_users.guest = true + +## Uncomment the following line if you want to allow access to the +## guest user from anywhere on the network. +# loopback_users.guest = false + +## Configuring SSL. +## See http://www.rabbitmq.com/ssl.html for full documentation. +## +# ssl_options.verify = verify_peer +# ssl_options.fail_if_no_peer_cert = false +# ssl_options.cacertfile = /path/to/cacert.pem +# ssl_options.certfile = /path/to/cert.pem +# ssl_options.keyfile = /path/to/key.pem + +## Select an authentication/authorisation backend to use. +## +## Alternative backends are provided by plugins, such as rabbitmq-auth-backend-ldap. +## +## NB: These settings require certain plugins to be enabled. +## See http://www.rabbitmq.com/plugins.html and http://rabbitmq.com/access-control.html +## for details. + +# auth_backends.1 = rabbit_auth_backend_internal + +## uses separate backends for authentication and authorisation, +## see below. +# auth_backends.1.authn = rabbit_auth_backend_ldap +# auth_backends.1.authz = rabbit_auth_backend_internal + +## The rabbitmq_auth_backend_ldap plugin allows the broker to +## perform authentication and authorisation by deferring to an +## external LDAP server. +## +## For more information about configuring the LDAP backend, see +## http://www.rabbitmq.com/ldap.html and http://rabbitmq.com/access-control.html. +## +## uses LDAP for both authentication and authorisation +# auth_backends.1 = rabbit_auth_backend_ldap + +## uses HTTP service for both authentication and +## authorisation +# auth_backends.1 = rabbit_auth_backend_http + +## uses two backends in a chain: HTTP first, then internal +# auth_backends.1 = rabbit_auth_backend_http +# auth_backends.2 = rabbit_auth_backend_internal + +## Authentication +## The built-in mechanisms are 'PLAIN', +## 'AMQPLAIN', and 'EXTERNAL' Additional mechanisms can be added via +## plugins. +## +## See http://www.rabbitmq.com/authentication.html for more details. +## +# auth_mechanisms.1 = PLAIN +# auth_mechanisms.2 = AMQPLAIN + +## The rabbitmq-auth-mechanism-ssl plugin makes it possible to +## authenticate a user based on the client's x509 (TLS) certificate. +## See http://www.rabbitmq.com/authentication.html for more info. +## +## To use auth-mechanism-ssl, the EXTERNAL mechanism should +## be enabled: +## +# auth_mechanisms.1 = PLAIN +# auth_mechanisms.2 = AMQPLAIN +# auth_mechanisms.3 = EXTERNAL + +## To force x509 certificate-based authentication on all clients, +## exclude all other mechanisms (note: this will disable password-based +## authentication even for the management UI!): +## +# auth_mechanisms.1 = EXTERNAL + +## This pertains to both the rabbitmq-auth-mechanism-ssl plugin and +## STOMP ssl_cert_login configurations. See the rabbitmq_stomp +## configuration section later in this file and the README in +## https://github.com/rabbitmq/rabbitmq-auth-mechanism-ssl for further +## details. +## +## To use the SSL cert's CN instead of its DN as the username +## +# ssl_cert_login_from = common_name + +## SSL handshake timeout, in milliseconds. +## +# ssl_handshake_timeout = 5000 + + +## Password hashing implementation. Will only affect newly +## created users. To recalculate hash for an existing user +## it's necessary to update her password. +## +## To use SHA-512, set to rabbit_password_hashing_sha512. +## +# password_hashing_module = rabbit_password_hashing_sha256 + +## When importing definitions exported from versions earlier +## than 3.6.0, it is possible to go back to MD5 (only do this +## as a temporary measure!) by setting this to rabbit_password_hashing_md5. +## +# password_hashing_module = rabbit_password_hashing_md5 + +## +## Default User / VHost +## ==================== +## + +## On first start RabbitMQ will create a vhost and a user. These +## config items control what gets created. See +## http://www.rabbitmq.com/access-control.html for further +## information about vhosts and access control. +## +# default_vhost = / +# default_user = guest +# default_pass = guest + +# default_permissions.configure = .* +# default_permissions.read = .* +# default_permissions.write = .* + +## Tags for default user +## +## For more details about tags, see the documentation for the +## Management Plugin at http://www.rabbitmq.com/management.html. +## +# default_user_tags.administrator = true + +## Define other tags like this: +# default_user_tags.management = true +# default_user_tags.custom_tag = true + +## +## Additional network and protocol related configuration +## ===================================================== +## + +## Set the default AMQP 0-9-1 heartbeat interval (in seconds). +## See http://rabbitmq.com/heartbeats.html for more details. +## +# heartbeat = 600 + +## Set the max permissible size of an AMQP frame (in bytes). +## +# frame_max = 131072 + +## Set the max frame size the server will accept before connection +## tuning occurs +## +# initial_frame_max = 4096 + +## Set the max permissible number of channels per connection. +## 0 means "no limit". +## +# channel_max = 128 + +## Customising Socket Options. +## +## See (http://www.erlang.org/doc/man/inet.html#setopts-2) for +## further documentation. +## + +# tcp_listen_options.backlog = 128 +# tcp_listen_options.nodelay = true +# tcp_listen_options.exit_on_close = false + +## +## Resource Limits & Flow Control +## ============================== +## +## See http://www.rabbitmq.com/memory.html for full details. + +## Memory-based Flow Control threshold. +## +# vm_memory_high_watermark.relative = 0.4 + +## Alternatively, we can set a limit (in bytes) of RAM used by the node. +## +# vm_memory_high_watermark.absolute = 1073741824 + +## Or you can set absolute value using memory units (with RabbitMQ 3.6.0+). +## Absolute watermark will be ignored if relative is defined! +## +# vm_memory_high_watermark.absolute = 2GB +## +## Supported units suffixes: +## +## kb, KB: kibibytes (2^10 bytes) +## mb, MB: mebibytes (2^20) +## gb, GB: gibibytes (2^30) + + + +## Fraction of the high watermark limit at which queues start to +## page message out to disc in order to free up memory. +## +## Values greater than 0.9 can be dangerous and should be used carefully. +## +# vm_memory_high_watermark_paging_ratio = 0.5 + +## Interval (in milliseconds) at which we perform the check of the memory +## levels against the watermarks. +## +# memory_monitor_interval = 2500 + +## Set disk free limit (in bytes). Once free disk space reaches this +## lower bound, a disk alarm will be set - see the documentation +## listed above for more details. +## +## Absolute watermark will be ignored if relative is defined! +# disk_free_limit.absolute = 50000 + +## Or you can set it using memory units (same as in vm_memory_high_watermark) +## with RabbitMQ 3.6.0+. +# disk_free_limit.absolute = 500KB +# disk_free_limit.absolute = 50mb +# disk_free_limit.absolute = 5GB + +## Alternatively, we can set a limit relative to total available RAM. +## +## Values lower than 1.0 can be dangerous and should be used carefully. +# disk_free_limit.relative = 2.0 + +## +## Clustering +## ===================== +## +# cluster_partition_handling = ignore + +## pause_if_all_down strategy require additional configuration +# cluster_partition_handling = pause_if_all_down + +## Recover strategy. Can be either 'autoheal' or 'ignore' +# cluster_partition_handling.pause_if_all_down.recover = ignore + +## Node names to check +# cluster_partition_handling.pause_if_all_down.nodes.1 = rabbit@localhost +# cluster_partition_handling.pause_if_all_down.nodes.2 = hare@localhost + +## Mirror sync batch size, in messages. Increasing this will speed +## up syncing but total batch size in bytes must not exceed 2 GiB. +## Available in RabbitMQ 3.6.0 or later. +## +# mirroring_sync_batch_size = 4096 + +## Make clustering happen *automatically* at startup - only applied +## to nodes that have just been reset or started for the first time. +## See http://www.rabbitmq.com/clustering.html#auto-config for +## further details. +## +# cluster_nodes.disc.rabbit = rabbit@my.host.com + +## You can define multiple nodes +# cluster_nodes.disc.hare = hare@my.host.com + +## There can be also ram nodes. +## Ram nodes should not be defined together with disk nodes +# cluster_nodes.ram.rabbit = rabbit@my.host.com + +## Interval (in milliseconds) at which we send keepalive messages +## to other cluster members. Note that this is not the same thing +## as net_ticktime; missed keepalive messages will not cause nodes +## to be considered down. +## +# cluster_keepalive_interval = 10000 + +## +## Statistics Collection +## ===================== +## + +## Set (internal) statistics collection granularity. +## +## Can be none, coarse or fine +# collect_statistics = none + +# collect_statistics = coarse + +## Statistics collection interval (in milliseconds). Increasing +## this will reduce the load on management database. +## +# collect_statistics_interval = 5000 + +## +## Misc/Advanced Options +## ===================== +## +## NB: Change these only if you understand what you are doing! +## + +## Explicitly enable/disable hipe compilation. +## +# hipe_compile = false + +## Timeout used when waiting for Mnesia tables in a cluster to +## become available. +## +# mnesia_table_loading_timeout = 30000 + +## Size in bytes below which to embed messages in the queue index. See +## http://www.rabbitmq.com/persistence-conf.html +## +# queue_index_embed_msgs_below = 4096 + +## You can also set this size in memory units +## +# queue_index_embed_msgs_below = 4kb + +## ---------------------------------------------------------------------------- +## Advanced Erlang Networking/Clustering Options. +## +## See http://www.rabbitmq.com/clustering.html for details +## ---------------------------------------------------------------------------- + +# ====================================== +# Kernel section +# ====================================== + +# kernel.net_ticktime = 60 + +## ---------------------------------------------------------------------------- +## RabbitMQ Management Plugin +## +## See http://www.rabbitmq.com/management.html for details +## ---------------------------------------------------------------------------- + +# ======================================= +# Management section +# ======================================= + +## Pre-Load schema definitions from the following JSON file. See +## http://www.rabbitmq.com/management.html#load-definitions +## +# management.load_definitions = /path/to/schema.json + +## Log all requests to the management HTTP API to a file. +## +# management.http_log_dir = /path/to/access.log + +## Change the port on which the HTTP listener listens, +## specifying an interface for the web server to bind to. +## Also set the listener to use SSL and provide SSL options. +## + +# QA: Maybe use IP type like in tcp_listener? +# management.listener.port = 12345 +# management.listener.ip = 127.0.0.1 +# management.listener.ssl = true + +# management.listener.ssl_opts.cacertfile = /path/to/cacert.pem +# management.listener.ssl_opts.certfile = /path/to/cert.pem +# management.listener.ssl_opts.keyfile = /path/to/key.pem + +## One of 'basic', 'detailed' or 'none'. See +## http://www.rabbitmq.com/management.html#fine-stats for more details. +# management.rates_mode = basic + +## Configure how long aggregated data (such as message rates and queue +## lengths) is retained. Please read the plugin's documentation in +## http://www.rabbitmq.com/management.html#configuration for more +## details. +## Your can use 'minute', 'hour' and '24hours' keys or integer key (in seconds) +# management.sample_retention_policies.global.minute = 5 +# management.sample_retention_policies.global.hour = 60 +# management.sample_retention_policies.global.24hours = 1200 + +# management.sample_retention_policies.basic.minute = 5 +# management.sample_retention_policies.basic.hour = 60 + +# management.sample_retention_policies.detailed.10 = 5 + +## ---------------------------------------------------------------------------- +## RabbitMQ Shovel Plugin +## +## See http://www.rabbitmq.com/shovel.html for details +## ---------------------------------------------------------------------------- + +## Shovel plugin config example is defined in additional.config file + + +## ---------------------------------------------------------------------------- +## RabbitMQ Stomp Adapter +## +## See http://www.rabbitmq.com/stomp.html for details +## ---------------------------------------------------------------------------- + +# ======================================= +# STOMP section +# ======================================= + +## Network Configuration - the format is generally the same as for the broker +## +# stomp.listeners.tcp.default = 61613 + +## Same for ssl listeners +## +# stomp.listeners.ssl.default = 61614 + +## Number of Erlang processes that will accept connections for the TCP +## and SSL listeners. +## +# stomp.num_acceptors.tcp = 10 +# stomp.num_acceptors.ssl = 1 + +## Additional SSL options + +## Extract a name from the client's certificate when using SSL. +## +# stomp.ssl_cert_login = true + +## Set a default user name and password. This is used as the default login +## whenever a CONNECT frame omits the login and passcode headers. +## +## Please note that setting this will allow clients to connect without +## authenticating! +## +# stomp.default_user = guest +# stomp.default_pass = guest + +## If a default user is configured, or you have configured use SSL client +## certificate based authentication, you can choose to allow clients to +## omit the CONNECT frame entirely. If set to true, the client is +## automatically connected as the default user or user supplied in the +## SSL certificate whenever the first frame sent on a session is not a +## CONNECT frame. +## +# stomp.implicit_connect = true + +## ---------------------------------------------------------------------------- +## RabbitMQ MQTT Adapter +## +## See https://github.com/rabbitmq/rabbitmq-mqtt/blob/stable/README.md +## for details +## ---------------------------------------------------------------------------- + +# ======================================= +# MQTT section +# ======================================= + +## Set the default user name and password. Will be used as the default login +## if a connecting client provides no other login details. +## +## Please note that setting this will allow clients to connect without +## authenticating! +## +# mqtt.default_user = guest +# mqtt.default_pass = guest + +## Enable anonymous access. If this is set to false, clients MUST provide +## login information in order to connect. See the default_user/default_pass +## configuration elements for managing logins without authentication. +## +# mqtt.allow_anonymous = true + +## If you have multiple chosts, specify the one to which the +## adapter connects. +## +# mqtt.vhost = / + +## Specify the exchange to which messages from MQTT clients are published. +## +# mqtt.exchange = amq.topic + +## Specify TTL (time to live) to control the lifetime of non-clean sessions. +## +# mqtt.subscription_ttl = 1800000 + +## Set the prefetch count (governing the maximum number of unacknowledged +## messages that will be delivered). +## +# mqtt.prefetch = 10 + +## TCP/SSL Configuration (as per the broker configuration). +## +# mqtt.listeners.tcp.default = 1883 + +## Same for ssl listener +## +# mqtt.listeners.ssl.default = 1884 + +## Number of Erlang processes that will accept connections for the TCP +## and SSL listeners. +## +# mqtt.num_acceptors.tcp = 10 +# mqtt.num_acceptors.ssl = 1 + +## TCP/Socket options (as per the broker configuration). +## +# mqtt.tcp_listen_options.backlog = 128 +# mqtt.tcp_listen_options.nodelay = true + +## ---------------------------------------------------------------------------- +## RabbitMQ AMQP 1.0 Support +## +## See https://github.com/rabbitmq/rabbitmq-amqp1.0/blob/stable/README.md +## for details +## ---------------------------------------------------------------------------- + +# ======================================= +# AMQP_1 section +# ======================================= + + +## Connections that are not authenticated with SASL will connect as this +## account. See the README for more information. +## +## Please note that setting this will allow clients to connect without +## authenticating! +## +# amqp1_0.default_user = guest + +## Enable protocol strict mode. See the README for more information. +## +# amqp1_0.protocol_strict_mode = false + +## Lager controls logging. +## See https://github.com/basho/lager for more documentation +## +## Log direcrory, taken from the RABBITMQ_LOG_BASE env variable by default. +## +# log.dir = /var/log/rabbitmq + +## Logging to console (can be true or false) +## +# log.console = false + +## Loglevel to log to console +## +# log.console.level = info + +## Logging to file. Can be false or filename. +## Default: +# log.file = rabbit.log + +## To turn off: +# log.file = false + +## Loglevel to log to file +## +# log.file.level = info + +## File rotation config. No rotation by defualt. +## DO NOT SET rotation date to ''. Leave unset if require "" value +# log.file.rotation.date = $D0 +# log.file.rotation.size = 0 + + +## QA: Config for syslog logging +# log.syslog = false +# log.syslog.identity = rabbitmq +# log.syslog.level = info +# log.syslog.facility = daemon + + +## ---------------------------------------------------------------------------- +## RabbitMQ LDAP Plugin +## +## See http://www.rabbitmq.com/ldap.html for details. +## +## ---------------------------------------------------------------------------- + +# ======================================= +# LDAP section +# ======================================= + +## +## Connecting to the LDAP server(s) +## ================================ +## + +## Specify servers to bind to. You *must* set this in order for the plugin +## to work properly. +## +# ldap.servers.1 = your-server-name-goes-here + +## You can define multiple servers +# ldap.servers.2 = your-other-server + +## Connect to the LDAP server using SSL +## +# ldap.use_ssl = false + +## Specify the LDAP port to connect to +## +# ldap.port = 389 + +## LDAP connection timeout, in milliseconds or 'infinity' +## +# ldap.timeout = infinity + +## Or number +# ldap.timeout = 500 + +## Enable logging of LDAP queries. +## One of +## - false (no logging is performed) +## - true (verbose logging of the logic used by the plugin) +## - network (as true, but additionally logs LDAP network traffic) +## +## Defaults to false. +## +# ldap.log = false + +## Also can be true or network +# ldap.log = true +# ldap.log = network + +## +## Authentication +## ============== +## + +## Pattern to convert the username given through AMQP to a DN before +## binding +## +# ldap.user_dn_pattern = cn=${username},ou=People,dc=example,dc=com + +## Alternatively, you can convert a username to a Distinguished +## Name via an LDAP lookup after binding. See the documentation for +## full details. + +## When converting a username to a dn via a lookup, set these to +## the name of the attribute that represents the user name, and the +## base DN for the lookup query. +## +# ldap.dn_lookup_attribute = userPrincipalName +# ldap.dn_lookup_base = DC=gopivotal,DC=com + +## Controls how to bind for authorisation queries and also to +## retrieve the details of users logging in without presenting a +## password (e.g., SASL EXTERNAL). +## One of +## - as_user (to bind as the authenticated user - requires a password) +## - anon (to bind anonymously) +## - {UserDN, Password} (to bind with a specified user name and password) +## +## Defaults to 'as_user'. +## +# ldap.other_bind = as_user + +## Or can be more complex: +# ldap.other_bind.user_dn = User +# ldap.other_bind.password = Password + +## If user_dn and password defined - other options is ignored. + +# ----------------------------- +# Too complex section of LDAP +# ----------------------------- + +## +## Authorisation +## ============= +## + +## The LDAP plugin can perform a variety of queries against your +## LDAP server to determine questions of authorisation. See +## http://www.rabbitmq.com/ldap.html#authorisation for more +## information. + +## Following configuration should be defined in additional.config file +## DO NOT UNCOMMENT THIS LINES! + +## Set the query to use when determining vhost access +## +## {vhost_access_query, {in_group, +## "ou=${vhost}-users,ou=vhosts,dc=example,dc=com"}}, + +## Set the query to use when determining resource (e.g., queue) access +## +## {resource_access_query, {constant, true}}, + +## Set queries to determine which tags a user has +## +## {tag_queries, []} +# ]}, +# ----------------------------- diff --git a/docs/rabbitmqctl.1.xml b/docs/rabbitmqctl.1.xml index 885243c657cd..ddacc7e8c216 100644 --- a/docs/rabbitmqctl.1.xml +++ b/docs/rabbitmqctl.1.xml @@ -535,7 +535,7 @@ - sync_queue queue + sync_queue -p vhost queue @@ -564,7 +564,7 @@ - cancel_sync_queue queue + cancel_sync_queue -p vhost queue @@ -584,7 +584,7 @@ - purge_queue queue + purge_queue -p vhost queue @@ -803,11 +803,11 @@ - add_vhost vhostpath + add_vhost vhost - vhostpath + vhost The name of the virtual host entry to create. @@ -824,11 +824,11 @@ - delete_vhost vhostpath + delete_vhost vhost - vhostpath + vhost The name of the virtual host entry to delete. @@ -885,11 +885,11 @@ - set_permissions -p vhostpath user conf write read + set_permissions -p vhost user conf write read - vhostpath + vhost The name of the virtual host to which to grant the user access, defaulting to /. @@ -925,11 +925,11 @@ - clear_permissions -p vhostpath username + clear_permissions -p vhost username - vhostpath + vhost The name of the virtual host to which to deny the user access, defaulting to /. @@ -951,11 +951,11 @@ - list_permissions -p vhostpath + list_permissions -p vhost - vhostpath + vhost The name of the virtual host for which to list the users that have been granted access to it, and their permissions. Defaults to /. @@ -1015,7 +1015,7 @@ - set_parameter -p vhostpath component_name name value + set_parameter -p vhost component_name name value Sets a parameter. @@ -1051,7 +1051,7 @@ - clear_parameter -p vhostpath component_name key + clear_parameter -p vhost component_name key Clears a parameter. @@ -1079,7 +1079,7 @@ - list_parameters -p vhostpath + list_parameters -p vhost Lists all parameters for a virtual host. @@ -1104,7 +1104,7 @@ - set_policy -p vhostpath --priority priority --apply-to apply-to name pattern definition + set_policy -p vhost --priority priority --apply-to apply-to name pattern definition Sets a policy. @@ -1151,7 +1151,7 @@ - clear_policy -p vhostpath name + clear_policy -p vhost name Clears a policy. @@ -1172,7 +1172,7 @@ - list_policies -p vhostpath + list_policies -p vhost Lists all policies for a virtual host. @@ -1254,7 +1254,7 @@ - list_queues -p vhostpath queueinfoitem ... + list_queues -p vhost queueinfoitem ... Returns queue details. Queue details of the / virtual host @@ -1430,7 +1430,7 @@ - list_exchanges -p vhostpath exchangeinfoitem ... + list_exchanges -p vhost exchangeinfoitem ... Returns exchange details. Exchange details of the / virtual host @@ -1493,7 +1493,7 @@ - list_bindings -p vhostpath bindinginfoitem ... + list_bindings -p vhost bindinginfoitem ... Returns binding details. By default the bindings for @@ -1835,7 +1835,7 @@ - list_consumers -p vhostpath + list_consumers -p vhost List consumers, i.e. subscriptions to a queue's message @@ -1872,6 +1872,21 @@ + + node_health_check + + + Health check of the RabbitMQ node. Verifies the rabbit application is + running, list_queues and list_channels return, and alarms are not set. + + For example: + rabbitmqctl node_health_check -n rabbit@stringer + + This command performs a health check on the RabbitMQ node. + + + + environment diff --git a/scripts/set_rabbitmq_policy.sh b/docs/set_rabbitmq_policy.sh.example similarity index 99% rename from scripts/set_rabbitmq_policy.sh rename to docs/set_rabbitmq_policy.sh.example index a88b0c417a4d..f46e901ad56b 100644 --- a/scripts/set_rabbitmq_policy.sh +++ b/docs/set_rabbitmq_policy.sh.example @@ -2,4 +2,3 @@ # cluster start up. It is a convenient place to set your cluster # policy here, for example: # ${OCF_RESKEY_ctl} set_policy ha-all "." '{"ha-mode":"all", "ha-sync-mode":"automatic"}' --apply-to all --priority 0 - diff --git a/include/rabbit_cli.hrl b/include/rabbit_cli.hrl index a23ea3bbc67c..ad8ac37b323f 100644 --- a/include/rabbit_cli.hrl +++ b/include/rabbit_cli.hrl @@ -46,8 +46,6 @@ -define(OFFLINE_DEF, {?OFFLINE_OPT, flag}). -define(ONLINE_DEF, {?ONLINE_OPT, flag}). --define(RPC_TIMEOUT, infinity). - %% Subset of standartized exit codes from sysexits.h, see %% https://github.com/rabbitmq/rabbitmq-server/issues/396 for discussion. -define(EX_OK , 0). diff --git a/packaging/RPMS/Fedora/rabbitmq-server.spec b/packaging/RPMS/Fedora/rabbitmq-server.spec index 7cf31774007f..b6715d430a6d 100644 --- a/packaging/RPMS/Fedora/rabbitmq-server.spec +++ b/packaging/RPMS/Fedora/rabbitmq-server.spec @@ -11,7 +11,7 @@ Source2: rabbitmq-server.logrotate URL: http://www.rabbitmq.com/ BuildArch: noarch BuildRequires: erlang >= R16B-03, python-simplejson, xmlto, libxslt, gzip, sed, zip, rsync -Requires: erlang >= R16B-03, logrotate +Requires: erlang >= R16B-03, logrotate, socat BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%{_arch}-root Summary: The RabbitMQ server Requires(post): %%REQUIRES%% @@ -26,7 +26,6 @@ RabbitMQ is an open source multi-protocol messaging broker. %define _rabbit_server_ocf scripts/rabbitmq-server.ocf %define _plugins_state_dir %{_localstatedir}/lib/rabbitmq/plugins %define _rabbit_server_ha_ocf scripts/rabbitmq-server-ha.ocf -%define _set_rabbitmq_policy_sh scripts/set_rabbitmq_policy.sh %define _maindir %{buildroot}%{_rabbit_erllibdir} @@ -51,7 +50,6 @@ mkdir -p %{buildroot}%{_localstatedir}/log/rabbitmq install -p -D -m 0755 %{S:1} %{buildroot}%{_initrddir}/rabbitmq-server install -p -D -m 0755 %{_rabbit_server_ocf} %{buildroot}%{_exec_prefix}/lib/ocf/resource.d/rabbitmq/rabbitmq-server install -p -D -m 0755 %{_rabbit_server_ha_ocf} %{buildroot}%{_exec_prefix}/lib/ocf/resource.d/rabbitmq/rabbitmq-server-ha -install -p -D -m 0644 %{_set_rabbitmq_policy_sh} %{buildroot}%{_exec_prefix}/lib/ocf/resource.d/rabbitmq/set_rabbitmq_policy.sh.example install -p -D -m 0644 %{S:2} %{buildroot}%{_sysconfdir}/logrotate.d/rabbitmq-server mkdir -p %{buildroot}%{_sysconfdir}/rabbitmq @@ -95,6 +93,8 @@ fi /sbin/chkconfig --add %{name} if [ -f %{_sysconfdir}/rabbitmq/rabbitmq.conf ] && [ ! -f %{_sysconfdir}/rabbitmq/rabbitmq-env.conf ]; then mv %{_sysconfdir}/rabbitmq/rabbitmq.conf %{_sysconfdir}/rabbitmq/rabbitmq-env.conf +else + touch %{_sysconfdir}/rabbitmq/rabbitmq-env.conf fi chmod -R o-rwx,g-w %{_localstatedir}/lib/rabbitmq/mnesia @@ -125,11 +125,15 @@ done %doc LICENSE* %doc README %doc docs/rabbitmq.config.example +%doc docs/set_rabbitmq_policy.sh.example %clean rm -rf %{buildroot} %changelog +* Tue Mar 1 2016 michael@rabbitmq.com 3.6.1-1 +- New Upstream Release + * Tue Dec 22 2015 michael@rabbitmq.com 3.6.0-1 - New Upstream Release diff --git a/packaging/debs/Debian/debian/changelog b/packaging/debs/Debian/debian/changelog index 372afa8258d1..adf8ce5aa546 100644 --- a/packaging/debs/Debian/debian/changelog +++ b/packaging/debs/Debian/debian/changelog @@ -1,3 +1,9 @@ +rabbitmq-server (3.6.1-1) unstable; urgency=low + + * New Upstream Release + + -- Michael Klishin Tue, 01 Mar 2016 13:19:57 +0000 + rabbitmq-server (3.6.0-1) unstable; urgency=low * New Upstream Release diff --git a/packaging/debs/Debian/debian/control b/packaging/debs/Debian/debian/control index 56acaa948ec5..9cf494ab8774 100644 --- a/packaging/debs/Debian/debian/control +++ b/packaging/debs/Debian/debian/control @@ -18,7 +18,7 @@ Standards-Version: 3.9.4 Package: rabbitmq-server Architecture: all -Depends: erlang-nox (>= 1:16.b.3) | esl-erlang, adduser, logrotate, ${misc:Depends} +Depends: erlang-nox (>= 1:16.b.3) | esl-erlang, adduser, logrotate, socat, ${misc:Depends} Description: Multi-protocol messaging broker RabbitMQ is an open source multi-protocol messaging broker. Homepage: http://www.rabbitmq.com/ diff --git a/packaging/debs/Debian/debian/postinst b/packaging/debs/Debian/debian/postinst index c83881e6bad4..2439612c428d 100644 --- a/packaging/debs/Debian/debian/postinst +++ b/packaging/debs/Debian/debian/postinst @@ -40,6 +40,8 @@ case "$1" in if [ -f /etc/rabbitmq/rabbitmq.conf ] && \ [ ! -f /etc/rabbitmq/rabbitmq-env.conf ]; then mv /etc/rabbitmq/rabbitmq.conf /etc/rabbitmq/rabbitmq-env.conf + else + touch /etc/rabbitmq/rabbitmq-env.conf fi ;; diff --git a/packaging/debs/Debian/debian/rabbitmq-server.docs b/packaging/debs/Debian/debian/rabbitmq-server.docs index 40d4f2dc8120..fbe9f95a054a 100644 --- a/packaging/debs/Debian/debian/rabbitmq-server.docs +++ b/packaging/debs/Debian/debian/rabbitmq-server.docs @@ -1 +1,2 @@ docs/rabbitmq.config.example +docs/set_rabbitmq_policy.sh.example diff --git a/packaging/debs/Debian/debian/rules b/packaging/debs/Debian/debian/rules index 669e30c177ec..053df181150f 100755 --- a/packaging/debs/Debian/debian/rules +++ b/packaging/debs/Debian/debian/rules @@ -49,8 +49,6 @@ override_dh_auto_install: $(DEB_DESTDIR)$(PREFIX)/lib/ocf/resource.d/rabbitmq/rabbitmq-server install -p -D -m 0755 scripts/rabbitmq-server-ha.ocf \ $(DEB_DESTDIR)$(PREFIX)/lib/ocf/resource.d/rabbitmq/rabbitmq-server-ha - install -p -D -m 0644 scripts/set_rabbitmq_policy.sh \ - $(DEB_DESTDIR)$(PREFIX)/lib/ocf/resource.d/rabbitmq/set_rabbitmq_policy.sh.example rm $(DEB_DESTDIR)$(RMQ_ERLAPP_DIR)/LICENSE* \ $(DEB_DESTDIR)$(RMQ_ERLAPP_DIR)/INSTALL diff --git a/packaging/windows-exe/rabbitmq_nsi.in b/packaging/windows-exe/rabbitmq_nsi.in index 3c868b91e7e9..ae1585c37292 100644 --- a/packaging/windows-exe/rabbitmq_nsi.in +++ b/packaging/windows-exe/rabbitmq_nsi.in @@ -10,6 +10,8 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !define uninstall "Software\Microsoft\Windows\CurrentVersion\Uninstall\RabbitMQ" +!define MUI_FINISHPAGE_NOAUTOCLOSE +!define MUI_UNFINISHPAGE_NOAUTOCLOSE ;-------------------------------- ; Third-party functions @@ -135,6 +137,20 @@ Section "RabbitMQ Server (required)" Rabbit ; Set output path to the user's data directory SetOutPath $APPDATA\RabbitMQ + IfFileExists advanced.config 0 +2 + Goto config_written + IfFileExists rabbitmq.config 0 +3 + Rename rabbitmq.config advanced.config + Goto config_written + ClearErrors + FileOpen $0 advanced.config w + IfErrors config_written + FileWrite $0 "[]." + FileClose $0 + config_written: + + + ; ...And put the example config file there File "rabbitmq_server-%%VERSION%%\etc\rabbitmq.config.example" diff --git a/priv/schema/.gitignore b/priv/schema/.gitignore new file mode 100644 index 000000000000..68e5b59a44b2 --- /dev/null +++ b/priv/schema/.gitignore @@ -0,0 +1,4 @@ +# plugin schemas are extracted +# into this directory: this is a Cuttlefish +# requirement. So we ignore them. +rabbitmq_*.schema diff --git a/priv/schema/rabbitmq.schema b/priv/schema/rabbitmq.schema new file mode 100644 index 000000000000..19040da409d3 --- /dev/null +++ b/priv/schema/rabbitmq.schema @@ -0,0 +1,961 @@ +% ============================== +% Rabbit app section +% ============================== + +%% +%% Network Connectivity +%% ==================== +%% + +%% By default, RabbitMQ will listen on all interfaces, using +%% the standard (reserved) AMQP port. +%% +%% {tcp_listeners, [5672]}, +%% To listen on a specific interface, provide a tuple of {IpAddress, Port}. +%% For example, to listen only on localhost for both IPv4 and IPv6: +%% +%% {tcp_listeners, [{"127.0.0.1", 5672}, +%% {"[::1]", 5672}]}, + +{mapping, "listeners.tcp", "rabbit.tcp_listeners",[ + {datatype, {enum, [none]}} +]}. + +{mapping, "listeners.tcp.$name", "rabbit.tcp_listeners",[ + {datatype, [integer, ip]} +]}. + +{translation, "rabbit.tcp_listeners", +fun(Conf) -> + case cuttlefish:conf_get("listeners.tcp", Conf, undefined) of + none -> []; + _ -> + Settings = cuttlefish_variable:filter_by_prefix("listeners.tcp", Conf), + [ V || {_, V} <- Settings ] + end +end}. + +%% SSL listeners are configured in the same fashion as TCP listeners, +%% including the option to control the choice of interface. +%% +%% {ssl_listeners, [5671]}, + +{mapping, "listeners.ssl", "rabbit.ssl_listeners",[ + {datatype, {enum, [none]}} +]}. + +{mapping, "listeners.ssl.$name", "rabbit.ssl_listeners",[ + {datatype, [integer, ip]} +]}. + +{translation, "rabbit.ssl_listeners", +fun(Conf) -> + case cuttlefish:conf_get("listeners.ssl", Conf, undefined) of + none -> []; + _ -> + Settings = cuttlefish_variable:filter_by_prefix("listeners.ssl", Conf), + [ V || {_, V} <- Settings ] + end +end}. + +%% Number of Erlang processes that will accept connections for the TCP +%% and SSL listeners. +%% +%% {num_tcp_acceptors, 10}, +%% {num_ssl_acceptors, 1}, + +{mapping, "num_acceptors.ssl", "rabbit.num_ssl_acceptors", [ + {datatype, integer} +]}. + +{mapping, "num_acceptors.tcp", "rabbit.num_tcp_acceptors", [ + {datatype, integer} +]}. + + +%% Maximum time for AMQP 0-8/0-9/0-9-1 handshake (after socket connection +%% and SSL handshake), in milliseconds. +%% +%% {handshake_timeout, 10000}, + +{mapping, "handshake_timeout", "rabbit.handshake_timeout", [ + {datatype, integer} +]}. + +%% Set to 'true' to perform reverse DNS lookups when accepting a +%% connection. Hostnames will then be shown instead of IP addresses +%% in rabbitmqctl and the management plugin. +%% +%% {reverse_dns_lookups, true}, + +{mapping, "reverse_dns_lookups", "rabbit.reverse_dns_lookups", [ + {datatype, {enum, [true, false]}} +]}. + +{mapping, "erlang.K", "vm_args.+K", [ + {default, "true"}, + {level, advanced} +]}. + +%% +%% Security / AAA +%% ============== +%% + +%% The default "guest" user is only permitted to access the server +%% via a loopback interface (e.g. localhost). +%% {loopback_users, [<<"guest">>]}, +%% +%% Uncomment the following line if you want to allow access to the +%% guest user from anywhere on the network. +%% {loopback_users, []}, + +{mapping, "loopback_users", "rabbit.loopback_users", [ + {datatype, {enum, [none]}} +]}. + +{mapping, "loopback_users.$user", "rabbit.loopback_users", [ + {datatype, atom} +]}. + +{translation, "rabbit.loopback_users", +fun(Conf) -> + None = cuttlefish:conf_get("loopback_users", Conf, undefined), + case None of + none -> []; + _ -> + Settings = cuttlefish_variable:filter_by_prefix("loopback_users", Conf), + [ list_to_binary(U) || {["loopback_users", U], V} <- Settings, V == true ] + end +end}. + +%% Configuring SSL. +%% See http://www.rabbitmq.com/ssl.html for full documentation. +%% +%% {ssl_options, [{cacertfile, "/path/to/testca/cacert.pem"}, +%% {certfile, "/path/to/server/cert.pem"}, +%% {keyfile, "/path/to/server/key.pem"}, +%% {verify, verify_peer}, +%% {fail_if_no_peer_cert, false}]}, + +%% SSL options section ======================================================== + +{mapping, "ssl_allow_poodle_attack", "rabbit.ssl_allow_poodle_attack", +[{datatype, {enum, [true, false]}}]}. + +{mapping, "ssl_options", "rabbit.ssl_options", [ + {datatype, {enum, [none]}} +]}. + +{translation, "rabbit.ssl_options", +fun(Conf) -> + case cuttlefish:conf_get("ssl_options", Conf, undefined) of + none -> []; + _ -> cuttlefish:invalid("Invalid ssl_options") + end +end}. + +{mapping, "ssl_options.verify", "rabbit.ssl_options.verify", [ + {datatype, {enum, [verify_peer, verify_none]}}]}. + +{mapping, "ssl_options.fail_if_no_peer_cert", "rabbit.ssl_options.fail_if_no_peer_cert", [ + {datatype, {enum, [true, false]}}]}. + +{mapping, "ssl_options.cacertfile", "rabbit.ssl_options.cacertfile", + [{datatype, string}, {validators, ["file_accessible"]}]}. + +{mapping, "ssl_options.certfile", "rabbit.ssl_options.certfile", + [{datatype, string}, {validators, ["file_accessible"]}]}. + +{mapping, "ssl_options.cacerts.$name", "rabbit.ssl_options.cacerts", + [{datatype, string}]}. + +{translation, "rabbit.ssl_options.cacerts", +fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("ssl_options.cacerts", Conf), + [ list_to_binary(V) || {_, V} <- Settings ] +end}. + +{mapping, "ssl_options.cert", "rabbit.ssl_options.cert", + [{datatype, string}]}. + +{translation, "rabbit.ssl_options.cert", +fun(Conf) -> + list_to_binary(cuttlefish:conf_get("ssl_options.cert", Conf)) +end}. + +{mapping, "ssl_options.client_renegotiation", "rabbit.ssl_options.client_renegotiation", + [{datatype, {enum, [true, false]}}]}. + +{mapping, "ssl_options.crl_check", "rabbit.ssl_options.crl_check", + [{datatype, [{enum, [true, false, peer, best_effort]}]}]}. + +{mapping, "ssl_options.depth", "rabbit.ssl_options.depth", + [{datatype, integer}, {validators, ["byte"]}]}. + +{mapping, "ssl_options.dh", "rabbit.ssl_options.dh", + [{datatype, string}]}. + +{translation, "rabbit.ssl_options.dh", +fun(Conf) -> + list_to_binary(cuttlefish:conf_get("ssl_options.dh", Conf)) +end}. + +{mapping, "ssl_options.dhfile", "rabbit.ssl_options.dhfile", + [{datatype, string}, {validators, ["file_accessible"]}]}. + +{mapping, "ssl_options.honor_cipher_order", "rabbit.ssl_options.honor_cipher_order", + [{datatype, {enum, [true, false]}}]}. + +{mapping, "ssl_options.key.RSAPrivateKey", "rabbit.ssl_options.key", + [{datatype, string}]}. + +{mapping, "ssl_options.key.DSAPrivateKey", "rabbit.ssl_options.key", + [{datatype, string}]}. + +{mapping, "ssl_options.key.PrivateKeyInfo", "rabbit.ssl_options.key", + [{datatype, string}]}. + +{translation, "rabbit.ssl_options.key", +fun(Conf) -> + case cuttlefish_variable:filter_by_prefix("ssl_options.key", Conf) of + [{[_,_,Key], Val}|_] -> {list_to_atom(Key), list_to_binary(Val)}; + _ -> undefined + end +end}. + +{mapping, "ssl_options.keyfile", "rabbit.ssl_options.keyfile", + [{datatype, string}, {validators, ["file_accessible"]}]}. + +{mapping, "ssl_options.log_alert", "rabbit.ssl_options.log_alert", + [{datatype, {enum, [true, false]}}]}. + +{mapping, "ssl_options.password", "rabbit.ssl_options.password", + [{datatype, string}]}. + +{mapping, "ssl_options.psk_identity", "rabbit.ssl_options.psk_identity", + [{datatype, string}]}. + +{mapping, "ssl_options.reuse_sessions", "rabbit.ssl_options.reuse_sessions", + [{datatype, {enum, [true, false]}}]}. + +{mapping, "ssl_options.secure_renegotiate", "rabbit.ssl_options.secure_renegotiate", + [{datatype, {enum, [true, false]}}]}. + +{mapping, "ssl_options.versions.$version", "rabbit.ssl_options.versions", + [{datatype, atom}]}. + +{translation, "rabbit.ssl_options.versions", +fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("ssl_options.versions", Conf), + [ V || {_, V} <- Settings ] +end}. + +%% =========================================================================== + +%% Choose the available SASL mechanism(s) to expose. +%% The two default (built in) mechanisms are 'PLAIN' and +%% 'AMQPLAIN'. Additional mechanisms can be added via +%% plugins. +%% +%% See http://www.rabbitmq.com/authentication.html for more details. +%% +%% {auth_mechanisms, ['PLAIN', 'AMQPLAIN']}, + +{mapping, "auth_mechanisms.$name", "rabbit.auth_mechanisms", [ + {datatype, atom}]}. + +{translation, "rabbit.auth_mechanisms", +fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("auth_mechanisms", Conf), + [ V || {_, V} <- Settings ] +end}. + + +%% Select an authentication backend to use. RabbitMQ provides an +%% internal backend in the core. +%% +%% {auth_backends, [rabbit_auth_backend_internal]}, + +{translation, "rabbit.auth_backends", +fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("auth_backends", Conf), + BackendModule = fun + (internal) -> rabbit_auth_backend_internal; + (ldap) -> rabbit_auth_backend_ldap; + (http) -> rabbit_auth_backend_http; + (amqp) -> rabbit_auth_backend_amqp; + (dummy) -> rabbit_auth_backend_dummy; + (Other) when is_atom(Other) -> Other; + (_) -> cuttlefish:invalid("Unknown/unsupported auth backend") + end, + AuthBackends = [{Num, {default, BackendModule(V)}} || {["auth_backends", Num], V} <- Settings], + AuthNBackends = [{Num, {authn, BackendModule(V)}} || {["auth_backends", Num, "authn"], V} <- Settings], + AuthZBackends = [{Num, {authz, BackendModule(V)}} || {["auth_backends", Num, "authz"], V} <- Settings], + Backends = lists:foldl( + fun({NumStr, {Type, V}}, Acc) -> + Num = case catch list_to_integer(NumStr) of + N when is_integer(N) -> N; + Err -> + cuttlefish:invalid( + iolist_to_binary(io_lib:format( + "Auth backend position in the chain should be an integer ~p", [Err]))) + end, + NewVal = case dict:find(Num, Acc) of + {ok, {AuthN, AuthZ}} -> + case {Type, AuthN, AuthZ} of + {authn, undefined, _} -> + {V, AuthZ}; + {authz, _, undefined} -> + {AuthN, V}; + _ -> + cuttlefish:invalid( + iolist_to_binary( + io_lib:format( + "Auth backend already defined for the ~pth ~p backend", + [Num, Type]))) + end; + error -> + case Type of + authn -> {V, undefined}; + authz -> {undefined, V}; + default -> {V, V} + end + end, + dict:store(Num, NewVal, Acc) + end, + dict:new(), + AuthBackends ++ AuthNBackends ++ AuthZBackends), + lists:map( + fun + ({Num, {undefined, AuthZ}}) -> + cuttlefish:warn( + io_lib:format( + "Auth backend undefined for the ~pth authz backend. Using ~p", + [Num, AuthZ])), + {AuthZ, AuthZ}; + ({Num, {AuthN, undefined}}) -> + cuttlefish:warn( + io_lib:format( + "Authz backend undefined for the ~pth authn backend. Using ~p", + [Num, AuthN])), + {AuthN, AuthN}; + ({_Num, {Auth, Auth}}) -> Auth; + ({_Num, {AuthN, AuthZ}}) -> {AuthN, AuthZ} + end, + lists:keysort(1, dict:to_list(Backends))) +end}. + +{mapping, "auth_backends.$num", "rabbit.auth_backends", [ + {datatype, atom} +]}. + +{mapping, "auth_backends.$num.authn", "rabbit.auth_backends",[ + {datatype, atom} +]}. + +{mapping, "auth_backends.$num.authz", "rabbit.auth_backends",[ + {datatype, atom} +]}. + +%% This pertains to both the rabbitmq_auth_mechanism_ssl plugin and +%% STOMP ssl_cert_login configurations. See the rabbitmq_stomp +%% configuration section later in this file and the README in +%% https://github.com/rabbitmq/rabbitmq-auth-mechanism-ssl for further +%% details. +%% +%% To use the SSL cert's CN instead of its DN as the username +%% +%% {ssl_cert_login_from, common_name}, + +{mapping, "ssl_cert_login_from", "rabbit.ssl_cert_login_from", [ + {datatype, {enum, [distinguished_name, common_name]}} +]}. + +%% SSL handshake timeout, in milliseconds. +%% +%% {ssl_handshake_timeout, 5000}, + +{mapping, "ssl_handshake_timeout", "rabbit.ssl_handshake_timeout", [ + {datatype, integer} +]}. + +%% Password hashing implementation. Will only affect newly +%% created users. To recalculate hash for an existing user +%% it's necessary to update her password. +%% +%% When importing definitions exported from versions earlier +%% than 3.6.0, it is possible to go back to MD5 (only do this +%% as a temporary measure!) by setting this to rabbit_password_hashing_md5. +%% +%% To use SHA-512, set to rabbit_password_hashing_sha512. +%% +%% {password_hashing_module, rabbit_password_hashing_sha256}, + +{mapping, "password_hashing_module", "rabbit.password_hashing_module", [ + {datatype, atom} +]}. + +%% +%% Default User / VHost +%% ==================== +%% + +%% On first start RabbitMQ will create a vhost and a user. These +%% config items control what gets created. See +%% http://www.rabbitmq.com/access-control.html for further +%% information about vhosts and access control. +%% +%% {default_vhost, <<"/">>}, +%% {default_user, <<"guest">>}, +%% {default_pass, <<"guest">>}, +%% {default_permissions, [<<".*">>, <<".*">>, <<".*">>]}, + +{mapping, "default_vhost", "rabbit.default_vhost", [ + {datatype, string} +]}. + +{translation, "rabbit.default_vhost", +fun(Conf) -> + list_to_binary(cuttlefish:conf_get("default_vhost", Conf)) +end}. + +{mapping, "default_user", "rabbit.default_user", [ + {datatype, string} +]}. + +{translation, "rabbit.default_user", +fun(Conf) -> + list_to_binary(cuttlefish:conf_get("default_user", Conf)) +end}. + +{mapping, "default_pass", "rabbit.default_pass", [ + {datatype, string} +]}. + +{translation, "rabbit.default_pass", +fun(Conf) -> + list_to_binary(cuttlefish:conf_get("default_pass", Conf)) +end}. + +{mapping, "default_permissions.configure", "rabbit.default_permissions", [ + {datatype, string} +]}. + +{mapping, "default_permissions.read", "rabbit.default_permissions", [ + {datatype, string} +]}. + +{mapping, "default_permissions.write", "rabbit.default_permissions", [ + {datatype, string} +]}. + +{translation, "rabbit.default_permissions", +fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("default_permissions", Conf), + Configure = proplists:get_value(["default_permissions", "configure"], Settings), + Read = proplists:get_value(["default_permissions", "read"], Settings), + Write = proplists:get_value(["default_permissions", "write"], Settings), + [list_to_binary(Configure), list_to_binary(Read), list_to_binary(Write)] +end}. + +%% Tags for default user +%% +%% For more details about tags, see the documentation for the +%% Management Plugin at http://www.rabbitmq.com/management.html. +%% +%% {default_user_tags, [administrator]}, + +{mapping, "default_user_tags.$tag", "rabbit.default_user_tags", + [{datatype, {enum, [true, false]}}]}. + +{translation, "rabbit.default_user_tags", +fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("default_user_tags", Conf), + [ list_to_atom(Key) || {[_,Key], Val} <- Settings, Val == true ] +end}. + +%% +%% Additional network and protocol related configuration +%% ===================================================== +%% + +%% Set the default AMQP heartbeat delay (in seconds). +%% +%% {heartbeat, 600}, + +{mapping, "heartbeat", "rabbit.heartbeat", [{datatype, integer}]}. + +%% Set the max permissible size of an AMQP frame (in bytes). +%% +%% {frame_max, 131072}, + +{mapping, "frame_max", "rabbit.frame_max", [{datatype, bytesize}]}. + +%% Set the max frame size the server will accept before connection +%% tuning occurs +%% +%% {initial_frame_max, 4096}, + +{mapping, "initial_frame_max", "rabbit.initial_frame_max", [{datatype, bytesize}]}. + +%% Set the max permissible number of channels per connection. +%% 0 means "no limit". +%% +%% {channel_max, 128}, + +{mapping, "channel_max", "rabbit.channel_max", [{datatype, integer}]}. + +%% Customising Socket Options. +%% +%% See (http://www.erlang.org/doc/man/inet.html#setopts-2) for +%% further documentation. +%% +%% {tcp_listen_options, [{backlog, 128}, +%% {nodelay, true}, +%% {exit_on_close, false}]}, + +%% TCP listener section ====================================================== + +{mapping, "tcp_listen_options", "rabbit.tcp_listen_options", [ + {datatype, {enum, [none]}}]}. + +{translation, "rabbit.tcp_listen_options", +fun(Conf) -> + case cuttlefish:conf_get("tcp_listen_options", undefined) of + none -> []; + _ -> cuttlefish:invalid("Invalid tcp_listen_options") + end +end}. + +{mapping, "tcp_listen_options.backlog", "rabbit.tcp_listen_options.backlog", [ + {datatype, integer} +]}. + +{mapping, "tcp_listen_options.nodelay", "rabbit.tcp_listen_options.nodelay", [ + {datatype, {enum, [true, false]}} +]}. + +{mapping, "tcp_listen_options.buffer", "rabbit.tcp_listen_options.buffer", + [{datatype, integer}]}. + +{mapping, "tcp_listen_options.delay_send", "rabbit.tcp_listen_options.delay_send", + [{datatype, {enum, [true, false]}}]}. + +{mapping, "tcp_listen_options.dontroute", "rabbit.tcp_listen_options.dontroute", + [{datatype, {enum, [true, false]}}]}. + +{mapping, "tcp_listen_options.exit_on_close", "rabbit.tcp_listen_options.exit_on_close", + [{datatype, {enum, [true, false]}}]}. + +{mapping, "tcp_listen_options.fd", "rabbit.tcp_listen_options.fd", + [{datatype, integer}]}. + +{mapping, "tcp_listen_options.high_msgq_watermark", "rabbit.tcp_listen_options.high_msgq_watermark", + [{datatype, integer}]}. + +{mapping, "tcp_listen_options.high_watermark", "rabbit.tcp_listen_options.high_watermark", + [{datatype, integer}]}. + +{mapping, "tcp_listen_options.keepalive", "rabbit.tcp_listen_options.keepalive", + [{datatype, {enum, [true, false]}}]}. + +{mapping, "tcp_listen_options.low_msgq_watermark", "rabbit.tcp_listen_options.low_msgq_watermark", + [{datatype, integer}]}. + +{mapping, "tcp_listen_options.low_watermark", "rabbit.tcp_listen_options.low_watermark", + [{datatype, integer}]}. + +{mapping, "tcp_listen_options.port", "rabbit.tcp_listen_options.port", + [{datatype, integer}, {validators, ["port"]}]}. + +{mapping, "tcp_listen_options.priority", "rabbit.tcp_listen_options.priority", + [{datatype, integer}]}. + +{mapping, "tcp_listen_options.recbuf", "rabbit.tcp_listen_options.recbuf", + [{datatype, integer}]}. + +{mapping, "tcp_listen_options.send_timeout", "rabbit.tcp_listen_options.send_timeout", + [{datatype, integer}]}. + +{mapping, "tcp_listen_options.send_timeout_close", "rabbit.tcp_listen_options.send_timeout_close", + [{datatype, {enum, [true, false]}}]}. + +{mapping, "tcp_listen_options.sndbuf", "rabbit.tcp_listen_options.sndbuf", + [{datatype, integer}]}. + +{mapping, "tcp_listen_options.tos", "rabbit.tcp_listen_options.tos", + [{datatype, integer}]}. + +%% ========================================================================== + +%% +%% Resource Limits & Flow Control +%% ============================== +%% +%% See http://www.rabbitmq.com/memory.html for full details. + +%% Memory-based Flow Control threshold. +%% +%% {vm_memory_high_watermark, 0.4}, + +%% Alternatively, we can set a limit (in bytes) of RAM used by the node. +%% +%% {vm_memory_high_watermark, {absolute, 1073741824}}, +%% +%% Or you can set absolute value using memory units (with RabbitMQ 3.6.0+). +%% +%% {vm_memory_high_watermark, {absolute, "1024M"}}, +%% +%% Supported units suffixes: +%% +%% kb, KB: kibibytes (2^10 bytes) +%% mb, MB: mebibytes (2^20) +%% gb, GB: gibibytes (2^30) + +{mapping, "vm_memory_high_watermark.relative", "rabbit.vm_memory_high_watermark", [ + {datatype, float}]}. + +{mapping, "vm_memory_high_watermark.absolute", "rabbit.vm_memory_high_watermark", [ + {datatype, [integer, string]}]}. + + +{translation, "rabbit.vm_memory_high_watermark", +fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("vm_memory_high_watermark", Conf), + Absolute = proplists:get_value(["vm_memory_high_watermark", "absolute"], Settings), + Relative = proplists:get_value(["vm_memory_high_watermark", "relative"], Settings), + case {Absolute, Relative} of + {undefined, undefined} -> cuttlefish:invalid("No vm watermark defined"); + {_, undefined} -> {absolute, Absolute}; + _ -> Relative + end +end}. + +%% Fraction of the high watermark limit at which queues start to +%% page message out to disc in order to free up memory. +%% +%% Values greater than 0.9 can be dangerous and should be used carefully. +%% +%% {vm_memory_high_watermark_paging_ratio, 0.5}, + +{mapping, "vm_memory_high_watermark_paging_ratio", + "rabbit.vm_memory_high_watermark_paging_ratio", + [{datatype, float}, {validators, ["less_than_1"]}]}. + +%% Interval (in milliseconds) at which we perform the check of the memory +%% levels against the watermarks. +%% +%% {memory_monitor_interval, 2500}, + +{mapping, "memory_monitor_interval", "rabbit.memory_monitor_interval", + [{datatype, integer}]}. + +%% Set disk free limit (in bytes). Once free disk space reaches this +%% lower bound, a disk alarm will be set - see the documentation +%% listed above for more details. +%% +%% {disk_free_limit, 50000000}, +%% +%% Or you can set it using memory units (same as in vm_memory_high_watermark) +%% with RabbitMQ 3.6.0+. +%% {disk_free_limit, "50MB"}, +%% {disk_free_limit, "50000kB"}, +%% {disk_free_limit, "2GB"}, + +%% Alternatively, we can set a limit relative to total available RAM. +%% +%% Values lower than 1.0 can be dangerous and should be used carefully. +%% {disk_free_limit, {mem_relative, 2.0}}, + +{mapping, "disk_free_limit.relative", "rabbit.disk_free_limit", [ + {datatype, float}]}. + +{mapping, "disk_free_limit.absolute", "rabbit.disk_free_limit", [ + {datatype, [integer, string]}]}. + + +{translation, "rabbit.disk_free_limit", +fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("disk_free_limit", Conf), + Absolute = proplists:get_value(["disk_free_limit", "absolute"], Settings), + Relative = proplists:get_value(["disk_free_limit", "relative"], Settings), + case {Absolute, Relative} of + {undefined, undefined} -> cuttlefish:invalid("No disk limit defined"); + {_, undefined} -> Absolute; + _ -> {mem_relative, Relative} + end +end}. + +%% +%% Clustering +%% ===================== +%% + +%% How to respond to cluster partitions. +%% See http://www.rabbitmq.com/partitions.html for further details. +%% +%% {cluster_partition_handling, ignore}, + +{mapping, "cluster_partition_handling", "rabbit.cluster_partition_handling", + [{datatype, {enum, [ignore, pause_minority, autoheal, pause_if_all_down]}}]}. + +{mapping, "cluster_partition_handling.pause_if_all_down.recover", + "rabbit.cluster_partition_handling", + [{datatype, {enum, [ignore, autoheal]}}]}. + +{mapping, "cluster_partition_handling.pause_if_all_down.nodes.$name", + "rabbit.cluster_partition_handling", + [{datatype, atom}]}. + +{translation, "rabbit.cluster_partition_handling", +fun(Conf) -> + case cuttlefish:conf_get("cluster_partition_handling", Conf) of + pause_if_all_down -> + PauseIfAllDownNodes = cuttlefish_variable:filter_by_prefix( + "cluster_partition_handling.pause_if_all_down.nodes", + Conf), + case PauseIfAllDownNodes of + [] -> + cuttlefish:invalid("Nodes required for pause_if_all_down"); + _ -> + Nodes = [ V || {K,V} <- PauseIfAllDownNodes ], + PauseIfAllDownRecover = cuttlefish:conf_get( + "cluster_partition_handling.pause_if_all_down.recover", + Conf), + case PauseIfAllDownRecover of + Recover when Recover == ignore; Recover == autoheal -> + {pause_if_all_down, Nodes, Recover}; + Invalid -> + cuttlefish:invalid("Recover strategy required for pause_if_all_down") + end + end; + Other -> Other + end +end}. + +%% Mirror sync batch size, in messages. Increasing this will speed +%% up syncing but total batch size in bytes must not exceed 2 GiB. +%% Available in RabbitMQ 3.6.0 or later. +%% +%% {mirroring_sync_batch_size, 4096}, + +{mapping, "mirroring_sync_batch_size", "rabbit.mirroring_sync_batch_size", + [{datatype, bytesize}, {validators, ["size_less_than_2G"]}]}. + +%% Make clustering happen *automatically* at startup - only applied +%% to nodes that have just been reset or started for the first time. +%% See http://www.rabbitmq.com/clustering.html#auto-config for +%% further details. +%% +%% {cluster_nodes, {['rabbit@my.host.com'], disc}}, + +{mapping, "cluster_nodes.disc.$node", "rabbit.cluster_nodes", + [{datatype, atom}]}. + +{mapping, "cluster_nodes.ram.$node", "rabbit.cluster_nodes", + [{datatype, atom}]}. + +{translation, "rabbit.cluster_nodes", +fun(Conf) -> + DiskNodes = [ V || {_, V} <- cuttlefish_variable:filter_by_prefix("cluster_nodes.disc", Conf)], + RamNodes = [ V || {_, V} <- cuttlefish_variable:filter_by_prefix("cluster_nodes.ram", Conf)], + + case {DiskNodes, RamNodes} of + {_, []} -> {DiskNodes, disc}; + {[], _} -> {RamNodes, ram} + end +end}. + + +%% Interval (in milliseconds) at which we send keepalive messages +%% to other cluster members. Note that this is not the same thing +%% as net_ticktime; missed keepalive messages will not cause nodes +%% to be considered down. +%% +%% {cluster_keepalive_interval, 10000}, + +{mapping, "cluster_keepalive_interval", "rabbit.cluster_keepalive_interval", + [{datatype, integer}]}. + + +{mapping, "queue_master_locator", "rabbit.queue_master_locator", + [{datatype, string}]}. + +{translation, "rabbit.queue_master_locator", +fun(Conf) -> + list_to_binary(cuttlefish:conf_get("queue_master_locator", Conf)) +end}. + +%% +%% Statistics Collection +%% ===================== +%% + +%% Set (internal) statistics collection granularity. +%% +%% {collect_statistics, none}, + +{mapping, "collect_statistics", "rabbit.collect_statistics", + [{datatype, {enum, [none, coarse, fine]}}]}. + +%% Statistics collection interval (in milliseconds). Increasing +%% this will reduce the load on management database. +%% +%% {collect_statistics_interval, 5000}, + +{mapping, "collect_statistics_interval", "rabbit.collect_statistics_interval", + [{datatype, integer}]}. + +%% +%% Misc/Advanced Options +%% ===================== +%% +%% NB: Change these only if you understand what you are doing! +%% + +%% Explicitly enable/disable hipe compilation. +%% +%% {hipe_compile, true}, + +{mapping, "hipe_compile", "rabbit.hipe_compile", + [{datatype, {enum, [true, false]}}]}. + +%% Timeout used when waiting for Mnesia tables in a cluster to +%% become available. +%% +%% {mnesia_table_loading_timeout, 30000}, + +{mapping, "mnesia_table_loading_timeout", "rabbit.mnesia_table_loading_timeout", + [{datatype, integer}]}. + +%% Size in bytes below which to embed messages in the queue index. See +%% http://www.rabbitmq.com/persistence-conf.html +%% +%% {queue_index_embed_msgs_below, 4096} + +{mapping, "queue_index_embed_msgs_below", "rabbit.queue_index_embed_msgs_below", + [{datatype, bytesize}]}. + +% ========================== +% Lager section +% ========================== + +{mapping, "log.dir", "lager.log_root", [ + {datatype, string}, + {validators, ["dir_writable"]}]}. + +{mapping, "log.console", "lager.handlers", [ + {datatype, {enum, [true, false]}} +]}. + +{mapping, "log.syslog", "lager.handlers", [ + {datatype, {enum, [true, false]}} +]}. +{mapping, "log.file", "lager.handlers", [ + {datatype, [{enum, [false]}, string]} +]}. + +{mapping, "log.file.level", "lager.handlers", [ + {datatype, {enum, [debug, info, warning, error]}} +]}. +{mapping, "log.$handler.level", "lager.handlers", [ + {datatype, {enum, [debug, info, warning, error]}} +]}. +{mapping, "log.file.rotation.date", "lager.handlers", [ + {datatype, string} +]}. +{mapping, "log.file.rotation.size", "lager.handlers", [ + {datatype, integer} +]}. +{mapping, "log.file.rotation.count", "lager.handlers", [ + {datatype, integer} +]}. + +{mapping, "log.syslog.identity", "lager.handlers", [ + {datatype, string} +]}. +{mapping, "log.syslog.facility", "lager.handlers", [ + {datatype, atom} +]}. + +{translation, "lager.handlers", +fun(Conf) -> + ConsoleHandler = case cuttlefish:conf_get("log.console", Conf, false) of + true -> + ConsoleLevel = cuttlefish:conf_get("log.console.level", Conf, info), + [{lager_console_backend, ConsoleLevel}]; + false -> [] + end, + FileHandler = case cuttlefish:conf_get("log.file", Conf, false) of + false -> []; + File -> + FileLevel = cuttlefish:conf_get("log.file.level", Conf, info), + RotationDate = cuttlefish:conf_get("log.file.rotation.date", Conf, ""), + RotationSize = cuttlefish:conf_get("log.file.rotation.size", Conf, 0), + RotationCount = cuttlefish:conf_get("log.file.rotation.count", Conf, 10), + [{lager_file_backend, [{file, File}, + {level, FileLevel}, + {date, RotationDate}, + {size, RotationSize}, + {count, RotationCount}]}] + end, + SyslogHandler = case cuttlefish:conf_get("log.syslog", Conf, false) of + false -> []; + true -> + SyslogLevel = cuttlefish:conf_get("log.syslog.level", Conf, info), + Identity = cuttlefish:conf_get("log.syslog.identity", Conf), + Facility = cuttlefish:conf_get("log.syslog.facility", Conf), + [{lager_syslog_backend, [Identity, Facility, SyslogLevel]}] + end, + case ConsoleHandler ++ FileHandler ++ SyslogHandler of + [] -> undefined; + Other -> Other + end +end}. + + +% =============================== +% Validators +% =============================== + +{validator, "size_less_than_2G", "Byte size should be less than 2G and greater than 0", +fun(Size) when is_integer(Size) -> + Size > 0 andalso Size < 2147483648 +end}. + +{validator, "less_than_1", "Flooat is not beetween 0 and 1", +fun(Float) when is_float(Float) -> + Float > 0 andalso Float < 1 +end}. + +{validator, "port", "Invalid port number", +fun(Port) when is_integer(Port) -> + Port > 0 andalso Port < 65535 +end}. + +{validator, "byte", "Integer is not 0 + Int > 0 andalso Int < 255 +end}. + +{validator, "dir_writable", "Cannot create file in dir", +fun(Dir) -> + TestFile = filename:join(Dir, "test_file"), + file:delete(TestFile), + Res = ok == file:write_file(TestFile, <<"test">>), + file:delete(TestFile), + Res +end}. + +{validator, "file_accessible", "file doesnt exist or unaccessible", +fun(File) -> + ReadFile = file:read_file_info(File), + element(1, ReadFile) == ok +end}. + +{validator, "is_ip", "string is a valid IP address", +fun(IpStr) -> + Res = inet:parse_address(IpStr), + element(1, Res) == ok +end}. diff --git a/rabbitmq-components.mk b/rabbitmq-components.mk index eed26fdac880..b200585b30f4 100644 --- a/rabbitmq-components.mk +++ b/rabbitmq-components.mk @@ -66,6 +66,8 @@ dep_rabbitmq_test = git_rmq rabbitmq-test $(current_rmq_ref) dep_rabbitmq_web_dispatch = git_rmq rabbitmq-web-dispatch $(current_rmq_ref) $(base_rmq_ref) master dep_rabbitmq_web_stomp = git_rmq rabbitmq-web-stomp $(current_rmq_ref) $(base_rmq_ref) master dep_rabbitmq_web_stomp_examples = git_rmq rabbitmq-web-stomp-examples $(current_rmq_ref) $(base_rmq_ref) master +dep_rabbitmq_web_mqtt = git_rmq rabbitmq-web-mqtt $(current_rmq_ref) $(base_rmq_ref) master +dep_rabbitmq_web_mqtt_examples = git_rmq rabbitmq-web-mqtt-examples $(current_rmq_ref) $(base_rmq_ref) master dep_rabbitmq_website = git_rmq rabbitmq-website $(current_rmq_ref) $(base_rmq_ref) live master dep_sockjs = git_rmq sockjs-erlang $(current_rmq_ref) $(base_rmq_ref) master dep_toke = git_rmq toke $(current_rmq_ref) $(base_rmq_ref) master @@ -117,6 +119,8 @@ RABBITMQ_COMPONENTS = amqp_client \ rabbitmq_top \ rabbitmq_tracing \ rabbitmq_web_dispatch \ + rabbitmq_web_mqtt \ + rabbitmq_web_mqtt_examples \ rabbitmq_web_stomp \ rabbitmq_web_stomp_examples \ rabbitmq_website diff --git a/rabbitmq.conf.d/ldap.conf b/rabbitmq.conf.d/ldap.conf new file mode 100644 index 000000000000..2f51cbb409a7 --- /dev/null +++ b/rabbitmq.conf.d/ldap.conf @@ -0,0 +1,138 @@ +# ## ---------------------------------------------------------------------------- +# ## RabbitMQ LDAP Plugin +# ## +# ## See http://www.rabbitmq.com/ldap.html for details. +# ## +# ## ---------------------------------------------------------------------------- + + +# ======================================= +# LDAP section +# ======================================= + +# Should be defined in additional.conf maybe? + +# {rabbitmq_auth_backend_ldap, +# [## +# ## Connecting to the LDAP server(s) +# ## ================================ +# ## + +# ## Specify servers to bind to. You *must* set this in order for the plugin +# ## to work properly. +# ## +# ## {servers, ["your-server-name-goes-here"]}, + +ldap.servers.myserver = your-server-name-goes-here + +# ## Connect to the LDAP server using SSL +# ## +# ## {use_ssl, false}, + +ldap.use_ssl = false + +# ## Specify the LDAP port to connect to +# ## +# ## {port, 389}, + +ldap.port = 389 + +# ## LDAP connection timeout, in milliseconds or 'infinity' +# ## +# ## {timeout, infinity}, + +ldap.timeout = infinity + +# Or number +# ldap.timeout = 500 + +# ## Enable logging of LDAP queries. +# ## One of +# ## - false (no logging is performed) +# ## - true (verbose logging of the logic used by the plugin) +# ## - network (as true, but additionally logs LDAP network traffic) +# ## +# ## Defaults to false. +# ## +# ## {log, false}, + +ldap.log = false + +# Also can be true or network +# ldap.log = true +# ldap.log = network + +# ## +# ## Authentication +# ## ============== +# ## + +# ## Pattern to convert the username given through AMQP to a DN before +# ## binding +# ## +# ## {user_dn_pattern, "cn=${username},ou=People,dc=example,dc=com"}, + +ldap.user_dn_pattern = cn=${username},ou=People,dc=example,dc=com + +# ## Alternatively, you can convert a username to a Distinguished +# ## Name via an LDAP lookup after binding. See the documentation for +# ## full details. + +# ## When converting a username to a dn via a lookup, set these to +# ## the name of the attribute that represents the user name, and the +# ## base DN for the lookup query. +# ## +# ## {dn_lookup_attribute, "userPrincipalName"}, +# ## {dn_lookup_base, "DC=gopivotal,DC=com"}, + +ldap.dn_lookup_attribute = userPrincipalName +ldap.dn_lookup_base = DC=gopivotal,DC=com + +# ## Controls how to bind for authorisation queries and also to +# ## retrieve the details of users logging in without presenting a +# ## password (e.g., SASL EXTERNAL). +# ## One of +# ## - as_user (to bind as the authenticated user - requires a password) +# ## - anon (to bind anonymously) +# ## - {UserDN, Password} (to bind with a specified user name and password) +# ## +# ## Defaults to 'as_user'. +# ## +# ## {other_bind, as_user}, + +ldap.other_bind = as_user + +# Or can be more complex: +# ldap.other_bind.user_dn = User +# ldap.other_bind.password = Password +# If user_dn and password defined - other options is ignored. + +# ----------------------------- +# Too complex section of LDAP +# ----------------------------- + +# ## +# ## Authorisation +# ## ============= +# ## + +# ## The LDAP plugin can perform a variety of queries against your +# ## LDAP server to determine questions of authorisation. See +# ## http://www.rabbitmq.com/ldap.html#authorisation for more +# ## information. + +# ## Set the query to use when determining vhost access +# ## +# ## {vhost_access_query, {in_group, +# ## "ou=${vhost}-users,ou=vhosts,dc=example,dc=com"}}, + +# ## Set the query to use when determining resource (e.g., queue) access +# ## +# ## {resource_access_query, {constant, true}}, + +# ## Set queries to determine which tags a user has +# ## +# ## {tag_queries, []} +# ]}, +# ----------------------------- + diff --git a/rabbitmq.conf.d/rabbitmq.conf b/rabbitmq.conf.d/rabbitmq.conf new file mode 100644 index 000000000000..e702ec08b424 --- /dev/null +++ b/rabbitmq.conf.d/rabbitmq.conf @@ -0,0 +1,726 @@ +# ====================================== +# RabbbitMQ broker section +# ====================================== + +## Network Connectivity +## ==================== +## +## By default, RabbitMQ will listen on all interfaces, using +## the standard (reserved) AMQP port. +## +listener.tcp.default = 5672 + + +## To listen on a specific interface, provide an IP address with port. +## For example, to listen only on localhost for both IPv4 and IPv6: +## +# IPv4 +# listener.tcp.local = 127.0.0.1:5672 +# IPv6 +# listener.tcp.local_v6 = ::1:5672 + +## You can define multiple listeners using listener names +# listener.tcp.other_port = 5673 +# listener.tcp.other_ip = 10.10.10.10:5672 + + +## SSL listeners are configured in the same fashion as TCP listeners, +## including the option to control the choice of interface. +## +# listener.ssl.default = 5671 + +## Number of Erlang processes that will accept connections for the TCP +## and SSL listeners. +## +num_acceptors.tcp = 10 +num_acceptors.ssl = 1 + + +## Maximum time for AMQP 0-8/0-9/0-9-1 handshake (after socket connection +## and SSL handshake), in milliseconds. +## +handshake_timeout = 10000 + +## Set to 'true' to perform reverse DNS lookups when accepting a +## connection. Hostnames will then be shown instead of IP addresses +## in rabbitmqctl and the management plugin. +## +reverse_dns_lookups = true + +## +## Security / AAA +## ============== +## + +## The default "guest" user is only permitted to access the server +## via a loopback interface (e.g. localhost). +## {loopback_users, [<<"guest">>]}, +## +loopback_user.guest = true + +## Uncomment the following line if you want to allow access to the +## guest user from anywhere on the network. +# loopback_user.guest = false + +## Configuring SSL. +## See http://www.rabbitmq.com/ssl.html for full documentation. +## +ssl_option.verify = verify_peer +ssl_option.fail_if_no_peer_cert = false +# ssl_option.cacertfile = /path/to/rabbitmq.crt +# ssl_option.certfile = /path/to/rabbitmq.crt +# ssl_option.keyfile = /path/to/rabbitmq.key + +## Choose the available SASL mechanism(s) to expose. +## The two default (built in) mechanisms are 'PLAIN' and +## 'AMQPLAIN'. Additional mechanisms can be added via +## plugins. +## +## See http://www.rabbitmq.com/authentication.html for more details. +## +auth_mechanism.plain = PLAIN +auth_mechanism.amqplain = AMQPLAIN + +## Select an authentication database to use. RabbitMQ comes bundled +## with a built-in auth-database, based on mnesia. +## +auth_backends.1 = internal + +auth_backends.2.authn = ldap +auth_backends.2.authz = internal + +auth_backends.3.authz = rabbit_auth_backend_uaa + +## Configurations supporting the rabbitmq_auth_mechanism_ssl and +## rabbitmq_auth_backend_ldap plugins. +## +## NB: These options require that the relevant plugin is enabled. +## See http://www.rabbitmq.com/plugins.html for further details. + + +## The RabbitMQ-auth-mechanism-ssl plugin makes it possible to +## authenticate a user based on the client's SSL certificate. +## +## To use auth-mechanism-ssl, add to or replace the auth_mechanisms +## with EXTERNAL value. +## +#auth_mechanism.external = EXTERNAL + +## The rabbitmq_auth_backend_ldap plugin allows the broker to +## perform authentication and authorisation by deferring to an +## external LDAP server. +## +## For more information about configuring the LDAP backend, see +## http://www.rabbitmq.com/ldap.html. +## +## Enable the LDAP auth backend by adding to or replacing the +## auth_backends entry: +## +# auth_backends.2 = rabbit_auth_backend_ldap + +## Add another backend +# auth_backend.3 = rabbit_auth_backend_http + + +## This pertains to both the rabbitmq_auth_mechanism_ssl plugin and +## STOMP ssl_cert_login configurations. See the rabbitmq_stomp +## configuration section later in this file and the README in +## https://github.com/rabbitmq/rabbitmq-auth-mechanism-ssl for further +## details. +## +## To use the SSL cert's CN instead of its DN as the username +## +# ssl_cert_login_from = common_name + +## SSL handshake timeout, in milliseconds. +## +# ssl_handshake_timeout = 5000 + + +## Password hashing implementation. Will only affect newly +## created users. To recalculate hash for an existing user +## it's necessary to update her password. +## +## To use SHA-512, set to rabbit_password_hashing_sha512. +## +password_hashing_module = rabbit_password_hashing_sha256 + +## When importing definitions exported from versions earlier +## than 3.6.0, it is possible to go back to MD5 (only do this +## as a temporary measure!) by setting this to rabbit_password_hashing_md5. +## +# password_hashing_module = rabbit_password_hashing_md5 + +## +## Default User / VHost +## ==================== +## + +## On first start RabbitMQ will create a vhost and a user. These +## config items control what gets created. See +## http://www.rabbitmq.com/access-control.html for further +## information about vhosts and access control. +## +default_vhost = / +default_user = guest +default_pass = guest + +default_permissions.configure = .* +default_permissions.read = .* +default_permissions.write = .* + +## Tags for default user +## +## For more details about tags, see the documentation for the +## Management Plugin at http://www.rabbitmq.com/management.html. +## +default_user_tags.administrator = true + +## Define other tags like this: +# default_user_tags.management = true +# default_user_tags.custom_tag = true + +## +## Additional network and protocol related configuration +## ===================================================== +## + +## Set the default AMQP heartbeat delay (in seconds). +## +heartbeat = 600 + +## Set the max permissible size of an AMQP frame (in bytes). +## +frame_max = 131072 + +## Set the max frame size the server will accept before connection +## tuning occurs +## +initial_frame_max = 4096 + +## Set the max permissible number of channels per connection. +## 0 means "no limit". +## +channel_max = 128 + +## Customising Socket Options. +## +## See (http://www.erlang.org/doc/man/inet.html#setopts-2) for +## further documentation. +## + +tcp_listen_option.backlog = 128 +tcp_listen_option.nodelay = true +tcp_listen_option.exit_on_close = false + +## +## Resource Limits & Flow Control +## ============================== +## +## See http://www.rabbitmq.com/memory.html for full details. + +## Memory-based Flow Control threshold. +## +vm_memory_high_watermark.relative = 0.4 + +## Alternatively, we can set a limit (in bytes) of RAM used by the node. +## +# vm_memory_high_watermark.absolute = 1073741824 + +## Or you can set absolute value using memory units (with RabbitMQ 3.6.0+). +## Absolute watermark will be ignored if relative is defined! +## +# vm_memory_high_watermark.absolute = 2GB +## +## Supported units suffixes: +## +## kb, KB: kibibytes (2^10 bytes) +## mb, MB: mebibytes (2^20) +## gb, GB: gibibytes (2^30) + + + +## Fraction of the high watermark limit at which queues start to +## page message out to disc in order to free up memory. +## +## Values greater than 0.9 can be dangerous and should be used carefully. +## +vm_memory_high_watermark_paging_ratio = 0.5 + +## Interval (in milliseconds) at which we perform the check of the memory +## levels against the watermarks. +## +memory_monitor_interval = 2500 + +## Set disk free limit (in bytes). Once free disk space reaches this +## lower bound, a disk alarm will be set - see the documentation +## listed above for more details. +## +## Absolute watermark will be ignored if relative is defined! +disk_free_limit.absolute = 50000 + +## Or you can set it using memory units (same as in vm_memory_high_watermark) +## with RabbitMQ 3.6.0+. +# disk_free_limit.absolute = 500KB +# disk_free_limit.absolute = 50mb +# disk_free_limit.absolute = 5GB + +## Alternatively, we can set a limit relative to total available RAM. +## +## Values lower than 1.0 can be dangerous and should be used carefully. +disk_free_limit.relative = 2.0 + +## +## Clustering +## ===================== +## +cluster_partition_handling = ignore + +## pause_if_all_down strategy require additional configuration +# cluster_partition_handling = pause_if_all_down + +## Recover strategy. Can be either 'autoheal' or 'ignore' +# cluster_partition_handling.pause_if_all_down.recover = ignore + +## Node names to check +# cluster_partition_handling.pause_if_all_down.node.rabbit = rabbit@localhost +# cluster_partition_handling.pause_if_all_down.node.hare = hare@localhost + +## Mirror sync batch size, in messages. Increasing this will speed +## up syncing but total batch size in bytes must not exceed 2 GiB. +## Available in RabbitMQ 3.6.0 or later. +## +mirroring_sync_batch_size = 4096 + +## Make clustering happen *automatically* at startup - only applied +## to nodes that have just been reset or started for the first time. +## See http://www.rabbitmq.com/clustering.html#auto-config for +## further details. +## +# cluster_nodes.disc.1 = rabbit@my.host.com + +## You can define multiple nodes +# cluster_nodes.disc.2 = hare@my.host.com + +## There can be also ram nodes. +## Ram nodes should not be defined together with disk nodes +# cluster_nodes.ram.1 = rabbit@my.host.com + +## Interval (in milliseconds) at which we send keepalive messages +## to other cluster members. Note that this is not the same thing +## as net_ticktime; missed keepalive messages will not cause nodes +## to be considered down. +## +# cluster_keepalive_interval = 10000 + +## +## Statistics Collection +## ===================== +## + +## Set (internal) statistics collection granularity. +## +## Can be none, coarse or fine +collect_statistics = none + +# collect_statistics = coarse + +## Statistics collection interval (in milliseconds). Increasing +## this will reduce the load on management database. +## +collect_statistics_interval = 5000 + +## +## Misc/Advanced Options +## ===================== +## +## NB: Change these only if you understand what you are doing! +## + +## Explicitly enable/disable hipe compilation. +## +hipe_compile = false + +## Timeout used when waiting for Mnesia tables in a cluster to +## become available. +## +mnesia_table_loading_timeout = 30000 + +## Size in bytes below which to embed messages in the queue index. See +## http://www.rabbitmq.com/persistence-conf.html +## +queue_index_embed_msgs_below = 4096 + +## You can also set this size in memory units +## +queue_index_embed_msgs_below = 4kb + +## ---------------------------------------------------------------------------- +## Advanced Erlang Networking/Clustering Options. +## +## See http://www.rabbitmq.com/clustering.html for details +## ---------------------------------------------------------------------------- + +# ====================================== +# Kernel section +# ====================================== + +# kernel.net_ticktime = 60 + +## ---------------------------------------------------------------------------- +## RabbitMQ Management Plugin +## +## See http://www.rabbitmq.com/management.html for details +## ---------------------------------------------------------------------------- + +# ======================================= +# Management section +# ======================================= + +## Pre-Load schema definitions from the following JSON file. See +## http://www.rabbitmq.com/management.html#load-definitions +## +# management.load_definitions = /path/to/schema.json + +## Log all requests to the management HTTP API to a file. +## +# management.http_log_dir = /path/to/access.log + +## Change the port on which the HTTP listener listens, +## specifying an interface for the web server to bind to. +## Also set the listener to use SSL and provide SSL options. +## + +# QA: Maybe use IP type like in tcp_listener? +management.listener.port = 12345 +management.listener.ip = 127.0.0.1 +# management.listener.ssl = true + +# management.listener.ssl_opts.cacertfile = /path/to/cacert.pem +# management.listener.ssl_opts.certfile = /path/to/cert.pem +# management.listener.ssl_opts.keyfile = /path/to/key.pem + +## One of 'basic', 'detailed' or 'none'. See +## http://www.rabbitmq.com/management.html#fine-stats for more details. +management.rates_mode = basic + +## Configure how long aggregated data (such as message rates and queue +## lengths) is retained. Please read the plugin's documentation in +## http://www.rabbitmq.com/management.html#configuration for more +## details. +## Your can use 'minute', 'hour' and '24hours' keys or integer key (in seconds) +management.sample_retention_policies.global.minute = 5 +management.sample_retention_policies.global.hour = 60 +management.sample_retention_policies.global.day = 1200 + +management.sample_retention_policies.basic.minute = 5 +management.sample_retention_policies.basic.hour = 60 + +management.sample_retention_policies.detailed.10 = 5 + +## ---------------------------------------------------------------------------- +## RabbitMQ Shovel Plugin +## +## See http://www.rabbitmq.com/shovel.html for details +## ---------------------------------------------------------------------------- + +## Shovel plugin config example is defined in additional.config file + + +## ---------------------------------------------------------------------------- +## RabbitMQ Stomp Adapter +## +## See http://www.rabbitmq.com/stomp.html for details +## ---------------------------------------------------------------------------- + +# ======================================= +# STOMP section +# ======================================= + +## Network Configuration - the format is generally the same as for the broker +## +stomp.listener.tcp.default = 61613 + +## Same for ssl listeners +## +# stomp.listener.ssl.default = 61614 + +## Number of Erlang processes that will accept connections for the TCP +## and SSL listeners. +## +stomp.num_acceptors.tcp = 10 +stomp.num_acceptors.ssl = 1 + +## Additional SSL options + +## Extract a name from the client's certificate when using SSL. +## +stomp.ssl_cert_login = true + +## Set a default user name and password. This is used as the default login +## whenever a CONNECT frame omits the login and passcode headers. +## +## Please note that setting this will allow clients to connect without +## authenticating! +## +# stomp.default_user = guest +# stomp.default_pass = guest + +## If a default user is configured, or you have configured use SSL client +## certificate based authentication, you can choose to allow clients to +## omit the CONNECT frame entirely. If set to true, the client is +## automatically connected as the default user or user supplied in the +## SSL certificate whenever the first frame sent on a session is not a +## CONNECT frame. +## +# stomp.implicit_connect = true + +## ---------------------------------------------------------------------------- +## RabbitMQ MQTT Adapter +## +## See https://github.com/rabbitmq/rabbitmq-mqtt/blob/stable/README.md +## for details +## ---------------------------------------------------------------------------- + +# ======================================= +# MQTT section +# ======================================= + +## Set the default user name and password. Will be used as the default login +## if a connecting client provides no other login details. +## +## Please note that setting this will allow clients to connect without +## authenticating! +## +# mqtt.default_user = guest +# mqtt.default_pass = guest + +## Enable anonymous access. If this is set to false, clients MUST provide +## login information in order to connect. See the default_user/default_pass +## configuration elements for managing logins without authentication. +## +# mqtt.allow_anonymous = true + +## If you have multiple chosts, specify the one to which the +## adapter connects. +## +mqtt.vhost = / + +## Specify the exchange to which messages from MQTT clients are published. +## +mqtt.exchange = amq.topic + +## Specify TTL (time to live) to control the lifetime of non-clean sessions. +## +# mqtt.subscription_ttl = 1800000 + +## Set the prefetch count (governing the maximum number of unacknowledged +## messages that will be delivered). +## +mqtt.prefetch = 10 + +## TCP/SSL Configuration (as per the broker configuration). +## +mqtt.listener.tcp.default = 1883 + +## Same for ssl listener +## +# mqtt.listener.ssl.default = 1884 + +## Number of Erlang processes that will accept connections for the TCP +## and SSL listeners. +## +mqtt.num_acceptors.tcp = 10 +mqtt.num_acceptors.ssl = 1 + +## TCP/Socket options (as per the broker configuration). +## +# mqtt.tcp_listen_option.backlog = 128 +# mqtt.tcp_listen_option.nodelay = true + +## ---------------------------------------------------------------------------- +## RabbitMQ AMQP 1.0 Support +## +## See https://github.com/rabbitmq/rabbitmq-amqp1.0/blob/stable/README.md +## for details +## ---------------------------------------------------------------------------- + +# ======================================= +# AMQP_1 section +# ======================================= + + +## Connections that are not authenticated with SASL will connect as this +## account. See the README for more information. +## +## Please note that setting this will allow clients to connect without +## authenticating! +## +amqp1_0.default_user = guest + +## Enable protocol strict mode. See the README for more information. +## +amqp1_0.protocol_strict_mode = false + +## Lager controls logging. +## See https://github.com/basho/lager for more documentation +## +## Log direcrory, taken from the RABBITMQ_LOG_BASE env variable by default. +## +# log.dir = /var/log/rabbitmq + +## Logging to console (can be true or false) +## +# log.console = false + +## Loglevel to log to console +## +# log.console.level = info + +## Logging to file. Can be false or filename. +## Default: +# log.file = rabbit.log + +## To turn off: +# log.file = false + +## Loglevel to log to file +## +# log.file.level = info + +## File rotation config. No rotation by defualt. +## DO NOT SET rotation date to ''. Leave unset if require "" value +# log.file.rotation.date = $D0 +# log.file.rotation.size = 0 + + +## QA: Config for syslog logging +# log.syslog = false +# log.syslog.identity = rabbitmq +# log.syslog.level = info +# log.syslog.facility = daemon + + +## ---------------------------------------------------------------------------- +## RabbitMQ LDAP Plugin +## +## See http://www.rabbitmq.com/ldap.html for details. +## +## ---------------------------------------------------------------------------- + +# ======================================= +# LDAP section +# ======================================= + +## +## Connecting to the LDAP server(s) +## ================================ +## + +## Specify servers to bind to. You *must* set this in order for the plugin +## to work properly. +## +# ldap.servers.1 = your-server-name-goes-here + +## You can define multiple servers +# ldap.servers.2 = your-other-server + +## Connect to the LDAP server using SSL +## +# ldap.use_ssl = false + +## Specify the LDAP port to connect to +## +# ldap.port = 389 + +## LDAP connection timeout, in milliseconds or 'infinity' +## +# ldap.timeout = infinity + +## Or number +# ldap.timeout = 500 + +## Enable logging of LDAP queries. +## One of +## - false (no logging is performed) +## - true (verbose logging of the logic used by the plugin) +## - network (as true, but additionally logs LDAP network traffic) +## +## Defaults to false. +## +# ldap.log = false + +## Also can be true or network +# ldap.log = true +# ldap.log = network + +## +## Authentication +## ============== +## + +## Pattern to convert the username given through AMQP to a DN before +## binding +## +# ldap.user_dn_pattern = cn=${username},ou=People,dc=example,dc=com + +## Alternatively, you can convert a username to a Distinguished +## Name via an LDAP lookup after binding. See the documentation for +## full details. + +## When converting a username to a dn via a lookup, set these to +## the name of the attribute that represents the user name, and the +## base DN for the lookup query. +## +# ldap.dn_lookup_attribute = userPrincipalName +# ldap.dn_lookup_base = DC=gopivotal,DC=com + +## Controls how to bind for authorisation queries and also to +## retrieve the details of users logging in without presenting a +## password (e.g., SASL EXTERNAL). +## One of +## - as_user (to bind as the authenticated user - requires a password) +## - anon (to bind anonymously) +## - {UserDN, Password} (to bind with a specified user name and password) +## +## Defaults to 'as_user'. +## +# ldap.other_bind = as_user + +## Or can be more complex: +# ldap.other_bind.user_dn = User +# ldap.other_bind.password = Password + +## If user_dn and password defined - other options is ignored. + +# ----------------------------- +# Too complex section of LDAP +# ----------------------------- + +## +## Authorisation +## ============= +## + +## The LDAP plugin can perform a variety of queries against your +## LDAP server to determine questions of authorisation. See +## http://www.rabbitmq.com/ldap.html#authorisation for more +## information. + +## Following configuration should be defined in additional.config file +## DO NOT UNCOMMENT THIS LINES! + +## Set the query to use when determining vhost access +## +## {vhost_access_query, {in_group, +## "ou=${vhost}-users,ou=vhosts,dc=example,dc=com"}}, + +## Set the query to use when determining resource (e.g., queue) access +## +## {resource_access_query, {constant, true}}, + +## Set queries to determine which tags a user has +## +## {tag_queries, []} +# ]}, +# ----------------------------- diff --git a/scripts/cuttlefish b/scripts/cuttlefish new file mode 100755 index 000000000000..6c1e4bbb8993 Binary files /dev/null and b/scripts/cuttlefish differ diff --git a/scripts/rabbitmq-defaults b/scripts/rabbitmq-defaults index c5d87822a24c..bccd0d7435b4 100644 --- a/scripts/rabbitmq-defaults +++ b/scripts/rabbitmq-defaults @@ -38,6 +38,9 @@ CONFIG_FILE=${SYS_PREFIX}/etc/rabbitmq/rabbitmq LOG_BASE=${SYS_PREFIX}/var/log/rabbitmq MNESIA_BASE=${SYS_PREFIX}/var/lib/rabbitmq/mnesia ENABLED_PLUGINS_FILE=${SYS_PREFIX}/etc/rabbitmq/enabled_plugins +GENERATED_CONFIG_DIR=${SYS_PREFIX}/var/lib/rabbitmq/config +ADVANCED_CONFIG_FILE=${SYS_PREFIX}/etc/rabbitmq/advanced +SCHEMA_DIR=${RABBITMQ_HOME}/priv/schema PLUGINS_DIR="${RABBITMQ_HOME}/plugins" IO_THREAD_POOL_SIZE=64 diff --git a/scripts/rabbitmq-defaults.bat b/scripts/rabbitmq-defaults.bat index 27edd0d11eab..5612af967ad2 100644 --- a/scripts/rabbitmq-defaults.bat +++ b/scripts/rabbitmq-defaults.bat @@ -41,6 +41,9 @@ set CONFIG_FILE=!RABBITMQ_BASE!\rabbitmq set LOG_BASE=!RABBITMQ_BASE!\log set MNESIA_BASE=!RABBITMQ_BASE!\db set ENABLED_PLUGINS_FILE=!RABBITMQ_BASE!\enabled_plugins +set GENERATED_CONFIG_DIR=!RABBITMQ_BASE!\config +set ADVANCED_CONFIG_FILE=!RABBITMQ_BASE!\advanced +set SCHEMA_DIR=!RABBITMQ_HOME!\priv\schema REM PLUGINS_DIR="${RABBITMQ_HOME}/plugins" for /f "delims=" %%F in ("!TDP0!..\plugins") do set PLUGINS_DIR=%%~dpsF%%~nF%%~xF diff --git a/scripts/rabbitmq-env b/scripts/rabbitmq-env index 872e5492dec7..8ceb8c94f29a 100644 --- a/scripts/rabbitmq-env +++ b/scripts/rabbitmq-env @@ -103,15 +103,11 @@ fi ##--- Set environment vars RABBITMQ_ to defaults if not set -SED_OPT="-E" -if [ $(uname -s) = "Linux" ]; then - SED_OPT="-r" -fi - rmq_normalize_path() { local path=$1 - echo "$path" | sed $SED_OPT -e 's,//+,/,g' -e 's,(.)/$,\1,' + # Remove redundant slashes and strip a trailing slash + echo "$path" | sed -e 's#/\{2,\}#/#g' -e 's#/$##' } rmq_normalize_path_var() { @@ -188,6 +184,9 @@ DEFAULT_NODE_PORT=5672 [ "x" = "x$RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS" ] && RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=${SERVER_ADDITIONAL_ERL_ARGS} [ "x" = "x$RABBITMQ_MNESIA_DIR" ] && RABBITMQ_MNESIA_DIR=${MNESIA_DIR} [ "x" = "x$RABBITMQ_MNESIA_DIR" ] && RABBITMQ_MNESIA_DIR=${RABBITMQ_MNESIA_BASE}/${RABBITMQ_NODENAME} +[ "x" = "x$RABBITMQ_GENERATED_CONFIG_DIR" ] && RABBITMQ_GENERATED_CONFIG_DIR=${GENERATED_CONFIG_DIR} +[ "x" = "x$RABBITMQ_ADVANCED_CONFIG_FILE" ] && RABBITMQ_ADVANCED_CONFIG_FILE=${ADVANCED_CONFIG_FILE} +[ "x" = "x$RABBITMQ_SCHEMA_DIR" ] && RABBITMQ_SCHEMA_DIR=${SCHEMA_DIR} rmq_normalize_path_var \ RABBITMQ_CONFIG_FILE \ @@ -262,7 +261,8 @@ if [ "${RABBITMQ_DEV_ENV}" ]; then RABBITMQ_ENABLED_PLUGINS_FILE="${enabled_plugins_file}" fi fi - + + if [ -d "${RABBITMQ_PLUGINS_DIR}" ]; then # RabbitMQ was started with "make run-broker" from its own # source tree. Take rabbit_common from the plugins directory. diff --git a/scripts/rabbitmq-env.bat b/scripts/rabbitmq-env.bat index 981a2feddc86..430395accf1c 100644 --- a/scripts/rabbitmq-env.bat +++ b/scripts/rabbitmq-env.bat @@ -77,6 +77,8 @@ if "!RABBITMQ_NODENAME!"=="" ( if "!NODENAME!"=="" ( REM We use Erlang to query the local hostname because REM !COMPUTERNAME! and Erlang may return different results. + REM Start erl with -sname to make sure epmd is started. + call "%ERLANG_HOME%\bin\erl.exe" -A0 -noinput -boot start_clean -sname rabbit-prelaunch-epmd -eval "init:stop()." >nul 2>&1 for /f "delims=" %%F in ('call "%ERLANG_HOME%\bin\erl.exe" -A0 -noinput -boot start_clean -eval "net_kernel:start([list_to_atom(""rabbit-gethostname-"" ++ os:getpid()), %NAMETYPE%]), [_, H] = string:tokens(atom_to_list(node()), ""@""), io:format(""~s~n"", [H]), init:stop()."') do @set HOSTNAME=%%F set RABBITMQ_NODENAME=rabbit@!HOSTNAME! set HOSTNAME= @@ -156,6 +158,31 @@ if "!RABBITMQ_CONFIG_FILE!"=="" ( ) ) +if "!RABBITMQ_GENERATED_CONFIG_DIR!"=="" ( + if "!GENERATED_CONFIG_DIR!"=="" ( + set RABBITMQ_GENERATED_CONFIG_DIR=!RABBITMQ_BASE!\config + ) else ( + set RABBITMQ_GENERATED_CONFIG_DIR=!GENERATED_CONFIG_DIR! + ) +) + +if "!RABBITMQ_ADVANCED_CONFIG_FILE!"=="" ( + if "!ADVANCED_CONFIG_FILE!"=="" ( + set RABBITMQ_ADVANCED_CONFIG_FILE=!RABBITMQ_BASE!\advanced + ) else ( + set RABBITMQ_ADVANCED_CONFIG_FILE=!GENERATED_CONFIG_DIR! + ) +) + +if "!RABBITMQ_SCHEMA_DIR!" == "" ( + if "!SCHEMA_DIR!"=="" ( + set RABBITMQ_SCHEMA_DIR=!RABBITMQ_HOME!\priv\schema + ) else ( + set RABBITMQ_SCHEMA_DIR=!SCHEMA_DIR! + ) +) + + REM [ "x" = "x$RABBITMQ_LOG_BASE" ] && RABBITMQ_LOG_BASE=${LOG_BASE} if "!RABBITMQ_LOG_BASE!"=="" ( if "!LOG_BASE!"=="" ( diff --git a/scripts/rabbitmq-script-wrapper b/scripts/rabbitmq-script-wrapper index ed4c276e53cf..9623f0170930 100644 --- a/scripts/rabbitmq-script-wrapper +++ b/scripts/rabbitmq-script-wrapper @@ -15,14 +15,9 @@ ## Copyright (c) 2007-2015 Pivotal Software, Inc. All rights reserved. ## -SED_OPT="-E" -if [ $(uname -s) = "Linux" ]; then - SED_OPT="-r" -fi - for arg in "$@" ; do # Wrap each arg in single quotes and wrap single quotes in double quotes, so that they're passed through cleanly. - arg=`printf %s "$arg" | sed $SED_OPT -e "s/'/'\"'\"'/g"` + arg=`printf %s "$arg" | sed -e "s#'#'\"'\"'#g"` CMDLINE="${CMDLINE} '${arg}'" done diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server index 6397d1cdbd42..0e3f06baf981 100755 --- a/scripts/rabbitmq-server +++ b/scripts/rabbitmq-server @@ -47,7 +47,7 @@ case "$(uname -s)" in exit $EX_CANTCREAT fi if ! echo $$ > ${RABBITMQ_PID_FILE}; then - # Bettern diagnostics - otherwise the only report in logs is about failed 'echo' + # Better diagnostics - otherwise the only report in logs is about failed 'echo' # command, but without any other details: neither what script has failed nor what # file output was redirected to. echo "Failed to write pid file: ${RABBITMQ_PID_FILE}" @@ -58,8 +58,13 @@ esac RABBITMQ_EBIN_ROOT="${RABBITMQ_HOME}/ebin" +[ "$NOTIFY_SOCKET" ] && RUNNING_UNDER_SYSTEMD=true + set +e +# NOTIFY_SOCKET is needed here to prevent epmd from impersonating the +# success of our startup sequence to systemd. +NOTIFY_SOCKET= \ RABBITMQ_CONFIG_FILE=$RABBITMQ_CONFIG_FILE \ RABBITMQ_DIST_PORT=$RABBITMQ_DIST_PORT \ ${ERL_DIR}erl -pa "$RABBITMQ_EBIN_ROOT" \ @@ -68,8 +73,11 @@ RABBITMQ_DIST_PORT=$RABBITMQ_DIST_PORT \ -hidden \ -s rabbit_prelaunch \ ${RABBITMQ_NAME_TYPE} rabbitmqprelaunch$$ \ + -conf_advanced "${RABBITMQ_ADVANCED_CONFIG_FILE}" \ + -rabbit enabled_plugins_file "\"$RABBITMQ_ENABLED_PLUGINS_FILE\"" \ + -rabbit plugins_dir "\"$RABBITMQ_PLUGINS_DIR\"" \ -extra "${RABBITMQ_NODENAME}" - + PRELAUNCH_RESULT=$? if [ ${PRELAUNCH_RESULT} = 2 ] ; then # dist port is mentioned in config, so do not set it @@ -81,10 +89,25 @@ else exit ${PRELAUNCH_RESULT} fi +if [ ! -f "${RABBITMQ_SCHEMA_DIR}/rabbitmq.schema" ]; then + cp "${RABBITMQ_HOME}/priv/schema/rabbitmq.schema" "${RABBITMQ_SCHEMA_DIR}" +fi + set -e -RABBITMQ_CONFIG_ARG= -[ -f "${RABBITMQ_CONFIG_FILE}.config" ] && RABBITMQ_CONFIG_ARG="-config ${RABBITMQ_CONFIG_FILE}" +if [ -f "${RABBITMQ_CONFIG_FILE}.config" ]; then + RABBITMQ_CONFIG_ARG="-config ${RABBITMQ_CONFIG_FILE}" +elif [ -f "${RABBITMQ_CONFIG_FILE}.conf" ]; then + RABBITMQ_CONFIG_ARG="-conf ${RABBITMQ_CONFIG_FILE} \ + -conf_dir ${RABBITMQ_GENERATED_CONFIG_DIR} \ + -conf_script_dir `dirname $0` \ + -conf_schema_dir ${RABBITMQ_SCHEMA_DIR}" + if [ -f "${RABBITMQ_ADVANCED_CONFIG_FILE}.config" ]; then + RABBITMQ_CONFIG_ARG="${RABBITMQ_CONFIG_ARG} \ + -conf_advanced ${RABBITMQ_ADVANCED_CONFIG_FILE} \ + -config ${RABBITMQ_ADVANCED_CONFIG_FILE}" + fi +fi RABBITMQ_LISTEN_ARG= [ "x" != "x$RABBITMQ_NODE_PORT" ] && [ "x" != "x$RABBITMQ_NODE_IP_ADDRESS" ] && RABBITMQ_LISTEN_ARG="-rabbit tcp_listeners [{\""${RABBITMQ_NODE_IP_ADDRESS}"\","${RABBITMQ_NODE_PORT}"}]" @@ -106,6 +129,7 @@ fi set -f start_rabbitmq_server() { + check_start_params && RABBITMQ_CONFIG_FILE=$RABBITMQ_CONFIG_FILE \ exec ${ERL_DIR}erl \ -pa ${RABBITMQ_EBIN_ROOT} \ @@ -144,7 +168,39 @@ stop_rabbitmq_server() { fi } -if [ 'x' = "x$RABBITMQ_ALLOW_INPUT" -a -z "$detached" ]; then +check_start_params() { + check_not_empty RABBITMQ_BOOT_MODULE + check_not_empty RABBITMQ_NAME_TYPE + check_not_empty RABBITMQ_NODENAME + check_not_empty SASL_BOOT_FILE + check_not_empty RABBITMQ_IO_THREAD_POOL_SIZE +} + +check_not_empty() { + local name="${1:?}" + local value + eval value=\$$name + if [ -z "$value" ]; then + echo "Error: ENV variable should be defined: $1. + Please check rabbitmq-env, rabbitmq-defaults, and $CONF_ENV_FILE script files" + exit 78 + fi +} + +if [ "$RABBITMQ_ALLOW_INPUT" -o "$RUNNING_UNDER_SYSTEMD" -o "$detached" ]; then + # Run erlang VM directly, completely replacing current shell + # process - so the pid file written in the code above will be + # valid (unless detached, which is also handled in the code + # above). + # + # And also this is the correct mode to run the broker under + # systemd - there is no need in a proxy process that converts + # signals to graceful shutdown command, the unit file should already + # contain instructions for graceful shutdown. Also by removing + # this additional process we could simply use value returned by + # `os:getpid/0` for a systemd ready notification. + start_rabbitmq_server "$@" +else # When RabbitMQ runs in the foreground but the Erlang shell is # disabled, we setup signal handlers to stop RabbitMQ properly. This # is at least useful in the case of Docker. @@ -153,7 +209,7 @@ if [ 'x' = "x$RABBITMQ_ALLOW_INPUT" -a -z "$detached" ]; then RABBITMQ_SERVER_START_ARGS="${RABBITMQ_SERVER_START_ARGS} +B i" # Signal handlers. They all stop RabbitMQ properly (using - # rabbitmqctl stop). Depending on the signal, this script will exwit + # rabbitmqctl stop). Depending on the signal, this script will exit # with a non-zero error code: # SIGHUP SIGTERM SIGTSTP # They are considered a normal process termination, so the script @@ -169,6 +225,4 @@ if [ 'x' = "x$RABBITMQ_ALLOW_INPUT" -a -z "$detached" ]; then # Block until RabbitMQ exits or a signal is caught. # Waits for last command (which is start_rabbitmq_server) wait $! -else - start_rabbitmq_server "$@" fi diff --git a/scripts/rabbitmq-server-ha.ocf b/scripts/rabbitmq-server-ha.ocf index 5505c105811a..ae7991833baa 100755 --- a/scripts/rabbitmq-server-ha.ocf +++ b/scripts/rabbitmq-server-ha.ocf @@ -13,8 +13,8 @@ # # See usage() function below for more details ... # -# Note that the script uses set_rabbitmq_policy.sh script located in the -# same directory to setup RabbitMQ policies. +# Note that the script uses an external file to setup RabbitMQ policies +# so make sure to create it from an example shipped with the package. # ####################################################################### # Initialization: @@ -45,7 +45,8 @@ OCF_RESKEY_erlang_cookie_default=false OCF_RESKEY_erlang_cookie_file_default="/var/lib/rabbitmq/.erlang.cookie" OCF_RESKEY_use_fqdn_default=false OCF_RESKEY_fqdn_prefix_default="" -OCF_RESKEY_max_rabbitmqctl_timeouts_default=1 +OCF_RESKEY_max_rabbitmqctl_timeouts_default=3 +OCF_RESKEY_policy_file_default="/usr/local/sbin/set_rabbitmq_policy" : ${HA_LOGTAG="lrmd"} : ${HA_LOGFACILITY="daemon"} @@ -66,6 +67,7 @@ OCF_RESKEY_max_rabbitmqctl_timeouts_default=1 : ${OCF_RESKEY_use_fqdn=${OCF_RESKEY_use_fqdn_default}} : ${OCF_RESKEY_fqdn_prefix=${OCF_RESKEY_fqdn_prefix_default}} : ${OCF_RESKEY_max_rabbitmqctl_timeouts=${OCF_RESKEY_max_rabbitmqctl_timeouts_default}} +: ${OCF_RESKEY_policy_file=${OCF_RESKEY_policy_file_default}} ####################################################################### @@ -288,6 +290,14 @@ If too many timeouts happen in a raw, the monitor call will return with error. + + +A path to the shell script to setup RabbitMQ policies + +A policy file path + + + $EXTENDED_OCF_PARAMS @@ -613,7 +623,7 @@ rmq_setup_env() { fi done - export LL="${OCF_RESOURCE_INSTANCE}:" + export LL="${OCF_RESOURCE_INSTANCE}[$$]:" update_cookie } @@ -752,6 +762,31 @@ get_alive_pacemaker_nodes_but() fi } +# Get current master. If a parameter is provided, +# do not check node with that name +get_master_name_but() +{ + local node + for node in $(get_alive_pacemaker_nodes_but "$@") + do + ocf_log info "${LH} looking if $node is master" + + if is_master $node; then + ocf_log info "${LH} master is $node" + echo $node + break + fi + done +} + +# Returns 0 if we are clustered with provideded node +is_clustered_with() +{ + get_running_nodes | grep -q $(rabbit_node_name $1); + return $? +} + + check_need_join_to() { local join_to local node @@ -929,9 +964,10 @@ unjoin_nodes_from_cluster() { local tries=0 until [ $tries -eq 5 ]; do tries=$((tries+1)) - if get_running_nodes | grep -q $(rabbit_node_name $nodename) - then + if is_clustered_with $nodename; then ocf_log info "${LH} the ${nodename} is alive and cannot be kicked from the cluster yet" + else + break fi sleep 10 done @@ -1260,6 +1296,7 @@ start_rmq_server_app() { get_status() { local what="${1:-kernel}" local rc=$OCF_NOT_RUNNING + local LH="${LL} get_status():" local body local beam_running @@ -1270,11 +1307,11 @@ get_status() { beam_running=$? # report not running only if the which_applications() reported an error AND the beam is not running if [ $rc -ne 0 -a $beam_running -ne 0 ] ; then - ocf_log info "get_status() failed with code ${rc}. Command output: ${body}" + ocf_log info "${LH} failed with code ${rc}. Command output: ${body}" return $OCF_NOT_RUNNING # return a generic error, if there were errors and beam is found running elif [ $rc -ne 0 ] ; then - ocf_log info "get_status() found the beam process running but failed with code ${rc}. Command output: ${body}" + ocf_log info "${LH} found the beam process running but failed with code ${rc}. Command output: ${body}" return $OCF_ERR_GENERIC fi @@ -1284,7 +1321,7 @@ get_status() { echo "$body" | grep "\{${what}," 2>&1 > /dev/null && rc=$OCF_SUCCESS if [ $rc -ne $OCF_SUCCESS ] ; then - ocf_log info "get_status(): app ${what} was not found in command output: ${body}" + ocf_log info "${LH} app ${what} was not found in command output: ${body}" fi fi @@ -1371,7 +1408,6 @@ get_monitor() { local name local node local nodelist - local rc_check=$OCF_SUCCESS local max local our_uptime local node_uptime @@ -1408,58 +1444,47 @@ get_monitor() { if [ $rabbit_running -eq $OCF_SUCCESS ] then - ocf_log info "${LH} rabbit app is running. checking if we are the part of healthy cluster" + ocf_log info "${LH} rabbit app is running. checking if we are the part of healthy cluster" + + if [ $rc -eq $OCF_RUNNING_MASTER ] ; then + # The master is always running inside of its cluster + ocf_log info "${LH} rabbit app is running and is master of cluster" + + else + local master_name=$(get_master_name_but $THIS_PCMK_NODE) + + if [ -z "$master_name" ]; then + ocf_log info "${LH} no master is elected currently. Skipping cluster health check." + + elif is_clustered_with $master_name; then + ocf_log info "${LH} rabbit app is running and is member of healthy cluster" - if [ $rc -eq $OCF_RUNNING_MASTER ] ; then - # The master is always running inside of its cluster - ocf_log info "${LH} rabbit app is running and is master of cluster" - rc_check=$OCF_SUCCESS else - rc_check=$OCF_ERR_GENERIC - nodelist=$(get_alive_pacemaker_nodes_but) - for node in $nodelist - do - ocf_log info "${LH} rabbit app is running. looking for master on $node" - is_master $node - status_master=$? - ocf_log info "${LH} fetched master attribute for $node. attr value is ${status_master}" - if [ $status_master -eq 0 ] ; then - ocf_log info "${LH} rabbit app is running. master is $node" - if get_running_nodes | grep -q $(rabbit_node_name $node) - then - ocf_log info "${LH} rabbit app is running and is member of healthy cluster" - rc_check=$OCF_SUCCESS - break - fi - fi - done - [ $rc_check -eq $OCF_ERR_GENERIC ] && ocf_log err "${LH} rabbit node is running out of the cluster" + # Rabbit is running but is not connected to master + # Failing to avoid split brain + ocf_log err "${LH} rabbit node is running out of the cluster" + rc=$OCF_ERR_GENERIC fi + fi else - if [ "$OCF_CHECK_LEVEL" -gt 20 ]; then + if [ "$OCF_CHECK_LEVEL" -gt 20 ]; then ocf_log info "${LH} rabbit app is not running. checking if there is a master" # Do not refetch the master status as we know it already if [ $rc -eq $OCF_RUNNING_MASTER ]; then ocf_log err "${LH} we are the master and rabbit app is not running. this is a failure" exit $OCF_FAILED_MASTER fi - nodelist=$(get_alive_pacemaker_nodes_but $THIS_PCMK_NODE) - rc_check=$OCF_SUCCESS - for node in $nodelist - do - is_master $node - status_master=$? - ocf_log info "${LH} fetched master attribute for $node. attr value is ${status_master}" - if [ $status_master -eq 0 ] ; then - rc_check=$OCF_ERR_GENERIC - ocf_log info "${LH} rabbit app is not running. master is $node. exiting to be restarted by pacemaker" - break - fi - done - fi + + local master_name=$(get_master_name_but $THIS_PCMK_NODE) + + if [ -n "$master_name" ]; then + ocf_log info "${LH} master exists and rabbit app is not running. Exiting to be restarted by pacemaker" + rc=$OCF_ERR_GENERIC + fi + fi fi - if [ $rc -eq $OCF_ERR_GENERIC -o $rc_check -eq $OCF_ERR_GENERIC ]; then + if [ $rc -eq $OCF_ERR_GENERIC ]; then ocf_log err "${LH} get_status() returns generic error ${rc}" ocf_log info "${LH} ensuring this slave does not get promoted." master_score 0 @@ -1887,23 +1912,33 @@ action_notify() { case "$OCF_RESKEY_CRM_meta_notify_operation" in promote) ocf_log info "${LH} post-promote begin." + + rc=$OCF_SUCCESS + # Do nothing, if the list of nodes being promoted reported empty. # Delegate recovery, if needed, to the "running out of the cluster" monitor's logic if [ -z "${OCF_RESKEY_CRM_meta_notify_promote_uname}" ] ; then - ocf_log warn "${LH} there are no nodes to join to reported on post-promote. Nothing to do." - ocf_log info "${LH} post-promote end." - return $OCF_SUCCESS + ocf_log warn "${LH} there are no nodes to join to reported on post-promote. Nothing to do." + + elif my_host "${OCF_RESKEY_CRM_meta_notify_promote_uname}"; then + ocf_log info "${LH} ignoring post-promote of self" + + elif is_clustered_with "${OCF_RESKEY_CRM_meta_notify_promote_uname}"; then + ocf_log info "${LH} we are already clustered with master - ${OCF_RESKEY_CRM_meta_notify_promote_uname}. Nothing to do." + + else + # Note, this should fail when the mnesia is inconsistent. + # For example, when the "old" master processing the promition of the new one. + # Later this ex-master node will rejoin the cluster at post-start. + jjj_join "${OCF_RESKEY_CRM_meta_notify_promote_uname}" + rc=$? + if [ $rc -eq $OCF_ERR_GENERIC ] ; then + ocf_log err "${LH} Failed to join the cluster on post-promote. The resource will be restarted." + fi fi - # Note, this should fail when the mnesia is inconsistent. - # For example, when the "old" master processing the promition of the new one. - # Later this ex-master node will rejoin the cluster at post-start. - jjj_join "${OCF_RESKEY_CRM_meta_notify_promote_uname}" - rc=$? + ocf_log info "${LH} post-promote end." - if [ $rc -eq $OCF_ERR_GENERIC ] ; then - ocf_log err "${LH} Failed to join the cluster on post-promote. The resource will be restarted." - return $OCF_ERR_GENERIC - fi + return $rc ;; start) ocf_log info "${LH} post-start begin." @@ -2073,8 +2108,7 @@ action_promote() { exit $OCF_FAILED_MASTER fi - local set_policy_path="$(dirname $0)/set_rabbitmq_policy.sh" - [ -f $set_policy_path ] && . $set_policy_path + [ -f "${OCF_RESKEY_policy_file}" ] && . "${OCF_RESKEY_policy_file}" # create timestamp file nowtime="$(now)" diff --git a/scripts/rabbitmq-server.bat b/scripts/rabbitmq-server.bat index 84908f380f3e..2a38e77a82e9 100644 --- a/scripts/rabbitmq-server.bat +++ b/scripts/rabbitmq-server.bat @@ -21,6 +21,7 @@ rem Preserve values that might contain exclamation marks before rem enabling delayed expansion set TDP0=%~dp0 set STAR=%* +set CONF_SCRIPT_DIR="%~dp0" setlocal enabledelayedexpansion REM Get default settings with user overrides for (RABBITMQ_) @@ -41,11 +42,16 @@ if not exist "!ERLANG_HOME!\bin\erl.exe" ( set RABBITMQ_EBIN_ROOT=!RABBITMQ_HOME!\ebin +set RABBITMQ_CONFIG_FILE="!RABBITMQ_CONFIG_FILE!" + "!ERLANG_HOME!\bin\erl.exe" ^ -pa "!RABBITMQ_EBIN_ROOT!" ^ -noinput -hidden ^ -s rabbit_prelaunch ^ !RABBITMQ_NAME_TYPE! rabbitmqprelaunch!RANDOM!!TIME:~9! ^ + -conf_advanced "!RABBITMQ_ADVANCED_CONFIG_FILE!" ^ + -rabbit enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" ^ + -rabbit plugins_dir "!$RABBITMQ_PLUGINS_DIR!" ^ -extra "!RABBITMQ_NODENAME!" if ERRORLEVEL 2 ( @@ -56,13 +62,25 @@ if ERRORLEVEL 2 ( set RABBITMQ_DIST_ARG=-kernel inet_dist_listen_min !RABBITMQ_DIST_PORT! -kernel inet_dist_listen_max !RABBITMQ_DIST_PORT! ) +if not exist "!RABBITMQ_SCHEMA_DIR!\rabbitmq.schema" ( + copy "!RABBITMQ_HOME!\priv\schema\rabbitmq.schema" "!RABBITMQ_SCHEMA_DIR!\rabbitmq.schema" +) + set RABBITMQ_EBIN_PATH="-pa !RABBITMQ_EBIN_ROOT!" if exist "!RABBITMQ_CONFIG_FILE!.config" ( set RABBITMQ_CONFIG_ARG=-config "!RABBITMQ_CONFIG_FILE!" -) else ( - set RABBITMQ_CONFIG_ARG= -) +) else if exist "!RABBITMQ_CONFIG_FILE!.conf" ( + set RABBITMQ_CONFIG_ARG=-conf "!RABBITMQ_CONFIG_FILE!" ^ + -conf_dir !RABBITMQ_GENERATED_CONFIG_DIR! ^ + -conf_script_dir !CONF_SCRIPT_DIR:\=/! ^ + -conf_schema_dir !RABBITMQ_SCHEMA_DIR! + if exist "!RABBITMQ_ADVANCED_CONFIG_FILE!.config" ( + set RABBITMQ_CONFIG_ARG=!RABBITMQ_CONFIG_ARG! ^ + -conf_advanced "!RABBITMQ_ADVANCED_CONFIG_FILE!" ^ + -config "!RABBITMQ_ADVANCED_CONFIG_FILE!" + ) +) set RABBITMQ_LISTEN_ARG= if not "!RABBITMQ_NODE_IP_ADDRESS!"=="" ( @@ -91,7 +109,18 @@ if "!RABBITMQ_NODE_ONLY!"=="" ( ) if "!RABBITMQ_IO_THREAD_POOL_SIZE!"=="" ( - set RABBITMQ_IO_THREAD_POOL_ARG=30 + set RABBITMQ_IO_THREAD_POOL_SIZE=64 +) + + +set ENV_OK=true +CALL :check_not_empty "RABBITMQ_BOOT_MODULE" !RABBITMQ_BOOT_MODULE! +CALL :check_not_empty "RABBITMQ_NAME_TYPE" !RABBITMQ_NAME_TYPE! +CALL :check_not_empty "RABBITMQ_NODENAME" !RABBITMQ_NODENAME! + + +if "!ENV_OK!"=="false" ( + EXIT /b 78 ) "!ERLANG_HOME!\bin\erl.exe" ^ @@ -122,5 +151,16 @@ if "!RABBITMQ_IO_THREAD_POOL_SIZE!"=="" ( !RABBITMQ_DIST_ARG! ^ !STAR! +EXIT /B 0 + +:check_not_empty +if "%~2"=="" ( + ECHO "Error: ENV variable should be defined: %1. Please check rabbitmq-env and rabbitmq-defaults, and !RABBITMQ_CONF_ENV_FILE! script files. Check also your Environment Variables settings" + set ENV_OK=false + EXIT /B 78 + ) +EXIT /B 0 + endlocal endlocal + diff --git a/scripts/rabbitmq-service.bat b/scripts/rabbitmq-service.bat index 404d167d0388..7e80e78398dc 100644 --- a/scripts/rabbitmq-service.bat +++ b/scripts/rabbitmq-service.bat @@ -21,6 +21,7 @@ rem Preserve values that might contain exclamation marks before rem enabling delayed expansion set TN0=%~n0 set TDP0=%~dp0 +set CONF_SCRIPT_DIR="%~dp0" set P1=%1 setlocal enabledelayedexpansion @@ -104,6 +105,16 @@ if not exist "!RABBITMQ_BASE!" ( echo Creating base directory !RABBITMQ_BASE! & md "!RABBITMQ_BASE!" ) +set ENV_OK=true +CALL :check_not_empty "RABBITMQ_BOOT_MODULE" !RABBITMQ_BOOT_MODULE! +CALL :check_not_empty "RABBITMQ_NAME_TYPE" !RABBITMQ_NAME_TYPE! +CALL :check_not_empty "RABBITMQ_NODENAME" !RABBITMQ_NODENAME! + + +if "!ENV_OK!"=="false" ( + EXIT /b 78 +) + "!ERLANG_SERVICE_MANAGER_PATH!\erlsrv" list !RABBITMQ_SERVICENAME! 2>NUL 1>NUL if errorlevel 1 ( "!ERLANG_SERVICE_MANAGER_PATH!\erlsrv" add !RABBITMQ_SERVICENAME! -internalservicename !RABBITMQ_SERVICENAME! @@ -113,10 +124,16 @@ if errorlevel 1 ( set RABBITMQ_EBIN_ROOT=!RABBITMQ_HOME!\ebin +set RABBITMQ_CONFIG_FILE="!RABBITMQ_CONFIG_FILE!" + + "!ERLANG_HOME!\bin\erl.exe" ^ -pa "!RABBITMQ_EBIN_ROOT!" ^ -noinput -hidden ^ -s rabbit_prelaunch ^ + -conf_advanced "!RABBITMQ_ADVANCED_CONFIG_FILE!" ^ + -rabbit enabled_plugins_file "!RABBITMQ_ENABLED_PLUGINS_FILE!" ^ + -rabbit plugins_dir "!$RABBITMQ_PLUGINS_DIR!" ^ !RABBITMQ_NAME_TYPE! rabbitmqprelaunch!RANDOM!!TIME:~9! if ERRORLEVEL 3 ( @@ -131,10 +148,29 @@ if ERRORLEVEL 3 ( set RABBITMQ_DIST_ARG=-kernel inet_dist_listen_min !RABBITMQ_DIST_PORT! -kernel inet_dist_listen_max !RABBITMQ_DIST_PORT! ) +if not exist "!RABBITMQ_SCHEMA_DIR!\rabbitmq.schema" ( + copy "!RABBITMQ_HOME!\priv\schema\rabbitmq.schema" "!RABBITMQ_SCHEMA_DIR!\rabbitmq.schema" +) + REM Try to create advanced config file, if it doesn't exist + REM It still can fail to be created, but at least not for default install +if not exist "!RABBITMQ_ADVANCED_CONFIG_FILE!.config" ( + echo []. > !RABBITMQ_ADVANCED_CONFIG_FILE!.config +) + if exist "!RABBITMQ_CONFIG_FILE!.config" ( set RABBITMQ_CONFIG_ARG=-config "!RABBITMQ_CONFIG_FILE!" ) else ( - set RABBITMQ_CONFIG_ARG= + rem Always specify generated config arguments, we cannot + rem assume .conf file is available + set RABBITMQ_CONFIG_ARG=-conf "!RABBITMQ_CONFIG_FILE!" ^ + -conf_dir !RABBITMQ_GENERATED_CONFIG_DIR! ^ + -conf_script_dir !CONF_SCRIPT_DIR:\=/! ^ + -conf_schema_dir !RABBITMQ_SCHEMA_DIR! + if exist "!RABBITMQ_ADVANCED_CONFIG_FILE!.config" ( + set RABBITMQ_CONFIG_ARG=!RABBITMQ_CONFIG_ARG! ^ + -conf_advanced "!RABBITMQ_ADVANCED_CONFIG_FILE!" ^ + -config "!RABBITMQ_ADVANCED_CONFIG_FILE!" + ) ) set RABBITMQ_LISTEN_ARG= @@ -156,7 +192,11 @@ if "!RABBITMQ_NODE_ONLY!"=="" ( ) if "!RABBITMQ_IO_THREAD_POOL_SIZE!"=="" ( - set RABBITMQ_IO_THREAD_POOL_SIZE=30 + set RABBITMQ_IO_THREAD_POOL_SIZE=64 +) + +if "!RABBITMQ_SERVICE_RESTART!"=="" ( + set RABBITMQ_SERVICE_RESTART=restart ) set ERLANG_SERVICE_ARGUMENTS= ^ @@ -187,10 +227,15 @@ set ERLANG_SERVICE_ARGUMENTS= ^ !RABBITMQ_DIST_ARG! ^ !STARVAR! +echo "!ERLANG_SERVICE_ARGUMENTS!" > "!RABBITMQ_CONFIG_FILE!.txt" + set ERLANG_SERVICE_ARGUMENTS=!ERLANG_SERVICE_ARGUMENTS:\=\\! set ERLANG_SERVICE_ARGUMENTS=!ERLANG_SERVICE_ARGUMENTS:"=\"! + + "!ERLANG_SERVICE_MANAGER_PATH!\erlsrv" set !RABBITMQ_SERVICENAME! ^ +-onfail !RABBITMQ_SERVICE_RESTART! ^ -machine "!ERLANG_SERVICE_MANAGER_PATH!\erl.exe" ^ -env ERL_CRASH_DUMP="!RABBITMQ_BASE:\=/!/erl_crash.dump" ^ -env ERL_LIBS="!ERL_LIBS!" ^ @@ -212,5 +257,15 @@ goto END :END +EXIT /B 0 + +:check_not_empty +if "%~2"=="" ( + ECHO "Error: ENV variable should be defined: %1. Please check rabbitmq-env, rabbitmq-default, and !RABBITMQ_CONF_ENV_FILE! script files. Check also your Environment Variables settings" + set ENV_OK=false + EXIT /B 78 + ) +EXIT /B 0 + endlocal endlocal diff --git a/scripts/rabbitmqctl b/scripts/rabbitmqctl index 3705b9a97924..2336c3d46613 100755 --- a/scripts/rabbitmqctl +++ b/scripts/rabbitmqctl @@ -30,7 +30,7 @@ fi RABBITMQ_USE_LONGNAME=${RABBITMQ_USE_LONGNAME} \ exec ${ERL_DIR}erl \ -pa "${RABBITMQ_HOME}/ebin" \ - -noinput \ + -noinput +B \ -hidden \ ${RABBITMQ_CTL_ERL_ARGS} \ -boot "${CLEAN_BOOT_FILE}" \ diff --git a/scripts/travis_test_ocf_ra.sh b/scripts/travis_test_ocf_ra.sh new file mode 100644 index 000000000000..e8f9a7419494 --- /dev/null +++ b/scripts/travis_test_ocf_ra.sh @@ -0,0 +1,30 @@ +#!/bin/sh -eux +# Prepare and run a smoke test against the RabbitMQ OCF RA only if +# the scripts/rabbitmq-server-ha.ocf has changes +if ! git diff HEAD~ --name-only | grep -q scripts/rabbitmq-server-ha.ocf +then + exit 0 +fi + +export VAGRANT_VERSION=1.8.1 +export DOCKER_IMAGE=bogdando/rabbitmq-cluster-ocf-wily +export UPLOAD_METHOD=none +export DOCKER_MOUNTS="$(pwd)/scripts/rabbitmq-server-ha.ocf:/tmp/rabbitmq-server-ha" + +# Install vagrant and requirements +sudo apt-get install -qq git wget +wget --no-verbose https://releases.hashicorp.com/vagrant/${VAGRANT_VERSION}/vagrant_${VAGRANT_VERSION}_x86_64.deb +sudo dpkg -i --force-all ./vagrant_${VAGRANT_VERSION}_x86_64.deb +vagrant plugin install vagrant-triggers + +# Update docker and prepare images +sudo apt-get update +sudo DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install --only-upgrade docker-engine +sudo service docker restart +docker pull $DOCKER_IMAGE + +# Prepare and run a smoke test for a rabbitmq cluster by the OCF RA +git clone https://github.com/bogdando/rabbitmq-cluster-ocf-vagrant.git +cd ./rabbitmq-cluster-ocf-vagrant +vagrant up --provider docker +docker exec -it n1 /bin/bash /vagrant/vagrant_script/test_rabbitcluster.sh rabbit@n1 rabbit@n2 diff --git a/src/rabbit.app.src b/src/rabbit.app.src index f347b24585d0..83e7237c806d 100644 --- a/src/rabbit.app.src +++ b/src/rabbit.app.src @@ -97,5 +97,8 @@ {msg_store_credit_disc_bound, {2000, 500}}, {msg_store_io_batch_size, 2048}, %% see rabbitmq-server#143 - {credit_flow_default_credit, {200, 50}} + {credit_flow_default_credit, {200, 50}}, + %% see rabbitmq-server#248 + %% and rabbitmq-server#667 + {channel_operation_timeout, 15000} ]}]}. diff --git a/src/rabbit.erl b/src/rabbit.erl index def466701a32..7816cd53aa98 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -272,6 +272,7 @@ start() -> boot() -> start_it(fun() -> + ensure_config(), ok = ensure_application_loaded(), HipeResult = rabbit_hipe:maybe_hipe_compile(), ok = start_logger(), @@ -285,20 +286,138 @@ boot() -> broker_start() end). +ensure_config() -> + case rabbit_config:prepare_and_use_config() of + {error, Reason} -> + {Format, Arg} = case Reason of + {generation_error, Error} -> {"~s", [Error]}; + Other -> {"~p", [Other]} + end, + log_boot_error_and_exit(generate_config_file, + "~nConfig file generation failed "++Format, + Arg); + ok -> ok + end. + + broker_start() -> Plugins = rabbit_plugins:setup(), ToBeLoaded = Plugins ++ ?APPS, start_apps(ToBeLoaded), - case os:type() of - {win32, _} -> ok; - _ -> - %% Only for systemd unit with Type=notify. Errors are intentionally - %% ignored: either you have working systemd-notify(1) or you don't - %% care about systemd at all. - os:cmd("systemd-notify --ready") - end, + maybe_sd_notify(), ok = log_broker_started(rabbit_plugins:active()). +%% Try to send systemd ready notification if it makes sense in the +%% current environment. standard_error is used intentionally in all +%% logging statements, so all this messages will end in systemd +%% journal. +maybe_sd_notify() -> + case sd_notify_ready() of + false -> + io:format(standard_error, "systemd READY notification failed, beware of timeouts~n", []); + _ -> + ok + end. + +sd_notify_ready() -> + case {os:type(), os:getenv("NOTIFY_SOCKET")} of + {{win32, _}, _} -> + true; + {_, [_|_]} -> %% Non-empty NOTIFY_SOCKET, give it a try + sd_notify_legacy() orelse sd_notify_socat(); + _ -> + true + end. + +sd_notify_data() -> + "READY=1\nSTATUS=Initialized\nMAINPID=" ++ os:getpid() ++ "\n". + +sd_notify_legacy() -> + case code:load_file(sd_notify) of + {module, sd_notify} -> + SDNotify = sd_notify, + SDNotify:sd_notify(0, sd_notify_data()), + true; + {error, _} -> + false + end. + +%% socat(1) is the most portable way the sd_notify could be +%% implemented in erlang, without introducing some NIF. Currently the +%% following issues prevent us from implementing it in a more +%% reasonable way: +%% - systemd-notify(1) is unstable for non-root users +%% - erlang doesn't support unix domain sockets. +%% +%% Some details on how we ended with such a solution: +%% https://github.com/rabbitmq/rabbitmq-server/issues/664 +sd_notify_socat() -> + case sd_current_unit() of + {ok, Unit} -> + io:format(standard_error, "systemd unit for activation check: \"~s\"~n", [Unit]), + sd_notify_socat(Unit); + _ -> + false + end. + +socat_socket_arg("@" ++ AbstractUnixSocket) -> + "abstract-sendto:" ++ AbstractUnixSocket; +socat_socket_arg(UnixSocket) -> + "unix-sendto:" ++ UnixSocket. + +sd_open_port() -> + open_port( + {spawn_executable, os:find_executable("socat")}, + [{args, [socat_socket_arg(os:getenv("NOTIFY_SOCKET")), "STDIO"]}, + use_stdio, out]). + +sd_notify_socat(Unit) -> + case sd_open_port() of + {'EXIT', Exit} -> + io:format(standard_error, "Failed to start socat ~p~n", [Exit]), + false; + Port -> + Port ! {self(), {command, sd_notify_data()}}, + Result = sd_wait_activation(Port, Unit), + port_close(Port), + Result + end. + +sd_current_unit() -> + case catch re:run(os:cmd("systemctl status " ++ os:getpid()), "([-.@0-9a-zA-Z]+)", [unicode, {capture, all_but_first, list}]) of + {'EXIT', _} -> + error; + {match, [Unit]} -> + {ok, Unit}; + _ -> + error + end. + +sd_wait_activation(Port, Unit) -> + case os:find_executable("systemctl") of + false -> + io:format(standard_error, "'systemctl' unavailable, falling back to sleep~n", []), + timer:sleep(5000), + true; + _ -> + sd_wait_activation(Port, Unit, 10) + end. + +sd_wait_activation(_, _, 0) -> + io:format(standard_error, "Service still in 'activating' state, bailing out~n", []), + false; +sd_wait_activation(Port, Unit, AttemptsLeft) -> + case os:cmd("systemctl show --property=ActiveState " ++ Unit) of + "ActiveState=activating\n" -> + timer:sleep(1000), + sd_wait_activation(Port, Unit, AttemptsLeft - 1); + "ActiveState=" ++ _ -> + true; + _ = Err-> + io:format(standard_error, "Unexpected status from systemd ~p~n", [Err]), + false + end. + start_it(StartFun) -> Marker = spawn_link(fun() -> receive stop -> ok end end), case catch register(rabbit_boot, Marker) of @@ -335,6 +454,10 @@ stop_and_halt() -> stop() after rabbit_log:info("Halting Erlang VM~n", []), + %% Also duplicate this information to stderr, so console where + %% foreground broker was running (or systemd journal) will + %% contain information about graceful termination. + io:format(standard_error, "Gracefully halting Erlang VM~n", []), init:stop() end, ok. @@ -643,7 +766,8 @@ print_banner() -> "~n ###### ##" "~n ########## Logs: ~s" ++ LogFmt ++ - "~n~n Starting broker...", + "~n~n Starting broker..." + "~n", [Product, Version, ?COPYRIGHT_MESSAGE, ?INFORMATION_MESSAGE] ++ LogLocations). @@ -678,11 +802,16 @@ log_banner() -> rabbit_log:info("~n~s", [Banner]). warn_if_kernel_config_dubious() -> - case erlang:system_info(kernel_poll) of - true -> ok; - false -> rabbit_log:warning( - "Kernel poll (epoll, kqueue, etc) is disabled. Throughput " - "and CPU utilization may worsen.~n") + case os:type() of + {win32, _} -> + ok; + _ -> + case erlang:system_info(kernel_poll) of + true -> ok; + false -> rabbit_log:warning( + "Kernel poll (epoll, kqueue, etc) is disabled. Throughput " + "and CPU utilization may worsen.~n") + end end, AsyncThreads = erlang:system_info(thread_pool_size), case AsyncThreads < ?ASYNC_THREADS_WARNING_THRESHOLD of @@ -788,32 +917,7 @@ home_dir() -> end. config_files() -> - Abs = fun (F) -> - filename:absname(filename:rootname(F, ".config") ++ ".config") - end, - case init:get_argument(config) of - {ok, Files} -> [Abs(File) || [File] <- Files]; - error -> case config_setting() of - none -> []; - File -> [Abs(File) ++ " (not found)"] - end - end. - -%% This is a pain. We want to know where the config file is. But we -%% can't specify it on the command line if it is missing or the VM -%% will fail to start, so we need to find it by some mechanism other -%% than init:get_arguments/0. We can look at the environment variable -%% which is responsible for setting it... but that doesn't work for a -%% Windows service since the variable can change and the service not -%% be reinstalled, so in that case we add a magic application env. -config_setting() -> - case application:get_env(rabbit, windows_service_config) of - {ok, File1} -> File1; - undefined -> case os:getenv("RABBITMQ_CONFIG_FILE") of - false -> none; - File2 -> File2 - end - end. + rabbit_config:config_files(). %% We don't want this in fhc since it references rabbit stuff. And we can't put %% this in the bootstep directly. diff --git a/src/rabbit_amqqueue_process.erl b/src/rabbit_amqqueue_process.erl index f79304632e64..6d3bf892b085 100644 --- a/src/rabbit_amqqueue_process.erl +++ b/src/rabbit_amqqueue_process.erl @@ -1367,7 +1367,7 @@ handle_pre_hibernate(State = #q{backing_queue = BQ, format_message_queue(Opt, MQ) -> rabbit_misc:format_message_queue(Opt, MQ). -log_delete_exclusive({ConPid, ConRef}, State) -> +log_delete_exclusive({ConPid, _ConRef}, State) -> log_delete_exclusive(ConPid, State); log_delete_exclusive(ConPid, #q{ q = #amqqueue{ name = Resource } }) -> #resource{ name = QName, virtual_host = VHost } = Resource, diff --git a/src/rabbit_auth_backend_dummy.erl b/src/rabbit_auth_backend_dummy.erl deleted file mode 100644 index 0077b4c99372..000000000000 --- a/src/rabbit_auth_backend_dummy.erl +++ /dev/null @@ -1,48 +0,0 @@ -%% The contents of this file are subject to the Mozilla Public License -%% Version 1.1 (the "License"); you may not use this file except in -%% compliance with the License. You may obtain a copy of the License -%% at http://www.mozilla.org/MPL/ -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and -%% limitations under the License. -%% -%% The Original Code is RabbitMQ. -%% -%% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_auth_backend_dummy). --include("rabbit.hrl"). - --behaviour(rabbit_authn_backend). --behaviour(rabbit_authz_backend). - --export([user/0]). --export([user_login_authentication/2, user_login_authorization/1, - check_vhost_access/3, check_resource_access/3]). - --ifdef(use_specs). - --spec(user/0 :: () -> rabbit_types:user()). - --endif. - -%% A user to be used by the direct client when permission checks are -%% not needed. This user can do anything AMQPish. -user() -> #user{username = <<"none">>, - tags = [], - authz_backends = [{?MODULE, none}]}. - -%% Implementation of rabbit_auth_backend - -user_login_authentication(_, _) -> - {refused, "cannot log in conventionally as dummy user", []}. - -user_login_authorization(_) -> - {refused, "cannot log in conventionally as dummy user", []}. - -check_vhost_access(#auth_user{}, _VHostPath, _Sock) -> true. -check_resource_access(#auth_user{}, #resource{}, _Permission) -> true. diff --git a/src/rabbit_auth_backend_internal.erl b/src/rabbit_auth_backend_internal.erl deleted file mode 100644 index d7705d8e7b70..000000000000 --- a/src/rabbit_auth_backend_internal.erl +++ /dev/null @@ -1,400 +0,0 @@ -%% The contents of this file are subject to the Mozilla Public License -%% Version 1.1 (the "License"); you may not use this file except in -%% compliance with the License. You may obtain a copy of the License -%% at http://www.mozilla.org/MPL/ -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and -%% limitations under the License. -%% -%% The Original Code is RabbitMQ. -%% -%% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_auth_backend_internal). --include("rabbit.hrl"). - --behaviour(rabbit_authn_backend). --behaviour(rabbit_authz_backend). - --export([user_login_authentication/2, user_login_authorization/1, - check_vhost_access/3, check_resource_access/3]). - --export([add_user/2, delete_user/1, lookup_user/1, - change_password/2, clear_password/1, - hash_password/2, change_password_hash/2, change_password_hash/3, - set_tags/2, set_permissions/5, clear_permissions/2]). --export([user_info_keys/0, perms_info_keys/0, - user_perms_info_keys/0, vhost_perms_info_keys/0, - user_vhost_perms_info_keys/0, - list_users/0, list_users/2, list_permissions/0, - list_user_permissions/1, list_user_permissions/3, - list_vhost_permissions/1, list_vhost_permissions/3, - list_user_vhost_permissions/2]). - -%% for testing --export([hashing_module_for_user/1]). - -%%---------------------------------------------------------------------------- - --ifdef(use_specs). - --type(regexp() :: binary()). - --spec(add_user/2 :: (rabbit_types:username(), rabbit_types:password()) -> 'ok'). --spec(delete_user/1 :: (rabbit_types:username()) -> 'ok'). --spec(lookup_user/1 :: (rabbit_types:username()) - -> rabbit_types:ok(rabbit_types:internal_user()) - | rabbit_types:error('not_found')). --spec(change_password/2 :: (rabbit_types:username(), rabbit_types:password()) - -> 'ok'). --spec(clear_password/1 :: (rabbit_types:username()) -> 'ok'). --spec(hash_password/2 :: (module(), rabbit_types:password()) - -> rabbit_types:password_hash()). --spec(change_password_hash/2 :: (rabbit_types:username(), - rabbit_types:password_hash()) -> 'ok'). --spec(set_tags/2 :: (rabbit_types:username(), [atom()]) -> 'ok'). --spec(set_permissions/5 ::(rabbit_types:username(), rabbit_types:vhost(), - regexp(), regexp(), regexp()) -> 'ok'). --spec(clear_permissions/2 :: (rabbit_types:username(), rabbit_types:vhost()) - -> 'ok'). --spec(user_info_keys/0 :: () -> rabbit_types:info_keys()). --spec(perms_info_keys/0 :: () -> rabbit_types:info_keys()). --spec(user_perms_info_keys/0 :: () -> rabbit_types:info_keys()). --spec(vhost_perms_info_keys/0 :: () -> rabbit_types:info_keys()). --spec(user_vhost_perms_info_keys/0 :: () -> rabbit_types:info_keys()). --spec(list_users/0 :: () -> [rabbit_types:infos()]). --spec(list_users/2 :: (reference(), pid()) -> 'ok'). --spec(list_permissions/0 :: () -> [rabbit_types:infos()]). --spec(list_user_permissions/1 :: - (rabbit_types:username()) -> [rabbit_types:infos()]). --spec(list_user_permissions/3 :: - (rabbit_types:username(), reference(), pid()) -> 'ok'). --spec(list_vhost_permissions/1 :: - (rabbit_types:vhost()) -> [rabbit_types:infos()]). --spec(list_vhost_permissions/3 :: - (rabbit_types:vhost(), reference(), pid()) -> 'ok'). --spec(list_user_vhost_permissions/2 :: - (rabbit_types:username(), rabbit_types:vhost()) - -> [rabbit_types:infos()]). - --endif. - -%%---------------------------------------------------------------------------- -%% Implementation of rabbit_auth_backend - -%% Returns a password hashing module for the user record provided. If -%% there is no information in the record, we consider it to be legacy -%% (inserted by a version older than 3.6.0) and fall back to MD5, the -%% now obsolete hashing function. -hashing_module_for_user(#internal_user{ - hashing_algorithm = ModOrUndefined}) -> - rabbit_password:hashing_mod(ModOrUndefined). - -user_login_authentication(Username, []) -> - internal_check_user_login(Username, fun(_) -> true end); -user_login_authentication(Username, [{password, Cleartext}]) -> - internal_check_user_login( - Username, - fun (#internal_user{password_hash = <>} = U) -> - Hash =:= rabbit_password:salted_hash( - hashing_module_for_user(U), Salt, Cleartext); - (#internal_user{}) -> - false - end); -user_login_authentication(Username, AuthProps) -> - exit({unknown_auth_props, Username, AuthProps}). - -user_login_authorization(Username) -> - case user_login_authentication(Username, []) of - {ok, #auth_user{impl = Impl, tags = Tags}} -> {ok, Impl, Tags}; - Else -> Else - end. - -internal_check_user_login(Username, Fun) -> - Refused = {refused, "user '~s' - invalid credentials", [Username]}, - case lookup_user(Username) of - {ok, User = #internal_user{tags = Tags}} -> - case Fun(User) of - true -> {ok, #auth_user{username = Username, - tags = Tags, - impl = none}}; - _ -> Refused - end; - {error, not_found} -> - Refused - end. - -check_vhost_access(#auth_user{username = Username}, VHostPath, _Sock) -> - case mnesia:dirty_read({rabbit_user_permission, - #user_vhost{username = Username, - virtual_host = VHostPath}}) of - [] -> false; - [_R] -> true - end. - -check_resource_access(#auth_user{username = Username}, - #resource{virtual_host = VHostPath, name = Name}, - Permission) -> - case mnesia:dirty_read({rabbit_user_permission, - #user_vhost{username = Username, - virtual_host = VHostPath}}) of - [] -> - false; - [#user_permission{permission = P}] -> - PermRegexp = case element(permission_index(Permission), P) of - %% <<"^$">> breaks Emacs' erlang mode - <<"">> -> <<$^, $$>>; - RE -> RE - end, - case re:run(Name, PermRegexp, [{capture, none}]) of - match -> true; - nomatch -> false - end - end. - -permission_index(configure) -> #permission.configure; -permission_index(write) -> #permission.write; -permission_index(read) -> #permission.read. - -%%---------------------------------------------------------------------------- -%% Manipulation of the user database - -add_user(Username, Password) -> - rabbit_log:info("Creating user '~s'~n", [Username]), - %% hash_password will pick the hashing function configured for us - %% but we also need to store a hint as part of the record, so we - %% retrieve it here one more time - HashingMod = rabbit_password:hashing_mod(), - User = #internal_user{username = Username, - password_hash = hash_password(HashingMod, Password), - tags = [], - hashing_algorithm = HashingMod}, - R = rabbit_misc:execute_mnesia_transaction( - fun () -> - case mnesia:wread({rabbit_user, Username}) of - [] -> - ok = mnesia:write(rabbit_user, User, write); - _ -> - mnesia:abort({user_already_exists, Username}) - end - end), - rabbit_event:notify(user_created, [{name, Username}]), - R. - -delete_user(Username) -> - rabbit_log:info("Deleting user '~s'~n", [Username]), - R = rabbit_misc:execute_mnesia_transaction( - rabbit_misc:with_user( - Username, - fun () -> - ok = mnesia:delete({rabbit_user, Username}), - [ok = mnesia:delete_object( - rabbit_user_permission, R, write) || - R <- mnesia:match_object( - rabbit_user_permission, - #user_permission{user_vhost = #user_vhost{ - username = Username, - virtual_host = '_'}, - permission = '_'}, - write)], - ok - end)), - rabbit_event:notify(user_deleted, [{name, Username}]), - R. - -lookup_user(Username) -> - rabbit_misc:dirty_read({rabbit_user, Username}). - -change_password(Username, Password) -> - rabbit_log:info("Changing password for '~s'~n", [Username]), - HashingAlgorithm = rabbit_password:hashing_mod(), - R = change_password_hash(Username, - hash_password(rabbit_password:hashing_mod(), - Password), - HashingAlgorithm), - rabbit_event:notify(user_password_changed, [{name, Username}]), - R. - -clear_password(Username) -> - rabbit_log:info("Clearing password for '~s'~n", [Username]), - R = change_password_hash(Username, <<"">>), - rabbit_event:notify(user_password_cleared, [{name, Username}]), - R. - -hash_password(HashingMod, Cleartext) -> - rabbit_password:hash(HashingMod, Cleartext). - -change_password_hash(Username, PasswordHash) -> - change_password_hash(Username, PasswordHash, rabbit_password:hashing_mod()). - - -change_password_hash(Username, PasswordHash, HashingAlgorithm) -> - update_user(Username, fun(User) -> - User#internal_user{ - password_hash = PasswordHash, - hashing_algorithm = HashingAlgorithm } - end). - -set_tags(Username, Tags) -> - rabbit_log:info("Setting user tags for user '~s' to ~p~n", - [Username, Tags]), - R = update_user(Username, fun(User) -> - User#internal_user{tags = Tags} - end), - rabbit_event:notify(user_tags_set, [{name, Username}, {tags, Tags}]), - R. - -set_permissions(Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm) -> - rabbit_log:info("Setting permissions for " - "'~s' in '~s' to '~s', '~s', '~s'~n", - [Username, VHostPath, ConfigurePerm, WritePerm, ReadPerm]), - lists:map( - fun (RegexpBin) -> - Regexp = binary_to_list(RegexpBin), - case re:compile(Regexp) of - {ok, _} -> ok; - {error, Reason} -> throw({error, {invalid_regexp, - Regexp, Reason}}) - end - end, [ConfigurePerm, WritePerm, ReadPerm]), - R = rabbit_misc:execute_mnesia_transaction( - rabbit_misc:with_user_and_vhost( - Username, VHostPath, - fun () -> ok = mnesia:write( - rabbit_user_permission, - #user_permission{user_vhost = #user_vhost{ - username = Username, - virtual_host = VHostPath}, - permission = #permission{ - configure = ConfigurePerm, - write = WritePerm, - read = ReadPerm}}, - write) - end)), - rabbit_event:notify(permission_created, [{user, Username}, - {vhost, VHostPath}, - {configure, ConfigurePerm}, - {write, WritePerm}, - {read, ReadPerm}]), - R. - -clear_permissions(Username, VHostPath) -> - R = rabbit_misc:execute_mnesia_transaction( - rabbit_misc:with_user_and_vhost( - Username, VHostPath, - fun () -> - ok = mnesia:delete({rabbit_user_permission, - #user_vhost{username = Username, - virtual_host = VHostPath}}) - end)), - rabbit_event:notify(permission_deleted, [{user, Username}, - {vhost, VHostPath}]), - R. - - -update_user(Username, Fun) -> - rabbit_misc:execute_mnesia_transaction( - rabbit_misc:with_user( - Username, - fun () -> - {ok, User} = lookup_user(Username), - ok = mnesia:write(rabbit_user, Fun(User), write) - end)). - -%%---------------------------------------------------------------------------- -%% Listing - --define(PERMS_INFO_KEYS, [configure, write, read]). --define(USER_INFO_KEYS, [user, tags]). - -user_info_keys() -> ?USER_INFO_KEYS. - -perms_info_keys() -> [user, vhost | ?PERMS_INFO_KEYS]. -vhost_perms_info_keys() -> [user | ?PERMS_INFO_KEYS]. -user_perms_info_keys() -> [vhost | ?PERMS_INFO_KEYS]. -user_vhost_perms_info_keys() -> ?PERMS_INFO_KEYS. - -list_users() -> - [extract_internal_user_params(U) || - U <- mnesia:dirty_match_object(rabbit_user, #internal_user{_ = '_'})]. - -list_users(Ref, AggregatorPid) -> - rabbit_control_misc:emitting_map( - AggregatorPid, Ref, - fun(U) -> extract_internal_user_params(U) end, - mnesia:dirty_match_object(rabbit_user, #internal_user{_ = '_'})). - -list_permissions() -> - list_permissions(perms_info_keys(), match_user_vhost('_', '_')). - -list_permissions(Keys, QueryThunk) -> - [extract_user_permission_params(Keys, U) || - %% TODO: use dirty ops instead - U <- rabbit_misc:execute_mnesia_transaction(QueryThunk)]. - -list_permissions(Keys, QueryThunk, Ref, AggregatorPid) -> - rabbit_control_misc:emitting_map( - AggregatorPid, Ref, fun(U) -> extract_user_permission_params(Keys, U) end, - %% TODO: use dirty ops instead - rabbit_misc:execute_mnesia_transaction(QueryThunk)). - -filter_props(Keys, Props) -> [T || T = {K, _} <- Props, lists:member(K, Keys)]. - -list_user_permissions(Username) -> - list_permissions( - user_perms_info_keys(), - rabbit_misc:with_user(Username, match_user_vhost(Username, '_'))). - -list_user_permissions(Username, Ref, AggregatorPid) -> - list_permissions( - user_perms_info_keys(), - rabbit_misc:with_user(Username, match_user_vhost(Username, '_')), - Ref, AggregatorPid). - -list_vhost_permissions(VHostPath) -> - list_permissions( - vhost_perms_info_keys(), - rabbit_vhost:with(VHostPath, match_user_vhost('_', VHostPath))). - -list_vhost_permissions(VHostPath, Ref, AggregatorPid) -> - list_permissions( - vhost_perms_info_keys(), - rabbit_vhost:with(VHostPath, match_user_vhost('_', VHostPath)), - Ref, AggregatorPid). - -list_user_vhost_permissions(Username, VHostPath) -> - list_permissions( - user_vhost_perms_info_keys(), - rabbit_misc:with_user_and_vhost( - Username, VHostPath, match_user_vhost(Username, VHostPath))). - -extract_user_permission_params(Keys, #user_permission{ - user_vhost = - #user_vhost{username = Username, - virtual_host = VHostPath}, - permission = #permission{ - configure = ConfigurePerm, - write = WritePerm, - read = ReadPerm}}) -> - filter_props(Keys, [{user, Username}, - {vhost, VHostPath}, - {configure, ConfigurePerm}, - {write, WritePerm}, - {read, ReadPerm}]). - -extract_internal_user_params(#internal_user{username = Username, tags = Tags}) -> - [{user, Username}, {tags, Tags}]. - -match_user_vhost(Username, VHostPath) -> - fun () -> mnesia:match_object( - rabbit_user_permission, - #user_permission{user_vhost = #user_vhost{ - username = Username, - virtual_host = VHostPath}, - permission = '_'}, - read) - end. diff --git a/src/rabbit_cli.erl b/src/rabbit_cli.erl index 4aad3c091656..415150495631 100644 --- a/src/rabbit_cli.erl +++ b/src/rabbit_cli.erl @@ -18,17 +18,19 @@ -include("rabbit_cli.hrl"). -export([main/3, start_distribution/0, start_distribution/1, - parse_arguments/4, rpc_call/4, rpc_call/5, rpc_call/7]). + parse_arguments/4, filter_opts/2, + rpc_call/4, rpc_call/5, rpc_call/7]). %%---------------------------------------------------------------------------- -ifdef(use_specs). +-type(option_name() :: string()). +-type(option_value() :: string() | node() | boolean()). -type(optdef() :: flag | {option, string()}). --type(parse_result() :: {'ok', {atom(), [{string(), string()}], [string()]}} | +-type(parse_result() :: {'ok', {atom(), [{option_name(), option_value()}], [string()]}} | 'no_command'). - -spec(main/3 :: (fun (([string()], string()) -> parse_result()), fun ((atom(), atom(), [any()], [any()]) -> any()), atom()) -> no_return()). @@ -38,6 +40,9 @@ -spec(parse_arguments/4 :: ([{atom(), [{string(), optdef()}]} | atom()], [{string(), optdef()}], string(), [string()]) -> parse_result()). + +-spec(filter_opts/2 :: ([{option_name(), option_value()}], [option_name()]) -> [boolean()]). + -spec(rpc_call/4 :: (node(), atom(), atom(), [any()]) -> any()). -spec(rpc_call/5 :: (node(), atom(), atom(), [any()], number()) -> any()). -spec(rpc_call/7 :: (node(), atom(), atom(), [any()], reference(), pid(), @@ -117,7 +122,10 @@ main(ParseFun, DoFun, UsageMod) -> _ -> print_error("unable to connect to node ~w: ~w", [Node, Reason]), print_badrpc_diagnostics([Node]), - rabbit_misc:quit(?EX_UNAVAILABLE) + case Command of + stop -> rabbit_misc:quit(?EX_OK); + _ -> rabbit_misc:quit(?EX_UNAVAILABLE) + end end; {badrpc_multi, Reason, Nodes} -> print_error("unable to connect to nodes ~p: ~w", [Nodes, Reason]), @@ -241,6 +249,22 @@ process_opts(Defs, C, [A | As], Found, KVs, Outs) -> {none, _, _} -> no_command end. +%% When we have a set of flags that are used for filtering, we want by +%% default to include every such option in our output. But if a user +%% explicitly specified any such flag, we want to include only items +%% which he has requested. +filter_opts(CurrentOptionValues, AllOptionNames) -> + Explicit = lists:map(fun(OptName) -> + proplists:get_bool(OptName, CurrentOptionValues) + end, + AllOptionNames), + case lists:member(true, Explicit) of + true -> + Explicit; + false -> + lists:duplicate(length(AllOptionNames), true) + end. + %%---------------------------------------------------------------------------- fmt_stderr(Format, Args) -> rabbit_misc:format_stderr(Format ++ "~n", Args). @@ -255,14 +279,10 @@ print_badrpc_diagnostics(Nodes) -> %% a timeout unless we set our ticktime to be the same. So let's do %% that. rpc_call(Node, Mod, Fun, Args) -> - rpc_call(Node, Mod, Fun, Args, ?RPC_TIMEOUT). + rabbit_misc:rpc_call(Node, Mod, Fun, Args). rpc_call(Node, Mod, Fun, Args, Timeout) -> - case rpc:call(Node, net_kernel, get_net_ticktime, [], Timeout) of - {badrpc, _} = E -> E; - Time -> net_kernel:set_net_ticktime(Time, 0), - rpc:call(Node, Mod, Fun, Args, Timeout) - end. + rabbit_misc:rpc_call(Node, Mod, Fun, Args, Timeout). rpc_call(Node, Mod, Fun, Args, Ref, Pid, Timeout) -> - rpc_call(Node, Mod, Fun, Args++[Ref, Pid], Timeout). + rabbit_misc:rpc_call(Node, Mod, Fun, Args, Ref, Pid, Timeout). diff --git a/src/rabbit_config.erl b/src/rabbit_config.erl new file mode 100644 index 000000000000..67e7523ec0e3 --- /dev/null +++ b/src/rabbit_config.erl @@ -0,0 +1,179 @@ +-module(rabbit_config). + +-export([ + generate_config_file/5, + prepare_and_use_config/0, + prepare_config/1, + update_app_config/1, + schema_dir/0, + config_files/0, + get_advanced_config/0 + ]). + +prepare_and_use_config() -> + case legacy_erlang_term_config_used() of + true -> + %% Use .config file + ok; + false -> + case prepare_config(get_confs()) of + ok -> + %% No .conf to generate from + ok; + {ok, GeneratedConfigFile} -> + %% Generated config file + update_app_config(GeneratedConfigFile); + {error, Err} -> + {error, Err} + end + end. + +%% we support both the classic Erlang term +%% config file (rabbitmq.config) as well as rabbitmq.conf +legacy_erlang_term_config_used() -> + case init:get_argument(config) of + error -> false; + {ok, [Config | _]} -> + ConfigFile = Config ++ ".config", + rabbit_file:is_file(ConfigFile) + andalso + get_advanced_config() == none + end. + +get_confs() -> + case init:get_argument(conf) of + {ok, Configs} -> Configs; + _ -> [] + end. + +prepare_config(Configs) -> + case {init:get_argument(conf_dir), init:get_argument(conf_script_dir)} of + {{ok, ConfDir}, {ok, ScriptDir}} -> + ConfFiles = [Config ++ ".conf" || [Config] <- Configs, + rabbit_file:is_file(Config ++ + ".conf")], + case ConfFiles of + [] -> ok; + _ -> + case generate_config_file(ConfFiles, ConfDir, ScriptDir) of + {ok, GeneratedConfigFile} -> + {ok, GeneratedConfigFile}; + {error, Reason} -> + {error, Reason} + end + end; + _ -> ok + end. + +update_app_config(ConfigFile) -> + ok = application_controller:change_application_data([], [ConfigFile]). + +generate_config_file(ConfFiles, ConfDir, ScriptDir) -> + generate_config_file(ConfFiles, ConfDir, ScriptDir, + schema_dir(), get_advanced_config()). + + +generate_config_file(ConfFiles, ConfDir, ScriptDir, SchemaDir, Advanced) -> + prepare_plugin_schemas(SchemaDir), + % SchemaFile = filename:join([ScriptDir, "rabbitmq.schema"]), + Cuttlefish = filename:join([ScriptDir, "cuttlefish"]), + GeneratedDir = filename:join([ConfDir, "generated"]), + + AdvancedConfigArg = case check_advanced_config(Advanced) of + {ok, FileName} -> [" -a ", FileName]; + none -> [] + end, + rabbit_file:recursive_delete([GeneratedDir]), + Command = lists:concat(["escript ", "\"", Cuttlefish, "\"", + " -f rabbitmq -s ", "\"", SchemaDir, "\"", + " -e ", "\"", ConfDir, "\"", + [[" -c ", ConfFile] || ConfFile <- ConfFiles], + AdvancedConfigArg]), + Result = rabbit_misc:os_cmd(Command), + case string:str(Result, " -config ") of + 0 -> {error, {generation_error, Result}}; + _ -> + [OutFile] = rabbit_file:wildcard("rabbitmq.*.config", GeneratedDir), + ResultFile = filename:join([GeneratedDir, "rabbitmq.config"]), + rabbit_file:rename(filename:join([GeneratedDir, OutFile]), + ResultFile), + {ok, ResultFile} + end. + +schema_dir() -> + case init:get_argument(conf_schema_dir) of + {ok, SchemaDir} -> SchemaDir; + _ -> + case code:priv_dir(rabbit) of + {error, bad_name} -> filename:join([".", "priv", "schema"]); + PrivDir -> filename:join([PrivDir, "schema"]) + end + end. + +check_advanced_config(none) -> none; +check_advanced_config(ConfigName) -> + case rabbit_file:is_file(ConfigName) of + true -> {ok, ConfigName}; + false -> none + end. + +get_advanced_config() -> + case init:get_argument(conf_advanced) of + %% There can be only one advanced.config + {ok, [FileName | _]} -> + ConfigName = FileName ++ ".config", + case rabbit_file:is_file(ConfigName) of + true -> ConfigName; + false -> none + end; + _ -> none + end. + + +prepare_plugin_schemas(SchemaDir) -> + case rabbit_file:is_dir(SchemaDir) of + true -> rabbit_plugins:extract_schemas(SchemaDir); + false -> ok + end. + + +config_files() -> + Abs = fun (F, Ex) -> filename:absname(filename:rootname(F, Ex) ++ Ex) end, + case legacy_erlang_term_config_used() of + true -> + case init:get_argument(config) of + {ok, Files} -> [Abs(File, ".config") || [File] <- Files]; + error -> case config_setting() of + none -> []; + File -> [Abs(File, ".config") + ++ + " (not found)"] + end + end; + false -> + ConfFiles = [Abs(File, ".conf") || File <- get_confs()], + AdvancedFiles = case get_advanced_config() of + none -> []; + FileName -> [Abs(FileName, ".config")] + end, + AdvancedFiles ++ ConfFiles + + end. + + +%% This is a pain. We want to know where the config file is. But we +%% can't specify it on the command line if it is missing or the VM +%% will fail to start, so we need to find it by some mechanism other +%% than init:get_arguments/0. We can look at the environment variable +%% which is responsible for setting it... but that doesn't work for a +%% Windows service since the variable can change and the service not +%% be reinstalled, so in that case we add a magic application env. +config_setting() -> + case application:get_env(rabbit, windows_service_config) of + {ok, File1} -> File1; + undefined -> case os:getenv("RABBITMQ_CONFIG_FILE") of + false -> none; + File2 -> File2 + end + end. + diff --git a/src/rabbit_control_main.erl b/src/rabbit_control_main.erl index 793af7025961..693a6dc6fa3d 100644 --- a/src/rabbit_control_main.erl +++ b/src/rabbit_control_main.erl @@ -17,12 +17,13 @@ -module(rabbit_control_main). -include("rabbit.hrl"). -include("rabbit_cli.hrl"). +-include("rabbit_misc.hrl"). -export([start/0, stop/0, parse_arguments/2, action/5, action/6, sync_queue/1, cancel_sync_queue/1, become/1, purge_queue/1]). --import(rabbit_cli, [rpc_call/4, rpc_call/5, rpc_call/7]). +-import(rabbit_misc, [rpc_call/4, rpc_call/5, rpc_call/7]). -define(EXTERNAL_CHECK_INTERVAL, 1000). @@ -75,7 +76,7 @@ {set_vhost_limits, [?VHOST_DEF]}, {clear_vhost_limits, [?VHOST_DEF]}, - {list_queues, [?VHOST_DEF]}, + {list_queues, [?VHOST_DEF, ?OFFLINE_DEF, ?ONLINE_DEF]}, {list_exchanges, [?VHOST_DEF]}, {list_bindings, [?VHOST_DEF]}, {list_connections, [?VHOST_DEF]}, @@ -86,6 +87,7 @@ report, set_cluster_name, eval, + node_health_check, close_connection, {trace_on, [?VHOST_DEF]}, @@ -114,7 +116,7 @@ [stop, stop_app, start_app, wait, reset, force_reset, rotate_logs, join_cluster, change_cluster_node_type, update_cluster_nodes, forget_cluster_node, rename_cluster_node, cluster_status, status, - environment, eval, force_boot, help]). + environment, eval, force_boot, help, node_health_check]). -define(COMMANDS_WITH_TIMEOUT, [list_user_permissions, list_policies, list_queues, list_exchanges, @@ -559,6 +561,17 @@ action(eval, Node, [Expr], _Opts, _Inform) -> action(help, _Node, _Args, _Opts, _Inform) -> io:format("~s", [rabbit_ctl_usage:usage()]); +action(node_health_check, Node, _Args, _Opts, Inform) -> + Inform("Checking health of node ~p", [Node]), + try + rabbit_health_check:node(Node), + io:format("Health check passed~n") + catch + {node_is_ko, ErrorMsg, ErrorCode} -> + io:format("Heath check failed:~n~s~n", [ErrorMsg]), + halt(ErrorCode) + end; + action(Command, Node, Args, Opts, Inform) -> %% For backward compatibility, run commands accepting a timeout with %% the default timeout. @@ -582,7 +595,8 @@ action(list_permissions, Node, [], Opts, Inform, Timeout) -> VHost = proplists:get_value(?VHOST_OPT, Opts), Inform("Listing permissions in vhost \"~s\"", [VHost]), call(Node, {rabbit_auth_backend_internal, list_vhost_permissions, [VHost]}, - rabbit_auth_backend_internal:vhost_perms_info_keys(), true, Timeout); + rabbit_auth_backend_internal:vhost_perms_info_keys(), true, Timeout, + true); action(list_parameters, Node, [], Opts, Inform, Timeout) -> VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), @@ -607,13 +621,15 @@ action(list_user_permissions, _Node, _Args = [], _Opts, _Inform, _Timeout) -> action(list_user_permissions, Node, Args = [_Username], _Opts, Inform, Timeout) -> Inform("Listing permissions for user ~p", Args), call(Node, {rabbit_auth_backend_internal, list_user_permissions, Args}, - rabbit_auth_backend_internal:user_perms_info_keys(), true, Timeout); + rabbit_auth_backend_internal:user_perms_info_keys(), true, Timeout, + true); action(list_queues, Node, Args, Opts, Inform, Timeout) -> + [Online, Offline] = rabbit_cli:filter_opts(Opts, [?ONLINE_OPT, ?OFFLINE_OPT]), Inform("Listing queues", []), VHostArg = list_to_binary(proplists:get_value(?VHOST_OPT, Opts)), ArgAtoms = default_if_empty(Args, [name, messages]), - call(Node, {rabbit_amqqueue, info_all, [VHostArg, ArgAtoms]}, + call(Node, {rabbit_amqqueue, info_all, [VHostArg, ArgAtoms, Online, Offline]}, ArgAtoms, Timeout); action(list_exchanges, Node, Args, Opts, Inform, Timeout) -> @@ -750,20 +766,33 @@ default_if_empty(List, Default) when is_list(List) -> true -> [list_to_atom(X) || X <- List] end. -display_info_message(Result, InfoItemKeys) -> +display_info_message_row(IsEscaped, Result, InfoItemKeys) -> display_row([format_info_item( case proplists:lookup(X, Result) of none when is_list(Result), length(Result) > 0 -> exit({error, {bad_info_key, X}}); none -> Result; {X, Value} -> Value - end) || X <- InfoItemKeys]). + end, IsEscaped) || X <- InfoItemKeys]). + +display_info_message(IsEscaped) -> + fun ([], _) -> + ok; + ([FirstResult|_] = List, InfoItemKeys) when is_list(FirstResult) -> + lists:foreach(fun(Result) -> + display_info_message_row(IsEscaped, Result, InfoItemKeys) + end, + List), + ok; + (Result, InfoItemKeys) -> + display_info_message_row(IsEscaped, Result, InfoItemKeys) + end. display_info_list(Results, InfoItemKeys) when is_list(Results) -> lists:foreach( fun (Result) -> display_row( - [format_info_item(proplists:get_value(X, Result)) || - X <- InfoItemKeys]) + [format_info_item(proplists:get_value(X, Result), true) + || X <- InfoItemKeys]) end, lists:sort(Results)), ok; display_info_list(Other, _) -> @@ -776,32 +805,33 @@ display_row(Row) -> -define(IS_U8(X), (X >= 0 andalso X =< 255)). -define(IS_U16(X), (X >= 0 andalso X =< 65535)). -format_info_item(#resource{name = Name}) -> - escape(Name); -format_info_item({N1, N2, N3, N4} = Value) when +format_info_item(#resource{name = Name}, IsEscaped) -> + escape(Name, IsEscaped); +format_info_item({N1, N2, N3, N4} = Value, _IsEscaped) when ?IS_U8(N1), ?IS_U8(N2), ?IS_U8(N3), ?IS_U8(N4) -> rabbit_misc:ntoa(Value); -format_info_item({K1, K2, K3, K4, K5, K6, K7, K8} = Value) when +format_info_item({K1, K2, K3, K4, K5, K6, K7, K8} = Value, _IsEscaped) when ?IS_U16(K1), ?IS_U16(K2), ?IS_U16(K3), ?IS_U16(K4), ?IS_U16(K5), ?IS_U16(K6), ?IS_U16(K7), ?IS_U16(K8) -> rabbit_misc:ntoa(Value); -format_info_item(Value) when is_pid(Value) -> +format_info_item(Value, _IsEscaped) when is_pid(Value) -> rabbit_misc:pid_to_string(Value); -format_info_item(Value) when is_binary(Value) -> - escape(Value); -format_info_item(Value) when is_atom(Value) -> - escape(atom_to_list(Value)); +format_info_item(Value, IsEscaped) when is_binary(Value) -> + escape(Value, IsEscaped); +format_info_item(Value, IsEscaped) when is_atom(Value) -> + escape(atom_to_list(Value), IsEscaped); format_info_item([{TableEntryKey, TableEntryType, _TableEntryValue} | _] = - Value) when is_binary(TableEntryKey) andalso - is_atom(TableEntryType) -> - io_lib:format("~1000000000000p", [prettify_amqp_table(Value)]); -format_info_item([T | _] = Value) + Value, IsEscaped) when is_binary(TableEntryKey) andalso + is_atom(TableEntryType) -> + io_lib:format("~1000000000000p", [prettify_amqp_table(Value, IsEscaped)]); +format_info_item([T | _] = Value, IsEscaped) when is_tuple(T) orelse is_pid(T) orelse is_binary(T) orelse is_atom(T) orelse is_list(T) -> "[" ++ lists:nthtail(2, lists:append( - [", " ++ format_info_item(E) || E <- Value])) ++ "]"; -format_info_item(Value) -> + [", " ++ format_info_item(E, IsEscaped) + || E <- Value])) ++ "]"; +format_info_item(Value, _IsEscaped) -> io_lib:format("~w", [Value]). display_call_result(Node, MFA) -> @@ -832,9 +862,12 @@ call(Node, {Mod, Fun, Args}) -> rpc_call(Node, Mod, Fun, lists:map(fun list_to_binary_utf8/1, Args)). call(Node, {Mod, Fun, Args}, InfoKeys, Timeout) -> - call(Node, {Mod, Fun, Args}, InfoKeys, false, Timeout). + call(Node, {Mod, Fun, Args}, InfoKeys, false, Timeout, false). call(Node, {Mod, Fun, Args}, InfoKeys, ToBinUtf8, Timeout) -> + call(Node, {Mod, Fun, Args}, InfoKeys, ToBinUtf8, Timeout, false). + +call(Node, {Mod, Fun, Args}, InfoKeys, ToBinUtf8, Timeout, IsEscaped) -> Args0 = case ToBinUtf8 of true -> lists:map(fun list_to_binary_utf8/1, Args); false -> Args @@ -854,7 +887,7 @@ call(Node, {Mod, Fun, Args}, InfoKeys, ToBinUtf8, Timeout) -> end end), rabbit_control_misc:wait_for_info_messages( - Pid, Ref, InfoKeys, fun display_info_message/2, Timeout). + Pid, Ref, InfoKeys, display_info_message(IsEscaped), Timeout). list_to_binary_utf8(L) -> B = list_to_binary(L), @@ -867,9 +900,14 @@ list_to_binary_utf8(L) -> %% characters. We don't escape characters above 127, since they may %% form part of UTF-8 strings. -escape(Atom) when is_atom(Atom) -> escape(atom_to_list(Atom)); -escape(Bin) when is_binary(Bin) -> escape(binary_to_list(Bin)); -escape(L) when is_list(L) -> escape_char(lists:reverse(L), []). +escape(Atom, IsEscaped) when is_atom(Atom) -> + escape(atom_to_list(Atom), IsEscaped); +escape(Bin, IsEscaped) when is_binary(Bin) -> + escape(binary_to_list(Bin), IsEscaped); +escape(L, false) when is_list(L) -> + escape_char(lists:reverse(L), []); +escape(L, true) when is_list(L) -> + L. escape_char([$\\ | T], Acc) -> escape_char(T, [$\\, $\\ | Acc]); @@ -881,14 +919,18 @@ escape_char([X | T], Acc) -> escape_char([], Acc) -> Acc. -prettify_amqp_table(Table) -> - [{escape(K), prettify_typed_amqp_value(T, V)} || {K, T, V} <- Table]. - -prettify_typed_amqp_value(longstr, Value) -> escape(Value); -prettify_typed_amqp_value(table, Value) -> prettify_amqp_table(Value); -prettify_typed_amqp_value(array, Value) -> [prettify_typed_amqp_value(T, V) || - {T, V} <- Value]; -prettify_typed_amqp_value(_Type, Value) -> Value. +prettify_amqp_table(Table, IsEscaped) -> + [{escape(K, IsEscaped), prettify_typed_amqp_value(T, V, IsEscaped)} + || {K, T, V} <- Table]. + +prettify_typed_amqp_value(longstr, Value, IsEscaped) -> + escape(Value, IsEscaped); +prettify_typed_amqp_value(table, Value, IsEscaped) -> + prettify_amqp_table(Value, IsEscaped); +prettify_typed_amqp_value(array, Value, IsEscaped) -> + [prettify_typed_amqp_value(T, V, IsEscaped) || {T, V} <- Value]; +prettify_typed_amqp_value(_Type, Value, _IsEscaped) -> + Value. split_list([]) -> []; split_list([_]) -> exit(even_list_needed); diff --git a/src/rabbit_hipe.erl b/src/rabbit_hipe.erl index 0302d82839e6..05b5f3719d1d 100644 --- a/src/rabbit_hipe.erl +++ b/src/rabbit_hipe.erl @@ -44,7 +44,8 @@ hipe_compile() -> %% happens when RabbitMQ is stopped (just the %% application, not the entire node) and started %% again. - already_hipe_compiled(HM)], + already_hipe_compiled(HM) + andalso (not compiled_with_version_support(HM))], case HipeModules of [] -> {ok, already_compiled}; _ -> do_hipe_compile(HipeModules) @@ -59,6 +60,10 @@ already_hipe_compiled(Mod) -> code:is_module_native(Mod) =:= false end. +compiled_with_version_support(Mod) -> + proplists:get_value(erlang_version_support, Mod:module_info(attributes)) + =/= undefined. + do_hipe_compile(HipeModules) -> Count = length(HipeModules), io:format("~nHiPE compiling: |~s|~n |", diff --git a/src/rabbit_mirror_queue_master.erl b/src/rabbit_mirror_queue_master.erl index 057a4fad3116..e447e9de820a 100644 --- a/src/rabbit_mirror_queue_master.erl +++ b/src/rabbit_mirror_queue_master.erl @@ -43,7 +43,8 @@ backing_queue_state, seen_status, confirmed, - known_senders + known_senders, + wait_timeout }). -ifdef(use_specs). @@ -130,7 +131,8 @@ init_with_existing_bq(Q = #amqqueue{name = QName}, BQ, BQS) -> backing_queue_state = BQS, seen_status = dict:new(), confirmed = [], - known_senders = sets:new() }. + known_senders = sets:new(), + wait_timeout = rabbit_misc:get_env(rabbit, slave_wait_timeout, 15000) }. stop_mirroring(State = #state { coordinator = CPid, backing_queue = BQ, @@ -203,7 +205,7 @@ delete_and_terminate(Reason, State = #state { backing_queue = BQ, stop_all_slaves(Reason, State), State#state{backing_queue_state = BQ:delete_and_terminate(Reason, BQS)}. -stop_all_slaves(Reason, #state{name = QName, gm = GM}) -> +stop_all_slaves(Reason, #state{name = QName, gm = GM, wait_timeout = WT}) -> {ok, #amqqueue{slave_pids = SPids}} = rabbit_amqqueue:lookup(QName), PidsMRefs = [{Pid, erlang:monitor(process, Pid)} || Pid <- [GM | SPids]], ok = gm:broadcast(GM, {delete_and_terminate, Reason}), @@ -215,7 +217,7 @@ stop_all_slaves(Reason, #state{name = QName, gm = GM}) -> [receive {'DOWN', MRef, process, _Pid, _Info} -> ok - after 15000 -> + after WT -> rabbit_mirror_queue_misc:log_warning( QName, "Missing 'DOWN' message from ~p in node ~p~n", [Pid, node(Pid)]), diff --git a/src/rabbit_plugins.erl b/src/rabbit_plugins.erl index c7f5d501bf23..2f084ed28a4c 100644 --- a/src/rabbit_plugins.erl +++ b/src/rabbit_plugins.erl @@ -16,9 +16,11 @@ -module(rabbit_plugins). -include("rabbit.hrl"). +-include_lib("stdlib/include/zip.hrl"). -export([setup/0, active/0, read_enabled/1, list/1, list/2, dependencies/3]). -export([ensure/1]). +-export([extract_schemas/1]). %%---------------------------------------------------------------------------- @@ -46,6 +48,7 @@ ensure(FileJustChanged0) -> FileJustChanged -> Enabled = read_enabled(OurFile), Wanted = prepare_plugins(Enabled), + rabbit_config:prepare_and_use_config(), Current = active(), Start = Wanted -- Current, Stop = Current -- Wanted, @@ -79,6 +82,50 @@ setup() -> Enabled = read_enabled(EnabledFile), prepare_plugins(Enabled). +extract_schemas(SchemaDir) -> + application:load(rabbit), + {ok, EnabledFile} = application:get_env(rabbit, enabled_plugins_file), + Enabled = read_enabled(EnabledFile), + + {ok, PluginsDistDir} = application:get_env(rabbit, plugins_dir), + + AllPlugins = list(PluginsDistDir), + Wanted = dependencies(false, Enabled, AllPlugins), + WantedPlugins = lookup_plugins(Wanted, AllPlugins), + [ extract_schema(Plugin, SchemaDir) || Plugin <- WantedPlugins ], + application:unload(rabbit), + ok. + +extract_schema(#plugin{type = ez, location = Location}, SchemaDir) -> + {ok, Files} = zip:extract(Location, + [memory, {file_filter, + fun(#zip_file{name = Name}) -> + string:str(Name, "priv/schema") > 0 + end}]), + lists:foreach( + fun({FileName, Content}) -> + ok = file:write_file(filename:join([SchemaDir, + filename:basename(FileName)]), + Content) + end, + Files), + ok; +extract_schema(#plugin{type = dir, location = Location}, SchemaDir) -> + PluginSchema = filename:join([Location, + "priv", + "schema"]), + case rabbit_file:is_dir(PluginSchema) of + false -> ok; + true -> + PluginSchemaFiles = + [ filename:join(PluginSchema, FileName) + || FileName <- rabbit_file:wildcard(".*\\.schema", + PluginSchema) ], + [ file:copy(SchemaFile, SchemaDir) + || SchemaFile <- PluginSchemaFiles ] + end. + + %% @doc Lists the plugins which are currently running. active() -> {ok, ExpandDir} = application:get_env(rabbit, plugins_expand_dir), diff --git a/src/rabbit_plugins_main.erl b/src/rabbit_plugins_main.erl index 4aeed4826c17..e248989a7a90 100644 --- a/src/rabbit_plugins_main.erl +++ b/src/rabbit_plugins_main.erl @@ -173,7 +173,7 @@ format_plugins(Node, Pattern, Opts, #cli{all = All, EnabledImplicitly = Implicit -- Enabled, {StatusMsg, Running} = - case rabbit_cli:rpc_call(Node, rabbit_plugins, active, []) of + case rabbit_misc:rpc_call(Node, rabbit_plugins, active, []) of {badrpc, _} -> {"[failed to contact ~s - status not shown]", []}; Active -> {"* = running on ~s", Active} end, @@ -279,7 +279,7 @@ sync(Node, ForceOnline, #cli{file = File}) -> rpc_call(Node, Online, Mod, Fun, Args) -> io:format("~nApplying plugin configuration to ~s...", [Node]), - case rabbit_cli:rpc_call(Node, Mod, Fun, Args) of + case rabbit_misc:rpc_call(Node, Mod, Fun, Args) of {ok, [], []} -> io:format(" nothing to do.~n", []); {ok, Start, []} -> diff --git a/src/rabbit_prelaunch.erl b/src/rabbit_prelaunch.erl index 5ecdd75acc42..3f83a153eaae 100644 --- a/src/rabbit_prelaunch.erl +++ b/src/rabbit_prelaunch.erl @@ -83,7 +83,7 @@ dist_port_set_check() -> false -> ok; File -> - case file:consult(File ++ ".config") of + case get_config(File) of {ok, [Config]} -> Kernel = pget(kernel, Config, []), case {pget(inet_dist_listen_min, Kernel, none), @@ -98,6 +98,16 @@ dist_port_set_check() -> end end. +get_config(File) -> + case rabbit_file:is_file(File ++ ".config") of + true -> file:consult(File ++ ".config"); + false -> + case rabbit_config:get_advanced_config() of + none -> {error, enoent}; + FileName -> file:consult(FileName) + end + end. + dist_port_range_check() -> case os:getenv("RABBITMQ_DIST_PORT") of false -> ok; diff --git a/src/rabbit_types.erl b/src/rabbit_types.erl deleted file mode 100644 index c45c1f6d5d90..000000000000 --- a/src/rabbit_types.erl +++ /dev/null @@ -1,188 +0,0 @@ -%% The contents of this file are subject to the Mozilla Public License -%% Version 1.1 (the "License"); you may not use this file except in -%% compliance with the License. You may obtain a copy of the License -%% at http://www.mozilla.org/MPL/ -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and -%% limitations under the License. -%% -%% The Original Code is RabbitMQ. -%% -%% The Initial Developer of the Original Code is GoPivotal, Inc. -%% Copyright (c) 2007-2016 Pivotal Software, Inc. All rights reserved. -%% - --module(rabbit_types). - --include("rabbit.hrl"). - --ifdef(use_specs). - --export_type([maybe/1, info/0, infos/0, info_key/0, info_keys/0, - message/0, msg_id/0, basic_message/0, - delivery/0, content/0, decoded_content/0, undecoded_content/0, - unencoded_content/0, encoded_content/0, message_properties/0, - vhost/0, ctag/0, amqp_error/0, r/1, r2/2, r3/3, listener/0, - binding/0, binding_source/0, binding_destination/0, - amqqueue/0, exchange/0, - connection/0, connection_name/0, tracked_connection/0, - protocol/0, auth_user/0, user/0, internal_user/0, - username/0, password/0, password_hash/0, - ok/1, error/1, ok_or_error/1, ok_or_error2/2, ok_pid_or_error/0, - channel_exit/0, connection_exit/0, mfargs/0, proc_name/0, - proc_type_and_name/0, timestamp/0]). - --type(maybe(T) :: T | 'none'). --type(timestamp() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}). --type(vhost() :: binary()). --type(ctag() :: binary()). - -%% TODO: make this more precise by tying specific class_ids to -%% specific properties --type(undecoded_content() :: - #content{class_id :: rabbit_framing:amqp_class_id(), - properties :: 'none', - properties_bin :: binary(), - payload_fragments_rev :: [binary()]} | - #content{class_id :: rabbit_framing:amqp_class_id(), - properties :: rabbit_framing:amqp_property_record(), - properties_bin :: 'none', - payload_fragments_rev :: [binary()]}). --type(unencoded_content() :: undecoded_content()). --type(decoded_content() :: - #content{class_id :: rabbit_framing:amqp_class_id(), - properties :: rabbit_framing:amqp_property_record(), - properties_bin :: maybe(binary()), - payload_fragments_rev :: [binary()]}). --type(encoded_content() :: - #content{class_id :: rabbit_framing:amqp_class_id(), - properties :: maybe(rabbit_framing:amqp_property_record()), - properties_bin :: binary(), - payload_fragments_rev :: [binary()]}). --type(content() :: undecoded_content() | decoded_content()). --type(msg_id() :: rabbit_guid:guid()). --type(basic_message() :: - #basic_message{exchange_name :: rabbit_exchange:name(), - routing_keys :: [rabbit_router:routing_key()], - content :: content(), - id :: msg_id(), - is_persistent :: boolean()}). --type(message() :: basic_message()). --type(delivery() :: - #delivery{mandatory :: boolean(), - sender :: pid(), - message :: message()}). --type(message_properties() :: - #message_properties{expiry :: pos_integer() | 'undefined', - needs_confirming :: boolean()}). - --type(info_key() :: atom()). --type(info_keys() :: [info_key()]). - --type(info() :: {info_key(), any()}). --type(infos() :: [info()]). - --type(amqp_error() :: - #amqp_error{name :: rabbit_framing:amqp_exception(), - explanation :: string(), - method :: rabbit_framing:amqp_method_name()}). - --type(r(Kind) :: - r2(vhost(), Kind)). --type(r2(VirtualHost, Kind) :: - r3(VirtualHost, Kind, rabbit_misc:resource_name())). --type(r3(VirtualHost, Kind, Name) :: - #resource{virtual_host :: VirtualHost, - kind :: Kind, - name :: Name}). - --type(listener() :: - #listener{node :: node(), - protocol :: atom(), - host :: rabbit_networking:hostname(), - port :: rabbit_networking:ip_port()}). - --type(binding_source() :: rabbit_exchange:name()). --type(binding_destination() :: rabbit_amqqueue:name() | rabbit_exchange:name()). - --type(binding() :: - #binding{source :: rabbit_exchange:name(), - destination :: binding_destination(), - key :: rabbit_binding:key(), - args :: rabbit_framing:amqp_table()}). - --type(amqqueue() :: - #amqqueue{name :: rabbit_amqqueue:name(), - durable :: boolean(), - auto_delete :: boolean(), - exclusive_owner :: rabbit_types:maybe(pid()), - arguments :: rabbit_framing:amqp_table(), - pid :: rabbit_types:maybe(pid()), - slave_pids :: [pid()]}). - --type(exchange() :: - #exchange{name :: rabbit_exchange:name(), - type :: rabbit_exchange:type(), - durable :: boolean(), - auto_delete :: boolean(), - arguments :: rabbit_framing:amqp_table()}). - --type(connection_name() :: binary()). - -%% used e.g. by rabbit_networking --type(connection() :: pid()). - -%% used e.g. by rabbit_connection_tracking --type(tracked_connection() :: - #tracked_connection{id :: {node(), connection_name()}, - node :: node(), - vhost :: vhost(), - name :: connection_name(), - pid :: pid(), - protocol :: protocol_name(), - peer_host :: rabbit_networking:hostname(), - peer_port :: rabbit_networking:ip_port(), - username :: username(), - connected_at :: integer()}). - -%% old AMQP 0-9-1-centric type, avoid when possible --type(protocol() :: rabbit_framing:protocol()). - --type(protocol_name() :: 'amqp0_8' | 'amqp0_9_1' | 'amqp1_0' | 'mqtt' | 'stomp' | any()). - --type(auth_user() :: - #auth_user{username :: username(), - tags :: [atom()], - impl :: any()}). - --type(user() :: - #user{username :: username(), - tags :: [atom()], - authz_backends :: [{atom(), any()}]}). - --type(internal_user() :: - #internal_user{username :: username(), - password_hash :: password_hash(), - tags :: [atom()]}). - --type(username() :: binary()). --type(password() :: binary()). --type(password_hash() :: binary()). - --type(ok(A) :: {'ok', A}). --type(error(A) :: {'error', A}). --type(ok_or_error(A) :: 'ok' | error(A)). --type(ok_or_error2(A, B) :: ok(A) | error(B)). --type(ok_pid_or_error() :: ok_or_error2(pid(), any())). - --type(channel_exit() :: no_return()). --type(connection_exit() :: no_return()). - --type(mfargs() :: {atom(), atom(), [any()]}). - --type(proc_name() :: term()). --type(proc_type_and_name() :: {atom(), proc_name()}). - --endif. % use_specs