- Notifications
You must be signed in to change notification settings - Fork 308
Make LoadKubeConfig public and refactor to use YAML helper methods #133
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| | @@ -3,10 +3,10 @@ | |
| using System.Linq; | ||
| using System.Runtime.InteropServices; | ||
| using System.Security.Cryptography.X509Certificates; | ||
| using System.Threading.Tasks; | ||
| using k8s.Exceptions; | ||
| using k8s.KubeConfigModels; | ||
| using YamlDotNet.Serialization; | ||
| | ||
| | ||
| namespace k8s | ||
| { | ||
| public partial class KubernetesClientConfiguration | ||
| | @@ -28,7 +28,7 @@ public partial class KubernetesClientConfiguration | |
| /// 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> | ||
| /// <param name="kubeconfigPath">Explicit file path to kubeconfig. Set to null to use the default file path</param> | ||
| public static KubernetesClientConfiguration BuildConfigFromConfigFile(string masterUrl = null, | ||
| string kubeconfigPath = null) | ||
| { | ||
| | @@ -57,7 +57,7 @@ public static KubernetesClientConfiguration BuildConfigFromConfigFile(FileInfo k | |
| | ||
| /// <summary> | ||
| /// </summary> | ||
| /// <param name="kubeconfig">Fileinfo of the kubeconfig, cannot be null, whitespaced or empty</param> | ||
| /// <param name="kubeconfig">String representation of kubeconfig contents, cannot be null, whitespaced or empty</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(string kubeconfig, | ||
| ||
| | @@ -68,7 +68,7 @@ public static KubernetesClientConfiguration BuildConfigFromConfigFile(string kub | |
| throw new NullReferenceException(nameof(kubeconfig)); | ||
| } | ||
| | ||
| var k8SConfig = LoadKubeConfig(kubeconfig); | ||
| var k8SConfig = Yaml.LoadFromString<K8SConfiguration>(kubeconfig); | ||
| var k8SConfiguration = GetKubernetesClientConfiguration(currentContext, masterUrl, k8SConfig); | ||
| | ||
| return k8SConfiguration; | ||
| | @@ -94,7 +94,7 @@ public static KubernetesClientConfiguration BuildConfigFromConfigFile(Stream kub | |
| | ||
| kubeconfig.Position = 0; | ||
| | ||
| var k8SConfig = LoadKubeConfig(kubeconfig); | ||
| var k8SConfig = Yaml.LoadFromStreamAsync<K8SConfiguration>(kubeconfig).GetAwaiter().GetResult(); | ||
| var k8SConfiguration = GetKubernetesClientConfiguration(currentContext, masterUrl, k8SConfig); | ||
| | ||
| return k8SConfiguration; | ||
| | @@ -259,53 +259,56 @@ private void SetUserDetails(K8SConfiguration k8SConfig, Context activeContext) | |
| throw new KubeConfigException( | ||
| $"User: {userDetails.Name} does not have appropriate auth credentials in kubeconfig"); | ||
| } | ||
| } | ||
| } | ||
| | ||
| /// <summary> | ||
| /// Loads entire Kube Config from default or explicit file path | ||
| /// </summary> | ||
| /// <param name="kubeconfigPath">Explicit file path to kubeconfig. Set to null to use the default file path</param> | ||
| /// <returns></returns> | ||
| public static async Task<K8SConfiguration> LoadKubeConfigAsync(string kubeconfigPath = null) | ||
| { | ||
| var fileInfo = new FileInfo(kubeconfigPath ?? KubeConfigDefaultLocation); | ||
| | ||
| return await LoadKubeConfigAsync(fileInfo); | ||
| } | ||
| | ||
| /// <summary> | ||
| /// Loads entire Kube Config from default or explicit file path | ||
| /// </summary> | ||
| /// <param name="kubeconfigPath">Explicit file path to kubeconfig. Set to null to use the default file path</param> | ||
| /// <returns></returns> | ||
| public static K8SConfiguration LoadKubeConfig(string kubeconfigPath = null) | ||
| { | ||
| return LoadKubeConfigAsync(kubeconfigPath).GetAwaiter().GetResult(); | ||
| } | ||
| | ||
| // <summary> | ||
| /// Loads Kube Config | ||
| /// </summary> | ||
| /// <param name="kubeconfig">Kube config file contents</param> | ||
| /// <returns>Instance of the <see cref="K8SConfiguration"/> class</returns> | ||
| private static K8SConfiguration LoadKubeConfig(FileInfo kubeconfig) | ||
| public static async Task<K8SConfiguration> LoadKubeConfigAsync(FileInfo kubeconfig) | ||
| { | ||
| if (!kubeconfig.Exists) | ||
| { | ||
| throw new KubeConfigException($"kubeconfig file not found at {kubeconfig.FullName}"); | ||
| } | ||
| | ||
| using (var stream = kubeconfig.OpenRead()) | ||
| { | ||
| return await Yaml.LoadFromStreamAsync<K8SConfiguration>(stream); | ||
| } | ||
| | ||
| var deserializeBuilder = new DeserializerBuilder(); | ||
| var deserializer = deserializeBuilder.Build(); | ||
| using (var kubeConfigTextStream = kubeconfig.OpenText()) | ||
| { | ||
| return deserializer.Deserialize<K8SConfiguration>(kubeConfigTextStream); | ||
| } | ||
| } | ||
| | ||
| /// <summary> | ||
| /// Loads Kube Config from string | ||
| /// </summary> | ||
| /// <param name="kubeconfig">Kube config file contents</param> | ||
| /// <returns>Instance of the <see cref="K8SConfiguration"/> class</returns> | ||
| private static K8SConfiguration LoadKubeConfig(string kubeconfig) | ||
| { | ||
| | ||
| var deserializeBuilder = new DeserializerBuilder(); | ||
| var deserializer = deserializeBuilder.Build(); | ||
| return deserializer.Deserialize<K8SConfiguration>(kubeconfig); | ||
| } | ||
| | ||
| /// <summary> | ||
| /// Loads Kube Config from stream. | ||
| /// Loads Kube Config | ||
| /// </summary> | ||
| /// <param name="kubeconfig">Kube config file contents</param> | ||
| /// <returns>Instance of the <see cref="K8SConfiguration"/> class</returns> | ||
| private static K8SConfiguration LoadKubeConfig(Stream kubeconfig) | ||
| public static K8SConfiguration LoadKubeConfig(FileInfo kubeconfig) | ||
| Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's keep this as a stream, since we want to support loading from network streams as well as files. Contributor Author There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. I'll add it back in and use Yaml.LoadFromStreamAsync. Should we keep the method with FileInfo kubeconfig? Essentially, we would have these method signatures. Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your call about | ||
| { | ||
| using (var sr = new StreamReader(kubeconfig)) | ||
| { | ||
| var strKubeConfig = sr.ReadToEnd(); | ||
| return LoadKubeConfig(strKubeConfig); | ||
| } | ||
| return LoadKubeConfigAsync(kubeconfig).GetAwaiter().GetResult(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| using System.IO; | ||
| using System.Linq; | ||
| using k8s.Exceptions; | ||
| using k8s.KubeConfigModels; | ||
| using Xunit; | ||
| | ||
| namespace k8s.Tests | ||
| | @@ -270,8 +272,8 @@ public void NoCurrentContext() | |
| public void DeletedConfigurationFile() | ||
| { | ||
| var assetFileInfo = new FileInfo("assets/kubeconfig.yml"); | ||
| var tempFileInfo = new FileInfo(Path.GetTempFileName()); | ||
| var tempFileInfo = new FileInfo(Path.GetTempFileName()); | ||
| | ||
| File.Copy(assetFileInfo.FullName, tempFileInfo.FullName, /* overwrite: */ true); | ||
| | ||
| KubernetesClientConfiguration config; | ||
| | @@ -322,6 +324,76 @@ public void AsUserExtra() | |
| | ||
| var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(txt, null, null); | ||
| Assert.NotNull(cfg.Host); | ||
| } | ||
| | ||
| /// <summary> | ||
| /// Ensures Kube config file is loaded from explicit file | ||
| /// </summary> | ||
| [Fact] | ||
| public void LoadKubeConfigExplicitFilePath() | ||
| { | ||
| var txt = File.ReadAllText("assets/kubeconfig.yml"); | ||
| var expectedCfg = Yaml.LoadFromString<K8SConfiguration>(txt); | ||
| | ||
| var cfg = KubernetesClientConfiguration.LoadKubeConfig("assets/kubeconfig.yml"); | ||
| | ||
| Assert.NotNull(cfg); | ||
| | ||
| ConfigsEqual(expectedCfg, cfg); | ||
| } | ||
| | ||
| private void ConfigsEqual(K8SConfiguration expected, K8SConfiguration actual) | ||
| ||
| { | ||
| Assert.Equal(expected.ApiVersion, actual.ApiVersion); | ||
| Assert.Equal(expected.CurrentContext, actual.CurrentContext); | ||
| | ||
| foreach (var expectedContext in expected.Contexts) | ||
| { | ||
| // Will throw exception if not found | ||
| var actualContext = actual.Contexts.First(c => c.Name.Equals(expectedContext.Name)); | ||
| | ||
| Assert.Equal(expectedContext.Namespace, actualContext.Namespace); | ||
| Assert.Equal(expectedContext.ContextDetails.Cluster, actualContext.ContextDetails.Cluster); | ||
| Assert.Equal(expectedContext.ContextDetails.User, actualContext.ContextDetails.User); | ||
| Assert.Equal(expectedContext.ContextDetails.Namespace, actualContext.ContextDetails.Namespace); | ||
| } | ||
| | ||
| foreach (var expectedCluster in expected.Clusters) | ||
| { | ||
| // Will throw exception if not found | ||
| var actualCluster = actual.Clusters.First(c => c.Name.Equals(expectedCluster.Name)); | ||
| | ||
| Assert.Equal(expectedCluster.ClusterEndpoint.CertificateAuthority, actualCluster.ClusterEndpoint.CertificateAuthority); | ||
| Assert.Equal(expectedCluster.ClusterEndpoint.CertificateAuthorityData, actualCluster.ClusterEndpoint.CertificateAuthorityData); | ||
| Assert.Equal(expectedCluster.ClusterEndpoint.Server, actualCluster.ClusterEndpoint.Server); | ||
| Assert.Equal(expectedCluster.ClusterEndpoint.SkipTlsVerify, actualCluster.ClusterEndpoint.SkipTlsVerify); | ||
| } | ||
| | ||
| foreach (var expectedUser in expected.Users) | ||
| { | ||
| // Will throw exception if not found | ||
| var actualUser = actual.Users.First(u => u.Name.Equals(expectedUser.Name)); | ||
| | ||
| var expectedCreds = expectedUser.UserCredentials; | ||
| var actualCreds = expectedUser.UserCredentials; | ||
| | ||
| Assert.Equal(expectedCreds.ClientCertificateData, actualCreds.ClientCertificateData); | ||
| Assert.Equal(expectedCreds.ClientCertificate, actualCreds.ClientCertificate); | ||
| Assert.Equal(expectedCreds.ClientKeyData, actualCreds.ClientKeyData); | ||
| Assert.Equal(expectedCreds.ClientKey, actualCreds.ClientKey); | ||
| Assert.Equal(expectedCreds.Token, actualCreds.Token); | ||
| Assert.Equal(expectedCreds.Impersonate, actualCreds.Impersonate); | ||
| Assert.Equal(expectedCreds.UserName, actualCreds.UserName); | ||
| Assert.Equal(expectedCreds.Password, actualCreds.Password); | ||
| | ||
| Assert.True(expectedCreds.ImpersonateGroups.All(x => actualCreds.ImpersonateGroups.Contains(x))); | ||
| Assert.True(expectedCreds.ImpersonateUserExtra.All(x => actualCreds.ImpersonateUserExtra.Contains(x))); | ||
| | ||
| if (expectedCreds.AuthProvider != null) | ||
| { | ||
| Assert.True(expectedCreds.AuthProvider.All(x => actualCreds.AuthProvider.Contains(x))); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
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.
ApplyNamingConventions = falseis need when using naming conventions like CamelCaseNamingConvention