Last active
August 25, 2018 06:19
-
-
Save tintoy/49b7c400fb89325c3a914667af4427b5 to your computer and use it in GitHub Desktop.
Discover available Kubernetes APIs
This file contains 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
using HTTPlease; | |
using KubeClient; | |
using KubeClient.Models; | |
using KubeClient.ResourceClients; | |
using Newtonsoft.Json; | |
using Serilog; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Net.Http; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace K8sApiExplorer | |
{ | |
/// <summary> | |
/// Discover available Kubernetes APIs. | |
/// </summary> | |
static class Program | |
{ | |
/// <summary> | |
/// The main program entry-point. | |
/// </summary> | |
static async Task Main() | |
{ | |
ConfigureLogging(); | |
try | |
{ | |
Log.Information("Scanning K8s API..."); | |
var pathsByKind = new Dictionary<string, List<string>>(); | |
using (KubeApiClient client = KubeApiClient.Create("http://127.0.0.1:8001/")) | |
{ | |
ApiGroupClientV1 apiGroupClient = client.ResourceClient<ApiGroupClientV1>( | |
kubeClient => new ApiGroupClientV1(kubeClient) | |
); | |
ApiResourceClientV1 apiClient = client.ResourceClient<ApiResourceClientV1>( | |
kubeClient => new ApiResourceClientV1(kubeClient) | |
); | |
var apiGroupPrefixes = new string[] { "api", "apis" }; | |
foreach (string apiGroupPrefix in apiGroupPrefixes) | |
{ | |
APIGroupListV1 apiGroups = await apiGroupClient.List(apiGroupPrefix); | |
// Special case for (old-style) v1 APIs ("/api/v1", rather than "/apis/{groupName}/{groupVersion}"). | |
if (apiGroupPrefix == "api") | |
{ | |
apiGroups.Groups.Add(new APIGroupV1 | |
{ | |
Name = "Core", | |
PreferredVersion = new GroupVersionForDiscoveryV1 | |
{ | |
GroupVersion = "v1" | |
} | |
}); | |
} | |
Log.Information("Found {ApiGroupCount} API groups with prefix {ApiGroupPrefix}:", | |
apiGroups.Groups.Count, | |
apiGroupPrefix | |
); | |
foreach (APIGroupV1 apiGroup in apiGroups.Groups) | |
{ | |
Log.Information(" API Group {ApiGroupName}: {ApiPreferredVersion}", | |
apiGroup.Name, | |
apiGroup.PreferredVersion.GroupVersion | |
); | |
APIResourceListV1 apis = await apiClient.List(apiGroupPrefix, apiGroup.PreferredVersion.GroupVersion); | |
Log.Information(" Found {ApiCount} resource types in API group {GroupVersion}:", | |
apis.Resources.Count, | |
apiGroup.PreferredVersion.GroupVersion | |
); | |
foreach (var apisForKind in apis.Resources.GroupBy(api => api.Kind)) | |
{ | |
Log.Information(" Resource {ApiKind}:", | |
apisForKind.Key | |
); | |
foreach (APIResourceV1 api in apisForKind.OrderBy(api => api.Kind)) | |
{ | |
var kindAndVersion = $"{apiGroup.PreferredVersion.GroupVersion}/{api.Kind}"; | |
List<string> paths; | |
if (!pathsByKind.TryGetValue(kindAndVersion, out paths)) | |
{ | |
paths = new List<string>(); | |
pathsByKind.Add(kindAndVersion, paths); | |
} | |
string apiPath = $"{apiGroupPrefix}/{apiGroup.PreferredVersion.GroupVersion}/{api.Name}"; | |
Log.Information(" Path: {ApiPath}", apiPath); | |
paths.Add(apiPath); | |
if (api.Kind == "Pod") | |
Log.Information("API: {@Api}", api); | |
if (api.Verbs.Count > 0) | |
{ | |
Log.Information(" Verbs:"); | |
foreach (string verb in api.Verbs) | |
Log.Information(" {ApiVerb}", verb); | |
} | |
} | |
} | |
} | |
} | |
} | |
Log.Information("PathsByKind: {PathsByKind}", | |
JsonConvert.SerializeObject(pathsByKind, Formatting.Indented) | |
); | |
} | |
catch (Exception unexpectedError) | |
{ | |
Log.Error(unexpectedError, "Unexpected error."); | |
} | |
} | |
/// <summary> | |
/// Configure the global application logger. | |
/// </summary> | |
static void ConfigureLogging() | |
{ | |
var loggerConfiguration = new LoggerConfiguration() | |
.MinimumLevel.Information() | |
.WriteTo.LiterateConsole( | |
outputTemplate: "[{Level:u3}] {Message:l}{NewLine}{Exception}" | |
); | |
Log.Logger = loggerConfiguration.CreateLogger(); | |
} | |
/// <summary> | |
/// Global initialisation. | |
/// </summary> | |
static Program() | |
{ | |
SynchronizationContext.SetSynchronizationContext( | |
new SynchronizationContext() | |
); | |
} | |
class ApiGroupClientV1 | |
: KubeResourceClient | |
{ | |
public ApiGroupClientV1(IKubeApiClient client) | |
: base(client) | |
{ | |
} | |
public async Task<APIGroupListV1> List(string prefix, CancellationToken cancellationToken = default) | |
{ | |
if (String.IsNullOrWhiteSpace(prefix)) | |
throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'prefix'.", nameof(prefix)); | |
HttpRequest request = KubeRequest.Create("{Prefix}") | |
.WithTemplateParameter("Prefix", prefix); | |
using (HttpResponseMessage responseMessage = await Http.GetAsync(request, cancellationToken).ConfigureAwait(false)) | |
{ | |
if (responseMessage.IsSuccessStatusCode) | |
return await responseMessage.ReadContentAsAsync<APIGroupListV1>().ConfigureAwait(false); | |
throw new KubeClientException($"Failed to list API groups (HTTP status {responseMessage.StatusCode}).", | |
innerException: new HttpRequestException<StatusV1>(responseMessage.StatusCode, | |
response: await responseMessage.ReadContentAsAsync<StatusV1, StatusV1>().ConfigureAwait(false) | |
) | |
); | |
} | |
} | |
} | |
class ApiResourceClientV1 | |
: KubeResourceClient | |
{ | |
public ApiResourceClientV1(IKubeApiClient client) | |
: base(client) | |
{ | |
} | |
public async Task<APIResourceListV1> List(string prefix, string groupVersion, CancellationToken cancellationToken = default) | |
{ | |
if (String.IsNullOrWhiteSpace(groupVersion)) | |
throw new ArgumentException("Argument cannot be null, empty, or entirely composed of whitespace: 'groupVersion'.", nameof(groupVersion)); | |
HttpRequest request = KubeRequest.Create("{Prefix}/{GroupVersion}") | |
.WithTemplateParameters(new | |
{ | |
Prefix = prefix, | |
GroupVersion = groupVersion | |
}); | |
using (HttpResponseMessage responseMessage = await Http.GetAsync(request, cancellationToken).ConfigureAwait(false)) | |
{ | |
if (responseMessage.IsSuccessStatusCode) | |
return await responseMessage.ReadContentAsAsync<APIResourceListV1>().ConfigureAwait(false); | |
if (responseMessage.Content.Headers.ContentType.MediaType == "application/json") | |
{ | |
throw new KubeClientException($"Failed to list {groupVersion} APIs (HTTP status {responseMessage.StatusCode}).", | |
innerException: new HttpRequestException<StatusV1>(responseMessage.StatusCode, | |
response: await responseMessage.ReadContentAsAsync<StatusV1, StatusV1>().ConfigureAwait(false) | |
) | |
); | |
} | |
string responseBody = await responseMessage.Content.ReadAsStringAsync(); | |
throw new KubeClientException( | |
$"Failed to list {groupVersion} APIs (HTTP status {responseMessage.StatusCode}).\nResponse from server:\n{responseBody}" | |
); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment