Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add an ability to delete logs based on project, folder, organization or billing account resource names #731

Merged
merged 6 commits into from Nov 3, 2021
Merged
9 changes: 9 additions & 0 deletions google-cloud-logging/clirr-ignored-differences.xml
@@ -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>
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";
losalex marked this conversation as resolved.
Show resolved Hide resolved
* 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
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
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 {
losalex marked this conversation as resolved.
Show resolved Hide resolved
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