mirror of
https://github.com/google/nomulus.git
synced 2025-08-04 08:52:12 +02:00
Add reporting retry, emailing and better logging
This change: - Adds retries to the staging action - Emails domain-registry-eng@ upon completion of either action - Simplifies logging to be more useful TODO: fix up Module @Inject naming conventions and yearMonth injection ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=173294822
This commit is contained in:
parent
7bc2d6badd
commit
2f539d6008
18 changed files with 374 additions and 147 deletions
|
@ -19,6 +19,7 @@ java_library(
|
|||
"//java/google/registry/util",
|
||||
"//javatests/google/registry/testing",
|
||||
"@com_google_apis_google_api_services_bigquery",
|
||||
"@com_google_appengine_api_1_0_sdk",
|
||||
"@com_google_appengine_tools_appengine_gcs_client",
|
||||
"@com_google_code_findbugs_jsr305",
|
||||
"@com_google_dagger",
|
||||
|
|
|
@ -17,7 +17,6 @@ package google.registry.reporting;
|
|||
import static com.google.common.net.MediaType.CSV_UTF_8;
|
||||
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static google.registry.testing.DatastoreHelper.createTld;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
|
@ -29,7 +28,6 @@ import com.google.api.client.testing.http.MockLowLevelHttpResponse;
|
|||
import com.google.api.client.util.Base64;
|
||||
import com.google.api.client.util.StringUtils;
|
||||
import com.google.common.io.ByteSource;
|
||||
import google.registry.request.HttpException.InternalServerErrorException;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import google.registry.testing.ExceptionRule;
|
||||
import java.io.IOException;
|
||||
|
@ -124,12 +122,7 @@ public class IcannHttpReporterTest {
|
|||
public void testFail_BadIirdeaResponse() throws Exception {
|
||||
IcannHttpReporter reporter = createReporter();
|
||||
reporter.httpTransport = createMockTransport(IIRDEA_BAD_XML);
|
||||
try {
|
||||
reporter.send(FAKE_PAYLOAD, "test-transactions-201706.csv");
|
||||
assertWithMessage("Expected InternalServerErrorException to be thrown").fail();
|
||||
} catch (InternalServerErrorException expected) {
|
||||
assertThat(expected).hasMessageThat().isEqualTo("The structure of the report is invalid.");
|
||||
}
|
||||
assertThat(reporter.send(FAKE_PAYLOAD, "test-transactions-201706.csv")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -14,14 +14,21 @@
|
|||
|
||||
package google.registry.reporting;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.bigquery.BigqueryJobFailureException;
|
||||
import google.registry.reporting.IcannReportingModule.ReportType;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.util.Retrier;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
@ -36,6 +43,7 @@ public class IcannReportingStagingActionTest {
|
|||
|
||||
FakeResponse response = new FakeResponse();
|
||||
IcannReportingStager stager = mock(IcannReportingStager.class);
|
||||
ReportingEmailUtils emailUtils = mock(ReportingEmailUtils.class);
|
||||
|
||||
@Rule
|
||||
public final AppEngineRule appEngine = AppEngineRule.builder()
|
||||
|
@ -54,6 +62,8 @@ public class IcannReportingStagingActionTest {
|
|||
action.reportTypes = reportingMode;
|
||||
action.response = response;
|
||||
action.stager = stager;
|
||||
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
|
||||
action.emailUtils = emailUtils;
|
||||
return action;
|
||||
}
|
||||
|
||||
|
@ -63,6 +73,10 @@ public class IcannReportingStagingActionTest {
|
|||
action.run();
|
||||
verify(stager).stageReports(ReportType.ACTIVITY);
|
||||
verify(stager).createAndUploadManifest(ImmutableList.of("a", "b"));
|
||||
verify(emailUtils)
|
||||
.emailResults(
|
||||
"ICANN Monthly report staging summary [SUCCESS]",
|
||||
"Completed staging the following 2 ICANN reports:\na\nb");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -73,6 +87,48 @@ public class IcannReportingStagingActionTest {
|
|||
verify(stager).stageReports(ReportType.ACTIVITY);
|
||||
verify(stager).stageReports(ReportType.TRANSACTIONS);
|
||||
verify(stager).createAndUploadManifest(ImmutableList.of("a", "b", "c", "d"));
|
||||
verify(emailUtils)
|
||||
.emailResults(
|
||||
"ICANN Monthly report staging summary [SUCCESS]",
|
||||
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryOnBigqueryException() throws Exception {
|
||||
IcannReportingStagingAction action =
|
||||
createAction(ImmutableList.of(ReportType.ACTIVITY, ReportType.TRANSACTIONS));
|
||||
when(stager.stageReports(ReportType.TRANSACTIONS))
|
||||
.thenThrow(new BigqueryJobFailureException("Expected failure", null, null, null))
|
||||
.thenReturn(ImmutableList.of("c", "d"));
|
||||
action.run();
|
||||
verify(stager, times(2)).stageReports(ReportType.ACTIVITY);
|
||||
verify(stager, times(2)).stageReports(ReportType.TRANSACTIONS);
|
||||
verify(stager).createAndUploadManifest(ImmutableList.of("a", "b", "c", "d"));
|
||||
verify(emailUtils)
|
||||
.emailResults(
|
||||
"ICANN Monthly report staging summary [SUCCESS]",
|
||||
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmailEng_onMoreThanRetriableFailure() throws Exception {
|
||||
IcannReportingStagingAction action =
|
||||
createAction(ImmutableList.of(ReportType.ACTIVITY));
|
||||
when(stager.stageReports(ReportType.ACTIVITY))
|
||||
.thenThrow(new BigqueryJobFailureException("Expected failure", null, null, null));
|
||||
try {
|
||||
action.run();
|
||||
assertWithMessage("Expected to encounter a BigqueryJobFailureException").fail();
|
||||
} catch (BigqueryJobFailureException expected) {
|
||||
// Expect the exception.
|
||||
assertThat(expected).hasMessageThat().isEqualTo("Expected failure");
|
||||
}
|
||||
verify(stager, times(3)).stageReports(ReportType.ACTIVITY);
|
||||
verify(emailUtils)
|
||||
.emailResults(
|
||||
"ICANN Monthly report staging summary [FAILURE]",
|
||||
"Staging failed due to BigqueryJobFailureException: Expected failure,"
|
||||
+ " check logs for more details.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,11 @@ import static com.google.common.truth.Truth.assertThat;
|
|||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static google.registry.testing.GcsTestingUtils.writeGcsFile;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.appengine.tools.cloudstorage.GcsFilename;
|
||||
import com.google.appengine.tools.cloudstorage.GcsService;
|
||||
|
@ -46,15 +46,14 @@ public class IcannReportingUploadActionTest {
|
|||
|
||||
@Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build();
|
||||
|
||||
private static final byte[] FAKE_PAYLOAD = "test,csv\n13,37".getBytes(UTF_8);
|
||||
private static final byte[] MANIFEST_PAYLOAD = "test-transactions-201706.csv\n".getBytes(UTF_8);
|
||||
private static final byte[] PAYLOAD_SUCCESS = "test,csv\n13,37".getBytes(UTF_8);
|
||||
private static final byte[] PAYLOAD_FAIL = "ahah,csv\n12,34".getBytes(UTF_8);
|
||||
private static final byte[] MANIFEST_PAYLOAD =
|
||||
"test-transactions-201706.csv\na-activity-201706.csv\n".getBytes(UTF_8);
|
||||
private final IcannHttpReporter mockReporter = mock(IcannHttpReporter.class);
|
||||
private final ReportingEmailUtils emailUtils = mock(ReportingEmailUtils.class);
|
||||
private final FakeResponse response = new FakeResponse();
|
||||
private final GcsService gcsService = GcsServiceFactory.createGcsService();
|
||||
private final GcsFilename reportFile =
|
||||
new GcsFilename("basin/icann/monthly/2017-06", "test-transactions-201706.csv");
|
||||
private final GcsFilename manifestFile =
|
||||
new GcsFilename("basin/icann/monthly/2017-06", "MANIFEST.txt");
|
||||
|
||||
private IcannReportingUploadAction createAction() {
|
||||
IcannReportingUploadAction action = new IcannReportingUploadAction();
|
||||
|
@ -63,40 +62,84 @@ public class IcannReportingUploadActionTest {
|
|||
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
|
||||
action.subdir = "icann/monthly/2017-06";
|
||||
action.reportingBucket = "basin";
|
||||
action.emailUtils = emailUtils;
|
||||
action.response = response;
|
||||
return action;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
writeGcsFile(gcsService, reportFile, FAKE_PAYLOAD);
|
||||
writeGcsFile(gcsService, manifestFile, MANIFEST_PAYLOAD);
|
||||
writeGcsFile(
|
||||
gcsService,
|
||||
new GcsFilename("basin/icann/monthly/2017-06", "test-transactions-201706.csv"),
|
||||
PAYLOAD_SUCCESS);
|
||||
writeGcsFile(
|
||||
gcsService,
|
||||
new GcsFilename("basin/icann/monthly/2017-06", "a-activity-201706.csv"),
|
||||
PAYLOAD_FAIL);
|
||||
writeGcsFile(
|
||||
gcsService,
|
||||
new GcsFilename("basin/icann/monthly/2017-06", "MANIFEST.txt"),
|
||||
MANIFEST_PAYLOAD);
|
||||
when(mockReporter.send(PAYLOAD_SUCCESS, "test-transactions-201706.csv")).thenReturn(true);
|
||||
when(mockReporter.send(PAYLOAD_FAIL, "a-activity-201706.csv")).thenReturn(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess() throws Exception {
|
||||
IcannReportingUploadAction action = createAction();
|
||||
action.run();
|
||||
verify(mockReporter).send(FAKE_PAYLOAD, "test-transactions-201706.csv");
|
||||
verify(mockReporter).send(PAYLOAD_SUCCESS, "test-transactions-201706.csv");
|
||||
verify(mockReporter).send(PAYLOAD_FAIL, "a-activity-201706.csv");
|
||||
verifyNoMoreInteractions(mockReporter);
|
||||
assertThat(((FakeResponse) action.response).getPayload())
|
||||
.isEqualTo("OK, sending: test,csv\n13,37");
|
||||
.isEqualTo("OK, attempted uploading 2 reports");
|
||||
verify(emailUtils)
|
||||
.emailResults(
|
||||
"ICANN Monthly report upload summary: 1/2 succeeded",
|
||||
"Report Filename - Upload status:\n"
|
||||
+ "test-transactions-201706.csv - SUCCESS\n"
|
||||
+ "a-activity-201706.csv - FAILURE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_WithRetry() throws Exception {
|
||||
IcannReportingUploadAction action = createAction();
|
||||
doThrow(new IOException("Expected exception."))
|
||||
.doNothing()
|
||||
.when(mockReporter)
|
||||
.send(FAKE_PAYLOAD, "test-transactions-201706.csv");
|
||||
when(mockReporter.send(PAYLOAD_SUCCESS, "test-transactions-201706.csv"))
|
||||
.thenThrow(new IOException("Expected exception."))
|
||||
.thenReturn(true);
|
||||
action.run();
|
||||
verify(mockReporter, times(2)).send(FAKE_PAYLOAD, "test-transactions-201706.csv");
|
||||
verify(mockReporter, times(2)).send(PAYLOAD_SUCCESS, "test-transactions-201706.csv");
|
||||
verify(mockReporter).send(PAYLOAD_FAIL, "a-activity-201706.csv");
|
||||
verifyNoMoreInteractions(mockReporter);
|
||||
assertThat(((FakeResponse) action.response).getPayload())
|
||||
.isEqualTo("OK, sending: test,csv\n13,37");
|
||||
.isEqualTo("OK, attempted uploading 2 reports");
|
||||
verify(emailUtils)
|
||||
.emailResults(
|
||||
"ICANN Monthly report upload summary: 1/2 succeeded",
|
||||
"Report Filename - Upload status:\n"
|
||||
+ "test-transactions-201706.csv - SUCCESS\n"
|
||||
+ "a-activity-201706.csv - FAILURE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailure_firstUnrecoverable_stillAttemptsUploadingBoth() throws Exception {
|
||||
IcannReportingUploadAction action = createAction();
|
||||
when(mockReporter.send(PAYLOAD_SUCCESS, "test-transactions-201706.csv"))
|
||||
.thenThrow(new IOException("Expected exception"));
|
||||
action.run();
|
||||
verify(mockReporter, times(3)).send(PAYLOAD_SUCCESS, "test-transactions-201706.csv");
|
||||
verify(mockReporter).send(PAYLOAD_FAIL, "a-activity-201706.csv");
|
||||
verifyNoMoreInteractions(mockReporter);
|
||||
assertThat(((FakeResponse) action.response).getPayload())
|
||||
.isEqualTo("OK, attempted uploading 2 reports");
|
||||
verify(emailUtils)
|
||||
.emailResults(
|
||||
"ICANN Monthly report upload summary: 0/2 succeeded",
|
||||
"Report Filename - Upload status:\n"
|
||||
+ "test-transactions-201706.csv - FAILURE\n"
|
||||
+ "a-activity-201706.csv - FAILURE");
|
||||
}
|
||||
@Test
|
||||
public void testFail_FileNotFound() throws Exception {
|
||||
IcannReportingUploadAction action = createAction();
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.reporting;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import google.registry.util.SendEmailService;
|
||||
import java.util.Properties;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.Message.RecipientType;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/** Unit tests for {@link ReportingEmailUtils}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class ReportingEmailUtilsTest {
|
||||
private Message msg;
|
||||
private final SendEmailService emailService = mock(SendEmailService.class);
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
msg = new MimeMessage(Session.getDefaultInstance(new Properties(), null));
|
||||
when(emailService.createMessage()).thenReturn(msg);
|
||||
}
|
||||
|
||||
private ReportingEmailUtils createEmailUtil() {
|
||||
ReportingEmailUtils emailUtils = new ReportingEmailUtils();
|
||||
emailUtils.sender = "test-project.appspotmail.com";
|
||||
emailUtils.recipient = "email@example.com";
|
||||
emailUtils.emailService = emailService;
|
||||
return emailUtils;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_sendsEmail() throws Exception {
|
||||
ReportingEmailUtils emailUtils = createEmailUtil();
|
||||
emailUtils.emailResults("Subject", "Body");
|
||||
|
||||
assertThat(msg.getFrom()).hasLength(1);
|
||||
assertThat(msg.getFrom()[0])
|
||||
.isEqualTo(new InternetAddress("test-project.appspotmail.com"));
|
||||
assertThat(msg.getRecipients(RecipientType.TO)).hasLength(1);
|
||||
assertThat(msg.getRecipients(RecipientType.TO)[0])
|
||||
.isEqualTo(new InternetAddress("email@example.com"));
|
||||
assertThat(msg.getSubject()).isEqualTo("Subject");
|
||||
assertThat(msg.getContentType()).isEqualTo("text/plain");
|
||||
assertThat(msg.getContent().toString()).isEqualTo("Body");
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.reporting;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import google.registry.reporting.IcannReportingModule.ReportType;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/** Unit tests for {@link google.registry.reporting.ReportingUtils}. */
|
||||
@RunWith(JUnit4.class)
|
||||
public class ReportingUtilsTest {
|
||||
@Test
|
||||
public void testCreateFilename_success() {
|
||||
assertThat(ReportingUtils.createFilename("test", "2017-06", ReportType.ACTIVITY))
|
||||
.isEqualTo("test-activity-201706.csv");
|
||||
assertThat(ReportingUtils.createFilename("foo", "2017-06", ReportType.TRANSACTIONS))
|
||||
.isEqualTo("foo-transactions-201706.csv");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateBucketName_success() {
|
||||
assertThat(ReportingUtils.createReportingBucketName("gs://domain-registry-basin", "my/subdir"))
|
||||
.isEqualTo("gs://domain-registry-basin/my/subdir");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue