stylecop fix followup, enforce SA1503 (#432)

* enforce SA1503

* fix spacing

* fix SA1413

* fix spacing

* fix SA1013
This commit is contained in:
Boshi Lian
2020-04-23 11:40:06 -07:00
committed by GitHub
parent 324a3e72fd
commit cfc4306528
75 changed files with 1072 additions and 786 deletions

View File

@@ -8,4 +8,8 @@
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)\stylecop.json" Visible="false" />
</ItemGroup>
</Project>

View File

@@ -23,7 +23,9 @@ namespace attach
private async static Task AttachToPod(IKubernetes client, V1Pod pod)
{
var webSocket = await client.WebSocketNamespacedPodAttachAsync(pod.Metadata.Name, "default", pod.Spec.Containers[0].Name);
var webSocket =
await client.WebSocketNamespacedPodAttachAsync(pod.Metadata.Name, "default",
pod.Spec.Containers[0].Name);
var demux = new StreamDemuxer(webSocket);
demux.Start();

View File

@@ -20,7 +20,9 @@ namespace exec
private async static Task ExecInPod(IKubernetes client, V1Pod pod)
{
var webSocket = await client.WebSocketNamespacedPodExecAsync(pod.Metadata.Name, "default", "ls", pod.Spec.Containers[0].Name);
var webSocket =
await client.WebSocketNamespacedPodExecAsync(pod.Metadata.Name, "default", "ls",
pod.Spec.Containers[0].Name);
var demux = new StreamDemuxer(webSocket);
demux.Start();

View File

@@ -28,6 +28,7 @@ namespace httpClientFactory
{
_logger.LogInformation(item.Metadata.Name);
}
if (list.Items.Count == 0)
{
_logger.LogInformation("Empty!");

View File

@@ -12,10 +12,7 @@ namespace httpClientFactory
{
// Learn more about generic hosts at https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host
using (var host = new HostBuilder()
.ConfigureLogging((logging) =>
{
logging.AddConsole();
})
.ConfigureLogging((logging) => { logging.AddConsole(); })
.ConfigureServices((hostBuilderContext, services) =>
{
// Ideally this config would be read from the .net core config constructs,

View File

@@ -21,11 +21,13 @@ namespace simple
{
continue;
}
var labels = new List<string>();
foreach (var key in item.Spec.Selector)
{
labels.Add(key.Key + "=" + key.Value);
}
var labelStr = string.Join(",", labels.ToArray());
Console.WriteLine(labelStr);
var podList = client.ListNamespacedPod("default", labelSelector: labelStr);
@@ -33,10 +35,12 @@ namespace simple
{
Console.WriteLine(pod.Metadata.Name);
}
if (podList.Items.Count == 0)
{
Console.WriteLine("Empty!");
}
Console.WriteLine();
}
}

View File

@@ -19,9 +19,11 @@ namespace logs
Console.WriteLine("No pods!");
return;
}
var pod = list.Items[0];
var response = await client.ReadNamespacedPodLogWithHttpMessagesAsync(pod.Metadata.Name, pod.Metadata.NamespaceProperty, follow: true);
var response = await client.ReadNamespacedPodLogWithHttpMessagesAsync(pod.Metadata.Name,
pod.Metadata.NamespaceProperty, follow: true);
var stream = response.Body;
stream.CopyTo(Console.OpenStandardOutput());
}

View File

@@ -15,6 +15,7 @@ namespace @namespace
{
Console.WriteLine(item.Metadata.Name);
}
if (list.Items.Count == 0)
{
Console.WriteLine("Empty!");
@@ -41,6 +42,7 @@ namespace @namespace
{
return;
}
throw ex;
}
}
@@ -51,6 +53,7 @@ namespace @namespace
{
return;
}
throw ex;
}
}
@@ -68,13 +71,7 @@ namespace @namespace
ListNamespaces(client);
var ns = new V1Namespace
{
Metadata = new V1ObjectMeta
{
Name = "test"
}
};
var ns = new V1Namespace { Metadata = new V1ObjectMeta { Name = "test" } };
var result = client.CreateNamespace(ns);
Console.WriteLine(result);

View File

@@ -20,10 +20,7 @@ namespace patch
var name = pod.Metadata.Name;
PrintLabels(pod);
var newlabels = new Dictionary<string, string>(pod.Metadata.Labels)
{
["test"] = "test"
};
var newlabels = new Dictionary<string, string>(pod.Metadata.Labels) { ["test"] = "test" };
var patch = new JsonPatchDocument<V1Pod>();
patch.Replace(e => e.Metadata.Labels, newlabels);
client.PatchNamespacedPod(new V1Patch(patch), name, "default");
@@ -38,6 +35,7 @@ namespace patch
{
Console.WriteLine($"{k} : {v}");
}
Console.WriteLine("=-=-=-=-=-=-=-=-=-=-=");
}
}

View File

@@ -16,6 +16,7 @@ namespace simple
{
Console.WriteLine(item.Metadata.Name);
}
if (list.Items.Count == 0)
{
Console.WriteLine("Empty!");

View File

@@ -49,10 +49,16 @@ namespace KubernetesWatchGenerator
swagger = await SwaggerDocument.FromFileAsync(Path.Combine(args[1], "swagger.json.unprocessed"));
_schemaToNameMap = swagger.Definitions.ToDictionary(x => x.Value, x => x.Key);
_schemaDefinitionsInMultipleGroups = _schemaToNameMap.Values.Select(x =>
{
var parts = x.Split(".");
return new { FullName = x, Name = parts[parts.Length - 1], Version = parts[parts.Length - 2], Group = parts[parts.Length - 3] };
})
{
var parts = x.Split(".");
return new
{
FullName = x,
Name = parts[parts.Length - 1],
Version = parts[parts.Length - 2],
Group = parts[parts.Length - 3],
};
})
.GroupBy(x => new { x.Name, x.Version })
.Where(x => x.Count() > 1)
.SelectMany(x => x)
@@ -61,7 +67,15 @@ namespace KubernetesWatchGenerator
_classNameToPluralMap = swagger.Operations
.Where(x => x.Operation.OperationId.StartsWith("list"))
.Select(x => { return new { PluralName = x.Path.Split("/").Last(), ClassName = GetClassNameForSchemaDefinition(x.Operation.Responses["200"].ActualResponseSchema) }; })
.Select(x =>
{
return new
{
PluralName = x.Path.Split("/").Last(),
ClassName = GetClassNameForSchemaDefinition(x.Operation.Responses["200"]
.ActualResponseSchema),
};
})
.Distinct()
.ToDictionary(x => x.ClassName, x => x.PluralName);
@@ -75,7 +89,6 @@ namespace KubernetesWatchGenerator
.ToDictionary(x => x.Key, x => x.Value);
// Register helpers used in the templating.
Helpers.Register(nameof(ToXmlDoc), ToXmlDoc);
Helpers.Register(nameof(GetClassName), GetClassName);
@@ -92,30 +105,27 @@ namespace KubernetesWatchGenerator
// Generate the Watcher operations
// We skip operations where the name of the class in the C# client could not be determined correctly.
// That's usually because there are different version of the same object (e.g. for deployments).
var blacklistedOperations = new HashSet<string>()
{
};
var blacklistedOperations = new HashSet<string>() { };
var watchOperations = swagger.Operations.Where(
o => o.Path.Contains("/watch/")
&& o.Operation.ActualParameters.Any(p => p.Name == "name")
&& !blacklistedOperations.Contains(o.Operation.OperationId)).ToArray();
&& o.Operation.ActualParameters.Any(p => p.Name == "name")
&& !blacklistedOperations.Contains(o.Operation.OperationId)).ToArray();
// Render.
Render.FileToFile("IKubernetes.Watch.cs.template", watchOperations, Path.Combine(outputDirectory, "IKubernetes.Watch.cs"));
Render.FileToFile("Kubernetes.Watch.cs.template", watchOperations, Path.Combine(outputDirectory, "Kubernetes.Watch.cs"));
Render.FileToFile("IKubernetes.Watch.cs.template", watchOperations,
Path.Combine(outputDirectory, "IKubernetes.Watch.cs"));
Render.FileToFile("Kubernetes.Watch.cs.template", watchOperations,
Path.Combine(outputDirectory, "Kubernetes.Watch.cs"));
// Generate the interface declarations
var skippedTypes = new HashSet<string>()
{
"V1WatchEvent",
};
var skippedTypes = new HashSet<string>() { "V1WatchEvent", };
var definitions = swagger.Definitions.Values
.Where(
d => d.ExtensionData != null
&& d.ExtensionData.ContainsKey("x-kubernetes-group-version-kind")
&& !skippedTypes.Contains(GetClassName(d)));
&& d.ExtensionData.ContainsKey("x-kubernetes-group-version-kind")
&& !skippedTypes.Contains(GetClassName(d)));
var modelsDir = Path.Combine(outputDirectory, "Models");
_classesWithValidation = Directory.EnumerateFiles(modelsDir)
@@ -124,10 +134,12 @@ namespace KubernetesWatchGenerator
.Select(x => x.Class)
.ToHashSet();
Render.FileToFile("ModelExtensions.cs.template", definitions, Path.Combine(outputDirectory, "ModelExtensions.cs"));
Render.FileToFile("ModelExtensions.cs.template", definitions,
Path.Combine(outputDirectory, "ModelExtensions.cs"));
}
static void ToXmlDoc(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
static void ToXmlDoc(RenderContext context, IList<object> arguments, IDictionary<string, object> options,
RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is string)
{
@@ -147,13 +159,15 @@ namespace KubernetesWatchGenerator
{
first = false;
}
context.Write(line);
}
}
}
}
static void GetClassName(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
static void GetClassName(RenderContext context, IList<object> arguments, IDictionary<string, object> options,
RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is SwaggerOperation)
{
@@ -167,7 +181,8 @@ namespace KubernetesWatchGenerator
static string GetClassName(SwaggerOperation watchOperation)
{
var groupVersionKind = (Dictionary<string, object>)watchOperation.ExtensionData["x-kubernetes-group-version-kind"];
var groupVersionKind =
(Dictionary<string, object>)watchOperation.ExtensionData["x-kubernetes-group-version-kind"];
return GetClassName(groupVersionKind);
}
@@ -187,20 +202,23 @@ namespace KubernetesWatchGenerator
return GetClassName(groupVersionKind);
}
private static void GetInterfaceName(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
{
private static void GetInterfaceName(RenderContext context, IList<object> arguments,
IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is JsonSchema4)
{
context.Write(GetInterfaceName(arguments[0] as JsonSchema4));
}
}
static string GetClassNameForSchemaDefinition(JsonSchema4 definition)
{
if (definition.ExtensionData != null && definition.ExtensionData.ContainsKey("x-kubernetes-group-version-kind"))
if (definition.ExtensionData != null &&
definition.ExtensionData.ContainsKey("x-kubernetes-group-version-kind"))
{
return GetClassName(definition);
}
var schemaName = _schemaToNameMap[definition];
@@ -209,11 +227,14 @@ namespace KubernetesWatchGenerator
var version = parts[parts.Length - 2];
var entityName = parts[parts.Length - 1];
if (!_schemaDefinitionsInMultipleGroups.Contains(schemaName))
group = null;
{
@group = null;
}
var className = ToPascalCase($"{group}{version}{entityName}");
return className;
}
static string GetInterfaceName(JsonSchema4 definition)
{
var groupVersionKindElements = (object[])definition.ExtensionData["x-kubernetes-group-version-kind"];
@@ -235,7 +256,9 @@ namespace KubernetesWatchGenerator
if (definition.Properties.TryGetValue("items", out var itemsProperty))
{
var schema = itemsProperty.Type == JsonObjectType.Object ? itemsProperty.Reference : itemsProperty.Item.Reference;
var schema = itemsProperty.Type == JsonObjectType.Object
? itemsProperty.Reference
: itemsProperty.Item.Reference;
interfaces.Add($"IItems<{GetClassNameForSchemaDefinition(schema)}>");
}
@@ -245,13 +268,17 @@ namespace KubernetesWatchGenerator
}
if (_classesWithValidation.Contains(className))
{
interfaces.Add("IValidate");
}
var result = string.Join(", ", interfaces);
return result;
}
static void GetKind(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
static void GetKind(RenderContext context, IList<object> arguments, IDictionary<string, object> options,
RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is JsonSchema4)
{
@@ -267,15 +294,20 @@ namespace KubernetesWatchGenerator
return groupVersionKind["kind"] as string;
}
static void GetPlural(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
static void GetPlural(RenderContext context, IList<object> arguments, IDictionary<string, object> options,
RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is JsonSchema4)
{
var plural = GetPlural(arguments[0] as JsonSchema4);
if (plural != null)
{
context.Write($"\"{plural}\"");
}
else
{
context.Write("null");
}
}
}
@@ -285,7 +317,8 @@ namespace KubernetesWatchGenerator
return _classNameToPluralMap.GetValueOrDefault(className, null);
}
static void GetGroup(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
static void GetGroup(RenderContext context, IList<object> arguments, IDictionary<string, object> options,
RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is JsonSchema4)
{
@@ -301,7 +334,8 @@ namespace KubernetesWatchGenerator
return groupVersionKind["group"] as string;
}
static void GetMethodName(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
static void GetMethodName(RenderContext context, IList<object> arguments, IDictionary<string, object> options,
RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is SwaggerOperation)
{
@@ -322,14 +356,17 @@ namespace KubernetesWatchGenerator
return methodName;
}
static void GetDotNetType(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
static void GetDotNetType(RenderContext context, IList<object> arguments, IDictionary<string, object> options,
RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is SwaggerParameter)
{
var parameter = arguments[0] as SwaggerParameter;
context.Write(GetDotNetType(parameter.Type, parameter.Name, parameter.IsRequired));
}
else if (arguments != null && arguments.Count > 2 && arguments[0] != null && arguments[1] != null && arguments[2] != null && arguments[0] is JsonObjectType && arguments[1] is string && arguments[2] is bool)
else if (arguments != null && arguments.Count > 2 && arguments[0] != null && arguments[1] != null &&
arguments[2] != null && arguments[0] is JsonObjectType && arguments[1] is string &&
arguments[2] is bool)
{
context.Write(GetDotNetType((JsonObjectType)arguments[0], (string)arguments[1], (bool)arguments[2]));
}
@@ -380,7 +417,8 @@ namespace KubernetesWatchGenerator
}
}
static void GetDotNetName(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
static void GetDotNetName(RenderContext context, IList<object> arguments, IDictionary<string, object> options,
RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is SwaggerParameter)
{
@@ -408,9 +446,11 @@ namespace KubernetesWatchGenerator
return jsonName;
}
static void GetPathExpression(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
static void GetPathExpression(RenderContext context, IList<object> arguments,
IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is SwaggerOperationDescription)
if (arguments != null && arguments.Count > 0 && arguments[0] != null &&
arguments[0] is SwaggerOperationDescription)
{
var operation = arguments[0] as SwaggerOperationDescription;
context.Write(GetPathExpression(operation));
@@ -430,7 +470,8 @@ namespace KubernetesWatchGenerator
return pathExpression;
}
static void GetApiVersion(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
static void GetApiVersion(RenderContext context, IList<object> arguments, IDictionary<string, object> options,
RenderBlock fn, RenderBlock inverse)
{
if (arguments != null && arguments.Count > 0 && arguments[0] != null && arguments[0] is JsonSchema4)
{

View File

@@ -4,5 +4,10 @@
<!-- https://github.com/DotNetAnalyzers/StyleCopAnalyzers/tree/master/documentation -->
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA1027" Action="Error" />
<Rule Id="SA1101" Action="None" />
<Rule Id="SA1309" Action="None" />
<Rule Id="SA1503" Action="Error" />
</Rules>
</RuleSet>

View File

@@ -78,20 +78,12 @@ namespace k8s
/// <summary>
/// Gets the offset from which the next byte will be read. Increased every time a caller reads data.
/// </summary>
public int ReadWaterMark
{
get;
private set;
}
public int ReadWaterMark { get; private set; }
/// <summary>
/// Gets the offset to which the next byte will be written. Increased every time a caller writes data.
/// </summary>
public int WriteWaterMark
{
get;
private set;
}
public int WriteWaterMark { get; private set; }
/// <summary>
/// Gets the amount of bytes availble for reading.
@@ -192,7 +184,8 @@ namespace k8s
if (length > availableBeforeWrapping)
{
Array.Copy(data, offset + availableBeforeWrapping, this.buffer, 0, length - availableBeforeWrapping);
Array.Copy(data, offset + availableBeforeWrapping, this.buffer, 0,
length - availableBeforeWrapping);
this.WriteWaterMark = length - availableBeforeWrapping;
}
@@ -256,7 +249,8 @@ namespace k8s
if (toRead > availableBeforeWrapping)
{
Array.Copy(this.buffer, 0, data, offset + availableBeforeWrapping, toRead - availableBeforeWrapping);
Array.Copy(this.buffer, 0, data, offset + availableBeforeWrapping,
toRead - availableBeforeWrapping);
this.ReadWaterMark = toRead - availableBeforeWrapping;
}

View File

@@ -49,6 +49,7 @@ namespace k8s
{
keyData = Convert.FromBase64String(config.ClientCertificateKeyData);
}
if (!string.IsNullOrWhiteSpace(config.ClientKeyFilePath))
{
keyData = File.ReadAllBytes(config.ClientKeyFilePath);
@@ -63,6 +64,7 @@ namespace k8s
{
certData = Convert.FromBase64String(config.ClientCertificateData);
}
if (!string.IsNullOrWhiteSpace(config.ClientCertificateFilePath))
{
certData = File.ReadAllBytes(config.ClientCertificateFilePath);
@@ -82,6 +84,7 @@ namespace k8s
"Client certificates must be marked for digital signing. " +
"See https://github.com/kubernetes-client/csharp/issues/319");
}
object obj;
using (var reader = new StreamReader(new MemoryStream(keyData)))
{

View File

@@ -32,6 +32,6 @@ namespace k8s
/// has a Width and Height property.
/// </summary>
/// <seealso href="https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/client-go/tools/remotecommand/resize.go"/>
Resize
Resize,
}
}

View File

@@ -12,12 +12,12 @@ namespace k8s.Exceptions
}
public KubeConfigException(string message)
: base(message)
: base(message)
{
}
public KubeConfigException(string message, Exception inner)
: base(message, inner)
: base(message, inner)
{
}
}

View File

@@ -12,12 +12,12 @@ namespace k8s.Exceptions
}
public KubernetesClientException(string message)
: base(message)
: base(message)
{
}
public KubernetesClientException(string message, Exception inner)
: base(message, inner)
: base(message, inner)
{
}
}

View File

@@ -30,6 +30,7 @@ namespace k8s
/// <returns>
/// A <see cref="Task"/> which represents the asynchronous operation.
/// </returns>
Task<int> NamespacedPodExecAsync(string name, string @namespace, string container, IEnumerable<string> command, bool tty, ExecAsyncCallback action, CancellationToken cancellationToken);
Task<int> NamespacedPodExecAsync(string name, string @namespace, string container, IEnumerable<string> command,
bool tty, ExecAsyncCallback action, CancellationToken cancellationToken);
}
}

View File

