Skip to content

Commit

Permalink
Make all remote calls accept CancellationTokens
Browse files Browse the repository at this point in the history
Fixes hashicorp#56 by adding an optional CancellationToken parameter to every
method that ends up doing an HTTP request. Some of these seem ill-advised
(e.g. allowing the release of a semaphore to be canceled) but in many
cases they should only be used if the call can possibly fail and a
secondary timeout is needed.
  • Loading branch information
highlyunavailable committed Jul 11, 2016
1 parent 538285a commit 33639dc
Show file tree
Hide file tree
Showing 30 changed files with 460 additions and 650 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
* `ConsulClient` is now `IDisposable` and should have `Dispose()` called to
clean it up. It is still supposed to be used in a long-lived fashion, though.

## 2016-07-10
* Add an optional CancellationToken parameter to every method that ends up
doing an HTTP request. Some of these can create an unstable Consul state
(e.g. allowing the release of a distribted Semaphore to be canceled) but in
many cases they should only be used if the call can possibly fail and a
secondary timeout is needed.

## 2016-07-07
* Add .NET Core port and build process thanks to work by @akatz0813.
* Converted all Locks and Semaphores to be totally `async` thanks to
Expand Down
9 changes: 5 additions & 4 deletions Consul.Test/ClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Security;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

Expand Down Expand Up @@ -65,7 +66,7 @@ public async Task Client_SetQueryOptions()
};
var request = client.Get<KVPair>("/v1/kv/foo", opts);

await Assert.ThrowsAsync<ConsulRequestException>(async () => await request.Execute());
await Assert.ThrowsAsync<ConsulRequestException>(async () => await request.Execute(CancellationToken.None));

Assert.Equal("foo", request.Params["dc"]);
Assert.True(request.Params.ContainsKey("consistent"));
Expand All @@ -85,7 +86,7 @@ public async Task Client_SetClientOptions()
});
var request = client.Get<KVPair>("/v1/kv/foo");

await Assert.ThrowsAsync<ConsulRequestException>(async () => await request.Execute());
await Assert.ThrowsAsync<ConsulRequestException>(async () => await request.Execute(CancellationToken.None));

Assert.Equal("foo", request.Params["dc"]);
Assert.Equal("1m40s", request.Params["wait"]);
Expand All @@ -104,7 +105,7 @@ public async Task Client_SetWriteOptions()

var request = client.Put("/v1/kv/foo", new KVPair("kv/foo"), opts);

await Assert.ThrowsAsync<ConsulRequestException>(async () => await request.Execute());
await Assert.ThrowsAsync<ConsulRequestException>(async () => await request.Execute(CancellationToken.None));

Assert.Equal("foo", request.Params["dc"]);
Assert.Equal("12345", request.Params["token"]);
Expand Down Expand Up @@ -141,7 +142,7 @@ public async Task Client_DisposeBehavior()

var request = client.Put("/v1/kv/foo", new KVPair("kv/foo"), opts);

await Assert.ThrowsAsync<ObjectDisposedException>(() => request.Execute());
await Assert.ThrowsAsync<ObjectDisposedException>(() => request.Execute(CancellationToken.None));
}

[Fact]
Expand Down
65 changes: 24 additions & 41 deletions Consul/ACL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ private class ACLCreationResult
/// </summary>
/// <param name="acl">The ACL entry to create</param>
/// <returns>A write result containing the newly created ACL token</returns>
public Task<WriteResult<string>> Create(ACLEntry acl)
public Task<WriteResult<string>> Create(ACLEntry acl, CancellationToken ct = default(CancellationToken))
{
return Create(acl, WriteOptions.Default);
return Create(acl, WriteOptions.Default, ct);
}

