using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; 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 { private static readonly IDeserializer Deserializer = new DeserializerBuilder() .WithNamingConvention(CamelCaseNamingConvention.Instance) .WithTypeConverter(new IntOrStringYamlConverter()) .WithTypeConverter(new ByteArrayStringYamlConverter()) .WithOverridesFromJsonPropertyAttributes() .IgnoreUnmatchedProperties() .Build(); private static readonly IValueSerializer Serializer = new SerializerBuilder() .DisableAliases() .WithNamingConvention(CamelCaseNamingConvention.Instance) .WithTypeConverter(new IntOrStringYamlConverter()) .WithTypeConverter(new ByteArrayStringYamlConverter()) .WithEventEmitter(e => new StringQuotingEmitter(e)) .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) .WithOverridesFromJsonPropertyAttributes() .BuildValueSerializer(); public class ByteArrayStringYamlConverter : IYamlTypeConverter { public bool Accepts(Type type) { return type == typeof(byte[]); } public object ReadYaml(IParser parser, Type type) { if (parser?.Current is Scalar scalar) { 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) { var obj = (byte[])value; emitter?.Emit(new Scalar(Encoding.UTF8.GetString(obj))); } } /// /// Load a collection of objects from a stream asynchronously /// /// caller is responsible for closing the stream /// /// /// The stream to load the objects from. /// /// /// A map from apiVersion/kind to Type. For example "v1/Pod" -> typeof(V1Pod) /// /// collection of objects 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 apiVersion/kind to Type. For example "v1/Pod" -> typeof(V1Pod) /// collection of objects public static async Task> LoadAllFromFileAsync(string fileName, Dictionary typeMap) { using (var fileStream = File.OpenRead(fileName)) { return await LoadAllFromStreamAsync(fileStream, typeMap).ConfigureAwait(false); } } /// /// Load a collection of objects from a string /// /// /// The string to load the objects from. /// /// /// A map from apiVersion/kind to Type. For example "v1/Pod" -> typeof(V1Pod) /// /// collection of objects public static List LoadAllFromString(string content, Dictionary typeMap) { if (typeMap == null) { throw new ArgumentNullException(nameof(typeMap)); } var types = new List(); var parser = new Parser(new StringReader(content)); parser.Consume(); while (parser.Accept(out _)) { var obj = Deserializer.Deserialize(parser); types.Add(typeMap[obj.ApiVersion + "/" + obj.Kind]); } parser = new Parser(new StringReader(content)); parser.Consume(); var ix = 0; var results = new List(); while (parser.Accept(out _)) { 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 (var fs = File.OpenRead(file)) { return await LoadFromStreamAsync(fs).ConfigureAwait(false); } } public static T LoadFromString(string content) { 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); emitter.Emit(new StreamStart()); emitter.Emit(new DocumentStart()); Serializer.SerializeValue(emitter, value, typeof(T)); return stringBuilder.ToString(); } private static TBuilder WithOverridesFromJsonPropertyAttributes(this TBuilder builder) where TBuilder : BuilderSkeleton { // Use VersionInfo from the model namespace as that should be stable. // If this is not generated in the future we will get an obvious compiler error. var targetNamespace = typeof(VersionInfo).Namespace; // Get all the concrete model types from the code generated namespace. var types = typeof(KubernetesEntityAttribute).Assembly .ExportedTypes .Where(type => type.Namespace == targetNamespace && !type.IsInterface && !type.IsAbstract); // Map any JsonPropertyAttribute instances to YamlMemberAttribute instances. foreach (var type in types) { foreach (var property in type.GetProperties()) { var jsonAttribute = property.GetCustomAttribute(); if (jsonAttribute == null) { continue; } var yamlAttribute = new YamlMemberAttribute { Alias = jsonAttribute.PropertyName }; builder.WithAttributeOverride(type, property.Name, yamlAttribute); } } return builder; } } }