@@ -54,6 +54,11 @@ namespace k8s
/// <returns>
/// A <see cref="Task"/> which represents the asynchronous operation, and returns a new watcher.
/// </returns>
Task<Watcher<T>> WatchObjectAsync<T>(string path, string @continue = null, string fieldSelector = null, bool? includeUninitialized = null, string labelSelector = null, int? limit = null, bool? pretty = null, int? timeoutSeconds = null, string resourceVersion = null, Dictionary<string, List<string>> customHeaders = null, Action<WatchEventType, T> onEvent = null, Action<Exception> onError = null, Action onClosed = null, CancellationToken cancellationToken = default(CancellationToken));
Task<Watcher<T>> WatchObjectAsync<T>(string path, string @continue = null, string fieldSelector = null,
bool? includeUninitialized = null, string labelSelector = null, int? limit = null, bool? pretty = null,
int? timeoutSeconds = null, string resourceVersion = null,
Dictionary<string, List<string>> customHeaders = null, Action<WatchEventType, T> onEvent = null,
Action<Exception> onError = null, Action onClosed = null,
CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@@ -56,7 +56,10 @@ namespace k8s
/// <return>
/// A <see cref="ClientWebSocket"/> which can be used to communicate with the process running in the pod.
/// </return>
Task<WebSocket> WebSocketNamespacedPodExecAsync(string name, string @namespace = "default", string command = null, string container = null, bool stderr = true, bool stdin = true, bool stdout = true, bool tty = true, string webSocketSubProtol = null, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
Task<WebSocket> WebSocketNamespacedPodExecAsync(string name, string @namespace = "default",
string command = null, string container = null, bool stderr = true, bool stdin = true, bool stdout = true,
bool tty = true, string webSocketSubProtol = null, Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Executes a command in a pod.
@@ -107,7 +110,11 @@ namespace k8s
/// <return>
/// A <see cref="ClientWebSocket"/> which can be used to communicate with the process running in the pod.
/// </return>
Task<WebSocket> WebSocketNamespacedPodExecAsync(string name, string @namespace = "default", IEnumerable<string> command = null, string container = null, bool stderr = true, bool stdin = true, bool stdout = true, bool tty = true, string webSocketSubProtol = null, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
Task<WebSocket> WebSocketNamespacedPodExecAsync(string name, string @namespace = "default",
IEnumerable<string> command = null, string container = null, bool stderr = true, bool stdin = true,
bool stdout = true, bool tty = true, string webSocketSubProtol = null,
Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Executes a command in a pod.
@@ -158,7 +165,12 @@ namespace k8s
/// <return>
/// A <see cref="IStreamDemuxer"/> which can be used to communicate with the process running in the pod.
/// </return>
Task<IStreamDemuxer> MuxedStreamNamespacedPodExecAsync(string name, string @namespace = "default", IEnumerable<string> command = null, string container = null, bool stderr = true, bool stdin = true, bool stdout = true, bool tty = true, string webSocketSubProtol = WebSocketProtocol.V4BinaryWebsocketProtocol, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
Task<IStreamDemuxer> MuxedStreamNamespacedPodExecAsync(string name, string @namespace = "default",
IEnumerable<string> command = null, string container = null, bool stderr = true, bool stdin = true,
bool stdout = true, bool tty = true,
string webSocketSubProtol = WebSocketProtocol.V4BinaryWebsocketProtocol,
Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Start port forwarding one or more ports of a pod.
@@ -182,7 +194,9 @@ namespace k8s
/// <param name='cancellationToken'>
/// The cancellation token.
/// </param>
Task<WebSocket> WebSocketNamespacedPodPortForwardAsync(string name, string @namespace, IEnumerable<int> ports, string webSocketSubProtocol = null, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
Task<WebSocket> WebSocketNamespacedPodPortForwardAsync(string name, string @namespace, IEnumerable<int> ports,
string webSocketSubProtocol = null, Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// connect GET requests to attach of Pod
@@ -239,6 +253,9 @@ namespace k8s
/// <return>
/// A response object containing the response body and response headers.
/// </return>
Task<WebSocket> WebSocketNamespacedPodAttachAsync(string name, string @namespace, string container = default(string), bool stderr = true, bool stdin = false, bool stdout = true, bool tty = false, string webSocketSubProtol = null, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
Task<WebSocket> WebSocketNamespacedPodAttachAsync(string name, string @namespace,
string container = default(string), bool stderr = true, bool stdin = false, bool stdout = true,
bool tty = false, string webSocketSubProtol = null, Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@@ -74,7 +74,8 @@ namespace k8s
/// <returns>
/// A <see cref="Task"/> which represents the asynchronous operation.
/// </returns>
Task Write(ChannelIndex index, byte[] buffer, int offset, int count, CancellationToken cancellationToken = default(CancellationToken));
Task Write(ChannelIndex index, byte[] buffer, int offset, int count,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Directly writes data to a channel.
@@ -97,6 +98,7 @@ namespace k8s
/// <returns>
/// A <see cref="Task"/> which represents the asynchronous operation.
/// </returns>
Task Write(byte index, byte[] buffer, int offset, int count, CancellationToken cancellationToken = default(CancellationToken));
Task Write(byte index, byte[] buffer, int offset, int count,
CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@@ -9,6 +9,5 @@ namespace k8s
/// Validate the object.
/// </summary>
void Validate();
}
}

View File

@@ -24,6 +24,7 @@ namespace k8s.Models
{
return null;
}
return new IntstrIntOrString(scalar.Value);
}
finally
@@ -31,6 +32,7 @@ namespace k8s.Models
parser.MoveNext();
}
}
throw new InvalidOperationException(parser.Current?.ToString());
}
@@ -98,9 +100,21 @@ namespace k8s.Models
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != this.GetType())
{
return false;
}
return Equals((IntstrIntOrString)obj);
}

View File

@@ -20,6 +20,5 @@ namespace k8s.KubeConfigModels
/// </summary>
[YamlMember(Alias = "config")]
public Dictionary<string, string> Config { get; set; }
}
}

View File

@@ -5,11 +5,8 @@ namespace k8s.KubeConfigModels
{
public class ExecCredentialResponse
{
[JsonProperty("apiVersion")]
public string ApiVersion { get; set; }
[JsonProperty("kind")]
public string Kind { get; set; }
[JsonProperty("status")]
public IDictionary<string, string> Status { get; set; }
[JsonProperty("apiVersion")] public string ApiVersion { get; set; }
[JsonProperty("kind")] public string Kind { get; set; }
[JsonProperty("status")] public IDictionary<string, string> Status { get; set; }
}
}

View File

@@ -5,18 +5,20 @@ namespace k8s.KubeConfigModels
{
public class ExternalExecution
{
[YamlMember(Alias = "apiVersion")]
public string ApiVersion { get; set; }
[YamlMember(Alias = "apiVersion")] public string ApiVersion { get; set; }
/// <summary>
/// The command to execute. Required.
/// </summary>
[YamlMember(Alias = "command")]
public string Command { get; set; }
/// <summary>
/// Environment variables to set when executing the plugin. Optional.
/// </summary>
[YamlMember(Alias = "env")]
public IDictionary<string, string> EnvironmentVariables { get; set; }
/// <summary>
/// Arguments to pass when executing the plugin. Optional.
/// </summary>

View File

@@ -19,11 +19,9 @@ namespace k8s.KubeConfigModels
[YamlMember(Alias = "preferences")]
public IDictionary<string, object> Preferences { get; set; }
[YamlMember(Alias = "apiVersion")]
public string ApiVersion { get; set; }
[YamlMember(Alias = "apiVersion")] public string ApiVersion { get; set; }
[YamlMember(Alias = "kind")]
public string Kind { get; set; }
[YamlMember(Alias = "kind")] public string Kind { get; set; }
/// <summary>
/// Gets or sets the name of the context that you would like to use by default.

View File

@@ -38,7 +38,8 @@ namespace k8s
/// <param name="disposeHttpClient">
/// Whether or not the <see cref="Kubernetes"/> object should own the lifetime of <paramref name="httpClient"/>.
/// </param>
public Kubernetes(KubernetesClientConfiguration config, HttpClient httpClient, bool disposeHttpClient) : this(httpClient, disposeHttpClient)
public Kubernetes(KubernetesClientConfiguration config, HttpClient httpClient, bool disposeHttpClient) : this(
httpClient, disposeHttpClient)
{
ValidateConfig(config);
CaCerts = config.SslCaCerts;
@@ -94,10 +95,11 @@ namespace k8s
if (config.SkipTlsVerify)
{
#if NET452
((WebRequestHandler) HttpClientHandler).ServerCertificateValidationCallback =
((WebRequestHandler)HttpClientHandler).ServerCertificateValidationCallback =
(sender, certificate, chain, sslPolicyErrors) => true;
#elif XAMARINIOS1_0 || MONOANDROID8_1
System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) =>
{
return true;
};
@@ -113,14 +115,17 @@ namespace k8s
throw new KubeConfigException("A CA must be set when SkipTlsVerify === false");
}
#if NET452
((WebRequestHandler) HttpClientHandler).ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
((WebRequestHandler)HttpClientHandler).ServerCertificateValidationCallback =
(sender, certificate, chain, sslPolicyErrors) =>
{
return Kubernetes.CertificateValidationCallBack(sender, CaCerts, certificate, chain, sslPolicyErrors);
};
#elif XAMARINIOS1_0
System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) =>
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) =>
{
var cert = new X509Certificate2(certificate);
var cert
= new X509Certificate2(certificate);
return Kubernetes.CertificateValidationCallBack(sender, CaCerts, cert, chain, sslPolicyErrors);
};
#elif MONOANDROID8_1
@@ -128,22 +133,28 @@ namespace k8s
foreach (X509Certificate2 caCert in CaCerts)
{
using (var certStream = new System.IO.MemoryStream(caCert.RawData))
using (var certStream
= new System.IO.MemoryStream(caCert.RawData))
{
Java.Security.Cert.Certificate cert = Java.Security.Cert.CertificateFactory.GetInstance("X509").GenerateCertificate(certStream);
Java.Security.Cert.Certificate cert
= Java.Security.Cert.CertificateFactory.GetInstance("X509").GenerateCertificate(certStream);
certList.Add(cert);
}
}
var handler = (Xamarin.Android.Net.AndroidClientHandler)this.HttpClientHandler;
var handler
= (Xamarin.Android.Net.AndroidClientHandler)this.HttpClientHandler;
handler.TrustedCerts = certList;
handler.TrustedCerts
= certList;
#else
HttpClientHandler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
return Kubernetes.CertificateValidationCallBack(sender, CaCerts, certificate, chain, sslPolicyErrors);
};
HttpClientHandler.ServerCertificateCustomValidationCallback =
(sender, certificate, chain, sslPolicyErrors) =>
{
return Kubernetes.CertificateValidationCallBack(sender, CaCerts, certificate, chain,
sslPolicyErrors);
};
#endif
}
}
@@ -168,7 +179,9 @@ namespace k8s
/// <summary>A <see cref="DelegatingHandler"/> that simply forwards a request with no further processing.</summary>
private sealed class ForwardingHandler : DelegatingHandler
{
public ForwardingHandler(HttpMessageHandler handler) : base(handler) { }
public ForwardingHandler(HttpMessageHandler handler) : base(handler)
{
}
}
private void AppendDelegatingHandler<T>() where T : DelegatingHandler, new()
@@ -183,10 +196,7 @@ namespace k8s
{
// last one
// append watcher handler between to last handler
cur.InnerHandler = new T
{
InnerHandler = cur.InnerHandler
};
cur.InnerHandler = new T { InnerHandler = cur.InnerHandler };
break;
}
@@ -211,11 +221,16 @@ namespace k8s
for (int i = handlers.Length - 1; i >= 0; i--)
{
DelegatingHandler handler = handlers[i];
while (handler.InnerHandler is DelegatingHandler d) handler = d;
while (handler.InnerHandler is DelegatingHandler d)
{
handler = d;
}
handler.InnerHandler = FirstMessageHandler;
FirstMessageHandler = handlers[i];
}
}
AppendDelegatingHandler<WatcherDelegatingHandler>();
HttpClient = new HttpClient(FirstMessageHandler, false);
}
@@ -287,7 +302,11 @@ namespace k8s
/// </summary>
public static ServiceClientCredentials CreateCredentials(KubernetesClientConfiguration config)
{
if (config == null) throw new ArgumentNullException(nameof(config));
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}
if (!string.IsNullOrEmpty(config.AccessToken))
{
return new TokenCredentials(config.AccessToken);
@@ -296,6 +315,7 @@ namespace k8s
{
return new BasicAuthenticationCredentials() { UserName = config.Username, Password = config.Password };
}
return null;
}
}

View File

