Skip to content
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

Services filter fixed. And service operations now throws dockerapiexc… #358

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/Docker.DotNet/Endpoints/DockerSwarmException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.Net;

namespace Docker.DotNet
{
public class DockerSwarmException : DockerApiException
{
public string DeserializedMessage { get; private set; }

public DockerSwarmException(HttpStatusCode statusCode, string responseBody, string deserializedMessage) : base(statusCode, responseBody)
{
DeserializedMessage = deserializedMessage;
}
}
}
108 changes: 45 additions & 63 deletions src/Docker.DotNet/Endpoints/SwarmOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ internal class SwarmOperations : ISwarmOperations
{
internal static readonly ApiResponseErrorHandlingDelegate SwarmResponseHandler = (statusCode, responseBody) =>
{
if (statusCode == HttpStatusCode.ServiceUnavailable)
if (statusCode < HttpStatusCode.OK || statusCode >= HttpStatusCode.BadRequest)
n7on marked this conversation as resolved.
Show resolved Hide resolved
{
// TODO: Make typed error.
throw new Exception("Node is not part of a swarm.");
var deserializedBody = new JsonSerializer().DeserializeObject<JSONError>(responseBody);
n7on marked this conversation as resolved.
Show resolved Hide resolved
throw new DockerSwarmException(statusCode, responseBody, deserializedBody.Message);
}
};

Expand Down Expand Up @@ -46,21 +46,11 @@ async Task<string> ISwarmOperations.InitSwarmAsync(SwarmInitParameters parameter
{
var data = new JsonRequestContent<SwarmInitParameters>(parameters ?? throw new ArgumentNullException(nameof(parameters)), this._client.JsonSerializer);
var response = await this._client.MakeRequestAsync(
new ApiResponseErrorHandlingDelegate[]
{
(statusCode, responseBody) =>
{
if (statusCode == HttpStatusCode.NotAcceptable)
{
// TODO: Make typed error.
throw new Exception("Node is already part of a swarm.");
}
}
},
new[] { SwarmResponseHandler },
HttpMethod.Post,
"swarm/init",
null,
data,
null,
data,
cancellationToken).ConfigureAwait(false);

return response.Body;
Expand All @@ -70,73 +60,68 @@ async Task<SwarmService> ISwarmOperations.InspectServiceAsync(string id, Cancell
{
if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id));

var response = await this._client.MakeRequestAsync(new[] { SwarmResponseHandler }, HttpMethod.Get, $"services/{id}", cancellationToken).ConfigureAwait(false);
var response = await this._client.MakeRequestAsync(
new[] { SwarmResponseHandler },
HttpMethod.Get,
$"services/{id}",
cancellationToken).ConfigureAwait(false);
return this._client.JsonSerializer.DeserializeObject<SwarmService>(response.Body);
}

async Task<SwarmInspectResponse> ISwarmOperations.InspectSwarmAsync(CancellationToken cancellationToken)
{
var response = await this._client.MakeRequestAsync(new[] { SwarmResponseHandler }, HttpMethod.Get, "swarm", cancellationToken).ConfigureAwait(false);
var response = await this._client.MakeRequestAsync(
new[] { SwarmResponseHandler },
HttpMethod.Get,
"swarm",
cancellationToken).ConfigureAwait(false);
return this._client.JsonSerializer.DeserializeObject<SwarmInspectResponse>(response.Body);
}

async Task ISwarmOperations.JoinSwarmAsync(SwarmJoinParameters parameters, CancellationToken cancellationToken)
{
var data = new JsonRequestContent<SwarmJoinParameters>(parameters ?? throw new ArgumentNullException(nameof(parameters)), this._client.JsonSerializer);
await this._client.MakeRequestAsync(
new ApiResponseErrorHandlingDelegate[]
{
(statusCode, responseBody) =>
{
if (statusCode == HttpStatusCode.ServiceUnavailable)
{
// TODO: Make typed error.
throw new Exception("Node is already part of a swarm.");
}
}
},
HttpMethod.Post,
"swarm/join",
null,
data,
new[] { SwarmResponseHandler },
HttpMethod.Post,
"swarm/join",
null,
data,
cancellationToken).ConfigureAwait(false);
}

async Task ISwarmOperations.LeaveSwarmAsync(SwarmLeaveParameters parameters, CancellationToken cancellationToken)
{
var query = parameters == null ? null : new QueryString<SwarmLeaveParameters>(parameters);
await this._client.MakeRequestAsync(
new ApiResponseErrorHandlingDelegate[]
{
(statusCode, responseBody) =>
{
if (statusCode == HttpStatusCode.ServiceUnavailable)
{
// TODO: Make typed error.
throw new Exception("Node is not part of a swarm.");
}
}
},
HttpMethod.Post,
"swarm/leave",
query,
new[] { SwarmResponseHandler },
HttpMethod.Post,
"swarm/leave",
query,
cancellationToken).ConfigureAwait(false);
}

async Task<IEnumerable<SwarmService>> ISwarmOperations.ListServicesAsync(ServicesListParameters parameters, CancellationToken cancellationToken)
{
var queryParameters = parameters != null ? new QueryString<ServicesListParameters>(parameters) : null;
var response = await this._client
.MakeRequestAsync(new[] { SwarmResponseHandler }, HttpMethod.Get, $"services", queryParameters, cancellationToken)
.ConfigureAwait(false);
var response = await this._client.MakeRequestAsync(
new[] { SwarmResponseHandler },
HttpMethod.Get,
$"services",
queryParameters,
cancellationToken).ConfigureAwait(false);
return this._client.JsonSerializer.DeserializeObject<SwarmService[]>(response.Body);
}

async Task ISwarmOperations.RemoveServiceAsync(string id, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id));

await this._client.MakeRequestAsync(new[] { SwarmResponseHandler }, HttpMethod.Delete, $"services/{id}", cancellationToken).ConfigureAwait(false);
await this._client.MakeRequestAsync(
new[] { SwarmResponseHandler },
HttpMethod.Delete,
$"services/{id}",
cancellationToken).ConfigureAwait(false);
}

