Merge pull request #117 from bsinno/hawkbit-sandbox

Hawkbit sandbox
This commit is contained in:
Kai Zimmermann
2016-04-04 13:48:38 +02:00
36 changed files with 390 additions and 72 deletions

View File

@@ -2,7 +2,7 @@
The device simulator handles software update commands from the update server.
## Run
## Run on your own workstation
```
java -jar examples/hawkbit-device-simulator/target/hawkbit-device-simulator-*-SNAPSHOT.jar
```
@@ -11,6 +11,11 @@ Or:
run org.eclipse.hawkbit.simulator.DeviceSimulator
```
## Deploy to cloud foundry environment
- Go to ```target``` subfolder.
- Run ```cf push```
## Notes
The simulator has user authentication enabled in **cloud profile**. Default credentials:
@@ -30,9 +35,9 @@ http://localhost:8083
```
![](src/main/images/generateScreenshot.png)
![](src/main/images/updateProcessScreenshot.png)
![](src/main/images/updateResultOverviewScreenshot.png)
@@ -54,12 +59,12 @@ Example: for 20 simulated devices (default)
http://localhost:8083/start
```
Example: for 10 simulated devices that start with the name prefix "activeSim":
Example: for 10 simulated devices that start with the name prefix "activeSim":
```
http://localhost:8083/start?amount=10&name=activeSim
```
Example: for 5 simulated devices that start with the name prefix "ddi" using the Direct Device Integration API (http):
Example: for 5 simulated devices that start with the name prefix "ddi" using the Direct Device Integration API (http):
```
http://localhost:8083/start?amount=5&name=ddi?api=ddi
```

View File

@@ -0,0 +1,22 @@
#
# 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
#
---
applications:
- name: hawkbit-simulator
memory: 1024M
instances: 1
buildpack: https://github.com/cloudfoundry/java-buildpack
path: ${project.build.finalName}.jar
services:
- dmf-rabbit
env:
SPRING_PROFILES_ACTIVE: cloud,amqp
CF_STAGING_TIMEOUT: 15
CF_STARTUP_TIMEOUT: 15

View File

@@ -42,6 +42,19 @@
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>cf</directory>
<filtering>true</filtering>
<targetPath>${project.build.directory}</targetPath>
<includes>
<include>manifest.yml</include>
</includes>
</resource>
</resources>
</build>
<dependencies>

View File

@@ -14,6 +14,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.eclipse.hawkbit.simulator.event.InitUpdate;
import org.eclipse.hawkbit.simulator.event.ProgressUpdate;
@@ -34,6 +35,9 @@ public class DeviceSimulatorUpdater {
@Autowired
private SpSenderService spSenderService;
@Autowired
private SimulatedDeviceFactory deviceFactory;
@Autowired
private EventBus eventbus;
@@ -58,7 +62,13 @@ public class DeviceSimulatorUpdater {
*/
public void startUpdate(final String tenant, final String id, final long actionId, final String swVersion,
final UpdaterCallback callback) {
final AbstractSimulatedDevice device = repository.get(tenant, id);
AbstractSimulatedDevice device = repository.get(tenant, id);
// plug and play - non existing device will be auto created
if (device == null) {
device = repository.add(deviceFactory.createSimulatedDevice(id, tenant, Protocol.DMF_AMQP, -1, null, null));
}
device.setProgress(0.0);
device.setSwversion(swVersion);
eventbus.post(new InitUpdate(device));

View File

@@ -55,7 +55,7 @@ public class SimulationController {
* number of delay in milliseconds to delay polling of DDI
* devices
* @param gatewayToken
* the hawkbit-update-server gatwaytoken in case authentication
* the hawkbit-update-server gatewaytoken in case authentication
* is enforced in hawkbit
* @return a response string that devices has been created
* @throws MalformedURLException
@@ -68,7 +68,7 @@ public class SimulationController {
@RequestParam(value = "endpoint", defaultValue = "http://localhost:8080") final String endpoint,
@RequestParam(value = "polldelay", defaultValue = "30") final int pollDelay,
@RequestParam(value = "gatewaytoken", defaultValue = "") final String gatewayToken)
throws MalformedURLException {
throws MalformedURLException {
final Protocol protocol;
switch (api.toLowerCase()) {

View File

@@ -0,0 +1,136 @@
/**
* 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.simulator;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* General simulator service properties.
*
*/
@Component
@ConfigurationProperties("hawkbit.device.simulator")
public class SimulationProperties {
/**
* List of tenants where the simulator should auto start simulations after
* startup.
*/
private final List<Autostart> autostarts = new ArrayList<>();
public List<Autostart> getAutostarts() {
return this.autostarts;
}
/**
* Auto start configuration for simulation setups that the simulator begins
* after startup.
*
*/
public static class Autostart {
/**
* Name prefix of simulated devices, followed by counter, e.g.
* simulated0, simulated1, simulated2....
*/
private String name = "simulated";
/**
* Amount of simulated devices.
*/
private int amount = 20;
/**
* Tenant name for the simulation.
*/
@NotEmpty
private String tenant;
/**
* API for simulation.
*/
private Protocol api = Protocol.DMF_AMQP;
/**
* Endpoint in case of DDI API based simulation.
*/
private String endpoint = "http://localhost:8080";
/**
* Poll time in case of DDI API based simulation.
*/
private int pollDelay = 30;
/**
* Optional gateway token for DDI API based simulation.
*/
private String gatewayToken = "";
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public int getAmount() {
return amount;
}
public void setAmount(final int amount) {
this.amount = amount;
}
public String getTenant() {
return tenant;
}
public void setTenant(final String tenant) {
this.tenant = tenant;
}
public Protocol getApi() {
return api;
}
public void setApi(final Protocol api) {
this.api = api;
}
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(final String endpoint) {
this.endpoint = endpoint;
}
public int getPollDelay() {
return pollDelay;
}
public void setPollDelay(final int pollDelay) {
this.pollDelay = pollDelay;
}
public String getGatewayToken() {
return gatewayToken;
}
public void setGatewayToken(final String gatewayToken) {
this.gatewayToken = gatewayToken;
}
}
}

View File

@@ -0,0 +1,59 @@
/**
* 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.simulator;
import java.net.URL;
import org.eclipse.hawkbit.simulator.amqp.SpSenderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
/**
* Execution of operations after startup. Set up of simulations.
*
*/
@Component
public class SimulatorStartup implements ApplicationListener<ContextRefreshedEvent> {
private static final Logger LOGGER = LoggerFactory.getLogger(SimulatorStartup.class);
@Autowired
private SimulationProperties simulationProperties;
@Autowired
private SpSenderService spSenderService;
@Autowired
private DeviceSimulatorRepository repository;
@Autowired
private SimulatedDeviceFactory deviceFactory;
@Override
public void onApplicationEvent(final ContextRefreshedEvent event) {
simulationProperties.getAutostarts().forEach(autostart -> {
for (int i = 0; i < autostart.getAmount(); i++) {
final String deviceId = autostart.getName() + i;
try {
repository.add(deviceFactory.createSimulatedDevice(deviceId, autostart.getTenant(),
autostart.getApi(), autostart.getPollDelay(), new URL(autostart.getEndpoint()),
autostart.getGatewayToken()));
} catch (final Exception e) {
LOGGER.error("Creation of simulated device at startup failed.", e);
}
spSenderService.createOrUpdateThing(autostart.getTenant(), deviceId);
}
});
}
}

View File

@@ -59,7 +59,8 @@ public class AmqpConfiguration {
}
/**
* Create the receiver queue from sp. Receive messages from sp.
* Creates the receiver queue from update server for receiving message from
* update server.
*
* @return the queue
*/
@@ -70,7 +71,7 @@ public class AmqpConfiguration {
}
/**
* Create the recevier exchange. Sp send messages to this exchange.
* Creates the receiver exchange for sending messages to update server.
*
* @return the exchange
*/

View File

@@ -19,26 +19,25 @@ import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("hawkbit.device.simulator.amqp")
public class AmqpProperties {
/**
* Queue for receiving DMF messages from update server.
*/
private String receiverConnectorQueueFromSp;
private String receiverConnectorQueueFromSp = "simulator_receiver";
/**
* Exchange for sending DMF messages to update server.
*/
private String senderForSpExchange;
private String senderForSpExchange = "simulator.replyTo";
/**
* Simulator dead letter queue.
*/
private String deadLetterQueue;
private String deadLetterQueue = "simulator_deadletter";
/**
* Simulator dead letter exchange.
*/
private String deadLetterExchange;
private String deadLetterExchange = "simulator.deadletter";
public String getReceiverConnectorQueueFromSp() {
return receiverConnectorQueueFromSp;

View File

@@ -26,14 +26,11 @@ import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
/**
* Handle all incoming Messages from SP.
*
*
* Handle all incoming Messages from hawkBit update server.
*
*/
@Component
public class SpReceiverService extends ReceiverService {
private static final Logger LOGGER = LoggerFactory.getLogger(ReceiverService.class);
public static final String SOFTWARE_MODULE_FIRMWARE = "firmware";
@@ -44,17 +41,6 @@ public class SpReceiverService extends ReceiverService {
/**
* Constructor.
*
* @param rabbitTemplate
* the rabbit template
* @param amqpProperties
* the amqp properties
* @param lwm2mSenderService
* the lwm2mSenderService
* @param spSenderService
* the spSenderService
* @param deviceUpdater
* the updater service to simulate update process
*/
@Autowired
public SpReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
@@ -62,12 +48,11 @@ public class SpReceiverService extends ReceiverService {
super(rabbitTemplate, amqpProperties);
this.spSenderService = spSenderService;
this.deviceUpdater = deviceUpdater;
}
/**
* Handle the incoming Message from Queue with the property
* (com.bosch.sp.lwm2m.connector.amqp.receiverConnectorQueueFromSp).
* (hawkbit.device.simulator.amqp.receiverConnectorQueueFromSp).
*
* @param message
* the incoming message

View File

@@ -7,18 +7,17 @@
# http://www.eclipse.org/legal/epl-v10.html
#
#########################################################################################
# PUBLIC configuration, i.e. can be changed by users at runtime (defaults provided here)
#########################################################################################
## Configuration for RabbitMQ communication
## Configuration for DMF communication
hawkbit.device.simulator.amqp.receiverConnectorQueueFromSp=simulator_receiver
hawkbit.device.simulator.amqp.deadLetterQueue=simulator_deadletter
hawkbit.device.simulator.amqp.deadLetterExchange=simulator.deadletter
hawkbit.device.simulator.amqp.senderForSpExchange=simulator.replyTo
## Configuration for simulations
hawkbit.device.simulator.autostarts.[0].tenant=DEFAULT
## Configuration for RabbitMQ integration
## Configuration for local RabbitMQ integration
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtualHost=/

View File

@@ -1,7 +1,15 @@
# hawkBit Example Application
The hawkBit example application is a standalone spring-boot application with an embedded servlet container to start the hawkBit.
The hawkBit example application is a standalone spring-boot application with an embedded servlet container to host the hawkBit Update Server.
## Run
We have have described several options for you to get access to the example.
## Try out the example application in our hawkBit sandbox on Bluemix
- try out Management UI https://hawkbit.eu-gb.mybluemix.net/UI
- try out Management API https://hawkbit.eu-gb.mybluemix.net/rest/v1/targets (don't forget basic auth header)
- try out DDI API https://hawkbit.eu-gb.mybluemix.net/DEFAULT/controller/v1/MYTESTDEVICE
## On your own workstation
### Run
```
java -jar examples/hawkbit-example-app/target/hawkbit-example-app-*-SNAPSHOT.jar
```
@@ -10,6 +18,14 @@ Or:
run org eclipse.hawkbit.app.Start
```
## Usage
The UI can be accessed via _http://localhost:8080/UI_.
The REST API can be accessed via _http://localhost:8080/rest/v1_.
### Usage
The Management UI can be accessed via http://localhost:8080/UI
The Management API can be accessed via http://localhost:8080/rest/v1
## Deploy example app to Cloud Foundry
- Go to ```target``` subfolder.
- Select one of the two manifests
- **manifest-simple.yml** for a standalone hawkBit installation with embedded H2.
- **manifest.yml** for a standalone hawkBit installation with embedded H2 and RabbitMQ service binding for DMF integration (note: this manifest is used for the sandbox above).
- Run ```cf push``` against you cloud foundry environment.