@@ -12,7 +12,8 @@ namespace k8s
{
public partial class Kubernetes
{
public async Task<int> NamespacedPodExecAsync(string name, string @namespace, string container, IEnumerable<string> command, bool tty, ExecAsyncCallback action, CancellationToken cancellationToken)
public async Task<int> NamespacedPodExecAsync(string name, string @namespace, string container,
IEnumerable<string> command, bool tty, ExecAsyncCallback action, CancellationToken cancellationToken)
{
// All other parameters are being validated by MuxedStreamNamespacedPodExecAsync
if (action == null)
@@ -22,7 +23,9 @@ namespace k8s
try
{
using (var muxedStream = await this.MuxedStreamNamespacedPodExecAsync(name: name, @namespace: @namespace, command: command, container: container, tty: tty, cancellationToken: cancellationToken).ConfigureAwait(false))
using (var muxedStream = await this.MuxedStreamNamespacedPodExecAsync(name: name,
@namespace: @namespace, command: command, container: container, tty: tty,
cancellationToken: cancellationToken).ConfigureAwait(false))
using (Stream stdIn = muxedStream.GetStream(null, ChannelIndex.StdIn))
using (Stream stdOut = muxedStream.GetStream(ChannelIndex.StdOut, null))
using (Stream stdErr = muxedStream.GetStream(ChannelIndex.StdErr, null))

View File

@@ -13,7 +13,12 @@ namespace k8s
public partial class Kubernetes
{
/// <inheritdoc/>
public async Task<Watcher<T>> WatchObjectAsync<T>(string path, string @continue = null, string fieldSelector = null, bool? includeUninitialized = null, string labelSelector = null, int? limit = null, bool? pretty = null, int? timeoutSeconds = null, string resourceVersion = null, Dictionary<string, List<string>> customHeaders = null, Action<WatchEventType, T> onEvent = null, Action<Exception> onError = null, Action onClosed = null, CancellationToken cancellationToken = default(CancellationToken))
public async Task<Watcher<T>> WatchObjectAsync<T>(string path, string @continue = null,
string fieldSelector = null, bool? includeUninitialized = null, string labelSelector = null,
int? limit = null, bool? pretty = null, int? timeoutSeconds = null, string resourceVersion = null,
Dictionary<string, List<string>> customHeaders = null, Action<WatchEventType, T> onEvent = null,
Action<Exception> onError = null, Action onClosed = null,
CancellationToken cancellationToken = default(CancellationToken))
{
// Tracing
bool _shouldTrace = ServiceClientTracing.IsEnabled;
@@ -59,7 +64,8 @@ namespace k8s
if (includeUninitialized != null)
{
Utilities.AddQueryParameter(query, "includeUninitialized", includeUninitialized.Value ? "true" : "false");
Utilities.AddQueryParameter(query, "includeUninitialized",
includeUninitialized.Value ? "true" : "false");
}
if (!string.IsNullOrEmpty(labelSelector))
@@ -87,7 +93,11 @@ namespace k8s
Utilities.AddQueryParameter(query, "resourceVersion", resourceVersion);
}
uriBuilder.Query = query.Length == 0 ? "" : query.ToString(1, query.Length - 1); // UriBuilder.Query doesn't like leading '?' chars, so trim it
uriBuilder.Query =
query.Length == 0
? ""
: query.ToString(1,
query.Length - 1); // UriBuilder.Query doesn't like leading '?' chars, so trim it
// Create HTTP transport objects
var httpRequest = new HttpRequestMessage(HttpMethod.Get, uriBuilder.ToString());
@@ -120,7 +130,9 @@ namespace k8s
}
cancellationToken.ThrowIfCancellationRequested();
var httpResponse = await HttpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var httpResponse = await HttpClient
.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
.ConfigureAwait(false);
if (_shouldTrace)
{
@@ -133,7 +145,8 @@ namespace k8s
{
string responseContent = string.Empty;
var ex = new HttpOperationException(string.Format("Operation returned an invalid status code '{0}'", httpResponse.StatusCode));
var ex = new HttpOperationException(string.Format("Operation returned an invalid status code '{0}'",
httpResponse.StatusCode));
if (httpResponse.Content != null)
{
responseContent = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);

View File

@@ -27,21 +27,37 @@ namespace k8s
public Func<WebSocketBuilder> CreateWebSocketBuilder { get; set; } = () => new WebSocketBuilder();
/// <inheritdoc/>
public Task<WebSocket> WebSocketNamespacedPodExecAsync(string name, string @namespace = "default", string command = null, string container = null, bool stderr = true, bool stdin = true, bool stdout = true, bool tty = true, string webSocketSubProtol = null, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
public Task<WebSocket> WebSocketNamespacedPodExecAsync(string name, string @namespace = "default",
string command = null, string container = null, bool stderr = true, bool stdin = true, bool stdout = true,
bool tty = true, string webSocketSubProtol = null, Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken))
{
return WebSocketNamespacedPodExecAsync(name, @namespace, new string[] { command }, container, stderr, stdin, stdout, tty, webSocketSubProtol, customHeaders, cancellationToken);
return WebSocketNamespacedPodExecAsync(name, @namespace, new string[] { command }, container, stderr, stdin,
stdout, tty, webSocketSubProtol, customHeaders, cancellationToken);
}
/// <inheritdoc/>
public virtual async Task<IStreamDemuxer> MuxedStreamNamespacedPodExecAsync(string name, string @namespace = "default", IEnumerable<string> command = null, string container = null, bool stderr = true, bool stdin = true, bool stdout = true, bool tty = true, string webSocketSubProtol = WebSocketProtocol.V4BinaryWebsocketProtocol, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
public virtual async Task<IStreamDemuxer> MuxedStreamNamespacedPodExecAsync(string name,
string @namespace = "default", IEnumerable<string> command = null, string container = null,
bool stderr = true, bool stdin = true, bool stdout = true, bool tty = true,
string webSocketSubProtol = WebSocketProtocol.V4BinaryWebsocketProtocol,
Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken))
{
WebSocket webSocket = await this.WebSocketNamespacedPodExecAsync(name: name, @namespace: @namespace, command: command, container: container, tty: tty, cancellationToken: cancellationToken).ConfigureAwait(false);
WebSocket webSocket = await this.WebSocketNamespacedPodExecAsync(name: name, @namespace: @namespace,
command: command, container: container, tty: tty, cancellationToken: cancellationToken)
.ConfigureAwait(false);
StreamDemuxer muxer = new StreamDemuxer(webSocket);
return muxer;
}
/// <inheritdoc/>
public virtual Task<WebSocket> WebSocketNamespacedPodExecAsync(string name, string @namespace = "default", IEnumerable<string> command = null, string container = null, bool stderr = true, bool stdin = true, bool stdout = true, bool tty = true, string webSocketSubProtol = WebSocketProtocol.V4BinaryWebsocketProtocol, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
public virtual Task<WebSocket> WebSocketNamespacedPodExecAsync(string name, string @namespace = "default",
IEnumerable<string> command = null, string container = null, bool stderr = true, bool stdin = true,
bool stdout = true, bool tty = true,
string webSocketSubProtol = WebSocketProtocol.V4BinaryWebsocketProtocol,
Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken))
{
if (name == null)
{
@@ -68,7 +84,8 @@ namespace k8s
{
if (c.Length > 0 && c[0] == 0xfeff)
{
throw new InvalidOperationException($"Detected an attempt to execute a command which starts with a Unicode byte order mark (BOM). This is probably incorrect. The command was {c}");
throw new InvalidOperationException(
$"Detected an attempt to execute a command which starts with a Unicode byte order mark (BOM). This is probably incorrect. The command was {c}");
}
}
@@ -89,7 +106,8 @@ namespace k8s
tracingParameters.Add("tty", tty);
tracingParameters.Add("webSocketSubProtol", webSocketSubProtol);
tracingParameters.Add("cancellationToken", cancellationToken);
ServiceClientTracing.Enter(_invocationId, this, nameof(WebSocketNamespacedPodExecAsync), tracingParameters);
ServiceClientTracing.Enter(_invocationId, this, nameof(WebSocketNamespacedPodExecAsync),
tracingParameters);
}
// Construct URL
@@ -115,17 +133,25 @@ namespace k8s
Utilities.AddQueryParameter(query, "container", container);
}
query.Append("&stderr=").Append(stderr ? '1' : '0'); // the query string is guaranteed not to be empty here because it has a 'command' param
query.Append("&stderr=")
.Append(stderr
? '1'
: '0'); // the query string is guaranteed not to be empty here because it has a 'command' param
query.Append("&stdin=").Append(stdin ? '1' : '0');
query.Append("&stdout=").Append(stdout ? '1' : '0');
query.Append("&tty=").Append(tty ? '1' : '0');
uriBuilder.Query = query.ToString(1, query.Length - 1); // UriBuilder.Query doesn't like leading '?' chars, so trim it
uriBuilder.Query =
query.ToString(1, query.Length - 1); // UriBuilder.Query doesn't like leading '?' chars, so trim it
return this.StreamConnectAsync(uriBuilder.Uri, _invocationId, webSocketSubProtol, customHeaders, cancellationToken);
return this.StreamConnectAsync(uriBuilder.Uri, _invocationId, webSocketSubProtol, customHeaders,
cancellationToken);
}
/// <inheritdoc/>
public Task<WebSocket> WebSocketNamespacedPodPortForwardAsync(string name, string @namespace, IEnumerable<int> ports, string webSocketSubProtocol = null, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
public Task<WebSocket> WebSocketNamespacedPodPortForwardAsync(string name, string @namespace,
IEnumerable<int> ports, string webSocketSubProtocol = null,
Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken))
{
if (name == null)
{
@@ -154,7 +180,8 @@ namespace k8s
tracingParameters.Add("ports", ports);
tracingParameters.Add("webSocketSubProtocol", webSocketSubProtocol);
tracingParameters.Add("cancellationToken", cancellationToken);
ServiceClientTracing.Enter(_invocationId, this, nameof(WebSocketNamespacedPodPortForwardAsync), tracingParameters);
ServiceClientTracing.Enter(_invocationId, this, nameof(WebSocketNamespacedPodPortForwardAsync),
tracingParameters);
}
// Construct URL
@@ -171,16 +198,25 @@ namespace k8s
var q = new StringBuilder();
foreach (var port in ports)
{
if (q.Length != 0) q.Append('&');
if (q.Length != 0)
{
q.Append('&');
}
q.Append("ports=").Append(port.ToString(CultureInfo.InvariantCulture));
}
uriBuilder.Query = q.ToString();
return StreamConnectAsync(uriBuilder.Uri, _invocationId, webSocketSubProtocol, customHeaders, cancellationToken);
return StreamConnectAsync(uriBuilder.Uri, _invocationId, webSocketSubProtocol, customHeaders,
cancellationToken);
}
/// <inheritdoc/>
public Task<WebSocket> WebSocketNamespacedPodAttachAsync(string name, string @namespace, string container = default(string), bool stderr = true, bool stdin = false, bool stdout = true, bool tty = false, string webSocketSubProtol = null, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
public Task<WebSocket> WebSocketNamespacedPodAttachAsync(string name, string @namespace,
string container = default(string), bool stderr = true, bool stdin = false, bool stdout = true,
bool tty = false, string webSocketSubProtol = null, Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken))
{
if (name == null)
{
@@ -208,7 +244,8 @@ namespace k8s
tracingParameters.Add("tty", tty);
tracingParameters.Add("webSocketSubProtol", webSocketSubProtol);
tracingParameters.Add("cancellationToken", cancellationToken);
ServiceClientTracing.Enter(_invocationId, this, nameof(WebSocketNamespacedPodAttachAsync), tracingParameters);
ServiceClientTracing.Enter(_invocationId, this, nameof(WebSocketNamespacedPodAttachAsync),
tracingParameters);
}
// Construct URL
@@ -228,12 +265,16 @@ namespace k8s
query.Append("&stdout=").Append(stdout ? '1' : '0');
query.Append("&tty=").Append(tty ? '1' : '0');
Utilities.AddQueryParameter(query, "container", container);
uriBuilder.Query = query.ToString(1, query.Length - 1); // UriBuilder.Query doesn't like leading '?' chars, so trim it
uriBuilder.Query =
query.ToString(1, query.Length - 1); // UriBuilder.Query doesn't like leading '?' chars, so trim it
return StreamConnectAsync(uriBuilder.Uri, _invocationId, webSocketSubProtol, customHeaders, cancellationToken);
return StreamConnectAsync(uriBuilder.Uri, _invocationId, webSocketSubProtol, customHeaders,
cancellationToken);
}
protected async Task<WebSocket> StreamConnectAsync(Uri uri, string invocationId = null, string webSocketSubProtocol = null, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
protected async Task<WebSocket> StreamConnectAsync(Uri uri, string invocationId = null,
string webSocketSubProtocol = null, Dictionary<string, List<string>> customHeaders = null,
CancellationToken cancellationToken = default(CancellationToken))
{
bool _shouldTrace = ServiceClientTracing.IsEnabled;
@@ -301,9 +342,13 @@ namespace k8s
WebSocket webSocket = null;
try
{
webSocket = await webSocketBuilder.BuildAndConnectAsync(uri, CancellationToken.None).ConfigureAwait(false);
webSocket = await webSocketBuilder.BuildAndConnectAsync(uri, CancellationToken.None)
.ConfigureAwait(false);
}
catch (WebSocketException wse) when (wse.WebSocketErrorCode == WebSocketError.HeaderError || (wse.InnerException is WebSocketException && ((WebSocketException)wse.InnerException).WebSocketErrorCode == WebSocketError.HeaderError))
catch (WebSocketException wse) when (wse.WebSocketErrorCode == WebSocketError.HeaderError ||
(wse.InnerException is WebSocketException &&
((WebSocketException)wse.InnerException).WebSocketErrorCode ==
WebSocketError.HeaderError))
{
// This usually indicates the server sent an error message, like 400 Bad Request. Unfortunately, the WebSocket client
// class doesn't give us a lot of information about what went wrong. So, retry the connection.
@@ -332,11 +377,13 @@ namespace k8s
status = SafeJsonConvert.DeserializeObject<V1Status>(content);
}
var ex = new HttpOperationException($"The operation returned an invalid status code: {response.StatusCode}", wse)
{
Response = new HttpResponseMessageWrapper(response, content),
Body = status != null ? (object)status : content,
};
var ex =
new HttpOperationException(
$"The operation returned an invalid status code: {response.StatusCode}", wse)
{
Response = new HttpResponseMessageWrapper(response, content),
Body = status != null ? (object)status : content,
};
response.Dispose();
@@ -362,15 +409,18 @@ namespace k8s
#if (NET452 || NETSTANDARD2_0)
if (this.CaCerts != null)
{
webSocketBuilder.CleanupServerCertificateValidationCallback(this.ServerCertificateValidationCallback);
webSocketBuilder.CleanupServerCertificateValidationCallback(
this.ServerCertificateValidationCallback);
}
#endif
}
return webSocket;
}
#if (NET452 || NETSTANDARD2_0)
internal bool ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
internal bool ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
return Kubernetes.CertificateValidationCallBack(sender, this.CaCerts, certificate, chain, sslPolicyErrors);
}

View File

@@ -49,7 +49,8 @@ namespace k8s
var kubeconfig = Environment.GetEnvironmentVariable(KubeConfigEnvironmentVariable);
if (kubeconfig != null)
{
var configList = kubeconfig.Split(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ';' : ':').Select((s) => new FileInfo(s));
var configList = kubeconfig.Split(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ';' : ':')
.Select((s) => new FileInfo(s));
var k8sConfig = LoadKubeConfig(configList.ToArray());
return BuildConfigFromConfigObject(k8sConfig);
}
@@ -95,7 +96,8 @@ namespace k8s
public static KubernetesClientConfiguration BuildConfigFromConfigFile(FileInfo kubeconfig,
string currentContext = null, string masterUrl = null, bool useRelativePaths = true)
{
return BuildConfigFromConfigFileAsync(kubeconfig, currentContext, masterUrl, useRelativePaths).GetAwaiter().GetResult();
return BuildConfigFromConfigFileAsync(kubeconfig, currentContext, masterUrl, useRelativePaths).GetAwaiter()
.GetResult();
}
/// <summary>
@@ -106,7 +108,6 @@ namespace k8s
/// <param name="masterUrl">override the kube api server endpoint, set null if do not want to override</param>
/// <param name="useRelativePaths">When <see langword="true"/>, the paths in the kubeconfig file will be considered to be relative to the directory in which the kubeconfig
/// file is located. When <see langword="false"/>, the paths will be considered to be relative to the current working directory.</param>
public static async Task<KubernetesClientConfiguration> BuildConfigFromConfigFileAsync(FileInfo kubeconfig,
string currentContext = null, string masterUrl = null, bool useRelativePaths = true)
{
@@ -166,10 +167,12 @@ namespace k8s
/// <param name="k8sConfig">A <see cref="K8SConfiguration"/>, for example loaded from <see cref="LoadKubeConfigAsync(string, bool)" /></param>
/// <param name="currentContext">Override the current context in config, set null if do not want to override</param>
/// <param name="masterUrl">Override the Kubernetes API server endpoint, set null if do not want to override</param>
public static KubernetesClientConfiguration BuildConfigFromConfigObject(K8SConfiguration k8SConfig, string currentContext = null, string masterUrl = null)
public static KubernetesClientConfiguration BuildConfigFromConfigObject(K8SConfiguration k8SConfig,
string currentContext = null, string masterUrl = null)
=> GetKubernetesClientConfiguration(currentContext, masterUrl, k8SConfig);
private static KubernetesClientConfiguration GetKubernetesClientConfiguration(string currentContext, string masterUrl, K8SConfiguration k8SConfig)
private static KubernetesClientConfiguration GetKubernetesClientConfiguration(string currentContext,
string masterUrl, K8SConfiguration k8SConfig)
{
var k8SConfiguration = new KubernetesClientConfiguration();
@@ -179,6 +182,7 @@ namespace k8s
{
k8SConfiguration.InitializeContext(k8SConfig, currentContext);
}
if (!string.IsNullOrWhiteSpace(masterUrl))
{
k8SConfiguration.Host = masterUrl;
@@ -237,6 +241,7 @@ namespace k8s
{
throw new KubeConfigException($"Cluster not found for context `{activeContext}` in kubeconfig");
}
if (string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.Server))
{
throw new KubeConfigException($"Server not found for current-context `{activeContext}` in kubeconfig");
@@ -259,7 +264,8 @@ namespace k8s
}
else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority))
{
SslCaCerts = new X509Certificate2Collection(new X509Certificate2(GetFullPath(k8SConfig, clusterDetails.ClusterEndpoint.CertificateAuthority)));
SslCaCerts = new X509Certificate2Collection(new X509Certificate2(GetFullPath(k8SConfig,
clusterDetails.ClusterEndpoint.CertificateAuthority)));
}
}
}
@@ -293,7 +299,7 @@ namespace k8s
userCredentialsFound = true;
}
else if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.UserName) &&
!string.IsNullOrWhiteSpace(userDetails.UserCredentials.Password))
!string.IsNullOrWhiteSpace(userDetails.UserCredentials.Password))
{
Username = userDetails.UserCredentials.UserName;
Password = userDetails.UserCredentials.Password;
@@ -320,7 +326,7 @@ namespace k8s
if (userDetails.UserCredentials.AuthProvider != null)
{
if (userDetails.UserCredentials.AuthProvider.Config != null
&& userDetails.UserCredentials.AuthProvider.Config.ContainsKey("access-token"))
&& userDetails.UserCredentials.AuthProvider.Config.ContainsKey("access-token"))
{
switch (userDetails.UserCredentials.AuthProvider.Name)
{
@@ -332,24 +338,25 @@ namespace k8s
var expiresOn = Int32.Parse(config["expires-on"]);
DateTimeOffset expires;
#if NET452
var epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
expires = epoch.AddSeconds(expiresOn);
var epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
expires
= epoch.AddSeconds(expiresOn);
#else
expires = DateTimeOffset.FromUnixTimeSeconds(expiresOn);
#endif
if (DateTimeOffset.Compare(expires
, DateTimeOffset.Now)
<= 0)
, DateTimeOffset.Now)
<= 0)
{
var tenantId = config["tenant-id"];
var clientId = config["client-id"];
var apiServerId = config["apiserver-id"];
var refresh = config["refresh-token"];
var newToken = RenewAzureToken(tenantId
, clientId
, apiServerId
, refresh);
, clientId
, apiServerId
, refresh);
config["access-token"] = newToken;
}
}
@@ -365,11 +372,11 @@ namespace k8s
if (config.ContainsKey(keyExpire))
{
if (DateTimeOffset.TryParse(config[keyExpire]
, out DateTimeOffset expires))
, out DateTimeOffset expires))
{
if (DateTimeOffset.Compare(expires
, DateTimeOffset.Now)
<= 0)
, DateTimeOffset.Now)
<= 0)
{
throw new KubeConfigException("Refresh not supported.");
}
@@ -388,10 +395,15 @@ namespace k8s
if (userDetails.UserCredentials.ExternalExecution != null)
{
if (string.IsNullOrWhiteSpace(userDetails.UserCredentials.ExternalExecution.Command))
{
throw new KubeConfigException(
"External command execution to receive user credentials must include a command to execute");
}
if (string.IsNullOrWhiteSpace(userDetails.UserCredentials.ExternalExecution.ApiVersion))
{
throw new KubeConfigException("External command execution missing ApiVersion key");
}
var token = ExecuteExternalCommand(userDetails.UserCredentials.ExternalExecution);
AccessToken = token;
@@ -426,12 +438,9 @@ namespace k8s
{
var execInfo = new Dictionary<string, dynamic>
{
{"apiVersion", config.ApiVersion},
{"kind", "ExecCredentials"},
{"spec", new Dictionary<string, bool>
{
{"interactive", Environment.UserInteractive}
}}
{"apiVersion", config.ApiVersion },
{"kind", "ExecCredentials" },
{"spec", new Dictionary<string, bool> {{"interactive", Environment.UserInteractive } } },
};
var process = new Process();
@@ -440,13 +449,20 @@ namespace k8s
JsonConvert.SerializeObject(execInfo));
if (config.EnvironmentVariables != null)
{
foreach (var configEnvironmentVariableKey in config.EnvironmentVariables.Keys)
{
process.StartInfo.Environment.Add(key: configEnvironmentVariableKey,
value: config.EnvironmentVariables[configEnvironmentVariableKey]);
}
}
process.StartInfo.FileName = config.Command;
if (config.Arguments != null)
{
process.StartInfo.Arguments = string.Join(" ", config.Arguments);
}
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
@@ -463,7 +479,9 @@ namespace k8s
var stdout = process.StandardOutput.ReadToEnd();
var stderr = process.StandardOutput.ReadToEnd();
if (string.IsNullOrWhiteSpace(stderr) == false)
{
throw new KubeConfigException($"external exec failed due to: {stderr}");
}
// Wait for a maximum of 5 seconds, if a response takes longer probably something went wrong...
process.WaitForExit(5);
@@ -472,8 +490,11 @@ namespace k8s
{
var responseObject = JsonConvert.DeserializeObject<ExecCredentialResponse>(stdout);
if (responseObject == null || responseObject.ApiVersion != config.ApiVersion)
{
throw new KubeConfigException(
$"external exec failed because api version {responseObject.ApiVersion} does not match {config.ApiVersion}");
}
return responseObject.Status["token"];
}
catch (JsonSerializationException ex)
@@ -484,9 +505,6 @@ namespace k8s
{
throw new KubeConfigException($"external exec failed due to uncaught exception: {ex}");
}
}
#endif
@@ -497,7 +515,8 @@ namespace k8s
/// <param name="useRelativePaths">When <see langword="true"/>, the paths in the kubeconfig file will be considered to be relative to the directory in which the kubeconfig
/// file is located. When <see langword="false"/>, the paths will be considered to be relative to the current working directory.</param>
/// <returns>Instance of the <see cref="K8SConfiguration"/> class</returns>
public static async Task<K8SConfiguration> LoadKubeConfigAsync(string kubeconfigPath = null, bool useRelativePaths = true)
public static async Task<K8SConfiguration> LoadKubeConfigAsync(string kubeconfigPath = null,
bool useRelativePaths = true)
{
var fileInfo = new FileInfo(kubeconfigPath ?? KubeConfigDefaultLocation);
@@ -523,7 +542,8 @@ namespace k8s
/// <param name="useRelativePaths">When <see langword="true"/>, the paths in the kubeconfig file will be considered to be relative to the directory in which the kubeconfig
/// file is located. When <see langword="false"/>, the paths will be considered to be relative to the current working directory.</param>
/// <returns>Instance of the <see cref="K8SConfiguration"/> class</returns>
public static async Task<K8SConfiguration> LoadKubeConfigAsync(FileInfo kubeconfig, bool useRelativePaths = true)
public static async Task<K8SConfiguration> LoadKubeConfigAsync(FileInfo kubeconfig,
bool useRelativePaths = true)
{
if (!kubeconfig.Exists)
{
@@ -602,7 +622,8 @@ namespace k8s
/// The kube config files will be merges into a single <see cref="K8SConfiguration"/>, where first occurence wins.
/// See https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#merging-kubeconfig-files.
/// </remarks>
internal static async Task<K8SConfiguration> LoadKubeConfigAsync(FileInfo[] kubeConfigs, bool useRelativePaths = true)
internal static async Task<K8SConfiguration> LoadKubeConfigAsync(FileInfo[] kubeConfigs,
bool useRelativePaths = true)
{
var basek8SConfig = await LoadKubeConfigAsync(kubeConfigs[0], useRelativePaths).ConfigureAwait(false);
@@ -659,7 +680,8 @@ namespace k8s
// Kinds must match in kube config, otherwise throw.
if (basek8SConfig.Kind != mergek8SConfig.Kind)
{
throw new KubeConfigException($"kubeconfig \"kind\" are different between {basek8SConfig.FileName} and {mergek8SConfig.FileName}");
throw new KubeConfigException(
$"kubeconfig \"kind\" are different between {basek8SConfig.FileName} and {mergek8SConfig.FileName}");
}
if (mergek8SConfig.Preferences != null)
@@ -691,7 +713,8 @@ namespace k8s
basek8SConfig.Contexts = MergeLists(basek8SConfig.Contexts, mergek8SConfig.Contexts, (s) => s.Name);
}
private static IEnumerable<T> MergeLists<T>(IEnumerable<T> baseList, IEnumerable<T> mergeList, Func<T, string> getNameFunc)
private static IEnumerable<T> MergeLists<T>(IEnumerable<T> baseList, IEnumerable<T> mergeList,
Func<T, string> getNameFunc)
{
if (mergeList != null && mergeList.Count() > 0)
{

View File

@@ -21,10 +21,12 @@ namespace k8s
}
else
{
httpClientHandler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
{
return Kubernetes.CertificateValidationCallBack(sender, this.SslCaCerts, certificate, chain, sslPolicyErrors);
};
httpClientHandler.ServerCertificateCustomValidationCallback =
(sender, certificate, chain, sslPolicyErrors) =>
{
return Kubernetes.CertificateValidationCallBack(sender, this.SslCaCerts, certificate, chain,
sslPolicyErrors);
};
}
}
#endif
@@ -37,14 +39,14 @@ namespace k8s
public void AddCertificates(HttpClientHandler handler)
{
if ((!string.IsNullOrWhiteSpace(this.ClientCertificateData) ||
!string.IsNullOrWhiteSpace(this.ClientCertificateFilePath)) &&
(!string.IsNullOrWhiteSpace(this.ClientCertificateKeyData) ||
!string.IsNullOrWhiteSpace(this.ClientKeyFilePath)))
!string.IsNullOrWhiteSpace(this.ClientCertificateFilePath)) &&
(!string.IsNullOrWhiteSpace(this.ClientCertificateKeyData) ||
!string.IsNullOrWhiteSpace(this.ClientKeyFilePath)))
{
var cert = CertUtils.GeneratePfx(this);
#if NET452
((WebRequestHandler) handler).ClientCertificates.Add(cert);
((WebRequestHandler)handler).ClientCertificates.Add(cert);
#else
handler.ClientCertificates.Add(cert);
#endif

View File

@@ -7,9 +7,11 @@ namespace k8s
public partial class KubernetesClientConfiguration
{
private static string ServiceAccountPath =
Path.Combine(new string[] {
$"{Path.DirectorySeparatorChar}var", "run", "secrets", "kubernetes.io", "serviceaccount"
Path.Combine(new string[]
{
$"{Path.DirectorySeparatorChar}var", "run", "secrets", "kubernetes.io", "serviceaccount",
});
private const string ServiceAccountTokenKeyFileName = "token";
private const string ServiceAccountRootCAKeyFileName = "ca.crt";
@@ -21,11 +23,13 @@ namespace k8s
{
return false;
}
var tokenPath = Path.Combine(ServiceAccountPath, ServiceAccountTokenKeyFileName);
if (!File.Exists(tokenPath))
{
return false;
}
var certPath = Path.Combine(ServiceAccountPath, ServiceAccountRootCAKeyFileName);
return File.Exists(certPath);
}
@@ -47,7 +51,7 @@ namespace k8s
{
Host = new UriBuilder("https", host, Convert.ToInt32(port)).ToString(),
AccessToken = token,
SslCaCerts = CertUtils.LoadPemFileCert(rootCAFile)
SslCaCerts = CertUtils.LoadPemFileCert(rootCAFile),
};
}
}

View File

@@ -5,22 +5,25 @@ namespace k8s.Models
/// <summary>
/// Describes object type in Kubernetes
/// </summary>
public class KubernetesEntityAttribute : Attribute
public sealed class KubernetesEntityAttribute : Attribute
{
/// <summary>
/// The Kubernetes named schema this object is based on
/// The Kubernetes named schema this object is based on.
/// </summary>
public string Kind { get; set; }
/// <summary>
/// The Group this Kubernetes type belongs to
/// The Group this Kubernetes type belongs to.
/// </summary>
public string Group { get; set; }
/// <summary>
/// The API Version this Kubernetes type belongs to
/// The API Version this Kubernetes type belongs to.
/// </summary>
public string ApiVersion { get; set; }
/// <summary>
/// The plural name of the entity
/// The plural name of the entity.
/// </summary>
public string PluralName { get; set; }
}

View File

@@ -76,10 +76,6 @@ namespace k8s
/// Gets, when this exception was raised because of a Kubernetes status message, the underlying
/// Kubernetes status message.
/// </summary>
public V1Status Status
{
get;
private set;
}
public V1Status Status { get; private set; }
}
}

View File

@@ -6,11 +6,10 @@ using Newtonsoft.Json;
namespace k8s.Models
{
public class KubernetesList<T> : IMetadata<V1ListMeta>, IItems<T> where T : IKubernetesObject
{
public KubernetesList(IList<T> items, string apiVersion = default(string), string kind = default(string), V1ListMeta metadata = default(V1ListMeta))
public KubernetesList(IList<T> items, string apiVersion = default(string), string kind = default(string),
V1ListMeta metadata = default(V1ListMeta))
{
ApiVersion = apiVersion;
Items = items;
@@ -29,8 +28,7 @@ namespace k8s.Models
[JsonProperty(PropertyName = "apiVersion")]
public string ApiVersion { get; set; }
[JsonProperty(PropertyName = "items")]
public IList<T> Items { get; set; }
[JsonProperty(PropertyName = "items")] public IList<T> Items { get; set; }
/// <summary>
/// Gets or sets kind is a string value representing the REST resource
@@ -60,6 +58,7 @@ namespace k8s.Models
{
throw new ValidationException(ValidationRules.CannotBeNull, "Items");
}
if (Items != null)
{
foreach (var element in Items.OfType<IValidate>())

View File

@@ -12,8 +12,9 @@ namespace k8s.Models
{
reason = ((HttpStatusCode)Code.Value).ToString();
}
return string.IsNullOrEmpty(Message) ? string.IsNullOrEmpty(reason) ? Status : reason :
string.IsNullOrEmpty(reason) ? Message : $"{reason} - {Message}";
string.IsNullOrEmpty(reason) ? Message : $"{reason} - {Message}";
}
}
}

View File

@@ -1,4 +1,8 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("KubernetesClient.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001004917ad4e106c573cc5dbb3b7456de8b6c07128ae43de292752b339eb423de60f0db6a6c0cb21e6640fc672cc84df4a772db85df1505e5dd08c98d5d115eed7a7b59c67fe1f4b32fa716b7177743a417b3fcf88606861650a81f565ac6614abbf8b6b7710436edb497a83974165f9fe6995b70af13047a110bf63cdbfa45f89ac")]
[assembly: InternalsVisibleTo("KubernetesClient.Informers.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001004917ad4e106c573cc5dbb3b7456de8b6c07128ae43de292752b339eb423de60f0db6a6c0cb21e6640fc672cc84df4a772db85df1505e5dd08c98d5d115eed7a7b59c67fe1f4b32fa716b7177743a417b3fcf88606861650a81f565ac6614abbf8b6b7710436edb497a83974165f9fe6995b70af13047a110bf63cdbfa45f89ac")]
[assembly:
InternalsVisibleTo(
"KubernetesClient.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001004917ad4e106c573cc5dbb3b7456de8b6c07128ae43de292752b339eb423de60f0db6a6c0cb21e6640fc672cc84df4a772db85df1505e5dd08c98d5d115eed7a7b59c67fe1f4b32fa716b7177743a417b3fcf88606861650a81f565ac6614abbf8b6b7710436edb497a83974165f9fe6995b70af13047a110bf63cdbfa45f89ac")]
[assembly:
InternalsVisibleTo(
"KubernetesClient.Informers.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001004917ad4e106c573cc5dbb3b7456de8b6c07128ae43de292752b339eb423de60f0db6a6c0cb21e6640fc672cc84df4a772db85df1505e5dd08c98d5d115eed7a7b59c67fe1f4b32fa716b7177743a417b3fcf88606861650a81f565ac6614abbf8b6b7710436edb497a83974165f9fe6995b70af13047a110bf63cdbfa45f89ac")]

View File

@@ -93,7 +93,7 @@ namespace k8s.Models
{
DecimalExponent,
BinarySI,
DecimalSI
DecimalSI,
}
public static readonly decimal MaxAllowed = (decimal)BigInteger.Pow(2, 63) - 1;
@@ -130,14 +130,17 @@ namespace k8s.Models
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != GetType())
{
return false;
}
return Equals((ResourceQuantity)obj);
}
@@ -258,28 +261,28 @@ namespace k8s.Models
{
// Don't emit an error when trying to produce
// a suffix for 2^0.
{"", (2, 0)},
{"Ki", (2, 10)},
{"Mi", (2, 20)},
{"Gi", (2, 30)},
{"Ti", (2, 40)},
{"Pi", (2, 50)},
{"Ei", (2, 60)}
{"", (2, 0) },
{"Ki", (2, 10) },
{"Mi", (2, 20) },
{"Gi", (2, 30) },
{"Ti", (2, 40) },
{"Pi", (2, 50) },
{"Ei", (2, 60) },
};
private static readonly IReadOnlyDictionary<string, (int, int)> DecSuffixes =
new Dictionary<string, (int, int)>
{
{"n", (10, -9)},
{"u", (10, -6)},
{"m", (10, -3)},
{"", (10, 0)},
{"k", (10, 3)},
{"M", (10, 6)},
{"G", (10, 9)},
{"T", (10, 12)},
{"P", (10, 15)},
{"E", (10, 18)}
{"n", (10, -9) },
{"u", (10, -6) },
{"m", (10, -3) },
{"", (10, 0) },
{"k", (10, 3) },
{"M", (10, 6) },
{"G", (10, 9) },
{"T", (10, 12) },
{"P", (10, 15) },
{"E", (10, 18) },
};
public Suffixer(string suffix)
@@ -394,6 +397,7 @@ namespace k8s.Models
{
lastv = round + 1;
}
return lastv;
}
}

View File

