You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by il...@apache.org on 2021/04/16 16:18:38 UTC

[ignite] branch ignite-14346 created (now 020eb58)

This is an automated email from the ASF dual-hosted git repository.

ilyak pushed a change to branch ignite-14346
in repository https://gitbox.apache.org/repos/asf/ignite.git.


      at 020eb58  Amendments

This branch includes the following new commits:

     new 854cac1  IGNITE-14346 Implement Azure Blob Storage based IP Finder - Fixes #8897.
     new 020eb58  Amendments

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[ignite] 01/02: IGNITE-14346 Implement Azure Blob Storage based IP Finder - Fixes #8897.

Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ilyak pushed a commit to branch ignite-14346
in repository https://gitbox.apache.org/repos/asf/ignite.git

commit 854cac183b125de087da8484d91aa599dc1dfe72
Author: Atri Sharma <at...@gmail.com>
AuthorDate: Fri Apr 16 18:37:32 2021 +0300

    IGNITE-14346 Implement Azure Blob Storage based IP Finder - Fixes #8897.
    
    Signed-off-by: Ilya Kasnacheev <il...@gmail.com>
---
 assembly/dependencies-apache-ignite-slim.xml       |   1 +
 assembly/libs/README.txt                           |   1 +
 docs/_docs/clustering/discovery-in-the-cloud.adoc  |  42 +++
 docs/_docs/code-snippets/java/pom.xml              |   5 +
 .../ignite/snippets/DiscoveryInTheCloud.java       |  24 ++
 docs/_docs/setup.adoc                              |   1 +
 modules/azure/README.txt                           |  32 ++
 modules/azure/pom.xml                              | 327 ++++++++++++++++++
 .../azure/TcpDiscoveryAzureBlobStoreIpFinder.java  | 382 +++++++++++++++++++++
 .../spi/discovery/tcp/ipfinder/package-info.java   |  23 ++
 ...TcpDiscoveryAzureBlobStoreIpFinderSelfTest.java |  91 +++++
 .../discovery/tcp/ipfinder/azure/package-info.java |  21 ++
 .../ignite/testsuites/IgniteAzureTestSuite.java    |  61 ++++
 parent/pom.xml                                     |   2 +
 pom.xml                                            |   1 +
 15 files changed, 1014 insertions(+)

diff --git a/assembly/dependencies-apache-ignite-slim.xml b/assembly/dependencies-apache-ignite-slim.xml
index 6d222c7..43f0fdf 100644
--- a/assembly/dependencies-apache-ignite-slim.xml
+++ b/assembly/dependencies-apache-ignite-slim.xml
@@ -145,6 +145,7 @@
                 <!-- Removed from slim packaging are: -->
                 <exclude>org.apache.ignite:ignite-aop</exclude>
                 <exclude>org.apache.ignite:ignite-aws</exclude>
+                <exclude>org.apache.ignite:ignite-azure</exclude>
                 <exclude>org.apache.ignite:ignite-cassandra-serializers</exclude>
                 <exclude>org.apache.ignite:ignite-cassandra-store</exclude>
                 <exclude>org.apache.ignite:ignite-cloud</exclude>
diff --git a/assembly/libs/README.txt b/assembly/libs/README.txt
index 39f342b..12d1256 100644
--- a/assembly/libs/README.txt
+++ b/assembly/libs/README.txt
@@ -73,6 +73,7 @@ All optional modules can be imported just like the core module, but with differe
 The following modules are available:
 - ignite-aop (for AOP-based grid-enabling)
 - ignite-aws (for seemless cluster discovery on AWS S3)
+- ignite-azure (for automatic cluster discovery on Azure Blob Storage)
 - ignite-cassandra (for Apache Cassandra integration)
 - ignite-cloud (for Apache JClouds integration) 
 - ignite-gce (for automatic cluster discovery on Google Compute Engine)
diff --git a/docs/_docs/clustering/discovery-in-the-cloud.adoc b/docs/_docs/clustering/discovery-in-the-cloud.adoc
index 0d74b81..4c73f7825 100644
--- a/docs/_docs/clustering/discovery-in-the-cloud.adoc
+++ b/docs/_docs/clustering/discovery-in-the-cloud.adoc
@@ -34,6 +34,7 @@ To mitigate the constantly changing IP addresses problem, Ignite supports a numb
 * Amazon S3 IP Finder
 * Amazon ELB IP Finder
 * Google Cloud Storage IP Finder
+* Azure Blob Storage IP Finder
 
 
 TIP: Cloud-based IP Finders allow you to create your configuration once and reuse it for all instances.
