diff --git a/graylog-storage-elasticsearch7/src/main/java/org/graylog/storage/elasticsearch7/Elasticsearch7Module.java b/graylog-storage-elasticsearch7/src/main/java/org/graylog/storage/elasticsearch7/Elasticsearch7Module.java index d3087e09e24a..a4f0dcdb8496 100644 --- a/graylog-storage-elasticsearch7/src/main/java/org/graylog/storage/elasticsearch7/Elasticsearch7Module.java +++ b/graylog-storage-elasticsearch7/src/main/java/org/graylog/storage/elasticsearch7/Elasticsearch7Module.java @@ -32,6 +32,7 @@ import org.graylog2.indexer.cluster.NodeAdapter; import org.graylog2.indexer.counts.CountsAdapter; import org.graylog2.indexer.datanode.ProxyRequestAdapter; +import org.graylog2.indexer.datanode.RemoteReindexingMigrationAdapter; import org.graylog2.indexer.datastream.DataStreamAdapter; import org.graylog2.indexer.fieldtypes.IndexFieldTypePollerAdapter; import org.graylog2.indexer.fieldtypes.streamfiltered.esadapters.StreamsForFieldRetriever; @@ -84,6 +85,8 @@ protected void configure() { bind(RestHighLevelClient.class).toProvider(RestHighLevelClientProvider.class); bind(CredentialsProvider.class).toProvider(ESCredentialsProvider.class); + + bindForSupportedVersion(RemoteReindexingMigrationAdapter.class).to(RemoteReindexingMigrationAdapterES7.class); } private LinkedBindingBuilder bindForSupportedVersion(Class interfaceClass) { diff --git a/graylog-storage-elasticsearch7/src/main/java/org/graylog/storage/elasticsearch7/RemoteReindexingMigrationAdapterES7.java b/graylog-storage-elasticsearch7/src/main/java/org/graylog/storage/elasticsearch7/RemoteReindexingMigrationAdapterES7.java new file mode 100644 index 000000000000..a0c55a76c84b --- /dev/null +++ b/graylog-storage-elasticsearch7/src/main/java/org/graylog/storage/elasticsearch7/RemoteReindexingMigrationAdapterES7.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +package org.graylog.storage.elasticsearch7; + +import org.graylog2.indexer.datanode.RemoteReindexingMigrationAdapter; +import org.graylog2.indexer.migration.RemoteReindexMigration; + +import java.net.URI; +import java.util.List; + +public class RemoteReindexingMigrationAdapterES7 implements RemoteReindexingMigrationAdapter { + @Override + public RemoteReindexMigration start(URI uri, String username, String password, List indices, boolean synchronous) { + throw new UnsupportedOperationException("Remote reindexing migrations are not supported for elasticsearch"); + } + + @Override + public RemoteReindexMigration status(String migrationID) { + throw new UnsupportedOperationException("Remote reindexing migrations are not supported for elasticsearch"); + } +} diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/actions/MigrationActions.java b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/actions/MigrationActions.java index 90a9db5072d8..b4b2ab41ef8e 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/actions/MigrationActions.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/actions/MigrationActions.java @@ -32,9 +32,7 @@ public interface MigrationActions { void reindexUpgradeSelected(); - boolean reindexingFinished(); - - void reindexOldData(); + boolean isRemoteReindexingFinished(); void stopMessageProcessing(); @@ -59,4 +57,8 @@ public interface MigrationActions { MigrationStateMachineContext getStateMachineContext(); + void startRemoteReindex(); + + void requestMigrationStatus(); + } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/actions/MigrationActionsImpl.java b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/actions/MigrationActionsImpl.java index a88a5852d150..1362125efa9d 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/actions/MigrationActionsImpl.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/actions/MigrationActionsImpl.java @@ -29,6 +29,8 @@ import org.graylog2.cluster.nodes.NodeService; import org.graylog2.cluster.preflight.DataNodeProvisioningConfig; import org.graylog2.cluster.preflight.DataNodeProvisioningService; +import org.graylog2.indexer.datanode.RemoteReindexingMigrationAdapter; +import org.graylog2.indexer.migration.RemoteReindexMigration; import org.graylog2.plugin.certificates.RenewalPolicy; import org.graylog2.plugin.cluster.ClusterConfigService; import org.graylog2.system.processing.control.ClusterProcessingControl; @@ -37,7 +39,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.URI; +import java.util.Collections; +import java.util.List; import java.util.Map; +import java.util.Objects; @Singleton public class MigrationActionsImpl implements MigrationActions { @@ -47,14 +53,17 @@ public class MigrationActionsImpl implements MigrationActions { private final ClusterProcessingControlFactory clusterProcessingControlFactory; private final NodeService nodeService; private final CaService caService; - private final DataNodeProvisioningService dataNodeProvisioningService; private final PreflightConfigService preflightConfigService; private MigrationStateMachineContext stateMachineContext; + private final DataNodeProvisioningService dataNodeProvisioningService; + + private final RemoteReindexingMigrationAdapter migrationService; @Inject public MigrationActionsImpl(final ClusterConfigService clusterConfigService, NodeService nodeService, final CaService caService, DataNodeProvisioningService dataNodeProvisioningService, + RemoteReindexingMigrationAdapter migrationService, final ClusterProcessingControlFactory clusterProcessingControlFactory, final PreflightConfigService preflightConfigService) { this.clusterConfigService = clusterConfigService; @@ -62,6 +71,7 @@ public MigrationActionsImpl(final ClusterConfigService clusterConfigService, Nod this.caService = caService; this.dataNodeProvisioningService = dataNodeProvisioningService; this.clusterProcessingControlFactory = clusterProcessingControlFactory; + this.migrationService = migrationService; this.preflightConfigService = preflightConfigService; } @@ -99,20 +109,9 @@ public void reindexUpgradeSelected() { } - @Override - public boolean reindexingFinished() { - // TODO: add real test - return true; - } - - @Override - public void reindexOldData() { - - } - @Override public void stopMessageProcessing() { - final String authToken = (String)stateMachineContext.getExtendedState(MigrationStateMachineContext.AUTH_TOKEN_KEY); + final String authToken = (String) stateMachineContext.getExtendedState(MigrationStateMachineContext.AUTH_TOKEN_KEY); final ClusterProcessingControl control = clusterProcessingControlFactory.create(authToken); LOG.info("Attempting to pause processing on all nodes..."); control.pauseProcessing(); @@ -124,7 +123,7 @@ public void stopMessageProcessing() { @Override public void startMessageProcessing() { - final String authToken = (String)stateMachineContext.getExtendedState(MigrationStateMachineContext.AUTH_TOKEN_KEY); + final String authToken = (String) stateMachineContext.getExtendedState(MigrationStateMachineContext.AUTH_TOKEN_KEY); final ClusterProcessingControl control = clusterProcessingControlFactory.create(authToken); LOG.info("Resuming message processing."); control.resumeGraylogMessageProcessing(); @@ -153,7 +152,7 @@ public boolean caAndRemovalPolicyExist() { public void provisionDataNodes() { // if we start provisioning DataNodes via the migration, Preflight is definitely done/no option anymore var preflight = preflightConfigService.getPreflightConfigResult(); - if(preflight == null || !preflight.equals(PreflightConfigResult.FINISHED)) { + if (preflight == null || !preflight.equals(PreflightConfigResult.FINISHED)) { preflightConfigService.setConfigResult(PreflightConfigResult.FINISHED); } final Map activeDataNodes = nodeService.allActive(); @@ -182,6 +181,33 @@ public boolean dataNodeStartupFinished() { return nodeService.allActive().values().stream().allMatch(node -> node.getDataNodeStatus() == DataNodeStatus.AVAILABLE); } + @Override + public void startRemoteReindex() { + final URI hostname = Objects.requireNonNull(URI.create(getStateMachineContext().getActionArgument("hostname", String.class)), "hostname has to be provided"); + final String user = getStateMachineContext().getActionArgumentOpt("user", String.class).orElse(null); + final String password = getStateMachineContext().getActionArgumentOpt("password", String.class).orElse(null); + final List indices = getStateMachineContext().getActionArgumentOpt("indices", List.class).orElse(Collections.emptyList()); // todo: generics! + final RemoteReindexMigration migration = migrationService.start(hostname, user, password, indices, false); + final String migrationID = migration.id(); + getStateMachineContext().addExtendedState(MigrationStateMachineContext.KEY_MIGRATION_ID, migrationID); + } + + @Override + public void requestMigrationStatus() { + getStateMachineContext().getExtendedState(MigrationStateMachineContext.KEY_MIGRATION_ID, String.class) + .map(migrationService::status) + .ifPresent(status -> getStateMachineContext().setResponse(status)); + } + + @Override + public boolean isRemoteReindexingFinished() { + return getStateMachineContext().getExtendedState(MigrationStateMachineContext.KEY_MIGRATION_ID, String.class) + .map(migrationService::status) + .filter(m -> m.status() == RemoteReindexingMigrationAdapter.Status.FINISHED) + .isPresent(); + } + + @Override public void setStateMachineContext(MigrationStateMachineContext context) { this.stateMachineContext = context; } @@ -190,5 +216,4 @@ public void setStateMachineContext(MigrationStateMachineContext context) { public MigrationStateMachineContext getStateMachineContext() { return stateMachineContext; } - } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationState.java b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationState.java index 7ddd9896a340..4b3f91ce8971 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationState.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationState.java @@ -29,6 +29,7 @@ public enum MigrationState { PROVISION_DATANODE_CERTIFICATES_RUNNING, EXISTING_DATA_MIGRATION_QUESTION_PAGE, MIGRATE_EXISTING_DATA, + REMOTE_REINDEX_RUNNING, ASK_TO_SHUTDOWN_OLD_CLUSTER, DIRECTORY_COMPATIBILITY_CHECK_PAGE, PROVISION_ROLLING_UPGRADE_NODES_WITH_CERTIFICATES, diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineBuilder.java b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineBuilder.java index dc18bcf15b43..eb13f32cee8c 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineBuilder.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineBuilder.java @@ -80,9 +80,17 @@ private static StateMachineConfig configureStates .permit(MigrationStep.SHOW_MIGRATE_EXISTING_DATA, MigrationState.MIGRATE_EXISTING_DATA) .permit(MigrationStep.SKIP_EXISTING_DATA_MIGRATION, MigrationState.ASK_TO_SHUTDOWN_OLD_CLUSTER); - config.configure(MigrationState.MIGRATE_EXISTING_DATA) - .onEntry(migrationActions::reindexOldData) - .permitIf(MigrationStep.SHOW_ASK_TO_SHUTDOWN_OLD_CLUSTER, MigrationState.ASK_TO_SHUTDOWN_OLD_CLUSTER, migrationActions::reindexingFinished); + // we now have enough information in the context to start the remote reindex migration. This will move us to the + // next state that will be active as long as the migration is running and will provide status information to the FE + config.configure(MigrationState.MIGRATE_EXISTING_DATA) // this state and screen has to request username, password and url of the old cluster + .permit(MigrationStep.START_REMOTE_REINDEX_MIGRATION, MigrationState.REMOTE_REINDEX_RUNNING, migrationActions::startRemoteReindex); + + // the state machine will stay in this state till the migration is finished or fails. It should provide + // current migration status every time we trigger MigrationStep.REQUEST_MIGRATION_STATUS. + config.configure(MigrationState.REMOTE_REINDEX_RUNNING) + .permitReentry(MigrationStep.REQUEST_MIGRATION_STATUS, migrationActions::requestMigrationStatus) + .permit(MigrationStep.RETRY_MIGRATE_EXISTING_DATA, MigrationState.MIGRATE_EXISTING_DATA) // allow one step back in case the migration fails + .permitIf(MigrationStep.SHOW_ASK_TO_SHUTDOWN_OLD_CLUSTER, MigrationState.ASK_TO_SHUTDOWN_OLD_CLUSTER, migrationActions::isRemoteReindexingFinished); config.configure(MigrationState.ASK_TO_SHUTDOWN_OLD_CLUSTER) .permitIf(MigrationStep.CONFIRM_OLD_CLUSTER_STOPPED, MigrationState.MANUALLY_REMOVE_OLD_CONNECTION_STRING_FROM_CONFIG, migrationActions::isOldClusterStopped); diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineContext.java b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineContext.java index eb7a36785fa1..3e7d8efa0d3e 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineContext.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineContext.java @@ -17,17 +17,21 @@ package org.graylog.plugins.views.storage.migration.state.machine; import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.Optional; @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.PROTECTED_AND_PUBLIC) public class MigrationStateMachineContext { public static final String AUTH_TOKEN_KEY = "authToken"; + public static final String KEY_MIGRATION_ID = "migrationID"; + @JsonProperty protected MigrationStep currentStep; @JsonProperty @@ -35,6 +39,9 @@ public class MigrationStateMachineContext { @JsonProperty protected Map extendedState; + @JsonIgnore + protected Object response; + public MigrationStateMachineContext() { this.actionArguments = new HashMap<>(); this.extendedState = new HashMap<>(); @@ -59,6 +66,18 @@ public T getActionArgument(String name, Class type) { return (T) arg; } + public Optional getActionArgumentOpt(String name, Class type) { + Map args = this.actionArguments.get(currentStep); + return Optional.ofNullable(args) + .map(arg -> arg.get(name)) + .map(arg -> { + if (!type.isInstance(arg)) { + throw new IllegalArgumentException("Argument " + name + " must be of type " + type); + } + return (T) arg; + }); + } + public void addActionArguments(MigrationStep step, Map args) { this.actionArguments.put(step, args); } @@ -71,4 +90,22 @@ public Object getExtendedState(String key) { return this.extendedState.get(key); } + public Optional getExtendedState(String name, Class type) { + if (!this.extendedState.containsKey(name)) { + return Optional.empty(); + } + Object value = this.extendedState.get(name); + if (!type.isInstance(value)) { + throw new IllegalArgumentException("Argument " + name + " must be of type " + type); + } + return Optional.of((T) value); + } + + public void setResponse(Object response) { + this.response = response; + } + + public Object getResponse() { + return response; + } } diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineImpl.java b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineImpl.java index 4154df044ef9..4ce390ce03c8 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineImpl.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineImpl.java @@ -56,7 +56,7 @@ public CurrentStateInformation trigger(MigrationStep step, Map a } context = migrationActions.getStateMachineContext(); persistenceService.saveStateMachineContext(context); - return new CurrentStateInformation(getState(), nextSteps(), errorMessage); + return new CurrentStateInformation(getState(), nextSteps(), errorMessage, context.getResponse()); } @Override diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStep.java b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStep.java index 959c174f952b..53b017ec429e 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStep.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStep.java @@ -26,6 +26,11 @@ public enum MigrationStep { PROVISION_DATANODE_CERTIFICATES, SHOW_DATA_MIGRATION_QUESTION, SHOW_MIGRATE_EXISTING_DATA, + START_REMOTE_REINDEX_MIGRATION, + + RETRY_MIGRATE_EXISTING_DATA, + REQUEST_MIGRATION_STATUS, + SHOW_ASK_TO_SHUTDOWN_OLD_CLUSTER, SHOW_PROVISION_ROLLING_UPGRADE_NODES_WITH_CERTIFICATES, SHOW_STOP_PROCESSING_PAGE, diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/rest/CurrentStateInformation.java b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/rest/CurrentStateInformation.java index e001c7b16722..665c752cd6aa 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/rest/CurrentStateInformation.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/rest/CurrentStateInformation.java @@ -22,10 +22,10 @@ import java.util.List; -public record CurrentStateInformation(MigrationState state, List nextSteps, String errorMessage) { +public record CurrentStateInformation(MigrationState state, List nextSteps, String errorMessage, Object response) { public CurrentStateInformation(MigrationState state, List nextSteps) { - this(state, nextSteps, null); + this(state, nextSteps, null, null); } public boolean hasErrors() { diff --git a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/rest/MigrationStateResource.java b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/rest/MigrationStateResource.java index f131f70d1f28..a7c9ff604b1e 100644 --- a/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/rest/MigrationStateResource.java +++ b/graylog2-server/src/main/java/org/graylog/plugins/views/storage/migration/state/rest/MigrationStateResource.java @@ -33,12 +33,11 @@ import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.graylog.plugins.views.storage.migration.state.machine.MigrationStateMachine; +import org.graylog.plugins.views.storage.migration.state.machine.MigrationStateMachineContext; import org.graylog2.audit.jersey.NoAuditEvent; import org.graylog2.shared.rest.resources.ProxiedResource; import org.graylog2.shared.security.RestPermissions; -import static org.graylog.plugins.views.storage.migration.state.machine.MigrationStateMachineContext.AUTH_TOKEN_KEY; - @Path("/migration") @RequiresAuthentication @Consumes(MediaType.APPLICATION_JSON) @@ -49,10 +48,9 @@ public class MigrationStateResource { private final MigrationStateMachine stateMachine; @Inject - public MigrationStateResource(MigrationStateMachine stateMachine, - @Context HttpHeaders httpHeaders) { + public MigrationStateResource(MigrationStateMachine stateMachine, @Context HttpHeaders httpHeaders) { this.stateMachine = stateMachine; - this.stateMachine.getContext().addExtendedState(AUTH_TOKEN_KEY, ProxiedResource.authenticationToken(httpHeaders)); + this.stateMachine.getContext().addExtendedState(MigrationStateMachineContext.AUTH_TOKEN_KEY, ProxiedResource.authenticationToken(httpHeaders)); } @POST @@ -60,11 +58,10 @@ public MigrationStateResource(MigrationStateMachine stateMachine, @NoAuditEvent("No Audit Event needed") // TODO: do we need audit log here? @RequiresPermissions(RestPermissions.DATANODE_MIGRATION) @ApiOperation(value = "trigger migration step") - public Response migrate(@ApiParam(name = "request") @NotNull MigrationStepRequest request) { + public Response trigger(@ApiParam(name = "request") @NotNull MigrationStepRequest request) { final CurrentStateInformation newState = stateMachine.trigger(request.step(), request.args()); - return Response - .status(newState.hasErrors() ? Response.Status.INTERNAL_SERVER_ERROR : Response.Status.OK) - .entity(newState) + Response.ResponseBuilder response = newState.hasErrors() ? Response.serverError() : Response.ok(); + return response.entity(newState) .build(); } diff --git a/graylog2-server/src/main/java/org/graylog2/indexer/migration/RemoteReindexMigration.java b/graylog2-server/src/main/java/org/graylog2/indexer/migration/RemoteReindexMigration.java index 1f46e60bdf39..a7d3f505a9b6 100644 --- a/graylog2-server/src/main/java/org/graylog2/indexer/migration/RemoteReindexMigration.java +++ b/graylog2-server/src/main/java/org/graylog2/indexer/migration/RemoteReindexMigration.java @@ -89,6 +89,9 @@ public RemoteReindexMigration setFinishCallback(Runnable finishCallback) { return this; } + public Status status() { + return status; + } public void finish() { if(finishCallback != null) { diff --git a/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationActionsAdapter.java b/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationActionsAdapter.java index 378aa9d37a3d..163b124f5913 100644 --- a/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationActionsAdapter.java +++ b/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationActionsAdapter.java @@ -45,6 +45,16 @@ public MigrationStateMachineContext getStateMachineContext() { return context; } + @Override + public void startRemoteReindex() { + + } + + @Override + public void requestMigrationStatus() { + + } + @Override public boolean runDirectoryCompatibilityCheck() { return false; @@ -71,15 +81,10 @@ public void reindexUpgradeSelected() { } @Override - public boolean reindexingFinished() { + public boolean isRemoteReindexingFinished() { return false; } - @Override - public void reindexOldData() { - - } - @Override public void stopMessageProcessing() { diff --git a/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineBuilderTest.java b/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineBuilderTest.java index a1e1cd4d6b66..f1ee78d1c629 100644 --- a/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineBuilderTest.java +++ b/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineBuilderTest.java @@ -167,13 +167,13 @@ public void testMigrateExistingData() { StateMachine stateMachine = getStateMachine(MigrationState.EXISTING_DATA_MIGRATION_QUESTION_PAGE); stateMachine.fire(MigrationStep.SHOW_MIGRATE_EXISTING_DATA); assertThat(stateMachine.getState()).isEqualTo(MigrationState.MIGRATE_EXISTING_DATA); - verify(migrationActions).reindexOldData(); - assertThat(stateMachine.getPermittedTriggers()).isEmpty(); - verify(migrationActions, times(1)).reindexingFinished(); + assertThat(stateMachine.getPermittedTriggers()).contains(MigrationStep.START_REMOTE_REINDEX_MIGRATION); + stateMachine.fire(MigrationStep.START_REMOTE_REINDEX_MIGRATION); + verify(migrationActions, times(1)).startRemoteReindex(); reset(migrationActions); - when(migrationActions.reindexingFinished()).thenReturn(true); - assertThat(stateMachine.getPermittedTriggers()).containsOnly(MigrationStep.SHOW_ASK_TO_SHUTDOWN_OLD_CLUSTER); - verify(migrationActions, times(1)).reindexingFinished(); + when(migrationActions.isRemoteReindexingFinished()).thenReturn(true); + assertThat(stateMachine.getPermittedTriggers()).contains(MigrationStep.SHOW_ASK_TO_SHUTDOWN_OLD_CLUSTER, MigrationStep.REQUEST_MIGRATION_STATUS, MigrationStep.RETRY_MIGRATE_EXISTING_DATA); + verify(migrationActions, times(1)).isRemoteReindexingFinished(); verifyNoMoreInteractions(migrationActions); } @@ -270,8 +270,8 @@ public void testAskToShutdownOldCluster() { @Test public void testAskToShutdownOldClusterFromReindexing() { - StateMachine stateMachine = getStateMachine(MigrationState.MIGRATE_EXISTING_DATA); - when(migrationActions.reindexingFinished()).thenReturn(true); + StateMachine stateMachine = getStateMachine(MigrationState.REMOTE_REINDEX_RUNNING); + when(migrationActions.isRemoteReindexingFinished()).thenReturn(true); stateMachine.fire(MigrationStep.SHOW_ASK_TO_SHUTDOWN_OLD_CLUSTER); assertThat(stateMachine.getState()).isEqualTo(MigrationState.ASK_TO_SHUTDOWN_OLD_CLUSTER); } diff --git a/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineImplTest.java b/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineImplTest.java index 5520d9e2d1af..367055c7b048 100644 --- a/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineImplTest.java +++ b/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/machine/MigrationStateMachineImplTest.java @@ -147,11 +147,7 @@ private StateMachine testStateMachineWithAction(C } - private static class TestMigrationActions extends MigrationActionsImpl { - - public TestMigrationActions() { - super(null, null, null, null, null, null); - } + private static class TestMigrationActions extends MigrationActionsAdapter { public void runTestFunction(Consumer testFunction) { testFunction.accept(getStateMachineContext()); diff --git a/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/rest/MigrationStateResourceTest.java b/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/rest/MigrationStateResourceTest.java index 22a630266af0..b6ddadfbc35d 100644 --- a/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/rest/MigrationStateResourceTest.java +++ b/graylog2-server/src/test/java/org/graylog/plugins/views/storage/migration/state/rest/MigrationStateResourceTest.java @@ -18,6 +18,7 @@ import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response; +import org.apache.commons.lang3.tuple.Pair; import org.graylog.plugins.views.storage.migration.state.machine.MigrationState; import org.graylog.plugins.views.storage.migration.state.machine.MigrationStateMachine; import org.graylog.plugins.views.storage.migration.state.machine.MigrationStateMachineContext; @@ -26,8 +27,11 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -36,35 +40,28 @@ import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.Mockito.when; -@ExtendWith(MockitoExtension.class) public class MigrationStateResourceTest { - public static final String AUTHORIZATION = "MyAuthorization"; - @Mock - MigrationStateMachine stateMachine; - @Mock - HttpHeaders httpHeaders; - MigrationStateMachineContext stateMachineContext; - MigrationStateResource migrationStateResource; - - @BeforeEach - public void setUp() { - stateMachineContext = new MigrationStateMachineContext(); - when(stateMachine.getContext()).thenReturn(stateMachineContext); - when(httpHeaders.getRequestHeader(HttpHeaders.AUTHORIZATION)).thenReturn(List.of(AUTHORIZATION)); - migrationStateResource = new MigrationStateResource(stateMachine, httpHeaders); - } - @Test public void authenticationTokenSetToStateMachineContext() { - assertThat(stateMachineContext.getExtendedState(MigrationStateMachineContext.AUTH_TOKEN_KEY)).isEqualTo(AUTHORIZATION); + final CurrentStateInformation state = new CurrentStateInformation(MigrationState.NEW, List.of(MigrationStep.SELECT_MIGRATION)); + final MigrationStateMachine stateMachine = mockStateMachine(state); + + final String expectedAuthToken = "MyAuthorization"; + final MigrationStateResource resource = new MigrationStateResource( + stateMachine, + mockHttpHeaders(Pair.of(HttpHeaders.AUTHORIZATION, expectedAuthToken)) + ); + + assertThat(stateMachine.getContext().getExtendedState(MigrationStateMachineContext.AUTH_TOKEN_KEY)).isEqualTo(expectedAuthToken); } @Test public void requestReturnsSuccessfulResult() { CurrentStateInformation state = new CurrentStateInformation(MigrationState.NEW, List.of(MigrationStep.SELECT_MIGRATION)); - when(stateMachine.trigger(any(), anyMap())).thenReturn(state); - try (Response response = migrationStateResource.migrate(new MigrationStepRequest(MigrationStep.SELECT_MIGRATION, Map.of()))) { + final MigrationStateResource resource = new MigrationStateResource(mockStateMachine(state), mockHttpHeaders()); + + try (Response response = resource.trigger(new MigrationStepRequest(MigrationStep.SELECT_MIGRATION, Map.of()))) { assertThat(response.getStatus()).isEqualTo(200); assertThat(response.getEntity()).isEqualTo(state); } @@ -72,14 +69,51 @@ public void requestReturnsSuccessfulResult() { @Test public void requestReturns500OnError() { - CurrentStateInformation state = - new CurrentStateInformation(MigrationState.NEW, List.of(MigrationStep.SELECT_MIGRATION), "Error"); - when(stateMachine.trigger(any(), anyMap())).thenReturn(state); - try (Response response = migrationStateResource.migrate(new MigrationStepRequest(MigrationStep.SELECT_MIGRATION, Map.of()))) { + final CurrentStateInformation expectedState = new CurrentStateInformation(MigrationState.NEW, List.of(MigrationStep.SELECT_MIGRATION), "Error", null); + final MigrationStateResource resource = new MigrationStateResource(mockStateMachine(expectedState), mockHttpHeaders()); + + try (Response response = resource.trigger(new MigrationStepRequest(MigrationStep.SELECT_MIGRATION, Map.of()))) { assertThat(response.getStatus()).isEqualTo(500); - assertThat(response.getEntity()).isEqualTo(state); + assertThat(response.getEntity()).isEqualTo(expectedState); } } + @SafeVarargs + private HttpHeaders mockHttpHeaders(Pair... headers) { + final HttpHeaders httpHeaders = Mockito.mock(HttpHeaders.class); + Arrays.stream(headers).forEach(pair -> when(httpHeaders.getRequestHeader(pair.getKey())).thenReturn(List.of(pair.getValue()))); + return httpHeaders; + } + private MigrationStateMachine mockStateMachine(CurrentStateInformation state) { + + final MigrationStateMachineContext context = new MigrationStateMachineContext(); + + return new MigrationStateMachine() { + @Override + public CurrentStateInformation trigger(MigrationStep step, Map args) { + return state; + } + + @Override + public MigrationState getState() { + return state.state(); + } + + @Override + public List nextSteps() { + return state.nextSteps(); + } + + @Override + public MigrationStateMachineContext getContext() { + return context; + } + + @Override + public String serialize() { + throw new UnsupportedOperationException("not implemented"); + } + }; + } }