From 7164deda26c9fca6d71807c0b09cc61689b860a2 Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Thu, 25 Sep 2025 03:47:21 -0300 Subject: [PATCH 1/2] Avoid crashing update-redirects when CLI output is empty --- .../AwsCloudFrontKeyValueStoreProxy.cs | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/src/services/Elastic.Documentation.Assembler/Deploying/Redirects/AwsCloudFrontKeyValueStoreProxy.cs b/src/services/Elastic.Documentation.Assembler/Deploying/Redirects/AwsCloudFrontKeyValueStoreProxy.cs index 550e9a01c..49fb42790 100644 --- a/src/services/Elastic.Documentation.Assembler/Deploying/Redirects/AwsCloudFrontKeyValueStoreProxy.cs +++ b/src/services/Elastic.Documentation.Assembler/Deploying/Redirects/AwsCloudFrontKeyValueStoreProxy.cs @@ -31,7 +31,10 @@ public void UpdateRedirects(string kvsName, IReadOnlyDictionary if (string.IsNullOrEmpty(eTag)) return; - var existingRedirects = ListAllKeys(kvsArn); + var listingSuccessful = TryListAllKeys(kvsArn, out var existingRedirects); + + if (!listingSuccessful) + return; var toPut = sourcedRedirects .Select(kvp => new PutKeyRequestListItem { Key = kvp.Key, Value = kvp.Value }) @@ -51,7 +54,14 @@ private string DescribeKeyValueStore(string kvsName) try { var json = CaptureMultiple("aws", "cloudfront", "describe-key-value-store", "--name", kvsName); - var describeResponse = JsonSerializer.Deserialize(string.Concat(json), AwsCloudFrontKeyValueStoreJsonContext.Default.DescribeKeyValueStoreResponse); + var concatJson = string.Concat(json); + if (string.IsNullOrWhiteSpace(concatJson)) + { + Collector.EmitError("", "The output from cloudfront:describe-key-value-store was empty"); + return string.Empty; + } + + var describeResponse = JsonSerializer.Deserialize(concatJson, AwsCloudFrontKeyValueStoreJsonContext.Default.DescribeKeyValueStoreResponse); if (describeResponse?.KeyValueStore is { ARN.Length: > 0 }) return describeResponse.KeyValueStore.ARN; @@ -71,7 +81,13 @@ private string AcquireETag(string kvsArn) try { var json = CaptureMultiple("aws", "cloudfront-keyvaluestore", "describe-key-value-store", "--kvs-arn", kvsArn); - var describeResponse = JsonSerializer.Deserialize(string.Concat(json), AwsCloudFrontKeyValueStoreJsonContext.Default.DescribeKeyValueStoreResponse); + var concatJson = string.Concat(json); + if (string.IsNullOrWhiteSpace(concatJson)) + { + Collector.EmitError("", "The output from cloudfront-keyvaluestore:describe-key-value-store was empty"); + return string.Empty; + } + var describeResponse = JsonSerializer.Deserialize(concatJson, AwsCloudFrontKeyValueStoreJsonContext.Default.DescribeKeyValueStoreResponse); if (describeResponse?.ETag is not null) return describeResponse.ETag; @@ -85,10 +101,10 @@ private string AcquireETag(string kvsArn) } } - private HashSet ListAllKeys(string kvsArn) + private bool TryListAllKeys(string kvsArn, out HashSet keys) { _logger.LogInformation("Acquiring existing redirects"); - var allKeys = new HashSet(); + keys = []; string[] baseArgs = ["cloudfront-keyvaluestore", "list-keys", "--kvs-arn", kvsArn, "--page-size", "50", "--max-items", "50"]; string? nextToken = null; try @@ -96,12 +112,18 @@ private HashSet ListAllKeys(string kvsArn) do { var json = CaptureMultiple("aws", [.. baseArgs, .. nextToken is not null ? (string[])["--starting-token", nextToken] : []]); - var response = JsonSerializer.Deserialize(string.Concat(json), AwsCloudFrontKeyValueStoreJsonContext.Default.ListKeysResponse); + var concatJson = string.Concat(json); + if (string.IsNullOrWhiteSpace(concatJson)) + { + Collector.EmitError("", "The output from cloudfront-keyvaluestore:list-keys was empty"); + throw new JsonException("Empty output from cloudfront-keyvaluestore:list-keys"); + } + var response = JsonSerializer.Deserialize(concatJson, AwsCloudFrontKeyValueStoreJsonContext.Default.ListKeysResponse); if (response?.Items != null) { foreach (var item in response.Items) - _ = allKeys.Add(item.Key); + _ = keys.Add(item.Key); } nextToken = response?.NextToken; @@ -110,9 +132,9 @@ private HashSet ListAllKeys(string kvsArn) catch (Exception e) { Collector.EmitError("", "An error occurred while acquiring existing redirects in the KeyValueStore", e); - return []; + return false; } - return allKeys; + return true; } @@ -138,7 +160,15 @@ private string ProcessBatchUpdates( }; var responseJson = CaptureMultiple(false, 1, "aws", "cloudfront-keyvaluestore", "update-keys", "--kvs-arn", kvsArn, "--if-match", eTag, $"--{operation.ToString().ToLowerInvariant()}", payload); - var updateResponse = JsonSerializer.Deserialize(string.Concat(responseJson), AwsCloudFrontKeyValueStoreJsonContext.Default.UpdateKeysResponse); + + var concatJson = string.Concat(responseJson); + if (string.IsNullOrWhiteSpace(concatJson)) + { + Collector.EmitError("", "The output from cloudfront-keyvaluestore:update-keys was empty"); + throw new JsonException("Empty output from cloudfront-keyvaluestore:update-keys"); + } + + var updateResponse = JsonSerializer.Deserialize(concatJson, AwsCloudFrontKeyValueStoreJsonContext.Default.UpdateKeysResponse); if (string.IsNullOrEmpty(updateResponse?.ETag)) throw new Exception("Failed to get new ETag after update operation."); From a428236d979c9eb3f2df26fc60e333b5af3d3b8d Mon Sep 17 00:00:00 2001 From: Felipe Cotti Date: Fri, 26 Sep 2025 14:11:49 -0300 Subject: [PATCH 2/2] Output message if an exception occurs during the execution attempt loop. Emit as a warning since later executions can be successful. --- .../ExternalCommands/ExternalCommandExecutor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Elastic.Documentation/ExternalCommands/ExternalCommandExecutor.cs b/src/Elastic.Documentation/ExternalCommands/ExternalCommandExecutor.cs index 346d39d1a..a75c3673f 100644 --- a/src/Elastic.Documentation/ExternalCommands/ExternalCommandExecutor.cs +++ b/src/Elastic.Documentation/ExternalCommands/ExternalCommandExecutor.cs @@ -50,6 +50,7 @@ protected string[] CaptureMultiple(bool muteExceptions, int attempts, string bin } catch (Exception ex) { + collector.EmitWarning("", $"An exception occurred on attempt {i} to capture output of {binary}: {ex?.Message}"); if (ex is not null) e = ex; }