Skip to content

Commit fe64c22

Browse files
committed
Track if remote listing completed succesully when validating incremental deploy plan
1 parent 1afb37e commit fe64c22

File tree

5 files changed

+27
-7
lines changed

5 files changed

+27
-7
lines changed

src/tooling/docs-assembler/Cli/DeployCommands.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ private void AssignOutputLogger()
4040
/// <param name="out"> The file to write the plan to</param>
4141
/// <param name="deleteThreshold"> The percentage of deletions allowed in the plan as percentage of total files to sync</param>
4242
/// <param name="ctx"></param>
43+
[Command("plan")]
4344
public async Task<int> Plan(
4445
string environment,
4546
string s3BucketName,
@@ -58,7 +59,7 @@ public async Task<int> Plan(
5859
var s3Client = new AmazonS3Client();
5960
var planner = new AwsS3SyncPlanStrategy(logFactory, s3Client, s3BucketName, assembleContext);
6061
var plan = await planner.Plan(deleteThreshold, ctx);
61-
_logger.LogInformation("Total files to sync: {TotalFiles}", plan.TotalSyncRequests);
62+
_logger.LogInformation("Remote listing completed: {RemoteListingCompleted}", plan.RemoteListingCompleted);
6263
_logger.LogInformation("Total files to delete: {DeleteCount}", plan.DeleteRequests.Count);
6364
_logger.LogInformation("Total files to add: {AddCount}", plan.AddRequests.Count);
6465
_logger.LogInformation("Total files to update: {UpdateCount}", plan.UpdateRequests.Count);
@@ -70,7 +71,7 @@ public async Task<int> Plan(
7071
if (!validationResult.Valid)
7172
{
7273
await githubActionsService.SetOutputAsync("plan-valid", "false");
73-
collector.EmitError(@out, $"Plan is invalid, delete ratio: {validationResult.DeleteRatio}, threshold: {validationResult.DeleteThreshold} over {plan.TotalRemoteFiles:N0} remote files while plan has {plan.DeleteRequests:N0} deletions");
74+
collector.EmitError(@out, $"Plan is invalid, {validationResult}, delete ratio: {validationResult.DeleteRatio}, remote listing completed: {plan.RemoteListingCompleted}");
7475
await collector.StopAsync(ctx);
7576
return collector.Errors;
7677
}
@@ -93,6 +94,7 @@ public async Task<int> Plan(
9394
/// <param name="s3BucketName">The S3 bucket name to deploy to</param>
9495
/// <param name="planFile">The path to the plan file to apply</param>
9596
/// <param name="ctx"></param>
97+
[Command("apply")]
9698
public async Task<int> Apply(string environment, string s3BucketName, string planFile, Cancel ctx = default)
9799
{
98100
AssignOutputLogger();
@@ -116,6 +118,7 @@ public async Task<int> Apply(string environment, string s3BucketName, string pla
116118
}
117119
var planJson = await File.ReadAllTextAsync(planFile, ctx);
118120
var plan = SyncPlan.Deserialize(planJson);
121+
_logger.LogInformation("Remote listing completed: {RemoteListingCompleted}", plan.RemoteListingCompleted);
119122
_logger.LogInformation("Total files to sync: {TotalFiles}", plan.TotalSyncRequests);
120123
_logger.LogInformation("Total files to delete: {DeleteCount}", plan.DeleteRequests.Count);
121124
_logger.LogInformation("Total files to add: {AddCount}", plan.AddRequests.Count);
@@ -133,7 +136,7 @@ public async Task<int> Apply(string environment, string s3BucketName, string pla
133136
var validationResult = validator.Validate(plan);
134137
if (!validationResult.Valid)
135138
{
136-
collector.EmitError(planFile, $"Plan is invalid, delete ratio: {validationResult.DeleteRatio}, threshold: {validationResult.DeleteThreshold} over {plan.TotalRemoteFiles:N0} remote files while plan has {plan.DeleteRequests:N0} deletions");
139+
collector.EmitError(planFile, $"Plan is invalid, {validationResult}, delete ratio: {validationResult.DeleteRatio}, remote listing completed: {plan.RemoteListingCompleted}");
137140
await collector.StopAsync(ctx);
138141
return collector.Errors;
139142
}

src/tooling/docs-assembler/Deploying/AwsS3SyncPlanStrategy.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ private bool IsSymlink(string path)
9292

9393
public async Task<SyncPlan> Plan(float? deleteThreshold, Cancel ctx = default)
9494
{
95-
var remoteObjects = await ListObjects(ctx);
95+
var (readToCompletion, remoteObjects) = await ListObjects(ctx);
9696
var localObjects = context.OutputDirectory.GetFiles("*", SearchOption.AllDirectories)
9797
.Where(f => !IsSymlink(f.FullName))
9898
.ToArray();
@@ -156,6 +156,7 @@ await Parallel.ForEachAsync(localObjects, ctx, async (localFile, token) =>
156156

157157
return new SyncPlan
158158
{
159+
RemoteListingCompleted = readToCompletion,
159160
DeleteThresholdDefault = deleteThreshold,
160161
TotalRemoteFiles = remoteObjects.Count,
161162
TotalSourceFiles = localObjects.Length,
@@ -167,24 +168,29 @@ await Parallel.ForEachAsync(localObjects, ctx, async (localFile, token) =>
167168
};
168169
}
169170

170-
private async Task<Dictionary<string, S3Object>> ListObjects(Cancel ctx = default)
171+
private async Task<(bool readToCompletion, Dictionary<string, S3Object> objects)> ListObjects(Cancel ctx = default)
171172
{
172173
var listBucketRequest = new ListObjectsV2Request
173174
{
174175
BucketName = bucketName,
175-
MaxKeys = 1000,
176+
MaxKeys = 1000
176177
};
177178
var objects = new List<S3Object>();
179+
var readToCompletion = true;
178180
ListObjectsV2Response response;
179181
do
180182
{
181183
response = await s3Client.ListObjectsV2Async(listBucketRequest, ctx);
182184
if (response is null or { S3Objects: null })
185+
{
186+
context.Collector.EmitGlobalError("Failed to list objects in S3 to completion");
187+
readToCompletion = false;
183188
break;
189+
}
184190
objects.AddRange(response.S3Objects);
185191
listBucketRequest.ContinuationToken = response.NextContinuationToken;
186192
} while (response.IsTruncated == true);
187193

188-
return objects.ToDictionary(o => o.Key);
194+
return (readToCompletion, objects.ToDictionary(o => o.Key));
189195
}
190196
}

src/tooling/docs-assembler/Deploying/DocsSync.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ public record SyncPlan
5555
[JsonPropertyName("deletion_threshold_default")]
5656
public required float? DeleteThresholdDefault { get; init; }
5757

58+
/// The user-specified delete threshold
59+
[JsonPropertyName("remote_listing_completed")]
60+
public required bool RemoteListingCompleted { get; init; }
61+
5862
/// The total number of source files that were located in the build output
5963
[JsonPropertyName("total_source_files")]
6064
public required int TotalSourceFiles { get; init; }

src/tooling/docs-assembler/Deploying/DocsSyncPlanValidator.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ public PlanValidationResult Validate(SyncPlan plan)
1616
_logger.LogInformation("Using user-specified delete threshold of {Threshold}", plan.DeleteThresholdDefault);
1717

1818
var deleteThreshold = plan.DeleteThresholdDefault ?? 0.2f;
19+
if (!plan.RemoteListingCompleted)
20+
{
21+
_logger.LogError("Remote files were not read to completion, cannot validate deployment plan");
22+
return new(false, 1.0f, deleteThreshold);
23+
}
24+
1925
if (plan.TotalSourceFiles == 0)
2026
{
2127
_logger.LogError("No files to sync");

tests/docs-assembler.Tests/src/docs-assembler.Tests/DocsSyncTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ public async Task TestApply()
235235
var context = new AssembleContext(config, configurationContext, "dev", collector, fileSystem, fileSystem, null, checkoutDirectory);
236236
var plan = new SyncPlan
237237
{
238+
RemoteListingCompleted = true,
238239
DeleteThresholdDefault = null,
239240
TotalRemoteFiles = 0,
240241
TotalSourceFiles = 5,

0 commit comments

Comments
 (0)