Merge remote-tracking branch 'origin/master' into
Fix_Consistent_context_menu_on_tables Conflicts: hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rollout/RolloutListGrid.java Signed-off-by: Melanie Retter <melanie.retter@bosch-si.com>
This commit is contained in:
@@ -71,6 +71,10 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.security</groupId>
|
<groupId>org.springframework.security</groupId>
|
||||||
<artifactId>spring-security-web</artifactId>
|
<artifactId>spring-security-web</artifactId>
|
||||||
@@ -83,26 +87,6 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter</artifactId>
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>com.vaadin</groupId>
|
<groupId>com.vaadin</groupId>
|
||||||
<artifactId>vaadin-spring-boot-starter</artifactId>
|
<artifactId>vaadin-spring-boot-starter</artifactId>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public abstract class AbstractSimulatedDevice {
|
|||||||
private UpdateStatus updateStatus = new UpdateStatus(ResponseStatus.SUCCESSFUL, "Simulation complete!");
|
private UpdateStatus updateStatus = new UpdateStatus(ResponseStatus.SUCCESSFUL, "Simulation complete!");
|
||||||
private Protocol protocol = Protocol.DMF_AMQP;
|
private Protocol protocol = Protocol.DMF_AMQP;
|
||||||
private String targetSecurityToken;
|
private String targetSecurityToken;
|
||||||
|
private int pollDelaySec;
|
||||||
private int nextPollCounterSec;
|
private int nextPollCounterSec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,13 +84,30 @@ public abstract class AbstractSimulatedDevice {
|
|||||||
* the ID of the simulated device
|
* the ID of the simulated device
|
||||||
* @param tenant
|
* @param tenant
|
||||||
* the tenant of the simulated device
|
* 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.id = id;
|
||||||
this.tenant = tenant;
|
this.tenant = tenant;
|
||||||
this.status = Status.UNKNWON;
|
this.status = Status.UNKNWON;
|
||||||
this.progress = 0.0;
|
this.progress = 0.0;
|
||||||
this.protocol = protocol;
|
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 static final Logger LOGGER = LoggerFactory.getLogger(DDISimulatedDevice.class);
|
||||||
|
|
||||||
private final int pollDelaySec;
|
|
||||||
private final ControllerResource controllerResource;
|
private final ControllerResource controllerResource;
|
||||||
|
|
||||||
private final DeviceSimulatorUpdater deviceUpdater;
|
private final DeviceSimulatorUpdater deviceUpdater;
|
||||||
@@ -45,11 +44,9 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
|
|||||||
*/
|
*/
|
||||||
public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec,
|
public DDISimulatedDevice(final String id, final String tenant, final int pollDelaySec,
|
||||||
final ControllerResource controllerResource, final DeviceSimulatorUpdater deviceUpdater) {
|
final ControllerResource controllerResource, final DeviceSimulatorUpdater deviceUpdater) {
|
||||||
super(id, tenant, Protocol.DDI_HTTP);
|
super(id, tenant, Protocol.DDI_HTTP, pollDelaySec);
|
||||||
this.pollDelaySec = pollDelaySec;
|
|
||||||
this.controllerResource = controllerResource;
|
this.controllerResource = controllerResource;
|
||||||
this.deviceUpdater = deviceUpdater;
|
this.deviceUpdater = deviceUpdater;
|
||||||
setNextPollCounterSec(pollDelaySec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,13 +55,10 @@ public class DDISimulatedDevice extends AbstractSimulatedDevice {
|
|||||||
removed = true;
|
removed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPollDelaySec() {
|
|
||||||
return pollDelaySec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Polls the base URL for the DDI API interface.
|
* Polls the base URL for the DDI API interface.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void poll() {
|
public void poll() {
|
||||||
if (!removed) {
|
if (!removed) {
|
||||||
final String basePollJson = controllerResource.get(getTenant(), getId());
|
final String basePollJson = controllerResource.get(getTenant(), getId());
|
||||||
|
|||||||
@@ -8,10 +8,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.simulator;
|
package org.eclipse.hawkbit.simulator;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simulated device using the DMF API of the hawkBit update server.
|
* A simulated device using the DMF API of the hawkBit update server.
|
||||||
*/
|
*/
|
||||||
public class DMFSimulatedDevice extends AbstractSimulatedDevice {
|
public class DMFSimulatedDevice extends AbstractSimulatedDevice {
|
||||||
|
private final SpSenderService spSenderService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param id
|
* @param id
|
||||||
@@ -19,8 +22,15 @@ public class DMFSimulatedDevice extends AbstractSimulatedDevice {
|
|||||||
* @param tenant
|
* @param tenant
|
||||||
* the tenant of the simulated device
|
* the tenant of the simulated device
|
||||||
*/
|
*/
|
||||||
public DMFSimulatedDevice(final String id, final String tenant) {
|
public DMFSimulatedDevice(final String id, final String tenant, final SpSenderService spSenderService,
|
||||||
super(id, tenant, Protocol.DMF_AMQP);
|
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;
|
package org.eclipse.hawkbit.simulator;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -25,7 +25,7 @@ import org.springframework.stereotype.Service;
|
|||||||
@Service
|
@Service
|
||||||
public class DeviceSimulatorRepository {
|
public class DeviceSimulatorRepository {
|
||||||
|
|
||||||
private final Map<DeviceKey, AbstractSimulatedDevice> devices = new LinkedHashMap<>();
|
private final Map<DeviceKey, AbstractSimulatedDevice> devices = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SimulatedDeviceFactory deviceFactory;
|
private SimulatedDeviceFactory deviceFactory;
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ package org.eclipse.hawkbit.simulator;
|
|||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.DigestOutputStream;
|
import java.security.DigestOutputStream;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
@@ -34,6 +32,7 @@ import org.apache.http.conn.ssl.SSLContextBuilder;
|
|||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
import org.apache.http.impl.client.HttpClients;
|
import org.apache.http.impl.client.HttpClients;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.Artifact;
|
import org.eclipse.hawkbit.dmf.json.model.Artifact;
|
||||||
|
import org.eclipse.hawkbit.dmf.json.model.Artifact.UrlProtocol;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.SoftwareModule;
|
import org.eclipse.hawkbit.dmf.json.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||||
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
|
import org.eclipse.hawkbit.simulator.UpdateStatus.ResponseStatus;
|
||||||
@@ -59,7 +58,7 @@ import com.google.common.io.ByteStreams;
|
|||||||
public class DeviceSimulatorUpdater {
|
public class DeviceSimulatorUpdater {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(DeviceSimulatorUpdater.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(DeviceSimulatorUpdater.class);
|
||||||
|
|
||||||
private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(4);
|
private static final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(8);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private SpSenderService spSenderService;
|
private SpSenderService spSenderService;
|
||||||
@@ -99,7 +98,8 @@ public class DeviceSimulatorUpdater {
|
|||||||
|
|
||||||
// plug and play - non existing device will be auto created
|
// plug and play - non existing device will be auto created
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
device = repository.add(deviceFactory.createSimulatedDevice(id, tenant, Protocol.DMF_AMQP, -1, null, null));
|
device = repository
|
||||||
|
.add(deviceFactory.createSimulatedDevice(id, tenant, Protocol.DMF_AMQP, 1800, null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
device.setProgress(0.0);
|
device.setProgress(0.0);
|
||||||
@@ -197,18 +197,14 @@ public class DeviceSimulatorUpdater {
|
|||||||
|
|
||||||
private static void handleArtifacts(final String targetToken, final List<UpdateStatus> status,
|
private static void handleArtifacts(final String targetToken, final List<UpdateStatus> status,
|
||||||
final Artifact artifact) {
|
final Artifact artifact) {
|
||||||
artifact.getUrls().entrySet().forEach(entry -> {
|
|
||||||
switch (entry.getKey()) {
|
if (artifact.getUrls().containsKey(UrlProtocol.HTTPS)) {
|
||||||
case HTTP:
|
status.add(downloadUrl(artifact.getUrls().get(UrlProtocol.HTTPS), targetToken,
|
||||||
case HTTPS:
|
artifact.getHashes().getSha1(), artifact.getSize()));
|
||||||
status.add(downloadUrl(entry.getValue(), targetToken, artifact.getHashes().getSha1(),
|
} else if (artifact.getUrls().containsKey(UrlProtocol.HTTP)) {
|
||||||
artifact.getSize()));
|
status.add(downloadUrl(artifact.getUrls().get(UrlProtocol.HTTP), targetToken,
|
||||||
break;
|
artifact.getHashes().getSha1(), artifact.getSize()));
|
||||||
default:
|
}
|
||||||
// not supported yet
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UpdateStatus downloadUrl(final String url, final String targetToken, final String sha1Hash,
|
private static UpdateStatus downloadUrl(final String url, final String targetToken, final String sha1Hash,
|
||||||
@@ -235,22 +231,15 @@ public class DeviceSimulatorUpdater {
|
|||||||
return new UpdateStatus(ResponseStatus.ERROR, message);
|
return new UpdateStatus(ResponseStatus.ERROR, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
final File tempFile = File.createTempFile("uploadFile", null);
|
|
||||||
|
|
||||||
// Exception squid:S2070 - not used for hashing sensitive
|
// Exception squid:S2070 - not used for hashing sensitive
|
||||||
// data
|
// data
|
||||||
@SuppressWarnings("squid:S2070")
|
@SuppressWarnings("squid:S2070")
|
||||||
final MessageDigest md = MessageDigest.getInstance("SHA-1");
|
final MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||||
|
|
||||||
try (final DigestOutputStream dos = new DigestOutputStream(new FileOutputStream(tempFile), md)) {
|
try (final BufferedOutputStream bdos = new BufferedOutputStream(
|
||||||
try (final BufferedOutputStream bdos = new BufferedOutputStream(dos)) {
|
new DigestOutputStream(ByteStreams.nullOutputStream(), md))) {
|
||||||
try (BufferedInputStream bis = new BufferedInputStream(response.getEntity().getContent())) {
|
try (BufferedInputStream bis = new BufferedInputStream(response.getEntity().getContent())) {
|
||||||
overallread = ByteStreams.copy(bis, bdos);
|
overallread = ByteStreams.copy(bis, bdos);
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (tempFile != null && !tempFile.delete()) {
|
|
||||||
LOGGER.error("Could not delete temporary file: {}", tempFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.simulator;
|
package org.eclipse.hawkbit.simulator;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
|
import org.eclipse.hawkbit.simulator.event.NextPollCounterUpdate;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -51,18 +50,17 @@ public class NextPollTimeController {
|
|||||||
private class NextPollUpdaterRunnable implements Runnable {
|
private class NextPollUpdaterRunnable implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final List<AbstractSimulatedDevice> devices = repository.getAll().stream()
|
final Collection<AbstractSimulatedDevice> devices = repository.getAll();
|
||||||
.filter(device -> device instanceof DDISimulatedDevice).collect(Collectors.toList());
|
|
||||||
|
|
||||||
devices.forEach(device -> {
|
devices.forEach(device -> {
|
||||||
int nextCounter = device.getNextPollCounterSec() - 1;
|
int nextCounter = device.getNextPollCounterSec() - 1;
|
||||||
if (nextCounter < 0 && device instanceof DDISimulatedDevice) {
|
if (nextCounter < 0) {
|
||||||
try {
|
try {
|
||||||
pollService.submit(() -> ((DDISimulatedDevice) device).poll());
|
pollService.submit(() -> device.poll());
|
||||||
} catch (final IllegalStateException e) {
|
} catch (final IllegalStateException e) {
|
||||||
LOGGER.trace("Device could not be polled", e);
|
LOGGER.trace("Device could not be polled", e);
|
||||||
}
|
}
|
||||||
nextCounter = ((DDISimulatedDevice) device).getPollDelaySec();
|
nextCounter = device.getPollDelaySec();
|
||||||
}
|
}
|
||||||
|
|
||||||
device.setNextPollCounterSec(nextCounter);
|
device.setNextPollCounterSec(nextCounter);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.simulator;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
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.ControllerResource;
|
||||||
import org.eclipse.hawkbit.simulator.http.GatewayTokenInterceptor;
|
import org.eclipse.hawkbit.simulator.http.GatewayTokenInterceptor;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -28,6 +29,9 @@ public class SimulatedDeviceFactory {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private DeviceSimulatorUpdater deviceUpdater;
|
private DeviceSimulatorUpdater deviceUpdater;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SpSenderService spSenderService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creating a simulated devices.
|
* Creating a simulated devices.
|
||||||
*
|
*
|
||||||
@@ -41,7 +45,7 @@ public class SimulatedDeviceFactory {
|
|||||||
*/
|
*/
|
||||||
public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant,
|
public AbstractSimulatedDevice createSimulatedDevice(final String id, final String tenant,
|
||||||
final Protocol protocol) {
|
final Protocol protocol) {
|
||||||
return createSimulatedDevice(id, tenant, protocol, 30, null, null);
|
return createSimulatedDevice(id, tenant, protocol, 1800, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -55,7 +59,7 @@ public class SimulatedDeviceFactory {
|
|||||||
* the protocol which should be used be the simulated device
|
* the protocol which should be used be the simulated device
|
||||||
* @param pollDelaySec
|
* @param pollDelaySec
|
||||||
* the poll delay time in seconds which should be used for
|
* the poll delay time in seconds which should be used for
|
||||||
* {@link DDISimulatedDevice}s
|
* {@link DDISimulatedDevice}s and {@link DMFSimulatedDevice}
|
||||||
* @param baseEndpoint
|
* @param baseEndpoint
|
||||||
* the http base endpoint which should be used for
|
* the http base endpoint which should be used for
|
||||||
* {@link DDISimulatedDevice}s
|
* {@link DDISimulatedDevice}s
|
||||||
@@ -68,7 +72,7 @@ public class SimulatedDeviceFactory {
|
|||||||
final int pollDelaySec, final URL baseEndpoint, final String gatewayToken) {
|
final int pollDelaySec, final URL baseEndpoint, final String gatewayToken) {
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case DMF_AMQP:
|
case DMF_AMQP:
|
||||||
return new DMFSimulatedDevice(id, tenant);
|
return new DMFSimulatedDevice(id, tenant, spSenderService, pollDelaySec);
|
||||||
case DDI_HTTP:
|
case DDI_HTTP:
|
||||||
final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger())
|
final ControllerResource controllerResource = Feign.builder().logger(new Logger.ErrorLogger())
|
||||||
.requestInterceptor(new GatewayTokenInterceptor(gatewayToken)).logLevel(Logger.Level.BASIC)
|
.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 = "tenant", defaultValue = "DEFAULT") final String tenant,
|
||||||
@RequestParam(value = "api", defaultValue = "dmf") final String api,
|
@RequestParam(value = "api", defaultValue = "dmf") final String api,
|
||||||
@RequestParam(value = "endpoint", defaultValue = "http://localhost:8080") final String endpoint,
|
@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)
|
@RequestParam(value = "gatewaytoken", defaultValue = "") final String gatewayToken)
|
||||||
throws MalformedURLException {
|
throws MalformedURLException {
|
||||||
|
|
||||||
@@ -86,10 +86,6 @@ public class SimulationController {
|
|||||||
final String deviceId = name + i;
|
final String deviceId = name + i;
|
||||||
repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, protocol, pollDelay, new URL(endpoint),
|
repository.add(deviceFactory.createSimulatedDevice(deviceId, tenant, protocol, pollDelay, new URL(endpoint),
|
||||||
gatewayToken));
|
gatewayToken));
|
||||||
|
|
||||||
if (protocol == Protocol.DMF_AMQP) {
|
|
||||||
spSenderService.createOrUpdateThing(tenant, deviceId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok("Updated " + amount + " DMF connected targets!");
|
return ResponseEntity.ok("Updated " + amount + " DMF connected targets!");
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.simulator;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
@@ -68,9 +69,9 @@ public class SimulationProperties {
|
|||||||
private String endpoint = "http://localhost:8080";
|
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.
|
* Optional gateway token for DDI API based simulation.
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.simulator;
|
|||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
|
|
||||||
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -54,10 +53,6 @@ public class SimulatorStartup implements ApplicationListener<ContextRefreshedEve
|
|||||||
} catch (final MalformedURLException e) {
|
} catch (final MalformedURLException e) {
|
||||||
LOGGER.error("Creation of simulated device at startup failed.", e);
|
LOGGER.error("Creation of simulated device at startup failed.", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autostart.getApi() == Protocol.DMF_AMQP) {
|
|
||||||
spSenderService.createOrUpdateThing(autostart.getTenant(), deviceId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,52 +12,103 @@ import java.time.Duration;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.amqp.core.Binding;
|
import org.springframework.amqp.core.Binding;
|
||||||
import org.springframework.amqp.core.BindingBuilder;
|
import org.springframework.amqp.core.BindingBuilder;
|
||||||
import org.springframework.amqp.core.FanoutExchange;
|
import org.springframework.amqp.core.FanoutExchange;
|
||||||
import org.springframework.amqp.core.Queue;
|
import org.springframework.amqp.core.Queue;
|
||||||
import org.springframework.amqp.core.QueueBuilder;
|
import org.springframework.amqp.core.QueueBuilder;
|
||||||
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
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.connection.ConnectionFactory;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||||
import org.springframework.amqp.support.converter.MessageConverter;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
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
|
* The spring AMQP configuration to use a AMQP for communication with SP update
|
||||||
* server.
|
* server.
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableConfigurationProperties(AmqpProperties.class)
|
@EnableConfigurationProperties(AmqpProperties.class)
|
||||||
public class AmqpConfiguration {
|
public class AmqpConfiguration {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(AmqpConfiguration.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected AmqpProperties amqpProperties;
|
protected AmqpProperties amqpProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ConnectionFactory connectionFactory;
|
private ConnectionFactory connectionFactory;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private RabbitTemplate rabbitTemplate;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create jackson message converter bean.
|
* @return {@link RabbitTemplate} with automatic retry, published confirms
|
||||||
*
|
* and {@link Jackson2JsonMessageConverter}.
|
||||||
* @return the jackson message converter
|
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public MessageConverter jsonMessageConverter() {
|
public RabbitTemplate rabbitTemplate() {
|
||||||
final Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
|
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
|
||||||
rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
|
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
|
||||||
return 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,8 +122,8 @@ public class AmqpConfiguration {
|
|||||||
final Map<String, Object> arguments = getDeadLetterExchangeArgs();
|
final Map<String, Object> arguments = getDeadLetterExchangeArgs();
|
||||||
arguments.putAll(getTTLMaxArgs());
|
arguments.putAll(getTTLMaxArgs());
|
||||||
|
|
||||||
return QueueBuilder.nonDurable(amqpProperties.getReceiverConnectorQueueFromSp()).withArguments(arguments)
|
return QueueBuilder.nonDurable(amqpProperties.getReceiverConnectorQueueFromSp()).autoDelete()
|
||||||
.build();
|
.withArguments(arguments).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,12 +133,12 @@ public class AmqpConfiguration {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public FanoutExchange exchangeQueueToConnector() {
|
public FanoutExchange exchangeQueueToConnector() {
|
||||||
return new FanoutExchange(amqpProperties.getSenderForSpExchange());
|
return new FanoutExchange(amqpProperties.getSenderForSpExchange(), false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the Binding
|
* Create the Binding
|
||||||
* {@link AmqpConfiguration#receiverConnectorQueueFromSp()} to
|
* {@link AmqpConfiguration#receiverConnectorQueueFromHawkBit()} to
|
||||||
* {@link AmqpConfiguration#exchangeQueueToConnector()}.
|
* {@link AmqpConfiguration#exchangeQueueToConnector()}.
|
||||||
*
|
*
|
||||||
* @return the binding and create the queue and exchange
|
* @return the binding and create the queue and exchange
|
||||||
@@ -114,7 +165,7 @@ public class AmqpConfiguration {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public FanoutExchange exchangeDeadLetter() {
|
public FanoutExchange exchangeDeadLetter() {
|
||||||
return new FanoutExchange(amqpProperties.getDeadLetterExchange());
|
return new FanoutExchange(amqpProperties.getDeadLetterExchange(), false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,10 +187,10 @@ public class AmqpConfiguration {
|
|||||||
@Bean(name = { "listenerContainerFactory" })
|
@Bean(name = { "listenerContainerFactory" })
|
||||||
public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
|
public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
|
||||||
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
|
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
|
||||||
containerFactory.setDefaultRequeueRejected(false);
|
containerFactory.setDefaultRequeueRejected(true);
|
||||||
containerFactory.setConnectionFactory(connectionFactory);
|
containerFactory.setConnectionFactory(connectionFactory);
|
||||||
containerFactory.setConcurrentConsumers(20);
|
containerFactory.setConcurrentConsumers(3);
|
||||||
containerFactory.setMaxConcurrentConsumers(20);
|
containerFactory.setMaxConcurrentConsumers(10);
|
||||||
containerFactory.setPrefetchCount(20);
|
containerFactory.setPrefetchCount(20);
|
||||||
return containerFactory;
|
return containerFactory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.simulator.amqp;
|
package org.eclipse.hawkbit.simulator.amqp;
|
||||||
|
|
||||||
|
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
import org.springframework.amqp.core.MessageProperties;
|
import org.springframework.amqp.core.MessageProperties;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
@@ -55,7 +56,7 @@ public abstract class ReceiverService extends MessageService {
|
|||||||
if (contentType != null && contentType.contains("json")) {
|
if (contentType != null && contentType.contains("json")) {
|
||||||
return;
|
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;
|
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.Message;
|
||||||
import org.springframework.amqp.core.MessageProperties;
|
import org.springframework.amqp.core.MessageProperties;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
|
import org.springframework.amqp.rabbit.support.CorrelationData;
|
||||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
@@ -22,6 +27,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
*/
|
*/
|
||||||
public abstract class SenderService extends MessageService {
|
public abstract class SenderService extends MessageService {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(SenderService.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for sender service.
|
* Constructor for sender service.
|
||||||
*
|
*
|
||||||
@@ -40,18 +47,26 @@ public abstract class SenderService extends MessageService {
|
|||||||
/**
|
/**
|
||||||
* Send a message if the message is not null.
|
* Send a message if the message is not null.
|
||||||
*
|
*
|
||||||
* @param adress
|
* @param address
|
||||||
* the exchange name
|
* the exchange name
|
||||||
* @param message
|
* @param message
|
||||||
* the amqp message which will be send if its not null
|
* 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) {
|
if (message == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME);
|
message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME);
|
||||||
rabbitTemplate.setExchange(adress);
|
final String correlationId = UUID.randomUUID().toString();
|
||||||
rabbitTemplate.send(message);
|
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 {
|
public class SpReceiverService extends ReceiverService {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(ReceiverService.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(ReceiverService.class);
|
||||||
|
|
||||||
public static final String SOFTWARE_MODULE_FIRMWARE = "firmware";
|
|
||||||
|
|
||||||
private final SpSenderService spSenderService;
|
private final SpSenderService spSenderService;
|
||||||
|
|
||||||
private final DeviceSimulatorUpdater deviceUpdater;
|
private final DeviceSimulatorUpdater deviceUpdater;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param rabbitTemplate
|
||||||
|
* for sending messages
|
||||||
|
* @param amqpProperties
|
||||||
|
* for amqp configuration
|
||||||
|
* @param spSenderService
|
||||||
|
* to send messages
|
||||||
|
* @param deviceUpdater
|
||||||
|
* simulator service for updates
|
||||||
*/
|
*/
|
||||||
@Autowired
|
@Autowired
|
||||||
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
|
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.simulator.event;
|
package org.eclipse.hawkbit.simulator.event;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
|||||||
*/
|
*/
|
||||||
public class NextPollCounterUpdate {
|
public class NextPollCounterUpdate {
|
||||||
|
|
||||||
private final List<AbstractSimulatedDevice> devices;
|
private final Collection<AbstractSimulatedDevice> devices;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates poll timer update event.
|
* Creates poll timer update event.
|
||||||
@@ -28,14 +28,14 @@ public class NextPollCounterUpdate {
|
|||||||
* @param devices
|
* @param devices
|
||||||
* the devices which progress has been updated
|
* the devices which progress has been updated
|
||||||
*/
|
*/
|
||||||
public NextPollCounterUpdate(final List<AbstractSimulatedDevice> devices) {
|
public NextPollCounterUpdate(final Collection<AbstractSimulatedDevice> devices) {
|
||||||
this.devices = devices;
|
this.devices = devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the devices of the event
|
* @return the devices of the event
|
||||||
*/
|
*/
|
||||||
public List<AbstractSimulatedDevice> getDevices() {
|
public Collection<AbstractSimulatedDevice> getDevices() {
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ public class GenerateDialog extends Window {
|
|||||||
|
|
||||||
pollDelayTextField = createRequiredTextfield("poll delay (sec)", new ObjectProperty<Integer>(10),
|
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));
|
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",
|
pollUrlTextField = createRequiredTextfield("base poll URL endpoint", "http://localhost:8080",
|
||||||
FontAwesome.FLAG_O, new RegexpValidator(
|
FontAwesome.FLAG_O, new RegexpValidator(
|
||||||
@@ -193,7 +192,6 @@ public class GenerateDialog extends Window {
|
|||||||
protocolGroup.select(Protocol.DMF_AMQP);
|
protocolGroup.select(Protocol.DMF_AMQP);
|
||||||
protocolGroup.addValueChangeListener(event -> {
|
protocolGroup.addValueChangeListener(event -> {
|
||||||
final boolean directDeviceOptionSelected = event.getProperty().getValue().equals(Protocol.DDI_HTTP);
|
final boolean directDeviceOptionSelected = event.getProperty().getValue().equals(Protocol.DDI_HTTP);
|
||||||
pollDelayTextField.setVisible(directDeviceOptionSelected);
|
|
||||||
pollUrlTextField.setVisible(directDeviceOptionSelected);
|
pollUrlTextField.setVisible(directDeviceOptionSelected);
|
||||||
gatewayTokenTextField.setVisible(directDeviceOptionSelected);
|
gatewayTokenTextField.setVisible(directDeviceOptionSelected);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.simulator.ui;
|
package org.eclipse.hawkbit.simulator.ui;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice;
|
||||||
@@ -167,7 +167,7 @@ public class SimulatorView extends VerticalLayout implements View {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void pollCounterUpdate(final NextPollCounterUpdate update) {
|
public void pollCounterUpdate(final NextPollCounterUpdate update) {
|
||||||
final List<AbstractSimulatedDevice> devices = update.getDevices();
|
final Collection<AbstractSimulatedDevice> devices = update.getDevices();
|
||||||
this.getUI().access(() -> devices.forEach(device -> {
|
this.getUI().access(() -> devices.forEach(device -> {
|
||||||
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
|
final BeanItem<AbstractSimulatedDevice> item = beanContainer.getItem(device.getId());
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ spring.rabbitmq.virtualHost=/
|
|||||||
spring.rabbitmq.host=localhost
|
spring.rabbitmq.host=localhost
|
||||||
spring.rabbitmq.port=5672
|
spring.rabbitmq.port=5672
|
||||||
spring.rabbitmq.dynamic=true
|
spring.rabbitmq.dynamic=true
|
||||||
spring.rabbitmq.listener.prefetch=100
|
|
||||||
|
|
||||||
security.basic.enabled=false
|
security.basic.enabled=false
|
||||||
server.port=8083
|
server.port=8083
|
||||||
|
|||||||
@@ -19,10 +19,7 @@
|
|||||||
|
|
||||||
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN" />
|
<Logger name="org.apache.coyote.http11.Http11NioProtocol" level="WARN" />
|
||||||
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN" />
|
<Logger name="org.apache.tomcat.util.net.NioSelectorPool" level="WARN" />
|
||||||
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
|
<Logger name="org.apache.catalina.startup.DigesterFactory" level="ERROR" />
|
||||||
|
|
||||||
<!-- Security Log with hints on potential attacks -->
|
|
||||||
<logger name="server-security" level="INFO" />
|
|
||||||
|
|
||||||
<Root level="INFO">
|
<Root level="INFO">
|
||||||
<AppenderRef ref="Console" />
|
<AppenderRef ref="Console" />
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
|||||||
/**
|
/**
|
||||||
* Client binding for the DistributionSet resource of the management API.
|
* 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 {
|
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.
|
* 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 {
|
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.
|
* 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 {
|
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
|
* A feign-client interface declaration which allows to build a feign-client
|
||||||
* stub.
|
* 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 {
|
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 {
|
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.
|
* 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 {
|
public interface MgmtRolloutClientResource extends MgmtRolloutRestApi {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,10 @@ import feign.Param;
|
|||||||
/**
|
/**
|
||||||
* Client binding for the SoftwareModule resource of the management API.
|
* 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 {
|
public interface MgmtSoftwareModuleClientResource extends MgmtSoftwareModuleRestApi {
|
||||||
|
|
||||||
|
@Override
|
||||||
@RequestMapping(method = RequestMethod.POST, value = "/{softwareModuleId}/artifacts")
|
@RequestMapping(method = RequestMethod.POST, value = "/{softwareModuleId}/artifacts")
|
||||||
ResponseEntity<MgmtArtifact> uploadArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId,
|
ResponseEntity<MgmtArtifact> uploadArtifact(@PathVariable("softwareModuleId") final Long softwareModuleId,
|
||||||
@Param("file") final MultipartFile file,
|
@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.
|
* 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 {
|
public interface MgmtSoftwareModuleTypeClientResource extends MgmtSoftwareModuleTypeRestApi {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
|||||||
* Client binding for the {@link MgmtSystemRestApi}.
|
* 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 {
|
public interface MgmtSystemClientResource extends MgmtSystemRestApi {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
|
|||||||
* Client binding for the {@link MgmtSystemManagementRestApi}.
|
* 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 {
|
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.
|
* 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 {
|
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.
|
* 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 {
|
public interface MgmtTargetTagClientResource extends MgmtTargetTagRestApi {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,32 +84,48 @@ public class DistributionSetBuilder {
|
|||||||
* @return a single entry list of {@link MgmtDistributionSetRequestBodyPost}
|
* @return a single entry list of {@link MgmtDistributionSetRequestBodyPost}
|
||||||
*/
|
*/
|
||||||
public List<MgmtDistributionSetRequestBodyPost> build() {
|
public List<MgmtDistributionSetRequestBodyPost> build() {
|
||||||
return Lists.newArrayList(doBuild(name));
|
return Lists.newArrayList(doBuild(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a list of multiple {@link MgmtDistributionSetRequestBodyPost} to
|
* Builds a list of multiple {@link MgmtDistributionSetRequestBodyPost} to
|
||||||
* create multiple distribution sets at once. An increasing number will be
|
* create multiple distribution sets at once. An increasing number will be
|
||||||
* added to the name of the distribution set. The version and type will
|
* used for version of the distribution set. The name and type will remain
|
||||||
* remain the same.
|
* the same.
|
||||||
*
|
*
|
||||||
* @param count
|
* @param count
|
||||||
* the amount of distribution sets body which should be created
|
* the amount of distribution sets body which should be created
|
||||||
* @return a list of {@link MgmtDistributionSetRequestBodyPost}
|
* @return a list of {@link MgmtDistributionSetRequestBodyPost}
|
||||||
*/
|
*/
|
||||||
public List<MgmtDistributionSetRequestBodyPost> buildAsList(final int count) {
|
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();
|
final ArrayList<MgmtDistributionSetRequestBodyPost> bodyList = Lists.newArrayList();
|
||||||
for (int index = 0; index < count; index++) {
|
for (int index = offset; index < count + offset; index++) {
|
||||||
bodyList.add(doBuild(name + index));
|
bodyList.add(doBuild(String.valueOf(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return bodyList;
|
return bodyList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MgmtDistributionSetRequestBodyPost doBuild(final String prefixName) {
|
private MgmtDistributionSetRequestBodyPost doBuild(final String suffix) {
|
||||||
final MgmtDistributionSetRequestBodyPost body = new MgmtDistributionSetRequestBodyPost();
|
final MgmtDistributionSetRequestBodyPost body = new MgmtDistributionSetRequestBodyPost();
|
||||||
body.setName(prefixName);
|
body.setName(name);
|
||||||
body.setVersion(version);
|
body.setVersion(version + suffix);
|
||||||
body.setType(type);
|
body.setType(type);
|
||||||
body.setDescription(description);
|
body.setDescription(description);
|
||||||
body.setModules(modules);
|
body.setModules(modules);
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public class DistributionSetTypeBuilder {
|
|||||||
* {@link MgmtDistributionSetTypeRequestBodyPost}
|
* {@link MgmtDistributionSetTypeRequestBodyPost}
|
||||||
*/
|
*/
|
||||||
public List<MgmtDistributionSetTypeRequestBodyPost> build() {
|
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) {
|
public List<MgmtDistributionSetTypeRequestBodyPost> buildAsList(final int count) {
|
||||||
final ArrayList<MgmtDistributionSetTypeRequestBodyPost> bodyList = Lists.newArrayList();
|
final ArrayList<MgmtDistributionSetTypeRequestBodyPost> bodyList = Lists.newArrayList();
|
||||||
for (int index = 0; index < count; index++) {
|
for (int index = 0; index < count; index++) {
|
||||||
bodyList.add(doBuild(name + index, key + index));
|
bodyList.add(doBuild(String.valueOf(index)));
|
||||||
}
|
}
|
||||||
return bodyList;
|
return bodyList;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MgmtDistributionSetTypeRequestBodyPost doBuild(final String prefixName, final String prefixKey) {
|
private MgmtDistributionSetTypeRequestBodyPost doBuild(final String suffix) {
|
||||||
final MgmtDistributionSetTypeRequestBodyPost body = new MgmtDistributionSetTypeRequestBodyPost();
|
final MgmtDistributionSetTypeRequestBodyPost body = new MgmtDistributionSetTypeRequestBodyPost();
|
||||||
body.setKey(prefixKey);
|
body.setKey(key + suffix);
|
||||||
body.setName(prefixName);
|
body.setName(name + suffix);
|
||||||
body.setDescription(description);
|
body.setDescription(description);
|
||||||
body.setMandatorymodules(mandatorymodules);
|
body.setMandatorymodules(mandatorymodules);
|
||||||
body.setOptionalmodules(optionalmodules);
|
body.setOptionalmodules(optionalmodules);
|
||||||
|
|||||||
@@ -90,13 +90,13 @@ public class SoftwareModuleBuilder {
|
|||||||
* @return a single entry list of {@link MgmtSoftwareModuleRequestBodyPost}
|
* @return a single entry list of {@link MgmtSoftwareModuleRequestBodyPost}
|
||||||
*/
|
*/
|
||||||
public List<MgmtSoftwareModuleRequestBodyPost> build() {
|
public List<MgmtSoftwareModuleRequestBodyPost> build() {
|
||||||
return Lists.newArrayList(doBuild(name));
|
return Lists.newArrayList(doBuild(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a list of multiple {@link MgmtSoftwareModuleRequestBodyPost} to
|
* Builds a list of multiple {@link MgmtSoftwareModuleRequestBodyPost} to
|
||||||
* create multiple software module at once. An increasing number will be
|
* 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.
|
* remain the same.
|
||||||
*
|
*
|
||||||
* @param count
|
* @param count
|
||||||
@@ -106,16 +106,16 @@ public class SoftwareModuleBuilder {
|
|||||||
public List<MgmtSoftwareModuleRequestBodyPost> buildAsList(final int count) {
|
public List<MgmtSoftwareModuleRequestBodyPost> buildAsList(final int count) {
|
||||||
final ArrayList<MgmtSoftwareModuleRequestBodyPost> bodyList = Lists.newArrayList();
|
final ArrayList<MgmtSoftwareModuleRequestBodyPost> bodyList = Lists.newArrayList();
|
||||||
for (int index = 0; index < count; index++) {
|
for (int index = 0; index < count; index++) {
|
||||||
bodyList.add(doBuild(name + index));
|
bodyList.add(doBuild(String.valueOf(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return bodyList;
|
return bodyList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MgmtSoftwareModuleRequestBodyPost doBuild(final String prefixName) {
|
private MgmtSoftwareModuleRequestBodyPost doBuild(final String suffix) {
|
||||||
final MgmtSoftwareModuleRequestBodyPost body = new MgmtSoftwareModuleRequestBodyPost();
|
final MgmtSoftwareModuleRequestBodyPost body = new MgmtSoftwareModuleRequestBodyPost();
|
||||||
body.setName(prefixName);
|
body.setName(name);
|
||||||
body.setVersion(version);
|
body.setVersion(version + suffix);
|
||||||
body.setType(type);
|
body.setType(type);
|
||||||
body.setVendor(vendor);
|
body.setVendor(vendor);
|
||||||
body.setDescription(description);
|
body.setDescription(description);
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class SoftwareModuleTypeBuilder {
|
|||||||
* {@link MgmtSoftwareModuleTypeRequestBodyPost}
|
* {@link MgmtSoftwareModuleTypeRequestBodyPost}
|
||||||
*/
|
*/
|
||||||
public List<MgmtSoftwareModuleTypeRequestBodyPost> build() {
|
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) {
|
public List<MgmtSoftwareModuleTypeRequestBodyPost> buildAsList(final int count) {
|
||||||
final ArrayList<MgmtSoftwareModuleTypeRequestBodyPost> bodyList = Lists.newArrayList();
|
final ArrayList<MgmtSoftwareModuleTypeRequestBodyPost> bodyList = Lists.newArrayList();
|
||||||
for (int index = 0; index < count; index++) {
|
for (int index = 0; index < count; index++) {
|
||||||
bodyList.add(doBuild(key + index, name + index));
|
bodyList.add(doBuild(String.valueOf(index)));
|
||||||
}
|
}
|
||||||
return bodyList;
|
return bodyList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MgmtSoftwareModuleTypeRequestBodyPost doBuild(final String prefixKey, final String prefixName) {
|
private MgmtSoftwareModuleTypeRequestBodyPost doBuild(final String suffix) {
|
||||||
final MgmtSoftwareModuleTypeRequestBodyPost body = new MgmtSoftwareModuleTypeRequestBodyPost();
|
final MgmtSoftwareModuleTypeRequestBodyPost body = new MgmtSoftwareModuleTypeRequestBodyPost();
|
||||||
body.setKey(prefixKey);
|
body.setKey(key + suffix);
|
||||||
body.setName(prefixName);
|
body.setName(name + suffix);
|
||||||
body.setDescription(description);
|
body.setDescription(description);
|
||||||
body.setMaxAssignments(maxAssignments);
|
body.setMaxAssignments(maxAssignments);
|
||||||
return body;
|
return body;
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.mgmt.client.resource.builder;
|
package org.eclipse.hawkbit.mgmt.client.resource.builder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeRequestBodyPost;
|
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeRequestBodyPost;
|
||||||
@@ -28,6 +27,7 @@ public class TargetBuilder {
|
|||||||
private String controllerId;
|
private String controllerId;
|
||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
|
private String address;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param controllerId
|
* @param controllerId
|
||||||
@@ -49,6 +49,16 @@ public class TargetBuilder {
|
|||||||
return this;
|
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
|
* @param description
|
||||||
* the description of the target
|
* the description of the target
|
||||||
@@ -66,32 +76,54 @@ public class TargetBuilder {
|
|||||||
* @return a single entry list of {@link MgmtTargetRequestBody}
|
* @return a single entry list of {@link MgmtTargetRequestBody}
|
||||||
*/
|
*/
|
||||||
public List<MgmtTargetRequestBody> build() {
|
public List<MgmtTargetRequestBody> build() {
|
||||||
return Lists.newArrayList(doBuild(controllerId));
|
return Lists.newArrayList(doBuild(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a list of multiple {@link MgmtTargetRequestBody} to create
|
* Builds a list of multiple {@link MgmtTargetRequestBody} to create
|
||||||
* multiple targets at once. An increasing number will be added to the
|
* 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
|
* @param count
|
||||||
* the amount of software module type bodies which should be
|
* the amount of target bodies which should be created
|
||||||
* created
|
* @param offset
|
||||||
|
* for
|
||||||
* @return a list of {@link MgmtSoftwareModuleTypeRequestBodyPost}
|
* @return a list of {@link MgmtSoftwareModuleTypeRequestBodyPost}
|
||||||
*/
|
*/
|
||||||
public List<MgmtTargetRequestBody> buildAsList(final int count) {
|
public List<MgmtTargetRequestBody> buildAsList(final int count) {
|
||||||
final ArrayList<MgmtTargetRequestBody> bodyList = Lists.newArrayList();
|
|
||||||
for (int index = 0; index < count; index++) {
|
return buildAsList(0, count);
|
||||||
bodyList.add(doBuild(controllerId + index));
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
return bodyList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MgmtTargetRequestBody doBuild(final String prefixControllerId) {
|
private MgmtTargetRequestBody doBuild(final String suffix) {
|
||||||
final MgmtTargetRequestBody body = new MgmtTargetRequestBody();
|
final MgmtTargetRequestBody body = new MgmtTargetRequestBody();
|
||||||
body.setControllerId(prefixControllerId);
|
body.setControllerId(controllerId + suffix);
|
||||||
body.setName(name);
|
if (name == null) {
|
||||||
|
name = controllerId;
|
||||||
|
}
|
||||||
|
body.setName(name + suffix);
|
||||||
body.setDescription(description);
|
body.setDescription(description);
|
||||||
|
body.setAddress(address);
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,23 +9,40 @@
|
|||||||
package org.eclipse.hawkbit.mgmt.client;
|
package org.eclipse.hawkbit.mgmt.client;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.feign.core.client.FeignClientConfiguration;
|
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.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.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.boot.CommandLineRunner;
|
import org.springframework.boot.CommandLineRunner;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
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.auth.BasicAuthRequestInterceptor;
|
||||||
|
import feign.jackson.JacksonDecoder;
|
||||||
|
import feign.slf4j.Slf4jLogger;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableFeignClients
|
@EnableFeignClients("org.eclipse.hawkbit.mgmt.client.resource")
|
||||||
@EnableConfigurationProperties(ClientConfigurationProperties.class)
|
@EnableConfigurationProperties(ClientConfigurationProperties.class)
|
||||||
@Configuration
|
@Configuration
|
||||||
@AutoConfigureAfter(FeignClientConfiguration.class)
|
@AutoConfigureAfter(FeignClientConfiguration.class)
|
||||||
@@ -36,7 +53,7 @@ public class Application implements CommandLineRunner {
|
|||||||
private ClientConfigurationProperties configuration;
|
private ClientConfigurationProperties configuration;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private GettingStartedDefaultScenario gettingStarted;
|
private ConfigurableScenario configuredScenario;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private CreateStartedRolloutExample gettingStartedRolloutScenario;
|
private CreateStartedRolloutExample gettingStartedRolloutScenario;
|
||||||
@@ -51,9 +68,8 @@ public class Application implements CommandLineRunner {
|
|||||||
// run the create and start rollout example
|
// run the create and start rollout example
|
||||||
gettingStartedRolloutScenario.run();
|
gettingStartedRolloutScenario.run();
|
||||||
} else {
|
} else {
|
||||||
// run the getting started scenario which creates a setup of
|
// run the configured scenario from properties
|
||||||
// distribution set and software modules to be used
|
configuredScenario.run();
|
||||||
gettingStarted.run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,8 +79,13 @@ public class Application implements CommandLineRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public GettingStartedDefaultScenario gettingStartedDefaultScenario() {
|
public ConfigurableScenario configurableScenario(final MgmtDistributionSetClientResource distributionSetResource,
|
||||||
return new GettingStartedDefaultScenario();
|
@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
|
@Bean
|
||||||
@@ -72,7 +93,27 @@ public class Application implements CommandLineRunner {
|
|||||||
return new CreateStartedRolloutExample();
|
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) {
|
for (final String arg : args) {
|
||||||
if (arg.equalsIgnoreCase(containsArg)) {
|
if (arg.equalsIgnoreCase(containsArg)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -80,4 +121,4 @@ public class Application implements CommandLineRunner {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.mgmt.client;
|
package org.eclipse.hawkbit.mgmt.client;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
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
|
private String password = "admin"; // NOSONAR this password is only used for
|
||||||
// examples
|
// 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() {
|
public String getUrl() {
|
||||||
return url;
|
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.softwaremodule.MgmtSoftwareModule;
|
||||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType;
|
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example for creating and starting a Rollout.
|
* Example for creating and starting a Rollout.
|
||||||
@@ -36,7 +37,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
public class CreateStartedRolloutExample {
|
public class CreateStartedRolloutExample {
|
||||||
|
|
||||||
/* known software module type name and key */
|
/* 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 */
|
/* known distribution set type name and key */
|
||||||
private static final String DS_MODULE_TYPE = SM_MODULE_TYPE;
|
private static final String DS_MODULE_TYPE = SM_MODULE_TYPE;
|
||||||
@@ -45,6 +46,7 @@ public class CreateStartedRolloutExample {
|
|||||||
private MgmtDistributionSetClientResource distributionSetResource;
|
private MgmtDistributionSetClientResource distributionSetResource;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("mgmtSoftwareModuleClientResource")
|
||||||
private MgmtSoftwareModuleClientResource softwareModuleResource;
|
private MgmtSoftwareModuleClientResource softwareModuleResource;
|
||||||
|
|
||||||
@Autowired
|
@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
|
# http://www.eclipse.org/legal/epl-v10.html
|
||||||
#
|
#
|
||||||
|
|
||||||
hawkbit.url=localhost:8080
|
hawkbit.url=http://localhost:8080
|
||||||
hawkbit.username=admin
|
hawkbit.username=admin
|
||||||
hawkbit.password=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>
|
<configuration>
|
||||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
<include resource="org/springframework/boot/logging/logback/base.xml" />
|
||||||
<!-- Log message format -->
|
|
||||||
<encoder>
|
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
|
|
||||||
</pattern>
|
|
||||||
</encoder>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<logger name="org.eclipse.hawkbit" level="info" />
|
<logger name="feign.Logger" level="debug" />
|
||||||
|
|
||||||
<root level="error">
|
<root level="INFO">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</root>
|
</root>
|
||||||
|
|
||||||
|
|||||||
@@ -87,9 +87,7 @@ public class ArtifactStore implements ArtifactRepository {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a {@link GridFSDBFile} from the store by it's MD5 hash.
|
* 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
|
* @param md5Hash
|
||||||
* the md5-hash of the file to lookup.
|
* the md5-hash of the file to lookup.
|
||||||
* @return The gridfs file object or {@code null} if no file exists.
|
* @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.
|
* 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
|
* @param id
|
||||||
* the id of the file to lookup.
|
* the id of the file to lookup.
|
||||||
* @return The gridfs file object or {@code null} if no file exists.
|
* @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
|
* @return a paged list of artifacts mapped from the given dbFiles
|
||||||
*/
|
*/
|
||||||
private List<DbArtifact> map(final List<GridFSDBFile> 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
|
* Retrieves a list of {@link GridFSDBFile} from the store by all SHA1
|
||||||
* hashes.
|
* hashes.
|
||||||
*
|
*
|
||||||
* @param tenant
|
|
||||||
* the tenant to retrieve the artifacts from, ignore case.
|
|
||||||
* @param sha1Hashes
|
* @param sha1Hashes
|
||||||
* the sha1-hashes of the files to lookup.
|
* the sha1-hashes of the files to lookup.
|
||||||
* @return list of artifacts
|
* @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.
|
* 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
|
* @param ids
|
||||||
* the ids of the files to lookup.
|
* the ids of the files to lookup.
|
||||||
* @return list of artfiacts
|
* @return list of artfiacts
|
||||||
|
|||||||
@@ -10,18 +10,13 @@ package org.eclipse.hawkbit.artifact.repository;
|
|||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Auto configuration for the {@link ArtifactStore}.
|
* Auto configuration for the {@link ArtifactStore}.
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan
|
|
||||||
@ConditionalOnMissingBean(value = ArtifactRepository.class)
|
@ConditionalOnMissingBean(value = ArtifactRepository.class)
|
||||||
@Import(value = MongoConfiguration.class)
|
@Import(value = MongoConfiguration.class)
|
||||||
public class ArtifactStoreAutoConfiguration {
|
public class ArtifactStoreAutoConfiguration {
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ public class AsyncConfigurerThreadpoolProperties {
|
|||||||
*/
|
*/
|
||||||
private Integer maxthreads = 20;
|
private Integer maxthreads = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Core processing threads for scheduled event executor.
|
||||||
|
*/
|
||||||
|
private Integer schedulerThreads = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the number of threads is greater than the core, this is the maximum
|
* When the number of threads is greater than the core, this is the maximum
|
||||||
* time that excess idle threads will wait for new tasks before terminating.
|
* time that excess idle threads will wait for new tasks before terminating.
|
||||||
@@ -70,4 +75,12 @@ public class AsyncConfigurerThreadpoolProperties {
|
|||||||
this.idletimeout = idletimeout;
|
this.idletimeout = idletimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Integer getSchedulerThreads() {
|
||||||
|
return schedulerThreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSchedulerThreads(final Integer schedulerThreads) {
|
||||||
|
this.schedulerThreads = schedulerThreads;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ package org.eclipse.hawkbit.autoconfigure.scheduling;
|
|||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
|
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
|
||||||
@@ -24,9 +26,12 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.task.TaskExecutor;
|
import org.springframework.core.task.TaskExecutor;
|
||||||
|
import org.springframework.scheduling.TaskScheduler;
|
||||||
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
|
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
|
||||||
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;
|
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;
|
||||||
|
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
||||||
|
import org.springframework.security.concurrent.DelegatingSecurityContextScheduledExecutorService;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
|
||||||
@@ -45,20 +50,28 @@ public class ExecutorAutoConfiguration {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ExecutorService with security context availability in thread
|
* @return ExecutorService with security context availability in thread
|
||||||
* execution..
|
* execution.
|
||||||
|
*/
|
||||||
|
@Bean(destroyMethod = "shutdown")
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public ExecutorService asyncExecutor() {
|
||||||
|
return new DelegatingSecurityContextExecutorService(threadPoolExecutor());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@link TaskExecutor} for task execution
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Executor asyncExecutor() {
|
public TaskExecutor taskExecutor() {
|
||||||
return new DelegatingSecurityContextExecutor(threadPoolExecutor());
|
return new ConcurrentTaskExecutor(asyncExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return central ThreadPoolExecutor for general purpose multi threaded
|
* @return central ThreadPoolExecutor for general purpose multi threaded
|
||||||
* operations. Tries an orderly shutdown when destroyed.
|
* operations. Tries an orderly shutdown when destroyed.
|
||||||
*/
|
*/
|
||||||
@Bean(destroyMethod = "shutdown")
|
private ThreadPoolExecutor threadPoolExecutor() {
|
||||||
public ThreadPoolExecutor threadPoolExecutor() {
|
|
||||||
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(
|
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(
|
||||||
asyncConfigurerProperties.getQueuesize());
|
asyncConfigurerProperties.getQueuesize());
|
||||||
return new ThreadPoolExecutor(asyncConfigurerProperties.getCorethreads(),
|
return new ThreadPoolExecutor(asyncConfigurerProperties.getCorethreads(),
|
||||||
@@ -92,31 +105,24 @@ public class ExecutorAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {@link TaskExecutor} for task execution
|
* @return {@link ScheduledExecutorService} with security context
|
||||||
|
* availability in thread execution.
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean(destroyMethod = "shutdown")
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public TaskExecutor taskExecutor() {
|
|
||||||
return new ConcurrentTaskExecutor(asyncExecutor());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {@link ScheduledExecutorService} based on
|
|
||||||
* {@link #threadPoolTaskScheduler()}.
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public ScheduledExecutorService scheduledExecutorService() {
|
public ScheduledExecutorService scheduledExecutorService() {
|
||||||
return threadPoolTaskScheduler().getScheduledExecutor();
|
return new DelegatingSecurityContextScheduledExecutorService(
|
||||||
|
Executors.newScheduledThreadPool(asyncConfigurerProperties.getSchedulerThreads(),
|
||||||
|
new ThreadFactoryBuilder().setNameFormat("central-scheduled-executor-pool-%d").build()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {@link ThreadPoolTaskScheduler} for scheduled operations.
|
* @return {@link TaskScheduler} for task execution
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
|
public TaskScheduler taskScheduler() {
|
||||||
return new ThreadPoolTaskScheduler();
|
return new ConcurrentTaskScheduler(scheduledExecutorService());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ import org.springframework.security.web.header.writers.frameoptions.StaticAllowF
|
|||||||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
|
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
|
||||||
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
|
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter.XFrameOptionsMode;
|
||||||
import org.springframework.security.web.session.HttpSessionEventPublisher;
|
import org.springframework.security.web.session.HttpSessionEventPublisher;
|
||||||
|
import org.springframework.security.web.session.SessionManagementFilter;
|
||||||
import org.vaadin.spring.security.VaadinSecurityContext;
|
import org.vaadin.spring.security.VaadinSecurityContext;
|
||||||
import org.vaadin.spring.security.annotation.EnableVaadinSecurity;
|
import org.vaadin.spring.security.annotation.EnableVaadinSecurity;
|
||||||
import org.vaadin.spring.security.web.VaadinDefaultRedirectStrategy;
|
import org.vaadin.spring.security.web.VaadinDefaultRedirectStrategy;
|
||||||
@@ -270,20 +271,6 @@ public class SecurityManagedConfiguration {
|
|||||||
return filterRegBean;
|
return filterRegBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Security configuration for the REST management API of the health url.
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
@Order(310)
|
|
||||||
public static class HealthSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configure(final HttpSecurity http) throws Exception {
|
|
||||||
http.regexMatcher("/system/health").csrf().disable().httpBasic().and().sessionManagement()
|
|
||||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security configuration for the REST management API.
|
* Security configuration for the REST management API.
|
||||||
*/
|
*/
|
||||||
@@ -309,7 +296,7 @@ public class SecurityManagedConfiguration {
|
|||||||
final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
|
final BasicAuthenticationEntryPoint basicAuthEntryPoint = new BasicAuthenticationEntryPoint();
|
||||||
basicAuthEntryPoint.setRealmName(springSecurityProperties.getBasic().getRealm());
|
basicAuthEntryPoint.setRealmName(springSecurityProperties.getBasic().getRealm());
|
||||||
|
|
||||||
HttpSecurity httpSec = http.regexMatcher("\\/rest.*|\\/system.*").csrf().disable();
|
HttpSecurity httpSec = http.regexMatcher("\\/rest.*|\\/system/admin.*").csrf().disable();
|
||||||
if (springSecurityProperties.isRequireSsl()) {
|
if (springSecurityProperties.isRequireSsl()) {
|
||||||
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
|
httpSec = httpSec.requiresChannel().anyRequest().requiresSecure().and();
|
||||||
}
|
}
|
||||||
@@ -333,12 +320,10 @@ public class SecurityManagedConfiguration {
|
|||||||
}, RequestHeaderAuthenticationFilter.class)
|
}, RequestHeaderAuthenticationFilter.class)
|
||||||
.addFilterAfter(
|
.addFilterAfter(
|
||||||
new AuthenticationSuccessTenantMetadataCreationFilter(tenantAware, systemManagement),
|
new AuthenticationSuccessTenantMetadataCreationFilter(tenantAware, systemManagement),
|
||||||
RequestHeaderAuthenticationFilter.class)
|
SessionManagementFilter.class)
|
||||||
.authorizeRequests().anyRequest().authenticated()
|
.authorizeRequests().anyRequest().authenticated()
|
||||||
.antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
|
.antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/admin/**")
|
||||||
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN)
|
.hasAnyAuthority(SpPermission.SYSTEM_ADMIN);
|
||||||
.antMatchers(MgmtRestConstants.BASE_SYSTEM_MAPPING + "/**")
|
|
||||||
.hasAnyAuthority(SpPermission.SYSTEM_DIAG);
|
|
||||||
|
|
||||||
httpSec.httpBasic().and().exceptionHandling().authenticationEntryPoint(basicAuthEntryPoint);
|
httpSec.httpBasic().and().exceptionHandling().authenticationEntryPoint(basicAuthEntryPoint);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,4 +41,5 @@ hawkbit.controller.minPollingTime=00:00:30
|
|||||||
# Configuration for RabbitMQ integration
|
# Configuration for RabbitMQ integration
|
||||||
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter_ttl
|
hawkbit.dmf.rabbitmq.deadLetterQueue=dmf_connector_deadletter_ttl
|
||||||
hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
|
hawkbit.dmf.rabbitmq.deadLetterExchange=dmf.connector.deadletter
|
||||||
hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver
|
hawkbit.dmf.rabbitmq.receiverQueue=dmf_receiver
|
||||||
|
hawkbit.dmf.rabbitmq.authenticationReceiverQueue=authentication_receiver
|
||||||
|
|||||||
@@ -24,147 +24,124 @@ public enum SpServerError {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_REPO_ENTITY_ALRREADY_EXISTS("hawkbit.server.error.repo.entitiyAlreayExists",
|
SP_REPO_ENTITY_ALRREADY_EXISTS("hawkbit.server.error.repo.entitiyAlreayExists", "The given entity already exists in database"),
|
||||||
"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",
|
SP_REPO_ENTITY_NOT_EXISTS("hawkbit.server.error.repo.entitiyNotFound", "The given entity does not exist in database"),
|
||||||
"The given entity does not exist in database"),
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_REPO_CONCURRENT_MODIFICATION("hawkbit.server.error.repo.concurrentModification",
|
SP_REPO_CONCURRENT_MODIFICATION("hawkbit.server.error.repo.concurrentModification", "The given entity has been changed by another user/session"),
|
||||||
"The given entity has been changed by another user/session"),
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_REST_SORT_PARAM_SYNTAX("hawkbit.server.error.rest.param.sortParamSyntax",
|
SP_REST_SORT_PARAM_SYNTAX("hawkbit.server.error.rest.param.sortParamSyntax", "The given sort paramter is not well formed"),
|
||||||
"The given sort paramter is not well formed"),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_REST_RSQL_SEARCH_PARAM_SYNTAX("hawkbit.server.error.rest.param.rsqlParamSyntax",
|
SP_REST_RSQL_SEARCH_PARAM_SYNTAX("hawkbit.server.error.rest.param.rsqlParamSyntax", "The given search paramter is not well formed"),
|
||||||
"The given search paramter is not well formed"),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_REST_RSQL_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.rsqlInvalidField",
|
SP_REST_RSQL_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.rsqlInvalidField", "The given search parameter field does not exist"),
|
||||||
"The given search parameter field does not exist"),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_REST_SORT_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.invalidField",
|
SP_REST_SORT_PARAM_INVALID_FIELD("hawkbit.server.error.rest.param.invalidField", "The given sort parameter field does not exist"),
|
||||||
"The given sort parameter field does not exist"),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_REST_SORT_PARAM_INVALID_DIRECTION("hawkbit.server.error.rest.param.invalidDirection",
|
SP_REST_SORT_PARAM_INVALID_DIRECTION("hawkbit.server.error.rest.param.invalidDirection", "The given sort parameter direction does not exist"),
|
||||||
"The given sort parameter direction does not exist"),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_REST_BODY_NOT_READABLE("hawkbit.server.error.rest.body.notReadable",
|
SP_REST_BODY_NOT_READABLE("hawkbit.server.error.rest.body.notReadable", "The given request body is not well formed"),
|
||||||
"The given request body is not well formed"),
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_ARTIFACT_UPLOAD_FAILED("hawkbit.server.error.artifact.uploadFailed",
|
SP_ARTIFACT_UPLOAD_FAILED("hawkbit.server.error.artifact.uploadFailed", "Upload of artifact failed with internal server error."),
|
||||||
"Upload of artifact failed with internal server error."),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_ARTIFACT_UPLOAD_FAILED_MD5_MATCH("hawkbit.server.error.artifact.uploadFailed.checksum.md5.match",
|
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."),
|
||||||
"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",
|
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."),
|
||||||
"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",
|
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."),
|
||||||
"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_INSUFFICIENT_PERMISSION("hawkbit.server.error.insufficientpermission", "Insufficient Permission"),
|
SP_INSUFFICIENT_PERMISSION("hawkbit.server.error.insufficientpermission", "Insufficient Permission"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_ARTIFACT_DELETE_FAILED("hawkbit.server.error.artifact.deleteFailed",
|
SP_ARTIFACT_DELETE_FAILED("hawkbit.server.error.artifact.deleteFailed", "Deletion of artifact failed with internal server error."),
|
||||||
"Deletion of artifact failed with internal server error."),
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_ARTIFACT_LOAD_FAILED("hawkbit.server.error.artifact.loadFailed",
|
SP_ARTIFACT_LOAD_FAILED("hawkbit.server.error.artifact.loadFailed", "Load of artifact failed with internal server error."),
|
||||||
"Load of artifact failed with internal server error."),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_ACTION_STATUS_TO_MANY_ENTRIES("hawkbit.server.error.action.status.tooManyEntries",
|
SP_ACTION_STATUS_TO_MANY_ENTRIES("hawkbit.server.error.action.status.tooManyEntries", "Too many status entries have been inserted."),
|
||||||
"Too many status entries have been inserted."),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_ATTRIBUTES_TO_MANY_ENTRIES("hawkbit.server.error.target.attributes.tooManyEntries",
|
SP_ATTRIBUTES_TO_MANY_ENTRIES("hawkbit.server.error.target.attributes.tooManyEntries", "Too many attribute entries have been inserted."),
|
||||||
"Too many attribute entries have been inserted."),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* error message, which describes that the action can not be canceled cause
|
* error message, which describes that the action can not be canceled cause
|
||||||
* the action is inactive.
|
* the action is inactive.
|
||||||
*/
|
*/
|
||||||
SP_ACTION_NOT_CANCELABLE("hawkbit.server.error.action.notcancelable",
|
SP_ACTION_NOT_CANCELABLE("hawkbit.server.error.action.notcancelable", "Only active actions which are in status pending are canceable."),
|
||||||
"Only active actions which are in status pending are canceable."),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* error message, which describes that the action can not be force quit
|
* error message, which describes that the action can not be force quit
|
||||||
* cause the action is inactive.
|
* cause the action is inactive.
|
||||||
*/
|
*/
|
||||||
SP_ACTION_NOT_FORCE_QUITABLE("hawkbit.server.error.action.notforcequitable",
|
SP_ACTION_NOT_FORCE_QUITABLE("hawkbit.server.error.action.notforcequitable", "Only active actions which are in status pending can be force quit."),
|
||||||
"Only active actions which are in status pending can be force quit."),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_DS_INCOMPLETE("hawkbit.server.error.distributionset.incomplete",
|
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)"),
|
||||||
"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",
|
SP_DS_TYPE_UNDEFINED("hawkbit.server.error.distributionset.type.undefined", "Distribution set type is not yet defined. Modules cannot be added until definition."),
|
||||||
"Distribution set type is not yet defined. Modules cannot be added until definition."),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_DS_MODULE_UNSUPPORTED("hawkbit.server.error.distributionset.modules.unsupported",
|
SP_DS_MODULE_UNSUPPORTED("hawkbit.server.error.distributionset.modules.unsupported", "Distribution set type does not contain the given module, i.e. is incompatible."),
|
||||||
"Distribution set type does not contain the given module, i.e. is incompatible."),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_REPO_TENANT_NOT_EXISTS("hawkbit.server.error.repo.tenantNotExists",
|
SP_REPO_TENANT_NOT_EXISTS("hawkbit.server.error.repo.tenantNotExists", "The entity cannot be inserted due the tenant does not exists"),
|
||||||
"The entity cannot be inserted due the tenant does not exists"),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -174,14 +151,12 @@ public enum SpServerError {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_REPO_ENTITY_READ_ONLY("hawkbit.server.error.entityreadonly",
|
SP_REPO_ENTITY_READ_ONLY("hawkbit.server.error.entityreadonly", "The given entity is read only and the change cannot be completed."),
|
||||||
"The given entity is read only and the change cannot be completed."),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_CONFIGURATION_VALUE_INVALID("hawkbit.server.error.configValueInvalid",
|
SP_CONFIGURATION_VALUE_INVALID("hawkbit.server.error.configValueInvalid", "The given configuration value is invalid."),
|
||||||
"The given configuration value is invalid."),
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -190,8 +165,7 @@ public enum SpServerError {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
SP_ROLLOUT_ILLEGAL_STATE("hawkbit.server.error.rollout.illegalstate",
|
SP_ROLLOUT_ILLEGAL_STATE("hawkbit.server.error.rollout.illegalstate", "The rollout is currently in the wrong state for the current operation");
|
||||||
"The rollout is currently in the wrong state for the current operation");
|
|
||||||
|
|
||||||
private final String key;
|
private final String key;
|
||||||
private final String message;
|
private final String message;
|
||||||
|
|||||||
@@ -79,12 +79,12 @@ public final class DataConversionHelper {
|
|||||||
final org.eclipse.hawkbit.repository.model.SoftwareModule module,
|
final org.eclipse.hawkbit.repository.model.SoftwareModule module,
|
||||||
final ArtifactUrlHandler artifactUrlHandler) {
|
final ArtifactUrlHandler artifactUrlHandler) {
|
||||||
final List<DdiArtifact> files = new ArrayList<>();
|
final List<DdiArtifact> files = new ArrayList<>();
|
||||||
|
|
||||||
module.getLocalArtifacts()
|
module.getLocalArtifacts()
|
||||||
.forEach(artifact -> files.add(createArtifact(targetid, artifactUrlHandler, artifact)));
|
.forEach(artifact -> files.add(createArtifact(targetid, artifactUrlHandler, artifact)));
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DdiArtifact createArtifact(final String targetid, final ArtifactUrlHandler artifactUrlHandler,
|
private static DdiArtifact createArtifact(final String targetid, final ArtifactUrlHandler artifactUrlHandler,
|
||||||
final LocalArtifact artifact) {
|
final LocalArtifact artifact) {
|
||||||
final DdiArtifact file = new DdiArtifact();
|
final DdiArtifact file = new DdiArtifact();
|
||||||
@@ -125,7 +125,7 @@ public final class DataConversionHelper {
|
|||||||
// response because of eTags.
|
// response because of eTags.
|
||||||
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||||
.getControllerBasedeploymentAction(target.getControllerId(), action.getId(),
|
.getControllerBasedeploymentAction(target.getControllerId(), action.getId(),
|
||||||
actions.hashCode())).withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION));
|
calculateEtag(action))).withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION));
|
||||||
addedUpdate = true;
|
addedUpdate = true;
|
||||||
} else if (action.isCancelingOrCanceled() && !addedCancel) {
|
} else if (action.isCancelingOrCanceled() && !addedCancel) {
|
||||||
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
result.add(linkTo(methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
|
||||||
@@ -142,6 +142,22 @@ public final class DataConversionHelper {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates an etag for the given {@link Action} based on the entities
|
||||||
|
* hashcode and the {@link Action#isHitAutoForceTime(long)} to reflect a
|
||||||
|
* force switch.
|
||||||
|
*
|
||||||
|
* @param action
|
||||||
|
* to calculate the etag for
|
||||||
|
* @return the etag
|
||||||
|
*/
|
||||||
|
private static int calculateEtag(final Action action) {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = action.hashCode();
|
||||||
|
result = prime * result + (action.isHitAutoForceTime(System.currentTimeMillis()) ? 1231 : 1237);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static void writeMD5FileResponse(final String fileName, final HttpServletResponse response,
|
static void writeMD5FileResponse(final String fileName, final HttpServletResponse response,
|
||||||
final LocalArtifact artifact) throws IOException {
|
final LocalArtifact artifact) throws IOException {
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
|||||||
@@ -23,15 +23,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RandomUtils;
|
import org.apache.commons.lang3.RandomUtils;
|
||||||
|
import org.eclipse.hawkbit.repository.DistributionSetAssignmentResult;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||||
|
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
|
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTestWithMongoDB;
|
||||||
@@ -43,6 +45,9 @@ import org.springframework.data.domain.PageRequest;
|
|||||||
import org.springframework.data.domain.Sort.Direction;
|
import org.springframework.data.domain.Sort.Direction;
|
||||||
import org.springframework.hateoas.MediaTypes;
|
import org.springframework.hateoas.MediaTypes;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
|
||||||
|
import com.jayway.jsonpath.JsonPath;
|
||||||
|
|
||||||
import ru.yandex.qatools.allure.annotations.Description;
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
@@ -229,6 +234,45 @@ public class DdiDeploymentBaseTest extends AbstractRestIntegrationTestWithMongoD
|
|||||||
assertThat(actionStatusMessage.getStatus()).isEqualTo(Status.RETRIEVED);
|
assertThat(actionStatusMessage.getStatus()).isEqualTo(Status.RETRIEVED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Checks that the deployementBase URL changes when the action is switched from soft to forced in TIMEFORCED case.")
|
||||||
|
public void changeEtagIfActionSwitchesFromSoftToForced() throws Exception {
|
||||||
|
// Prepare test data
|
||||||
|
final Target target = targetManagement.createTarget(entityFactory.generateTarget("4712"));
|
||||||
|
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
|
||||||
|
|
||||||
|
final DistributionSetAssignmentResult result = deploymentManagement.assignDistributionSet(ds.getId(),
|
||||||
|
ActionType.TIMEFORCED, System.currentTimeMillis() + 1_000, target.getControllerId());
|
||||||
|
|
||||||
|
final Action action = deploymentManagement.findActiveActionsByTarget(result.getAssignedEntity().get(0)).get(0);
|
||||||
|
|
||||||
|
MvcResult mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaTypes.HAL_JSON)).andReturn();
|
||||||
|
|
||||||
|
final String urlBeforeSwitch = JsonPath.compile("_links.deploymentBase.href")
|
||||||
|
.read(mvcResult.getResponse().getContentAsString()).toString();
|
||||||
|
|
||||||
|
// Time is not yet over, so we should see the same URL
|
||||||
|
mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaTypes.HAL_JSON)).andReturn();
|
||||||
|
assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString())
|
||||||
|
.toString()).isEqualTo(urlBeforeSwitch)
|
||||||
|
.startsWith("http://localhost/" + tenantAware.getCurrentTenant()
|
||||||
|
+ "/controller/v1/4712/deploymentBase/" + action.getId());
|
||||||
|
|
||||||
|
// After the time is over we should see a new etag
|
||||||
|
TimeUnit.MILLISECONDS.sleep(1_000);
|
||||||
|
|
||||||
|
mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
.andExpect(content().contentType(MediaTypes.HAL_JSON)).andReturn();
|
||||||
|
|
||||||
|
assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString())
|
||||||
|
.toString()).isNotEqualTo(urlBeforeSwitch);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Attempt/soft deployment to a controller. Checks if the resource reponse payload for a given deployment is as expected.")
|
@Description("Attempt/soft deployment to a controller. Checks if the resource reponse payload for a given deployment is as expected.")
|
||||||
public void deplomentAttemptAction() throws Exception {
|
public void deplomentAttemptAction() throws Exception {
|
||||||
|
|||||||
21
hawkbit-ddi-resource/src/test/resources/logback.xml
Normal file
21
hawkbit-ddi-resource/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||||
|
|
||||||
|
All rights reserved. This program and the accompanying materials
|
||||||
|
are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
which accompanies this distribution, and is available at
|
||||||
|
http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
|
||||||
|
-->
|
||||||
|
<configuration>
|
||||||
|
<include resource="org/springframework/boot/logging/logback/base.xml" />
|
||||||
|
|
||||||
|
<!-- <Logger name="org.eclipse.hawkbit.rest.util.MockMvcResultPrinter" level="DEBUG" /> -->
|
||||||
|
|
||||||
|
<Root level="INFO">
|
||||||
|
<AppenderRef ref="Console" />
|
||||||
|
</Root>
|
||||||
|
|
||||||
|
</configuration>
|
||||||
@@ -8,8 +8,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.amqp;
|
package org.eclipse.hawkbit.amqp;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
|
|
||||||
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
|
import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -18,11 +21,12 @@ import org.springframework.amqp.core.Binding;
|
|||||||
import org.springframework.amqp.core.BindingBuilder;
|
import org.springframework.amqp.core.BindingBuilder;
|
||||||
import org.springframework.amqp.core.FanoutExchange;
|
import org.springframework.amqp.core.FanoutExchange;
|
||||||
import org.springframework.amqp.core.Queue;
|
import org.springframework.amqp.core.Queue;
|
||||||
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
import org.springframework.amqp.core.QueueBuilder;
|
||||||
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
|
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
|
||||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
|
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
|
||||||
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -51,10 +55,6 @@ public class AmqpConfiguration {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private AmqpDeadletterProperties amqpDeadletterProperties;
|
private AmqpDeadletterProperties amqpDeadletterProperties;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
@Qualifier("threadPoolExecutor")
|
|
||||||
private ThreadPoolExecutor threadPoolExecutor;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ConnectionFactory rabbitConnectionFactory;
|
private ConnectionFactory rabbitConnectionFactory;
|
||||||
|
|
||||||
@@ -66,8 +66,8 @@ public class AmqpConfiguration {
|
|||||||
private AmqpProperties amqpProperties;
|
private AmqpProperties amqpProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("threadPoolExecutor")
|
@Qualifier("asyncExecutor")
|
||||||
private ThreadPoolExecutor threadPoolExecutor;
|
private Executor threadPoolExecutor;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ScheduledExecutorService scheduledExecutorService;
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
@@ -145,26 +145,71 @@ public class AmqpConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the sp receiver queue.
|
* Create the DMF API receiver queue for retrieving DMF messages.
|
||||||
*
|
*
|
||||||
* @return the receiver queue
|
* @return the receiver queue
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public Queue receiverQueue() {
|
public Queue dmfReceiverQueue() {
|
||||||
return new Queue(amqpProperties.getReceiverQueue(), true, false, false,
|
return new Queue(amqpProperties.getReceiverQueue(), true, false, false,
|
||||||
amqpDeadletterProperties.getDeadLetterExchangeArgs(amqpProperties.getDeadLetterExchange()));
|
amqpDeadletterProperties.getDeadLetterExchangeArgs(amqpProperties.getDeadLetterExchange()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the dead letter fanout exchange.
|
* Create the DMF API receiver queue for authentication requests called by
|
||||||
|
* 3rd party artifact storages for download authorization by devices.
|
||||||
|
*
|
||||||
|
* @return the receiver queue
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public Queue authenticationReceiverQueue() {
|
||||||
|
return QueueBuilder.nonDurable(amqpProperties.getAuthenticationReceiverQueue()).autoDelete()
|
||||||
|
.withArguments(getTTLMaxArgsAuthenticationQueue()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create DMF exchange.
|
||||||
*
|
*
|
||||||
* @return the fanout exchange
|
* @return the fanout exchange
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public FanoutExchange senderExchange() {
|
public FanoutExchange dmfSenderExchange() {
|
||||||
return new FanoutExchange(AmqpSettings.DMF_EXCHANGE);
|
return new FanoutExchange(AmqpSettings.DMF_EXCHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the Binding {@link AmqpConfiguration#dmfReceiverQueue()} to
|
||||||
|
* {@link AmqpConfiguration#dmfSenderExchange()}.
|
||||||
|
*
|
||||||
|
* @return the binding and create the queue and exchange
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public Binding bindDmfSenderExchangeToDmfQueue() {
|
||||||
|
return BindingBuilder.bind(dmfReceiverQueue()).to(dmfSenderExchange());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create authentication exchange.
|
||||||
|
*
|
||||||
|
* @return the fanout exchange
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public FanoutExchange authenticationExchange() {
|
||||||
|
return new FanoutExchange(AmqpSettings.AUTHENTICATION_EXCHANGE, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the Binding
|
||||||
|
* {@link AmqpConfiguration#authenticationReceiverQueue()} to
|
||||||
|
* {@link AmqpConfiguration#authenticationExchange()}.
|
||||||
|
*
|
||||||
|
* @return the binding and create the queue and exchange
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public Binding bindAuthenticationSenderExchangeToAuthenticationQueue() {
|
||||||
|
return BindingBuilder.bind(authenticationReceiverQueue()).to(authenticationExchange());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create dead letter queue.
|
* Create dead letter queue.
|
||||||
*
|
*
|
||||||
@@ -181,29 +226,18 @@ public class AmqpConfiguration {
|
|||||||
* @return the fanout exchange
|
* @return the fanout exchange
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public FanoutExchange exchangeDeadLetter() {
|
public FanoutExchange deadLetterExchange() {
|
||||||
return new FanoutExchange(amqpProperties.getDeadLetterExchange());
|
return new FanoutExchange(amqpProperties.getDeadLetterExchange());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the Binding deadLetterQueue to exchangeDeadLetter.
|
* Create the Binding deadLetterQueue to deadLetterExchange.
|
||||||
*
|
*
|
||||||
* @return the binding
|
* @return the binding
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public Binding bindDeadLetterQueueToLwm2mExchange() {
|
public Binding bindDeadLetterQueueToDeadLetterExchange() {
|
||||||
return BindingBuilder.bind(deadLetterQueue()).to(exchangeDeadLetter());
|
return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange());
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create the Binding {@link AmqpConfiguration#receiverQueueFromSp()} to
|
|
||||||
* {@link AmqpConfiguration#senderConnectorToSpExchange()}.
|
|
||||||
*
|
|
||||||
* @return the binding and create the queue and exchange
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
public Binding bindSenderExchangeToSpQueue() {
|
|
||||||
return BindingBuilder.bind(receiverQueue()).to(senderExchange());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -234,12 +268,15 @@ public class AmqpConfiguration {
|
|||||||
* AMQP messages
|
* AMQP messages
|
||||||
*/
|
*/
|
||||||
@Bean(name = { "listenerContainerFactory" })
|
@Bean(name = { "listenerContainerFactory" })
|
||||||
public SimpleRabbitListenerContainerFactory listenerContainerFactory() {
|
public RabbitListenerContainerFactory<SimpleMessageListenerContainer> listenerContainerFactory() {
|
||||||
final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory();
|
return new ConfigurableRabbitListenerContainerFactory(amqpProperties, rabbitConnectionFactory);
|
||||||
containerFactory.setDefaultRequeueRejected(false);
|
}
|
||||||
containerFactory.setConnectionFactory(rabbitConnectionFactory);
|
|
||||||
containerFactory.setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal());
|
private static Map<String, Object> getTTLMaxArgsAuthenticationQueue() {
|
||||||
return containerFactory;
|
final Map<String, Object> args = new HashMap<>();
|
||||||
|
args.put("x-message-ttl", Duration.ofSeconds(30).toMillis());
|
||||||
|
args.put("x-max-length", 1_000);
|
||||||
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.eclipse.hawkbit.api.HostnameResolver;
|
import org.eclipse.hawkbit.api.HostnameResolver;
|
||||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifact;
|
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.ArtifactManagement;
|
||||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
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.eventbus.event.TargetAssignDistributionSetEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
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;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
@@ -44,9 +47,11 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
|
|||||||
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
import org.eclipse.hawkbit.util.IpUtil;
|
import org.eclipse.hawkbit.util.IpUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
import org.springframework.amqp.core.MessageProperties;
|
import org.springframework.amqp.core.MessageProperties;
|
||||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||||
@@ -101,6 +106,9 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private EntityFactory entityFactory;
|
private EntityFactory entityFactory;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SystemSecurityContext systemSecurityContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
@@ -111,27 +119,39 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
super(defaultTemplate);
|
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.
|
* Method to handle all incoming DMF amqp messages.
|
||||||
*
|
*
|
||||||
* @param message
|
* @param message
|
||||||
* incoming message
|
* incoming message
|
||||||
* @param type
|
* @param type
|
||||||
* the message type
|
* the message type
|
||||||
* @param contentType
|
|
||||||
* the contentType of the message
|
|
||||||
* @param tenant
|
* @param tenant
|
||||||
* the contentType of the message
|
* the contentType of the message
|
||||||
* @param virtualHost
|
*
|
||||||
* the virtual host
|
|
||||||
* @return a message if <null> no message is send back to sender
|
* @return a message if <null> no message is send back to sender
|
||||||
*/
|
*/
|
||||||
|
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.receiverQueue}", containerFactory = "listenerContainerFactory")
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@RabbitListener(queues = "${hawkbit.dmf.rabbitmq.authenticationReceiverQueue}", containerFactory = "listenerContainerFactory")
|
||||||
|
public Message onAuthenticationRequest(final Message message) {
|
||||||
|
checkContentTypeJson(message);
|
||||||
|
final SecurityContext oldContext = SecurityContextHolder.getContext();
|
||||||
|
try {
|
||||||
|
return handleAuthentifiactionMessage(message);
|
||||||
|
} catch (final IllegalArgumentException ex) {
|
||||||
|
throw new AmqpRejectAndDontRequeueException("Invalid message!", ex);
|
||||||
|
} catch (final TenantNotExistException teex) {
|
||||||
|
throw new AmqpRejectAndDontRequeueException(teex);
|
||||||
|
} finally {
|
||||||
|
SecurityContextHolder.setContext(oldContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Message onMessage(final Message message, final String type, final String tenant, final String virtualHost) {
|
public Message onMessage(final Message message, final String type, final String tenant, final String virtualHost) {
|
||||||
checkContentTypeJson(message);
|
checkContentTypeJson(message);
|
||||||
final SecurityContext oldContext = SecurityContextHolder.getContext();
|
final SecurityContext oldContext = SecurityContextHolder.getContext();
|
||||||
@@ -148,11 +168,13 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
final EventTopic eventTopic = EventTopic.valueOf(topicValue);
|
final EventTopic eventTopic = EventTopic.valueOf(topicValue);
|
||||||
handleIncomingEvent(message, eventTopic);
|
handleIncomingEvent(message, eventTopic);
|
||||||
break;
|
break;
|
||||||
case AUTHENTIFICATION:
|
|
||||||
return handleAuthentifiactionMessage(message);
|
|
||||||
default:
|
default:
|
||||||
logAndThrowMessageError(message, "No handle method was found for the given message type.");
|
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 {
|
} finally {
|
||||||
SecurityContextHolder.setContext(oldContext);
|
SecurityContextHolder.setContext(oldContext);
|
||||||
}
|
}
|
||||||
@@ -308,9 +330,10 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
final DistributionSet distributionSet = action.getDistributionSet();
|
final DistributionSet distributionSet = action.getDistributionSet();
|
||||||
final List<SoftwareModule> softwareModuleList = controllerManagement
|
final List<SoftwareModule> softwareModuleList = controllerManagement
|
||||||
.findSoftwareModulesByDistributionSet(distributionSet);
|
.findSoftwareModulesByDistributionSet(distributionSet);
|
||||||
|
final String targetSecurityToken = systemSecurityContext.runAsSystem(() -> target.getSecurityToken());
|
||||||
eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(),
|
eventBus.post(new TargetAssignDistributionSetEvent(target.getOptLockRevision(), target.getTenant(),
|
||||||
target.getControllerId(), action.getId(), softwareModuleList, target.getTargetInfo().getAddress(),
|
target.getControllerId(), action.getId(), softwareModuleList, target.getTargetInfo().getAddress(),
|
||||||
target.getSecurityToken()));
|
targetSecurityToken));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,11 +363,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
final ActionUpdateStatus actionUpdateStatus = convertMessage(message, ActionUpdateStatus.class);
|
final ActionUpdateStatus actionUpdateStatus = convertMessage(message, ActionUpdateStatus.class);
|
||||||
final Action action = checkActionExist(message, actionUpdateStatus);
|
final Action action = checkActionExist(message, actionUpdateStatus);
|
||||||
|
|
||||||
final ActionStatus actionStatus = entityFactory.generateActionStatus();
|
final ActionStatus actionStatus = createActionStatus(message, actionUpdateStatus, action);
|
||||||
actionUpdateStatus.getMessage().forEach(actionStatus::addMessage);
|
|
||||||
|
|
||||||
actionStatus.setAction(action);
|
|
||||||
actionStatus.setOccurredAt(System.currentTimeMillis());
|
|
||||||
|
|
||||||
switch (actionUpdateStatus.getActionStatus()) {
|
switch (actionUpdateStatus.getActionStatus()) {
|
||||||
case DOWNLOAD:
|
case DOWNLOAD:
|
||||||
@@ -382,6 +401,25 @@ 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 "
|
||||||
|
+ convertCorrelationId(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
actionStatus.setAction(action);
|
||||||
|
actionStatus.setOccurredAt(System.currentTimeMillis());
|
||||||
|
return actionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String convertCorrelationId(final Message message) {
|
||||||
|
return new String(message.getMessageProperties().getCorrelationId());
|
||||||
|
}
|
||||||
|
|
||||||
private Action getUpdateActionStatus(final ActionStatus actionStatus) {
|
private Action getUpdateActionStatus(final ActionStatus actionStatus) {
|
||||||
if (actionStatus.getStatus().equals(Status.CANCELED)) {
|
if (actionStatus.getStatus().equals(Status.CANCELED)) {
|
||||||
return controllerManagement.addCancelActionStatus(actionStatus);
|
return controllerManagement.addCancelActionStatus(actionStatus);
|
||||||
@@ -421,12 +459,12 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkContentTypeJson(final Message message) {
|
private static void checkContentTypeJson(final Message message) {
|
||||||
final MessageProperties messageProperties = message.getMessageProperties();
|
final MessageProperties messageProperties = message.getMessageProperties();
|
||||||
if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) {
|
if (messageProperties.getContentType() != null && messageProperties.getContentType().contains("json")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Content-Type is not JSON compatible");
|
throw new AmqpRejectAndDontRequeueException("Content-Type is not JSON compatible");
|
||||||
}
|
}
|
||||||
|
|
||||||
void setControllerManagement(final ControllerManagement controllerManagement) {
|
void setControllerManagement(final ControllerManagement controllerManagement) {
|
||||||
@@ -457,4 +495,7 @@ public class AmqpMessageHandlerService extends BaseAmqpService {
|
|||||||
this.entityFactory = entityFactory;
|
this.entityFactory = entityFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSystemSecurityContext(final SystemSecurityContext systemSecurityContext) {
|
||||||
|
this.systemSecurityContext = systemSecurityContext;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,20 +31,98 @@ public class AmqpProperties {
|
|||||||
private String deadLetterExchange = "dmf.connector.deadletter";
|
private String deadLetterExchange = "dmf.connector.deadletter";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DMF API receiving queue.
|
* DMF API receiving queue for EVENT or THING_CREATED message.
|
||||||
*/
|
*/
|
||||||
private String receiverQueue = "dmf_receiver";
|
private String receiverQueue = "dmf_receiver";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authentication request called by 3rd party artifact storages for download
|
||||||
|
* authorizations.
|
||||||
|
*/
|
||||||
|
private String authenticationReceiverQueue = "authentication_receiver";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Missing queue fatal.
|
* Missing queue fatal.
|
||||||
*/
|
*/
|
||||||
private boolean missingQueuesFatal = false;
|
private boolean missingQueuesFatal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requested heartbeat interval from broker in {@link TimeUnit#SECONDS}.
|
* Requested heartbeat interval from broker in {@link TimeUnit#SECONDS}.
|
||||||
*/
|
*/
|
||||||
private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(60);
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of retry attempts when passive queue declaration fails.
|
||||||
|
* Passive queue declaration occurs when the consumer starts or, when
|
||||||
|
* consuming from multiple queues, when not all queues were available during
|
||||||
|
* initialization.
|
||||||
|
*/
|
||||||
|
private int declarationRetries = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the declarationRetries
|
||||||
|
*/
|
||||||
|
public int getDeclarationRetries() {
|
||||||
|
return declarationRetries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param declarationRetries
|
||||||
|
* the declarationRetries to set
|
||||||
|
*/
|
||||||
|
public void setDeclarationRetries(final int declarationRetries) {
|
||||||
|
this.declarationRetries = declarationRetries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthenticationReceiverQueue() {
|
||||||
|
return authenticationReceiverQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthenticationReceiverQueue(final String authenticationReceiverQueue) {
|
||||||
|
this.authenticationReceiverQueue = authenticationReceiverQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
* Is missingQueuesFatal enabled
|
||||||
*
|
*
|
||||||
@@ -106,10 +184,6 @@ public class AmqpProperties {
|
|||||||
return receiverQueue;
|
return receiverQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReceiverQueue(final String receiverQueue) {
|
|
||||||
this.receiverQueue = receiverQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRequestedHeartBeat() {
|
public int getRequestedHeartBeat() {
|
||||||
return requestedHeartBeat;
|
return requestedHeartBeat;
|
||||||
}
|
}
|
||||||
@@ -118,4 +192,8 @@ public class AmqpProperties {
|
|||||||
this.requestedHeartBeat = requestedHeartBeat;
|
this.requestedHeartBeat = requestedHeartBeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setReceiverQueue(final String receiverQueue) {
|
||||||
|
this.receiverQueue = receiverQueue;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
import org.springframework.amqp.support.converter.AbstractJavaTypeMapper;
|
||||||
@@ -110,7 +111,7 @@ public class BaseAmqpService {
|
|||||||
|
|
||||||
protected final void logAndThrowMessageError(final Message message, final String error) {
|
protected final void logAndThrowMessageError(final Message message, final String error) {
|
||||||
LOGGER.warn("Warning! \"{}\" reported by message: {}", error, message);
|
LOGGER.warn("Warning! \"{}\" reported by message: {}", error, message);
|
||||||
throw new IllegalArgumentException(error);
|
throw new AmqpRejectAndDontRequeueException(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected RabbitTemplate getRabbitTemplate() {
|
protected RabbitTemplate getRabbitTemplate() {
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||||
|
*
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.amqp;
|
||||||
|
|
||||||
|
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
||||||
|
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||||
|
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
|
||||||
|
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link RabbitListenerContainerFactory} that can be configured through
|
||||||
|
* hawkBit's {@link AmqpProperties}.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ConfigurableRabbitListenerContainerFactory extends SimpleRabbitListenerContainerFactory {
|
||||||
|
private final AmqpProperties amqpProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param rabbitConnectionFactory
|
||||||
|
* for the container factory
|
||||||
|
* @param amqpProperties
|
||||||
|
* to configure the container factory
|
||||||
|
*/
|
||||||
|
public ConfigurableRabbitListenerContainerFactory(final AmqpProperties amqpProperties,
|
||||||
|
final ConnectionFactory rabbitConnectionFactory) {
|
||||||
|
this.amqpProperties = amqpProperties;
|
||||||
|
setDefaultRequeueRejected(true);
|
||||||
|
setConnectionFactory(rabbitConnectionFactory);
|
||||||
|
setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal());
|
||||||
|
setConcurrentConsumers(amqpProperties.getInitialConcurrentConsumers());
|
||||||
|
setMaxConcurrentConsumers(amqpProperties.getMaxConcurrentConsumers());
|
||||||
|
setPrefetchCount(amqpProperties.getPrefetchCount());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
// Exception squid:UnusedProtectedMethod - called by
|
||||||
|
// AbstractRabbitListenerContainerFactory
|
||||||
|
@SuppressWarnings("squid:UnusedProtectedMethod")
|
||||||
|
protected void initializeContainer(final SimpleMessageListenerContainer instance) {
|
||||||
|
super.initializeContainer(instance);
|
||||||
|
instance.setDeclarationRetries(amqpProperties.getDeclarationRetries());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,7 @@ public class DefaultAmqpSenderService implements AmqpSenderService {
|
|||||||
|
|
||||||
final String correlationId = UUID.randomUUID().toString();
|
final String correlationId = UUID.randomUUID().toString();
|
||||||
final String exchange = extractExchange(replyTo);
|
final String exchange = extractExchange(replyTo);
|
||||||
|
message.getMessageProperties().setCorrelationId(correlationId.getBytes());
|
||||||
|
|
||||||
if (LOGGER.isTraceEnabled()) {
|
if (LOGGER.isTraceEnabled()) {
|
||||||
LOGGER.trace("Sending message {} to exchange {} with correlationId {}", message, exchange, correlationId);
|
LOGGER.trace("Sending message {} to exchange {} with correlationId {}", message, exchange, correlationId);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import org.springframework.core.task.TaskExecutor;
|
import org.springframework.core.task.TaskExecutor;
|
||||||
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
|
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||||
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;
|
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
|
||||||
@@ -78,18 +78,17 @@ public class AmqpTestConfiguration {
|
|||||||
* @return ExecutorService with security context availability in thread
|
* @return ExecutorService with security context availability in thread
|
||||||
* execution..
|
* execution..
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean(destroyMethod = "shutdown")
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Executor asyncExecutor() {
|
public Executor asyncExecutor() {
|
||||||
return new DelegatingSecurityContextExecutor(threadPoolExecutor());
|
return new DelegatingSecurityContextExecutorService(threadPoolExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return central ThreadPoolExecutor for general purpose multi threaded
|
* @return central ThreadPoolExecutor for general purpose multi threaded
|
||||||
* operations. Tries an orderly shutdown when destroyed.
|
* operations. Tries an orderly shutdown when destroyed.
|
||||||
*/
|
*/
|
||||||
@Bean(destroyMethod = "shutdown")
|
private ThreadPoolExecutor threadPoolExecutor() {
|
||||||
public ThreadPoolExecutor threadPoolExecutor() {
|
|
||||||
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(10);
|
final BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(10);
|
||||||
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS,
|
final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS,
|
||||||
blockingQueue, new ThreadFactoryBuilder().setNameFormat("central-executor-pool-%d").build());
|
blockingQueue, new ThreadFactoryBuilder().setNameFormat("central-executor-pool-%d").build());
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ public class AmqpControllerAuthenticationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Tests authentication message without principal")
|
@Description("Tests authentication message without principal")
|
||||||
public void testAuthenticationMessageBadCredantialsWithoutPricipal() {
|
public void testAuthenticationMessageBadCredantialsWithoutPricipal() {
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
|
final MessageProperties messageProperties = createMessageProperties(null);
|
||||||
|
|
||||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
||||||
FileResource.sha1("12345"));
|
FileResource.sha1("12345"));
|
||||||
@@ -166,8 +166,7 @@ public class AmqpControllerAuthenticationTest {
|
|||||||
messageProperties);
|
messageProperties);
|
||||||
|
|
||||||
// test
|
// test
|
||||||
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
|
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||||
TENANT, "vHost");
|
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||||
@@ -178,7 +177,7 @@ public class AmqpControllerAuthenticationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Tests authentication message without wrong credential")
|
@Description("Tests authentication message without wrong credential")
|
||||||
public void testAuthenticationMessageBadCredantialsWithWrongCredential() {
|
public void testAuthenticationMessageBadCredantialsWithWrongCredential() {
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
|
final MessageProperties messageProperties = createMessageProperties(null);
|
||||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
||||||
FileResource.sha1("12345"));
|
FileResource.sha1("12345"));
|
||||||
when(tenantConfigurationManagement.getConfigurationValue(
|
when(tenantConfigurationManagement.getConfigurationValue(
|
||||||
@@ -189,8 +188,7 @@ public class AmqpControllerAuthenticationTest {
|
|||||||
messageProperties);
|
messageProperties);
|
||||||
|
|
||||||
// test
|
// test
|
||||||
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
|
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||||
TENANT, "vHost");
|
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||||
@@ -201,7 +199,7 @@ public class AmqpControllerAuthenticationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Tests authentication message successfull")
|
@Description("Tests authentication message successfull")
|
||||||
public void testSuccessfullMessageAuthentication() {
|
public void testSuccessfullMessageAuthentication() {
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
|
final MessageProperties messageProperties = createMessageProperties(null);
|
||||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, CONTROLLLER_ID,
|
||||||
FileResource.sha1("12345"));
|
FileResource.sha1("12345"));
|
||||||
when(tenantConfigurationManagement.getConfigurationValue(
|
when(tenantConfigurationManagement.getConfigurationValue(
|
||||||
@@ -212,8 +210,7 @@ public class AmqpControllerAuthenticationTest {
|
|||||||
messageProperties);
|
messageProperties);
|
||||||
|
|
||||||
// test
|
// test
|
||||||
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
|
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||||
TENANT, "vHost");
|
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||||
@@ -232,7 +229,9 @@ public class AmqpControllerAuthenticationTest {
|
|||||||
|
|
||||||
private MessageProperties createMessageProperties(final MessageType type, final String replyTo) {
|
private MessageProperties createMessageProperties(final MessageType type, final String replyTo) {
|
||||||
final MessageProperties messageProperties = new MessageProperties();
|
final MessageProperties messageProperties = new MessageProperties();
|
||||||
messageProperties.setHeader(MessageHeaderKey.TYPE, type.name());
|
if (type != null) {
|
||||||
|
messageProperties.setHeader(MessageHeaderKey.TYPE, type.name());
|
||||||
|
}
|
||||||
messageProperties.setHeader(MessageHeaderKey.TENANT, TENANT);
|
messageProperties.setHeader(MessageHeaderKey.TENANT, TENANT);
|
||||||
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
|
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
|
||||||
messageProperties.setReplyTo(replyTo);
|
messageProperties.setReplyTo(replyTo);
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import org.eclipse.hawkbit.repository.model.LocalArtifact;
|
|||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
import org.eclipse.hawkbit.repository.model.TargetInfo;
|
||||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||||
|
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -59,6 +60,7 @@ import org.mockito.ArgumentCaptor;
|
|||||||
import org.mockito.Matchers;
|
import org.mockito.Matchers;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
import org.springframework.amqp.AmqpRejectAndDontRequeueException;
|
||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
import org.springframework.amqp.core.MessageProperties;
|
import org.springframework.amqp.core.MessageProperties;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
@@ -111,6 +113,9 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private RabbitTemplate rabbitTemplate;
|
private RabbitTemplate rabbitTemplate;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SystemSecurityContext systemSecurityContextMock;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
messageConverter = new Jackson2JsonMessageConverter();
|
messageConverter = new Jackson2JsonMessageConverter();
|
||||||
@@ -123,6 +128,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
amqpMessageHandlerService.setHostnameResolver(hostnameResolverMock);
|
amqpMessageHandlerService.setHostnameResolver(hostnameResolverMock);
|
||||||
amqpMessageHandlerService.setEventBus(eventBus);
|
amqpMessageHandlerService.setEventBus(eventBus);
|
||||||
amqpMessageHandlerService.setEntityFactory(entityFactoryMock);
|
amqpMessageHandlerService.setEntityFactory(entityFactoryMock);
|
||||||
|
amqpMessageHandlerService.setSystemSecurityContext(systemSecurityContextMock);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,8 +140,8 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
final Message message = new Message(new byte[0], messageProperties);
|
final Message message = new Message(new byte[0], messageProperties);
|
||||||
try {
|
try {
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
||||||
fail("IllegalArgumentException was excepeted due to worng content type");
|
fail("AmqpRejectAndDontRequeueException was excepeted due to worng content type");
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (final AmqpRejectAndDontRequeueException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,8 +175,8 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
||||||
fail("IllegalArgumentException was excepeted since no replyTo header was set");
|
fail("AmqpRejectAndDontRequeueException was excepeted since no replyTo header was set");
|
||||||
} catch (final IllegalArgumentException exception) {
|
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||||
// test ok - exception was excepted
|
// test ok - exception was excepted
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,8 +189,8 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
|
final Message message = messageConverter.toMessage(new byte[0], messageProperties);
|
||||||
try {
|
try {
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, MessageType.THING_CREATED.name(), TENANT, "vHost");
|
||||||
fail("IllegalArgumentException was excepeted since no thingID was set");
|
fail("AmqpRejectAndDontRequeueException was excepeted since no thingID was set");
|
||||||
} catch (final IllegalArgumentException exception) {
|
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||||
// test ok - exception was excepted
|
// test ok - exception was excepted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,8 +205,8 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
amqpMessageHandlerService.onMessage(message, type, TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, type, TENANT, "vHost");
|
||||||
fail("IllegalArgumentException was excepeted due to unknown message type");
|
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown message type");
|
||||||
} catch (final IllegalArgumentException exception) {
|
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||||
// test ok - exception was excepted
|
// test ok - exception was excepted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,22 +218,22 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
final Message message = new Message(new byte[0], messageProperties);
|
final Message message = new Message(new byte[0], messageProperties);
|
||||||
try {
|
try {
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||||
fail("IllegalArgumentException was excepeted due to unknown message type");
|
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown message type");
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (final AmqpRejectAndDontRequeueException e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, "wrongTopic");
|
messageProperties.setHeader(MessageHeaderKey.TOPIC, "wrongTopic");
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||||
fail("IllegalArgumentException was excepeted due to unknown topic");
|
fail("AmqpRejectAndDontRequeueException was excepeted due to unknown topic");
|
||||||
} catch (final IllegalArgumentException e) {
|
} catch (final AmqpRejectAndDontRequeueException e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.CANCEL_DOWNLOAD.name());
|
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.CANCEL_DOWNLOAD.name());
|
||||||
try {
|
try {
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||||
fail("IllegalArgumentException was excepeted because there was no event topic");
|
fail("AmqpRejectAndDontRequeueException was excepeted because there was no event topic");
|
||||||
} catch (final IllegalArgumentException exception) {
|
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||||
// test ok - exception was excepted
|
// test ok - exception was excepted
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,8 +251,8 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||||
fail("IllegalArgumentException was excepeted since no action id was set");
|
fail("AmqpRejectAndDontRequeueException was excepeted since no action id was set");
|
||||||
} catch (final IllegalArgumentException exception) {
|
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||||
// test ok - exception was excepted
|
// test ok - exception was excepted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,8 +268,8 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
amqpMessageHandlerService.onMessage(message, MessageType.EVENT.name(), TENANT, "vHost");
|
||||||
fail("IllegalArgumentException was excepeted since no action id was set");
|
fail("AmqpRejectAndDontRequeueException was excepeted since no action id was set");
|
||||||
} catch (final IllegalArgumentException exception) {
|
} catch (final AmqpRejectAndDontRequeueException exception) {
|
||||||
// test ok - exception was excepted
|
// test ok - exception was excepted
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,14 +278,13 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Tests that an download request is denied for an artifact which does not exists")
|
@Description("Tests that an download request is denied for an artifact which does not exists")
|
||||||
public void authenticationRequestDeniedForArtifactWhichDoesNotExists() {
|
public void authenticationRequestDeniedForArtifactWhichDoesNotExists() {
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
|
final MessageProperties messageProperties = createMessageProperties(null);
|
||||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", FileResource.sha1("12345"));
|
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", FileResource.sha1("12345"));
|
||||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||||
messageProperties);
|
messageProperties);
|
||||||
|
|
||||||
// test
|
// test
|
||||||
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
|
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||||
TENANT, "vHost");
|
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||||
@@ -291,7 +296,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Tests that an download request is denied for an artifact which is not assigned to the requested target")
|
@Description("Tests that an download request is denied for an artifact which is not assigned to the requested target")
|
||||||
public void authenticationRequestDeniedForArtifactWhichIsNotAssignedToTarget() {
|
public void authenticationRequestDeniedForArtifactWhichIsNotAssignedToTarget() {
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
|
final MessageProperties messageProperties = createMessageProperties(null);
|
||||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", FileResource.sha1("12345"));
|
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", FileResource.sha1("12345"));
|
||||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||||
messageProperties);
|
messageProperties);
|
||||||
@@ -302,8 +307,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
.thenThrow(EntityNotFoundException.class);
|
.thenThrow(EntityNotFoundException.class);
|
||||||
|
|
||||||
// test
|
// test
|
||||||
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
|
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||||
TENANT, "vHost");
|
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||||
@@ -315,7 +319,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Tests that an download request is allowed for an artifact which exists and assigned to the requested target")
|
@Description("Tests that an download request is allowed for an artifact which exists and assigned to the requested target")
|
||||||
public void authenticationRequestAllowedForArtifactWhichExistsAndAssignedToTarget() throws MalformedURLException {
|
public void authenticationRequestAllowedForArtifactWhichExistsAndAssignedToTarget() throws MalformedURLException {
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.AUTHENTIFICATION);
|
final MessageProperties messageProperties = createMessageProperties(null);
|
||||||
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", FileResource.sha1("12345"));
|
final TenantSecurityToken securityToken = new TenantSecurityToken(TENANT, "123", FileResource.sha1("12345"));
|
||||||
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
final Message message = amqpMessageHandlerService.getMessageConverter().toMessage(securityToken,
|
||||||
messageProperties);
|
messageProperties);
|
||||||
@@ -333,8 +337,7 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost"));
|
when(hostnameResolverMock.resolveHostname()).thenReturn(new URL("http://localhost"));
|
||||||
|
|
||||||
// test
|
// test
|
||||||
final Message onMessage = amqpMessageHandlerService.onMessage(message, MessageType.AUTHENTIFICATION.name(),
|
final Message onMessage = amqpMessageHandlerService.onAuthenticationRequest(message);
|
||||||
TENANT, "vHost");
|
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
final DownloadResponse downloadResponse = (DownloadResponse) messageConverter.fromMessage(onMessage);
|
||||||
@@ -366,6 +369,8 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
when(controllerManagementMock.findSoftwareModulesByDistributionSet(Matchers.any()))
|
when(controllerManagementMock.findSoftwareModulesByDistributionSet(Matchers.any()))
|
||||||
.thenReturn(softwareModuleList);
|
.thenReturn(softwareModuleList);
|
||||||
|
|
||||||
|
when(systemSecurityContextMock.runAsSystem(anyObject())).thenReturn("securityToken");
|
||||||
|
|
||||||
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
final MessageProperties messageProperties = createMessageProperties(MessageType.EVENT);
|
||||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
messageProperties.setHeader(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.name());
|
||||||
final ActionUpdateStatus actionUpdateStatus = createActionUpdateStatus(ActionStatus.FINISHED, 23L);
|
final ActionUpdateStatus actionUpdateStatus = createActionUpdateStatus(ActionStatus.FINISHED, 23L);
|
||||||
@@ -410,7 +415,9 @@ public class AmqpMessageHandlerServiceTest {
|
|||||||
|
|
||||||
private MessageProperties createMessageProperties(final MessageType type, final String replyTo) {
|
private MessageProperties createMessageProperties(final MessageType type, final String replyTo) {
|
||||||
final MessageProperties messageProperties = new MessageProperties();
|
final MessageProperties messageProperties = new MessageProperties();
|
||||||
messageProperties.setHeader(MessageHeaderKey.TYPE, type.name());
|
if (type != null) {
|
||||||
|
messageProperties.setHeader(MessageHeaderKey.TYPE, type.name());
|
||||||
|
}
|
||||||
messageProperties.setHeader(MessageHeaderKey.TENANT, TENANT);
|
messageProperties.setHeader(MessageHeaderKey.TENANT, TENANT);
|
||||||
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
|
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
|
||||||
messageProperties.setReplyTo(replyTo);
|
messageProperties.setReplyTo(replyTo);
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
|||||||
@Stories("Test to generate the artifact download URL")
|
@Stories("Test to generate the artifact download URL")
|
||||||
@SpringApplicationConfiguration(classes = { AmqpTestConfiguration.class,
|
@SpringApplicationConfiguration(classes = { AmqpTestConfiguration.class,
|
||||||
org.eclipse.hawkbit.RepositoryApplicationConfiguration.class })
|
org.eclipse.hawkbit.RepositoryApplicationConfiguration.class })
|
||||||
|
|
||||||
public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTestWithMongoDB {
|
public class PropertyBasedArtifactUrlHandlerTest extends AbstractIntegrationTestWithMongoDB {
|
||||||
|
|
||||||
private static final String HTTPS_LOCALHOST = "https://localhost:8080/";
|
private static final String HTTPS_LOCALHOST = "https://localhost:8080/";
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ public final class AmqpSettings {
|
|||||||
|
|
||||||
public static final String DMF_EXCHANGE = "dmf.exchange";
|
public static final String DMF_EXCHANGE = "dmf.exchange";
|
||||||
|
|
||||||
|
public static final String AUTHENTICATION_EXCHANGE = "authentication.exchange";
|
||||||
|
|
||||||
private AmqpSettings() {
|
private AmqpSettings() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,4 @@ public enum MessageType {
|
|||||||
*/
|
*/
|
||||||
THING_CREATED,
|
THING_CREATED,
|
||||||
|
|
||||||
/**
|
|
||||||
* The authentication type.
|
|
||||||
*/
|
|
||||||
AUTHENTIFICATION,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,20 @@ public class MgmtTargetRequestBody {
|
|||||||
@JsonProperty(required = true)
|
@JsonProperty(required = true)
|
||||||
private String controllerId;
|
private String controllerId;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String securityToken;
|
||||||
|
|
||||||
|
public String getSecurityToken() {
|
||||||
|
return securityToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecurityToken(final String securityToken) {
|
||||||
|
this.securityToken = securityToken;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the name
|
* @return the name
|
||||||
*/
|
*/
|
||||||
@@ -66,4 +80,12 @@ public class MgmtTargetRequestBody {
|
|||||||
return this;
|
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.
|
* Handles the GET request of retrieving a single target.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* the ID of the target to retrieve
|
* the ID of the target to retrieve
|
||||||
* @return a single target with status OK.
|
* @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 })
|
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.
|
* 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
|
* path of the request. A given ID in the request body is ignored. It's not
|
||||||
* possible to set fields to {@code null} values.
|
* possible to set fields to {@code null} values.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* the path parameter which contains the ID of the target
|
* the path parameter which contains the ID of the target
|
||||||
* @param targetRest
|
* @param targetRest
|
||||||
* the request body which contains the fields which should be
|
* 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
|
* @return the updated target response which contains all fields also fields
|
||||||
* which have not updated
|
* 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 })
|
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);
|
@RequestBody final MgmtTargetRequestBody targetRest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the DELETE request of deleting a target.
|
* Handles the DELETE request of deleting a target.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* the ID of the target to be deleted
|
* the ID of the target to be deleted
|
||||||
* @return If the given targetId could exists and could be deleted Http OK.
|
* @return If the given controllerId could exists and could be deleted Http
|
||||||
* In any failure the JsonResponseExceptionHandler is handling the
|
* OK. In any failure the JsonResponseExceptionHandler is handling
|
||||||
* response.
|
* 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 })
|
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
|
* Handles the GET request of retrieving the attributes of a specific
|
||||||
* target.
|
* target.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* the ID of the target to retrieve the attributes.
|
* the ID of the target to retrieve the attributes.
|
||||||
* @return the target attributes as map response with status OK
|
* @return the target attributes as map response with status OK
|
||||||
*/
|
*/
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/attributes", produces = { "application/hal+json",
|
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/attributes", produces = {
|
||||||
MediaType.APPLICATION_JSON_VALUE })
|
"application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("targetId") final String targetId);
|
ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("controllerId") final String controllerId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the GET request of retrieving the Actions of a specific target.
|
* Handles the GET request of retrieving the Actions of a specific target.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* to load actions for
|
* to load actions for
|
||||||
* @param pagingOffsetParam
|
* @param pagingOffsetParam
|
||||||
* the offset of list of targets for pagination, might not be
|
* 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
|
* status OK. The response is always paged. In any failure the
|
||||||
* JsonResponseExceptionHandler is handling the response.
|
* 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 })
|
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_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_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_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
|
* Handles the GET request of retrieving a specific Actions of a specific
|
||||||
* Target.
|
* Target.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* to load the action for
|
* to load the action for
|
||||||
* @param actionId
|
* @param actionId
|
||||||
* to load
|
* to load
|
||||||
* @return the action
|
* @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 })
|
"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);
|
@PathVariable("actionId") final Long actionId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the DELETE request of canceling an specific Actions of a specific
|
* Handles the DELETE request of canceling an specific Actions of a specific
|
||||||
* Target.
|
* Target.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* the ID of the target in the URL path parameter
|
* the ID of the target in the URL path parameter
|
||||||
* @param actionId
|
* @param actionId
|
||||||
* the ID of the action in the URL path parameter
|
* the ID of the action in the URL path parameter
|
||||||
@@ -186,8 +186,8 @@ public interface MgmtTargetRestApi {
|
|||||||
* optional parameter, which indicates a force cancel
|
* optional parameter, which indicates a force cancel
|
||||||
* @return status no content in case cancellation was successful
|
* @return status no content in case cancellation was successful
|
||||||
*/
|
*/
|
||||||
@RequestMapping(method = RequestMethod.DELETE, value = "/{targetId}/actions/{actionId}")
|
@RequestMapping(method = RequestMethod.DELETE, value = "/{controllerId}/actions/{actionId}")
|
||||||
ResponseEntity<Void> cancelAction(@PathVariable("targetId") final String targetId,
|
ResponseEntity<Void> cancelAction(@PathVariable("controllerId") final String controllerId,
|
||||||
@PathVariable("actionId") final Long actionId,
|
@PathVariable("actionId") final Long actionId,
|
||||||
@RequestParam(value = "force", required = false, defaultValue = "false") final boolean force);
|
@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
|
* Handles the GET request of retrieving the ActionStatus of a specific
|
||||||
* target and action.
|
* target and action.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* of the the action
|
* of the the action
|
||||||
* @param actionId
|
* @param actionId
|
||||||
* of the status we are intend to load
|
* 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
|
* with status OK. The response is always paged. In any failure the
|
||||||
* JsonResponseExceptionHandler is handling the response.
|
* 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 })
|
"application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<PagedList<MgmtActionStatus>> getActionStatusList(@PathVariable("targetId") final String targetId,
|
ResponseEntity<PagedList<MgmtActionStatus>> getActionStatusList(
|
||||||
@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_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_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_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
|
* Handles the GET request of retrieving the assigned distribution set of an
|
||||||
* specific target.
|
* specific target.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* the ID of the target to retrieve the assigned distribution
|
* the ID of the target to retrieve the assigned distribution
|
||||||
* @return the assigned distribution set with status OK, if none is assigned
|
* @return the assigned distribution set with status OK, if none is assigned
|
||||||
* than {@code null} content (e.g. "{}")
|
* than {@code null} content (e.g. "{}")
|
||||||
*/
|
*/
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/assignedDS", produces = { "application/hal+json",
|
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/assignedDS", produces = {
|
||||||
MediaType.APPLICATION_JSON_VALUE })
|
"application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(@PathVariable("targetId") final String targetId);
|
ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(
|
||||||
|
@PathVariable("controllerId") final String controllerId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Changes the assigned distribution set of a target.
|
* Changes the assigned distribution set of a target.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* of the target to change
|
* of the target to change
|
||||||
* @param dsId
|
* @param dsId
|
||||||
* of the distributionset that is to be assigned
|
* of the distributionset that is to be assigned
|
||||||
* @return http status
|
* @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 })
|
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);
|
@RequestBody final MgmtDistributionSetAssigment dsId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the GET request of retrieving the installed distribution set of
|
* Handles the GET request of retrieving the installed distribution set of
|
||||||
* an specific target.
|
* an specific target.
|
||||||
*
|
*
|
||||||
* @param targetId
|
* @param controllerId
|
||||||
* the ID of the target to retrieve
|
* the ID of the target to retrieve
|
||||||
* @return the assigned installed set with status OK, if none is installed
|
* @return the assigned installed set with status OK, if none is installed
|
||||||
* than {@code null} content (e.g. "{}")
|
* than {@code null} content (e.g. "{}")
|
||||||
*/
|
*/
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/{targetId}/installedDS", produces = { "application/hal+json",
|
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/installedDS", produces = {
|
||||||
MediaType.APPLICATION_JSON_VALUE })
|
"application/hal+json", MediaType.APPLICATION_JSON_VALUE })
|
||||||
ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(@PathVariable("targetId") final String targetId);
|
ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(
|
||||||
|
@PathVariable("controllerId") final String controllerId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,28 +68,24 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
|
|||||||
@RequestParam(value = "md5sum", required = false) final String md5Sum,
|
@RequestParam(value = "md5sum", required = false) final String md5Sum,
|
||||||
@RequestParam(value = "sha1sum", required = false) final String sha1Sum) {
|
@RequestParam(value = "sha1sum", required = false) final String sha1Sum) {
|
||||||
|
|
||||||
Artifact result;
|
if (file.isEmpty()) {
|
||||||
if (!file.isEmpty()) {
|
|
||||||
String fileName = optionalFileName;
|
|
||||||
|
|
||||||
if (null == fileName) {
|
|
||||||
fileName = file.getOriginalFilename();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
result = artifactManagement.createLocalArtifact(file.getInputStream(), softwareModuleId, fileName,
|
|
||||||
md5Sum == null ? null : md5Sum.toLowerCase(), sha1Sum == null ? null : sha1Sum.toLowerCase(),
|
|
||||||
false, file.getContentType());
|
|
||||||
} catch (final IOException e) {
|
|
||||||
LOG.error("Failed to store artifact", e);
|
|
||||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
String fileName = optionalFileName;
|
||||||
|
|
||||||
return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(result), HttpStatus.CREATED);
|
if (fileName == null) {
|
||||||
|
fileName = file.getOriginalFilename();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Artifact result = artifactManagement.createLocalArtifact(file.getInputStream(), softwareModuleId,
|
||||||
|
fileName, md5Sum == null ? null : md5Sum.toLowerCase(),
|
||||||
|
sha1Sum == null ? null : sha1Sum.toLowerCase(), false, file.getContentType());
|
||||||
|
return new ResponseEntity<>(MgmtSoftwareModuleMapper.toResponse(result), HttpStatus.CREATED);
|
||||||
|
} catch (final IOException e) {
|
||||||
|
LOG.error("Failed to store artifact", e);
|
||||||
|
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.model.PollStatus;
|
|||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
import org.eclipse.hawkbit.rest.data.SortDirection;
|
import org.eclipse.hawkbit.rest.data.SortDirection;
|
||||||
|
import org.eclipse.hawkbit.util.IpUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapper which maps repository model to RESTful model representation and
|
* 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();
|
final URI address = target.getTargetInfo().getAddress();
|
||||||
if (address != null) {
|
if (address != null) {
|
||||||
targetRest.setIpAddress(address.getHost());
|
if (IpUtil.isIpAddresKnown(address)) {
|
||||||
|
targetRest.setIpAddress(address.getHost());
|
||||||
|
}
|
||||||
targetRest.setAddress(address.toString());
|
targetRest.setAddress(address.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,9 +182,11 @@ public final class MgmtTargetMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Target fromRequest(final EntityFactory entityFactory, final MgmtTargetRequestBody targetRest) {
|
static Target fromRequest(final EntityFactory entityFactory, final MgmtTargetRequestBody targetRest) {
|
||||||
final Target target = entityFactory.generateTarget(targetRest.getControllerId());
|
final Target target = entityFactory.generateTarget(targetRest.getControllerId(), targetRest.getSecurityToken());
|
||||||
target.setDescription(targetRest.getDescription());
|
target.setDescription(targetRest.getDescription());
|
||||||
target.setName(targetRest.getName());
|
target.setName(targetRest.getName());
|
||||||
|
target.getTargetInfo().setAddress(targetRest.getAddress());
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
private EntityFactory entityFactory;
|
private EntityFactory entityFactory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<MgmtTarget> getTarget(@PathVariable("targetId") final String targetId) {
|
public ResponseEntity<MgmtTarget> getTarget(@PathVariable("controllerId") final String controllerId) {
|
||||||
final Target findTarget = findTargetWithExceptionIfNotFound(targetId);
|
final Target findTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||||
// to single response include poll status
|
// to single response include poll status
|
||||||
final MgmtTarget response = MgmtTargetMapper.toResponse(findTarget);
|
final MgmtTarget response = MgmtTargetMapper.toResponse(findTarget);
|
||||||
MgmtTargetMapper.addPollStatus(findTarget, response);
|
MgmtTargetMapper.addPollStatus(findTarget, response);
|
||||||
@@ -115,9 +115,9 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<MgmtTarget> updateTarget(@PathVariable("targetId") final String targetId,
|
public ResponseEntity<MgmtTarget> updateTarget(@PathVariable("controllerId") final String controllerId,
|
||||||
@RequestBody final MgmtTargetRequestBody targetRest) {
|
@RequestBody final MgmtTargetRequestBody targetRest) {
|
||||||
final Target existingTarget = findTargetWithExceptionIfNotFound(targetId);
|
final Target existingTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||||
LOG.debug("updating target {}", existingTarget.getId());
|
LOG.debug("updating target {}", existingTarget.getId());
|
||||||
if (targetRest.getDescription() != null) {
|
if (targetRest.getDescription() != null) {
|
||||||
existingTarget.setDescription(targetRest.getDescription());
|
existingTarget.setDescription(targetRest.getDescription());
|
||||||
@@ -125,22 +125,29 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
if (targetRest.getName() != null) {
|
if (targetRest.getName() != null) {
|
||||||
existingTarget.setName(targetRest.getName());
|
existingTarget.setName(targetRest.getName());
|
||||||
}
|
}
|
||||||
|
if (targetRest.getAddress() != null) {
|
||||||
|
existingTarget.getTargetInfo().setAddress(targetRest.getAddress());
|
||||||
|
}
|
||||||
|
if (targetRest.getSecurityToken() != null) {
|
||||||
|
existingTarget.setSecurityToken(targetRest.getSecurityToken());
|
||||||
|
}
|
||||||
|
|
||||||
final Target updateTarget = this.targetManagement.updateTarget(existingTarget);
|
final Target updateTarget = this.targetManagement.updateTarget(existingTarget);
|
||||||
|
|
||||||
return new ResponseEntity<>(MgmtTargetMapper.toResponse(updateTarget), HttpStatus.OK);
|
return new ResponseEntity<>(MgmtTargetMapper.toResponse(updateTarget), HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<Void> deleteTarget(@PathVariable("targetId") final String targetId) {
|
public ResponseEntity<Void> deleteTarget(@PathVariable("controllerId") final String controllerId) {
|
||||||
final Target target = findTargetWithExceptionIfNotFound(targetId);
|
final Target target = findTargetWithExceptionIfNotFound(controllerId);
|
||||||
this.targetManagement.deleteTargets(target.getId());
|
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);
|
return new ResponseEntity<>(HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("targetId") final String targetId) {
|
public ResponseEntity<MgmtTargetAttributes> getAttributes(@PathVariable("controllerId") final String controllerId) {
|
||||||
final Target foundTarget = findTargetWithExceptionIfNotFound(targetId);
|
final Target foundTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||||
final Map<String, String> controllerAttributes = foundTarget.getTargetInfo().getControllerAttributes();
|
final Map<String, String> controllerAttributes = foundTarget.getTargetInfo().getControllerAttributes();
|
||||||
if (controllerAttributes.isEmpty()) {
|
if (controllerAttributes.isEmpty()) {
|
||||||
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
@@ -153,13 +160,14 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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_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_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_SORTING, required = false) final String sortParam,
|
||||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam) {
|
@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 sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||||
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||||
@@ -177,14 +185,15 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new ResponseEntity<>(
|
return new ResponseEntity<>(
|
||||||
new PagedList<>(MgmtTargetMapper.toResponse(targetId, activeActions.getContent()), totalActionCount),
|
new PagedList<>(MgmtTargetMapper.toResponse(controllerId, activeActions.getContent()),
|
||||||
|
totalActionCount),
|
||||||
HttpStatus.OK);
|
HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<MgmtAction> getAction(@PathVariable("targetId") final String targetId,
|
public ResponseEntity<MgmtAction> getAction(@PathVariable("controllerId") final String controllerId,
|
||||||
@PathVariable("actionId") final Long actionId) {
|
@PathVariable("actionId") final Long actionId) {
|
||||||
final Target target = findTargetWithExceptionIfNotFound(targetId);
|
final Target target = findTargetWithExceptionIfNotFound(controllerId);
|
||||||
|
|
||||||
final Action action = findActionWithExceptionIfNotFound(actionId);
|
final Action action = findActionWithExceptionIfNotFound(actionId);
|
||||||
if (!action.getTarget().getId().equals(target.getId())) {
|
if (!action.getTarget().getId().equals(target.getId())) {
|
||||||
@@ -192,18 +201,18 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
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()) {
|
if (!action.isCancelingOrCanceled()) {
|
||||||
result.add(linkTo(
|
result.add(linkTo(
|
||||||
methodOn(MgmtDistributionSetRestApi.class).getDistributionSet(action.getDistributionSet().getId()))
|
methodOn(MgmtDistributionSetRestApi.class).getDistributionSet(action.getDistributionSet().getId()))
|
||||||
.withRel("distributionset"));
|
.withRel("distributionset"));
|
||||||
} else if (action.isCancelingOrCanceled()) {
|
} 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));
|
.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,
|
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
|
||||||
ActionStatusFields.ID.getFieldName() + ":" + SortDirection.DESC))
|
ActionStatusFields.ID.getFieldName() + ":" + SortDirection.DESC))
|
||||||
.withRel(MgmtRestConstants.TARGET_V1_ACTION_STATUS));
|
.withRel(MgmtRestConstants.TARGET_V1_ACTION_STATUS));
|
||||||
@@ -212,10 +221,10 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<Void> cancelAction(@PathVariable("targetId") final String targetId,
|
public ResponseEntity<Void> cancelAction(@PathVariable("controllerId") final String controllerId,
|
||||||
@PathVariable("actionId") final Long actionId,
|
@PathVariable("actionId") final Long actionId,
|
||||||
@RequestParam(value = "force", required = false, defaultValue = "false") final boolean force) {
|
@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);
|
final Action action = findActionWithExceptionIfNotFound(actionId);
|
||||||
|
|
||||||
if (force) {
|
if (force) {
|
||||||
@@ -231,12 +240,12 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<PagedList<MgmtActionStatus>> getActionStatusList(
|
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_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_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_SORTING, required = false) final String sortParam) {
|
||||||
|
|
||||||
final Target target = findTargetWithExceptionIfNotFound(targetId);
|
final Target target = findTargetWithExceptionIfNotFound(controllerId);
|
||||||
|
|
||||||
final Action action = findActionWithExceptionIfNotFound(actionId);
|
final Action action = findActionWithExceptionIfNotFound(actionId);
|
||||||
if (!action.getTarget().getId().equals(target.getId())) {
|
if (!action.getTarget().getId().equals(target.getId())) {
|
||||||
@@ -260,8 +269,8 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(
|
public ResponseEntity<MgmtDistributionSet> getAssignedDistributionSet(
|
||||||
@PathVariable("targetId") final String targetId) {
|
@PathVariable("controllerId") final String controllerId) {
|
||||||
final Target findTarget = findTargetWithExceptionIfNotFound(targetId);
|
final Target findTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||||
final MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper
|
final MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper
|
||||||
.toResponse(findTarget.getAssignedDistributionSet());
|
.toResponse(findTarget.getAssignedDistributionSet());
|
||||||
final HttpStatus retStatus;
|
final HttpStatus retStatus;
|
||||||
@@ -274,29 +283,29 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<Void> postAssignedDistributionSet(@PathVariable("targetId") final String targetId,
|
public ResponseEntity<Void> postAssignedDistributionSet(@PathVariable("controllerId") final String controllerId,
|
||||||
@RequestBody final MgmtDistributionSetAssigment dsId) {
|
@RequestBody final MgmtDistributionSetAssigment dsId) {
|
||||||
|
|
||||||
findTargetWithExceptionIfNotFound(targetId);
|
findTargetWithExceptionIfNotFound(controllerId);
|
||||||
final ActionType type = (dsId.getType() != null) ? MgmtRestModelMapper.convertActionType(dsId.getType())
|
final ActionType type = (dsId.getType() != null) ? MgmtRestModelMapper.convertActionType(dsId.getType())
|
||||||
: ActionType.FORCED;
|
: ActionType.FORCED;
|
||||||
final Iterator<Target> changed = this.deploymentManagement
|
final Iterator<Target> changed = this.deploymentManagement
|
||||||
.assignDistributionSet(dsId.getId(), type, dsId.getForcetime(), targetId).getAssignedEntity()
|
.assignDistributionSet(dsId.getId(), type, dsId.getForcetime(), controllerId).getAssignedEntity()
|
||||||
.iterator();
|
.iterator();
|
||||||
if (changed.hasNext()) {
|
if (changed.hasNext()) {
|
||||||
return new ResponseEntity<>(HttpStatus.OK);
|
return new ResponseEntity<>(HttpStatus.OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.error("Target update (ds {} assigment to target {}) failed! Returnd target list is empty.", dsId.getId(),
|
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);
|
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(
|
public ResponseEntity<MgmtDistributionSet> getInstalledDistributionSet(
|
||||||
@PathVariable("targetId") final String targetId) {
|
@PathVariable("controllerId") final String controllerId) {
|
||||||
final Target findTarget = findTargetWithExceptionIfNotFound(targetId);
|
final Target findTarget = findTargetWithExceptionIfNotFound(controllerId);
|
||||||
final MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper
|
final MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper
|
||||||
.toResponse(findTarget.getTargetInfo().getInstalledDistributionSet());
|
.toResponse(findTarget.getTargetInfo().getInstalledDistributionSet());
|
||||||
final HttpStatus retStatus;
|
final HttpStatus retStatus;
|
||||||
@@ -308,10 +317,10 @@ public class MgmtTargetResource implements MgmtTargetRestApi {
|
|||||||
return new ResponseEntity<>(distributionSetRest, retStatus);
|
return new ResponseEntity<>(distributionSetRest, retStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Target findTargetWithExceptionIfNotFound(final String targetId) {
|
private Target findTargetWithExceptionIfNotFound(final String controllerId) {
|
||||||
final Target findTarget = this.targetManagement.findTargetByControllerID(targetId);
|
final Target findTarget = this.targetManagement.findTargetByControllerID(controllerId);
|
||||||
if (findTarget == null) {
|
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;
|
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;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
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.ActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
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.AbstractRestIntegrationTest;
|
||||||
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
|
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
|
||||||
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
||||||
@@ -109,8 +109,8 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
|
|||||||
final String knownTargetId = "targetId";
|
final String knownTargetId = "targetId";
|
||||||
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
|
final List<Action> actions = generateTargetWithTwoUpdatesWithOneOverride(knownTargetId);
|
||||||
actions.get(0).setStatus(Status.FINISHED);
|
actions.get(0).setStatus(Status.FINISHED);
|
||||||
controllerManagament.addUpdateActionStatus(entityFactory.generateActionStatus(actions.get(0),
|
controllerManagament.addUpdateActionStatus(entityFactory.generateActionStatus(actions.get(0), Status.FINISHED,
|
||||||
Status.FINISHED, System.currentTimeMillis(), "testmessage"));
|
System.currentTimeMillis(), "testmessage"));
|
||||||
|
|
||||||
final PageRequest pageRequest = new PageRequest(0, 1000, Direction.ASC, ActionFields.ID.getFieldName());
|
final PageRequest pageRequest = new PageRequest(0, 1000, Direction.ASC, ActionFields.ID.getFieldName());
|
||||||
final ActionStatus status = deploymentManagement
|
final ActionStatus status = deploymentManagement
|
||||||
@@ -355,6 +355,54 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
|
|||||||
assertThat(findTargetByControllerID.getName()).isEqualTo(knownNameNotModiy);
|
assertThat(findTargetByControllerID.getName()).isEqualTo(knownNameNotModiy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Ensures that target update request is reflected by repository.")
|
||||||
|
public void updateTargetSecurityToken() throws Exception {
|
||||||
|
final String knownControllerId = "123";
|
||||||
|
final String knownNewToken = "6567576565";
|
||||||
|
final String knownNameNotModiy = "nameNotModiy";
|
||||||
|
final String body = new JSONObject().put("securityToken", knownNewToken).toString();
|
||||||
|
|
||||||
|
// prepare
|
||||||
|
final Target t = entityFactory.generateTarget(knownControllerId);
|
||||||
|
t.setName(knownNameNotModiy);
|
||||||
|
targetManagement.createTarget(t);
|
||||||
|
|
||||||
|
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownControllerId).content(body)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.controllerId", equalTo(knownControllerId)))
|
||||||
|
.andExpect(jsonPath("$.securityToken", equalTo(knownNewToken)))
|
||||||
|
.andExpect(jsonPath("$.name", equalTo(knownNameNotModiy)));
|
||||||
|
|
||||||
|
final Target findTargetByControllerID = targetManagement.findTargetByControllerID(knownControllerId);
|
||||||
|
assertThat(findTargetByControllerID.getSecurityToken()).isEqualTo(knownNewToken);
|
||||||
|
assertThat(findTargetByControllerID.getName()).isEqualTo(knownNameNotModiy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Ensures that target update request is reflected by repository.")
|
||||||
|
public void updateTargetAddress() throws Exception {
|
||||||
|
final String knownControllerId = "123";
|
||||||
|
final String knownNewAddress = "amqp://test123/foobar";
|
||||||
|
final String knownNameNotModiy = "nameNotModiy";
|
||||||
|
final String body = new JSONObject().put("address", knownNewAddress).toString();
|
||||||
|
|
||||||
|
// prepare
|
||||||
|
final Target t = entityFactory.generateTarget(knownControllerId);
|
||||||
|
t.setName(knownNameNotModiy);
|
||||||
|
targetManagement.createTarget(t);
|
||||||
|
|
||||||
|
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownControllerId).content(body)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.controllerId", equalTo(knownControllerId)))
|
||||||
|
.andExpect(jsonPath("$.address", equalTo(knownNewAddress)))
|
||||||
|
.andExpect(jsonPath("$.name", equalTo(knownNameNotModiy)));
|
||||||
|
|
||||||
|
final Target findTargetByControllerID = targetManagement.findTargetByControllerID(knownControllerId);
|
||||||
|
assertThat(findTargetByControllerID.getTargetInfo().getAddress().toString()).isEqualTo(knownNewAddress);
|
||||||
|
assertThat(findTargetByControllerID.getName()).isEqualTo(knownNameNotModiy);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Ensures that target query returns list of targets in defined format.")
|
@Description("Ensures that target query returns list of targets in defined format.")
|
||||||
public void getTargetWithoutAddtionalRequestParameters() throws Exception {
|
public void getTargetWithoutAddtionalRequestParameters() throws Exception {
|
||||||
@@ -679,9 +727,10 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createTargetsListReturnsSuccessful() throws Exception {
|
public void createTargetsListReturnsSuccessful() throws Exception {
|
||||||
final Target test1 = entityFactory.generateTarget("id1");
|
final Target test1 = entityFactory.generateTarget("id1", "token");
|
||||||
test1.setDescription("testid1");
|
test1.setDescription("testid1");
|
||||||
test1.setName("testname1");
|
test1.setName("testname1");
|
||||||
|
test1.getTargetInfo().setAddress("amqp://test123/foobar");
|
||||||
final Target test2 = entityFactory.generateTarget("id2");
|
final Target test2 = entityFactory.generateTarget("id2");
|
||||||
test2.setDescription("testid2");
|
test2.setDescription("testid2");
|
||||||
test2.setName("testname2");
|
test2.setName("testname2");
|
||||||
@@ -695,7 +744,7 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
|
|||||||
targets.add(test3);
|
targets.add(test3);
|
||||||
|
|
||||||
final MvcResult mvcResult = mvc
|
final MvcResult mvcResult = mvc
|
||||||
.perform(post("/rest/v1/targets/").content(JsonBuilder.targets(targets))
|
.perform(post("/rest/v1/targets/").content(JsonBuilder.targets(targets, true))
|
||||||
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
|
||||||
@@ -704,6 +753,8 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
|
|||||||
.andExpect(jsonPath("[0].description", equalTo("testid1")))
|
.andExpect(jsonPath("[0].description", equalTo("testid1")))
|
||||||
.andExpect(jsonPath("[0].createdAt", not(equalTo(0))))
|
.andExpect(jsonPath("[0].createdAt", not(equalTo(0))))
|
||||||
.andExpect(jsonPath("[0].createdBy", equalTo("bumlux")))
|
.andExpect(jsonPath("[0].createdBy", equalTo("bumlux")))
|
||||||
|
.andExpect(jsonPath("[0].securityToken", equalTo("token")))
|
||||||
|
.andExpect(jsonPath("[0].address", equalTo("amqp://test123/foobar")))
|
||||||
.andExpect(jsonPath("[1].name", equalTo("testname2")))
|
.andExpect(jsonPath("[1].name", equalTo("testname2")))
|
||||||
.andExpect(jsonPath("[1].createdBy", equalTo("bumlux")))
|
.andExpect(jsonPath("[1].createdBy", equalTo("bumlux")))
|
||||||
.andExpect(jsonPath("[1].controllerId", equalTo("id2")))
|
.andExpect(jsonPath("[1].controllerId", equalTo("id2")))
|
||||||
@@ -729,6 +780,9 @@ public class MgmtTargetResourceTest extends AbstractRestIntegrationTest {
|
|||||||
assertThat(targetManagement.findTargetByControllerID("id1")).isNotNull();
|
assertThat(targetManagement.findTargetByControllerID("id1")).isNotNull();
|
||||||
assertThat(targetManagement.findTargetByControllerID("id1").getName()).isEqualTo("testname1");
|
assertThat(targetManagement.findTargetByControllerID("id1").getName()).isEqualTo("testname1");
|
||||||
assertThat(targetManagement.findTargetByControllerID("id1").getDescription()).isEqualTo("testid1");
|
assertThat(targetManagement.findTargetByControllerID("id1").getDescription()).isEqualTo("testid1");
|
||||||
|
assertThat(targetManagement.findTargetByControllerID("id1").getSecurityToken()).isEqualTo("token");
|
||||||
|
assertThat(targetManagement.findTargetByControllerID("id1").getTargetInfo().getAddress().toString())
|
||||||
|
.isEqualTo("amqp://test123/foobar");
|
||||||
assertThat(targetManagement.findTargetByControllerID("id2")).isNotNull();
|
assertThat(targetManagement.findTargetByControllerID("id2")).isNotNull();
|
||||||
assertThat(targetManagement.findTargetByControllerID("id2").getName()).isEqualTo("testname2");
|
assertThat(targetManagement.findTargetByControllerID("id2").getName()).isEqualTo("testname2");
|
||||||
assertThat(targetManagement.findTargetByControllerID("id2").getDescription()).isEqualTo("testid2");
|
assertThat(targetManagement.findTargetByControllerID("id2").getDescription()).isEqualTo("testid2");
|
||||||
|
|||||||
@@ -269,8 +269,7 @@ public interface DeploymentManagement {
|
|||||||
* @return the actions referring a specific rollout and a specific parent
|
* @return the actions referring a specific rollout and a specific parent
|
||||||
* rollout group in a specific status
|
* rollout group in a specific status
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
List<Action> findActionsByRolloutGroupParentAndStatus(@NotNull Rollout rollout,
|
List<Action> findActionsByRolloutGroupParentAndStatus(@NotNull Rollout rollout,
|
||||||
@NotNull RolloutGroup rolloutGroupParent, @NotNull Action.Status actionStatus);
|
@NotNull RolloutGroup rolloutGroupParent, @NotNull Action.Status actionStatus);
|
||||||
|
|
||||||
@@ -496,8 +495,7 @@ public interface DeploymentManagement {
|
|||||||
* the action to start now.
|
* the action to start now.
|
||||||
* @return the action which has been started
|
* @return the action which has been started
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
Action startScheduledAction(@NotNull Action action);
|
Action startScheduledAction(@NotNull Action action);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -287,6 +287,7 @@ public interface EntityFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an empty {@link Target} without persisting it.
|
* Generates an empty {@link Target} without persisting it.
|
||||||
|
* {@link Target#getSecurityToken()} is generated.
|
||||||
*
|
*
|
||||||
* @param controllerID
|
* @param controllerID
|
||||||
* of the {@link Target}
|
* of the {@link Target}
|
||||||
@@ -295,6 +296,19 @@ public interface EntityFactory {
|
|||||||
*/
|
*/
|
||||||
Target generateTarget(@NotEmpty String controllerID);
|
Target generateTarget(@NotEmpty String controllerID);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an empty {@link Target} without persisting it.
|
||||||
|
*
|
||||||
|
* @param controllerID
|
||||||
|
* of the {@link Target}
|
||||||
|
* @param securityToken
|
||||||
|
* of the {@link Target} for authentication if enabled on tenant.
|
||||||
|
* Generates one if empty or <code>null</code>.
|
||||||
|
*
|
||||||
|
* @return {@link Target} object
|
||||||
|
*/
|
||||||
|
Target generateTarget(@NotEmpty String controllerID, @NotEmpty String securityToken);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an empty {@link TargetFilterQuery} without persisting it.
|
* Generates an empty {@link TargetFilterQuery} without persisting it.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -61,8 +61,7 @@ public interface RolloutManagement {
|
|||||||
* this check. This check is only applied if the last check is
|
* this check. This check is only applied if the last check is
|
||||||
* less than (lastcheck-delay).
|
* less than (lastcheck-delay).
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
void checkRunningRollouts(long delayBetweenChecks);
|
void checkRunningRollouts(long delayBetweenChecks);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -266,8 +265,7 @@ public interface RolloutManagement {
|
|||||||
* if given rollout is not in {@link RolloutStatus#RUNNING}.
|
* if given rollout is not in {@link RolloutStatus#RUNNING}.
|
||||||
* Only running rollouts can be paused.
|
* Only running rollouts can be paused.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
void pauseRollout(@NotNull Rollout rollout);
|
void pauseRollout(@NotNull Rollout rollout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -281,8 +279,7 @@ public interface RolloutManagement {
|
|||||||
* if given rollout is not in {@link RolloutStatus#PAUSED}. Only
|
* if given rollout is not in {@link RolloutStatus#PAUSED}. Only
|
||||||
* paused rollouts can be resumed.
|
* paused rollouts can be resumed.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
void resumeRollout(@NotNull Rollout rollout);
|
void resumeRollout(@NotNull Rollout rollout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -303,8 +300,7 @@ public interface RolloutManagement {
|
|||||||
* if given rollout is not in {@link RolloutStatus#READY}. Only
|
* if given rollout is not in {@link RolloutStatus#READY}. Only
|
||||||
* ready rollouts can be started.
|
* ready rollouts can be started.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
Rollout startRollout(@NotNull Rollout rollout);
|
Rollout startRollout(@NotNull Rollout rollout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,8 +322,7 @@ public interface RolloutManagement {
|
|||||||
* if given rollout is not in {@link RolloutStatus#READY}. Only
|
* if given rollout is not in {@link RolloutStatus#READY}. Only
|
||||||
* ready rollouts can be started.
|
* ready rollouts can be started.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
Rollout startRolloutAsync(@NotNull Rollout rollout);
|
Rollout startRolloutAsync(@NotNull Rollout rollout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -39,16 +39,14 @@ public interface SystemManagement {
|
|||||||
* @param tenant
|
* @param tenant
|
||||||
* to delete
|
* to delete
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
void deleteTenant(@NotNull String tenant);
|
void deleteTenant(@NotNull String tenant);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return list of all tenant names in the system.
|
* @return list of all tenant names in the system.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
List<String> findTenants();
|
List<String> findTenants();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,8 +66,8 @@ public interface SystemManagement {
|
|||||||
/**
|
/**
|
||||||
* Returns {@link TenantMetaData} of given and current tenant. Creates for
|
* Returns {@link TenantMetaData} of given and current tenant. Creates for
|
||||||
* new tenants also two {@link SoftwareModuleType} (os and app) and
|
* new tenants also two {@link SoftwareModuleType} (os and app) and
|
||||||
* {@link RepositoryConstants#DEFAULT_DS_TYPES_IN_TENANT} {@link DistributionSetType}s
|
* {@link RepositoryConstants#DEFAULT_DS_TYPES_IN_TENANT}
|
||||||
* (os and os_app).
|
* {@link DistributionSetType}s (os and os_app).
|
||||||
*
|
*
|
||||||
* DISCLAIMER: this variant is used during initial login (where the tenant
|
* DISCLAIMER: this variant is used during initial login (where the tenant
|
||||||
* is not yet in the session). Please user {@link #getTenantMetadata()} for
|
* is not yet in the session). Please user {@link #getTenantMetadata()} for
|
||||||
|
|||||||
@@ -179,25 +179,6 @@ public interface TargetManagement {
|
|||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
||||||
List<Target> createTargets(@NotNull Collection<Target> targets);
|
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.
|
* Deletes all targets with the given IDs.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ public interface TenantConfigurationManagement {
|
|||||||
* @return <null> if no default value is set and no database value available
|
* @return <null> if no default value is set and no database value available
|
||||||
* or returns the tenant configuration value
|
* or returns the tenant configuration value
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
<T> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(TenantConfigurationKey configurationKey,
|
<T> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(TenantConfigurationKey configurationKey,
|
||||||
Class<T> propertyType, TenantConfiguration tenantConfiguration);
|
Class<T> propertyType, TenantConfiguration tenantConfiguration);
|
||||||
|
|
||||||
@@ -87,8 +86,7 @@ public interface TenantConfigurationManagement {
|
|||||||
* if the property cannot be converted to the given
|
* if the property cannot be converted to the given
|
||||||
* {@code propertyType}
|
* {@code propertyType}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
<T> TenantConfigurationValue<T> getConfigurationValue(TenantConfigurationKey configurationKey);
|
<T> TenantConfigurationValue<T> getConfigurationValue(TenantConfigurationKey configurationKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -114,8 +112,7 @@ public interface TenantConfigurationManagement {
|
|||||||
* if the property cannot be converted to the given
|
* if the property cannot be converted to the given
|
||||||
* {@code propertyType}
|
* {@code propertyType}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
<T> TenantConfigurationValue<T> getConfigurationValue(TenantConfigurationKey configurationKey,
|
<T> TenantConfigurationValue<T> getConfigurationValue(TenantConfigurationKey configurationKey,
|
||||||
Class<T> propertyType);
|
Class<T> propertyType);
|
||||||
|
|
||||||
@@ -139,7 +136,6 @@ public interface TenantConfigurationManagement {
|
|||||||
* if the property cannot be converted to the given
|
* if the property cannot be converted to the given
|
||||||
* {@code propertyType}
|
* {@code propertyType}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION + SpringEvalExpressions.HAS_AUTH_OR
|
@PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION)
|
||||||
+ SpringEvalExpressions.IS_SYSTEM_CODE)
|
|
||||||
<T> T getGlobalConfigurationValue(TenantConfigurationKey configurationKey, Class<T> propertyType);
|
<T> T getGlobalConfigurationValue(TenantConfigurationKey configurationKey, Class<T> propertyType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown if a multi part exception occurred.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class MultiPartFileUploadException extends SpServerRtException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cause
|
||||||
|
* for the exception
|
||||||
|
*/
|
||||||
|
public MultiPartFileUploadException(final Throwable cause) {
|
||||||
|
super(cause.getMessage(), SpServerError.SP_ARTIFACT_UPLOAD_FAILED, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -58,4 +58,10 @@ public interface Target extends NamedEntity {
|
|||||||
*/
|
*/
|
||||||
String getSecurityToken();
|
String getSecurityToken();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param token
|
||||||
|
* new securityToken
|
||||||
|
*/
|
||||||
|
void setSecurityToken(String token);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,10 +16,19 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
public interface TargetInfo extends Serializable {
|
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();
|
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.
|
* @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.DistributionSetManagement;
|
||||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||||
import org.eclipse.hawkbit.repository.ReportManagement;
|
import org.eclipse.hawkbit.repository.ReportManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||||
import org.eclipse.hawkbit.repository.SoftwareManagement;
|
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.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -70,7 +72,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
|||||||
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
|
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" })
|
@EnableJpaRepositories(basePackages = { "org.eclipse.hawkbit.repository.jpa" })
|
||||||
@@ -80,6 +82,7 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
|
|||||||
@Configuration
|
@Configuration
|
||||||
@ComponentScan
|
@ComponentScan
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
|
@EnableConfigurationProperties(RepositoryProperties.class)
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
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.ControllerManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
|
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
@@ -92,6 +93,9 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private HawkbitSecurityProperties securityProperties;
|
private HawkbitSecurityProperties securityProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RepositoryProperties repositoryProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TenantConfigurationRepository tenantConfigurationRepository;
|
private TenantConfigurationRepository tenantConfigurationRepository;
|
||||||
|
|
||||||
@@ -249,7 +253,12 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus) {
|
public Action addUpdateActionStatus(@NotNull final ActionStatus actionStatus) {
|
||||||
final JpaAction action = (JpaAction) actionStatus.getAction();
|
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.",
|
LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
|
||||||
actionStatus.getId(), action.getId());
|
actionStatus.getId(), action.getId());
|
||||||
return action;
|
return action;
|
||||||
@@ -257,6 +266,12 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
return handleAddUpdateActionStatus((JpaActionStatus) actionStatus, action);
|
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}.
|
* Sets {@link TargetUpdateStatus} based on given {@link ActionStatus}.
|
||||||
*
|
*
|
||||||
@@ -284,8 +299,7 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
case CANCELED:
|
case CANCELED:
|
||||||
case WARNING:
|
case WARNING:
|
||||||
case RUNNING:
|
case RUNNING:
|
||||||
DeploymentHelper.updateTargetInfo(mergedTarget, TargetUpdateStatus.PENDING, false, targetInfoRepository,
|
handleIntermediateFeedback(mergedAction, mergedTarget);
|
||||||
entityManager);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -298,6 +312,16 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
return actionRepository.save(mergedAction);
|
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) {
|
private void handleErrorOnAction(final JpaAction mergedAction, final JpaTarget mergedTarget) {
|
||||||
mergedAction.setActive(false);
|
mergedAction.setActive(false);
|
||||||
mergedAction.setStatus(Status.ERROR);
|
mergedAction.setStatus(Status.ERROR);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||||
@@ -87,6 +88,14 @@ public class JpaEntityFactory implements EntityFactory {
|
|||||||
return new JpaTarget(controllerId);
|
return new JpaTarget(controllerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Target generateTarget(final String controllerId, final String securityToken) {
|
||||||
|
if (StringUtils.isEmpty(securityToken)) {
|
||||||
|
return new JpaTarget(controllerId);
|
||||||
|
}
|
||||||
|
return new JpaTarget(controllerId, securityToken);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TargetTag generateTargetTag() {
|
public TargetTag generateTargetTag() {
|
||||||
return new JpaTargetTag();
|
return new JpaTargetTag();
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ public class JpaSoftwareManagement implements SoftwareManagement {
|
|||||||
final JpaSoftwareModuleType type = softwareModuleTypeRepository.findOne(sm.getId());
|
final JpaSoftwareModuleType type = softwareModuleTypeRepository.findOne(sm.getId());
|
||||||
|
|
||||||
boolean updated = false;
|
boolean updated = false;
|
||||||
if (sm.getDescription() != null && !sm.getDescription().equals(type.getDescription())) {
|
if (sm.getDescription() == null || !sm.getDescription().equals(type.getDescription())) {
|
||||||
type.setDescription(sm.getDescription());
|
type.setDescription(sm.getDescription());
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -606,24 +606,7 @@ public class JpaTargetManagement implements TargetManagement {
|
|||||||
}
|
}
|
||||||
final List<Target> savedTargets = new ArrayList<>();
|
final List<Target> savedTargets = new ArrayList<>();
|
||||||
for (final Target t : targets) {
|
for (final Target t : targets) {
|
||||||
final Target myTarget = createTarget(t);
|
final Target myTarget = createTarget(t, TargetUpdateStatus.UNKNOWN, null, t.getTargetInfo().getAddress());
|
||||||
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);
|
|
||||||
savedTargets.add(myTarget);
|
savedTargets.add(myTarget);
|
||||||
}
|
}
|
||||||
return savedTargets;
|
return savedTargets;
|
||||||
|
|||||||
@@ -115,9 +115,21 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Persistable<Lon
|
|||||||
* controller ID of the {@link Target}
|
* controller ID of the {@link Target}
|
||||||
*/
|
*/
|
||||||
public JpaTarget(final String controllerId) {
|
public JpaTarget(final String controllerId) {
|
||||||
|
this(controllerId, SecurityTokenGeneratorHolder.getInstance().generateToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param controllerId
|
||||||
|
* controller ID of the {@link Target}
|
||||||
|
* @param securityToken
|
||||||
|
* for target authentication if enabled
|
||||||
|
*/
|
||||||
|
public JpaTarget(final String controllerId, final String securityToken) {
|
||||||
this.controllerId = controllerId;
|
this.controllerId = controllerId;
|
||||||
setName(controllerId);
|
setName(controllerId);
|
||||||
securityToken = SecurityTokenGeneratorHolder.getInstance().generateToken();
|
this.securityToken = securityToken;
|
||||||
targetInfo = new JpaTargetInfo(this);
|
targetInfo = new JpaTargetInfo(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import javax.persistence.OneToOne;
|
|||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.Transient;
|
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.SystemSecurityContextHolder;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantConfigurationManagementHolder;
|
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantConfigurationManagementHolder;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
@@ -168,15 +169,21 @@ public class JpaTargetInfo implements Persistable<Long>, TargetInfo {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param address
|
* @param address
|
||||||
* the ipAddress to set
|
* the target address to set
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException
|
* @throws IllegalArgumentException
|
||||||
* If the given string violates RFC 2396
|
* If the given string violates RFC 2396
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void setAddress(final String address) {
|
public void setAddress(final String address) {
|
||||||
// check if this is a real URI
|
// check if this is a real URI
|
||||||
if (address != null) {
|
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;
|
this.address = address;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import java.util.List;
|
|||||||
import javax.validation.ConstraintViolationException;
|
import javax.validation.ConstraintViolationException;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
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.JpaActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
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.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import ru.yandex.qatools.allure.annotations.Description;
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
@@ -34,6 +36,8 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
|||||||
@Features("Component Tests - Repository")
|
@Features("Component Tests - Repository")
|
||||||
@Stories("Controller Management")
|
@Stories("Controller Management")
|
||||||
public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
||||||
|
@Autowired
|
||||||
|
private RepositoryProperties repositoryProperties;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Controller adds a new action status.")
|
@Description("Controller adds a new action status.")
|
||||||
@@ -94,7 +98,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Controller trys to finish an update process after it has been finished by an error action status.")
|
@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
|
// mock
|
||||||
final Target target = new JpaTarget("Rabbit");
|
final Target target = new JpaTarget("Rabbit");
|
||||||
@@ -120,16 +124,101 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
|
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
|
||||||
.isEqualTo(TargetUpdateStatus.ERROR);
|
.isEqualTo(TargetUpdateStatus.ERROR);
|
||||||
|
|
||||||
|
// try with disabled late feedback
|
||||||
|
repositoryProperties.setRejectActionStatusForClosedAction(true);
|
||||||
final ActionStatus actionStatusMessage3 = new JpaActionStatus(savedAction, Action.Status.FINISHED,
|
final ActionStatus actionStatusMessage3 = new JpaActionStatus(savedAction, Action.Status.FINISHED,
|
||||||
System.currentTimeMillis());
|
System.currentTimeMillis());
|
||||||
actionStatusMessage3.addMessage("finish");
|
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
|
// test
|
||||||
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
|
assertThat(targetManagement.findTargetByControllerID("Rabbit").getTargetInfo().getUpdateStatus())
|
||||||
.isEqualTo(TargetUpdateStatus.ERROR);
|
.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetInfo;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
|
|
||||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.Tag;
|
import org.eclipse.hawkbit.repository.model.Tag;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetIdName;
|
import org.eclipse.hawkbit.repository.model.TargetIdName;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||||
|
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
|
||||||
|
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Ensures that retrieving the target security is only permitted with the necessary permissions.")
|
@Description("Ensures that retrieving the target security is only permitted with the necessary permissions.")
|
||||||
public void getTargetSecurityTokenOnlyWithCorrectPermission() throws Exception {
|
public void getTargetSecurityTokenOnlyWithCorrectPermission() throws Exception {
|
||||||
final Target createdTarget = targetManagement.createTarget(new JpaTarget("targetWithSecurityToken"));
|
final Target createdTarget = targetManagement.createTarget(new JpaTarget("targetWithSecurityToken", "token"));
|
||||||
|
|
||||||
// retrieve security token only with READ_TARGET_SEC_TOKEN permission
|
// retrieve security token only with READ_TARGET_SEC_TOKEN permission
|
||||||
final String securityTokenWithReadPermission = securityRule.runAs(WithSpringAuthorityRule
|
final String securityTokenWithReadPermission = securityRule.runAs(WithSpringAuthorityRule
|
||||||
@@ -80,7 +80,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
return createdTarget.getSecurityToken();
|
return createdTarget.getSecurityToken();
|
||||||
});
|
});
|
||||||
|
|
||||||
assertThat(createdTarget.getSecurityToken()).isNotNull();
|
assertThat(createdTarget.getSecurityToken()).isEqualTo("token");
|
||||||
assertThat(securityTokenWithReadPermission).isNotNull();
|
assertThat(securityTokenWithReadPermission).isNotNull();
|
||||||
assertThat(securityTokenAsSystemCode).isNotNull();
|
assertThat(securityTokenAsSystemCode).isNotNull();
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.data.domain.AuditorAware;
|
import org.springframework.data.domain.AuditorAware;
|
||||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||||
import org.springframework.security.concurrent.DelegatingSecurityContextExecutor;
|
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
|
|
||||||
import com.google.common.eventbus.AsyncEventBus;
|
import com.google.common.eventbus.AsyncEventBus;
|
||||||
@@ -99,7 +99,7 @@ public class TestConfiguration implements AsyncConfigurer {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Executor asyncExecutor() {
|
public Executor asyncExecutor() {
|
||||||
return new DelegatingSecurityContextExecutor(Executors.newSingleThreadExecutor());
|
return new DelegatingSecurityContextExecutorService(Executors.newSingleThreadExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
@@ -9,13 +9,15 @@
|
|||||||
package org.eclipse.hawkbit.rest.exception;
|
package org.eclipse.hawkbit.rest.exception;
|
||||||
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.tomcat.util.http.fileupload.FileUploadBase.FileSizeLimitExceededException;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
import org.eclipse.hawkbit.exception.SpServerError;
|
import org.eclipse.hawkbit.exception.SpServerError;
|
||||||
import org.eclipse.hawkbit.exception.SpServerRtException;
|
import org.eclipse.hawkbit.exception.SpServerRtException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.MultiPartFileUploadException;
|
||||||
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -26,12 +28,10 @@ import org.springframework.web.bind.annotation.ControllerAdvice;
|
|||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.multipart.MultipartException;
|
import org.springframework.web.multipart.MultipartException;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General controller advice for exception handling.
|
* General controller advice for exception handling.
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
public class ResponseExceptionHandler {
|
public class ResponseExceptionHandler {
|
||||||
@@ -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_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_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_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) {
|
private static HttpStatus getStatusOrDefault(final SpServerError error) {
|
||||||
@@ -88,14 +89,11 @@ public class ResponseExceptionHandler {
|
|||||||
@ExceptionHandler(SpServerRtException.class)
|
@ExceptionHandler(SpServerRtException.class)
|
||||||
public ResponseEntity<ExceptionInfo> handleSpServerRtExceptions(final HttpServletRequest request,
|
public ResponseEntity<ExceptionInfo> handleSpServerRtExceptions(final HttpServletRequest request,
|
||||||
final Exception ex) {
|
final Exception ex) {
|
||||||
LOG.debug("Handling exception of request {}", request.getRequestURL());
|
logRequest(request, ex);
|
||||||
final ExceptionInfo response = new ExceptionInfo();
|
final ExceptionInfo response = createExceptionInfo(ex);
|
||||||
final HttpStatus responseStatus;
|
final HttpStatus responseStatus;
|
||||||
response.setMessage(ex.getMessage());
|
|
||||||
response.setExceptionClass(ex.getClass().getName());
|
|
||||||
if (ex instanceof SpServerRtException) {
|
if (ex instanceof SpServerRtException) {
|
||||||
responseStatus = getStatusOrDefault(((SpServerRtException) ex).getError());
|
responseStatus = getStatusOrDefault(((SpServerRtException) ex).getError());
|
||||||
response.setErrorCode(((SpServerRtException) ex).getError().getKey());
|
|
||||||
} else {
|
} else {
|
||||||
responseStatus = DEFAULT_RESPONSE_STATUS;
|
responseStatus = DEFAULT_RESPONSE_STATUS;
|
||||||
}
|
}
|
||||||
@@ -117,11 +115,8 @@ public class ResponseExceptionHandler {
|
|||||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||||
public ResponseEntity<ExceptionInfo> handleHttpMessageNotReadableException(final HttpServletRequest request,
|
public ResponseEntity<ExceptionInfo> handleHttpMessageNotReadableException(final HttpServletRequest request,
|
||||||
final Exception ex) {
|
final Exception ex) {
|
||||||
LOG.debug("Handling exception {} of request {}", ex.getClass().getName(), request.getRequestURL());
|
logRequest(request, ex);
|
||||||
final ExceptionInfo response = new ExceptionInfo();
|
final ExceptionInfo response = createExceptionInfo(new MessageNotReadableException());
|
||||||
response.setErrorCode(SpServerError.SP_REST_BODY_NOT_READABLE.getKey());
|
|
||||||
response.setMessage(SpServerError.SP_REST_BODY_NOT_READABLE.getMessage());
|
|
||||||
response.setExceptionClass(MessageNotReadableException.class.getName());
|
|
||||||
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,35 +133,30 @@ public class ResponseExceptionHandler {
|
|||||||
* as entity.
|
* as entity.
|
||||||
*/
|
*/
|
||||||
@ExceptionHandler(MultipartException.class)
|
@ExceptionHandler(MultipartException.class)
|
||||||
public ResponseEntity<ExceptionInfo> handleFileLimitExceededException(final HttpServletRequest request,
|
public ResponseEntity<ExceptionInfo> handleMultipartException(final HttpServletRequest request,
|
||||||
final Exception ex) {
|
final Exception ex) {
|
||||||
LOG.debug("Handling exception {} of request {}", ex.getClass().getName(), request.getRequestURL());
|
|
||||||
|
|
||||||
final ExceptionInfo response = new ExceptionInfo();
|
logRequest(request, ex);
|
||||||
|
|
||||||
if (searchForCause(ex, FileSizeLimitExceededException.class)) {
|
|
||||||
response.setErrorCode(SpServerError.SP_ARTIFACT_UPLOAD_FILE_LIMIT_EXCEEDED.getKey());
|
|
||||||
response.setMessage(SpServerError.SP_ARTIFACT_UPLOAD_FILE_LIMIT_EXCEEDED.getMessage());
|
|
||||||
response.setExceptionClass(FileSizeLimitExceededException.class.getName());
|
|
||||||
} else {
|
|
||||||
response.setErrorCode(SpServerError.SP_ARTIFACT_UPLOAD_FAILED.getKey());
|
|
||||||
response.setMessage(SpServerError.SP_ARTIFACT_UPLOAD_FAILED.getMessage());
|
|
||||||
response.setExceptionClass(MultipartException.class.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
final List<Throwable> throwables = ExceptionUtils.getThrowableList(ex);
|
||||||
|
final Throwable responseCause = Iterables.getLast(throwables);
|
||||||
|
final ExceptionInfo response = createExceptionInfo(new MultiPartFileUploadException(responseCause));
|
||||||
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
|
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean searchForCause(final Throwable t, final Class<?> lookFor) {
|
private void logRequest(final HttpServletRequest request, final Exception ex) {
|
||||||
if (t != null && t.getCause() != null) {
|
LOG.debug("Handling exception {} of request {}", ex.getClass().getName(), request.getRequestURL());
|
||||||
if (t.getCause().getClass().isAssignableFrom(lookFor)) {
|
}
|
||||||
return true;
|
|
||||||
} else {
|
private ExceptionInfo createExceptionInfo(final Exception ex) {
|
||||||
return searchForCause(t.getCause(), lookFor);
|
final ExceptionInfo response = new ExceptionInfo();
|
||||||
}
|
response.setMessage(ex.getMessage());
|
||||||
|
response.setExceptionClass(ex.getClass().getName());
|
||||||
|
if (ex instanceof SpServerRtException) {
|
||||||
|
response.setErrorCode(((SpServerRtException) ex).getError().getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ public abstract class JsonBuilder {
|
|||||||
try {
|
try {
|
||||||
builder.append(new JSONObject().put("name", module.getName())
|
builder.append(new JSONObject().put("name", module.getName())
|
||||||
.put("description", module.getDescription()).put("type", module.getType().getKey())
|
.put("description", module.getDescription()).put("type", module.getType().getKey())
|
||||||
.put("id", Long.MAX_VALUE).put("vendor", module.getVendor())
|
.put("id", Long.MAX_VALUE).put("vendor", module.getVendor()).put("version", module.getVersion())
|
||||||
.put("version", module.getVersion()).put("createdAt", "0").put("updatedAt", "0")
|
.put("createdAt", "0").put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh")
|
||||||
.put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh").toString());
|
.put("updatedBy", "fghdfkjghdfkjh").toString());
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -185,15 +185,13 @@ public abstract class JsonBuilder {
|
|||||||
final List<String> messages = new ArrayList<String>();
|
final List<String> messages = new ArrayList<String>();
|
||||||
messages.add(message);
|
messages.add(message);
|
||||||
|
|
||||||
return new JSONObject()
|
return new JSONObject().put("id", id).put("time", "20140511T121314")
|
||||||
.put("id", id)
|
|
||||||
.put("time", "20140511T121314")
|
|
||||||
.put("status",
|
.put("status",
|
||||||
new JSONObject()
|
new JSONObject().put("execution", execution)
|
||||||
.put("execution", execution)
|
|
||||||
.put("result",
|
.put("result",
|
||||||
new JSONObject().put("finished", finished).put("progress",
|
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();
|
.toString();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -369,21 +367,22 @@ public abstract class JsonBuilder {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static String targets(final List<Target> targets, final boolean withToken) {
|
||||||
* @param targets
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static String targets(final List<Target> targets) {
|
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
builder.append("[");
|
builder.append("[");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (final Target target : targets) {
|
for (final Target target : targets) {
|
||||||
try {
|
try {
|
||||||
|
final String address = target.getTargetInfo().getAddress() != null
|
||||||
|
? target.getTargetInfo().getAddress().toString() : null;
|
||||||
|
|
||||||
|
final String token = withToken ? target.getSecurityToken() : null;
|
||||||
|
|
||||||
builder.append(new JSONObject().put("controllerId", target.getControllerId())
|
builder.append(new JSONObject().put("controllerId", target.getControllerId())
|
||||||
.put("description", target.getDescription()).put("name", target.getName())
|
.put("description", target.getDescription()).put("name", target.getName()).put("createdAt", "0")
|
||||||
.put("createdAt", "0").put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh")
|
.put("updatedAt", "0").put("createdBy", "fghdfkjghdfkjh").put("updatedBy", "fghdfkjghdfkjh")
|
||||||
.put("updatedBy", "fghdfkjghdfkjh").toString());
|
.put("address", address).put("securityToken", token).toString());
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -441,9 +440,7 @@ public abstract class JsonBuilder {
|
|||||||
throws JSONException {
|
throws JSONException {
|
||||||
final List<String> messages = new ArrayList<String>();
|
final List<String> messages = new ArrayList<String>();
|
||||||
messages.add(message);
|
messages.add(message);
|
||||||
return new JSONObject()
|
return new JSONObject().put("id", id).put("time", "20140511T121314")
|
||||||
.put("id", id)
|
|
||||||
.put("time", "20140511T121314")
|
|
||||||
.put("status",
|
.put("status",
|
||||||
new JSONObject().put("execution", execution)
|
new JSONObject().put("execution", execution)
|
||||||
.put("result", new JSONObject().put("finished", "success")).put("details", messages))
|
.put("result", new JSONObject().put("finished", "success")).put("details", messages))
|
||||||
@@ -453,13 +450,12 @@ public abstract class JsonBuilder {
|
|||||||
|
|
||||||
public static String configData(final String id, final Map<String, String> attributes, final String execution)
|
public static String configData(final String id, final Map<String, String> attributes, final String execution)
|
||||||
throws JSONException {
|
throws JSONException {
|
||||||
return new JSONObject()
|
return new JSONObject().put("id", id).put("time", "20140511T121314")
|
||||||
.put("id", id)
|
|
||||||
.put("time", "20140511T121314")
|
|
||||||
.put("status",
|
.put("status",
|
||||||
new JSONObject().put("execution", execution)
|
new JSONObject().put("execution", execution)
|
||||||
.put("result", new JSONObject().put("finished", "success"))
|
.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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ public final class PermissionUtils {
|
|||||||
|
|
||||||
for (final String role : roles) {
|
for (final String role : roles) {
|
||||||
authorities.add(new SimpleGrantedAuthority(role));
|
authorities.add(new SimpleGrantedAuthority(role));
|
||||||
|
// add spring security ROLE authority which is indicated by the
|
||||||
|
// `ROLE_` prefix
|
||||||
|
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
|
||||||
}
|
}
|
||||||
|
|
||||||
return authorities;
|
return authorities;
|
||||||
|
|||||||
@@ -224,8 +224,10 @@ public final class SpPermission {
|
|||||||
/*
|
/*
|
||||||
* Spring security eval expressions.
|
* Spring security eval expressions.
|
||||||
*/
|
*/
|
||||||
private static final String HAS_AUTH_PREFIX = "hasAuthority('";
|
private static final String BRACKET_OPEN = "(";
|
||||||
private static final String HAS_AUTH_SUFFIX = "')";
|
private static final String BRACKET_CLOSE = ")";
|
||||||
|
private static final String HAS_AUTH_PREFIX = "hasAuthority" + BRACKET_OPEN + "'";
|
||||||
|
private static final String HAS_AUTH_SUFFIX = "'" + BRACKET_CLOSE;
|
||||||
private static final String HAS_AUTH_AND = " and ";
|
private static final String HAS_AUTH_AND = " and ";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -257,99 +259,6 @@ public final class SpPermission {
|
|||||||
*/
|
*/
|
||||||
public static final String HAS_AUTH_OR = " or ";
|
public static final String HAS_AUTH_OR = " or ";
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#UPDATE_TARGET}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_UPDATE_TARGET = HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#SYSTEM_ADMIN}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_SYSTEM_ADMIN = HAS_AUTH_PREFIX + SYSTEM_ADMIN + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#READ_TARGET}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_READ_TARGET = HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#CREATE_TARGET}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_CREATE_TARGET = HAS_AUTH_PREFIX + CREATE_TARGET + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#DELETE_TARGET}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_DELETE_TARGET = HAS_AUTH_PREFIX + DELETE_TARGET + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#READ_REPOSITORY} and
|
|
||||||
* {@link SpPermission#UPDATE_TARGET}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET = HAS_AUTH_PREFIX + READ_REPOSITORY
|
|
||||||
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#CREATE_REPOSITORY}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_CREATE_REPOSITORY = HAS_AUTH_PREFIX + CREATE_REPOSITORY + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#DELETE_REPOSITORY}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_DELETE_REPOSITORY = HAS_AUTH_PREFIX + DELETE_REPOSITORY + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#READ_REPOSITORY}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_READ_REPOSITORY = HAS_AUTH_PREFIX + READ_REPOSITORY + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#UPDATE_REPOSITORY}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_UPDATE_REPOSITORY = HAS_AUTH_PREFIX + UPDATE_REPOSITORY + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#READ_REPOSITORY} and
|
|
||||||
* {@link SpPermission#READ_TARGET}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET = HAS_AUTH_PREFIX + READ_REPOSITORY
|
|
||||||
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
|
||||||
* context contains {@link SpPermission#DOWNLOAD_REPOSITORY_ARTIFACT}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_AUTH_DOWNLOAD_ARTIFACT = HAS_AUTH_PREFIX + DOWNLOAD_REPOSITORY_ARTIFACT
|
|
||||||
+ HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAnyRole expression to check if the spring
|
|
||||||
* context contains the anoynmous role or the controller specific role
|
|
||||||
* {@link SpPermission#CONTROLLER_ROLE}.
|
|
||||||
*/
|
|
||||||
public static final String IS_CONTROLLER = "hasAnyRole('" + CONTROLLER_ROLE_ANONYMOUS + "', '" + CONTROLLER_ROLE
|
|
||||||
+ "')";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring security eval hasAuthority expression to check if the spring
|
|
||||||
* context contains the role to allow controllers to download specific
|
|
||||||
* role {@link SpPermission#CONTROLLER_DOWNLOAD_ROLE}.
|
|
||||||
*/
|
|
||||||
public static final String HAS_CONTROLLER_DOWNLOAD = HAS_AUTH_PREFIX + CONTROLLER_DOWNLOAD_ROLE
|
|
||||||
+ HAS_AUTH_SUFFIX;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring security eval hasAnyRole expression to check if the spring
|
* Spring security eval hasAnyRole expression to check if the spring
|
||||||
* context contains system code role
|
* context contains system code role
|
||||||
@@ -359,42 +268,168 @@ public final class SpPermission {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
* context contains {@link SpPermission#CREATE_REPOSITORY} and
|
* context contains {@link SpPermission#UPDATE_TARGET} or
|
||||||
* {@link SpPermission#CREATE_TARGET}.
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
*/
|
*/
|
||||||
public static final String HAS_AUTH_CREATE_REPOSITORY_AND_CREATE_TARGET = HAS_AUTH_PREFIX + CREATE_REPOSITORY
|
public static final String HAS_AUTH_UPDATE_TARGET = HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX
|
||||||
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + CREATE_TARGET + HAS_AUTH_SUFFIX;
|
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
* context contains {@link SpPermission#ROLLOUT_MANAGEMENT}
|
* context contains {@link SpPermission#SYSTEM_ADMIN} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_SYSTEM_ADMIN = HAS_AUTH_PREFIX + SYSTEM_ADMIN + HAS_AUTH_SUFFIX
|
||||||
|
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#READ_TARGET} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_READ_TARGET = HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX + HAS_AUTH_OR
|
||||||
|
+ IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#CREATE_TARGET} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_CREATE_TARGET = HAS_AUTH_PREFIX + CREATE_TARGET + HAS_AUTH_SUFFIX
|
||||||
|
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#DELETE_TARGET} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_DELETE_TARGET = HAS_AUTH_PREFIX + DELETE_TARGET + HAS_AUTH_SUFFIX
|
||||||
|
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#READ_REPOSITORY} and
|
||||||
|
* {@link SpPermission#UPDATE_TARGET} or {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET = BRACKET_OPEN + HAS_AUTH_PREFIX
|
||||||
|
+ READ_REPOSITORY + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX
|
||||||
|
+ BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#CREATE_REPOSITORY} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_CREATE_REPOSITORY = HAS_AUTH_PREFIX + CREATE_REPOSITORY + HAS_AUTH_SUFFIX
|
||||||
|
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#DELETE_REPOSITORY} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_DELETE_REPOSITORY = HAS_AUTH_PREFIX + DELETE_REPOSITORY + HAS_AUTH_SUFFIX
|
||||||
|
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#READ_REPOSITORY} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_READ_REPOSITORY = HAS_AUTH_PREFIX + READ_REPOSITORY + HAS_AUTH_SUFFIX
|
||||||
|
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#UPDATE_REPOSITORY} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_UPDATE_REPOSITORY = HAS_AUTH_PREFIX + UPDATE_REPOSITORY + HAS_AUTH_SUFFIX
|
||||||
|
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#READ_REPOSITORY} and
|
||||||
|
* {@link SpPermission#READ_TARGET} or {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_READ_REPOSITORY_AND_READ_TARGET = BRACKET_OPEN + HAS_AUTH_PREFIX
|
||||||
|
+ READ_REPOSITORY + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX
|
||||||
|
+ BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#DOWNLOAD_REPOSITORY_ARTIFACT} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_DOWNLOAD_ARTIFACT = HAS_AUTH_PREFIX + DOWNLOAD_REPOSITORY_ARTIFACT
|
||||||
|
+ HAS_AUTH_SUFFIX + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAnyRole expression to check if the spring
|
||||||
|
* context contains the anoynmous role or the controller specific role
|
||||||
|
* {@link SpringEvalExpressions#CONTROLLER_ROLE}.
|
||||||
|
*/
|
||||||
|
public static final String IS_CONTROLLER = "hasAnyRole('" + CONTROLLER_ROLE_ANONYMOUS + "', '" + CONTROLLER_ROLE
|
||||||
|
+ "')";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if the spring
|
||||||
|
* context contains the role to allow controllers to download specific
|
||||||
|
* role {@link SpringEvalExpressions#CONTROLLER_DOWNLOAD_ROLE}
|
||||||
|
*/
|
||||||
|
public static final String HAS_CONTROLLER_DOWNLOAD = HAS_AUTH_PREFIX + CONTROLLER_DOWNLOAD_ROLE
|
||||||
|
+ HAS_AUTH_SUFFIX;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#CREATE_REPOSITORY} and
|
||||||
|
* {@link SpPermission#CREATE_TARGET} or {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_CREATE_REPOSITORY_AND_CREATE_TARGET = BRACKET_OPEN + HAS_AUTH_PREFIX
|
||||||
|
+ CREATE_REPOSITORY + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + CREATE_TARGET + HAS_AUTH_SUFFIX
|
||||||
|
+ BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#ROLLOUT_MANAGEMENT} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
*/
|
*/
|
||||||
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_READ = HAS_AUTH_PREFIX + ROLLOUT_MANAGEMENT
|
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_READ = HAS_AUTH_PREFIX + ROLLOUT_MANAGEMENT
|
||||||
+ HAS_AUTH_SUFFIX;
|
+ HAS_AUTH_SUFFIX + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
* context contains {@link SpPermission#ROLLOUT_MANAGEMENT} and
|
* context contains {@link SpPermission#ROLLOUT_MANAGEMENT} and
|
||||||
* {@link SpPermission#READ_TARGET}
|
* {@link SpPermission#READ_TARGET} or {@link #IS_SYSTEM_CODE}.
|
||||||
*/
|
*/
|
||||||
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ = HAS_AUTH_PREFIX
|
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ = BRACKET_OPEN + HAS_AUTH_PREFIX
|
||||||
+ ROLLOUT_MANAGEMENT + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + READ_TARGET
|
+ ROLLOUT_MANAGEMENT + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + READ_TARGET + HAS_AUTH_SUFFIX
|
||||||
+ HAS_AUTH_SUFFIX;;
|
+ BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
* context contains {@link SpPermission#ROLLOUT_MANAGEMENT} and
|
* context contains {@link SpPermission#ROLLOUT_MANAGEMENT} and
|
||||||
* {@link SpPermission#UPDATE_TARGET}.
|
* {@link SpPermission#UPDATE_TARGET} or {@link #IS_SYSTEM_CODE}.
|
||||||
*/
|
*/
|
||||||
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE = HAS_AUTH_PREFIX + ROLLOUT_MANAGEMENT
|
public static final String HAS_AUTH_ROLLOUT_MANAGEMENT_WRITE = BRACKET_OPEN + HAS_AUTH_PREFIX
|
||||||
+ HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + UPDATE_TARGET + HAS_AUTH_SUFFIX;
|
+ ROLLOUT_MANAGEMENT + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + UPDATE_TARGET
|
||||||
|
+ HAS_AUTH_SUFFIX + BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spring security eval hasAuthority expression to check if spring
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
* context contains {@link SpPermission#TENANT_CONFIGURATION}
|
* context contains {@link SpPermission#TENANT_CONFIGURATION} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
*/
|
*/
|
||||||
public static final String HAS_AUTH_TENANT_CONFIGURATION = HAS_AUTH_PREFIX + TENANT_CONFIGURATION
|
public static final String HAS_AUTH_TENANT_CONFIGURATION = HAS_AUTH_PREFIX + TENANT_CONFIGURATION
|
||||||
+ HAS_AUTH_SUFFIX;
|
+ HAS_AUTH_SUFFIX + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring security eval hasAuthority expression to check if spring
|
||||||
|
* context contains {@link SpPermission#SYSTEM_MONITOR} or
|
||||||
|
* {@link #IS_SYSTEM_CODE}.
|
||||||
|
*/
|
||||||
|
public static final String HAS_AUTH_SYSTEM_MONITOR = HAS_AUTH_PREFIX + SYSTEM_MONITOR + HAS_AUTH_SUFFIX
|
||||||
|
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||||
|
|
||||||
private SpringEvalExpressions() {
|
private SpringEvalExpressions() {
|
||||||
// utility class
|
// utility class
|
||||||
|
|||||||
@@ -8,13 +8,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.security;
|
package org.eclipse.hawkbit.security;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
import org.eclipse.hawkbit.im.authentication.TenantAwareAuthenticationDetails;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link TenantAware} implemenation which retrieves the ID of the tenant from
|
* A {@link TenantAware} implemenation which retrieves the ID of the tenant from
|
||||||
@@ -22,15 +24,9 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
|||||||
* {@link Authentication#getDetails()} which holds the
|
* {@link Authentication#getDetails()} which holds the
|
||||||
* {@link TenantAwareAuthenticationDetails} object.
|
* {@link TenantAwareAuthenticationDetails} object.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class SecurityContextTenantAware implements TenantAware {
|
public class SecurityContextTenantAware implements TenantAware {
|
||||||
|
|
||||||
private static final ThreadLocal<String> TENANT_THREAD_LOCAL = new ThreadLocal<>();
|
|
||||||
private static final ThreadLocal<AtomicInteger> RUN_AS_DEPTH = new ThreadLocal<>();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (non-Javadoc)
|
* (non-Javadoc)
|
||||||
*
|
*
|
||||||
@@ -38,9 +34,6 @@ public class SecurityContextTenantAware implements TenantAware {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getCurrentTenant() {
|
public String getCurrentTenant() {
|
||||||
if (TENANT_THREAD_LOCAL.get() != null) {
|
|
||||||
return TENANT_THREAD_LOCAL.get();
|
|
||||||
}
|
|
||||||
final SecurityContext context = SecurityContextHolder.getContext();
|
final SecurityContext context = SecurityContextHolder.getContext();
|
||||||
if (context.getAuthentication() != null) {
|
if (context.getAuthentication() != null) {
|
||||||
final Object authDetails = context.getAuthentication().getDetails();
|
final Object authDetails = context.getAuthentication().getDetails();
|
||||||
@@ -51,29 +44,88 @@ public class SecurityContextTenantAware implements TenantAware {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see hawkbit.server.tenancy.TenantAware#runAsTenant(java.lang.String,
|
|
||||||
* java.util.concurrent.Callable)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T runAsTenant(final String tenant, final TenantRunner<T> callable) {
|
public <T> T runAsTenant(final String tenant, final TenantRunner<T> callable) {
|
||||||
AtomicInteger runAsDepth = RUN_AS_DEPTH.get();
|
final SecurityContext originalContext = SecurityContextHolder.getContext();
|
||||||
if (runAsDepth == null) {
|
|
||||||
runAsDepth = new AtomicInteger(1);
|
|
||||||
RUN_AS_DEPTH.set(runAsDepth);
|
|
||||||
} else {
|
|
||||||
runAsDepth.incrementAndGet();
|
|
||||||
}
|
|
||||||
TENANT_THREAD_LOCAL.set(tenant);
|
|
||||||
try {
|
try {
|
||||||
|
SecurityContextHolder.setContext(buildSecurityContext(tenant));
|
||||||
return callable.run();
|
return callable.run();
|
||||||
} finally {
|
} finally {
|
||||||
if (runAsDepth.decrementAndGet() <= 0) {
|
SecurityContextHolder.setContext(originalContext);
|
||||||
RUN_AS_DEPTH.remove();
|
}
|
||||||
TENANT_THREAD_LOCAL.remove();
|
}
|
||||||
}
|
|
||||||
|
private SecurityContext buildSecurityContext(final String tenant) {
|
||||||
|
final SecurityContextImpl securityContext = new SecurityContextImpl();
|
||||||
|
securityContext.setAuthentication(
|
||||||
|
new AuthenticationDelegate(SecurityContextHolder.getContext().getAuthentication(), tenant));
|
||||||
|
return securityContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link Authentication} implementation to delegate to an existing
|
||||||
|
* {@link Authentication} object except setting the details specifically for
|
||||||
|
* a specific tenant.
|
||||||
|
*/
|
||||||
|
private class AuthenticationDelegate implements Authentication {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final Authentication delegate;
|
||||||
|
private final TenantAwareAuthenticationDetails tenantAwareAuthenticationDetails;
|
||||||
|
|
||||||
|
private AuthenticationDelegate(final Authentication delegate, final String tenant) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
tenantAwareAuthenticationDetails = new TenantAwareAuthenticationDetails(tenant, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object another) {
|
||||||
|
return delegate.equals(another);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return delegate.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return delegate.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return delegate.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||||
|
return delegate.getAuthorities();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getCredentials() {
|
||||||
|
return delegate.getCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getDetails() {
|
||||||
|
return tenantAwareAuthenticationDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getPrincipal() {
|
||||||
|
return delegate.getPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAuthenticated() {
|
||||||
|
return delegate.isAuthenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
|
||||||
|
delegate.setAuthenticated(isAuthenticated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user