From ca9bd061ccfdbf76247f8d6e011f1bc7f53eb520 Mon Sep 17 00:00:00 2001 From: "Neil A. Wilson" Date: Wed, 1 Sep 2021 10:32:36 -0500 Subject: [PATCH] Update ldap-diff to support legacy exit codes Updated the ldap-diff to add support for a --useLegacyExitCode argument that can be used to indicate that the tool should use the same exit codes as a legacy version of the tool. These legacy exit codes are: * 0 -- Success with no differences identified (the same as the non-legacy exit code). * 3 -- Success with one or more differences identified (as opposed to a non-legacy exit code of 5, which corresponds to the LDAP compareFalse result code). * 2 -- An argument parsing error occurred (as opposed to a non-legacy exit code of 89, which corresponds to the LDAP paramError result code). * 1 -- An unknown error occurred (as opposed to a non-legacy exit code that corresponds to the LDAP result code that most closely reflects the error that occurred). --- messages/unboundid-ldapsdk-tools.properties | 13 ++ .../ldap/sdk/unboundidds/tools/LDAPDiff.java | 78 ++++++- .../unboundidds/tools/LDAPDiffTestCase.java | 197 ++++++++++++++++++ 3 files changed, 287 insertions(+), 1 deletion(-) diff --git a/messages/unboundid-ldapsdk-tools.properties b/messages/unboundid-ldapsdk-tools.properties index ecc4620c5..a8f4d1b97 100644 --- a/messages/unboundid-ldapsdk-tools.properties +++ b/messages/unboundid-ldapsdk-tools.properties @@ -4244,6 +4244,19 @@ INFO_LDAP_DIFF_ARG_DESC_MISSING_ONLY=Only report on entries that are present \ in one of the servers but not in the other. If this option is used, then \ entries that exist in both servers will not be compared for differences. \ This can significantly reduce the length of time required to run the tool. +INFO_LDAP_DIFF_ARG_DESC_USE_LEGACY_EXIT_CODE=Use the same exit codes as a \ + legacy version of this tool. If all processing completes successfully and \ + no differences are identified, then a legacy exit code of 0 will be used \ + (which is the same in legacy and non-legacy modes). If all processing \ + completes successfully but one or more differences are identified, then a \ + legacy exit code of 3 will be used (rather than the default of 5, which \ + corresponds to the compareFalse LDAP result code). If an error occurs \ + while processing the command-line arguments, then a legacy exit code of 2 \ + will be used (rather than the default of 89, which corresponds to the \ + paramError LDAP result code). If any other error occurs, then a legacy \ + exit code of 1 will be used (rather than the default behavior of using the \ + integer value that corresponds to the LDAP result code that most closely \ + reflects the error that occurred). ERR_LDAP_DIFF_EMPTY_BASE_DN=The base DN must not be empty. ERR_LDAP_DIFF_CANNOT_SET_DEFAULT_BIND_DN=An error occurred while attempting \ to set a default value of ''{0}'' for argument ''{1}'': {2} diff --git a/src/com/unboundid/ldap/sdk/unboundidds/tools/LDAPDiff.java b/src/com/unboundid/ldap/sdk/unboundidds/tools/LDAPDiff.java index 4d4410b3b..362319343 100644 --- a/src/com/unboundid/ldap/sdk/unboundidds/tools/LDAPDiff.java +++ b/src/com/unboundid/ldap/sdk/unboundidds/tools/LDAPDiff.java @@ -200,6 +200,44 @@ public final class LDAPDiff + /** + * The legacy version of the result code that will be used to indicate that + * an error occurred while processing command-line arguments for the tool. + */ + @NotNull private static final ResultCode LEGACY_EXIT_CODE_ARG_PARSING_ERROR = + ResultCode.PROTOCOL_ERROR; + + + + /** + * The legacy version of the result code that will be used to indicate that + * all processing completed successfully, but that one or more differences + * were identified between the source and target servers. + */ + @NotNull private static final ResultCode LEGACY_EXIT_CODE_OUT_OF_SYNC = + ResultCode.TIME_LIMIT_EXCEEDED; + + + + /** + * The legacy version of the result code that will be used to indicate that + * all processing completed successfully and no differences were identified + * between the source and target servers. + */ + @NotNull private static final ResultCode LEGACY_EXIT_CODE_SUCCESS = + ResultCode.SUCCESS; + + + + /** + * The legacy version of the result code that will be used to indicate that + * an unexpected error occurred during processing. + */ + @NotNull private static final ResultCode LEGACY_EXIT_CODE_UNEXPECTED_ERROR = + ResultCode.OPERATIONS_ERROR; + + + // A reference to the tool completion message for this tool. @NotNull private final AtomicReference toolCompletionMessageRef; @@ -225,6 +263,7 @@ public final class LDAPDiff // Legacy arguments used only to provide compatibility with an older version // of this tool. @Nullable private BooleanArgument legacyTrustAllArg; + @Nullable private BooleanArgument useLegacyExitCodeArg; @Nullable private DNArgument legacySourceBindDNArg; @Nullable private FileArgument legacyKeyStorePathArg; @Nullable private FileArgument legacyKeyStorePasswordFileArg; @@ -285,7 +324,29 @@ public static ResultCode main(@Nullable final OutputStream out, @NotNull final String... args) { final LDAPDiff ldapDiff = new LDAPDiff(out, err); - return ldapDiff.runTool(args); + + ResultCode resultCode = ldapDiff.runTool(args); + if ((ldapDiff.useLegacyExitCodeArg != null) && + (ldapDiff.useLegacyExitCodeArg.isPresent())) + { + switch (resultCode.intValue()) + { + case ResultCode.SUCCESS_INT_VALUE: + resultCode = LEGACY_EXIT_CODE_SUCCESS; + break; + case ResultCode.COMPARE_FALSE_INT_VALUE: + resultCode = LEGACY_EXIT_CODE_OUT_OF_SYNC; + break; + case ResultCode.PARAM_ERROR_INT_VALUE: + resultCode = LEGACY_EXIT_CODE_ARG_PARSING_ERROR; + break; + default: + resultCode = LEGACY_EXIT_CODE_UNEXPECTED_ERROR; + break; + } + } + + return resultCode; } @@ -322,6 +383,7 @@ public LDAPDiff(@Nullable final OutputStream out, searchScopeArg = null; legacyTrustAllArg = null; + useLegacyExitCodeArg = null; legacySourceBindDNArg = null; legacyKeyStorePathArg = null; legacyKeyStorePasswordFileArg = null; @@ -601,6 +663,20 @@ public void addNonLDAPArguments(@NotNull final ArgumentParser parser) // Add legacy arguments that will be used to help provide compatibility with // an older version of this tool. + useLegacyExitCodeArg = new BooleanArgument(null, "useLegacyExitCode", 1, + INFO_LDAP_DIFF_ARG_DESC_USE_LEGACY_EXIT_CODE.get()); + useLegacyExitCodeArg.addLongIdentifier("use-legacy-exit-code", true); + useLegacyExitCodeArg.addLongIdentifier("useLegacyResultCode", true); + useLegacyExitCodeArg.addLongIdentifier("use-legacy-result-code", true); + useLegacyExitCodeArg.addLongIdentifier("legacyExitCode", true); + useLegacyExitCodeArg.addLongIdentifier("legacy-exit-code", true); + useLegacyExitCodeArg.addLongIdentifier("legacyResultCode", true); + useLegacyExitCodeArg.addLongIdentifier("legacy-result-code", true); + useLegacyExitCodeArg.setArgumentGroupName( + INFO_LDAP_DIFF_ARG_GROUP_PROCESSING_ARGS.get()); + parser.addArgument(useLegacyExitCodeArg); + + legacySourceHostArg = new StringArgument('h', null, false, 1, null, ""); legacySourceHostArg.setHidden(true); parser.addArgument(legacySourceHostArg); diff --git a/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/tools/LDAPDiffTestCase.java b/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/tools/LDAPDiffTestCase.java index 36b349f82..a9fc72964 100644 --- a/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/tools/LDAPDiffTestCase.java +++ b/tests/unit/src/com/unboundid/ldap/sdk/unboundidds/tools/LDAPDiffTestCase.java @@ -994,6 +994,203 @@ public void testConnectAndAuthenticateFailures() + /** + * Tests to ensure that the appropriate exit code is used when processing + * is successful, no differences are identified, and legacy exit codes should + * be used. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testLegacyExitCodeSuccess() + throws Exception + { + try (InMemoryDirectoryServer sourceDS = createTestDS(false, false, 0); + InMemoryDirectoryServer targetDS = createTestDS(false, false, 0)) + { + final File outputFile = createTempFile(); + assertTrue(outputFile.delete()); + + final List argsList = new ArrayList<>(); + argsList.addAll(Arrays.asList( + "--sourceHostname", "localhost", + "--sourcePort", String.valueOf(sourceDS.getListenPort()), + "--sourceBindDN", "cn=Directory Manager", + "--sourceBindPassword", "password", + "--targetHostname", "localhost", + "--targetPort", String.valueOf(targetDS.getListenPort()), + "--targetBindDN", "cn=Directory Manager", + "--targetBindPassword", "password", + "--baseDN", "dc=example,dc=com", + "--secondsBetweenPasses", "0", + "--outputLDIF", outputFile.getAbsolutePath())); + String[] argsArray = argsList.toArray(StaticUtils.NO_STRINGS); + + assertEquals(LDAPDiff.main(null, null, argsArray), + ResultCode.SUCCESS); + + assertTrue(outputFile.delete()); + argsList.add("--useLegacyExitCode"); + argsArray = argsList.toArray(StaticUtils.NO_STRINGS); + + assertEquals(LDAPDiff.main(null, null, argsArray), + ResultCode.SUCCESS); + } + } + + + + /** + * Tests to ensure that the appropriate exit code is used when processing + * is successful, differences are identified, and legacy exit codes should + * be used. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testLegacyExitCodeOutOfSync() + throws Exception + { + try (InMemoryDirectoryServer sourceDS = createTestDS(false, false, 0); + InMemoryDirectoryServer targetDS = createTestDS(false, false, 0)) + { + sourceDS.add( + "dn: dc=example,dc=com", + "objectClass: top", + "objectClass: domain", + "dc: example", + "description: source description"); + targetDS.add( + "dn: dc=example,dc=com", + "objectClass: top", + "objectClass: domain", + "dc: example", + "description: target description"); + + final File outputFile = createTempFile(); + assertTrue(outputFile.delete()); + + final List argsList = new ArrayList<>(); + argsList.addAll(Arrays.asList( + "--sourceHostname", "localhost", + "--sourcePort", String.valueOf(sourceDS.getListenPort()), + "--sourceBindDN", "cn=Directory Manager", + "--sourceBindPassword", "password", + "--targetHostname", "localhost", + "--targetPort", String.valueOf(targetDS.getListenPort()), + "--targetBindDN", "cn=Directory Manager", + "--targetBindPassword", "password", + "--baseDN", "dc=example,dc=com", + "--secondsBetweenPasses", "0", + "--outputLDIF", outputFile.getAbsolutePath())); + String[] argsArray = argsList.toArray(StaticUtils.NO_STRINGS); + + assertEquals(LDAPDiff.main(null, null, argsArray), + ResultCode.COMPARE_FALSE); + + assertTrue(outputFile.delete()); + argsList.add("--useLegacyExitCode"); + argsArray = argsList.toArray(StaticUtils.NO_STRINGS); + + assertEquals(LDAPDiff.main(null, null, argsArray), + ResultCode.TIME_LIMIT_EXCEEDED); + } + } + + + + /** + * Tests to ensure that the appropriate exit code is used when an argument + * processing error occurs and legacy exit codes should be used. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testLegacyExitCodeArgParsingError() + throws Exception + { + try (InMemoryDirectoryServer sourceDS = createTestDS(false, false, 0); + InMemoryDirectoryServer targetDS = createTestDS(false, false, 0)) + { + final File outputFile = createTempFile(); + assertTrue(outputFile.delete()); + + final List argsList = new ArrayList<>(); + argsList.addAll(Arrays.asList( + "--sourceHostname", "localhost", + "--sourcePort", String.valueOf(sourceDS.getListenPort()), + "--sourceBindDN", "cn=Directory Manager", + "--sourceBindPassword", "password", + "--targetHostname", "localhost", + "--targetPort", String.valueOf(targetDS.getListenPort()), + "--targetBindDN", "cn=Directory Manager", + "--targetBindPassword", "password", + "--baseDN", "", + "--secondsBetweenPasses", "0", + "--outputLDIF", outputFile.getAbsolutePath())); + String[] argsArray = argsList.toArray(StaticUtils.NO_STRINGS); + + assertEquals(LDAPDiff.main(null, null, argsArray), + ResultCode.PARAM_ERROR); + + argsList.add("--useLegacyExitCode"); + argsArray = argsList.toArray(StaticUtils.NO_STRINGS); + + assertEquals(LDAPDiff.main(null, null, argsArray), + ResultCode.PROTOCOL_ERROR); + } + } + + + + /** + * Tests to ensure that the appropriate exit code is used when a + * non-argument-related error occurs during processing and legacy exit codes + * should be used. + * + * @throws Exception If an unexpected problem occurs. + */ + @Test() + public void testLegacyExitCodeUnknownError() + throws Exception + { + try (InMemoryDirectoryServer sourceDS = createTestDS(false, false, 0); + InMemoryDirectoryServer targetDS = createTestDS(false, false, 0)) + { + final File outputFile = createTempFile(); + assertTrue(outputFile.delete()); + + final List argsList = new ArrayList<>(); + argsList.addAll(Arrays.asList( + "--sourceHostname", "localhost", + "--sourcePort", String.valueOf(sourceDS.getListenPort()), + "--sourceBindDN", "cn=Directory Manager", + "--sourceBindPassword", "password", + "--targetHostname", "localhost", + "--targetPort", String.valueOf(targetDS.getListenPort()), + "--targetBindDN", "cn=Directory Manager", + "--targetBindPassword", "password", + "--baseDN", "dc=example,dc=com", + "--secondsBetweenPasses", "0", + "--outputLDIF", outputFile.getAbsolutePath())); + String[] argsArray = argsList.toArray(StaticUtils.NO_STRINGS); + + sourceDS.shutDown(true); + targetDS.shutDown(true); + + assertEquals(LDAPDiff.main(null, null, argsArray), + ResultCode.CONNECT_ERROR); + + argsList.add("--useLegacyExitCode"); + argsArray = argsList.toArray(StaticUtils.NO_STRINGS); + + assertEquals(LDAPDiff.main(null, null, argsArray), + ResultCode.OPERATIONS_ERROR); + } + } + + + /** * Creates a new in-memory directory server instance with the specified * number of entries.