Skip to content

Commit

Permalink
feat: Add an ability to delete logs based on project, folder, organiz…
Browse files Browse the repository at this point in the history
…ation or billing account resource names (#731)

* fets: Add an ability to delete logs based on project, folder, organization or billing account resource names

* Add an ability to make async and non-async delete calls in tests

* Add proper validations with meaningful error message and also fix test name typo

* Address PR comments

* Move NPE validations into deleteLogAsync from getLogName
  • Loading branch information
losalex authored Nov 3, 2021
1 parent 1996cb4 commit 25673fd
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 5 deletions.
9 changes: 9 additions & 0 deletions google-cloud-logging/clirr-ignored-differences.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- see http://www.mojohaus.org/clirr-maven-plugin/examples/ignored-differences.html -->
<differences>
<difference>
<differenceType>7012</differenceType>
<className>com/google/cloud/logging/Logging</className>
<method>* deleteLog*(java.lang.String, com.google.cloud.logging.LogDestinationName)</method>
</difference>
</differences>
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,63 @@ default ApiFuture<AsyncPage<String>> listLogsAsync(ListOption... options) {
*/
boolean deleteLog(String log);

/**
* Deletes a log and all its log entries for given log destination (see 'logName' parameter in
* https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry). The log will reappear if
* new entries are written to it.
*
* <p>Example of deleting a log by folder destination.
*
* <pre>
* {
* &#64;code
* String logName = "my_log_name";
* String folder = "my_folder";
* boolean deleted = logging.deleteLog(logName, LogDestinationName.folder(folder));
* if (deleted) {
* // the log was deleted
* } else {
* // the log was not found
* }
* }
* </pre>
*
* @return {@code true} if the log was deleted, {@code false} if it was not found
*/
default boolean deleteLog(String log, LogDestinationName destination) {
throw new UnsupportedOperationException(
"method deleteLog() does not have default implementation");
}

/**
* Sends a request for deleting a log and all its log entries for given log destination (see
* 'logName' parameter in https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry).
* This method returns a {@code ApiFuture} object to consume the result. {@link ApiFuture#get()}
* returns {@code true} if the log was deleted, {@code false} if it was not found.
*
* <p>Example of asynchronously deleting a log by folder destination.
*
* <pre>
* {
* &#64;code
* String logName = "my_log_name";
* String folder = "my_folder";
* ApiFuture<Boolean> future = logging.deleteLogAsync(logName, LogDestinationName.folder(folder));
* // ...
* boolean deleted = future.get();
* if (deleted) {
* // the log was deleted
* } else {
* // the log was not found
* }
* }
* </pre>
*/
default ApiFuture<Boolean> deleteLogAsync(String log, LogDestinationName destination) {
throw new UnsupportedOperationException(
"method deleteLogAsync() does not have default implementation");
}

/**
* Sends a request for deleting a log and all its log entries. This method returns a {@code
* ApiFuture} object to consume the result. {@link ApiFuture#get()} returns {@code true} if the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.google.cloud.logging.spi.v2.LoggingRpc;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
Expand Down Expand Up @@ -440,14 +441,27 @@ public ApiFuture<AsyncPage<String>> listLogsAsync(ListOption... options) {
}

public boolean deleteLog(String log) {
return get(deleteLogAsync(log));
return get(deleteLogAsync(log, null));
}

@Override
public boolean deleteLog(String log, LogDestinationName destination) {
return get(deleteLogAsync(log, destination));
}

public ApiFuture<Boolean> deleteLogAsync(String log) {
DeleteLogRequest request =
DeleteLogRequest.newBuilder()
.setLogName(LogName.ofProjectLogName(getOptions().getProjectId(), log).toString())
.build();
return deleteLogAsync(log, null);
}

@Override
public ApiFuture<Boolean> deleteLogAsync(String log, LogDestinationName destination) {
Preconditions.checkNotNull(log, "log parameter cannot be null");
String projectId = getOptions().getProjectId();
if (destination == null) {
Preconditions.checkNotNull(projectId, "projectId parameter cannot be null");
}
LogName name = getLogName(projectId, log, destination);
DeleteLogRequest request = DeleteLogRequest.newBuilder().setLogName(name.toString()).build();
return transform(rpc.delete(request), EMPTY_TO_BOOLEAN_FUNCTION);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,50 @@ public void testDeleteLogAsync() throws ExecutionException, InterruptedException
assertTrue(logging.deleteLogAsync(LOG_NAME).get());
}

@Test
public void testDeleteLogBillingDestination() throws ExecutionException, InterruptedException {
testDeleteByDestination(
LOG_NAME, LOG_NAME_BILLING_PATH, LogDestinationName.billingAccount(BILLING), false);
}

@Test
public void testDeleteLogBillingDestinationAsync()
throws ExecutionException, InterruptedException {
testDeleteByDestination(
LOG_NAME, LOG_NAME_BILLING_PATH, LogDestinationName.billingAccount(BILLING), true);
}

@Test
public void testDeleteLogFolderDestination() throws ExecutionException, InterruptedException {
testDeleteByDestination(
LOG_NAME, LOG_NAME_FOLDER_PATH, LogDestinationName.folder(FOLDER), false);
}

@Test
public void testDeleteLogFolderDestinationAsync()
throws ExecutionException, InterruptedException {
testDeleteByDestination(
LOG_NAME, LOG_NAME_FOLDER_PATH, LogDestinationName.folder(FOLDER), true);
}

@Test
public void testDeleteLogOrgDestination() throws ExecutionException, InterruptedException {
testDeleteByDestination(
LOG_NAME, LOG_NAME_ORGANIZATION_PATH, LogDestinationName.organization(ORGANIZATION), false);
}

@Test
public void testDeleteLogOrgDestinationAsync() throws ExecutionException, InterruptedException {
testDeleteByDestination(
LOG_NAME, LOG_NAME_ORGANIZATION_PATH, LogDestinationName.organization(ORGANIZATION), true);
}

@Test
public void testDeleteLogProjectDestination() throws ExecutionException, InterruptedException {
testDeleteByDestination(
LOG_NAME, LOG_NAME_PROJECT_PATH, LogDestinationName.project(PROJECT), false);
}

@Test
public void testDeleteLogAsync_Null() throws ExecutionException, InterruptedException {
DeleteLogRequest request =
Expand Down Expand Up @@ -2241,6 +2285,20 @@ public void run() {
assertSame(0, exceptions.get());
}

private void testDeleteByDestination(
String logId, String logName, LogDestinationName destination, boolean useAsyncDelete)
throws ExecutionException, InterruptedException {
DeleteLogRequest request = DeleteLogRequest.newBuilder().setLogName(logName).build();
ApiFuture<Empty> response = ApiFutures.immediateFuture(Empty.getDefaultInstance());
EasyMock.expect(loggingRpcMock.delete(request)).andReturn(response);
EasyMock.replay(rpcFactoryMock, loggingRpcMock);
logging = options.getService();
assertTrue(
useAsyncDelete
? logging.deleteLogAsync(logId, destination).get()
: logging.deleteLog(logId, destination));
}

private void testWriteLogEntriesWithDestination(
String projectId, String fullLogNamePath, LogDestinationName destination) {
Map<String, String> labels = ImmutableMap.of("key", "value");
Expand Down

0 comments on commit 25673fd

Please sign in to comment.