1
1
using System ;
2
+ using System . Collections . Generic ;
2
3
using System . Linq ;
4
+ using System . Text ;
3
5
using System . Threading ;
4
6
using System . Threading . Tasks ;
5
7
using Docker . DotNet . Models ;
@@ -10,11 +12,18 @@ namespace Docker.DotNet.Tests
10
12
[ Collection ( nameof ( TestCollection ) ) ]
11
13
public class ISwarmOperationsTests
12
14
{
15
+ private readonly CancellationTokenSource _cts ;
16
+
13
17
private readonly DockerClient _dockerClient ;
14
18
private readonly string _imageId ;
15
19
16
20
public ISwarmOperationsTests ( TestFixture testFixture )
17
21
{
22
+ // Do not wait forever in case it gets stuck
23
+ _cts = CancellationTokenSource . CreateLinkedTokenSource ( testFixture . Cts . Token ) ;
24
+ _cts . CancelAfter ( TimeSpan . FromMinutes ( 5 ) ) ;
25
+ _cts . Token . Register ( ( ) => throw new TimeoutException ( "SwarmOperationTests timeout" ) ) ;
26
+
18
27
_dockerClient = testFixture . DockerClient ;
19
28
_imageId = testFixture . Image . ID ;
20
29
}
@@ -148,5 +157,103 @@ public async Task GetServices_Succeeds()
148
157
await _dockerClient . Swarm . RemoveServiceAsync ( secondServiceId , default ) ;
149
158
await _dockerClient . Swarm . RemoveServiceAsync ( thirdServiceid , default ) ;
150
159
}
160
+
161
+ [ Fact ]
162
+ public async Task GetServiceLogs_Succeeds ( )
163
+ {
164
+ var cts = new CancellationTokenSource ( ) ;
165
+ var linkedCts = CancellationTokenSource . CreateLinkedTokenSource ( _cts . Token , cts . Token ) ;
166
+
167
+ var serviceName = $ "service-withLogs-{ Guid . NewGuid ( ) . ToString ( ) . Substring ( 1 , 10 ) } ";
168
+ var serviceId = _dockerClient . Swarm . CreateServiceAsync ( new ServiceCreateParameters
169
+ {
170
+ Service = new ServiceSpec
171
+ {
172
+ Name = serviceName ,
173
+ TaskTemplate = new TaskSpec { ContainerSpec = new ContainerSpec { Image = _imageId } }
174
+ }
175
+ } ) . Result . ID ;
176
+
177
+ var _stream = await _dockerClient . Swarm . GetServiceLogsAsync ( serviceName , false , new ServiceLogsParameters
178
+ {
179
+ Follow = true ,
180
+ ShowStdout = true ,
181
+ ShowStderr = true
182
+ } ) ;
183
+
184
+ int maxRetries = 3 ;
185
+ int currentRetry = 0 ;
186
+ TimeSpan delayBetweenRetries = TimeSpan . FromSeconds ( 5 ) ;
187
+ List < string > logLines = null ;
188
+
189
+ while ( currentRetry < maxRetries && ! linkedCts . IsCancellationRequested )
190
+ {
191
+ logLines = new List < string > ( ) ;
192
+ TimeSpan delay = TimeSpan . FromSeconds ( 10 ) ;
193
+ cts . CancelAfter ( delay ) ;
194
+
195
+ bool cancelRequested = false ; // Add a flag to indicate cancellation
196
+
197
+ while ( ! linkedCts . IsCancellationRequested && ! cancelRequested )
198
+ {
199
+ var line = new List < byte > ( ) ;
200
+ var buffer = new byte [ 4096 ] ;
201
+
202
+ try
203
+ {
204
+ while ( true )
205
+ {
206
+ var res = await _stream . ReadOutputAsync ( buffer , 0 , buffer . Length , linkedCts . Token ) ;
207
+
208
+ if ( res . Count == 0 )
209
+ {
210
+ continue ;
211
+ }
212
+
213
+ int newlineIndex = Array . IndexOf ( buffer , ( byte ) '\n ' , 0 , res . Count ) ;
214
+
215
+ if ( newlineIndex != - 1 )
216
+ {
217
+ line . AddRange ( buffer . Take ( newlineIndex ) ) ;
218
+ break ;
219
+ }
220
+ else
221
+ {
222
+ line . AddRange ( buffer . Take ( res . Count ) ) ;
223
+ }
224
+ }
225
+
226
+ logLines . Add ( Encoding . UTF8 . GetString ( line . ToArray ( ) ) ) ;
227
+ }
228
+ catch ( OperationCanceledException )
229
+ {
230
+ cancelRequested = true ; // Set the flag when cancellation is requested
231
+
232
+ // Reset the CancellationTokenSource for the next attempt
233
+ cts = new CancellationTokenSource ( ) ;
234
+ linkedCts = CancellationTokenSource . CreateLinkedTokenSource ( _cts . Token , cts . Token ) ;
235
+ cts . CancelAfter ( delay ) ;
236
+ }
237
+ }
238
+
239
+ if ( logLines . Any ( ) && logLines . First ( ) . Contains ( "[INF]" ) )
240
+ {
241
+ break ;
242
+ }
243
+ else
244
+ {
245
+ currentRetry ++ ;
246
+ if ( currentRetry < maxRetries )
247
+ {
248
+ await Task . Delay ( delayBetweenRetries ) ;
249
+ }
250
+ }
251
+ }
252
+
253
+ Assert . True ( logLines . Any ( ) ) ;
254
+ Assert . Contains ( "[INF]" , logLines . First ( ) ) ;
255
+
256
+ await _dockerClient . Swarm . RemoveServiceAsync ( serviceId , default ) ;
257
+ }
151
258
}
152
259
}
0 commit comments