stylecop fix followup, enforce SA1503 (#432)
* enforce SA1503 * fix spacing * fix SA1413 * fix spacing * fix SA1013
This commit is contained in:
@@ -8,4 +8,8 @@
|
||||
<PrivateAssets>All</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="$(MSBuildThisFileDirectory)\stylecop.json" Visible="false" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -28,6 +28,7 @@ namespace httpClientFactory
|
||||
{
|
||||
_logger.LogInformation(item.Metadata.Name);
|
||||
}
|
||||
|
||||
if (list.Items.Count == 0)
|
||||
{
|
||||
_logger.LogInformation("Empty!");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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("=-=-=-=-=-=-=-=-=-=-=");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace simple
|
||||
{
|
||||
Console.WriteLine(item.Metadata.Name);
|
||||
}
|
||||
|
||||
if (list.Items.Count == 0)
|
||||
{
|
||||
Console.WriteLine("Empty!");
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)))
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,5 @@ namespace k8s
|
||||
/// Validate the object.
|
||||
/// </summary>
|
||||
void Validate();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,5 @@ namespace k8s.KubeConfigModels
|
||||
/// </summary>
|
||||
[YamlMember(Alias = "config")]
|
||||
public Dictionary<string, string> Config { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>())
|
||||
|
||||
@@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -17,6 +17,6 @@ namespace k8s
|
||||
/// <summary>
|
||||
/// This <see cref="StreamDemuxer"/> object is used in port forwarding.
|
||||
/// </summary>
|
||||
PortForward
|
||||
PortForward,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace k8s.Models
|
||||
[JsonConverter(typeof(V1PathJsonConverter))]
|
||||
public partial class V1Patch
|
||||
{
|
||||
|
||||
public enum PathType
|
||||
{
|
||||
JsonPatch,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user