@@ -43,7 +43,8 @@ namespace k8s
/// A value indicating whether this instance of the <see cref="StreamDemuxer"/> owns the underlying <see cref="WebSocket"/>,
/// and should dispose of it when this instance is disposed of.
/// </param>
public StreamDemuxer(WebSocket webSocket, StreamType streamType = StreamType.RemoteCommand, bool ownsSocket = false)
public StreamDemuxer(WebSocket webSocket, StreamType streamType = StreamType.RemoteCommand,
bool ownsSocket = false)
{
this.streamType = streamType;
this.webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket));
@@ -148,7 +149,8 @@ namespace k8s
/// <returns>
/// A <see cref="Task"/> which represents the asynchronous operation.
/// </returns>
public Task Write(ChannelIndex index, byte[] buffer, int offset, int count, CancellationToken cancellationToken = default(CancellationToken))
public Task Write(ChannelIndex index, byte[] buffer, int offset, int count,
CancellationToken cancellationToken = default(CancellationToken))
{
return Write((byte)index, buffer, offset, count, cancellationToken);
}
@@ -174,7 +176,8 @@ namespace k8s
/// <returns>
/// A <see cref="Task"/> which represents the asynchronous operation.
/// </returns>
public async Task Write(byte index, byte[] buffer, int offset, int count, CancellationToken cancellationToken = default(CancellationToken))
public async Task Write(byte index, byte[] buffer, int offset, int count,
CancellationToken cancellationToken = default(CancellationToken))
{
byte[] writeBuffer = ArrayPool<byte>.Shared.Rent(count + 1);
@@ -183,7 +186,8 @@ namespace k8s
writeBuffer[0] = (byte)index;
Array.Copy(buffer, offset, writeBuffer, 1, count);
ArraySegment<byte> segment = new ArraySegment<byte>(writeBuffer, 0, count + 1);
await this.webSocket.SendAsync(segment, WebSocketMessageType.Binary, false, cancellationToken).ConfigureAwait(false);
await this.webSocket.SendAsync(segment, WebSocketMessageType.Binary, false, cancellationToken)
.ConfigureAwait(false);
}
finally
{
@@ -246,6 +250,7 @@ namespace k8s
this.buffers[streamIndex].Write(buffer, extraByteCount, bytesCount);
}
}
streamBytesToSkipMap[streamIndex] = bytesToSkip;
if (result.EndOfMessage == true)
@@ -257,7 +262,6 @@ namespace k8s
result = await this.webSocket.ReceiveAsync(segment, cancellationToken).ConfigureAwait(false);
}
}
}
finally
{

View File

@@ -17,6 +17,6 @@ namespace k8s
/// <summary>
/// This <see cref="StreamDemuxer"/> object is used in port forwarding.
/// </summary>
PortForward
PortForward,
}
}

View File

@@ -8,10 +8,21 @@ namespace k8s
/// <summary>Given a <see cref="StringBuilder"/> that is building a query string, adds a parameter to it.</summary>
public static void AddQueryParameter(StringBuilder sb, string key, string value)
{
if (sb == null) throw new ArgumentNullException(nameof(sb));
if (string.IsNullOrEmpty(key)) throw new ArgumentNullException(nameof(key));
if (sb == null)
{
throw new ArgumentNullException(nameof(sb));
}
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException(nameof(key));
}
sb.Append(sb.Length != 0 ? '&' : '?').Append(Uri.EscapeDataString(key)).Append('=');
if (!string.IsNullOrEmpty(value)) sb.Append(Uri.EscapeDataString(value));
if (!string.IsNullOrEmpty(value))
{
sb.Append(Uri.EscapeDataString(value));
}
}
}
}

View File

@@ -27,7 +27,6 @@ namespace k8s.Models
[JsonConverter(typeof(V1PathJsonConverter))]
public partial class V1Patch
{
public enum PathType
{
JsonPatch,

View File

@@ -27,11 +27,7 @@ namespace k8s.Models
// should be an object
}
return new V1Status
{
_original = obj,
HasObject = true
};
return new V1Status { _original = obj, HasObject = true };
}
public override bool CanConvert(Type objectType)

View File

@@ -61,7 +61,8 @@ namespace k8s
/// <param name="onClosed">
/// The action to invoke when the server closes the connection.
/// </param>
public Watcher(Func<Task<StreamReader>> streamReaderCreator, Action<WatchEventType, T> onEvent, Action<Exception> onError, Action onClosed = null)
public Watcher(Func<Task<StreamReader>> streamReaderCreator, Action<WatchEventType, T> onEvent,
Action<Exception> onError, Action onClosed = null)
: this(
async () => (TextReader)await streamReaderCreator().ConfigureAwait(false),
onEvent, onError, onClosed)
@@ -83,7 +84,8 @@ namespace k8s
/// <param name="onClosed">
/// The action to invoke when the server closes the connection.
/// </param>
public Watcher(Func<Task<TextReader>> streamReaderCreator, Action<WatchEventType, T> onEvent, Action<Exception> onError, Action onClosed = null)
public Watcher(Func<Task<TextReader>> streamReaderCreator, Action<WatchEventType, T> onEvent,
Action<Exception> onError, Action onClosed = null)
{
_streamReaderCreator = streamReaderCreator;
OnEvent += onEvent;
@@ -141,7 +143,8 @@ namespace k8s
try
{
var genericEvent = SafeJsonConvert.DeserializeObject<k8s.Watcher<KubernetesObject>.WatchEvent>(line);
var genericEvent =
SafeJsonConvert.DeserializeObject<k8s.Watcher<KubernetesObject>.WatchEvent>(line);
if (genericEvent.Object.Kind == "Status")
{

View File

@@ -58,11 +58,13 @@ namespace k8s
public override int Read(byte[] buffer, int offset, int count) =>
_innerStream.ReadAsync(buffer, offset, count, _cancellationToken).GetAwaiter().GetResult();
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken)
{
using (var cancellationTokenSource = CreateCancellationTokenSource(cancellationToken))
{
return await _innerStream.ReadAsync(buffer, offset, count, cancellationTokenSource.Token).ConfigureAwait(false);
return await _innerStream.ReadAsync(buffer, offset, count, cancellationTokenSource.Token)
.ConfigureAwait(false);
}
}
@@ -73,11 +75,13 @@ namespace k8s
public override void Write(byte[] buffer, int offset, int count) =>
_innerStream.WriteAsync(buffer, offset, count, _cancellationToken).GetAwaiter().GetResult();
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
public override async Task WriteAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken)
{
using (var cancellationTokenSource = CreateCancellationTokenSource(cancellationToken))
{
await _innerStream.WriteAsync(buffer, offset, count, cancellationTokenSource.Token).ConfigureAwait(false);
await _innerStream.WriteAsync(buffer, offset, count, cancellationTokenSource.Token)
.ConfigureAwait(false);
}
}
@@ -101,6 +105,7 @@ namespace k8s
{
_innerStream.Dispose();
}
base.Dispose(disposing);
}
@@ -205,11 +210,13 @@ namespace k8s
public override int Read(char[] buffer, int index, int count) => throw new NotImplementedException();
public override Task<int> ReadAsync(char[] buffer, int index, int count) => throw new NotImplementedException();
public override Task<int> ReadAsync(char[] buffer, int index, int count) =>
throw new NotImplementedException();
public override int ReadBlock(char[] buffer, int index, int count) => throw new NotImplementedException();
public override Task<int> ReadBlockAsync(char[] buffer, int index, int count) => throw new NotImplementedException();
public override Task<int> ReadBlockAsync(char[] buffer, int index, int count) =>
throw new NotImplementedException();
public override string ReadToEnd() => throw new NotImplementedException();
@@ -221,6 +228,7 @@ namespace k8s
{
_inner.Dispose();
}
base.Dispose(disposing);
}
}

View File

@@ -40,7 +40,8 @@ namespace k8s
}
#if (NET452 || NETSTANDARD2_0)
public WebSocketBuilder SetServerCertificateValidationCallback(RemoteCertificateValidationCallback validationCallback)
public WebSocketBuilder SetServerCertificateValidationCallback(
RemoteCertificateValidationCallback validationCallback)
{
System.Net.ServicePointManager.ServerCertificateValidationCallback += validationCallback;
return this;
@@ -55,7 +56,8 @@ namespace k8s
#if NETCOREAPP2_1
public WebSocketBuilder ExpectServerCertificate(X509Certificate2Collection serverCertificate)
{
Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
Options.RemoteCertificateValidationCallback
= (sender, certificate, chain, sslPolicyErrors) =>
{
return Kubernetes.CertificateValidationCallBack(sender, serverCertificate, certificate, chain, sslPolicyErrors);
};
@@ -65,7 +67,8 @@ namespace k8s
public WebSocketBuilder SkipServerCertificateValidation()
{
Options.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
Options.RemoteCertificateValidationCallback
= (sender, certificate, chain, sslPolicyErrors) => true;
return this;
}

View File

@@ -16,8 +16,7 @@ namespace k8s
/// <summary>
/// This is a utility class that helps you load objects from YAML files.
/// </summary>
public class Yaml
public static class Yaml
{
/// <summary>
/// Load a collection of objects from a stream asynchronously
@@ -44,7 +43,6 @@ namespace k8s
/// <param name="typeMap">
/// A map from <apiVersion>/<kind> to Type. For example "v1/Pod" -> typeof(V1Pod)
/// </param>
public static Task<List<object>> LoadAllFromFileAsync(String fileName, Dictionary<String, Type> typeMap)
{
var reader = File.OpenRead(fileName);
@@ -60,7 +58,6 @@ namespace k8s
/// <param name="typeMap">
/// A map from <apiVersion>/<kind> to Type. For example "v1/Pod" -> typeof(V1Pod)
/// </param>
public static List<object> LoadAllFromString(String content, Dictionary<String, Type> typeMap)
{
var deserializer =
@@ -95,6 +92,7 @@ namespace k8s
var obj = deserializer.Deserialize(parser, objType);
results.Add(obj);
}
return results;
}
@@ -117,10 +115,10 @@ namespace k8s
{
var deserializer =
new DeserializerBuilder()
.WithNamingConvention(new CamelCaseNamingConvention())
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
.WithTypeConverter(new IntOrStringYamlConverter())
.Build();
.WithNamingConvention(new CamelCaseNamingConvention())
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
.WithTypeConverter(new IntOrStringYamlConverter())
.Build();
var obj = deserializer.Deserialize<T>(content);
return obj;
}
@@ -133,11 +131,11 @@ namespace k8s
var serializer =
new SerializerBuilder()
.DisableAliases()
.WithNamingConvention(new CamelCaseNamingConvention())
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
.WithTypeConverter(new IntOrStringYamlConverter())
.BuildValueSerializer();
.DisableAliases()
.WithNamingConvention(new CamelCaseNamingConvention())
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
.WithTypeConverter(new IntOrStringYamlConverter())
.BuildValueSerializer();
emitter.Emit(new StreamStart());
emitter.Emit(new DocumentStart());
serializer.SerializeValue(emitter, value, typeof(T));
@@ -207,9 +205,23 @@ namespace k8s
public Type Type => _inner.Type;
public Type TypeOverride { get => _inner.TypeOverride; set => _inner.TypeOverride = value; }
public int Order { get => _inner.Order; set => _inner.Order = value; }
public ScalarStyle ScalarStyle { get => _inner.ScalarStyle; set => _inner.ScalarStyle = value; }
public Type TypeOverride
{
get => _inner.TypeOverride;
set => _inner.TypeOverride = value;
}
public int Order
{
get => _inner.Order;
set => _inner.Order = value;
}
public ScalarStyle ScalarStyle
{
get => _inner.ScalarStyle;
set => _inner.ScalarStyle = value;
}
public T GetCustomAttribute<T>() where T : Attribute
{

View File

@@ -1,5 +1,10 @@
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
}
"$schema":
"https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"settings": {
"orderingRules": {
"systemUsingDirectivesFirst": false,
"usingDirectivesPlacement": "outsideNamespace"
}
}
}

View File

@@ -42,10 +42,7 @@ namespace k8s.Tests
{
using (var server = new MockKubeApiServer(testOutput))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var listTask = ExecuteListPods(client);
@@ -59,10 +56,7 @@ namespace k8s.Tests
return Task.FromResult(false);
}))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var listTask = ExecuteListPods(client);
@@ -80,7 +74,8 @@ namespace k8s.Tests
{
var header = cxt.Request.Headers["Authorization"].FirstOrDefault();
var expect = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{testName}:{testPassword}")))
var expect = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(Encoding.UTF8.GetBytes($"{testName}:{testPassword}")))
.ToString();
if (header != expect)
@@ -97,7 +92,7 @@ namespace k8s.Tests
{
Host = server.Uri.ToString(),
Username = testName,
Password = testPassword
Password = testPassword,
});
var listTask = ExecuteListPods(client);
@@ -110,7 +105,7 @@ namespace k8s.Tests
{
Host = server.Uri.ToString(),
Username = "wrong name",
Password = testPassword
Password = testPassword,
});
var listTask = ExecuteListPods(client);
@@ -123,7 +118,7 @@ namespace k8s.Tests
{
Host = server.Uri.ToString(),
Username = testName,
Password = "wrong password"
Password = "wrong password",
});
var listTask = ExecuteListPods(client);
@@ -136,7 +131,7 @@ namespace k8s.Tests
{
Host = server.Uri.ToString(),
Username = "both wrong",
Password = "wrong password"
Password = "wrong password",
});
var listTask = ExecuteListPods(client);
@@ -145,10 +140,7 @@ namespace k8s.Tests
}
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var listTask = ExecuteListPods(client);
@@ -159,7 +151,7 @@ namespace k8s.Tests
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString(),
Username = "xx"
Username = "xx",
});
var listTask = ExecuteListPods(client);
@@ -188,7 +180,8 @@ namespace k8s.Tests
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
using (MemoryStream serverCertificateStream = new MemoryStream(Convert.FromBase64String(serverCertificateData)))
using (MemoryStream serverCertificateStream =
new MemoryStream(Convert.FromBase64String(serverCertificateData)))
{
serverCertificate = OpenCertificateStore(serverCertificateStream);
}
@@ -212,7 +205,7 @@ namespace k8s.Tests
{
clientCertificateValidationCalled = true;
return clientCertificate.Equals(certificate);
}
},
});
}))
{
@@ -224,7 +217,7 @@ namespace k8s.Tests
ClientCertificateData = clientCertificateData,
ClientCertificateKeyData = clientCertificateKeyData,
SslCaCerts = new X509Certificate2Collection(serverCertificate),
SkipTlsVerify = false
SkipTlsVerify = false,
});
var listTask = ExecuteListPods(client);
@@ -241,7 +234,7 @@ namespace k8s.Tests
Host = server.Uri.ToString(),
ClientCertificateData = clientCertificateData,
ClientCertificateKeyData = clientCertificateKeyData,
SkipTlsVerify = true
SkipTlsVerify = true,
});
var listTask = ExecuteListPods(client);
@@ -256,9 +249,10 @@ namespace k8s.Tests
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString(),
ClientCertificateFilePath = "assets/client.crt", // TODO amazoning why client.crt != client-data.txt
ClientCertificateFilePath =
"assets/client.crt", // TODO amazoning why client.crt != client-data.txt
ClientKeyFilePath = "assets/client.key",
SkipTlsVerify = true
SkipTlsVerify = true,
});
Assert.ThrowsAny<Exception>(() => ExecuteListPods(client));
@@ -270,7 +264,7 @@ namespace k8s.Tests
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString(),
SkipTlsVerify = true
SkipTlsVerify = true,
});
Assert.ThrowsAny<Exception>(() => ExecuteListPods(client));
@@ -285,18 +279,24 @@ namespace k8s.Tests
[Fact]
public void ExternalToken()
{
const string token = "testingtoken";
const string name = "testing_irrelevant";
const string token
= "testingtoken";
const string name
= "testing_irrelevant";
using (var server = new MockKubeApiServer(testOutput, cxt =>
using (var server
= new MockKubeApiServer(testOutput, cxt =>
{
var header = cxt.Request.Headers["Authorization"].FirstOrDefault();
var header
= cxt.Request.Headers["Authorization"].FirstOrDefault();
var expect = new AuthenticationHeaderValue("Bearer", token).ToString();
var expect
= new AuthenticationHeaderValue("Bearer", token).ToString();
if (header != expect)
{
cxt.Response.StatusCode = (int) HttpStatusCode.Unauthorized;
cxt.Response.StatusCode
= (int) HttpStatusCode.Unauthorized;
return Task.FromResult(false);
}
@@ -304,18 +304,26 @@ namespace k8s.Tests
}))
{
{
var kubernetesConfig = GetK8SConfiguration(server.Uri.ToString(), token, name);
var clientConfig = KubernetesClientConfiguration.BuildConfigFromConfigObject(kubernetesConfig, name);
var client = new Kubernetes(clientConfig);
var listTask = ExecuteListPods(client);
var kubernetesConfig
= GetK8SConfiguration(server.Uri.ToString(), token, name);
var clientConfig
= KubernetesClientConfiguration.BuildConfigFromConfigObject(kubernetesConfig, name);
var client
= new Kubernetes(clientConfig);
var listTask
= ExecuteListPods(client);
Assert.True(listTask.Response.IsSuccessStatusCode);
Assert.Equal(1, listTask.Body.Items.Count);
}
{
var kubernetesConfig = GetK8SConfiguration(server.Uri.ToString(), "wrong token", name);
var clientConfig = KubernetesClientConfiguration.BuildConfigFromConfigObject(kubernetesConfig, name);
var client = new Kubernetes(clientConfig);
var listTask = ExecuteListPods(client);
var kubernetesConfig
= GetK8SConfiguration(server.Uri.ToString(), "wrong token", name);
var clientConfig
= KubernetesClientConfiguration.BuildConfigFromConfigObject(kubernetesConfig, name);
var client
= new Kubernetes(clientConfig);
var listTask
= ExecuteListPods(client);
Assert.Equal(HttpStatusCode.Unauthorized, listTask.Response.StatusCode);
}
}
@@ -346,7 +354,7 @@ namespace k8s.Tests
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString(),
AccessToken = token
AccessToken = token,
});
var listTask = ExecuteListPods(client);
@@ -358,7 +366,7 @@ namespace k8s.Tests
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString(),
AccessToken = "wrong token"
AccessToken = "wrong token",
});
var listTask = ExecuteListPods(client);
@@ -372,7 +380,7 @@ namespace k8s.Tests
{
Host = server.Uri.ToString(),
Username = "wrong name",
Password = "same password"
Password = "same password",
});
var listTask = ExecuteListPods(client);
@@ -381,10 +389,7 @@ namespace k8s.Tests
}
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var listTask = ExecuteListPods(client);
@@ -420,7 +425,7 @@ namespace k8s.Tests
var contexts = new List<Context>
{
new Context {Name = name, ContextDetails = new ContextDetails {Cluster = name, User = username}}
new Context {Name = name, ContextDetails = new ContextDetails {Cluster = name, User = username } },
};
var responseJson = $"{{\"apiVersion\": \"testingversion\", \"status\": {{\"token\": \"{token}\"}}}}";
@@ -431,19 +436,27 @@ namespace k8s.Tests
new Cluster
{
Name = name,
ClusterEndpoint = new ClusterEndpoint {SkipTlsVerify = true, Server = serverUri}
}
ClusterEndpoint = new ClusterEndpoint {SkipTlsVerify = true, Server = serverUri }
},
};
var command = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cmd.exe" : "echo";
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
command = "printf";
}
var arguments = new string[] { };
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
arguments = ($"/c echo {responseJson}").Split(" ");
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
arguments = new[] { responseJson };
}
var users = new List<User>
@@ -460,7 +473,7 @@ namespace k8s.Tests
Arguments = arguments.ToList()
}
}
}
},
};
var kubernetesConfig = new K8SConfiguration { Clusters = clusters, Users = users, Contexts = contexts };
return kubernetesConfig;

View File

