Feature dmf integration tests (#459)

* Add rabbit mq integration test
* Change DMF Api for ActionUpdateStatus JSON
* Add test for invalid target messages.
* Insert tests for AmqpAuthenticationMessageHandler

Signed-off-by: Melanie Retter <melanie.retter@bosch-si.com>

* - Added vhost creation to tests
- Added missing test 
- Created beans for dmf client and vhost
- refactoring


Signed-off-by: Jonathan Philip Knoblauch <JonathanPhilip.Knoblauch@bosch-si.com>

* Completed missing tests

Signed-off-by: Jonathan Philip Knoblauch <JonathanPhilip.Knoblauch@bosch-si.com>

* Create vhost after every test class

Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>

* Add readme.md

Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>

* Add check if rabbit mq management is available

Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>

* refactoring test

Signed-off-by: Jonathan Philip Knoblauch <JonathanPhilip.Knoblauch@bosch-si.com>

* Create integration test for amqpAuthentication
Add test description

Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>

* Fix sonar issue
Add rabbitmq username and password

Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>

* Merge master

Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>

* Fix some code smells

Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>

* Make some member immutable
Change executor

Signed-off-by: SirWayne <dennis.melzer@bosch-si.com>
This commit is contained in:
Dennis Melzer
2017-03-22 15:50:03 +01:00
committed by GitHub
parent 59107c521f
commit 7de6edb5fe
24 changed files with 2124 additions and 197 deletions

View File

@@ -53,7 +53,7 @@ public class SpSenderService extends SenderService {
public SpSenderService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties, public SpSenderService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
final SimulationProperties simulationProperties) { final SimulationProperties simulationProperties) {
super(rabbitTemplate, amqpProperties); super(rabbitTemplate, amqpProperties);
this.spExchange = AmqpSettings.DMF_EXCHANGE; spExchange = AmqpSettings.DMF_EXCHANGE;
this.simulationProperties = simulationProperties; this.simulationProperties = simulationProperties;
} }
@@ -226,15 +226,13 @@ public class SpSenderService extends SenderService {
final List<String> updateResultMessages, final Long actionId) { final List<String> updateResultMessages, final Long actionId) {
final MessageProperties messageProperties = new MessageProperties(); final MessageProperties messageProperties = new MessageProperties();
final Map<String, Object> headers = messageProperties.getHeaders(); final Map<String, Object> headers = messageProperties.getHeaders();
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(actionId, actionStatus);
actionUpdateStatus.setActionStatus(actionStatus);
headers.put(MessageHeaderKey.TYPE, MessageType.EVENT.name()); headers.put(MessageHeaderKey.TYPE, MessageType.EVENT.name());
headers.put(MessageHeaderKey.TENANT, tenant); headers.put(MessageHeaderKey.TENANT, tenant);
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON); headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
actionUpdateStatus.addMessage(updateResultMessages); actionUpdateStatus.addMessage(updateResultMessages);
actionUpdateStatus.setActionId(actionId);
return convertMessage(actionUpdateStatus, messageProperties); return convertMessage(actionUpdateStatus, messageProperties);
} }
@@ -242,14 +240,12 @@ public class SpSenderService extends SenderService {
final List<String> updateResultMessages) { final List<String> updateResultMessages) {
final MessageProperties messageProperties = new MessageProperties(); final MessageProperties messageProperties = new MessageProperties();
final Map<String, Object> headers = messageProperties.getHeaders(); final Map<String, Object> headers = messageProperties.getHeaders();
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(cacheValue.getActionId(), actionStatus);
actionUpdateStatus.setActionStatus(actionStatus);
headers.put(MessageHeaderKey.TYPE, MessageType.EVENT.name()); headers.put(MessageHeaderKey.TYPE, MessageType.EVENT.name());
headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant()); headers.put(MessageHeaderKey.TENANT, cacheValue.getTenant());
headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); headers.put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON); headers.put(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
actionUpdateStatus.addMessage(updateResultMessages); actionUpdateStatus.addMessage(updateResultMessages);
actionUpdateStatus.setActionId(cacheValue.getActionId());
return convertMessage(actionUpdateStatus, messageProperties); return convertMessage(actionUpdateStatus, messageProperties);
} }

View File

@@ -0,0 +1,10 @@
# hawkBit device management federation API - implementation
This is the AMQP implementation for the device management federation API. The implementation uses the spring-amqp project.
# Integration Test
This modules contains some integration tests for the device management federation API implementation which uses a RabbitMQ. If there is no RabbitMQ running on the system, all tests will be marked as skipped. You can disable this rule and the tests will fail if there is no RabbitMQ running. n order to disable the rule at runtime, set an environment variable RABBITMQ_SERVER_REQUIRED=true.
The default RabbitMQ hostname is localhost. To set another hostname, set the property spring.rabbitmq.host to the new hostname.
The default RabbitMQ username is guest. To set another username, set the property spring.rabbitmq.username to the new username.
The default RabbitMQ password is guest. To set another password, set the property spring.rabbitmq.password to the new password.

View File

@@ -8,6 +8,7 @@
http://www.eclipse.org/legal/epl-v10.html http://www.eclipse.org/legal/epl-v10.html
--> -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <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"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -65,7 +66,7 @@
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
</dependency> </dependency>
<!-- Optional --> <!-- Optional -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@@ -107,21 +108,11 @@
<artifactId>allure-junit-adaptor</artifactId> <artifactId>allure-junit-adaptor</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId> <artifactId>spring-security-aspects</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.security</groupId> <groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId> <artifactId>spring-security-config</artifactId>
@@ -132,6 +123,16 @@
<artifactId>spring-context-support</artifactId> <artifactId>spring-context-support</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -30,7 +30,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.amqp.AmqpRejectAndDontRequeueException;
import org.springframework.amqp.core.Message; import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@@ -96,7 +95,7 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
* the amqp message * the amqp message
* @return the rpc message back to supplier. * @return the rpc message back to supplier.
*/ */
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory") @RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue:authentication_receiver}", containerFactory = "listenerContainerFactory")
public Message onAuthenticationRequest(final Message message) { public Message onAuthenticationRequest(final Message message) {
checkContentTypeJson(message); checkContentTypeJson(message);
final SecurityContext oldContext = SecurityContextHolder.getContext(); final SecurityContext oldContext = SecurityContextHolder.getContext();
@@ -157,6 +156,11 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
private Optional<org.eclipse.hawkbit.repository.model.Artifact> findArtifactByFileResource( private Optional<org.eclipse.hawkbit.repository.model.Artifact> findArtifactByFileResource(
final FileResource fileResource) { final FileResource fileResource) {
if (fileResource == null) {
return Optional.empty();
}
if (fileResource.getSha1() != null) { if (fileResource.getSha1() != null) {
return artifactManagement.findFirstArtifactBySHA1(fileResource.getSha1()); return artifactManagement.findFirstArtifactBySHA1(fileResource.getSha1());
} }
@@ -188,9 +192,7 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
private Message handleAuthenticationMessage(final Message message) { private Message handleAuthenticationMessage(final Message message) {
final DownloadResponse authentificationResponse = new DownloadResponse(); final DownloadResponse authentificationResponse = new DownloadResponse();
final MessageProperties messageProperties = message.getMessageProperties();
final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class); final TenantSecurityToken secruityToken = convertMessage(message, TenantSecurityToken.class);
final FileResource fileResource = secruityToken.getFileResource(); final FileResource fileResource = secruityToken.getFileResource();
try { try {
SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken)); SecurityContextHolder.getContext().setAuthentication(authenticationManager.doAuthenticate(secruityToken));
@@ -228,7 +230,7 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
authentificationResponse.setMessage(errorMessage); authentificationResponse.setMessage(errorMessage);
} }
return getMessageConverter().toMessage(authentificationResponse, messageProperties); return getMessageConverter().toMessage(authentificationResponse, message.getMessageProperties());
} }
} }

View File

