GUACAMOLE-1174: Merge support for Kubernetes "exec" API call.
diff --git a/src/protocols/kubernetes/kubernetes.c b/src/protocols/kubernetes/kubernetes.c
index 1998d13..a0ed7ea 100644
--- a/src/protocols/kubernetes/kubernetes.c
+++ b/src/protocols/kubernetes/kubernetes.c
@@ -213,10 +213,11 @@
}
/* Generate endpoint for attachment URL */
- if (guac_kubernetes_endpoint_attach(endpoint_path, sizeof(endpoint_path),
+ if (guac_kubernetes_endpoint_uri(endpoint_path, sizeof(endpoint_path),
settings->kubernetes_namespace,
settings->kubernetes_pod,
- settings->kubernetes_container)) {
+ settings->kubernetes_container,
+ settings->exec_command)) {
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
"Unable to generate path for Kubernetes API endpoint: "
"Resulting path too long");
diff --git a/src/protocols/kubernetes/settings.c b/src/protocols/kubernetes/settings.c
index 3421b1e..1d437bc 100644
--- a/src/protocols/kubernetes/settings.c
+++ b/src/protocols/kubernetes/settings.c
@@ -31,6 +31,7 @@
"namespace",
"pod",
"container",
+ "exec-command",
"use-ssl",
"client-cert",
"client-key",
@@ -87,6 +88,11 @@
IDX_CONTAINER,
/**
+ * The command used by exec call. If omitted, attach call will be used.
+ */
+ IDX_EXEC_COMMAND,
+
+ /**
* Whether SSL/TLS should be used. If omitted, SSL/TLS will not be used.
*/
IDX_USE_SSL,
@@ -275,6 +281,11 @@
guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_CONTAINER, NULL);
+ /* Read exec command (optional) */
+ settings->exec_command =
+ guac_user_parse_args_string(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
+ IDX_EXEC_COMMAND, NULL);
+
/* Parse whether SSL should be used */
settings->use_ssl =
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
@@ -406,6 +417,9 @@
free(settings->kubernetes_pod);
free(settings->kubernetes_container);
+ /* Free Kubernetes exec command */
+ free(settings->exec_command);
+
/* Free SSL/TLS details */
free(settings->client_cert);
free(settings->client_key);
diff --git a/src/protocols/kubernetes/settings.h b/src/protocols/kubernetes/settings.h
index eef4973..1ad5805 100644
--- a/src/protocols/kubernetes/settings.h
+++ b/src/protocols/kubernetes/settings.h
@@ -98,6 +98,12 @@
char* kubernetes_container;
/**
+ * The command to generate api endpoint for call exec.
+ * If omitted call attach will be used.
+ */
+ char* exec_command;
+
+ /**
* Whether SSL/TLS should be used.
*/
bool use_ssl;
diff --git a/src/protocols/kubernetes/url.c b/src/protocols/kubernetes/url.c
index 78c116e..4bca015 100644
--- a/src/protocols/kubernetes/url.c
+++ b/src/protocols/kubernetes/url.c
@@ -89,15 +89,56 @@
}
-int guac_kubernetes_endpoint_attach(char* buffer, int length,
- const char* kubernetes_namespace, const char* kubernetes_pod,
- const char* kubernetes_container) {
+int guac_kubernetes_append_endpoint_param(char* buffer, int length,
+ const char* param_name, const char* param_value) {
+ char escaped_param_value[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
+
+ /* Escape value */
+ if (guac_kubernetes_escape_url_component(escaped_param_value,
+ sizeof(escaped_param_value), param_value))
+ return 1;
+
+ char* str = buffer;
+
+ int str_len = 0;
+ int qmark = 0;
+
+ while (*str != '\0') {
+
+ /* Look for a question mark */
+ if (*str=='?') qmark = 1;
+
+ /* Compute the buffer string length */
+ str_len++;
+
+ /* Verify the buffer null terminated */
+ if (str_len >= length) return 1;
+
+ /* Next character */
+ str++;
+ }
+
+ /* Determine the parameter delimiter */
+ char delimiter = '?';
+ if (qmark) delimiter = '&';
+
+ /* Write the parameter to the buffer */
int written;
+ written = snprintf(buffer + str_len, length - str_len,
+ "%c%s=%s", delimiter, param_name, escaped_param_value);
+
+ /* The parameter was successfully added if it was written to the given
+ * buffer without truncation */
+ return (written < 0 || written >= length);
+}
+
+int guac_kubernetes_endpoint_uri(char* buffer, int length,
+ const char* kubernetes_namespace, const char* kubernetes_pod,
+ const char* kubernetes_container, const char* exec_command) {
char escaped_namespace[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
char escaped_pod[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
- char escaped_container[GUAC_KUBERNETES_MAX_ENDPOINT_LENGTH];
/* Escape Kubernetes namespace */
if (guac_kubernetes_escape_url_component(escaped_namespace,
@@ -109,29 +150,38 @@
sizeof(escaped_pod), kubernetes_pod))
return 1;
- /* Generate attachment endpoint URL */
- if (kubernetes_container != NULL) {
+ /* Determine the call type */
+ char* call = "attach";
+ if (exec_command != NULL)
+ call = "exec";
- /* Escape container name */
- if (guac_kubernetes_escape_url_component(escaped_container,
- sizeof(escaped_container), kubernetes_container))
- return 1;
+ int written;
- written = snprintf(buffer, length,
- "/api/v1/namespaces/%s/pods/%s/attach"
- "?container=%s&stdin=true&stdout=true&tty=true",
- escaped_namespace, escaped_pod, escaped_container);
- }
- else {
- written = snprintf(buffer, length,
- "/api/v1/namespaces/%s/pods/%s/attach"
- "?stdin=true&stdout=true&tty=true",
- escaped_namespace, escaped_pod);
- }
+ /* Generate the endpoint path and write to the buffer */
+ written = snprintf(buffer, length,
+ "/api/v1/namespaces/%s/pods/%s/%s", escaped_namespace, escaped_pod, call);
- /* Endpoint URL was successfully generated if it was written to the given
+ /* Operation successful if the endpoint path was written to the given
* buffer without truncation */
- return !(written < length - 1);
+ if (written < 0 || written >= length)
+ return 1;
+ /* Append exec command parameter */
+ if (exec_command != NULL) {
+ if (guac_kubernetes_append_endpoint_param(buffer,
+ length, "command", exec_command))
+ return 1;
+ }
+
+ /* Append kubernetes container parameter */
+ if (kubernetes_container != NULL) {
+ if (guac_kubernetes_append_endpoint_param(buffer,
+ length, "container", kubernetes_container))
+ return 1;
+ }
+
+ /* Append stdin, stdout and tty parameters */
+ return (guac_kubernetes_append_endpoint_param(buffer, length, "stdin", "true"))
+ || (guac_kubernetes_append_endpoint_param(buffer, length, "stdout", "true"))
+ || (guac_kubernetes_append_endpoint_param(buffer, length, "tty", "true"));
}
-
diff --git a/src/protocols/kubernetes/url.h b/src/protocols/kubernetes/url.h
index 285baa2..96e5098 100644
--- a/src/protocols/kubernetes/url.h
+++ b/src/protocols/kubernetes/url.h
@@ -50,6 +50,31 @@
const char* str);
/**
+ * Append the parameter to the endpoint path.
+ * Value within the path will be URL-escaped as necessary.
+ *
+ * @param buffer
+ * The buffer which should receive the parameter. It could contain the endpoint path.
+ * The parameter will be written to the end of the buffer.
+ *
+ * @param length
+ * The number of bytes available in the given buffer.
+ *
+ * @param param_name
+ * The name of the parameter.
+ *
+ * @param param_value
+ * The value of the parameter.
+ *
+ * @return
+ * Zero if the parameter was successfully attached to the buffer,
+ * non-zero if insufficient space exists within the buffer or
+ * buffer not null terminated.
+ */
+int guac_kubernetes_append_endpoint_param(char* buffer, int length,
+ const char* param_name, const char* param_value);
+
+/**
* Generates the full path to the Kubernetes API endpoint which handles
* attaching to running containers within specific pods. Values within the path
* will be URL-escaped as necessary.
@@ -72,14 +97,18 @@
* @param kubernetes_container
* The name of the container to attach to, or NULL to arbitrarily attach
* to the first container in the pod.
+ *
+ * @param exec_command
+ * The command used to run a new process and attach to it,
+ * instead of the main container process.
*
* @return
* Zero if the endpoint path was successfully written to the provided
* buffer, non-zero if insufficient space exists within the buffer.
*/
-int guac_kubernetes_endpoint_attach(char* buffer, int length,
+int guac_kubernetes_endpoint_uri(char* buffer, int length,
const char* kubernetes_namespace, const char* kubernetes_pod,
- const char* kubernetes_container);
+ const char* kubernetes_container, const char* exec_command);
#endif