Skip to content

Commit 5bfa74b

Browse files
committed
Extensive update
Add service details. Extra Go formatting. Use null delimiters. Add tasks under service.
1 parent d83c017 commit 5bfa74b

File tree

8 files changed

+602
-44
lines changed

8 files changed

+602
-44
lines changed

DockerClient.cs

Lines changed: 203 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Diagnostics;
4+
using System.Linq;
45
using System.Text;
56

67
namespace StackDeployCheck
@@ -16,14 +17,32 @@ public StackState GetCurrentState(string stackName)
1617
};
1718

1819
GetStackTasks(stackName);
20+
GetStackServices(stackName);
21+
22+
foreach (var service in currentStackState.Services)
23+
{
24+
GetServiceStates(service.Name);
25+
}
26+
1927

2028
return currentStackState;
2129
}
2230

23-
public void GetStackTasks(string stackName)
31+
private void GetServiceStates(string serviceName)
2432
{
25-
var formatString = "{{.ID}} {{.Name}} {{.Image}} {{.Node}} {{.DesiredState}} [{{.CurrentState}}] [{{.Error}}]";
26-
ProcessStartInfo processStartInfo = new ProcessStartInfo("docker", $"stack ps {stackName} --no-trunc --format \"{formatString}\"")
33+
var formatString = "{{.Spec.Name}} " +
34+
"{{if not .UpdateStatus}}" +
35+
"NotUpdated" +
36+
"{{else}}" +
37+
"{{if eq .UpdateStatus.State \\\"rollback_started\\\"}}" +
38+
"RollbackStarted" +
39+
"{{else if eq .UpdateStatus.State \\\"rollback_completed\\\"}}" +
40+
"RollbackCompleted" +
41+
"{{else}}" +
42+
"{{.UpdateStatus.State}}" +
43+
"{{end}}" +
44+
"{{end}}";
45+
ProcessStartInfo processStartInfo = new ProcessStartInfo("docker", $"service inspect {serviceName} --format \"{formatString}\"")
2746
{
2847
RedirectStandardError = true,
2948
RedirectStandardOutput = true,
@@ -37,7 +56,7 @@ public void GetStackTasks(string stackName)
3756
EnableRaisingEvents = true
3857
})
3958
{
40-
process.OutputDataReceived += Process_OutputDataReceived;
59+
process.OutputDataReceived += Process_OutputServiceStateDataReceived;
4160
process.ErrorDataReceived += Process_ErrorDataReceived;
4261
//process.Exited += Process_Exited;
4362
process.Start();
@@ -48,53 +67,139 @@ public void GetStackTasks(string stackName)
4867
}
4968
}
5069

70+
private void Process_OutputServiceStateDataReceived(object sender, DataReceivedEventArgs e)
71+
{
72+
if (e.Data == String.Empty
73+
|| e.Data == null)
74+
{
75+
return;
76+
}
77+
78+
string currentLine = e.Data;
5179

52-
private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
80+
if (currentLine.StartsWith("Status: Template parsing error", StringComparison.InvariantCultureIgnoreCase))
81+
{
82+
return;
83+
}
84+
85+
var serviceStateElements = currentLine.Split(" ");
86+
if (serviceStateElements.Length < 2)
87+
{
88+
return;
89+
}
90+
91+
// "{{.Spec.Name}} {{.UpdateStatus.State}} {{.UpdateStatus.CompletedAt}} {{.UpdateStatus.Message}}";
92+
93+
string name = serviceStateElements[0];
94+
string state = serviceStateElements[1];
95+
96+
if (state.Equals("completed_rollback"))
97+
{
98+
state = "RolledBack";
99+
}
100+
101+
var service = currentStackState.Services.FirstOrDefault<DockerService>(ds => ds.Name == name);
102+
if (service != null)
103+
{
104+
UpdateState updateState;
105+
if (Enum.TryParse<UpdateState>(state, true, out updateState))
106+
{
107+
service.UpdateState = updateState;
108+
}
109+
else
110+
{
111+
Console.Error.WriteLine($"ERROR: unable to parse service update state {state}");
112+
service.UpdateState = UpdateState.Unknown;
113+
}
114+
}
115+
}
116+
117+
public void GetStackTasks(string stackName)
53118
{
54-
if (!String.IsNullOrEmpty(e.Data))
119+
var formatString = "{{.ID}}{{\\\"\\x00\\\"}}{{.Name}}{{\\\"\\x00\\\"}}{{.Image}}{{\\\"\\x00\\\"}}{{.Node}}{{\\\"\\x00\\\"}}{{.DesiredState}}{{\\\"\\x00\\\"}}{{.CurrentState}}{{\\\"\\x00\\\"}}{{.Error}}";
120+
ProcessStartInfo processStartInfo = new ProcessStartInfo("docker", $"stack ps {stackName} --no-trunc --format \"{formatString}\"")
121+
{
122+
RedirectStandardError = true,
123+
RedirectStandardOutput = true,
124+
RedirectStandardInput = true,
125+
UseShellExecute = false,
126+
CreateNoWindow = true
127+
};
128+
using (Process process = new Process()
129+
{
130+
StartInfo = processStartInfo,
131+
EnableRaisingEvents = true
132+
})
55133
{
56-
Console.Error.WriteLineAsync($"ERROR: {e.Data}");
134+
process.OutputDataReceived += Process_OutputTasksDataReceived;
135+
process.ErrorDataReceived += Process_ErrorDataReceived;
136+
//process.Exited += Process_Exited;
137+
process.Start();
138+
process.BeginOutputReadLine();
139+
process.BeginErrorReadLine();
140+
//process.WaitForExit(waitTimeout);
141+
process.WaitForExit();
57142
}
58143
}
59144

