From 9c4c0a0978c33d683b8b7e1cdc7400629d3fd9a3 Mon Sep 17 00:00:00 2001 From: Adam Milazzo Date: Tue, 7 Apr 2020 18:06:26 -0700 Subject: [PATCH 1/6] Add useful model extensions --- src/KubernetesClient/ModelExtensions.cs | 239 ++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 src/KubernetesClient/ModelExtensions.cs diff --git a/src/KubernetesClient/ModelExtensions.cs b/src/KubernetesClient/ModelExtensions.cs new file mode 100644 index 000000000..a8e853062 --- /dev/null +++ b/src/KubernetesClient/ModelExtensions.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; + +namespace k8s.Models +{ + public static class ModelExtensions + { + /// Extracts the Kubernetes API group from the . + public static string ApiGroup(this IKubernetesObject obj) + { + if(obj == null) throw new ArgumentNullException(nameof(obj)); + if(obj.ApiVersion == null) return null; + int slash = obj.ApiVersion.IndexOf('/'); + return slash < 0 ? string.Empty : obj.ApiVersion.Substring(0, slash); + } + + /// Extracts the Kubernetes API version (excluding the group) from the . + public static string ApiGroupVersion(this IKubernetesObject obj) + { + if(obj == null) throw new ArgumentNullException(nameof(obj)); + if(obj.ApiVersion == null) return null; + int slash = obj.ApiVersion.IndexOf('/'); + return slash < 0 ? obj.ApiVersion : obj.ApiVersion.Substring(slash+1); + } + + /// Splits the Kubernetes API version into the group and version. + public static (string, string) ApiGroupAndVersion(this IKubernetesObject obj) + { + string group, version; + obj.GetApiGroupAndVersion(out group, out version); + return (group, version); + } + + /// Splits the Kubernetes API version into the group and version. + public static void GetApiGroupAndVersion(this IKubernetesObject obj, out string group, out string version) + { + if(obj == null) throw new ArgumentNullException(nameof(obj)); + if(obj.ApiVersion == null) + { + group = version = null; + } + else + { + int slash = obj.ApiVersion.IndexOf('/'); + if(slash < 0) (group, version) = (string.Empty, obj.ApiVersion); + else (group, version) = (obj.ApiVersion.Substring(0, slash), obj.ApiVersion.Substring(slash+1)); + } + } + + /// Gets the continuation token version of a Kubernetes list. + public static string Continue(this IMetadata list) => list.Metadata?.ContinueProperty; + + /// Ensures that the metadata field is set, and returns it. + public static V1ListMeta EnsureMetadata(this IMetadata obj) + { + if(obj.Metadata == null) obj.Metadata = new V1ListMeta(); + return obj.Metadata; + } + + /// Gets the resource version of a Kubernetes list. + public static string ResourceVersion(this IMetadata list) => list.Metadata?.ResourceVersion; + + /// Adds an owner reference to the object. No attempt is made to ensure the reference is correct or fits with the + /// other references. + /// + public static void AddOwnerReference(this IMetadata obj, V1OwnerReference ownerRef) + { + if(obj == null) throw new ArgumentNullException(nameof(obj)); + if(ownerRef == null) throw new ArgumentNullException(nameof(ownerRef)); + if(obj.EnsureMetadata().OwnerReferences == null) obj.Metadata.OwnerReferences = new List(); + obj.Metadata.OwnerReferences.Add(ownerRef); + } + + /// Gets the annotations of a Kubernetes object. + public static IDictionary Annotations(this IMetadata obj) => obj.Metadata?.Annotations; + + /// Gets the creation time of a Kubernetes object, or null if it hasn't been created yet. + public static DateTime? CreationTimestamp(this IMetadata obj) => obj.Metadata?.CreationTimestamp; + + /// Gets the deletion time of a Kubernetes object, or null if it hasn't been scheduled for deletion. + public static DateTime? DeletionTimestamp(this IMetadata obj) => obj.Metadata?.DeletionTimestamp; + + /// Ensures that the metadata field is set, and returns it. + public static V1ObjectMeta EnsureMetadata(this IMetadata obj) + { + if(obj.Metadata == null) obj.Metadata = new V1ObjectMeta(); + return obj.Metadata; + } + + /// Gets the index of the that matches the given object, or -1 if no such + /// reference could be found. + /// + public static int FindOwnerRef(this IMetadata obj, IKubernetesObject owner) + { + var ownerRefs = obj.OwnerReferences(); + if(ownerRefs != null) + { + for(int i = 0; i < ownerRefs.Count; i++) + { + if(ownerRefs[i].Matches(owner)) return i; + } + } + return -1; + } + + /// Gets the generation a Kubernetes object. + public static long? Generation(this IMetadata obj) => obj.Metadata?.Generation; + + /// Returns the given annotation from a Kubernetes object or null if the annotation was not found. + public static string GetAnnotation(this IMetadata obj, string key) + { + if(obj == null) throw new ArgumentNullException(nameof(obj)); + if(key == null) throw new ArgumentNullException(nameof(key)); + IDictionary annotations = obj.Annotations(); + return annotations != null && annotations.TryGetValue(key, out string value) ? value : null; + } + + /// Gets the for the controller of this object, or null if it couldn't be found. + public static V1OwnerReference GetController(this IMetadata obj) => + obj.OwnerReferences()?.FirstOrDefault(r => r.Controller.GetValueOrDefault()); + + /// Returns the given label from a Kubernetes object or null if the label was not found. + public static string GetLabel(this IMetadata obj, string key) + { + if(obj == null) throw new ArgumentNullException(nameof(obj)); + if(key == null) throw new ArgumentNullException(nameof(key)); + IDictionary labels = obj.Labels(); + return labels != null && labels.TryGetValue(key, out string value) ? value : null; + } + + /// Creates a that refers to the given object. + public static V1ObjectReference GetObjectReference(this T obj) where T : IKubernetesObject, IMetadata + { + if(obj == null) throw new ArgumentNullException(nameof(obj)); + string apiVersion = obj.ApiVersion, kind = obj.Kind; // default to using the API version and kind from the object + if(string.IsNullOrEmpty(apiVersion) || string.IsNullOrEmpty(kind)) // but if either of them is missing... + { + object[] attrs = typeof(T).GetCustomAttributes(typeof(KubernetesEntityAttribute), true); + if(attrs.Length == 0) throw new ArgumentException("Unable to determine the object's API version and Kind."); + var attr = (KubernetesEntityAttribute)attrs[0]; + (apiVersion, kind) = (string.IsNullOrEmpty(attr.Group) ? attr.ApiVersion : attr.Group + "/" + attr.ApiVersion, attr.Kind); + } + return new V1ObjectReference() + { + ApiVersion = apiVersion, Kind = kind, Name = obj.Name(), NamespaceProperty = obj.Namespace(), Uid = obj.Uid(), + ResourceVersion = obj.ResourceVersion() + }; + } + + /// Gets the labels of a Kubernetes object. + public static IDictionary Labels(this IMetadata obj) => obj.Metadata?.Labels; + + /// Gets the name of a Kubernetes object. + public static string Name(this IMetadata obj) => obj.Metadata?.Name; + + /// Gets the namespace of a Kubernetes object. + public static string Namespace(this IMetadata obj) => obj.Metadata?.NamespaceProperty; + + /// Gets the owner references of a Kubernetes object. + public static IList OwnerReferences(this IMetadata obj) => obj.Metadata?.OwnerReferences; + + /// Gets the resource version of a Kubernetes object. + public static string ResourceVersion(this IMetadata obj) => obj.Metadata?.ResourceVersion; + + /// Sets or removes an annotation on a Kubernetes object. + public static void SetAnnotation(this IMetadata obj, string key, string value) + { + if(obj == null) throw new ArgumentNullException(nameof(obj)); + if(key == null) throw new ArgumentNullException(nameof(key)); + if(value != null) obj.EnsureMetadata().EnsureAnnotations()[key] = value; + else obj.Metadata?.Annotations?.Remove(key); + } + + /// Sets or removes a label on a Kubernetes object. + public static void SetLabel(this IMetadata obj, string key, string value) + { + if(obj == null) throw new ArgumentNullException(nameof(obj)); + if(key == null) throw new ArgumentNullException(nameof(key)); + if(value != null) obj.EnsureMetadata().EnsureLabels()[key] = value; + else obj.Metadata?.Labels?.Remove(key); + } + + /// Gets the unique ID of a Kubernetes object. + public static string Uid(this IMetadata obj) => obj.Metadata?.Uid; + + /// Ensures that the field is not null, and returns it. + public static IDictionary EnsureAnnotations(this V1ObjectMeta meta) + { + if(meta.Annotations == null) meta.Annotations = new Dictionary(); + return meta.Annotations; + } + + /// Ensures that the field is not null, and returns it. + public static IDictionary EnsureLabels(this V1ObjectMeta meta) + { + if(meta.Labels == null) meta.Labels = new Dictionary(); + return meta.Labels; + } + + /// Gets the namespace from Kubernetes metadata. + public static string Namespace(this V1ObjectMeta meta) => meta.NamespaceProperty; + + /// Sets the namespace from Kubernetes metadata. + public static void SetNamespace(this V1ObjectMeta meta, string ns) => meta.NamespaceProperty = ns; + + /// Determines whether an object reference references the given object. + public static bool Matches(this V1ObjectReference objref, IKubernetesObject obj) + { + if(objref == null) throw new ArgumentNullException(nameof(objref)); + if(obj == null) throw new ArgumentNullException(nameof(obj)); + return objref.ApiVersion == obj.ApiVersion && objref.Kind == obj.Kind && objref.Name == obj.Name() && objref.Uid == obj.Uid() && + objref.NamespaceProperty == obj.Namespace(); + } + + /// Determines whether an owner reference references the given object. + public static bool Matches(this V1OwnerReference owner, IKubernetesObject obj) + { + if(owner == null) throw new ArgumentNullException(nameof(owner)); + if(obj == null) throw new ArgumentNullException(nameof(obj)); + return owner.ApiVersion == obj.ApiVersion && owner.Kind == obj.Kind && owner.Name == obj.Name() && owner.Uid == obj.Uid(); + } + } + + public partial class V1Status + { + /// Converts a object representing an error into a short description of the error. + public override string ToString() + { + string reason = Reason; + if(string.IsNullOrEmpty(reason) && Code.GetValueOrDefault() != 0) + { + reason = ((HttpStatusCode)Code.Value).ToString(); + } + return string.IsNullOrEmpty(Message) ? reason : string.IsNullOrEmpty(reason) ? Message : $"{reason} - {Message}"; + } + } +} From 69d3cddd04c03ea7e0325d492a002008835d63bf Mon Sep 17 00:00:00 2001 From: Adam Milazzo Date: Tue, 7 Apr 2020 18:51:31 -0700 Subject: [PATCH 2/6] Rename a function --- src/KubernetesClient/ModelExtensions.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/KubernetesClient/ModelExtensions.cs b/src/KubernetesClient/ModelExtensions.cs index a8e853062..246ed01e6 100644 --- a/src/KubernetesClient/ModelExtensions.cs +++ b/src/KubernetesClient/ModelExtensions.cs @@ -92,7 +92,7 @@ public static V1ObjectMeta EnsureMetadata(this IMetadata obj) /// Gets the index of the that matches the given object, or -1 if no such /// reference could be found. /// - public static int FindOwnerRef(this IMetadata obj, IKubernetesObject owner) + public static int FindOwnerReference(this IMetadata obj, IKubernetesObject owner) { var ownerRefs = obj.OwnerReferences(); if(ownerRefs != null) @@ -131,13 +131,13 @@ public static string GetLabel(this IMetadata obj, string key) } /// Creates a that refers to the given object. - public static V1ObjectReference GetObjectReference(this T obj) where T : IKubernetesObject, IMetadata + public static V1ObjectReference GetObjectReference(this IKubernetesObject obj) { if(obj == null) throw new ArgumentNullException(nameof(obj)); string apiVersion = obj.ApiVersion, kind = obj.Kind; // default to using the API version and kind from the object if(string.IsNullOrEmpty(apiVersion) || string.IsNullOrEmpty(kind)) // but if either of them is missing... { - object[] attrs = typeof(T).GetCustomAttributes(typeof(KubernetesEntityAttribute), true); + object[] attrs = obj.GetType().GetCustomAttributes(typeof(KubernetesEntityAttribute), true); if(attrs.Length == 0) throw new ArgumentException("Unable to determine the object's API version and Kind."); var attr = (KubernetesEntityAttribute)attrs[0]; (apiVersion, kind) = (string.IsNullOrEmpty(attr.Group) ? attr.ApiVersion : attr.Group + "/" + attr.ApiVersion, attr.Kind); From e05f5955df8c1a5c11e80fe5e32fb8b269c0a61a Mon Sep 17 00:00:00 2001 From: Adam Milazzo Date: Wed, 8 Apr 2020 14:28:00 -0700 Subject: [PATCH 3/6] Add more model extensions; add unit tests --- src/KubernetesClient/ModelExtensions.cs | 93 ++++++++++ .../ModelExtensionTests.cs | 171 ++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 tests/KubernetesClient.Tests/ModelExtensionTests.cs diff --git a/src/KubernetesClient/ModelExtensions.cs b/src/KubernetesClient/ModelExtensions.cs index 246ed01e6..9c2f26630 100644 --- a/src/KubernetesClient/ModelExtensions.cs +++ b/src/KubernetesClient/ModelExtensions.cs @@ -7,6 +7,17 @@ namespace k8s.Models { public static class ModelExtensions { + /// Adds the given finalizer to a Kubernetes object if it doesn't already exist. + /// Returns true if the finalizer was added and false if it already existed. + public static bool AddFinalizer(this IMetadata obj, string finalizer) + { + if(string.IsNullOrEmpty(finalizer)) throw new ArgumentNullException(nameof(finalizer)); + if(obj.EnsureMetadata().Finalizers == null) obj.Metadata.Finalizers = new List(); + if(obj.Metadata.Finalizers.Contains(finalizer)) return false; + obj.Metadata.Finalizers.Add(finalizer); + return true; + } + /// Extracts the Kubernetes API group from the . public static string ApiGroup(this IKubernetesObject obj) { @@ -76,6 +87,24 @@ public static void AddOwnerReference(this IMetadata obj, V1OwnerRe /// Gets the annotations of a Kubernetes object. public static IDictionary Annotations(this IMetadata obj) => obj.Metadata?.Annotations; + /// Creates a that refers to the given object. + public static V1OwnerReference CreateOwnerReference(this IKubernetesObject obj, bool? controller = null, bool? blockDeletion = null) + { + if(obj == null) throw new ArgumentNullException(nameof(obj)); + string apiVersion = obj.ApiVersion, kind = obj.Kind; // default to using the API version and kind from the object + if(string.IsNullOrEmpty(apiVersion) || string.IsNullOrEmpty(kind)) // but if either of them is missing... + { + object[] attrs = obj.GetType().GetCustomAttributes(typeof(KubernetesEntityAttribute), true); + if(attrs.Length == 0) throw new ArgumentException("Unable to determine the object's API version and Kind."); + var attr = (KubernetesEntityAttribute)attrs[0]; + (apiVersion, kind) = (string.IsNullOrEmpty(attr.Group) ? attr.ApiVersion : attr.Group + "/" + attr.ApiVersion, attr.Kind); + } + return new V1OwnerReference() + { + ApiVersion = apiVersion, Kind = kind, Name = obj.Name(), Uid = obj.Uid(), Controller = controller, BlockOwnerDeletion = blockDeletion + }; + } + /// Gets the creation time of a Kubernetes object, or null if it hasn't been created yet. public static DateTime? CreationTimestamp(this IMetadata obj) => obj.Metadata?.CreationTimestamp; @@ -89,6 +118,9 @@ public static V1ObjectMeta EnsureMetadata(this IMetadata obj) return obj.Metadata; } + /// Gets the of a Kubernetes object. + public static IList Finalizers(this IMetadata obj) => obj.Metadata?.Finalizers; + /// Gets the index of the that matches the given object, or -1 if no such /// reference could be found. /// @@ -149,6 +181,13 @@ public static V1ObjectReference GetObjectReference(this IKubernetesObjectDetermines whether the Kubernetes object has the given finalizer. + public static bool HasFinalizer(this IMetadata obj, string finalizer) + { + if(string.IsNullOrEmpty(finalizer)) throw new ArgumentNullException(nameof(finalizer)); + return obj.Finalizers() != null && obj.Metadata.Finalizers.Contains(finalizer); + } + /// Gets the labels of a Kubernetes object. public static IDictionary Labels(this IMetadata obj) => obj.Metadata?.Labels; @@ -161,6 +200,53 @@ public static V1ObjectReference GetObjectReference(this IKubernetesObjectGets the owner references of a Kubernetes object. public static IList OwnerReferences(this IMetadata obj) => obj.Metadata?.OwnerReferences; + /// Removes the given finalizer from a Kubernetes object if it exists. + /// Returns true if the finalizer was removed and false if it didn't exist. + public static bool RemoveFinalizer(this IMetadata obj, string finalizer) + { + if(string.IsNullOrEmpty(finalizer)) throw new ArgumentNullException(nameof(finalizer)); + return obj.Finalizers() != null && obj.Metadata.Finalizers.Remove(finalizer); + } + + /// Removes the first that matches the given object and returns it, or returns null if no + /// matching reference could be found. + /// + public static V1OwnerReference RemoveOwnerReference(this IMetadata obj, IKubernetesObject owner) + { + int index = obj.FindOwnerReference(owner); + V1OwnerReference ownerRef = index >= 0 ? obj.Metadata.OwnerReferences[index] : null; + if(index >= 0) obj.Metadata.OwnerReferences.RemoveAt(index); + return ownerRef; + } + + /// Removes all owner references that match the given predicate, and returns true if + /// any were removed. + /// + public static bool RemoveOwnerReferences(this IMetadata obj, Predicate predicate) + { + if(predicate == null) throw new ArgumentNullException(nameof(predicate)); + bool removed = false; + IList refs = obj.Metadata?.OwnerReferences; + if(refs != null) + { + for(int i = refs.Count-1; i >= 0; i--) + { + if(predicate(refs[i])) + { + refs.RemoveAt(i); + removed = true; + } + } + } + return removed; + } + + /// Removes all owner references that match the given object, and returns true if + /// any were removed. + /// + public static bool RemoveOwnerReferences(this IMetadata obj, IKubernetesObject owner) => + RemoveOwnerReferences(obj, r => r.Matches(owner)); + /// Gets the resource version of a Kubernetes object. public static string ResourceVersion(this IMetadata obj) => obj.Metadata?.ResourceVersion; @@ -192,6 +278,13 @@ public static IDictionary EnsureAnnotations(this V1ObjectMeta me return meta.Annotations; } + /// Ensures that the field is not null, and returns it. + public static IList EnsureFinalizers(this V1ObjectMeta meta) + { + if(meta.Finalizers == null) meta.Finalizers = new List(); + return meta.Finalizers; + } + /// Ensures that the field is not null, and returns it. public static IDictionary EnsureLabels(this V1ObjectMeta meta) { diff --git a/tests/KubernetesClient.Tests/ModelExtensionTests.cs b/tests/KubernetesClient.Tests/ModelExtensionTests.cs new file mode 100644 index 000000000..13e220982 --- /dev/null +++ b/tests/KubernetesClient.Tests/ModelExtensionTests.cs @@ -0,0 +1,171 @@ +using System; +using k8s.Models; +using Xunit; + +namespace k8s.Tests +{ + public class ModelExtensionTests + { + [Fact] + public void TestMetadata() + { + // test getters on null metadata + var pod = new V1Pod(); + Assert.Null(pod.Annotations()); + Assert.Null(pod.ApiGroup()); + var (g, v) = pod.ApiGroupAndVersion(); + Assert.Null(g); + Assert.Null(v); + Assert.Null(pod.ApiGroupVersion()); + Assert.Null(pod.CreationTimestamp()); + Assert.Null(pod.DeletionTimestamp()); + Assert.Null(pod.Finalizers()); + Assert.Null(pod.Generation()); + Assert.Null(pod.GetAnnotation("x")); + Assert.Null(pod.GetController()); + Assert.Null(pod.GetLabel("x")); + Assert.False(pod.HasFinalizer("x")); + Assert.Null(pod.Labels()); + Assert.Null(pod.Name()); + Assert.Null(pod.Namespace()); + Assert.Null(pod.OwnerReferences()); + Assert.Null(pod.ResourceVersion()); + Assert.Null(pod.Uid()); + Assert.Null(pod.Metadata); + + // test API version stuff + pod = new V1Pod() { ApiVersion = "v1" }; + Assert.Equal("", pod.ApiGroup()); + (g, v) = pod.ApiGroupAndVersion(); + Assert.Equal("", g); + Assert.Equal("v1", v); + Assert.Equal("v1", pod.ApiGroupVersion()); + pod.ApiVersion = "abc/v2"; + Assert.Equal("abc", pod.ApiGroup()); + (g, v) = pod.ApiGroupAndVersion(); + Assert.Equal("abc", g); + Assert.Equal("v2", v); + Assert.Equal("v2", pod.ApiGroupVersion()); + + // test the Ensure*() functions + Assert.NotNull(pod.EnsureMetadata()); + Assert.NotNull(pod.Metadata); + Assert.NotNull(pod.Metadata.EnsureAnnotations()); + Assert.NotNull(pod.Metadata.Annotations); + Assert.NotNull(pod.Metadata.EnsureFinalizers()); + Assert.NotNull(pod.Metadata.Finalizers); + Assert.NotNull(pod.Metadata.EnsureLabels()); + Assert.NotNull(pod.Metadata.Labels); + + // test getters with non-null values + DateTime ts = DateTime.UtcNow, ts2 = DateTime.Now; + pod.Metadata = new V1ObjectMeta() + { + CreationTimestamp = ts, DeletionTimestamp = ts2, Generation = 1, Name = "name", NamespaceProperty = "ns", ResourceVersion = "42", Uid = "id" + }; + Assert.Equal(ts, pod.CreationTimestamp().Value); + Assert.Equal(ts2, pod.DeletionTimestamp().Value); + Assert.Equal(1, pod.Generation().Value); + Assert.Equal("name", pod.Name()); + Assert.Equal("ns", pod.Namespace()); + Assert.Equal("42", pod.ResourceVersion()); + Assert.Equal("id", pod.Uid()); + + // test annotations and labels + pod.SetAnnotation("x", "y"); + pod.SetLabel("a", "b"); + Assert.Equal(1, pod.Annotations().Count); + Assert.Equal(1, pod.Labels().Count); + Assert.Equal("y", pod.GetAnnotation("x")); + Assert.Equal("y", pod.Metadata.Annotations["x"]); + Assert.Null(pod.GetAnnotation("a")); + Assert.Equal("b", pod.GetLabel("a")); + Assert.Equal("b", pod.Metadata.Labels["a"]); + Assert.Null(pod.GetLabel("x")); + pod.SetAnnotation("x", null); + Assert.Equal(0, pod.Annotations().Count); + pod.SetLabel("a", null); + Assert.Equal(0, pod.Labels().Count); + + // test finalizers + Assert.False(pod.HasFinalizer("abc")); + Assert.True(pod.AddFinalizer("abc")); + Assert.True(pod.HasFinalizer("abc")); + Assert.False(pod.AddFinalizer("abc")); + Assert.False(pod.HasFinalizer("xyz")); + Assert.False(pod.RemoveFinalizer("xyz")); + Assert.True(pod.RemoveFinalizer("abc")); + Assert.False(pod.HasFinalizer("abc")); + Assert.False(pod.RemoveFinalizer("abc")); + } + + [Fact] + public void TestReferences() + { + // test object references + var pod = new V1Pod() { ApiVersion = "abc/xyz", Kind = "sometimes" }; + pod.Metadata = new V1ObjectMeta() { Name = "name", NamespaceProperty = "ns", ResourceVersion = "ver", Uid = "id" }; + var objr = pod.GetObjectReference(); + Assert.Equal(pod.ApiVersion, objr.ApiVersion); + Assert.Equal(pod.Kind, objr.Kind); + Assert.Equal(pod.Name(), objr.Name); + Assert.Equal(pod.Namespace(), objr.NamespaceProperty); + Assert.Equal(pod.ResourceVersion(), objr.ResourceVersion); + Assert.Equal(pod.Uid(), objr.Uid); + Assert.True(objr.Matches(pod)); + + (pod.ApiVersion, pod.Kind) = (null, null); + objr = pod.GetObjectReference(); + Assert.Equal("v1", objr.ApiVersion); + Assert.Equal("Pod", objr.Kind); + Assert.False(objr.Matches(pod)); + (pod.ApiVersion, pod.Kind) = (objr.ApiVersion, objr.Kind); + Assert.True(objr.Matches(pod)); + pod.Metadata.Name = "nome"; + Assert.False(objr.Matches(pod)); + + // test owner references + (pod.ApiVersion, pod.Kind) = ("abc/xyz", "sometimes"); + var ownr = pod.CreateOwnerReference(true, false); + Assert.Equal(pod.ApiVersion, ownr.ApiVersion); + Assert.Equal(pod.Kind, ownr.Kind); + Assert.Equal(pod.Name(), ownr.Name); + Assert.Equal(pod.Uid(), ownr.Uid); + Assert.True(ownr.Controller.Value); + Assert.False(ownr.BlockOwnerDeletion.Value); + Assert.True(ownr.Matches(pod)); + + (pod.ApiVersion, pod.Kind) = (null, null); + Assert.False(ownr.Matches(pod)); + ownr = pod.CreateOwnerReference(); + Assert.Equal("v1", ownr.ApiVersion); + Assert.Equal("Pod", ownr.Kind); + Assert.Null(ownr.Controller); + Assert.Null(ownr.BlockOwnerDeletion); + Assert.False(ownr.Matches(pod)); + (pod.ApiVersion, pod.Kind) = (ownr.ApiVersion, ownr.Kind); + Assert.True(ownr.Matches(pod)); + ownr.Name = "nim"; + Assert.False(ownr.Matches(pod)); + ownr.Name = pod.Name(); + + var svc = new V1Service(); + svc.AddOwnerReference(ownr); + Assert.Equal(0, svc.FindOwnerReference(pod)); + Assert.Equal(-1, svc.FindOwnerReference(svc)); + Assert.Null(svc.GetController()); + svc.OwnerReferences()[0].Controller = true; + Assert.Same(ownr, svc.GetController()); + Assert.Same(ownr, svc.RemoveOwnerReference(pod)); + Assert.Equal(0, svc.OwnerReferences().Count); + svc.AddOwnerReference(pod.CreateOwnerReference(true)); + svc.AddOwnerReference(pod.CreateOwnerReference(false)); + svc.AddOwnerReference(pod.CreateOwnerReference()); + Assert.Equal(3, svc.OwnerReferences().Count); + Assert.NotNull(svc.RemoveOwnerReference(pod)); + Assert.Equal(2, svc.OwnerReferences().Count); + Assert.True(svc.RemoveOwnerReferences(pod)); + Assert.Equal(0, svc.OwnerReferences().Count); + } + } +} From 3e2eac018993a10be93b4589216d60070543b58d Mon Sep 17 00:00:00 2001 From: Adam Milazzo Date: Thu, 9 Apr 2020 16:19:34 -0700 Subject: [PATCH 4/6] Add a couple more extensions requested in PR --- src/KubernetesClient/ModelExtensions.cs | 168 +++++++++++------- .../ModelExtensionTests.cs | 8 +- 2 files changed, 109 insertions(+), 67 deletions(-) diff --git a/src/KubernetesClient/ModelExtensions.cs b/src/KubernetesClient/ModelExtensions.cs index 9c2f26630..fe5a880be 100644 --- a/src/KubernetesClient/ModelExtensions.cs +++ b/src/KubernetesClient/ModelExtensions.cs @@ -5,15 +5,16 @@ namespace k8s.Models { + /// Adds convenient extensions for Kubernetes objects. public static class ModelExtensions { /// Adds the given finalizer to a Kubernetes object if it doesn't already exist. /// Returns true if the finalizer was added and false if it already existed. public static bool AddFinalizer(this IMetadata obj, string finalizer) { - if(string.IsNullOrEmpty(finalizer)) throw new ArgumentNullException(nameof(finalizer)); - if(obj.EnsureMetadata().Finalizers == null) obj.Metadata.Finalizers = new List(); - if(obj.Metadata.Finalizers.Contains(finalizer)) return false; + if (string.IsNullOrEmpty(finalizer)) throw new ArgumentNullException(nameof(finalizer)); + if (EnsureMetadata(obj).Finalizers == null) obj.Metadata.Finalizers = new List(); + if (obj.Metadata.Finalizers.Contains(finalizer)) return false; obj.Metadata.Finalizers.Add(finalizer); return true; } @@ -21,8 +22,8 @@ public static bool AddFinalizer(this IMetadata obj, string finaliz /// Extracts the Kubernetes API group from the . public static string ApiGroup(this IKubernetesObject obj) { - if(obj == null) throw new ArgumentNullException(nameof(obj)); - if(obj.ApiVersion == null) return null; + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (obj.ApiVersion == null) return null; int slash = obj.ApiVersion.IndexOf('/'); return slash < 0 ? string.Empty : obj.ApiVersion.Substring(0, slash); } @@ -30,8 +31,8 @@ public static string ApiGroup(this IKubernetesObject obj) /// Extracts the Kubernetes API version (excluding the group) from the . public static string ApiGroupVersion(this IKubernetesObject obj) { - if(obj == null) throw new ArgumentNullException(nameof(obj)); - if(obj.ApiVersion == null) return null; + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (obj.ApiVersion == null) return null; int slash = obj.ApiVersion.IndexOf('/'); return slash < 0 ? obj.ApiVersion : obj.ApiVersion.Substring(slash+1); } @@ -40,22 +41,22 @@ public static string ApiGroupVersion(this IKubernetesObject obj) public static (string, string) ApiGroupAndVersion(this IKubernetesObject obj) { string group, version; - obj.GetApiGroupAndVersion(out group, out version); + GetApiGroupAndVersion(obj, out group, out version); return (group, version); } /// Splits the Kubernetes API version into the group and version. public static void GetApiGroupAndVersion(this IKubernetesObject obj, out string group, out string version) { - if(obj == null) throw new ArgumentNullException(nameof(obj)); - if(obj.ApiVersion == null) + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (obj.ApiVersion == null) { group = version = null; } else { int slash = obj.ApiVersion.IndexOf('/'); - if(slash < 0) (group, version) = (string.Empty, obj.ApiVersion); + if (slash < 0) (group, version) = (string.Empty, obj.ApiVersion); else (group, version) = (obj.ApiVersion.Substring(0, slash), obj.ApiVersion.Substring(slash+1)); } } @@ -66,7 +67,8 @@ public static void GetApiGroupAndVersion(this IKubernetesObject obj, out string /// Ensures that the metadata field is set, and returns it. public static V1ListMeta EnsureMetadata(this IMetadata obj) { - if(obj.Metadata == null) obj.Metadata = new V1ListMeta(); + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (obj.Metadata == null) obj.Metadata = new V1ListMeta(); return obj.Metadata; } @@ -78,24 +80,49 @@ public static V1ListMeta EnsureMetadata(this IMetadata obj) /// public static void AddOwnerReference(this IMetadata obj, V1OwnerReference ownerRef) { - if(obj == null) throw new ArgumentNullException(nameof(obj)); - if(ownerRef == null) throw new ArgumentNullException(nameof(ownerRef)); - if(obj.EnsureMetadata().OwnerReferences == null) obj.Metadata.OwnerReferences = new List(); + if (ownerRef == null) throw new ArgumentNullException(nameof(ownerRef)); + if (EnsureMetadata(obj).OwnerReferences == null) obj.Metadata.OwnerReferences = new List(); obj.Metadata.OwnerReferences.Add(ownerRef); } + /// Adds an owner reference to the object. No attempt is made to ensure the reference is correct or fits with the + /// other references. + /// + public static void AddOwnerReference( + this IMetadata obj, IKubernetesObject owner, bool? controller = null, bool? blockDeletion = null) => + AddOwnerReference(obj, CreateOwnerReference(owner, controller, blockDeletion)); + /// Gets the annotations of a Kubernetes object. public static IDictionary Annotations(this IMetadata obj) => obj.Metadata?.Annotations; + /// Creates a that refers to the given object. + public static V1ObjectReference CreateObjectReference(this IKubernetesObject obj) + { + if (obj == null) throw new ArgumentNullException(nameof(obj)); + string apiVersion = obj.ApiVersion, kind = obj.Kind; // default to using the API version and kind from the object + if (string.IsNullOrEmpty(apiVersion) || string.IsNullOrEmpty(kind)) // but if either of them is missing... + { + object[] attrs = obj.GetType().GetCustomAttributes(typeof(KubernetesEntityAttribute), true); + if (attrs.Length == 0) throw new ArgumentException("Unable to determine the object's API version and Kind."); + var attr = (KubernetesEntityAttribute)attrs[0]; + (apiVersion, kind) = (string.IsNullOrEmpty(attr.Group) ? attr.ApiVersion : attr.Group + "/" + attr.ApiVersion, attr.Kind); + } + return new V1ObjectReference() + { + ApiVersion = apiVersion, Kind = kind, Name = obj.Name(), NamespaceProperty = obj.Namespace(), Uid = obj.Uid(), + ResourceVersion = obj.ResourceVersion() + }; + } + /// Creates a that refers to the given object. public static V1OwnerReference CreateOwnerReference(this IKubernetesObject obj, bool? controller = null, bool? blockDeletion = null) { - if(obj == null) throw new ArgumentNullException(nameof(obj)); + if (obj == null) throw new ArgumentNullException(nameof(obj)); string apiVersion = obj.ApiVersion, kind = obj.Kind; // default to using the API version and kind from the object - if(string.IsNullOrEmpty(apiVersion) || string.IsNullOrEmpty(kind)) // but if either of them is missing... + if (string.IsNullOrEmpty(apiVersion) || string.IsNullOrEmpty(kind)) // but if either of them is missing... { object[] attrs = obj.GetType().GetCustomAttributes(typeof(KubernetesEntityAttribute), true); - if(attrs.Length == 0) throw new ArgumentException("Unable to determine the object's API version and Kind."); + if (attrs.Length == 0) throw new ArgumentException("Unable to determine the object's API version and Kind."); var attr = (KubernetesEntityAttribute)attrs[0]; (apiVersion, kind) = (string.IsNullOrEmpty(attr.Group) ? attr.ApiVersion : attr.Group + "/" + attr.ApiVersion, attr.Kind); } @@ -114,7 +141,8 @@ public static V1OwnerReference CreateOwnerReference(this IKubernetesObjectEnsures that the metadata field is set, and returns it. public static V1ObjectMeta EnsureMetadata(this IMetadata obj) { - if(obj.Metadata == null) obj.Metadata = new V1ObjectMeta(); + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (obj.Metadata == null) obj.Metadata = new V1ObjectMeta(); return obj.Metadata; } @@ -124,14 +152,22 @@ public static V1ObjectMeta EnsureMetadata(this IMetadata obj) /// Gets the index of the that matches the given object, or -1 if no such /// reference could be found. /// - public static int FindOwnerReference(this IMetadata obj, IKubernetesObject owner) + public static int FindOwnerReference(this IMetadata obj, IKubernetesObject owner) => + FindOwnerReference(obj, r => r.Matches(owner)); + + /// Gets the index of the that matches the given predicate, or -1 if no such + /// reference could be found. + /// + public static int FindOwnerReference(this IMetadata obj, Predicate predicate) { + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); var ownerRefs = obj.OwnerReferences(); - if(ownerRefs != null) + if (ownerRefs != null) { - for(int i = 0; i < ownerRefs.Count; i++) + for (int i = 0; i < ownerRefs.Count; i++) { - if(ownerRefs[i].Matches(owner)) return i; + if (predicate(ownerRefs[i])) return i; } } return -1; @@ -143,8 +179,8 @@ public static int FindOwnerReference(this IMetadata obj, IKubernet /// Returns the given annotation from a Kubernetes object or null if the annotation was not found. public static string GetAnnotation(this IMetadata obj, string key) { - if(obj == null) throw new ArgumentNullException(nameof(obj)); - if(key == null) throw new ArgumentNullException(nameof(key)); + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (key == null) throw new ArgumentNullException(nameof(key)); IDictionary annotations = obj.Annotations(); return annotations != null && annotations.TryGetValue(key, out string value) ? value : null; } @@ -156,38 +192,35 @@ public static V1OwnerReference GetController(this IMetadata obj) = /// Returns the given label from a Kubernetes object or null if the label was not found. public static string GetLabel(this IMetadata obj, string key) { - if(obj == null) throw new ArgumentNullException(nameof(obj)); - if(key == null) throw new ArgumentNullException(nameof(key)); + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (key == null) throw new ArgumentNullException(nameof(key)); IDictionary labels = obj.Labels(); return labels != null && labels.TryGetValue(key, out string value) ? value : null; } - /// Creates a that refers to the given object. - public static V1ObjectReference GetObjectReference(this IKubernetesObject obj) + /// Gets that matches the given object, or null if no matching reference exists. + public static V1OwnerReference GetOwnerReference(this IMetadata obj, IKubernetesObject owner) => + GetOwnerReference(obj, r => r.Matches(owner)); + + /// Gets the that matches the given predicate, or null if no matching reference exists. + public static V1OwnerReference GetOwnerReference(this IMetadata obj, Predicate predicate) { - if(obj == null) throw new ArgumentNullException(nameof(obj)); - string apiVersion = obj.ApiVersion, kind = obj.Kind; // default to using the API version and kind from the object - if(string.IsNullOrEmpty(apiVersion) || string.IsNullOrEmpty(kind)) // but if either of them is missing... - { - object[] attrs = obj.GetType().GetCustomAttributes(typeof(KubernetesEntityAttribute), true); - if(attrs.Length == 0) throw new ArgumentException("Unable to determine the object's API version and Kind."); - var attr = (KubernetesEntityAttribute)attrs[0]; - (apiVersion, kind) = (string.IsNullOrEmpty(attr.Group) ? attr.ApiVersion : attr.Group + "/" + attr.ApiVersion, attr.Kind); - } - return new V1ObjectReference() - { - ApiVersion = apiVersion, Kind = kind, Name = obj.Name(), NamespaceProperty = obj.Namespace(), Uid = obj.Uid(), - ResourceVersion = obj.ResourceVersion() - }; + int index = FindOwnerReference(obj, predicate); + return index >= 0 ? obj.Metadata.OwnerReferences[index] : null; } /// Determines whether the Kubernetes object has the given finalizer. public static bool HasFinalizer(this IMetadata obj, string finalizer) { - if(string.IsNullOrEmpty(finalizer)) throw new ArgumentNullException(nameof(finalizer)); + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (string.IsNullOrEmpty(finalizer)) throw new ArgumentNullException(nameof(finalizer)); return obj.Finalizers() != null && obj.Metadata.Finalizers.Contains(finalizer); } + /// Determines whether one object is owned by another. + public static bool IsOwnedBy(this IMetadata obj, IKubernetesObject owner) => + FindOwnerReference(obj, owner) >= 0; + /// Gets the labels of a Kubernetes object. public static IDictionary Labels(this IMetadata obj) => obj.Metadata?.Labels; @@ -204,7 +237,8 @@ public static bool HasFinalizer(this IMetadata obj, string finaliz /// Returns true if the finalizer was removed and false if it didn't exist. public static bool RemoveFinalizer(this IMetadata obj, string finalizer) { - if(string.IsNullOrEmpty(finalizer)) throw new ArgumentNullException(nameof(finalizer)); + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (string.IsNullOrEmpty(finalizer)) throw new ArgumentNullException(nameof(finalizer)); return obj.Finalizers() != null && obj.Metadata.Finalizers.Remove(finalizer); } @@ -213,9 +247,9 @@ public static bool RemoveFinalizer(this IMetadata obj, string fina /// public static V1OwnerReference RemoveOwnerReference(this IMetadata obj, IKubernetesObject owner) { - int index = obj.FindOwnerReference(owner); + int index = FindOwnerReference(obj, owner); V1OwnerReference ownerRef = index >= 0 ? obj.Metadata.OwnerReferences[index] : null; - if(index >= 0) obj.Metadata.OwnerReferences.RemoveAt(index); + if (index >= 0) obj.Metadata.OwnerReferences.RemoveAt(index); return ownerRef; } @@ -224,14 +258,15 @@ public static V1OwnerReference RemoveOwnerReference(this IMetadata /// public static bool RemoveOwnerReferences(this IMetadata obj, Predicate predicate) { - if(predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); bool removed = false; IList refs = obj.Metadata?.OwnerReferences; - if(refs != null) + if (refs != null) { - for(int i = refs.Count-1; i >= 0; i--) + for (int i = refs.Count-1; i >= 0; i--) { - if(predicate(refs[i])) + if (predicate(refs[i])) { refs.RemoveAt(i); removed = true; @@ -253,18 +288,18 @@ public static bool RemoveOwnerReferences(this IMetadata obj, IKube /// Sets or removes an annotation on a Kubernetes object. public static void SetAnnotation(this IMetadata obj, string key, string value) { - if(obj == null) throw new ArgumentNullException(nameof(obj)); - if(key == null) throw new ArgumentNullException(nameof(key)); - if(value != null) obj.EnsureMetadata().EnsureAnnotations()[key] = value; + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (key == null) throw new ArgumentNullException(nameof(key)); + if (value != null) obj.EnsureMetadata().EnsureAnnotations()[key] = value; else obj.Metadata?.Annotations?.Remove(key); } /// Sets or removes a label on a Kubernetes object. public static void SetLabel(this IMetadata obj, string key, string value) { - if(obj == null) throw new ArgumentNullException(nameof(obj)); - if(key == null) throw new ArgumentNullException(nameof(key)); - if(value != null) obj.EnsureMetadata().EnsureLabels()[key] = value; + if (obj == null) throw new ArgumentNullException(nameof(obj)); + if (key == null) throw new ArgumentNullException(nameof(key)); + if (value != null) obj.EnsureMetadata().EnsureLabels()[key] = value; else obj.Metadata?.Labels?.Remove(key); } @@ -274,21 +309,24 @@ public static void SetLabel(this IMetadata obj, string key, string /// Ensures that the field is not null, and returns it. public static IDictionary EnsureAnnotations(this V1ObjectMeta meta) { - if(meta.Annotations == null) meta.Annotations = new Dictionary(); + if (meta == null) throw new ArgumentNullException(nameof(meta)); + if (meta.Annotations == null) meta.Annotations = new Dictionary(); return meta.Annotations; } /// Ensures that the field is not null, and returns it. public static IList EnsureFinalizers(this V1ObjectMeta meta) { - if(meta.Finalizers == null) meta.Finalizers = new List(); + if (meta == null) throw new ArgumentNullException(nameof(meta)); + if (meta.Finalizers == null) meta.Finalizers = new List(); return meta.Finalizers; } /// Ensures that the field is not null, and returns it. public static IDictionary EnsureLabels(this V1ObjectMeta meta) { - if(meta.Labels == null) meta.Labels = new Dictionary(); + if (meta == null) throw new ArgumentNullException(nameof(meta)); + if (meta.Labels == null) meta.Labels = new Dictionary(); return meta.Labels; } @@ -301,8 +339,8 @@ public static IDictionary EnsureLabels(this V1ObjectMeta meta) /// Determines whether an object reference references the given object. public static bool Matches(this V1ObjectReference objref, IKubernetesObject obj) { - if(objref == null) throw new ArgumentNullException(nameof(objref)); - if(obj == null) throw new ArgumentNullException(nameof(obj)); + if (objref == null) throw new ArgumentNullException(nameof(objref)); + if (obj == null) throw new ArgumentNullException(nameof(obj)); return objref.ApiVersion == obj.ApiVersion && objref.Kind == obj.Kind && objref.Name == obj.Name() && objref.Uid == obj.Uid() && objref.NamespaceProperty == obj.Namespace(); } @@ -310,8 +348,8 @@ public static bool Matches(this V1ObjectReference objref, IKubernetesObjectDetermines whether an owner reference references the given object. public static bool Matches(this V1OwnerReference owner, IKubernetesObject obj) { - if(owner == null) throw new ArgumentNullException(nameof(owner)); - if(obj == null) throw new ArgumentNullException(nameof(obj)); + if (owner == null) throw new ArgumentNullException(nameof(owner)); + if (obj == null) throw new ArgumentNullException(nameof(obj)); return owner.ApiVersion == obj.ApiVersion && owner.Kind == obj.Kind && owner.Name == obj.Name() && owner.Uid == obj.Uid(); } } @@ -322,7 +360,7 @@ public partial class V1Status public override string ToString() { string reason = Reason; - if(string.IsNullOrEmpty(reason) && Code.GetValueOrDefault() != 0) + if (string.IsNullOrEmpty(reason) && Code.GetValueOrDefault() != 0) { reason = ((HttpStatusCode)Code.Value).ToString(); } diff --git a/tests/KubernetesClient.Tests/ModelExtensionTests.cs b/tests/KubernetesClient.Tests/ModelExtensionTests.cs index 13e220982..27ec02c75 100644 --- a/tests/KubernetesClient.Tests/ModelExtensionTests.cs +++ b/tests/KubernetesClient.Tests/ModelExtensionTests.cs @@ -20,10 +20,12 @@ public void TestMetadata() Assert.Null(pod.CreationTimestamp()); Assert.Null(pod.DeletionTimestamp()); Assert.Null(pod.Finalizers()); + Assert.Equal(-1, pod.FindOwnerReference(r => true)); Assert.Null(pod.Generation()); Assert.Null(pod.GetAnnotation("x")); Assert.Null(pod.GetController()); Assert.Null(pod.GetLabel("x")); + Assert.Null(pod.GetOwnerReference(r => true)); Assert.False(pod.HasFinalizer("x")); Assert.Null(pod.Labels()); Assert.Null(pod.Name()); @@ -105,7 +107,7 @@ public void TestReferences() // test object references var pod = new V1Pod() { ApiVersion = "abc/xyz", Kind = "sometimes" }; pod.Metadata = new V1ObjectMeta() { Name = "name", NamespaceProperty = "ns", ResourceVersion = "ver", Uid = "id" }; - var objr = pod.GetObjectReference(); + var objr = pod.CreateObjectReference(); Assert.Equal(pod.ApiVersion, objr.ApiVersion); Assert.Equal(pod.Kind, objr.Kind); Assert.Equal(pod.Name(), objr.Name); @@ -115,7 +117,7 @@ public void TestReferences() Assert.True(objr.Matches(pod)); (pod.ApiVersion, pod.Kind) = (null, null); - objr = pod.GetObjectReference(); + objr = pod.CreateObjectReference(); Assert.Equal("v1", objr.ApiVersion); Assert.Equal("Pod", objr.Kind); Assert.False(objr.Matches(pod)); @@ -153,6 +155,8 @@ public void TestReferences() svc.AddOwnerReference(ownr); Assert.Equal(0, svc.FindOwnerReference(pod)); Assert.Equal(-1, svc.FindOwnerReference(svc)); + Assert.Same(ownr, svc.GetOwnerReference(pod)); + Assert.Null(svc.GetOwnerReference(svc)); Assert.Null(svc.GetController()); svc.OwnerReferences()[0].Controller = true; Assert.Same(ownr, svc.GetController()); From 8dde3ac8fe194ca9042bb0ddfab3affa83a4f175 Mon Sep 17 00:00:00 2001 From: Adam Milazzo Date: Sat, 18 Apr 2020 00:56:13 -0700 Subject: [PATCH 5/6] Rebase on master --- src/KubernetesClient/ModelExtensions.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/KubernetesClient/ModelExtensions.cs b/src/KubernetesClient/ModelExtensions.cs index fe5a880be..ddda97a78 100644 --- a/src/KubernetesClient/ModelExtensions.cs +++ b/src/KubernetesClient/ModelExtensions.cs @@ -353,18 +353,4 @@ public static bool Matches(this V1OwnerReference owner, IKubernetesObjectConverts a object representing an error into a short description of the error. - public override string ToString() - { - string reason = Reason; - if (string.IsNullOrEmpty(reason) && Code.GetValueOrDefault() != 0) - { - reason = ((HttpStatusCode)Code.Value).ToString(); - } - return string.IsNullOrEmpty(Message) ? reason : string.IsNullOrEmpty(reason) ? Message : $"{reason} - {Message}"; - } - } } From 967c52e50474a612a689859994821298648bfaf6 Mon Sep 17 00:00:00 2001 From: Adam Milazzo Date: Sat, 18 Apr 2020 07:43:43 -0700 Subject: [PATCH 6/6] Remove unused using statement --- src/KubernetesClient/ModelExtensions.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/KubernetesClient/ModelExtensions.cs b/src/KubernetesClient/ModelExtensions.cs index ddda97a78..69fe9b373 100644 --- a/src/KubernetesClient/ModelExtensions.cs +++ b/src/KubernetesClient/ModelExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; namespace k8s.Models {