Feature horizontal scalability (#305)
Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
committed by
Kai Zimmermann
parent
07cb62a3dd
commit
866bc72114
@@ -15,6 +15,7 @@ import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.eclipse.hawkbit.cache.DownloadArtifactCache;
|
||||
import org.eclipse.hawkbit.cache.DownloadIdCache;
|
||||
import org.eclipse.hawkbit.cache.DownloadType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.Artifact;
|
||||
import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
|
||||
@@ -31,7 +32,6 @@ import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
@@ -56,7 +56,7 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
||||
|
||||
private final ArtifactManagement artifactManagement;
|
||||
|
||||
private final Cache cache;
|
||||
private final DownloadIdCache cache;
|
||||
|
||||
private final HostnameResolver hostnameResolver;
|
||||
|
||||
@@ -78,7 +78,7 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
||||
*/
|
||||
public AmqpAuthenticationMessageHandler(final RabbitTemplate rabbitTemplate,
|
||||
final AmqpControllerAuthentication authenticationManager, final ArtifactManagement artifactManagement,
|
||||
final Cache cache, final HostnameResolver hostnameResolver,
|
||||
final DownloadIdCache cache, final HostnameResolver hostnameResolver,
|
||||
final ControllerManagement controllerManagement) {
|
||||
super(rabbitTemplate);
|
||||
this.authenticationManager = authenticationManager;
|
||||
@@ -89,7 +89,7 @@ public class AmqpAuthenticationMessageHandler extends BaseAmqpService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed on a authentication request.
|
||||
* Executed on an authentication request.
|
||||
*
|
||||
* @param message
|
||||
* the amqp message
|
||||
|
||||
@@ -15,12 +15,13 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.cache.CacheConstants;
|
||||
import org.eclipse.hawkbit.cache.DownloadIdCache;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.security.DdiSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
@@ -45,7 +46,7 @@ import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cloud.bus.ServiceMatcher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
|
||||
@@ -74,6 +75,9 @@ public class AmqpConfiguration {
|
||||
@Autowired
|
||||
private ConnectionFactory rabbitConnectionFactory;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ServiceMatcher serviceMatcher;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(ConnectionFactory.class)
|
||||
@ConditionalOnProperty(prefix = "hawkbit.dmf.rabbitmq", name = "enabled", matchIfMissing = true)
|
||||
@@ -288,7 +292,7 @@ public class AmqpConfiguration {
|
||||
* for target authentication
|
||||
* @param artifactManagement
|
||||
* for artifact URI generation
|
||||
* @param cache
|
||||
* @param downloadIdCache
|
||||
* for download IDs
|
||||
* @param hostnameResolver
|
||||
* for resolving the host for downloads
|
||||
@@ -299,10 +303,10 @@ public class AmqpConfiguration {
|
||||
@Bean
|
||||
public AmqpAuthenticationMessageHandler amqpAuthenticationMessageHandler(final RabbitTemplate rabbitTemplate,
|
||||
final AmqpControllerAuthentication authenticationManager, final ArtifactManagement artifactManagement,
|
||||
@Qualifier(CacheConstants.DOWNLOAD_ID_CACHE) final Cache cache, final HostnameResolver hostnameResolver,
|
||||
final DownloadIdCache downloadIdCache, final HostnameResolver hostnameResolver,
|
||||
final ControllerManagement controllerManagement) {
|
||||
return new AmqpAuthenticationMessageHandler(rabbitTemplate, authenticationManager, artifactManagement, cache,
|
||||
hostnameResolver, controllerManagement);
|
||||
return new AmqpAuthenticationMessageHandler(rabbitTemplate, authenticationManager, artifactManagement,
|
||||
downloadIdCache, hostnameResolver, controllerManagement);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,6 +334,23 @@ public class AmqpConfiguration {
|
||||
return new ConfigurableRabbitListenerContainerFactory(amqpProperties, rabbitConnectionFactory, errorHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* create the authentication bean for controller over amqp.
|
||||
*
|
||||
* @param systemManagement
|
||||
* the systemManagement
|
||||
* @param controllerManagement
|
||||
* the controllerManagement
|
||||
* @param tenantConfigurationManagement
|
||||
* the tenantConfigurationManagement
|
||||
* @param tenantAware
|
||||
* the tenantAware
|
||||
* @param ddiSecruityProperties
|
||||
* the ddiSecruityProperties
|
||||
* @param systemSecurityContext
|
||||
* the systemSecurityContext
|
||||
* @return the bean
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AmqpControllerAuthentication.class)
|
||||
public AmqpControllerAuthentication amqpControllerAuthentication(final SystemManagement systemManagement,
|
||||
@@ -340,13 +361,31 @@ public class AmqpConfiguration {
|
||||
tenantAware, ddiSecruityProperties, systemSecurityContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the dispatcher bean.
|
||||
*
|
||||
* @param rabbitTemplate
|
||||
* the rabbitTemplate
|
||||
* @param amqpSenderService
|
||||
* to send AMQP message
|
||||
* @param artifactUrlHandler
|
||||
* for generating download URLs
|
||||
* @param systemSecurityContext
|
||||
* for execution with system permissions
|
||||
* @param systemManagement
|
||||
* the systemManagement
|
||||
* @param targetManagement
|
||||
* to access target information
|
||||
* @return the bean
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AmqpMessageDispatcherService.class)
|
||||
public AmqpMessageDispatcherService amqpMessageDispatcherService(final RabbitTemplate rabbitTemplate,
|
||||
final AmqpSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
|
||||
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement) {
|
||||
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
|
||||
final TargetManagement targetManagement) {
|
||||
return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler,
|
||||
systemSecurityContext, systemManagement);
|
||||
systemSecurityContext, systemManagement, targetManagement, serviceMatcher);
|
||||
}
|
||||
|
||||
private static Map<String, Object> getTTLMaxArgsAuthenticationQueue() {
|
||||
|
||||
@@ -25,38 +25,40 @@ import org.eclipse.hawkbit.dmf.json.model.Artifact;
|
||||
import org.eclipse.hawkbit.dmf.json.model.ArtifactHash;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
|
||||
import org.eclipse.hawkbit.dmf.json.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.eventbus.EventSubscriber;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import org.springframework.cloud.bus.ServiceMatcher;
|
||||
import org.springframework.cloud.bus.event.RemoteApplicationEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
/**
|
||||
* {@link AmqpMessageDispatcherService} create all outgoing AMQP messages and
|
||||
* delegate the messages to a {@link AmqpSenderService}.
|
||||
*
|
||||
*
|
||||
* Additionally the dispatcher listener/subscribe for some target events e.g.
|
||||
* assignment.
|
||||
*
|
||||
*/
|
||||
@EventSubscriber
|
||||
public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
|
||||
private final ArtifactUrlHandler artifactUrlHandler;
|
||||
private final AmqpSenderService amqpSenderService;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final SystemManagement systemManagement;
|
||||
private final TargetManagement targetManagement;
|
||||
private final ServiceMatcher serviceMatcher;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*
|
||||
* @param rabbitTemplate
|
||||
* the rabbitTemplate
|
||||
* @param amqpSenderService
|
||||
@@ -66,51 +68,68 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
* @param systemSecurityContext
|
||||
* for execution with system permissions
|
||||
* @param systemManagement
|
||||
* to access to tenant metadata
|
||||
* the systemManagement
|
||||
* @param targetManagement
|
||||
* to access target information
|
||||
* @param serviceMatcher
|
||||
* to check in cluster case if the message is from the same
|
||||
* cluster node
|
||||
*/
|
||||
public AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate, final AmqpSenderService amqpSenderService,
|
||||
final ArtifactUrlHandler artifactUrlHandler, final SystemSecurityContext systemSecurityContext,
|
||||
final SystemManagement systemManagement) {
|
||||
final SystemManagement systemManagement, final TargetManagement targetManagement,
|
||||
final ServiceMatcher serviceMatcher) {
|
||||
super(rabbitTemplate);
|
||||
this.artifactUrlHandler = artifactUrlHandler;
|
||||
this.amqpSenderService = amqpSenderService;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.systemManagement = systemManagement;
|
||||
this.targetManagement = targetManagement;
|
||||
this.serviceMatcher = serviceMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to send a message to a RabbitMQ Exchange after the Distribution
|
||||
* set has been assign to a Target.
|
||||
*
|
||||
* @param targetAssignDistributionSetEvent
|
||||
* @param assignedEvent
|
||||
* the object to be send.
|
||||
*/
|
||||
@Subscribe
|
||||
public void targetAssignDistributionSet(final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent) {
|
||||
final URI targetAdress = targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress();
|
||||
@EventListener(classes = TargetAssignDistributionSetEvent.class)
|
||||
public void targetAssignDistributionSet(final TargetAssignDistributionSetEvent assignedEvent) {
|
||||
if (isFromSelf(assignedEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendUpdateMessageToTarget(assignedEvent.getTenant(),
|
||||
targetManagement.findTargetByControllerID(assignedEvent.getControllerId()), assignedEvent.getActionId(),
|
||||
assignedEvent.getModules());
|
||||
}
|
||||
|
||||
void sendUpdateMessageToTarget(final String tenant, final Target target, final Long actionId,
|
||||
final Collection<org.eclipse.hawkbit.repository.model.SoftwareModule> modules) {
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final URI targetAdress = target.getTargetInfo().getAddress();
|
||||
if (!IpUtil.isAmqpUri(targetAdress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String controllerId = targetAssignDistributionSetEvent.getTarget().getControllerId();
|
||||
final Collection<org.eclipse.hawkbit.repository.model.SoftwareModule> modules = targetAssignDistributionSetEvent
|
||||
.getSoftwareModules();
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = new DownloadAndUpdateRequest();
|
||||
downloadAndUpdateRequest.setActionId(targetAssignDistributionSetEvent.getActionId());
|
||||
downloadAndUpdateRequest.setActionId(actionId);
|
||||
|
||||
final String targetSecurityToken = systemSecurityContext
|
||||
.runAsSystem(targetAssignDistributionSetEvent.getTarget()::getSecurityToken);
|
||||
final String targetSecurityToken = systemSecurityContext.runAsSystem(target::getSecurityToken);
|
||||
downloadAndUpdateRequest.setTargetSecurityToken(targetSecurityToken);
|
||||
|
||||
for (final org.eclipse.hawkbit.repository.model.SoftwareModule softwareModule : modules) {
|
||||
final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule(
|
||||
targetAssignDistributionSetEvent.getTarget(), softwareModule);
|
||||
final SoftwareModule amqpSoftwareModule = convertToAmqpSoftwareModule(target, softwareModule);
|
||||
downloadAndUpdateRequest.addSoftwareModule(amqpSoftwareModule);
|
||||
}
|
||||
|
||||
final Message message = getMessageConverter().toMessage(downloadAndUpdateRequest,
|
||||
createConnectorMessageProperties(targetAssignDistributionSetEvent.getTenant(), controllerId,
|
||||
EventTopic.DOWNLOAD_AND_INSTALL));
|
||||
createConnectorMessageProperties(tenant, target.getControllerId(), EventTopic.DOWNLOAD_AND_INSTALL));
|
||||
amqpSenderService.sendMessage(message, targetAdress);
|
||||
}
|
||||
|
||||
@@ -118,19 +137,29 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
* Method to send a message to a RabbitMQ Exchange after the assignment of
|
||||
* the Distribution set to a Target has been canceled.
|
||||
*
|
||||
* @param cancelTargetAssignmentDistributionSetEvent
|
||||
* @param cancelEvent
|
||||
* the object to be send.
|
||||
*/
|
||||
@Subscribe
|
||||
public void targetCancelAssignmentToDistributionSet(
|
||||
final CancelTargetAssignmentEvent cancelTargetAssignmentDistributionSetEvent) {
|
||||
final String controllerId = cancelTargetAssignmentDistributionSetEvent.getTarget().getControllerId();
|
||||
final Long actionId = cancelTargetAssignmentDistributionSetEvent.getActionId();
|
||||
final Message message = getMessageConverter().toMessage(actionId, createConnectorMessageProperties(
|
||||
cancelTargetAssignmentDistributionSetEvent.getTenant(), controllerId, EventTopic.CANCEL_DOWNLOAD));
|
||||
@EventListener(classes = CancelTargetAssignmentEvent.class)
|
||||
public void targetCancelAssignmentToDistributionSet(final CancelTargetAssignmentEvent cancelEvent) {
|
||||
if (isFromSelf(cancelEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
amqpSenderService.sendMessage(message,
|
||||
cancelTargetAssignmentDistributionSetEvent.getTarget().getTargetInfo().getAddress());
|
||||
sendCancelMessageToTarget(cancelEvent.getTenant(), cancelEvent.getEntity().getControllerId(),
|
||||
cancelEvent.getActionId(), cancelEvent.getEntity().getTargetInfo().getAddress());
|
||||
}
|
||||
|
||||
private boolean isFromSelf(final RemoteApplicationEvent event) {
|
||||
return serviceMatcher != null && !serviceMatcher.isFromSelf(event);
|
||||
}
|
||||
|
||||
void sendCancelMessageToTarget(final String tenant, final String controllerId, final Long actionId,
|
||||
final URI address) {
|
||||
final Message message = getMessageConverter().toMessage(actionId,
|
||||
createConnectorMessageProperties(tenant, controllerId, EventTopic.CANCEL_DOWNLOAD));
|
||||
|
||||
amqpSenderService.sendMessage(message, address);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.amqp;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -26,15 +25,11 @@ import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.exception.TooManyStatusEntriesException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
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.util.IpUtil;
|
||||
import org.slf4j.Logger;
|
||||
@@ -176,7 +171,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
final String replyTo = message.getMessageProperties().getReplyTo();
|
||||
|
||||
if (StringUtils.isEmpty(replyTo)) {
|
||||
logAndThrowMessageError(message, "No ReplyTo was set for the createThing Event.");
|
||||
logAndThrowMessageError(message, "No ReplyTo was set for the createThing TenantAwareEvent.");
|
||||
}
|
||||
|
||||
final URI amqpUri = IpUtil.createAmqpUri(virtualHost, replyTo);
|
||||
@@ -187,23 +182,20 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
}
|
||||
|
||||
private void lookIfUpdateAvailable(final Target target) {
|
||||
final Optional<Action> action = controllerManagement.findOldestActiveActionByTarget(target);
|
||||
if (!action.isPresent()) {
|
||||
final Optional<Action> actionOptional = controllerManagement.findOldestActiveActionByTarget(target);
|
||||
if (!actionOptional.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (action.get().isCancelingOrCanceled()) {
|
||||
amqpMessageDispatcherService.targetCancelAssignmentToDistributionSet(
|
||||
new CancelTargetAssignmentEvent(target, action.get().getId()));
|
||||
final Action action = actionOptional.get();
|
||||
if (action.isCancelingOrCanceled()) {
|
||||
amqpMessageDispatcherService.sendCancelMessageToTarget(target.getTenant(), target.getControllerId(),
|
||||
action.getId(), target.getTargetInfo().getAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
final DistributionSet distributionSet = action.get().getDistributionSet();
|
||||
final List<SoftwareModule> softwareModuleList = controllerManagement
|
||||
.findSoftwareModulesByDistributionSet(distributionSet);
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(new TargetAssignDistributionSetEvent(
|
||||
target.getOptLockRevision(), target.getTenant(), target, action.get().getId(), softwareModuleList));
|
||||
|
||||
amqpMessageDispatcherService.sendUpdateMessageToTarget(action.getTenant(), action.getTarget(), action.getId(),
|
||||
action.getDistributionSet().getModules());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,5 +324,4 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
"Cancel recjected message is not allowed, if action is on state: " + action.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.net.URL;
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.eclipse.hawkbit.cache.DownloadIdCache;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DownloadResponse;
|
||||
@@ -54,7 +55,6 @@ import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -95,7 +95,7 @@ public class AmqpControllerAuthenticationTest {
|
||||
private SystemManagement systemManagement;
|
||||
|
||||
@Mock
|
||||
private Cache cacheMock;
|
||||
private DownloadIdCache cacheMock;
|
||||
|
||||
@Mock
|
||||
private HostnameResolver hostnameResolverMock;
|
||||
|
||||
@@ -19,9 +19,9 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.eclipse.hawkbit.api.ArtifactUrl;
|
||||
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
|
||||
@@ -31,8 +31,12 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DownloadAndUpdateRequest;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.jpa.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
@@ -48,6 +52,7 @@ import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.test.context.ActiveProfiles;
|
||||
|
||||
@@ -82,11 +87,15 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
||||
|
||||
private Target testTarget;
|
||||
|
||||
@Autowired
|
||||
protected ActionRepository actionRepository;
|
||||
|
||||
@Override
|
||||
public void before() throws Exception {
|
||||
super.before();
|
||||
testTarget = entityFactory.generateTarget(CONTROLLER_ID, TEST_TOKEN);
|
||||
testTarget.getTargetInfo().setAddress(AMQP_URI.toString());
|
||||
final Target generateTarget = entityFactory.generateTarget(CONTROLLER_ID, TEST_TOKEN);
|
||||
generateTarget.getTargetInfo().setAddress(AMQP_URI.toString());
|
||||
testTarget = targetManagement.createTarget(generateTarget);
|
||||
|
||||
this.rabbitTemplate = Mockito.mock(RabbitTemplate.class);
|
||||
when(rabbitTemplate.getMessageConverter()).thenReturn(new Jackson2JsonMessageConverter());
|
||||
@@ -105,41 +114,62 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
||||
when(systemManagement.getTenantMetadata()).thenReturn(tenantMetaData);
|
||||
|
||||
amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService,
|
||||
artifactUrlHandlerMock, systemSecurityContext, systemManagement);
|
||||
artifactUrlHandlerMock, systemSecurityContext, systemManagement, targetManagement, serviceMatcher);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verfies that download and install event with no software modul works")
|
||||
public void testSendDownloadRequesWithEmptySoftwareModules() {
|
||||
final Action action = createAction(
|
||||
testdataFactory.createDistributionSetWithNoSoftwareModules(UUID.randomUUID().toString(), "1.0"));
|
||||
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
|
||||
1L, TENANT, testTarget, 1L, Collections.emptyList());
|
||||
action, serviceMatcher.getServiceId());
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(
|
||||
targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress());
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
|
||||
|
||||
final Message sendMessage = getCaptureAdressEvent(targetAssignDistributionSetEvent);
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage, action);
|
||||
assertThat(downloadAndUpdateRequest.getTargetSecurityToken()).isEqualTo(TEST_TOKEN);
|
||||
assertTrue("No softwaremmodule should be contained in the request",
|
||||
downloadAndUpdateRequest.getSoftwareModules().isEmpty());
|
||||
}
|
||||
|
||||
private Message getCaptureAdressEvent(final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent) {
|
||||
final Target target = targetManagement
|
||||
.findTargetByControllerID(targetAssignDistributionSetEvent.getControllerId());
|
||||
final Message sendMessage = createArgumentCapture(target.getTargetInfo().getAddress());
|
||||
return sendMessage;
|
||||
}
|
||||
|
||||
private JpaAction createAction(final DistributionSet testDs) {
|
||||
final Action action = entityFactory.generateAction();
|
||||
final JpaAction jpaAction = (JpaAction) action;
|
||||
action.setDistributionSet(testDs);
|
||||
action.setTarget(testTarget);
|
||||
jpaAction.setActionType(ActionType.FORCED);
|
||||
|
||||
return actionRepository.save(jpaAction);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verfies that download and install event with 3 software moduls and no artifacts works")
|
||||
public void testSendDownloadRequesWithSoftwareModulesAndNoArtifacts() {
|
||||
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
||||
final DistributionSet createDistributionSet = testdataFactory
|
||||
.createDistributionSet(UUID.randomUUID().toString());
|
||||
final Action action = createAction(createDistributionSet);
|
||||
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
|
||||
1L, TENANT, testTarget, 1L, dsA.getModules());
|
||||
action, serviceMatcher.getServiceId());
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(
|
||||
targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress());
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
|
||||
assertEquals("Expecting a size of 3 software modules in the reuqest", 3,
|
||||
downloadAndUpdateRequest.getSoftwareModules().size());
|
||||
final Message sendMessage = getCaptureAdressEvent(targetAssignDistributionSetEvent);
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage, action);
|
||||
assertThat(createDistributionSet.getModules()).hasSameSizeAs(downloadAndUpdateRequest.getSoftwareModules());
|
||||
assertThat(downloadAndUpdateRequest.getTargetSecurityToken()).isEqualTo(TEST_TOKEN);
|
||||
for (final org.eclipse.hawkbit.dmf.json.model.SoftwareModule softwareModule : downloadAndUpdateRequest
|
||||
.getSoftwareModules()) {
|
||||
assertTrue("Artifact list for softwaremodule should be empty", softwareModule.getArtifacts().isEmpty());
|
||||
for (final SoftwareModule softwareModule2 : dsA.getModules()) {
|
||||
for (final SoftwareModule softwareModule2 : action.getDistributionSet().getModules()) {
|
||||
assertNotNull("Sofware module ID should be set", softwareModule.getModuleId());
|
||||
if (!softwareModule.getModuleId().equals(softwareModule2.getId())) {
|
||||
continue;
|
||||
@@ -157,7 +187,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
||||
@Test
|
||||
@Description("Verfies that download and install event with software moduls and artifacts works")
|
||||
public void testSendDownloadRequest() {
|
||||
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
||||
final DistributionSet dsA = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
|
||||
final SoftwareModule module = dsA.getModules().iterator().next();
|
||||
final List<DbArtifact> receivedList = new ArrayList<>();
|
||||
for (final Artifact artifact : testdataFactory.createArtifacts(module.getId())) {
|
||||
@@ -165,14 +195,16 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
||||
receivedList.add(new DbArtifact());
|
||||
}
|
||||
|
||||
final Action action = createAction(dsA);
|
||||
|
||||
Mockito.when(rabbitTemplate.convertSendAndReceive(any())).thenReturn(receivedList);
|
||||
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = new TargetAssignDistributionSetEvent(
|
||||
1L, TENANT, testTarget, 1L, dsA.getModules());
|
||||
action, serviceMatcher.getServiceId());
|
||||
amqpMessageDispatcherService.targetAssignDistributionSet(targetAssignDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(
|
||||
targetAssignDistributionSetEvent.getTarget().getTargetInfo().getAddress());
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage);
|
||||
final Message sendMessage = getCaptureAdressEvent(targetAssignDistributionSetEvent);
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = assertDownloadAndInstallMessage(sendMessage, action);
|
||||
|
||||
assertEquals("DownloadAndUpdateRequest event should contains 3 software modules", 3,
|
||||
downloadAndUpdateRequest.getSoftwareModules().size());
|
||||
assertThat(downloadAndUpdateRequest.getTargetSecurityToken()).isEqualTo(TEST_TOKEN);
|
||||
@@ -201,11 +233,11 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
||||
@Description("Verfies that send cancel event works")
|
||||
public void testSendCancelRequest() {
|
||||
final CancelTargetAssignmentEvent cancelTargetAssignmentDistributionSetEvent = new CancelTargetAssignmentEvent(
|
||||
testTarget, 1L);
|
||||
testTarget, 1L, serviceMatcher.getServiceId());
|
||||
amqpMessageDispatcherService
|
||||
.targetCancelAssignmentToDistributionSet(cancelTargetAssignmentDistributionSetEvent);
|
||||
final Message sendMessage = createArgumentCapture(
|
||||
cancelTargetAssignmentDistributionSetEvent.getTarget().getTargetInfo().getAddress());
|
||||
cancelTargetAssignmentDistributionSetEvent.getEntity().getTargetInfo().getAddress());
|
||||
assertCancelMessage(sendMessage);
|
||||
|
||||
}
|
||||
@@ -219,12 +251,11 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
private DownloadAndUpdateRequest assertDownloadAndInstallMessage(final Message sendMessage) {
|
||||
private DownloadAndUpdateRequest assertDownloadAndInstallMessage(final Message sendMessage, final Action action) {
|
||||
assertEventMessage(sendMessage);
|
||||
final DownloadAndUpdateRequest downloadAndUpdateRequest = convertMessage(sendMessage,
|
||||
DownloadAndUpdateRequest.class);
|
||||
assertEquals("The action ID of the downloadAndUpdateRequest event shuold be 1",
|
||||
downloadAndUpdateRequest.getActionId(), Long.valueOf(1));
|
||||
assertEquals(downloadAndUpdateRequest.getActionId(), action.getId());
|
||||
assertEquals("The topic of the event shuold contain DOWNLOAD_AND_INSTALL", EventTopic.DOWNLOAD_AND_INSTALL,
|
||||
sendMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC));
|
||||
assertEquals("Security token of target", downloadAndUpdateRequest.getTargetSecurityToken(), TEST_TOKEN);
|
||||
|
||||
@@ -21,14 +21,14 @@ import java.lang.reflect.Field;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.artifact.repository.ArtifactRepository;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||
import org.eclipse.hawkbit.cache.DownloadIdCache;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
@@ -40,17 +40,16 @@ import org.eclipse.hawkbit.dmf.json.model.TenantSecurityToken.FileResource;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.eventbus.event.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
@@ -67,7 +66,6 @@ import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
@@ -108,7 +106,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
private ArtifactRepository artifactRepositoryMock;
|
||||
|
||||
@Mock
|
||||
private Cache cacheMock;
|
||||
private DownloadIdCache downloadIdCache;
|
||||
|
||||
@Mock
|
||||
private HostnameResolver hostnameResolverMock;
|
||||
@@ -120,11 +118,13 @@ public class AmqpMessageHandlerServiceTest {
|
||||
public void before() throws Exception {
|
||||
messageConverter = new Jackson2JsonMessageConverter();
|
||||
when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
|
||||
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock,
|
||||
controllerManagementMock, entityFactoryMock);
|
||||
|
||||
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock,
|
||||
controllerManagementMock, entityFactoryMock);
|
||||
amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate,
|
||||
authenticationManagerMock, artifactManagementMock, cacheMock, hostnameResolverMock,
|
||||
authenticationManagerMock, artifactManagementMock, downloadIdCache, hostnameResolverMock,
|
||||
controllerManagementMock);
|
||||
}
|
||||
|
||||
@@ -363,10 +363,6 @@ public class AmqpMessageHandlerServiceTest {
|
||||
// for the test the same action can be used
|
||||
when(controllerManagementMock.findOldestActiveActionByTarget(Matchers.any())).thenReturn(Optional.of(action));
|
||||
|
||||
final List<SoftwareModule> softwareModuleList = createSoftwareModuleList();
|
||||
when(controllerManagementMock.findSoftwareModulesByDistributionSet(Matchers.any()))
|
||||
.thenReturn(softwareModuleList);
|
||||
|
||||
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||
final ActionUpdateStatus actionUpdateStatus = createActionUpdateStatus(ActionStatus.FINISHED, 23L);
|
||||
@@ -380,20 +376,19 @@ public class AmqpMessageHandlerServiceTest {
|
||||
verify(controllerManagementMock).updateTargetStatus(Matchers.any(TargetInfo.class),
|
||||
Matchers.isNull(TargetUpdateStatus.class), Matchers.isNotNull(Long.class), Matchers.isNull(URI.class));
|
||||
|
||||
final ArgumentCaptor<TargetAssignDistributionSetEvent> captorTargetAssignDistributionSetEvent = ArgumentCaptor
|
||||
.forClass(TargetAssignDistributionSetEvent.class);
|
||||
verify(amqpMessageDispatcherServiceMock, times(1))
|
||||
.targetAssignDistributionSet(captorTargetAssignDistributionSetEvent.capture());
|
||||
final TargetAssignDistributionSetEvent targetAssignDistributionSetEvent = captorTargetAssignDistributionSetEvent
|
||||
.getValue();
|
||||
final ArgumentCaptor<String> tenantCaptor = ArgumentCaptor.forClass(String.class);
|
||||
final ArgumentCaptor<Target> targetCaptor = ArgumentCaptor.forClass(Target.class);
|
||||
final ArgumentCaptor<Long> actionIdCaptor = ArgumentCaptor.forClass(Long.class);
|
||||
|
||||
assertThat(targetAssignDistributionSetEvent.getTarget().getControllerId()).as("event has wrong controller id")
|
||||
.isEqualTo("target1");
|
||||
assertThat(targetAssignDistributionSetEvent.getTarget().getSecurityToken())
|
||||
.as("targetoken not filled correctly").isEqualTo(action.getTarget().getSecurityToken());
|
||||
assertThat(targetAssignDistributionSetEvent.getActionId()).as("event has wrong action id").isEqualTo(22L);
|
||||
assertThat(targetAssignDistributionSetEvent.getSoftwareModules()).as("event has wrong sofware modules")
|
||||
.isEqualTo(softwareModuleList);
|
||||
verify(amqpMessageDispatcherServiceMock, times(1)).sendUpdateMessageToTarget(tenantCaptor.capture(),
|
||||
targetCaptor.capture(), actionIdCaptor.capture(), Matchers.any(Collection.class));
|
||||
final String tenant = tenantCaptor.getValue();
|
||||
final String controllerId = targetCaptor.getValue().getControllerId();
|
||||
final Long actionId = actionIdCaptor.getValue();
|
||||
|
||||
assertThat(tenant).as("event has tenant").isEqualTo("DEFAULT");
|
||||
assertThat(controllerId).as("event has wrong controller id").isEqualTo("target1");
|
||||
assertThat(actionId).as("event has wrong action id").isEqualTo(22L);
|
||||
|
||||
}
|
||||
|
||||
@@ -424,14 +419,6 @@ public class AmqpMessageHandlerServiceTest {
|
||||
return messageProperties;
|
||||
}
|
||||
|
||||
private List<SoftwareModule> createSoftwareModuleList() {
|
||||
final List<SoftwareModule> softwareModuleList = new ArrayList<>();
|
||||
final JpaSoftwareModule softwareModule = new JpaSoftwareModule();
|
||||
softwareModule.setId(777L);
|
||||
softwareModuleList.add(softwareModule);
|
||||
return softwareModuleList;
|
||||
}
|
||||
|
||||
private Action createActionWithTarget(final Long targetId, final Status status) throws IllegalAccessException {
|
||||
// is needed for the creation of targets
|
||||
initalizeSecurityTokenGenerator();
|
||||
@@ -440,6 +427,10 @@ public class AmqpMessageHandlerServiceTest {
|
||||
final JpaAction actionMock = mock(JpaAction.class);
|
||||
final JpaTarget targetMock = mock(JpaTarget.class);
|
||||
final TargetInfo targetInfoMock = mock(TargetInfo.class);
|
||||
final DistributionSet distributionSetMock = mock(DistributionSet.class);
|
||||
|
||||
when(distributionSetMock.getId()).thenReturn(1L);
|
||||
when(actionMock.getDistributionSet()).thenReturn(distributionSetMock);
|
||||
when(actionMock.getId()).thenReturn(targetId);
|
||||
when(actionMock.getStatus()).thenReturn(status);
|
||||
when(actionMock.getTenant()).thenReturn("DEFAULT");
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
# http://www.eclipse.org/legal/epl-v10.html
|
||||
#
|
||||
|
||||
|
||||
|
||||
# supported: H2, MYSQL
|
||||
hawkbit.server.database=H2
|
||||
|
||||
@@ -35,4 +33,4 @@ H2.spring.datasource.password=sa
|
||||
MYSQL.spring.datasource.url=jdbc:mysql://localhost:3306/sp_test
|
||||
MYSQL.spring.datasource.driverClassName=org.mariadb.jdbc.Driver
|
||||
MYSQL.spring.datasource.username=root
|
||||
MYSQL.spring.datasource.password=
|
||||
MYSQL.spring.datasource.password=
|
||||
Reference in New Issue
Block a user