Exemple #1
0
            /// <summary>
            /// <para>
            /// <b>INTERNAL USE ONLY:</b> Returns a <see cref="HiveBus"/> connected to the hive
            /// <b>neon</b> virtual host.  This property is intended for use only by neonHIVE
            /// tools, services and containers.
            /// </para>
            /// <note>
            /// <b>WARNING:</b> The <see cref="HiveBus"/> instance returned should <b>NEVER BE DISPOSED</b>.
            /// </note>
            /// </summary>
            /// <param name="useBootstrap">
            /// Optionally specifies that the settings returned should directly
            /// reference to the HiveMQ cluster nodes rather than routing traffic
            /// through the <b>private</b> traffic manager.  This is used internally
            /// to resolve chicken-and-the-egg dilemmas for the traffic manager and
            /// proxy implementations that rely on HiveMQ messaging.
            /// </param>
            public HiveBus NeonHiveBus(bool useBootstrap = false)
            {
                var bus = neonHiveBus;

                if (bus != null)
                {
                    return(bus);
                }

                lock (syncLock)
                {
                    if (neonHiveBus != null)
                    {
                        return(neonHiveBus);
                    }

                    if (!useBootstrap)
                    {
                        return(neonHiveBus = parent.GetNeonSettings(useBootstrap: false).ConnectHiveBus());
                    }

                    // Remember the bootstrap settings and then start a task that
                    // periodically polls Consul for settings changes to raise the
                    // [HiveMQBootstrapChanged] event when changes happen.

                    bootstrapSettings       = parent.GetNeonSettings(useBootstrap);
                    neonHiveBus             = bootstrapSettings.ConnectHiveBus();
                    bootstrapChangeDetector = Task.Run(() => BootstrapChangeDetector());

                    return(neonHiveBus);
                }
            }
Exemple #2
0
        /// <summary>
        /// Returns a RabbitMQ cluster connection using specified settings and credentials
        /// loaded from a Docker secret.  This works only for Docker services where the
        /// Docker secret was mounted into the service containers.
        /// </summary>
        /// <param name="settings">The Couchbase settings.</param>
        /// <param name="secretName">The local name of the Docker secret holding the credentials.</param>
        /// <param name="dispatchConsumersAsync">Optionally enables <c>async</c> message consumers.  This defaults to <c>false</c>.</param>
        /// <returns>The RabbitMQ <see cref="IConnection"/>.</returns>
        /// <remarks>
        /// The credentials must be formatted as JSON as serialized by the <see cref="Credentials"/>
        /// class.
        /// </remarks>
        public static IConnection ConnectUsingSecret(this HiveMQSettings settings, string secretName, bool dispatchConsumersAsync = false)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(settings.VirtualHost));
            Covenant.Requires <ArgumentNullException>(settings.AmqpHosts != null && settings.AmqpHosts.Count > 0);
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(secretName));

            var credentials = NeonHelper.JsonDeserialize <Credentials>(HiveHelper.GetSecret(secretName), dispatchConsumersAsync);

            return(new RabbitMQConnection(settings.ConnectRabbitMQ(credentials)));
        }
Exemple #3
0
            /// <summary>
            /// Polls Consul for changes to the <b>neon</b> virtual host HiveMQ
            /// bootstrap settings and raises the <see cref="HiveMQBootstrapChanged"/>
            /// when this happens.
            /// </summary>
            /// <returns>The tracking <see cref="Task"/>.</returns>
            private async Task BootstrapChangeDetector()
            {
                var pollInterval = TimeSpan.FromSeconds(120);

                // Delay for a random period of time between [0..pollInterval].  This
                // will help prevent Consul traffic spikes when services are started
                // at the same time.

                await Task.Delay(NeonHelper.RandTimespan(pollInterval));

                // This will spin forever once started when a NeonHiveBus using bootstrap
                // settings is created above.  This polls Consul for changes to the [neon]
                // HiveMQ virtual host settings stored in Consul.  We'll be performing two
                // Consul lookups for each poll (one to get the [neon] vhost settings and
                // the other to obtain the bootstrap settings.
                //
                // When a settings change is detected, we'll first ensure that we've
                // establisted a new [neonHiveBus] connection using the new settings and
                // then we'll raise the change event.

                while (true)
                {
                    try
                    {
                        var latestBootstrapSettngs = parent.GetNeonSettings(useBootstrap: true);

                        if (!NeonHelper.JsonEquals(bootstrapSettings, latestBootstrapSettngs))
                        {
                            // The latest bootstrap settings don't match what we used to
                            // connect the current [bus].

                            lock (syncLock)
                            {
                                bootstrapSettings = latestBootstrapSettngs;
                                neonHiveBus       = bootstrapSettings.ConnectHiveBus();
                            }

                            var handler = bootstrapChangedEvent;

                            handler?.Invoke(this, latestBootstrapSettngs);
                        }
                    }
                    catch (Exception e)
                    {
                        log.LogError(e);
                    }

                    await Task.Delay(pollInterval);
                }
            }
