-
Notifications
You must be signed in to change notification settings - Fork 303
Decouple yaml file and KubernetesClientConfiguration object #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
using System; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Runtime.InteropServices; | ||
using System.Security.Cryptography.X509Certificates; | ||
using k8s.Exceptions; | ||
using k8s.KubeConfigModels; | ||
using YamlDotNet.Serialization; | ||
|
||
namespace k8s | ||
{ | ||
public partial class KubernetesClientConfiguration | ||
{ | ||
/// <summary> | ||
/// Gets CurrentContext | ||
/// </summary> | ||
public string CurrentContext { get; private set; } | ||
|
||
/// <summary> | ||
/// kubeconfig Default Location | ||
/// </summary> | ||
private static readonly string KubeConfigDefaultLocation = | ||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) | ||
? Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), @".kube\config") | ||
: Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".kube/config"); | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="KubernetesClientConfiguration"/> class. | ||
/// </summary> | ||
/// <param name="kubeconfig">kubeconfig file info</param> | ||
/// <param name="currentContext">Context to use from kube config</param> | ||
public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentContext = null) | ||
{ | ||
var k8SConfig = LoadKubeConfig(kubeconfig ?? new FileInfo(KubeConfigDefaultLocation)); | ||
this.Initialize(k8SConfig, currentContext); | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="KubernetesClientConfiguration"/> from config file | ||
/// </summary> | ||
/// <param name="masterUrl">kube api server endpoint</param> | ||
/// <param name="kubeconfigPath">kubeconfig filepath</param> | ||
public static KubernetesClientConfiguration BuildConfigFromConfigFile(string masterUrl = null, string kubeconfigPath = null) | ||
{ | ||
return BuildConfigFromConfigFile(new FileInfo(kubeconfigPath ?? KubeConfigDefaultLocation), null, masterUrl); | ||
} | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <param name="kubeconfig">Fileinfo of the kubeconfig, cannot be null</param> | ||
/// <param name="currentContext">override the context in config file, set null if do not want to override</param> | ||
/// <param name="masterUrl">overrider kube api server endpoint, set null if do not want to override</param> | ||
public static KubernetesClientConfiguration BuildConfigFromConfigFile(FileInfo kubeconfig, string currentContext = null, string masterUrl = null) | ||
{ | ||
if (kubeconfig == null) | ||
{ | ||
throw new NullReferenceException(nameof(kubeconfig)); | ||
} | ||
|
||
var k8SConfig = LoadKubeConfig(kubeconfig); | ||
var k8SConfiguration = new KubernetesClientConfiguration(); | ||
k8SConfiguration.Initialize(k8SConfig); | ||
|
||
if (!string.IsNullOrWhiteSpace(masterUrl)) | ||
{ | ||
k8SConfiguration.Host = masterUrl; | ||
} | ||
return k8SConfiguration; | ||
} | ||
|
||
|
||
/// <summary> | ||
/// Validates and Intializes Client Configuration | ||
/// </summary> | ||
/// <param name="k8SConfig">Kubernetes Configuration</param> | ||
/// <param name="currentContext">Current Context</param> | ||
private void Initialize(K8SConfiguration k8SConfig, string currentContext = null) | ||
{ | ||
if (k8SConfig.Contexts == null) | ||
{ | ||
throw new KubeConfigException("No contexts found in kubeconfig"); | ||
} | ||
|
||
if (k8SConfig.Clusters == null) | ||
{ | ||
throw new KubeConfigException($"No clusters found in kubeconfig"); | ||
} | ||
|
||
if (k8SConfig.Users == null) | ||
{ | ||
throw new KubeConfigException($"No users found in kubeconfig"); | ||
} | ||
|
||
// 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"); | ||
} | ||
|
||
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($"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); | ||
} | ||
this.SkipTlsVerify = clusterDetails.ClusterEndpoint.SkipTlsVerify; | ||
|
||
// user | ||
this.SetUserDetails(k8SConfig, activeContext); | ||
} | ||
|
||
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 context {activeContext.Name} in kubeconfig"); | ||
} | ||
|
||
if (userDetails.UserCredentials == null) | ||
{ | ||
throw new KubeConfigException($"User credentials not found for user: {userDetails.Name} in kubeconfig"); | ||
} | ||
|
||
var userCredentialsFound = false; | ||
|
||
// Basic and bearer tokens are mutually exclusive | ||
if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.Token)) | ||
{ | ||
this.AccessToken = userDetails.UserCredentials.Token; | ||
userCredentialsFound = true; | ||
} | ||
else if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.UserName) && | ||
!string.IsNullOrWhiteSpace(userDetails.UserCredentials.Password)) | ||
{ | ||
this.Username = userDetails.UserCredentials.UserName; | ||
this.Password = userDetails.UserCredentials.Password; | ||
userCredentialsFound = true; | ||
} | ||
|
||
// Token and cert based auth can co-exist | ||
if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientCertificateData) && | ||
!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientKeyData)) | ||
{ | ||
this.ClientCertificateData = userDetails.UserCredentials.ClientCertificateData; | ||
this.ClientCertificateKey = userDetails.UserCredentials.ClientKeyData; | ||
userCredentialsFound = true; | ||
} | ||
|
||
if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientCertificate) && | ||
!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientKey)) | ||
{ | ||
this.ClientCertificate = userDetails.UserCredentials.ClientCertificate; | ||
this.ClientKey = userDetails.UserCredentials.ClientKey; | ||
userCredentialsFound = true; | ||
} | ||
|
||
if (!userCredentialsFound) | ||
{ | ||
throw new KubeConfigException( | ||
$"User: {userDetails.Name} does not have appropriate auth credentials in kubeconfig"); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Loads Kube Config | ||
/// </summary> | ||
/// <param name="config">Kube config file contents</param> | ||
/// <returns>Instance of the <see cref="K8SConfiguration"/> class</returns> | ||
private static K8SConfiguration LoadKubeConfig(FileInfo kubeconfig) | ||
{ | ||
if (!kubeconfig.Exists) | ||
{ | ||
throw new KubeConfigException($"kubeconfig file not found at {kubeconfig.FullName}"); | ||
} | ||
var kubeconfigContent = File.ReadAllText(kubeconfig.FullName); | ||
|
||
var deserializeBuilder = new DeserializerBuilder(); | ||
var deserializer = deserializeBuilder.Build(); | ||
return deserializer.Deserialize<K8SConfiguration>(kubeconfigContent); | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System; | ||
using System.IO; | ||
using k8s.Exceptions; | ||
|
||
namespace k8s | ||
{ | ||
public partial class KubernetesClientConfiguration | ||
{ | ||
private const string ServiceaccountPath = "/var/run/secrets/kubernetes.io/serviceaccount/"; | ||
private const string ServiceAccountTokenKeyFileName = "token"; | ||
private const string ServiceAccountRootCAKeyFileName = "ca.crt"; | ||
|
||
public static KubernetesClientConfiguration InClusterConfig() | ||
{ | ||
var host = Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_HOST"); | ||
var port = Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_PORT"); | ||
|
||
if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(port)) | ||
{ | ||
throw new KubeConfigException( | ||
"unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined"); | ||
} | ||
|
||
var token = File.ReadAllText(Path.Combine(ServiceaccountPath, ServiceAccountTokenKeyFileName)); | ||
var rootCAFile = Path.Combine(ServiceaccountPath, ServiceAccountRootCAKeyFileName); | ||
|
||
return new KubernetesClientConfiguration | ||
{ | ||
Host = new UriBuilder("https", host, Convert.ToInt32(port)).ToString(), | ||
AccessToken = token, | ||
SslCaCert = Utils.LoadPemFileCert(rootCAFile) | ||
}; | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
combine the
else
andif
into anelse if
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, wait, nevermind, I see there's a statement under the if...