Skip to content

Fixed tests and included pending unmerged feature PRs #480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
522117f
Squash all commits
Jan 4, 2021
5603efb
Test failing where image wasn't already pulled
Jan 4, 2021
8eaed95
Exception is thrown in CI/CD pipeline
Jan 4, 2021
0756d05
Exception on closed stream
Jan 4, 2021
4ebcd25
Exception
Jan 4, 2021
b1c18f0
IDisposable exception
Jan 4, 2021
9c495e2
Adapting test to CI/CD pipeline
Jan 4, 2021
840adfa
Events not matching
Jan 4, 2021
8e73510
Updated Readme with message processing
Jan 4, 2021
195c0fc
Updated readme with #466
Jan 4, 2021
c667998
Fixed tests when image is being pulled
Jan 4, 2021
77799af
Discarded non functional changes
Jan 4, 2021
9cba768
Update README.md
dgvives Jan 4, 2021
932cf9d
Removed previously discarded line from tests
Jan 4, 2021
3c22074
Exception thrown when cancelling stream within MonitorStreamForMessag…
Jan 4, 2021
79176ff
Corrects identation replacing tabs by spaces
Jan 6, 2021
1238295
Restored space between null argument checks
Jan 6, 2021
e101440
Restored previous return type and fixed spacing
Jan 6, 2021
48235f2
Spacing
Jan 6, 2021
4f375f9
Spacing
Jan 6, 2021
12e6e71
Included go module files,
Jan 6, 2021
aaca892
Tag [email protected]
Jan 6, 2021
83b71a5
Spacing
Jan 6, 2021
dfe37ac
Exception is not always thrown when cancelling task for monitoring ev…
Jan 6, 2021
67561bb
Exception is not always thrown when cancelling task
Jan 6, 2021
e9f1744
Removing ProgressJSONMessage check, image is not always pulled on CI/…
Jan 6, 2021
e0eead2
Removed Swarm init exception traces to ease test debugging in CI/CD p…
Jan 6, 2021
10018f1
Failed tests within CI/CD pipeline
Jan 6, 2021
737ff05
Included 'untag' status as one of the valids
Jan 6, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
76 changes: 64 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ The code below pulls `fedora/memcached` image to your Docker instance using your
anonymously download the image as well by passing `null` instead of AuthConfig object:

```csharp
Stream stream = await client.Images.CreateImageAsync(
await _client.Images.CreateImageAsync(
new ImagesCreateParameters
{
Parent = "fedora/memcached",
FromImage = "fedora/memcached",
Tag = "alpha",
},
},
new AuthConfig
{
Email = "[email protected]",
Expand All @@ -102,6 +102,26 @@ Stream stream = await client.Images.CreateImageAsync(
});
```

#### Example: Create an image and monitor progress messages

```c#
private static async Task Main(string[] args)
{
var progress = new Progress<JSONMessage>(m => System.Diagnostics.Debug.WriteLine($"Create image status: {m.Status}"));

var _client = new Docker.DotNet.DockerClientConfiguration().CreateClient();

await _client.Images.CreateImageAsync(
new ImagesCreateParameters
{
FromImage = "hello-world",
Tag = "latest",
},
null,
progress);
}
```

#### Example: Start a container

The following code will start the created container with specified `HostConfig` object. This object is optional, therefore you can pass a null.
Expand Down Expand Up @@ -132,11 +152,9 @@ var stopped = await client.Containers.StopContainerAsync(
CancellationToken.None);
```

#### Example: Dealing with Stream responses
#### Example: Processing stream messages using IProgress < Message >

Some Docker API endpoints are designed to return stream responses. For example
[Monitoring Docker events](https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/monitor-docker-s-events)
continuously streams the status in a format like :
Some Docker API endpoints are designed to stream response messages using IProgress< Message > for processing. For example [Monitoring Docker events](https://docs.docker.com/engine/reference/api/docker_remote_api_v1.24/#/monitor-docker-s-events) continuously streams the status in a format like :

```json
{"status":"create","id":"dfdf82bd3881","from":"base:latest","time":1374067924}
Expand All @@ -146,15 +164,49 @@ continuously streams the status in a format like :
...
```

To obtain this stream you can use:
To capture messages from this stream you can use:

```csharp
CancellationTokenSource cancellation = new CancellationTokenSource();
Stream stream = await client.System.MonitorEventsAsync(new ContainerEventsParameters(), new Progress<JSONMessage>(), cancellation.Token);
// Initialize a StreamReader...
using System;
using System.Threading;
using System.Threading.Tasks;
using Docker.DotNet.Models;

