Browse Source

Support for accessing Azure repositories through a proxy (#23518)

You can define a proxy using the following settings:

```yml
azure.client.default.proxy.host: proxy.host
azure.client.default.proxy.port: 8888
azure.client.default.proxy.type: http
```

Supported values for `proxy.type` are `direct`, `http` or `socks`. Defaults to `direct` (no proxy).

Closes #23506

BTW I changed a test `testGetSelectedClientBackoffPolicyNbRetries` as it was using an old setting name `cloud.azure.storage.azure.max_retries` instead of `azure.client.azure1.max_retries`.
David Pilato 8 years ago
parent
commit
a34db4e09f

+ 13 - 0
docs/plugins/repository-azure.asciidoc

@@ -67,6 +67,19 @@ The Azure Repository plugin works with all Standard storage accounts
 https://azure.microsoft.com/en-gb/documentation/articles/storage-premium-storage[Premium Locally Redundant Storage] (`Premium_LRS`) is **not supported** as it is only usable as VM disk storage, not as general storage.
 ===============================================
 
+You can register a proxy per client using the following settings:
+
+[source,yaml]
+----
+azure.client.default.proxy.host: proxy.host
+azure.client.default.proxy.port: 8888
+azure.client.default.proxy.type: http
+----
+
+Supported values for `proxy.type` are `direct` (default), `http` or `socks`.
+When `proxy.type` is set to `http` or `socks`, `proxy.host` and `proxy.port` must be provided.
+
+
 [[repository-azure-repository-settings]]
 ===== Repository settings
 

+ 3 - 0
plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageService.java

@@ -22,13 +22,16 @@ package org.elasticsearch.cloud.azure.storage;
 import com.microsoft.azure.storage.LocationMode;
 import com.microsoft.azure.storage.StorageException;
 import org.elasticsearch.common.blobstore.BlobMetaData;
+import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.unit.ByteSizeUnit;
 import org.elasticsearch.common.unit.ByteSizeValue;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.Proxy;
 import java.net.URISyntaxException;
+import java.util.Locale;
 import java.util.Map;
 
 /**

+ 29 - 14
plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceImpl.java

@@ -21,6 +21,7 @@ package org.elasticsearch.cloud.azure.storage;
 
 import com.microsoft.azure.storage.CloudStorageAccount;
 import com.microsoft.azure.storage.LocationMode;
+import com.microsoft.azure.storage.OperationContext;
 import com.microsoft.azure.storage.RetryExponentialRetry;
 import com.microsoft.azure.storage.RetryPolicy;
 import com.microsoft.azure.storage.StorageException;
@@ -29,6 +30,7 @@ import com.microsoft.azure.storage.blob.BlobProperties;
 import com.microsoft.azure.storage.blob.CloudBlobClient;
 import com.microsoft.azure.storage.blob.CloudBlobContainer;
 import com.microsoft.azure.storage.blob.CloudBlockBlob;
+import com.microsoft.azure.storage.blob.DeleteSnapshotsOption;
 import com.microsoft.azure.storage.blob.ListBlobItem;
 import org.apache.logging.log4j.message.ParameterizedMessage;
 import org.apache.logging.log4j.util.Supplier;
@@ -131,12 +133,23 @@ public class AzureStorageServiceImpl extends AbstractComponent implements AzureS
         return client;
     }
 
+    private OperationContext generateOperationContext(String clientName) {
+        OperationContext context = new OperationContext();
+        AzureStorageSettings azureStorageSettings = this.storageSettings.get(clientName);
+
+        if (azureStorageSettings.getProxy() != null) {
+            context.setProxy(azureStorageSettings.getProxy());
+        }
+
+        return context;
+    }
+
     @Override
     public boolean doesContainerExist(String account, LocationMode mode, String container) {
         try {
             CloudBlobClient client = this.getSelectedClient(account, mode);
             CloudBlobContainer blobContainer = client.getContainerReference(container);
-            return SocketAccess.doPrivilegedException(blobContainer::exists);
+            return SocketAccess.doPrivilegedException(() -> blobContainer.exists(null, null, generateOperationContext(account)));
         } catch (Exception e) {
             logger.error("can not access container [{}]", container);
         }
@@ -148,7 +161,7 @@ public class AzureStorageServiceImpl extends AbstractComponent implements AzureS
         CloudBlobClient client = this.getSelectedClient(account, mode);
         CloudBlobContainer blobContainer = client.getContainerReference(container);
         logger.trace("removing container [{}]", container);
-        SocketAccess.doPrivilegedException(blobContainer::deleteIfExists);
+        SocketAccess.doPrivilegedException(() -> blobContainer.deleteIfExists(null, null, generateOperationContext(account)));
     }
 
     @Override
@@ -157,7 +170,7 @@ public class AzureStorageServiceImpl extends AbstractComponent implements AzureS
             CloudBlobClient client = this.getSelectedClient(account, mode);
             CloudBlobContainer blobContainer = client.getContainerReference(container);
             logger.trace("creating container [{}]", container);
-            SocketAccess.doPrivilegedException(blobContainer::createIfNotExists);
+            SocketAccess.doPrivilegedException(() -> blobContainer.createIfNotExists(null, null, generateOperationContext(account)));
         } catch (IllegalArgumentException e) {
             logger.trace((Supplier<?>) () -> new ParameterizedMessage("fails creating container [{}]", container), e);
             throw new RepositoryException(container, e.getMessage(), e);
@@ -174,7 +187,8 @@ public class AzureStorageServiceImpl extends AbstractComponent implements AzureS
         SocketAccess.doPrivilegedVoidException(() -> {
             if (blobContainer.exists()) {
                 // We list the blobs using a flat blob listing mode
-                for (ListBlobItem blobItem : blobContainer.listBlobs(path, true)) {
+                for (ListBlobItem blobItem : blobContainer.listBlobs(path, true, EnumSet.noneOf(BlobListingDetails.class), null,
+                    generateOperationContext(account))) {
                     String blobName = blobNameFromUri(blobItem.getUri());
                     logger.trace("removing blob [{}] full URI was [{}]", blobName, blobItem.getUri());
                     deleteBlob(account, mode, container, blobName);
@@ -208,9 +222,9 @@ public class AzureStorageServiceImpl extends AbstractComponent implements AzureS
         // Container name must be lower case.
         CloudBlobClient client = this.getSelectedClient(account, mode);
         CloudBlobContainer blobContainer = client.getContainerReference(container);
-        if (SocketAccess.doPrivilegedException(blobContainer::exists)) {
+        if (SocketAccess.doPrivilegedException(() -> blobContainer.exists(null, null, generateOperationContext(account)))) {
             CloudBlockBlob azureBlob = blobContainer.getBlockBlobReference(blob);
-            return SocketAccess.doPrivilegedException(azureBlob::exists);
+            return SocketAccess.doPrivilegedException(() -> azureBlob.exists(null, null, generateOperationContext(account)));
         }
 
         return false;
@@ -223,10 +237,11 @@ public class AzureStorageServiceImpl extends AbstractComponent implements AzureS
         // Container name must be lower case.
         CloudBlobClient client = this.getSelectedClient(account, mode);
         CloudBlobContainer blobContainer = client.getContainerReference(container);
-        if (SocketAccess.doPrivilegedException(blobContainer::exists)) {
+        if (SocketAccess.doPrivilegedException(() -> blobContainer.exists(null, null, generateOperationContext(account)))) {
             logger.trace("container [{}]: blob [{}] found. removing.", container, blob);
             CloudBlockBlob azureBlob = blobContainer.getBlockBlobReference(blob);
-            SocketAccess.doPrivilegedVoidException(azureBlob::delete);
+            SocketAccess.doPrivilegedVoidException(() -> azureBlob.delete(DeleteSnapshotsOption.NONE, null, null,
+                generateOperationContext(account)));
         }
     }
 
@@ -235,7 +250,7 @@ public class AzureStorageServiceImpl extends AbstractComponent implements AzureS
         logger.trace("reading container [{}], blob [{}]", container, blob);
         CloudBlobClient client = this.getSelectedClient(account, mode);
         CloudBlockBlob blockBlobReference = client.getContainerReference(container).getBlockBlobReference(blob);
-        return SocketAccess.doPrivilegedException(blockBlobReference::openInputStream);
+        return SocketAccess.doPrivilegedException(() -> blockBlobReference.openInputStream(null, null, generateOperationContext(account)));
     }
 
     @Override
@@ -243,7 +258,7 @@ public class AzureStorageServiceImpl extends AbstractComponent implements AzureS
         logger.trace("writing container [{}], blob [{}]", container, blob);
         CloudBlobClient client = this.getSelectedClient(account, mode);
         CloudBlockBlob blockBlobReference = client.getContainerReference(container).getBlockBlobReference(blob);
-        return SocketAccess.doPrivilegedException(blockBlobReference::openOutputStream);
+        return SocketAccess.doPrivilegedException(() -> blockBlobReference.openOutputStream(null, null, generateOperationContext(account)));
     }
 
     @Override
@@ -260,7 +275,7 @@ public class AzureStorageServiceImpl extends AbstractComponent implements AzureS
         SocketAccess.doPrivilegedVoidException(() -> {
             if (blobContainer.exists()) {
                 for (ListBlobItem blobItem : blobContainer.listBlobs(keyPath + (prefix == null ? "" : prefix), false,
-                    enumBlobListingDetails, null, null)) {
+                    enumBlobListingDetails, null, generateOperationContext(account))) {
                     URI uri = blobItem.getUri();
                     logger.trace("blob url [{}]", uri);
 
@@ -284,11 +299,11 @@ public class AzureStorageServiceImpl extends AbstractComponent implements AzureS
         CloudBlobClient client = this.getSelectedClient(account, mode);
         CloudBlobContainer blobContainer = client.getContainerReference(container);
         CloudBlockBlob blobSource = blobContainer.getBlockBlobReference(sourceBlob);
-        if (SocketAccess.doPrivilegedException(blobSource::exists)) {
+        if (SocketAccess.doPrivilegedException(() -> blobSource.exists(null, null, generateOperationContext(account)))) {
             CloudBlockBlob blobTarget = blobContainer.getBlockBlobReference(targetBlob);
             SocketAccess.doPrivilegedVoidException(() -> {
-                blobTarget.startCopy(blobSource);
-                blobSource.delete();
+                blobTarget.startCopy(blobSource, null, null, null, generateOperationContext(account));
+                blobSource.delete(DeleteSnapshotsOption.NONE, null, null, generateOperationContext(account));
             });
             logger.debug("moveBlob container [{}], sourceBlob [{}], targetBlob [{}] -> done", container, sourceBlob, targetBlob);
         }

+ 56 - 13
plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageSettings.java

@@ -20,16 +20,23 @@
 package org.elasticsearch.cloud.azure.storage;
 
 import com.microsoft.azure.storage.RetryPolicy;
+import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.settings.SecureSetting;
 import org.elasticsearch.common.settings.SecureString;
 import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Setting.AffixSetting;
 import org.elasticsearch.common.settings.Setting.Property;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.settings.SettingsException;
 import org.elasticsearch.common.unit.TimeValue;
 
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.UnknownHostException;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
@@ -37,38 +44,66 @@ public final class AzureStorageSettings {
     // prefix for azure client settings
     private static final String PREFIX = "azure.client.";
 
-    /**
-     * Azure account name
-     */
-    public static final AffixSetting<SecureString> ACCOUNT_SETTING = Setting.affixKeySetting(PREFIX, "account",
-        key -> SecureSetting.secureString(key, null));
+    /** Azure account name */
+    public static final AffixSetting<SecureString> ACCOUNT_SETTING =
+        Setting.affixKeySetting(PREFIX, "account", key -> SecureSetting.secureString(key, null));
 
-    /**
-     * max_retries: Number of retries in case of Azure errors. Defaults to 3 (RetryPolicy.DEFAULT_CLIENT_RETRY_COUNT).
-     */
+    /** max_retries: Number of retries in case of Azure errors. Defaults to 3 (RetryPolicy.DEFAULT_CLIENT_RETRY_COUNT). */
     private static final Setting<Integer> MAX_RETRIES_SETTING =
         Setting.affixKeySetting(PREFIX, "max_retries",
             (key) -> Setting.intSetting(key, RetryPolicy.DEFAULT_CLIENT_RETRY_COUNT, Setting.Property.NodeScope));
 
-    /**
-     * Azure key
-     */
+    /** Azure key */
     public static final AffixSetting<SecureString> KEY_SETTING = Setting.affixKeySetting(PREFIX, "key",
         key -> SecureSetting.secureString(key, null));
 
     public static final AffixSetting<TimeValue> TIMEOUT_SETTING = Setting.affixKeySetting(PREFIX, "timeout",
         (key) -> Setting.timeSetting(key, TimeValue.timeValueMinutes(-1), Property.NodeScope));
 
+    /** The type of the proxy to connect to azure through. Can be direct (no proxy, default), http or socks */
+    public static final AffixSetting<Proxy.Type> PROXY_TYPE_SETTING = Setting.affixKeySetting(PREFIX, "proxy.type",
+        (key) -> new Setting<>(key, "direct", s -> Proxy.Type.valueOf(s.toUpperCase(Locale.ROOT)), Property.NodeScope));
+
+    /** The host name of a proxy to connect to azure through. */
+    public static final Setting<String> PROXY_HOST_SETTING = Setting.affixKeySetting(PREFIX, "proxy.host",
+        (key) -> Setting.simpleString(key, Property.NodeScope));
+
+    /** The port of a proxy to connect to azure through. */
+    public static final Setting<Integer> PROXY_PORT_SETTING = Setting.affixKeySetting(PREFIX, "proxy.port",
+        (key) -> Setting.intSetting(key, 0, 0, 65535, Setting.Property.NodeScope));
+
     private final String account;
     private final String key;
     private final TimeValue timeout;
     private final int maxRetries;
+    private final Proxy proxy;
+
 
-    public AzureStorageSettings(String account, String key, TimeValue timeout, int maxRetries) {
+    public AzureStorageSettings(String account, String key, TimeValue timeout, int maxRetries, Proxy.Type proxyType, String proxyHost,
+                                Integer proxyPort) {
         this.account = account;
         this.key = key;
         this.timeout = timeout;
         this.maxRetries = maxRetries;
+
+        // Register the proxy if we have any
+        // Validate proxy settings
+        if (proxyType.equals(Proxy.Type.DIRECT) && (proxyPort != 0 || Strings.hasText(proxyHost))) {
+            throw new SettingsException("Azure Proxy port or host have been set but proxy type is not defined.");
+        }
+        if (proxyType.equals(Proxy.Type.DIRECT) == false && (proxyPort == 0 || Strings.isEmpty(proxyHost))) {
+            throw new SettingsException("Azure Proxy type has been set but proxy host or port is not defined.");
+        }
+
+        if (proxyType.equals(Proxy.Type.DIRECT)) {
+            proxy = null;
+        } else {
+            try {
+                proxy = new Proxy(proxyType, new InetSocketAddress(InetAddress.getByName(proxyHost), proxyPort));
+            } catch (UnknownHostException e) {
+                throw new SettingsException("Azure proxy host is unknown.", e);
+            }
+        }
     }
 
     public String getKey() {
@@ -87,6 +122,10 @@ public final class AzureStorageSettings {
         return maxRetries;
     }
 
+    public Proxy getProxy() {
+        return proxy;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder("AzureStorageSettings{");
@@ -94,6 +133,7 @@ public final class AzureStorageSettings {
         sb.append(", key='").append(key).append('\'');
         sb.append(", timeout=").append(timeout);
         sb.append(", maxRetries=").append(maxRetries);
+        sb.append(", proxy=").append(proxy);
         sb.append('}');
         return sb.toString();
     }
@@ -127,7 +167,10 @@ public final class AzureStorageSettings {
              SecureString key = getConfigValue(settings, clientName, KEY_SETTING)) {
             return new AzureStorageSettings(account.toString(), key.toString(),
                 getValue(settings, clientName, TIMEOUT_SETTING),
-                getValue(settings, clientName, MAX_RETRIES_SETTING));
+                getValue(settings, clientName, MAX_RETRIES_SETTING),
+                getValue(settings, clientName, PROXY_TYPE_SETTING),
+                getValue(settings, clientName, PROXY_HOST_SETTING),
+                getValue(settings, clientName, PROXY_PORT_SETTING));
         }
     }
 

+ 4 - 1
plugins/repository-azure/src/main/java/org/elasticsearch/plugin/repository/azure/AzureRepositoryPlugin.java

@@ -64,7 +64,10 @@ public class AzureRepositoryPlugin extends Plugin implements RepositoryPlugin {
         return Arrays.asList(
             AzureStorageSettings.ACCOUNT_SETTING,
             AzureStorageSettings.KEY_SETTING,
-            AzureStorageSettings.TIMEOUT_SETTING
+            AzureStorageSettings.TIMEOUT_SETTING,
+            AzureStorageSettings.PROXY_TYPE_SETTING,
+            AzureStorageSettings.PROXY_HOST_SETTING,
+            AzureStorageSettings.PROXY_PORT_SETTING
         );
     }
 }

+ 117 - 1
plugins/repository-azure/src/test/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceTests.java

@@ -20,14 +20,20 @@
 package org.elasticsearch.cloud.azure.storage;
 
 import com.microsoft.azure.storage.LocationMode;
+import com.microsoft.azure.storage.OperationContext;
 import com.microsoft.azure.storage.RetryExponentialRetry;
 import com.microsoft.azure.storage.blob.CloudBlobClient;
 import org.elasticsearch.common.settings.MockSecureSettings;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.settings.SettingsException;
 import org.elasticsearch.test.ESTestCase;
 
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.net.UnknownHostException;
 import java.util.Map;
 
 import static org.elasticsearch.cloud.azure.storage.AzureStorageServiceImpl.blobNameFromUri;
@@ -115,7 +121,7 @@ public class AzureStorageServiceTests extends ESTestCase {
     public void testGetSelectedClientBackoffPolicyNbRetries() {
         Settings timeoutSettings = Settings.builder()
             .setSecureSettings(buildSecureSettings())
-            .put("cloud.azure.storage.azure.max_retries", 7)
+            .put("azure.client.azure1.max_retries", 7)
             .build();
 
         AzureStorageServiceImpl azureStorageService = new AzureStorageServiceMock(timeoutSettings);
@@ -124,6 +130,116 @@ public class AzureStorageServiceTests extends ESTestCase {
         assertThat(client1.getDefaultRequestOptions().getRetryPolicyFactory(), instanceOf(RetryExponentialRetry.class));
     }
 
+    public void testNoProxy() {
+        Settings settings = Settings.builder()
+            .setSecureSettings(buildSecureSettings())
+            .build();
+        AzureStorageServiceMock mock = new AzureStorageServiceMock(settings);
+        assertThat(mock.storageSettings.get("azure1").getProxy(), nullValue());
+        assertThat(mock.storageSettings.get("azure2").getProxy(), nullValue());
+        assertThat(mock.storageSettings.get("azure3").getProxy(), nullValue());
+    }
+
+    public void testProxyHttp() throws UnknownHostException {
+        Settings settings = Settings.builder()
+            .setSecureSettings(buildSecureSettings())
+            .put("azure.client.azure1.proxy.host", "127.0.0.1")
+            .put("azure.client.azure1.proxy.port", 8080)
+            .put("azure.client.azure1.proxy.type", "http")
+            .build();
+        AzureStorageServiceMock mock = new AzureStorageServiceMock(settings);
+        Proxy azure1Proxy = mock.storageSettings.get("azure1").getProxy();
+
+        assertThat(azure1Proxy, notNullValue());
+        assertThat(azure1Proxy.type(), is(Proxy.Type.HTTP));
+        assertThat(azure1Proxy.address(), is(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 8080)));
+        assertThat(mock.storageSettings.get("azure2").getProxy(), nullValue());
+        assertThat(mock.storageSettings.get("azure3").getProxy(), nullValue());
+    }
+
+    public void testMultipleProxies() throws UnknownHostException {
+        Settings settings = Settings.builder()
+            .setSecureSettings(buildSecureSettings())
+            .put("azure.client.azure1.proxy.host", "127.0.0.1")
+            .put("azure.client.azure1.proxy.port", 8080)
+            .put("azure.client.azure1.proxy.type", "http")
+            .put("azure.client.azure2.proxy.host", "127.0.0.1")
+            .put("azure.client.azure2.proxy.port", 8081)
+            .put("azure.client.azure2.proxy.type", "http")
+            .build();
+        AzureStorageServiceMock mock = new AzureStorageServiceMock(settings);
+        Proxy azure1Proxy = mock.storageSettings.get("azure1").getProxy();
+        assertThat(azure1Proxy, notNullValue());
+        assertThat(azure1Proxy.type(), is(Proxy.Type.HTTP));
+        assertThat(azure1Proxy.address(), is(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 8080)));
+        Proxy azure2Proxy = mock.storageSettings.get("azure2").getProxy();
+        assertThat(azure2Proxy, notNullValue());
+        assertThat(azure2Proxy.type(), is(Proxy.Type.HTTP));
+        assertThat(azure2Proxy.address(), is(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 8081)));
+        assertThat(mock.storageSettings.get("azure3").getProxy(), nullValue());
+    }
+
+    public void testProxySocks() throws UnknownHostException {
+        Settings settings = Settings.builder()
+            .setSecureSettings(buildSecureSettings())
+            .put("azure.client.azure1.proxy.host", "127.0.0.1")
+            .put("azure.client.azure1.proxy.port", 8080)
+            .put("azure.client.azure1.proxy.type", "socks")
+            .build();
+        AzureStorageServiceMock mock = new AzureStorageServiceMock(settings);
+        Proxy azure1Proxy = mock.storageSettings.get("azure1").getProxy();
+        assertThat(azure1Proxy, notNullValue());
+        assertThat(azure1Proxy.type(), is(Proxy.Type.SOCKS));
+        assertThat(azure1Proxy.address(), is(new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 8080)));
+        assertThat(mock.storageSettings.get("azure2").getProxy(), nullValue());
+        assertThat(mock.storageSettings.get("azure3").getProxy(), nullValue());
+    }
+
+    public void testProxyNoHost() {
+        Settings settings = Settings.builder()
+            .setSecureSettings(buildSecureSettings())
+            .put("azure.client.azure1.proxy.port", 8080)
+            .put("azure.client.azure1.proxy.type", randomFrom("socks", "http"))
+            .build();
+
+        SettingsException e = expectThrows(SettingsException.class, () -> new AzureStorageServiceMock(settings));
+        assertEquals("Azure Proxy type has been set but proxy host or port is not defined.", e.getMessage());
+    }
+
+    public void testProxyNoPort() {
+        Settings settings = Settings.builder()
+            .setSecureSettings(buildSecureSettings())
+            .put("azure.client.azure1.proxy.host", "127.0.0.1")
+            .put("azure.client.azure1.proxy.type", randomFrom("socks", "http"))
+            .build();
+
+        SettingsException e = expectThrows(SettingsException.class, () -> new AzureStorageServiceMock(settings));
+        assertEquals("Azure Proxy type has been set but proxy host or port is not defined.", e.getMessage());
+    }
+
+    public void testProxyNoType() {
+        Settings settings = Settings.builder()
+            .setSecureSettings(buildSecureSettings())
+            .put("azure.client.azure1.proxy.host", "127.0.0.1")
+            .put("azure.client.azure1.proxy.port", 8080)
+            .build();
+
+        SettingsException e = expectThrows(SettingsException.class, () -> new AzureStorageServiceMock(settings));
+        assertEquals("Azure Proxy port or host have been set but proxy type is not defined.", e.getMessage());
+    }
+
+    public void testProxyWrongHost() {
+        Settings settings = Settings.builder()
+            .setSecureSettings(buildSecureSettings())
+            .put("azure.client.azure1.proxy.type", randomFrom("socks", "http"))
+            .put("azure.client.azure1.proxy.host", "thisisnotavalidhostorwehavebeensuperunlucky")
+            .put("azure.client.azure1.proxy.port", 8080)
+            .build();
+
+        SettingsException e = expectThrows(SettingsException.class, () -> new AzureStorageServiceMock(settings));
+        assertEquals("Azure proxy host is unknown.", e.getMessage());
+    }
+
     /**
      * This internal class just overload createClient method which is called by AzureStorageServiceImpl.doStart()
      */

