Merge remote-tracking branch 'origin/master' into fix_Introduce_consitent_button_icon_for_update_create_dialogs_refactor
This commit is contained in:
@@ -79,12 +79,12 @@ public final class DataConversionHelper {
|
||||
final org.eclipse.hawkbit.repository.model.SoftwareModule module,
|
||||
final ArtifactUrlHandler artifactUrlHandler) {
|
||||
final List<DdiArtifact> files = new ArrayList<>();
|
||||
|
||||
|
||||
module.getLocalArtifacts()
|
||||
.forEach(artifact -> files.add(createArtifact(targetid, artifactUrlHandler, artifact)));
|
||||
.forEach(artifact -> files.add(createArtifact(targetid, artifactUrlHandler, artifact)));
|
||||
return files;
|
||||
}
|
||||
|
||||
|
||||
private static DdiArtifact createArtifact(final String targetid, final ArtifactUrlHandler artifactUrlHandler,
|
||||
final LocalArtifact artifact) {
|
||||
final DdiArtifact file = new DdiArtifact();
|
||||
@@ -125,7 +125,7 @@ public final class DataConversionHelper {
|
||||
// response because of eTags.
|
||||
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||
.getControllerBasedeploymentAction(target.getControllerId(), action.getId(),
|
||||
actions.hashCode())).withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION));
|
||||
calculateEtag(action))).withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION));
|
||||
addedUpdate = true;
|
||||
} else if (action.isCancelingOrCanceled() && !addedCancel) {
|
||||
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||
@@ -142,6 +142,22 @@ public final class DataConversionHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates an etag for the given {@link Action} based on the entities
|
||||
* hashcode and the {@link Action#isHitAutoForceTime(long)} to reflect a
|
||||
* force switch.
|
||||
*
|
||||
* @param action
|
||||
* to calculate the etag for
|
||||
* @return the etag
|
||||
*/
|
||||
private static int calculateEtag(final Action action) {
|
||||
final int prime = 31;
|
||||
int result = action.hashCode();
|
||||
result = prime * result + (action.isHitAutoForceTime(System.currentTimeMillis()) ? 1231 : 1237);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void writeMD5FileResponse(final String fileName, final HttpServletResponse response,
|
||||
final LocalArtifact artifact) throws IOException {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
||||
@@ -23,15 +23,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
|
||||
@@ -43,6 +45,9 @@ import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
@@ -229,6 +234,45 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
|
||||
assertThat(actionStatusMessage.getStatus()).isEqualTo(Status.RETRIEVED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Checks that the deployementBase URL changes when the action is switched from soft to forced in TIMEFORCED case.")
|
||||
public void changeEtagIfActionSwitchesFromSoftToForced() throws Exception {
|
||||
// Prepare test data
|
||||
final Target target = targetManagement.createTarget(entityFactory.generateTarget("4712"));
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
|
||||
|
||||
final DistributionSetAssignmentResult result = deploymentManagement.assignDistributionSet(ds.getId(),
|
||||
ActionType.TIMEFORCED, System.currentTimeMillis() + 1_000, target.getControllerId());
|
||||
|
||||
final Action action = deploymentManagement.findActiveActionsByTarget(result.getAssignedEntity().get(0)).get(0);
|
||||
|
||||
MvcResult mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaTypes.HAL_JSON)).andReturn();
|
||||
|
||||
final String urlBeforeSwitch = JsonPath.compile("_links.deploymentBase.href")
|
||||
.read(mvcResult.getResponse().getContentAsString()).toString();
|
||||
|
||||
// Time is not yet over, so we should see the same URL
|
||||
mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaTypes.HAL_JSON)).andReturn();
|
||||
assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString())
|
||||
.toString()).isEqualTo(urlBeforeSwitch)
|
||||
.startsWith("http://localhost/" + tenantAware.getCurrentTenant()
|
||||
+ "/controller/v1/4712/deploymentBase/" + action.getId());
|
||||
|
||||
// After the time is over we should see a new etag
|
||||
TimeUnit.MILLISECONDS.sleep(1_000);
|
||||
|
||||
mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaTypes.HAL_JSON)).andReturn();
|
||||
|
||||
assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString())
|
||||
.toString()).isNotEqualTo(urlBeforeSwitch);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Attempt/soft deployment to a controller. Checks if the resource reponse payload for a given deployment is as expected.")
|
||||
public void deplomentAttemptAction() throws Exception {
|
||||
|
||||
21
hawkbit-ddi-resource/src/test/resources/logback.xml
Normal file
21
hawkbit-ddi-resource/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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
|
||||
|
||||
-->
|
||||
<configuration>
|
||||
<include resource="org/springframework/boot/logging/logback/base.xml" />
|
||||
|
||||
<!-- <Logger name="org.eclipse.hawkbit.rest.util.MockMvcResultPrinter" level="DEBUG" /> -->
|
||||
|
||||
<Root level="INFO">
|
||||
<AppenderRef ref="Console" />
|
||||
</Root>
|
||||
|
||||
</configuration>
|
||||
@@ -22,11 +22,11 @@ import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.FanoutExchange;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.core.QueueBuilder;
|
||||
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -268,15 +268,8 @@ public class AmqpConfiguration {
|
||||
* AMQP messages
|
||||
*/
|
||||
@Bean(name = { "listenerContainerFactory" })
|
||||
public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
|
||||
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
|
||||
containerFactory.setDefaultRequeueRejected(true);
|
||||
containerFactory.setConnectionFactory(rabbitConnectionFactory);
|
||||
containerFactory.setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal());
|
||||
containerFactory.setConcurrentConsumers(amqpProperties.getInitialConcurrentConsumers());
|
||||
containerFactory.setMaxConcurrentConsumers(amqpProperties.getMaxConcurrentConsumers());
|
||||
containerFactory.setPrefetchCount(amqpProperties.getPrefetchCount());
|
||||
return containerFactory;
|
||||
public RabbitListenerContainerFactory<SimpleMessageListenerContainer> listenerContainerFactory() {
|
||||
return new ConfigurableRabbitListenerContainerFactory(amqpProperties, rabbitConnectionFactory);
|
||||
}
|
||||
|
||||
private static Map<String, Object> getTTLMaxArgsAuthenticationQueue() {
|
||||
|
||||
@@ -138,9 +138,18 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory")
|
||||
public Message onAuthenticationRequest(final Message message,
|
||||
@Header(MessageHeaderKey.TENANT) final String tenant) {
|
||||
return onAuthenticationRequest(message);
|
||||
public Message onAuthenticationRequest(final Message message) {
|
||||
checkContentTypeJson(message);
|
||||
final SecurityContext oldContext = SecurityContextHolder.getContext();
|
||||
try {
|
||||
return handleAuthentifiactionMessage(message);
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
|
||||
} catch (final TenantNotExistException teex) {
|
||||
throw new AmqpRejectAndDontRequeueException(teex);
|
||||
} finally {
|
||||
SecurityContextHolder.setContext(oldContext);
|
||||
}
|
||||
}
|
||||
|
||||
public Message onMessage(final Message message, final String type, final String tenant, final String virtualHost) {
|
||||
@@ -159,7 +168,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
final EventTopic eventTopic = EventTopic.valueOf(topicValue);
|
||||
handleIncomingEvent(message, eventTopic);
|
||||
break;
|
||||
|
||||
default:
|
||||
logAndThrowMessageError(message, "No handle method was found for the given message type.");
|
||||
}
|
||||
@@ -173,20 +181,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Message onAuthenticationRequest(final Message message) {
|
||||
checkContentTypeJson(message);
|
||||
final SecurityContext oldContext = SecurityContextHolder.getContext();
|
||||
try {
|
||||
return handleAuthentifiactionMessage(message);
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
|
||||
} catch (final TenantNotExistException teex) {
|
||||
throw new AmqpRejectAndDontRequeueException(teex);
|
||||
} finally {
|
||||
SecurityContextHolder.setContext(oldContext);
|
||||
}
|
||||
}
|
||||
|
||||
private Message handleAuthentifiactionMessage(final Message message) {
|
||||
final DownloadResponse authentificationResponse = new DownloadResponse();
|
||||
final MessageProperties messageProperties = message.getMessageProperties();
|
||||
@@ -414,7 +408,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
|
||||
if (ArrayUtils.isNotEmpty(message.getMessageProperties().getCorrelationId())) {
|
||||
actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "DMF message correlation-id "
|
||||
+ message.getMessageProperties().getCorrelationId());
|
||||
+ convertCorrelationId(message));
|
||||
}
|
||||
|
||||
actionStatus.setAction(action);
|
||||
@@ -422,6 +416,10 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
return actionStatus;
|
||||
}
|
||||
|
||||
private static String convertCorrelationId(final Message message) {
|
||||
return new String(message.getMessageProperties().getCorrelationId());
|
||||
}
|
||||
|
||||
private Action getUpdateActionStatus(final ActionStatus actionStatus) {
|
||||
if (actionStatus.getStatus().equals(Status.CANCELED)) {
|
||||
return controllerManagement.addCancelActionStatus(actionStatus);
|
||||
@@ -466,7 +464,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) {
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("Content-Type is not JSON compatible");
|
||||
throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible");
|
||||
}
|
||||
|
||||
void setControllerManagement(final ControllerManagement controllerManagement) {
|
||||
|
||||
@@ -68,6 +68,29 @@ public class AmqpProperties {
|
||||
*/
|
||||
private int initialConcurrentConsumers = 3;
|
||||
|
||||
/**
|
||||
* The number of retry attempts when passive queue declaration fails.
|
||||
* Passive queue declaration occurs when the consumer starts or, when
|
||||
* consuming from multiple queues, when not all queues were available during
|
||||
* initialization.
|
||||
*/
|
||||
private int declarationRetries = 50;
|
||||
|
||||
/**
|
||||
* @return the declarationRetries
|
||||
*/
|
||||
public int getDeclarationRetries() {
|
||||
return declarationRetries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param declarationRetries
|
||||
* the declarationRetries to set
|
||||
*/
|
||||
public void setDeclarationRetries(final int declarationRetries) {
|
||||
this.declarationRetries = declarationRetries;
|
||||
}
|
||||
|
||||
public String getAuthenticationReceiverQueue() {
|
||||
return authenticationReceiverQueue;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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.amqp;
|
||||
|
||||
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||
|
||||
/**
|
||||
* {@link RabbitListenerContainerFactory} that can be configured through
|
||||
* hawkBit's {@link AmqpProperties}.
|
||||
*
|
||||
*/
|
||||
public class ConfigurableRabbitListenerContainerFactory extends SimpleRabbitListenerContainerFactory {
|
||||
private final AmqpProperties amqpProperties;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param rabbitConnectionFactory
|
||||
* for the container factory
|
||||
* @param amqpProperties
|
||||
* to configure the container factory
|
||||
*/
|
||||
public ConfigurableRabbitListenerContainerFactory(final AmqpProperties amqpProperties,
|
||||
final ConnectionFactory rabbitConnectionFactory) {
|
||||
this.amqpProperties = amqpProperties;
|
||||
setDefaultRequeueRejected(true);
|
||||
setConnectionFactory(rabbitConnectionFactory);
|
||||
setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal());
|
||||
setConcurrentConsumers(amqpProperties.getInitialConcurrentConsumers());
|
||||
setMaxConcurrentConsumers(amqpProperties.getMaxConcurrentConsumers());
|
||||
setPrefetchCount(amqpProperties.getPrefetchCount());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
// Exception squid:UnusedProtectedMethod - called by
|
||||
// AbstractRabbitListenerContainerFactory
|
||||
@SuppressWarnings("squid:UnusedProtectedMethod")
|
||||
protected void initializeContainer(final SimpleMessageListenerContainer instance) {
|
||||
super.initializeContainer(instance);
|
||||
instance.setDeclarationRetries(amqpProperties.getDeclarationRetries());
|
||||
}
|
||||
}
|
||||
@@ -140,8 +140,8 @@ public class AmqpMessageHandlerServiceTest {
|
||||
final Message message = new Message(new byte[0], messageProperties);
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted due to worng content type");
|
||||
} catch (final IllegalArgumentException e) {
|
||||
fail("AmqpRejectAndDontRequeueException was excepeted due to worng content type");
|
||||
} catch (final AmqpRejectAndDontRequeueException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted since no replyTo header was set");
|
||||
fail("AmqpRejectAndDontRequeueException was excepeted since no replyTo header was set");
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
@@ -189,7 +189,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted since no thingID was set");
|
||||
fail("AmqpRejectAndDontRequeueException was excepeted since no thingID was set");
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
@@ -205,7 +205,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, type, TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted due to unknown message type");
|
||||
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown message type");
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
@@ -218,21 +218,21 @@ public class AmqpMessageHandlerServiceTest {
|
||||
final Message message = new Message(new byte[0], messageProperties);
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted due to unknown message type");
|
||||
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown message type");
|
||||
} catch (final AmqpRejectAndDontRequeueException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, "wrongTopic");
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted due to unknown topic");
|
||||
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown topic");
|
||||
} catch (final AmqpRejectAndDontRequeueException e) {
|
||||
}
|
||||
|
||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.CANCEL_DOWNLOAD.name());
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted because there was no event topic");
|
||||
fail("AmqpRejectAndDontRequeueException was excepeted because there was no event topic");
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
@@ -251,7 +251,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted since no action id was set");
|
||||
fail("AmqpRejectAndDontRequeueException was excepeted since no action id was set");
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
@@ -268,7 +268,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted since no action id was set");
|
||||
fail("AmqpRejectAndDontRequeueException was excepeted since no action id was set");
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user