@@ -12,7 +12,10 @@ namespace k8s.Tests
/// </summary>
public class ByteBufferTests
{
private readonly byte[] writeData = new byte[] { 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
private readonly byte[] writeData = new byte[]
{
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
};
/// <summary>
/// Tests a sequential read and write operation.
@@ -261,8 +264,7 @@ namespace k8s.Tests
await TaskAssert.Completed(readTask,
timeout: TimeSpan.FromMilliseconds(1000),
message: "Timed out waiting for read task to complete."
);
message: "Timed out waiting for read task to complete.");
Assert.Equal(3, read);
Assert.Equal(0xF0, readData[0]);
@@ -427,26 +429,34 @@ namespace k8s.Tests
[Fact]
public async Task RandomReadWriteTest()
{
ByteBuffer buffer = new ByteBuffer(1, 1024 * 1024);
ByteBuffer buffer
= new ByteBuffer(1, 1024 * 1024);
var generatorTask = Task.Run(() => this.Generate(buffer, SHA256.Create()));
var consumerTask = Task.Run(() => this.Consume(buffer, SHA256.Create()));
var generatorTask
= Task.Run(() => this.Generate(buffer, SHA256.Create()));
var consumerTask
= Task.Run(() => this.Consume(buffer, SHA256.Create()));
await Task.WhenAll(generatorTask, consumerTask);
var generatorHash = await generatorTask;
var consumerHash = await consumerTask;
var generatorHash
= await generatorTask;
var consumerHash
= await consumerTask;
Assert.Equal(generatorHash, consumerHash);
}
private byte[] Generate(ByteBuffer buffer, HashAlgorithm hash)
{
RandomNumberGenerator g = RandomNumberGenerator.Create();
RandomNumberGenerator g
= RandomNumberGenerator.Create();
byte[] next = new byte[32];
byte[] next
= new byte[32];
int iterations = 0;
int iterations
= 0;
while (buffer.Size < buffer.MaximumSize)
{
iterations++;
@@ -463,14 +473,19 @@ namespace k8s.Tests
private byte[] Consume(ByteBuffer buffer, HashAlgorithm hash)
{
byte[] data = new byte[32];
byte[] data
= new byte[32];
AsyncAutoResetEvent onBufferResized = new AsyncAutoResetEvent();
buffer.OnResize += (sender, e) => onBufferResized.Set();
AsyncAutoResetEvent onBufferResized
= new AsyncAutoResetEvent();
buffer.OnResize
+= (sender, e) => onBufferResized.Set();
int read;
int iterations = 0;
while ((read = buffer.Read(data, 0, data.Length)) > 0)
int iterations
= 0;
while ((read
= buffer.Read(data, 0, data.Length)) > 0)
{
iterations++;

View File

@@ -14,13 +14,13 @@ namespace k8s.Tests
/// This file contains a sample kubeconfig file. The paths to the certificate files are relative
/// to the current working directly.
/// </summary>
private static readonly string kubeConfigFileName = "assets/kubeconfig.yml";
private const string KubeConfigFileName = "assets/kubeconfig.yml";
/// <summary>
/// This file contains a sample kubeconfig file. The paths to the certificate files are relative
/// to the directory in which the kubeconfig file is located.
/// </summary>
private static readonly string kubeConfigWithRelativePathsFileName = "assets/kubeconfig.relative.yml";
private const string KubeConfigWithRelativePathsFileName = "assets/kubeconfig.relative.yml";
/// <summary>
/// Checks that a certificate can be loaded from files.
@@ -28,7 +28,8 @@ namespace k8s.Tests
[Fact]
public void LoadFromFiles()
{
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(kubeConfigFileName, "federal-context", useRelativePaths: false);
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(KubeConfigFileName, "federal-context",
useRelativePaths: false);
// Just validate that this doesn't throw and private key is non-null
var cert = CertUtils.GeneratePfx(cfg);
@@ -41,7 +42,8 @@ namespace k8s.Tests
[Fact]
public void LoadFromFilesRelativePath()
{
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(kubeConfigWithRelativePathsFileName, "federal-context");
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(KubeConfigWithRelativePathsFileName,
"federal-context");
// Just validate that this doesn't throw and private key is non-null
var cert = CertUtils.GeneratePfx(cfg);
@@ -54,7 +56,8 @@ namespace k8s.Tests
[Fact]
public void LoadFromInlineData()
{
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(kubeConfigFileName, "victorian-context", useRelativePaths: false);
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(KubeConfigFileName, "victorian-context",
useRelativePaths: false);
// Just validate that this doesn't throw and private key is non-null
var cert = CertUtils.GeneratePfx(cfg);
@@ -67,7 +70,8 @@ namespace k8s.Tests
[Fact]
public void LoadFromInlineDataRelativePath()
{
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(kubeConfigWithRelativePathsFileName, "victorian-context");
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(KubeConfigWithRelativePathsFileName,
"victorian-context");
// Just validate that this doesn't throw and private key is non-null
var cert = CertUtils.GeneratePfx(cfg);

View File

@@ -48,9 +48,9 @@ namespace k8s.Tests
if (!Debugger.IsAttached)
{
CancellationSource.CancelAfter(
TimeSpan.FromSeconds(5)
);
TimeSpan.FromSeconds(5));
}
await Host.StartAsync(TestCancellation);
using (Kubernetes client = CreateTestClient())
@@ -66,14 +66,17 @@ namespace k8s.Tests
stdin: false,
stdout: true,
webSocketSubProtol: WebSocketProtocol.ChannelWebSocketProtocol,
cancellationToken: TestCancellation
);
Assert.Equal(WebSocketProtocol.ChannelWebSocketProtocol, clientSocket.SubProtocol); // For WebSockets, the Kubernetes API defaults to the binary channel (v1) protocol.
cancellationToken: TestCancellation);
Assert.Equal(WebSocketProtocol.ChannelWebSocketProtocol,
clientSocket
.SubProtocol); // For WebSockets, the Kubernetes API defaults to the binary channel (v1) protocol.
testOutput.WriteLine($"Client socket connected (socket state is {clientSocket.State}). Waiting for server-side socket to become available...");
testOutput.WriteLine(
$"Client socket connected (socket state is {clientSocket.State}). Waiting for server-side socket to become available...");
WebSocket serverSocket = await WebSocketTestAdapter.AcceptedPodExecV1Connection;
testOutput.WriteLine($"Server-side socket is now available (socket state is {serverSocket.State}). Sending data to server socket...");
testOutput.WriteLine(
$"Server-side socket is now available (socket state is {serverSocket.State}). Sending data to server socket...");
const int STDOUT = 1;
const string expectedOutput = "This is text send to STDOUT.";
@@ -82,15 +85,15 @@ namespace k8s.Tests
testOutput.WriteLine($"Sent {bytesSent} bytes to server socket; receiving from client socket...");
(string receivedText, byte streamIndex, int bytesReceived) = await ReceiveTextMultiplexed(clientSocket);
testOutput.WriteLine($"Received {bytesReceived} bytes from client socket ('{receivedText}', stream {streamIndex}).");
testOutput.WriteLine(
$"Received {bytesReceived} bytes from client socket ('{receivedText}', stream {streamIndex}).");
Assert.Equal(STDOUT, streamIndex);
Assert.Equal(expectedOutput, receivedText);
await Disconnect(clientSocket, serverSocket,
closeStatus: WebSocketCloseStatus.NormalClosure,
closeStatusDescription: "Normal Closure"
);
closeStatusDescription: "Normal Closure");
WebSocketTestAdapter.CompleteTest();
}
@@ -100,11 +103,7 @@ namespace k8s.Tests
[Fact]
public void GetExitCodeOrThrow_Success()
{
var status = new V1Status()
{
Metadata = null,
Status = "Success",
};
var status = new V1Status() { Metadata = null, Status = "Success", };
Assert.Equal(0, Kubernetes.GetExitCodeOrThrow(status));
}
@@ -121,14 +120,10 @@ namespace k8s.Tests
Details = new V1StatusDetails()
{
Causes = new List<V1StatusCause>()
{
new V1StatusCause()
{
Reason = "ExitCode",
Message = "1"
}
}
}
{
new V1StatusCause() {Reason = "ExitCode", Message = "1" }
}
},
};
Assert.Equal(1, Kubernetes.GetExitCodeOrThrow(status));
@@ -147,13 +142,9 @@ namespace k8s.Tests
{
Causes = new List<V1StatusCause>()
{
new V1StatusCause()
{
Reason = "ExitCode",
Message = "abc"
}
new V1StatusCause() {Reason = "ExitCode", Message = "abc" }
}
}
},
};
var ex = Assert.Throws<KubernetesException>(() => Kubernetes.GetExitCodeOrThrow(status));
@@ -169,12 +160,7 @@ namespace k8s.Tests
Status = "Failure",
Message = "command terminated with non-zero exit code: Error executing in Docker Container: 1",
Reason = "NonZeroExitCode",
Details = new V1StatusDetails()
{
Causes = new List<V1StatusCause>()
{
}
}
Details = new V1StatusDetails() { Causes = new List<V1StatusCause>() { } },
};
var ex = Assert.Throws<KubernetesException>(() => Kubernetes.GetExitCodeOrThrow(status));
@@ -184,12 +170,7 @@ namespace k8s.Tests
[Fact]
public void GetExitCodeOrThrow_OtherError()
{
var status = new V1Status()
{
Metadata = null,
Status = "Failure",
Reason = "SomethingElse"
};
var status = new V1Status() { Metadata = null, Status = "Failure", Reason = "SomethingElse" };
var ex = Assert.Throws<KubernetesException>(() => Kubernetes.GetExitCodeOrThrow(status));
Assert.Equal(status, ex.Status);
@@ -213,12 +194,16 @@ namespace k8s.Tests
new object[] { Moq.Mock.Of<ServiceClientCredentials>(), new DelegatingHandler[] { } });
var command = new string[] { "/bin/bash", "-c", "echo Hello, World!" };
kubernetesMock.Setup(m => m.MuxedStreamNamespacedPodExecAsync("pod-name", "pod-namespace", command, "my-container", true, true, true, false, WebSocketProtocol.V4BinaryWebsocketProtocol, null, CancellationToken.None))
kubernetesMock.Setup(m => m.MuxedStreamNamespacedPodExecAsync("pod-name", "pod-namespace", command,
"my-container", true, true, true, false, WebSocketProtocol.V4BinaryWebsocketProtocol, null,
CancellationToken.None))
.Returns(Task.FromResult(muxedStream.Object));
using (Kubernetes client = kubernetesMock.Object)
{
await Assert.ThrowsAsync<ArgumentNullException>(() => client.NamespacedPodExecAsync("pod-name", "pod-namespace", "my-container", command, false, null, CancellationToken.None)).ConfigureAwait(false);
await Assert.ThrowsAsync<ArgumentNullException>(() => client.NamespacedPodExecAsync("pod-name",
"pod-namespace", "my-container", command, false, null, CancellationToken.None))
.ConfigureAwait(false);
}
}
}
@@ -232,12 +217,16 @@ namespace k8s.Tests
var handler = new ExecAsyncCallback((stdIn, stdOut, stdError) => Task.CompletedTask);
var status = new V1Status();
kubernetesMock.Setup(m => m.MuxedStreamNamespacedPodExecAsync("pod-name", "pod-namespace", command, "my-container", true, true, true, false, WebSocketProtocol.V4BinaryWebsocketProtocol, null, CancellationToken.None))
kubernetesMock.Setup(m => m.MuxedStreamNamespacedPodExecAsync("pod-name", "pod-namespace", command,
"my-container", true, true, true, false, WebSocketProtocol.V4BinaryWebsocketProtocol, null,
CancellationToken.None))
.Throws(new HttpOperationException() { Body = status });
using (Kubernetes client = kubernetesMock.Object)
{
var ex = await Assert.ThrowsAsync<KubernetesException>(() => client.NamespacedPodExecAsync("pod-name", "pod-namespace", "my-container", command, false, handler, CancellationToken.None)).ConfigureAwait(false);
var ex = await Assert.ThrowsAsync<KubernetesException>(() => client.NamespacedPodExecAsync("pod-name",
"pod-namespace", "my-container", command, false, handler, CancellationToken.None))
.ConfigureAwait(false);
Assert.Same(status, ex.Status);
}
}
@@ -251,12 +240,16 @@ namespace k8s.Tests
var handler = new ExecAsyncCallback((stdIn, stdOut, stdError) => Task.CompletedTask);
var exception = new HttpOperationException();
kubernetesMock.Setup(m => m.MuxedStreamNamespacedPodExecAsync("pod-name", "pod-namespace", command, "my-container", true, true, true, false, WebSocketProtocol.V4BinaryWebsocketProtocol, null, CancellationToken.None))
kubernetesMock.Setup(m => m.MuxedStreamNamespacedPodExecAsync("pod-name", "pod-namespace", command,
"my-container", true, true, true, false, WebSocketProtocol.V4BinaryWebsocketProtocol, null,
CancellationToken.None))
.Throws(exception);
using (Kubernetes client = kubernetesMock.Object)
{
var ex = await Assert.ThrowsAsync<HttpOperationException>(() => client.NamespacedPodExecAsync("pod-name", "pod-namespace", "my-container", command, false, handler, CancellationToken.None)).ConfigureAwait(false);
var ex = await Assert.ThrowsAsync<HttpOperationException>(() =>
client.NamespacedPodExecAsync("pod-name", "pod-namespace", "my-container", command, false, handler,
CancellationToken.None)).ConfigureAwait(false);
Assert.Same(exception, ex);
}
}
@@ -270,12 +263,16 @@ namespace k8s.Tests
var handler = new ExecAsyncCallback((stdIn, stdOut, stdError) => Task.CompletedTask);
var exception = new Exception();
kubernetesMock.Setup(m => m.MuxedStreamNamespacedPodExecAsync("pod-name", "pod-namespace", command, "my-container", true, true, true, false, WebSocketProtocol.V4BinaryWebsocketProtocol, null, CancellationToken.None))
kubernetesMock.Setup(m => m.MuxedStreamNamespacedPodExecAsync("pod-name", "pod-namespace", command,
"my-container", true, true, true, false, WebSocketProtocol.V4BinaryWebsocketProtocol, null,
CancellationToken.None))
.Throws(exception);
using (Kubernetes client = kubernetesMock.Object)
{
var ex = await Assert.ThrowsAsync<Exception>(() => client.NamespacedPodExecAsync("pod-name", "pod-namespace", "my-container", command, false, handler, CancellationToken.None)).ConfigureAwait(false);
var ex = await Assert.ThrowsAsync<Exception>(() => client.NamespacedPodExecAsync("pod-name",
"pod-namespace", "my-container", command, false, handler, CancellationToken.None))
.ConfigureAwait(false);
Assert.Same(exception, ex);
}
}
@@ -292,14 +289,10 @@ namespace k8s.Tests
Details = new V1StatusDetails()
{
Causes = new List<V1StatusCause>()
{
new V1StatusCause()
{
Reason = "ExitCode",
Message = "1"
}
}
}
{
new V1StatusCause() {Reason = "ExitCode", Message = "1" }
}
},
};
var processStatusJson = Encoding.UTF8.GetBytes(SafeJsonConvert.SerializeObject(processStatus));
@@ -321,12 +314,15 @@ namespace k8s.Tests
var command = new string[] { "/bin/bash", "-c", "echo Hello, World!" };
var exception = new Exception();
kubernetesMock.Setup(m => m.MuxedStreamNamespacedPodExecAsync("pod-name", "pod-namespace", command, "my-container", true, true, true, false, WebSocketProtocol.V4BinaryWebsocketProtocol, null, CancellationToken.None))
kubernetesMock.Setup(m => m.MuxedStreamNamespacedPodExecAsync("pod-name", "pod-namespace", command,
"my-container", true, true, true, false, WebSocketProtocol.V4BinaryWebsocketProtocol, null,
CancellationToken.None))
.Returns(Task.FromResult(muxedStream.Object));
using (Kubernetes client = kubernetesMock.Object)
{
var exitCode = await client.NamespacedPodExecAsync("pod-name", "pod-namespace", "my-container", command, false, handler, CancellationToken.None).ConfigureAwait(false);
var exitCode = await client.NamespacedPodExecAsync("pod-name", "pod-namespace", "my-container",
command, false, handler, CancellationToken.None).ConfigureAwait(false);
Assert.Equal(1, exitCode);
}
}

View File

@@ -27,7 +27,7 @@ namespace k8s.Tests
var credentials = new BasicAuthenticationCredentials()
{
UserName = "my-user",
Password = "my-secret-password"
Password = "my-secret-password",
};
Kubernetes client = new Kubernetes(credentials);
@@ -47,18 +47,22 @@ namespace k8s.Tests
tty: true,
customHeaders: new Dictionary<string, List<string>>()
{
{ "X-My-Header", new List<string>() { "myHeaderValue", "myHeaderValue2"} }
{"X-My-Header", new List<string>() {"myHeaderValue", "myHeaderValue2" } },
},
cancellationToken: CancellationToken.None).ConfigureAwait(false);
var expectedHeaders = new Dictionary<string, string>()
{
{ "X-My-Header", "myHeaderValue myHeaderValue2" },
{ "Authorization", "Basic bXktdXNlcjpteS1zZWNyZXQtcGFzc3dvcmQ=" }
{"X-My-Header", "myHeaderValue myHeaderValue2" },
{"Authorization", "Basic bXktdXNlcjpteS1zZWNyZXQtcGFzc3dvcmQ=" },
};
Assert.Equal(mockWebSocketBuilder.PublicWebSocket, webSocket); // Did the method return the correct web socket?
Assert.Equal(new Uri("ws://localhost/api/v1/namespaces/mynamespace/pods/mypod/exec?command=%2Fbin%2Fbash&command=-c&command=echo%20Hello%2C%20World%0Aexit%200%0A&container=mycontainer&stderr=1&stdin=1&stdout=1&tty=1"), mockWebSocketBuilder.Uri); // Did we connect to the correct URL?
Assert.Equal(mockWebSocketBuilder.PublicWebSocket,
webSocket); // Did the method return the correct web socket?
Assert.Equal(
new Uri(
"ws://localhost/api/v1/namespaces/mynamespace/pods/mypod/exec?command=%2Fbin%2Fbash&command=-c&command=echo%20Hello%2C%20World%0Aexit%200%0A&container=mycontainer&stderr=1&stdin=1&stdout=1&tty=1"),
mockWebSocketBuilder.Uri); // Did we connect to the correct URL?
Assert.Empty(mockWebSocketBuilder.Certificates); // No certificates were used in this test
Assert.Equal(expectedHeaders, mockWebSocketBuilder.RequestHeaders); // Did we use the expected headers
}
@@ -69,7 +73,7 @@ namespace k8s.Tests
var credentials = new BasicAuthenticationCredentials()
{
UserName = "my-user",
Password = "my-secret-password"
Password = "my-secret-password",
};
Kubernetes client = new Kubernetes(credentials);
@@ -84,18 +88,21 @@ namespace k8s.Tests
ports: new int[] { 80, 8080 },
customHeaders: new Dictionary<string, List<string>>()
{
{ "X-My-Header", new List<string>() { "myHeaderValue", "myHeaderValue2"} }
{"X-My-Header", new List<string>() {"myHeaderValue", "myHeaderValue2" } },
},
cancellationToken: CancellationToken.None).ConfigureAwait(false);
var expectedHeaders = new Dictionary<string, string>()
{
{ "X-My-Header", "myHeaderValue myHeaderValue2" },
{ "Authorization", "Basic bXktdXNlcjpteS1zZWNyZXQtcGFzc3dvcmQ=" }
{"X-My-Header", "myHeaderValue myHeaderValue2" },
{"Authorization", "Basic bXktdXNlcjpteS1zZWNyZXQtcGFzc3dvcmQ=" },
};
Assert.Equal(mockWebSocketBuilder.PublicWebSocket, webSocket); // Did the method return the correct web socket?
Assert.Equal(new Uri("ws://localhost/api/v1/namespaces/mynamespace/pods/mypod/portforward?ports=80&ports=8080"), mockWebSocketBuilder.Uri); // Did we connect to the correct URL?
Assert.Equal(mockWebSocketBuilder.PublicWebSocket,
webSocket); // Did the method return the correct web socket?
Assert.Equal(
new Uri("ws://localhost/api/v1/namespaces/mynamespace/pods/mypod/portforward?ports=80&ports=8080"),
mockWebSocketBuilder.Uri); // Did we connect to the correct URL?
Assert.Empty(mockWebSocketBuilder.Certificates); // No certificates were used in this test
Assert.Equal(expectedHeaders, mockWebSocketBuilder.RequestHeaders); // Did we use the expected headers
}
@@ -106,7 +113,7 @@ namespace k8s.Tests
var credentials = new BasicAuthenticationCredentials()
{
UserName = "my-user",
Password = "my-secret-password"
Password = "my-secret-password",
};
Kubernetes client = new Kubernetes(credentials);
@@ -125,18 +132,22 @@ namespace k8s.Tests
tty: true,
customHeaders: new Dictionary<string, List<string>>()
{
{ "X-My-Header", new List<string>() { "myHeaderValue", "myHeaderValue2"} }
{"X-My-Header", new List<string>() {"myHeaderValue", "myHeaderValue2" } },
},
cancellationToken: CancellationToken.None).ConfigureAwait(false);
var expectedHeaders = new Dictionary<string, string>()
{
{ "X-My-Header", "myHeaderValue myHeaderValue2" },
{ "Authorization", "Basic bXktdXNlcjpteS1zZWNyZXQtcGFzc3dvcmQ=" }
{"X-My-Header", "myHeaderValue myHeaderValue2" },
{"Authorization", "Basic bXktdXNlcjpteS1zZWNyZXQtcGFzc3dvcmQ=" },
};
Assert.Equal(mockWebSocketBuilder.PublicWebSocket, webSocket); // Did the method return the correct web socket?
Assert.Equal(new Uri("ws://localhost:80/api/v1/namespaces/mynamespace/pods/mypod/attach?stderr=1&stdin=1&stdout=1&tty=1&container=my-container"), mockWebSocketBuilder.Uri); // Did we connect to the correct URL?
Assert.Equal(mockWebSocketBuilder.PublicWebSocket,
webSocket); // Did the method return the correct web socket?
Assert.Equal(
new Uri(
"ws://localhost:80/api/v1/namespaces/mynamespace/pods/mypod/attach?stderr=1&stdin=1&stdout=1&tty=1&container=my-container"),
mockWebSocketBuilder.Uri); // Did we connect to the correct URL?
Assert.Empty(mockWebSocketBuilder.Certificates); // No certificates were used in this test
Assert.Equal(expectedHeaders, mockWebSocketBuilder.RequestHeaders); // Did we use the expected headers
}

View File

