Merge pull request #56 from Microsoft/refactor

Implement azure redis cluster auto config
This commit is contained in:
Warren Zhu 2018-07-10 09:50:32 +08:00 коммит произвёл GitHub
Родитель 006d6e366b a0dbade934
Коммит 2c87e6eaa5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 153 добавлений и 35 удалений

Просмотреть файл

@ -12,6 +12,7 @@ import com.microsoft.azure.spring.cloud.autoconfigure.context.AzureContextAutoCo
import com.microsoft.azure.spring.cloud.autoconfigure.context.AzureProperties;
import com.microsoft.azure.spring.cloud.autoconfigure.telemetry.TelemetryTracker;
import com.microsoft.azure.spring.cloud.autoconfigure.telemetry.TelemetryUtils;
import com.microsoft.azure.spring.cloud.context.core.AzureAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
@ -26,6 +27,7 @@ import org.springframework.context.annotation.Primary;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Arrays;
/**
* An auto-configuration for Spring cache using Azure redis cache
@ -48,26 +50,32 @@ public class AzureRedisAutoConfiguration {
}
@ConditionalOnMissingBean
@Primary
@Bean
public RedisProperties redisProperties(Azure azure, AzureProperties azureProperties,
AzureRedisProperties azureRedisProperties) throws IOException {
public RedisProperties redisProperties(AzureAdmin azureAdmin,
AzureRedisProperties azureRedisProperties) throws IOException {
String cacheName = azureRedisProperties.getName();
RedisCache redisCache = azure.redisCaches()
.getByResourceGroup(azureProperties.getResourceGroup(), cacheName);
RedisCache redisCache = azureAdmin.getOrCreateRedisCache(cacheName);
RedisProperties redisProperties = new RedisProperties();
redisProperties.setHost(redisCache.hostName());
redisProperties.setPassword(redisCache.getKeys().primaryKey());
boolean useSsl = !redisCache.nonSslPort();
int port = useSsl ? redisCache.sslPort() : redisCache.port();
redisProperties.setPort(useSsl ? redisCache.sslPort() : redisCache.port());
boolean isCluster = redisCache.shardCount() > 0;
if (isCluster) {
RedisProperties.Cluster cluster = new RedisProperties.Cluster();
cluster.setNodes(Arrays.asList(redisCache.hostName() + ":" + port));
redisProperties.setCluster(cluster);
} else {
redisProperties.setHost(redisCache.hostName());
redisProperties.setPort(port);
}
redisProperties.setPassword(redisCache.getKeys().primaryKey());
redisProperties.setSsl(useSsl);
// TODO: handle cluster related config, Azure redis management api unsupported
return redisProperties;
}
}

Просмотреть файл

@ -6,20 +6,16 @@
package com.microsoft.azure.spring.cloud.autoconfigure.cache;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Warren Zhu
*/
@Getter
@Setter
@ConfigurationProperties("spring.cloud.azure.redis")
public class AzureRedisProperties {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}

Просмотреть файл

@ -6,20 +6,16 @@
package com.microsoft.azure.spring.cloud.autoconfigure.eventhub;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Warren Zhu
*/
@Getter
@Setter
@ConfigurationProperties("spring.cloud.azure.eventhub")
public class AzureEventHubProperties {
private String namespace;
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
}

Просмотреть файл