View File

@@ -0,0 +1,20 @@
#
# 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
#
---
applications:
- name: hawkbit-simple
memory: 1024M
instances: 1
buildpack: https://github.com/cloudfoundry/java-buildpack
path: ${project.build.finalName}.jar
env:
SPRING_PROFILES_ACTIVE: cloudsandbox
CF_STAGING_TIMEOUT: 15
CF_STARTUP_TIMEOUT: 15

View File

@@ -0,0 +1,22 @@
#
# 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
#
---
applications:
- name: hawkbit
memory: 1024M
instances: 1
buildpack: https://github.com/cloudfoundry/java-buildpack
path: ${project.build.finalName}.jar
services:
- dmf-rabbit
env:
SPRING_PROFILES_ACTIVE: cloudsandbox,amqp
CF_STAGING_TIMEOUT: 15
CF_STARTUP_TIMEOUT: 15

View File

@@ -39,6 +39,20 @@
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>cf</directory>
<filtering>true</filtering>
<targetPath>${project.build.directory}</targetPath>
<includes>
<include>manifest.yml</include>
<include>manifest-simple.yml</include>
</includes>
</resource>
</resources>
</build>
<dependencies>

View File

@@ -0,0 +1,10 @@
#
# 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
#
vaadin.servlet.productionMode=true