@@ -160,7 +160,9 @@ namespace k8s.Tests
[Fact]
public void CreatedFromPreLoadedConfig()
{
var k8sConfig = KubernetesClientConfiguration.LoadKubeConfig(new FileInfo("assets/kubeconfig.yml"), useRelativePaths: false);
var k8sConfig =
KubernetesClientConfiguration.LoadKubeConfig(new FileInfo("assets/kubeconfig.yml"),
useRelativePaths: false);
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigObject(k8sConfig);
Assert.NotNull(cfg.Host);
}
@@ -171,7 +173,8 @@ namespace k8s.Tests
[Fact]
public void DefaultConfigurationLoaded()
{
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(new FileInfo("assets/kubeconfig.yml"), useRelativePaths: false);
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(new FileInfo("assets/kubeconfig.yml"),
useRelativePaths: false);
Assert.NotNull(cfg.Host);
}
@@ -182,7 +185,8 @@ namespace k8s.Tests
public void IncompleteUserCredentials()
{
var fi = new FileInfo("assets/kubeconfig.no-credentials.yml");
Assert.Throws<KubeConfigException>(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, useRelativePaths: false));
Assert.Throws<KubeConfigException>(() =>
KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, useRelativePaths: false));
}
/// <summary>
@@ -245,7 +249,8 @@ namespace k8s.Tests
public void UserNotFound()
{
var fi = new FileInfo("assets/kubeconfig.user-not-found.yml");
Assert.Throws<KubeConfigException>(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, useRelativePaths: false));
Assert.Throws<KubeConfigException>(() =>
KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, useRelativePaths: false));
}
/// <summary>
@@ -267,7 +272,8 @@ namespace k8s.Tests
public void OverrideByMasterUrl()
{
var fi = new FileInfo("assets/kubeconfig.yml");
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, masterUrl: "http://test.server", useRelativePaths: false);
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, masterUrl: "http://test.server",
useRelativePaths: false);
Assert.Equal("http://test.server", cfg.Host);
}
@@ -332,7 +338,8 @@ namespace k8s.Tests
public void DefaultConfigurationAsStringLoaded()
{
var filePath = "assets/kubeconfig.yml";
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(filePath, null, null, useRelativePaths: false);
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(filePath, null, null,
useRelativePaths: false);
Assert.NotNull(cfg.Host);
}
@@ -358,7 +365,8 @@ namespace k8s.Tests
{
var filePath = "assets/kubeconfig.as-user-extra.yml";
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(filePath, null, null, useRelativePaths: false);
var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(filePath, null, null,
useRelativePaths: false);
Assert.NotNull(cfg.Host);
}
@@ -434,7 +442,8 @@ namespace k8s.Tests
var filePath = Path.GetFullPath("assets/kubeconfig.relative.yml");
var environmentVariable = "KUBECONFIG_LoadKubeConfigFromEnvironmentVariable_MultipleConfigs";
Environment.SetEnvironmentVariable(environmentVariable, string.Concat(filePath, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ';' : ':', filePath));
Environment.SetEnvironmentVariable(environmentVariable,
string.Concat(filePath, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ';' : ':', filePath));
KubernetesClientConfiguration.KubeConfigEnvironmentVariable = environmentVariable;
var cfg = KubernetesClientConfiguration.BuildDefaultConfig();
@@ -461,7 +470,10 @@ namespace k8s.Tests
var firstPath = Path.GetFullPath("assets/kubeconfig.as-user-extra.yml");
var secondPath = Path.GetFullPath("assets/kubeconfig.yml");
var cfg = KubernetesClientConfiguration.LoadKubeConfig(new FileInfo[] { new FileInfo(firstPath), new FileInfo(secondPath) });
var cfg = KubernetesClientConfiguration.LoadKubeConfig(new FileInfo[]
{
new FileInfo(firstPath), new FileInfo(secondPath),
});
// Merged file has 6 users now.
Assert.Equal(6, cfg.Users.Count());
@@ -475,7 +487,10 @@ namespace k8s.Tests
var firstPath = Path.GetFullPath("assets/kubeconfig.no-cluster.yml");
var secondPath = Path.GetFullPath("assets/kubeconfig.no-context.yml");
var cfg = KubernetesClientConfiguration.LoadKubeConfig(new FileInfo[] { new FileInfo(firstPath), new FileInfo(secondPath) });
var cfg = KubernetesClientConfiguration.LoadKubeConfig(new FileInfo[]
{
new FileInfo(firstPath), new FileInfo(secondPath),
});
var user = cfg.Users.Where(u => u.Name == "green-user").Single();
Assert.NotNull(user.UserCredentials.Password);
@@ -488,7 +503,10 @@ namespace k8s.Tests
var firstPath = Path.GetFullPath("assets/kubeconfig.no-current-context.yml");
var secondPath = Path.GetFullPath("assets/kubeconfig.no-user.yml");
var cfg = KubernetesClientConfiguration.LoadKubeConfig(new FileInfo[] { new FileInfo(firstPath), new FileInfo(secondPath) });
var cfg = KubernetesClientConfiguration.LoadKubeConfig(new FileInfo[]
{
new FileInfo(firstPath), new FileInfo(secondPath),
});
// green-user
Assert.NotNull(cfg.CurrentContext);
@@ -499,7 +517,10 @@ namespace k8s.Tests
{
var path = Path.GetFullPath("assets/kubeconfig.preferences-extensions.yml");
var cfg = KubernetesClientConfiguration.LoadKubeConfig(new FileInfo[] { new FileInfo(path), new FileInfo(path) });
var cfg = KubernetesClientConfiguration.LoadKubeConfig(new FileInfo[]
{
new FileInfo(path), new FileInfo(path),
});
Assert.Equal(1, cfg.Extensions.Count);
Assert.Equal(1, cfg.Preferences.Count);
@@ -555,7 +576,8 @@ namespace k8s.Tests
{
Assert.Equal(expected.Name, actual.Name);
Assert.Equal(expected.ClusterEndpoint.CertificateAuthority, actual.ClusterEndpoint.CertificateAuthority);
Assert.Equal(expected.ClusterEndpoint.CertificateAuthorityData, actual.ClusterEndpoint.CertificateAuthorityData);
Assert.Equal(expected.ClusterEndpoint.CertificateAuthorityData,
actual.ClusterEndpoint.CertificateAuthorityData);
Assert.Equal(expected.ClusterEndpoint.Server, actual.ClusterEndpoint.Server);
Assert.Equal(expected.ClusterEndpoint.SkipTlsVerify, actual.ClusterEndpoint.SkipTlsVerify);
}

View File

@@ -26,10 +26,16 @@ namespace k8s.Tests.Logging
public TestOutputLogger(ITestOutputHelper testOutput, string loggerCategory, LogLevel minLogLevel)
{
if (testOutput == null)
{
throw new ArgumentNullException(nameof(testOutput));
}
if (String.IsNullOrWhiteSpace(loggerCategory))
throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'loggerCategory'.", nameof(loggerCategory));
{
throw new ArgumentException(
"Argument cannot be null, empty, or entirely composed of whitespace: 'loggerCategory'.",
nameof(loggerCategory));
}
TestOutput = testOutput;
LoggerCategory = loggerCategory;
@@ -69,22 +75,23 @@ namespace k8s.Tests.Logging
/// <param name="formatter">
/// A function that creates a <c>string</c> log message from the <paramref name="state"/> and <paramref name="exception"/>.
/// </param>
public void Log<TState>(LogLevel level, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
public void Log<TState>(LogLevel level, EventId eventId, TState state, Exception exception,
Func<TState, Exception, string> formatter)
{
if (formatter == null)
{
throw new ArgumentNullException(nameof(formatter));
}
TestOutput.WriteLine(String.Format("[{0}] {1}: {2}",
level,
LoggerCategory,
formatter(state, exception)
));
formatter(state, exception)));
if (exception != null)
{
TestOutput.WriteLine(
exception.ToString()
);
exception.ToString());
}
}

View File

@@ -22,7 +22,9 @@ namespace k8s.Tests.Logging
public TestOutputLoggerProvider(ITestOutputHelper testOutput, LogLevel minLogLevel)
{
if (testOutput == null)
{
throw new ArgumentNullException(nameof(testOutput));
}
TestOutput = testOutput;
MinLogLevel = minLogLevel;

View File

@@ -21,17 +21,21 @@ namespace k8s.Tests.Logging
/// <param name="minLogLevel">
/// The minimum level to log at.
/// </param>
public static void AddTestOutput(this ILoggingBuilder logging, ITestOutputHelper testOutput, LogLevel minLogLevel = LogLevel.Information)
public static void AddTestOutput(this ILoggingBuilder logging, ITestOutputHelper testOutput,
LogLevel minLogLevel = LogLevel.Information)
{
if (logging == null)
{
throw new ArgumentNullException(nameof(logging));
}
if (testOutput == null)
{
throw new ArgumentNullException(nameof(testOutput));
}
logging.AddProvider(
new TestOutputLoggerProvider(testOutput, minLogLevel)
);
new TestOutputLoggerProvider(testOutput, minLogLevel));
}
/// <summary>
@@ -49,17 +53,21 @@ namespace k8s.Tests.Logging
/// <returns>
/// The logger factory (enables inline use / method-chaining).
/// </returns>
public static ILoggerFactory AddTestOutput(this ILoggerFactory loggers, ITestOutputHelper testOutput, LogLevel minLogLevel = LogLevel.Information)
public static ILoggerFactory AddTestOutput(this ILoggerFactory loggers, ITestOutputHelper testOutput,
LogLevel minLogLevel = LogLevel.Information)
{
if (loggers == null)
{
throw new ArgumentNullException(nameof(loggers));
}
if (testOutput == null)
{
throw new ArgumentNullException(nameof(testOutput));
}
loggers.AddProvider(
new TestOutputLoggerProvider(testOutput, minLogLevel)
);
new TestOutputLoggerProvider(testOutput, minLogLevel));
return loggers;
}

View File

@@ -18,12 +18,12 @@ namespace k8s.Tests.Mock
{
// paste from minikube /api/v1/namespaces/default/pods
public const string MockPodResponse =
"{\r\n \"kind\": \"PodList\",\r\n \"apiVersion\": \"v1\",\r\n \"metadata\": {\r\n \"selfLink\": \"/api/v1/namespaces/default/pods\",\r\n \"resourceVersion\": \"1762810\"\r\n },\r\n \"items\": [\r\n {\r\n \"metadata\": {\r\n \"name\": \"nginx-1493591563-xb2v4\",\r\n \"generateName\": \"nginx-1493591563-\",\r\n \"namespace\": \"default\",\r\n \"selfLink\": \"/api/v1/namespaces/default/pods/nginx-1493591563-xb2v4\",\r\n \"uid\": \"ac1abb94-9c58-11e7-aaf5-00155d744505\",\r\n \"resourceVersion\": \"1737928\",\r\n \"creationTimestamp\": \"2017-09-18T10:03:51Z\",\r\n \"labels\": {\r\n \"app\": \"nginx\",\r\n \"pod-template-hash\": \"1493591563\"\r\n },\r\n \"annotations\": {\r\n \"kubernetes.io/created-by\": \"{\\\"kind\\\":\\\"SerializedReference\\\",\\\"apiVersion\\\":\\\"v1\\\",\\\"reference\\\":{\\\"kind\\\":\\\"ReplicaSet\\\",\\\"namespace\\\":\\\"default\\\",\\\"name\\\":\\\"nginx-1493591563\\\",\\\"uid\\\":\\\"ac013b63-9c58-11e7-aaf5-00155d744505\\\",\\\"apiVersion\\\":\\\"extensions\\\",\\\"resourceVersion\\\":\\\"5306\\\"}}\\n\"\r\n },\r\n \"ownerReferences\": [\r\n {\r\n \"apiVersion\": \"extensions/v1beta1\",\r\n \"kind\": \"ReplicaSet\",\r\n \"name\": \"nginx-1493591563\",\r\n \"uid\": \"ac013b63-9c58-11e7-aaf5-00155d744505\",\r\n \"controller\": true,\r\n \"blockOwnerDeletion\": true\r\n }\r\n ]\r\n },\r\n \"spec\": {\r\n \"volumes\": [\r\n {\r\n \"name\": \"default-token-3zzcj\",\r\n \"secret\": {\r\n \"secretName\": \"default-token-3zzcj\",\r\n \"defaultMode\": 420\r\n }\r\n }\r\n ],\r\n \"containers\": [\r\n {\r\n \"name\": \"nginx\",\r\n \"image\": \"nginx\",\r\n \"resources\": {},\r\n \"volumeMounts\": [\r\n {\r\n \"name\": \"default-token-3zzcj\",\r\n \"readOnly\": true,\r\n \"mountPath\": \"/var/run/secrets/kubernetes.io/serviceaccount\"\r\n }\r\n ],\r\n \"terminationMessagePath\": \"/dev/termination-log\",\r\n \"terminationMessagePolicy\": \"File\",\r\n \"imagePullPolicy\": \"Always\"\r\n }\r\n ],\r\n \"restartPolicy\": \"Always\",\r\n \"terminationGracePeriodSeconds\": 30,\r\n \"dnsPolicy\": \"ClusterFirst\",\r\n \"serviceAccountName\": \"default\",\r\n \"serviceAccount\": \"default\",\r\n \"nodeName\": \"ubuntu\",\r\n \"securityContext\": {},\r\n \"schedulerName\": \"default-scheduler\"\r\n },\r\n \"status\": {\r\n \"phase\": \"Running\",\r\n \"conditions\": [\r\n {\r\n \"type\": \"Initialized\",\r\n \"status\": \"True\",\r\n \"lastProbeTime\": null,\r\n \"lastTransitionTime\": \"2017-09-18T10:03:51Z\"\r\n },\r\n {\r\n \"type\": \"Ready\",\r\n \"status\": \"True\",\r\n \"lastProbeTime\": null,\r\n \"lastTransitionTime\": \"2017-10-12T07:09:21Z\"\r\n },\r\n {\r\n \"type\": \"PodScheduled\",\r\n \"status\": \"True\",\r\n \"lastProbeTime\": null,\r\n \"lastTransitionTime\": \"2017-09-18T10:03:51Z\"\r\n }\r\n ],\r\n \"hostIP\": \"192.168.188.42\",\r\n \"podIP\": \"172.17.0.5\",\r\n \"startTime\": \"2017-09-18T10:03:51Z\",\r\n \"containerStatuses\": [\r\n {\r\n \"name\": \"nginx\",\r\n \"state\": {\r\n \"running\": {\r\n \"startedAt\": \"2017-10-12T07:09:20Z\"\r\n }\r\n },\r\n \"lastState\": {\r\n \"terminated\": {\r\n \"exitCode\": 0,\r\n \"reason\": \"Completed\",\r\n \"startedAt\": \"2017-10-10T21:35:51Z\",\r\n \"finishedAt\": \"2017-10-12T07:07:37Z\",\r\n \"containerID\": \"docker://94df3f3965807421ad6dc76618e00b76cb15d024919c4946f3eb46a92659c62a\"\r\n }\r\n },\r\n \"ready\": true,\r\n \"restartCount\": 7,\r\n \"image\": \"nginx:latest\",\r\n \"imageID\": \"docker-pullable://nginx@sha256:004ac1d5e791e705f12a17c80d7bb1e8f7f01aa7dca7deee6e65a03465392072\",\r\n \"containerID\": \"docker://fa11bdd48c9b7d3a6c4c3f9b6d7319743c3455ab8d00c57d59c083b319b88194\"\r\n }\r\n ],\r\n \"qosClass\": \"BestEffort\"\r\n }\r\n }\r\n ]\r\n}"
;
"{\r\n \"kind\": \"PodList\",\r\n \"apiVersion\": \"v1\",\r\n \"metadata\": {\r\n \"selfLink\": \"/api/v1/namespaces/default/pods\",\r\n \"resourceVersion\": \"1762810\"\r\n },\r\n \"items\": [\r\n {\r\n \"metadata\": {\r\n \"name\": \"nginx-1493591563-xb2v4\",\r\n \"generateName\": \"nginx-1493591563-\",\r\n \"namespace\": \"default\",\r\n \"selfLink\": \"/api/v1/namespaces/default/pods/nginx-1493591563-xb2v4\",\r\n \"uid\": \"ac1abb94-9c58-11e7-aaf5-00155d744505\",\r\n \"resourceVersion\": \"1737928\",\r\n \"creationTimestamp\": \"2017-09-18T10:03:51Z\",\r\n \"labels\": {\r\n \"app\": \"nginx\",\r\n \"pod-template-hash\": \"1493591563\"\r\n },\r\n \"annotations\": {\r\n \"kubernetes.io/created-by\": \"{\\\"kind\\\":\\\"SerializedReference\\\",\\\"apiVersion\\\":\\\"v1\\\",\\\"reference\\\":{\\\"kind\\\":\\\"ReplicaSet\\\",\\\"namespace\\\":\\\"default\\\",\\\"name\\\":\\\"nginx-1493591563\\\",\\\"uid\\\":\\\"ac013b63-9c58-11e7-aaf5-00155d744505\\\",\\\"apiVersion\\\":\\\"extensions\\\",\\\"resourceVersion\\\":\\\"5306\\\"}}\\n\"\r\n },\r\n \"ownerReferences\": [\r\n {\r\n \"apiVersion\": \"extensions/v1beta1\",\r\n \"kind\": \"ReplicaSet\",\r\n \"name\": \"nginx-1493591563\",\r\n \"uid\": \"ac013b63-9c58-11e7-aaf5-00155d744505\",\r\n \"controller\": true,\r\n \"blockOwnerDeletion\": true\r\n }\r\n ]\r\n },\r\n \"spec\": {\r\n \"volumes\": [\r\n {\r\n \"name\": \"default-token-3zzcj\",\r\n \"secret\": {\r\n \"secretName\": \"default-token-3zzcj\",\r\n \"defaultMode\": 420\r\n }\r\n }\r\n ],\r\n \"containers\": [\r\n {\r\n \"name\": \"nginx\",\r\n \"image\": \"nginx\",\r\n \"resources\": {},\r\n \"volumeMounts\": [\r\n {\r\n \"name\": \"default-token-3zzcj\",\r\n \"readOnly\": true,\r\n \"mountPath\": \"/var/run/secrets/kubernetes.io/serviceaccount\"\r\n }\r\n ],\r\n \"terminationMessagePath\": \"/dev/termination-log\",\r\n \"terminationMessagePolicy\": \"File\",\r\n \"imagePullPolicy\": \"Always\"\r\n }\r\n ],\r\n \"restartPolicy\": \"Always\",\r\n \"terminationGracePeriodSeconds\": 30,\r\n \"dnsPolicy\": \"ClusterFirst\",\r\n \"serviceAccountName\": \"default\",\r\n \"serviceAccount\": \"default\",\r\n \"nodeName\": \"ubuntu\",\r\n \"securityContext\": {},\r\n \"schedulerName\": \"default-scheduler\"\r\n },\r\n \"status\": {\r\n \"phase\": \"Running\",\r\n \"conditions\": [\r\n {\r\n \"type\": \"Initialized\",\r\n \"status\": \"True\",\r\n \"lastProbeTime\": null,\r\n \"lastTransitionTime\": \"2017-09-18T10:03:51Z\"\r\n },\r\n {\r\n \"type\": \"Ready\",\r\n \"status\": \"True\",\r\n \"lastProbeTime\": null,\r\n \"lastTransitionTime\": \"2017-10-12T07:09:21Z\"\r\n },\r\n {\r\n \"type\": \"PodScheduled\",\r\n \"status\": \"True\",\r\n \"lastProbeTime\": null,\r\n \"lastTransitionTime\": \"2017-09-18T10:03:51Z\"\r\n }\r\n ],\r\n \"hostIP\": \"192.168.188.42\",\r\n \"podIP\": \"172.17.0.5\",\r\n \"startTime\": \"2017-09-18T10:03:51Z\",\r\n \"containerStatuses\": [\r\n {\r\n \"name\": \"nginx\",\r\n \"state\": {\r\n \"running\": {\r\n \"startedAt\": \"2017-10-12T07:09:20Z\"\r\n }\r\n },\r\n \"lastState\": {\r\n \"terminated\": {\r\n \"exitCode\": 0,\r\n \"reason\": \"Completed\",\r\n \"startedAt\": \"2017-10-10T21:35:51Z\",\r\n \"finishedAt\": \"2017-10-12T07:07:37Z\",\r\n \"containerID\": \"docker://94df3f3965807421ad6dc76618e00b76cb15d024919c4946f3eb46a92659c62a\"\r\n }\r\n },\r\n \"ready\": true,\r\n \"restartCount\": 7,\r\n \"image\": \"nginx:latest\",\r\n \"imageID\": \"docker-pullable://nginx@sha256:004ac1d5e791e705f12a17c80d7bb1e8f7f01aa7dca7deee6e65a03465392072\",\r\n \"containerID\": \"docker://fa11bdd48c9b7d3a6c4c3f9b6d7319743c3455ab8d00c57d59c083b319b88194\"\r\n }\r\n ],\r\n \"qosClass\": \"BestEffort\"\r\n }\r\n }\r\n ]\r\n}";
private readonly IWebHost _webHost;
public MockKubeApiServer(ITestOutputHelper testOutput, Func<HttpContext, Task<bool>> shouldNext = null, Action<ListenOptions> listenConfigure = null,
public MockKubeApiServer(ITestOutputHelper testOutput, Func<HttpContext, Task<bool>> shouldNext = null,
Action<ListenOptions> listenConfigure = null,
string resp = MockPodResponse)
{
shouldNext = shouldNext ?? (_ => Task.FromResult(true));
@@ -43,7 +43,9 @@ namespace k8s.Tests.Mock
logging.ClearProviders();
if (testOutput != null)
{
logging.AddTestOutput(testOutput);
}
})
.Build();

View File

@@ -34,13 +34,14 @@ namespace k8s.Tests.Mock
{
Buffer = buffer,
MessageType = messageType,
EndOfMessage = endOfMessage
EndOfMessage = endOfMessage,
});
this.receiveEvent.Set();
return Task.CompletedTask;
}
#region WebSocket overrides
public override WebSocketCloseStatus? CloseStatus => this.closeStatus;
public override string CloseStatusDescription => this.closeStatusDescription;
@@ -54,7 +55,8 @@ namespace k8s.Tests.Mock
throw new NotImplementedException();
}
public override Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
public override Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription,
CancellationToken cancellationToken)
{
this.closeStatus = closeStatus;
this.closeStatusDescription = statusDescription;
@@ -62,13 +64,14 @@ namespace k8s.Tests.Mock
{
Buffer = new ArraySegment<byte>(new byte[] { }),
EndOfMessage = true,
MessageType = WebSocketMessageType.Close
MessageType = WebSocketMessageType.Close,
});
this.receiveEvent.Set();
return Task.CompletedTask;
}
public override Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
public override Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription,
CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
@@ -79,7 +82,8 @@ namespace k8s.Tests.Mock
this.receiveEvent.Set();
}
public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken)
public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer,
CancellationToken cancellationToken)
{
if (this.receiveBuffers.Count == 0)
{
@@ -113,19 +117,22 @@ namespace k8s.Tests.Mock
return new WebSocketReceiveResult(bytesReceived, messageType, endOfMessage);
}
public override Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken)
public override Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage,
CancellationToken cancellationToken)
{
this.MessageSent?.Invoke(this, new MessageDataEventArgs()
{
Data = new MessageData()
this.MessageSent?.Invoke(this,
new MessageDataEventArgs()
{
Buffer = buffer,
MessageType = messageType,
EndOfMessage = endOfMessage
}
});
Data = new MessageData()
{
Buffer = buffer,
MessageType = messageType,
EndOfMessage = endOfMessage
},
});
return Task.CompletedTask;
}
#endregion
public class MessageData

View File