@ -6,6 +6,8 @@
package com.microsoft.azure.spring.cloud.autoconfigure.sql;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
@ -13,6 +15,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
*
* @author Warren Zhu
*/
@Getter
@Setter
@ConfigurationProperties("spring.cloud.azure.sql")
public class AzureSqlProperties {

Просмотреть файл

@ -6,20 +6,16 @@
package com.microsoft.azure.spring.cloud.autoconfigure.storage;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Warren Zhu
*/
@Getter
@Setter
@ConfigurationProperties("spring.cloud.azure.storage")
public class AzureStorageProperties {
private String account;
public String getAccount() {
return this.account;
}
public void setAccount(String account) {
this.account = account;
}
}

Просмотреть файл

@ -0,0 +1,85 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE in the project root for
* license information.
*/
package com.microsoft.azure.spring.cloud.autoconfigure.cache;
import com.microsoft.azure.management.Azure;
import com.microsoft.azure.management.redis.RedisAccessKeys;
import com.microsoft.azure.management.redis.RedisCache;
import com.microsoft.azure.spring.cloud.autoconfigure.context.AzureContextAutoConfiguration;
import com.microsoft.azure.spring.cloud.context.core.AzureAdmin;
import com.microsoft.azure.spring.cloud.context.core.CredentialsProvider;
import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class AzureRedisAutoConfigurationTest {
private static final String KEY = "KEY";
private static final String HOST = "localhost";
private static final int PORT = 6379;
private static final boolean IS_SSL = true;
private ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(
AutoConfigurations.of(AzureContextAutoConfiguration.class, AzureRedisAutoConfiguration.class))
.withUserConfiguration(
TestConfiguration.class);
@Test
public void testWithoutAzureRedisProperties() {
this.contextRunner.run(context -> assertThat(context).doesNotHaveBean(AzureRedisProperties.class));
}
@Test
public void testAzureRedisPropertiesConfigured() {
this.contextRunner.withPropertyValues("spring.cloud.azure.redis.name=redis").run(context -> {
assertThat(context).hasSingleBean(AzureRedisProperties.class);
assertThat(context.getBean(AzureRedisProperties.class).getName()).isEqualTo("redis");
assertThat(context).hasSingleBean(RedisProperties.class);
assertThat(context.getBean(RedisProperties.class).getPassword()).isEqualTo(KEY);
assertThat(context.getBean(RedisProperties.class).getHost()).isEqualTo(HOST);
assertThat(context.getBean(RedisProperties.class).getPort()).isEqualTo(PORT);
assertThat(context.getBean(RedisProperties.class).isSsl()).isEqualTo(IS_SSL);
});
}
@Configuration
static class TestConfiguration {
@Bean
public CredentialsProvider credentialsProvider() {
return mock(CredentialsProvider.class);
}
@Bean
Azure azure() {
return mock(Azure.class);
}
@Bean
AzureAdmin azureAdmin() {
AzureAdmin azureAdmin = mock(AzureAdmin.class);
RedisCache redisCache = mock(RedisCache.class);
RedisAccessKeys accessKeys = mock(RedisAccessKeys.class);
when(accessKeys.primaryKey()).thenReturn(KEY);
when(redisCache.hostName()).thenReturn(HOST);
when(redisCache.nonSslPort()).thenReturn(!IS_SSL);
when(redisCache.sslPort()).thenReturn(PORT);
when(redisCache.shardCount()).thenReturn(0);
when(redisCache.getKeys()).thenReturn(accessKeys);
when(azureAdmin.getOrCreateRedisCache(isA(String.class))).thenReturn(redisCache);
return azureAdmin;
}
}
}

Просмотреть файл

@ -9,6 +9,7 @@ package com.microsoft.azure.spring.cloud.context.core;
import com.microsoft.azure.management.Azure;
import com.microsoft.azure.management.eventhub.EventHub;
import com.microsoft.azure.management.eventhub.EventHubNamespace;
import com.microsoft.azure.management.redis.RedisCache;
import com.microsoft.azure.management.servicebus.Queue;
import com.microsoft.azure.management.servicebus.ServiceBusNamespace;
import com.microsoft.azure.management.servicebus.ServiceBusSubscription;
@ -17,6 +18,8 @@ import com.microsoft.azure.management.sql.SqlServer;
import com.microsoft.azure.management.storage.StorageAccount;
import org.springframework.util.Assert;
import java.util.function.Function;
public class AzureAdmin {
private final Azure azure;
@ -144,7 +147,13 @@ public class AzureAdmin {
}
public ServiceBusNamespace getServiceBusNamespace(String namespace) {
return azure.serviceBusNamespaces().getByResourceGroup(resourceGroup, namespace);
try {
return azure.serviceBusNamespaces().getByResourceGroup(resourceGroup, namespace);
} catch (NullPointerException ignore) {
// azure management api has no way to determine whether an service bus namespace exists
// Workaround for this is by catching NPE
return null;
}
}
public ServiceBusNamespace createServiceBusNamespace(String namespace) {
@ -206,4 +215,28 @@ public class AzureAdmin {
return topic.subscriptions().define(name).create();
}
public RedisCache getRedisCache(String name) {
return azure.redisCaches().getByResourceGroup(resourceGroup, name);
}
public RedisCache createRedisCache(String name) {
return azure.redisCaches().define(name).withRegion(region).withExistingResourceGroup(resourceGroup)
.withBasicSku().create();
}
public RedisCache getOrCreateRedisCache(String name) {
return getOrCreate(this::getRedisCache, this::createRedisCache).apply(name);
}
private <T, R> Function<T, R> getOrCreate(Function<T, R> getter, Function<T, R> creator) {
return t -> {
R result = getter.apply(t);
if (result != null) {
return result;
}
return creator.apply(t);
};
}
}