/// <summary>
Expand All @@ -162,9 +162,9 @@ public Task<WriteResult<string>> Create(ACLEntry acl)
/// <param name="acl">The ACL entry to create</param>
/// <param name="q">Customized write options</param>
/// <returns>A write result containing the newly created ACL token</returns>
public async Task<WriteResult<string>> Create(ACLEntry acl, WriteOptions q)
public async Task<WriteResult<string>> Create(ACLEntry acl, WriteOptions q, CancellationToken ct = default(CancellationToken))
{
var res = await _client.Put<ACLEntry, ACLCreationResult>("/v1/acl/create", acl, q).Execute().ConfigureAwait(false);
var res = await _client.Put<ACLEntry, ACLCreationResult>("/v1/acl/create", acl, q).Execute(ct).ConfigureAwait(false);
return new WriteResult<string>(res, res.Response.ID);
}

Expand All @@ -173,9 +173,9 @@ public async Task<WriteResult<string>> Create(ACLEntry acl, WriteOptions q)
/// </summary>
/// <param name="acl">The ACL entry to update</param>
/// <returns>An empty write result</returns>
public Task<WriteResult> Update(ACLEntry acl)
public Task<WriteResult> Update(ACLEntry acl, CancellationToken ct = default(CancellationToken))
{
return Update(acl, WriteOptions.Default);
return Update(acl, WriteOptions.Default, ct);
}

/// <summary>
Expand All @@ -184,19 +184,19 @@ public Task<WriteResult> Update(ACLEntry acl)
/// <param name="acl">The ACL entry to update</param>
/// <param name="q">Customized write options</param>
/// <returns>An empty write result</returns>
public Task<WriteResult> Update(ACLEntry acl, WriteOptions q)
public Task<WriteResult> Update(ACLEntry acl, WriteOptions q, CancellationToken ct = default(CancellationToken))
{
return _client.Put("/v1/acl/update", acl, q).Execute();
return _client.Put("/v1/acl/update", acl, q).Execute(ct);
}

/// <summary>
/// Destroy is used to destroy a given ACL token ID
/// </summary>
/// <param name="id">The ACL ID to destroy</param>
/// <returns>An empty write result</returns>
public Task<WriteResult<bool>> Destroy(string id)
public Task<WriteResult<bool>> Destroy(string id, CancellationToken ct = default(CancellationToken))
{
return Destroy(id, WriteOptions.Default);
return Destroy(id, WriteOptions.Default,ct);
}

/// <summary>
Expand All @@ -205,19 +205,19 @@ public Task<WriteResult<bool>> Destroy(string id)
/// <param name="id">The ACL ID to destroy</param>
/// <param name="q">Customized write options</param>
/// <returns>An empty write result</returns>
public Task<WriteResult<bool>> Destroy(string id, WriteOptions q)
public Task<WriteResult<bool>> Destroy(string id, WriteOptions q, CancellationToken ct = default(CancellationToken))
{
return _client.EmptyPut<bool>(string.Format("/v1/acl/destroy/{0}", id), q).Execute();
return _client.EmptyPut<bool>(string.Format("/v1/acl/destroy/{0}", id), q).Execute(ct);
}

/// <summary>
/// Clone is used to return a new token cloned from an existing one
/// </summary>
/// <param name="id">The ACL ID to clone</param>
/// <returns>A write result containing the newly created ACL token</returns>
public Task<WriteResult<string>> Clone(string id)
public Task<WriteResult<string>> Clone(string id, CancellationToken ct = default(CancellationToken))
{
return Clone(id, WriteOptions.Default);
return Clone(id, WriteOptions.Default,ct);
}

/// <summary>
Expand All @@ -226,9 +226,9 @@ public Task<WriteResult<string>> Clone(string id)
/// <param name="id">The ACL ID to clone</param>
/// <param name="q">Customized write options</param>
/// <returns>A write result containing the newly created ACL token</returns>
public async Task<WriteResult<string>> Clone(string id, WriteOptions q)
public async Task<WriteResult<string>> Clone(string id, WriteOptions q, CancellationToken ct = default(CancellationToken))
{
var res = await _client.EmptyPut<ACLCreationResult>(string.Format("/v1/acl/clone/{0}", id), q).Execute().ConfigureAwait(false);
var res = await _client.EmptyPut<ACLCreationResult>(string.Format("/v1/acl/clone/{0}", id), q).Execute(ct).ConfigureAwait(false);
return new WriteResult<string>(res, res.Response.ID);
}