async Task ISwarmOperations.UnlockSwarmAsync(SwarmUnlockParameters parameters, CancellationToken cancellationToken)
Expand All @@ -152,7 +137,14 @@ async Task<ServiceUpdateResponse> ISwarmOperations.UpdateServiceAsync(string id,

var query = new QueryString<ServiceUpdateParameters>(parameters);
var body = new JsonRequestContent<ServiceSpec>(parameters.Service ?? throw new ArgumentNullException(nameof(parameters.Service)), this._client.JsonSerializer);
var response = await this._client.MakeRequestAsync(new[] { SwarmResponseHandler }, HttpMethod.Post, $"services/{id}/update", query, body, RegistryAuthHeaders(parameters.RegistryAuth), cancellationToken).ConfigureAwait(false);
var response = await this._client.MakeRequestAsync(
new[] { SwarmResponseHandler },
HttpMethod.Post,
$"services/{id}/update",
query,
body,
RegistryAuthHeaders(parameters.RegistryAuth),
cancellationToken).ConfigureAwait(false);
return this._client.JsonSerializer.DeserializeObject<ServiceUpdateResponse>(response.Body);
}

Expand All @@ -161,17 +153,7 @@ async Task ISwarmOperations.UpdateSwarmAsync(SwarmUpdateParameters parameters, C
var query = new QueryString<SwarmUpdateParameters>(parameters ?? throw new ArgumentNullException(nameof(parameters)));
var body = new JsonRequestContent<Spec>(parameters.Spec ?? throw new ArgumentNullException(nameof(parameters.Spec)), this._client.JsonSerializer);
await this._client.MakeRequestAsync(
new ApiResponseErrorHandlingDelegate[]
{
(statusCode, responseBody) =>
{
if (statusCode == HttpStatusCode.ServiceUnavailable)
{
// TODO: Make typed error.
throw new Exception("Node is not part of a swarm.");
}
}
},
new[] { SwarmResponseHandler },
HttpMethod.Post,
"swarm/update",
query,
Expand Down
22 changes: 15 additions & 7 deletions src/Docker.DotNet/Models/ServiceFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,32 @@ public class ServicesListParameters
public ServiceFilter Filters { get; set; }
}

public class ServiceFilter : Dictionary<string, string>
public class ServiceFilter : Dictionary<string, string[]>
{
public string Id
public string[] Id
{
get => this["id"];
set => this["id"] = value;
}
public string Label
public string[] Label
{
get => this["label"];
set => this["label"] = value;
}
public ServiceCreationMode Mode
public ServiceCreationMode[] Mode
{
get => !Enum.TryParse(this["mode"], out ServiceCreationMode mode) ? ServiceCreationMode.Replicated : mode;
set => this["mode"] = value.ToString();
get
{
var lst = new ServiceCreationMode[this["mode"].Length];
for(int i = 0; i< this["mode"].Length; i++)
{
lst[i] = (ServiceCreationMode)Enum.Parse(typeof(ServiceCreationMode), this["mode"][i]);
}
return lst;
}
set => this["mode"] = value.Select(m => m.ToString()).ToArray();
}
public string Name
public string[] Name
{
get => this["name"];
set => this["name"] = value;
Expand Down
49 changes: 46 additions & 3 deletions test/Docker.DotNet.Tests/ISwarmOperationsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,73 @@
using System.Threading;
using System.Threading.Tasks;
using Docker.DotNet.Models;
using System.Collections.Generic;
using Xunit;
using System;

namespace Docker.DotNet.Tests
{
public class ISwarmOperationsTests
public class ISwarmOperationsTests :IDisposable
{
private readonly DockerClient _client;
private readonly string _testServiceId;
private static string _testServiceName = "docker-dotnet-test-service";

public ISwarmOperationsTests()
{
_client = new DockerClientConfiguration().CreateClient();

_testServiceId = _client.Swarm.CreateServiceAsync(new ServiceCreateParameters()
{
Service = new ServiceSpec
{
Name = _testServiceName,
TaskTemplate = new TaskSpec()
{
ContainerSpec = new ContainerSpec()
{
Image = "nginx:latest"
}
}
}
}).Result.ID;
}

[Fact]
public async Task GetServicesAsync_Succeeds()
{
var services = await _client.Swarm.ListServicesAsync(cancellationToken: CancellationToken.None);
Assert.Equal(2, services.Count());
Assert.Contains(_testServiceId, services.Select(s => s.ID));
}
[Fact]
public async Task GetFilteredServicesAsync_Succeeds()
{
var services = await _client.Swarm.ListServicesAsync(new ServicesListParameters { Filters = new ServiceFilter { Id = "pr6264hhb836" } }, CancellationToken.None);
var services = await _client.Swarm.ListServicesAsync(new ServicesListParameters { Filters = new ServiceFilter { Name = new string[] { _testServiceName } } }, CancellationToken.None);
Assert.Single(services);
}
[Fact]
public async Task CreateServiceAsync_FaultyNetwork_Throws()
{
await Assert.ThrowsAsync<DockerSwarmException>(() => _client.Swarm.CreateServiceAsync(new ServiceCreateParameters()
{
Service = new ServiceSpec
{
Name = $"{_testServiceName}2",
TaskTemplate = new TaskSpec()
{
ContainerSpec = new ContainerSpec()
{
Image = "nginx:latest"
}
},
Networks = new List<NetworkAttachmentConfig>() { new NetworkAttachmentConfig() { Target = "non-existing-network" } }
}
}));
}

public void Dispose()
{
_client.Swarm.RemoveServiceAsync(_testServiceId, CancellationToken.None);
}
}
}
2 changes: 1 addition & 1 deletion test/Docker.DotNet.Tests/ISystemOperations.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public ISystemOperationsTests()
public void DockerService_IsRunning()
{
var services = ServiceController.GetServices();
using (var dockerService = services.SingleOrDefault(service => service.ServiceName == "docker"))
using (var dockerService = services.SingleOrDefault(service => service.ServiceName == "docker" || service.ServiceName == "com.docker.service"))
{
Assert.NotNull(dockerService); // docker is not running
Assert.Equal(ServiceControllerStatus.Running, dockerService.Status);
Expand Down
26 changes: 22 additions & 4 deletions test/Docker.DotNet.Tests/QueryStringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,36 @@ public class QueryStringTests
[Fact]
n7on marked this conversation as resolved.
Show resolved Hide resolved
public void ServicesListParameters_GenerateIdFilters()
{
var p = new ServicesListParameters { Filters = new ServiceFilter { Id = "service-id" } };
var p = new ServicesListParameters { Filters = new ServiceFilter { Id = new string[]{ "service-id" } } };
var qs = new QueryString<ServicesListParameters>(p);

Assert.Equal("filters={\"id\":\"service-id\"}", Uri.UnescapeDataString(qs.GetQueryString()));
Assert.Equal("filters={\"id\":[\"service-id\"]}", Uri.UnescapeDataString(qs.GetQueryString()));
}
[Fact]
public void ServicesListParameters_GenerateCompositeFilters()
{
var p = new ServicesListParameters { Filters = new ServiceFilter { Id = "service-id", Label = "label" } };
var p = new ServicesListParameters { Filters = new ServiceFilter { Id = new string[] { "service-id" }, Label = new string[] { "label" } } };
var qs = new QueryString<ServicesListParameters>(p);

Assert.Equal("filters={\"id\":\"service-id\",\"label\":\"label\"}", Uri.UnescapeDataString(qs.GetQueryString()));
Assert.Equal("filters={\"id\":[\"service-id\"],\"label\":[\"label\"]}", Uri.UnescapeDataString(qs.GetQueryString()));
}

[Fact]
public void ServicesListParameters_GenerateNullFilters()
{
var p = new ServicesListParameters { Filters = new ServiceFilter() };
var qs = new QueryString<ServicesListParameters>(p);
Assert.Equal("filters={}", Uri.UnescapeDataString(qs.GetQueryString()));
}

[Fact]
public void ServicesListParameters_GenerateNullModeFilters()
{
var p = new ServicesListParameters { Filters = new ServiceFilter() { Mode = new ServiceCreationMode[] { } } };
var qs = new QueryString<ServicesListParameters>(p);
var tmp = qs.GetQueryString();
var tmp2 = Uri.UnescapeDataString(tmp);
Assert.Equal("filters={\"mode\":[]}", tmp2);
}
}
}