View File

@@ -14,7 +14,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
/**
* Client binding for the DistributionSet resource of the management API.
*/
@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/distributionsets")
@FeignClient(url = "${hawkbit.url:localhost:8080}/rest/v1/distributionsets")
public interface DistributionSetResourceClient extends DistributionSetRestApi {
}

View File

@@ -14,7 +14,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
/**
* Client binding for the DistributionSetTag resource of the management API.
*/
@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/distributionsettags")
@FeignClient(url = "${hawkbit.url:localhost:8080}/rest/v1/distributionsettags")
public interface DistributionSetTagResourceClient extends DistributionSetTagRestApi {
}

View File

@@ -15,7 +15,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
* Client binding for the DistributionSetType resource of the management API.
*
*/
@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/distributionsettypes")
@FeignClient(url = "${hawkbit.url:localhost:8080}/rest/v1/distributionsettypes")
public interface DistributionSetTypeResourceClient extends DistributionSetTypeRestApi {
}

View File

@@ -14,7 +14,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
/**
* Client binding for the Rollout resource of the management API.
*/
@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/rollouts")
@FeignClient(url = "${hawkbit.url:localhost:8080}/rest/v1/rollouts")
public interface RolloutResourceClient extends RolloutRestApi {
}

View File

@@ -14,7 +14,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
/**
* Client binding for the SoftwareModule resource of the management API.
*/
@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/softwaremodules")
@FeignClient(url = "${hawkbit.url:localhost:8080}/rest/v1/softwaremodules")
public interface SoftwareModuleResourceClient extends SoftwareModuleRestAPI {
}

