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 <Operation>(), new PSObjectAwareContractResolver()); var comparer = new KubeResourceComparer(LoggerFactory); string apiGroupVersion = (string)Original.ApiVersion; string apiVersion = apiGroupVersion.Split('/').Last(); string kind = (string)Original.Kind; Type type = ModelTypes.GetValueOrDefault((kind, apiVersion)); if (type == null) { 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); } }
private async Task updateResource(dynamic modified, CancellationToken cancellationToken) { if (modified == null) { throw new ArgumentNullException(nameof(modified)); } string kind = (string)modified.Kind; if (String.IsNullOrEmpty(kind)) { throw new Exception("Input object does not have Kind set"); } string apiVersion = (string)modified.ApiVersion; if (String.IsNullOrEmpty(apiVersion)) { throw new Exception("Input object does not have ApiVersion set"); } // Figure out the model class - needed for diffing 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, Resource)); return; } dynamic metadata = modified.Metadata; string name = (string)metadata.Name; if (String.IsNullOrEmpty(name)) { throw new Exception("Input object does not have Metadata.Name set"); } string kubeNamespace = (string)metadata.Namespace; // Get current resource state from server WriteVerbose($"Getting resource \"{name}\" of kind \"{kind}\" from namespace \"{kubeNamespace}\""); object current = await client.Dynamic().Get(name, kind, apiVersion, kubeNamespace, cancellationToken); if (current == null) { WriteError(new ErrorRecord(new Exception($"{kind} ({apiVersion}) \"{name}\" does not exist in namespace \"{kubeNamespace}\""), null, ErrorCategory.InvalidData, Resource)); return; } // Generate three-way patch from current to modified // TODO do not pass the ContractResolver here once KubeClient allows customizing the serialisation var patch = new JsonPatchDocument(new List <Operation>(), new PSObjectAwareContractResolver()); var comparer = new KubeResourceComparer(LoggerFactory); comparer.CreateThreeWayPatchFromLastApplied(current, modified, type, patch, true); WriteVerbose("Patch: " + JsonConvert.SerializeObject(patch, new JsonSerializerSettings { Formatting = Formatting.Indented, Converters = new[] { new PSObjectJsonConverter() } })); // Send patch to server if (ShouldProcess($"Sending patch for {kind} \"{name}\" in namespace \"{kubeNamespace}\"", $"Send patch for {kind} \"{name}\" in namespace \"{kubeNamespace}\"?", "Confirm")) { var result = await client.Dynamic().Patch( name: name, kind: kind, apiVersion: apiVersion, patch: patch, kubeNamespace: kubeNamespace, cancellationToken: cancellationToken ); WriteObject(result); } }