Merge pull request #217 from bsinno/feature_configurable_mgmt_sim_scenario
Feature configurable mgmt sim scenario
This commit is contained in:
@@ -71,6 +71,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
@@ -83,26 +87,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<!-- Log4j API and Core implementation required for binding -->
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
</dependency>
|
||||
<!-- Logging binding for java-util-logging e.g. Tomcat -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
</dependency>
|
||||
<!-- Logging binding for Jakarta Commons Logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jcl-over-slf4j</artifactId>
|
||||
</dependency>
|
||||
<!-- Logging binding for Log4J Logging -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vaadin</groupId>
|
||||
<artifactId>vaadin-spring-boot-starter</artifactId>
|
||||
|
||||
@@ -27,7 +27,7 @@ public abstract class AbstractSimulatedDevice {
|
||||
private UpdateStatus updateStatus = new UpdateStatus(ResponseStatus.SUCCESSFUL, "Simulation complete!");
|
||||
private Protocol protocol = Protocol.DMF_AMQP;
|
||||
private String targetSecurityToken;
|
||||
|
||||
private int pollDelaySec;
|
||||
private int nextPollCounterSec;
|
||||
|
||||
/**
|
||||
@@ -84,13 +84,30 @@ public abstract class AbstractSimulatedDevice {
|
||||
* the ID of the simulated device
|
||||
* @param tenant
|
||||
* the tenant of the simulated device
|
||||
* @param int
|
||||
* pollDelaySec
|
||||
*/
|
||||
AbstractSimulatedDevice(final String id, final String tenant, final Protocol protocol) {
|
||||
AbstractSimulatedDevice(final String id, final String tenant, final Protocol protocol, final int pollDelaySec) {
|
||||
this.id = id;
|
||||
this.tenant = tenant;
|
||||
this.status = Status.UNKNWON;
|
||||
this.progress = 0.0;
|
||||
this.protocol = protocol;
|
||||
this.pollDelaySec = pollDelaySec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called by a scheduler to trigger a device polling, like in real
|
||||
* scenarios devices are frequently asking for updates etc.
|
||||
*/
|
||||
public abstract void poll();
|
||||
|
||||
public int getPollDelaySec() {
|
||||
return pollDelaySec;
|
||||
}
|
||||
|
||||
public void setPollDelaySec(final int pollDelaySec) {
|
||||
this.pollDelaySec = pollDelaySec;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,6 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DDISimulatedDevice.class);
|
||||
|
||||
private final int pollDelaySec;
|
||||
private final ControllerResource controllerResource;
|
||||
|
||||
private final DeviceSimulatorUpdater deviceUpdater;
|
||||
@@ -45,11 +44,9 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
|
||||
*/
|
||||
public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec,
|
||||
final ControllerResource controllerResource, final DeviceSimulatorUpdater deviceUpdater) {
|
||||
super(id, tenant, Protocol.DDI_HTTP);
|
||||
this.pollDelaySec = pollDelaySec;
|
||||
super(id, tenant, Protocol.DDI_HTTP, pollDelaySec);
|
||||
this.controllerResource = controllerResource;
|
||||
this.deviceUpdater = deviceUpdater;
|
||||
setNextPollCounterSec(pollDelaySec);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,13 +55,10 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
|
||||
removed = true;
|
||||
}
|
||||
|
||||
public int getPollDelaySec() {
|
||||
return pollDelaySec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the base URL for the DDI API interface.
|
||||
*/
|
||||
@Override
|
||||
public void poll() {
|
||||
if (!removed) {
|
||||
final String basePollJson = controllerResource.get(getTenant(), getId());
|
||||
|
||||
@@ -8,10 +8,13 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
||||
|
||||
/**
|
||||
* A simulated device using the DMF API of the hawkBit update server.
|
||||
*/
|
||||
public class DMFSimulatedDevice extends AbstractSimulatedDevice {
|
||||
private final SpSenderService spSenderService;
|
||||
|
||||
/**
|
||||
* @param id
|
||||
@@ -19,8 +22,15 @@ public class DMFSimulatedDevice extends AbstractSimulatedDevice {
|
||||
* @param tenant
|
||||
* the tenant of the simulated device
|
||||
*/
|
||||
public DMFSimulatedDevice(final String id, final String tenant) {
|
||||
super(id, tenant, Protocol.DMF_AMQP);
|
||||
public DMFSimulatedDevice(final String id, final String tenant, final SpSenderService spSenderService,
|
||||
final int pollDelaySec) {
|
||||
super(id, tenant, Protocol.DMF_AMQP, pollDelaySec);
|
||||
this.spSenderService = spSenderService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void poll() {
|
||||
spSenderService.createOrUpdateThing(super.getTenant(), super.getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
package org.eclipse.hawkbit.simulator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -25,7 +25,7 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class DeviceSimulatorRepository {
|
||||
|
||||
private final Map<DeviceKey, AbstractSimulatedDevice> devices = new LinkedHashMap<>();
|
||||
private final Map<DeviceKey, AbstractSimulatedDevice> devices = new ConcurrentHashMap<>();
|
||||
|
||||
@Autowired
|
||||
private SimulatedDeviceFactory deviceFactory;
|
||||
|
||||
@@ -8,12 +8,11 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
|
||||
import org.slf4j.Logger;
|
||||
@@ -51,18 +50,17 @@ public class NextPollTimeController {
|
||||
private class NextPollUpdaterRunnable implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
final List<AbstractSimulatedDevice> devices = repository.getAll().stream()
|
||||
.filter(device -> device instanceof DDISimulatedDevice).collect(Collectors.toList());
|
||||
final Collection<AbstractSimulatedDevice> devices = repository.getAll();
|
||||
|
||||
devices.forEach(device -> {
|
||||
int nextCounter = device.getNextPollCounterSec() - 1;
|
||||
if (nextCounter < 0 && device instanceof DDISimulatedDevice) {
|
||||
if (nextCounter < 0) {
|
||||
try {
|
||||
pollService.submit(() -> ((DDISimulatedDevice) device).poll());
|
||||
pollService.submit(() -> device.poll());
|
||||
} catch (final IllegalStateException e) {
|
||||
LOGGER.trace("Device could not be polled", e);
|
||||
}
|
||||
nextCounter = ((DDISimulatedDevice) device).getPollDelaySec();
|
||||
nextCounter = device.getPollDelaySec();
|
||||
}
|
||||
|
||||
device.setNextPollCounterSec(nextCounter);
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.simulator;
|
||||
import java.net.URL;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
||||
import org.eclipse.hawkbit.simulator.http.ControllerResource;
|
||||
import org.eclipse.hawkbit.simulator.http.GatewayTokenInterceptor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -28,6 +29,9 @@ public class SimulatedDeviceFactory {
|
||||
@Autowired
|
||||
private DeviceSimulatorUpdater deviceUpdater;
|
||||
|
||||
@Autowired
|
||||
private SpSenderService spSenderService;
|
||||
|
||||
/**
|
||||
* Creating a simulated devices.
|
||||
*
|
||||
@@ -55,7 +59,7 @@ public class SimulatedDeviceFactory {
|
||||
* the protocol which should be used be the simulated device
|
||||
* @param pollDelaySec
|
||||
* the poll delay time in seconds which should be used for
|
||||
* {@link DDISimulatedDevice}s
|
||||
* {@link DDISimulatedDevice}s and {@link DMFSimulatedDevice}
|
||||
* @param baseEndpoint
|
||||
* the http base endpoint which should be used for
|
||||
* {@link DDISimulatedDevice}s
|
||||
@@ -68,7 +72,7 @@ public class SimulatedDeviceFactory {
|
||||
final int pollDelaySec, final URL baseEndpoint, final String gatewayToken) {
|
||||
switch (protocol) {
|
||||
case DMF_AMQP:
|
||||
return new DMFSimulatedDevice(id, tenant);
|
||||
return new DMFSimulatedDevice(id, tenant, spSenderService, pollDelaySec);
|
||||
case DDI_HTTP:
|
||||
final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger())
|
||||
.requestInterceptor(new GatewayTokenInterceptor(gatewayToken)).logLevel(Logger.Level.BASIC)
|
||||
|
||||
@@ -66,7 +66,7 @@ public class SimulationController {
|
||||
@RequestParam(value = "tenant", defaultValue = "DEFAULT") final String tenant,
|
||||
@RequestParam(value = "api", defaultValue = "dmf") final String api,
|
||||
@RequestParam(value = "endpoint", defaultValue = "http://localhost:8080") final String endpoint,
|
||||
@RequestParam(value = "polldelay", defaultValue = "30") final int pollDelay,
|
||||
@RequestParam(value = "polldelay", defaultValue = "1800") final int pollDelay,
|
||||
@RequestParam(value = "gatewaytoken", defaultValue = "") final String gatewayToken)
|
||||
throws MalformedURLException {
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.simulator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
@@ -68,9 +69,9 @@ public class SimulationProperties {
|
||||
private String endpoint = "http://localhost:8080";
|
||||
|
||||
/**
|
||||
* Poll time in case of DDI API based simulation.
|
||||
* Poll time in {@link TimeUnit#SECONDS} for simulated devices.
|
||||
*/
|
||||
private int pollDelay = 30;
|
||||
private int pollDelay = (int) TimeUnit.MINUTES.toSeconds(30);
|
||||
|
||||
/**
|
||||
* Optional gateway token for DDI API based simulation.
|
||||
|
||||
@@ -12,52 +12,103 @@ import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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.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.RabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
|
||||
/**
|
||||
* The spring AMQP configuration to use a AMQP for communication with SP update
|
||||
* server.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(AmqpProperties.class)
|
||||
public class AmqpConfiguration {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AmqpConfiguration.class);
|
||||
|
||||
@Autowired
|
||||
protected AmqpProperties amqpProperties;
|
||||
|
||||
@Autowired
|
||||
private ConnectionFactory connectionFactory;
|
||||
|
||||
@Autowired
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
|
||||
/**
|
||||
* Create jackson message converter bean.
|
||||
*
|
||||
* @return the jackson message converter
|
||||
* @return {@link RabbitTemplate} with automatic retry, published confirms
|
||||
* and {@link Jackson2JsonMessageConverter}.
|
||||
*/
|
||||
@Bean
|
||||
public MessageConverter jsonMessageConverter() {
|
||||
final Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
|
||||
rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
|
||||
return jackson2JsonMessageConverter;
|
||||
public RabbitTemplate rabbitTemplate() {
|
||||
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
|
||||
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
|
||||
|
||||
final RetryTemplate retryTemplate = new RetryTemplate();
|
||||
retryTemplate.setBackOffPolicy(new ExponentialBackOffPolicy());
|
||||
rabbitTemplate.setRetryTemplate(retryTemplate);
|
||||
|
||||
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
|
||||
if (ack) {
|
||||
LOGGER.debug("Message with correlation ID {} confirmed by broker.", correlationData.getId());
|
||||
} else {
|
||||
LOGGER.error("Broker is unable to handle message with correlation ID {} : {}", correlationData.getId(),
|
||||
cause);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return rabbitTemplate;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class RabbitConnectionFactoryCreator {
|
||||
|
||||
/**
|
||||
* {@link ConnectionFactory} with enabled publisher confirms and
|
||||
* heartbeat.
|
||||
*
|
||||
* @param config
|
||||
* with standard {@link RabbitProperties}
|
||||
* @return {@link ConnectionFactory}
|
||||
*/
|
||||
@Bean
|
||||
public ConnectionFactory rabbitConnectionFactory(final RabbitProperties config) {
|
||||
final CachingConnectionFactory factory = new CachingConnectionFactory();
|
||||
factory.setRequestedHeartBeat(60);
|
||||
factory.setPublisherConfirms(true);
|
||||
|
||||
final String addresses = config.getAddresses();
|
||||
factory.setAddresses(addresses);
|
||||
if (config.getHost() != null) {
|
||||
factory.setHost(config.getHost());
|
||||
factory.setPort(config.getPort());
|
||||
}
|
||||
if (config.getUsername() != null) {
|
||||
factory.setUsername(config.getUsername());
|
||||
}
|
||||
if (config.getPassword() != null) {
|
||||
factory.setPassword(config.getPassword());
|
||||
}
|
||||
if (config.getVirtualHost() != null) {
|
||||
factory.setVirtualHost(config.getVirtualHost());
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,10 +187,10 @@ public class AmqpConfiguration {
|
||||
@Bean(name = { "listenerContainerFactory" })
|
||||
public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
|
||||
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
|
||||
containerFactory.setDefaultRequeueRejected(false);
|
||||
containerFactory.setDefaultRequeueRejected(true);
|
||||
containerFactory.setConnectionFactory(connectionFactory);
|
||||
containerFactory.setConcurrentConsumers(20);
|
||||
containerFactory.setMaxConcurrentConsumers(20);
|
||||
containerFactory.setConcurrentConsumers(3);
|
||||
containerFactory.setMaxConcurrentConsumers(10);
|
||||
containerFactory.setPrefetchCount(20);
|
||||
return containerFactory;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator.amqp;
|
||||
|
||||
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
@@ -55,7 +56,7 @@ public abstract class ReceiverService extends MessageService {
|
||||
if (contentType != null && contentType.contains("json")) {
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("Content-Type is not JSON compatible");
|
||||
throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,9 +8,14 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator.amqp;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.rabbit.support.CorrelationData;
|
||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@@ -22,6 +27,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
*/
|
||||
public abstract class SenderService extends MessageService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SenderService.class);
|
||||
|
||||
/**
|
||||
* Constructor for sender service.
|
||||
*
|
||||
@@ -40,18 +47,26 @@ public abstract class SenderService extends MessageService {
|
||||
/**
|
||||
* Send a message if the message is not null.
|
||||
*
|
||||
* @param adress
|
||||
* @param address
|
||||
* the exchange name
|
||||
* @param message
|
||||
* the amqp message which will be send if its not null
|
||||
*/
|
||||
public void sendMessage(final String adress, final Message message) {
|
||||
public void sendMessage(final String address, final Message message) {
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME);
|
||||
rabbitTemplate.setExchange(adress);
|
||||
rabbitTemplate.send(message);
|
||||
final String correlationId = UUID.randomUUID().toString();
|
||||
message.getMessageProperties().setCorrelationId(correlationId.getBytes());
|
||||
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("Sending message {} to exchange {} with correlationId {}", message, address, correlationId);
|
||||
} else {
|
||||
LOGGER.debug("Sending message to exchange {} with correlationId {}", address, correlationId);
|
||||
}
|
||||
|
||||
rabbitTemplate.send(address, null, message, new CorrelationData(correlationId));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,14 +35,21 @@ import com.google.common.collect.Lists;
|
||||
public class SpReceiverService extends ReceiverService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ReceiverService.class);
|
||||
|
||||
public static final String SOFTWARE_MODULE_FIRMWARE = "firmware";
|
||||
|
||||
private final SpSenderService spSenderService;
|
||||
|
||||
private final DeviceSimulatorUpdater deviceUpdater;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param rabbitTemplate
|
||||
* for sending messages
|
||||
* @param amqpProperties
|
||||
* for amqp configuration
|
||||
* @param spSenderService
|
||||
* to send messages
|
||||
* @param deviceUpdater
|
||||
* simulator service for updates
|
||||
*/
|
||||
@Autowired
|
||||
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator.event;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
||||
*/
|
||||
public class NextPollCounterUpdate {
|
||||
|
||||
private final List<AbstractSimulatedDevice> devices;
|
||||
private final Collection<AbstractSimulatedDevice> devices;
|
||||
|
||||
/**
|
||||
* Creates poll timer update event.
|
||||
@@ -28,14 +28,14 @@ public class NextPollCounterUpdate {
|
||||
* @param devices
|
||||
* the devices which progress has been updated
|
||||
*/
|
||||
public NextPollCounterUpdate(final List<AbstractSimulatedDevice> devices) {
|
||||
public NextPollCounterUpdate(final Collection<AbstractSimulatedDevice> devices) {
|
||||
this.devices = devices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the devices of the event
|
||||
*/
|
||||
public List<AbstractSimulatedDevice> getDevices() {
|
||||
public Collection<AbstractSimulatedDevice> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,6 @@ public class GenerateDialog extends Window {
|
||||
|
||||
pollDelayTextField = createRequiredTextfield("poll delay (sec)", new ObjectProperty<Integer>(10),
|
||||
FontAwesome.CLOCK_O, new RangeValidator<Integer>("Must be between 1 and 60", Integer.class, 1, 60));
|
||||
pollDelayTextField.setVisible(false);
|
||||
|
||||
pollUrlTextField = createRequiredTextfield("base poll URL endpoint", "http://localhost:8080",
|
||||
FontAwesome.FLAG_O, new RegexpValidator(
|
||||
@@ -193,7 +192,6 @@ public class GenerateDialog extends Window {
|
||||
protocolGroup.select(Protocol.DMF_AMQP);
|
||||
protocolGroup.addValueChangeListener(event -> {
|
||||
final boolean directDeviceOptionSelected = event.getProperty().getValue().equals(Protocol.DDI_HTTP);
|
||||
pollDelayTextField.setVisible(directDeviceOptionSelected);
|
||||
pollUrlTextField.setVisible(directDeviceOptionSelected);
|
||||
gatewayTokenTextField.setVisible(directDeviceOptionSelected);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.simulator.ui;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
||||
@@ -167,7 +167,7 @@ public class SimulatorView extends VerticalLayout implements View {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Subscribe
|
||||
public void pollCounterUpdate(final NextPollCounterUpdate update) {
|
||||
final List<AbstractSimulatedDevice> devices = update.getDevices();
|
||||
final Collection<AbstractSimulatedDevice> devices = update.getDevices();
|
||||
this.getUI().access(() -> devices.forEach(device -> {
|
||||
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
|
||||
if (item != null) {
|
||||
|
||||
@@ -24,7 +24,6 @@ spring.rabbitmq.virtualHost=/
|
||||
spring.rabbitmq.host=localhost
|
||||
spring.rabbitmq.port=5672
|
||||
spring.rabbitmq.dynamic=true
|
||||
spring.rabbitmq.listener.prefetch=100
|
||||
|
||||
security.basic.enabled=false
|
||||
server.port=8083
|
||||
|
||||
@@ -19,10 +19,7 @@
|
||||
|
||||
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN" />
|
||||
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN" />
|
||||
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
|
||||
|
||||
<!-- Security Log with hints on potential attacks -->
|
||||
<logger name="server-security" level="INFO" />
|
||||
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
|
||||
|
||||
<Root level="INFO">
|
||||
<AppenderRef ref="Console" />
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
/**
|
||||
* Client binding for the DistributionSet resource of the management API.
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING)
|
||||
public interface MgmtDistributionSetClientResource extends MgmtDistributionSetRestApi {
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
/**
|
||||
* Client binding for the DistributionSetTag resource of the management API.
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING)
|
||||
public interface MgmtDistributionSetTagClientResource extends MgmtDistributionSetTagRestApi {
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
* Client binding for the DistributionSetType resource of the management API.
|
||||
*
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_REQUEST_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_REQUEST_MAPPING)
|
||||
public interface MgmtDistributionSetTypeClientResource extends MgmtDistributionSetTypeRestApi {
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
* A feign-client interface declaration which allows to build a feign-client
|
||||
* stub.
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING)
|
||||
public interface MgmtDownloadArtifactClientResource extends MgmtDownloadArtifactRestApi {
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING_BASE)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING_BASE)
|
||||
public interface MgmtDownloadClientResource extends MgmtDownloadRestApi {
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
/**
|
||||
* Client binding for the Rollout resource of the management API.
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING)
|
||||
public interface MgmtRolloutClientResource extends MgmtRolloutRestApi {
|
||||
}
|
||||
|
||||
@@ -24,9 +24,10 @@ import feign.Param;
|
||||
/**
|
||||
* Client binding for the SoftwareModule resource of the management API.
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING)
|
||||
public interface MgmtSoftwareModuleClientResource extends MgmtSoftwareModuleRestApi {
|
||||
|
||||
@Override
|
||||
@RequestMapping(method = RequestMethod.POST, value = "/{softwareModuleId}/artifacts")
|
||||
ResponseEntity<MgmtArtifact> uploadArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||
@Param("file") final MultipartFile file,
|
||||
|
||||
@@ -15,6 +15,6 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
/**
|
||||
* Client binding for the SoftwareModuleType resource of the management API.
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.SOFTWAREMODULETYPE_V1_REQUEST_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.SOFTWAREMODULETYPE_V1_REQUEST_MAPPING)
|
||||
public interface MgmtSoftwareModuleTypeClientResource extends MgmtSoftwareModuleTypeRestApi {
|
||||
}
|
||||
|
||||
@@ -16,6 +16,6 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
* Client binding for the {@link MgmtSystemRestApi}.
|
||||
*
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING)
|
||||
public interface MgmtSystemClientResource extends MgmtSystemRestApi {
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
* Client binding for the {@link MgmtSystemManagementRestApi}.
|
||||
*
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.SYSTEM_ADMIN_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.SYSTEM_ADMIN_MAPPING)
|
||||
public interface MgmtSystemManagementClientResource extends MgmtSystemManagementRestApi {
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
/**
|
||||
* Client binding for the Target resource of the management API.
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.TARGET_V1_REQUEST_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.TARGET_V1_REQUEST_MAPPING)
|
||||
public interface MgmtTargetClientResource extends MgmtTargetRestApi {
|
||||
}
|
||||
|
||||
@@ -15,6 +15,6 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
/**
|
||||
* Client binding for the TargetTag resource of the management API.
|
||||
*/
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}/" + MgmtRestConstants.TARGET_TAG_V1_REQUEST_MAPPING)
|
||||
@FeignClient(url = "${hawkbit.url:localhost:8080}" + MgmtRestConstants.TARGET_TAG_V1_REQUEST_MAPPING)
|
||||
public interface MgmtTargetTagClientResource extends MgmtTargetTagRestApi {
|
||||
}
|
||||
|
||||
@@ -84,32 +84,48 @@ public class DistributionSetBuilder {
|
||||
* @return a single entry list of {@link MgmtDistributionSetRequestBodyPost}
|
||||
*/
|
||||
public List<MgmtDistributionSetRequestBodyPost> build() {
|
||||
return Lists.newArrayList(doBuild(name));
|
||||
return Lists.newArrayList(doBuild(""));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of multiple {@link MgmtDistributionSetRequestBodyPost} to
|
||||
* create multiple distribution sets at once. An increasing number will be
|
||||
* added to the name of the distribution set. The version and type will
|
||||
* remain the same.
|
||||
* used for version of the distribution set. The name and type will remain
|
||||
* the same.
|
||||
*
|
||||
* @param count
|
||||
* the amount of distribution sets body which should be created
|
||||
* @return a list of {@link MgmtDistributionSetRequestBodyPost}
|
||||
*/
|
||||
public List<MgmtDistributionSetRequestBodyPost> buildAsList(final int count) {
|
||||
return buildAsList(0, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of multiple {@link MgmtDistributionSetRequestBodyPost} to
|
||||
* create multiple distribution sets at once. An increasing number will be
|
||||
* used for version of the distribution set starting from given offset. The
|
||||
* name and type will remain the same.
|
||||
*
|
||||
* @param count
|
||||
* the amount of distribution sets body which should be created
|
||||
* @param offset
|
||||
* for for index start
|
||||
* @return a list of {@link MgmtDistributionSetRequestBodyPost}
|
||||
*/
|
||||
public List<MgmtDistributionSetRequestBodyPost> buildAsList(final int offset, final int count) {
|
||||
final ArrayList<MgmtDistributionSetRequestBodyPost> bodyList = Lists.newArrayList();
|
||||
for (int index = 0; index < count; index++) {
|
||||
bodyList.add(doBuild(name + index));
|
||||
for (int index = offset; index < count + offset; index++) {
|
||||
bodyList.add(doBuild(String.valueOf(index)));
|
||||
}
|
||||
|
||||
return bodyList;
|
||||
}
|
||||
|
||||
private MgmtDistributionSetRequestBodyPost doBuild(final String prefixName) {
|
||||
private MgmtDistributionSetRequestBodyPost doBuild(final String suffix) {
|
||||
final MgmtDistributionSetRequestBodyPost body = new MgmtDistributionSetRequestBodyPost();
|
||||
body.setName(prefixName);
|
||||
body.setVersion(version);
|
||||
body.setName(name);
|
||||
body.setVersion(version + suffix);
|
||||
body.setType(type);
|
||||
body.setDescription(description);
|
||||
body.setModules(modules);
|
||||
|
||||
@@ -101,7 +101,7 @@ public class DistributionSetTypeBuilder {
|
||||
* {@link MgmtDistributionSetTypeRequestBodyPost}
|
||||
*/
|
||||
public List<MgmtDistributionSetTypeRequestBodyPost> build() {
|
||||
return Lists.newArrayList(doBuild(name, key));
|
||||
return Lists.newArrayList(doBuild(""));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,16 +118,16 @@ public class DistributionSetTypeBuilder {
|
||||
public List<MgmtDistributionSetTypeRequestBodyPost> buildAsList(final int count) {
|
||||
final ArrayList<MgmtDistributionSetTypeRequestBodyPost> bodyList = Lists.newArrayList();
|
||||
for (int index = 0; index < count; index++) {
|
||||
bodyList.add(doBuild(name + index, key + index));
|
||||
bodyList.add(doBuild(String.valueOf(index)));
|
||||
}
|
||||
return bodyList;
|
||||
|
||||
}
|
||||
|
||||
private MgmtDistributionSetTypeRequestBodyPost doBuild(final String prefixName, final String prefixKey) {
|
||||
private MgmtDistributionSetTypeRequestBodyPost doBuild(final String suffix) {
|
||||
final MgmtDistributionSetTypeRequestBodyPost body = new MgmtDistributionSetTypeRequestBodyPost();
|
||||
body.setKey(prefixKey);
|
||||
body.setName(prefixName);
|
||||
body.setKey(key + suffix);
|
||||
body.setName(name + suffix);
|
||||
body.setDescription(description);
|
||||
body.setMandatorymodules(mandatorymodules);
|
||||
body.setOptionalmodules(optionalmodules);
|
||||
|
||||
@@ -90,13 +90,13 @@ public class SoftwareModuleBuilder {
|
||||
* @return a single entry list of {@link MgmtSoftwareModuleRequestBodyPost}
|
||||
*/
|
||||
public List<MgmtSoftwareModuleRequestBodyPost> build() {
|
||||
return Lists.newArrayList(doBuild(name));
|
||||
return Lists.newArrayList(doBuild(""));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of multiple {@link MgmtSoftwareModuleRequestBodyPost} to
|
||||
* create multiple software module at once. An increasing number will be
|
||||
* added to the name of the software module. The version and type will
|
||||
* added to the version of the software module. The name and type will
|
||||
* remain the same.
|
||||
*
|
||||
* @param count
|
||||
@@ -106,16 +106,16 @@ public class SoftwareModuleBuilder {
|
||||
public List<MgmtSoftwareModuleRequestBodyPost> buildAsList(final int count) {
|
||||
final ArrayList<MgmtSoftwareModuleRequestBodyPost> bodyList = Lists.newArrayList();
|
||||
for (int index = 0; index < count; index++) {
|
||||
bodyList.add(doBuild(name + index));
|
||||
bodyList.add(doBuild(String.valueOf(index)));
|
||||
}
|
||||
|
||||
return bodyList;
|
||||
}
|
||||
|
||||
private MgmtSoftwareModuleRequestBodyPost doBuild(final String prefixName) {
|
||||
private MgmtSoftwareModuleRequestBodyPost doBuild(final String suffix) {
|
||||
final MgmtSoftwareModuleRequestBodyPost body = new MgmtSoftwareModuleRequestBodyPost();
|
||||
body.setName(prefixName);
|
||||
body.setVersion(version);
|
||||
body.setName(name);
|
||||
body.setVersion(version + suffix);
|
||||
body.setType(type);
|
||||
body.setVendor(vendor);
|
||||
body.setDescription(description);
|
||||
|
||||
@@ -69,7 +69,7 @@ public class SoftwareModuleTypeBuilder {
|
||||
* {@link MgmtSoftwareModuleTypeRequestBodyPost}
|
||||
*/
|
||||
public List<MgmtSoftwareModuleTypeRequestBodyPost> build() {
|
||||
return Lists.newArrayList(doBuild(key, name));
|
||||
return Lists.newArrayList(doBuild(""));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,15 +85,15 @@ public class SoftwareModuleTypeBuilder {
|
||||
public List<MgmtSoftwareModuleTypeRequestBodyPost> buildAsList(final int count) {
|
||||
final ArrayList<MgmtSoftwareModuleTypeRequestBodyPost> bodyList = Lists.newArrayList();
|
||||
for (int index = 0; index < count; index++) {
|
||||
bodyList.add(doBuild(key + index, name + index));
|
||||
bodyList.add(doBuild(String.valueOf(index)));
|
||||
}
|
||||
return bodyList;
|
||||
}
|
||||
|
||||
private MgmtSoftwareModuleTypeRequestBodyPost doBuild(final String prefixKey, final String prefixName) {
|
||||
private MgmtSoftwareModuleTypeRequestBodyPost doBuild(final String suffix) {
|
||||
final MgmtSoftwareModuleTypeRequestBodyPost body = new MgmtSoftwareModuleTypeRequestBodyPost();
|
||||
body.setKey(prefixKey);
|
||||
body.setName(prefixName);
|
||||
body.setKey(key + suffix);
|
||||
body.setName(name + suffix);
|
||||
body.setDescription(description);
|
||||
body.setMaxAssignments(maxAssignments);
|
||||
return body;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.client.resource.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeRequestBodyPost;
|
||||
@@ -28,6 +27,7 @@ public class TargetBuilder {
|
||||
private String controllerId;
|
||||
private String name;
|
||||
private String description;
|
||||
private String address;
|
||||
|
||||
/**
|
||||
* @param controllerId
|
||||
@@ -49,6 +49,16 @@ public class TargetBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param address
|
||||
* the address of the target
|
||||
* @return the builder itself
|
||||
*/
|
||||
public TargetBuilder address(final String address) {
|
||||
this.address = address;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param description
|
||||
* the description of the target
|
||||
@@ -66,32 +76,54 @@ public class TargetBuilder {
|
||||
* @return a single entry list of {@link MgmtTargetRequestBody}
|
||||
*/
|
||||
public List<MgmtTargetRequestBody> build() {
|
||||
return Lists.newArrayList(doBuild(controllerId));
|
||||
return Lists.newArrayList(doBuild(""));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of multiple {@link MgmtTargetRequestBody} to create
|
||||
* multiple targets at once. An increasing number will be added to the
|
||||
* controllerId of the target. The name and description will remain.
|
||||
* controllerId and name of the target. The description will remain.
|
||||
*
|
||||
* @param count
|
||||
* the amount of software module type bodies which should be
|
||||
* created
|
||||
* the amount of target bodies which should be created
|
||||
* @param offset
|
||||
* for
|
||||
* @return a list of {@link MgmtSoftwareModuleTypeRequestBodyPost}
|
||||
*/
|
||||
public List<MgmtTargetRequestBody> buildAsList(final int count) {
|
||||
final ArrayList<MgmtTargetRequestBody> bodyList = Lists.newArrayList();
|
||||
for (int index = 0; index < count; index++) {
|
||||
bodyList.add(doBuild(controllerId + index));
|
||||
|
||||
return buildAsList(0, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of multiple {@link MgmtTargetRequestBody} to create
|
||||
* multiple targets at once. An increasing number will be added to the
|
||||
* controllerId and name of the target starting from the provided offset.
|
||||
* The description will remain.
|
||||
*
|
||||
* @param count
|
||||
* the amount of target bodies which should be created
|
||||
* @param offset
|
||||
* for for index start
|
||||
* @return a list of {@link MgmtSoftwareModuleTypeRequestBodyPost}
|
||||
*/
|
||||
public List<MgmtTargetRequestBody> buildAsList(final int offset, final int count) {
|
||||
final List<MgmtTargetRequestBody> bodyList = Lists.newArrayList();
|
||||
for (int index = offset; index < count + offset; index++) {
|
||||
bodyList.add(doBuild(String.valueOf(index)));
|
||||
}
|
||||
return bodyList;
|
||||
}
|
||||
|
||||
private MgmtTargetRequestBody doBuild(final String prefixControllerId) {
|
||||
private MgmtTargetRequestBody doBuild(final String suffix) {
|
||||
final MgmtTargetRequestBody body = new MgmtTargetRequestBody();
|
||||
body.setControllerId(prefixControllerId);
|
||||
body.setName(name);
|
||||
body.setControllerId(controllerId + suffix);
|
||||
if (name == null) {
|
||||
name = controllerId;
|
||||
}
|
||||
body.setName(name + suffix);
|
||||
body.setDescription(description);
|
||||
body.setAddress(address);
|
||||
return body;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,23 +9,40 @@
|
||||
package org.eclipse.hawkbit.mgmt.client;
|
||||
|
||||
import org.eclipse.hawkbit.feign.core.client.FeignClientConfiguration;
|
||||
import org.eclipse.hawkbit.feign.core.client.IgnoreMultipleConsumersProducersSpringMvcContract;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtDistributionSetClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtRolloutClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtSoftwareModuleClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtTargetClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.scenarios.ConfigurableScenario;
|
||||
import org.eclipse.hawkbit.mgmt.client.scenarios.CreateStartedRolloutExample;
|
||||
import org.eclipse.hawkbit.mgmt.client.scenarios.GettingStartedDefaultScenario;
|
||||
import org.eclipse.hawkbit.mgmt.client.scenarios.upload.FeignMultipartEncoder;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.hateoas.hal.Jackson2HalModule;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.Logger;
|
||||
import feign.auth.BasicAuthRequestInterceptor;
|
||||
import feign.jackson.JacksonDecoder;
|
||||
import feign.slf4j.Slf4jLogger;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableFeignClients
|
||||
@EnableFeignClients("org.eclipse.hawkbit.mgmt.client.resource")
|
||||
@EnableConfigurationProperties(ClientConfigurationProperties.class)
|
||||
@Configuration
|
||||
@AutoConfigureAfter(FeignClientConfiguration.class)
|
||||
@@ -36,7 +53,7 @@ public class Application implements CommandLineRunner {
|
||||
private ClientConfigurationProperties configuration;
|
||||
|
||||
@Autowired
|
||||
private GettingStartedDefaultScenario gettingStarted;
|
||||
private ConfigurableScenario configuredScenario;
|
||||
|
||||
@Autowired
|
||||
private CreateStartedRolloutExample gettingStartedRolloutScenario;
|
||||
@@ -51,9 +68,8 @@ public class Application implements CommandLineRunner {
|
||||
// run the create and start rollout example
|
||||
gettingStartedRolloutScenario.run();
|
||||
} else {
|
||||
// run the getting started scenario which creates a setup of
|
||||
// distribution set and software modules to be used
|
||||
gettingStarted.run();
|
||||
// run the configured scenario from properties
|
||||
configuredScenario.run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,8 +79,13 @@ public class Application implements CommandLineRunner {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GettingStartedDefaultScenario gettingStartedDefaultScenario() {
|
||||
return new GettingStartedDefaultScenario();
|
||||
public ConfigurableScenario configurableScenario(final MgmtDistributionSetClientResource distributionSetResource,
|
||||
@Qualifier("mgmtSoftwareModuleClientResource") final MgmtSoftwareModuleClientResource softwareModuleResource,
|
||||
@Qualifier("uploadSoftwareModule") final MgmtSoftwareModuleClientResource uploadSoftwareModule,
|
||||
final MgmtTargetClientResource targetResource, final MgmtRolloutClientResource rolloutResource,
|
||||
final ClientConfigurationProperties clientConfigurationProperties) {
|
||||
return new ConfigurableScenario(distributionSetResource, softwareModuleResource, uploadSoftwareModule,
|
||||
targetResource, rolloutResource, clientConfigurationProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -72,7 +93,27 @@ public class Application implements CommandLineRunner {
|
||||
return new CreateStartedRolloutExample();
|
||||
}
|
||||
|
||||
private boolean containsArg(final String containsArg, final String... args) {
|
||||
@Bean
|
||||
public Logger.Level feignLoggerLevel() {
|
||||
return Logger.Level.FULL;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MgmtSoftwareModuleClientResource uploadSoftwareModule() {
|
||||
final ObjectMapper mapper = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.registerModule(new Jackson2HalModule());
|
||||
|
||||
return Feign.builder().contract(new IgnoreMultipleConsumersProducersSpringMvcContract())
|
||||
.requestInterceptor(
|
||||
new BasicAuthRequestInterceptor(configuration.getUsername(), configuration.getPassword()))
|
||||
.logger(new Slf4jLogger()).encoder(new FeignMultipartEncoder())
|
||||
.decoder(new ResponseEntityDecoder(new JacksonDecoder(mapper)))
|
||||
.target(MgmtSoftwareModuleClientResource.class,
|
||||
configuration.getUrl() + MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING);
|
||||
}
|
||||
|
||||
private static boolean containsArg(final String containsArg, final String... args) {
|
||||
for (final String arg : args) {
|
||||
if (arg.equalsIgnoreCase(containsArg)) {
|
||||
return true;
|
||||
@@ -80,4 +121,4 @@ public class Application implements CommandLineRunner {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.mgmt.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
@@ -34,6 +37,142 @@ public class ClientConfigurationProperties {
|
||||
private String password = "admin"; // NOSONAR this password is only used for
|
||||
// examples
|
||||
|
||||
private final List<Scenario> scenarios = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Simulation {@link Scenario}.
|
||||
*
|
||||
*/
|
||||
public static class Scenario {
|
||||
private boolean cleanRepository;
|
||||
private int targets = 100;
|
||||
private int distributionSets = 10;
|
||||
private int appModulesPerDistributionSet = 2;
|
||||
private String dsName = "Package";
|
||||
private String smSwName = "Application";
|
||||
private String smFwName = "Firmware";
|
||||
private String targetName = "Device";
|
||||
private int artifactsPerSM = 1;
|
||||
private String targetAddress = "amqp:/simulator.replyTo";
|
||||
private boolean runRollouts = true;
|
||||
private int rolloutDeploymentGroups = 4;
|
||||
|
||||
/**
|
||||
* Artifact size. Values can use the suffixed "MB" or "KB" to indicate a
|
||||
* Megabyte or Kilobyte size.
|
||||
*/
|
||||
private String artifactSize = "1MB";
|
||||
|
||||
public boolean isCleanRepository() {
|
||||
return cleanRepository;
|
||||
}
|
||||
|
||||
public void setCleanRepository(final boolean cleanRepository) {
|
||||
this.cleanRepository = cleanRepository;
|
||||
}
|
||||
|
||||
public int getRolloutDeploymentGroups() {
|
||||
return rolloutDeploymentGroups;
|
||||
}
|
||||
|
||||
public void setRolloutDeploymentGroups(final int rolloutDeploymentGroups) {
|
||||
this.rolloutDeploymentGroups = rolloutDeploymentGroups;
|
||||
}
|
||||
|
||||
public boolean isRunRollouts() {
|
||||
return runRollouts;
|
||||
}
|
||||
|
||||
public void setRunRollouts(final boolean runRollouts) {
|
||||
this.runRollouts = runRollouts;
|
||||
}
|
||||
|
||||
public String getTargetAddress() {
|
||||
return targetAddress;
|
||||
}
|
||||
|
||||
public void setTargetAddress(final String targetAddress) {
|
||||
this.targetAddress = targetAddress;
|
||||
}
|
||||
|
||||
public int getArtifactsPerSM() {
|
||||
return artifactsPerSM;
|
||||
}
|
||||
|
||||
public void setArtifactsPerSM(final int artifactsPerSM) {
|
||||
this.artifactsPerSM = artifactsPerSM;
|
||||
}
|
||||
|
||||
public String getArtifactSize() {
|
||||
return artifactSize;
|
||||
}
|
||||
|
||||
public void setArtifactSize(final String artifactSize) {
|
||||
this.artifactSize = artifactSize;
|
||||
}
|
||||
|
||||
public String getTargetName() {
|
||||
return targetName;
|
||||
}
|
||||
|
||||
public void setTargetName(final String targetName) {
|
||||
this.targetName = targetName;
|
||||
}
|
||||
|
||||
public String getDsName() {
|
||||
return dsName;
|
||||
}
|
||||
|
||||
public void setDsName(final String dsName) {
|
||||
this.dsName = dsName;
|
||||
}
|
||||
|
||||
public String getSmSwName() {
|
||||
return smSwName;
|
||||
}
|
||||
|
||||
public void setSmSwName(final String smSwName) {
|
||||
this.smSwName = smSwName;
|
||||
}
|
||||
|
||||
public String getSmFwName() {
|
||||
return smFwName;
|
||||
}
|
||||
|
||||
public void setSmFwName(final String smFwName) {
|
||||
this.smFwName = smFwName;
|
||||
}
|
||||
|
||||
public int getTargets() {
|
||||
return targets;
|
||||
}
|
||||
|
||||
public int getDistributionSets() {
|
||||
return distributionSets;
|
||||
}
|
||||
|
||||
public int getAppModulesPerDistributionSet() {
|
||||
return appModulesPerDistributionSet;
|
||||
}
|
||||
|
||||
public void setTargets(final int targets) {
|
||||
this.targets = targets;
|
||||
}
|
||||
|
||||
public void setDistributionSets(final int distributionSets) {
|
||||
this.distributionSets = distributionSets;
|
||||
}
|
||||
|
||||
public void setAppModulesPerDistributionSet(final int appModulesPerDistributionSet) {
|
||||
this.appModulesPerDistributionSet = appModulesPerDistributionSet;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<Scenario> getScenarios() {
|
||||
return scenarios;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* 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.mgmt.client.scenarios;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.client.ClientConfigurationProperties;
|
||||
import org.eclipse.hawkbit.mgmt.client.ClientConfigurationProperties.Scenario;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtDistributionSetClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtRolloutClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtSoftwareModuleClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtTargetClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.builder.DistributionSetBuilder;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.builder.RolloutBuilder;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleAssigmentBuilder;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleBuilder;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.builder.TargetBuilder;
|
||||
import org.eclipse.hawkbit.mgmt.client.scenarios.upload.ArtifactFile;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutResponseBody;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
/**
|
||||
*
|
||||
* A configurable scenario which runs the configured scenarios.
|
||||
*
|
||||
* @see {@link ClientConfigurationProperties#getScenarios()}
|
||||
*
|
||||
*/
|
||||
public class ConfigurableScenario {
|
||||
|
||||
private static final int PAGE_SIZE = 100;
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurableScenario.class);
|
||||
|
||||
private final MgmtDistributionSetClientResource distributionSetResource;
|
||||
|
||||
private final MgmtSoftwareModuleClientResource softwareModuleResource;
|
||||
|
||||
private final MgmtSoftwareModuleClientResource uploadSoftwareModule;
|
||||
|
||||
private final MgmtTargetClientResource targetResource;
|
||||
|
||||
private final MgmtRolloutClientResource rolloutResource;
|
||||
|
||||
private final ClientConfigurationProperties clientConfigurationProperties;
|
||||
|
||||
public ConfigurableScenario(final MgmtDistributionSetClientResource distributionSetResource,
|
||||
@Qualifier("mgmtSoftwareModuleClientResource") final MgmtSoftwareModuleClientResource softwareModuleResource,
|
||||
@Qualifier("uploadSoftwareModule") final MgmtSoftwareModuleClientResource uploadSoftwareModule,
|
||||
final MgmtTargetClientResource targetResource, final MgmtRolloutClientResource rolloutResource,
|
||||
final ClientConfigurationProperties clientConfigurationProperties) {
|
||||
this.distributionSetResource = distributionSetResource;
|
||||
this.softwareModuleResource = softwareModuleResource;
|
||||
this.uploadSoftwareModule = uploadSoftwareModule;
|
||||
this.targetResource = targetResource;
|
||||
this.rolloutResource = rolloutResource;
|
||||
this.clientConfigurationProperties = clientConfigurationProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the default getting started scenario.
|
||||
*/
|
||||
public void run() {
|
||||
|
||||
LOGGER.info("Running Configurable Scenario...");
|
||||
|
||||
clientConfigurationProperties.getScenarios().forEach(this::createScenario);
|
||||
}
|
||||
|
||||
private void createScenario(final Scenario scenario) {
|
||||
if (scenario.isCleanRepository()) {
|
||||
cleanRepository();
|
||||
}
|
||||
|
||||
createTargets(scenario);
|
||||
createDistributionSets(scenario);
|
||||
|
||||
if (scenario.isRunRollouts()) {
|
||||
runRollouts(scenario);
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanRepository() {
|
||||
LOGGER.info("Cleaning repository");
|
||||
deleteTargets();
|
||||
deleteRollouts();
|
||||
deleteDistributionSets();
|
||||
deleteSoftwareModules();
|
||||
LOGGER.info("Cleaning repository -> Done");
|
||||
}
|
||||
|
||||
private void deleteRollouts() {
|
||||
// TODO: complete this as soon as rollouts can be deleted
|
||||
|
||||
}
|
||||
|
||||
private void deleteSoftwareModules() {
|
||||
PagedList<MgmtSoftwareModule> modules;
|
||||
do {
|
||||
modules = softwareModuleResource.getSoftwareModules(0, PAGE_SIZE, null, null).getBody();
|
||||
modules.getContent().forEach(module -> softwareModuleResource.deleteSoftwareModule(module.getModuleId()));
|
||||
} while (modules.getTotal() > PAGE_SIZE);
|
||||
}
|
||||
|
||||
private void deleteDistributionSets() {
|
||||
PagedList<MgmtDistributionSet> distributionSets;
|
||||
do {
|
||||
distributionSets = distributionSetResource.getDistributionSets(0, PAGE_SIZE, null, null).getBody();
|
||||
distributionSets.getContent().forEach(set -> distributionSetResource.deleteDistributionSet(set.getDsId()));
|
||||
} while (distributionSets.getTotal() > PAGE_SIZE);
|
||||
}
|
||||
|
||||
private void deleteTargets() {
|
||||
PagedList<MgmtTarget> targets;
|
||||
do {
|
||||
targets = targetResource.getTargets(0, PAGE_SIZE, null, null).getBody();
|
||||
targets.getContent().forEach(target -> targetResource.deleteTarget(target.getControllerId()));
|
||||
} while (targets.getTotal() > PAGE_SIZE);
|
||||
}
|
||||
|
||||
private void runRollouts(final Scenario scenario) {
|
||||
distributionSetResource.getDistributionSets(0, scenario.getDistributionSets(), null, null).getBody()
|
||||
.getContent().forEach(set -> runRollout(set, scenario));
|
||||
|
||||
}
|
||||
|
||||
private void runRollout(final MgmtDistributionSet set, final Scenario scenario) {
|
||||
LOGGER.info("Run rollout for set {}", set.getDsId());
|
||||
// create a Rollout
|
||||
final MgmtRolloutResponseBody rolloutResponseBody = rolloutResource
|
||||
.create(new RolloutBuilder().name("Rollout" + set.getName() + set.getVersion())
|
||||
.groupSize(scenario.getRolloutDeploymentGroups()).targetFilterQuery("name==*")
|
||||
.distributionSetId(set.getDsId()).successThreshold("80").errorThreshold("5").build())
|
||||
.getBody();
|
||||
|
||||
// start the created Rollout
|
||||
rolloutResource.start(rolloutResponseBody.getRolloutId(), true);
|
||||
|
||||
// wait until rollout is complete
|
||||
do {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(35);
|
||||
} catch (final InterruptedException e) {
|
||||
LOGGER.warn("Interrupted!");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
} while (targetResource.getTargets(0, 1, null, "updateStatus==IN_SYNC").getBody().getTotal() < scenario
|
||||
.getTargets());
|
||||
LOGGER.info("Run rollout for set {} -> Done", set.getDsId());
|
||||
}
|
||||
|
||||
private void createDistributionSets(final Scenario scenario) {
|
||||
LOGGER.info("Creating {} distribution sets", scenario.getDistributionSets());
|
||||
final byte[] artifact = generateArtifact(scenario);
|
||||
|
||||
distributionSetResource.createDistributionSets(new DistributionSetBuilder().name(scenario.getDsName())
|
||||
.type("os_app").version("1.0.").buildAsList(scenario.getDistributionSets())).getBody()
|
||||
.forEach(dsSet -> {
|
||||
final List<MgmtSoftwareModule> modules = addModules(scenario, dsSet, artifact);
|
||||
|
||||
final SoftwareModuleAssigmentBuilder assign = new SoftwareModuleAssigmentBuilder();
|
||||
modules.forEach(module -> assign.id(module.getModuleId()));
|
||||
distributionSetResource.assignSoftwareModules(dsSet.getDsId(), assign.build());
|
||||
});
|
||||
|
||||
LOGGER.info("Creating {} distribution sets -> Done", scenario.getDistributionSets());
|
||||
}
|
||||
|
||||
private List<MgmtSoftwareModule> addModules(final Scenario scenario, final MgmtDistributionSet dsSet,
|
||||
final byte[] artifact) {
|
||||
final List<MgmtSoftwareModule> modules = softwareModuleResource
|
||||
.createSoftwareModules(new SoftwareModuleBuilder().name(scenario.getSmFwName() + "-os")
|
||||
.version(dsSet.getVersion()).type("os").build())
|
||||
.getBody();
|
||||
modules.addAll(softwareModuleResource.createSoftwareModules(
|
||||
new SoftwareModuleBuilder().name(scenario.getSmSwName() + "-app").version(dsSet.getVersion() + ".")
|
||||
.type("application").buildAsList(scenario.getAppModulesPerDistributionSet()))
|
||||
.getBody());
|
||||
|
||||
for (int x = 0; x < scenario.getArtifactsPerSM(); x++) {
|
||||
modules.forEach(module -> {
|
||||
final ArtifactFile file = new ArtifactFile("dummyfile.dummy", null, "multipart/form-data", artifact);
|
||||
uploadSoftwareModule.uploadArtifact(module.getModuleId(), file, null, null, null);
|
||||
});
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
private static byte[] generateArtifact(final Scenario scenario) {
|
||||
|
||||
// Exception squid:S2245 - not used for cryptographic function
|
||||
@SuppressWarnings("squid:S2245")
|
||||
final Random random = new Random();
|
||||
|
||||
// create byte array
|
||||
final byte[] nbyte = new byte[parseSize(scenario.getArtifactSize())];
|
||||
|
||||
// put the next byte in the array
|
||||
random.nextBytes(nbyte);
|
||||
|
||||
return nbyte;
|
||||
}
|
||||
|
||||
private void createTargets(final Scenario scenario) {
|
||||
LOGGER.info("Creating {} targets", scenario.getTargets());
|
||||
|
||||
for (int i = 0; i < (scenario.getTargets() / PAGE_SIZE); i++) {
|
||||
targetResource.createTargets(
|
||||
new TargetBuilder().controllerId(scenario.getTargetName()).address(scenario.getTargetAddress())
|
||||
.buildAsList(i * PAGE_SIZE, (i + 1) * PAGE_SIZE > scenario.getTargets()
|
||||
? (scenario.getTargets() - (i * PAGE_SIZE)) : PAGE_SIZE));
|
||||
}
|
||||
|
||||
LOGGER.info("Creating {} targets -> Done", scenario.getTargets());
|
||||
}
|
||||
|
||||
private static int parseSize(final String s) {
|
||||
final String size = s.toUpperCase();
|
||||
if (size.endsWith("KB")) {
|
||||
return Integer.valueOf(size.substring(0, size.length() - 2)) * 1024;
|
||||
}
|
||||
if (size.endsWith("MB")) {
|
||||
return Integer.valueOf(size.substring(0, size.length() - 2)) * 1024 * 1024;
|
||||
}
|
||||
return Integer.valueOf(size);
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import org.eclipse.hawkbit.mgmt.json.model.rollout.MgmtRolloutResponseBody;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
/**
|
||||
* Example for creating and starting a Rollout.
|
||||
@@ -36,7 +37,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
public class CreateStartedRolloutExample {
|
||||
|
||||
/* known software module type name and key */
|
||||
private static final String SM_MODULE_TYPE = "firmware";
|
||||
private static final String SM_MODULE_TYPE = "gettingstarted-rollout-example";
|
||||
|
||||
/* known distribution set type name and key */
|
||||
private static final String DS_MODULE_TYPE = SM_MODULE_TYPE;
|
||||
@@ -45,6 +46,7 @@ public class CreateStartedRolloutExample {
|
||||
private MgmtDistributionSetClientResource distributionSetResource;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("mgmtSoftwareModuleClientResource")
|
||||
private MgmtSoftwareModuleClientResource softwareModuleResource;
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
/**
|
||||
* 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.mgmt.client.scenarios;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtDistributionSetClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtDistributionSetTypeClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtSoftwareModuleClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.MgmtSoftwareModuleTypeClientResource;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.builder.DistributionSetBuilder;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.builder.DistributionSetTypeBuilder;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleAssigmentBuilder;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleBuilder;
|
||||
import org.eclipse.hawkbit.mgmt.client.resource.builder.SoftwareModuleTypeBuilder;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
*
|
||||
* Default getting started scenario.
|
||||
*
|
||||
*/
|
||||
public class GettingStartedDefaultScenario {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GettingStartedDefaultScenario.class);
|
||||
|
||||
/* known software module type name and key */
|
||||
private static final String SM_MODULE_TYPE = "gettingstarted";
|
||||
|
||||
/* known distribution set type name and key */
|
||||
private static final String DS_MODULE_TYPE = SM_MODULE_TYPE;
|
||||
|
||||
/* known distribution name of this getting started example */
|
||||
private static final String SM_EXAMPLE_NAME = "gettingstarted-example";
|
||||
|
||||
/* known distribution name of this getting started example */
|
||||
private static final String DS_EXAMPLE_NAME = SM_EXAMPLE_NAME;
|
||||
|
||||
@Autowired
|
||||
private MgmtDistributionSetClientResource distributionSetResource;
|
||||
|
||||
@Autowired
|
||||
private MgmtDistributionSetTypeClientResource distributionSetTypeResource;
|
||||
|
||||
@Autowired
|
||||
private MgmtSoftwareModuleClientResource softwareModuleResource;
|
||||
|
||||
@Autowired
|
||||
private MgmtSoftwareModuleTypeClientResource softwareModuleTypeResource;
|
||||
|
||||
/**
|
||||
* Run the default getting started scenario.
|
||||
*/
|
||||
public void run() {
|
||||
|
||||
LOGGER.info("Running Getting-Started-Scenario...");
|
||||
|
||||
// create one SoftwareModuleTypes
|
||||
LOGGER.info("Creating software module type {}", SM_MODULE_TYPE);
|
||||
final List<MgmtSoftwareModuleType> createdSoftwareModuleTypes = softwareModuleTypeResource
|
||||
.createSoftwareModuleTypes(new SoftwareModuleTypeBuilder().key(SM_MODULE_TYPE).name(SM_MODULE_TYPE)
|
||||
.maxAssignments(1).build())
|
||||
.getBody();
|
||||
|
||||
// create one DistributionSetType
|
||||
LOGGER.info("Creating distribution set type {}", DS_MODULE_TYPE);
|
||||
distributionSetTypeResource.createDistributionSetTypes(new DistributionSetTypeBuilder().key(DS_MODULE_TYPE)
|
||||
.name(DS_MODULE_TYPE).mandatorymodules(createdSoftwareModuleTypes.get(0).getModuleId()).build());
|
||||
|
||||
// create three DistributionSet
|
||||
final String dsVersion1 = "1.0.0";
|
||||
final String dsVersion2 = "2.0.0";
|
||||
final String dsVersion3 = "2.1.0";
|
||||
|
||||
LOGGER.info("Creating distribution set {}:{}", DS_EXAMPLE_NAME, dsVersion1);
|
||||
final List<MgmtDistributionSet> distributionSetsRest1 = distributionSetResource.createDistributionSets(
|
||||
new DistributionSetBuilder().name(DS_EXAMPLE_NAME).version(dsVersion1).type(DS_MODULE_TYPE).build())
|
||||
.getBody();
|
||||
|
||||
LOGGER.info("Creating distribution set {}:{}", DS_EXAMPLE_NAME, dsVersion2);
|
||||
final List<MgmtDistributionSet> distributionSetsRest2 = distributionSetResource.createDistributionSets(
|
||||
new DistributionSetBuilder().name(DS_EXAMPLE_NAME).version(dsVersion2).type(DS_MODULE_TYPE).build())
|
||||
.getBody();
|
||||
|
||||
LOGGER.info("Creating distribution set {}:{}", DS_EXAMPLE_NAME, dsVersion3);
|
||||
final List<MgmtDistributionSet> distributionSetsRest3 = distributionSetResource.createDistributionSets(
|
||||
new DistributionSetBuilder().name(DS_EXAMPLE_NAME).version(dsVersion3).type(DS_MODULE_TYPE).build())
|
||||
.getBody();
|
||||
|
||||
// create three SoftwareModules
|
||||
final String swVersion1 = "1";
|
||||
final String swVersion2 = "2";
|
||||
final String swVersion3 = "3";
|
||||
|
||||
LOGGER.info("Creating distribution set {}:{}", SM_EXAMPLE_NAME, swVersion1);
|
||||
final List<MgmtSoftwareModule> softwareModulesRest1 = softwareModuleResource.createSoftwareModules(
|
||||
new SoftwareModuleBuilder().name(SM_EXAMPLE_NAME).version(swVersion1).type(SM_MODULE_TYPE).build())
|
||||
.getBody();
|
||||
LOGGER.info("Creating distribution set {}:{}", SM_EXAMPLE_NAME, swVersion2);
|
||||
final List<MgmtSoftwareModule> softwareModulesRest2 = softwareModuleResource.createSoftwareModules(
|
||||
new SoftwareModuleBuilder().name(SM_EXAMPLE_NAME).version(swVersion2).type(SM_MODULE_TYPE).build())
|
||||
.getBody();
|
||||
LOGGER.info("Creating distribution set {}:{}", SM_EXAMPLE_NAME, swVersion3);
|
||||
final List<MgmtSoftwareModule> softwareModulesRest3 = softwareModuleResource.createSoftwareModules(
|
||||
new SoftwareModuleBuilder().name(SM_EXAMPLE_NAME).version(swVersion3).type(SM_MODULE_TYPE).build())
|
||||
.getBody();
|
||||
|
||||
// Assign SoftwareModules to DistributionSet
|
||||
LOGGER.info("Assign software module {}:{} to distribution set {}:{}", SM_EXAMPLE_NAME, swVersion1,
|
||||
DS_EXAMPLE_NAME, dsVersion1);
|
||||
distributionSetResource.assignSoftwareModules(distributionSetsRest1.get(0).getDsId(),
|
||||
new SoftwareModuleAssigmentBuilder().id(softwareModulesRest1.get(0).getModuleId()).build());
|
||||
|
||||
LOGGER.info("Assign software module {}:{} to distribution set {}:{}", SM_EXAMPLE_NAME, swVersion2,
|
||||
DS_EXAMPLE_NAME, dsVersion2);
|
||||
distributionSetResource.assignSoftwareModules(distributionSetsRest2.get(0).getDsId(),
|
||||
new SoftwareModuleAssigmentBuilder().id(softwareModulesRest2.get(0).getModuleId()).build());
|
||||
|
||||
LOGGER.info("Assign software module {}:{} to distribution set {}:{}", SM_EXAMPLE_NAME, swVersion3,
|
||||
DS_EXAMPLE_NAME, dsVersion3);
|
||||
distributionSetResource.assignSoftwareModules(distributionSetsRest3.get(0).getDsId(),
|
||||
new SoftwareModuleAssigmentBuilder().id(softwareModulesRest3.get(0).getModuleId()).build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* 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.mgmt.client.scenarios.upload;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/**
|
||||
* Implementation for {@link MultipartFile} for hawkBit artifact upload.
|
||||
*
|
||||
*/
|
||||
public class ArtifactFile implements MultipartFile {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String originalFilename;
|
||||
|
||||
private final String contentType;
|
||||
|
||||
private final byte[] content;
|
||||
|
||||
/**
|
||||
* Create a new ArtifactFile with the given content.
|
||||
*
|
||||
* @param name
|
||||
* the name of the file
|
||||
* @param content
|
||||
* the content of the file
|
||||
*/
|
||||
public ArtifactFile(final String name, final byte[] content) {
|
||||
this(name, "", null, content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ArtifactFile with the given content.
|
||||
*
|
||||
* @param name
|
||||
* of the file
|
||||
* @param originalFilename
|
||||
* the original filename (as on the client's machine)
|
||||
* @param contentType
|
||||
* the content type
|
||||
* @param content
|
||||
* of the file
|
||||
*/
|
||||
public ArtifactFile(final String name, final String originalFilename, final String contentType,
|
||||
final byte[] content) {
|
||||
Assert.hasLength(name, "Name must not be null");
|
||||
this.name = name;
|
||||
this.originalFilename = Optional.ofNullable(originalFilename).orElse("");
|
||||
this.contentType = contentType;
|
||||
this.content = Optional.ofNullable(content).orElse(new byte[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOriginalFilename() {
|
||||
return this.originalFilename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.content.length == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSize() {
|
||||
return this.content.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBytes() throws IOException {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream(this.content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferTo(final File dest) throws IOException {
|
||||
FileCopyUtils.copy(this.content, dest);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* 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.mgmt.client.scenarios.upload;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import feign.RequestTemplate;
|
||||
import feign.codec.EncodeException;
|
||||
import feign.codec.Encoder;
|
||||
|
||||
/**
|
||||
* A feign encoder implementation which handles {@link MultipartFile} body.
|
||||
*/
|
||||
public class FeignMultipartEncoder implements Encoder {
|
||||
|
||||
private final List<HttpMessageConverter<?>> converters = new RestTemplate().getMessageConverters();
|
||||
private final HttpHeaders multipartHeaders = new HttpHeaders();
|
||||
private final HttpHeaders jsonHeaders = new HttpHeaders();
|
||||
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
||||
public FeignMultipartEncoder() {
|
||||
multipartHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||
jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(final Object object, final Type bodyType, final RequestTemplate template) {
|
||||
encodeMultipartFormRequest(object, template);
|
||||
}
|
||||
|
||||
private void encodeMultipartFormRequest(final Object value, final RequestTemplate template) {
|
||||
if (value == null) {
|
||||
throw new EncodeException("Cannot encode request with null value.");
|
||||
}
|
||||
if (!isMultipartFile(value)) {
|
||||
throw new EncodeException("Only multipart can be handled by this encoder");
|
||||
}
|
||||
encodeRequest(encodeMultipartFile((MultipartFile) value), multipartHeaders, template);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void encodeRequest(final Object value, final HttpHeaders requestHeaders, final RequestTemplate template) {
|
||||
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
final HttpOutputMessage dummyRequest = new HttpOutputMessageImpl(outputStream, requestHeaders);
|
||||
try {
|
||||
final Class<?> requestType = value.getClass();
|
||||
final MediaType requestContentType = requestHeaders.getContentType();
|
||||
for (final HttpMessageConverter<?> messageConverter : converters) {
|
||||
if (messageConverter.canWrite(requestType, requestContentType)) {
|
||||
((HttpMessageConverter<Object>) messageConverter).write(value, requestContentType, dummyRequest);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (final IOException ex) {
|
||||
throw new EncodeException("Cannot encode request.", ex);
|
||||
}
|
||||
final HttpHeaders headers = dummyRequest.getHeaders();
|
||||
if (headers != null) {
|
||||
for (final Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
template.header(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
/*
|
||||
* we should use a template output stream... this will cause issues if
|
||||
* files are too big, since the whole request will be in memory.
|
||||
*/
|
||||
template.body(outputStream.toByteArray(), UTF_8);
|
||||
}
|
||||
|
||||
private MultiValueMap<String, Object> encodeMultipartFile(final MultipartFile file) {
|
||||
try {
|
||||
final MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
|
||||
multiValueMap.add("file", new MultipartFileResource(file.getName(), file.getSize(), file.getInputStream()));
|
||||
return multiValueMap;
|
||||
} catch (final IOException ex) {
|
||||
throw new EncodeException("Cannot encode request.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isMultipartFile(final Object object) {
|
||||
return object instanceof MultipartFile;
|
||||
}
|
||||
|
||||
private static final class HttpOutputMessageImpl implements HttpOutputMessage {
|
||||
|
||||
private final OutputStream body;
|
||||
private final HttpHeaders headers;
|
||||
|
||||
private HttpOutputMessageImpl(final OutputStream body, final HttpHeaders headers) {
|
||||
this.body = body;
|
||||
this.headers = headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getBody() throws IOException {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy resource class. Wraps file content and its original name.
|
||||
*/
|
||||
static class MultipartFileResource extends InputStreamResource {
|
||||
|
||||
private final String filename;
|
||||
private final long size;
|
||||
|
||||
public MultipartFileResource(final String filename, final long size, final InputStream inputStream) {
|
||||
super(inputStream);
|
||||
this.size = size;
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return this.filename;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() throws IOException {
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,16 @@
|
||||
# http://www.eclipse.org/legal/epl-v10.html
|
||||
#
|
||||
|
||||
hawkbit.url=localhost:8080
|
||||
hawkbit.url=http://localhost:8080
|
||||
hawkbit.username=admin
|
||||
hawkbit.password=admin
|
||||
|
||||
spring.main.show-banner=false
|
||||
spring.main.show-banner=false
|
||||
|
||||
hawkbit.scenarios.[0].cleanRepository=false
|
||||
hawkbit.scenarios.[0].targets=0
|
||||
hawkbit.scenarios.[0].ds-name=gettingstarted-example
|
||||
hawkbit.scenarios.[0].distribution-sets=3
|
||||
hawkbit.scenarios.[0].sm-fw-name=gettingstarted-example
|
||||
hawkbit.scenarios.[0].sm-sw-name=gettingstarted-example
|
||||
hawkbit.scenarios.[0].runRollouts=false
|
||||
@@ -10,17 +10,11 @@
|
||||
|
||||
-->
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<!-- Log message format -->
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
||||
</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<include resource="org/springframework/boot/logging/logback/base.xml" />
|
||||
|
||||
<logger name="org.eclipse.hawkbit" level="info" />
|
||||
<logger name="feign.Logger" level="debug" />
|
||||
|
||||
<root level="error">
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
|
||||
@@ -87,9 +87,7 @@ public class ArtifactStore implements ArtifactRepository {
|
||||
|
||||
/**
|
||||
* Retrieves a {@link GridFSDBFile} from the store by it's MD5 hash.
|
||||
*
|
||||
* @param tenant
|
||||
* the tenant to retrieve the artifacts from, ignore case.
|
||||
*
|
||||
* @param md5Hash
|
||||
* the md5-hash of the file to lookup.
|
||||
* @return The gridfs file object or {@code null} if no file exists.
|
||||
@@ -100,9 +98,7 @@ public class ArtifactStore implements ArtifactRepository {
|
||||
|
||||
/**
|
||||
* Retrieves a {@link GridFSDBFile} from the store by it's object id.
|
||||
*
|
||||
* @param tenant
|
||||
* the tenant to retrieve the artifacts from, ignore case.
|
||||
*
|
||||
* @param id
|
||||
* the id of the file to lookup.
|
||||
* @return The gridfs file object or {@code null} if no file exists.
|
||||
@@ -231,15 +227,13 @@ public class ArtifactStore implements ArtifactRepository {
|
||||
* @return a paged list of artifacts mapped from the given dbFiles
|
||||
*/
|
||||
private List<DbArtifact> map(final List<GridFSDBFile> dbFiles) {
|
||||
return dbFiles.stream().map(dbFile -> map(dbFile)).collect(Collectors.toList());
|
||||
return dbFiles.stream().map(this::map).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of {@link GridFSDBFile} from the store by all SHA1
|
||||
* hashes.
|
||||
*
|
||||
* @param tenant
|
||||
* the tenant to retrieve the artifacts from, ignore case.
|
||||
*
|
||||
* @param sha1Hashes
|
||||
* the sha1-hashes of the files to lookup.
|
||||
* @return list of artifacts
|
||||
@@ -252,8 +246,6 @@ public class ArtifactStore implements ArtifactRepository {
|
||||
/**
|
||||
* Retrieves a list of {@link GridFSDBFile} from the store by all ids.
|
||||
*
|
||||
* @param tenant
|
||||
* the tenant to retrieve the artifacts from, ignore case.
|
||||
* @param ids
|
||||
* the ids of the files to lookup.
|
||||
* @return list of artfiacts
|
||||
|
||||
@@ -10,18 +10,13 @@ package org.eclipse.hawkbit.artifact.repository;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Auto configuration for the {@link ArtifactStore}.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@ConditionalOnMissingBean(value = ArtifactRepository.class)
|
||||
@Import(value = MongoConfiguration.class)
|
||||
public class ArtifactStoreAutoConfiguration {
|
||||
|
||||
@@ -24,82 +24,73 @@ public enum SpServerError {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REPO_ENTITY_ALRREADY_EXISTS("hawkbit.server.error.repo.entitiyAlreayExists",
|
||||
"The given entity already exists in database"),
|
||||
SP_REPO_ENTITY_ALRREADY_EXISTS("hawkbit.server.error.repo.entitiyAlreayExists", "The given entity already exists in database"),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REPO_INVALID_TARGET_ADDRESS("hawkbit.server.error.repo.invalidTargetAddress", "The target address is not well formed"),
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REPO_ENTITY_NOT_EXISTS("hawkbit.server.error.repo.entitiyNotFound",
|
||||
"The given entity does not exist in database"),
|
||||
SP_REPO_ENTITY_NOT_EXISTS("hawkbit.server.error.repo.entitiyNotFound", "The given entity does not exist in database"),
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REPO_CONCURRENT_MODIFICATION("hawkbit.server.error.repo.concurrentModification",
|
||||
"The given entity has been changed by another user/session"),
|
||||
SP_REPO_CONCURRENT_MODIFICATION("hawkbit.server.error.repo.concurrentModification", "The given entity has been changed by another user/session"),
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REST_SORT_PARAM_SYNTAX("hawkbit.server.error.rest.param.sortParamSyntax",
|
||||
"The given sort paramter is not well formed"),
|
||||
SP_REST_SORT_PARAM_SYNTAX("hawkbit.server.error.rest.param.sortParamSyntax", "The given sort paramter is not well formed"),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REST_RSQL_SEARCH_PARAM_SYNTAX("hawkbit.server.error.rest.param.rsqlParamSyntax",
|
||||
"The given search paramter is not well formed"),
|
||||
SP_REST_RSQL_SEARCH_PARAM_SYNTAX("hawkbit.server.error.rest.param.rsqlParamSyntax", "The given search paramter is not well formed"),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REST_RSQL_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.rsqlInvalidField",
|
||||
"The given search parameter field does not exist"),
|
||||
SP_REST_RSQL_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.rsqlInvalidField", "The given search parameter field does not exist"),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REST_SORT_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.invalidField",
|
||||
"The given sort parameter field does not exist"),
|
||||
SP_REST_SORT_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.invalidField", "The given sort parameter field does not exist"),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REST_SORT_PARAM_INVALID_DIRECTION("hawkbit.server.error.rest.param.invalidDirection",
|
||||
"The given sort parameter direction does not exist"),
|
||||
SP_REST_SORT_PARAM_INVALID_DIRECTION("hawkbit.server.error.rest.param.invalidDirection", "The given sort parameter direction does not exist"),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REST_BODY_NOT_READABLE("hawkbit.server.error.rest.body.notReadable",
|
||||
"The given request body is not well formed"),
|
||||
SP_REST_BODY_NOT_READABLE("hawkbit.server.error.rest.body.notReadable", "The given request body is not well formed"),
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_ARTIFACT_UPLOAD_FAILED("hawkbit.server.error.artifact.uploadFailed",
|
||||
"Upload of artifact failed with internal server error."),
|
||||
SP_ARTIFACT_UPLOAD_FAILED("hawkbit.server.error.artifact.uploadFailed", "Upload of artifact failed with internal server error."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH("hawkbit.server.error.artifact.uploadFailed.checksum.md5.match",
|
||||
"Upload of artifact failed as the provided MD5 checksum did not match with the provided artifact."),
|
||||
SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH("hawkbit.server.error.artifact.uploadFailed.checksum.md5.match", "Upload of artifact failed as the provided MD5 checksum did not match with the provided artifact."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH("hawkbit.server.error.artifact.uploadFailed.checksum.sha1.match",
|
||||
"Upload of artifact failed as the provided SHA1 checksum did not match with the provided artifact."),
|
||||
SP_ARTIFACT_UPLOAD_FAILED_SHA1_MATCH("hawkbit.server.error.artifact.uploadFailed.checksum.sha1.match", "Upload of artifact failed as the provided SHA1 checksum did not match with the provided artifact."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_DS_CREATION_FAILED_MISSING_MODULE("hawkbit.server.error.distributionset.creationFailed.missingModule",
|
||||
"Creation if Distribution Set failed as module is missing that is configured as mandatory."),
|
||||
SP_DS_CREATION_FAILED_MISSING_MODULE("hawkbit.server.error.distributionset.creationFailed.missingModule", "Creation if Distribution Set failed as module is missing that is configured as mandatory."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_ARTIFACT_UPLOAD_FILE_LIMIT_EXCEEDED("hawkbit.server.error.artifact.uploadFailed.sizelimitexceeded",
|
||||
"Upload of artifact failed as the file exceeds its maximum permitted size"),
|
||||
SP_ARTIFACT_UPLOAD_FILE_LIMIT_EXCEEDED("hawkbit.server.error.artifact.uploadFailed.sizelimitexceeded", "Upload of artifact failed as the file exceeds its maximum permitted size"),
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -108,63 +99,53 @@ public enum SpServerError {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_ARTIFACT_DELETE_FAILED("hawkbit.server.error.artifact.deleteFailed",
|
||||
"Deletion of artifact failed with internal server error."),
|
||||
SP_ARTIFACT_DELETE_FAILED("hawkbit.server.error.artifact.deleteFailed", "Deletion of artifact failed with internal server error."),
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_ARTIFACT_LOAD_FAILED("hawkbit.server.error.artifact.loadFailed",
|
||||
"Load of artifact failed with internal server error."),
|
||||
SP_ARTIFACT_LOAD_FAILED("hawkbit.server.error.artifact.loadFailed", "Load of artifact failed with internal server error."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_ACTION_STATUS_TO_MANY_ENTRIES("hawkbit.server.error.action.status.tooManyEntries",
|
||||
"Too many status entries have been inserted."),
|
||||
SP_ACTION_STATUS_TO_MANY_ENTRIES("hawkbit.server.error.action.status.tooManyEntries", "Too many status entries have been inserted."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_ATTRIBUTES_TO_MANY_ENTRIES("hawkbit.server.error.target.attributes.tooManyEntries",
|
||||
"Too many attribute entries have been inserted."),
|
||||
SP_ATTRIBUTES_TO_MANY_ENTRIES("hawkbit.server.error.target.attributes.tooManyEntries", "Too many attribute entries have been inserted."),
|
||||
|
||||
/**
|
||||
* error message, which describes that the action can not be canceled cause
|
||||
* the action is inactive.
|
||||
*/
|
||||
SP_ACTION_NOT_CANCELABLE("hawkbit.server.error.action.notcancelable",
|
||||
"Only active actions which are in status pending are canceable."),
|
||||
SP_ACTION_NOT_CANCELABLE("hawkbit.server.error.action.notcancelable", "Only active actions which are in status pending are canceable."),
|
||||
|
||||
/**
|
||||
* error message, which describes that the action can not be force quit
|
||||
* cause the action is inactive.
|
||||
*/
|
||||
SP_ACTION_NOT_FORCE_QUITABLE("hawkbit.server.error.action.notforcequitable",
|
||||
"Only active actions which are in status pending can be force quit."),
|
||||
SP_ACTION_NOT_FORCE_QUITABLE("hawkbit.server.error.action.notforcequitable", "Only active actions which are in status pending can be force quit."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_DS_INCOMPLETE("hawkbit.server.error.distributionset.incomplete",
|
||||
"Distribution set is assigned to a a target that is incomplete (i.e. mandatory modules are missing)"),
|
||||
SP_DS_INCOMPLETE("hawkbit.server.error.distributionset.incomplete", "Distribution set is assigned to a a target that is incomplete (i.e. mandatory modules are missing)"),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_DS_TYPE_UNDEFINED("hawkbit.server.error.distributionset.type.undefined",
|
||||
"Distribution set type is not yet defined. Modules cannot be added until definition."),
|
||||
SP_DS_TYPE_UNDEFINED("hawkbit.server.error.distributionset.type.undefined", "Distribution set type is not yet defined. Modules cannot be added until definition."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_DS_MODULE_UNSUPPORTED("hawkbit.server.error.distributionset.modules.unsupported",
|
||||
"Distribution set type does not contain the given module, i.e. is incompatible."),
|
||||
SP_DS_MODULE_UNSUPPORTED("hawkbit.server.error.distributionset.modules.unsupported", "Distribution set type does not contain the given module, i.e. is incompatible."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REPO_TENANT_NOT_EXISTS("hawkbit.server.error.repo.tenantNotExists",
|
||||
"The entity cannot be inserted due the tenant does not exists"),
|
||||
SP_REPO_TENANT_NOT_EXISTS("hawkbit.server.error.repo.tenantNotExists", "The entity cannot be inserted due the tenant does not exists"),
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -174,14 +155,12 @@ public enum SpServerError {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_REPO_ENTITY_READ_ONLY("hawkbit.server.error.entityreadonly",
|
||||
"The given entity is read only and the change cannot be completed."),
|
||||
SP_REPO_ENTITY_READ_ONLY("hawkbit.server.error.entityreadonly", "The given entity is read only and the change cannot be completed."),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_CONFIGURATION_VALUE_INVALID("hawkbit.server.error.configValueInvalid",
|
||||
"The given configuration value is invalid."),
|
||||
SP_CONFIGURATION_VALUE_INVALID("hawkbit.server.error.configValueInvalid", "The given configuration value is invalid."),
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -190,8 +169,7 @@ public enum SpServerError {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_ROLLOUT_ILLEGAL_STATE("hawkbit.server.error.rollout.illegalstate",
|
||||
"The rollout is currently in the wrong state for the current operation");
|
||||
SP_ROLLOUT_ILLEGAL_STATE("hawkbit.server.error.rollout.illegalstate", "The rollout is currently in the wrong state for the current operation");
|
||||
|
||||
private final String key;
|
||||
private final String message;
|
||||
|
||||
@@ -196,8 +196,8 @@ public class AmqpConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the Binding {@link AmqpConfiguration#receiverQueueFromSp()} to
|
||||
* {@link AmqpConfiguration#senderConnectorToSpExchange()}.
|
||||
* Create the Binding {@link AmqpConfiguration#receiverQueue()} to
|
||||
* {@link AmqpConfiguration#senderExchange()}.
|
||||
*
|
||||
* @return the binding and create the queue and exchange
|
||||
*/
|
||||
@@ -236,9 +236,12 @@ public class AmqpConfiguration {
|
||||
@Bean(name = { "listenerContainerFactory" })
|
||||
public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
|
||||
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
|
||||
containerFactory.setDefaultRequeueRejected(false);
|
||||
containerFactory.setDefaultRequeueRejected(true);
|
||||
containerFactory.setConnectionFactory(rabbitConnectionFactory);
|
||||
containerFactory.setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal());
|
||||
containerFactory.setConcurrentConsumers(amqpProperties.getInitialConcurrentConsumers());
|
||||
containerFactory.setMaxConcurrentConsumers(amqpProperties.getMaxConcurrentConsumers());
|
||||
containerFactory.setPrefetchCount(amqpProperties.getPrefetchCount());
|
||||
return containerFactory;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
||||
@@ -35,8 +36,10 @@ import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
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.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
@@ -47,6 +50,7 @@ import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
@@ -111,12 +115,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
super(defaultTemplate);
|
||||
}
|
||||
|
||||
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue}", containerFactory = "listenerContainerFactory")
|
||||
private Message onMessage(final Message message, @Header(MessageHeaderKey.TYPE) final String type,
|
||||
@Header(MessageHeaderKey.TENANT) final String tenant) {
|
||||
return onMessage(message, type, tenant, getRabbitTemplate().getConnectionFactory().getVirtualHost());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to handle all incoming amqp messages.
|
||||
*
|
||||
@@ -124,14 +122,17 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
* incoming message
|
||||
* @param type
|
||||
* the message type
|
||||
* @param contentType
|
||||
* the contentType of the message
|
||||
* @param tenant
|
||||
* the contentType of the message
|
||||
* @param virtualHost
|
||||
* the virtual host
|
||||
*
|
||||
* @return a message if <null> no message is send back to sender
|
||||
*/
|
||||
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue}", containerFactory = "listenerContainerFactory")
|
||||
public Message onMessage(final Message message, @Header(MessageHeaderKey.TYPE) final String type,
|
||||
@Header(MessageHeaderKey.TENANT) final String tenant) {
|
||||
return onMessage(message, type, tenant, getRabbitTemplate().getConnectionFactory().getVirtualHost());
|
||||
}
|
||||
|
||||
public Message onMessage(final Message message, final String type, final String tenant, final String virtualHost) {
|
||||
checkContentTypeJson(message);
|
||||
final SecurityContext oldContext = SecurityContextHolder.getContext();
|
||||
@@ -153,6 +154,10 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
default:
|
||||
logAndThrowMessageError(message, "No handle method was found for the given message type.");
|
||||
}
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
|
||||
} catch (final TenantNotExistException teex) {
|
||||
throw new AmqpRejectAndDontRequeueException(teex);
|
||||
} finally {
|
||||
SecurityContextHolder.setContext(oldContext);
|
||||
}
|
||||
@@ -340,11 +345,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
final ActionUpdateStatus actionUpdateStatus = convertMessage(message, ActionUpdateStatus.class);
|
||||
final Action action = checkActionExist(message, actionUpdateStatus);
|
||||
|
||||
final ActionStatus actionStatus = entityFactory.generateActionStatus();
|
||||
actionUpdateStatus.getMessage().forEach(actionStatus::addMessage);
|
||||
|
||||
actionStatus.setAction(action);
|
||||
actionStatus.setOccurredAt(System.currentTimeMillis());
|
||||
final ActionStatus actionStatus = createActionStatus(message, actionUpdateStatus, action);
|
||||
|
||||
switch (actionUpdateStatus.getActionStatus()) {
|
||||
case DOWNLOAD:
|
||||
@@ -382,6 +383,21 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
}
|
||||
}
|
||||
|
||||
private ActionStatus createActionStatus(final Message message, final ActionUpdateStatus actionUpdateStatus,
|
||||
final Action action) {
|
||||
final ActionStatus actionStatus = entityFactory.generateActionStatus();
|
||||
actionUpdateStatus.getMessage().forEach(actionStatus::addMessage);
|
||||
|
||||
if (ArrayUtils.isNotEmpty(message.getMessageProperties().getCorrelationId())) {
|
||||
actionStatus.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "DMF message correlation-id "
|
||||
+ message.getMessageProperties().getCorrelationId());
|
||||
}
|
||||
|
||||
actionStatus.setAction(action);
|
||||
actionStatus.setOccurredAt(System.currentTimeMillis());
|
||||
return actionStatus;
|
||||
}
|
||||
|
||||
private Action getUpdateActionStatus(final ActionStatus actionStatus) {
|
||||
if (actionStatus.getStatus().equals(Status.CANCELED)) {
|
||||
return controllerManagement.addCancelActionStatus(actionStatus);
|
||||
@@ -421,7 +437,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkContentTypeJson(final Message message) {
|
||||
private static void checkContentTypeJson(final Message message) {
|
||||
final MessageProperties messageProperties = message.getMessageProperties();
|
||||
if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) {
|
||||
return;
|
||||
|
||||
@@ -38,13 +38,54 @@ public class AmqpProperties {
|
||||
/**
|
||||
* Missing queue fatal.
|
||||
*/
|
||||
private boolean missingQueuesFatal = false;
|
||||
private boolean missingQueuesFatal;
|
||||
|
||||
/**
|
||||
* Requested heartbeat interval from broker in {@link TimeUnit#SECONDS}.
|
||||
*/
|
||||
private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(60);
|
||||
|
||||
/**
|
||||
* Sets an upper limit to the number of consumers.
|
||||
*/
|
||||
private int maxConcurrentConsumers = 10;
|
||||
|
||||
/**
|
||||
* Tells the broker how many messages to send to each consumer in a single
|
||||
* request. Often this can be set quite high to improve throughput.
|
||||
*/
|
||||
private int prefetchCount = 10;
|
||||
|
||||
/**
|
||||
* Initial number of consumers. Is scaled up if necessary up to
|
||||
* {@link #maxConcurrentConsumers}.
|
||||
*/
|
||||
private int initialConcurrentConsumers = 3;
|
||||
|
||||
public int getPrefetchCount() {
|
||||
return prefetchCount;
|
||||
}
|
||||
|
||||
public void setPrefetchCount(final int prefetchCount) {
|
||||
this.prefetchCount = prefetchCount;
|
||||
}
|
||||
|
||||
public int getInitialConcurrentConsumers() {
|
||||
return initialConcurrentConsumers;
|
||||
}
|
||||
|
||||
public void setInitialConcurrentConsumers(final int initialConcurrentConsumers) {
|
||||
this.initialConcurrentConsumers = initialConcurrentConsumers;
|
||||
}
|
||||
|
||||
public int getMaxConcurrentConsumers() {
|
||||
return maxConcurrentConsumers;
|
||||
}
|
||||
|
||||
public void setMaxConcurrentConsumers(final int maxConcurrentConsumers) {
|
||||
this.maxConcurrentConsumers = maxConcurrentConsumers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is missingQueuesFatal enabled
|
||||
*
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
||||
@@ -110,7 +111,7 @@ public class BaseAmqpService {
|
||||
|
||||
protected final void logAndThrowMessageError(final Message message, final String error) {
|
||||
LOGGER.warn("Warning! \"{}\" reported by message: {}", error, message);
|
||||
throw new IllegalArgumentException(error);
|
||||
throw new AmqpRejectAndDontRequeueException(error);
|
||||
}
|
||||
|
||||
protected RabbitTemplate getRabbitTemplate() {
|
||||
|
||||
@@ -46,6 +46,7 @@ public class DefaultAmqpSenderService implements AmqpSenderService {
|
||||
|
||||
final String correlationId = UUID.randomUUID().toString();
|
||||
final String exchange = extractExchange(replyTo);
|
||||
message.getMessageProperties().setCorrelationId(correlationId.getBytes());
|
||||
|
||||
if (LOGGER.isTraceEnabled()) {
|
||||
LOGGER.trace("Sending message {} to exchange {} with correlationId {}", message, exchange, correlationId);
|
||||
|
||||
@@ -59,6 +59,7 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||
import org.springframework.amqp.core.Message;
|
||||
import org.springframework.amqp.core.MessageProperties;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
@@ -170,7 +171,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted since no replyTo header was set");
|
||||
} catch (final IllegalArgumentException exception) {
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
|
||||
@@ -184,7 +185,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted since no thingID was set");
|
||||
} catch (final IllegalArgumentException exception) {
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
}
|
||||
@@ -200,7 +201,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, type, TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted due to unknown message type");
|
||||
} catch (final IllegalArgumentException exception) {
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
}
|
||||
@@ -213,21 +214,21 @@ public class AmqpMessageHandlerServiceTest {
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted due to unknown message type");
|
||||
} catch (final IllegalArgumentException e) {
|
||||
} 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");
|
||||
} catch (final IllegalArgumentException e) {
|
||||
} 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");
|
||||
} catch (final IllegalArgumentException exception) {
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
|
||||
@@ -246,7 +247,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted since no action id was set");
|
||||
} catch (final IllegalArgumentException exception) {
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
}
|
||||
@@ -263,7 +264,7 @@ public class AmqpMessageHandlerServiceTest {
|
||||
try {
|
||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||
fail("IllegalArgumentException was excepeted since no action id was set");
|
||||
} catch (final IllegalArgumentException exception) {
|
||||
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||
// test ok - exception was excepted
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
||||
@Stories("Test to generate the artifact download URL")
|
||||
@SpringApplicationConfiguration(classes = { AmqpTestConfiguration.class,
|
||||
org.eclipse.hawkbit.RepositoryApplicationConfiguration.class })
|
||||
|
||||
public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTestWithMongoDB {
|
||||
|
||||
private static final String HTTPS_LOCALHOST = "https://localhost:8080/";
|
||||
|
||||
@@ -18,6 +18,9 @@ public class MgmtTargetRequestBody {
|
||||
@JsonProperty(required = true)
|
||||
private String controllerId;
|
||||
|
||||
@JsonProperty
|
||||
private String address;
|
||||
|
||||
/**
|
||||
* @return the name
|
||||
*/
|
||||
@@ -66,4 +69,12 @@ public class MgmtTargetRequestBody {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(final String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,14 +35,14 @@ public interface MgmtTargetRestApi {
|
||||
/**
|
||||
* Handles the GET request of retrieving a single target.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* the ID of the target to retrieve
|
||||
* @return a single target with status OK.
|
||||
*/
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}", produces = { "application/hal+json",
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}", produces = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtTarget> getTarget(@PathVariable("targetId") final String targetId);
|
||||
ResponseEntity<MgmtTarget> getTarget(@PathVariable("controllerId") final String controllerId);
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving all targets.
|
||||
@@ -91,7 +91,7 @@ public interface MgmtTargetRestApi {
|
||||
* path of the request. A given ID in the request body is ignored. It's not
|
||||
* possible to set fields to {@code null} values.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* the path parameter which contains the ID of the target
|
||||
* @param targetRest
|
||||
* the request body which contains the fields which should be
|
||||
@@ -100,40 +100,40 @@ public interface MgmtTargetRestApi {
|
||||
* @return the updated target response which contains all fields also fields
|
||||
* which have not updated
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.PUT, value = "/{targetId}", consumes = { "application/hal+json",
|
||||
@RequestMapping(method = RequestMethod.PUT, value = "/{controllerId}", consumes = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtTarget> updateTarget(@PathVariable("targetId") final String targetId,
|
||||
ResponseEntity<MgmtTarget> updateTarget(@PathVariable("controllerId") final String controllerId,
|
||||
@RequestBody final MgmtTargetRequestBody targetRest);
|
||||
|
||||
/**
|
||||
* Handles the DELETE request of deleting a target.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* the ID of the target to be deleted
|
||||
* @return If the given targetId could exists and could be deleted Http OK.
|
||||
* In any failure the JsonResponseExceptionHandler is handling the
|
||||
* response.
|
||||
* @return If the given controllerId could exists and could be deleted Http
|
||||
* OK. In any failure the JsonResponseExceptionHandler is handling
|
||||
* the response.
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.DELETE, value = "/{targetId}", produces = { "application/hal+json",
|
||||
@RequestMapping(method = RequestMethod.DELETE, value = "/{controllerId}", produces = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<Void> deleteTarget(@PathVariable("targetId") final String targetId);
|
||||
ResponseEntity<Void> deleteTarget(@PathVariable("controllerId") final String controllerId);
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving the attributes of a specific
|
||||
* target.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* the ID of the target to retrieve the attributes.
|
||||
* @return the target attributes as map response with status OK
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/attributes", produces = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("targetId") final String targetId);
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/attributes", produces = {
|
||||
"application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("controllerId") final String controllerId);
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving the Actions of a specific target.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* to load actions for
|
||||
* @param pagingOffsetParam
|
||||
* the offset of list of targets for pagination, might not be
|
||||
@@ -151,9 +151,9 @@ public interface MgmtTargetRestApi {
|
||||
* status OK. The response is always paged. In any failure the
|
||||
* JsonResponseExceptionHandler is handling the response.
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/actions", produces = { "application/hal+json",
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/actions", produces = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<PagedList<MgmtAction>> getActionHistory(@PathVariable("targetId") final String targetId,
|
||||
ResponseEntity<PagedList<MgmtAction>> getActionHistory(@PathVariable("controllerId") final String controllerId,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
|
||||
@@ -163,22 +163,22 @@ public interface MgmtTargetRestApi {
|
||||
* Handles the GET request of retrieving a specific Actions of a specific
|
||||
* Target.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* to load the action for
|
||||
* @param actionId
|
||||
* to load
|
||||
* @return the action
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/actions/{actionId}", produces = {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/actions/{actionId}", produces = {
|
||||
"application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtAction> getAction(@PathVariable("targetId") final String targetId,
|
||||
ResponseEntity<MgmtAction> getAction(@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") final Long actionId);
|
||||
|
||||
/**
|
||||
* Handles the DELETE request of canceling an specific Actions of a specific
|
||||
* Target.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* the ID of the target in the URL path parameter
|
||||
* @param actionId
|
||||
* the ID of the action in the URL path parameter
|
||||
@@ -186,8 +186,8 @@ public interface MgmtTargetRestApi {
|
||||
* optional parameter, which indicates a force cancel
|
||||
* @return status no content in case cancellation was successful
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.DELETE, value = "/{targetId}/actions/{actionId}")
|
||||
ResponseEntity<Void> cancelAction(@PathVariable("targetId") final String targetId,
|
||||
@RequestMapping(method = RequestMethod.DELETE, value = "/{controllerId}/actions/{actionId}")
|
||||
ResponseEntity<Void> cancelAction(@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") final Long actionId,
|
||||
@RequestParam(value = "force", required = false, defaultValue = "false") final boolean force);
|
||||
|
||||
@@ -195,7 +195,7 @@ public interface MgmtTargetRestApi {
|
||||
* Handles the GET request of retrieving the ActionStatus of a specific
|
||||
* target and action.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* of the the action
|
||||
* @param actionId
|
||||
* of the status we are intend to load
|
||||
@@ -212,10 +212,10 @@ public interface MgmtTargetRestApi {
|
||||
* with status OK. The response is always paged. In any failure the
|
||||
* JsonResponseExceptionHandler is handling the response.
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/actions/{actionId}/status", produces = {
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/actions/{actionId}/status", produces = {
|
||||
"application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<PagedList<MgmtActionStatus>> getActionStatusList(@PathVariable("targetId") final String targetId,
|
||||
@PathVariable("actionId") final Long actionId,
|
||||
ResponseEntity<PagedList<MgmtActionStatus>> getActionStatusList(
|
||||
@PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam);
|
||||
@@ -224,40 +224,43 @@ public interface MgmtTargetRestApi {
|
||||
* Handles the GET request of retrieving the assigned distribution set of an
|
||||
* specific target.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* the ID of the target to retrieve the assigned distribution
|
||||
* @return the assigned distribution set with status OK, if none is assigned
|
||||
* than {@code null} content (e.g. "{}")
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/assignedDS", produces = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(@PathVariable("targetId") final String targetId);
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/assignedDS", produces = {
|
||||
"application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(
|
||||
@PathVariable("controllerId") final String controllerId);
|
||||
|
||||
/**
|
||||
* Changes the assigned distribution set of a target.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* of the target to change
|
||||
* @param dsId
|
||||
* of the distributionset that is to be assigned
|
||||
* @return http status
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.POST, value = "/{targetId}/assignedDS", consumes = { "application/hal+json",
|
||||
@RequestMapping(method = RequestMethod.POST, value = "/{controllerId}/assignedDS", consumes = {
|
||||
"application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<Void> postAssignedDistributionSet(@PathVariable("targetId") final String targetId,
|
||||
ResponseEntity<Void> postAssignedDistributionSet(@PathVariable("controllerId") final String controllerId,
|
||||
@RequestBody final MgmtDistributionSetAssigment dsId);
|
||||
|
||||
/**
|
||||
* Handles the GET request of retrieving the installed distribution set of
|
||||
* an specific target.
|
||||
*
|
||||
* @param targetId
|
||||
* @param controllerId
|
||||
* the ID of the target to retrieve
|
||||
* @return the assigned installed set with status OK, if none is installed
|
||||
* than {@code null} content (e.g. "{}")
|
||||
*/
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/installedDS", produces = { "application/hal+json",
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(@PathVariable("targetId") final String targetId);
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/installedDS", produces = {
|
||||
"application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(
|
||||
@PathVariable("controllerId") final String controllerId);
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.rest.data.SortDirection;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
|
||||
/**
|
||||
* A mapper which maps repository model to RESTful model representation and
|
||||
@@ -141,7 +142,9 @@ public final class MgmtTargetMapper {
|
||||
|
||||
final URI address = target.getTargetInfo().getAddress();
|
||||
if (address != null) {
|
||||
targetRest.setIpAddress(address.getHost());
|
||||
if (IpUtil.isIpAddresKnown(address)) {
|
||||
targetRest.setIpAddress(address.getHost());
|
||||
}
|
||||
targetRest.setAddress(address.toString());
|
||||
}
|
||||
|
||||
@@ -182,6 +185,7 @@ public final class MgmtTargetMapper {
|
||||
final Target target = entityFactory.generateTarget(targetRest.getControllerId());
|
||||
target.setDescription(targetRest.getDescription());
|
||||
target.setName(targetRest.getName());
|
||||
target.getTargetInfo().setAddress(targetRest.getAddress());
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,8 +68,8 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
private EntityFactory entityFactory;
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtTarget> getTarget(@PathVariable("targetId") final String targetId) {
|
||||
final Target findTarget = findTargetWithExceptionIfNotFound(targetId);
|
||||
public ResponseEntity<MgmtTarget> getTarget(@PathVariable("controllerId") final String controllerId) {
|
||||
final Target findTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||
// to single response include poll status
|
||||
final MgmtTarget response = MgmtTargetMapper.toResponse(findTarget);
|
||||
MgmtTargetMapper.addPollStatus(findTarget, response);
|
||||
@@ -115,9 +115,9 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtTarget> updateTarget(@PathVariable("targetId") final String targetId,
|
||||
public ResponseEntity<MgmtTarget> updateTarget(@PathVariable("controllerId") final String controllerId,
|
||||
@RequestBody final MgmtTargetRequestBody targetRest) {
|
||||
final Target existingTarget = findTargetWithExceptionIfNotFound(targetId);
|
||||
final Target existingTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||
LOG.debug("updating target {}", existingTarget.getId());
|
||||
if (targetRest.getDescription() != null) {
|
||||
existingTarget.setDescription(targetRest.getDescription());
|
||||
@@ -131,16 +131,16 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> deleteTarget(@PathVariable("targetId") final String targetId) {
|
||||
final Target target = findTargetWithExceptionIfNotFound(targetId);
|
||||
public ResponseEntity<Void> deleteTarget(@PathVariable("controllerId") final String controllerId) {
|
||||
final Target target = findTargetWithExceptionIfNotFound(controllerId);
|
||||
this.targetManagement.deleteTargets(target.getId());
|
||||
LOG.debug("{} target deleted, return status {}", targetId, HttpStatus.OK);
|
||||
LOG.debug("{} target deleted, return status {}", controllerId, HttpStatus.OK);
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("targetId") final String targetId) {
|
||||
final Target foundTarget = findTargetWithExceptionIfNotFound(targetId);
|
||||
public ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("controllerId") final String controllerId) {
|
||||
final Target foundTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||
final Map<String, String> controllerAttributes = foundTarget.getTargetInfo().getControllerAttributes();
|
||||
if (controllerAttributes.isEmpty()) {
|
||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
@@ -153,13 +153,14 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<PagedList<MgmtAction>> getActionHistory(@PathVariable("targetId") final String targetId,
|
||||
public ResponseEntity<PagedList<MgmtAction>> getActionHistory(
|
||||
@PathVariable("controllerId") final String controllerId,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam) {
|
||||
|
||||
final Target foundTarget = findTargetWithExceptionIfNotFound(targetId);
|
||||
final Target foundTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||
|
||||
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||
@@ -177,14 +178,15 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(
|
||||
new PagedList<>(MgmtTargetMapper.toResponse(targetId, activeActions.getContent()), totalActionCount),
|
||||
new PagedList<>(MgmtTargetMapper.toResponse(controllerId, activeActions.getContent()),
|
||||
totalActionCount),
|
||||
HttpStatus.OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtAction> getAction(@PathVariable("targetId") final String targetId,
|
||||
public ResponseEntity<MgmtAction> getAction(@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") final Long actionId) {
|
||||
final Target target = findTargetWithExceptionIfNotFound(targetId);
|
||||
final Target target = findTargetWithExceptionIfNotFound(controllerId);
|
||||
|
||||
final Action action = findActionWithExceptionIfNotFound(actionId);
|
||||
if (!action.getTarget().getId().equals(target.getId())) {
|
||||
@@ -192,18 +194,18 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
final MgmtAction result = MgmtTargetMapper.toResponse(targetId, action, action.isActive());
|
||||
final MgmtAction result = MgmtTargetMapper.toResponse(controllerId, action, action.isActive());
|
||||
|
||||
if (!action.isCancelingOrCanceled()) {
|
||||
result.add(linkTo(
|
||||
methodOn(MgmtDistributionSetRestApi.class).getDistributionSet(action.getDistributionSet().getId()))
|
||||
.withRel("distributionset"));
|
||||
} else if (action.isCancelingOrCanceled()) {
|
||||
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getAction(targetId, action.getId()))
|
||||
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getAction(controllerId, action.getId()))
|
||||
.withRel(MgmtRestConstants.TARGET_V1_CANCELED_ACTION));
|
||||
}
|
||||
|
||||
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionStatusList(targetId, action.getId(), 0,
|
||||
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionStatusList(controllerId, action.getId(), 0,
|
||||
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
|
||||
ActionStatusFields.ID.getFieldName() + ":" + SortDirection.DESC))
|
||||
.withRel(MgmtRestConstants.TARGET_V1_ACTION_STATUS));
|
||||
@@ -212,10 +214,10 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> cancelAction(@PathVariable("targetId") final String targetId,
|
||||
public ResponseEntity<Void> cancelAction(@PathVariable("controllerId") final String controllerId,
|
||||
@PathVariable("actionId") final Long actionId,
|
||||
@RequestParam(value = "force", required = false, defaultValue = "false") final boolean force) {
|
||||
final Target target = findTargetWithExceptionIfNotFound(targetId);
|
||||
final Target target = findTargetWithExceptionIfNotFound(controllerId);
|
||||
final Action action = findActionWithExceptionIfNotFound(actionId);
|
||||
|
||||
if (force) {
|
||||
@@ -231,12 +233,12 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
|
||||
@Override
|
||||
public ResponseEntity<PagedList<MgmtActionStatus>> getActionStatusList(
|
||||
@PathVariable("targetId") final String targetId, @PathVariable("actionId") final Long actionId,
|
||||
@PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam) {
|
||||
|
||||
final Target target = findTargetWithExceptionIfNotFound(targetId);
|
||||
final Target target = findTargetWithExceptionIfNotFound(controllerId);
|
||||
|
||||
final Action action = findActionWithExceptionIfNotFound(actionId);
|
||||
if (!action.getTarget().getId().equals(target.getId())) {
|
||||
@@ -260,8 +262,8 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(
|
||||
@PathVariable("targetId") final String targetId) {
|
||||
final Target findTarget = findTargetWithExceptionIfNotFound(targetId);
|
||||
@PathVariable("controllerId") final String controllerId) {
|
||||
final Target findTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||
final MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper
|
||||
.toResponse(findTarget.getAssignedDistributionSet());
|
||||
final HttpStatus retStatus;
|
||||
@@ -274,29 +276,29 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<Void> postAssignedDistributionSet(@PathVariable("targetId") final String targetId,
|
||||
public ResponseEntity<Void> postAssignedDistributionSet(@PathVariable("controllerId") final String controllerId,
|
||||
@RequestBody final MgmtDistributionSetAssigment dsId) {
|
||||
|
||||
findTargetWithExceptionIfNotFound(targetId);
|
||||
findTargetWithExceptionIfNotFound(controllerId);
|
||||
final ActionType type = (dsId.getType() != null) ? MgmtRestModelMapper.convertActionType(dsId.getType())
|
||||
: ActionType.FORCED;
|
||||
final Iterator<Target> changed = this.deploymentManagement
|
||||
.assignDistributionSet(dsId.getId(), type, dsId.getForcetime(), targetId).getAssignedEntity()
|
||||
.assignDistributionSet(dsId.getId(), type, dsId.getForcetime(), controllerId).getAssignedEntity()
|
||||
.iterator();
|
||||
if (changed.hasNext()) {
|
||||
return new ResponseEntity<>(HttpStatus.OK);
|
||||
}
|
||||
|
||||
LOG.error("Target update (ds {} assigment to target {}) failed! Returnd target list is empty.", dsId.getId(),
|
||||
targetId);
|
||||
controllerId);
|
||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(
|
||||
@PathVariable("targetId") final String targetId) {
|
||||
final Target findTarget = findTargetWithExceptionIfNotFound(targetId);
|
||||
@PathVariable("controllerId") final String controllerId) {
|
||||
final Target findTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||
final MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper
|
||||
.toResponse(findTarget.getTargetInfo().getInstalledDistributionSet());
|
||||
final HttpStatus retStatus;
|
||||
@@ -308,10 +310,10 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
||||
return new ResponseEntity<>(distributionSetRest, retStatus);
|
||||
}
|
||||
|
||||
private Target findTargetWithExceptionIfNotFound(final String targetId) {
|
||||
final Target findTarget = this.targetManagement.findTargetByControllerID(targetId);
|
||||
private Target findTargetWithExceptionIfNotFound(final String controllerId) {
|
||||
final Target findTarget = this.targetManagement.findTargetByControllerID(controllerId);
|
||||
if (findTarget == null) {
|
||||
throw new EntityNotFoundException("Target with Id {" + targetId + "} does not exist");
|
||||
throw new EntityNotFoundException("Target with Id {" + controllerId + "} does not exist");
|
||||
}
|
||||
return findTarget;
|
||||
}
|
||||
|
||||
@@ -40,11 +40,11 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||
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.test.util.WithUser;
|
||||
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.repository.test.util.WithUser;
|
||||
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
|
||||
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
|
||||
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
||||
@@ -109,8 +109,8 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
|
||||
final String knownTargetId = "targetId";
|
||||
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
|
||||
actions.get(0).setStatus(Status.FINISHED);
|
||||
controllerManagament.addUpdateActionStatus(entityFactory.generateActionStatus(actions.get(0),
|
||||
Status.FINISHED, System.currentTimeMillis(), "testmessage"));
|
||||
controllerManagament.addUpdateActionStatus(entityFactory.generateActionStatus(actions.get(0), Status.FINISHED,
|
||||
System.currentTimeMillis(), "testmessage"));
|
||||
|
||||
final PageRequest pageRequest = new PageRequest(0, 1000, Direction.ASC, ActionFields.ID.getFieldName());
|
||||
final ActionStatus status = deploymentManagement
|
||||
@@ -682,6 +682,7 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
|
||||
final Target test1 = entityFactory.generateTarget("id1");
|
||||
test1.setDescription("testid1");
|
||||
test1.setName("testname1");
|
||||
test1.getTargetInfo().setAddress("amqp://test123/foobar");
|
||||
final Target test2 = entityFactory.generateTarget("id2");
|
||||
test2.setDescription("testid2");
|
||||
test2.setName("testname2");
|
||||
@@ -704,6 +705,7 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
|
||||
.andExpect(jsonPath("[0].description", equalTo("testid1")))
|
||||
.andExpect(jsonPath("[0].createdAt", not(equalTo(0))))
|
||||
.andExpect(jsonPath("[0].createdBy", equalTo("bumlux")))
|
||||
.andExpect(jsonPath("[0].address", equalTo("amqp://test123/foobar")))
|
||||
.andExpect(jsonPath("[1].name", equalTo("testname2")))
|
||||
.andExpect(jsonPath("[1].createdBy", equalTo("bumlux")))
|
||||
.andExpect(jsonPath("[1].controllerId", equalTo("id2")))
|
||||
|
||||
@@ -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.repository;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for the repository.
|
||||
*
|
||||
*/
|
||||
@ConfigurationProperties("hawkbit.server.repository")
|
||||
public class RepositoryProperties {
|
||||
|
||||
/**
|
||||
* Set to <code>true</code> if the repository has to reject
|
||||
* {@link ActionStatus} entries for actions that are closed. Note: if this
|
||||
* is enforced you have to make sure that the feedback channel from the
|
||||
* devices i in order.
|
||||
*/
|
||||
private boolean rejectActionStatusForClosedAction = false;
|
||||
|
||||
public boolean isRejectActionStatusForClosedAction() {
|
||||
return rejectActionStatusForClosedAction;
|
||||
}
|
||||
|
||||
public void setRejectActionStatusForClosedAction(final boolean rejectActionStatusForClosedAction) {
|
||||
this.rejectActionStatusForClosedAction = rejectActionStatusForClosedAction;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -179,25 +179,6 @@ public interface TargetManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
||||
List<Target> createTargets(@NotNull Collection<Target> targets);
|
||||
|
||||
/**
|
||||
* creating a new {@link Target} including poll status data. useful
|
||||
* especially in plug and play scenarios.
|
||||
*
|
||||
* @param targets
|
||||
* to be created *
|
||||
* @param status
|
||||
* of the target
|
||||
* @param lastTargetQuery
|
||||
* if a plug and play case
|
||||
* @param address
|
||||
* if a plug and play case
|
||||
*
|
||||
* @return newly created target
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
||||
List<Target> createTargets(@NotNull Collection<Target> targets, @NotNull TargetUpdateStatus status,
|
||||
Long lastTargetQuery, URI address);
|
||||
|
||||
/**
|
||||
* Deletes all targets with the given IDs.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.repository.exception;
|
||||
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
import org.eclipse.hawkbit.exception.SpServerRtException;
|
||||
|
||||
/**
|
||||
* Exception which is thrown when trying to set an invalid target address.
|
||||
*/
|
||||
public class InvalidTargetAddressException extends SpServerRtException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* the message for this exception
|
||||
*/
|
||||
public InvalidTargetAddressException(final String message) {
|
||||
super(message, SpServerError.SP_REPO_INVALID_TARGET_ADDRESS);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param message
|
||||
* the message for this exception
|
||||
* @param cause
|
||||
* the cause for this exception
|
||||
*/
|
||||
public InvalidTargetAddressException(final String message, final Throwable cause) {
|
||||
super(message, SpServerError.SP_REPO_INVALID_TARGET_ADDRESS, cause);
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,19 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public interface TargetInfo extends Serializable {
|
||||
/**
|
||||
* @return the address under whioch the target can be reached
|
||||
* @return the address under which the target can be reached
|
||||
*/
|
||||
URI getAddress();
|
||||
|
||||
/**
|
||||
* @param address
|
||||
* the target address to set
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* If the given string violates RFC 2396
|
||||
*/
|
||||
void setAddress(String address);
|
||||
|
||||
/**
|
||||
* @return {@link Target} this info element belongs to.
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.ReportManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.SoftwareManagement;
|
||||
@@ -56,6 +57,7 @@ import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -70,7 +72,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
|
||||
|
||||
/**
|
||||
* General configuration for the SP Repository.
|
||||
* General configuration for hawkBit's Repository.
|
||||
*
|
||||
*/
|
||||
@EnableJpaRepositories(basePackages = { "org.eclipse.hawkbit.repository.jpa" })
|
||||
@@ -80,6 +82,7 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableAutoConfiguration
|
||||
@EnableConfigurationProperties(RepositoryProperties.class)
|
||||
@EnableScheduling
|
||||
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,7 @@ import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
@@ -92,6 +93,9 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
@Autowired
|
||||
private HawkbitSecurityProperties securityProperties;
|
||||
|
||||
@Autowired
|
||||
private RepositoryProperties repositoryProperties;
|
||||
|
||||
@Autowired
|
||||
private TenantConfigurationRepository tenantConfigurationRepository;
|
||||
|
||||
@@ -249,7 +253,12 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus) {
|
||||
final JpaAction action = (JpaAction) actionStatus.getAction();
|
||||
|
||||
if (!action.isActive()) {
|
||||
// if action is already closed we accept further status updates if
|
||||
// permitted so by configuration. This is especially useful if the
|
||||
// action status feedback channel order from the device cannot be
|
||||
// guaranteed. However, if an action is closed we do not accept further
|
||||
// close messages.
|
||||
if (actionIsNotActiveButIntermediateFeedbackStillAllowed(actionStatus, action)) {
|
||||
LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
|
||||
actionStatus.getId(), action.getId());
|
||||
return action;
|
||||
@@ -257,6 +266,12 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
return handleAddUpdateActionStatus((JpaActionStatus) actionStatus, action);
|
||||
}
|
||||
|
||||
private boolean actionIsNotActiveButIntermediateFeedbackStillAllowed(final ActionStatus actionStatus,
|
||||
final JpaAction action) {
|
||||
return !action.isActive() && (repositoryProperties.isRejectActionStatusForClosedAction()
|
||||
|| (Status.ERROR.equals(actionStatus.getStatus()) || Status.FINISHED.equals(actionStatus.getStatus())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}.
|
||||
*
|
||||
@@ -284,8 +299,7 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
case CANCELED:
|
||||
case WARNING:
|
||||
case RUNNING:
|
||||
DeploymentHelper.updateTargetInfo(mergedTarget, TargetUpdateStatus.PENDING, false, targetInfoRepository,
|
||||
entityManager);
|
||||
handleIntermediateFeedback(mergedAction, mergedTarget);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -298,6 +312,16 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
return actionRepository.save(mergedAction);
|
||||
}
|
||||
|
||||
private void handleIntermediateFeedback(final JpaAction mergedAction, final JpaTarget mergedTarget) {
|
||||
// we change the target state only if the action is still running
|
||||
// otherwise this is considered as late feedback that does not have
|
||||
// an impact on the state anymore.
|
||||
if (mergedAction.isActive()) {
|
||||
DeploymentHelper.updateTargetInfo(mergedTarget, TargetUpdateStatus.PENDING, false, targetInfoRepository,
|
||||
entityManager);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleErrorOnAction(final JpaAction mergedAction, final JpaTarget mergedTarget) {
|
||||
mergedAction.setActive(false);
|
||||
mergedAction.setStatus(Status.ERROR);
|
||||
|
||||
@@ -606,24 +606,7 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
}
|
||||
final List<Target> savedTargets = new ArrayList<>();
|
||||
for (final Target t : targets) {
|
||||
final Target myTarget = createTarget(t);
|
||||
savedTargets.add(myTarget);
|
||||
}
|
||||
return savedTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
|
||||
public List<Target> createTargets(final Collection<Target> targets, final TargetUpdateStatus status,
|
||||
final Long lastTargetQuery, final URI address) {
|
||||
if (targetRepository.countByControllerIdIn(
|
||||
targets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())) > 0) {
|
||||
throw new EntityAlreadyExistsException();
|
||||
}
|
||||
final List<Target> savedTargets = new ArrayList<>();
|
||||
for (final Target t : targets) {
|
||||
final Target myTarget = createTarget(t, status, lastTargetQuery, address);
|
||||
final Target myTarget = createTarget(t, TargetUpdateStatus.UNKNOWN, null, t.getTargetInfo().getAddress());
|
||||
savedTargets.add(myTarget);
|
||||
}
|
||||
return savedTargets;
|
||||
|
||||
@@ -37,6 +37,7 @@ import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
@@ -168,15 +169,21 @@ public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
|
||||
|
||||
/**
|
||||
* @param address
|
||||
* the ipAddress to set
|
||||
* the target address to set
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* If the given string violates RFC 2396
|
||||
*/
|
||||
@Override
|
||||
public void setAddress(final String address) {
|
||||
// check if this is a real URI
|
||||
if (address != null) {
|
||||
URI.create(address);
|
||||
try {
|
||||
URI.create(address);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
throw new InvalidTargetAddressException(
|
||||
"The given address " + address + " violates the RFC-2396 specification", e);
|
||||
}
|
||||
}
|
||||
|
||||
this.address = address;
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.util.List;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -26,6 +27,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import ru.yandex.qatools.allure.annotations.Description;
|
||||
import ru.yandex.qatools.allure.annotations.Features;
|
||||
@@ -34,6 +36,8 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
||||
@Features("Component Tests - Repository")
|
||||
@Stories("Controller Management")
|
||||
public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Autowired
|
||||
private RepositoryProperties repositoryProperties;
|
||||
|
||||
@Test
|
||||
@Description("Controller adds a new action status.")
|
||||
@@ -94,7 +98,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Controller trys to finish an update process after it has been finished by an error action status.")
|
||||
public void tryToFinishUpdateProcessMoreThenOnce() {
|
||||
public void tryToFinishUpdateProcessMoreThanOnce() {
|
||||
|
||||
// mock
|
||||
final Target target = new JpaTarget("Rabbit");
|
||||
@@ -120,16 +124,101 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.ERROR);
|
||||
|
||||
// try with disabled late feedback
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(true);
|
||||
final ActionStatus actionStatusMessage3 = new JpaActionStatus(savedAction, Action.Status.FINISHED,
|
||||
System.currentTimeMillis());
|
||||
actionStatusMessage3.addMessage("finish");
|
||||
controllerManagament.addUpdateActionStatus(actionStatusMessage3);
|
||||
savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage3);
|
||||
|
||||
targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus();
|
||||
// test
|
||||
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.ERROR);
|
||||
|
||||
// try with enabled late feedback
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(false);
|
||||
final ActionStatus actionStatusMessage4 = new JpaActionStatus(savedAction, Action.Status.FINISHED,
|
||||
System.currentTimeMillis());
|
||||
actionStatusMessage4.addMessage("finish");
|
||||
controllerManagament.addUpdateActionStatus(actionStatusMessage3);
|
||||
|
||||
// test
|
||||
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.ERROR);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Controller trys to send an update feedback after it has been finished which is reject as the repository is "
|
||||
+ "configured to reject that.")
|
||||
public void sendUpdatesForFinishUpdateProcessDropedIfDisabled() {
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(true);
|
||||
|
||||
final Action action = prepareFinishedUpdate("Rabbit");
|
||||
|
||||
final ActionStatus actionStatusMessage1 = new JpaActionStatus(action, Action.Status.RUNNING,
|
||||
System.currentTimeMillis());
|
||||
actionStatusMessage1.addMessage("got some additional feedback");
|
||||
controllerManagament.addUpdateActionStatus(actionStatusMessage1);
|
||||
|
||||
// nothing changed as "feedback after close" is disabled
|
||||
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||
assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(3);
|
||||
assertThat(deploymentManagement.findActionStatusByAction(pageReq, action).getNumberOfElements()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Controller trys to send an update feedback after it has been finished which is actepted as the repository is "
|
||||
+ "configured to accept them.")
|
||||
public void sendUpdatesForFinishUpdateProcessAcceptedIfEnabled() {
|
||||
repositoryProperties.setRejectActionStatusForClosedAction(false);
|
||||
|
||||
Action action = prepareFinishedUpdate("Rabbit");
|
||||
|
||||
final ActionStatus actionStatusMessage1 = new JpaActionStatus(action, Action.Status.RUNNING,
|
||||
System.currentTimeMillis());
|
||||
actionStatusMessage1.addMessage("got some additional feedback");
|
||||
action = controllerManagament.addUpdateActionStatus(actionStatusMessage1);
|
||||
|
||||
// nothing changed as "feedback after close" is disabled
|
||||
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||
assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(4);
|
||||
assertThat(deploymentManagement.findActionStatusByAction(pageReq, action).getNumberOfElements()).isEqualTo(4);
|
||||
}
|
||||
|
||||
private Action prepareFinishedUpdate(final String controllerId) {
|
||||
// mock
|
||||
final Target target = new JpaTarget(controllerId);
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
||||
Target savedTarget = targetManagement.createTarget(target);
|
||||
final List<Target> toAssign = new ArrayList<>();
|
||||
toAssign.add(savedTarget);
|
||||
savedTarget = deploymentManagement.assignDistributionSet(ds, toAssign).getAssignedEntity().iterator().next();
|
||||
Action savedAction = deploymentManagement.findActiveActionsByTarget(savedTarget).get(0);
|
||||
|
||||
// test and verify
|
||||
final ActionStatus actionStatusMessage = new JpaActionStatus(savedAction, Action.Status.RUNNING,
|
||||
System.currentTimeMillis());
|
||||
actionStatusMessage.addMessage("running");
|
||||
savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage);
|
||||
assertThat(targetManagement.findTargetByControllerID(controllerId).getTargetInfo().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.PENDING);
|
||||
|
||||
final ActionStatus actionStatusMessage2 = new JpaActionStatus(savedAction, Action.Status.FINISHED,
|
||||
System.currentTimeMillis());
|
||||
actionStatusMessage2.addMessage("finish");
|
||||
savedAction = controllerManagament.addUpdateActionStatus(actionStatusMessage2);
|
||||
|
||||
// test
|
||||
assertThat(targetManagement.findTargetByControllerID(controllerId).getTargetInfo().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||
|
||||
assertThat(actionStatusRepository.findAll(pageReq).getNumberOfElements()).isEqualTo(3);
|
||||
assertThat(deploymentManagement.findActionStatusByAction(pageReq, savedAction).getNumberOfElements())
|
||||
.isEqualTo(3);
|
||||
|
||||
return savedAction;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ public class ResponseExceptionHandler {
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_ROLLOUT_ILLEGAL_STATE, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_VALUE_INVALID, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_KEY_INVALID, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_REPO_INVALID_TARGET_ADDRESS, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
private static HttpStatus getStatusOrDefault(final SpServerError error) {
|
||||
|
||||
@@ -41,9 +41,9 @@ public abstract class JsonBuilder {
|
||||
try {
|
||||
builder.append(new JSONObject().put("name", module.getName())
|
||||
.put("description", module.getDescription()).put("type", module.getType().getKey())
|
||||
.put("id", Long.MAX_VALUE).put("vendor", module.getVendor())
|
||||
.put("version", module.getVersion()).put("createdAt", "0").put("updatedAt", "0")
|
||||
.put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh").toString());
|
||||
.put("id", Long.MAX_VALUE).put("vendor", module.getVendor()).put("version", module.getVersion())
|
||||
.put("createdAt", "0").put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh")
|
||||
.put("updatedBy", "fghdfkjghdfkjh").toString());
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -185,15 +185,13 @@ public abstract class JsonBuilder {
|
||||
final List<String> messages = new ArrayList<String>();
|
||||
messages.add(message);
|
||||
|
||||
return new JSONObject()
|
||||
.put("id", id)
|
||||
.put("time", "20140511T121314")
|
||||
return new JSONObject().put("id", id).put("time", "20140511T121314")
|
||||
.put("status",
|
||||
new JSONObject()
|
||||
.put("execution", execution)
|
||||
new JSONObject().put("execution", execution)
|
||||
.put("result",
|
||||
new JSONObject().put("finished", finished).put("progress",
|
||||
new JSONObject().put("cnt", 2).put("of", 5))).put("details", messages))
|
||||
new JSONObject().put("cnt", 2).put("of", 5)))
|
||||
.put("details", messages))
|
||||
.toString();
|
||||
|
||||
}
|
||||
@@ -380,10 +378,13 @@ public abstract class JsonBuilder {
|
||||
int i = 0;
|
||||
for (final Target target : targets) {
|
||||
try {
|
||||
final String address = target.getTargetInfo().getAddress() != null
|
||||
? target.getTargetInfo().getAddress().toString() : null;
|
||||
|
||||
builder.append(new JSONObject().put("controllerId", target.getControllerId())
|
||||
.put("description", target.getDescription()).put("name", target.getName())
|
||||
.put("createdAt", "0").put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh")
|
||||
.put("updatedBy", "fghdfkjghdfkjh").toString());
|
||||
.put("description", target.getDescription()).put("name", target.getName()).put("createdAt", "0")
|
||||
.put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
|
||||
.put("address", address).toString());
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -441,9 +442,7 @@ public abstract class JsonBuilder {
|
||||
throws JSONException {
|
||||
final List<String> messages = new ArrayList<String>();
|
||||
messages.add(message);
|
||||
return new JSONObject()
|
||||
.put("id", id)
|
||||
.put("time", "20140511T121314")
|
||||
return new JSONObject().put("id", id).put("time", "20140511T121314")
|
||||
.put("status",
|
||||
new JSONObject().put("execution", execution)
|
||||
.put("result", new JSONObject().put("finished", "success")).put("details", messages))
|
||||
@@ -453,13 +452,12 @@ public abstract class JsonBuilder {
|
||||
|
||||
public static String configData(final String id, final Map<String, String> attributes, final String execution)
|
||||
throws JSONException {
|
||||
return new JSONObject()
|
||||
.put("id", id)
|
||||
.put("time", "20140511T121314")
|
||||
return new JSONObject().put("id", id).put("time", "20140511T121314")
|
||||
.put("status",
|
||||
new JSONObject().put("execution", execution)
|
||||
.put("result", new JSONObject().put("finished", "success"))
|
||||
.put("details", new ArrayList<String>())).put("data", attributes).toString();
|
||||
.put("details", new ArrayList<String>()))
|
||||
.put("data", attributes).toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.google.common.net.HttpHeaders;
|
||||
*/
|
||||
public final class IpUtil {
|
||||
|
||||
private static final String HIDDEN_IP = "***";
|
||||
private static final String SCHEME_SEPERATOR = "://";
|
||||
private static final String HTTP_SCHEME = "http";
|
||||
private static final String AMPQP_SCHEME = "amqp";
|
||||
@@ -87,7 +88,7 @@ public final class IpUtil {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
} else {
|
||||
ip = "***";
|
||||
ip = HIDDEN_IP;
|
||||
}
|
||||
|
||||
return createHttpUri(ip);
|
||||
@@ -178,4 +179,17 @@ public final class IpUtil {
|
||||
public static boolean isAmqpUri(final URI uri) {
|
||||
return uri != null && AMPQP_SCHEME.equals(uri.getScheme());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the IP address of that {@link URI} is known, i.e. not an AQMP
|
||||
* exchange in DMF case and not HIDDEN_IP in DDI case.
|
||||
*
|
||||
* @param uri
|
||||
* the uri
|
||||
* @return <code>true</code> if IP address is actually known by the server
|
||||
*/
|
||||
public static boolean isIpAddresKnown(final URI uri) {
|
||||
return uri != null && !(AMPQP_SCHEME.equals(uri.getScheme()) || HIDDEN_IP.equals(uri.getHost()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
6
pom.xml
6
pom.xml
@@ -69,6 +69,7 @@
|
||||
<hibernate-validator.version>5.2.4.Final</hibernate-validator.version>
|
||||
<spring-cloud-connectors.version>1.2.0.RELEASE</spring-cloud-connectors.version>
|
||||
<spring-amqp.version>1.6.0.RELEASE</spring-amqp.version>
|
||||
<rabbitmq.version>3.6.2</rabbitmq.version>
|
||||
<spring-hateoas.version>0.18.0.RELEASE</spring-hateoas.version>
|
||||
<!-- Support for MongoDB 3 -->
|
||||
<spring-data-releasetrain.version>Fowler-SR1</spring-data-releasetrain.version>
|
||||
@@ -526,6 +527,11 @@
|
||||
<artifactId>org.eclipse.persistence.jpa</artifactId>
|
||||
<version>${eclipselink.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.rabbitmq</groupId>
|
||||
<artifactId>amqp-client</artifactId>
|
||||
<version>${rabbitmq.version}</version>
|
||||
</dependency>
|
||||
<!-- RSQL / FIQL parser -->
|
||||
<dependency>
|
||||
<groupId>cz.jirutka.rsql</groupId>
|
||||
|
||||
Reference in New Issue
Block a user