2019-02-12 18:07:00 +13:00
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2018-03-12 14:55:35 -07:00
|
|
|
using System.IO;
|
2019-02-12 18:07:00 +13:00
|
|
|
using System.Linq;
|
2018-03-31 07:02:29 +02:00
|
|
|
using System.Text;
|
2018-03-12 14:55:35 -07:00
|
|
|
using System.Threading.Tasks;
|
2018-03-31 07:02:29 +02:00
|
|
|
using YamlDotNet.Core;
|
|
|
|
|
using YamlDotNet.Core.Events;
|
2018-03-12 14:55:35 -07:00
|
|
|
using YamlDotNet.Serialization;
|
|
|
|
|
using YamlDotNet.Serialization.NamingConventions;
|
2019-09-30 00:47:38 -07:00
|
|
|
using k8s.Models;
|
2018-03-12 14:55:35 -07:00
|
|
|
|
|
|
|
|
namespace k8s
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This is a utility class that helps you load objects from YAML files.
|
|
|
|
|
/// </summary>
|
2020-04-23 11:40:06 -07:00
|
|
|
public static class Yaml
|
2020-04-22 12:15:45 -07:00
|
|
|
{
|
2020-09-28 02:42:48 -07:00
|
|
|
public class ByteArrayStringYamlConverter : IYamlTypeConverter
|
|
|
|
|
{
|
|
|
|
|
public bool Accepts(Type type)
|
|
|
|
|
{
|
2020-10-23 08:31:57 -07:00
|
|
|
return type == typeof(byte[]);
|
2020-09-28 02:42:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object ReadYaml(IParser parser, Type type)
|
|
|
|
|
{
|
2020-11-22 14:52:09 -08:00
|
|
|
if (parser?.Current is Scalar scalar)
|
2020-09-28 02:42:48 -07:00
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(scalar.Value))
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Encoding.UTF8.GetBytes(scalar.Value);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
parser.MoveNext();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new InvalidOperationException(parser.Current?.ToString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void WriteYaml(IEmitter emitter, object value, Type type)
|
|
|
|
|
{
|
2020-10-23 08:31:57 -07:00
|
|
|
var obj = (byte[])value;
|
2020-11-22 14:52:09 -08:00
|
|
|
emitter?.Emit(new Scalar(Encoding.UTF8.GetString(obj)));
|
2020-09-28 02:42:48 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-10 13:59:37 -08:00
|
|
|
/// <summary>
|
|
|
|
|
/// Load a collection of objects from a stream asynchronously
|
2021-02-24 21:35:24 -08:00
|
|
|
///
|
|
|
|
|
/// caller is responsible for closing the stream
|
2020-01-10 13:59:37 -08:00
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="stream">
|
|
|
|
|
/// The stream to load the objects from.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="typeMap">
|
2020-11-22 14:52:09 -08:00
|
|
|
/// A map from apiVersion/kind to Type. For example "v1/Pod" -> typeof(V1Pod)
|
2020-01-10 13:59:37 -08:00
|
|
|
/// </param>
|
2020-11-22 14:52:09 -08:00
|
|
|
/// <returns>collection of objects</returns>
|
2020-10-23 08:31:57 -07:00
|
|
|
public static async Task<List<object>> LoadAllFromStreamAsync(Stream stream, Dictionary<string, Type> typeMap)
|
2020-04-22 12:15:45 -07:00
|
|
|
{
|
2020-01-10 13:59:37 -08:00
|
|
|
var reader = new StreamReader(stream);
|
2020-03-19 04:54:44 +00:00
|
|
|
var content = await reader.ReadToEndAsync().ConfigureAwait(false);
|
2020-01-10 13:59:37 -08:00
|
|
|
return LoadAllFromString(content, typeMap);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-22 14:52:09 -08:00
|
|
|
|
2020-01-10 13:59:37 -08:00
|
|
|
/// <summary>
|
|
|
|
|
/// Load a collection of objects from a file asynchronously
|
|
|
|
|
/// </summary>
|
2020-11-22 14:52:09 -08:00
|
|
|
/// <param name="fileName">The name of the file to load from.</param>
|
|
|
|
|
/// <param name="typeMap">A map from apiVersion/kind to Type. For example "v1/Pod" -> typeof(V1Pod)</param>
|
|
|
|
|
/// <returns>collection of objects</returns>
|
2021-02-27 17:21:43 +10:00
|
|
|
public static async Task<List<object>> LoadAllFromFileAsync(string fileName, Dictionary<string, Type> typeMap)
|
2020-01-10 13:59:37 -08:00
|
|
|
{
|
2021-02-27 17:21:43 +10:00
|
|
|
using (var fileStream = File.OpenRead(fileName))
|
2021-02-24 21:35:24 -08:00
|
|
|
{
|
2021-02-27 17:21:43 +10:00
|
|
|
return await LoadAllFromStreamAsync(fileStream, typeMap).ConfigureAwait(false);
|
2021-02-24 21:35:24 -08:00
|
|
|
}
|
2020-01-10 13:59:37 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Load a collection of objects from a string
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="content">
|
|
|
|
|
/// The string to load the objects from.
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="typeMap">
|
2020-11-22 14:52:09 -08:00
|
|
|
/// A map from apiVersion/kind to Type. For example "v1/Pod" -> typeof(V1Pod)
|
2020-01-10 13:59:37 -08:00
|
|
|
/// </param>
|
2020-11-22 14:52:09 -08:00
|
|
|
/// <returns>collection of objects</returns>
|
2020-10-23 08:31:57 -07:00
|
|
|
public static List<object> LoadAllFromString(string content, Dictionary<string, Type> typeMap)
|
2020-04-22 12:15:45 -07:00
|
|
|
{
|
2020-11-22 14:52:09 -08:00
|
|
|
if (typeMap == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(typeMap));
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-10 13:59:37 -08:00
|
|
|
var deserializer =
|
|
|
|
|
new DeserializerBuilder()
|
2020-11-22 14:52:09 -08:00
|
|
|
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
2020-01-10 13:59:37 -08:00
|
|
|
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
|
|
|
|
|
.WithTypeConverter(new IntOrStringYamlConverter())
|
2020-09-28 02:42:48 -07:00
|
|
|
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
2020-01-10 13:59:37 -08:00
|
|
|
.IgnoreUnmatchedProperties()
|
|
|
|
|
.Build();
|
|
|
|
|
var types = new List<Type>();
|
|
|
|
|
var parser = new Parser(new StringReader(content));
|
2020-11-22 14:52:09 -08:00
|
|
|
parser.Consume<StreamStart>();
|
|
|
|
|
while (parser.Accept<DocumentStart>(out _))
|
2020-04-22 12:15:45 -07:00
|
|
|
{
|
2020-01-10 13:59:37 -08:00
|
|
|
var obj = deserializer.Deserialize<KubernetesObject>(parser);
|
|
|
|
|
types.Add(typeMap[obj.ApiVersion + "/" + obj.Kind]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
deserializer =
|
|
|
|
|
new DeserializerBuilder()
|
2020-11-22 14:52:09 -08:00
|
|
|
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
2020-01-10 13:59:37 -08:00
|
|
|
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
|
|
|
|
|
.WithTypeConverter(new IntOrStringYamlConverter())
|
2020-09-28 02:42:48 -07:00
|
|
|
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
2020-01-10 13:59:37 -08:00
|
|
|
.Build();
|
|
|
|
|
parser = new Parser(new StringReader(content));
|
2020-11-22 14:52:09 -08:00
|
|
|
parser.Consume<StreamStart>();
|
2020-01-10 13:59:37 -08:00
|
|
|
var ix = 0;
|
|
|
|
|
var results = new List<object>();
|
2020-11-22 14:52:09 -08:00
|
|
|
while (parser.Accept<DocumentStart>(out _))
|
2020-04-22 12:15:45 -07:00
|
|
|
{
|
2020-01-10 13:59:37 -08:00
|
|
|
var objType = types[ix++];
|
|
|
|
|
var obj = deserializer.Deserialize(parser, objType);
|
|
|
|
|
results.Add(obj);
|
|
|
|
|
}
|
2020-04-23 11:40:06 -07:00
|
|
|
|
2020-01-10 13:59:37 -08:00
|
|
|
return results;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 12:15:45 -07:00
|
|
|
public static async Task<T> LoadFromStreamAsync<T>(Stream stream)
|
|
|
|
|
{
|
2018-03-12 14:55:35 -07:00
|
|
|
var reader = new StreamReader(stream);
|
2020-03-19 04:54:44 +00:00
|
|
|
var content = await reader.ReadToEndAsync().ConfigureAwait(false);
|
2018-03-12 14:55:35 -07:00
|
|
|
return LoadFromString<T>(content);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 12:15:45 -07:00
|
|
|
public static async Task<T> LoadFromFileAsync<T>(string file)
|
|
|
|
|
{
|
2020-11-22 14:52:09 -08:00
|
|
|
using (var fs = File.OpenRead(file))
|
2020-04-22 12:15:45 -07:00
|
|
|
{
|
2020-03-19 04:54:44 +00:00
|
|
|
return await LoadFromStreamAsync<T>(fs).ConfigureAwait(false);
|
2018-03-12 14:55:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 12:15:45 -07:00
|
|
|
public static T LoadFromString<T>(string content)
|
|
|
|
|
{
|
2018-03-31 07:02:29 +02:00
|
|
|
var deserializer =
|
2018-03-12 14:55:35 -07:00
|
|
|
new DeserializerBuilder()
|
2020-11-22 14:52:09 -08:00
|
|
|
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
2020-04-23 11:40:06 -07:00
|
|
|
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
|
|
|
|
|
.WithTypeConverter(new IntOrStringYamlConverter())
|
2020-09-28 02:42:48 -07:00
|
|
|
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
2020-04-23 11:40:06 -07:00
|
|
|
.Build();
|
2018-03-12 14:55:35 -07:00
|
|
|
var obj = deserializer.Deserialize<T>(content);
|
|
|
|
|
return obj;
|
|
|
|
|
}
|
2018-03-31 07:02:29 +02:00
|
|
|
|
|
|
|
|
public static string SaveToString<T>(T value)
|
|
|
|
|
{
|
|
|
|
|
var stringBuilder = new StringBuilder();
|
|
|
|
|
var writer = new StringWriter(stringBuilder);
|
|
|
|
|
var emitter = new Emitter(writer);
|
|
|
|
|
|
|
|
|
|
var serializer =
|
|
|
|
|
new SerializerBuilder()
|
2020-04-23 11:40:06 -07:00
|
|
|
.DisableAliases()
|
2020-11-22 14:52:09 -08:00
|
|
|
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
2020-04-23 11:40:06 -07:00
|
|
|
.WithTypeInspector(ti => new AutoRestTypeInspector(ti))
|
|
|
|
|
.WithTypeConverter(new IntOrStringYamlConverter())
|
2020-09-28 02:42:48 -07:00
|
|
|
.WithTypeConverter(new ByteArrayStringYamlConverter())
|
2020-05-11 16:12:21 -05:00
|
|
|
.WithEventEmitter(e => new StringQuotingEmitter(e))
|
2020-10-27 09:22:00 -07:00
|
|
|
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
|
2020-04-23 11:40:06 -07:00
|
|
|
.BuildValueSerializer();
|
2018-03-31 07:02:29 +02:00
|
|
|
emitter.Emit(new StreamStart());
|
|
|
|
|
emitter.Emit(new DocumentStart());
|
|
|
|
|
serializer.SerializeValue(emitter, value, typeof(T));
|
|
|
|
|
|
|
|
|
|
return stringBuilder.ToString();
|
|
|
|
|
}
|
2019-02-12 18:07:00 +13:00
|
|
|
|
|
|
|
|
private class AutoRestTypeInspector : ITypeInspector
|
|
|
|
|
{
|
|
|
|
|
private readonly ITypeInspector _inner;
|
|
|
|
|
|
|
|
|
|
public AutoRestTypeInspector(ITypeInspector inner)
|
|
|
|
|
{
|
|
|
|
|
_inner = inner;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
|
|
|
|
|
{
|
|
|
|
|
var pds = _inner.GetProperties(type, container);
|
|
|
|
|
return pds.Select(pd => TrimPropertySuffix(pd, type)).ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IPropertyDescriptor GetProperty(Type type, object container, string name, bool ignoreUnmatched)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
return _inner.GetProperty(type, container, name, ignoreUnmatched);
|
|
|
|
|
}
|
|
|
|
|
catch (System.Runtime.Serialization.SerializationException)
|
|
|
|
|
{
|
|
|
|
|
return _inner.GetProperty(type, container, name + "Property", ignoreUnmatched);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IPropertyDescriptor TrimPropertySuffix(IPropertyDescriptor pd, Type type)
|
|
|
|
|
{
|
2020-11-22 14:52:09 -08:00
|
|
|
if (!pd.Name.EndsWith("Property", StringComparison.InvariantCulture))
|
2019-02-12 18:07:00 +13:00
|
|
|
{
|
|
|
|
|
return pd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This might have been renamed by AutoRest. See if there is a
|
|
|
|
|
// JsonPropertyAttribute.PropertyName and use that instead if there is.
|
|
|
|
|
var jpa = pd.GetCustomAttribute<JsonPropertyAttribute>();
|
2020-10-23 08:31:57 -07:00
|
|
|
if (jpa == null || string.IsNullOrEmpty(jpa.PropertyName))
|
2019-02-12 18:07:00 +13:00
|
|
|
{
|
|
|
|
|
return pd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new RenamedPropertyDescriptor(pd, jpa.PropertyName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class RenamedPropertyDescriptor : IPropertyDescriptor
|
|
|
|
|
{
|
|
|
|
|
private readonly IPropertyDescriptor _inner;
|
|
|
|
|
private readonly string _name;
|
|
|
|
|
|
|
|
|
|
public RenamedPropertyDescriptor(IPropertyDescriptor inner, string name)
|
|
|
|
|
{
|
|
|
|
|
_inner = inner;
|
|
|
|
|
_name = name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Name => _name;
|
|
|
|
|
|
|
|
|
|
public bool CanWrite => _inner.CanWrite;
|
|
|
|
|
|
|
|
|
|
public Type Type => _inner.Type;
|
|
|
|
|
|
2020-04-23 11:40:06 -07:00
|
|
|
public Type TypeOverride
|
|
|
|
|
{
|
|
|
|
|
get => _inner.TypeOverride;
|
|
|
|
|
set => _inner.TypeOverride = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Order
|
|
|
|
|
{
|
|
|
|
|
get => _inner.Order;
|
|
|
|
|
set => _inner.Order = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ScalarStyle ScalarStyle
|
|
|
|
|
{
|
|
|
|
|
get => _inner.ScalarStyle;
|
|
|
|
|
set => _inner.ScalarStyle = value;
|
|
|
|
|
}
|
2019-02-12 18:07:00 +13:00
|
|
|
|
2020-11-01 12:24:51 -08:00
|
|
|
public T GetCustomAttribute<T>()
|
|
|
|
|
where T : Attribute
|
2019-02-12 18:07:00 +13:00
|
|
|
{
|
|
|
|
|
return _inner.GetCustomAttribute<T>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IObjectDescriptor Read(object target)
|
|
|
|
|
{
|
|
|
|
|
return _inner.Read(target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Write(object target, object value)
|
|
|
|
|
{
|
|
|
|
|
_inner.Write(target, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-12 14:55:35 -07:00
|
|
|
}
|
|
|
|
|
}
|