Improve usability of WipeOutCloudSqlAction (#1118)

* Improve usability of WipeOutCloudSqlAction

Replace the "drop owned" statement with ones that drops only tables and
sequences. The former statement also drops default grants for the
nomulus user, which must be restored before the database can be used by
the nomulus server and tools.
This commit is contained in:
Weimin Yu
2021-04-29 23:09:20 -04:00
committed by GitHub
parent 1fdf9cb979
commit 431710c95b
3 changed files with 184 additions and 14 deletions
@@ -19,6 +19,7 @@ import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static javax.servlet.http.HttpServletResponse.SC_OK;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.times;
@@ -32,8 +33,10 @@ import google.registry.testing.FakeResponse;
import google.registry.testing.FakeSleeper;
import google.registry.util.Retrier;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.flywaydb.core.api.FlywayException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -46,6 +49,8 @@ public class WipeOutCloudSqlActionTest {
@Mock private Statement stmt;
@Mock private Connection conn;
@Mock private DatabaseMetaData metaData;
@Mock private ResultSet resultSet;
private FakeResponse response = new FakeResponse();
private Retrier retrier = new Retrier(new FakeSleeper(new FakeClock()), 2);
@@ -53,6 +58,17 @@ public class WipeOutCloudSqlActionTest {
@BeforeEach
void beforeEach() throws Exception {
lenient().when(conn.createStatement()).thenReturn(stmt);
lenient().when(conn.getMetaData()).thenReturn(metaData);
lenient()
.when(
metaData.getTables(
nullable(String.class),
nullable(String.class),
nullable(String.class),
nullable(String[].class)))
.thenReturn(resultSet);
lenient().when(stmt.executeQuery(anyString())).thenReturn(resultSet);
lenient().when(resultSet.next()).thenReturn(false);
}
@Test
@@ -61,7 +77,7 @@ public class WipeOutCloudSqlActionTest {
new WipeOutCloudSqlAction("domain-registry-qa", () -> conn, response, retrier);
action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK);
verify(stmt, times(1)).execute(anyString());
verify(stmt, times(1)).executeQuery(anyString());
verify(stmt, times(1)).close();
verifyNoMoreInteractions(stmt);
}
@@ -77,25 +93,23 @@ public class WipeOutCloudSqlActionTest {
@Test
void run_nonRetrieableFailure() throws Exception {
doThrow(new FlywayException()).when(stmt).execute(anyString());
doThrow(new SQLException()).when(conn).getMetaData();
WipeOutCloudSqlAction action =
new WipeOutCloudSqlAction("domain-registry-qa", () -> conn, response, retrier);
action.run();
assertThat(response.getStatus()).isEqualTo(SC_INTERNAL_SERVER_ERROR);
verify(stmt, times(1)).execute(anyString());
verify(stmt, times(1)).close();
verifyNoMoreInteractions(stmt);
verifyNoInteractions(stmt);
}
@Test
void run_retrieableFailure() throws Exception {
when(stmt.execute(anyString())).thenThrow(new RuntimeException()).thenReturn(true);
when(conn.getMetaData()).thenThrow(new RuntimeException()).thenReturn(metaData);
WipeOutCloudSqlAction action =
new WipeOutCloudSqlAction("domain-registry-qa", () -> conn, response, retrier);
action.run();
assertThat(response.getStatus()).isEqualTo(SC_OK);
verify(stmt, times(2)).execute(anyString());
verify(stmt, times(2)).close();
verify(stmt, times(1)).executeQuery(anyString());
verify(stmt, times(1)).close();
verifyNoMoreInteractions(stmt);
}
}
@@ -0,0 +1,89 @@
// Copyright 2021 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.batch;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import google.registry.persistence.NomulusPostgreSql;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Properties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
/** Tests the database wipeout mechanism used by {@link WipeOutCloudSqlAction}. */
@Testcontainers
public class WipeOutCloudSqlIntegrationTest {
@Container
PostgreSQLContainer container = new PostgreSQLContainer(NomulusPostgreSql.getDockerTag());
private Connection getJdbcConnection() throws Exception {
Properties properties = new Properties();
properties.setProperty("user", container.getUsername());
properties.setProperty("password", container.getPassword());
return container.getJdbcDriverInstance().connect(container.getJdbcUrl(), properties);
}
@BeforeEach
void beforeEach() throws Exception {
try (Connection conn = getJdbcConnection();
Statement statement = conn.createStatement()) {
statement.addBatch("CREATE TABLE public.\"Domain\" (value int);");
statement.addBatch("CREATE SEQUENCE public.\"Domain_seq\"");
statement.executeBatch();
}
}
@Test
void listTables() throws Exception {
try (Connection conn = getJdbcConnection()) {
ImmutableList<String> tables = WipeOutCloudSqlAction.listTables(conn);
assertThat(tables).containsExactly("public.\"Domain\"");
}
}
@Test
void dropAllTables() throws Exception {
try (Connection conn = getJdbcConnection()) {
ImmutableList<String> tables = WipeOutCloudSqlAction.listTables(conn);
assertThat(tables).isNotEmpty();
WipeOutCloudSqlAction.dropAllTables(conn, tables);
assertThat(WipeOutCloudSqlAction.listTables(conn)).isEmpty();
}
}
@Test
void listAllSequences() throws Exception {
try (Connection conn = getJdbcConnection()) {
ImmutableList<String> sequences = WipeOutCloudSqlAction.listSequences(conn);
assertThat(sequences).containsExactly("\"Domain_seq\"");
}
}
@Test
void dropAllSequences() throws Exception {
try (Connection conn = getJdbcConnection()) {
ImmutableList<String> sequences = WipeOutCloudSqlAction.listSequences(conn);
assertThat(sequences).isNotEmpty();
WipeOutCloudSqlAction.dropAllSequences(conn, sequences);
assertThat(WipeOutCloudSqlAction.listSequences(conn)).isEmpty();
}
}
}