diff --git a/core/src/main/java/google/registry/model/rde/RdeRevision.java b/core/src/main/java/google/registry/model/rde/RdeRevision.java index 231477e3f..58b0afdb1 100644 --- a/core/src/main/java/google/registry/model/rde/RdeRevision.java +++ b/core/src/main/java/google/registry/model/rde/RdeRevision.java @@ -103,6 +103,15 @@ public final class RdeRevision extends BackupGroupRoot implements NonReplicatedE return revisionOptional.map(rdeRevision -> rdeRevision.revision + 1).orElse(0); } + /** Returns the latest revision of the report already generated for the given triplet. */ + public static Optional getCurrentRevision(String tld, DateTime date, RdeMode mode) { + int nextRevision = getNextRevision(tld, date, mode); + if (nextRevision == 0) { + return Optional.empty(); + } + return Optional.of(nextRevision - 1); + } + /** * Sets the revision ID for a given triplet. * diff --git a/core/src/main/java/google/registry/rde/RdeReportAction.java b/core/src/main/java/google/registry/rde/RdeReportAction.java index 2bf89ff6b..72112a71b 100644 --- a/core/src/main/java/google/registry/rde/RdeReportAction.java +++ b/core/src/main/java/google/registry/rde/RdeReportAction.java @@ -24,6 +24,7 @@ import static google.registry.request.Action.Method.POST; import static google.registry.util.DateTimeUtils.isBeforeOrAt; import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.common.flogger.FluentLogger; import com.google.common.io.ByteStreams; import google.registry.config.RegistryConfig.Config; import google.registry.gcs.GcsUtils; @@ -31,6 +32,7 @@ import google.registry.keyring.api.KeyModule.Key; import google.registry.model.common.Cursor; import google.registry.model.common.Cursor.CursorType; import google.registry.model.rde.RdeNamingUtils; +import google.registry.model.rde.RdeRevision; import google.registry.model.registry.Registry; import google.registry.rde.EscrowTaskRunner.EscrowTask; import google.registry.request.Action; @@ -57,6 +59,8 @@ import org.joda.time.Duration; auth = Auth.AUTH_INTERNAL_OR_ADMIN) public final class RdeReportAction implements Runnable, EscrowTask { + private static final FluentLogger logger = FluentLogger.forEnclosingClass(); + static final String PATH = "/_dr/task/rdeReport"; @Inject GcsUtils gcsUtils; @@ -88,12 +92,17 @@ public final class RdeReportAction implements Runnable, EscrowTask { + "last upload completion was at %s", tld, watermark, cursorTime)); } - String prefix = RdeNamingUtils.makeRydeFilename(tld, watermark, FULL, 1, 0); + int revision = + RdeRevision.getCurrentRevision(tld, watermark, FULL) + .orElseThrow( + () -> new IllegalStateException("RdeRevision was not set on generated deposit")); + String prefix = RdeNamingUtils.makeRydeFilename(tld, watermark, FULL, 1, revision); GcsFilename reportFilename = new GcsFilename(bucket, prefix + "-report.xml.ghostryde"); verify(gcsUtils.existsAndNotEmpty(reportFilename), "Missing file: %s", reportFilename); reporter.send(readReportFromGcs(reportFilename)); response.setContentType(PLAIN_TEXT_UTF_8); response.setPayload(String.format("OK %s %s\n", tld, watermark)); + logger.atInfo().log("Successfully sent report %s.", reportFilename); } /** Reads and decrypts the XML file from cloud storage. */ diff --git a/core/src/main/java/google/registry/rde/RdeUploadAction.java b/core/src/main/java/google/registry/rde/RdeUploadAction.java index 004017081..4ffcf5e0d 100644 --- a/core/src/main/java/google/registry/rde/RdeUploadAction.java +++ b/core/src/main/java/google/registry/rde/RdeUploadAction.java @@ -160,8 +160,10 @@ public final class RdeUploadAction implements Runnable, EscrowTask { sftpCursorTime, timeSinceLastSftp.getStandardMinutes())); } - int revision = RdeRevision.getNextRevision(tld, watermark, FULL) - 1; - verify(revision >= 0, "RdeRevision was not set on generated deposit"); + int revision = + RdeRevision.getCurrentRevision(tld, watermark, FULL) + .orElseThrow( + () -> new IllegalStateException("RdeRevision was not set on generated deposit")); final String name = RdeNamingUtils.makeRydeFilename(tld, watermark, FULL, 1, revision); final GcsFilename xmlFilename = new GcsFilename(bucket, name + ".xml.ghostryde"); final GcsFilename xmlLengthFilename = new GcsFilename(bucket, name + ".xml.length"); diff --git a/core/src/test/java/google/registry/rde/RdeReportActionTest.java b/core/src/test/java/google/registry/rde/RdeReportActionTest.java index 37106ae83..81f06d72c 100644 --- a/core/src/test/java/google/registry/rde/RdeReportActionTest.java +++ b/core/src/test/java/google/registry/rde/RdeReportActionTest.java @@ -19,10 +19,12 @@ import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.common.Cursor.CursorType.RDE_REPORT; import static google.registry.model.common.Cursor.CursorType.RDE_UPLOAD; +import static google.registry.model.rde.RdeMode.FULL; import static google.registry.persistence.transaction.TransactionManagerFactory.tm; import static google.registry.testing.DatabaseHelper.createTld; import static google.registry.testing.DatabaseHelper.loadByKey; import static google.registry.testing.DatabaseHelper.persistResource; +import static google.registry.testing.GcsTestingUtils.deleteGcsFile; import static google.registry.testing.GcsTestingUtils.writeGcsFile; import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import static javax.servlet.http.HttpServletResponse.SC_OK; @@ -47,6 +49,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteSource; import google.registry.gcs.GcsUtils; import google.registry.model.common.Cursor; +import google.registry.model.rde.RdeRevision; import google.registry.model.registry.Registry; import google.registry.request.HttpException.InternalServerErrorException; import google.registry.request.HttpException.NoContentException; @@ -124,6 +127,7 @@ public class RdeReportActionTest { persistResource( Cursor.create(RDE_UPLOAD, DateTime.parse("2006-06-07TZ"), Registry.get("test"))); writeGcsFile(gcsService, reportFile, Ghostryde.encode(REPORT_XML.read(), encryptKey)); + tm().transact(() -> RdeRevision.saveRevision("test", DateTime.parse("2006-06-06TZ"), FULL, 0)); } @TestOfyAndSql @@ -164,6 +168,20 @@ public class RdeReportActionTest { } @TestOfyAndSql + void testRunWithLock_regeneratedReport() throws Exception { + deleteGcsFile(gcsService, reportFile); + GcsFilename newReport = + new GcsFilename("tub", "test_2006-06-06_full_S1_R1-report.xml.ghostryde"); + PGPPublicKey encryptKey = new FakeKeyringModule().get().getRdeStagingEncryptionKey(); + writeGcsFile(gcsService, newReport, Ghostryde.encode(REPORT_XML.read(), encryptKey)); + tm().transact(() -> RdeRevision.saveRevision("test", DateTime.parse("2006-06-06TZ"), FULL, 1)); + when(httpResponse.getResponseCode()).thenReturn(SC_OK); + when(httpResponse.getContent()).thenReturn(IIRDEA_GOOD_XML.read()); + when(urlFetchService.fetch(request.capture())).thenReturn(httpResponse); + createAction().runWithLock(loadRdeReportCursor()); + assertThat(response.getStatus()).isEqualTo(200); + } + void testRunWithLock_nonexistentCursor_throws204() { tm().transact(() -> tm().delete(Cursor.createVKey(RDE_UPLOAD, "test"))); NoContentException thrown = diff --git a/core/src/test/java/google/registry/testing/GcsTestingUtils.java b/core/src/test/java/google/registry/testing/GcsTestingUtils.java index 8f0befc86..a51bb3dfe 100644 --- a/core/src/test/java/google/registry/testing/GcsTestingUtils.java +++ b/core/src/test/java/google/registry/testing/GcsTestingUtils.java @@ -40,5 +40,9 @@ public final class GcsTestingUtils { gcsService.createOrReplace(file, GcsFileOptions.getDefaultInstance(), ByteBuffer.wrap(data)); } + public static void deleteGcsFile(GcsService gcsService, GcsFilename file) throws IOException { + gcsService.delete(file); + } + private GcsTestingUtils() {} }