diff --git a/src/KubeConfigModels/K8SConfiguration.cs b/src/KubeConfigModels/K8SConfiguration.cs index 723b360..7d6184f 100644 --- a/src/KubeConfigModels/K8SConfiguration.cs +++ b/src/KubeConfigModels/K8SConfiguration.cs @@ -1,4 +1,4 @@ -namespace k8s.KubeConfigModels +namespace k8s.KubeConfigModels { using System.Collections.Generic; using YamlDotNet.Serialization; @@ -9,7 +9,7 @@ public class K8SConfiguration { [YamlMember(Alias = "preferences")] - public IDictionary preferences{ get; set; } + public IDictionary Preferences{ get; set; } [YamlMember(Alias = "apiVersion")] public string ApiVersion { get; set; } @@ -21,12 +21,12 @@ public string CurrentContext { get; set; } [YamlMember(Alias = "contexts")] - public IEnumerable Contexts { get; set; } + public IEnumerable Contexts { get; set; } = new Context[0]; [YamlMember(Alias = "clusters")] - public IEnumerable Clusters { get; set; } + public IEnumerable Clusters { get; set; } = new Cluster[0]; [YamlMember(Alias = "users")] - public IEnumerable Users { get; set; } + public IEnumerable Users { get; set; } = new User[0]; } } diff --git a/src/Kubernetes.ConfigInit.cs b/src/Kubernetes.ConfigInit.cs index cb01062..a95a6e3 100644 --- a/src/Kubernetes.ConfigInit.cs +++ b/src/Kubernetes.ConfigInit.cs @@ -1,10 +1,10 @@ -using k8s.Models; using System; using System.Diagnostics.CodeAnalysis; using System.Net.Http; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using k8s.Exceptions; +using k8s.Models; using Microsoft.Rest; namespace k8s @@ -22,8 +22,21 @@ namespace k8s /// public Kubernetes(KubernetesClientConfiguration config, params DelegatingHandler[] handlers) : this(handlers) { + if (string.IsNullOrWhiteSpace(config.Host)) + { + throw new KubeConfigException("Host url must be set"); + } + + try + { + BaseUri = new Uri(config.Host); + } + catch (UriFormatException e) + { + throw new KubeConfigException("Bad host url", e); + } + CaCert = config.SslCaCert; - BaseUri = new Uri(config.Host); if (BaseUri.Scheme == "https") { diff --git a/src/KubernetesClientConfiguration.ConfigFile.cs b/src/KubernetesClientConfiguration.ConfigFile.cs index 8024764..559d07a 100644 --- a/src/KubernetesClientConfiguration.ConfigFile.cs +++ b/src/KubernetesClientConfiguration.ConfigFile.cs @@ -12,12 +12,7 @@ namespace k8s public partial class KubernetesClientConfiguration { /// - /// Gets CurrentContext - /// - public string CurrentContext { get; private set; } - - /// - /// kubeconfig Default Location + /// kubeconfig Default Location /// private static readonly string KubeConfigDefaultLocation = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) @@ -25,22 +20,29 @@ namespace k8s : Path.Combine(Environment.GetEnvironmentVariable("HOME"), ".kube/config"); /// - /// Initializes a new instance of the from config file + /// Gets CurrentContext + /// + public string CurrentContext { get; private set; } + + /// + /// Initializes a new instance of the from config file /// /// kube api server endpoint /// kubeconfig filepath - public static KubernetesClientConfiguration BuildConfigFromConfigFile(string masterUrl = null, string kubeconfigPath = null) + public static KubernetesClientConfiguration BuildConfigFromConfigFile(string masterUrl = null, + string kubeconfigPath = null) { - return BuildConfigFromConfigFile(new FileInfo(kubeconfigPath ?? KubeConfigDefaultLocation), null, masterUrl); + return BuildConfigFromConfigFile(new FileInfo(kubeconfigPath ?? KubeConfigDefaultLocation), null, + masterUrl); } /// - /// /// /// Fileinfo of the kubeconfig, cannot be null /// override the context in config file, set null if do not want to override /// overrider kube api server endpoint, set null if do not want to override - public static KubernetesClientConfiguration BuildConfigFromConfigFile(FileInfo kubeconfig, string currentContext = null, string masterUrl = null) + public static KubernetesClientConfiguration BuildConfigFromConfigFile(FileInfo kubeconfig, + string currentContext = null, string masterUrl = null) { if (kubeconfig == null) { @@ -49,41 +51,37 @@ namespace k8s var k8SConfig = LoadKubeConfig(kubeconfig); var k8SConfiguration = new KubernetesClientConfiguration(); - k8SConfiguration.Initialize(k8SConfig, currentContext); + + currentContext = currentContext ?? k8SConfig.CurrentContext; + + // only init context if context if set + if (currentContext != null) + { + k8SConfiguration.InitializeContext(k8SConfig, currentContext); + } if (!string.IsNullOrWhiteSpace(masterUrl)) { k8SConfiguration.Host = masterUrl; } + + if (string.IsNullOrWhiteSpace(k8SConfiguration.Host)) + { + throw new KubeConfigException("Cannot infer server host url either from context or masterUrl"); + } + return k8SConfiguration; } - /// - /// Validates and Intializes Client Configuration + /// Validates and Intializes Client Configuration /// /// Kubernetes Configuration /// Current Context - private void Initialize(K8SConfiguration k8SConfig, string currentContext = null) + private void InitializeContext(K8SConfiguration k8SConfig, string currentContext) { - 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 = + var activeContext = k8SConfig.Contexts.FirstOrDefault( c => c.Name.Equals(currentContext, StringComparison.OrdinalIgnoreCase)); if (activeContext == null) @@ -91,12 +89,21 @@ namespace k8s throw new KubeConfigException($"CurrentContext: {currentContext} not found in contexts in kubeconfig"); } - this.CurrentContext = activeContext.Name; + CurrentContext = activeContext.Name; // cluster + SetClusterDetails(k8SConfig, activeContext); + + // user + SetUserDetails(k8SConfig, activeContext); + } + + private void SetClusterDetails(K8SConfiguration k8SConfig, Context activeContext) + { 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"); @@ -106,33 +113,49 @@ namespace k8s { throw new KubeConfigException($"Server not found for current-context {activeContext} in kubeconfig"); } + Host = clusterDetails.ClusterEndpoint.Server; - 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"); - } + SkipTlsVerify = clusterDetails.ClusterEndpoint.SkipTlsVerify; - this.Host = clusterDetails.ClusterEndpoint.Server; - if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData)) + try { - string data = clusterDetails.ClusterEndpoint.CertificateAuthorityData; - this.SslCaCert = new X509Certificate2(Convert.FromBase64String(data)); - } - else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority)) - { - this.SslCaCert = new X509Certificate2(clusterDetails.ClusterEndpoint.CertificateAuthority); - } - this.SkipTlsVerify = clusterDetails.ClusterEndpoint.SkipTlsVerify; + var uri = new Uri(Host); + if (uri.Scheme == "https") + { + + // check certificate for https + 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"); + } - // user - this.SetUserDetails(k8SConfig, activeContext); + if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthorityData)) + { + var data = clusterDetails.ClusterEndpoint.CertificateAuthorityData; + SslCaCert = new X509Certificate2(Convert.FromBase64String(data)); + } + else if (!string.IsNullOrEmpty(clusterDetails.ClusterEndpoint.CertificateAuthority)) + { + SslCaCert = new X509Certificate2(clusterDetails.ClusterEndpoint.CertificateAuthority); + } + } + } + catch (UriFormatException e) + { + throw new KubeConfigException("Bad Server host url", e); + } } private void SetUserDetails(K8SConfiguration k8SConfig, Context activeContext) { + if (string.IsNullOrWhiteSpace(activeContext.ContextDetails.User)) + { + return; + } + var userDetails = k8SConfig.Users.FirstOrDefault(c => c.Name.Equals(activeContext.ContextDetails.User, StringComparison.OrdinalIgnoreCase)); @@ -151,14 +174,14 @@ namespace k8s // Basic and bearer tokens are mutually exclusive if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.Token)) { - this.AccessToken = userDetails.UserCredentials.Token; + 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; + Username = userDetails.UserCredentials.UserName; + Password = userDetails.UserCredentials.Password; userCredentialsFound = true; } @@ -166,16 +189,16 @@ namespace k8s if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientCertificateData) && !string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientKeyData)) { - this.ClientCertificateData = userDetails.UserCredentials.ClientCertificateData; - this.ClientCertificateKeyData = userDetails.UserCredentials.ClientKeyData; + ClientCertificateData = userDetails.UserCredentials.ClientCertificateData; + ClientCertificateKeyData = userDetails.UserCredentials.ClientKeyData; userCredentialsFound = true; } if (!string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientCertificate) && !string.IsNullOrWhiteSpace(userDetails.UserCredentials.ClientKey)) { - this.ClientCertificateFilePath = userDetails.UserCredentials.ClientCertificate; - this.ClientKeyFilePath = userDetails.UserCredentials.ClientKey; + ClientCertificateFilePath = userDetails.UserCredentials.ClientCertificate; + ClientKeyFilePath = userDetails.UserCredentials.ClientKey; userCredentialsFound = true; } @@ -187,7 +210,7 @@ namespace k8s } /// - /// Loads Kube Config + /// Loads Kube Config /// /// Kube config file contents /// Instance of the class @@ -197,11 +220,10 @@ namespace k8s { 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(kubeconfigContent); + return deserializer.Deserialize(kubeconfig.OpenText()); } } -} +} diff --git a/tests/KubernetesClientConfigurationTests.cs b/tests/KubernetesClientConfigurationTests.cs index 62d24e7..e996c05 100755 --- a/tests/KubernetesClientConfigurationTests.cs +++ b/tests/KubernetesClientConfigurationTests.cs @@ -1,97 +1,26 @@ -using Xunit; using System.IO; +using k8s.Exceptions; +using Xunit; namespace k8s.Tests { public class KubernetesClientConfigurationTests { - - public static string readLine(string fileName) - { - StreamReader reader = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read)); - return reader.ReadLine(); - } - /// - /// This file contains a sample kubeconfig file - /// - private static readonly string kubeConfigFileName = "assets/kubeconfig.yml"; - - /// - /// Invalid test file with no context on purpose - /// - private static readonly string kubeConfigNoContexts = "assets/kubeconfig-no-context.yml"; - - /// - /// Sample configuration file with user/password authentication - /// - private static readonly string kubeConfigUserPassword = "assets/kubeconfig.user-pass.yml"; - - /// - /// Sample configuration file with incorrect user credentials structures on purpose - /// - private static readonly string kubeConfigNoCredentials = "assets/kubeconfig.no-credentials.yml"; - - /// - /// Sample configuration file with incorrect cluster/server structure on purpose - /// - private static readonly string kubeConfigNoServer = "assets/kubeconfig.no-server.yml"; - - /// - /// Sample configuration file with incorrect cluster/server structure on purpose - /// - private static readonly string kubeConfigNoCluster = "assets/kubeconfig.no-cluster.yml"; - - /// - /// Sample configuration file with incorrect match in cluster name - /// - private static readonly string kubeConfigClusterMissmatch = "assets/kubeconfig.cluster-missmatch.yml"; - - /// - /// Sample configuration file with incorrect TLS configuration in cluster section - /// - private static readonly string kubeConfigTlsNoSkipError = "assets/kubeconfig.tls-no-skip-error.yml"; - - /// - /// Sample configuration file with incorrect TLS configuration in cluster section - /// - private static readonly string kubeConfigTlsSkip = "assets/kubeconfig.tls-skip.yml"; - - /// - /// The configuration file is not present. An KubeConfigException should be thrown - /// - [Fact] - public void ConfigurationFileNotFound() - { - var fi = new FileInfo("/path/to/nowhere"); - Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); - } - - /// - /// Checks Host is loaded from the default configuration file - /// - [Fact] - public void DefaultConfigurationLoaded() - { - var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(new FileInfo(kubeConfigFileName)); - Assert.NotNull(cfg.Host); - } - - /// - /// Check if host is properly loaded, per context + /// Check if host is properly loaded, per context /// [Theory] [InlineData("federal-context", "https://horse.org:4443")] [InlineData("queen-anne-context", "https://pig.org:443")] public void ContextHost(string context, string host) { - var fi = new FileInfo(kubeConfigFileName); + var fi = new FileInfo("assets/kubeconfig.yml"); var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, context); Assert.Equal(host, cfg.Host); } /// - /// Checks if user-based token is loaded properly from the config file, per context + /// Checks if user-based token is loaded properly from the config file, per context /// /// /// @@ -99,7 +28,7 @@ namespace k8s.Tests [InlineData("queen-anne-context", "black-token")] public void ContextUserToken(string context, string token) { - var fi = new FileInfo(kubeConfigFileName); + var fi = new FileInfo("assets/kubeconfig.yml"); var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, context); Assert.Equal(context, cfg.CurrentContext); Assert.Null(cfg.Username); @@ -107,16 +36,16 @@ namespace k8s.Tests } /// - /// Checks if certificate-based authentication is loaded properly from the config file, per context + /// Checks if certificate-based authentication is loaded properly from the config file, per context /// /// Context to retreive the configuration - /// 'client-certificate-data' node content + /// 'client-certificate-data' node content /// 'client-key-data' content [Theory] [InlineData("federal-context", "assets/client.crt", "assets/client.key")] - public void ContextCertificateTest(string context, string clientCert, string clientCertKey) + public void ContextCertificate(string context, string clientCert, string clientCertKey) { - var fi = new FileInfo(kubeConfigFileName); + var fi = new FileInfo("assets/kubeconfig.yml"); var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, context); Assert.Equal(context, cfg.CurrentContext); Assert.Equal(cfg.ClientCertificateFilePath, clientCert); @@ -124,138 +53,211 @@ namespace k8s.Tests } /// - /// Checks if certificate-based authentication is loaded properly from the config file, per context + /// Checks if certificate-based authentication is loaded properly from the config file, per context /// /// Context to retreive the configuration [Theory] [InlineData("victorian-context")] public void ClientData(string context) { - var fi = new FileInfo(kubeConfigFileName); + var fi = new FileInfo("assets/kubeconfig.yml"); var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, context); Assert.Equal(context, cfg.CurrentContext); Assert.NotNull(cfg.SslCaCert); - Assert.Equal(readLine("assets/client-certificate-data.txt"), cfg.ClientCertificateData); - Assert.Equal(readLine("assets/client-key-data.txt"), cfg.ClientCertificateKeyData); - } - - - /// - /// Test that an Exception is thrown when initializating a KubernetClientConfiguration whose config file Context is not present - /// - [Fact] - public void ContextNotFound() - { - var fi = new FileInfo(kubeConfigFileName); - Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, "context-not-found")); + Assert.Equal(File.ReadAllText("assets/client-certificate-data.txt"), cfg.ClientCertificateData); + Assert.Equal(File.ReadAllText("assets/client-key-data.txt"), cfg.ClientCertificateKeyData); } /// - /// Test if KubeConfigException is thrown when no Contexts and we use the default context name - /// - [Fact] - public void NoContexts() - { - var fi = new FileInfo(kubeConfigNoContexts); - Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); - } - - /// - /// Test if KubeConfigException is thrown when no Contexts are set and we specify a concrete context name - /// - [Fact] - public void NoContextsExplicit() - { - var fi = new FileInfo(kubeConfigNoContexts); - Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, "context")); - } - - /// - /// Checks user/password authentication information is read properly - /// - [Fact] - public void UserPasswordAuthentication() - { - var fi = new FileInfo(kubeConfigUserPassword); - var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi); - Assert.Equal("admin", cfg.Username); - Assert.Equal("secret", cfg.Password); - } - - /// - /// Checks that a KubeConfigException is thrown when incomplete user credentials - /// - [Fact] - public void IncompleteUserCredentials() - { - var fi = new FileInfo(kubeConfigNoCredentials); - Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); - } - - /// - /// Checks that a KubeConfigException is thrown when the server property is not set in cluster - /// - [Fact] - public void ServerNotFound() - { - var fi = new FileInfo(kubeConfigNoServer); - Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); - } - - /// - /// Checks that a KubeConfigException is thrown when the clusters section is missing - /// - [Fact] - public void ClusterNotFound() - { - var fi = new FileInfo(kubeConfigNoCluster); - Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); - } - - /// - /// Checks that a KubeConfigException is thrown when the cluster defined in clusters and contexts do not match - /// - [Fact] - public void ClusterNameMissmatch() - { - var fi = new FileInfo(kubeConfigClusterMissmatch); - Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); - } - - /// - /// Checks that a KubeConfigException is thrown when no certificate-authority-data is set and user do not require tls skip + /// Checks that a KubeConfigException is thrown when no certificate-authority-data is set and user do not require tls + /// skip /// [Fact] public void CheckClusterTlsCorrectness() { - var fi = new FileInfo(kubeConfigTlsNoSkipError); - Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); + var fi = new FileInfo("assets/kubeconfig.tls-no-skip-error.yml"); + Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); } /// - /// Checks that a KubeConfigException is thrown when no certificate-authority-data is set and user do not require tls skip + /// Checks that a KubeConfigException is thrown when no certificate-authority-data is set and user do not require tls + /// skip /// [Fact] public void CheckClusterTlsSkipCorrectness() { - var fi = new FileInfo(kubeConfigTlsSkip); + var fi = new FileInfo("assets/kubeconfig.tls-skip.yml"); var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi); Assert.NotNull(cfg.Host); Assert.Null(cfg.SslCaCert); Assert.True(cfg.SkipTlsVerify); } - // /// - // /// Checks if the are pods - // /// - // [Fact] - // public void ListDefaultNamespacedPod() - // { - // var k8sClientConfig = KubernetesClientConfiguration.BuildConfigFromConfigFile(); - // IKubernetes client = new Kubernetes(k8sClientConfig); - // var listTask = client.ListNamespacedPodWithHttpMessagesAsync("default").Result; - // var list = listTask.Body; - // Assert.NotEqual(0, list.Items.Count); - // } + /// + /// Checks that a KubeConfigException is thrown when the cluster defined in clusters and contexts do not match + /// + [Fact] + public void ClusterNameMissmatch() + { + var fi = new FileInfo("assets/kubeconfig.cluster-missmatch.yml"); + Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); + } + + /// + /// Checks that a KubeConfigException is thrown when the clusters section is missing + /// + [Fact] + public void ClusterNotFound() + { + var fi = new FileInfo("assets/kubeconfig.no-cluster.yml"); + Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); + } + + /// + /// The configuration file is not present. An KubeConfigException should be thrown + /// + [Fact] + public void ConfigurationFileNotFound() + { + var fi = new FileInfo("/path/to/nowhere"); + Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); + } + + + /// + /// Test that an Exception is thrown when initializating a KubernetClientConfiguration whose config file Context is not + /// present + /// + [Fact] + public void ContextNotFound() + { + var fi = new FileInfo("assets/kubeconfig.yml"); + Assert.Throws(() => + KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, "context-not-found")); + } + + /// + /// Checks Host is loaded from the default configuration file + /// + [Fact] + public void DefaultConfigurationLoaded() + { + var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(new FileInfo("assets/kubeconfig.yml")); + Assert.NotNull(cfg.Host); + } + + /// + /// Checks that a KubeConfigException is thrown when incomplete user credentials + /// + [Fact] + public void IncompleteUserCredentials() + { + var fi = new FileInfo("assets/kubeconfig.no-credentials.yml"); + Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); + } + + /// + /// Test if KubeConfigException is thrown when no Contexts and we use the default context name + /// + [Fact] + public void NoContexts() + { + var fi = new FileInfo("assets/kubeconfig.no-context.yml"); + Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); + } + + /// + /// Test if KubeConfigException is thrown when no Contexts are set and we specify a concrete context name + /// + [Fact] + public void NoContextsExplicit() + { + var fi = new FileInfo("assets/kubeconfig-no-context.yml"); + Assert.Throws(() => + KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, "context")); + } + + /// + /// Checks that a KubeConfigException is thrown when the server property is not set in cluster + /// + [Fact] + public void ServerNotFound() + { + var fi = new FileInfo("assets/kubeconfig.no-server.yml"); + Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); + } + + /// + /// Checks user/password authentication information is read properly + /// + [Fact] + public void UserPasswordAuthentication() + { + var fi = new FileInfo("assets/kubeconfig.user-pass.yml"); + var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi); + Assert.Equal("admin", cfg.Username); + Assert.Equal("secret", cfg.Password); + } + + /// + /// Checks that a KubeConfigException is thrown when user cannot be found in users + /// + [Fact] + public void UserNotFound() + { + var fi = new FileInfo("assets/kubeconfig.user-not-found.yml"); + Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); + } + + /// + /// Make sure that user is not a necessary field. set #issue 24 + /// + [Fact] + public void EmptyUserNotFound() + { + var fi = new FileInfo("assets/kubeconfig.no-user.yml"); + var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi); + + Assert.NotEmpty(cfg.Host); + } + + /// + /// Make sure Host is replaced by masterUrl + /// + [Fact] + public void OverrideByMasterUrl() + { + var fi = new FileInfo("assets/kubeconfig.yml"); + var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, masterUrl: "http://test.server"); + Assert.Equal("http://test.server", cfg.Host); + } + + /// + /// Make sure that http urls are loaded even if insecure-skip-tls-verify === true + /// + [Fact] + public void SmartSkipTlsVerify() + { + var fi = new FileInfo("assets/kubeconfig.tls-skip-http.yml"); + var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi); + Assert.False(cfg.SkipTlsVerify); + Assert.Equal("http://horse.org", cfg.Host); + } + + /// + /// Checks config could work well when current-context is not set but masterUrl is set. #issue 24 + /// + [Fact] + public void NoCurrentContext() + { + var fi = new FileInfo("assets/kubeconfig.no-current-context.yml"); + + // failed if cannot infer any server host + Assert.Throws(() => KubernetesClientConfiguration.BuildConfigFromConfigFile(fi)); + + // survive when masterUrl is set + var cfg = KubernetesClientConfiguration.BuildConfigFromConfigFile(fi, masterUrl: "http://test.server"); + Assert.Equal("http://test.server", cfg.Host); + } } } diff --git a/tests/assets/kubeconfig-no-context.yml b/tests/assets/kubeconfig.no-context.yml similarity index 100% rename from tests/assets/kubeconfig-no-context.yml rename to tests/assets/kubeconfig.no-context.yml diff --git a/tests/assets/kubeconfig.no-current-context.yml b/tests/assets/kubeconfig.no-current-context.yml new file mode 100644 index 0000000..2dd066f --- /dev/null +++ b/tests/assets/kubeconfig.no-current-context.yml @@ -0,0 +1,19 @@ +--- +apiVersion: v1 +clusters: +- cluster: + certificate-authority: assets/ca.crt + server: https://horse.org:4443 + name: horse-cluster +contexts: +- context: + cluster: horse-cluster + namespace: chisel-ns + user: green-user + name: federal-context +kind: Config +users: +- name: green-user + user: + password: secret + username: admin \ No newline at end of file diff --git a/tests/assets/kubeconfig.no-user.yml b/tests/assets/kubeconfig.no-user.yml new file mode 100644 index 0000000..3fdbc2c --- /dev/null +++ b/tests/assets/kubeconfig.no-user.yml @@ -0,0 +1,14 @@ +--- +current-context: federal-context +apiVersion: v1 +clusters: +- cluster: + certificate-authority: assets/ca.crt + server: https://horse.org:4443 + name: horse-cluster +contexts: +- context: + cluster: horse-cluster + namespace: chisel-ns + name: federal-context +kind: Config \ No newline at end of file diff --git a/tests/assets/kubeconfig.tls-skip-http.yml b/tests/assets/kubeconfig.tls-skip-http.yml new file mode 100644 index 0000000..55fd56a --- /dev/null +++ b/tests/assets/kubeconfig.tls-skip-http.yml @@ -0,0 +1,19 @@ +--- +current-context: federal-context +apiVersion: v1 +clusters: +- cluster: + server: http://horse.org + name: horse-cluster +contexts: +- context: + cluster: horse-cluster + namespace: chisel-ns + user: green-user + name: federal-context +kind: Config +users: +- name: green-user + user: + password: secret + username: admin \ No newline at end of file diff --git a/tests/assets/kubeconfig.user-not-found.yml b/tests/assets/kubeconfig.user-not-found.yml new file mode 100644 index 0000000..39b79f6 --- /dev/null +++ b/tests/assets/kubeconfig.user-not-found.yml @@ -0,0 +1,15 @@ +--- +current-context: federal-context +apiVersion: v1 +clusters: +- cluster: + certificate-authority: assets/ca.crt + server: https://horse.org:4443 + name: horse-cluster +contexts: +- context: + cluster: horse-cluster + namespace: chisel-ns + user: green-user + name: federal-context +kind: Config \ No newline at end of file