diff --git a/src/KubernetesClient/KubeConfigModels/ExternalExecution.cs b/src/KubernetesClient/KubeConfigModels/ExternalExecution.cs index ffd1fa5..811192b 100644 --- a/src/KubernetesClient/KubeConfigModels/ExternalExecution.cs +++ b/src/KubernetesClient/KubeConfigModels/ExternalExecution.cs @@ -17,7 +17,7 @@ namespace k8s.KubeConfigModels /// Environment variables to set when executing the plugin. Optional. /// [YamlMember(Alias = "env")] - public IDictionary EnvironmentVariables { get; set; } + public IList> EnvironmentVariables { get; set; } /// /// Arguments to pass when executing the plugin. Optional. diff --git a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs index dca200e..e4e95e1 100644 --- a/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs +++ b/src/KubernetesClient/KubernetesClientConfiguration.ConfigFile.cs @@ -407,16 +407,7 @@ namespace k8s throw new KubeConfigException("Refresh not supported."); } - /// - /// Implementation of the proposal for out-of-tree client - /// authentication providers as described here -- - /// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/auth/kubectl-exec-plugins.md - /// Took inspiration from python exec_provider.py -- - /// https://github.com/kubernetes-client/python-base/blob/master/config/exec_provider.py - /// - /// The external command execution configuration - /// The token received from the external command execution - public static string ExecuteExternalCommand(ExternalExecution config) + public static Process CreateRunnableExternalProcess(ExternalExecution config) { var execInfo = new Dictionary { @@ -430,10 +421,19 @@ namespace k8s process.StartInfo.EnvironmentVariables.Add("KUBERNETES_EXEC_INFO", JsonConvert.SerializeObject(execInfo)); if (config.EnvironmentVariables != null) { - foreach (var configEnvironmentVariableKey in config.EnvironmentVariables.Keys) + foreach (var configEnvironmentVariable in config.EnvironmentVariables) { - process.StartInfo.EnvironmentVariables.Add(key: configEnvironmentVariableKey, - value: config.EnvironmentVariables[configEnvironmentVariableKey]); + if (configEnvironmentVariable.ContainsKey("name") && configEnvironmentVariable.ContainsKey("value")) + { + process.StartInfo.EnvironmentVariables.Add( + configEnvironmentVariable["name"], + configEnvironmentVariable["value"]); + } + else + { + var badVariable = string.Join(",", configEnvironmentVariable.Select(x => $"{x.Key}={x.Value}")); + throw new KubeConfigException($"Invalid environment variable defined: {badVariable}"); + } } } @@ -447,6 +447,22 @@ namespace k8s process.StartInfo.RedirectStandardError = true; process.StartInfo.UseShellExecute = false; + return process; + } + + /// + /// Implementation of the proposal for out-of-tree client + /// authentication providers as described here -- + /// https://github.com/kubernetes/community/blob/master/contributors/design-proposals/auth/kubectl-exec-plugins.md + /// Took inspiration from python exec_provider.py -- + /// https://github.com/kubernetes-client/python-base/blob/master/config/exec_provider.py + /// + /// The external command execution configuration + /// The token received from the external command execution + public static string ExecuteExternalCommand(ExternalExecution config) + { + var process = CreateRunnableExternalProcess(config); + try { process.Start(); diff --git a/tests/KubernetesClient.Tests/ExternalExecutionTests.cs b/tests/KubernetesClient.Tests/ExternalExecutionTests.cs new file mode 100644 index 0000000..c33ebf8 --- /dev/null +++ b/tests/KubernetesClient.Tests/ExternalExecutionTests.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using k8s.KubeConfigModels; +using Xunit; + +namespace k8s.Tests +{ + public class ExternalExecutionTests + { + [Fact] + public void CreateRunnableExternalProcess() + { + var actual = KubernetesClientConfiguration.CreateRunnableExternalProcess(new ExternalExecution + { + ApiVersion = "testingversion", + Command = "command", + Arguments = new List { "arg1", "arg2" }, + EnvironmentVariables = new List> + { new Dictionary { { "name", "testkey" }, { "value", "testvalue" } } } + }); + + var actualExecInfo = JsonConvert.DeserializeObject>(actual.StartInfo.EnvironmentVariables["KUBERNETES_EXEC_INFO"]); + Assert.Equal("testingversion", actualExecInfo["apiVersion"]); + Assert.Equal("ExecCredentials", actualExecInfo["kind"]); + + Assert.Equal("command", actual.StartInfo.FileName); + Assert.Equal("arg1 arg2", actual.StartInfo.Arguments); + Assert.Equal("testvalue", actual.StartInfo.EnvironmentVariables["testkey"]); + } + } +}