Skip to content

Commit 47fa1f5

Browse files
bollhalsmichaelklishin
authored andcommitted
simplify code around RabbitMQCtl
1 parent af8c072 commit 47fa1f5

File tree

1 file changed

+71
-165
lines changed

1 file changed

+71
-165
lines changed

projects/Unit/RabbitMQCtl.cs

Lines changed: 71 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -32,100 +32,83 @@
3232
#pragma warning disable 2002
3333

3434
using System;
35-
using System.Collections.Generic;
3635
using System.Diagnostics;
3736
using System.IO;
38-
using System.Linq;
3937
using System.Text;
4038
using System.Text.RegularExpressions;
4139
using System.Threading;
4240

4341
namespace RabbitMQ.Client.Unit
4442
{
43+
#nullable enable
4544
public static class RabbitMQCtl
4645
{
47-
//
48-
// Shelling Out
49-
//
50-
private static Process ExecRabbitMQCtl(string args)
46+
private static readonly Func<string, Process> s_invokeRabbitMqCtl = GetRabbitMqCtlInvokeAction();
47+
48+
private static Func<string, Process> GetRabbitMqCtlInvokeAction()
5149
{
52-
// Allow the path to the rabbitmqctl.bat to be set per machine
53-
string envVariable = Environment.GetEnvironmentVariable("RABBITMQ_RABBITMQCTL_PATH");
54-
string rabbitmqctlPath;
50+
string precomputedArguments;
51+
string? envVariable = Environment.GetEnvironmentVariable("RABBITMQ_RABBITMQCTL_PATH");
5552

56-
if (envVariable != null)
53+
if (envVariable is not null)
5754
{
58-
var regex = new Regex(@"^DOCKER:(?<dockerMachine>.+)$");
59-
Match match = regex.Match(envVariable);
60-
61-
if (match.Success)
55+
const string DockerPrefix = "DOCKER:";
56+
if (envVariable.StartsWith(DockerPrefix))
6257
{
63-
return ExecRabbitMQCtlUsingDocker(args, match.Groups["dockerMachine"].Value);
64-
}
65-
else
66-
{
67-
rabbitmqctlPath = envVariable;
58+
// Call docker
59+
precomputedArguments = $"exec {envVariable.Substring(DockerPrefix.Length)} rabbitmqctl ";
60+
return args => CreateProcess("docker", precomputedArguments + args);
6861
}
62+
63+
// call the path from the env var
64+
return args => CreateProcess(envVariable, args);
65+
}
66+
67+
// Try default
68+
string umbrellaRabbitmqctlPath;
69+
string providedRabbitmqctlPath;
70+
71+
if (IsRunningOnMonoOrDotNetCore())
72+
{
73+
umbrellaRabbitmqctlPath = "../../../../../../rabbit/scripts/rabbitmqctl";
74+
providedRabbitmqctlPath = "rabbitmqctl";
6975
}
7076
else
7177
{
72-
// provided by the umbrella
73-
string umbrellaRabbitmqctlPath;
74-
// provided in PATH by a RabbitMQ installation
75-
string providedRabbitmqctlPath;
78+
umbrellaRabbitmqctlPath = @"..\..\..\..\..\..\rabbit\scripts\rabbitmqctl.bat";
79+
providedRabbitmqctlPath = "rabbitmqctl.bat";
80+
}
7681

77-
if (IsRunningOnMonoOrDotNetCore())
78-
{
79-
umbrellaRabbitmqctlPath = "../../../../../../rabbit/scripts/rabbitmqctl";
80-
providedRabbitmqctlPath = "rabbitmqctl";
81-
}
82-
else
83-
{
84-
umbrellaRabbitmqctlPath = @"..\..\..\..\..\..\rabbit\scripts\rabbitmqctl.bat";
85-
providedRabbitmqctlPath = "rabbitmqctl.bat";
86-
}
82+
string path = File.Exists(umbrellaRabbitmqctlPath) ? umbrellaRabbitmqctlPath : providedRabbitmqctlPath;
8783

88-
if (File.Exists(umbrellaRabbitmqctlPath))
89-
{
90-
rabbitmqctlPath = umbrellaRabbitmqctlPath;
91-
}
92-
else
93-
{
94-
rabbitmqctlPath = providedRabbitmqctlPath;
95-
}
84+
if (IsRunningOnMonoOrDotNetCore())
85+
{
86+
return args => CreateProcess(path, args);
9687
}
9788

98-
return ExecCommand(rabbitmqctlPath, args);
89+
precomputedArguments = $"/c \"\"{path}\" ";
90+
return args => CreateProcess("cmd.exe", precomputedArguments + args);
9991
}
10092

101-
private static Process ExecRabbitMQCtlUsingDocker(string args, string dockerMachineName)
93+
//
94+
// Shelling Out
95+
//
96+
private static string ExecRabbitMQCtl(string args)
10297
{
103-
var proc = new Process
104-
{
105-
StartInfo =
106-
{
107-
CreateNoWindow = true,
108-
UseShellExecute = false
109-
}
110-
};
111-
11298
try
11399
{
114-
proc.StartInfo.FileName = "docker";
115-
proc.StartInfo.Arguments = $"exec {dockerMachineName} rabbitmqctl {args}";
116-
proc.StartInfo.RedirectStandardError = true;
117-
proc.StartInfo.RedirectStandardOutput = true;
100+
using var process = s_invokeRabbitMqCtl(args);
101+
process.Start();
102+
process.WaitForExit();
103+
string stderr = process.StandardError.ReadToEnd();
104+
string stdout = process.StandardOutput.ReadToEnd();
118105

119-
proc.Start();
120-
string stderr = proc.StandardError.ReadToEnd();
121-
proc.WaitForExit();
122-
if (stderr.Length > 0 || proc.ExitCode > 0)
106+
if (stderr.Length > 0 || process.ExitCode > 0)
123107
{
124-
string stdout = proc.StandardOutput.ReadToEnd();
125108
ReportExecFailure("rabbitmqctl", args, $"{stderr}\n{stdout}");
126109
}
127110

128-
return proc;
111+
return stdout;
129112
}
130113
catch (Exception e)
131114
{
@@ -134,60 +117,21 @@ private static Process ExecRabbitMQCtlUsingDocker(string args, string dockerMach
134117
}
135118
}
136119

137-
private static Process ExecCommand(string command, string args)
138-
{
139-
return ExecCommand(command, args, null);
140-
}
141-
142-
private static Process ExecCommand(string ctl, string args, string changeDirTo)
120+
private static Process CreateProcess(string cmd, string arguments, string? workDirectory = null)
143121
{
144-
var proc = new Process
122+
return new Process
145123
{
146124
StartInfo =
147125
{
148126
CreateNoWindow = true,
149-
UseShellExecute = false
127+
UseShellExecute = false,
128+
RedirectStandardError = true,
129+
RedirectStandardOutput = true,
130+
FileName = cmd,
131+
Arguments = arguments,
132+
WorkingDirectory = workDirectory
150133
}
151134
};
152-
if (changeDirTo != null)
153-
{
154-
proc.StartInfo.WorkingDirectory = changeDirTo;
155-
}
156-
157-
string cmd;
158-
if (IsRunningOnMonoOrDotNetCore())
159-
{
160-
cmd = ctl;
161-
}
162-
else
163-
{
164-
cmd = "cmd.exe";
165-
args = $"/c \"\"{ctl}\" {args}\"";
166-
}
167-
168-
try
169-
{
170-
proc.StartInfo.FileName = cmd;
171-
proc.StartInfo.Arguments = args;
172-
proc.StartInfo.RedirectStandardError = true;
173-
proc.StartInfo.RedirectStandardOutput = true;
174-
175-
proc.Start();
176-
string stderr = proc.StandardError.ReadToEnd();
177-
proc.WaitForExit();
178-
if (stderr.Length > 0 || proc.ExitCode > 0)
179-
{
180-
string stdout = proc.StandardOutput.ReadToEnd();
181-
ReportExecFailure(cmd, args, $"{stderr}\n{stdout}");
182-
}
183-
184-
return proc;
185-
}
186-
catch (Exception e)
187-
{
188-
ReportExecFailure(cmd, args, e.Message);
189-
throw;
190-
}
191135
}
192136

193137
private static void ReportExecFailure(string cmd, string args, string msg)
@@ -207,7 +151,6 @@ private static bool IsRunningOnMonoOrDotNetCore()
207151
//
208152
// Flow Control
209153
//
210-
211154
public static void Block(IConnection conn, Encoding encoding)
212155
{
213156
ExecRabbitMQCtl("set_vm_memory_high_watermark 0.000000001");
@@ -222,88 +165,51 @@ public static void Publish(IConnection conn, Encoding encoding)
222165
ch.BasicPublish("amq.fanout", "", encoding.GetBytes("message"));
223166
}
224167

225-
226168
public static void Unblock()
227169
{
228170
ExecRabbitMQCtl("set_vm_memory_high_watermark 0.4");
229171
}
230172

231-
private static readonly Regex s_getConnectionProperties = new Regex(@"(?<pid>.*)\s\[.*\""connection_name\"",\""(?<connection_name>.*?)\"".*\]", RegexOptions.Compiled);
232-
public class ConnectionInfo
173+
public static void CloseConnection(IConnection conn)
233174
{
234-
public string Pid
235-
{
236-
get; set;
237-
}
238-
239-
public string Name
240-
{
241-
get; set;
242-
}
243-
244-
public ConnectionInfo(string pid, string name)
245-
{
246-
Pid = pid;
247-
Name = name;
248-
}
249-
250-
public override string ToString()
251-
{
252-
return $"pid = {Pid}, name: {Name}";
253-
}
175+
CloseConnection(GetConnectionPid(conn.ClientProvidedName));
254176
}
255177

256-
public static List<ConnectionInfo> ListConnections()
178+
private static readonly Regex s_getConnectionProperties = new Regex(@"^(?<pid><[^>]*>)\s\[.*""connection_name"",""(?<connection_name>[^""]*)"".*\]$", RegexOptions.Multiline | RegexOptions.Compiled);
179+
private static string GetConnectionPid(string connectionName)
257180
{
258-
Process proc = ExecRabbitMQCtl("list_connections --silent pid client_properties");
259-
string stdout = proc.StandardOutput.ReadToEnd();
181+
string stdout = ExecRabbitMQCtl("list_connections --silent pid client_properties");
260182

261-
try
183+
var match = s_getConnectionProperties.Match(stdout);
184+
while (match.Success)
262185
{
263-
// {Environment.NewLine} is not sufficient
264-
var matches = s_getConnectionProperties.Matches(stdout);
265-
if (matches.Count > 0)
186+
if (match.Groups["connection_name"].Value == connectionName)
266187
{
267-
var list = new List<ConnectionInfo>(matches.Count);
268-
for (int i = 0; i < matches.Count; i++)
269-
{
270-
var s = matches[i];
271-
Debug.Assert(s.Success, "Unable to parse connection list.");
272-
Debug.Assert(s.Groups.ContainsKey("pid"), "Unable to parse pid from {stdout}");
273-
Debug.Assert(s.Groups.ContainsKey("connection_name"), "Unable to parse connection_name from {stdout}");
274-
list.Add(new ConnectionInfo(s.Groups["pid"].Value, s.Groups["connection_name"].Value));
275-
}
276-
277-
return list;
188+
return match.Groups["pid"].Value;
278189
}
279190

280-
return null;
281-
}
282-
catch (Exception)
283-
{
284-
Console.WriteLine($"Bad response from rabbitmqctl list_connections --silent pid client_properties{Environment.NewLine}{stdout}");
285-
throw;
191+
match = match.NextMatch();
286192
}
193+
194+
throw new Exception($"No connection found with name: {connectionName}");
287195
}
288196

289-
public static void CloseConnection(IConnection conn)
197+
private static void CloseConnection(string pid)
290198
{
291-
ConnectionInfo ci = ListConnections().First(x => conn.ClientProvidedName == x.Name);
292-
CloseConnection(ci.Pid);
199+
ExecRabbitMQCtl($"close_connection \"{pid}\" \"Closed via rabbitmqctl\"");
293200
}
294201

295202
public static void CloseAllConnections()
296203
{
297-
List<ConnectionInfo> cs = ListConnections();
298-
foreach (ConnectionInfo c in cs)
204+
foreach (var pid in EnumerateConnectionsPid())
299205
{
300-
CloseConnection(c.Pid);
206+
CloseConnection(pid);
301207
}
302208
}
303209

304-
public static void CloseConnection(string pid)
210+
private static string[] EnumerateConnectionsPid()
305211
{
306-
ExecRabbitMQCtl($"close_connection \"{pid}\" \"Closed via rabbitmqctl\"");
212+
return ExecRabbitMQCtl("list_connections --silent pid").Split('\n', StringSplitOptions.RemoveEmptyEntries);
307213
}
308214

309215
public static void RestartRabbitMQ()

0 commit comments

Comments
 (0)