+ 4 - 0
plugins/repository-azure/src/test/java/org/elasticsearch/repositories/azure/AzureSnapshotRestoreListSnapshotsTests.java

@@ -35,6 +35,7 @@ import org.junit.After;
 import org.junit.Before;
 
 import java.net.URISyntaxException;
+import java.net.UnknownHostException;
 import java.util.concurrent.TimeUnit;
 
 import static org.elasticsearch.cloud.azure.AzureTestUtils.readSettingsFromFile;
@@ -63,6 +64,9 @@ public class AzureSnapshotRestoreListSnapshotsTests extends AbstractAzureWithThi
         AzureStorageSettings.load(readSettingsFromFile()));
     private final String containerName = getContainerName();
 
+    public AzureSnapshotRestoreListSnapshotsTests() {
+    }
+
     public void testList() throws Exception {
         Client client = client();
         logger.info("-->  creating azure primary repository");

+ 5 - 4
plugins/repository-azure/src/test/java/org/elasticsearch/repositories/azure/AzureSnapshotRestoreTests.java

@@ -54,6 +54,7 @@ import org.junit.Before;
 import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.Collection;
+import java.net.UnknownHostException;
 import java.util.Locale;
 import java.util.concurrent.TimeUnit;
 
@@ -103,7 +104,7 @@ public class AzureSnapshotRestoreTests extends AbstractAzureWithThirdPartyIntegT
     }
 
     @Before @After