@@ -117,19 +117,18 @@ public class AmqpControllerAuthentication {
/** /**
* Performs authentication with the security token. * Performs authentication with the security token.
* *
* @param secruityToken * @param securityToken
* the authentication request object * the authentication request object
* @return the authentication object * @return the authentication object
*/ */
public Authentication doAuthenticate(final TenantSecurityToken secruityToken) { public Authentication doAuthenticate(final TenantSecurityToken securityToken) {
resolveTenant(secruityToken); resolveTenant(securityToken);
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null); PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(null, null);
for (final PreAuthentificationFilter filter : filterChain) { for (final PreAuthentificationFilter filter : filterChain) {
final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, secruityToken); final PreAuthenticatedAuthenticationToken authenticationRest = createAuthentication(filter, securityToken);
if (authenticationRest != null) { if (authenticationRest != null) {
authentication = authenticationRest; authentication = authenticationRest;
authentication.setDetails(new TenantAwareAuthenticationDetails(secruityToken.getTenant(), true)); authentication.setDetails(new TenantAwareAuthenticationDetails(securityToken.getTenant(), true));
break; break;
} }
} }

View File

@@ -109,10 +109,10 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
LOG.debug("targetAssignDistributionSet retrieved for controller {}. I will forward it to DMF broker.", LOG.debug("targetAssignDistributionSet retrieved for controller {}. I will forward it to DMF broker.",
assignedEvent.getControllerId()); assignedEvent.getControllerId());
targetManagement.findTargetByControllerID(assignedEvent.getControllerId()).ifPresent(target -> sendUpdateMessageToTarget(assignedEvent.getTenant(), targetManagement.findTargetByControllerID(assignedEvent.getControllerId())
target, .ifPresent(target -> sendUpdateMessageToTarget(assignedEvent.getTenant(), target,
assignedEvent.getActionId(), assignedEvent.getModules())); assignedEvent.getActionId(), assignedEvent.getModules()));
} }
void sendUpdateMessageToTarget(final String tenant, final Target target, final Long actionId, void sendUpdateMessageToTarget(final String tenant, final Target target, final Long actionId,
@@ -165,7 +165,6 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
if (!IpUtil.isAmqpUri(address)) { if (!IpUtil.isAmqpUri(address)) {
return; return;
} }
final Message message = getMessageConverter().toMessage(actionId, final Message message = getMessageConverter().toMessage(actionId,
createConnectorMessageProperties(tenant, controllerId, EventTopic.CANCEL_DOWNLOAD)); createConnectorMessageProperties(tenant, controllerId, EventTopic.CANCEL_DOWNLOAD));

View File

@@ -96,7 +96,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
* *
* @return a message if <null> no message is send back to sender * @return a message if <null> no message is send back to sender
*/ */
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue}", containerFactory = "listenerContainerFactory") @RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue:dmf_receiver}", containerFactory = "listenerContainerFactory")
public Message onMessage(final Message message, @Header(MessageHeaderKey.TYPE) final String type, public Message onMessage(final Message message, @Header(MessageHeaderKey.TYPE) final String type,
@Header(MessageHeaderKey.TENANT) final String tenant) { @Header(MessageHeaderKey.TENANT) final String tenant) {
return onMessage(message, type, tenant, getRabbitTemplate().getConnectionFactory().getVirtualHost()); return onMessage(message, type, tenant, getRabbitTemplate().getConnectionFactory().getVirtualHost());
@@ -169,7 +169,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
final String replyTo = message.getMessageProperties().getReplyTo(); final String replyTo = message.getMessageProperties().getReplyTo();
if (StringUtils.isEmpty(replyTo)) { if (StringUtils.isEmpty(replyTo)) {
logAndThrowMessageError(message, "No ReplyTo was set for the createThing TenantAwareEvent."); logAndThrowMessageError(message, "No ReplyTo was set for the createThing message.");
} }
final URI amqpUri = IpUtil.createAmqpUri(virtualHost, replyTo); final URI amqpUri = IpUtil.createAmqpUri(virtualHost, replyTo);
@@ -180,8 +180,10 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
} }
private void lookIfUpdateAvailable(final Target target) { private void lookIfUpdateAvailable(final Target target) {
final Optional<Action> actionOptional = controllerManagement final Optional<Action> actionOptional = controllerManagement
.findOldestActiveActionByTarget(target.getControllerId()); .findOldestActiveActionByTarget(target.getControllerId());
if (!actionOptional.isPresent()) { if (!actionOptional.isPresent()) {
return; return;
} }
@@ -206,7 +208,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
* the topic of the event. * the topic of the event.
*/ */
private void handleIncomingEvent(final Message message, final EventTopic topic) { private void handleIncomingEvent(final Message message, final EventTopic topic) {
switch (topic) { switch (topic) {
case UPDATE_ACTION_STATUS: case UPDATE_ACTION_STATUS:
updateActionStatus(message); updateActionStatus(message);
@@ -294,11 +295,11 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
private Status hanldeCancelRejectedState(final Message message, final Action action) { private Status hanldeCancelRejectedState(final Message message, final Action action) {
if (action.isCancelingOrCanceled()) { if (action.isCancelingOrCanceled()) {
return Status.CANCEL_REJECTED; return Status.CANCEL_REJECTED;
} else {
logAndThrowMessageError(message,
"Cancel recjected message is not allowed, if action is on state: " + action.getStatus());
return null;
} }
logAndThrowMessageError(message,
"Cancel rejected message is not allowed, if action is on state: " + action.getStatus());
return null;
} }
private void updateLastPollTime(final Target target) { private void updateLastPollTime(final Target target) {
@@ -321,13 +322,10 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
@SuppressWarnings("squid:S3655") @SuppressWarnings("squid:S3655")
private Action checkActionExist(final Message message, final ActionUpdateStatus actionUpdateStatus) { private Action checkActionExist(final Message message, final ActionUpdateStatus actionUpdateStatus) {
final Long actionId = actionUpdateStatus.getActionId(); final Long actionId = actionUpdateStatus.getActionId();
LOG.debug("Target notifies intermediate about action {} with status {}.", actionId, LOG.debug("Target notifies intermediate about action {} with status {}.", actionId,
actionUpdateStatus.getActionStatus()); actionUpdateStatus.getActionStatus());
if (actionId == null) {
logAndThrowMessageError(message, "Invalid message no action id");
}
final Optional<Action> findActionWithDetails = controllerManagement.findActionWithDetails(actionId); final Optional<Action> findActionWithDetails = controllerManagement.findActionWithDetails(actionId);
if (!findActionWithDetails.isPresent()) { if (!findActionWithDetails.isPresent()) {
logAndThrowMessageError(message, logAndThrowMessageError(message,

View File

@@ -8,11 +8,10 @@
*/ */
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.validation.constraints.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpRejectAndDontRequeueException; import org.springframework.amqp.AmqpRejectAndDontRequeueException;
@@ -20,6 +19,7 @@ import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper; import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
import org.springframework.amqp.support.converter.MessageConversionException;
import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.amqp.support.converter.MessageConverter;
/** /**
@@ -58,45 +58,27 @@ public class BaseAmqpService {
* @return the converted object * @return the converted object
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T convertMessage(final Message message, final Class<T> clazz) { public <T> T convertMessage(@NotNull final Message message, final Class<T> clazz) {
if (isMessageBodyEmpty(message)) { checkMessageBody(message);
return null;
}
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME, message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME,
clazz.getName()); clazz.getName());
return (T) rabbitTemplate.getMessageConverter().fromMessage(message); return (T) rabbitTemplate.getMessageConverter().fromMessage(message);
} }
private static boolean isMessageBodyEmpty(final Message message) { protected MessageConverter getMessageConverter() {
return message == null || message.getBody() == null || message.getBody().length == 0;
}
/**
* Is needed to convert a incoming message to is originally list object
* type.
*
* @param message
* the message to convert.
* @param clazz
* the class of the list content.
* @return the list of converted objects
*/
@SuppressWarnings("unchecked")
public <T> List<T> convertMessageList(final Message message, final Class<T> clazz) {
if (isMessageBodyEmpty(message)) {
return Collections.emptyList();
}
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME,
ArrayList.class.getName());
message.getMessageProperties().getHeaders().put(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME,
clazz.getName());
return (List<T>) rabbitTemplate.getMessageConverter().fromMessage(message);
}
public MessageConverter getMessageConverter() {
return rabbitTemplate.getMessageConverter(); return rabbitTemplate.getMessageConverter();
} }
private static boolean isMessageBodyEmpty(final Message message) {
return message.getBody() == null || message.getBody().length == 0;
}
protected void checkMessageBody(@NotNull final Message message) {
if (isMessageBodyEmpty(message)) {
throw new MessageConversionException("Message body cannot be null");
}
}
protected String getStringHeaderKey(final Message message, final String key, final String errorMessageIfNull) { protected String getStringHeaderKey(final Message message, final String key, final String errorMessageIfNull) {
final Map<String, Object> header = message.getMessageProperties().getHeaders(); final Map<String, Object> header = message.getMessageProperties().getHeaders();
final Object value = header.get(key); final Object value = header.get(key);

View File

@@ -8,23 +8,33 @@
*/ */
package org.eclipse.hawkbit; package org.eclipse.hawkbit;
import java.util.concurrent.ArrayBlockingQueue; import java.net.MalformedURLException;
import java.util.concurrent.BlockingQueue; import java.net.URL;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.RabbitMqSetupService.AlivenessException;
import org.eclipse.hawkbit.amqp.AmqpProperties; import org.eclipse.hawkbit.amqp.AmqpProperties;
import org.eclipse.hawkbit.amqp.AmqpSenderService; import org.eclipse.hawkbit.api.HostnameResolver;
import org.eclipse.hawkbit.amqp.DefaultAmqpSenderService; import org.eclipse.hawkbit.integration.listener.DeadletterListener;
import org.eclipse.hawkbit.integration.listener.ReplyToListener;
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder; import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.junit.BrokerRunning;
import org.springframework.amqp.rabbit.test.RabbitListenerTest;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -33,14 +43,21 @@ import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService; import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.base.Throwables;
/** /**
* *
*/ */
@Configuration @Configuration
@EnableConfigurationProperties({ AmqpProperties.class }) @EnableConfigurationProperties({ AmqpProperties.class })
@RabbitListenerTest
public class AmqpTestConfiguration { public class AmqpTestConfiguration {
private static final Logger LOG = LoggerFactory.getLogger(AmqpTestConfiguration.class);
public static final String REPLY_TO_EXCHANGE = "reply.queue";
public static final String REPLY_TO_QUEUE = "reply_queue";
/** /**
* @return the {@link SystemSecurityContext} singleton bean which make it * @return the {@link SystemSecurityContext} singleton bean which make it
* accessible in beans which cannot access the service directly, * accessible in beans which cannot access the service directly,
@@ -51,56 +68,15 @@ public class AmqpTestConfiguration {
return SystemSecurityContextHolder.getInstance(); return SystemSecurityContextHolder.getInstance();
} }
/**
* Method to set the Jackson2JsonMessageConverter.
*
* @return the Jackson2JsonMessageConverter
*/
@Bean @Bean
public MessageConverter jsonMessageConverter() { Executor asyncExecutor() {
return new Jackson2JsonMessageConverter(); return new DelegatingSecurityContextExecutorService(Executors.newSingleThreadExecutor());
}
/**
* Create default amqp sender service bean.
*
* @param rabbitTemplate
*
* @return the default amqp sender service bean
*/
@Bean
@Autowired
public AmqpSenderService amqpSenderServiceBean(final RabbitTemplate rabbitTemplate) {
return new DefaultAmqpSenderService(rabbitTemplate);
}
/**
* @return ExecutorService with security context availability in thread
* execution..
*/
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean
public Executor asyncExecutor() {
return new DelegatingSecurityContextExecutorService(threadPoolExecutor());
}
/**
* @return central ThreadPoolExecutor for general purpose multi threaded
* operations. Tries an orderly shutdown when destroyed.
*/
private ThreadPoolExecutor threadPoolExecutor() {
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(10);
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS,
blockingQueue, new ThreadFactoryBuilder().setNameFormat("central-executor-pool-%d").build());
return threadPoolExecutor;
} }
/** /**
* @return {@link TaskExecutor} for task execution * @return {@link TaskExecutor} for task execution
*/ */
@Bean @Bean
@ConditionalOnMissingBean
public TaskExecutor taskExecutor() { public TaskExecutor taskExecutor() {
return new ConcurrentTaskExecutor(asyncExecutor()); return new ConcurrentTaskExecutor(asyncExecutor());
} }
@@ -110,7 +86,6 @@ public class AmqpTestConfiguration {
* {@link #threadPoolTaskScheduler()}. * {@link #threadPoolTaskScheduler()}.
*/ */
@Bean @Bean
@ConditionalOnMissingBean
public ScheduledExecutorService scheduledExecutorService() { public ScheduledExecutorService scheduledExecutorService() {
return threadPoolTaskScheduler().getScheduledExecutor(); return threadPoolTaskScheduler().getScheduledExecutor();
} }
@@ -119,8 +94,83 @@ public class AmqpTestConfiguration {
* @return {@link ThreadPoolTaskScheduler} for scheduled operations. * @return {@link ThreadPoolTaskScheduler} for scheduled operations.
*/ */
@Bean @Bean
@ConditionalOnMissingBean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() { public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler(); return new ThreadPoolTaskScheduler();
} }
@Bean
public HostnameResolver hostnameResolver(final HawkbitServerProperties serverProperties) {
return () -> {
try {
return new URL(serverProperties.getUrl());
} catch (final MalformedURLException e) {
throw Throwables.propagate(e);
}
};
}
@Bean(name = "dmfClient")
public RabbitTemplate dmfClient(ConnectionFactory connectionFactory) {
final RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(new Jackson2JsonMessageConverter());
template.setReceiveTimeout(TimeUnit.SECONDS.toMillis(5));
return template;
}
@Bean
public Queue replyToQueue() {
return new Queue(REPLY_TO_QUEUE, false, false, true);
}
@Bean
public FanoutExchange replyToExchange() {
return new FanoutExchange(REPLY_TO_EXCHANGE, false, true);
}
@Bean
public Binding bindQueueToReplyToExchange() {
return BindingBuilder.bind(replyToQueue()).to(replyToExchange());
}
@Bean
public DeadletterListener deadletterListener() {
return new DeadletterListener();
}
@Bean
public ReplyToListener replyToListener() {
return new ReplyToListener();
}
@Bean
public ConnectionFactory rabbitConnectionFactory(final RabbitMqSetupService rabbitmqSetupService)
throws AlivenessException {
final CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setHost(rabbitmqSetupService.getHostname());
factory.setPort(5672);
factory.setUsername(rabbitmqSetupService.getUsername());
factory.setPassword(rabbitmqSetupService.getPassword());
try {
factory.setVirtualHost(rabbitmqSetupService.createVirtualHost());
} catch (final Exception e) {
Throwables.propagateIfInstanceOf(e, AlivenessException.class);
LOG.error("Cannot create virtual host {}", e.getMessage());
}
return factory;
}
@Bean
public RabbitMqSetupService rabbitmqSetupService(RabbitProperties properties) {
return new RabbitMqSetupService(properties);
}
@Bean
public BrokerRunning brokerRunning(RabbitMqSetupService rabbitmqSetupService) {
final BrokerRunning brokerRunning = BrokerRunning.isRunning();
brokerRunning.setHostName(rabbitmqSetupService.getHostname());
brokerRunning.getConnectionFactory().setUsername(rabbitmqSetupService.getUsername());
brokerRunning.getConnectionFactory().setPassword(rabbitmqSetupService.getPassword());
return brokerRunning;
}
} }

View File

@@ -0,0 +1,117 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.UUID;
import javax.annotation.PreDestroy;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Throwables;
import com.rabbitmq.http.client.Client;
import com.rabbitmq.http.client.domain.UserPermissions;
/**
* Creates and deletes a new virtual host if the rabbit mq management api is
* available.
*
*
*/
public class RabbitMqSetupService {
private static Client rabbitmqHttpClient;
private String virtualHost;
private final String hostname;
private String username;
private String password;
public RabbitMqSetupService(RabbitProperties properties) {
hostname = properties.getHost();
username = properties.getUsername();
if (StringUtils.isEmpty(username)) {
username = "guest";
}
password = properties.getPassword();
if (StringUtils.isEmpty(password)) {
password = "guest";
}
}
private Client getRabbitmqHttpClient() {
if (rabbitmqHttpClient == null) {
try {
rabbitmqHttpClient = new Client("http://" + getHostname() + ":15672/api/", getUsername(),
getPassword());
} catch (MalformedURLException | URISyntaxException e) {
throw Throwables.propagate(e);
}
}
return rabbitmqHttpClient;
}
String createVirtualHost() throws JsonProcessingException, AlivenessException {
if (!getRabbitmqHttpClient().alivenessTest("/")) {
throw new AlivenessException(getHostname());
}
virtualHost = UUID.randomUUID().toString();
getRabbitmqHttpClient().createVhost(virtualHost);
getRabbitmqHttpClient().updatePermissions(virtualHost, getUsername(), createUserPermissionsFullAccess());
return virtualHost;
}
@PreDestroy
void deleteVirtualHost() {
if (StringUtils.isEmpty(virtualHost)) {
return;
}
getRabbitmqHttpClient().deleteVhost(virtualHost);
}
public String getHostname() {
return hostname;
}
public String getPassword() {
return password;
}
public String getUsername() {
return username;
}
private UserPermissions createUserPermissionsFullAccess() {
final UserPermissions permissions = new UserPermissions();
permissions.setVhost(virtualHost);
permissions.setRead(".*");
permissions.setConfigure(".*");
permissions.setWrite(".*");
return permissions;
}
static class AlivenessException extends Exception {
private static final long serialVersionUID = 1L;
public AlivenessException(String hostname) {
super("Aliveness test failed for " + hostname
+ ":15672 guest/quest; rabbit mq management api not available");
}
}
}

View File

@@ -277,10 +277,10 @@ public class AmqpMessageHandlerServiceTest {
@Test @Test
@Description("Tests the update of an action of a target without a exist action id") @Description("Tests the update of an action of a target without a exist action id")
public void updateActionStatusWithoutActionId() { public void updateActionStatusWithoutActionId() {
when(controllerManagementMock.findActionWithDetails(any())).thenReturn(Optional.empty());
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT); final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name()); messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(1L, ActionStatus.DOWNLOAD);
actionUpdateStatus.setActionStatus(ActionStatus.DOWNLOAD);
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(actionUpdateStatus, final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(actionUpdateStatus,
messageProperties); messageProperties);
@@ -442,10 +442,8 @@ public class AmqpMessageHandlerServiceTest {
} }
private ActionUpdateStatus createActionUpdateStatus(final ActionStatus status, final Long id) { private ActionUpdateStatus createActionUpdateStatus(final ActionStatus status, final Long id) {
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(id, status);
actionUpdateStatus.setActionId(id);
actionUpdateStatus.setSoftwareModuleId(Long.valueOf(2)); actionUpdateStatus.setSoftwareModuleId(Long.valueOf(2));
actionUpdateStatus.setActionStatus(status);
return actionUpdateStatus; return actionUpdateStatus;
} }

View File

@@ -9,12 +9,14 @@
package org.eclipse.hawkbit.amqp; package org.eclipse.hawkbit.amqp;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import java.util.ArrayList; import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
import java.util.List;
import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus; import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -24,6 +26,7 @@ import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConversionException;
import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Features;
@@ -49,57 +52,68 @@ public class BaseAmqpServiceTest {
@Test @Test
@Description("Verify that the message conversion works") @Description("Verify that the message conversion works")
public void convertMessageTest() { public void convertMessageTest() {
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); final ActionUpdateStatus actionUpdateStatus = createActionStatus();
actionUpdateStatus.setActionId(1L);
actionUpdateStatus.setSoftwareModuleId(2L);
actionUpdateStatus.addMessage("Message 1");
actionUpdateStatus.addMessage("Message 2");
final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus, final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatus,
new MessageProperties()); createJsonProperties());
ActionUpdateStatus convertedActionUpdateStatus = baseAmqpService.convertMessage(message, final ActionUpdateStatus convertedActionUpdateStatus = baseAmqpService.convertMessage(message,
ActionUpdateStatus.class); ActionUpdateStatus.class);
assertThat(convertedActionUpdateStatus).as("Converted Action Status is wrong") assertThat(convertedActionUpdateStatus).isEqualToComparingFieldByField(actionUpdateStatus);
.isEqualToComparingFieldByField(actionUpdateStatus);
convertedActionUpdateStatus = baseAmqpService.convertMessage(null, ActionUpdateStatus.class);
assertThat(convertedActionUpdateStatus).as("Converted Object should be null when message is null").isNull();
convertedActionUpdateStatus = baseAmqpService.convertMessage(new Message(null, new MessageProperties()),
ActionUpdateStatus.class);
assertThat(convertedActionUpdateStatus).as("Converted Object should be null when message body is null")
.isNull();
} }
@Test @Test
@Description("Verify that a conversion of a list from a message works") @Description("Tests invalid null message content")
public void convertMessageListTest() { @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
final List<ActionUpdateStatus> actionUpdateStatusList = new ArrayList<>(); public void convertMessageWithNullContent() {
for (int i = 0; i < 5; i++) { try {
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(); baseAmqpService.convertMessage(new Message(null, createJsonProperties()), ActionUpdateStatus.class);
actionUpdateStatus.setActionId(Long.valueOf(i)); fail("Expected MessageConversionException for inavlid JSON");
actionUpdateStatus.setSoftwareModuleId(Long.valueOf(i)); } catch (final MessageConversionException e) {
actionUpdateStatusList.add(actionUpdateStatus); // expected
} }
final Message message = rabbitTemplate.getMessageConverter().toMessage(actionUpdateStatusList, }
new MessageProperties());
List<ActionUpdateStatus> convertedActionUpdateStatus = baseAmqpService.convertMessageList(message,
ActionUpdateStatus.class);
assertThat(convertedActionUpdateStatus).as("Converted Action Status list is wrong") @Test
.hasSameClassAs(actionUpdateStatusList); @Description("Tests invalid empty message content")
assertThat(convertedActionUpdateStatus).as("Converted Action Status list is wrong") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
.hasSameSizeAs(actionUpdateStatusList); public void updateActionStatusWithEmptyContent() {
try {
baseAmqpService.convertMessage(new Message("".getBytes(), createJsonProperties()),
ActionUpdateStatus.class);
fail("Expected MessageConversionException for inavlid JSON");
} catch (final MessageConversionException e) {
// expected
}
}
convertedActionUpdateStatus = baseAmqpService.convertMessageList(null, ActionUpdateStatus.class); private MessageProperties createJsonProperties() {
assertThat(convertedActionUpdateStatus).as("Converted list should be empty when message is null").isEmpty(); final MessageProperties messageProperties = new MessageProperties();
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
return messageProperties;
}
convertedActionUpdateStatus = baseAmqpService.convertMessageList(new Message(null, new MessageProperties()), @Test
ActionUpdateStatus.class); @Description("Tests invalid json message content")
assertThat(convertedActionUpdateStatus).as("Converted list should be empty when message body is null") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
.isEmpty(); public void updateActionStatusWithInvalidJsonContent() {
try {
baseAmqpService.convertMessage(new Message("Invalid Json".getBytes(), createJsonProperties()),
ActionUpdateStatus.class);
fail("Expected MessageConversionException for inavlid JSON");
} catch (final MessageConversionException e) {
// expected
}
}
private ActionUpdateStatus createActionStatus() {
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(1L, ActionStatus.RUNNING);
actionUpdateStatus.setSoftwareModuleId(2L);
actionUpdateStatus.addMessage("Message 1");
actionUpdateStatus.addMessage("Message 2");
return actionUpdateStatus;
} }
} }

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.integration;
import static java.util.concurrent.TimeUnit.SECONDS;
import org.eclipse.hawkbit.AmqpTestConfiguration;
import org.eclipse.hawkbit.amqp.DmfApiConfiguration;
import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration;
import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.junit.BrokerRunning;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import com.jayway.awaitility.Awaitility;
import com.jayway.awaitility.core.ConditionFactory;
@SpringApplicationConfiguration(classes = { RepositoryApplicationConfiguration.class, AmqpTestConfiguration.class,
DmfApiConfiguration.class })
// Dirty context is necessary to create a new vhost and recreate all necessary
// beans after every test class.
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public abstract class AbstractAmqpIntegrationTest extends AbstractIntegrationTest {
@Rule
@Autowired
public BrokerRunning brokerRunning;
@Autowired
@Qualifier("dmfClient")
private RabbitTemplate dmfClient;
@Before
public void setup() {
dmfClient.setExchange(getExchange());
}
protected abstract String getExchange();
protected RabbitTemplate getDmfClient() {
return dmfClient;
}
protected ConditionFactory createConditionFactory() {
return Awaitility.await().atMost(2, SECONDS);
}
protected Message createMessage(final Object payload, final MessageProperties messageProperties) {
if (payload == null) {
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
return new Message(null, messageProperties);
}
return getDmfClient().getMessageConverter().toMessage(payload, messageProperties);
}
}

View File

@@ -0,0 +1,443 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.integration;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken;
import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.Before;
import org.junit.Test;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.context.annotation.Description;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Device Management Federation API")
@Stories("Amqp Authentication Message Handler")
public class AmqpAuthenticationMessageHandlerIntegrationTest extends AbstractAmqpIntegrationTest {
private static final String TARGET_SECRUITY_TOKEN = "12345";
private static final String TARGET_TOKEN_HEADER = "TargetToken " + TARGET_SECRUITY_TOKEN;
private static final String TENANT_EXIST = "DEFAULT";
private static final String TARGET = "NewDmfTarget";
@Before
public void testSetup() {
enableTargetTokenAuthentification();
}
@Test
@Description("Tests wrong content type. This message is invalid and should not be requeued. Additional the receive message is null")
public void wrongContentType() {
final Message createAndSendMessage = getDmfClient()
.sendAndReceive(new Message("".getBytes(), new MessageProperties()));
assertThat(createAndSendMessage).isNull();
}
@Test
@Description("Tests a null message. This message is invalid and should not be requeued. Additional the receive message is null")
public void securityTokenIsNull() {
final Message createAndSendMessage = sendAndReceiveAuthenticationMessage(null);
assertThat(createAndSendMessage).isNull();
}
@Test
@Description("Tenant in the message is null. This message is invalid and should not be requeued. Additional the receive message is null")
public void securityTokenTenantIsNull() {
final TenantSecurityToken securityToken = createTenantSecurityToken(null, TARGET,
FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN));
final Message createAndSendMessage = sendAndReceiveAuthenticationMessage(securityToken);
assertThat(createAndSendMessage).isNull();
}
@Test
@Description("Target in the message is null.This message is invalid and should not requeued. Additional the receive message is null")
public void securityTokenFileResourceIsNull() {
enableAnonymousAuthentification();
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, null);
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND, null);
}
@Test
@Description("Verify that login fails if the given credential not match.")
public void loginFailedBadCredentials() {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED,
false);
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN));
final Message createAndSendMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(createAndSendMessage, HttpStatus.FORBIDDEN, "Login failed");
}
@Test
@Description("Verify that the receive message contains a 404 code,if the artifact could not found")
public void fileResourceGetSha1InSecurityTokenIsNull() {
enableAnonymousAuthentification();
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceBySha1(null));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
"Artifact for resource FileResource [sha1=null, artifactId=null, filename=null]not found ");
}
@Test
@Description("Verify that the receive message contains a 404 code , if the distributionSet is not assigned to the target")
public void artifactForFileResourceSHA1FoundByTargetIdTargetExistsButIsNotAssigned() {
final Target target = createTarget(TARGET);
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final String sha1Hash = artifacts.get(0).getSha1Hash();
final TenantSecurityToken securityToken = createTenantSecurityToken(target.getTenant(), target.getId(), null,
FileResource.createFileResourceBySha1(sha1Hash));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
"Artifact for resource FileResource [sha1=" + sha1Hash + ", artifactId=null, filename=null]not found ");
}
@Test
@Description("Verify that the receive message contains a 404 code, if there is no artifact for the given sha1")
public void artifactForFileResourceSHA1NotFound() {
enableAnonymousAuthentification();
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceBySha1(TARGET_SECRUITY_TOKEN));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
"Artifact for resource FileResource [sha1=12345, artifactId=null, filename=null]not found ");
}
@Test
@Description("Verify that the receive message contains a 404 code, if there is no existing target for the given controller id")
public void artifactForFileResourceSHA1FoundTargetNotExists() {
enableAnonymousAuthentification();
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final String sha1Hash = artifacts.get(0).getSha1Hash();
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceBySha1(sha1Hash));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
"Artifact for resource FileResource [sha1=" + sha1Hash + ", artifactId=null, filename=null]not found ");
}
@Test
@Description("Verify that the receive message contains a 404 code, if there is no existing artifact for the target")
public void artifactForFileResourceSHA1FoundTargetExistsButNotAssigned() {
createTarget(TARGET);
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceBySha1(artifacts.get(0).getSha1Hash()));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
"Artifact for resource FileResource [sha1=2f532b93ed23b4341a81dc9b1ee8a1c44b5526ab, artifactId=null, filename=null]not found ");
}
@Test
@Description("Verify that the receive message contains a 200 code and a artifact for the existing controller id ")
public void artifactForFileResourceSHA1FoundTargetExistsIsAssigned() {
createTarget(TARGET);
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final Artifact artifact = artifacts.get(0);
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
deploymentManagement.assignDistributionSet(distributionSet.getId(),
Arrays.asList(new TargetWithActionType(TARGET)));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyOkResult(returnMessage, artifact);
}
@Test
@Description("Verify that the receive message contains a 200 code and a artifact for the existing target id ")
public void artifactForFileResourceSHA1FoundByTargetIdTargetExistsIsAssigned() {
final Target target = createTarget(TARGET);
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final Artifact artifact = artifacts.get(0);
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, target.getId(), null,
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
deploymentManagement.assignDistributionSet(distributionSet.getId(),
Arrays.asList(new TargetWithActionType(TARGET)));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyOkResult(returnMessage, artifact);
}
@Test
@Description("Verify that the receive message contains a 200 code and a artifact without a controller id (anonymous enabled)")
public void anonymousAuthentification() {
enableAnonymousAuthentification();
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final Artifact artifact = artifacts.get(0);
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, null, null,
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyOkResult(returnMessage, artifact);
}
@Test
@Description("Artifact is downloaded anonymous as there is not target id or controller id assigned to the target.")
public void targetTokenAuthentification() {
createTarget(TARGET);
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final Artifact artifact = artifacts.get(0);
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceBySha1(artifact.getSha1Hash()));
deploymentManagement.assignDistributionSet(distributionSet.getId(),
Arrays.asList(new TargetWithActionType(TARGET)));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyOkResult(returnMessage, artifact);
}
@Test
@Description("Verify that the receive message contains a 404, if there is no artifact to the given filename")
public void artifactForFileResourceFileNameNotFound() {
enableAnonymousAuthentification();
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceByFilename("Test.txt"));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
"Artifact for resource FileResource [sha1=null, artifactId=null, filename=Test.txt]not found ");
}
@Test
@Description("Verify that the receive message contains a 404, if there is no distribution set assigned to the target")
public void artifactForFileResourceFileNameFoundTargetExistsButNotAssigned() {
createTarget(TARGET);
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.createFileResourceByFilename(artifacts.get(0).getFilename()));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
"Artifact for resource FileResource [sha1=null, artifactId=null, filename=filename0]not found ");
}
@Test
@Description("Verify that the receive message contains a 404, if there is no exisiting target")
public void artifactForFileResourceArtifactIdFoundTargetNotExists() {
enableAnonymousAuthentification();
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final FileResource fileResource = FileResource.createFileResourceByArtifactId(artifacts.get(0).getId());
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource);
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND, "Artifact for resource FileResource [sha1=null, artifactId="
+ artifacts.get(0).getId() + ", filename=null]not found ");
}
@Test
@Description("Verify that the receive message contains a 200 and a artifact for the given artifact id")
public void artifactForFileResourceArtifactIdFoundTargetExistsIsAssigned() {
createTarget(TARGET);
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final Artifact artifact = artifacts.get(0);
final FileResource fileResource = FileResource.createFileResourceByArtifactId(artifact.getId());
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource);
deploymentManagement.assignDistributionSet(distributionSet.getId(),
Arrays.asList(new TargetWithActionType(TARGET)));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyOkResult(returnMessage, artifact);
}
@Test
@Description("Verify that the receive message contains a 404, if there is no artifact to the given softwareModuleFilename")
public void artifactForFileResourceSoftwareModuleFilenameNotFound() {
enableAnonymousAuthentification();
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET,
FileResource.softwareModuleFilename(1L, "Test.txt"));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
"Artifact for resource FileResource [sha1=null, artifactId=null, filename=null]not found ");
}
@Test
@Description("Verify that the receive message contains a 404, if there is no existing target for the file resource")
public void artifactForFileResourceSoftwareModuleFilenameFoundTargetNotExists() {
enableAnonymousAuthentification();
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final SoftwareModule softwareModule = distributionSet.getModules().stream().findFirst().get();
final Artifact artifact = findArtifactOfSoftwareModule(artifacts, softwareModule);
final FileResource fileResource = FileResource.softwareModuleFilename(softwareModule.getId(),
softwareModule.getArtifact(artifact.getId()).get().getFilename());
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource);
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyResult(returnMessage, HttpStatus.NOT_FOUND,
"Artifact for resource FileResource [sha1=null, artifactId=null, filename=null]not found ");
}
@Test
@Description("Verify that the receive message contains a 200 and a artifact, if there is a existing artifct fpt the for the given softwareModuleFilename")
public void artifactForFileResourceSoftwareModuleFilenameFoundTargetExistsIsAssigned() {
createTarget(TARGET);
final DistributionSet distributionSet = createDistributionSet();
final List<Artifact> artifacts = createArtifacts(distributionSet);
final SoftwareModule softwareModule = distributionSet.getModules().stream().findFirst().get();
final Artifact artifact = findArtifactOfSoftwareModule(artifacts, softwareModule);
final FileResource fileResource = FileResource.softwareModuleFilename(softwareModule.getId(),
softwareModule.getArtifact(artifact.getId()).get().getFilename());
final TenantSecurityToken securityToken = createTenantSecurityToken(TENANT_EXIST, TARGET, fileResource);
deploymentManagement.assignDistributionSet(distributionSet.getId(),
Arrays.asList(new TargetWithActionType(TARGET)));
final Message returnMessage = sendAndReceiveAuthenticationMessage(securityToken);
verifyOkResult(returnMessage, artifact);
}
private void verifyOkResult(Message returnMessage, Artifact artifact) {
final DownloadResponse convertedMessage = verifyResult(returnMessage, HttpStatus.OK, null);
assertThat(convertedMessage.getDownloadUrl()).isNotNull();
assertThat(convertedMessage.getArtifact()).isNotNull();
assertThat(convertedMessage.getArtifact().getHashes().getSha1()).isEqualTo(artifact.getSha1Hash());
}
private void enableAnonymousAuthentification() {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED,
true);
tenantConfigurationManagement.addOrUpdateConfiguration(
TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED, false);
}
private void enableTargetTokenAuthentification() {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.ANONYMOUS_DOWNLOAD_MODE_ENABLED,
false);
tenantConfigurationManagement.addOrUpdateConfiguration(
TenantConfigurationKey.AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED, true);
}
private Target createTarget(final String controllerId) {
return targetManagement.createTarget(
entityFactory.target().create().controllerId(controllerId).securityToken(TARGET_SECRUITY_TOKEN));
}
private TenantSecurityToken createTenantSecurityToken(final String tenant, final String controllerId,
final FileResource fileResource) {
final TenantSecurityToken tenantSecurityToken = new TenantSecurityToken(tenant, controllerId, fileResource);
tenantSecurityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, TARGET_TOKEN_HEADER);
return tenantSecurityToken;
}
private TenantSecurityToken createTenantSecurityToken(final String tenant, final Long targetId,
final String controllerId, final FileResource fileResource) {
final TenantSecurityToken tenantSecurityToken = new TenantSecurityToken(tenant, null, controllerId, targetId,
fileResource);
tenantSecurityToken.putHeader(TenantSecurityToken.AUTHORIZATION_HEADER, TARGET_TOKEN_HEADER);
return tenantSecurityToken;
}
private DistributionSet createDistributionSet() {
return testdataFactory.createDistributionSet("one");
}
private List<Artifact> createArtifacts(final DistributionSet distributionSet) {
final List<Artifact> artifacts = new ArrayList<>();
for (final SoftwareModule module : distributionSet.getModules()) {
artifacts.addAll(testdataFactory.createArtifacts(module.getId()));
}
return artifacts;
}
private DownloadResponse verifyResult(final Message returnMessage, final HttpStatus expectedStatus,
final String expectedMessage) {
final DownloadResponse convertedMessage = (DownloadResponse) getDmfClient().getMessageConverter()
.fromMessage(returnMessage);
assertThat(convertedMessage.getResponseCode()).isEqualTo(expectedStatus.value());
if (!StringUtils.isEmpty(expectedMessage)) {
assertThat(convertedMessage.getMessage()).isEqualTo(expectedMessage);
}
return convertedMessage;
}
private Message sendAndReceiveAuthenticationMessage(final TenantSecurityToken securityToken) {
return getDmfClient().sendAndReceive(createMessage(securityToken, new MessageProperties()));
}
private Artifact findArtifactOfSoftwareModule(final List<Artifact> artifacts, final SoftwareModule softwareModule) {
return artifacts.stream().filter(space -> space.getSoftwareModule().getId().equals(softwareModule.getId()))
.findFirst().get();
}
@Override
protected String getExchange() {
return AmqpSettings.AUTHENTICATION_EXCHANGE;
}
}