60-
private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
145+
private void Process_OutputTasksDataReceived(object sender, DataReceivedEventArgs e)
61146
{
62147
if (e.Data == String.Empty
63148
|| e.Data == null)
64149
{
65150
return;
66151
}
67-
//if (e.Data.StartsWith("ID "))
68-
//{
69-
// //headerLine = e.Data;
70-
// return;
71-
//}
72-
string currentLine = e.Data;
73152

74-
var currentStart = currentLine.IndexOf("[") + 1;
75-
var currentEnd = currentLine.IndexOf("]");
76-
var errorStart = currentLine.IndexOf("[", currentEnd) + 1;
77-
var errorEnd = currentLine.LastIndexOf("]");
153+
var taskElements = e.Data.Split('\x00');
154+
if (taskElements.Length < 7)
155+
{
156+
return;
157+
}
78158

79-
var taskElements = e.Data.Split(" ");
80159
var dockerTask = new DockerTask()
81160
{
82161
Id = taskElements[0],
83162
Name = taskElements[1],
84163
Image = taskElements[2],
85164
Node = taskElements[3],
86-
DesiredState = taskElements[4],
87-
CurrentState = currentLine.Substring(currentStart, currentEnd - currentStart),
88-
Error = currentLine.Substring(errorStart, errorEnd - errorStart)
165+
Error = taskElements[6]
89166
};
167+
dockerTask.SetDesiredState(taskElements[4]);
168+
dockerTask.SetCurrentState(taskElements[5]);
90169

91-
if (dockerTask.DesiredState == "Ready"
92-
|| dockerTask.DesiredState == "Running")
93-
{
94-
dockerTask.IsCurrent = true;
95-
}
96170
currentStackState.Tasks.Add(dockerTask);
97171

172+
//if (e.Data.StartsWith("ID "))
173+
//{
174+
// //headerLine = e.Data;
175+
// return;
176+
//}
177+
//string currentLine = e.Data;
178+
179+
//var currentStart = currentLine.IndexOf("[") + 1;
180+
//var currentEnd = currentLine.IndexOf("]");
181+
//var errorStart = currentLine.IndexOf("[", currentEnd) + 1;
182+
//var errorEnd = currentLine.LastIndexOf("]");
183+
184+
//var taskElements = e.Data.Split(" ");
185+
//if (taskElements.Length < 5)
186+
//{
187+
// return;
188+
//}
189+
190+
//var dockerTask = new DockerTask()
191+
//{
192+
// Id = taskElements[0],
193+
// Name = taskElements[1],
194+
// Image = taskElements[2],
195+
// Node = taskElements[3],
196+
// Error = currentLine.Substring(errorStart, errorEnd - errorStart)
197+
//};
198+
//dockerTask.SetDesiredState(taskElements[4]);
199+
//dockerTask.SetCurrentState(currentLine.Substring(currentStart, currentEnd - currentStart));
200+
201+
//currentStackState.Tasks.Add(dockerTask);
202+
98203
//Console.Error.WriteLineAsync($"OUT: {e.Data}");
99204
//Console.Out.WriteLine(dockerTask.Id);
100205
//Console.Out.WriteLine($"{dockerTask.IsCurrent} {dockerTask.Name}");
@@ -103,7 +208,77 @@ private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
103208
//Console.Out.WriteLine(dockerTask.DesiredState);
104209
//Console.Out.WriteLine(dockerTask.CurrentState);
105210
//Console.Out.WriteLine(dockerTask.Error);
106-
//Console.Out.WriteLine("");
211+
//Console.Out.WriteLine("--------------------------------------------------");
212+
}
213+
public void GetStackServices(string stackName)
214+
{
215+
var formatString = "{{.ID}}{{\\\"\\x00\\\"}}{{.Name}}{{\\\"\\x00\\\"}}{{.Mode}}{{\\\"\\x00\\\"}}{{.Replicas}}{{\\\"\\x00\\\"}}{{.Image}}{{\\\"\\x00\\\"}}{{.Ports}}";
216+
ProcessStartInfo processStartInfo = new ProcessStartInfo("docker", $"stack services {stackName} --format \"{formatString}\"")
217+
{
218+
RedirectStandardError = true,
219+
RedirectStandardOutput = true,
220+
RedirectStandardInput = true,
221+
UseShellExecute = false,
222+
CreateNoWindow = true
223+
};
224+
using (Process process = new Process()
225+
{
226+
StartInfo = processStartInfo,
227+
EnableRaisingEvents = true
228+
})
229+
{
230+
process.OutputDataReceived += Process_OutputServicesDataReceived;
231+
process.ErrorDataReceived += Process_ErrorDataReceived;
232+
//process.Exited += Process_Exited;
233+
process.Start();
234+
process.BeginOutputReadLine();
235+
process.BeginErrorReadLine();
236+
//process.WaitForExit(waitTimeout);
237+
process.WaitForExit();
238+
}
239+
240+
//
107241
}
242+
243+
private void Process_OutputServicesDataReceived(object sender, DataReceivedEventArgs e)
244+
{
245+
if (e.Data == String.Empty
246+
|| e.Data == null)
247+
{
248+
return;
249+
}
250+
string currentLine = e.Data;
251+
var serviceElements = currentLine.Split("\x00");
252+
if (serviceElements.Length < 6)
253+
{
254+
return;
255+
}
256+
257+
var dockerService = new DockerService()
258+
{
259+
Id = serviceElements[0],
260+
Name = serviceElements[1],
261+
Mode = serviceElements[2],
262+
Image = serviceElements[4],
263+
Ports = serviceElements[5]
264+
};
265+
dockerService.SetReplicas(serviceElements[3]);
266+
currentStackState.Services.Add(dockerService);
267+
}
268+
private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
269+
{
270+
if (!String.IsNullOrEmpty(e.Data))
271+
{
272+
// don't log if no tasks found
273+
if (e.Data.StartsWith("nothing found in stack", StringComparison.InvariantCultureIgnoreCase)
274+
|| e.Data.StartsWith("Status: Template parsing error: template: :1:30: executing", StringComparison.InvariantCultureIgnoreCase))
275+
{
276+
return;
277+
}
278+
Console.Out.WriteLineAsync($"ERROR FROM DOCKER: {e.Data}");
279+
}
280+
}
281+
282+
108283
}
109284
}

