|
15 | 15 | import java.io.IOException; |
16 | 16 | import java.nio.file.Path; |
17 | 17 | import java.time.Instant; |
| 18 | +import java.util.Collections; |
18 | 19 | import java.util.Set; |
| 20 | +import java.util.concurrent.ExecutorService; |
19 | 21 | import java.util.concurrent.TimeoutException; |
20 | 22 |
|
21 | 23 | import com.fasterxml.jackson.databind.InjectableValues; |
|
34 | 36 | import org.opensearch.cluster.ClusterChangedEvent; |
35 | 37 | import org.opensearch.cluster.ClusterState; |
36 | 38 | import org.opensearch.cluster.ClusterStateUpdateTask; |
| 39 | +import org.opensearch.cluster.RestoreInProgress; |
37 | 40 | import org.opensearch.cluster.block.ClusterBlocks; |
38 | 41 | import org.opensearch.cluster.metadata.IndexMetadata; |
39 | 42 | import org.opensearch.cluster.metadata.MappingMetadata; |
40 | 43 | import org.opensearch.cluster.metadata.Metadata; |
41 | 44 | import org.opensearch.cluster.node.DiscoveryNode; |
42 | 45 | import org.opensearch.cluster.node.DiscoveryNodes; |
| 46 | +import org.opensearch.cluster.routing.ShardRouting; |
43 | 47 | import org.opensearch.cluster.service.ClusterService; |
44 | 48 | import org.opensearch.common.Priority; |
45 | 49 | import org.opensearch.common.settings.Settings; |
46 | 50 | import org.opensearch.core.action.ActionListener; |
| 51 | +import org.opensearch.core.index.Index; |
| 52 | +import org.opensearch.core.index.shard.ShardId; |
47 | 53 | import org.opensearch.core.rest.RestStatus; |
| 54 | +import org.opensearch.index.shard.IndexShard; |
48 | 55 | import org.opensearch.security.DefaultObjectMapper; |
49 | 56 | import org.opensearch.security.auditlog.AuditLog; |
50 | 57 | import org.opensearch.security.securityconf.DynamicConfigFactory; |
|
63 | 70 |
|
64 | 71 | import org.mockito.ArgumentCaptor; |
65 | 72 | import org.mockito.Mock; |
| 73 | +import org.mockito.Mockito; |
66 | 74 | import org.mockito.junit.MockitoJUnitRunner; |
67 | 75 | import org.mockito.stubbing.OngoingStubbing; |
68 | 76 |
|
|
82 | 90 | import static org.mockito.Mockito.anyString; |
83 | 91 | import static org.mockito.Mockito.doAnswer; |
84 | 92 | import static org.mockito.Mockito.doCallRealMethod; |
| 93 | +import static org.mockito.Mockito.doReturn; |
85 | 94 | import static org.mockito.Mockito.mock; |
86 | 95 | import static org.mockito.Mockito.never; |
87 | 96 | import static org.mockito.Mockito.reset; |
| 97 | +import static org.mockito.Mockito.spy; |
88 | 98 | import static org.mockito.Mockito.times; |
89 | 99 | import static org.mockito.Mockito.verify; |
90 | 100 | import static org.mockito.Mockito.verifyNoMoreInteractions; |
@@ -183,6 +193,20 @@ private ConfigurationRepository createConfigurationRepository(Settings settings) |
183 | 193 | ); |
184 | 194 | } |
185 | 195 |
|
| 196 | + private ConfigurationRepository createConfigurationRepository(Settings settings, ThreadPool threadPool) { |
| 197 | + return new ConfigurationRepository( |
| 198 | + settings.get(ConfigConstants.SECURITY_CONFIG_INDEX_NAME, ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX), |
| 199 | + settings, |
| 200 | + path, |
| 201 | + threadPool, |
| 202 | + localClient, |
| 203 | + clusterService, |
| 204 | + auditLog, |
| 205 | + securityIndexHandler, |
| 206 | + configurationLoaderSecurity7 |
| 207 | + ); |
| 208 | + } |
| 209 | + |
186 | 210 | @Test |
187 | 211 | public void create_shouldReturnConfigurationRepository() { |
188 | 212 | ConfigurationRepository configRepository = createConfigurationRepository(Settings.EMPTY); |
@@ -569,6 +593,57 @@ public void getConfigurationsFromIndex_SecurityIndexNotInitiallyReady() throws I |
569 | 593 | assertThat(result.size(), is(CType.values().size())); |
570 | 594 | } |
571 | 595 |
|
| 596 | + @Test |
| 597 | + public void afterIndexShardStarted_whenSecurityIndexUpdated() throws InterruptedException, TimeoutException { |
| 598 | + Settings settings = Settings.builder().build(); |
| 599 | + IndexShard indexShard = mock(IndexShard.class); |
| 600 | + ShardRouting shardRouting = mock(ShardRouting.class); |
| 601 | + ShardId shardId = mock(ShardId.class); |
| 602 | + Index index = mock(Index.class); |
| 603 | + ClusterState mockClusterState = mock(ClusterState.class); |
| 604 | + RestoreInProgress mockRestore = mock(RestoreInProgress.class); |
| 605 | + RestoreInProgress.Entry mockEntry = mock(RestoreInProgress.Entry.class); |
| 606 | + ExecutorService executorService = mock(ExecutorService.class); |
| 607 | + ThreadPool threadPool = mock(ThreadPool.class); |
| 608 | + ConfigurationRepository configurationRepository = spy(createConfigurationRepository(settings, threadPool)); |
| 609 | + |
| 610 | + // Setup mock behavior |
| 611 | + when(indexShard.shardId()).thenReturn(shardId); |
| 612 | + when(shardId.getIndex()).thenReturn(index); |
| 613 | + when(index.getName()).thenReturn(ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); |
| 614 | + when(indexShard.routingEntry()).thenReturn(shardRouting); |
| 615 | + when(clusterService.state()).thenReturn(mockClusterState); |
| 616 | + when(mockClusterState.custom(RestoreInProgress.TYPE)).thenReturn(mockRestore); |
| 617 | + when(threadPool.generic()).thenReturn(executorService); |
| 618 | + |
| 619 | + // when replica shard updated |
| 620 | + when(shardRouting.primary()).thenReturn(false); |
| 621 | + configurationRepository.afterIndexShardStarted(indexShard); |
| 622 | + verify(executorService, never()).execute(any()); |
| 623 | + verify(configurationRepository, never()).reloadConfiguration(any()); |
| 624 | + |
| 625 | + // when primary shard updated |
| 626 | + doReturn(true).when(configurationRepository).reloadConfiguration(any()); |
| 627 | + when(shardRouting.primary()).thenReturn(true); |
| 628 | + when(mockRestore.iterator()).thenReturn(Collections.singletonList(mockEntry).iterator()); |
| 629 | + when(mockEntry.indices()).thenReturn(Collections.singletonList(ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX)); |
| 630 | + ArgumentCaptor<Runnable> successRunnableCaptor = ArgumentCaptor.forClass(Runnable.class); |
| 631 | + configurationRepository.afterIndexShardStarted(indexShard); |
| 632 | + verify(executorService).execute(successRunnableCaptor.capture()); |
| 633 | + successRunnableCaptor.getValue().run(); |
| 634 | + verify(configurationRepository).reloadConfiguration(CType.values()); |
| 635 | + |
| 636 | + // When there is error in checking if restored from snapshot |
| 637 | + Mockito.reset(configurationRepository, executorService); |
| 638 | + ArgumentCaptor<Runnable> errorRunnableCaptor = ArgumentCaptor.forClass(Runnable.class); |
| 639 | + when(clusterService.state()).thenThrow(new RuntimeException("ClusterState exception")); |
| 640 | + when(shardRouting.primary()).thenReturn(true); |
| 641 | + configurationRepository.afterIndexShardStarted(indexShard); |
| 642 | + verify(executorService).execute(errorRunnableCaptor.capture()); |
| 643 | + errorRunnableCaptor.getValue().run(); |
| 644 | + verify(configurationRepository, never()).reloadConfiguration(any()); |
| 645 | + } |
| 646 | + |
572 | 647 | void assertClusterState(final ArgumentCaptor<ClusterStateUpdateTask> clusterStateUpdateTaskCaptor) throws Exception { |
573 | 648 | final var initializedStateUpdate = clusterStateUpdateTaskCaptor.getValue(); |
574 | 649 | assertThat(initializedStateUpdate.priority(), is(Priority.IMMEDIATE)); |
|
0 commit comments