Feature horizontal scalability (#305)

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
Dennis Melzer
2016-11-03 15:53:53 +01:00
committed by Kai Zimmermann
parent 07cb62a3dd
commit 866bc72114
287 changed files with 4219 additions and 5046 deletions

View File

@@ -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;

View File

@@ -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);

View File

@@ -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");

View File

@@ -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=