Fix YAML serialization of ResourceQuantity values (#126)
* Fix YAML serialization of ResourceQuantity values * Address PR feedback
This commit is contained in:
committed by
Brendan Burns
parent
3f2f2696d9
commit
801455c4d8
@@ -4,14 +4,17 @@ using System.Linq;
|
||||
using System.Numerics;
|
||||
using Fractions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Core.Events;
|
||||
using YamlDotNet.Serialization;
|
||||
|
||||
namespace k8s.Models
|
||||
{
|
||||
internal class QuantityConverter : JsonConverter
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var q = (ResourceQuantity) value;
|
||||
var q = (ResourceQuantity)value;
|
||||
|
||||
if (q != null)
|
||||
{
|
||||
@@ -83,8 +86,8 @@ namespace k8s.Models
|
||||
/// writing some sort of special handling code in the hopes that that will
|
||||
/// cause implementors to also use a fixed point implementation.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(QuantityConverter))]
|
||||
public partial class ResourceQuantity
|
||||
[JsonConverter(typeof(QuantityConverter))]
|
||||
public partial class ResourceQuantity : IYamlConvertible
|
||||
{
|
||||
public enum SuffixFormat
|
||||
{
|
||||
@@ -175,8 +178,16 @@ namespace k8s.Models
|
||||
|
||||
// ctor
|
||||
partial void CustomInit()
|
||||
{
|
||||
var value = (Value ?? "").Trim();
|
||||
{
|
||||
if (Value == null)
|
||||
{
|
||||
// No value has been defined, initialize to 0.
|
||||
_unitlessValue = new Fraction(0);
|
||||
Format = SuffixFormat.BinarySI;
|
||||
return;
|
||||
}
|
||||
|
||||
var value = Value.Trim();
|
||||
|
||||
var si = value.IndexOfAny(SuffixChars);
|
||||
if (si == -1)
|
||||
@@ -204,8 +215,30 @@ namespace k8s.Models
|
||||
}
|
||||
|
||||
return BigInteger.Remainder(value.Numerator, value.Denominator) > 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer)
|
||||
{
|
||||
if (expectedType != typeof(ResourceQuantity))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(expectedType));
|
||||
}
|
||||
|
||||
if (parser.Current is Scalar)
|
||||
{
|
||||
Value = ((Scalar)parser.Current).Value;
|
||||
parser.MoveNext();
|
||||
CustomInit();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer)
|
||||
{
|
||||
emitter.Emit(new Scalar(this.ToString()));
|
||||
}
|
||||
|
||||
public static implicit operator decimal(ResourceQuantity v)
|
||||
{
|
||||
return v._unitlessValue.ToDecimal();
|
||||
@@ -298,32 +331,32 @@ namespace k8s.Models
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case SuffixFormat.DecimalExponent:
|
||||
{
|
||||
var minE = -9;
|
||||
var lastv = Roundup(value * Fraction.Pow(10, -minE));
|
||||
|
||||
for (var exp = minE;; exp += 3)
|
||||
{
|
||||
var v = value * Fraction.Pow(10, -exp);
|
||||
if (HasMantissa(v))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
minE = exp;
|
||||
lastv = v;
|
||||
case SuffixFormat.DecimalExponent:
|
||||
{
|
||||
var minE = -9;
|
||||
var lastv = Roundup(value * Fraction.Pow(10, -minE));
|
||||
|
||||
for (var exp = minE;; exp += 3)
|
||||
{
|
||||
var v = value * Fraction.Pow(10, -exp);
|
||||
if (HasMantissa(v))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
minE = exp;
|
||||
lastv = v;
|
||||
}
|
||||
|
||||
|
||||
if (minE == 0)
|
||||
{
|
||||
return $"{(decimal) lastv}";
|
||||
}
|
||||
|
||||
return $"{(decimal) lastv}e{minE}";
|
||||
}
|
||||
|
||||
|
||||
if (minE == 0)
|
||||
{
|
||||
return $"{(decimal) lastv}";
|
||||
}
|
||||
|
||||
return $"{(decimal) lastv}e{minE}";
|
||||
}
|
||||
|
||||
case SuffixFormat.BinarySI:
|
||||
return AppendMaxSuffix(value, BinSuffixes);
|
||||
case SuffixFormat.DecimalSI:
|
||||
|
||||
24
src/Yaml.cs
24
src/Yaml.cs
@@ -1,5 +1,8 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using YamlDotNet.Core;
|
||||
using YamlDotNet.Core.Events;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
@@ -17,18 +20,35 @@ namespace k8s
|
||||
}
|
||||
|
||||
public static async Task<T> LoadFromFileAsync<T> (string file) {
|
||||
using (FileStream fs = File.OpenRead(file)) {
|
||||
using (FileStream fs = File.OpenRead(file)) {
|
||||
return await LoadFromStreamAsync<T>(fs);
|
||||
}
|
||||
}
|
||||
|
||||
public static T LoadFromString<T>(string content) {
|
||||
var deserializer =
|
||||
var deserializer =
|
||||
new DeserializerBuilder()
|
||||
.WithNamingConvention(new CamelCaseNamingConvention())
|
||||
.Build();
|
||||
var obj = deserializer.Deserialize<T>(content);
|
||||
return obj;
|
||||
}
|
||||
|
||||
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()
|
||||
.WithNamingConvention(new CamelCaseNamingConvention())
|
||||
.BuildValueSerializer();
|
||||
emitter.Emit(new StreamStart());
|
||||
emitter.Emit(new DocumentStart());
|
||||
serializer.SerializeValue(emitter, value, typeof(T));
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,12 @@ namespace k8s.Tests
|
||||
Assert.ThrowsAny<Exception>(() => { new ResourceQuantity(s); });
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConstructorTest()
|
||||
{
|
||||
Assert.Throws<FormatException>(() => new ResourceQuantity(string.Empty));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void QuantityString()
|
||||
@@ -233,6 +239,20 @@ namespace k8s.Tests
|
||||
ResourceQuantity quantity = 12000;
|
||||
Assert.Equal("\"12e3\"", JsonConvert.SerializeObject(quantity));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeserializeYaml()
|
||||
{
|
||||
var value = Yaml.LoadFromString<ResourceQuantity>("\"1\"");
|
||||
Assert.Equal(new ResourceQuantity(1, 0, DecimalSI), value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SerializeYaml()
|
||||
{
|
||||
var value = Yaml.SaveToString(new ResourceQuantity(1, -1, DecimalSI));
|
||||
Assert.Equal("100m", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,5 +35,66 @@ metadata:
|
||||
Assert.Equal("foo", obj.Metadata.Name);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriteToString()
|
||||
{
|
||||
var pod = new V1Pod()
|
||||
{
|
||||
ApiVersion = "v1",
|
||||
Kind = "Pod",
|
||||
Metadata = new V1ObjectMeta()
|
||||
{
|
||||
Name = "foo"
|
||||
}
|
||||
};
|
||||
|
||||
var yaml = Yaml.SaveToString(pod);
|
||||
Assert.Equal(@"apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: foo", yaml);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CpuRequestAndLimitFromString()
|
||||
{
|
||||
// Taken from https://raw.githubusercontent.com/kubernetes/website/master/docs/tasks/configure-pod-container/cpu-request-limit.yaml, although
|
||||
// the 'namespace' property on 'metadata' was removed since it was rejected by the C# client.
|
||||
var content = @"apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: cpu-demo
|
||||
spec:
|
||||
containers:
|
||||
- name: cpu-demo-ctr
|
||||
image: vish/stress
|
||||
resources:
|
||||
limits:
|
||||
cpu: ""1""
|
||||
requests:
|
||||
cpu: ""0.5""
|
||||
args:
|
||||
- -cpus
|
||||
- ""2""";
|
||||
|
||||
var obj = Yaml.LoadFromString<V1Pod>(content);
|
||||
|
||||
Assert.NotNull(obj?.Spec?.Containers);
|
||||
var container = Assert.Single(obj.Spec.Containers);
|
||||
|
||||
Assert.NotNull(container.Resources);
|
||||
Assert.NotNull(container.Resources.Limits);
|
||||
Assert.NotNull(container.Resources.Requests);
|
||||
|
||||
var cpuLimit = Assert.Single(container.Resources.Limits);
|
||||
var cpuRequest = Assert.Single(container.Resources.Requests);
|
||||
|
||||
Assert.Equal("cpu", cpuLimit.Key);
|
||||
Assert.Equal("1", cpuLimit.Value.ToString());
|
||||
|
||||
Assert.Equal("cpu", cpuRequest.Key);
|
||||
Assert.Equal("500m", cpuRequest.Value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user