View File

@@ -14,7 +14,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
/**
* Client binding for the oftwareModuleType resource of the management API.
*/
@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/softwaremoduletypes")
@FeignClient(url = "${hawkbit.url:localhost:8080}/rest/v1/softwaremoduletypes")
public interface SoftwareModuleTypeResourceClient extends SoftwareModuleTypeRestApi {
}

View File

@@ -14,7 +14,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
/**
* Client binding for the Target resource of the management API.
*/
@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/targets")
@FeignClient(url = "${hawkbit.url:localhost:8080}/rest/v1/targets")
public interface TargetResourceClient extends TargetRestApi {
}

View File

@@ -14,7 +14,7 @@ import org.springframework.cloud.netflix.feign.FeignClient;
/**
* Client binding for the TargetTag resource of the management API.
*/
@FeignClient(url = "${hawkbit.endpoint.url:localhost:8080}/rest/v1/targettags")
@FeignClient(url = "${hawkbit.url:localhost:8080}/rest/v1/targettags")
public interface TargetTagResourceClient extends TargetTagRestApi {
}

View File

@@ -16,11 +16,7 @@ import org.eclipse.hawkbit.rest.resource.model.distributionset.DistributionSetRe
import com.google.common.collect.Lists;
/**
*
* Builder pattern for building {@link DistributionSetRequestBodyPost}.
*
* @author Jonathan Knoblauch
*
*/
public class DistributionSetBuilder {

View File

@@ -19,8 +19,6 @@ import com.google.common.collect.Lists;
/**
*
* Builder pattern for building {@link DistributionSetTypeRequestBodyPost}.
*
* @author Jonathan Knoblauch
*
*/
public class DistributionSetTypeBuilder {

View File

@@ -15,8 +15,6 @@ import org.eclipse.hawkbit.rest.resource.model.rollout.RolloutRestRequestBody;
/**
*
* Builder pattern for building {@link RolloutRestRequestBody}.
*
* @author Jonathan Knoblauch
*
*/
public class RolloutBuilder {

View File

@@ -16,8 +16,6 @@ import org.eclipse.hawkbit.rest.resource.model.softwaremodule.SoftwareModuleAssi
/**
*
* Builder pattern for building {@link SoftwareModuleAssigmentRest}.
*
* @author Jonathan Knoblauch
*
*/
public class SoftwareModuleAssigmentBuilder {

View File

@@ -19,8 +19,6 @@ import com.google.common.collect.Lists;
/**
*
* Builder pattern for building {@link SoftwareModuleRequestBodyPost}.
*
* @author Jonathan Knoblauch
*
*/
public class SoftwareModuleBuilder {

View File

@@ -19,8 +19,6 @@ import com.google.common.collect.Lists;
/**
*
* Builder pattern for building {@link SoftwareModuleRequestBodyPost}.
*
* @author Jonathan Knoblauch
*
*/
public class SoftwareModuleTypeBuilder {

View File

@@ -17,8 +17,6 @@ import com.google.common.collect.Lists;
/**
* Builder pattern for building {@link TagRequestBodyPut}.
*
* @author Jonathan Knoblauch
*
*/
public class TagBuilder {

View File

@@ -19,8 +19,6 @@ import com.google.common.collect.Lists;
/**
*
* Builder pattern for building {@link TargetRequestBody}.
*
* @author Jonathan Knoblauch
*
*/
public class TargetBuilder {

View File

@@ -41,7 +41,7 @@ public class CreateStartedRolloutExample {
private static final String SM_MODULE_TYPE = "firmware";
/* known distribution set type name and key */
private static final String DS_MODULE_TYPE = "firmware";
private static final String DS_MODULE_TYPE = SM_MODULE_TYPE;
@Autowired
private DistributionSetResourceClient distributionSetResource;

View File

@@ -41,13 +41,13 @@ public class GettingStartedDefaultScenario {
private static final String SM_MODULE_TYPE = "gettingstarted";
/* known distribution set type name and key */
private static final String DS_MODULE_TYPE = "gettingstarted";
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 = "gettingstarted-example";
private static final String DS_EXAMPLE_NAME = SM_EXAMPLE_NAME;
@Autowired
private DistributionSetResourceClient distributionSetResource;