Skip to content

Commit e6fab82

Browse files
committed
introduce go client style config creating functions
1 parent d68e940 commit e6fab82

File tree

4 files changed

+258
-158
lines changed

4 files changed

+258
-158
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Runtime.InteropServices;
5+
using System.Security.Cryptography.X509Certificates;
6+
using k8s.Exceptions;
7+
using k8s.KubeConfigModels;
8+
using YamlDotNet.Serialization;
9+
10+
namespace k8s
11+
{
12+
public partial class KubernetesClientConfiguration
13+
{
14+
/// <summary>
15+
/// Gets CurrentContext
16+
/// </summary>
17+
public string CurrentContext { get; private set; }
18+
19+
/// <summary>
20+
/// kubeconfig Default Location
21+
/// </summary>
22+
private static readonly string KubeConfigDefaultLocation =
23+
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
24+
? Path.Combine(Environment.GetEnvironmentVariable("USERPROFILE"), @".kube\config")
25+
: Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".kube/config");
26+
27+
/// <summary>
28+
/// Initializes a new instance of the <see cref="KubernetesClientConfiguration"/> class.
29+
/// </summary>
30+
/// <param name="kubeconfig">kubeconfig file info</param>
31+
/// <param name="currentContext">Context to use from kube config</param>
32+
public KubernetesClientConfiguration(FileInfo kubeconfig = null, string currentContext = null)
33+
{
34+
var k8SConfig = LoadKubeConfig(kubeconfig ?? new FileInfo(KubeConfigDefaultLocation));
35+
this.Initialize(k8SConfig, currentContext);
36+
}
37+
38+
/// <summary>
39+
/// Initializes a new instance of the <see cref="KubernetesClientConfiguration"/> from config file
40+
/// </summary>
41+
/// <param name="masterUrl">kube api server endpoint</param>
42+
/// <param name="kubeconfigPath">kubeconfig filepath</param>
43+
/// <returns></returns>
44+
public static KubernetesClientConfiguration BuildConfigFromConfigFile(string masterUrl = null, string kubeconfigPath = null)
45+
{
46+
var k8SConfig = LoadKubeConfig(new FileInfo(kubeconfigPath ?? KubeConfigDefaultLocation));
47+
var k8SConfiguration = new KubernetesClientConfiguration();
48+
k8SConfiguration.Initialize(k8SConfig);
49+
50+
if (!string.IsNullOrWhiteSpace(masterUrl))
51+
{
52+
k8SConfiguration.Host = masterUrl;
53+
}
54+
return k8SConfiguration;
55+
}
56+
57+
/// <summary>
58+
/// Validates and Intializes Client Configuration
59+
/// </summary>
60+
/// <param name="k8SConfig">Kubernetes Configuration</param>
61+
/// <param name="currentContext">Current Context</param>
62+
private void Initialize(K8SConfiguration k8SConfig, string currentContext = null)
63+
{
64+
if (k8SConfig.Contexts == null)
65+
{
66+
throw new KubeConfigException("No contexts found in kubeconfig");
67+
}
68+
69+
if (k8SConfig.Clusters == null)
70+
{
71+
throw new KubeConfigException($"No clusters found in kubeconfig");
72+
}
73+
74+
if (k8SConfig.Users == null)
75+
{
76+
throw new KubeConfigException($"No users found in kubeconfig");
77+
}
78+
79+
// current context
80+
currentContext = currentContext ?? k8SConfig.CurrentContext;
81+
Context activeContext =
82+
k8SConfig.Contexts.FirstOrDefault(
83+
c => c.Name.Equals(currentContext, StringComparison.OrdinalIgnoreCase));
84+
if (activeContext == null)
85+
{
86+
throw new KubeConfigException($"CurrentContext: {currentContext} not found in contexts in kubeconfig");
87+
}
88+
89+
this.CurrentContext = activeContext.Name;
90+
91+
// cluster
92+
var clusterDetails =
93+
k8SConfig.Clusters.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.Cluster,
94+
StringComparison.OrdinalIgnoreCase));
95+
if (clusterDetails?.ClusterEndpoint == null)
96+
{
97+
throw new KubeConfigException($"Cluster not found for context {activeContext} in kubeconfig");
98+
}
99+
100+
if (string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.Server))
101+
{
102+
throw new KubeConfigException($"Server not found for current-context {activeContext} in kubeconfig");
103+
}
104+
105+
if (!clusterDetails.ClusterEndpoint.SkipTlsVerify &&
106+
string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthorityData) &&
107+
string.IsNullOrWhiteSpace(clusterDetails.ClusterEndpoint.CertificateAuthority))
108+
{
109+
throw new KubeConfigException(
110+
$"neither certificate-authority-data nor certificate-authority not found for current-context :{activeContext} in kubeconfig");
111+
}
112+
113+
this.Host = clusterDetails.ClusterEndpoint.Server;
114+
if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData))
115+
{
116+
string data = clusterDetails.ClusterEndpoint.CertificateAuthorityData;
117+
this.SslCaCert = new X509Certificate2(System.Text.Encoding.UTF8.GetBytes(Utils.Base64Decode(data)));
118+
}
119+
else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority))
120+
{
121+
this.SslCaCert = new X509Certificate2(clusterDetails.ClusterEndpoint.CertificateAuthority);
122+
}
123+
this.SkipTlsVerify = clusterDetails.ClusterEndpoint.SkipTlsVerify;
124+
125+
// user
126+
this.SetUserDetails(k8SConfig, activeContext);
127+
}
128+
129+
private void SetUserDetails(K8SConfiguration k8SConfig, Context activeContext)
130+
{
131+
var userDetails = k8SConfig.Users.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.User,
132+
StringComparison.OrdinalIgnoreCase));
133+
134+
if (userDetails == null)
135+
{
136+
throw new KubeConfigException("User not found for context {activeContext.Name} in kubeconfig");
137+
}
138+
139+
if (userDetails.UserCredentials == null)
140+
{
141+
throw new KubeConfigException($"User credentials not found for user: {userDetails.Name} in kubeconfig");
142+
}
143+
144+
var userCredentialsFound = false;
145+
146+
// Basic and bearer tokens are mutually exclusive
147+
if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.Token))
148+
{
149+
this.AccessToken = userDetails.UserCredentials.Token;
150+
userCredentialsFound = true;
151+
}
152+
else if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.UserName) &&
153+
!string.IsNullOrWhiteSpace(userDetails.UserCredentials.Password))
154+
{
155+
this.Username = userDetails.UserCredentials.UserName;
156+
this.Password = userDetails.UserCredentials.Password;
157+
userCredentialsFound = true;
158+
}
159+
160+
// Token and cert based auth can co-exist
161+
if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientCertificateData) &&
162+
!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientKeyData))
163+
{
164+
this.ClientCertificateData = userDetails.UserCredentials.ClientCertificateData;
165+
this.ClientCertificateKey = userDetails.UserCredentials.ClientKeyData;
166+
userCredentialsFound = true;
167+
}
168+
169+
if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientCertificate) &&
170+
!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientKey))
171+
{
172+
this.ClientCertificate = userDetails.UserCredentials.ClientCertificate;
173+
this.ClientKey = userDetails.UserCredentials.ClientKey;
174+
userCredentialsFound = true;
175+
}
176+
177+
if (!userCredentialsFound)
178+
{
179+
throw new KubeConfigException(
180+
$"User: {userDetails.Name} does not have appropriate auth credentials in kubeconfig");
181+
}
182+
}
183+
184+
/// <summary>
185+
/// Loads Kube Config
186+
/// </summary>
187+
/// <param name="config">Kube config file contents</param>
188+
/// <returns>Instance of the <see cref="K8SConfiguration"/> class</returns>
189+
private static K8SConfiguration LoadKubeConfig(FileInfo kubeconfig)
190+
{
191+
if (!kubeconfig.Exists)
192+
{
193+
throw new KubeConfigException($"kubeconfig file not found at {kubeconfig.FullName}");
194+
}
195+
var kubeconfigContent = File.ReadAllText(kubeconfig.FullName);
196+
197+
var deserializeBuilder = new DeserializerBuilder();
198+
var deserializer = deserializeBuilder.Build();
199+
return deserializer.Deserialize<K8SConfiguration>(kubeconfigContent);
200+
}
201+
}
202+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.IO;
3+
using System.Security.Cryptography.X509Certificates;
4+
using k8s.Exceptions;
5+
6+
namespace k8s
7+
{
8+
public partial class KubernetesClientConfiguration
9+
{
10+
private const string ServiceAccountTokenKey = "token";
11+
private const string ServiceAccountRootCAKey = "ca.crt";
12+
13+
public static KubernetesClientConfiguration InClusterConfig()
14+
{
15+
var host = Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_HOST");
16+
var port = Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_PORT");
17+
18+
if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(port))
19+
{
20+
throw new KubeConfigException(
21+
"unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined");
22+
}
23+
24+
var token = File.ReadAllText("/var/run/secrets/kubernetes.io/serviceaccount/" + ServiceAccountTokenKey);
25+
var rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/" + ServiceAccountRootCAKey;
26+
27+
return new KubernetesClientConfiguration
28+
{
29+
Host = new UriBuilder("https", host, Convert.ToInt32(port)).ToString(),
30+
AccessToken = token,
31+
SslCaCert = Utils.LoadPemFileCert(rootCAFile)
32+
};
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)