Fix kubeconfig extension handling (#556)
Extensions on kubeconfig files are stored as a list of NamedExtension objects, not a dictionary.
This commit is contained in:
@@ -37,6 +37,6 @@ namespace k8s.KubeConfigModels
|
|||||||
/// Gets or sets additional information. This is useful for extenders so that reads and writes don't clobber unknown fields.
|
/// Gets or sets additional information. This is useful for extenders so that reads and writes don't clobber unknown fields.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[YamlMember(Alias = "extensions")]
|
[YamlMember(Alias = "extensions")]
|
||||||
public IDictionary<string, dynamic> Extensions { get; set; }
|
public IEnumerable<NamedExtension> Extensions { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using YamlDotNet.Serialization;
|
using YamlDotNet.Serialization;
|
||||||
|
|
||||||
namespace k8s.KubeConfigModels
|
namespace k8s.KubeConfigModels
|
||||||
@@ -20,6 +21,13 @@ namespace k8s.KubeConfigModels
|
|||||||
[YamlMember(Alias = "name")]
|
[YamlMember(Alias = "name")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets additional information. This is useful for extenders so that reads and writes don't clobber unknown fields.
|
||||||
|
/// </summary>
|
||||||
|
[YamlMember(Alias = "extensions")]
|
||||||
|
public IEnumerable<NamedExtension> Extensions { get; set; }
|
||||||
|
|
||||||
|
|
||||||
[Obsolete("This property is not set by the YAML config. Use ContextDetails.Namespace instead.")]
|
[Obsolete("This property is not set by the YAML config. Use ContextDetails.Namespace instead.")]
|
||||||
[YamlMember(Alias = "namespace")]
|
[YamlMember(Alias = "namespace")]
|
||||||
public string Namespace { get; set; }
|
public string Namespace { get; set; }
|
||||||
|
|||||||
@@ -31,6 +31,6 @@ namespace k8s.KubeConfigModels
|
|||||||
/// Gets or sets additional information. This is useful for extenders so that reads and writes don't clobber unknown fields.
|
/// Gets or sets additional information. This is useful for extenders so that reads and writes don't clobber unknown fields.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[YamlMember(Alias = "extensions")]
|
[YamlMember(Alias = "extensions")]
|
||||||
public IDictionary<string, dynamic> Extensions { get; set; }
|
public IEnumerable<NamedExtension> Extensions { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace k8s.KubeConfigModels
|
|||||||
/// Gets or sets additional information. This is useful for extenders so that reads and writes don't clobber unknown fields.
|
/// Gets or sets additional information. This is useful for extenders so that reads and writes don't clobber unknown fields.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[YamlMember(Alias = "extensions")]
|
[YamlMember(Alias = "extensions")]
|
||||||
public IDictionary<string, dynamic> Extensions { get; set; }
|
public IEnumerable<NamedExtension> Extensions { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of the Kubernetes configuration file. This property is set only when the configuration
|
/// Gets or sets the name of the Kubernetes configuration file. This property is set only when the configuration
|
||||||
|
|||||||
22
src/KubernetesClient/KubeConfigModels/NamedExtension.cs
Normal file
22
src/KubernetesClient/KubeConfigModels/NamedExtension.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using YamlDotNet.Serialization;
|
||||||
|
|
||||||
|
namespace k8s.KubeConfigModels
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="NamedExtension"/> relates nicknames to extension information
|
||||||
|
/// </summary>
|
||||||
|
public class NamedExtension
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the nickname for this extension.
|
||||||
|
/// </summary>
|
||||||
|
[YamlMember(Alias = "name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get or sets the extension information.
|
||||||
|
/// </summary>
|
||||||
|
[YamlMember(Alias = "extension")]
|
||||||
|
public dynamic Extension { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,7 +78,7 @@ namespace k8s.KubeConfigModels
|
|||||||
/// Gets or sets additional information. This is useful for extenders so that reads and writes don't clobber unknown fields.
|
/// Gets or sets additional information. This is useful for extenders so that reads and writes don't clobber unknown fields.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[YamlMember(Alias = "extensions")]
|
[YamlMember(Alias = "extensions")]
|
||||||
public IDictionary<string, dynamic> Extensions { get; set; }
|
public IEnumerable<NamedExtension> Extensions { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets external command and its arguments to receive user credentials
|
/// Gets or sets external command and its arguments to receive user credentials
|
||||||
|
|||||||
@@ -773,19 +773,9 @@ namespace k8s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mergek8SConfig.Extensions != null)
|
|
||||||
{
|
|
||||||
foreach (var extension in mergek8SConfig.Extensions)
|
|
||||||
{
|
|
||||||
if (basek8SConfig.Extensions?.ContainsKey(extension.Key) == false)
|
|
||||||
{
|
|
||||||
basek8SConfig.Extensions[extension.Key] = extension.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note, Clusters, Contexts, and Extensions are map-like in config despite being represented as a list here:
|
// Note, Clusters, Contexts, and Extensions are map-like in config despite being represented as a list here:
|
||||||
// https://github.com/kubernetes/client-go/blob/ede92e0fe62deed512d9ceb8bf4186db9f3776ff/tools/clientcmd/api/types.go#L238
|
// https://github.com/kubernetes/client-go/blob/ede92e0fe62deed512d9ceb8bf4186db9f3776ff/tools/clientcmd/api/types.go#L238
|
||||||
|
basek8SConfig.Extensions = MergeLists(basek8SConfig.Extensions, mergek8SConfig.Extensions, (s) => s.Name);
|
||||||
basek8SConfig.Clusters = MergeLists(basek8SConfig.Clusters, mergek8SConfig.Clusters, (s) => s.Name);
|
basek8SConfig.Clusters = MergeLists(basek8SConfig.Clusters, mergek8SConfig.Clusters, (s) => s.Name);
|
||||||
basek8SConfig.Users = MergeLists(basek8SConfig.Users, mergek8SConfig.Users, (s) => s.Name);
|
basek8SConfig.Users = MergeLists(basek8SConfig.Users, mergek8SConfig.Users, (s) => s.Name);
|
||||||
basek8SConfig.Contexts = MergeLists(basek8SConfig.Contexts, mergek8SConfig.Contexts, (s) => s.Name);
|
basek8SConfig.Contexts = MergeLists(basek8SConfig.Contexts, mergek8SConfig.Contexts, (s) => s.Name);
|
||||||
|
|||||||
17
tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs
Executable file → Normal file
17
tests/KubernetesClient.Tests/KubernetesClientConfigurationTests.cs
Executable file → Normal file
@@ -1,10 +1,11 @@
|
|||||||
|
using k8s.Exceptions;
|
||||||
|
using k8s.KubeConfigModels;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using k8s.Exceptions;
|
using System.Threading.Tasks;
|
||||||
using k8s.KubeConfigModels;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace k8s.Tests
|
namespace k8s.Tests
|
||||||
@@ -372,6 +373,14 @@ namespace k8s.Tests
|
|||||||
Assert.NotNull(cfg.Host);
|
Assert.NotNull(cfg.Host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ContextWithClusterExtensions()
|
||||||
|
{
|
||||||
|
var path = Path.GetFullPath("assets/kubeconfig.cluster-extensions.yml");
|
||||||
|
|
||||||
|
var cfg = await KubernetesClientConfiguration.BuildConfigFromConfigFileAsync(new FileInfo(path)).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ensures Kube config file is loaded from explicit file
|
/// Ensures Kube config file is loaded from explicit file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -525,8 +534,8 @@ namespace k8s.Tests
|
|||||||
new FileInfo(path), new FileInfo(path),
|
new FileInfo(path), new FileInfo(path),
|
||||||
});
|
});
|
||||||
|
|
||||||
Assert.Equal(1, cfg.Extensions.Count);
|
Assert.Single(cfg.Extensions);
|
||||||
Assert.Equal(1, cfg.Preferences.Count);
|
Assert.Single(cfg.Preferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
clusters:
|
||||||
|
- cluster:
|
||||||
|
extensions:
|
||||||
|
- extension:
|
||||||
|
last-update: Wed, 27 Jan 2021 11:44:41 UTC
|
||||||
|
provider: minikube.sigs.k8s.io
|
||||||
|
version: v1.17.0
|
||||||
|
name: cluster_info
|
||||||
|
server: https://192.168.49.2:8443
|
||||||
|
name: minikube
|
||||||
|
contexts:
|
||||||
|
- context:
|
||||||
|
cluster: minikube
|
||||||
|
extensions:
|
||||||
|
- extension:
|
||||||
|
last-update: Wed, 27 Jan 2021 11:44:41 UTC
|
||||||
|
provider: minikube.sigs.k8s.io
|
||||||
|
version: v1.17.0
|
||||||
|
name: context_info
|
||||||
|
namespace: default
|
||||||
|
user: minikube
|
||||||
|
name: minikube
|
||||||
|
current-context: minikube
|
||||||
|
kind: Config
|
||||||
|
preferences: {}
|
||||||
|
users:
|
||||||
|
- name: minikube
|
||||||
|
user:
|
||||||
|
password: secret
|
||||||
|
username: admin
|
||||||
@@ -3,4 +3,6 @@ kind: Config
|
|||||||
preferences:
|
preferences:
|
||||||
colors: true
|
colors: true
|
||||||
extensions:
|
extensions:
|
||||||
foo: bar
|
- name: foo
|
||||||
|
extension:
|
||||||
|
foo: bar
|
||||||
|
|||||||
Reference in New Issue
Block a user