chunk websocket frame when buff > 15M (#981)
* ws is now chunked * remove chunk in example * fix build
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
@@ -9,6 +9,7 @@
|
||||
<ItemGroup>
|
||||
|
||||
<PackageReference Include="JsonPatch.Net" Version="2.0.3" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.0" />
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
@@ -14,6 +14,9 @@ using k8s.Models;
|
||||
using k8s.Autorest;
|
||||
using Nito.AsyncEx;
|
||||
using Xunit;
|
||||
using ICSharpCode.SharpZipLib.Tar;
|
||||
using System.Text;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace k8s.E2E
|
||||
{
|
||||
@@ -398,7 +401,7 @@ namespace k8s.E2E
|
||||
async Task<V1Pod> Pod()
|
||||
{
|
||||
var pods = client.CoreV1.ListNamespacedPod(namespaceParameter);
|
||||
var pod = pods.Items.First();
|
||||
var pod = pods.Items.First(p => p.Metadata.Name == podName);
|
||||
while (pod.Status.Phase != "Running")
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
||||
@@ -548,6 +551,181 @@ namespace k8s.E2E
|
||||
}
|
||||
|
||||
|
||||
[MinikubeFact]
|
||||
public async Task CopyToPodTestAsync()
|
||||
{
|
||||
var namespaceParameter = "default";
|
||||
var podName = "k8scsharp-e2e-cp-pod";
|
||||
|
||||
var client = CreateClient();
|
||||
|
||||
async Task<int> CopyFileToPodAsync(string name, string @namespace, string container, Stream inputFileStream, string destinationFilePath, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
// The callback which processes the standard input, standard output and standard error of exec method
|
||||
var handler = new ExecAsyncCallback(async (stdIn, stdOut, stdError) =>
|
||||
{
|
||||
var fileInfo = new FileInfo(destinationFilePath);
|
||||
try
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
using (var tarOutputStream = new TarOutputStream(memoryStream, Encoding.Default))
|
||||
{
|
||||
tarOutputStream.IsStreamOwner = false;
|
||||
|
||||
var fileSize = inputFileStream.Length;
|
||||
var entry = TarEntry.CreateTarEntry(fileInfo.Name);
|
||||
|
||||
entry.Size = fileSize;
|
||||
|
||||
tarOutputStream.PutNextEntry(entry);
|
||||
await inputFileStream.CopyToAsync(tarOutputStream).ConfigureAwait(false);
|
||||
tarOutputStream.CloseEntry();
|
||||
}
|
||||
|
||||
memoryStream.Position = 0;
|
||||
|
||||
await memoryStream.CopyToAsync(stdIn).ConfigureAwait(false);
|
||||
await memoryStream.FlushAsync().ConfigureAwait(false);
|
||||
stdIn.Close();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new IOException($"Copy command failed: {ex.Message}");
|
||||
}
|
||||
|
||||
using StreamReader streamReader = new StreamReader(stdError);
|
||||
while (streamReader.EndOfStream == false)
|
||||
{
|
||||
string error = await streamReader.ReadToEndAsync().ConfigureAwait(false);
|
||||
throw new IOException($"Copy command failed: {error}");
|
||||
}
|
||||
});
|
||||
|
||||
string destinationFolder = Path.GetDirectoryName(destinationFilePath).Replace("\\", "/");
|
||||
|
||||
return await client.NamespacedPodExecAsync(
|
||||
name,
|
||||
@namespace,
|
||||
container,
|
||||
new string[] { "tar", "-xmf", "-", "-C", destinationFolder },
|
||||
false,
|
||||
handler,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
var pods = client.CoreV1.ListNamespacedPod(namespaceParameter);
|
||||
while (pods.Items.Any(p => p.Metadata.Name == podName))
|
||||
{
|
||||
try
|
||||
{
|
||||
client.CoreV1.DeleteNamespacedPod(podName, namespaceParameter);
|
||||
}
|
||||
catch (HttpOperationException e)
|
||||
{
|
||||
if (e.Response.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Cleanup();
|
||||
|
||||
client.CoreV1.CreateNamespacedPod(
|
||||
new V1Pod()
|
||||
{
|
||||
Metadata = new V1ObjectMeta { Name = podName, },
|
||||
Spec = new V1PodSpec
|
||||
{
|
||||
Containers = new[]
|
||||
{
|
||||
new V1Container()
|
||||
{
|
||||
Name = "container",
|
||||
Image = "ubuntu",
|
||||
// Image = "busybox", // TODO not work with busybox
|
||||
Command = new[] { "sleep" },
|
||||
Args = new[] { "infinity" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
namespaceParameter);
|
||||
|
||||
var lines = new List<string>();
|
||||
var started = new ManualResetEvent(false);
|
||||
|
||||
async Task<V1Pod> Pod()
|
||||
{
|
||||
var pods = client.CoreV1.ListNamespacedPod(namespaceParameter);
|
||||
var pod = pods.Items.First(p => p.Metadata.Name == podName);
|
||||
while (pod.Status.Phase != "Running")
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
|
||||
return await Pod().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return pod;
|
||||
}
|
||||
|
||||
var pod = await Pod().ConfigureAwait(false);
|
||||
|
||||
|
||||
async Task AssertMd5sumAsync(string file, byte[] orig)
|
||||
{
|
||||
var ws = await client.WebSocketNamespacedPodExecAsync(
|
||||
pod.Metadata.Name,
|
||||
pod.Metadata.NamespaceProperty,
|
||||
new string[] { "md5sum", file },
|
||||
"container").ConfigureAwait(false);
|
||||
|
||||
var demux = new StreamDemuxer(ws);
|
||||
demux.Start();
|
||||
|
||||
var buff = new byte[4096];
|
||||
var stream = demux.GetStream(1, 1);
|
||||
var read = stream.Read(buff, 0, 4096);
|
||||
var remotemd5 = Encoding.Default.GetString(buff);
|
||||
remotemd5 = remotemd5.Substring(0, 32);
|
||||
|
||||
var md5 = MD5.Create().ComputeHash(orig);
|
||||
var localmd5 = BitConverter.ToString(md5).Replace("-", string.Empty).ToLower();
|
||||
|
||||
Assert.Equal(localmd5, remotemd5);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
{
|
||||
// small
|
||||
var content = new byte[1 * 1024 * 1024];
|
||||
new Random().NextBytes(content);
|
||||
await CopyFileToPodAsync(pod.Metadata.Name, pod.Metadata.NamespaceProperty, "container", new MemoryStream(content), "/tmp/test").ConfigureAwait(false);
|
||||
await AssertMd5sumAsync("/tmp/test", content).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
{
|
||||
// big
|
||||
var content = new byte[40 * 1024 * 1024];
|
||||
new Random().NextBytes(content);
|
||||
await CopyFileToPodAsync(pod.Metadata.Name, pod.Metadata.NamespaceProperty, "container", new MemoryStream(content), "/tmp/test").ConfigureAwait(false);
|
||||
await AssertMd5sumAsync("/tmp/test", content).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
public static IKubernetes CreateClient()
|
||||
{
|
||||
return new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig());
|
||||
|
||||
Reference in New Issue
Block a user