diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java index e954cec96..57d4762b0 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/AmqpConfiguration.java @@ -12,6 +12,8 @@ import java.time.Duration; import java.util.HashMap; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.FanoutExchange; @@ -22,42 +24,53 @@ import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; -import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.retry.backoff.ExponentialBackOffPolicy; +import org.springframework.retry.support.RetryTemplate; /** * The spring AMQP configuration to use a AMQP for communication with SP update * server. - * - * - * */ @Configuration @EnableConfigurationProperties(AmqpProperties.class) public class AmqpConfiguration { + private static final Logger LOGGER = LoggerFactory.getLogger(AmqpConfiguration.class); + @Autowired protected AmqpProperties amqpProperties; @Autowired private ConnectionFactory connectionFactory; - @Autowired - private RabbitTemplate rabbitTemplate; - /** - * Create jackson message converter bean. - * - * @return the jackson message converter + * @return {@link RabbitTemplate} with automatic retry, published confirms + * and {@link Jackson2JsonMessageConverter}. */ @Bean - public MessageConverter jsonMessageConverter() { - final Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter(); - rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter); - return jackson2JsonMessageConverter; + public RabbitTemplate rabbitTemplate() { + final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); + rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter()); + + final RetryTemplate retryTemplate = new RetryTemplate(); + retryTemplate.setBackOffPolicy(new ExponentialBackOffPolicy()); + rabbitTemplate.setRetryTemplate(retryTemplate); + + rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { + if (ack) { + LOGGER.debug("Message with correlation ID {} confirmed by broker.", correlationData.getId()); + } else { + LOGGER.error("Broker is unable to handle message with correlation ID {} : {}", correlationData.getId(), + cause); + } + + }); + + return rabbitTemplate; } /** @@ -138,8 +151,8 @@ public class AmqpConfiguration { final SimpleRabbitListenerContainerFactory containerFactory = new SimpleRabbitListenerContainerFactory(); containerFactory.setDefaultRequeueRejected(false); containerFactory.setConnectionFactory(connectionFactory); - containerFactory.setConcurrentConsumers(20); - containerFactory.setMaxConcurrentConsumers(20); + containerFactory.setConcurrentConsumers(3); + containerFactory.setMaxConcurrentConsumers(10); containerFactory.setPrefetchCount(20); return containerFactory; } diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SenderService.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SenderService.java index 6ed6ff0cb..6f16ac736 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SenderService.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SenderService.java @@ -8,9 +8,14 @@ */ package org.eclipse.hawkbit.simulator.amqp; +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.support.CorrelationData; import org.springframework.amqp.support.converter.AbstractJavaTypeMapper; import org.springframework.beans.factory.annotation.Autowired; @@ -22,6 +27,8 @@ import org.springframework.beans.factory.annotation.Autowired; */ public abstract class SenderService extends MessageService { + private static final Logger LOGGER = LoggerFactory.getLogger(SenderService.class); + /** * Constructor for sender service. * @@ -40,18 +47,25 @@ public abstract class SenderService extends MessageService { /** * Send a message if the message is not null. * - * @param adress + * @param address * the exchange name * @param message * the amqp message which will be send if its not null */ - public void sendMessage(final String adress, final Message message) { + public void sendMessage(final String address, final Message message) { if (message == null) { return; } message.getMessageProperties().getHeaders().remove(AbstractJavaTypeMapper.DEFAULT_CLASSID_FIELD_NAME); - rabbitTemplate.setExchange(adress); - rabbitTemplate.send(message); + final String correlationId = UUID.randomUUID().toString(); + + 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)); } /** diff --git a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java index e06b2baf3..4f2981be1 100644 --- a/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java +++ b/examples/hawkbit-device-simulator/src/main/java/org/eclipse/hawkbit/simulator/amqp/SpReceiverService.java @@ -35,14 +35,21 @@ import com.google.common.collect.Lists; public class SpReceiverService extends ReceiverService { private static final Logger LOGGER = LoggerFactory.getLogger(ReceiverService.class); - public static final String SOFTWARE_MODULE_FIRMWARE = "firmware"; - private final SpSenderService spSenderService; private final DeviceSimulatorUpdater deviceUpdater; /** * Constructor. + * + * @param rabbitTemplate + * for sending messages + * @param amqpProperties + * for amqp configuration + * @param spSenderService + * to send messages + * @param deviceUpdater + * simulator service for updates */ @Autowired public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties, diff --git a/examples/hawkbit-device-simulator/src/main/resources/logback.xml b/examples/hawkbit-device-simulator/src/main/resources/logback-spring.xml similarity index 94% rename from examples/hawkbit-device-simulator/src/main/resources/logback.xml rename to examples/hawkbit-device-simulator/src/main/resources/logback-spring.xml index 469c7bde3..f25a61cd1 100644 --- a/examples/hawkbit-device-simulator/src/main/resources/logback.xml +++ b/examples/hawkbit-device-simulator/src/main/resources/logback-spring.xml @@ -19,7 +19,9 @@ - + + + diff --git a/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/Application.java b/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/Application.java index 036c19ed1..e89f3211b 100644 --- a/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/Application.java +++ b/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/Application.java @@ -48,7 +48,7 @@ public class Application implements CommandLineRunner { private ClientConfigurationProperties configuration; @Autowired - private ConfigurableScenario configurableScenario; + private ConfigurableScenario configuredScenario; @Autowired private CreateStartedRolloutExample gettingStartedRolloutScenario; @@ -63,9 +63,8 @@ public class Application implements CommandLineRunner { // run the create and start rollout example gettingStartedRolloutScenario.run(); } else { - // run the getting started scenario which creates a setup of - // distribution set and software modules to be used - configurableScenario.run(); + // run the configured scenario from properties + configuredScenario.run(); } } @@ -107,4 +106,4 @@ public class Application implements CommandLineRunner { } return false; } -} \ No newline at end of file +} diff --git a/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java b/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java index cc5fe27e9..8a5c61575 100644 --- a/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java +++ b/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/ClientConfigurationProperties.java @@ -39,8 +39,13 @@ public class ClientConfigurationProperties { private final List scenarios = new ArrayList<>(); + /** + * Simulation {@link Scenario}. + * + */ public static class Scenario { private String tenant = "DEFAULT"; + private boolean cleanRepository; private int targets = 100; private int distributionSets = 10; private int appModulesPerDistributionSet = 2; @@ -50,6 +55,8 @@ public class ClientConfigurationProperties { 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 @@ -57,6 +64,30 @@ public class ClientConfigurationProperties { */ 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; } diff --git a/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/ConfigurableScenario.java b/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/ConfigurableScenario.java index 60144a88e..84b3da6ee 100644 --- a/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/ConfigurableScenario.java +++ b/examples/hawkbit-example-mgmt-simulator/src/main/java/org/eclipse/hawkbit/mgmt/client/scenarios/ConfigurableScenario.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. +x * 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 @@ -10,20 +10,25 @@ 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.MgmtSystemManagementClientResource; 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.Autowired; @@ -53,7 +58,7 @@ public class ConfigurableScenario { private MgmtTargetClientResource targetResource; @Autowired - private MgmtSystemManagementClientResource systemManagementResource; + private MgmtRolloutClientResource rolloutResource; @Autowired private ClientConfigurationProperties clientConfigurationProperties; @@ -65,16 +70,93 @@ public class ConfigurableScenario { LOGGER.info("Running Configurable Scenario..."); - clientConfigurationProperties.getScenarios().parallelStream().forEach(this::createScenario); + clientConfigurationProperties.getScenarios().forEach(this::createScenario); } private void createScenario(final Scenario scenario) { - systemManagementResource.deleteTenant(scenario.getTenant()); + 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 modules; + do { + modules = softwareModuleResource.getSoftwareModules(0, 100, null, null).getBody(); + modules.getContent().forEach(module -> softwareModuleResource.deleteSoftwareModule(module.getModuleId())); + } while (modules.getTotal() > 100); + } + + private void deleteDistributionSets() { + PagedList distributionSets; + do { + distributionSets = distributionSetResource.getDistributionSets(0, 100, null, null).getBody(); + distributionSets.getContent().forEach(set -> distributionSetResource.deleteDistributionSet(set.getDsId())); + } while (distributionSets.getTotal() > 100); + } + + private void deleteTargets() { + PagedList targets; + do { + targets = targetResource.getTargets(0, 100, null, null).getBody(); + targets.getContent().forEach(target -> targetResource.deleteTarget(target.getControllerId())); + } while (targets.getTotal() > 100); + } + + 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 @@ -87,6 +169,8 @@ public class ConfigurableScenario { modules.forEach(module -> assign.id(module.getModuleId())); distributionSetResource.assignSoftwareModules(dsSet.getDsId(), assign.build()); }); + + LOGGER.info("Creating {} distribution sets -> Done", scenario.getDistributionSets()); } private List addModules(final Scenario scenario, final MgmtDistributionSet dsSet, @@ -110,8 +194,10 @@ public class ConfigurableScenario { return modules; } - private byte[] generateArtifact(final Scenario scenario) { - // create random object + 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 @@ -124,14 +210,18 @@ public class ConfigurableScenario { } private void createTargets(final Scenario scenario) { + LOGGER.info("Creating {} targets", scenario.getTargets()); + for (int i = 0; i < scenario.getTargets() / 100; i++) { targetResource.createTargets(new TargetBuilder().controllerId(scenario.getTargetName()) .address(scenario.getTargetAddress()).buildAsList(i * 100, - (i + 1) * 100 > scenario.getTargets() ? scenario.getTargets() - i * 100 : 100)); + (i + 1) * 100 > scenario.getTargets() ? (scenario.getTargets() - (i * 100)) : 100)); } + + LOGGER.info("Creating {} targets -> Done", scenario.getTargets()); } - private int parseSize(final String s) { + 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; diff --git a/examples/hawkbit-example-mgmt-simulator/src/main/resources/application.properties b/examples/hawkbit-example-mgmt-simulator/src/main/resources/application.properties index f538bb1cb..e19b83e19 100644 --- a/examples/hawkbit-example-mgmt-simulator/src/main/resources/application.properties +++ b/examples/hawkbit-example-mgmt-simulator/src/main/resources/application.properties @@ -13,12 +13,8 @@ hawkbit.password=admin spring.main.show-banner=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].targets=10000 -hawkbit.scenarios.[0].distribution-sets=100 -hawkbit.scenarios.[0].artifactsPerSM=0 \ No newline at end of file +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 \ No newline at end of file diff --git a/examples/hawkbit-example-mgmt-simulator/src/main/resources/logback.xml b/examples/hawkbit-example-mgmt-simulator/src/main/resources/logback-spring.xml similarity index 100% rename from examples/hawkbit-example-mgmt-simulator/src/main/resources/logback.xml rename to examples/hawkbit-example-mgmt-simulator/src/main/resources/logback-spring.xml diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java index 541ae414c..3fbc6aee2 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java @@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.TaskExecutor; import org.springframework.retry.backoff.ExponentialBackOffPolicy; import org.springframework.retry.support.RetryTemplate;; @@ -58,6 +59,9 @@ public class AmqpConfiguration { @Autowired private ConnectionFactory rabbitConnectionFactory; + @Autowired + private TaskExecutor taskExecutor; + @Configuration protected static class RabbitConnectionFactoryCreator { @@ -240,6 +244,9 @@ public class AmqpConfiguration { containerFactory.setDefaultRequeueRejected(false); containerFactory.setConnectionFactory(rabbitConnectionFactory); containerFactory.setMissingQueuesFatal(amqpProperties.isMissingQueuesFatal()); + containerFactory.setTaskExecutor(taskExecutor); + containerFactory.setConcurrentConsumers(3); + containerFactory.setMaxConcurrentConsumers(amqpProperties.getMaxConcurrentConsumers()); return containerFactory; } diff --git a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java index f9b4cceb6..a20c59792 100644 --- a/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java +++ b/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpProperties.java @@ -38,13 +38,26 @@ public class AmqpProperties { /** * Missing queue fatal. */ - private boolean missingQueuesFatal = false; + private boolean missingQueuesFatal; /** * Requested heartbeat interval from broker in {@link TimeUnit#SECONDS}. */ private int requestedHeartBeat = (int) TimeUnit.SECONDS.toSeconds(60); + /** + * Sets an upper limit to the number of consumers. + */ + private int maxConcurrentConsumers = 10; + + public int getMaxConcurrentConsumers() { + return maxConcurrentConsumers; + } + + public void setMaxConcurrentConsumers(final int maxConcurrentConsumers) { + this.maxConcurrentConsumers = maxConcurrentConsumers; + } + /** * Is missingQueuesFatal enabled * diff --git a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetRestApi.java b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetRestApi.java index bf4e169a0..97a0eae5c 100644 --- a/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetRestApi.java +++ b/hawkbit-mgmt-api/src/main/java/org/eclipse/hawkbit/mgmt/rest/api/MgmtTargetRestApi.java @@ -35,14 +35,14 @@ public interface MgmtTargetRestApi { /** * Handles the GET request of retrieving a single target. * - * @param targetId + * @param controllerId * the ID of the target to retrieve * @return a single target with status OK. */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetId}", produces = { "application/hal+json", + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}", produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity getTarget(@PathVariable("targetId") final String targetId); + ResponseEntity getTarget(@PathVariable("controllerId") final String controllerId); /** * Handles the GET request of retrieving all targets. @@ -91,7 +91,7 @@ public interface MgmtTargetRestApi { * path of the request. A given ID in the request body is ignored. It's not * possible to set fields to {@code null} values. * - * @param targetId + * @param controllerId * the path parameter which contains the ID of the target * @param targetRest * the request body which contains the fields which should be @@ -100,40 +100,40 @@ public interface MgmtTargetRestApi { * @return the updated target response which contains all fields also fields * which have not updated */ - @RequestMapping(method = RequestMethod.PUT, value = "/{targetId}", consumes = { "application/hal+json", + @RequestMapping(method = RequestMethod.PUT, value = "/{controllerId}", consumes = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity updateTarget(@PathVariable("targetId") final String targetId, + ResponseEntity updateTarget(@PathVariable("controllerId") final String controllerId, @RequestBody final MgmtTargetRequestBody targetRest); /** * Handles the DELETE request of deleting a target. * - * @param targetId + * @param controllerId * the ID of the target to be deleted - * @return If the given targetId could exists and could be deleted Http OK. - * In any failure the JsonResponseExceptionHandler is handling the - * response. + * @return If the given controllerId could exists and could be deleted Http + * OK. In any failure the JsonResponseExceptionHandler is handling + * the response. */ - @RequestMapping(method = RequestMethod.DELETE, value = "/{targetId}", produces = { "application/hal+json", + @RequestMapping(method = RequestMethod.DELETE, value = "/{controllerId}", produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity deleteTarget(@PathVariable("targetId") final String targetId); + ResponseEntity deleteTarget(@PathVariable("controllerId") final String controllerId); /** * Handles the GET request of retrieving the attributes of a specific * target. * - * @param targetId + * @param controllerId * the ID of the target to retrieve the attributes. * @return the target attributes as map response with status OK */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/attributes", produces = { "application/hal+json", - MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity getAttributes(@PathVariable("targetId") final String targetId); + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/attributes", produces = { + "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) + ResponseEntity getAttributes(@PathVariable("controllerId") final String controllerId); /** * Handles the GET request of retrieving the Actions of a specific target. * - * @param targetId + * @param controllerId * to load actions for * @param pagingOffsetParam * the offset of list of targets for pagination, might not be @@ -151,9 +151,9 @@ public interface MgmtTargetRestApi { * status OK. The response is always paged. In any failure the * JsonResponseExceptionHandler is handling the response. */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/actions", produces = { "application/hal+json", + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/actions", produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity> getActionHistory(@PathVariable("targetId") final String targetId, + ResponseEntity> getActionHistory(@PathVariable("controllerId") final String controllerId, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam, @@ -163,22 +163,22 @@ public interface MgmtTargetRestApi { * Handles the GET request of retrieving a specific Actions of a specific * Target. * - * @param targetId + * @param controllerId * to load the action for * @param actionId * to load * @return the action */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/actions/{actionId}", produces = { + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/actions/{actionId}", produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity getAction(@PathVariable("targetId") final String targetId, + ResponseEntity getAction(@PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId); /** * Handles the DELETE request of canceling an specific Actions of a specific * Target. * - * @param targetId + * @param controllerId * the ID of the target in the URL path parameter * @param actionId * the ID of the action in the URL path parameter @@ -186,8 +186,8 @@ public interface MgmtTargetRestApi { * optional parameter, which indicates a force cancel * @return status no content in case cancellation was successful */ - @RequestMapping(method = RequestMethod.DELETE, value = "/{targetId}/actions/{actionId}") - ResponseEntity cancelAction(@PathVariable("targetId") final String targetId, + @RequestMapping(method = RequestMethod.DELETE, value = "/{controllerId}/actions/{actionId}") + ResponseEntity cancelAction(@PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId, @RequestParam(value = "force", required = false, defaultValue = "false") final boolean force); @@ -195,7 +195,7 @@ public interface MgmtTargetRestApi { * Handles the GET request of retrieving the ActionStatus of a specific * target and action. * - * @param targetId + * @param controllerId * of the the action * @param actionId * of the status we are intend to load @@ -212,10 +212,10 @@ public interface MgmtTargetRestApi { * with status OK. The response is always paged. In any failure the * JsonResponseExceptionHandler is handling the response. */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/actions/{actionId}/status", produces = { + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/actions/{actionId}/status", produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity> getActionStatusList(@PathVariable("targetId") final String targetId, - @PathVariable("actionId") final Long actionId, + ResponseEntity> getActionStatusList( + @PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam); @@ -224,40 +224,43 @@ public interface MgmtTargetRestApi { * Handles the GET request of retrieving the assigned distribution set of an * specific target. * - * @param targetId + * @param controllerId * the ID of the target to retrieve the assigned distribution * @return the assigned distribution set with status OK, if none is assigned * than {@code null} content (e.g. "{}") */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/assignedDS", produces = { "application/hal+json", - MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity getAssignedDistributionSet(@PathVariable("targetId") final String targetId); + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/assignedDS", produces = { + "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) + ResponseEntity getAssignedDistributionSet( + @PathVariable("controllerId") final String controllerId); /** * Changes the assigned distribution set of a target. * - * @param targetId + * @param controllerId * of the target to change * @param dsId * of the distributionset that is to be assigned * @return http status */ - @RequestMapping(method = RequestMethod.POST, value = "/{targetId}/assignedDS", consumes = { "application/hal+json", + @RequestMapping(method = RequestMethod.POST, value = "/{controllerId}/assignedDS", consumes = { + "application/hal+json", MediaType.APPLICATION_JSON_VALUE }, produces = { "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity postAssignedDistributionSet(@PathVariable("targetId") final String targetId, + ResponseEntity postAssignedDistributionSet(@PathVariable("controllerId") final String controllerId, @RequestBody final MgmtDistributionSetAssigment dsId); /** * Handles the GET request of retrieving the installed distribution set of * an specific target. * - * @param targetId + * @param controllerId * the ID of the target to retrieve * @return the assigned installed set with status OK, if none is installed * than {@code null} content (e.g. "{}") */ - @RequestMapping(method = RequestMethod.GET, value = "/{targetId}/installedDS", produces = { "application/hal+json", - MediaType.APPLICATION_JSON_VALUE }) - ResponseEntity getInstalledDistributionSet(@PathVariable("targetId") final String targetId); + @RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/installedDS", produces = { + "application/hal+json", MediaType.APPLICATION_JSON_VALUE }) + ResponseEntity getInstalledDistributionSet( + @PathVariable("controllerId") final String controllerId); } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java index d77cbaa53..f6cf9f54b 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetMapper.java @@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.model.PollStatus; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.rest.data.SortDirection; +import org.eclipse.hawkbit.util.IpUtil; /** * A mapper which maps repository model to RESTful model representation and @@ -141,7 +142,9 @@ public final class MgmtTargetMapper { final URI address = target.getTargetInfo().getAddress(); if (address != null) { - targetRest.setIpAddress(address.getHost()); + if (IpUtil.isIpAddresKnown(address)) { + targetRest.setIpAddress(address.getHost()); + } targetRest.setAddress(address.toString()); } diff --git a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java index cc7bbcde6..c41dadd27 100644 --- a/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java +++ b/hawkbit-mgmt-resource/src/main/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResource.java @@ -68,8 +68,8 @@ public class MgmtTargetResource implements MgmtTargetRestApi { private EntityFactory entityFactory; @Override - public ResponseEntity getTarget(@PathVariable("targetId") final String targetId) { - final Target findTarget = findTargetWithExceptionIfNotFound(targetId); + public ResponseEntity getTarget(@PathVariable("controllerId") final String controllerId) { + final Target findTarget = findTargetWithExceptionIfNotFound(controllerId); // to single response include poll status final MgmtTarget response = MgmtTargetMapper.toResponse(findTarget); MgmtTargetMapper.addPollStatus(findTarget, response); @@ -115,9 +115,9 @@ public class MgmtTargetResource implements MgmtTargetRestApi { } @Override - public ResponseEntity updateTarget(@PathVariable("targetId") final String targetId, + public ResponseEntity updateTarget(@PathVariable("controllerId") final String controllerId, @RequestBody final MgmtTargetRequestBody targetRest) { - final Target existingTarget = findTargetWithExceptionIfNotFound(targetId); + final Target existingTarget = findTargetWithExceptionIfNotFound(controllerId); LOG.debug("updating target {}", existingTarget.getId()); if (targetRest.getDescription() != null) { existingTarget.setDescription(targetRest.getDescription()); @@ -131,16 +131,16 @@ public class MgmtTargetResource implements MgmtTargetRestApi { } @Override - public ResponseEntity deleteTarget(@PathVariable("targetId") final String targetId) { - final Target target = findTargetWithExceptionIfNotFound(targetId); + public ResponseEntity deleteTarget(@PathVariable("controllerId") final String controllerId) { + final Target target = findTargetWithExceptionIfNotFound(controllerId); this.targetManagement.deleteTargets(target.getId()); - LOG.debug("{} target deleted, return status {}", targetId, HttpStatus.OK); + LOG.debug("{} target deleted, return status {}", controllerId, HttpStatus.OK); return new ResponseEntity<>(HttpStatus.OK); } @Override - public ResponseEntity getAttributes(@PathVariable("targetId") final String targetId) { - final Target foundTarget = findTargetWithExceptionIfNotFound(targetId); + public ResponseEntity getAttributes(@PathVariable("controllerId") final String controllerId) { + final Target foundTarget = findTargetWithExceptionIfNotFound(controllerId); final Map controllerAttributes = foundTarget.getTargetInfo().getControllerAttributes(); if (controllerAttributes.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); @@ -153,13 +153,14 @@ public class MgmtTargetResource implements MgmtTargetRestApi { } @Override - public ResponseEntity> getActionHistory(@PathVariable("targetId") final String targetId, + public ResponseEntity> getActionHistory( + @PathVariable("controllerId") final String controllerId, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam) { - final Target foundTarget = findTargetWithExceptionIfNotFound(targetId); + final Target foundTarget = findTargetWithExceptionIfNotFound(controllerId); final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam); final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam); @@ -177,14 +178,15 @@ public class MgmtTargetResource implements MgmtTargetRestApi { } return new ResponseEntity<>( - new PagedList<>(MgmtTargetMapper.toResponse(targetId, activeActions.getContent()), totalActionCount), + new PagedList<>(MgmtTargetMapper.toResponse(controllerId, activeActions.getContent()), + totalActionCount), HttpStatus.OK); } @Override - public ResponseEntity getAction(@PathVariable("targetId") final String targetId, + public ResponseEntity getAction(@PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId) { - final Target target = findTargetWithExceptionIfNotFound(targetId); + final Target target = findTargetWithExceptionIfNotFound(controllerId); final Action action = findActionWithExceptionIfNotFound(actionId); if (!action.getTarget().getId().equals(target.getId())) { @@ -192,18 +194,18 @@ public class MgmtTargetResource implements MgmtTargetRestApi { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - final MgmtAction result = MgmtTargetMapper.toResponse(targetId, action, action.isActive()); + final MgmtAction result = MgmtTargetMapper.toResponse(controllerId, action, action.isActive()); if (!action.isCancelingOrCanceled()) { result.add(linkTo( methodOn(MgmtDistributionSetRestApi.class).getDistributionSet(action.getDistributionSet().getId())) .withRel("distributionset")); } else if (action.isCancelingOrCanceled()) { - result.add(linkTo(methodOn(MgmtTargetRestApi.class).getAction(targetId, action.getId())) + result.add(linkTo(methodOn(MgmtTargetRestApi.class).getAction(controllerId, action.getId())) .withRel(MgmtRestConstants.TARGET_V1_CANCELED_ACTION)); } - result.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionStatusList(targetId, action.getId(), 0, + result.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionStatusList(controllerId, action.getId(), 0, MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE, ActionStatusFields.ID.getFieldName() + ":" + SortDirection.DESC)) .withRel(MgmtRestConstants.TARGET_V1_ACTION_STATUS)); @@ -212,10 +214,10 @@ public class MgmtTargetResource implements MgmtTargetRestApi { } @Override - public ResponseEntity cancelAction(@PathVariable("targetId") final String targetId, + public ResponseEntity cancelAction(@PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId, @RequestParam(value = "force", required = false, defaultValue = "false") final boolean force) { - final Target target = findTargetWithExceptionIfNotFound(targetId); + final Target target = findTargetWithExceptionIfNotFound(controllerId); final Action action = findActionWithExceptionIfNotFound(actionId); if (force) { @@ -231,12 +233,12 @@ public class MgmtTargetResource implements MgmtTargetRestApi { @Override public ResponseEntity> getActionStatusList( - @PathVariable("targetId") final String targetId, @PathVariable("actionId") final Long actionId, + @PathVariable("controllerId") final String controllerId, @PathVariable("actionId") final Long actionId, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) final int pagingOffsetParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) final int pagingLimitParam, @RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) final String sortParam) { - final Target target = findTargetWithExceptionIfNotFound(targetId); + final Target target = findTargetWithExceptionIfNotFound(controllerId); final Action action = findActionWithExceptionIfNotFound(actionId); if (!action.getTarget().getId().equals(target.getId())) { @@ -260,8 +262,8 @@ public class MgmtTargetResource implements MgmtTargetRestApi { @Override public ResponseEntity getAssignedDistributionSet( - @PathVariable("targetId") final String targetId) { - final Target findTarget = findTargetWithExceptionIfNotFound(targetId); + @PathVariable("controllerId") final String controllerId) { + final Target findTarget = findTargetWithExceptionIfNotFound(controllerId); final MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper .toResponse(findTarget.getAssignedDistributionSet()); final HttpStatus retStatus; @@ -274,29 +276,29 @@ public class MgmtTargetResource implements MgmtTargetRestApi { } @Override - public ResponseEntity postAssignedDistributionSet(@PathVariable("targetId") final String targetId, + public ResponseEntity postAssignedDistributionSet(@PathVariable("controllerId") final String controllerId, @RequestBody final MgmtDistributionSetAssigment dsId) { - findTargetWithExceptionIfNotFound(targetId); + findTargetWithExceptionIfNotFound(controllerId); final ActionType type = (dsId.getType() != null) ? MgmtRestModelMapper.convertActionType(dsId.getType()) : ActionType.FORCED; final Iterator changed = this.deploymentManagement - .assignDistributionSet(dsId.getId(), type, dsId.getForcetime(), targetId).getAssignedEntity() + .assignDistributionSet(dsId.getId(), type, dsId.getForcetime(), controllerId).getAssignedEntity() .iterator(); if (changed.hasNext()) { return new ResponseEntity<>(HttpStatus.OK); } LOG.error("Target update (ds {} assigment to target {}) failed! Returnd target list is empty.", dsId.getId(), - targetId); + controllerId); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } @Override public ResponseEntity getInstalledDistributionSet( - @PathVariable("targetId") final String targetId) { - final Target findTarget = findTargetWithExceptionIfNotFound(targetId); + @PathVariable("controllerId") final String controllerId) { + final Target findTarget = findTargetWithExceptionIfNotFound(controllerId); final MgmtDistributionSet distributionSetRest = MgmtDistributionSetMapper .toResponse(findTarget.getTargetInfo().getInstalledDistributionSet()); final HttpStatus retStatus; @@ -308,10 +310,10 @@ public class MgmtTargetResource implements MgmtTargetRestApi { return new ResponseEntity<>(distributionSetRest, retStatus); } - private Target findTargetWithExceptionIfNotFound(final String targetId) { - final Target findTarget = this.targetManagement.findTargetByControllerID(targetId); + private Target findTargetWithExceptionIfNotFound(final String controllerId) { + final Target findTarget = this.targetManagement.findTargetByControllerID(controllerId); if (findTarget == null) { - throw new EntityNotFoundException("Target with Id {" + targetId + "} does not exist"); + throw new EntityNotFoundException("Target with Id {" + controllerId + "} does not exist"); } return findTarget; } diff --git a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/util/IpUtil.java b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/util/IpUtil.java index 96fc557aa..c7778f776 100644 --- a/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/util/IpUtil.java +++ b/hawkbit-security-core/src/main/java/org/eclipse/hawkbit/util/IpUtil.java @@ -26,6 +26,7 @@ import com.google.common.net.HttpHeaders; */ public final class IpUtil { + private static final String HIDDEN_IP = "***"; private static final String SCHEME_SEPERATOR = "://"; private static final String HTTP_SCHEME = "http"; private static final String AMPQP_SCHEME = "amqp"; @@ -87,7 +88,7 @@ public final class IpUtil { ip = request.getRemoteAddr(); } } else { - ip = "***"; + ip = HIDDEN_IP; } return createHttpUri(ip); @@ -178,4 +179,17 @@ public final class IpUtil { public static boolean isAmqpUri(final URI uri) { return uri != null && AMPQP_SCHEME.equals(uri.getScheme()); } + + /** + * Check if the IP address of that {@link URI} is known, i.e. not an AQMP + * exchange in DMF case and not HIDDEN_IP in DDI case. + * + * @param uri + * the uri + * @return true if IP address is actually known by the server + */ + public static boolean isIpAddresKnown(final URI uri) { + return uri != null && !(AMPQP_SCHEME.equals(uri.getScheme()) || HIDDEN_IP.equals(uri.getHost())); + } + }