This is an automated email from the ASF dual-hosted git repository.
sk0x50 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 323c9ab45a IGNITE-16945 Implemented node version REST command. Fixes
#1073
323c9ab45a is described below
commit 323c9ab45ae40e39150b63afc102110b688e28b2
Author: Aleksandr Pakhomov <[email protected]>
AuthorDate: Wed Sep 21 17:39:05 2022 +0300
IGNITE-16945 Implemented node version REST command. Fixes #1073
Signed-off-by: Slava Koptilin <[email protected]>
---
.../cli/commands/node/NodeVersionCommandTest.java | 41 +++++++++++++++++
...liCommandTestNotInitializedIntegrationBase.java | 6 +++
.../internal/rest/ItGeneratedRestClientTest.java | 5 +++
.../cli/call/node/version/NodeVersionCall.java | 45 +++++++++++++++++++
.../commands/node/version/NodeVersionCommand.java | 49 +++++++++++++++++++++
.../node/version/NodeVersionReplCommand.java | 51 ++++++++++++++++++++++
.../internal/cli/commands/node/NodeCommand.java | 3 +-
.../cli/commands/node/NodeReplCommand.java | 3 +-
.../internal/rest/api/node/NodeManagementApi.java | 12 +++++
modules/rest/openapi/openapi.yaml | 19 ++++++++
.../rest/ItInitializedClusterRestTest.java | 17 ++++++++
.../rest/ItNotInitializedClusterRestTest.java | 16 +++++++
.../rest/node/NodeManagementController.java | 6 +++
13 files changed, 271 insertions(+), 2 deletions(-)
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/node/NodeVersionCommandTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/node/NodeVersionCommandTest.java
new file mode 100644
index 0000000000..f5e80c9c41
--- /dev/null
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/cli/commands/node/NodeVersionCommandTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.cli.commands.node;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import
org.apache.ignite.internal.cli.commands.CliCommandTestNotInitializedIntegrationBase;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/** Test for ignite node version command. */
+public class NodeVersionCommandTest extends
CliCommandTestNotInitializedIntegrationBase {
+ @Test
+ @DisplayName("Should display node version with provided cluster url")
+ void nodeVersion() {
+ // When
+ execute("node", "version", "--node-url", NODE_URL);
+
+ // Then
+ assertAll(
+ this::assertExitCodeIsZero,
+ this::assertErrOutputIsEmpty,
+ () ->
assertOutputMatches("[1-9]\\d*\\.\\d+\\.\\d+(?:-[a-zA-Z0-9]+)?\\s+")
+ );
+ }
+}
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
index b9faad25d6..216479b4e3 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/CliCommandTestNotInitializedIntegrationBase.java
@@ -128,6 +128,12 @@ public class CliCommandTestNotInitializedIntegrationBase
extends IntegrationTest
.contains(expectedOutput);
}
+ protected void assertOutputMatches(String regex) {
+ assertThat(sout.toString())
+ .as("Expected command output to match regex: " + regex + " but
it is not: " + sout.toString())
+ .matches(regex);
+ }
+
protected void assertOutputIsEmpty() {
assertThat(sout.toString())
.as("Expected command output to be empty")
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
index b9dc04f8aa..ba4961a06e 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
@@ -317,6 +317,11 @@ public class ItGeneratedRestClientTest {
assertThat(topologyApi.physical(), hasSize(3));
}
+ @Test
+ void nodeVersion() throws ApiException {
+ assertThat(nodeManagementApi.nodeVersion(), is(notNullValue()));
+ }
+
private CompletableFuture<Ignite> startNodeAsync(TestInfo testInfo, int
index) {
String nodeName = testNodeName(testInfo, BASE_PORT + index);
diff --git
a/modules/cli/src/main/java/org/apache/ignite/cli/call/node/version/NodeVersionCall.java
b/modules/cli/src/main/java/org/apache/ignite/cli/call/node/version/NodeVersionCall.java
new file mode 100644
index 0000000000..f0bac00da2
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/cli/call/node/version/NodeVersionCall.java
@@ -0,0 +1,45 @@
+/*
+ * 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.cli.call.node.version;
+
+import jakarta.inject.Singleton;
+import org.apache.ignite.internal.cli.core.call.Call;
+import org.apache.ignite.internal.cli.core.call.CallOutput;
+import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
+import org.apache.ignite.internal.cli.core.call.UrlCallInput;
+import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.rest.client.api.NodeManagementApi;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.apache.ignite.rest.client.invoker.ApiException;
+
+/** Call to get node version. */
+@Singleton
+public class NodeVersionCall implements Call<UrlCallInput, String> {
+ @Override
+ public CallOutput<String> execute(UrlCallInput input) {
+ try {
+ return DefaultCallOutput.success(getNodeVersion(input.getUrl()));
+ } catch (ApiException | IllegalArgumentException e) {
+ return DefaultCallOutput.failure(new IgniteCliApiException(e,
input.getUrl()));
+ }
+ }
+
+ private String getNodeVersion(String url) throws ApiException {
+ return new NodeManagementApi(new
ApiClient().setBasePath(url)).nodeVersion();
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/version/NodeVersionCommand.java
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/version/NodeVersionCommand.java
new file mode 100644
index 0000000000..31874aff42
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/version/NodeVersionCommand.java
@@ -0,0 +1,49 @@
+/*
+ * 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.cli.commands.node.version;
+
+import jakarta.inject.Inject;
+import java.util.concurrent.Callable;
+import org.apache.ignite.cli.call.node.version.NodeVersionCall;
+import org.apache.ignite.internal.cli.commands.BaseCommand;
+import org.apache.ignite.internal.cli.commands.node.NodeUrlProfileMixin;
+import org.apache.ignite.internal.cli.core.call.CallExecutionPipeline;
+import org.apache.ignite.internal.cli.core.call.UrlCallInput;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Mixin;
+
+/** Display the node build version. */
+@Command(name = "version", description = "Prints the node build version")
+public class NodeVersionCommand extends BaseCommand implements
Callable<Integer> {
+ @Mixin
+ private NodeUrlProfileMixin nodeUrl;
+
+ @Inject
+ private NodeVersionCall nodeVersionCall;
+
+ /** {@inheritDoc} */
+ @Override
+ public Integer call() {
+ return CallExecutionPipeline.builder(nodeVersionCall)
+ .inputProvider(() -> new UrlCallInput(nodeUrl.getNodeUrl()))
+ .output(spec.commandLine().getOut())
+ .errOutput(spec.commandLine().getErr())
+ .build()
+ .runPipeline();
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/version/NodeVersionReplCommand.java
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/version/NodeVersionReplCommand.java
new file mode 100644
index 0000000000..e84489a4a1
--- /dev/null
+++
b/modules/cli/src/main/java/org/apache/ignite/cli/commands/node/version/NodeVersionReplCommand.java
@@ -0,0 +1,51 @@
+/*
+ * 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.cli.commands.node.version;
+
+import jakarta.inject.Inject;
+import org.apache.ignite.cli.call.node.version.NodeVersionCall;
+import org.apache.ignite.internal.cli.commands.BaseCommand;
+import org.apache.ignite.internal.cli.commands.node.NodeUrlMixin;
+import
org.apache.ignite.internal.cli.commands.questions.ConnectToClusterQuestion;
+import org.apache.ignite.internal.cli.core.call.UrlCallInput;
+import org.apache.ignite.internal.cli.core.flow.builder.Flows;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Mixin;
+
+/** Display the node version in REPL. */
+@Command(name = "version", description = "Prints the node build version")
+public class NodeVersionReplCommand extends BaseCommand implements Runnable {
+ @Mixin
+ private NodeUrlMixin nodeUrl;
+
+ @Inject
+ private NodeVersionCall nodeVersionCall;
+
+ @Inject
+ private ConnectToClusterQuestion question;
+
+ /** {@inheritDoc} */
+ @Override
+ public void run() {
+ question.askQuestionIfNotConnected(nodeUrl.getNodeUrl())
+ .map(UrlCallInput::new)
+ .then(Flows.fromCall(nodeVersionCall))
+ .print()
+ .start();
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeCommand.java
index 52f11660e5..abbe1811c6 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeCommand.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.cli.commands.node;
+import org.apache.ignite.cli.commands.node.version.NodeVersionCommand;
import org.apache.ignite.internal.cli.commands.node.config.NodeConfigCommand;
import org.apache.ignite.internal.cli.commands.node.status.NodeStatusCommand;
import org.apache.ignite.internal.cli.deprecated.spec.NodeCommandSpec;
@@ -27,7 +28,7 @@ import picocli.CommandLine.Mixin;
* Node command.
*/
@Command(name = "node",
- subcommands = {NodeConfigCommand.class, NodeStatusCommand.class},
+ subcommands = {NodeConfigCommand.class, NodeStatusCommand.class,
NodeVersionCommand.class},
description = "Node operations")
public class NodeCommand {
@Mixin
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeReplCommand.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeReplCommand.java
index 1b6f008b31..70f82aa242 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeReplCommand.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/node/NodeReplCommand.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.cli.commands.node;
+import org.apache.ignite.cli.commands.node.version.NodeVersionReplCommand;
import
org.apache.ignite.internal.cli.commands.node.config.NodeConfigReplCommand;
import
org.apache.ignite.internal.cli.commands.node.status.NodeStatusReplCommand;
import org.apache.ignite.internal.cli.deprecated.spec.NodeCommandSpec;
@@ -27,7 +28,7 @@ import picocli.CommandLine.Mixin;
* Node command in REPL mode.
*/
@Command(name = "node",
- subcommands = {NodeConfigReplCommand.class,
NodeStatusReplCommand.class},
+ subcommands = {NodeConfigReplCommand.class,
NodeStatusReplCommand.class, NodeVersionReplCommand.class},
description = "Node operations")
public class NodeReplCommand {
@Mixin
diff --git
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeManagementApi.java
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeManagementApi.java
index 109d84eedd..de99719880 100644
---
a/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeManagementApi.java
+++
b/modules/rest-api/src/main/java/org/apache/ignite/internal/rest/api/node/NodeManagementApi.java
@@ -49,4 +49,16 @@ public interface NodeManagementApi {
MediaType.PROBLEM_JSON
})
NodeState state();
+
+ @Get("version")
+ @Operation(operationId = "nodeVersion")
+ @ApiResponse(responseCode = "200", description = "Return node version",
+ content = @Content(mediaType = MediaType.TEXT_PLAIN, schema =
@Schema(type = "string")))
+ @ApiResponse(responseCode = "500", description = "Internal error",
+ content = @Content(mediaType = MediaType.PROBLEM_JSON, schema =
@Schema(implementation = Problem.class)))
+ @Produces({
+ MediaType.TEXT_PLAIN,
+ MediaType.PROBLEM_JSON
+ })
+ String version();
}
diff --git a/modules/rest/openapi/openapi.yaml
b/modules/rest/openapi/openapi.yaml
index c00ab0d877..b11b2ea498 100644
--- a/modules/rest/openapi/openapi.yaml
+++ b/modules/rest/openapi/openapi.yaml
@@ -324,6 +324,25 @@ paths:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
+ /management/v1/node/version:
+ get:
+ tags:
+ - nodeManagement
+ operationId: nodeVersion
+ parameters: []
+ responses:
+ "200":
+ description: Return node version
+ content:
+ text/plain:
+ schema:
+ type: string
+ "500":
+ description: Internal error
+ content:
+ application/problem+json:
+ schema:
+ $ref: '#/components/schemas/Problem'
components:
schemas:
ClusterNode:
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
index 5ad1d98e25..508edbe24f 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItInitializedClusterRestTest.java
@@ -22,6 +22,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.matchesRegex;
import static org.junit.jupiter.api.Assertions.assertAll;
import com.typesafe.config.Config;
@@ -38,6 +39,10 @@ import org.junit.jupiter.api.TestInfo;
* Test for the REST endpoints in case cluster is initialized.
*/
public class ItInitializedClusterRestTest extends AbstractRestTestBase {
+ /** <a href="https://semver.org">semver</a> compatible regex. */
+ private static final String IGNITE_SEMVER_REGEX =
+
"(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<maintenance>\\d+)((?<snapshot>-SNAPSHOT)|-(?<alpha>alpha\\d+)|--(?<beta>beta\\d+))?";
+
@BeforeEach
void setUp(TestInfo testInfo) throws IOException, InterruptedException {
super.setUp(testInfo);
@@ -235,4 +240,16 @@ public class ItInitializedClusterRestTest extends
AbstractRestTestBase {
() -> assertThat(response.body(), hasJsonPath("$.state",
is(equalTo("STARTED"))))
);
}
+
+ @Test
+ @DisplayName("Node version is available on initialized cluster")
+ void nodeVersion() throws IOException, InterruptedException {
+ // When GET /management/v1/node/version/
+ HttpResponse<String> response =
client.send(get("/management/v1/node/version/"), BodyHandlers.ofString());
+
+ // Then
+ assertThat(response.statusCode(), is(200));
+ // And version is a semver
+ assertThat(response.body(), matchesRegex(IGNITE_SEMVER_REGEX));
+ }
}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItNotInitializedClusterRestTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItNotInitializedClusterRestTest.java
index b142b14d77..c38cf00f84 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItNotInitializedClusterRestTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/rest/ItNotInitializedClusterRestTest.java
@@ -22,6 +22,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.matchesRegex;
import static org.junit.jupiter.api.Assertions.assertAll;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -43,6 +44,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
*/
@ExtendWith(WorkDirectoryExtension.class)
public class ItNotInitializedClusterRestTest extends AbstractRestTestBase {
+ /** <a href="https://semver.org">semver</a> compatible regex. */
+ private static final String IGNITE_SEMVER_REGEX =
+
"(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<maintenance>\\d+)((?<snapshot>-SNAPSHOT)|-(?<alpha>alpha\\d+)|--(?<beta>beta\\d+))?";
private ObjectMapper objectMapper;
@@ -188,4 +192,16 @@ public class ItNotInitializedClusterRestTest extends
AbstractRestTestBase {
is("Cluster not initialized. Call
/management/v1/cluster/init in order to initialize cluster"))
);
}
+
+ @Test
+ @DisplayName("Node version is available on not initialized cluster")
+ void nodeVersion() throws IOException, InterruptedException {
+ // When GET /management/v1/node/version/
+ HttpResponse<String> response =
client.send(get("/management/v1/node/version/"), BodyHandlers.ofString());
+
+ // Then
+ assertThat(response.statusCode(), is(200));
+ // And version is a semver
+ assertThat(response.body(), matchesRegex(IGNITE_SEMVER_REGEX));
+ }
}
diff --git
a/modules/runner/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementController.java
b/modules/runner/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementController.java
index 283622485b..dbb5ade3f7 100644
---
a/modules/runner/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementController.java
+++
b/modules/runner/src/main/java/org/apache/ignite/internal/rest/node/NodeManagementController.java
@@ -18,6 +18,7 @@
package org.apache.ignite.internal.rest.node;
import io.micronaut.http.annotation.Controller;
+import org.apache.ignite.internal.properties.IgniteProductVersion;
import org.apache.ignite.internal.rest.api.node.NodeManagementApi;
import org.apache.ignite.internal.rest.api.node.NodeState;
@@ -39,4 +40,9 @@ public class NodeManagementController implements
NodeManagementApi {
public NodeState state() {
return new NodeState(nameProvider.getName(), stateProvider.getState());
}
+
+ @Override
+ public String version() {
+ return IgniteProductVersion.CURRENT_VERSION.toString();
+ }
}