@@ -268,3 +269,44 @@ include::{javaFile}[tag=google,indent=0]
 tab:C#/.NET[unsupported]
 tab:C++[unsupported]
 --
+
+== Azure Blob Storage
+
+Ignite supports automatic node discovery by utilizing Azure Blob Storage.
+This mechanism is implemented in `TcpDiscoveryAzureBlobStorageIpFinder`.
+On start-up, each node registers its IP address in the storage and discovers other nodes by reading the storage.
+
+IMPORTANT: To use `TcpDiscoveryAzureBlobStorageIpFinder`, enable the `ignite-azure` link:setup#enabling-modules[module] in your application.
+
+Here is an example of how to configure Azure Blob Storage based IP finder:
+
+[tabs]
+--
+tab:XML[]
+[source,xml]
+----
+<bean class="org.apache.ignite.configuration.IgniteConfiguration">
+
+  <property name="discoverySpi">
+    <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+      <property name="ipFinder">
+        <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.azure.TcpDiscoveryAzureBlobStoreIpFinder">
+          <property name="accountName" value="YOUR_AZURE_BLOB_STORAGE_ACCOUNT_NAME"/>
+          <property name="accountKey" value="YOUR_AZURE_BLOB_STORAGE_ACCOUNT_KEY"/>
+          <property name="accountEndpoint" value="YOUR_END_POINT"/>
+          <property name="containerName" value="YOUR_CONTAINER_NAME"/>
+        </bean>
+      </property>
+    </bean>
+  </property>
+</bean>
+----
+
+tab:Java[]
+[source,java]
+----
+include::{javaFile}[tag=google,indent=0]
+----
+tab:C#/.NET[unsupported]
+tab:C++[unsupported]
+--
diff --git a/docs/_docs/code-snippets/java/pom.xml b/docs/_docs/code-snippets/java/pom.xml
index de5623d..4589866 100644
--- a/docs/_docs/code-snippets/java/pom.xml
+++ b/docs/_docs/code-snippets/java/pom.xml
@@ -91,6 +91,11 @@
 		</dependency>
 		<dependency>
 			<groupId>org.apache.ignite</groupId>
+			<artifactId>ignite-azure</artifactId>
+			<version>${ignite.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.ignite</groupId>
 			<artifactId>ignite-compress</artifactId>
 			<version>${ignite.version}</version>
 		</dependency>
diff --git a/docs/_docs/code-snippets/java/src/main/java/org/apache/ignite/snippets/DiscoveryInTheCloud.java b/docs/_docs/code-snippets/java/src/main/java/org/apache/ignite/snippets/DiscoveryInTheCloud.java
index 576b36d..607eb5a 100644
--- a/docs/_docs/code-snippets/java/src/main/java/org/apache/ignite/snippets/DiscoveryInTheCloud.java
+++ b/docs/_docs/code-snippets/java/src/main/java/org/apache/ignite/snippets/DiscoveryInTheCloud.java
@@ -148,4 +148,28 @@ public class DiscoveryInTheCloud {
         Ignition.start(cfg);
         //end::google[]
     }
+
+    public static void azureBlobStorageExample() {
+        //tag::azureBlobStorage[]
+        TcpDiscoverySpi spi = new TcpDiscoverySpi();
+
+        TcpDiscoveryAzureBlobStorageIpFinder ipFinder = new TcpDiscoveryGoogleStorageIpFinder();
+
+        finder.setAccountName("yourAccountName");
+        finder.setAccountKey("yourAccountKey");
+        finder.setAccountEndpoint("yourEndpoint");
+
+        finder.setContainerName("yourContainerName");
+
+        spi.setIpFinder(ipFinder);
+
+        IgniteConfiguration cfg = new IgniteConfiguration();
+
+        // Override default discovery SPI.
+        cfg.setDiscoverySpi(spi);
+
+        // Start the node.
+        Ignition.start(cfg);
+        //end::azureBlobStorage[]
+    }
 }
diff --git a/docs/_docs/setup.adoc b/docs/_docs/setup.adoc
index 603934b..44dfda6 100644
--- a/docs/_docs/setup.adoc
+++ b/docs/_docs/setup.adoc
@@ -203,6 +203,7 @@ adding @Gridify annotation to it.
 
 |ignite-aws |Cluster discovery on AWS S3. Refer to link:clustering/discovery-in-the-cloud#amazon-s3-ip-finder[Amazon S3 IP Finder] for details.
 
+|ignite-azure| Ignite Azure provides Azure Blob Storagebased implementations of IP finder for TCP discovery.
 
 |ignite-cassandra-serializers | The Ignite Cassandra Serializers module provides additional serializers to store objects as BLOBs in Cassandra. The module could be used as in conjunction with the Ignite Cassandra Store module.
 
