diff --git a/PSKubectl/Formats/JsonPatch.Operations.Operation.Format.ps1xml b/PSKubectl/Formats/JsonPatch.Operations.Operation.Format.ps1xml
deleted file mode 100644
index 8f531db..0000000
--- a/PSKubectl/Formats/JsonPatch.Operations.Operation.Format.ps1xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
- Default
-
- Microsoft.AspNetCore.JsonPatch.Operations.Operation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $color = switch ($_.OperationType) {
- "Remove" { 31 }
- "Add" { 32 }
- "Replace" { 33 }
- "Copy" { 34 }
- "Move" { 35 }
- "Test" { 0 }
- }
- "$([char]0x001b)[$($color)m" + $_.OperationType
-
-
-
- Path
-
-
- Value
-
-
- $_.From + "$([char]0x001b)[0m"
-
-
-
-
-
-
-
-
diff --git a/PSKubectl/PSKubectl.psd1 b/PSKubectl/PSKubectl.psd1
index 4dad413..f0acf5d 100644
--- a/PSKubectl/PSKubectl.psd1
+++ b/PSKubectl/PSKubectl.psd1
@@ -77,7 +77,6 @@
FormatsToProcess = @(
'Formats/Config.Format.ps1xml',
'Formats/DeploymentV1.Format.ps1xml',
- 'Formats/JsonPatch.Operations.Operation.Format.ps1xml',
'Formats/NamespaceV1.Format.ps1xml',
'Formats/PodV1.Format.ps1xml'
)
@@ -90,7 +89,6 @@
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @(
- 'Compare-KubeResource',
'ConvertFrom-KubeYaml',
'ConvertTo-KubeYaml',
'Get-KubeConfig',
diff --git a/Tests/PSKubectl.Tests.ps1 b/Tests/PSKubectl.Tests.ps1
index a6de913..22b8ec3 100644
--- a/Tests/PSKubectl.Tests.ps1
+++ b/Tests/PSKubectl.Tests.ps1
@@ -299,7 +299,7 @@ Describe Get-KubeConfig {
}
}
-Describe Convert-KubeYaml {
+Describe ConvertFrom-KubeYaml {
It 'Should read in YAML' {
$parsed = Get-Content -Raw $PSScriptRoot/test.Deployment.yml | ConvertFrom-KubeYaml
$parsed.PSObject.TypeNames | Should -Contain 'KubeClient.Models.DeploymentV1'
diff --git a/src/Annotations.cs b/src/Annotations.cs
deleted file mode 100644
index 35d15a2..0000000
--- a/src/Annotations.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Management.Automation;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using KubeClient;
-using KubeClient.Models;
-using KubeClient.ResourceClients;
-using Microsoft.AspNetCore.JsonPatch;
-using Microsoft.AspNetCore.JsonPatch.Operations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using Newtonsoft.Json.Serialization;
-
-namespace Kubectl {
- public static class Annotations {
- public const string LastAppliedConfig = "kubectl.kubernetes.io/last-applied-configuration";
- }
-}
diff --git a/src/Cmdlets/CompareKubeResourceCmdlet.cs b/src/Cmdlets/CompareKubeResourceCmdlet.cs
deleted file mode 100644
index f401c01..0000000
--- a/src/Cmdlets/CompareKubeResourceCmdlet.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Management.Automation;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using KubeClient;
-using KubeClient.Models;
-using KubeClient.ResourceClients;
-using Microsoft.AspNetCore.JsonPatch;
-using Microsoft.AspNetCore.JsonPatch.Operations;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using Newtonsoft.Json.Serialization;
-
-namespace Kubectl.Cmdlets {
- [Cmdlet(VerbsData.Compare, "KubeResource")]
- [OutputType(new[] { typeof(Operation) })]
- public sealed class CompareKubeResourceCmdlet : KubeCmdlet {
- [Parameter(Mandatory = true, Position = 0)]
- public dynamic Original { get; set; }
-
- [Parameter(Mandatory = true, Position = 1)]
-
- public dynamic Modified { get; set; }
-
- [Parameter(Mandatory = true, Position = 2, ParameterSetName = "ThreeWay")]
- public dynamic Current { get; set; }
-
- [Parameter(Mandatory = true, ParameterSetName = "ThreeWayFromLastApplied")]
- public SwitchParameter ThreeWayFromLastApplied { get; set; }
-
- [Parameter(ParameterSetName = "ThreeWayFromLastApplied")]
- public SwitchParameter Annotate { get; set; }
-
- [Parameter(ParameterSetName = "TwoWay")]
- public SwitchParameter IgnoreDeletions { get; set; }
-
- [Parameter(ParameterSetName = "TwoWay")]
- public SwitchParameter IgnoreAdditionsAndModifications { get; set; }
-
- protected override async Task ProcessRecordAsync(CancellationToken cancellationToken) {
- await base.ProcessRecordAsync(cancellationToken);
- // TODO do not pass the ContractResolver here once KubeClient allows customizing the serialisation
- var patch = new JsonPatchDocument(new List(), new PSObjectAwareContractResolver());
- var comparer = new KubeResourceComparer(LoggerFactory);
- string apiGroupVersion = (string)Original.ApiVersion;
- string apiVersion = apiGroupVersion.Split('/').Last();
- string kind = (string)Original.Kind;
- if (!ModelTypes.TryGetValue((kind, apiVersion), out Type type)) {
- WriteError(new ErrorRecord(new Exception($"Unknown (kind: {kind}, apiVersion: {apiVersion}). {ModelTypes.Count} Known:\n{String.Join("\n", ModelTypes.Keys)}"), null, ErrorCategory.InvalidData, null));
- return;
- }
- if (ThreeWayFromLastApplied) {
- comparer.CreateThreeWayPatchFromLastApplied(
- current: Original,
- modified: Modified,
- type: type,
- patch: patch,
- annotate: Annotate
- );
- } else if (Current == null) {
- comparer.CreateTwoWayPatch(
- original: Original,
- modified: Modified,
- type: type,
- patch: patch,
- ignoreDeletions: IgnoreDeletions,
- ignoreAdditionsAndModifications: IgnoreAdditionsAndModifications
- );
- } else {
- comparer.CreateThreeWayPatch(
- original: Original,
- modified: Modified,
- current: Current,
- type: type,
- patch: patch
- );
- }
- foreach (var operation in patch.Operations) {
- WriteObject(operation);
- }
- }
- }
-}
diff --git a/src/KubeResourceComparer.cs b/src/KubeResourceComparer.cs
deleted file mode 100644
index 2568164..0000000
--- a/src/KubeResourceComparer.cs
+++ /dev/null
@@ -1,424 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using System.Management.Automation;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using KubeClient;
-using KubeClient.Models;
-using KubeClient.ResourceClients;
-using Microsoft.AspNetCore.JsonPatch;
-using Microsoft.AspNetCore.JsonPatch.Operations;
-using Microsoft.Extensions.Logging;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using Newtonsoft.Json.Serialization;
-
-namespace Kubectl {
- public sealed class KubeResourceComparer {
- private Dictionary> nonUpdateableTypes = new Dictionary> {
- [typeof(KubeObjectV1)] = new HashSet {
- "ApiVersion",
- "Kind",
- },
- [typeof(KubeResourceV1)] = new HashSet {
- // Status cannot be updated directly, only spec
- // Not that Status is not strictly a property of KubeResourceV1 but effectively all subclasses have it
- "Status",
- },
- [typeof(ObjectMetaV1)] = new HashSet {
- "ResourceVersion",
- "DeletionTimestamp",
- "Generation",
- "Uid",
- "SelfLink",
- "CreationTimestamp",
- },
- // [typeof(ContainerV1)] = new HashSet {
- // "TerminationMessagePolicy",
- // "TerminationMessagePath",
- // }
- };
- private ILogger logger;
- public KubeResourceComparer(ILoggerFactory loggerFactory) {
- this.logger = loggerFactory.CreateLogger(nameof(KubeResourceComparer));
- }
-
- ///
- /// CreateThreeWayMergePatch reconciles a modified configuration with an original configuration,
- /// while preserving any changes or deletions made to the original configuration in the interim,
- /// and not overridden by the current configuration.
- ///
- /// The configuration in the current state on the server
- /// The user-modified local configuration
- /// If true, update the annotation in modified with the value of modified before diffing
- public void CreateThreeWayPatchFromLastApplied(object current, object modified, Type type, JsonPatchDocument patch, bool annotate) {
- if (current == null) throw new ArgumentNullException(nameof(current));
- if (modified == null) throw new ArgumentNullException(nameof(modified));
- if (type == null) throw new ArgumentNullException(nameof(type));
- if (patch == null) throw new ArgumentNullException(nameof(patch));
-
- object original = modified;
- var originalAnnotations = (IDictionary)current.GetDynamicPropertyValue("Metadata")?.GetDynamicPropertyValue("Annotations");
- var originalJson = (string)originalAnnotations?[Annotations.LastAppliedConfig];
- if (!String.IsNullOrEmpty(originalJson)) {
- original = JsonConvert.DeserializeObject(originalJson, type);
- }
- if (annotate) {
- var modifiedJson = JsonConvert.SerializeObject(modified, new PSObjectJsonConverter());
- if (modified.GetDynamicPropertyValue("Metadata") == null) {
- modified.SetDynamicPropertyValue("Metadata", new PSObject());
- }
- if (modified.GetDynamicPropertyValue("Metadata").GetDynamicPropertyValue("Annotations") == null) {
- modified.GetDynamicPropertyValue("Metadata").SetDynamicPropertyValue("Annotations", new Dictionary());
- }
- var annotations = (IDictionary)modified.GetDynamicPropertyValue("Metadata").GetDynamicPropertyValue("Annotations");
- annotations[Annotations.LastAppliedConfig] = modifiedJson;
- }
- CreateThreeWayPatch(original, modified, current, type, patch);
- }
-
- ///
- /// CreateThreeWayMergePatch reconciles a modified configuration with an original configuration,
- /// while preserving any changes or deletions made to the original configuration in the interim,
- /// and not overridden by the current configuration.
- ///
- /// The original configuration from the annotation in current
- /// The user-modified local configuration
- /// The configuration in the current state on the server
- public void CreateThreeWayPatch(object original, object modified, dynamic current, Type type, JsonPatchDocument patch) {
- if (current == null) throw new ArgumentNullException(nameof(current));
- if (modified == null) throw new ArgumentNullException(nameof(modified));
- if (current == null) throw new ArgumentNullException(nameof(current));
- if (type == null) throw new ArgumentNullException(nameof(type));
- if (patch == null) throw new ArgumentNullException(nameof(patch));
-
- CreateTwoWayPatch(current, modified, type, patch, ignoreDeletions: true);
- CreateTwoWayPatch(original, modified, type, patch, ignoreAdditionsAndModifications: true);
- }
-
- ///
- /// Configures the passed JSON Patch so that it yields modified when applied to original
- /// - Adding fields to the patch present in modified, missing from original
- /// - Setting fields to the patch present in modified and original with different values
- /// - Delete fields present in original, missing from modified through
- /// - IFF map field - set to nil in patch ???
- /// - IFF list of maps && merge strategy - use deleteDirective for the elements ???
- /// - IFF list of primitives && merge strategy - use parallel deletion list ???
- /// - IFF list of maps or primitives with replace strategy (default) - set patch value to the value in modified ???
- /// - Build $retainKeys directive for fields with retainKeys patch strategy ???
- ///
- /// The original object
- /// The modified object
- /// The type that original and modified represent (even if they are not actually that type, but a PSObject)
- /// The JSON pointer to the currently inspected values
- /// The strategy to use for patching (replace or merge with mergeKey)
- public void CreateTwoWayPatch(object original, object modified, Type type, JsonPatchDocument patch, string path = "", MergeStrategyAttribute mergeStrategy = null, bool ignoreDeletions = false, bool ignoreAdditionsAndModifications = false) {
- if (type == null) throw new ArgumentNullException(nameof(type));
- if (patch == null) throw new ArgumentNullException(nameof(patch));
- if (path == null) throw new ArgumentNullException(nameof(path));
-
- logger.LogTrace($"Path: {path}");
- if (modified == null && original == null) {
- return;
- }
- if (modified == null && original != null) {
- if (!ignoreDeletions) {
- patch.Replace(path, modified);
- }
- return;
- }
- if (original == null && modified != null) {
- if (!ignoreAdditionsAndModifications) {
- patch.Replace(path, modified);
- }
- return;
- }
-
- // From this point, original and modified are known to be non-null
-
- logger.LogTrace($"Type: original {original?.GetType().Name} modified {modified?.GetType().Name} expected {type?.Name}");
-
- // string, int, float, bool, enum, DateTime
- if (modified is string || type.IsValueType) {
- logger.LogTrace($"Is value type, comparing {original} <-> {modified}");
- // Replace if changed, otherwise do nothing
- // We NEED to use Equals() here instead of != because the static type is object, meaning the scalar is boxed.
- // Since operators are resolved at compile time, this would use the == implementation for object,
- // while Equals() is dynamically dispatched on the real boxed type.
- if (!original.Equals(modified)) {
- patch.Replace(path, modified);
- }
- return;
- }
-
- // From this point, original and modified are known to be reference types
-
- if (System.Object.ReferenceEquals(original, modified)) {
- // Same object, short cut
- return;
- }
-
- if (modified is IList) {
- logger.LogTrace("Is List");
- Type valueType = type.GetGenericArguments()[0];
- // Handle lists
- // Really just casting to generic IEnumerable get access to more LINQ. It's all object anyway.
- IEnumerable