Skip to content

Commit

Permalink
Servicebus health indicator implementation and doc. (#23050)
Browse files Browse the repository at this point in the history
* Add health indicator doc.
* Service bus health indicator logic implementation.
* Add unit tests.
  • Loading branch information
ZhuXiaoBing-cn authored Aug 9, 2021
1 parent ee215fc commit c2b1294
Show file tree
Hide file tree
Showing 32 changed files with 689 additions and 31 deletions.
91 changes: 91 additions & 0 deletions sdk/spring/azure-spring-boot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,97 @@ variable and setting the appropriate properties used by auto-configuration code.

For details, please see sample code in the [azure-spring-boot-sample-cloud-foundry](https://github.com/Azure-Samples/azure-spring-boot-samples/tree/tag_azure-spring-boot_3.6.0/cloudfoundry/azure-cloud-foundry-service-sample)

## Health indicator

You can use health information to check the status of your running application. It is often used by
monitoring software to alert someone when a production system goes down. The information exposed by
the health endpoint depends on the management.endpoint.health.show-details and
management.endpoint.health.show-components properties which can be configured with one of the
following values:

| key | Name |
| ---- | ---- |
| never | Details are never shown. |
| when-authorized | Details are only shown to authorized users. Authorized roles can be configured using management.endpoint.health.roles. |
| always |Details are shown to all users. |

The default value is never. A user is considered to be authorized when they are in one or more of
the endpoint’s roles. If the endpoint has no configured roles (the default) all authenticated users
are considered to be authorized. The roles can be configured using the
management.endpoint.health.roles property.

**NOTE:** If you have secured your application and wish to use `always`, your security configuration
must permit access to the health endpoint for both authenticated and unauthenticated users.

### Auto-configured HealthIndicators

The following HealthIndicators are auto-configured by Azure Spring Boot when appropriate. You can
also enable/disable selected indicators by configuring management.health.key.enabled, with the key
listed in the table below.

| key | Name | Description |
| ---- | ---- | ---- |
| azure-cosmos | CosmosHealthIndicator | Checks that a cosmos database is up. |
| azure-key-vault | KeyVaultHealthIndicator | Checks that a key vault is up. |
| azure-storage | BlobStorageHealthIndicator | Checks that a storage blob is up. |
| azure-storage | FileStorageHealthIndicator | Checks that a storage file is up. |

### Add the dependent

```yaml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
```

### Enabling the Actuator

When you do the following configuration in `application.yml` , you can access the endpoint to get
the health of the component.

```yaml
management:
health:
azure-cosmos:
enabled: true
azure-key-vault:
enabled: true
azure-storage:
enabled: true
endpoint:
health:
show-details: always
```
Access the Health Endpoint:
```json
{
"status": "UP",
"components": {
"blobStorage": {
"status": "UP",
"details": {
"URL": "https://xxxx.blob.core.windows.net"
}
},
"cosmos": {
"status": "UP",
"details": {
"database": "xxx"
}
},
"keyVault": {
"status": "UP"
}
}
}
```


## Examples
The following section provides sample projects illustrating how to use the Azure Spring Boot starters.
### More sample code
Expand Down
81 changes: 81 additions & 0 deletions sdk/spring/azure-spring-cloud-autoconfigure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,87 @@ This project provides auto-configuration for the following Azure services:
- [Service Bus][service_bus]
- [Storage Queue][storage_queue]

## Health indicator

You can use health information to check the status of your running application. It is often used by
monitoring software to alert someone when a production system goes down. The information exposed by
the health endpoint depends on the management.endpoint.health.show-details and
management.endpoint.health.show-components properties which can be configured with one of the
following values:

| key | Name |
| ---- | ---- |
| never | Details are never shown. |
| when-authorized | Details are only shown to authorized users. Authorized roles can be configured using management.endpoint.health.roles. |
| always |Details are shown to all users. |

The default value is never. A user is considered to be authorized when they are in one or more of
the endpoint’s roles. If the endpoint has no configured roles (the default) all authenticated users
are considered to be authorized. The roles can be configured using the
management.endpoint.health.roles property.

**NOTE:** If you have secured your application and wish to use `always`, your security configuration
must permit access to the health endpoint for both authenticated and unauthenticated users.

### Auto-configured HealthIndicators

The following HealthIndicators are auto-configured by Azure Spring Boot when appropriate. You can
also enable/disable selected indicators by configuring management.health.key.enabled, with the key
listed in the table below.

| key | Name | Description |
| ---- | ---- | ---- |
| binders | EventHubHealthIndicator | Checks that an event hub is up. |
| binders | ServiceBusQueueHealthIndicator | Checks that a service bus queue is up. |
| binders | ServiceBusTopicHealthIndicator | Checks that a service bus topic is up. |

### Add the dependent

```yaml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
```

### Enabling the Actuator

When you do the following configuration in `application.yml` , you can access the endpoint to get
the health of the component.

```yaml
management:
health:
binders:
enabled: true
endpoint:
health:
show-details: always
```
Access the Health Endpoint:
```json
{
"status": "UP",
"components": {
"binders": {
"status": "UP",
"components": {
"eventhub-1": {
"status": "UP"
},
"servicebus-1": {
"status": "UP"
}
}
}
}
}
```

## Examples

The following section provides sample projects illustrating how to use the Spring Cloud for Azure starters.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,4 @@ public ServiceBusConnectionStringProvider serviceBusConnectionStringProvider(

return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,4 @@ public ServiceBusTopicOperation topicOperation(ServiceBusTopicClientFactory fact
ServiceBusMessageConverter messageConverter) {
return new ServiceBusTopicTemplate(factory, messageConverter);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.5.3</version> <!-- {x-version-update;org.springframework.boot:spring-boot-starter-actuator;external_dependency} -->
<optional>true</optional>
</dependency>

<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>azure-spring-cloud-stream-binder-test</artifactId>
Expand Down Expand Up @@ -65,6 +72,7 @@
<bannedDependencies>
<includes>
<include>org.springframework.boot:spring-boot-configuration-processor:[2.5.3]</include> <!-- {x-include-update;org.springframework.boot:spring-boot-configuration-processor;external_dependency} -->
<include>org.springframework.boot:spring-boot-starter-actuator:[2.5.3]</include> <!-- {x-include-update;org.springframework.boot:spring-boot-starter-actuator;external_dependency} -->
</includes>
</bannedDependencies>
</rules>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.servicebus.stream.binder;

import com.azure.spring.integration.servicebus.health.Instrumentation;
import com.azure.spring.integration.servicebus.health.InstrumentationManager;
import com.azure.spring.integration.servicebus.queue.ServiceBusQueueOperation;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;

/**
* Implementation of a {@link AbstractHealthIndicator} returning status information for
* service bus queue.
*/
public class ServiceBusQueueHealthIndicator extends AbstractHealthIndicator {

private final InstrumentationManager instrumentationManager;

public ServiceBusQueueHealthIndicator(ServiceBusQueueOperation serviceBusQueueOperation) {
super("Service bus health check failed");
this.instrumentationManager = serviceBusQueueOperation.getInstrumentationManager();
}

@Override
protected void doHealthCheck(Health.Builder builder) {
if (instrumentationManager == null || instrumentationManager.getHealthInstrumentations().isEmpty()) {
builder.unknown();
return;
}
if (instrumentationManager.getHealthInstrumentations().stream()
.allMatch(Instrumentation::isUp)) {
builder.up();
return;
}
if (instrumentationManager.getHealthInstrumentations().stream()
.allMatch(Instrumentation::isOutOfService)) {
builder.outOfService();
return;
}
builder.down();
instrumentationManager.getHealthInstrumentations().stream()
.filter(instrumentation -> !instrumentation.isStarted())
.forEach(instrumentation -> builder
.withDetail(instrumentation.getName() + ":" + instrumentation.getType().getTypeName(),
instrumentation.getStartException()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
AzureEnvironmentAutoConfiguration.class,
AzureContextAutoConfiguration.class,
AzureServiceBusAutoConfiguration.class,
AzureServiceBusQueueAutoConfiguration.class })
AzureServiceBusQueueAutoConfiguration.class,
ServiceBusQueueBinderHealthIndicatorConfiguration.class
})
@EnableConfigurationProperties({ AzureServiceBusProperties.class, ServiceBusQueueExtendedBindingProperties.class })
public class ServiceBusQueueBinderConfiguration {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.spring.servicebus.stream.binder.config;

import com.azure.spring.integration.servicebus.queue.ServiceBusQueueOperation;
import com.azure.spring.servicebus.stream.binder.ServiceBusQueueHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* Auto configuration for {@link ServiceBusQueueHealthIndicator}.
*/
@Configuration
@ConditionalOnClass(name = "org.springframework.boot.actuate.health.HealthIndicator")
@ConditionalOnEnabledHealthIndicator("binders")
public class ServiceBusQueueBinderHealthIndicatorConfiguration {

@Bean
public ServiceBusQueueHealthIndicator serviceBusQueueHealthIndicator(ServiceBusQueueOperation serviceBusQueueOperation) {
return new ServiceBusQueueHealthIndicator(serviceBusQueueOperation);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.servicebus.stream.binder;

import com.azure.messaging.servicebus.ServiceBusProcessorClient;
import com.azure.spring.integration.servicebus.ServiceBusClientConfig;
import com.azure.spring.integration.servicebus.ServiceBusMessageProcessor;
import com.azure.spring.integration.servicebus.ServiceBusRuntimeException;
import com.azure.spring.integration.servicebus.factory.ServiceBusQueueClientFactory;
import com.azure.spring.integration.servicebus.queue.ServiceBusQueueTemplate;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status;
import org.springframework.messaging.Message;

import java.util.function.Consumer;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;

public class ServiceBusQueueBinderHealthIndicatorTest {

@Mock
private ServiceBusQueueClientFactory serviceBusQueueClientFactory;

@Mock
private ServiceBusProcessorClient processorClient;

private ServiceBusQueueHealthIndicator serviceBusQueueHealthIndicator;

private ServiceBusQueueTemplate serviceBusQueueTemplate;

private Consumer<Message<?>> consumer = message -> {
};

@BeforeEach
public void init() {
MockitoAnnotations.openMocks(this);
serviceBusQueueTemplate = new ServiceBusQueueTemplate(serviceBusQueueClientFactory);
serviceBusQueueHealthIndicator = new ServiceBusQueueHealthIndicator(serviceBusQueueTemplate);
}

@Test
public void testNoInstrumentationInUse() {
final Health health = serviceBusQueueHealthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.UNKNOWN);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testServiceBusQueueIsUp() {
when(serviceBusQueueClientFactory.getOrCreateProcessor(anyString(), any(ServiceBusClientConfig.class),
any(ServiceBusMessageProcessor.class))).thenReturn(processorClient);
serviceBusQueueTemplate.subscribe("queue-test-1", consumer, byte[].class);
final Health health = serviceBusQueueHealthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.UP);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
@Test
public void testServiceBusQueueIsDown() {
when(serviceBusQueueClientFactory.getOrCreateProcessor(anyString(), any(ServiceBusClientConfig.class),
any(ServiceBusMessageProcessor.class))).thenReturn(processorClient);
doThrow(NullPointerException.class).when(processorClient).start();
assertThrows(ServiceBusRuntimeException.class, () -> {
serviceBusQueueTemplate.subscribe("queue-test-1", consumer, byte[].class);
});
final Health health = serviceBusQueueHealthIndicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@
@RunWith(MockitoJUnitRunner.class)
public class ServiceBusQueuePartitionBinderTests
extends AzurePartitionBinderTests<ServiceBusQueueTestBinder,
ExtendedConsumerProperties<ServiceBusConsumerProperties>,
ExtendedProducerProperties<ServiceBusProducerProperties>> {
//TODO (Xiaobing Zhu): It is currently impossible to upgrade JUnit 4 to JUnit 5 due to the inheritance of Spring unit tests.
ExtendedConsumerProperties<ServiceBusConsumerProperties>,
ExtendedProducerProperties<ServiceBusProducerProperties>> {
//TODO (Xiaobing Zhu): It is currently impossible to upgrade JUnit 4 to JUnit 5 due to the inheritance of Spring
// unit tests.

@Mock
ServiceBusQueueClientFactory clientFactory;
Expand Down
Loading

0 comments on commit c2b1294

Please sign in to comment.