diff --git a/modules/azure/README.txt b/modules/azure/README.txt
new file mode 100644
index 0000000..6d7df7d
--- /dev/null
+++ b/modules/azure/README.txt
@@ -0,0 +1,32 @@
+Apache Ignite Azure Module
+------------------------
+
+Apache Ignite Azure module provides Azure Blob Storage based implementations of IP finder for TCP discovery.
+
+To enable Azure module when starting a standalone node, move 'optional/ignite-azure' folder to
+'libs' folder before running 'ignite.{sh|bat}' script. The content of the module folder will
+be added to classpath in this case.
+
+Importing Azure Module In Maven Project
+-------------------------------------
+
+If you are using Maven to manage dependencies of your project, you can add Azure module
+dependency like this (replace '${ignite.version}' with actual Ignite version you are
+interested in):
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                        http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    ...
+    <dependencies>
+        ...
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-azure</artifactId>
+            <version>${ignite.version}</version>
+        </dependency>
+        ...
+    </dependencies>
+    ...
+</project>
diff --git a/modules/azure/pom.xml b/modules/azure/pom.xml
new file mode 100644
index 0000000..7e221eb
--- /dev/null
+++ b/modules/azure/pom.xml
@@ -0,0 +1,327 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>ignite-parent</artifactId>
+        <groupId>org.apache.ignite</groupId>
+        <version>1</version>
+        <relativePath>../../parent</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ignite-azure</artifactId>
+    <version>2.11.0-SNAPSHOT</version>
+    <url>http://ignite.apache.org</url>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>com.azure</groupId>
+                <artifactId>azure-sdk-bom</artifactId>
+                <version>1.0.2</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-tools</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.jetbrains/annotations -->
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>16.0.3</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.azure</groupId>
+            <artifactId>azure-core-http-netty</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.azure</groupId>
+            <artifactId>azure-core</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.azure</groupId>
+            <artifactId>azure-storage-blob</artifactId>
+            <version>${azure.sdk.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.azure</groupId>
+            <artifactId>azure-storage-common</artifactId>
+            <version>${azure.sdk.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.azure</groupId>
+            <artifactId>azure-security-keyvault-secrets</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.azure</groupId>
+            <artifactId>azure-identity</artifactId>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformat-xml</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jsr310</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-jaxb-annotations -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.module</groupId>
+            <artifactId>jackson-module-jaxb-annotations</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <version>${log4j.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <version>${log4j.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-slf4j-impl</artifactId>
+            <version>${log4j.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-buffer -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-buffer</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-codec -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-codec</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-codec-http2 -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-codec-http2</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-codec-http -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-codec-http</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-codec-socks -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-codec-socks</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-common -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-common</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-handler -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-handler</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-handler-proxy -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-handler-proxy</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-resolver -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-resolver</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-transport -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-transport</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-transport-native-epoll -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-transport-native-epoll</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-transport-native-kqueue -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-transport-native-kqueue</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-transport-native-kqueue -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-transport-native-kqueue</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.netty/netty-transport-native-unix-common -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-transport-native-unix-common</artifactId>
+            <version>${azure.netty.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.projectreactor.netty</groupId>
+            <artifactId>reactor-netty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.7</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.reactivestreams</groupId>
+            <artifactId>reactive-streams</artifactId>
+            <version>1.0.3</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.projectreactor/reactor-core -->
+        <dependency>
+            <groupId>io.projectreactor</groupId>
+            <artifactId>reactor-core</artifactId>
+            <version>3.3.0.RELEASE</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/io.projectreactor.netty/reactor-netty -->
+        <dependency>
+            <groupId>io.projectreactor.netty</groupId>
+            <artifactId>reactor-netty</artifactId>
+            <version>0.9.0.RELEASE</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
+        <dependency>
+            <groupId>org.reflections</groupId>
+            <artifactId>reflections</artifactId>
+            <version>0.9.12</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <version>1.8.0-beta4</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/org.codehaus.woodstox/stax2-api -->
+        <dependency>
+            <groupId>org.codehaus.woodstox</groupId>
+            <artifactId>stax2-api</artifactId>
+            <version>4.2.1</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
+    </dependencies>
+
+
+</project>
\ No newline at end of file
diff --git a/modules/azure/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/azure/TcpDiscoveryAzureBlobStoreIpFinder.java b/modules/azure/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/azure/TcpDiscoveryAzureBlobStoreIpFinder.java
new file mode 100644
index 0000000..031554b
--- /dev/null
+++ b/modules/azure/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/azure/TcpDiscoveryAzureBlobStoreIpFinder.java
@@ -0,0 +1,382 @@
+package org.apache.ignite.spi.discovery.tcp.ipfinder.azure;
+/*
+ * 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.
+ */
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.InetSocketAddress;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.azure.storage.blob.BlobContainerClient;
+import com.azure.storage.blob.BlobServiceClient;
+import com.azure.storage.blob.BlobServiceClientBuilder;
+import com.azure.storage.blob.models.BlobItem;
+import com.azure.storage.blob.models.BlobStorageException;
+import com.azure.storage.blob.specialized.BlockBlobClient;
+import com.azure.storage.common.StorageSharedKeyCredential;
+import org.apache.ignite.IgniteLogger;
+import org.apache.ignite.internal.IgniteInterruptedCheckedException;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.resources.LoggerResource;
+import org.apache.ignite.spi.IgniteSpiConfiguration;
+import org.apache.ignite.spi.IgniteSpiException;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinderAdapter;
+
+import static com.nimbusds.oauth2.sdk.util.URLUtils.CHARSET;
+
+/**
+ * Azure Blob Storage based IP Finder
+ * <p>
+ * For information about Blob Storage visit <a href="https://azure.microsoft.com/en-in/services/storage/blobs/">azure.microsoft.com</a>.
+ * <h1 class="header">Configuration</h1>
+ * <h2 class="header">Mandatory</h2>
+ * <ul>
+ *      <li>AccountName (see {@link #setAccountName(String)})</li>
+ *      <li>AccountKey (see {@link #setAccountKey(String)})</li>
+ *      <li>Account Endpoint (see {@link #setAccountEndpoint(String)})</li>
+ *      <li>Container Name (see {@link #setContainerName(String)})</li>
+ * </ul>
+ * <h2 class="header">Optional</h2>
+ * <ul>
+ *      <li>Shared flag (see {@link #setShared(boolean)})</li>
+ * </ul>
+ * <p>
+ * The finder will create a container with the provided name. The container will contain entries named
+ * like the following: {@code 192.168.1.136#1001}.
+ * <p>
+ * Note that storing data in Azure Blob Storage service will result in charges to your Azure account.
+ * Choose another implementation of {@link org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder} for local
+ * or home network tests.
+ * <p>
+ * Note that this finder is shared by default (see {@link org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder#isShared()}.
+ */
+public class TcpDiscoveryAzureBlobStoreIpFinder extends TcpDiscoveryIpFinderAdapter {
+    /** Default object's content. */
+    private static final byte[] OBJECT_CONTENT = new byte[0];
+
+    /** Grid logger. */
+    @LoggerResource
+    private IgniteLogger log;
+
+    /** Azure Blob Storage's account name*/
+    private String accountName;
+
+    /** Azure Blob Storage's account key */
+    private String accountKey;
+
+    /** End point URL */
+    private String endPoint;
+
+    /** Container name */
+    private String containerName;
+
+    /** Storage credential */
+    StorageSharedKeyCredential credential;
+
+    /** Blob service client */
+    private BlobServiceClient blobServiceClient;
+
+    /** Blob container client */
+    private BlobContainerClient blobContainerClient;
+
+    /** Init routine guard. */
+    private final AtomicBoolean initGuard = new AtomicBoolean();
+
+    /** Init routine latch. */
+    private final CountDownLatch initLatch = new CountDownLatch(1);
+
+    /**
+     * Default constructor
+     */
+    public TcpDiscoveryAzureBlobStoreIpFinder() {
+        setShared(true);
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<InetSocketAddress> getRegisteredAddresses() throws IgniteSpiException {
+        init();
+
+        Collection<InetSocketAddress> addrs = new ArrayList<>();
+        Set<String> seenBlobNames = new HashSet<>();
+
+        Iterator<BlobItem> blobItemIterator = blobContainerClient.listBlobs().iterator();
+
+        while (blobItemIterator.hasNext()) {
+            BlobItem blobItem = blobItemIterator.next();
+
+            // https://github.com/Azure/azure-sdk-for-java/issues/20515
+            if (seenBlobNames.contains(blobItem.getName())) {
+                break;
+            }
+
+            try {
+                if (!blobItem.isDeleted()) {
+                    addrs.add(addrFromString(blobItem.getName()));
+                    seenBlobNames.add(blobItem.getName());
+                }
+            }
+            catch (Exception e) {
+                throw new IgniteSpiException("Failed to get content from the container: " + containerName, e);
+            }
+        }
+
+        return addrs;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void registerAddresses(Collection<InetSocketAddress> addrs) throws IgniteSpiException {
+        assert !F.isEmpty(addrs);
+
+        init();
+
+        for (InetSocketAddress addr : addrs) {
+            String key = keyFromAddr(addr);
+            try {
+                key = URLEncoder.encode(key, CHARSET);
+            } catch (UnsupportedEncodingException e) {
+                throw new IgniteSpiException("Unable to encode URL due to error "
+                        + e.getMessage());
+            }
+
+            BlockBlobClient blobClient = blobContainerClient.getBlobClient(key).getBlockBlobClient();
+            InputStream dataStream = new ByteArrayInputStream(OBJECT_CONTENT);
+
+            try {
+                blobClient.upload(dataStream, OBJECT_CONTENT.length);
+            }
+            catch (BlobStorageException e) {
+                // If the blob already exists, ignore
+                if (!(e.getStatusCode() == 409)) {
+                    throw new IgniteSpiException("Failed to upload blob with exception " +
+                            e.getMessage());
+                }
+            }
+
+            try {
+                dataStream.close();
+            }
+            catch (IOException e) {
+                throw new IgniteSpiException(e.getMessage());
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void unregisterAddresses(Collection<InetSocketAddress> addrs) throws IgniteSpiException {
+        assert !F.isEmpty(addrs);
+
+        init();
+
+        for (InetSocketAddress addr : addrs) {
+            String key = keyFromAddr(addr);
+
+            try {
+                blobContainerClient.getBlobClient(key).delete();
+            } catch (Exception e) {
+                // https://github.com/Azure/azure-sdk-for-java/issues/20551
+                if ((!(e.getMessage().contains("InterruptedException"))) || (e instanceof BlobStorageException
+                && (!((BlobStorageException) e).getErrorCode().equals(404)))) {
+                    throw new IgniteSpiException("Failed to delete entry [containerName=" + containerName +
+                            ", entry=" + key + ']', e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets Azure Blob Storage Account Name.
+     * <p>
+     * For details refer to Azure Blob Storage API reference.
+     *
+     * @param accountName Account Name
+     * @return {@code this} for chaining.
+     */
+    @IgniteSpiConfiguration(optional = false)
+    public TcpDiscoveryAzureBlobStoreIpFinder setAccountName(String accountName) {
+        this.accountName = accountName;
+
+        return this;
+    }
+
+    /**
+     * Sets Azure Blob Storage Account Key
+     * <p>
+     * For details refer to Azure Blob Storage API reference.
+     *
+     * @param accountKey Account Key
+     * @return {@code this} for chaining.
+     */
+    @IgniteSpiConfiguration(optional = false)
+    public TcpDiscoveryAzureBlobStoreIpFinder setAccountKey(String accountKey) {
+        this.accountKey = accountKey;
+
+        return this;
+    }
+
+    /**
+     * Sets Azure Blob Storage endpoint
+     * <p>
+     * For details refer to Azure Blob Storage API reference.
+     *
+     * @param endPoint Endpoint for storage
+     * @return {@code this} for chaining.
+     */
+    @IgniteSpiConfiguration(optional = false)
+    public TcpDiscoveryAzureBlobStoreIpFinder setAccountEndpoint(String endPoint) {
+        this.endPoint = endPoint;
+
+        return this;
+    }
+
+    /**
+     * Sets container name for using in the context
+     * If the container name doesn't exist Ignite will automatically create itß.
+     *
+     * @param containerName Container Name.
+     * @return {@code this} for chaining.
+     */
+    @IgniteSpiConfiguration(optional = false)
+    public TcpDiscoveryAzureBlobStoreIpFinder setContainerName(String containerName) {
+        this.containerName = containerName;
+
+        return this;
+    }
+
+    /**
+     * Initialize the IP finder
+     * @throws IgniteSpiException
+     */
+    private void init() throws IgniteSpiException {
+        if (initGuard.compareAndSet(false, true)) {
+            if (accountKey == null || accountName == null || containerName == null || endPoint == null) {
+                throw new IgniteSpiException(
+                        "One or more of the required parameters is not set [accountName=" +
+                                accountName + ", accountKey=" + accountKey + ", containerName=" +
+                                containerName + ", endPoint=" + endPoint + "]");
+            }
+
+            try {
+                credential = new StorageSharedKeyCredential(accountName, accountKey);
+                blobServiceClient = new BlobServiceClientBuilder().endpoint(endPoint).credential(credential).buildClient();
+                blobContainerClient = blobServiceClient.getBlobContainerClient(containerName);
+
+                if (!blobContainerClient.exists()) {
+                    U.warn(log, "Container doesn't exist, will create it [containerName=" + containerName + "]");
+
+                    blobContainerClient.create();
+                }
+            }
+            finally {
+                initLatch.countDown();
+            }
+        }
+        else {
+            try {
+                U.await(initLatch);
+            }
+            catch (IgniteInterruptedCheckedException e) {
+                throw new IgniteSpiException("Thread has been interrupted.", e);
+            }
+
+            try {
+                if (!blobContainerClient.exists())
+                    throw new IgniteSpiException("IpFinder has not been initialized properly");
+            } catch (Exception e) {
+                // Check if this is a nested exception wrapping an InterruptedException
+                // https://github.com/Azure/azure-sdk-for-java/issues/20551
+                if (!(e.getCause() instanceof InterruptedException)) {
+                    throw e;
+                }
+            }
+        }
+    }
+
+    /**
+     * Constructs a node address from bucket's key.
+     *
+     * @param key Bucket key.
+     * @return Node address.
+     * @throws IgniteSpiException In case of error.
+     */
+    private InetSocketAddress addrFromString(String key) throws IgniteSpiException {
+        //TODO: This needs to move out to a generic helper class
+        String[] res = key.split("#");
+
+        if (res.length != 2)
+            throw new IgniteSpiException("Invalid address string: " + key);
+
+        int port;
+
+        try {
+            port = Integer.parseInt(res[1]);
+        }
+        catch (NumberFormatException ignored) {
+            throw new IgniteSpiException("Invalid port number: " + res[1]);
+        }
+
+        return new InetSocketAddress(res[0], port);
+    }
+
+    /**
+     * Constructs bucket's key from an address.
+     *
+     * @param addr Node address.
+     * @return Bucket key.
+     */
+    private String keyFromAddr(InetSocketAddress addr) {
+        // TODO: This needs to move out to a generic helper class
+        return addr.getAddress().getHostAddress() + "#" + addr.getPort();
+    }
+
+    /**
+     * Used by TEST SUITES only. Called through reflection.
+     *
+     * @param containerName Container to delete
+     */
+    private void removeContainer(String containerName) {
+        init();
+
+        try {
+            blobServiceClient.getBlobContainerClient(containerName).delete();
+        }
+        catch (Exception e) {
+            throw new IgniteSpiException("Failed to remove the container: " + containerName, e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public TcpDiscoveryAzureBlobStoreIpFinder setShared(boolean shared) {
+        super.setShared(shared);
+
+        return this;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        return S.toString(TcpDiscoveryAzureBlobStoreIpFinder.class, this);
+    }
+}
diff --git a/modules/azure/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/package-info.java b/modules/azure/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/package-info.java
new file mode 100644
index 0000000..b8410b3
--- /dev/null
+++ b/modules/azure/src/main/java/org/apache/ignite/spi/discovery/tcp/ipfinder/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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 description. -->
+ * Contains Azure Blob Storage IP finder.
+ */
+
+package org.apache.ignite.spi.discovery.tcp.ipfinder;
diff --git a/modules/azure/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/azure/TcpDiscoveryAzureBlobStoreIpFinderSelfTest.java b/modules/azure/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/azure/TcpDiscoveryAzureBlobStoreIpFinderSelfTest.java
new file mode 100644
index 0000000..3c172c2
--- /dev/null
+++ b/modules/azure/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/azure/TcpDiscoveryAzureBlobStoreIpFinderSelfTest.java
@@ -0,0 +1,91 @@
+package org.apache.ignite.spi.discovery.tcp.ipfinder.azure;
+/*
+ * 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.
+ */
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinderAbstractSelfTest;
+import org.apache.ignite.testsuites.IgniteAzureTestSuite;
+
+public class TcpDiscoveryAzureBlobStoreIpFinderSelfTest
+        extends TcpDiscoveryIpFinderAbstractSelfTest<TcpDiscoveryAzureBlobStoreIpFinder> {
+    private static String containerName;
+
+    /**
+     * Constructor.
+     *
+     * @throws Exception If any error occurs.
+     */
+    public TcpDiscoveryAzureBlobStoreIpFinderSelfTest() throws Exception {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        containerName = "ip-finder-test-container-" + InetAddress.getLocalHost().getAddress()[3];
+
+        super.beforeTestsStarted();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() {
+        try {
+            Method method = TcpDiscoveryAzureBlobStoreIpFinder.class.getDeclaredMethod("removeContainer", String.class);
+
+            method.setAccessible(true);
+
+            method.invoke(finder, containerName);
+        }
+        catch (Exception e) {
+            log.warning("Failed to remove bucket on Azure [containerName=" + containerName + ", mes=" + e.getMessage() + ']');
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected TcpDiscoveryAzureBlobStoreIpFinder ipFinder() throws Exception {
+        TcpDiscoveryAzureBlobStoreIpFinder finder = new TcpDiscoveryAzureBlobStoreIpFinder();
+
+        injectLogger(finder);
+
+        assert finder.isShared() : "Ip finder must be shared by default.";
+
+        finder.setAccountName(IgniteAzureTestSuite.getAccountName());
+        finder.setAccountKey(IgniteAzureTestSuite.getAccountKey());
+        finder.setAccountEndpoint(IgniteAzureTestSuite.getEndpoint());
+
+        finder.setContainerName(containerName);
+
+        for (int i = 0; i < 5; i++) {
+            Collection<InetSocketAddress> addrs = finder.getRegisteredAddresses();
+
+            if (!addrs.isEmpty())
+                finder.unregisterAddresses(addrs);
+            else
+                return finder;
+
+            U.sleep(1000);
+        }
+
+        if (!finder.getRegisteredAddresses().isEmpty())
+            throw new Exception("Failed to initialize IP finder.");
+
+        return finder;
+    }
+}
diff --git a/modules/azure/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/azure/package-info.java b/modules/azure/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/azure/package-info.java
new file mode 100644
index 0000000..c3f6711
--- /dev/null
+++ b/modules/azure/src/test/java/org/apache/ignite/spi/discovery/tcp/ipfinder/azure/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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 description. -->
+ * Contains Azure Blob Storage IP finder internal tests.
+ */
+package org.apache.ignite.spi.discovery.tcp.ipfinder.azure;
diff --git a/modules/azure/src/test/java/org/apache/ignite/testsuites/IgniteAzureTestSuite.java b/modules/azure/src/test/java/org/apache/ignite/testsuites/IgniteAzureTestSuite.java
new file mode 100644
index 0000000..0549568
--- /dev/null
+++ b/modules/azure/src/test/java/org/apache/ignite/testsuites/IgniteAzureTestSuite.java
@@ -0,0 +1,61 @@
+package org.apache.ignite.testsuites;
+/*
+ * 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.
+ */
+
+import org.apache.ignite.spi.discovery.tcp.ipfinder.azure.TcpDiscoveryAzureBlobStoreIpFinderSelfTest;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Azure integration tests
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({TcpDiscoveryAzureBlobStoreIpFinderSelfTest.class})
+public class IgniteAzureTestSuite {
+    /**
+     * @return Account Name
+     */
+    public static String getAccountName() {
+        String id = System.getenv("test.azure.account.name");
+
+        assert id != null : "Environment variable 'test.azure.account.name' is not set";
+
+        return id;
+    }
+
+    /**
+     * @return Account Key
+     */
+    public static String getAccountKey() {
+        String path = System.getenv("test.azure.account.key");
+
+        assert path != null : "Environment variable 'test.azure.account.key' is not set";
+
+        return path;
+    }
+
+    /**
+     * @return Endpoint
+     */
+    public static String getEndpoint() {
+        String name = System.getenv("test.azure.endpoint");
+
+        assert name != null : "Environment variable 'test.azure.endpoint' is not set";
+
+        return name;
+    }
+}
diff --git a/parent/pom.xml b/parent/pom.xml
index b227c30..05203a1 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -54,6 +54,8 @@
         <aspectj.version>1.8.13</aspectj.version>
         <aws.sdk.bundle.version>1.10.12_1</aws.sdk.bundle.version>
         <aws.sdk.version>1.11.75</aws.sdk.version>
+        <azure.sdk.version>12.0.0</azure.sdk.version>
+        <azure.netty.version>4.1.54.Final</azure.netty.version>
         <camel.version>2.22.0</camel.version>
         <aws.encryption.sdk.version>1.3.2</aws.encryption.sdk.version>
         <bouncycastle.version>1.60</bouncycastle.version>
diff --git a/pom.xml b/pom.xml
index a054b86..ed2c607 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,7 @@
         <module>modules/jcl</module>
         <module>modules/codegen</module>
         <module>modules/gce</module>
+        <module>modules/azure</module>
         <module>modules/cloud</module>
         <module>modules/mesos</module>
         <module>modules/yarn</module>

[ignite] 02/02: Amendments

Posted by il...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ilyak pushed a commit to branch ignite-14346
in repository https://gitbox.apache.org/repos/asf/ignite.git

commit 020eb587ad76414fcfeb8f7e233b5af453628bfe
Author: Ilya Kasnacheev <il...@gmail.com>
AuthorDate: Fri Apr 16 19:17:41 2021 +0300

    Amendments
---
 docs/_docs/setup.adoc    |  2 +-
 modules/azure/README.txt |  2 +-
 modules/azure/pom.xml    | 61 ++++++++++++++++++++++++++++++------------------
 3 files changed, 40 insertions(+), 25 deletions(-)

diff --git a/docs/_docs/setup.adoc b/docs/_docs/setup.adoc
index 44dfda6..754f4e7 100644
--- a/docs/_docs/setup.adoc
+++ b/docs/_docs/setup.adoc
@@ -203,7 +203,7 @@ adding @Gridify annotation to it.
 
 |ignite-aws |Cluster discovery on AWS S3. Refer to link:clustering/discovery-in-the-cloud#amazon-s3-ip-finder[Amazon S3 IP Finder] for details.
 
-|ignite-azure| Ignite Azure provides Azure Blob Storagebased implementations of IP finder for TCP discovery.
+|ignite-azure| Ignite Azure provides Azure Blob Storage-based implementation of IP finder for TCP discovery.
 
 |ignite-cassandra-serializers | The Ignite Cassandra Serializers module provides additional serializers to store objects as BLOBs in Cassandra. The module could be used as in conjunction with the Ignite Cassandra Store module.
 
diff --git a/modules/azure/README.txt b/modules/azure/README.txt
index 6d7df7d..33501f0 100644
--- a/modules/azure/README.txt
+++ b/modules/azure/README.txt
@@ -1,7 +1,7 @@
 Apache Ignite Azure Module
 ------------------------
 
-Apache Ignite Azure module provides Azure Blob Storage based implementations of IP finder for TCP discovery.
+Apache Ignite Azure module provides Azure Blob Storage based implementation of IP finder for TCP discovery.
 
 To enable Azure module when starting a standalone node, move 'optional/ignite-azure' folder to
 'libs' folder before running 'ignite.{sh|bat}' script. The content of the module folder will
diff --git a/modules/azure/pom.xml b/modules/azure/pom.xml
index 7e221eb..c3951b3 100644
--- a/modules/azure/pom.xml
+++ b/modules/azure/pom.xml
@@ -67,7 +67,7 @@
         <dependency>
             <groupId>org.jetbrains</groupId>
             <artifactId>annotations</artifactId>
-            <version>16.0.3</version>
+            <version>${jetbrains.annotations.version}</version>
         </dependency>
 
         <dependency>
@@ -110,12 +110,6 @@
             <artifactId>azure-identity</artifactId>
         </dependency>
 
-        <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
-        <dependency>
-            <groupId>io.netty</groupId>
-            <artifactId>netty-all</artifactId>
-        </dependency>
-
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
@@ -263,13 +257,9 @@
         </dependency>
 
         <dependency>
-            <groupId>io.projectreactor.netty</groupId>
-            <artifactId>reactor-netty</artifactId>
-        </dependency>
-        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
-            <version>1.7.7</version>
+            <version>${slf4j.version}</version>
         </dependency>
 
         <dependency>
@@ -292,6 +282,20 @@
             <version>0.9.0.RELEASE</version>
         </dependency>
 
+        <!-- https://mvnrepository.com/artifact/org.codehaus.woodstox/stax2-api -->
+        <dependency>
+            <groupId>org.codehaus.woodstox</groupId>
+            <artifactId>stax2-api</artifactId>
+            <version>4.2.1</version>
+        </dependency>
+
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-core</artifactId>
+            <version>${jackson.version}</version>
+        </dependency>
+
         <!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
         <dependency>
             <groupId>org.reflections</groupId>
@@ -303,25 +307,36 @@
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-simple</artifactId>
-            <version>1.8.0-beta4</version>
+            <version>${slf4j.version}</version>
             <scope>test</scope>
         </dependency>
 
-        <!-- https://mvnrepository.com/artifact/org.codehaus.woodstox/stax2-api -->
         <dependency>
-            <groupId>org.codehaus.woodstox</groupId>
-            <artifactId>stax2-api</artifactId>
-            <version>4.2.1</version>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <scope>test</scope>
         </dependency>
 
-        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
         <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-core</artifactId>
-            <version>${jackson.version}</version>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+            <version>${spring.version}</version>
+            <scope>test</scope>
         </dependency>
 
-    </dependencies>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+            <version>${spring.version}</version>
+            <scope>test</scope>
+        </dependency>
 
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+            <version>${spring.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
 
-</project>
\ No newline at end of file
+</project>