View File

@@ -0,0 +1,116 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.integration;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.Callable;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.junit.Test;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Device Management Federation API")
@Stories("Amqp Message Dispatcher Service")
public class AmqpMessageDispatcherServiceIntegrationTest extends AmqpServiceIntegrationTest {
@Test
@Description("Verify that a distribution assignment send a download and install message.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) })
public void sendDownloadAndInstallStatus() {
registerTargetAndAssignDistributionSet();
createAndSendTarget(TENANT_EXIST);
waitUntilTargetStatusIsPending();
assertDownloadAndInstallMessage(getDistributionSet().getModules());
}
@Test
@Description("Verify that a distribution assignment multiple times send cancel and assign events with right softwaremodules")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 3) })
public void assignDistributionSetMultipleTimes() {
final DistributionSetAssignmentResult assignmentResult = registerTargetAndAssignDistributionSet();
final DistributionSet distributionSet2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
registerTargetAndAssignDistributionSet(distributionSet2.getId(), TargetUpdateStatus.PENDING,
getDistributionSet().getModules());
assertCancelActionMessage(assignmentResult.getActions().get(0));
createAndSendTarget(TENANT_EXIST);
waitUntilTargetStatusIsPending();
assertCancelActionMessage(assignmentResult.getActions().get(0));
}
@Test
@Description("Verify that a cancel assignment send a cancel message.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) })
public void sendCancelStatus() {
final Long actionId = registerTargetAndCancelActionId();
createAndSendTarget(TENANT_EXIST);
waitUntilTargetStatusIsPending();
assertCancelActionMessage(actionId);
}
private void waitUntilTargetStatusIsPending() {
waitUntil(() -> {
final Optional<Target> findTargetByControllerID = targetManagement
.findTargetByControllerID(REGISTER_TARGET);
return findTargetByControllerID.isPresent() && TargetUpdateStatus.PENDING
.equals(findTargetByControllerID.get().getUpdateStatus());
});
}
private void waitUntil(final Callable<Boolean> callable) {
createConditionFactory().until(() -> {
return securityRule.runAsPrivileged(callable);
});
}
private void createAndSendTarget(final String tenant) {
createAndSendTarget(REGISTER_TARGET, tenant);
}
}

