From 0e1b6b7b7d786d12d4cb5106594841801ef39fce Mon Sep 17 00:00:00 2001 From: Jason Poon Date: Wed, 9 Aug 2017 16:49:45 -0700 Subject: [PATCH] Update docs. Better error message when openssl fails. Simplifying `KubernetesClientConfiguration` --- README.md | 31 ++++-- src/Kubernetes.Auth.cs | 4 - src/KubernetesClientConfiguration.cs | 106 ++++++++------------ src/Utils.cs | 40 +++++--- tests/KubernetesClientConfigurationTests.cs | 4 +- 5 files changed, 95 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index e14831570..5e229f1d0 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,58 @@ # csharp Work In Progress -Currently only supported on Linux [![Travis](https://img.shields.io/travis/kubernetes-client/csharp.svg)](https://travis-ci.org/kubernetes-client/csharp) -# Generating the client code +# Generating the Client Code ## Prerequisites Check out the generator project into some other directory (henceforth `$GEN_DIR`) -``` +```bash cd $GEN_DIR/.. git clone https://github.com/kubernetes-client/gen ``` Install the [`autorest` tool](https://github.com/azure/autorest): -``` +```bash npm install autorest ``` ## Generating code -``` +```bash # Where REPO_DIR points to the root of the csharp repository cd ${REPO_DIR}/csharp/src ${GEN_DIR}/openapi/csharp.sh generated csharp.settings ``` -# Testing +# Usage + +## Prerequisities + +* [OpenSSL](https://www.openssl.org/) +* For Linux/Mac: + * LibCurl built with OpenSSL (Mac: `brew install curl --with-nghttp2`) + +## Running the Examples + +```bash +git clone git@github.com:kubernetes-client/csharp.git +cd csharp\examples\simple +dotnet run +``` + +## Testing The project uses [XUnit](https://xunit.github.io) as unit testing framework. -To run the tests you need to: +To run the tests ```bash -cd tests +cd csharp\tests dotnet restore dotnet xunit ``` \ No newline at end of file diff --git a/src/Kubernetes.Auth.cs b/src/Kubernetes.Auth.cs index 7ea4fcdd3..02767a4e2 100644 --- a/src/Kubernetes.Auth.cs +++ b/src/Kubernetes.Auth.cs @@ -71,10 +71,6 @@ private async Task SetCredentialsAsync(KubernetesClientConfiguration config, Htt !string.IsNullOrWhiteSpace(config.ClientKey))) { var pfxFilePath = await Utils.GeneratePfxAsync(config).ConfigureAwait(false); - if (string.IsNullOrWhiteSpace(pfxFilePath)) - { - throw new KubernetesClientException("Failed to generate pfx file"); - } var cert = new X509Certificate2(pfxFilePath, string.Empty, X509KeyStorageFlags.PersistKeySet); handler.ClientCertificates.Add(cert); diff --git a/src/KubernetesClientConfiguration.cs b/src/KubernetesClientConfiguration.cs index 71fcb42ac..916504e1a 100644 --- a/src/KubernetesClientConfiguration.cs +++ b/src/KubernetesClientConfiguration.cs @@ -16,17 +16,12 @@ public class KubernetesClientConfiguration { /// /// Initializes a new instance of the class. - /// Initializes a new instance of the ClientConfiguration class /// /// kubeconfig file info /// Context to use from kube config public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentContext = null) { - if (kubeconfig == null) - { - kubeconfig = new FileInfo(KubeConfigDefaultLocation); - } - var k8SConfig = this.LoadKubeConfig(kubeconfig); + var k8SConfig = this.LoadKubeConfig(kubeconfig ?? new FileInfo(KubeConfigDefaultLocation)); this.Initialize(k8SConfig, currentContext); } @@ -108,86 +103,73 @@ public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentC /// Current Context private void Initialize(K8SConfiguration k8SConfig, string currentContext = null) { - Context activeContext; - if (k8SConfig.Contexts == null) { throw new KubeConfigException("No contexts found in kubeconfig"); - } + } - // set the currentCOntext to passed context if not null - if (!string.IsNullOrWhiteSpace(currentContext)) + if (k8SConfig.Clusters == null) { - - activeContext = k8SConfig.Contexts.FirstOrDefault(c => c.Name.Equals(currentContext, StringComparison.OrdinalIgnoreCase)); - if (activeContext != null) - { - this.CurrentContext = activeContext.Name; - } - else - { - throw new KubeConfigException($"CurrentContext: {0} not found in contexts in kubeconfig"); - } + throw new KubeConfigException($"No clusters found in kubeconfig"); } - // otherwise set current context from kubeconfig - else + + if (k8SConfig.Users == null) { - activeContext = k8SConfig.Contexts.FirstOrDefault(c => c.Name.Equals(k8SConfig.CurrentContext, StringComparison.OrdinalIgnoreCase)); - - if (activeContext == null) - { - throw new KubeConfigException($"CurrentContext: {currentContext} not found in contexts in kubeconfig"); - } + throw new KubeConfigException($"No users found in kubeconfig"); + } - this.CurrentContext = activeContext.Name; + // current context + currentContext = currentContext ?? k8SConfig.CurrentContext; + Context activeContext = k8SConfig.Contexts.FirstOrDefault(c => c.Name.Equals(currentContext, StringComparison.OrdinalIgnoreCase)); + if (activeContext == null) + { + throw new KubeConfigException($"CurrentContext: {currentContext} not found in contexts in kubeconfig"); } - if (k8SConfig.Clusters == null) + this.CurrentContext = activeContext.Name; + + // cluster + var clusterDetails = k8SConfig.Clusters.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.Cluster, StringComparison.OrdinalIgnoreCase)); + if (clusterDetails?.ClusterEndpoint == null) { - throw new KubeConfigException($"clusters not found for current-context :{activeContext} in kubeconfig"); + throw new KubeConfigException($"Cluster not found for context {activeContext} in kubeconfig"); } - var clusterDetails = k8SConfig.Clusters.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.Cluster, StringComparison.OrdinalIgnoreCase)); - if (clusterDetails?.ClusterEndpoint != null) + if (string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.Server)) { - if (string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.Server)) - { - throw new KubeConfigException($"server not found for current-context :{activeContext} in kubeconfig"); - } + throw new KubeConfigException($"Server not found for current-context {activeContext} in kubeconfig"); + } - if (!clusterDetails.ClusterEndpoint.SkipTlsVerify && - string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthorityData) && - string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthority)) - { - throw new KubeConfigException($"neither certificate-authority-data nor certificate-authority not found for current-context :{activeContext} in kubeconfig"); - } + if (!clusterDetails.ClusterEndpoint.SkipTlsVerify && + string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthorityData) && + string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthority)) + { + throw new KubeConfigException($"neither certificate-authority-data nor certificate-authority not found for current-context :{activeContext} in kubeconfig"); + } - this.Host = clusterDetails.ClusterEndpoint.Server; - if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData)) { - string data = clusterDetails.ClusterEndpoint.CertificateAuthorityData; - this.SslCaCert = new X509Certificate2(System.Text.Encoding.UTF8.GetBytes(Utils.Base64Decode(data))); - } - else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority)) { - this.SslCaCert = new X509Certificate2(clusterDetails.ClusterEndpoint.CertificateAuthority, null); - } - this.SkipTlsVerify = clusterDetails.ClusterEndpoint.SkipTlsVerify; + this.Host = clusterDetails.ClusterEndpoint.Server; + if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData)) + { + string data = clusterDetails.ClusterEndpoint.CertificateAuthorityData; + this.SslCaCert = new X509Certificate2(System.Text.Encoding.UTF8.GetBytes(Utils.Base64Decode(data))); } - else + else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority)) { - throw new KubeConfigException($"Cluster details not found for current-context: {activeContext} in kubeconfig"); + this.SslCaCert = new X509Certificate2(clusterDetails.ClusterEndpoint.CertificateAuthority, null); } + this.SkipTlsVerify = clusterDetails.ClusterEndpoint.SkipTlsVerify; - // set user details from kubeconfig - var userDetails = k8SConfig.Users.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.User, StringComparison.OrdinalIgnoreCase)); - - this.SetUserDetails(userDetails); + // user + this.SetUserDetails(k8SConfig, activeContext); } - private void SetUserDetails(User userDetails) + private void SetUserDetails(K8SConfiguration k8SConfig, Context activeContext) { + var userDetails = k8SConfig.Users.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.User, StringComparison.OrdinalIgnoreCase)); + if (userDetails == null) { - throw new KubeConfigException("User not found for the current context in kubeconfig"); + throw new KubeConfigException("User not found for context {activeContext.Name} in kubeconfig"); } if (userDetails.UserCredentials == null) @@ -229,7 +211,7 @@ private void SetUserDetails(User userDetails) if (!userCredentialsFound) { - throw new KubeConfigException($"User: {userDetails.Name} does not have appropriate auth credentials in kube config"); + throw new KubeConfigException($"User: {userDetails.Name} does not have appropriate auth credentials in kubeconfig"); } } diff --git a/src/Utils.cs b/src/Utils.cs index 4db4fa6ea..af3801f83 100644 --- a/src/Utils.cs +++ b/src/Utils.cs @@ -1,8 +1,9 @@ namespace k8s { + using k8s.Exceptions; using System; + using System.ComponentModel; using System.Diagnostics; - using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Text; @@ -50,7 +51,8 @@ public static async Task GeneratePfxAsync(KubernetesClientConfiguration var filePrefix = config.CurrentContext; var pfxFilePath = Path.Combine(certDirPath, filePrefix + "pfx"); - if (!string.IsNullOrWhiteSpace(config.ClientCertificateKey)) { + if (!string.IsNullOrWhiteSpace(config.ClientCertificateKey)) + { keyFilePath = Path.Combine(certDirPath, filePrefix + "key"); using (FileStream fs = File.Create(keyFilePath)) { @@ -58,24 +60,27 @@ public static async Task GeneratePfxAsync(KubernetesClientConfiguration await fs.WriteAsync(info, 0, info.Length).ConfigureAwait(false); } } - if (!string.IsNullOrWhiteSpace(config.ClientKey)) { + if (!string.IsNullOrWhiteSpace(config.ClientKey)) + { keyFilePath = config.ClientKey; } - if (!string.IsNullOrWhiteSpace(config.ClientCertificateData)) { + if (!string.IsNullOrWhiteSpace(config.ClientCertificateData)) + { certFilePath = Path.Combine(certDirPath, filePrefix + "cert"); - + using (FileStream fs = File.Create(certFilePath)) { byte[] info = Convert.FromBase64String(config.ClientCertificateData); await fs.WriteAsync(info, 0, info.Length).ConfigureAwait(false); } } - if (!string.IsNullOrWhiteSpace(config.ClientCertificate)) { + if (!string.IsNullOrWhiteSpace(config.ClientCertificate)) + { certFilePath = config.ClientCertificate; } - var process = new Process(); - process.StartInfo = new ProcessStartInfo() + + var processStartInfo = new ProcessStartInfo { FileName = @"openssl", Arguments = $"pkcs12 -export -out {pfxFilePath} -inkey {keyFilePath} -in {certFilePath} -passout pass:", @@ -84,14 +89,23 @@ public static async Task GeneratePfxAsync(KubernetesClientConfiguration RedirectStandardOutput = true }; - process.Start(); - process.WaitForExit(); - if (process.ExitCode == 0) + try + { + using (Process process = Process.Start(processStartInfo)) + { + process.WaitForExit(); + if (process.ExitCode != 0) + { + throw new KubernetesClientException($"Failed to generate pfx file with openssl. ExitCode = {process.ExitCode}."); + } + } + } + catch (Win32Exception e) { - return pfxFilePath; + throw new KubernetesClientException("Failed to generate pfx file with openssl.", e); } - return null; + return pfxFilePath; } } } diff --git a/tests/KubernetesClientConfigurationTests.cs b/tests/KubernetesClientConfigurationTests.cs index c4baefb3d..2ac872f47 100755 --- a/tests/KubernetesClientConfigurationTests.cs +++ b/tests/KubernetesClientConfigurationTests.cs @@ -1,6 +1,4 @@ -using System; -using Xunit; -using k8s; +using Xunit; using System.IO; namespace k8s.Tests