-    public final void wipeAzureRepositories() throws StorageException, URISyntaxException {
+    public final void wipeAzureRepositories() throws StorageException, URISyntaxException, UnknownHostException {
         wipeRepositories();
         cleanRepositoryFiles(
             getContainerName(),
@@ -455,7 +456,7 @@ public class AzureSnapshotRestoreTests extends AbstractAzureWithThirdPartyIntegT
                 try {
                     logger.info("--> remove container [{}]", container);
                     cleanRepositoryFiles(container);
-                } catch (StorageException | URISyntaxException e) {
+                } catch (StorageException | URISyntaxException | UnknownHostException ignored) {
                     // We can ignore that as we just try to clean after the test
                 }
                 assertTrue(putRepositoryResponse.isAcknowledged() == correct);
@@ -498,7 +499,7 @@ public class AzureSnapshotRestoreTests extends AbstractAzureWithThirdPartyIntegT
     public void testRemoveAndCreateContainer() throws Exception {
         final String container = getContainerName().concat("-testremove");
         final AzureStorageService storageService = new AzureStorageServiceImpl(nodeSettings(0),AzureStorageSettings.load(nodeSettings(0)));
-      
+
         // It could happen that we run this test really close to a previous one
         // so we might need some time to be able to create the container
         assertBusy(() -> {
@@ -549,7 +550,7 @@ public class AzureSnapshotRestoreTests extends AbstractAzureWithThirdPartyIntegT
     /**
      * Purge the test containers
      */
-    public void cleanRepositoryFiles(String... containers) throws StorageException, URISyntaxException {
+    public void cleanRepositoryFiles(String... containers) throws StorageException, URISyntaxException, UnknownHostException {
         Settings settings = readSettingsFromFile();
         AzureStorageService client = new AzureStorageServiceImpl(settings, AzureStorageSettings.load(settings));
         for (String container : containers) {