DockerService.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace StackDeployCheck
6+
{
7+
public class DockerService
8+
{
9+
public string Id { get; internal set; }
10+
public string Name { get; internal set; }
11+
public string Mode { get; internal set; }
12+
public string ReplicasDesc { get; internal set; }
13+
public int CurrentReplicas { get; internal set; }
14+
public int DesiredReplicas { get; internal set; }
15+
public string Image { get; internal set; }
16+
public string Ports { get; internal set; }
17+
public UpdateState UpdateState { get; internal set; }
18+
public string LastUpdated { get; internal set; }
19+
public string UpdateMessage { get; internal set; }
20+
21+
public int TotalTasks { get; internal set; }
22+
public int StableTasks { get; internal set; }
23+
public int CompleteTasks { get; internal set; }
24+
25+
public DockerService()
26+
{
27+
UpdateState = UpdateState.NotUpdated;
28+
}
29+
30+
public void SetReplicas(string replicasString)
31+
{
32+
ReplicasDesc = replicasString;
33+
var parts = replicasString.Split(@"/");
34+
CurrentReplicas = 0;
35+
DesiredReplicas = 0;
36+
int current;
37+
if (!int.TryParse(parts[0], out current))
38+
{
39+
Console.Error.WriteLine($"ERROR: unable to parse current replicas {replicasString}");
40+
return;
41+
}
42+
CurrentReplicas = current;
43+
44+
int desired;
45+
if (!int.TryParse(parts[1], out desired))
46+
{
47+
Console.Error.WriteLine($"ERROR: unable to parse desired replicas {replicasString}");
48+
return;
49+
}
50+
DesiredReplicas = desired;
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)