Update docs. Better error message when openssl fails. Simplifying
`KubernetesClientConfiguration`
This commit is contained in:
31
README.md
31
README.md
@@ -1,43 +1,58 @@
|
|||||||
# csharp
|
# csharp
|
||||||
Work In Progress
|
Work In Progress
|
||||||
Currently only supported on Linux
|
|
||||||
|
|
||||||
[](https://travis-ci.org/kubernetes-client/csharp)
|
[](https://travis-ci.org/kubernetes-client/csharp)
|
||||||
|
|
||||||
# Generating the client code
|
# Generating the Client Code
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Check out the generator project into some other directory
|
Check out the generator project into some other directory
|
||||||
(henceforth `$GEN_DIR`)
|
(henceforth `$GEN_DIR`)
|
||||||
|
|
||||||
```
|
```bash
|
||||||
cd $GEN_DIR/..
|
cd $GEN_DIR/..
|
||||||
git clone https://github.com/kubernetes-client/gen
|
git clone https://github.com/kubernetes-client/gen
|
||||||
```
|
```
|
||||||
|
|
||||||
Install the [`autorest` tool](https://github.com/azure/autorest):
|
Install the [`autorest` tool](https://github.com/azure/autorest):
|
||||||
|
|
||||||
```
|
```bash
|
||||||
npm install autorest
|
npm install autorest
|
||||||
```
|
```
|
||||||
|
|
||||||
## Generating code
|
## Generating code
|
||||||
|
|
||||||
```
|
```bash
|
||||||
# Where REPO_DIR points to the root of the csharp repository
|
# Where REPO_DIR points to the root of the csharp repository
|
||||||
cd ${REPO_DIR}/csharp/src
|
cd ${REPO_DIR}/csharp/src
|
||||||
${GEN_DIR}/openapi/csharp.sh generated csharp.settings
|
${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.
|
The project uses [XUnit](https://xunit.github.io) as unit testing framework.
|
||||||
|
|
||||||
To run the tests you need to:
|
To run the tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd tests
|
cd csharp\tests
|
||||||
dotnet restore
|
dotnet restore
|
||||||
dotnet xunit
|
dotnet xunit
|
||||||
```
|
```
|
||||||
@@ -71,10 +71,6 @@
|
|||||||
!string.IsNullOrWhiteSpace(config.ClientKey)))
|
!string.IsNullOrWhiteSpace(config.ClientKey)))
|
||||||
{
|
{
|
||||||
var pfxFilePath = await Utils.GeneratePfxAsync(config).ConfigureAwait(false);
|
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);
|
var cert = new X509Certificate2(pfxFilePath, string.Empty, X509KeyStorageFlags.PersistKeySet);
|
||||||
handler.ClientCertificates.Add(cert);
|
handler.ClientCertificates.Add(cert);
|
||||||
|
|||||||
@@ -16,17 +16,12 @@ namespace k8s
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="KubernetesClientConfiguration"/> class.
|
/// Initializes a new instance of the <see cref="KubernetesClientConfiguration"/> class.
|
||||||
/// Initializes a new instance of the ClientConfiguration class
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kubeconfig">kubeconfig file info</param>
|
/// <param name="kubeconfig">kubeconfig file info</param>
|
||||||
/// <param name="currentContext">Context to use from kube config</param>
|
/// <param name="currentContext">Context to use from kube config</param>
|
||||||
public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentContext = null)
|
public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentContext = null)
|
||||||
{
|
{
|
||||||
if (kubeconfig == null)
|
var k8SConfig = this.LoadKubeConfig(kubeconfig ?? new FileInfo(KubeConfigDefaultLocation));
|
||||||
{
|
|
||||||
kubeconfig = new FileInfo(KubeConfigDefaultLocation);
|
|
||||||
}
|
|
||||||
var k8SConfig = this.LoadKubeConfig(kubeconfig);
|
|
||||||
this.Initialize(k8SConfig, currentContext);
|
this.Initialize(k8SConfig, currentContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,86 +103,73 @@ namespace k8s
|
|||||||
/// <param name="currentContext">Current Context</param>
|
/// <param name="currentContext">Current Context</param>
|
||||||
private void Initialize(K8SConfiguration k8SConfig, string currentContext = null)
|
private void Initialize(K8SConfiguration k8SConfig, string currentContext = null)
|
||||||
{
|
{
|
||||||
Context activeContext;
|
|
||||||
|
|
||||||
if (k8SConfig.Contexts == null)
|
if (k8SConfig.Contexts == null)
|
||||||
{
|
{
|
||||||
throw new KubeConfigException("No contexts found in kubeconfig");
|
throw new KubeConfigException("No contexts found in kubeconfig");
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the currentCOntext to passed context if not null
|
|
||||||
if (!string.IsNullOrWhiteSpace(currentContext))
|
|
||||||
{
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// otherwise set current context from kubeconfig
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.CurrentContext = activeContext.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k8SConfig.Clusters == null)
|
if (k8SConfig.Clusters == null)
|
||||||
{
|
{
|
||||||
throw new KubeConfigException($"clusters not found for current-context :{activeContext} in kubeconfig");
|
throw new KubeConfigException($"No clusters found in kubeconfig");
|
||||||
}
|
}
|
||||||
|
|
||||||
var clusterDetails = k8SConfig.Clusters.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.Cluster, StringComparison.OrdinalIgnoreCase));
|
if (k8SConfig.Users == null)
|
||||||
if (clusterDetails?.ClusterEndpoint != null)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.Server))
|
throw new KubeConfigException($"No users found 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
// current context
|
||||||
|
currentContext = currentContext ?? k8SConfig.CurrentContext;
|
||||||
|
Context activeContext = k8SConfig.Contexts.FirstOrDefault(c => c.Name.Equals(currentContext, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (activeContext == null)
|
||||||
{
|
{
|
||||||
throw new KubeConfigException($"Cluster details not found for current-context: {activeContext} in kubeconfig");
|
throw new KubeConfigException($"CurrentContext: {currentContext} not found in contexts in kubeconfig");
|
||||||
}
|
}
|
||||||
|
|
||||||
// set user details from kubeconfig
|
this.CurrentContext = activeContext.Name;
|
||||||
var userDetails = k8SConfig.Users.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.User, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
this.SetUserDetails(userDetails);
|
// cluster
|
||||||
|
var clusterDetails = k8SConfig.Clusters.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.Cluster, StringComparison.OrdinalIgnoreCase));
|
||||||
|
if (clusterDetails?.ClusterEndpoint == null)
|
||||||
|
{
|
||||||
|
throw new KubeConfigException($"Cluster not found for context {activeContext} in kubeconfig");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.Server))
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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)
|
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)
|
if (userDetails.UserCredentials == null)
|
||||||
@@ -229,7 +211,7 @@ namespace k8s
|
|||||||
|
|
||||||
if (!userCredentialsFound)
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
38
src/Utils.cs
38
src/Utils.cs
@@ -1,8 +1,9 @@
|
|||||||
namespace k8s
|
namespace k8s
|
||||||
{
|
{
|
||||||
|
using k8s.Exceptions;
|
||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -50,7 +51,8 @@
|
|||||||
|
|
||||||
var filePrefix = config.CurrentContext;
|
var filePrefix = config.CurrentContext;
|
||||||
var pfxFilePath = Path.Combine(certDirPath, filePrefix + "pfx");
|
var pfxFilePath = Path.Combine(certDirPath, filePrefix + "pfx");
|
||||||
if (!string.IsNullOrWhiteSpace(config.ClientCertificateKey)) {
|
if (!string.IsNullOrWhiteSpace(config.ClientCertificateKey))
|
||||||
|
{
|
||||||
keyFilePath = Path.Combine(certDirPath, filePrefix + "key");
|
keyFilePath = Path.Combine(certDirPath, filePrefix + "key");
|
||||||
using (FileStream fs = File.Create(keyFilePath))
|
using (FileStream fs = File.Create(keyFilePath))
|
||||||
{
|
{
|
||||||
@@ -58,11 +60,13 @@
|
|||||||
await fs.WriteAsync(info, 0, info.Length).ConfigureAwait(false);
|
await fs.WriteAsync(info, 0, info.Length).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrWhiteSpace(config.ClientKey)) {
|
if (!string.IsNullOrWhiteSpace(config.ClientKey))
|
||||||
|
{
|
||||||
keyFilePath = config.ClientKey;
|
keyFilePath = config.ClientKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(config.ClientCertificateData)) {
|
if (!string.IsNullOrWhiteSpace(config.ClientCertificateData))
|
||||||
|
{
|
||||||
certFilePath = Path.Combine(certDirPath, filePrefix + "cert");
|
certFilePath = Path.Combine(certDirPath, filePrefix + "cert");
|
||||||
|
|
||||||
using (FileStream fs = File.Create(certFilePath))
|
using (FileStream fs = File.Create(certFilePath))
|
||||||
@@ -71,11 +75,12 @@
|
|||||||
await fs.WriteAsync(info, 0, info.Length).ConfigureAwait(false);
|
await fs.WriteAsync(info, 0, info.Length).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrWhiteSpace(config.ClientCertificate)) {
|
if (!string.IsNullOrWhiteSpace(config.ClientCertificate))
|
||||||
|
{
|
||||||
certFilePath = config.ClientCertificate;
|
certFilePath = config.ClientCertificate;
|
||||||
}
|
}
|
||||||
var process = new Process();
|
|
||||||
process.StartInfo = new ProcessStartInfo()
|
var processStartInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = @"openssl",
|
FileName = @"openssl",
|
||||||
Arguments = $"pkcs12 -export -out {pfxFilePath} -inkey {keyFilePath} -in {certFilePath} -passout pass:",
|
Arguments = $"pkcs12 -export -out {pfxFilePath} -inkey {keyFilePath} -in {certFilePath} -passout pass:",
|
||||||
@@ -84,14 +89,23 @@
|
|||||||
RedirectStandardOutput = true
|
RedirectStandardOutput = true
|
||||||
};
|
};
|
||||||
|
|
||||||
process.Start();
|
try
|
||||||
process.WaitForExit();
|
|
||||||
if (process.ExitCode == 0)
|
|
||||||
{
|
{
|
||||||
return pfxFilePath;
|
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)
|
||||||
|
{
|
||||||
|
throw new KubernetesClientException("Failed to generate pfx file with openssl.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return pfxFilePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
using Xunit;
|
||||||
using Xunit;
|
|
||||||
using k8s;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace k8s.Tests
|
namespace k8s.Tests
|
||||||
|
|||||||
Reference in New Issue
Block a user