diff --git a/src/main/kotlin/org/opensearch/replication/action/autofollow/TransportAutoFollowClusterManagerNodeAction.kt b/src/main/kotlin/org/opensearch/replication/action/autofollow/TransportAutoFollowClusterManagerNodeAction.kt index 51a0bd06..b0f85838 100644 --- a/src/main/kotlin/org/opensearch/replication/action/autofollow/TransportAutoFollowClusterManagerNodeAction.kt +++ b/src/main/kotlin/org/opensearch/replication/action/autofollow/TransportAutoFollowClusterManagerNodeAction.kt @@ -111,6 +111,7 @@ class TransportAutoFollowClusterManagerNodeAction @Inject constructor(transportS } catch(e: ResourceAlreadyExistsException) { // Log and bail as task is already running log.warn("Task already started for '$clusterAlias:$patternName'", e) + throw OpenSearchException("Exisiting autofollow replication rule cannot be recreated/updated", e) } catch (e: Exception) { log.error("Failed to start auto follow task for cluster '$clusterAlias:$patternName'", e) throw OpenSearchException(AUTOFOLLOW_EXCEPTION_GENERIC_STRING) diff --git a/src/test/kotlin/org/opensearch/replication/integ/rest/UpdateAutoFollowPatternIT.kt b/src/test/kotlin/org/opensearch/replication/integ/rest/UpdateAutoFollowPatternIT.kt index 78e4ead4..7b8efee0 100644 --- a/src/test/kotlin/org/opensearch/replication/integ/rest/UpdateAutoFollowPatternIT.kt +++ b/src/test/kotlin/org/opensearch/replication/integ/rest/UpdateAutoFollowPatternIT.kt @@ -115,7 +115,7 @@ class UpdateAutoFollowPatternIT: MultiClusterRestTestCase() { try { // Set poll duration to 30sec from 60sec (default) val settings = Settings.builder().put(ReplicationPlugin.REPLICATION_AUTOFOLLOW_REMOTE_INDICES_POLL_INTERVAL.key, - TimeValue.timeValueSeconds(30)) + TimeValue.timeValueSeconds(30)) val clusterUpdateSetttingsReq = ClusterUpdateSettingsRequest().persistentSettings(settings) val clusterUpdateResponse = followerClient.cluster().putSettings(clusterUpdateSetttingsReq, RequestOptions.DEFAULT) var lastExecutionTime = 0L @@ -138,12 +138,12 @@ class UpdateAutoFollowPatternIT: MultiClusterRestTestCase() { } }, 30, TimeUnit.SECONDS) assertBusy({ - var af_stats = stats.get("autofollow_stats")!! as ArrayList> - for (key in af_stats) { - if(key["name"] == indexPatternName) { - Assertions.assertThat(key["last_execution_time"]!! as Long).isNotEqualTo(lastExecutionTime) + var af_stats = stats.get("autofollow_stats")!! as ArrayList> + for (key in af_stats) { + if(key["name"] == indexPatternName) { + Assertions.assertThat(key["last_execution_time"]!! as Long).isNotEqualTo(lastExecutionTime) + } } - } }, 40, TimeUnit.SECONDS) } finally { followerClient.deleteAutoFollowPattern(connectionAlias, indexPatternName) @@ -184,7 +184,7 @@ class UpdateAutoFollowPatternIT: MultiClusterRestTestCase() { followerClient.deleteAutoFollowPattern(connectionAlias, indexPatternName) } } - + fun `test auto follow stats`() { val indexPatternName2 = "test_pattern2" val indexPattern2 = "lead_index*" @@ -263,35 +263,35 @@ class UpdateAutoFollowPatternIT: MultiClusterRestTestCase() { val followerClient = getClientForCluster(FOLLOWER) createConnectionBetweenClusters(FOLLOWER, LEADER, connectionAlias) assertPatternNameValidation(followerClient, "testPattern", - "Value testPattern must be lowercase") + "Value testPattern must be lowercase") assertPatternNameValidation(followerClient, "testPattern*", - "Value testPattern* must not contain the following characters") + "Value testPattern* must not contain the following characters") assertPatternNameValidation(followerClient, "test#", - "Value test# must not contain '#' or ':'") + "Value test# must not contain '#' or ':'") assertPatternNameValidation(followerClient, "test:", - "Value test: must not contain '#' or ':'") + "Value test: must not contain '#' or ':'") assertPatternNameValidation(followerClient, ".", - "Value . must not be '.' or '..'") + "Value . must not be '.' or '..'") assertPatternNameValidation(followerClient, "..", - "Value .. must not be '.' or '..'") + "Value .. must not be '.' or '..'") assertPatternNameValidation(followerClient, "_leader", - "Value _leader must not start with '_' or '-' or '+'") + "Value _leader must not start with '_' or '-' or '+'") assertPatternNameValidation(followerClient, "-leader", - "Value -leader must not start with '_' or '-' or '+'") + "Value -leader must not start with '_' or '-' or '+'") assertPatternNameValidation(followerClient, "+leader", - "Value +leader must not start with '_' or '-' or '+'") + "Value +leader must not start with '_' or '-' or '+'") assertPatternNameValidation(followerClient, longIndexPatternName, - "Value $longIndexPatternName must not be longer than ${MetadataCreateIndexService.MAX_INDEX_NAME_BYTES} bytes") + "Value $longIndexPatternName must not be longer than ${MetadataCreateIndexService.MAX_INDEX_NAME_BYTES} bytes") assertPatternNameValidation(followerClient, ".leaderIndex", - "Value .leaderIndex must not start with '.'") + "Value .leaderIndex must not start with '.'") } private fun assertPatternNameValidation(followerClient: RestHighLevelClient, patternName: String, - errorMsg: String) { + errorMsg: String) { Assertions.assertThatThrownBy { followerClient.updateAutoFollowPattern(connectionAlias, patternName, indexPattern) }.isInstanceOf(ResponseException::class.java) - .hasMessageContaining(errorMsg) + .hasMessageContaining(errorMsg) } fun `test deletion of auto follow pattern`() { @@ -308,6 +308,32 @@ class UpdateAutoFollowPatternIT: MultiClusterRestTestCase() { followerClient.deleteAutoFollowPattern(connectionAlias, indexPatternName) }.doesNotThrowAnyException() + } + fun `test updation of auto follow pattern`() { + val followerClient = getClientForCluster(FOLLOWER) + createConnectionBetweenClusters(FOLLOWER, LEADER, connectionAlias) + followerClient.updateAutoFollowPattern(connectionAlias, indexPatternName, indexPattern) + val indexPattern1 = "log*" + //Re-create the same replication rule + Assertions.assertThatThrownBy { + followerClient.updateAutoFollowPattern(connectionAlias, indexPatternName, indexPattern) + }.isInstanceOf(ResponseException::class.java) + .hasMessageContaining("autofollow replication rule cannot be recreated/updated") + + //Update the replication rule with different indexpattern + Assertions.assertThatThrownBy { + followerClient.updateAutoFollowPattern(connectionAlias, indexPatternName, indexPattern1) + }.isInstanceOf(ResponseException::class.java) + .hasMessageContaining("autofollow replication rule cannot be recreated/updated") + + //Create a new replication rule with same indexpattern but unique rule name + Assertions.assertThatCode { + followerClient.updateAutoFollowPattern(connectionAlias, "unique-rule", indexPattern1) + }.doesNotThrowAnyException() + + followerClient.deleteAutoFollowPattern(connectionAlias, indexPatternName) + followerClient.deleteAutoFollowPattern(connectionAlias, "unique-rule") + } fun `test removing autofollow pattern stop autofollow task`() { @@ -344,12 +370,12 @@ class UpdateAutoFollowPatternIT: MultiClusterRestTestCase() { try { //modify retry duration to account for autofollow trigger in next retry followerClient.updateAutofollowRetrySetting("1m") + followerClient.updateAutoFollowPattern(connectionAlias, indexPatternName, indexPattern) for (repeat in 1..2) { log.info("Current Iteration $repeat") // Add replication start block followerClient.updateReplicationStartBlockSetting(true) createRandomIndex(leaderClient) - followerClient.updateAutoFollowPattern(connectionAlias, indexPatternName, indexPattern) sleep(95000) // wait for auto follow trigger in the worst case // verify both index replication tasks and autofollow tasks // Replication shouldn't have been started - (repeat-1) tasks as for current loop index shouldn't be @@ -392,13 +418,13 @@ class UpdateAutoFollowPatternIT: MultiClusterRestTestCase() { // Verify that existing index matching the pattern are replicated. assertBusy { Assertions.assertThat(followerClient.indices() - .exists(GetIndexRequest(leaderIndexName1), RequestOptions.DEFAULT)) - .isEqualTo(true) + .exists(GetIndexRequest(leaderIndexName1), RequestOptions.DEFAULT)) + .isEqualTo(true) } assertBusy { Assertions.assertThat(followerClient.indices() - .exists(GetIndexRequest(leaderIndexName2), RequestOptions.DEFAULT)) - .isEqualTo(true) + .exists(GetIndexRequest(leaderIndexName2), RequestOptions.DEFAULT)) + .isEqualTo(true) } sleep(30000) // Default poll for auto follow in worst case Assertions.assertThat(getAutoFollowTasks(FOLLOWER).size).isEqualTo(1)