using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; using k8s.Models; namespace k8s { /// /// This is a utility class that helps you load objects from YAML files. /// public static class Yaml { /// /// Load a collection of objects from a stream asynchronously /// /// /// The stream to load the objects from. /// /// /// A map from / to Type. For example "v1/Pod" -> typeof(V1Pod) /// public static async Task> LoadAllFromStreamAsync(Stream stream, Dictionary typeMap) { var reader = new StreamReader(stream); var content = await reader.ReadToEndAsync().ConfigureAwait(false); return LoadAllFromString(content, typeMap); } /// /// Load a collection of objects from a file asynchronously /// /// /// The name of the file to load from. /// /// /// A map from / to Type. For example "v1/Pod" -> typeof(V1Pod) /// public static Task> LoadAllFromFileAsync(String fileName, Dictionary typeMap) { var reader = File.OpenRead(fileName); return LoadAllFromStreamAsync(reader, typeMap); } /// /// Load a collection of objects from a string /// /// /// The string to load the objects from. /// /// /// A map from / to Type. For example "v1/Pod" -> typeof(V1Pod) /// public static List LoadAllFromString(String content, Dictionary typeMap) { var deserializer = new DeserializerBuilder() .WithNamingConvention(new CamelCaseNamingConvention()) .WithTypeInspector(ti => new AutoRestTypeInspector(ti)) .WithTypeConverter(new IntOrStringYamlConverter()) .IgnoreUnmatchedProperties() .Build(); var types = new List(); var parser = new Parser(new StringReader(content)); parser.Expect(); while (parser.Accept()) { var obj = deserializer.Deserialize(parser); types.Add(typeMap[obj.ApiVersion + "/" + obj.Kind]); } deserializer = new DeserializerBuilder() .WithNamingConvention(new CamelCaseNamingConvention()) .WithTypeInspector(ti => new AutoRestTypeInspector(ti)) .WithTypeConverter(new IntOrStringYamlConverter()) .Build(); parser = new Parser(new StringReader(content)); parser.Expect(); var ix = 0; var results = new List(); while (parser.Accept()) { var objType = types[ix++]; var obj = deserializer.Deserialize(parser, objType); results.Add(obj); } return results; } public static async Task LoadFromStreamAsync(Stream stream) { var reader = new StreamReader(stream); var content = await reader.ReadToEndAsync().ConfigureAwait(false); return LoadFromString(content); } public static async Task LoadFromFileAsync(string file) { using (FileStream fs = File.OpenRead(file)) { return await LoadFromStreamAsync(fs).ConfigureAwait(false); } } public static T LoadFromString(string content) { var deserializer = new DeserializerBuilder() .WithNamingConvention(new CamelCaseNamingConvention()) .WithTypeInspector(ti => new AutoRestTypeInspector(ti)) .WithTypeConverter(new IntOrStringYamlConverter()) .Build(); var obj = deserializer.Deserialize(content); return obj; } public static string SaveToString(T value) { var stringBuilder = new StringBuilder(); var writer = new StringWriter(stringBuilder); var emitter = new Emitter(writer); var serializer = new SerializerBuilder() .DisableAliases() .WithNamingConvention(new CamelCaseNamingConvention()) .WithTypeInspector(ti => new AutoRestTypeInspector(ti)) .WithTypeConverter(new IntOrStringYamlConverter()) .WithEventEmitter(e => new StringQuotingEmitter(e)) .BuildValueSerializer(); emitter.Emit(new StreamStart()); emitter.Emit(new DocumentStart()); serializer.SerializeValue(emitter, value, typeof(T)); return stringBuilder.ToString(); } private class AutoRestTypeInspector : ITypeInspector { private readonly ITypeInspector _inner; public AutoRestTypeInspector(ITypeInspector inner) { _inner = inner; } public IEnumerable 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) { if (!pd.Name.EndsWith("Property")) { 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(); if (jpa == null || String.IsNullOrEmpty(jpa.PropertyName)) { 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; 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; } public T GetCustomAttribute() where T : Attribute { return _inner.GetCustomAttribute(); } public IObjectDescriptor Read(object target) { return _inner.Read(target); } public void Write(object target, object value) { _inner.Write(target, value); } } } } }