Expand All @@ -237,28 +237,19 @@ public async Task<WriteResult<string>> Clone(string id, WriteOptions q)
/// </summary>
/// <param name="id">The ACL ID to request information about</param>
/// <returns>A query result containing the ACL entry matching the provided ID, or a query result with a null response if no token matched the provided ID</returns>
public Task<QueryResult<ACLEntry>> Info(string id)
public Task<QueryResult<ACLEntry>> Info(string id, CancellationToken ct = default(CancellationToken))
{
return Info(id, QueryOptions.Default, CancellationToken.None);
}
/// <summary>
/// Info is used to query for information about an ACL token
/// </summary>
/// <param name="id">The ACL ID to request information about</param>
/// <param name="q">Customized query options</param>
/// <returns>A query result containing the ACL entry matching the provided ID, or a query result with a null response if no token matched the provided ID</returns>
public Task<QueryResult<ACLEntry>> Info(string id, QueryOptions q)
{
return Info(id, q, CancellationToken.None);
return Info(id, QueryOptions.Default, ct);
}

/// <summary>
/// Info is used to query for information about an ACL token
/// </summary>
/// <param name="id">The ACL ID to request information about</param>
/// <param name="q">Customized query options</param>
/// <param name="ct">Cancellation token for long poll request. If set, OperationCanceledException will be thrown if the request is cancelled before completing</param>
/// <returns>A query result containing the ACL entry matching the provided ID, or a query result with a null response if no token matched the provided ID</returns>
public async Task<QueryResult<ACLEntry>> Info(string id, QueryOptions q, CancellationToken ct)
public async Task<QueryResult<ACLEntry>> Info(string id, QueryOptions q, CancellationToken ct = default(CancellationToken))
{
var res = await _client.Get<ACLEntry[]>(string.Format("/v1/acl/info/{0}", id), q).Execute(ct).ConfigureAwait(false);
return new QueryResult<ACLEntry>(res, res.Response != null && res.Response.Length > 0 ? res.Response[0] : null);
Expand All @@ -268,26 +259,18 @@ public async Task<QueryResult<ACLEntry>> Info(string id, QueryOptions q, Cancell
/// List is used to get all the ACL tokens
/// </summary>
/// <returns>A write result containing the list of all ACLs</returns>
public Task<QueryResult<ACLEntry[]>> List()
public Task<QueryResult<ACLEntry[]>> List(CancellationToken ct = default(CancellationToken))
{
return List(QueryOptions.Default, CancellationToken.None);
}
/// <summary>
/// List is used to get all the ACL tokens
/// </summary>
/// <param name="q">Customized query options</param>
/// <returns>A write result containing the list of all ACLs</returns>
public Task<QueryResult<ACLEntry[]>> List(QueryOptions q)
{
return List(q, CancellationToken.None);
return List(QueryOptions.Default, ct);
}

/// <summary>
/// List is used to get all the ACL tokens
/// </summary>
/// <param name="q">Customized query options</param>
/// <param name="ct">Cancellation token for long poll request. If set, OperationCanceledException will be thrown if the request is cancelled before completing</param>
/// <returns>A write result containing the list of all ACLs</returns>
public Task<QueryResult<ACLEntry[]>> List(QueryOptions q, CancellationToken ct)
public Task<QueryResult<ACLEntry[]>> List(QueryOptions q, CancellationToken ct = default(CancellationToken))
{
return _client.Get<ACLEntry[]>("/v1/acl/list", q).Execute(ct);
}
Expand Down
Loading

0 comments on commit 33639dc

Please sign in to comment.