Exemple #4
0
        /// <summary>
        /// <para>
        /// <b>Internal use by neonHIVE services only:</b> Generates a <see cref="HiveMQSettings"/> instance
        /// that directly references the HiveMQ nodes and then persists this to Consul as
        /// <see cref="HiveGlobals.HiveMQSettingsBootstrap"/>.
        /// </para>
        /// <note>
        /// The persisted settings do not include any credentials.
        /// </note>
        /// </summary>
        public void SaveBootstrapSettings()
        {
            var settings = new HiveMQSettings()
            {
                AmqpPort    = HiveHostPorts.HiveMQAMQP,
                AdminPort   = HiveHostPorts.HiveMQManagement,
                TlsEnabled  = false,
                Username    = null,
                Password    = null,
                VirtualHost = HiveConst.HiveMQNeonVHost
            };

            foreach (var node in hive.Definition.SortedNodes.Where(n => n.Labels.HiveMQ))
            {
                settings.AmqpHosts.Add($"{node.Name}.{hive.Definition.Hostnames.HiveMQ}");
            }

            foreach (var node in hive.Definition.SortedNodes.Where(n => n.Labels.HiveMQManager))
            {
                settings.AdminHosts.Add($"{node.Name}.{hive.Definition.Hostnames.HiveMQ}");
            }

            hive.Globals.Set(HiveGlobals.HiveMQSettingsBootstrap, settings);
        }
Exemple #5
0
        /// <summary>
        /// Actually starts RabbitMQ within the initialization <see cref="Action"/>.  You'll
        /// generally want to use <see cref="Start(string, string, List{string}, string, string, bool)"/>
        /// but this method is used internally or for special situations.
        /// </summary>
        /// <param name="image">Optionally specifies the RabbitMQ container image (defaults to <b>nhive/rabbitmq-test:latest</b>).</param>
        /// <param name="name">Optionally specifies the RabbitMQ container name (defaults to <c>rmq-test</c>).</param>
        /// <param name="env">Optional environment variables to be passed to the RabbitMQ container, formatted as <b>NAME=VALUE</b> or just <b>NAME</b>.</param>
        /// <param name="username">Optional RabbitMQ username (defaults to <b>Administrator</b>).</param>
        /// <param name="password">Optional RabbitMQ password (defaults to <b>password</b>).</param>
        /// <param name="precompile">
        /// Optionally configure RabbitMQ precompiling.  This may improve RabbitMQ performance by
        /// 20-50% at the cost of an additional 30-45 seconds of startup time.  This can be
        /// enabled for performance oriented unit tests.  This defaults to <c>false</c>.
        /// </param>
        /// <returns>
        /// <c>true</c> if the fixture wasn't previously initialized and
        /// this method call initialized it or <c>false</c> if the fixture
        /// was already initialized.
        /// </returns>
        public void StartInAction(
            string image      = "nhive/rabbitmq-test:latest",
            string name       = "rmq-test",
            List <string> env = null,
            string username   = "******",
            string password   = "******",
            bool precompile   = false)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(image));

            base.CheckWithinAction();

            if (IsInitialized)
            {
                return;
            }

            if (precompile)
            {
                if (env == null)
                {
                    env = new List <string>();
                }

                env.Add("RABBITMQ_HIPE_COMPILE=1");
            }

            RunContainer(
                name,
                image,
                new string[] {
                "--detach",
                "--mount", "type=volume,target=/var/lib/rabbitmq",
                "--publish", $"{NetworkPorts.AMQP}:{NetworkPorts.AMQP}",
                "--publish", $"{NetworkPorts.RabbitMQAdmin}:{NetworkPorts.RabbitMQAdmin}",
                "--env", "DEBUG=false"
            },
                env: env);

            var hosts = new string[] { "127.0.0.1" };

            Settings = new HiveMQSettings()
            {
                AdminHosts = hosts.ToList(),
                AmqpHosts  = hosts.ToList(),
                Username   = username,
                Password   = password,
                NeonLog    = false
            };

            Covenant.Assert(Settings.IsValid);

            // Wait for container to warm up by ensuring that we can connect admin and AMQP clients.

            NeonHelper.WaitFor(
                () =>
            {
                try
                {
                    using (Settings.ConnectRabbitMQ(Username, Password, dispatchConsumersAsync: false))
                    {
                        return(true);
                    }
                }
                catch
                {
                    return(false);
                }
            },
                timeout: TimeSpan.FromSeconds(precompile ? 120 : 30),   // We need to wait longer then precompiling (it takes an additional 45-60 seconds to compile).
                pollTime: TimeSpan.FromSeconds(0.5));

            NeonHelper.WaitFor(
                () =>
            {
                try
                {
                    using (var manager = Settings.ConnectManager(Username, Password))
                    {
                        // Ensure that the manager can actually process requests.

                        return(!manager.GetVHostsAsync().Result.IsEmpty());
                    }
                }
                catch
                {
                    return(false);
                }
            },
                timeout: TimeSpan.FromSeconds(30),
                pollTime: TimeSpan.FromSeconds(0.5));
        }