WebSocketNamespacedPodExecAsync: Support specifying command arguments (#123)

* WebSocketNamespacedPodExecAsync: Support specifying command argumets

* Address PR feedback

* Address PR feedback

* Fix unstable test
This commit is contained in:
Frederik Carlier
2018-03-31 07:56:52 +02:00
committed by Brendan Burns
parent cf1c9950a0
commit d20e2597b9
4 changed files with 81 additions and 12 deletions

View File

@@ -52,7 +52,54 @@ 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 = "/bin/bash", string container = null, bool stderr = true, bool stdin = true, bool stdout = true, bool tty = true, 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, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Executes a command in a pod.
/// </summary>
/// <param name='name'>
/// name of the Pod
/// </param>
/// <param name='namespace'>
/// object name and auth scope, such as for teams and projects
/// </param>
/// <param name='command'>
/// Command is the remote command to execute. argv array. Not executed within a
/// shell.
/// </param>
/// <param name='container'>
/// Container in which to execute the command. Defaults to only container if
/// there is only one container in the pod.
/// </param>
/// <param name='stderr'>
/// Redirect the standard error stream of the pod for this call. Defaults to
/// <see langword="true"/>.
/// </param>
/// <param name='stdin'>
/// Redirect the standard input stream of the pod for this call. Defaults to
/// <see langword="true"/>.
/// </param>
/// <param name='stdout'>
/// Redirect the standard output stream of the pod for this call. Defaults to
/// <see langword="true"/>.
/// </param>
/// <param name='tty'>
/// TTY if true indicates that a tty will be allocated for the exec call.
/// Defaults to <see langword="true"/>.
/// </param>
/// <param name='customHeaders'>
/// Headers that will be added to request.
/// </param>
/// <param name='cancellationToken'>
/// The cancellation token.
/// </param>
/// <exception cref="ArgumentNullException">
/// Thrown when a required parameter is null
/// </exception>
/// <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, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Start port forwarding one or more ports of a pod.

View File

@@ -20,7 +20,13 @@ namespace k8s
public Func<WebSocketBuilder> CreateWebSocketBuilder { get; set; } = () => new WebSocketBuilder();
/// <inheritdoc/>
public Task<WebSocket> WebSocketNamespacedPodExecAsync(string name, string @namespace = "default", string command = "/bin/sh", string container = null, bool stderr = true, bool stdin = true, bool stdout = true, bool tty = true, 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, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
{
return WebSocketNamespacedPodExecAsync(name, @namespace, new string[] { command }, container, stderr, stdin, stdout, tty, customHeaders, cancellationToken);
}
/// <inheritdoc/>
public 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, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
{
if (name == null)
{
@@ -37,6 +43,11 @@ namespace k8s
throw new ArgumentNullException(nameof(command));
}
if (!command.Any())
{
throw new ArgumentOutOfRangeException(nameof(command));
}
// Tracing
bool _shouldTrace = ServiceClientTracing.IsEnabled;
string _invocationId = null;
@@ -67,17 +78,28 @@ namespace k8s
uriBuilder.Path += $"api/v1/namespaces/{@namespace}/pods/{name}/exec";
var query = string.Empty;
uriBuilder.Query = QueryHelpers.AddQueryString(string.Empty, new Dictionary<string, string>
foreach (var c in command)
{
query = QueryHelpers.AddQueryString(query, "command", c);
}
if (container != null)
{
query = QueryHelpers.AddQueryString(query, "container", Uri.EscapeDataString(container));
}
query = QueryHelpers.AddQueryString(query, new Dictionary<string, string>
{
{ "command", command},
{ "container", container},
{"stderr", stderr ? "1" : "0"},
{"stdin", stdin ? "1" : "0"},
{"stdout", stdout ? "1" : "0"},
{"tty", tty ? "1" : "0"}
}).TrimStart('?');
uriBuilder.Query = query;
return this.StreamConnectAsync(uriBuilder.Uri, _invocationId, customHeaders, cancellationToken);
}

View File

@@ -54,7 +54,7 @@ namespace k8s.Tests
WebSocket clientSocket = await client.WebSocketNamespacedPodExecAsync(
name: "mypod",
@namespace: "mynamespace",
command: "/bin/bash",
command: new string[] { "/bin/bash" },
container: "mycontainer",
stderr: false,
stdin: false,

View File

@@ -39,7 +39,7 @@ namespace k8s.tests
var webSocket = await client.WebSocketNamespacedPodExecAsync(
name: "mypod",
@namespace: "mynamespace",
command: "/bin/bash",
command: new string[] { "/bin/bash", "-c", $"echo Hello, World\nexit 0\n" },
container: "mycontainer",
stderr: true,
stdin: true,
@@ -58,7 +58,7 @@ namespace k8s.tests
};
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/exec?command=%2Fbin%2Fbash&container=mycontainer&stderr=1&stdin=1&stdout=1&tty=1"), mockWebSocketBuilder.Uri); // Did we connect to the correct URL?
Assert.Equal(new Uri("ws://localhost/api/v1/namespaces/mynamespace/pods/mypod/exec?command=%2Fbin%2Fbash&command=-c&command=echo%20Hello,%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
}