@@ -1,5 +1,4 @@
#if !NETCOREAPP2_1
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -12,9 +11,11 @@ namespace k8s.Tests.Mock
{
public class MockWebSocketBuilder : WebSocketBuilder
{
public Dictionary<string, string> RequestHeaders { get; } = new Dictionary<string, string>();
public Dictionary<string, string> RequestHeaders { get; }
= new Dictionary<string, string>();
public Collection<X509Certificate2> Certificates { get; } = new Collection<X509Certificate2>();
public Collection<X509Certificate2> Certificates { get; }
= new Collection<X509Certificate2>();
public Uri Uri { get; private set; }
@@ -28,7 +29,8 @@ namespace k8s.Tests.Mock
public override Task<WebSocket> BuildAndConnectAsync(Uri uri, CancellationToken cancellationToken)
{
this.Uri = uri;
this.Uri
= uri;
return Task.FromResult(this.PublicWebSocket);
}

View File

@@ -22,7 +22,9 @@ namespace k8s.Tests.Mock.Server.Controllers
public PodExecController(WebSocketTestAdapter webSocketTestAdapter)
{
if (webSocketTestAdapter == null)
{
throw new ArgumentNullException(nameof(webSocketTestAdapter));
}
WebSocketTestAdapter = webSocketTestAdapter;
}
@@ -45,11 +47,12 @@ namespace k8s.Tests.Mock.Server.Controllers
public async Task<IActionResult> Exec(string kubeNamespace, string podName)
{
if (!HttpContext.WebSockets.IsWebSocketRequest)
{
return BadRequest("Exec requires WebSockets");
}
WebSocket webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(
subProtocol: WebSocketProtocol.ChannelWebSocketProtocol
);
subProtocol: WebSocketProtocol.ChannelWebSocketProtocol);
WebSocketTestAdapter.AcceptedPodExecV1Connection.AcceptServerSocket(webSocket);

View File

@@ -23,7 +23,9 @@ namespace k8s.Tests.Mock.Server.Controllers
public PodPortForwardController(WebSocketTestAdapter webSocketTestAdapter)
{
if (webSocketTestAdapter == null)
{
throw new ArgumentNullException(nameof(webSocketTestAdapter));
}
WebSocketTestAdapter = webSocketTestAdapter;
}
@@ -49,11 +51,12 @@ namespace k8s.Tests.Mock.Server.Controllers
public async Task<IActionResult> Exec(string kubeNamespace, string podName, IEnumerable<string> ports)
{
if (!HttpContext.WebSockets.IsWebSocketRequest)
{
return BadRequest("PortForward requires WebSockets");
}
WebSocket webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(
subProtocol: WebSocketProtocol.ChannelWebSocketProtocol
);
subProtocol: WebSocketProtocol.ChannelWebSocketProtocol);
WebSocketTestAdapter.AcceptedPodPortForwardV1Connection.AcceptServerSocket(webSocket);

View File

@@ -26,7 +26,9 @@ namespace k8s.Tests.Mock.Server
public void ConfigureServices(IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.AddLogging(logging =>
{
@@ -46,7 +48,7 @@ namespace k8s.Tests.Mock.Server
app.UseWebSockets(new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromSeconds(5),
ReceiveBufferSize = 2048
ReceiveBufferSize = 2048,
});
app.UseMvc();
}

View File

@@ -67,7 +67,9 @@ namespace k8s.Tests.Mock.Server
public void AcceptServerSocket(WebSocket serverSocket)
{
if (serverSocket == null)
{
throw new ArgumentNullException(nameof(serverSocket));
}
_completion.SetResult(serverSocket);
}
@@ -81,7 +83,9 @@ namespace k8s.Tests.Mock.Server
public void RejectServerSocket(Exception reason)
{
if (reason == null)
{
throw new ArgumentNullException(nameof(reason));
}
_completion.SetException(reason);
}

View File

@@ -29,7 +29,13 @@ namespace k8s.Tests
s = new V1Status() { Status = "Failure", Code = 400, Message = "It's all messed up." };
Assert.Equal("BadRequest - It's all messed up.", s.ToString());
s = new V1Status() { Status = "Failure", Code = 400, Reason = "IllegalValue", Message = "You're breaking the LAW!", };
s = new V1Status()
{
Status = "Failure",
Code = 400,
Reason = "IllegalValue",
Message = "You're breaking the LAW!",
};
Assert.Equal("IllegalValue - You're breaking the LAW!", s.ToString());
}
}

View File

@@ -22,47 +22,33 @@ namespace k8s.Tests
{
foreach (var (input, expect) in new[]
{
("0", new ResourceQuantity(0, 0, DecimalSI)),
("0n", new ResourceQuantity(0, 0, DecimalSI)),
("0u", new ResourceQuantity(0, 0, DecimalSI)),
("0m", new ResourceQuantity(0, 0, DecimalSI)),
("0Ki", new ResourceQuantity(0, 0, BinarySI)),
("0k", new ResourceQuantity(0, 0, DecimalSI)),
("0Mi", new ResourceQuantity(0, 0, BinarySI)),
("0M", new ResourceQuantity(0, 0, DecimalSI)),
("0Gi", new ResourceQuantity(0, 0, BinarySI)),
("0G", new ResourceQuantity(0, 0, DecimalSI)),
("0Ti", new ResourceQuantity(0, 0, BinarySI)),
("0T", new ResourceQuantity(0, 0, DecimalSI)),
("0", new ResourceQuantity(0, 0, DecimalSI)), ("0n", new ResourceQuantity(0, 0, DecimalSI)),
("0u", new ResourceQuantity(0, 0, DecimalSI)), ("0m", new ResourceQuantity(0, 0, DecimalSI)),
("0Ki", new ResourceQuantity(0, 0, BinarySI)), ("0k", new ResourceQuantity(0, 0, DecimalSI)),
("0Mi", new ResourceQuantity(0, 0, BinarySI)), ("0M", new ResourceQuantity(0, 0, DecimalSI)),
("0Gi", new ResourceQuantity(0, 0, BinarySI)), ("0G", new ResourceQuantity(0, 0, DecimalSI)),
("0Ti", new ResourceQuantity(0, 0, BinarySI)), ("0T", new ResourceQuantity(0, 0, DecimalSI)),
// Quantity less numbers are allowed
("1", new ResourceQuantity(1, 0, DecimalSI)),
// Binary suffixes
("1Ki", new ResourceQuantity(1024, 0, BinarySI)),
("8Ki", new ResourceQuantity(8 * 1024, 0, BinarySI)),
("1Ki", new ResourceQuantity(1024, 0, BinarySI)), ("8Ki", new ResourceQuantity(8 * 1024, 0, BinarySI)),
("7Mi", new ResourceQuantity(7 * 1024 * 1024, 0, BinarySI)),
("6Gi", new ResourceQuantity(6L * 1024 * 1024 * 1024, 0, BinarySI)),
("5Ti", new ResourceQuantity(5L * 1024 * 1024 * 1024 * 1024, 0, BinarySI)),
("4Pi", new ResourceQuantity(4L * 1024 * 1024 * 1024 * 1024 * 1024, 0, BinarySI)),
("3Ei", new ResourceQuantity(3L * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, 0, BinarySI)),
("10Ti", new ResourceQuantity(10L * 1024 * 1024 * 1024 * 1024, 0, BinarySI)),
("100Ti", new ResourceQuantity(100L * 1024 * 1024 * 1024 * 1024, 0, BinarySI)),
// Decimal suffixes
("5n", new ResourceQuantity(5, -9, DecimalSI)),
("4u", new ResourceQuantity(4, -6, DecimalSI)),
("3m", new ResourceQuantity(3, -3, DecimalSI)),
("9", new ResourceQuantity(9, 0, DecimalSI)),
("8k", new ResourceQuantity(8, 3, DecimalSI)),
("50k", new ResourceQuantity(5, 4, DecimalSI)),
("7M", new ResourceQuantity(7, 6, DecimalSI)),
("6G", new ResourceQuantity(6, 9, DecimalSI)),
("5T", new ResourceQuantity(5, 12, DecimalSI)),
("40T", new ResourceQuantity(4, 13, DecimalSI)),
("300T", new ResourceQuantity(3, 14, DecimalSI)),
("2P", new ResourceQuantity(2, 15, DecimalSI)),
("5n", new ResourceQuantity(5, -9, DecimalSI)), ("4u", new ResourceQuantity(4, -6, DecimalSI)),
("3m", new ResourceQuantity(3, -3, DecimalSI)), ("9", new ResourceQuantity(9, 0, DecimalSI)),
("8k", new ResourceQuantity(8, 3, DecimalSI)), ("50k", new ResourceQuantity(5, 4, DecimalSI)),
("7M", new ResourceQuantity(7, 6, DecimalSI)), ("6G", new ResourceQuantity(6, 9, DecimalSI)),
("5T", new ResourceQuantity(5, 12, DecimalSI)), ("40T", new ResourceQuantity(4, 13, DecimalSI)),
("300T", new ResourceQuantity(3, 14, DecimalSI)), ("2P", new ResourceQuantity(2, 15, DecimalSI)),
("1E", new ResourceQuantity(1, 18, DecimalSI)),
// Decimal exponents
@@ -81,12 +67,9 @@ namespace k8s.Tests
("100.035k", new ResourceQuantity(100035, 0, DecimalSI)),
// Things that look like floating point
("0.001", new ResourceQuantity(1, -3, DecimalSI)),
("0.0005k", new ResourceQuantity(5, -1, DecimalSI)),
("0.005", new ResourceQuantity(5, -3, DecimalSI)),
("0.05", new ResourceQuantity(5, -2, DecimalSI)),
("0.5", new ResourceQuantity(5, -1, DecimalSI)),
("0.00050k", new ResourceQuantity(5, -1, DecimalSI)),
("0.001", new ResourceQuantity(1, -3, DecimalSI)), ("0.0005k", new ResourceQuantity(5, -1, DecimalSI)),
("0.005", new ResourceQuantity(5, -3, DecimalSI)), ("0.05", new ResourceQuantity(5, -2, DecimalSI)),
("0.5", new ResourceQuantity(5, -1, DecimalSI)), ("0.00050k", new ResourceQuantity(5, -1, DecimalSI)),
("0.00500", new ResourceQuantity(5, -3, DecimalSI)),
("0.05000", new ResourceQuantity(5, -2, DecimalSI)),
("0.50000", new ResourceQuantity(5, -1, DecimalSI)),
@@ -95,23 +78,19 @@ namespace k8s.Tests
("0.5e-2", new ResourceQuantity(5, -3, DecimalExponent)),
("0.5e0", new ResourceQuantity(5, -1, DecimalExponent)),
("10.035M", new ResourceQuantity(10035, 3, DecimalSI)),
("1.2e3", new ResourceQuantity(12, 2, DecimalExponent)),
("1.3E+6", new ResourceQuantity(13, 5, DecimalExponent)),
("1.40e9", new ResourceQuantity(14, 8, DecimalExponent)),
("1.53E12", new ResourceQuantity(153, 10, DecimalExponent)),
("1.6e15", new ResourceQuantity(16, 14, DecimalExponent)),
("1.7E18", new ResourceQuantity(17, 17, DecimalExponent)),
("9.01", new ResourceQuantity(901, -2, DecimalSI)),
("8.1k", new ResourceQuantity(81, 2, DecimalSI)),
("9.01", new ResourceQuantity(901, -2, DecimalSI)), ("8.1k", new ResourceQuantity(81, 2, DecimalSI)),
("7.123456M", new ResourceQuantity(7123456, 0, DecimalSI)),
("6.987654321G", new ResourceQuantity(6987654321, 0, DecimalSI)),
("5.444T", new ResourceQuantity(5444, 9, DecimalSI)),
("40.1T", new ResourceQuantity(401, 11, DecimalSI)),
("300.2T", new ResourceQuantity(3002, 11, DecimalSI)),
("2.5P", new ResourceQuantity(25, 14, DecimalSI)),
("1.01E", new ResourceQuantity(101, 16, DecimalSI)),
("2.5P", new ResourceQuantity(25, 14, DecimalSI)), ("1.01E", new ResourceQuantity(101, 16, DecimalSI)),
// Things that saturate/round
("3.001n", new ResourceQuantity(4, -9, DecimalSI)),
@@ -135,10 +114,8 @@ namespace k8s.Tests
// Things written by trolls
("0.000000000001Ki", new ResourceQuantity(2, -9, DecimalSI)), // rounds up, changes format
(".001", new ResourceQuantity(1, -3, DecimalSI)),
(".0001k", new ResourceQuantity(100, -3, DecimalSI)),
("1.", new ResourceQuantity(1, 0, DecimalSI)),
("1.G", new ResourceQuantity(1, 9, DecimalSI))
(".001", new ResourceQuantity(1, -3, DecimalSI)), (".0001k", new ResourceQuantity(100, -3, DecimalSI)),
("1.", new ResourceQuantity(1, 0, DecimalSI)), ("1.G", new ResourceQuantity(1, 9, DecimalSI)),
})
{
Assert.Equal(expect.ToString(), new ResourceQuantity(input).ToString());
@@ -146,15 +123,7 @@ namespace k8s.Tests
foreach (var s in new[]
{
"1.1.M",
"1+1.0M",
"0.1mi",
"0.1am",
"aoeu",
".5i",
"1i",
"-3.01i",
"-3.01e-"
"1.1.M", "1+1.0M", "0.1mi", "0.1am", "aoeu", ".5i", "1i", "-3.01i", "-3.01e-",
// TODO support trailing whitespace is forbidden
// " 1",
@@ -198,8 +167,7 @@ namespace k8s.Tests
(new ResourceQuantity(1234567, -3, BinarySI), "1234567m", ""),
(new ResourceQuantity(3, 3, DecimalSI), "3k", ""),
(new ResourceQuantity(1025, 0, BinarySI), "1025", ""),
(new ResourceQuantity(0, 0, DecimalSI), "0", ""),
(new ResourceQuantity(0, 0, BinarySI), "0", ""),
(new ResourceQuantity(0, 0, DecimalSI), "0", ""), (new ResourceQuantity(0, 0, BinarySI), "0", ""),
(new ResourceQuantity(1, 9, DecimalExponent), "1e9", ".001e12"),
(new ResourceQuantity(1, -3, DecimalExponent), "1e-3", "0.001e0"),
(new ResourceQuantity(1, -9, DecimalExponent), "1e-9", "1000e-12"),
@@ -217,7 +185,7 @@ namespace k8s.Tests
(new ResourceQuantity(10800, -10, DecimalSI), "1080n", ""),
(new ResourceQuantity(1, -6, DecimalSI), "1u", ""),
(new ResourceQuantity(80, -6, DecimalSI), "80u", ""),
(new ResourceQuantity(1080, -6, DecimalSI), "1080u", "")
(new ResourceQuantity(1080, -6, DecimalSI), "1080u", ""),
})
{
Assert.Equal(expect, input.ToString());

View File

@@ -27,10 +27,7 @@ namespace k8s.Tests
using (StreamDemuxer demuxer = new StreamDemuxer(ws))
{
List<byte> sentBuffer = new List<byte>();
ws.MessageSent += (sender, args) =>
{
sentBuffer.AddRange(args.Data.Buffer);
};
ws.MessageSent += (sender, args) => { sentBuffer.AddRange(args.Data.Buffer); };
demuxer.Start();
@@ -40,7 +37,8 @@ namespace k8s.Tests
stream.Write(b, 0, b.Length);
// Send 100 bytes, expect 1 (channel index) + 100 (payload) = 101 bytes
Assert.True(await WaitForAsync(() => sentBuffer.Count == 101), $"Demuxer error: expect to send 101 bytes, but actually send {sentBuffer.Count} bytes.");
Assert.True(await WaitForAsync(() => sentBuffer.Count == 101),
$"Demuxer error: expect to send 101 bytes, but actually send {sentBuffer.Count} bytes.");
Assert.True(sentBuffer[0] == channelIndex, "The first sent byte is not channel index!");
Assert.True(sentBuffer[1] == 0xEF, "Incorrect payload!");
}
@@ -53,10 +51,7 @@ namespace k8s.Tests
using (StreamDemuxer demuxer = new StreamDemuxer(ws))
{
List<byte> sentBuffer = new List<byte>();
ws.MessageSent += (sender, args) =>
{
sentBuffer.AddRange(args.Data.Buffer);
};
ws.MessageSent += (sender, args) => { sentBuffer.AddRange(args.Data.Buffer); };
demuxer.Start();
@@ -68,7 +63,8 @@ namespace k8s.Tests
stream.Write(b, 0, b.Length);
// Send 300 bytes in 2 messages, expect 1 (channel index) * 2 + 300 (payload) = 302 bytes
Assert.True(await WaitForAsync(() => sentBuffer.Count == 302), $"Demuxer error: expect to send 302 bytes, but actually send {sentBuffer.Count} bytes.");
Assert.True(await WaitForAsync(() => sentBuffer.Count == 302),
$"Demuxer error: expect to send 302 bytes, but actually send {sentBuffer.Count} bytes.");
Assert.True(sentBuffer[0] == channelIndex, "The first sent byte is not channel index!");
Assert.True(sentBuffer[1] == 0xEF, "The first part of payload incorrect!");
Assert.True(sentBuffer[101] == channelIndex, "The second message first byte is not channel index!");
@@ -93,9 +89,15 @@ namespace k8s.Tests
var t = Task.Run(async () =>
{
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(100, channelIndex, 0xAA, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(200, channelIndex, 0xAB, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(300, channelIndex, 0xAC, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(100, channelIndex, 0xAA, false)),
WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(200, channelIndex, 0xAB, false)),
WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(300, channelIndex, 0xAC, false)),
WebSocketMessageType.Binary, true);
await WaitForAsync(() => receivedBuffer.Count == expectedCount);
await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "normal", CancellationToken.None);
@@ -108,14 +110,17 @@ namespace k8s.Tests
{
break;
}
for (int i = 0; i < cRead; i++)
{
receivedBuffer.Add(buffer[i]);
}
}
await t;
Assert.True(receivedBuffer.Count == expectedCount, $"Demuxer error: expect to receive {expectedCount} bytes, but actually got {receivedBuffer.Count} bytes.");
Assert.True(receivedBuffer.Count == expectedCount,
$"Demuxer error: expect to receive {expectedCount} bytes, but actually got {receivedBuffer.Count} bytes.");
Assert.True(receivedBuffer[0] == 0xAA, "The first payload incorrect!");
Assert.True(receivedBuffer[98] == 0xAA, "The first payload incorrect!");
Assert.True(receivedBuffer[99] == 0xAB, "The second payload incorrect!");
@@ -143,9 +148,15 @@ namespace k8s.Tests
var t = Task.Run(async () =>
{
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(100, channelIndex, 0xB1, true)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(200, channelIndex, 0xB2, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(300, channelIndex, 0xB3, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(100, channelIndex, 0xB1, true)),
WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(200, channelIndex, 0xB2, false)),
WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(300, channelIndex, 0xB3, false)),
WebSocketMessageType.Binary, true);
await WaitForAsync(() => receivedBuffer.Count == expectedCount);
await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "normal", CancellationToken.None);
@@ -158,14 +169,17 @@ namespace k8s.Tests
{
break;
}
for (int i = 0; i < cRead; i++)
{
receivedBuffer.Add(buffer[i]);
}
}
await t;
Assert.True(receivedBuffer.Count == expectedCount, $"Demuxer error: expect to receive {expectedCount} bytes, but actually got {receivedBuffer.Count} bytes.");
Assert.True(receivedBuffer.Count == expectedCount,
$"Demuxer error: expect to receive {expectedCount} bytes, but actually got {receivedBuffer.Count} bytes.");
Assert.True(receivedBuffer[0] == 0xB1, "The first payload incorrect!");
Assert.True(receivedBuffer[96] == 0xB1, "The first payload incorrect!");
Assert.True(receivedBuffer[97] == 0xB2, "The second payload incorrect!");
@@ -193,9 +207,15 @@ namespace k8s.Tests
var t = Task.Run(async () =>
{
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(2, channelIndex, 0xC1, true)), WebSocketMessageType.Binary, false);
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(100, channelIndex, 0xC2, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(300, channelIndex, 0xC3, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(2, channelIndex, 0xC1, true)),
WebSocketMessageType.Binary, false);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(100, channelIndex, 0xC2, false)),
WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(300, channelIndex, 0xC3, false)),
WebSocketMessageType.Binary, true);
await WaitForAsync(() => receivedBuffer.Count == expectedCount);
await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "normal", CancellationToken.None);
@@ -208,14 +228,17 @@ namespace k8s.Tests
{
break;
}
for (int i = 0; i < cRead; i++)
{
receivedBuffer.Add(buffer[i]);
}
}
await t;
Assert.True(receivedBuffer.Count == expectedCount, $"Demuxer error: expect to receive {expectedCount} bytes, but actually got {receivedBuffer.Count} bytes.");
Assert.True(receivedBuffer.Count == expectedCount,
$"Demuxer error: expect to receive {expectedCount} bytes, but actually got {receivedBuffer.Count} bytes.");
Assert.True(receivedBuffer[0] == 0xC2, "The first payload incorrect!");
Assert.True(receivedBuffer[98] == 0xC2, "The first payload incorrect!");
Assert.True(receivedBuffer[99] == 0xC3, "The second payload incorrect!");
@@ -247,9 +270,15 @@ namespace k8s.Tests
var t1 = Task.Run(async () =>
{
// Simulate WebSocket received remote data to multiple streams
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(100, channelIndex1, 0xD1, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(200, channelIndex2, 0xD2, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(300, channelIndex1, 0xD3, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(100, channelIndex1, 0xD1, false)),
WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(200, channelIndex2, 0xD2, false)),
WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(300, channelIndex1, 0xD3, false)),
WebSocketMessageType.Binary, true);
await WaitForAsync(() => receivedBuffer1.Count == expectedCount1);
await WaitForAsync(() => receivedBuffer2.Count == expectedCount2);
@@ -265,6 +294,7 @@ namespace k8s.Tests
{
break;
}
for (int i = 0; i < cRead; i++)
{
receivedBuffer1.Add(buffer[i]);
@@ -281,6 +311,7 @@ namespace k8s.Tests
{
break;
}
for (int i = 0; i < cRead; i++)
{
receivedBuffer2.Add(buffer[i]);
@@ -289,8 +320,10 @@ namespace k8s.Tests
});
await Task.WhenAll(t1, t2, t3);
Assert.True(receivedBuffer1.Count == expectedCount1, $"Demuxer error: expect to receive {expectedCount1} bytes, but actually got {receivedBuffer1.Count} bytes.");
Assert.True(receivedBuffer2.Count == expectedCount2, $"Demuxer error: expect to receive {expectedCount2} bytes, but actually got {receivedBuffer2.Count} bytes.");
Assert.True(receivedBuffer1.Count == expectedCount1,
$"Demuxer error: expect to receive {expectedCount1} bytes, but actually got {receivedBuffer1.Count} bytes.");
Assert.True(receivedBuffer2.Count == expectedCount2,
$"Demuxer error: expect to receive {expectedCount2} bytes, but actually got {receivedBuffer2.Count} bytes.");
Assert.True(receivedBuffer1[0] == 0xD1, "The first payload incorrect!");
Assert.True(receivedBuffer1[98] == 0xD1, "The first payload incorrect!");
Assert.True(receivedBuffer1[99] == 0xD3, "The second payload incorrect!");
@@ -326,9 +359,15 @@ namespace k8s.Tests
var t1 = Task.Run(async () =>
{
// Simulate WebSocket received remote data to multiple streams
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(100, channelIndex1, 0xE1, true)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(200, channelIndex2, 0xE2, true)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(new ArraySegment<byte>(GenerateRandomBuffer(300, channelIndex1, 0xE3, false)), WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(100, channelIndex1, 0xE1, true)),
WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(200, channelIndex2, 0xE2, true)),
WebSocketMessageType.Binary, true);
await ws.InvokeReceiveAsync(
new ArraySegment<byte>(GenerateRandomBuffer(300, channelIndex1, 0xE3, false)),
WebSocketMessageType.Binary, true);
await WaitForAsync(() => receivedBuffer1.Count == expectedCount1);
await WaitForAsync(() => receivedBuffer2.Count == expectedCount2);
@@ -344,6 +383,7 @@ namespace k8s.Tests
{
break;
}
for (int i = 0; i < cRead; i++)
{
receivedBuffer1.Add(buffer[i]);
@@ -360,6 +400,7 @@ namespace k8s.Tests
{
break;
}
for (int i = 0; i < cRead; i++)
{
receivedBuffer2.Add(buffer[i]);
@@ -368,8 +409,10 @@ namespace k8s.Tests
});
await Task.WhenAll(t1, t2, t3);
Assert.True(receivedBuffer1.Count == expectedCount1, $"Demuxer error: expect to receive {expectedCount1} bytes, but actually got {receivedBuffer1.Count} bytes.");
Assert.True(receivedBuffer2.Count == expectedCount2, $"Demuxer error: expect to receive {expectedCount2} bytes, but actually got {receivedBuffer2.Count} bytes.");
Assert.True(receivedBuffer1.Count == expectedCount1,
$"Demuxer error: expect to receive {expectedCount1} bytes, but actually got {receivedBuffer1.Count} bytes.");
Assert.True(receivedBuffer2.Count == expectedCount2,
$"Demuxer error: expect to receive {expectedCount2} bytes, but actually got {receivedBuffer2.Count} bytes.");
Assert.True(receivedBuffer1[0] == 0xE1, "The first payload incorrect!");
Assert.True(receivedBuffer1[96] == 0xE1, "The first payload incorrect!");
Assert.True(receivedBuffer1[97] == 0xE3, "The second payload incorrect!");
@@ -387,13 +430,15 @@ namespace k8s.Tests
{
if (length > 1)
{
buffer[1] = 0xFF; // the first port bytes
buffer[1] = 0xFF; // the first port bytes
}
if (length > 2)
{
buffer[2] = 0xFF; // the 2nd port bytes
buffer[2] = 0xFF; // the 2nd port bytes
}
}
return buffer;
}
@@ -404,6 +449,7 @@ namespace k8s.Tests
{
buffer[i] = content;
}
return buffer;
}
@@ -418,8 +464,10 @@ namespace k8s.Tests
{
return true;
}
await Task.Delay(10);
} while (w.Elapsed.Duration().TotalSeconds < waitForSeconds);
return false;
}
finally

