You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2020/12/08 06:18:51 UTC
[ignite] branch master updated: IGNITE-13320 Cache encryption key
rotation CLI management - Fixes #8242.
This is an automated email from the ASF dual-hosted git repository.
alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 8678857 IGNITE-13320 Cache encryption key rotation CLI management - Fixes #8242.
8678857 is described below
commit 8678857ebf033b696ab7196c87cd481b184bea9a
Author: Pavel Pereslegin <xx...@gmail.com>
AuthorDate: Mon Dec 7 18:21:15 2020 +0300
IGNITE-13320 Cache encryption key rotation CLI management - Fixes #8242.
Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
.../ignite/internal/commandline/CommandList.java | 4 +-
.../encryption/CacheGroupEncryptionCommand.java | 264 +++++++++++++++++++++
.../encryption/ChangeCacheGroupKeyCommand.java | 95 ++++++++
.../encryption/ChangeMasterKeyCommand.java | 88 +++++++
.../commandline/encryption/EncryptionCommand.java | 130 ----------
.../commandline/encryption/EncryptionCommands.java | 70 ++++++
.../encryption/EncryptionSubcommand.java | 59 -----
.../encryption/EncryptionSubcommands.java | 99 ++++++++
.../encryption/GetMasterKeyNameCommand.java | 73 ++++++
.../encryption/ReencryptionRateCommand.java | 137 +++++++++++
.../util/GridCommandHandlerAbstractTest.java | 30 ++-
.../apache/ignite/util/GridCommandHandlerTest.java | 205 +++++++++++++++-
.../configuration/EncryptionConfiguration.java | 2 +-
.../managers/encryption/CacheGroupPageScanner.java | 86 ++++++-
.../managers/encryption/GridEncryptionManager.java | 48 ++++
.../processors/cache/CacheGroupMetricsImpl.java | 46 +---
.../ignite/internal/util/BasicRateLimiter.java | 20 +-
.../encryption/VisorCacheGroupEncryptionTask.java | 137 +++++++++++
.../VisorCacheGroupEncryptionTaskArg.java | 68 ++++++
.../VisorCacheGroupEncryptionTaskResult.java | 90 +++++++
.../encryption/VisorChangeCacheGroupKeyTask.java | 61 +++++
.../encryption/VisorEncryptionKeyIdsTask.java | 83 +++++++
.../encryption/VisorReencryptionRateTask.java | 133 +++++++++++
.../encryption/VisorReencryptionRateTaskArg.java | 74 ++++++
.../encryption/VisorReencryptionResumeTask.java | 60 +++++
.../encryption/VisorReencryptionStatusTask.java | 88 +++++++
.../encryption/VisorReencryptionSuspendTask.java | 83 +++++++
.../main/resources/META-INF/classnames.properties | 19 ++
.../encryption/CacheGroupReencryptionTest.java | 12 +-
.../ignite/internal/util/BasicRateLimiterTest.java | 15 ++
...ridCommandHandlerClusterByClassTest_help.output | 21 ++
...andHandlerClusterByClassWithSSLTest_help.output | 21 ++
32 files changed, 2163 insertions(+), 258 deletions(-)
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java
index e9f8cd3..7c45a36 100644
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/CommandList.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.commandline;
import org.apache.ignite.internal.commandline.cache.CacheCommands;
import org.apache.ignite.internal.commandline.diagnostic.DiagnosticCommand;
-import org.apache.ignite.internal.commandline.encryption.EncryptionCommand;
+import org.apache.ignite.internal.commandline.encryption.EncryptionCommands;
import org.apache.ignite.internal.commandline.meta.MetadataCommand;
import org.apache.ignite.internal.commandline.metric.MetricCommand;
import org.apache.ignite.internal.commandline.property.PropertyCommand;
@@ -59,7 +59,7 @@ public enum CommandList {
DIAGNOSTIC("--diagnostic", new DiagnosticCommand()),
/** Encryption features command. */
- ENCRYPTION("--encryption", new EncryptionCommand()),
+ ENCRYPTION("--encryption", new EncryptionCommands()),
/** Kill command. */
KILL("--kill", new KillCommand()),
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/CacheGroupEncryptionCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/CacheGroupEncryptionCommand.java
new file mode 100644
index 0000000..d4c09b8
--- /dev/null
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/CacheGroupEncryptionCommand.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.commandline.encryption;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.logging.Logger;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientConfiguration;
+import org.apache.ignite.internal.commandline.AbstractCommand;
+import org.apache.ignite.internal.commandline.Command;
+import org.apache.ignite.internal.commandline.CommandArgIterator;
+import org.apache.ignite.internal.commandline.CommandList;
+import org.apache.ignite.internal.commandline.CommandLogger;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.visor.encryption.VisorCacheGroupEncryptionTaskArg;
+import org.apache.ignite.internal.visor.encryption.VisorCacheGroupEncryptionTaskResult;
+import org.apache.ignite.internal.visor.encryption.VisorEncryptionKeyIdsTask;
+import org.apache.ignite.internal.visor.encryption.VisorReencryptionResumeTask;
+import org.apache.ignite.internal.visor.encryption.VisorReencryptionStatusTask;
+import org.apache.ignite.internal.visor.encryption.VisorReencryptionSuspendTask;
+
+import static org.apache.ignite.internal.commandline.CommandList.ENCRYPTION;
+import static org.apache.ignite.internal.commandline.CommandLogger.DOUBLE_INDENT;
+import static org.apache.ignite.internal.commandline.CommandLogger.INDENT;
+import static org.apache.ignite.internal.commandline.TaskExecutor.BROADCAST_UUID;
+import static org.apache.ignite.internal.commandline.TaskExecutor.executeTaskByNameOnNode;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.CACHE_GROUP_KEY_IDS;
+
+/**
+ * Base cache group encryption multinode subcommand.
+ *
+ * @param <T> Command result type.
+ */
+public abstract class CacheGroupEncryptionCommand<T> extends AbstractCommand<VisorCacheGroupEncryptionTaskArg> {
+ /** Cache group reencryption task argument. */
+ private VisorCacheGroupEncryptionTaskArg taskArg;
+
+ /** {@inheritDoc} */
+ @Override public VisorCacheGroupEncryptionTaskArg arg() {
+ return taskArg;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void parseArguments(CommandArgIterator argIter) {
+ String grpName = argIter.nextArg("Сache group name is expected.");
+
+ if (argIter.hasNextSubArg())
+ throw new IllegalArgumentException("Unexpected command argument: " + argIter.peekNextArg());
+
+ taskArg = new VisorCacheGroupEncryptionTaskArg(grpName);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object execute(GridClientConfiguration clientCfg, Logger log) throws Exception {
+ try (GridClient client = Command.startClient(clientCfg)) {
+ VisorCacheGroupEncryptionTaskResult<T> res = executeTaskByNameOnNode(
+ client,
+ visorTaskName(),
+ taskArg,
+ BROADCAST_UUID,
+ clientCfg
+ );
+
+ printResults(res, taskArg.groupName(), log);
+
+ return res;
+ }
+ catch (Throwable e) {
+ log.severe("Failed to perform operation.");
+ log.severe(CommandLogger.errorMessage(e));
+
+ throw e;
+ }
+ }
+
+ /**
+ * @param res Response.
+ * @param grpName Cache group name.
+ * @param log Logger.
+ */
+ protected void printResults(VisorCacheGroupEncryptionTaskResult<T> res, String grpName, Logger log) {
+ Map<UUID, IgniteException> exceptions = res.exceptions();
+
+ for (Map.Entry<UUID, IgniteException> entry : exceptions.entrySet()) {
+ log.info(INDENT + "Node " + entry.getKey() + ":");
+
+ log.info(String.format("%sfailed to execute command for the cache group \"%s\": %s.",
+ DOUBLE_INDENT, grpName, entry.getValue().getMessage()));
+ }
+
+ Map<UUID, T> results = res.results();
+
+ for (Map.Entry<UUID, T> entry : results.entrySet()) {
+ log.info(INDENT + "Node " + entry.getKey() + ":");
+
+ printNodeResult(entry.getValue(), grpName, log);
+ }
+ }
+
+ /**
+ * @param res Response.
+ * @param grpName Cache group name.
+ * @param log Logger.
+ */
+ protected abstract void printNodeResult(T res, String grpName, Logger log);
+
+ /**
+ * @return Visor task name.
+ */
+ protected abstract String visorTaskName();
+
+ /** Subcommand to Display re-encryption status of the cache group. */
+ protected static class ReencryptionStatus extends CacheGroupEncryptionCommand<Long> {
+ /** {@inheritDoc} */
+ @Override protected void printNodeResult(Long bytesLeft, String grpName, Logger log) {
+ if (bytesLeft == -1)
+ log.info(DOUBLE_INDENT + "re-encryption completed or not required");
+ else if (bytesLeft == 0)
+ log.info(DOUBLE_INDENT + "re-encryption will be completed after the next checkpoint");
+ else
+ log.info(String.format("%s%d KB of data left for re-encryption", DOUBLE_INDENT, bytesLeft / 1024));
+ }
+
+ /** {@inheritDoc} */
+ @Override protected String visorTaskName() {
+ return VisorReencryptionStatusTask.class.getName();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return EncryptionSubcommands.REENCRYPTION_STATUS.text().toUpperCase();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void printUsage(Logger log) {
+ Command.usage(log, "Display re-encryption status of the cache group:", CommandList.ENCRYPTION,
+ EncryptionSubcommands.REENCRYPTION_STATUS.toString(), "cacheGroupName");
+ }
+ }
+
+ /** Subcommand to view current encryption key IDs of the cache group. */
+ protected static class CacheKeyIds extends CacheGroupEncryptionCommand<List<Integer>> {
+ /** {@inheritDoc} */
+ @Override protected void printResults(
+ VisorCacheGroupEncryptionTaskResult<List<Integer>> res,
+ String grpName,
+ Logger log
+ ) {
+ log.info("Encryption key identifiers for cache: " + grpName);
+
+ super.printResults(res, grpName, log);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void printNodeResult(List<Integer> keyIds, String grpName, Logger log) {
+ if (F.isEmpty(keyIds)) {
+ log.info(DOUBLE_INDENT + "---");
+
+ return;
+ }
+
+ for (int i = 0; i < keyIds.size(); i++)
+ log.info(DOUBLE_INDENT + keyIds.get(i) + (i == 0 ? " (active)" : ""));
+ }
+
+ /** {@inheritDoc} */
+ @Override protected String visorTaskName() {
+ return VisorEncryptionKeyIdsTask.class.getName();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return CACHE_GROUP_KEY_IDS.text().toUpperCase();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void printUsage(Logger log) {
+ Command.usage(log, "View encryption key identifiers of the cache group:", ENCRYPTION,
+ CACHE_GROUP_KEY_IDS.toString(), "cacheGroupName");
+ }
+ }
+
+ /** Subcommand to suspend re-encryption of the cache group. */
+ protected static class SuspendReencryption extends CacheGroupEncryptionCommand<Boolean> {
+ /** {@inheritDoc} */
+ @Override protected String visorTaskName() {
+ return VisorReencryptionSuspendTask.class.getName();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return EncryptionSubcommands.REENCRYPTION_SUSPEND.text().toUpperCase();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void printUsage(Logger log) {
+ Command.usage(log, "Suspend re-encryption of the cache group:", CommandList.ENCRYPTION,
+ EncryptionSubcommands.REENCRYPTION_SUSPEND.toString(), "cacheGroupName");
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void printNodeResult(Boolean success, String grpName, Logger log) {
+ log.info(String.format("%sre-encryption of the cache group \"%s\" has %sbeen suspended.",
+ DOUBLE_INDENT, grpName, (success ? "" : "already ")));
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void printResults(
+ VisorCacheGroupEncryptionTaskResult<Boolean> res,
+ String grpName,
+ Logger log
+ ) {
+ super.printResults(res, grpName, log);
+
+ log.info("");
+ log.info("Note: the re-encryption suspend status is not persisted, re-encryption will be started " +
+ "automatically after the node is restarted.");
+ log.info("");
+ }
+ }
+
+ /** Subcommand to resume re-encryption of the cache group. */
+ protected static class ResumeReencryption extends CacheGroupEncryptionCommand<Boolean> {
+ /** {@inheritDoc} */
+ @Override protected String visorTaskName() {
+ return VisorReencryptionResumeTask.class.getName();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return EncryptionSubcommands.REENCRYPTION_RESUME.text().toUpperCase();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void printUsage(Logger log) {
+ Command.usage(log, "Resume re-encryption of the cache group:", CommandList.ENCRYPTION,
+ EncryptionSubcommands.REENCRYPTION_RESUME.toString(), "cacheGroupName");
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void printNodeResult(Boolean success, String grpName, Logger log) {
+ log.info(String.format("%sre-encryption of the cache group \"%s\" has %sbeen resumed.",
+ DOUBLE_INDENT, grpName, (success ? "" : "already ")));
+ }
+ }
+}
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/ChangeCacheGroupKeyCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/ChangeCacheGroupKeyCommand.java
new file mode 100644
index 0000000..8518e5d
--- /dev/null
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/ChangeCacheGroupKeyCommand.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.commandline.encryption;
+
+import java.util.logging.Logger;
+import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientConfiguration;
+import org.apache.ignite.internal.commandline.AbstractCommand;
+import org.apache.ignite.internal.commandline.Command;
+import org.apache.ignite.internal.commandline.CommandArgIterator;
+import org.apache.ignite.internal.commandline.CommandLogger;
+import org.apache.ignite.internal.visor.encryption.VisorCacheGroupEncryptionTaskArg;
+import org.apache.ignite.internal.visor.encryption.VisorChangeCacheGroupKeyTask;
+
+import static org.apache.ignite.internal.commandline.CommandList.ENCRYPTION;
+import static org.apache.ignite.internal.commandline.TaskExecutor.executeTaskByNameOnNode;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.CHANGE_CACHE_GROUP_KEY;
+
+/**
+ * Change cache group key encryption subcommand.
+ */
+public class ChangeCacheGroupKeyCommand extends AbstractCommand<VisorCacheGroupEncryptionTaskArg> {
+ /** Change cache group key task argument. */
+ private VisorCacheGroupEncryptionTaskArg taskArg;
+
+ /** {@inheritDoc} */
+ @Override public Object execute(GridClientConfiguration clientCfg, Logger log) throws Exception {
+ try (GridClient client = Command.startClient(clientCfg)) {
+ executeTaskByNameOnNode(
+ client,
+ VisorChangeCacheGroupKeyTask.class.getName(),
+ taskArg,
+ null,
+ clientCfg
+ );
+
+ log.info("The encryption key has been changed for the cache group \"" + taskArg.groupName() + "\".");
+
+ return null;
+ }
+ catch (Throwable e) {
+ log.severe("Failed to perform operation.");
+ log.severe(CommandLogger.errorMessage(e));
+
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public String confirmationPrompt() {
+ return "Warning: the command will change the encryption key of the cache group. Joining a node during " +
+ "the key change process is prohibited and will be rejected.";
+ }
+
+ /** {@inheritDoc} */
+ @Override public VisorCacheGroupEncryptionTaskArg arg() {
+ return taskArg;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void parseArguments(CommandArgIterator argIter) {
+ String argCacheGrpName = argIter.nextArg("Сache group name is expected.");
+
+ taskArg = new VisorCacheGroupEncryptionTaskArg(argCacheGrpName);
+
+ if (argIter.hasNextSubArg())
+ throw new IllegalArgumentException("Unexpected command argument: " + argIter.peekNextArg());
+ }
+
+ /** {@inheritDoc} */
+ @Override public void printUsage(Logger log) {
+ Command.usage(log, "Change the encryption key of the cache group:", ENCRYPTION,
+ CHANGE_CACHE_GROUP_KEY.toString(), "cacheGroupName");
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return CHANGE_CACHE_GROUP_KEY.text().toUpperCase();
+ }
+}
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/ChangeMasterKeyCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/ChangeMasterKeyCommand.java
new file mode 100644
index 0000000..a48dc4b
--- /dev/null
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/ChangeMasterKeyCommand.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.commandline.encryption;
+
+import java.util.logging.Logger;
+import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientConfiguration;
+import org.apache.ignite.internal.commandline.AbstractCommand;
+import org.apache.ignite.internal.commandline.Command;
+import org.apache.ignite.internal.commandline.CommandArgIterator;
+import org.apache.ignite.internal.commandline.CommandLogger;
+import org.apache.ignite.internal.visor.encryption.VisorChangeMasterKeyTask;
+
+import static org.apache.ignite.internal.commandline.CommandList.ENCRYPTION;
+import static org.apache.ignite.internal.commandline.TaskExecutor.executeTaskByNameOnNode;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.CHANGE_MASTER_KEY;
+
+/**
+ * Change master key encryption subcommand.
+ */
+public class ChangeMasterKeyCommand extends AbstractCommand<String> {
+ /** New master key name. */
+ private String argMasterKeyName;
+
+ /** {@inheritDoc} */
+ @Override public Object execute(GridClientConfiguration clientCfg, Logger log) throws Exception {
+ try (GridClient client = Command.startClient(clientCfg)) {
+ String resMsg = executeTaskByNameOnNode(
+ client,
+ VisorChangeMasterKeyTask.class.getName(),
+ argMasterKeyName,
+ null,
+ clientCfg
+ );
+
+ log.info(resMsg);
+
+ return resMsg;
+ }
+ catch (Throwable e) {
+ log.severe("Failed to perform operation.");
+ log.severe(CommandLogger.errorMessage(e));
+
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public String confirmationPrompt() {
+ return "Warning: the command will change the master key. Cache start and node join during the key change " +
+ "process is prohibited and will be rejected.";
+ }
+
+ /** {@inheritDoc} */
+ @Override public String arg() {
+ return argMasterKeyName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void parseArguments(CommandArgIterator argIter) {
+ argMasterKeyName = argIter.nextArg("Expected master key name.");
+ }
+
+ /** {@inheritDoc} */
+ @Override public void printUsage(Logger log) {
+ Command.usage(log, "Change the master key:", ENCRYPTION, CHANGE_MASTER_KEY.toString(), "newMasterKeyName");
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return CHANGE_MASTER_KEY.text().toUpperCase();
+ }
+}
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionCommand.java
deleted file mode 100644
index 5cbd723..0000000
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionCommand.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.commandline.encryption;
-
-import java.util.logging.Logger;
-import org.apache.ignite.internal.client.GridClient;
-import org.apache.ignite.internal.client.GridClientConfiguration;
-import org.apache.ignite.internal.commandline.AbstractCommand;
-import org.apache.ignite.internal.commandline.Command;
-import org.apache.ignite.internal.commandline.CommandArgIterator;
-import org.apache.ignite.internal.commandline.CommandLogger;
-import org.apache.ignite.internal.visor.encryption.VisorChangeMasterKeyTask;
-import org.apache.ignite.internal.visor.encryption.VisorGetMasterKeyNameTask;
-
-import static org.apache.ignite.internal.commandline.CommandList.ENCRYPTION;
-import static org.apache.ignite.internal.commandline.TaskExecutor.executeTaskByNameOnNode;
-import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommand.CHANGE_MASTER_KEY;
-import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommand.GET_MASTER_KEY_NAME;
-import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommand.of;
-
-/**
- * Commands assosiated with encryption features.
- *
- * @see EncryptionSubcommand
- */
-public class EncryptionCommand extends AbstractCommand<Object> {
- /** Subcommand. */
- EncryptionSubcommand cmd;
-
- /** The task name. */
- String taskName;
-
- /** The task arguments. */
- Object taskArgs;
-
- /** {@inheritDoc} */
- @Override public Object execute(GridClientConfiguration clientCfg, Logger logger) throws Exception {
- try (GridClient client = Command.startClient(clientCfg)) {
- String res = executeTaskByNameOnNode(
- client,
- taskName,
- taskArgs,
- null,
- clientCfg
- );
-
- logger.info(res);
-
- return res;
- }
- catch (Throwable e) {
- logger.severe("Failed to perform operation.");
- logger.severe(CommandLogger.errorMessage(e));
-
- throw e;
- }
- }
-
- /** {@inheritDoc} */
- @Override public String confirmationPrompt() {
- if (CHANGE_MASTER_KEY == cmd) {
- return "Warning: the command will change the master key. Cache start and node join during the key change " +
- "process is prohibited and will be rejected.";
- }
-
- return null;
- }
-
- /** {@inheritDoc} */
- @Override public void parseArguments(CommandArgIterator argIter) {
- EncryptionSubcommand cmd = of(argIter.nextArg("Expected encryption action."));
-
- if (cmd == null)
- throw new IllegalArgumentException("Expected correct encryption action.");
-
- switch (cmd) {
- case GET_MASTER_KEY_NAME:
- taskName = VisorGetMasterKeyNameTask.class.getName();
-
- taskArgs = null;
-
- break;
-
- case CHANGE_MASTER_KEY:
- String masterKeyName = argIter.nextArg("Expected master key name.");
-
- taskName = VisorChangeMasterKeyTask.class.getName();
-
- taskArgs = masterKeyName;
-
- break;
-
- default:
- throw new IllegalArgumentException("Unknown encryption subcommand: " + cmd);
- }
-
- this.cmd = cmd;
- }
-
- /** {@inheritDoc} */
- @Override public Object arg() {
- return taskArgs;
- }
-
- /** {@inheritDoc} */
- @Override public void printUsage(Logger logger) {
- Command.usage(logger, "Print the current master key name:", ENCRYPTION, GET_MASTER_KEY_NAME.toString());
- Command.usage(logger, "Change the master key:", ENCRYPTION, CHANGE_MASTER_KEY.toString(), "newMasterKeyName");
- }
-
- /** {@inheritDoc} */
- @Override public String name() {
- return ENCRYPTION.toCommandName();
- }
-}
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionCommands.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionCommands.java
new file mode 100644
index 0000000..fbae770
--- /dev/null
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionCommands.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.commandline.encryption;
+
+import java.util.logging.Logger;
+import org.apache.ignite.internal.client.GridClientConfiguration;
+import org.apache.ignite.internal.commandline.AbstractCommand;
+import org.apache.ignite.internal.commandline.CommandArgIterator;
+import org.apache.ignite.internal.commandline.CommandList;
+
+/**
+ * Commands related to encryption functions.
+ *
+ * @see EncryptionSubcommands
+ */
+public class EncryptionCommands extends AbstractCommand<EncryptionSubcommands> {
+ /** Subcommand. */
+ private EncryptionSubcommands cmd;
+
+ /** {@inheritDoc} */
+ @Override public Object execute(GridClientConfiguration clientCfg, Logger logger) throws Exception {
+ return cmd.subcommand().execute(clientCfg, logger);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void parseArguments(CommandArgIterator argIter) {
+ EncryptionSubcommands cmd = EncryptionSubcommands.of(argIter.nextArg("Expected encryption action."));
+
+ if (cmd == null)
+ throw new IllegalArgumentException("Expected correct encryption action.");
+
+ cmd.subcommand().parseArguments(argIter);
+
+ if (argIter.hasNextSubArg())
+ throw new IllegalArgumentException("Unexpected argument of --encryption subcommand: " + argIter.peekNextArg());
+
+ this.cmd = cmd;
+ }
+
+ /** {@inheritDoc} */
+ @Override public EncryptionSubcommands arg() {
+ return cmd;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void printUsage(Logger logger) {
+ for (EncryptionSubcommands cmd : EncryptionSubcommands.values())
+ cmd.subcommand().printUsage(logger);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return CommandList.ENCRYPTION.toCommandName();
+ }
+}
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionSubcommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionSubcommand.java
deleted file mode 100644
index 3c47c02..0000000
--- a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionSubcommand.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.commandline.encryption;
-
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Set of encryption subcommands.
- *
- * @see EncryptionCommand
- */
-public enum EncryptionSubcommand {
- /** Subcommand to get the current master key name. */
- GET_MASTER_KEY_NAME("get_master_key_name"),
-
- /** Subcommand to change the master key. */
- CHANGE_MASTER_KEY("change_master_key");
-
- /** Subcommand name. */
- private final String name;
-
- /** @param name Encryption subcommand name. */
- EncryptionSubcommand(String name) {
- this.name = name;
- }
-
- /**
- * @param text Command text (case insensitive).
- * @return Command for the text. {@code Null} if there is no such command.
- */
- @Nullable public static EncryptionSubcommand of(String text) {
- for (EncryptionSubcommand cmd : EncryptionSubcommand.values()) {
- if (cmd.name.equalsIgnoreCase(text))
- return cmd;
- }
-
- return null;
- }
-
- /** {@inheritDoc} */
- @Override public String toString() {
- return name;
- }
-}
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionSubcommands.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionSubcommands.java
new file mode 100644
index 0000000..c8d0941
--- /dev/null
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/EncryptionSubcommands.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.commandline.encryption;
+
+import org.apache.ignite.internal.commandline.Command;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Set of encryption subcommands.
+ *
+ * @see EncryptionCommands
+ */
+public enum EncryptionSubcommands {
+ /** Subcommand to get the current master key name. */
+ GET_MASTER_KEY_NAME("get_master_key_name", new GetMasterKeyNameCommand()),
+
+ /** Subcommand to change the master key. */
+ CHANGE_MASTER_KEY("change_master_key", new ChangeMasterKeyCommand()),
+
+ /** Subcommand to change the current encryption key for specified cache group. */
+ CHANGE_CACHE_GROUP_KEY("change_cache_key", new ChangeCacheGroupKeyCommand()),
+
+ /** Subcommand to view current encryption key IDs of the cache group. */
+ CACHE_GROUP_KEY_IDS("cache_key_ids", new CacheGroupEncryptionCommand.CacheKeyIds()),
+
+ /** Subcommand to display re-encryption status of the cache group. */
+ REENCRYPTION_STATUS("reencryption_status", new CacheGroupEncryptionCommand.ReencryptionStatus()),
+
+ /** Subcommand to suspend re-encryption of the cache group. */
+ REENCRYPTION_SUSPEND("suspend_reencryption", new CacheGroupEncryptionCommand.SuspendReencryption()),
+
+ /** Subcommand to resume re-encryption of the cache group. */
+ REENCRYPTION_RESUME("resume_reencryption", new CacheGroupEncryptionCommand.ResumeReencryption()),
+
+ /** Subcommand to view/change cache group re-encryption rate limit. */
+ REENCRYPTION_RATE("reencryption_rate_limit", new ReencryptionRateCommand());
+
+ /** Subcommand name. */
+ private final String name;
+
+ /** Command. */
+ private final Command<?> cmd;
+
+ /**
+ * @param name Encryption subcommand name.
+ * @param cmd Command implementation.
+ */
+ EncryptionSubcommands(String name, Command<?> cmd) {
+ this.name = name;
+ this.cmd = cmd;
+ }
+
+ /**
+ * @return Name.
+ */
+ public String text() {
+ return name;
+ }
+
+ /**
+ * @return Cache subcommand implementation.
+ */
+ public Command<?> subcommand() {
+ return cmd;
+ }
+
+ /**
+ * @param text Command text (case insensitive).
+ * @return Command for the text. {@code Null} if there is no such command.
+ */
+ @Nullable public static EncryptionSubcommands of(String text) {
+ for (EncryptionSubcommands cmd : values()) {
+ if (cmd.name.equalsIgnoreCase(text))
+ return cmd;
+ }
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return name;
+ }
+}
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/GetMasterKeyNameCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/GetMasterKeyNameCommand.java
new file mode 100644
index 0000000..02bb8ed
--- /dev/null
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/GetMasterKeyNameCommand.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.commandline.encryption;
+
+import java.util.logging.Logger;
+import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientConfiguration;
+import org.apache.ignite.internal.commandline.AbstractCommand;
+import org.apache.ignite.internal.commandline.Command;
+import org.apache.ignite.internal.commandline.CommandLogger;
+import org.apache.ignite.internal.visor.encryption.VisorGetMasterKeyNameTask;
+
+import static org.apache.ignite.internal.commandline.CommandList.ENCRYPTION;
+import static org.apache.ignite.internal.commandline.TaskExecutor.executeTaskByNameOnNode;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.GET_MASTER_KEY_NAME;
+
+/**
+ * Get master key name encryption subcommand.
+ */
+public class GetMasterKeyNameCommand extends AbstractCommand<Void> {
+ /** {@inheritDoc} */
+ @Override public Object execute(GridClientConfiguration clientCfg, Logger log) throws Exception {
+ try (GridClient client = Command.startClient(clientCfg)) {
+ String masterKeyName = executeTaskByNameOnNode(
+ client,
+ VisorGetMasterKeyNameTask.class.getName(),
+ null,
+ null,
+ clientCfg
+ );
+
+ log.info(masterKeyName);
+
+ return masterKeyName;
+ }
+ catch (Throwable e) {
+ log.severe("Failed to perform operation.");
+ log.severe(CommandLogger.errorMessage(e));
+
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public Void arg() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void printUsage(Logger log) {
+ Command.usage(log, "Print the current master key name:", ENCRYPTION, GET_MASTER_KEY_NAME.toString());
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return GET_MASTER_KEY_NAME.text().toUpperCase();
+ }
+}
diff --git a/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/ReencryptionRateCommand.java b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/ReencryptionRateCommand.java
new file mode 100644
index 0000000..7eb9f79
--- /dev/null
+++ b/modules/control-utility/src/main/java/org/apache/ignite/internal/commandline/encryption/ReencryptionRateCommand.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.commandline.encryption;
+
+import java.util.Map;
+import java.util.UUID;
+import java.util.logging.Logger;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.client.GridClient;
+import org.apache.ignite.internal.client.GridClientConfiguration;
+import org.apache.ignite.internal.commandline.AbstractCommand;
+import org.apache.ignite.internal.commandline.Command;
+import org.apache.ignite.internal.commandline.CommandArgIterator;
+import org.apache.ignite.internal.commandline.CommandLogger;
+import org.apache.ignite.internal.visor.encryption.VisorCacheGroupEncryptionTaskResult;
+import org.apache.ignite.internal.visor.encryption.VisorReencryptionRateTask;
+import org.apache.ignite.internal.visor.encryption.VisorReencryptionRateTaskArg;
+
+import static java.util.Collections.singletonMap;
+import static org.apache.ignite.internal.commandline.CommandList.ENCRYPTION;
+import static org.apache.ignite.internal.commandline.CommandLogger.DOUBLE_INDENT;
+import static org.apache.ignite.internal.commandline.CommandLogger.INDENT;
+import static org.apache.ignite.internal.commandline.CommandLogger.optional;
+import static org.apache.ignite.internal.commandline.TaskExecutor.BROADCAST_UUID;
+import static org.apache.ignite.internal.commandline.TaskExecutor.executeTaskByNameOnNode;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.REENCRYPTION_RATE;
+
+/**
+ * View/change cache group re-encryption rate limit subcommand.
+ */
+public class ReencryptionRateCommand extends AbstractCommand<VisorReencryptionRateTaskArg> {
+ /** Re-encryption rate task argument. */
+ private VisorReencryptionRateTaskArg taskArg;
+
+ /** {@inheritDoc} */
+ @Override public Object execute(GridClientConfiguration clientCfg, Logger log) throws Exception {
+ try (GridClient client = Command.startClient(clientCfg)) {
+ VisorCacheGroupEncryptionTaskResult<Double> res = executeTaskByNameOnNode(
+ client,
+ VisorReencryptionRateTask.class.getName(),
+ taskArg,
+ BROADCAST_UUID,
+ clientCfg
+ );
+
+ Map<UUID, IgniteException> exceptions = res.exceptions();
+
+ for (Map.Entry<UUID, IgniteException> entry : exceptions.entrySet()) {
+ log.info(INDENT + "Node " + entry.getKey() + ":");
+ log.info(DOUBLE_INDENT +
+ "failed to get/set re-encryption rate limit: " + entry.getValue().getMessage());
+ }
+
+ Map<UUID, Double> results = res.results();
+ boolean read = taskArg.rate() == null;
+
+ for (Map.Entry<UUID, Double> entry : results.entrySet()) {
+ log.info(INDENT + "Node " + entry.getKey() + ":");
+
+ double rateLimit = read ? entry.getValue() : taskArg.rate();
+
+ if (rateLimit == 0)
+ log.info(DOUBLE_INDENT + "re-encryption rate is not limited.");
+ else {
+ log.info(String.format("%sre-encryption rate %s limited to %.2f MB/s.",
+ DOUBLE_INDENT, (read ? "is" : "has been"), rateLimit));
+ }
+ }
+
+ if (read)
+ return null;
+
+ log.info("");
+ log.info("Note: the changed value of the re-encryption rate limit is not persisted. " +
+ "When the node is restarted, the value will be set from the configuration.");
+ log.info("");
+
+ return null;
+ }
+ catch (Throwable e) {
+ log.severe("Failed to perform operation.");
+ log.severe(CommandLogger.errorMessage(e));
+
+ throw e;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public VisorReencryptionRateTaskArg arg() {
+ return taskArg;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void parseArguments(CommandArgIterator argIter) {
+ Double rateLimit = null;
+
+ while (argIter.hasNextSubArg()) {
+ String rateLimitArg = argIter.nextArg("Expected decimal value for re-encryption rate.");
+
+ try {
+ rateLimit = Double.parseDouble(rateLimitArg);
+ }
+ catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Failed to parse command argument. Decimal value expected.", e);
+ }
+ }
+
+ taskArg = new VisorReencryptionRateTaskArg(rateLimit);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void printUsage(Logger log) {
+ Command.usage(log, "View/change re-encryption rate limit:", ENCRYPTION,
+ singletonMap("new_limit", "Decimal value to change re-encryption rate limit (MB/s)."),
+ REENCRYPTION_RATE.toString(), optional("new_limit"));
+ }
+
+ /** {@inheritDoc} */
+ @Override public String name() {
+ return REENCRYPTION_RATE.text().toUpperCase();
+ }
+}
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java
index bc31ce7..8dfbb37 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerAbstractTest.java
@@ -40,6 +40,7 @@ import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.ConnectorConfiguration;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
+import org.apache.ignite.configuration.EncryptionConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.configuration.WALMode;
import org.apache.ignite.internal.IgniteEx;
@@ -68,6 +69,8 @@ import static java.util.Arrays.asList;
import static java.util.Objects.nonNull;
import static org.apache.ignite.IgniteSystemProperties.IGNITE_ENABLE_EXPERIMENTAL_COMMAND;
import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_CHECKPOINT_FREQ;
+import static org.apache.ignite.configuration.EncryptionConfiguration.DFLT_REENCRYPTION_BATCH_SIZE;
+import static org.apache.ignite.configuration.EncryptionConfiguration.DFLT_REENCRYPTION_RATE_MBPS;
import static org.apache.ignite.internal.encryption.AbstractEncryptionTest.KEYSTORE_PASSWORD;
import static org.apache.ignite.internal.encryption.AbstractEncryptionTest.KEYSTORE_PATH;
import static org.apache.ignite.internal.processors.cache.verify.VerifyBackupPartitionsDumpTask.IDLE_DUMP_FILE_PREFIX;
@@ -85,6 +88,9 @@ public abstract class GridCommandHandlerAbstractTest extends GridCommonAbstractT
/** */
protected static final String CLIENT_NODE_NAME_PREFIX = "client";
+ /** */
+ protected static final String DAEMON_NODE_NAME_PREFIX = "daemon";
+
/** Option is used for auto confirmation. */
protected static final String CMD_AUTO_CONFIRMATION = "--yes";
@@ -113,7 +119,13 @@ public abstract class GridCommandHandlerAbstractTest extends GridCommonAbstractT
protected boolean autoConfirmation = true;
/** {@code True} if encription is enabled. */
- protected boolean encriptionEnabled;
+ protected boolean encryptionEnabled;
+
+ /** Re-encryption rate limit in megabytes per second. */
+ protected double reencryptSpeed = DFLT_REENCRYPTION_RATE_MBPS;
+
+ /** The number of pages that is scanned during re-encryption under checkpoint lock. */
+ protected int reencryptBatchSize = DFLT_REENCRYPTION_BATCH_SIZE;
/** Last operation result. */
protected Object lastOperationResult;
@@ -171,7 +183,7 @@ public abstract class GridCommandHandlerAbstractTest extends GridCommonAbstractT
testOut.reset();
- encriptionEnabled = false;
+ encryptionEnabled = false;
GridClientFactory.stopAll(false);
}
@@ -233,13 +245,22 @@ public abstract class GridCommandHandlerAbstractTest extends GridCommonAbstractT
cfg.setClientMode(igniteInstanceName.startsWith(CLIENT_NODE_NAME_PREFIX));
- if (encriptionEnabled) {
+ cfg.setDaemon(igniteInstanceName.startsWith(DAEMON_NODE_NAME_PREFIX));
+
+ if (encryptionEnabled) {
KeystoreEncryptionSpi encSpi = new KeystoreEncryptionSpi();
encSpi.setKeyStorePath(KEYSTORE_PATH);
encSpi.setKeyStorePassword(KEYSTORE_PASSWORD.toCharArray());
cfg.setEncryptionSpi(encSpi);
+
+ EncryptionConfiguration encCfg = new EncryptionConfiguration();
+
+ encCfg.setReencryptionRateLimit(reencryptSpeed);
+ encCfg.setReencryptionBatchSize(reencryptBatchSize);
+
+ dsCfg.setEncryptionConfiguration(encCfg);
}
return cfg;
@@ -418,7 +439,8 @@ public abstract class GridCommandHandlerAbstractTest extends GridCommonAbstractT
CacheConfiguration<?, ?> ccfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME)
.setAffinity(new RendezvousAffinityFunction(false, partitions))
- .setBackups(1);
+ .setBackups(1)
+ .setEncryptionEnabled(encryptionEnabled);
if (filter != null)
ccfg.setNodeFilter(filter);
diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
index cd22973..ec1508d 100644
--- a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
+++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerTest.java
@@ -102,8 +102,10 @@ import org.apache.ignite.internal.processors.cache.warmup.BlockedWarmUpStrategy;
import org.apache.ignite.internal.processors.cache.warmup.WarmUpTestPluginProvider;
import org.apache.ignite.internal.processors.cluster.GridClusterStateProcessor;
import org.apache.ignite.internal.util.lang.GridAbsPredicate;
+import org.apache.ignite.internal.util.lang.GridFunc;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.X;
+import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.visor.cache.VisorFindAndDeleteGarbageInPersistenceTaskResult;
import org.apache.ignite.internal.visor.tx.VisorTxInfo;
@@ -140,6 +142,12 @@ import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_IN
import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK;
import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_UNEXPECTED_ERROR;
import static org.apache.ignite.internal.commandline.CommandList.DEACTIVATE;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.CACHE_GROUP_KEY_IDS;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.CHANGE_CACHE_GROUP_KEY;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.REENCRYPTION_RATE;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.REENCRYPTION_RESUME;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.REENCRYPTION_STATUS;
+import static org.apache.ignite.internal.commandline.encryption.EncryptionSubcommands.REENCRYPTION_SUSPEND;
import static org.apache.ignite.internal.encryption.AbstractEncryptionTest.MASTER_KEY_NAME_2;
import static org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager.IGNITE_PDS_SKIP_CHECKPOINT_ON_NODE_STOP;
import static org.apache.ignite.internal.processors.cache.persistence.snapshot.AbstractSnapshotSelfTest.doSnapshotCancellationTest;
@@ -2630,7 +2638,7 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
/** @throws Exception If failed. */
@Test
public void testMasterKeyChange() throws Exception {
- encriptionEnabled = true;
+ encryptionEnabled = true;
injectTestSystemOut();
@@ -2667,8 +2675,187 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
/** @throws Exception If failed. */
@Test
+ public void testCacheGroupKeyChange() throws Exception {
+ encryptionEnabled = true;
+
+ injectTestSystemOut();
+
+ int srvNodes = 2;
+
+ IgniteEx ignite = startGrids(srvNodes);
+
+ startGrid(CLIENT_NODE_NAME_PREFIX);
+ startGrid(DAEMON_NODE_NAME_PREFIX);
+
+ ignite.cluster().state(ACTIVE);
+
+ List<Ignite> srvGrids = GridFunc.asList(grid(0), grid(1));
+
+ enableCheckpoints(srvGrids, false);
+
+ createCacheAndPreload(ignite, 1000);
+
+ int ret = execute("--encryption", CACHE_GROUP_KEY_IDS.toString(), DEFAULT_CACHE_NAME);
+
+ assertEquals(EXIT_CODE_OK, ret);
+ assertContains(log, testOut.toString(), "Encryption key identifiers for cache: " + DEFAULT_CACHE_NAME);
+ assertEquals(srvNodes, countSubstrs(testOut.toString(), "0 (active)"));
+
+ ret = execute("--encryption", CHANGE_CACHE_GROUP_KEY.toString(), DEFAULT_CACHE_NAME);
+
+ assertEquals(EXIT_CODE_OK, ret);
+ assertContains(log, testOut.toString(),
+ "The encryption key has been changed for the cache group \"" + DEFAULT_CACHE_NAME + '"');
+
+ ret = execute("--encryption", CACHE_GROUP_KEY_IDS.toString(), DEFAULT_CACHE_NAME);
+
+ assertEquals(testOut.toString(), EXIT_CODE_OK, ret);
+ assertContains(log, testOut.toString(), "Encryption key identifiers for cache: " + DEFAULT_CACHE_NAME);
+ assertEquals(srvNodes, countSubstrs(testOut.toString(), "1 (active)"));
+
+ GridTestUtils.waitForCondition(() -> {
+ execute("--encryption", REENCRYPTION_STATUS.toString(), DEFAULT_CACHE_NAME);
+
+ return srvNodes == countSubstrs(testOut.toString(),
+ "re-encryption will be completed after the next checkpoint");
+ }, getTestTimeout());
+
+ enableCheckpoints(srvGrids, true);
+ forceCheckpoint(srvGrids);
+
+ GridTestUtils.waitForCondition(() -> {
+ execute("--encryption", REENCRYPTION_STATUS.toString(), DEFAULT_CACHE_NAME);
+
+ return srvNodes == countSubstrs(testOut.toString(), "re-encryption completed or not required");
+ }, getTestTimeout());
+ }
+
+ /** @throws Exception If failed. */
+ @Test
+ public void testChangeReencryptionRate() throws Exception {
+ int srvNodes = 2;
+
+ IgniteEx ignite = startGrids(srvNodes);
+
+ ignite.cluster().state(ACTIVE);
+
+ injectTestSystemOut();
+
+ int ret = execute("--encryption", REENCRYPTION_RATE.toString());
+
+ assertEquals(EXIT_CODE_OK, ret);
+ assertEquals(srvNodes, countSubstrs(testOut.toString(), "re-encryption rate is not limited."));
+
+ double newRate = 0.01;
+
+ ret = execute("--encryption", REENCRYPTION_RATE.toString(), Double.toString(newRate));
+
+ assertEquals(EXIT_CODE_OK, ret);
+ assertEquals(srvNodes, countSubstrs(testOut.toString(),
+ String.format("re-encryption rate has been limited to %.2f MB/s.", newRate)));
+
+ ret = execute("--encryption", REENCRYPTION_RATE.toString());
+
+ assertEquals(EXIT_CODE_OK, ret);
+ assertEquals(srvNodes, countSubstrs(testOut.toString(),
+ String.format("re-encryption rate is limited to %.2f MB/s.", newRate)));
+
+ ret = execute("--encryption", REENCRYPTION_RATE.toString(), "0");
+
+ assertEquals(EXIT_CODE_OK, ret);
+ assertEquals(srvNodes, countSubstrs(testOut.toString(), "re-encryption rate is not limited."));
+ }
+
+ /** @throws Exception If failed. */
+ @Test
+ public void testReencryptionSuspendAndResume() throws Exception {
+ encryptionEnabled = true;
+ reencryptSpeed = 0.01;
+ reencryptBatchSize = 1;
+
+ int srvNodes = 2;
+
+ IgniteEx ignite = startGrids(srvNodes);
+
+ ignite.cluster().state(ACTIVE);
+
+ injectTestSystemOut();
+
+ createCacheAndPreload(ignite, 10_000);
+
+ ignite.encryption().changeCacheGroupKey(Collections.singleton(DEFAULT_CACHE_NAME)).get();
+
+ assertTrue(isReencryptionStarted(DEFAULT_CACHE_NAME));
+
+ int ret = execute("--encryption", REENCRYPTION_STATUS.toString(), DEFAULT_CACHE_NAME);
+
+ assertEquals(EXIT_CODE_OK, ret);
+
+ Pattern ptrn = Pattern.compile("(?m)Node [-0-9a-f]{36}:\n\\s+(?<left>\\d+) KB of data.+");
+ Matcher matcher = ptrn.matcher(testOut.toString());
+ int matchesCnt = 0;
+
+ while (matcher.find()) {
+ assertEquals(1, matcher.groupCount());
+
+ int pagesLeft = Integer.parseInt(matcher.group("left"));
+
+ assertTrue(pagesLeft > 0);
+
+ matchesCnt++;
+ }
+
+ assertEquals(srvNodes, matchesCnt);
+
+ ret = execute("--encryption", REENCRYPTION_SUSPEND.toString(), DEFAULT_CACHE_NAME);
+
+ assertEquals(EXIT_CODE_OK, ret);
+ assertEquals(srvNodes, countSubstrs(testOut.toString(),
+ "re-encryption of the cache group \"" + DEFAULT_CACHE_NAME + "\" has been suspended."));
+ assertFalse(isReencryptionStarted(DEFAULT_CACHE_NAME));
+
+ ret = execute("--encryption", REENCRYPTION_SUSPEND.toString(), DEFAULT_CACHE_NAME);
+
+ assertEquals(EXIT_CODE_OK, ret);
+ assertEquals(srvNodes, countSubstrs(testOut.toString(),
+ "re-encryption of the cache group \"" + DEFAULT_CACHE_NAME + "\" has already been suspended."));
+
+ ret = execute("--encryption", REENCRYPTION_RESUME.toString(), DEFAULT_CACHE_NAME);
+
+ assertEquals(EXIT_CODE_OK, ret);
+ assertEquals(srvNodes, countSubstrs(testOut.toString(),
+ "re-encryption of the cache group \"" + DEFAULT_CACHE_NAME + "\" has been resumed."));
+ assertTrue(isReencryptionStarted(DEFAULT_CACHE_NAME));
+
+ ret = execute("--encryption", REENCRYPTION_RESUME.toString(), DEFAULT_CACHE_NAME);
+
+ assertEquals(EXIT_CODE_OK, ret);
+ assertEquals(srvNodes, countSubstrs(testOut.toString(),
+ "re-encryption of the cache group \"" + DEFAULT_CACHE_NAME + "\" has already been resumed."));
+ }
+
+ /**
+ * @param cacheName Cache name.
+ * @return {@code True} if re-encryption of the specified cache is started on all server nodes.
+ */
+ private boolean isReencryptionStarted(String cacheName) {
+ for (Ignite grid : G.allGrids()) {
+ ClusterNode locNode = grid.cluster().localNode();
+
+ if (locNode.isClient() || locNode.isDaemon())
+ continue;
+
+ if (((IgniteEx)grid).context().encryption().reencryptionFuture(CU.cacheId(cacheName)).isDone())
+ return false;
+ }
+
+ return true;
+ }
+
+ /** @throws Exception If failed. */
+ @Test
public void testMasterKeyChangeOnInactiveCluster() throws Exception {
- encriptionEnabled = true;
+ encryptionEnabled = true;
injectTestSystemOut();
@@ -2838,4 +3025,18 @@ public class GridCommandHandlerTest extends GridCommandHandlerClusterPerMethodAb
return hnd.getLastOperationResult();
}
+
+ /**
+ * @param str String.
+ * @param substr Substring to find in the specified string.
+ * @return The number of substrings found in the specified string.
+ */
+ private int countSubstrs(String str, String substr) {
+ int cnt = 0;
+
+ for (int off = 0; (off = str.indexOf(substr, off)) != -1; off++)
+ ++cnt;
+
+ return cnt;
+ }
}
diff --git a/modules/core/src/main/java/org/apache/ignite/configuration/EncryptionConfiguration.java b/modules/core/src/main/java/org/apache/ignite/configuration/EncryptionConfiguration.java
index 79e205e..6b9345d 100644
--- a/modules/core/src/main/java/org/apache/ignite/configuration/EncryptionConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/configuration/EncryptionConfiguration.java
@@ -75,7 +75,7 @@ public class EncryptionConfiguration implements Serializable {
*/
public EncryptionConfiguration setReencryptionRateLimit(double reencryptionRateLimit) {
A.ensure(reencryptionRateLimit >= 0,
- "Reencryption rate limit (" + reencryptionRateLimit + ") must be non-negative.");
+ "Re-encryption rate limit (" + reencryptionRateLimit + ") must be non-negative.");
this.reencryptionRateLimit = reencryptionRateLimit;
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/encryption/CacheGroupPageScanner.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/encryption/CacheGroupPageScanner.java
index dc0a29b..c2a6895 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/encryption/CacheGroupPageScanner.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/encryption/CacheGroupPageScanner.java
@@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
@@ -77,15 +78,15 @@ public class CacheGroupPageScanner implements CheckpointListener {
/** Collection of groups waiting for a checkpoint. */
private final Collection<GroupScanTask> cpWaitGrps = new ConcurrentLinkedQueue<>();
- /** Page scanning speed limiter. */
- private final BasicRateLimiter limiter;
-
/** Single-threaded executor to run cache group scan task. */
private final ThreadPoolExecutor singleExecSvc;
/** Number of pages that is scanned during reencryption under checkpoint lock. */
private final int batchSize;
+ /** Page scanning speed limiter. */
+ private final BasicRateLimiter limiter;
+
/** Stop flag. */
private boolean stopped;
@@ -109,8 +110,7 @@ public class CacheGroupPageScanner implements CheckpointListener {
double rateLimit = dsCfg.getEncryptionConfiguration().getReencryptionRateLimit();
- limiter = rateLimit > 0 ? new BasicRateLimiter(rateLimit * MB /
- (dsCfg.getPageSize() == 0 ? DataStorageConfiguration.DFLT_PAGE_SIZE : dsCfg.getPageSize())) : null;
+ limiter = new BasicRateLimiter(calcPermits(rateLimit, dsCfg));
batchSize = dsCfg.getEncryptionConfiguration().getReencryptionBatchSize();
@@ -210,10 +210,13 @@ public class CacheGroupPageScanner implements CheckpointListener {
}
Set<Integer> parts = new HashSet<>();
+ long[] pagesLeft = new long[1];
forEachPageStore(grp, new IgniteInClosureX<Integer>() {
@Override public void applyx(Integer partId) {
- if (ctx.encryption().getEncryptionState(grpId, partId) == 0) {
+ long encState = ctx.encryption().getEncryptionState(grpId, partId);
+
+ if (encState == 0) {
if (log.isDebugEnabled())
log.debug("Skipping partition reencryption [grp=" + grpId + ", p=" + partId + "]");
@@ -221,10 +224,12 @@ public class CacheGroupPageScanner implements CheckpointListener {
}
parts.add(partId);
+
+ pagesLeft[0] += (ReencryptStateUtils.pageCount(encState) - ReencryptStateUtils.pageIndex(encState));
}
});
- GroupScanTask grpScan = new GroupScanTask(grp, parts);
+ GroupScanTask grpScan = new GroupScanTask(grp, parts, pagesLeft[0]);
singleExecSvc.submit(grpScan);
@@ -314,6 +319,51 @@ public class CacheGroupPageScanner implements CheckpointListener {
}
/**
+ * @param grpId Cache group ID.
+ * @return Number of remaining memory pages to scan.
+ */
+ public long remainingPagesCount(int grpId) {
+ GroupScanTask grpScanTask = grps.get(grpId);
+
+ if (grpScanTask != null)
+ return grpScanTask.remainingPagesCount();
+
+ return 0;
+ }
+
+ /**
+ * @return Re-encryption rate limit in megabytes per second ({@code 0} - unlimited).
+ */
+ public double getRate() {
+ DataStorageConfiguration dsCfg = ctx.config().getDataStorageConfiguration();
+
+ if (CU.isPersistenceEnabled(dsCfg))
+ return dsCfg.getPageSize() * limiter.getRate() / MB;
+
+ return 0;
+ }
+
+ /**
+ * @param rate Re-encryption rate limit in megabytes per second ({@code 0} - unlimited).
+ */
+ public void setRate(double rate) {
+ DataStorageConfiguration dsCfg = ctx.config().getDataStorageConfiguration();
+
+ if (CU.isPersistenceEnabled(dsCfg))
+ limiter.setRate(calcPermits(rate, dsCfg));
+ }
+
+ /**
+ * @param rate Maximum scan speed in megabytes per second
+ * @param dsCfg Datastorage configuration.
+ * @return The number of permits allowed per second.
+ */
+ private double calcPermits(double rate, DataStorageConfiguration dsCfg) {
+ return rate * MB /
+ (dsCfg.getPageSize() == 0 ? DataStorageConfiguration.DFLT_PAGE_SIZE : dsCfg.getPageSize());
+ }
+
+ /**
* @param grp Cache group.
* @param hnd Partition handler.
*/
@@ -345,13 +395,17 @@ public class CacheGroupPageScanner implements CheckpointListener {
/** Page memory. */
private final PageMemoryEx pageMem;
+ /** Total memory pages left for reencryption. */
+ private final AtomicLong remainingPagesCntr;
+
/**
* @param grp Cache group.
*/
- public GroupScanTask(CacheGroupContext grp, Set<Integer> parts) {
+ public GroupScanTask(CacheGroupContext grp, Set<Integer> parts, long remainingPagesCnt) {
this.grp = grp;
this.parts = new GridConcurrentHashSet<>(parts);
+ remainingPagesCntr = new AtomicLong(remainingPagesCnt);
pageMem = (PageMemoryEx)grp.dataRegion().pageMemory();
}
@@ -367,6 +421,10 @@ public class CacheGroupPageScanner implements CheckpointListener {
* @return {@code True} if reencryption was cancelled.
*/
public synchronized boolean excludePartition(int partId) {
+ long state = ctx.encryption().getEncryptionState(groupId(), partId);
+
+ remainingPagesCntr.addAndGet(ReencryptStateUtils.pageIndex(state) - ReencryptStateUtils.pageCount(state));
+
return parts.remove(partId);
}
@@ -377,6 +435,13 @@ public class CacheGroupPageScanner implements CheckpointListener {
return grp.groupId();
}
+ /**
+ * @return Number of remaining memory pages to scan.
+ */
+ public long remainingPagesCount() {
+ return remainingPagesCntr.get();
+ }
+
/** {@inheritDoc} */
@Override public void run() {
try {
@@ -418,8 +483,7 @@ public class CacheGroupPageScanner implements CheckpointListener {
while (off < cnt) {
int pagesCnt = Math.min(batchSize, cnt - off);
- if (limiter != null)
- limiter.acquire(pagesCnt);
+ limiter.acquire(pagesCnt);
synchronized (this) {
if (isDone() || !parts.contains(partId))
@@ -435,6 +499,8 @@ public class CacheGroupPageScanner implements CheckpointListener {
}
}
+ remainingPagesCntr.addAndGet(-pagesCnt);
+
ctx.encryption().setEncryptionState(grp, partId, off, cnt);
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/encryption/GridEncryptionManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/encryption/GridEncryptionManager.java
index 2c31dcd..d0d467f 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/managers/encryption/GridEncryptionManager.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/encryption/GridEncryptionManager.java
@@ -824,6 +824,20 @@ public class GridEncryptionManager extends GridManagerAdapter<EncryptionSpi> imp
}
/**
+ * @return Re-encryption rate limit in megabytes per second ({@code 0} - unlimited).
+ */
+ public double getReencryptionRate() {
+ return pageScanner.getRate();
+ }
+
+ /**
+ * @param rate Re-encryption rate limit in megabytes per second ({@code 0} - unlimited).
+ */
+ public void setReencryptionRate(double rate) {
+ pageScanner.setRate(rate);
+ }
+
+ /**
* Removes encryption key(s).
*
* @param grpId Cache group ID.
@@ -1116,6 +1130,14 @@ public class GridEncryptionManager extends GridManagerAdapter<EncryptionSpi> imp
}
/**
+ * @param grpId Cache group ID.
+ * @return The number of bytes left for re-ecryption.
+ */
+ public long getBytesLeftForReencryption(int grpId) {
+ return pageScanner.remainingPagesCount(grpId) * ctx.config().getDataStorageConfiguration().getPageSize();
+ }
+
+ /**
* @param keyCnt Count of keys to generate.
* @return Future that will contain results of generation.
*/
@@ -1160,6 +1182,32 @@ public class GridEncryptionManager extends GridManagerAdapter<EncryptionSpi> imp
}
/**
+ * Suspend re-encryption of the cache group.
+ *
+ * @param grpId Cache group ID.
+ */
+ public boolean suspendReencryption(int grpId) throws IgniteCheckedException {
+ return reencryptionFuture(grpId).cancel();
+ }
+
+ /**
+ * Forces re-encryption of the cache group.
+ *
+ * @param grpId Cache group ID.
+ */
+ public boolean resumeReencryption(int grpId) throws IgniteCheckedException {
+ if (!reencryptionFuture(grpId).isDone())
+ return false;
+
+ if (!reencryptionInProgress(grpId))
+ throw new IgniteCheckedException("Re-encryption completed or not required [grpId=" + grpId + "]");
+
+ startReencryption(Collections.singleton(grpId));
+
+ return true;
+ }
+
+ /**
* @param grpIds Cache group IDs.
* @throws IgniteCheckedException If failed.
*/
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupMetricsImpl.java
index 75fdd15..b7c0300 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupMetricsImpl.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheGroupMetricsImpl.java
@@ -25,15 +25,10 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
-import org.apache.ignite.IgniteCheckedException;
-import org.apache.ignite.IgniteException;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
-import org.apache.ignite.internal.managers.encryption.GridEncryptionManager;
-import org.apache.ignite.internal.managers.encryption.ReencryptStateUtils;
-import org.apache.ignite.internal.pagemem.PageIdAllocator;
import org.apache.ignite.internal.pagemem.store.PageStore;
import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
@@ -44,7 +39,6 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.topology.Grid
import org.apache.ignite.internal.processors.cache.persistence.DataRegion;
import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetricsImpl;
import org.apache.ignite.internal.processors.cache.persistence.GridCacheDatabaseSharedManager;
-import org.apache.ignite.internal.processors.cache.persistence.file.FilePageStoreManager;
import org.apache.ignite.internal.processors.metric.MetricRegistry;
import org.apache.ignite.internal.processors.metric.impl.AtomicLongMetric;
import org.apache.ignite.internal.processors.metric.impl.LongAdderMetric;
@@ -187,9 +181,9 @@ public class CacheGroupMetricsImpl {
() -> !ctx.shared().kernalContext().encryption().reencryptionInProgress(ctx.groupId()),
"The flag indicates whether reencryption is finished or not.");
- mreg.register("ReencryptionPagesLeft",
- this::getPagesLeftForReencryption,
- "Number of pages left for reencryption.");
+ mreg.register("ReencryptionBytesLeft",
+ () -> ctx.shared().kernalContext().encryption().getBytesLeftForReencryption(ctx.groupId()),
+ "The number of bytes left for re-ecryption.");
}
}
@@ -502,40 +496,6 @@ public class CacheGroupMetricsImpl {
return sparseStorageSize == null ? 0 : sparseStorageSize.value();
}
- /** */
- public long getPagesLeftForReencryption() {
- if (!ctx.shared().kernalContext().encryption().reencryptionInProgress(ctx.groupId()))
- return 0;
-
- long pagesLeft = 0;
-
- FilePageStoreManager mgr = (FilePageStoreManager)ctx.shared().pageStore();
-
- GridEncryptionManager encMgr = ctx.shared().kernalContext().encryption();
-
- try {
- for (int p = 0; p < ctx.affinity().partitions(); p++) {
- PageStore pageStore = mgr.getStore(ctx.groupId(), p);
-
- if (!pageStore.exists())
- continue;
-
- long state = encMgr.getEncryptionState(ctx.groupId(), p);
-
- pagesLeft += ReencryptStateUtils.pageCount(state) - ReencryptStateUtils.pageIndex(state);
- }
-
- long state = encMgr.getEncryptionState(ctx.groupId(), PageIdAllocator.INDEX_PARTITION);
-
- pagesLeft += ReencryptStateUtils.pageCount(state) - ReencryptStateUtils.pageIndex(state);
- }
- catch (IgniteCheckedException e) {
- throw new IgniteException(e);
- }
-
- return pagesLeft;
- }
-
/** Removes all metric for cache group. */
public void remove() {
if (ctx.shared().kernalContext().isStopping())
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/BasicRateLimiter.java b/modules/core/src/main/java/org/apache/ignite/internal/util/BasicRateLimiter.java
index 8429ccb..195264e 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/BasicRateLimiter.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/BasicRateLimiter.java
@@ -65,6 +65,11 @@ public class BasicRateLimiter {
private long nextFreeTicketMicros;
/**
+ * The flag indicates that the rate is not limited.
+ */
+ private volatile boolean unlimited;
+
+ /**
* @param permitsPerSecond Estimated number of permits per second.
*/
public BasicRateLimiter(double permitsPerSecond) {
@@ -74,11 +79,14 @@ public class BasicRateLimiter {
/**
* Updates the stable rate.
*
- * @param permitsPerSecond The new stable rate of this {@code RateLimiter}.
+ * @param permitsPerSecond The new stable rate of this {@code RateLimiter}, set {@code 0} for unlimited rate.
* @throws IllegalArgumentException If {@code permitsPerSecond} is negative or zero.
*/
public void setRate(double permitsPerSecond) {
- A.ensure(permitsPerSecond > 0, "Requested permits (" + permitsPerSecond + ") must be positive");
+ A.ensure(permitsPerSecond >= 0, "Requested permits (" + permitsPerSecond + ") must be non-negative.");
+
+ if (unlimited = (permitsPerSecond == 0))
+ return;
synchronized (mux) {
resync();
@@ -88,9 +96,12 @@ public class BasicRateLimiter {
}
/**
- * @return The stable rate (as {@code permits per seconds}).
+ * @return The stable rate as {@code permits per seconds} ({@code 0} means that the rate is unlimited).
*/
public double getRate() {
+ if (unlimited)
+ return 0;
+
synchronized (mux) {
return SECONDS.toMicros(1L) / stableIntervalMicros;
}
@@ -104,6 +115,9 @@ public class BasicRateLimiter {
* @throws IllegalArgumentException If the requested number of permits is negative or zero.
*/
public void acquire(int permits) throws IgniteInterruptedCheckedException {
+ if (unlimited)
+ return;
+
long microsToWait = reserve(permits);
try {
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorCacheGroupEncryptionTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorCacheGroupEncryptionTask.java
new file mode 100644
index 0000000..37d5096
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorCacheGroupEncryptionTask.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.encryption;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.cache.IgniteInternalCache;
+import org.apache.ignite.internal.util.typedef.internal.CU;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.apache.ignite.internal.visor.VisorMultiNodeTask;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Visor encrypted cache group multinode task.
+ *
+ * @param <T> The type of the task result.
+ */
+public abstract class VisorCacheGroupEncryptionTask<T> extends VisorMultiNodeTask<VisorCacheGroupEncryptionTaskArg,
+ VisorCacheGroupEncryptionTaskResult<T>, VisorCacheGroupEncryptionTask.VisorSingleFieldDto<T>>
+{
+ /** {@inheritDoc} */
+ @Nullable @Override protected VisorCacheGroupEncryptionTaskResult<T> reduce0(List<ComputeJobResult> results) {
+ Map<UUID, T> jobResults = new HashMap<>();
+ Map<UUID, IgniteException> exceptions = new HashMap<>();
+
+ for (ComputeJobResult res : results) {
+ UUID nodeId = res.getNode().id();
+
+ if (res.getException() != null) {
+ exceptions.put(nodeId, res.getException());
+
+ continue;
+ }
+
+ VisorSingleFieldDto<T> dtoRes = res.getData();
+
+ jobResults.put(nodeId, dtoRes.value());
+ }
+
+ return new VisorCacheGroupEncryptionTaskResult<>(jobResults, exceptions);
+ }
+
+ /** */
+ protected abstract static class VisorSingleFieldDto<T> extends IgniteDataTransferObject {
+ /** Object value. */
+ private T val;
+
+ /**
+ * @return Object value.
+ */
+ protected T value() {
+ return val;
+ }
+
+ /**
+ * @param val Data object.
+ * @return {@code this} for chaining.
+ */
+ protected VisorSingleFieldDto<T> value(T val) {
+ this.val = val;
+
+ return this;
+ }
+ }
+
+ /**
+ * @param <T> Type of job result.
+ */
+ protected abstract static class VisorReencryptionBaseJob<T>
+ extends VisorJob<VisorCacheGroupEncryptionTaskArg, VisorSingleFieldDto<T>> {
+ /**
+ * @param arg Job argument.
+ * @param debug Flag indicating whether debug information should be printed into node log.
+ */
+ protected VisorReencryptionBaseJob(@Nullable VisorCacheGroupEncryptionTaskArg arg, boolean debug) {
+ super(arg, debug);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected VisorSingleFieldDto<T> run(VisorCacheGroupEncryptionTaskArg arg) throws IgniteException {
+ try {
+ String grpName = arg.groupName();
+ CacheGroupContext grp = ignite.context().cache().cacheGroup(CU.cacheId(grpName));
+
+ if (grp == null) {
+ IgniteInternalCache<Object, Object> cache = ignite.context().cache().cache(grpName);
+
+ if (cache == null)
+ throw new IgniteException("Cache group " + grpName + " not found.");
+
+ grp = cache.context().group();
+
+ if (grp.sharedGroup()) {
+ throw new IgniteException("Cache or group \"" + grpName + "\" is a part of group \"" +
+ grp.name() + "\". Provide group name instead of cache name for shared groups.");
+ }
+ }
+
+ return run0(grp);
+ }
+ catch (IgniteCheckedException e) {
+ throw new IgniteException(e);
+ }
+ }
+
+ /**
+ * Executes internal logic of the job.
+ *
+ * @param grp Cache group.
+ * @return Result.
+ * @throws IgniteCheckedException In case of error.
+ */
+ protected abstract VisorSingleFieldDto<T> run0(CacheGroupContext grp) throws IgniteCheckedException;
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorCacheGroupEncryptionTaskArg.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorCacheGroupEncryptionTaskArg.java
new file mode 100644
index 0000000..ecea9ed
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorCacheGroupEncryptionTaskArg.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.encryption;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Cache group aware task argument.
+ */
+public class VisorCacheGroupEncryptionTaskArg extends IgniteDataTransferObject {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Cache group name. */
+ private String grpName;
+
+ /** Default constructor. */
+ public VisorCacheGroupEncryptionTaskArg() {
+ // No-op.
+ }
+
+ /**
+ * @param grpName Cache group name.
+ */
+ public VisorCacheGroupEncryptionTaskArg(String grpName) {
+ this.grpName = grpName;
+ }
+
+ /** @return Cache group name. */
+ public String groupName() {
+ return grpName;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void writeExternalData(ObjectOutput out) throws IOException {
+ U.writeString(out, grpName);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void readExternalData(byte ver, ObjectInput in) throws IOException, ClassNotFoundException {
+ grpName = U.readString(in);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(VisorCacheGroupEncryptionTaskArg.class, this);
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorCacheGroupEncryptionTaskResult.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorCacheGroupEncryptionTaskResult.java
new file mode 100644
index 0000000..f29a99b
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorCacheGroupEncryptionTaskResult.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.encryption;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Collections;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.util.tostring.GridToStringInclude;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+
+/**
+ * Multinode cache group encryption task result.
+ *
+ * @param <T> Job result type.
+ */
+@SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
+public class VisorCacheGroupEncryptionTaskResult<T> extends IgniteDataTransferObject {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Per node job result. */
+ @GridToStringInclude
+ private Map<UUID, T> results;
+
+ /** Per node execution problems. */
+ @GridToStringInclude
+ private Map<UUID, IgniteException> exceptions;
+
+ /**
+ * @param results Per node job result.
+ * @param exceptions Per node execution problems.
+ */
+ public VisorCacheGroupEncryptionTaskResult(Map<UUID, T> results, Map<UUID, IgniteException> exceptions) {
+ this.results = results;
+ this.exceptions = exceptions;
+ }
+
+ /** */
+ public VisorCacheGroupEncryptionTaskResult() {
+ // No-op.
+ }
+
+ /** @return Per node job result. */
+ public Map<UUID, T> results() {
+ return results == null ? Collections.emptyMap() : results;
+ }
+
+ /** @return Per node execution problems. */
+ public Map<UUID, IgniteException> exceptions() {
+ return exceptions == null ? Collections.emptyMap() : exceptions;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void writeExternalData(ObjectOutput out) throws IOException {
+ U.writeMap(out, results);
+ U.writeMap(out, exceptions);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void readExternalData(byte ver, ObjectInput in) throws IOException, ClassNotFoundException {
+ results = U.readMap(in);
+ exceptions = U.readMap(in);
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(VisorCacheGroupEncryptionTaskResult.class, this);
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorChangeCacheGroupKeyTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorChangeCacheGroupKeyTask.java
new file mode 100644
index 0000000..d665941
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorChangeCacheGroupKeyTask.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.encryption;
+
+import java.util.Collection;
+import java.util.Collections;
+import org.apache.ignite.IgniteEncryption;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.apache.ignite.internal.visor.VisorOneNodeTask;
+
+/**
+ * The task for changing the encryption key of the cache group.
+ *
+ * @see IgniteEncryption#changeCacheGroupKey(Collection)
+ */
+public class VisorChangeCacheGroupKeyTask extends VisorOneNodeTask<VisorCacheGroupEncryptionTaskArg, Void> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override protected VisorJob<VisorCacheGroupEncryptionTaskArg, Void> job(VisorCacheGroupEncryptionTaskArg arg) {
+ return new VisorChangeCacheGroupKeyJob(arg, debug);
+ }
+
+ /** The job for changing the encryption key of the cache group. */
+ private static class VisorChangeCacheGroupKeyJob extends VisorJob<VisorCacheGroupEncryptionTaskArg, Void> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * @param arg Job argument.
+ * @param debug Flag indicating whether debug information should be printed into node log.
+ */
+ protected VisorChangeCacheGroupKeyJob(VisorCacheGroupEncryptionTaskArg arg, boolean debug) {
+ super(arg, debug);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Void run(VisorCacheGroupEncryptionTaskArg taskArg) throws IgniteException {
+ ignite.encryption().changeCacheGroupKey(Collections.singleton(taskArg.groupName())).get();
+
+ return null;
+ }
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorEncryptionKeyIdsTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorEncryptionKeyIdsTask.java
new file mode 100644
index 0000000..ca5a254
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorEncryptionKeyIdsTask.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.encryption;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.List;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Get current encryption key IDs of the cache group.
+ */
+@GridInternal
+public class VisorEncryptionKeyIdsTask extends VisorCacheGroupEncryptionTask<List<Integer>> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override protected VisorJob<VisorCacheGroupEncryptionTaskArg, VisorSingleFieldDto<List<Integer>>> job(
+ VisorCacheGroupEncryptionTaskArg arg) {
+ return new VisorEncryptionKeyIdsJob(arg, debug);
+ }
+
+ /** The job for get current encryption key IDs of the cache group. */
+ private static class VisorEncryptionKeyIdsJob extends VisorReencryptionBaseJob<List<Integer>> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * @param arg Job argument.
+ * @param debug Flag indicating whether debug information should be printed into node log.
+ */
+ protected VisorEncryptionKeyIdsJob(@Nullable VisorCacheGroupEncryptionTaskArg arg, boolean debug) {
+ super(arg, debug);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected VisorSingleFieldDto<List<Integer>> run0(CacheGroupContext grp) {
+ return new VisorEncryptionKeyIdsResult().value(ignite.context().encryption().groupKeyIds(grp.groupId()));
+ }
+ }
+
+ /** */
+ protected static class VisorEncryptionKeyIdsResult extends VisorSingleFieldDto<List<Integer>> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ public VisorEncryptionKeyIdsResult() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void writeExternalData(ObjectOutput out) throws IOException {
+ U.writeCollection(out, value());
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void readExternalData(byte ver, ObjectInput in) throws IOException, ClassNotFoundException {
+ value(U.readList(in));
+ }
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionRateTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionRateTask.java
new file mode 100644
index 0000000..425fab5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionRateTask.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.encryption;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import org.apache.ignite.IgniteException;
+import org.apache.ignite.compute.ComputeJobResult;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.apache.ignite.internal.visor.VisorMultiNodeTask;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * View/change cache group re-encryption rate limit .
+ */
+@GridInternal
+public class VisorReencryptionRateTask extends VisorMultiNodeTask<VisorReencryptionRateTaskArg,
+ VisorCacheGroupEncryptionTaskResult<Double>, VisorReencryptionRateTask.ReencryptionRateJobResult>
+{
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override protected VisorJob<VisorReencryptionRateTaskArg, ReencryptionRateJobResult> job(
+ VisorReencryptionRateTaskArg arg) {
+ return new VisorReencryptionRateJob(arg, debug);
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override protected VisorCacheGroupEncryptionTaskResult<Double> reduce0(List<ComputeJobResult> results) {
+ Map<UUID, Double> jobResults = new HashMap<>();
+ Map<UUID, IgniteException> exceptions = new HashMap<>();
+
+ for (ComputeJobResult res : results) {
+ UUID nodeId = res.getNode().id();
+
+ if (res.getException() != null) {
+ exceptions.put(nodeId, res.getException());
+
+ continue;
+ }
+
+ ReencryptionRateJobResult dtoRes = res.getData();
+
+ jobResults.put(nodeId, dtoRes.limit());
+ }
+
+ return new VisorCacheGroupEncryptionTaskResult<>(jobResults, exceptions);
+ }
+
+ /** The job for view/change cache group re-encryption rate limit. */
+ private static class VisorReencryptionRateJob
+ extends VisorJob<VisorReencryptionRateTaskArg, ReencryptionRateJobResult> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * @param arg Job argument.
+ * @param debug Flag indicating whether debug information should be printed into node log.
+ */
+ protected VisorReencryptionRateJob(VisorReencryptionRateTaskArg arg, boolean debug) {
+ super(arg, debug);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected ReencryptionRateJobResult run(VisorReencryptionRateTaskArg arg) throws IgniteException {
+ double prevRate = ignite.context().encryption().getReencryptionRate();
+
+ if (arg.rate() != null)
+ ignite.context().encryption().setReencryptionRate(arg.rate());
+
+ return new ReencryptionRateJobResult(prevRate);
+ }
+ }
+
+ /** */
+ protected static class ReencryptionRateJobResult extends IgniteDataTransferObject {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** Re-encryption rate limit. */
+ private Double limit;
+
+ /** */
+ public ReencryptionRateJobResult() {
+ // No-op.
+ }
+
+ /** */
+ public ReencryptionRateJobResult(Double limit) {
+ this.limit = limit;
+ }
+
+ /**
+ * @return Re-encryption rate limit.
+ */
+ public Double limit() {
+ return limit;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void writeExternalData(ObjectOutput out) throws IOException {
+ out.writeDouble(limit);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void readExternalData(byte ver, ObjectInput in) throws IOException, ClassNotFoundException {
+ limit = in.readDouble();
+ }
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionRateTaskArg.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionRateTaskArg.java
new file mode 100644
index 0000000..2471296
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionRateTaskArg.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.encryption;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.apache.ignite.internal.dto.IgniteDataTransferObject;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Re-encryption rate task argument.
+ */
+public class VisorReencryptionRateTaskArg extends IgniteDataTransferObject {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** Re-encryption rate limit in megabytes per second. */
+ private Double rate;
+
+ /** Default constructor. */
+ public VisorReencryptionRateTaskArg() {
+ // No-op.
+ }
+
+ /**
+ * @param rate Re-encryption rate limit in megabytes per second.
+ */
+ public VisorReencryptionRateTaskArg(Double rate) {
+ this.rate = rate;
+ }
+
+ /**
+ * @return Re-encryption rate limit in megabytes per second.
+ */
+ public @Nullable Double rate() {
+ return rate;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void writeExternalData(ObjectOutput out) throws IOException {
+ out.writeBoolean(rate != null);
+
+ if (rate != null)
+ out.writeDouble(rate);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void readExternalData(byte ver, ObjectInput in) throws IOException, ClassNotFoundException {
+ if (in.readBoolean())
+ rate = in.readDouble();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(VisorReencryptionRateTaskArg.class, this);
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionResumeTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionResumeTask.java
new file mode 100644
index 0000000..171130c
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionResumeTask.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.encryption;
+
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.apache.ignite.internal.visor.encryption.VisorReencryptionSuspendTask.VisorReencryptionSuspendResumeJobResult;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Resume re-encryption of the cache group.
+ */
+@GridInternal
+public class VisorReencryptionResumeTask extends VisorCacheGroupEncryptionTask<Boolean> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override protected VisorJob<VisorCacheGroupEncryptionTaskArg, VisorSingleFieldDto<Boolean>> job(
+ VisorCacheGroupEncryptionTaskArg arg) {
+ return new VisorReencryptionResumeJob(arg, debug);
+ }
+
+ /** The job to resume re-encryption of the cache group. */
+ private static class VisorReencryptionResumeJob extends VisorReencryptionBaseJob<Boolean> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * @param arg Job argument.
+ * @param debug Flag indicating whether debug information should be printed into node log.
+ */
+ protected VisorReencryptionResumeJob(@Nullable VisorCacheGroupEncryptionTaskArg arg, boolean debug) {
+ super(arg, debug);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected VisorSingleFieldDto<Boolean> run0(CacheGroupContext grp) throws IgniteCheckedException {
+ return new VisorReencryptionSuspendResumeJobResult().value(
+ ignite.context().encryption().resumeReencryption(grp.groupId()));
+ }
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionStatusTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionStatusTask.java
new file mode 100644
index 0000000..df60047
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionStatusTask.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.encryption;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Get re-encryption status of the cache group.
+ */
+@GridInternal
+public class VisorReencryptionStatusTask extends VisorCacheGroupEncryptionTask<Long> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override protected VisorJob<VisorCacheGroupEncryptionTaskArg, VisorSingleFieldDto<Long>> job(
+ VisorCacheGroupEncryptionTaskArg arg) {
+ return new VisorReencryptionStatusJob(arg, debug);
+ }
+
+ /** The job to get re-encryption status of the cache group. */
+ private static class VisorReencryptionStatusJob extends VisorReencryptionBaseJob<Long> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * @param arg Job argument.
+ * @param debug Flag indicating whether debug information should be printed into node log.
+ */
+ protected VisorReencryptionStatusJob(@Nullable VisorCacheGroupEncryptionTaskArg arg, boolean debug) {
+ super(arg, debug);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected VisorSingleFieldDto<Long> run0(CacheGroupContext grp) {
+ long res;
+
+ if (!ignite.context().encryption().reencryptionInProgress(grp.groupId()))
+ res = -1;
+ else
+ res = ignite.context().encryption().getBytesLeftForReencryption(grp.groupId());
+
+ return new VisorReencryptionStatusResult().value(res);
+ }
+ }
+
+ /** */
+ protected static class VisorReencryptionStatusResult extends VisorSingleFieldDto<Long> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ public VisorReencryptionStatusResult() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void writeExternalData(ObjectOutput out) throws IOException {
+ out.writeLong(value());
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void readExternalData(byte ver, ObjectInput in) throws IOException, ClassNotFoundException {
+ value(in.readLong());
+ }
+ }
+}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionSuspendTask.java b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionSuspendTask.java
new file mode 100644
index 0000000..edbfd46
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/visor/encryption/VisorReencryptionSuspendTask.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.visor.encryption;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.internal.processors.cache.CacheGroupContext;
+import org.apache.ignite.internal.processors.task.GridInternal;
+import org.apache.ignite.internal.visor.VisorJob;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Suspend re-encryption of the cache group.
+ */
+@GridInternal
+public class VisorReencryptionSuspendTask extends VisorCacheGroupEncryptionTask<Boolean> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** {@inheritDoc} */
+ @Override protected VisorJob<VisorCacheGroupEncryptionTaskArg, VisorSingleFieldDto<Boolean>> job(
+ VisorCacheGroupEncryptionTaskArg arg) {
+ return new VisorReencryptionSuspendJob(arg, debug);
+ }
+
+ /** The job to suspend re-encryption of the cache group. */
+ private static class VisorReencryptionSuspendJob extends VisorReencryptionBaseJob<Boolean> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * @param arg Job argument.
+ * @param debug Flag indicating whether debug information should be printed into node log.
+ */
+ protected VisorReencryptionSuspendJob(@Nullable VisorCacheGroupEncryptionTaskArg arg, boolean debug) {
+ super(arg, debug);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected VisorSingleFieldDto<Boolean> run0(CacheGroupContext grp) throws IgniteCheckedException {
+ return new VisorReencryptionSuspendResumeJobResult().value(
+ ignite.context().encryption().suspendReencryption(grp.groupId()));
+ }
+ }
+
+ /** */
+ protected static class VisorReencryptionSuspendResumeJobResult extends VisorSingleFieldDto<Boolean> {
+ /** Serial version uid. */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ public VisorReencryptionSuspendResumeJobResult() {
+ // No-op.
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void writeExternalData(ObjectOutput out) throws IOException {
+ out.writeBoolean(value());
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void readExternalData(byte ver, ObjectInput in) throws IOException, ClassNotFoundException {
+ value(in.readBoolean());
+ }
+ }
+}
diff --git a/modules/core/src/main/resources/META-INF/classnames.properties b/modules/core/src/main/resources/META-INF/classnames.properties
index f480d0f..1548b78 100644
--- a/modules/core/src/main/resources/META-INF/classnames.properties
+++ b/modules/core/src/main/resources/META-INF/classnames.properties
@@ -2314,6 +2314,25 @@ org.apache.ignite.internal.visor.encryption.VisorGetMasterKeyNameTask
org.apache.ignite.internal.visor.encryption.VisorGetMasterKeyNameTask$VisorGetMasterKeyNameJob
org.apache.ignite.internal.visor.encryption.VisorChangeMasterKeyTask
org.apache.ignite.internal.visor.encryption.VisorChangeMasterKeyTask$VisorChangeMasterKeyJob
+org.apache.ignite.internal.visor.encryption.VisorCacheGroupEncryptionTaskArg
+org.apache.ignite.internal.visor.encryption.VisorCacheGroupEncryptionTaskResult
+org.apache.ignite.internal.visor.encryption.VisorChangeCacheGroupKeyTask
+org.apache.ignite.internal.visor.encryption.VisorChangeCacheGroupKeyTask$VisorChangeCacheGroupKeyJob
+org.apache.ignite.internal.visor.encryption.VisorEncryptionKeyIdsTask
+org.apache.ignite.internal.visor.encryption.VisorEncryptionKeyIdsTask$VisorEncryptionKeyIdsJob
+org.apache.ignite.internal.visor.encryption.VisorEncryptionKeyIdsTask$VisorEncryptionKeyIdsResult
+org.apache.ignite.internal.visor.encryption.VisorReencryptionRateTask
+org.apache.ignite.internal.visor.encryption.VisorReencryptionRateTask$VisorReencryptionRateJob
+org.apache.ignite.internal.visor.encryption.VisorReencryptionRateTask$ReencryptionRateJobResult
+org.apache.ignite.internal.visor.encryption.VisorReencryptionRateTaskArg
+org.apache.ignite.internal.visor.encryption.VisorReencryptionResumeTask
+org.apache.ignite.internal.visor.encryption.VisorReencryptionResumeTask$VisorReencryptionResumeJob
+org.apache.ignite.internal.visor.encryption.VisorReencryptionStatusTask
+org.apache.ignite.internal.visor.encryption.VisorReencryptionStatusTask$VisorReencryptionStatusJob
+org.apache.ignite.internal.visor.encryption.VisorReencryptionStatusTask$VisorReencryptionStatusResult
+org.apache.ignite.internal.visor.encryption.VisorReencryptionSuspendTask
+org.apache.ignite.internal.visor.encryption.VisorReencryptionSuspendTask$VisorReencryptionSuspendJob
+org.apache.ignite.internal.visor.encryption.VisorReencryptionSuspendTask$VisorReencryptionSuspendResumeJobResult
org.apache.ignite.internal.visor.util.VisorClusterGroupEmptyException
org.apache.ignite.internal.visor.util.VisorEventMapper
org.apache.ignite.internal.visor.util.VisorExceptionWrapper
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/encryption/CacheGroupReencryptionTest.java b/modules/core/src/test/java/org/apache/ignite/internal/encryption/CacheGroupReencryptionTest.java
index 1f7cf36..19c8351 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/encryption/CacheGroupReencryptionTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/encryption/CacheGroupReencryptionTest.java
@@ -385,7 +385,7 @@ public class CacheGroupReencryptionTest extends AbstractEncryptionTest {
@Test
public void testPartitionFileDestroy() throws Exception {
backups = 1;
- pageScanRate = 1;
+ pageScanRate = 0.2;
pageScanBatchSize = 10;
T2<IgniteEx, IgniteEx> nodes = startTestGrids(true);
@@ -409,6 +409,10 @@ public class CacheGroupReencryptionTest extends AbstractEncryptionTest {
assertTrue(isReencryptionInProgress(Collections.singleton(cacheName())));
+ // Set unlimited re-encryption rate.
+ nodes.get1().context().encryption().setReencryptionRate(0);
+ nodes.get2().context().encryption().setReencryptionRate(0);
+
checkGroupKey(CU.cacheId(cacheName()), INITIAL_KEY_ID + 1, MAX_AWAIT_MILLIS);
}
@@ -758,12 +762,12 @@ public class CacheGroupReencryptionTest extends AbstractEncryptionTest {
MetricRegistry registry =
node.context().metric().registry(metricName(CacheGroupMetricsImpl.CACHE_GROUP_METRICS_PREFIX, cacheName()));
- LongMetric pagesLeft = registry.findMetric("ReencryptionPagesLeft");
+ LongMetric bytesLeft = registry.findMetric("ReencryptionBytesLeft");
if (finished)
- assertEquals(0, pagesLeft.value());
+ assertEquals(0, bytesLeft.value());
else
- assertTrue(pagesLeft.value() > 0);
+ assertTrue(bytesLeft.value() > 0);
BooleanMetric reencryptionFinished = registry.findMetric("ReencryptionFinished");
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/BasicRateLimiterTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/BasicRateLimiterTest.java
index c136cb8..9f4bca9 100644
--- a/modules/core/src/test/java/org/apache/ignite/internal/util/BasicRateLimiterTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/BasicRateLimiterTest.java
@@ -68,6 +68,21 @@ public class BasicRateLimiterTest {
}
/**
+ * Check that the rate can be set as unlimited.
+ */
+ @Test
+ public void testUnlimitedRate() throws IgniteInterruptedCheckedException {
+ BasicRateLimiter limiter = new BasicRateLimiter(0);
+ limiter.acquire(Integer.MAX_VALUE);
+
+ limiter.setRate(1);
+ limiter.acquire(1);
+
+ limiter.setRate(0);
+ limiter.acquire(Integer.MAX_VALUE);
+ }
+
+ /**
* Check rate limit with multiple threads.
*/
@Test
diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
index a4c2ec4..ef98769 100644
--- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
+++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output
@@ -71,6 +71,27 @@ This utility can do the following commands:
Change the master key:
control.(sh|bat) --encryption change_master_key newMasterKeyName
+ Change the encryption key of the cache group:
+ control.(sh|bat) --encryption change_cache_key cacheGroupName
+
+ View encryption key identifiers of the cache group:
+ control.(sh|bat) --encryption cache_key_ids cacheGroupName
+
+ Display re-encryption status of the cache group:
+ control.(sh|bat) --encryption reencryption_status cacheGroupName
+
+ Suspend re-encryption of the cache group:
+ control.(sh|bat) --encryption suspend_reencryption cacheGroupName
+
+ Resume re-encryption of the cache group:
+ control.(sh|bat) --encryption resume_reencryption cacheGroupName
+
+ View/change re-encryption rate limit:
+ control.(sh|bat) --encryption reencryption_rate_limit [new_limit]
+
+ Parameters:
+ new_limit - Decimal value to change re-encryption rate limit (MB/s).
+
Kill compute task by session id:
control.(sh|bat) --kill COMPUTE session_id
diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
index a4c2ec4..ef98769 100644
--- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
+++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output
@@ -71,6 +71,27 @@ This utility can do the following commands:
Change the master key:
control.(sh|bat) --encryption change_master_key newMasterKeyName
+ Change the encryption key of the cache group:
+ control.(sh|bat) --encryption change_cache_key cacheGroupName
+
+ View encryption key identifiers of the cache group:
+ control.(sh|bat) --encryption cache_key_ids cacheGroupName
+
+ Display re-encryption status of the cache group:
+ control.(sh|bat) --encryption reencryption_status cacheGroupName
+
+ Suspend re-encryption of the cache group:
+ control.(sh|bat) --encryption suspend_reencryption cacheGroupName
+
+ Resume re-encryption of the cache group:
+ control.(sh|bat) --encryption resume_reencryption cacheGroupName
+
+ View/change re-encryption rate limit:
+ control.(sh|bat) --encryption reencryption_rate_limit [new_limit]
+
+ Parameters:
+ new_limit - Decimal value to change re-encryption rate limit (MB/s).
+
Kill compute task by session id:
control.(sh|bat) --kill COMPUTE session_id