Skip to content
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0b041c0
feat: introduce `Namespace`
winstxnhdw Jul 31, 2023
4cc0f50
Merge branch 'master' into namespace
winstxnhdw Jul 31, 2023
1447adc
chore: add notice for enterprise-only features
winstxnhdw Aug 2, 2023
85d3d03
Merge branch 'master' into namespace
marcin-krystianc Aug 2, 2023
9bf414e
Merge branch 'master' into namespace
winstxnhdw Aug 9, 2023
351b98d
feat: add `Namespace` controller
winstxnhdw Aug 9, 2023
2f8186f
Merge branch 'master' into namespace
winstxnhdw Aug 9, 2023
0c32831
fix: intialise `Namespace` correctly
winstxnhdw Aug 9, 2023
79ec6a4
fix: use correct env variable for default namespace
winstxnhdw Aug 10, 2023
9bf6e5b
feat: add overload to use default Query/WriteOptions
winstxnhdw Aug 16, 2023
103cf5b
feat: add test for Namespaces API
winstxnhdw Aug 21, 2023
acc987f
chore: remove unused `using`
winstxnhdw Aug 21, 2023
cd4fbb7
fix: assign `_namespaces`
winstxnhdw Aug 21, 2023
0cf301e
fix: skip namespace test if not supported
winstxnhdw Aug 23, 2023
b0d882d
fix: remove `Partition` field
winstxnhdw Aug 23, 2023
b8f4d30
chore: sort usings
winstxnhdw Aug 23, 2023
9e18df6
Merge branch 'master' into namespace
winstxnhdw Aug 24, 2023
207d2a6
fix: use `DateTime` instead of `TimeSpan`
winstxnhdw Aug 27, 2023
a32d82b
fix: deleted namespaces are not immediately deleted
winstxnhdw Aug 27, 2023
6274a47
fix: `ToHashSet` is not supported in net461
winstxnhdw Aug 27, 2023
b2e85a7
fix: `Put` request must specify incoming response
winstxnhdw Aug 28, 2023
2231c57
refactor: `DeletedAt` should be in main `Namespace` class
winstxnhdw Aug 28, 2023
d6931cd
fix: use `EnterpriseOnlyFact` attribute instead
winstxnhdw Aug 28, 2023
3a689cb
refactor: expose correct response
winstxnhdw Aug 28, 2023
6a66429
chore: try skipping without a skippable attribute
winstxnhdw Aug 29, 2023
a0812b1
feat: make `EnterpriseOnlyFact` skippable
winstxnhdw Aug 29, 2023
b783ee9
chore: fix name when skipping test
winstxnhdw Aug 29, 2023
476dadf
docs: add documentation for Namespaces
winstxnhdw Aug 29, 2023
9064700
refactor: verify if created namespaces exist
winstxnhdw Aug 30, 2023
0c47061
feat: add Namespace test with KV
winstxnhdw Aug 30, 2023
f50a2ac
chore: fix comment indentation
winstxnhdw Aug 30, 2023
6f0f8a1
fix: apply dotnet format
winstxnhdw Aug 31, 2023
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
2 changes: 1 addition & 1 deletion Consul.Test/BaseFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public async Task InitializeAsync()
}
}