View File

@@ -15,8 +15,7 @@ namespace k8s.Tests
public static async Task Completed(Task task, TimeSpan timeout, string message = "Task timed out")
{
var timeoutTask = Task.Delay(
TimeSpan.FromMilliseconds(1000)
);
TimeSpan.FromMilliseconds(1000));
var completedTask = await Task.WhenAny(task, timeoutTask);
Assert.True(ReferenceEquals(task, completedTask), message);
@@ -28,11 +27,10 @@ namespace k8s.Tests
{
var timeoutTask =
Task.Delay(
TimeSpan.FromMilliseconds(1000)
)
.ContinueWith(
completedTimeoutTask => default(T) // Value is never returned, but we need a task of the same result type in order to use Task.WhenAny.
);
TimeSpan.FromMilliseconds(1000))
.ContinueWith(
completedTimeoutTask =>
default(T)); // Value is never returned, but we need a task of the same result type in order to use Task.WhenAny.
var completedTask = await Task.WhenAny(task, timeoutTask);
Assert.True(ReferenceEquals(task, completedTask), message);

View File

@@ -18,18 +18,11 @@ namespace k8s.Tests
[Fact]
public void ReturnStatus()
{
var v1Status = new V1Status
{
Message = "test message",
Status = "test status"
};
var v1Status = new V1Status { Message = "test message", Status = "test status" };
using (var server = new MockKubeApiServer(testOutput, resp: JsonConvert.SerializeObject(v1Status)))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var status = client.DeleteNamespace("test", new V1DeleteOptions());
@@ -44,22 +37,13 @@ namespace k8s.Tests
{
var corev1Namespace = new V1Namespace()
{
Metadata = new V1ObjectMeta()
{
Name = "test name"
},
Status = new V1NamespaceStatus()
{
Phase = "test termating"
}
Metadata = new V1ObjectMeta() { Name = "test name" },
Status = new V1NamespaceStatus() { Phase = "test termating" },
};
using (var server = new MockKubeApiServer(testOutput, resp: JsonConvert.SerializeObject(corev1Namespace)))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var status = client.DeleteNamespace("test", new V1DeleteOptions());
@@ -70,7 +54,6 @@ namespace k8s.Tests
Assert.Equal(obj.Metadata.Name, corev1Namespace.Metadata.Name);
Assert.Equal(obj.Status.Phase, corev1Namespace.Status.Phase);
}
}
}
}

View File

@@ -40,11 +40,9 @@ namespace k8s.Tests
private static string BuildWatchEventStreamLine(WatchEventType eventType)
{
var corev1PodList = JsonConvert.DeserializeObject<V1PodList>(MockKubeApiServer.MockPodResponse);
return JsonConvert.SerializeObject(new Watcher<V1Pod>.WatchEvent
{
Type = eventType,
Object = corev1PodList.Items.First()
}, new StringEnumConverter());
return JsonConvert.SerializeObject(
new Watcher<V1Pod>.WatchEvent { Type = eventType, Object = corev1PodList.Items.First() },
new StringEnumConverter());
}
private static async Task WriteStreamLine(HttpContext httpContext, string reponseLine)
@@ -60,19 +58,15 @@ namespace k8s.Tests
{
using (var server = new MockKubeApiServer(testOutput: testOutput))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
// did not pass watch param
var listTask = client.ListNamespacedPodWithHttpMessagesAsync("default");
var onErrorCalled = false;
using (listTask.Watch<V1Pod, V1PodList>((type, item) => { }, e =>
using (listTask.Watch<V1Pod, V1PodList>((type, item) => { }, e => { onErrorCalled = true; }))
{
onErrorCalled = true;
})) { }
}
await Task.Delay(TimeSpan.FromSeconds(1)); // delay for onerror to be called
Assert.True(onErrorCalled);
@@ -103,17 +97,11 @@ namespace k8s.Tests
return false;
}))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var listTask = client.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
using (listTask.Watch<V1Pod, V1PodList>((type, item) =>
{
eventsReceived.Set();
}))
using (listTask.Watch<V1Pod, V1PodList>((type, item) => { eventsReceived.Set(); }))
{
// here watcher is ready to use, but http server has not responsed yet.
created.Set();
@@ -151,10 +139,7 @@ namespace k8s.Tests
return false;
}))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var listTask = await client.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
@@ -176,16 +161,14 @@ namespace k8s.Tests
errors += 1;
eventsReceived.Signal();
},
onClosed: connectionClosed.Set
);
onClosed: connectionClosed.Set);
// wait server yields all events
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout));
Assert.True(
eventsReceived.CurrentCount == 0,
"Timed out waiting for all events / errors to be received."
);
"Timed out waiting for all events / errors to be received.");
Assert.Contains(WatchEventType.Added, events);
Assert.Contains(WatchEventType.Modified, events);
@@ -221,10 +204,7 @@ namespace k8s.Tests
return true;
}))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var listTask = await client.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
@@ -236,15 +216,13 @@ namespace k8s.Tests
events.Add(type);
eventsReceived.Signal();
},
onClosed: connectionClosed.Set
);
onClosed: connectionClosed.Set);
// wait at least an event
await Task.WhenAny(Task.Run(() => eventsReceived.Wait()), Task.Delay(TestTimeout));
Assert.True(
eventsReceived.CurrentCount == 0,
"Timed out waiting for events."
);
"Timed out waiting for events.");
Assert.NotEmpty(events);
Assert.True(watcher.Watching);
@@ -271,7 +249,8 @@ namespace k8s.Tests
[Fact]
public async Task WatchAllEvents()
{
AsyncCountdownEvent eventsReceived = new AsyncCountdownEvent(4 /* first line of response is eaten by WatcherDelegatingHandler */);
AsyncCountdownEvent eventsReceived =
new AsyncCountdownEvent(4 /* first line of response is eaten by WatcherDelegatingHandler */);
AsyncManualResetEvent serverShutdown = new AsyncManualResetEvent();
var waitForClosed = new AsyncManualResetEvent(false);
@@ -287,10 +266,7 @@ namespace k8s.Tests
return false;
}))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var listTask = await client.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
@@ -312,16 +288,14 @@ namespace k8s.Tests
errors += 1;
eventsReceived.Signal();
},
onClosed: waitForClosed.Set
);
onClosed: waitForClosed.Set);
// wait server yields all events
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout));
Assert.True(
eventsReceived.CurrentCount == 0,
"Timed out waiting for all events / errors to be received."
);
"Timed out waiting for all events / errors to be received.");
Assert.Contains(WatchEventType.Added, events);
Assert.Contains(WatchEventType.Deleted, events);
@@ -361,10 +335,7 @@ namespace k8s.Tests
return false;
}))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var listTask = await client.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
@@ -386,16 +357,14 @@ namespace k8s.Tests
errors += 1;
eventsReceived.Signal();
},
onClosed: connectionClosed.Set
);
onClosed: connectionClosed.Set);
// wait server yields all events
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout));
Assert.True(
eventsReceived.CurrentCount == 0,
"Timed out waiting for all events / errors to be received."
);
"Timed out waiting for all events / errors to be received.");
Assert.Contains(WatchEventType.Added, events);
Assert.Contains(WatchEventType.Deleted, events);
@@ -428,10 +397,7 @@ namespace k8s.Tests
throw new IOException("server down");
}))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var listTask = await client.ListNamespacedPodWithHttpMessagesAsync("default", watch: true);
@@ -451,8 +417,7 @@ namespace k8s.Tests
Assert.True(
exceptionReceived.IsSet,
"Timed out waiting for exception"
);
"Timed out waiting for exception");
await Task.WhenAny(waitForClosed.WaitAsync(), Task.Delay(TestTimeout));
Assert.True(waitForClosed.IsSet);
@@ -492,10 +457,8 @@ namespace k8s.Tests
var handler1 = new DummyHandler();
var handler2 = new DummyHandler();
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
}, handler1, handler2);
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() }, handler1,
handler2);
Assert.False(handler1.Called);
Assert.False(handler2.Called);
@@ -509,16 +472,14 @@ namespace k8s.Tests
{
events.Add(type);
eventsReceived.Signal();
}
);
});
// wait server yields all events
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout));
Assert.True(
eventsReceived.CurrentCount == 0,
"Timed out waiting for all events / errors to be received."
);
"Timed out waiting for all events / errors to be received.");
Assert.Contains(WatchEventType.Added, events);
@@ -548,10 +509,7 @@ namespace k8s.Tests
return false;
}))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var events = new HashSet<WatchEventType>();
var errors = 0;
@@ -560,31 +518,29 @@ namespace k8s.Tests
name: "myPod",
@namespace: "default",
onEvent:
(type, item) =>
{
testOutput.WriteLine($"Watcher received '{type}' event.");
(type, item) =>
{
testOutput.WriteLine($"Watcher received '{type}' event.");
events.Add(type);
eventsReceived.Signal();
},
events.Add(type);
eventsReceived.Signal();
},
onError:
error =>
{
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
error =>
{
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
errors += 1;
eventsReceived.Signal();
},
onClosed: connectionClosed.Set
);
errors += 1;
eventsReceived.Signal();
},
onClosed: connectionClosed.Set);
// wait server yields all events
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout));
Assert.True(
eventsReceived.CurrentCount == 0,
"Timed out waiting for all events / errors to be received."
);
"Timed out waiting for all events / errors to be received.");
Assert.Contains(WatchEventType.Added, events);
Assert.Contains(WatchEventType.Deleted, events);
@@ -605,7 +561,9 @@ namespace k8s.Tests
[Fact(Skip = "Integration Test")]
public async Task WatcherIntegrationTest()
{
var kubernetesConfig = KubernetesClientConfiguration.BuildConfigFromConfigFile(kubeconfigPath: @"C:\Users\frede\Source\Repos\cloud\minikube.config");
var kubernetesConfig =
KubernetesClientConfiguration.BuildConfigFromConfigFile(
kubeconfigPath: @"C:\Users\frede\Source\Repos\cloud\minikube.config");
var kubernetes = new Kubernetes(kubernetesConfig);
var job = await kubernetes.CreateNamespacedJobAsync(
@@ -613,13 +571,9 @@ namespace k8s.Tests
{
ApiVersion = "batch/v1",
Kind = V1Job.KubeKind,
Metadata = new V1ObjectMeta()
{
Name = nameof(WatcherIntegrationTest).ToLowerInvariant()
},
Metadata = new V1ObjectMeta() { Name = nameof(WatcherIntegrationTest).ToLowerInvariant() },
Spec = new V1JobSpec()
{
Template = new V1PodTemplateSpec()
{
Spec = new V1PodSpec()
@@ -630,12 +584,7 @@ namespace k8s.Tests
{
Image = "ubuntu/xenial",
Name = "runner",
Command = new List<string>()
{
"/bin/bash",
"-c",
"--"
},
Command = new List<string>() {"/bin/bash", "-c", "--" },
Args = new List<string>()
{
"trap : TERM INT; sleep infinity & wait"
@@ -645,7 +594,7 @@ namespace k8s.Tests
RestartPolicy = "Never"
},
}
}
},
},
"default");
@@ -699,10 +648,7 @@ namespace k8s.Tests
return false;
}))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var events = new HashSet<WatchEventType>();
var errors = 0;
@@ -711,30 +657,28 @@ namespace k8s.Tests
name: "myPod",
@namespace: "default",
onEvent:
(type, item) =>
{
testOutput.WriteLine($"Watcher received '{type}' event.");
(type, item) =>
{
testOutput.WriteLine($"Watcher received '{type}' event.");
events.Add(type);
eventsReceived.Signal();
},
events.Add(type);
eventsReceived.Signal();
},
onError:
error =>
{
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
error =>
{
testOutput.WriteLine($"Watcher received '{error.GetType().FullName}' error.");
errors += 1;
eventsReceived.Signal();
}
);
errors += 1;
eventsReceived.Signal();
});
// wait server yields all events
await Task.WhenAny(eventsReceived.WaitAsync(), Task.Delay(TestTimeout));
Assert.True(
eventsReceived.CurrentCount == 0,
"Timed out waiting for all events / errors to be received."
);
"Timed out waiting for all events / errors to be received.");
Assert.Contains(WatchEventType.Added, events);
Assert.Contains(WatchEventType.Deleted, events);
@@ -763,17 +707,15 @@ namespace k8s.Tests
return true;
}, resp: ""))
{
var client = new Kubernetes(new KubernetesClientConfiguration
{
Host = server.Uri.ToString()
});
var client = new Kubernetes(new KubernetesClientConfiguration { Host = server.Uri.ToString() });
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(2));
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () =>
{
await client.ListNamespacedPodWithHttpMessagesAsync("default", watch: true, cancellationToken: cts.Token);
await client.ListNamespacedPodWithHttpMessagesAsync("default", watch: true,
cancellationToken: cts.Token);
});
}
}

View File

@@ -13,7 +13,8 @@ namespace k8s.Tests
[Fact]
public void ReadError()
{
byte[] data = Encoding.UTF8.GetBytes("{\"type\":\"ERROR\",\"object\":{\"kind\":\"Status\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"too old resource version: 44982(53593)\",\"reason\":\"Gone\",\"code\":410}}");
byte[] data = Encoding.UTF8.GetBytes(
"{\"type\":\"ERROR\",\"object\":{\"kind\":\"Status\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"too old resource version: 44982(53593)\",\"reason\":\"Gone\",\"code\":410}}");
using (MemoryStream stream = new MemoryStream(data))
using (StreamReader reader = new StreamReader(stream))

View File

@@ -42,8 +42,7 @@ namespace k8s.Tests
// Useful to diagnose test timeouts.
TestCancellation.Register(
() => testOutput.WriteLine("Test-level cancellation token has been canceled.")
);
() => testOutput.WriteLine("Test-level cancellation token has been canceled."));
ServerBaseAddress = new Uri($"http://localhost:{port}");
WebSocketBaseAddress = new Uri($"ws://localhost:{port}");
@@ -96,7 +95,9 @@ namespace k8s.Tests
protected virtual void ConfigureTestServerServices(IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
// Inject WebSocketTestData.
services.AddSingleton(WebSocketTestAdapter);
@@ -111,7 +112,9 @@ namespace k8s.Tests
protected virtual void ConfigureTestServerLogging(ILoggingBuilder logging)
{
if (logging == null)
{
throw new ArgumentNullException(nameof(logging));
}
logging.ClearProviders(); // Don't log to console.
logging.AddTestOutput(this.testOutput, LogLevel.Information);
@@ -128,10 +131,7 @@ namespace k8s.Tests
/// </returns>
protected virtual Kubernetes CreateTestClient(ServiceClientCredentials credentials = null)
{
return new Kubernetes(credentials ?? AnonymousClientCredentials.Instance)
{
BaseUri = ServerBaseAddress
};
return new Kubernetes(credentials ?? AnonymousClientCredentials.Instance) { BaseUri = ServerBaseAddress };
}
/// <summary>
@@ -156,13 +156,19 @@ namespace k8s.Tests
/// <returns>
/// A <see cref="Task"/> representing the asynchronous operation.
/// </returns>
protected async Task Disconnect(WebSocket clientSocket, WebSocket serverSocket, WebSocketCloseStatus closeStatus = WebSocketCloseStatus.NormalClosure, string closeStatusDescription = "Normal Closure")
protected async Task Disconnect(WebSocket clientSocket, WebSocket serverSocket,
WebSocketCloseStatus closeStatus = WebSocketCloseStatus.NormalClosure,
string closeStatusDescription = "Normal Closure")
{
if (clientSocket == null)
{
throw new ArgumentNullException(nameof(clientSocket));
}
if (serverSocket == null)
{
throw new ArgumentNullException(nameof(serverSocket));
}
testOutput.WriteLine("Disconnecting...");
@@ -172,22 +178,28 @@ namespace k8s.Tests
.ContinueWith(async received =>
{
if (received.IsFaulted)
testOutput.WriteLine("Server socket operation to receive Close message failed: {0}", received.Exception.Flatten().InnerExceptions[0]);
{
testOutput.WriteLine("Server socket operation to receive Close message failed: {0}",
received.Exception.Flatten().InnerExceptions[0]);
}
else if (received.IsCanceled)
{
testOutput.WriteLine("Server socket operation to receive Close message was canceled.");
}
else
{
testOutput.WriteLine($"Received {received.Result.MessageType} message from server socket (expecting {WebSocketMessageType.Close}).");
testOutput.WriteLine(
$"Received {received.Result.MessageType} message from server socket (expecting {WebSocketMessageType.Close}).");
if (received.Result.MessageType == WebSocketMessageType.Close)
{
testOutput.WriteLine($"Closing server socket (with status {received.Result.CloseStatus})...");
testOutput.WriteLine(
$"Closing server socket (with status {received.Result.CloseStatus})...");
await serverSocket.CloseAsync(
received.Result.CloseStatus.Value,
received.Result.CloseStatusDescription,
TestCancellation
);
TestCancellation);
testOutput.WriteLine("Server socket closed.");
}
@@ -231,10 +243,14 @@ namespace k8s.Tests
protected async Task<int> SendMultiplexed(WebSocket webSocket, byte streamIndex, string text)
{
if (webSocket == null)
{
throw new ArgumentNullException(nameof(webSocket));
}
if (text == null)
{
throw new ArgumentNullException(nameof(text));
}
byte[] payload = Encoding.ASCII.GetBytes(text);
byte[] sendBuffer = new byte[payload.Length + 1];
@@ -244,8 +260,7 @@ namespace k8s.Tests
await webSocket.SendAsync(sendBuffer, WebSocketMessageType.Binary,
endOfMessage: true,
cancellationToken: TestCancellation
);
cancellationToken: TestCancellation);
return sendBuffer.Length;
}
@@ -262,10 +277,13 @@ namespace k8s.Tests
/// <returns>
/// A tuple containing the received text, 0-based substream index, and total bytes received.
/// </returns>
protected async Task<(string text, byte streamIndex, int totalBytes)> ReceiveTextMultiplexed(WebSocket webSocket)
protected async Task<(string text, byte streamIndex, int totalBytes)> ReceiveTextMultiplexed(
WebSocket webSocket)
{
if (webSocket == null)
{
throw new ArgumentNullException(nameof(webSocket));
}
byte[] receivedData;
using (MemoryStream buffer = new MemoryStream())
@@ -273,7 +291,10 @@ namespace k8s.Tests
byte[] receiveBuffer = new byte[1024];
WebSocketReceiveResult receiveResult = await webSocket.ReceiveAsync(receiveBuffer, TestCancellation);
if (receiveResult.MessageType != WebSocketMessageType.Binary)
throw new IOException($"Received unexpected WebSocket message of type '{receiveResult.MessageType}'.");
{
throw new IOException(
$"Received unexpected WebSocket message of type '{receiveResult.MessageType}'.");
}
buffer.Write(receiveBuffer, 0, receiveResult.Count);
@@ -291,8 +312,7 @@ namespace k8s.Tests
return (
text: Encoding.ASCII.GetString(receivedData, 1, receivedData.Length - 1),
streamIndex: receivedData[0],
totalBytes: receivedData.Length
);
totalBytes: receivedData.Length);
}
public void Dispose()

View File

@@ -111,15 +111,7 @@ metadata:
[Fact]
public void WriteToString()
{
var pod = new V1Pod()
{
ApiVersion = "v1",
Kind = "Pod",
Metadata = new V1ObjectMeta()
{
Name = "foo"
}
};
var pod = new V1Pod() { ApiVersion = "v1", Kind = "Pod", Metadata = new V1ObjectMeta() { Name = "foo" } };
var yaml = Yaml.SaveToString(pod);
Assert.True(ToLines(@"apiVersion: v1
@@ -135,11 +127,7 @@ metadata:
{
ApiVersion = "v1",
Kind = "Pod",
Metadata = new V1ObjectMeta()
{
Name = "foo",
NamespaceProperty = "bar"
}
Metadata = new V1ObjectMeta() { Name = "foo", NamespaceProperty = "bar" },
};
var yaml = Yaml.SaveToString(pod);
@@ -157,11 +145,7 @@ metadata:
{
ApiVersion = "v1",
Kind = "Pod",
Metadata = new V1ObjectMeta()
{
Name = "foo",
NamespaceProperty = "bar"
},
Metadata = new V1ObjectMeta() { Name = "foo", NamespaceProperty = "bar" },
Spec = new V1PodSpec()
{
Containers = new[]
@@ -173,20 +157,16 @@ metadata:
{
new V1VolumeMount
{
Name = "vm1",
MountPath = "/vm1",
ReadOnlyProperty = true
Name = "vm1", MountPath = "/vm1", ReadOnlyProperty = true
},
new V1VolumeMount
{
Name = "vm2",
MountPath = "/vm2",
ReadOnlyProperty = false
Name = "vm2", MountPath = "/vm2", ReadOnlyProperty = false
},
}
}
}
}
},
};
var yaml = Yaml.SaveToString(pod);
@@ -218,6 +198,7 @@ spec:
{
yield break;
}
yield return line;
}
}
@@ -295,10 +276,7 @@ spec:
- port: 3000
targetPort: 3000";
Dictionary<string, string> labels = new Dictionary<string, string>
{
{"app", "test"}
};
Dictionary<string, string> labels = new Dictionary<string, string> { { "app", "test" } };
var obj = new V1Service
{
Kind = "Service",
@@ -306,15 +284,8 @@ spec:
ApiVersion = "v1",
Spec = new V1ServiceSpec
{
Ports = new List<V1ServicePort>
{
new V1ServicePort
{
Port = 3000,
TargetPort = 3000
}
}
}
Ports = new List<V1ServicePort> { new V1ServicePort { Port = 3000, TargetPort = 3000 } }
},
};
var output = Yaml.SaveToString<V1Service>(obj);