internal class Program
{
private static async Task Main(string[] args)
{
var progress = new Progress()
{
_onCalled = (m) =>
{
Console.WriteLine($"Received event: {m.Type} -> {m.Action}");
}
};

var _client = new Docker.DotNet.DockerClientConfiguration().CreateClient();
using var cts = new CancellationTokenSource();

var task = Task.Run(() => _client.System.MonitorEventsAsync(new EventsParameters(), progress, cts.Token));

await _client.Images.TagImageAsync("hello-world", new ImageTagParameters { RepositoryName = "hello-world", Tag = "test" }, cts.Token);
cts.Cancel();
await task;
}

private class Progress : IProgress<Message>
{
internal Action<Message> _onCalled;

void IProgress<Message>.Report(Message value)
{
_onCalled(value);
}
}
}
```

You can cancel streaming using the CancellationToken. On the other hand, if you wish to continuously stream, you can simply pass `CancellationToken.None`.
You can cancel message processing using the CancellationToken. On the other hand, if you wish to continuously process messages, you can simply pass `CancellationToken.None`.

#### Example: HTTPS Authentication to Docker

Expand Down
6 changes: 6 additions & 0 deletions src/Docker.DotNet.BasicAuth/Docker.DotNet.BasicAuth.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@
<ItemGroup>
<ProjectReference Include="..\Docker.DotNet\Docker.DotNet.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Nerdbank.GitVersioning" Version="3.3.37">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
6 changes: 6 additions & 0 deletions src/Docker.DotNet.X509/Docker.DotNet.X509.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,10 @@
<ItemGroup>
<ProjectReference Include="..\Docker.DotNet\Docker.DotNet.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Nerdbank.GitVersioning" Version="3.3.37">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
8 changes: 7 additions & 1 deletion src/Docker.DotNet/Docker.DotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
<PropertyGroup>
<PackageId>Docker.DotNet</PackageId>
<Description>Docker.DotNet is a library that allows you to interact with the Docker Remote API programmatically with fully asynchronous, non-blocking and object-oriented code in your .NET applications.</Description>
<AssemblyName>Docker.DotNet</AssemblyName>
<AssemblyName>Docker.DotNet</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Nerdbank.GitVersioning" Version="3.3.37">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions src/Docker.DotNet/DockerApiResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Docker.DotNet
{
internal class DockerApiResponse
public class DockerApiResponse
{
public HttpStatusCode StatusCode { get; private set; }

Expand All @@ -14,4 +14,4 @@ public DockerApiResponse(HttpStatusCode statusCode, string body)
this.Body = body;
}
}
}
}
18 changes: 12 additions & 6 deletions src/Docker.DotNet/DockerClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
using Microsoft.Net.Http.Client;

#if (NETSTANDARD1_6 || NETSTANDARD2_0)

using System.Net.Sockets;

#endif

namespace Docker.DotNet
Expand Down Expand Up @@ -39,6 +41,7 @@ internal DockerClient(DockerClientConfiguration configuration, Version requested
System = new SystemOperations(this);
Networks = new NetworkOperations(this);
Secrets = new SecretsOperations(this);
Configs = new ConfigsOperations(this);
Swarm = new SwarmOperations(this);
Tasks = new TasksOperations(this);
Volumes = new VolumeOperations(this);
Expand Down Expand Up @@ -121,6 +124,7 @@ internal DockerClient(DockerClientConfiguration configuration, Version requested
_endpointBaseUri = uri;

_client = new HttpClient(Configuration.Credentials.GetHandler(handler), true);
_client.DefaultRequestHeaders.Add(nameof(_endpointBaseUri.Host), _endpointBaseUri.Host);
DefaultTimeout = Configuration.DefaultTimeout;
_client.Timeout = s_InfiniteTimeout;
}
Expand All @@ -139,6 +143,8 @@ internal DockerClient(DockerClientConfiguration configuration, Version requested

public ISecretsOperations Secrets { get; }

public IConfigsOperations Configs { get; }

public ISwarmOperations Swarm { get; }

public ITasksOperations Tasks { get; }
Expand Down Expand Up @@ -327,8 +333,8 @@ internal async Task<WriteClosableStream> MakeRequestForHijackedStreamAsync(

await HandleIfErrorResponseAsync(response.StatusCode, response, errorHandlers);

var content = response.Content as HttpConnectionResponseContent;
if (content == null)
var content = response.Content as HttpConnectionResponseContent;
if (content == null)
{
throw new NotSupportedException("message handler does not support hijacked streams");
}
Expand Down Expand Up @@ -364,7 +370,7 @@ private async Task<HttpResponseMessage> PrivateMakeRequestAsync(
return await _client.SendAsync(request, completionOption, cancellationToken).ConfigureAwait(false);
}

private async Task HandleIfErrorResponseAsync(HttpStatusCode statusCode, HttpResponseMessage response, IEnumerable<ApiResponseErrorHandlingDelegate> handlers)
private async static Task HandleIfErrorResponseAsync(HttpStatusCode statusCode, HttpResponseMessage response, IEnumerable<ApiResponseErrorHandlingDelegate> handlers)
{
bool isErrorResponse = statusCode < HttpStatusCode.OK || statusCode >= HttpStatusCode.BadRequest;

Expand Down Expand Up @@ -394,7 +400,7 @@ private async Task HandleIfErrorResponseAsync(HttpStatusCode statusCode, HttpRes
}
}

public async Task HandleIfErrorResponseAsync(HttpStatusCode statusCode, HttpResponseMessage response)
public async static Task HandleIfErrorResponseAsync(HttpStatusCode statusCode, HttpResponseMessage response)
{
bool isErrorResponse = statusCode < HttpStatusCode.OK || statusCode >= HttpStatusCode.BadRequest;

Expand Down Expand Up @@ -423,7 +429,7 @@ internal HttpRequestMessage PrepareRequest(HttpMethod method, string path, IQuer
}

var request = new HttpRequestMessage(method, HttpUtility.BuildUri(_endpointBaseUri, this._requestedApiVersion, path, queryString));

request.Version = new Version(1, 1);

request.Headers.Add("User-Agent", UserAgent);
Expand Down Expand Up @@ -452,4 +458,4 @@ public void Dispose()
}

internal delegate void ApiResponseErrorHandlingDelegate(HttpStatusCode statusCode, string responseBody);
}
}
28 changes: 14 additions & 14 deletions src/Docker.DotNet/DockerClientConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,28 @@ public class DockerClientConfiguration : IDisposable

private static Uri LocalDockerUri()
{
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
return isWindows ? new Uri("npipe://./pipe/docker_engine") : new Uri("unix:/var/run/docker.sock");
var dockerHostVar = Environment.GetEnvironmentVariable("DOCKER_HOST");
var defaultDockerUrl = !string.IsNullOrEmpty(dockerHostVar)
? dockerHostVar
: !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? "unix:///var/run/docker.sock"
: "npipe://./pipe/docker_engine";

return new Uri(defaultDockerUrl);
}

public DockerClientConfiguration(Credentials credentials = null, TimeSpan defaultTimeout = default(TimeSpan))
: this(LocalDockerUri(), credentials, defaultTimeout)
{
}

public DockerClientConfiguration(Uri endpoint, Credentials credentials = null,
TimeSpan defaultTimeout = default(TimeSpan))
public DockerClientConfiguration(Uri endpoint, Credentials credentials = null, TimeSpan defaultTimeout = default(TimeSpan))
{
if (endpoint == null)
throw new ArgumentNullException(nameof(endpoint));


Credentials = credentials ?? new AnonymousCredentials();

EndpointBaseUri = endpoint;
if (defaultTimeout != TimeSpan.Zero)
{
Expand All @@ -44,19 +49,14 @@ public DockerClientConfiguration(Uri endpoint, Credentials credentials = null,
}
}

public DockerClient CreateClient()
{
return this.CreateClient(null);
}
public DockerClient CreateClient() => this.CreateClient(null);

public DockerClient CreateClient(Version requestedApiVersion)
{
return new DockerClient(this, requestedApiVersion);
}
public DockerClient CreateClient(Version requestedApiVersion) => new DockerClient(this, requestedApiVersion);

public void Dispose()
{
Credentials.Dispose();
GC.SuppressFinalize(this);
}
}
}
}
2 changes: 1 addition & 1 deletion src/Docker.DotNet/DockerPipeStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,4 @@ protected override void Dispose(bool disposing)
}
}
}
}
}
53 changes: 53 additions & 0 deletions src/Docker.DotNet/Endpoints/ConfigsOperations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Docker.DotNet.Models;

namespace Docker.DotNet
{
internal class ConfigsOperations : IConfigsOperations
{
private readonly DockerClient _client;

internal ConfigsOperations(DockerClient client)
{
_client = client;
}

async Task<IList<SwarmConfig>> IConfigsOperations.ListAsync(CancellationToken cancellationToken)
{
var response = await _client.MakeRequestAsync(_client.NoErrorHandlers, HttpMethod.Get, "configs", cancellationToken).ConfigureAwait(false);
return _client.JsonSerializer.DeserializeObject<IList<SwarmConfig>>(response.Body);
}

async Task<SwarmCreateConfigResponse> IConfigsOperations.CreateAsync(ConfigSpec body, CancellationToken cancellationToken)
{
var data = new JsonRequestContent<ConfigSpec>(body ?? throw new ArgumentNullException(nameof(body)), _client.JsonSerializer);
var response = await _client.MakeRequestAsync(_client.NoErrorHandlers, HttpMethod.Post, "configs/create", null, data, cancellationToken).ConfigureAwait(false);
return _client.JsonSerializer.DeserializeObject<SwarmCreateConfigResponse>(response.Body);
}

async Task<SwarmConfig> IConfigsOperations.InspectAsync(string id, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException(nameof(id));
}

var response = await _client.MakeRequestAsync(_client.NoErrorHandlers, HttpMethod.Get, $"configs/{id}", cancellationToken).ConfigureAwait(false);
return _client.JsonSerializer.DeserializeObject<SwarmConfig>(response.Body);
}

Task IConfigsOperations.DeleteAsync(string id, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(id))
{
throw new ArgumentNullException(nameof(id));
}

return _client.MakeRequestAsync(_client.NoErrorHandlers, HttpMethod.Delete, $"configs/{id}", cancellationToken);
}
}
}
3 changes: 1 addition & 2 deletions src/Docker.DotNet/Endpoints/ContainerOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ public Task<Stream> GetContainerStatsAsync(string id, ContainerStatsParameters p
throw new ArgumentNullException(nameof(parameters));
}


IQueryString queryParameters = new QueryString<ContainerRestartParameters>(parameters);
// since specified wait timespan can be greater than HttpClient's default, we set the
// client timeout to infinite and provide a cancellation token.
Expand Down Expand Up @@ -413,4 +412,4 @@ public async Task<ContainersPruneResponse> PruneContainersAsync(ContainersPruneP
return this._client.JsonSerializer.DeserializeObject<ContainerUpdateResponse>(response.Body);
}
}
}
}
Loading