Skip to content

Commit 6c94791

Browse files
authored
[NuGet symbol server]Delete snupkg with Package Hard/soft delete (#6423)
1 parent 6aa02bc commit 6c94791

4 files changed

Lines changed: 283 additions & 25 deletions

File tree

src/NuGetGallery/Controllers/ApiController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ public virtual async Task<ActionResult> CreateSymbolPackagePutAsync()
431431

432432
// Ensure the corresponding package exists before pushing a snupkg.
433433
var package = PackageService.FindPackageByIdAndVersionStrict(id, version.ToStringSafe());
434-
if (package == null)
434+
if (package == null || package.PackageStatusKey == PackageStatus.Deleted)
435435
{
436436
return new HttpStatusCodeWithBodyResult(HttpStatusCode.NotFound, string.Format(
437437
CultureInfo.CurrentCulture,

src/NuGetGallery/Services/PackageDeleteService.cs

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ DELETE pr FROM PackageRegistrations AS pr
4343
private readonly IPackageDeleteConfiguration _config;
4444
private readonly IStatisticsService _statisticsService;
4545
private readonly ITelemetryService _telemetryService;
46+
private readonly ISymbolPackageFileService _symbolPackageFileService;
47+
private readonly ISymbolPackageService _symbolPackageService;
48+
private readonly IEntityRepository<SymbolPackage> _symbolPackageRepository;
4649

4750
public PackageDeleteService(
4851
IEntityRepository<Package> packageRepository,
@@ -55,7 +58,10 @@ public PackageDeleteService(
5558
IAuditingService auditingService,
5659
IPackageDeleteConfiguration config,
5760
IStatisticsService statisticsService,
58-
ITelemetryService telemetryService)
61+
ITelemetryService telemetryService,
62+
ISymbolPackageFileService symbolPackageFileService,
63+
ISymbolPackageService symbolPackageService,
64+
IEntityRepository<SymbolPackage> symbolPackageRepository)
5965
{
6066
_packageRepository = packageRepository ?? throw new ArgumentNullException(nameof(packageRepository));
6167
_packageRegistrationRepository = packageRegistrationRepository ?? throw new ArgumentNullException(nameof(packageRegistrationRepository));
@@ -68,6 +74,9 @@ public PackageDeleteService(
6874
_config = config ?? throw new ArgumentNullException(nameof(config));
6975
_statisticsService = statisticsService ?? throw new ArgumentNullException(nameof(statisticsService));
7076
_telemetryService = telemetryService ?? throw new ArgumentNullException(nameof(telemetryService));
77+
_symbolPackageFileService = symbolPackageFileService ?? throw new ArgumentNullException(nameof(symbolPackageFileService));
78+
_symbolPackageService = symbolPackageService ?? throw new ArgumentNullException(nameof(symbolPackageService));
79+
_symbolPackageRepository = symbolPackageRepository ?? throw new ArgumentNullException(nameof(symbolPackageRepository));
7180

7281
if (config.HourLimitWithMaximumDownloads.HasValue
7382
&& config.StatisticsUpdateFrequencyInHours.HasValue
@@ -267,6 +276,15 @@ await _packageService.UpdatePackageStatusAsync(
267276
PackageStatus.Deleted,
268277
commitChanges: false);
269278

279+
// Mark all associated symbol packages for deletion.
280+
foreach (var symbolPackage in package.SymbolPackages)
281+
{
282+
await _symbolPackageService.UpdateStatusAsync(
283+
symbolPackage,
284+
PackageStatus.Deleted,
285+
commitChanges: false);
286+
}
287+
270288
packageDelete.Packages.Add(package);
271289

272290
await _auditingService.SaveAuditRecordAsync(CreateAuditRecord(package, package.PackageRegistration, AuditedPackageAction.SoftDelete, reason));
@@ -278,6 +296,7 @@ await _packageService.UpdatePackageStatusAsync(
278296

279297
// Commit changes
280298
await _packageRepository.CommitChangesAsync();
299+
await _symbolPackageRepository.CommitChangesAsync();
281300
await _packageDeletesRepository.CommitChangesAsync();
282301
transaction.Commit();
283302
}
@@ -427,37 +446,52 @@ private async Task BackupPackageBinaries(IEnumerable<Package> packages)
427446
// Backup the package binaries and remove from main storage
428447
foreach (var package in packages)
429448
{
430-
// Backup the package from the "validating" container.
431-
using (var packageStream = await _packageFileService.DownloadValidationPackageFileAsync(package))
432-
{
433-
if (packageStream != null)
434-
{
435-
await _packageFileService.StorePackageFileInBackupLocationAsync(package, packageStream);
436-
}
437-
}
449+
// Backup the package and symbols package from the "validating" container.
450+
await BackupFromValidationsContainerAsync(_packageFileService, package);
451+
await BackupFromValidationsContainerAsync(_symbolPackageFileService, package);
438452

439-
// Backup the package from the "packages" container.
440-
using (var packageStream = await _packageFileService.DownloadPackageFileAsync(package))
441-
{
442-
if (packageStream != null)
443-
{
444-
await _packageFileService.StorePackageFileInBackupLocationAsync(package, packageStream);
445-
}
446-
}
453+
// Backup the package and symbols package from the "packages"/"symbol-packages" containers, respectively.
454+
await BackupFromPackagesContainerAsync(_packageFileService, package);
455+
await BackupFromPackagesContainerAsync(_symbolPackageFileService, package);
447456

448457
var id = package.PackageRegistration.Id;
449458
var version = string.IsNullOrEmpty(package.NormalizedVersion)
450459
? NuGetVersion.Parse(package.Version).ToNormalizedString()
451460
: package.NormalizedVersion;
452461

453462
await _packageFileService.DeletePackageFileAsync(id, version);
463+
await _symbolPackageFileService.DeletePackageFileAsync(id, version);
464+
454465
await _packageFileService.DeleteValidationPackageFileAsync(id, version);
466+
await _symbolPackageFileService.DeleteValidationPackageFileAsync(id, version);
455467

456468
// Delete readme file for this package.
457469
await TryDeleteReadMeMdFile(package);
458470
}
459471
}
460472

473+
private async Task BackupFromValidationsContainerAsync(ICorePackageFileService fileService, Package package)
474+
{
475+
using (var packageStream = await fileService.DownloadValidationPackageFileAsync(package))
476+
{
477+
if (packageStream != null)
478+
{
479+
await fileService.StorePackageFileInBackupLocationAsync(package, packageStream);
480+
}
481+
}
482+
}
483+
484+
private async Task BackupFromPackagesContainerAsync(ICorePackageFileService fileService, Package package)
485+
{
486+
using (var packageStream = await fileService.DownloadPackageFileAsync(package))
487+
{
488+
if (packageStream != null)
489+
{
490+
await fileService.StorePackageFileInBackupLocationAsync(package, packageStream);
491+
}
492+
}
493+
}
494+
461495
/// <summary>
462496
/// Delete package readme.md file, if it exists.
463497
/// </summary>

tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ public async Task CreateSymbolPackage_WillReturn400IfFileIsNotANuGetPackageInter
265265
}
266266

267267
[Fact]
268-
public async Task CreateSymbolPackage_WillReturn404IfCorrespondingPackageDoesnNotExist()
268+
public async Task CreateSymbolPackage_WillReturn404IfCorrespondingPackageDoesNotExist()
269269
{
270270
// Arrange
271271
var user = new User() { EmailAddress = "[email protected]" };
@@ -287,6 +287,31 @@ public async Task CreateSymbolPackage_WillReturn404IfCorrespondingPackageDoesnNo
287287
ResultAssert.IsStatusCode(result, HttpStatusCode.NotFound);
288288
}
289289

290+
[Fact]
291+
public async Task CreateSymbolPackage_WillReturn404IfCorrespondingPackageIsDeleted()
292+
{
293+
// Arrange
294+
var user = new User() { EmailAddress = "[email protected]" };
295+
296+
var controller = new TestableApiController(GetConfigurationService());
297+
controller.SetCurrentUser(user);
298+
299+
var nuGetPackage = TestPackage.CreateTestPackageStream("theId", "1.0.42");
300+
controller.SetupPackageFromInputStream(nuGetPackage);
301+
302+
controller.MockPackageService
303+
.Setup(x => x.FindPackageByIdAndVersionStrict(It.IsAny<string>(), It.IsAny<string>()))
304+
.Returns(new Package() {
305+
PackageStatusKey = PackageStatus.Deleted
306+
});
307+
308+
// Act
309+
ActionResult result = await controller.CreateSymbolPackagePutAsync();
310+
311+
// Assert
312+
ResultAssert.IsStatusCode(result, HttpStatusCode.NotFound);
313+
}
314+
290315
[Fact]
291316
public async Task CreateSymbolPackage_UnauthorizedUserWillGet403()
292317
{

0 commit comments

Comments
 (0)