public class EnterpriseOnlyFact : FactAttribute
public class EnterpriseOnlyFact : SkippableFactAttribute
{
public EnterpriseOnlyFact()
{
Expand Down
171 changes: 171 additions & 0 deletions Consul.Test/NamespaceTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// -----------------------------------------------------------------------
// <copyright file="NamespaceTest.cs" company="G-Research Limited">
// Copyright 2020 G-Research Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
// -----------------------------------------------------------------------

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NuGet.Versioning;
using Xunit;

namespace Consul.Test
{
public class NamespaceTest : BaseFixture
{
[EnterpriseOnlyFact]
public async Task Namespaces_CreateNamespace()
{
var cutOffVersion = SemanticVersion.Parse("1.7.0");
Skip.If(AgentVersion < cutOffVersion, $"Current version is {AgentVersion}, but `Namespaces` is only supported from Consul {cutOffVersion}");

var name = "test";

var ns = new Namespace
{
Name = name
};

var request = await _client.Namespaces.Create(ns);

Assert.Equal(request.Response.Name, name);
}

[EnterpriseOnlyFact]
public async Task Namespaces_UpdateNamespace()
{
var cutOffVersion = SemanticVersion.Parse("1.7.0");
Skip.If(AgentVersion < cutOffVersion, $"Current version is {AgentVersion}, but `Namespaces` is only supported from Consul {cutOffVersion}");

var name = "test";

var ns = new Namespace
{
Name = name
};

await _client.Namespaces.Create(ns);

var description = "updated namespace";

var newNamespace = new Namespace
{
Name = name,
Description = description
};

var updateRequest = await _client.Namespaces.Update(newNamespace);

Assert.Equal(updateRequest.Response.Description, description);
}

[EnterpriseOnlyFact]
public async Task Namespaces_ReadNamespace()
{
var cutOffVersion = SemanticVersion.Parse("1.7.0");
Skip.If(AgentVersion < cutOffVersion, $"Current version is {AgentVersion}, but `Namespaces` is only supported from Consul {cutOffVersion}");

var name = "test";

var ns = new Namespace
{
Name = name
};

await _client.Namespaces.Create(ns);
var request = await _client.Namespaces.Read(name);

Assert.Equal(request.Response.Name, name);
}


[EnterpriseOnlyFact]
public async Task Namespaces_ListNamespaces()
{
var cutOffVersion = SemanticVersion.Parse("1.7.0");
Skip.If(AgentVersion < cutOffVersion, $"Current version is {AgentVersion}, but `Namespaces` is only supported from Consul {cutOffVersion}");

var testNames = new HashSet<string> { "test-a", "test-b", "test-c" };

foreach (var name in testNames)
{
var ns = new Namespace
{
Name = name
};

await _client.Namespaces.Create(ns);
}

var request = await _client.Namespaces.List();
testNames.Add("default");
Assert.True(new HashSet<string>(request.Response.Select(x => x.Name)).SetEquals(testNames));
}

[EnterpriseOnlyFact]
public async Task Namespaces_DeleteNamespace()
{
var cutOffVersion = SemanticVersion.Parse("1.7.0");
Skip.If(AgentVersion < cutOffVersion, $"Current version is {AgentVersion}, but `Namespaces` is only supported from Consul {cutOffVersion}");

var name = "test";

var ns = new Namespace
{
Name = name
};

var createRequest = await _client.Namespaces.Create(ns);
Assert.Equal(name, createRequest.Response.Name);
Assert.Null(createRequest.Response.DeletedAt);

await _client.Namespaces.Delete(name);

var readRequest = await _client.Namespaces.Read(name);
Assert.NotNull(readRequest.Response.DeletedAt);
}

[EnterpriseOnlyFact]
public async Task Namespaces_KVIsolation()
{
var cutOffVersion = SemanticVersion.Parse("1.7.0");
Skip.If(AgentVersion < cutOffVersion, $"Current version is {AgentVersion}, but `Namespaces` is only supported from Consul {cutOffVersion}");

var name = "test";

await _client.Namespaces.Create(new Namespace
{
Name = name
});

var key = "key";

var requestPair = new KVPair
{
Key = key,
Value = Encoding.UTF8.GetBytes("value")
};

await _client.KV.Put(requestPair, new WriteOptions { Namespace = name });
var namespaceResponsePair = await _client.KV.Get(key, new QueryOptions { Namespace = name });
Assert.Equal(requestPair.Value, namespaceResponsePair.Response.Value);

var defaultNamespaceResponsePair = await _client.KV.Get(key);
Assert.Null(defaultNamespaceResponsePair.Response?.Value);
}
}
}
23 changes: 17 additions & 6 deletions Consul/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ internal bool DisableServerCertificateValidation
/// </summary>
public Uri Address { get; set; }

/// <summary>
/// Namespace is the name of the namespace to send along for the request
/// when no other Namespace is present in the QueryOptions
/// </summary>
public string Namespace { get; set; }

/// <summary>
/// Datacenter to provide with each request. If not provided, the default agent datacenter is used.
/// </summary>
Expand All @@ -86,10 +92,7 @@ internal bool DisableServerCertificateValidation
#endif
public NetworkCredential HttpAuth
{
internal get
{
return _httpauth;
}
internal get => _httpauth;
set
{
_httpauth = value;
Expand Down Expand Up @@ -148,6 +151,12 @@ public ConsulClientConfiguration()
UriBuilder consulAddress = new UriBuilder("http://127.0.0.1:8500");
ConfigureFromEnvironment(consulAddress);
Address = consulAddress.Uri;

string ns = Environment.GetEnvironmentVariable("CONSUL_NAMESPACE");
if (!string.IsNullOrEmpty(ns))
{
Namespace = ns;
}
}

/// <summary>
Expand Down Expand Up @@ -234,9 +243,10 @@ private void ConfigureFromEnvironment(UriBuilder consulAddress)
#pragma warning restore CS0618 // Type or member is obsolete
}

if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CONSUL_HTTP_TOKEN")))
string token = Environment.GetEnvironmentVariable("CONSUL_HTTP_TOKEN");
if (!string.IsNullOrEmpty(token))
{
Token = Environment.GetEnvironmentVariable("CONSUL_HTTP_TOKEN");
Token = token;
}
}

Expand Down Expand Up @@ -471,6 +481,7 @@ private void InitializeEndpoints()
_token = new Lazy<Token>(() => new Token(this));
_aclReplication = new Lazy<ACLReplication>(() => new ACLReplication(this));
_authMethod = new Lazy<AuthMethod>(() => new AuthMethod(this));
_namespaces = new Lazy<Namespaces>(() => new Namespaces(this));
}

