Skip to content

Commit

Permalink
Add force option to dropAll DAT-16418 (#5692)
Browse files Browse the repository at this point in the history
* DAT-16418

Implement force flag for dropAll

* Test fix

DAT-16418

* Tweak message

DAT-16418

* Make requireForce be a dropAll command argument not a global

DAT-16418

* Remove global configuration option which has been moved to command

DAT-16418

* Fix all the tests

DAT-16418
  • Loading branch information
wwillard7800 committed Mar 21, 2024
1 parent 7b4ac21 commit f9e57fe
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ package liquibase.extension.testing.command
import liquibase.change.ColumnConfig
import liquibase.change.core.CreateTableChange
import liquibase.change.core.TagDatabaseChange
import liquibase.exception.CommandExecutionException
import liquibase.exception.CommandValidationException
import liquibase.extension.testing.setup.SetupCleanResources
import liquibase.extension.testing.setup.SetupEnvironmentVariableProvider

CommandTests.define {
command = ["dropAll"]
signature = """
Short Description: Drop all database objects owned by the user
Long Description: NOT SET
Required Args:
force (Boolean) Argument to allow use of dropAll with values of 'true' or 'false'. The default is 'false'.
requireForce (Boolean) Argument to require user of dropAll to supply a 'force' argument, with values of 'true' or 'false'. The default is 'false'.
url (String) The JDBC database connection URL
OBFUSCATED
Optional Args:
Expand Down Expand Up @@ -70,10 +75,157 @@ Optional Args:
]

expectedUI = [
"All objects dropped from LBUSER@jdbc:h2:mem:lbcat"
"INFO: The drop-all command may result in unrecoverable destructive changes to objects at",
"To protect against unwanted drops, set --requireForce=true, which will require a --force=true flag on the command.",
"Learn more at https://docs.liquibase.com/dropall."
]
}

run "Happy path with explicit requireDropAllForce=false", {
arguments = [
url : { it.url },
username : { it.username },
password : { it.password }
]
setup {
def add = [ LIQUIBASE_DROP_ALL_REQUIRE_FORCE:"false" ]
String[] remove = [:]
run(
new SetupEnvironmentVariableProvider(add, remove)
)
database = [
new CreateTableChange(
tableName: "FirstTable",
columns: [
ColumnConfig.fromName("FirstColumn")
.setType("VARCHAR(255)")
]
),
new CreateTableChange(
tableName: "SecondTable",
columns: [
ColumnConfig.fromName("SecondColumn")
.setType("VARCHAR(255)")
]
),
new TagDatabaseChange(
tag: "version_2.0"
),
new CreateTableChange(
tableName: "liquibaseRunInfo",
columns: [
ColumnConfig.fromName("timesRan")
.setType("INT")
]
),
]
}

expectedResults = [
statusCode : 0,
]

expectedUI = [
"INFO: The drop-all command may result in unrecoverable destructive changes to objects at",
"To protect against unwanted drops, set --requireForce=true, which will require a --force=true flag on the command.",
"Learn more at https://docs.liquibase.com/dropall."
]
}

run "Happy path with requireDropAllForce=true and force=true", {
arguments = [
url : { it.url },
username : { it.username },
password : { it.password },
force: { true }
]
setup {
def add = [ LIQUIBASE_COMMAND_DROP_ALL_REQUIRE_FORCE:"true" ]
String[] remove = [:]
run(
new SetupEnvironmentVariableProvider(add, remove)
)
database = [
new CreateTableChange(
tableName: "FirstTable",
columns: [
ColumnConfig.fromName("FirstColumn")
.setType("VARCHAR(255)")
]
),
new CreateTableChange(
tableName: "SecondTable",
columns: [
ColumnConfig.fromName("SecondColumn")
.setType("VARCHAR(255)")
]
),
new TagDatabaseChange(
tag: "version_2.0"
),
new CreateTableChange(
tableName: "liquibaseRunInfo",
columns: [
ColumnConfig.fromName("timesRan")
.setType("INT")
]
),
]
}

expectedResults = [
statusCode : 0,
]

expectedUI = [
"All objects dropped from"
]
}

run "Run with require dropAll flag set to true", {
arguments = [
url : { it.url },
username : { it.username },
password : { it.password },
requireForce: { true }
]
setup {
cleanResources(SetupCleanResources.CleanupMode.CLEAN_ON_BOTH, "liquibase.flowfile.yaml")
database = [
new CreateTableChange(
tableName: "FirstTable",
columns: [
ColumnConfig.fromName("FirstColumn")
.setType("VARCHAR(255)")
]
),
new CreateTableChange(
tableName: "SecondTable",
columns: [
ColumnConfig.fromName("SecondColumn")
.setType("VARCHAR(255)")
]
),
new TagDatabaseChange(
tag: "version_2.0"
),
new CreateTableChange(
tableName: "liquibaseRunInfo",
columns: [
ColumnConfig.fromName("timesRan")
.setType("INT")
]
),
]
}

expectedException = CommandExecutionException.class
expectedExceptionMessage =
"""
The drop-all command may result in unrecoverable destructive changes by dropping all the objects at database
"""
}

run "Run without a URL should throw an exception", {
arguments = [
url : "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public class GlobalConfiguration implements AutoloadedConfigurations {
.setDescription("Complete list of Location(s) to search for files such as changelog files in. Multiple paths can be specified by separating them with commas.")
.build();

ALWAYS_DROP_INSTEAD_OF_REPLACE = builder.define("alwaysDropInsteadOfReplace", Boolean.class)
ALWAYS_DROP_INSTEAD_OF_REPLACE = builder.define("alwaysDropInsteadOfReplace", Boolean.class)
.setDescription("If true, drop and recreate a view instead of replacing it.")
.setDefaultValue(false)
.setValueHandler(ValueHandlerUtil::booleanValueHandler)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package liquibase.command.core;

import liquibase.CatalogAndSchema;
import liquibase.GlobalConfiguration;
import liquibase.Scope;
import liquibase.changelog.ChangeLogHistoryServiceFactory;
import liquibase.command.*;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.database.Database;
import liquibase.exception.DatabaseException;
import liquibase.exception.LiquibaseException;
Expand All @@ -13,6 +15,7 @@
import liquibase.logging.Logger;
import liquibase.snapshot.SnapshotControl;
import liquibase.util.StringUtil;
import liquibase.util.ValueHandlerUtil;

import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -28,12 +31,28 @@ public class DropAllCommandStep extends AbstractCommandStep {
public static final CommandArgumentDefinition<String> SCHEMAS_ARG;
public static final CommandArgumentDefinition<CatalogAndSchema[]> CATALOG_AND_SCHEMAS_ARG;

public static final CommandArgumentDefinition<Boolean> REQUIRE_FORCE_ARG;

public static final CommandArgumentDefinition<Boolean> FORCE_ARG;

static {
CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
SCHEMAS_ARG = builder.argument("schemas", String.class).description("Schemas to include in drop").build();
CATALOG_AND_SCHEMAS_ARG = builder.argument("catalogAndSchemas", CatalogAndSchema[].class)
.description("Catalog and schemas to include in drop. It has precedence over SCHEMAS_ARG").supersededBy(SCHEMAS_ARG).hidden().build();
SCHEMAS_ARG.setSupersededBy(CATALOG_AND_SCHEMAS_ARG);
REQUIRE_FORCE_ARG = builder.argument("requireForce", Boolean.class)
.description("Argument to require user of dropAll to supply a 'force' argument, with values of 'true' or 'false'. The default is 'false'.")
.setValueHandler(ValueHandlerUtil::booleanValueHandler)
.required()
.defaultValue(false)
.build();
FORCE_ARG = builder.argument("force", Boolean.class)
.description("Argument to allow use of dropAll with values of 'true' or 'false'. The default is 'false'.")
.setValueHandler(ValueHandlerUtil::booleanValueHandler)
.required()
.defaultValue(false)
.build();
}

@Override
Expand All @@ -55,6 +74,23 @@ public List<Class<?>> requiredDependencies() {
public void run(CommandResultsBuilder resultsBuilder) throws Exception {
CommandScope commandScope = resultsBuilder.getCommandScope();
Database database = (Database) commandScope.getDependency(Database.class);
if (Boolean.FALSE.equals(commandScope.getArgumentValue(REQUIRE_FORCE_ARG))) {
String noRequirementForForceMessage =
String.format("The drop-all command may result in unrecoverable destructive changes to objects at '%s'.%n" +
"To protect against unwanted drops, set --requireForce=true, which " +
"will require a --force=true flag on the command.%nLearn more at https://docs.liquibase.com/dropall.%n",
database.getConnection().getURL());
Scope.getCurrentScope().getUI().sendMessage("INFO: " + noRequirementForForceMessage);
} else {
boolean force = commandScope.getArgumentValue(FORCE_ARG);
if (!force) {
String message =
"The drop-all command may result in unrecoverable destructive changes by dropping all the objects at database '" +
database.getConnection().getURL() +
"'. This message can be suppressed and the drop-all command executed by adding the --force flag.";
throw new LiquibaseException(message);
}
}
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();

Expand Down

0 comments on commit f9e57fe

Please sign in to comment.