View File

@@ -0,0 +1,618 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.integration;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.ActionStatus;
import org.eclipse.hawkbit.dmf.json.model.ActionUpdateStatus;
import org.eclipse.hawkbit.dmf.json.model.AttributeUpdate;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
@Features("Component Tests - Device Management Federation API")
@Stories("Amqp Message Handler Service")
public class AmqpMessageHandlerServiceIntegrationTest extends AmqpServiceIntegrationTest {
@Test
@Description("Tests register target")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2),
@Expect(type = TargetPollEvent.class, count = 3) })
public void registerTargets() {
registerAndAssertTargetWithExistingTenant(REGISTER_TARGET, 1);
final String target2 = "Target2";
registerAndAssertTargetWithExistingTenant(target2, 2);
final Long pollingTimeTarget2 = controllerManagement.findByControllerId(target2).get().getLastTargetQuery();
registerSameTargetAndAssertBasedOnLastPolling(target2, 2, TargetUpdateStatus.REGISTERED, pollingTimeTarget2);
Mockito.verifyZeroInteractions(getDeadletterListener());
}
@Test
@Description("Tests register invalid target withy empty controller id. Tests register invalid target with null controller id")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void registerEmptyTarget() {
createAndSendTarget("", TENANT_EXIST);
assertAllTargetsCount(0);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests register invalid target with whitspace controller id. Tests register invalid target with null controller id")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void registerWhitespaceTarget() {
createAndSendTarget("Invalid Invalid", TENANT_EXIST);
assertAllTargetsCount(0);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests register invalid target with null controller id. Tests register invalid target with null controller id")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void registerInvalidNullTargets() {
createAndSendTarget(null, TENANT_EXIST);
assertAllTargetsCount(0);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests not allowed content-type in message. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void wrongContentType() {
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, TENANT_EXIST);
createTargetMessage.getMessageProperties().setContentType("WrongContentType");
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests null reply to property in message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void missingReplyToProperty() {
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, TENANT_EXIST);
createTargetMessage.getMessageProperties().setReplyTo(null);
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests missing reply to property in message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void emptyReplyToProperty() {
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, TENANT_EXIST);
createTargetMessage.getMessageProperties().setReplyTo("");
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests missing thing id property in message. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void missingThingIdProperty() {
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
createTargetMessage.getMessageProperties().getHeaders().remove(MessageHeaderKey.THING_ID);
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests null thing id property in message. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void nullThingIdProperty() {
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests missing tenant message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void missingTenantHeader() {
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, TENANT_EXIST);
createTargetMessage.getMessageProperties().getHeaders().remove(MessageHeaderKey.TENANT);
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests null tenant message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void nullTenantHeader() {
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, null);
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests empty tenant message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void emptyTenantHeader() {
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, "");
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests tenant not exist. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void tenantNotExist() {
final Message createTargetMessage = createTargetMessage(REGISTER_TARGET, "TenantNotExist");
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertThat(systemManagement.findTenants()).hasSize(1);
}
@Test
@Description("Tests missing type message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void missingTypeHeader() {
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
createTargetMessage.getMessageProperties().getHeaders().remove(MessageHeaderKey.TYPE);
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests null type message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void nullTypeHeader() {
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
createTargetMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TYPE, null);
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests empty type message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void emptyTypeHeader() {
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
createTargetMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TYPE, "");
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests invalid type message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void invalidTypeHeader() {
final Message createTargetMessage = createTargetMessage(null, TENANT_EXIST);
createTargetMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TYPE, "NotExist");
getDmfClient().send(createTargetMessage);
verifyDeadLetterMessages(1);
assertAllTargetsCount(0);
}
@Test
@Description("Tests null topic message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void nullTopicHeader() {
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, "");
eventMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TOPIC, null);
getDmfClient().send(eventMessage);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests null topic message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void emptyTopicHeader() {
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, "");
eventMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TOPIC, "");
getDmfClient().send(eventMessage);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests null topic message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void invalidTopicHeader() {
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, "");
eventMessage.getMessageProperties().getHeaders().put(MessageHeaderKey.TOPIC, "NotExist");
getDmfClient().send(eventMessage);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests missing topic message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void missingTopicHeader() {
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, "");
eventMessage.getMessageProperties().getHeaders().remove(MessageHeaderKey.TOPIC);
getDmfClient().send(eventMessage);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests invalid null message content. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void updateActionStatusWithNullContent() {
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, null);
getDmfClient().send(eventMessage);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests invalid empty message content. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void updateActionStatusWithEmptyContent() {
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, "");
getDmfClient().send(eventMessage);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests invalid json message content. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void updateActionStatusWithInvalidJsonContent() {
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS,
"Invalid Content");
getDmfClient().send(eventMessage);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests invalid topic message header. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void updateActionStatusWithInvalidActionId() {
final ActionUpdateStatus actionUpdateStatus = new ActionUpdateStatus(1L, ActionStatus.RUNNING);
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS,
actionUpdateStatus);
getDmfClient().send(eventMessage);
verifyDeadLetterMessages(1);
}
@Test
@Description("Tests register target and cancel a assignment")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) })
public void finishActionStatus() {
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.FINISHED, Status.FINISHED);
}
@Test
@Description("Register a target and send a update action status (running). Verfiy if the updated action status is correct.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionUpdatedEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void runningActionStatus() {
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.RUNNING, Status.RUNNING);
}
@Test
@Description("Register a target and send a update action status (download). Verfiy if the updated action status is correct.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void downloadActionStatus() {
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.DOWNLOAD, Status.DOWNLOAD);
}
@Test
@Description("Register a target and send a update action status (error). Verfiy if the updated action status is correct.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) })
public void errorActionStatus() {
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.ERROR, Status.ERROR);
}
@Test
@Description("Register a target and send a update action status (warning). Verfiy if the updated action status is correct.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void warningActionStatus() {
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.WARNING, Status.WARNING);
}
@Test
@Description("Register a target and send a update action status (retrieved). Verfiy if the updated action status is correct.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void retrievedActionStatus() {
registerTargetAndSendAndAssertUpdateActionStatus(ActionStatus.RETRIEVED, Status.RETRIEVED);
}
@Test
@Description("Register a target and send a invalid update action status (cancel). This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void cancelNotAllowActionStatus() {
registerTargetAndSendActionStatus(ActionStatus.CANCELED);
verifyDeadLetterMessages(1);
}
@Test
@Description("Verfiy receiving a download and install message if a deployment is done before the target has polled the first time.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void receiveDownLoadAndInstallMessageAfterAssignment() {
// setup
controllerManagement.findOrRegisterTargetIfItDoesNotexist(REGISTER_TARGET, null);
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
assignDistributionSet(distributionSet.getId(), REGISTER_TARGET);
// test
registerAndAssertTargetWithExistingTenant(REGISTER_TARGET, 1, TargetUpdateStatus.PENDING, "bumlux");
// verify
assertDownloadAndInstallMessage(distributionSet.getModules());
Mockito.verifyZeroInteractions(getDeadletterListener());
}
@Test
@Description("Verfiy receiving a cancel update message if a deployment is canceled before the target has polled the first time.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void receiveCancelUpdateMessageAfterAssignmentWasCanceled() {
// Setup
controllerManagement.findOrRegisterTargetIfItDoesNotexist(REGISTER_TARGET, null);
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final DistributionSetAssignmentResult distributionSetAssignmentResult = assignDistributionSet(
distributionSet.getId(), REGISTER_TARGET);
deploymentManagement.cancelAction(distributionSetAssignmentResult.getActions().get(0));
// test
registerAndAssertTargetWithExistingTenant(REGISTER_TARGET, 1, TargetUpdateStatus.PENDING, "bumlux");
// verify
assertCancelActionMessage(distributionSetAssignmentResult.getActions().get(0));
Mockito.verifyZeroInteractions(getDeadletterListener());
}
@Test
@Description("Register a target and send a invalid update action status (canceled). The current status (pending) is not a canceling state. This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void actionNotExists() {
final Long actionId = registerTargetAndCancelActionId();
final Long actionNotExist = actionId + 1;
sendActionUpdateStatus(new ActionUpdateStatus(actionNotExist, ActionStatus.CANCELED));
verifyDeadLetterMessages(1);
}
@Test
@Description("Register a target and send a invalid update action status (cancel_rejected). This message should forwarded to the deadletter queue")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void canceledRejectedNotAllowActionStatus() {
registerTargetAndSendActionStatus(ActionStatus.CANCEL_REJECTED);
verifyDeadLetterMessages(1);
}
@Test
@Description("Register a target and send a valid update action status (cancel_rejected). Verfiy if the updated action status is correct.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void canceledRejectedActionStatus() {
final Long actionId = registerTargetAndCancelActionId();
sendActionUpdateStatus(new ActionUpdateStatus(actionId, ActionStatus.CANCEL_REJECTED));
assertAction(actionId, Status.RUNNING, Status.CANCELING, Status.CANCEL_REJECTED);
}
@Test
@Description("Verify that sending an update controller attribute message to an existing target works.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void updateAttributes() {
// setup
final String target = "ControllerAttributeTestTarget";
registerAndAssertTargetWithExistingTenant(target, 1);
final AttributeUpdate controllerAttribute = new AttributeUpdate();
controllerAttribute.getAttributes().put("test1", "testA");
controllerAttribute.getAttributes().put("test2", "testB");
// test
sendUpdateAttributeMessage(target, TENANT_EXIST, controllerAttribute);
// validate
assertUpdateAttributes(target, controllerAttribute.getAttributes());
}
@Test
@Description("Verify that sending an update controller attribute message with no thingid header to an existing target does not work.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 0), @Expect(type = TargetPollEvent.class, count = 1) })
public void updateAttributesWithNoThingId() {
// setup
final String target = "ControllerAttributeTestTarget";
registerAndAssertTargetWithExistingTenant(target, 1);
final AttributeUpdate controllerAttribute = new AttributeUpdate();
controllerAttribute.getAttributes().put("test1", "testA");
controllerAttribute.getAttributes().put("test2", "testB");
final Message createUpdateAttributesMessage = createUpdateAttributesMessage(null, TENANT_EXIST,
controllerAttribute);
createUpdateAttributesMessage.getMessageProperties().getHeaders().remove(MessageHeaderKey.THING_ID);
// test
getDmfClient().send(createUpdateAttributesMessage);
// verify
verifyDeadLetterMessages(1);
final AttributeUpdate controllerAttributeEmpty = new AttributeUpdate();
assertUpdateAttributes(target, controllerAttributeEmpty.getAttributes());
}
@Test
@Description("Verify that sending an update controller attribute message with invalid body to an existing target does not work.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 0), @Expect(type = TargetPollEvent.class, count = 1) })
public void updateAttributesWithWrongBody() {
// setup
final String target = "ControllerAttributeTestTarget";
registerAndAssertTargetWithExistingTenant(target, 1);
final AttributeUpdate controllerAttribute = new AttributeUpdate();
controllerAttribute.getAttributes().put("test1", "testA");
controllerAttribute.getAttributes().put("test2", "testB");
final Message createUpdateAttributesMessageWrongBody = createUpdateAttributesMessageWrongBody(target,
TENANT_EXIST);
// test
getDmfClient().send(createUpdateAttributesMessageWrongBody);
// verify
verifyDeadLetterMessages(1);
}
private Long registerTargetAndSendActionStatus(final ActionStatus sendActionStatus) {
final DistributionSetAssignmentResult assignmentResult = registerTargetAndAssignDistributionSet();
final Long actionId = assignmentResult.getActions().get(0);
sendActionUpdateStatus(new ActionUpdateStatus(actionId, sendActionStatus));
return actionId;
}
private void sendActionUpdateStatus(final ActionUpdateStatus actionStatus) {
final Message eventMessage = createEventMessage(TENANT_EXIST, EventTopic.UPDATE_ACTION_STATUS, actionStatus);
getDmfClient().send(eventMessage);
}
private void registerTargetAndSendAndAssertUpdateActionStatus(final ActionStatus sendActionStatus,
final Status expectedActionStatus) {
final Long actionId = registerTargetAndSendActionStatus(sendActionStatus);
assertAction(actionId, Status.RUNNING, expectedActionStatus);
}
private void assertAction(final Long actionId, final Status... expectedActionStates) {
final Action action = waitUntilIsPresent(() -> controllerManagement.findActionWithDetails(actionId));
final List<Status> status = action.getActionStatus().stream().map(actionStatus -> actionStatus.getStatus())
.collect(Collectors.toList());
assertThat(status).containsOnly(expectedActionStates);
}
private Message createEventMessage(final String tenant, final EventTopic eventTopic, final Object payload) {
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
messageProperties.getHeaders().put(MessageHeaderKey.TYPE, MessageType.EVENT.toString());
messageProperties.getHeaders().put(MessageHeaderKey.TOPIC, eventTopic.toString());
return createMessage(payload, messageProperties);
}
private void sendUpdateAttributeMessage(final String target, final String tenant,
final AttributeUpdate attributeUpdate) {
final Message updateMessage = createUpdateAttributesMessage(target, tenant, attributeUpdate);
getDmfClient().send(updateMessage);
}
}

View File

@@ -0,0 +1,281 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.integration;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import org.eclipse.hawkbit.AmqpTestConfiguration;
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
import org.eclipse.hawkbit.dmf.json.model.AttributeUpdate;
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
import org.eclipse.hawkbit.integration.listener.DeadletterListener;
import org.eclipse.hawkbit.integration.listener.ReplyToListener;
import org.eclipse.hawkbit.matcher.SoftwareModuleJsonMatcher;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.util.IpUtil;
import org.junit.Assert;
import org.junit.Before;
import org.mockito.Mockito;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.test.RabbitListenerTestHarness;
import org.springframework.beans.factory.annotation.Autowired;
/**
*
* Common class for {@link AmqpMessageHandlerServiceIntegrationTest} and
* {@link AmqpMessageDispatcherServiceIntegrationTest}.
*/
public abstract class AmqpServiceIntegrationTest extends AbstractAmqpIntegrationTest {
protected static final String TENANT_EXIST = "DEFAULT";
protected static final String REGISTER_TARGET = "NewDmfTarget";
protected static final String CREATED_BY = "CONTROLLER_PLUG_AND_PLAY";
private DeadletterListener deadletterListener;
private ReplyToListener replyToListener;
private DistributionSet distributionSet;
@Autowired
private RabbitListenerTestHarness harness;
@Autowired
private ConnectionFactory connectionFactory;
@Before
public void initListener() {
deadletterListener = harness.getSpy(DeadletterListener.LISTENER_ID);
assertThat(deadletterListener).isNotNull();
Mockito.reset(deadletterListener);
replyToListener = harness.getSpy(ReplyToListener.LISTENER_ID);
assertThat(replyToListener).isNotNull();
Mockito.reset(replyToListener);
getDmfClient().setExchange(AmqpSettings.DMF_EXCHANGE);
}
protected <T> T waitUntilIsPresent(final Callable<Optional<T>> callable) {
createConditionFactory().until(() -> {
return securityRule.runAsPrivileged(() -> callable.call().isPresent());
});
try {
return callable.call().get();
} catch (final Exception e) {
return null;
}
}
protected void verifyDeadLetterMessages(final int expectedMessages) {
createConditionFactory().until(() -> {
Mockito.verify(getDeadletterListener(), Mockito.times(expectedMessages)).handleMessage(Mockito.any());
});
}
protected DeadletterListener getDeadletterListener() {
return deadletterListener;
}
protected DistributionSet getDistributionSet() {
return distributionSet;
}
protected DistributionSetAssignmentResult registerTargetAndAssignDistributionSet() {
distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
return registerTargetAndAssignDistributionSet(distributionSet.getId(), TargetUpdateStatus.REGISTERED,
distributionSet.getModules());
}
protected DistributionSetAssignmentResult registerTargetAndAssignDistributionSet(final Long assignDs,
final TargetUpdateStatus expectedStatus,
final Set<org.eclipse.hawkbit.repository.model.SoftwareModule> expectedSoftwareModulesInMessage) {
registerAndAssertTargetWithExistingTenant(REGISTER_TARGET, 1, expectedStatus, CREATED_BY);
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(assignDs, REGISTER_TARGET);
assertDownloadAndInstallMessage(expectedSoftwareModulesInMessage);
return assignmentResult;
}
protected void assertCancelActionMessage(final Long actionId) {
final Message replyMessage = assertReplyMessageHeader(EventTopic.CANCEL_DOWNLOAD);
final Long actionUpdateStatus = (Long) getDmfClient().getMessageConverter().fromMessage(replyMessage);
assertThat(actionUpdateStatus).isEqualTo(actionId);
}
protected void assertDownloadAndInstallMessage(
final Set<org.eclipse.hawkbit.repository.model.SoftwareModule> dsModules) {
final Message replyMessage = assertReplyMessageHeader(EventTopic.DOWNLOAD_AND_INSTALL);
assertAllTargetsCount(1);
final DownloadAndUpdateRequest downloadAndUpdateRequest = (DownloadAndUpdateRequest) getDmfClient()
.getMessageConverter().fromMessage(replyMessage);
Assert.assertThat(dsModules,
SoftwareModuleJsonMatcher.containsExactly(downloadAndUpdateRequest.getSoftwareModules()));
final Target updatedTarget = waitUntilIsPresent(
() -> targetManagement.findTargetByControllerID(REGISTER_TARGET));
assertThat(updatedTarget.getSecurityToken()).isEqualTo(downloadAndUpdateRequest.getTargetSecurityToken());
}
protected void createAndSendTarget(final String target, final String tenant) {
final Message message = createTargetMessage(target, tenant);
getDmfClient().send(message);
}
protected void verifyReplyToListener() {
createConditionFactory().until(() -> {
Mockito.verify(replyToListener, Mockito.atLeast(1)).handleMessage(Mockito.any());
});
}
protected Long cancelAction(final Long actionId) {
deploymentManagement.cancelAction(actionId);
assertCancelActionMessage(actionId);
return actionId;
}
protected Long registerTargetAndCancelActionId() {
final DistributionSetAssignmentResult assignmentResult = registerTargetAndAssignDistributionSet();
return cancelAction(assignmentResult.getActions().get(0));
}
protected void assertAllTargetsCount(final long expectedTargetsCount) {
assertThat(targetManagement.countTargetsAll()).isEqualTo(expectedTargetsCount);
}
private Message assertReplyMessageHeader(final EventTopic eventTopic) {
verifyReplyToListener();
final Message replyMessage = replyToListener.getMessages().get(eventTopic);
assertAllTargetsCount(1);
final Map<String, Object> headers = replyMessage.getMessageProperties().getHeaders();
assertThat(headers.get(MessageHeaderKey.TOPIC)).isEqualTo(eventTopic.toString());
assertThat(headers.get(MessageHeaderKey.THING_ID)).isEqualTo(REGISTER_TARGET);
assertThat(headers.get(MessageHeaderKey.TENANT)).isEqualTo(TENANT_EXIST);
assertThat(headers.get(MessageHeaderKey.TYPE)).isEqualTo(MessageType.EVENT.toString());
return replyMessage;
}
protected void registerAndAssertTargetWithExistingTenant(final String target,
final int existingTargetsAfterCreation) {
registerAndAssertTargetWithExistingTenant(target, existingTargetsAfterCreation, TargetUpdateStatus.REGISTERED,
CREATED_BY);
}
protected void registerAndAssertTargetWithExistingTenant(final String target,
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus,
final String createdBy) {
createAndSendTarget(target, TENANT_EXIST);
final Target registerdTarget = waitUntilIsPresent(() -> targetManagement.findTargetByControllerID(target));
assertAllTargetsCount(existingTargetsAfterCreation);
assertTarget(registerdTarget, expectedTargetStatus, createdBy);
}
protected void registerSameTargetAndAssertBasedOnLastPolling(final String target,
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus,
final Long pollingTimeTargetOld) {
createAndSendTarget(target, TENANT_EXIST);
final Target registerdTarget = waitUntilIsPresent(
() -> findTargetBasedOnNewPolltime(target, pollingTimeTargetOld));
assertAllTargetsCount(existingTargetsAfterCreation);
assertThat(registerdTarget.getUpdateStatus()).isEqualTo(expectedTargetStatus);
}
private Optional<Target> findTargetBasedOnNewPolltime(final String controllerId, final Long pollingTimeTargetOld) {
final Optional<Target> target2 = controllerManagement.findByControllerId(controllerId);
final Long pollingTimeTargetNew = target2.get().getLastTargetQuery();
if (pollingTimeTargetOld < pollingTimeTargetNew) {
return target2;
}
return Optional.empty();
}
private void assertTarget(final Target target, final TargetUpdateStatus updateStatus, final String createdBy) {
assertThat(target.getTenant()).isEqualTo(TENANT_EXIST);
assertThat(target.getDescription()).contains("Plug and Play");
assertThat(target.getDescription()).contains(target.getControllerId());
assertThat(target.getCreatedBy()).isEqualTo(createdBy);
assertThat(target.getUpdateStatus()).isEqualTo(updateStatus);
assertThat(target.getAddress()).isEqualTo(
IpUtil.createAmqpUri(connectionFactory.getVirtualHost(), AmqpTestConfiguration.REPLY_TO_EXCHANGE));
}
protected Message createTargetMessage(final String target, final String tenant) {
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
messageProperties.getHeaders().put(MessageHeaderKey.THING_ID, target);
messageProperties.getHeaders().put(MessageHeaderKey.TYPE, MessageType.THING_CREATED.toString());
messageProperties.setReplyTo(AmqpTestConfiguration.REPLY_TO_EXCHANGE);
return createMessage("", messageProperties);
}
protected MessageProperties createMessagePropertiesWithTenant(final String tenant) {
final MessageProperties messageProperties = new MessageProperties();
messageProperties.getHeaders().put(MessageHeaderKey.TENANT, tenant);
return messageProperties;
}
protected Message createUpdateAttributesMessage(final String target, final String tenant,
final AttributeUpdate attributeUpdate) {
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
messageProperties.getHeaders().put(MessageHeaderKey.THING_ID, target);
messageProperties.getHeaders().put(MessageHeaderKey.TYPE, MessageType.EVENT.toString());
messageProperties.getHeaders().put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ATTRIBUTES.toString());
return createMessage(attributeUpdate, messageProperties);
}
protected Message createUpdateAttributesMessageWrongBody(final String target, final String tenant) {
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
messageProperties.getHeaders().put(MessageHeaderKey.THING_ID, target);
messageProperties.getHeaders().put(MessageHeaderKey.TYPE, MessageType.EVENT.toString());
messageProperties.getHeaders().put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ATTRIBUTES.toString());
return createMessage("wrongbody", messageProperties);
}
protected void assertUpdateAttributes(final String controllerId, final Map<String, String> attributes) {
final Target findByControllerId = waitUntilIsPresent(
() -> controllerManagement.findByControllerId(controllerId));
final Map<String, String> controllerAttributes = targetManagement
.getControllerAttributes(findByControllerId.getControllerId());
assertThat(controllerAttributes.size()).isEqualTo(attributes.size());
attributes.forEach((k, v) -> assertKeyValueInMap(k, v, controllerAttributes));
}
private void assertKeyValueInMap(final String key, final String value,
final Map<String, String> controllerAttributes) {
assertThat(controllerAttributes.containsKey(key)).isTrue();
assertThat(controllerAttributes.get(key)).isEqualTo(value);
}
@Override
protected String getExchange() {
return AmqpSettings.DMF_EXCHANGE;
}
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.integration.listener;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
public class DeadletterListener implements TestRabbitListener {
public static final String LISTENER_ID = "deadletter";
@Override
@RabbitListener(id = "deadletter", queues = "dmf_connector_deadletter_ttl")
public void handleMessage(Message message) {
// currently the message is not needed
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.integration.listener;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.AmqpTestConfiguration;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
public class ReplyToListener implements TestRabbitListener {
public static final String LISTENER_ID = "replyto";
private final Map<EventTopic, Message> messages = new HashMap<>();
@Override
@RabbitListener(id = LISTENER_ID, queues = AmqpTestConfiguration.REPLY_TO_QUEUE)
public void handleMessage(Message message) {
final EventTopic eventTopic = EventTopic
.valueOf(message.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC).toString());
messages.put(eventTopic, message);
}
public Map<EventTopic, Message> getMessages() {
return messages;
}
}

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.integration.listener;
import org.springframework.amqp.core.Message;
public interface TestRabbitListener {
void handleMessage(Message message);
}

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.matcher;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Factory;
/**
* Set matcher for {@link SoftwareModule} and a list of
* {@link org.eclipse.hawkbit.dmf.json.model.SoftwareModule}.
*/
public final class SoftwareModuleJsonMatcher {
/**
* Creates a matcher that matches when the list of repository software
* modules arelogically equal to the specified JSON software modules.
* <p>
* If the specified repository software modules are <code>null</code> then
* the created matcher will only match if the JSON software modules are
* <code>null</code>
* <p>
* For example:
*
* <pre>
* List<SoftwareModule> modules;
* List<org.eclipse.hawkbit.dmf.json.model.SoftwareModule> expectedModules;
*
* assertThat(modules, containsExactly(expectedModules));
* </pre>
*
* @param expectedModules
* the json sofware modules.
*/
@Factory
public static SoftwareModulesMatcher containsExactly(
final List<org.eclipse.hawkbit.dmf.json.model.SoftwareModule> expectedModules) {
return new SoftwareModulesMatcher(expectedModules);
}
private static class SoftwareModulesMatcher extends BaseMatcher<Set<SoftwareModule>> {
private final List<org.eclipse.hawkbit.dmf.json.model.SoftwareModule> expectedModules;
public SoftwareModulesMatcher(List<org.eclipse.hawkbit.dmf.json.model.SoftwareModule> expectedModules) {
this.expectedModules = expectedModules;
}
static boolean containsExactly(Object actual,
List<org.eclipse.hawkbit.dmf.json.model.SoftwareModule> expected) {
if (actual == null) {
return expected == null;
}
@SuppressWarnings("unchecked")
final Collection<SoftwareModule> modules = (Collection<SoftwareModule>) actual;
if (modules.size() != expected.size()) {
return false;
}
for (final SoftwareModule repoSoftwareModule : modules) {
boolean containsElement = false;
for (final org.eclipse.hawkbit.dmf.json.model.SoftwareModule jsonSoftwareModule : expected) {
if (!jsonSoftwareModule.getModuleId().equals(repoSoftwareModule.getId())) {
continue;
}
containsElement = true;
if (!jsonSoftwareModule.getModuleType().equals(repoSoftwareModule.getType().getKey())) {
return false;
}
if (!jsonSoftwareModule.getModuleVersion().equals(repoSoftwareModule.getVersion())) {
return false;
}
if (jsonSoftwareModule.getArtifacts().size() != repoSoftwareModule.getArtifacts().size()) {
return false;
}
}
if (!containsElement) {
return false;
}
}
return true;
}
@Override
public boolean matches(Object actualValue) {
return containsExactly(actualValue, expectedModules);
}
@Override
public void describeTo(Description description) {
description.appendValue(expectedModules);
}
}
}

View File

@@ -7,6 +7,9 @@
# http://www.eclipse.org/legal/epl-v10.html # http://www.eclipse.org/legal/epl-v10.html
# #
# RabbitMQ
spring.rabbitmq.host=localhost
# supported: H2, MYSQL # supported: H2, MYSQL
hawkbit.server.database=H2 hawkbit.server.database=H2
@@ -33,4 +36,32 @@ H2.spring.datasource.password=sa
MYSQL.spring.datasource.url=jdbc:mysql://localhost:3306/sp_test MYSQL.spring.datasource.url=jdbc:mysql://localhost:3306/sp_test
MYSQL.spring.datasource.driverClassName=org.mariadb.jdbc.Driver MYSQL.spring.datasource.driverClassName=org.mariadb.jdbc.Driver
MYSQL.spring.datasource.username=root MYSQL.spring.datasource.username=root
MYSQL.spring.datasource.password= MYSQL.spring.datasource.password=
# Default tenant configuration properties
hawkbit.server.tenant.configuration.authentication-header-enabled.keyName=authentication.header.enabled
hawkbit.server.tenant.configuration.authentication-header-enabled.defaultValue=false
hawkbit.server.tenant.configuration.authentication-header-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.authentication-header-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.authentication-header-authority.keyName=authentication.header.authority
hawkbit.server.tenant.configuration.authentication-header-authority.defaultValue=false
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.keyName=authentication.targettoken.enabled
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.defaultValue=false
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.authentication-targettoken-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.keyName=authentication.gatewaytoken.enabled
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.defaultValue=false
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.authentication-gatewaytoken-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.authentication-gatewaytoken-key.keyName=authentication.gatewaytoken.key
hawkbit.server.tenant.configuration.authentication-gatewaytoken-key.defaultValue=false
hawkbit.server.tenant.configuration.anonymous-download-enabled.keyName=anonymous.download.enabled
hawkbit.server.tenant.configuration.anonymous-download-enabled.defaultValue=false
hawkbit.server.tenant.configuration.anonymous-download-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.anonymous-download-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator

View File

@@ -24,21 +24,24 @@ import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(Include.NON_NULL) @JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ActionUpdateStatus { public class ActionUpdateStatus {
@JsonProperty(required = true)
private Long actionId; private final Long actionId;
private final ActionStatus actionStatus;
@JsonProperty @JsonProperty
private Long softwareModuleId; private Long softwareModuleId;
@JsonProperty(required = true)
private ActionStatus actionStatus;
@JsonProperty @JsonProperty
private List<String> message; private List<String> message;
public Long getActionId() { public ActionUpdateStatus(@JsonProperty(value = "actionId", required = true) Long actionId,
return actionId; @JsonProperty(value = "actionStatus", required = true) ActionStatus actionStatus) {
this.actionId = actionId;
this.actionStatus = actionStatus;
} }
public void setActionId(final Long actionId) { public Long getActionId() {
this.actionId = actionId; return actionId;
} }
public Long getSoftwareModuleId() { public Long getSoftwareModuleId() {
@@ -53,10 +56,6 @@ public class ActionUpdateStatus {
return actionStatus; return actionStatus;
} }
public void setActionStatus(final ActionStatus actionStatus) {
this.actionStatus = actionStatus;
}
public List<String> getMessage() { public List<String> getMessage() {
if (message == null) { if (message == null) {
return Collections.emptyList(); return Collections.emptyList();
@@ -78,12 +77,12 @@ public class ActionUpdateStatus {
return false; return false;
} }
if (this.message == null) { if (message == null) {
this.message = new ArrayList<>(messages); message = new ArrayList<>(messages);
return true; return true;
} }
return this.message.addAll(messages); return message.addAll(messages);
} }
} }

17
pom.xml
View File

@@ -112,6 +112,7 @@
<!-- Incompatible changes in 2.2 --> <!-- Incompatible changes in 2.2 -->
<json-path.version>2.0.0</json-path.version> <json-path.version>2.0.0</json-path.version>
<jetty.version>9.3.14.v20161028</jetty.version> <jetty.version>9.3.14.v20161028</jetty.version>
<spring-amqp.version>1.7.1.RELEASE</spring-amqp.version>
<!-- Spring boot version overrides - END --> <!-- Spring boot version overrides - END -->
<!-- Vaadin versions - START --> <!-- Vaadin versions - START -->
@@ -714,8 +715,20 @@
<artifactId>mariadb-java-client</artifactId> <artifactId>mariadb-java-client</artifactId>
<version>${mariadb-java-client.version}</version> <version>${mariadb-java-client.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-junit</artifactId>
<version>${spring-amqp.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<version>${spring-amqp.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>${guava.version}</version> <version>${guava.version}</version>