#region IDisposable Support
Expand Down
25 changes: 20 additions & 5 deletions Consul/Client_DeleteRequests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public DeleteReturnRequest(ConsulClient client, string url, WriteOptions options
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException(nameof(url));
throw new ArgumentException(null, nameof(url));
}
Options = options ?? WriteOptions.Default;
}
Expand Down Expand Up @@ -93,6 +93,11 @@ protected override void ApplyOptions(ConsulClientConfiguration clientConfig)
return;
}

if (!string.IsNullOrEmpty(Options.Namespace))
{
Params["ns"] = Options.Namespace;
}

if (!string.IsNullOrEmpty(Options.Datacenter))
{
Params["dc"] = Options.Datacenter;
Expand All @@ -119,7 +124,7 @@ public DeleteRequest(ConsulClient client, string url, WriteOptions options = nul
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException(nameof(url));
throw new ArgumentException(null, nameof(url));
}
Options = options ?? WriteOptions.Default;
}
Expand Down Expand Up @@ -168,6 +173,11 @@ protected override void ApplyOptions(ConsulClientConfiguration clientConfig)
return;
}

if (!string.IsNullOrEmpty(Options.Namespace))
{
Params["ns"] = Options.Namespace;
}

if (!string.IsNullOrEmpty(Options.Datacenter))
{
Params["dc"] = Options.Datacenter;
Expand All @@ -190,13 +200,13 @@ protected override void ApplyHeaders(HttpRequestMessage message, ConsulClientCon
public class DeleteAcceptingRequest<TIn> : ConsulRequest
{
public WriteOptions Options { get; set; }
private TIn _body;
private readonly TIn _body;

public DeleteAcceptingRequest(ConsulClient client, string url, TIn body, WriteOptions options = null) : base(client, url, HttpMethod.Delete)
{
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException(nameof(url));
throw new ArgumentException(null, nameof(url));
}
_body = body;
Options = options ?? WriteOptions.Default;
Expand All @@ -217,7 +227,7 @@ public async Task<WriteResult> Execute(CancellationToken ct)

if (typeof(TIn) == typeof(byte[]))
{
content = new ByteArrayContent((_body as byte[]) ?? new byte[0]);
content = new ByteArrayContent((_body as byte[]) ?? Array.Empty<byte>());
}
else if (typeof(TIn) == typeof(Stream))
{
Expand Down Expand Up @@ -263,6 +273,11 @@ protected override void ApplyOptions(ConsulClientConfiguration clientConfig)
return;
}

if (!string.IsNullOrEmpty(Options.Namespace))
{
Params["ns"] = Options.Namespace;
}

if (!string.IsNullOrEmpty(Options.Datacenter))
{
Params["dc"] = Options.Datacenter;
Expand Down
10 changes: 10 additions & 0 deletions Consul/Client_GetRequests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ protected override void ApplyOptions(ConsulClientConfiguration clientConfig)
return;
}

if (!string.IsNullOrEmpty(Options.Namespace))
{
Params["ns"] = Options.Namespace;
}

if (!string.IsNullOrEmpty(Options.Datacenter))
{
Params["dc"] = Options.Datacenter;
Expand Down Expand Up @@ -348,6 +353,11 @@ protected override void ApplyOptions(ConsulClientConfiguration clientConfig)
return;
}

if (!string.IsNullOrEmpty(Options.Namespace))
{
Params["ns"] = Options.Namespace;
}

if (!string.IsNullOrEmpty(Options.Datacenter))
{
Params["dc"] = Options.Datacenter;
Expand Down
13 changes: 13 additions & 0 deletions Consul/Client_Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ public class QueryOptions
WaitIndex = 0
};

/// <summary>
/// Namespace is the name of the namespace to send along for the request when no other Namespace is present in the QueryOptions.
/// Namespace is an Enterprise-only feature.
/// </summary>
public string Namespace { get; set; }

/// <summary>
/// Providing a datacenter overwrites the DC provided by the Config.
/// </summary>
Expand All @@ -48,6 +54,7 @@ public class QueryOptions
public ConsistencyMode Consistency { get; set; }

/// <summary>
/// WaitIndex is used to enable a blocking query. Waits until the timeout or the next index is reached.
/// UseCache requests that the agent cache results locally.
/// See https://www.consul.io/api/features/caching.html for more details on the semantics.
/// </summary>
Expand Down Expand Up @@ -106,6 +113,12 @@ public class WriteOptions
Token = string.Empty
};

/// <summary>
/// Namespace is the name of the namespace to send along for the request when no other Namespace is present in the QueryOptions
/// Namespace is an Enterprise-only feature.
/// </summary>
public string Namespace { get; set; }

/// <summary>
/// Providing a datacenter overwrites the DC provided by the Config
/// </summary>
Expand Down
Loading