Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrating GCP IoT Core with HawkBit #34

Draft
wants to merge 55 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
867219f
init GCP
charbull Feb 17, 2019
4a543fb
Listing devices
charbull Feb 18, 2019
5028d92
GCP hack
charbull Feb 19, 2019
adab692
app engine nature
charbull Feb 20, 2019
a64c912
script
charbull Feb 20, 2019
a9ad099
integrating bucket
charbull Feb 22, 2019
5b60c57
working on bucket upload
charbull Feb 22, 2019
d3e34dc
adding config and state devices retrieval
charbull Feb 22, 2019
85c8274
adding bucket uploader
charbull Feb 23, 2019
f7e69e3
async download reading device state from pubsub and updating hawkbit
charbull Feb 25, 2019
07c18ea
pubsub and device state fw to update hawkbit
charbull Feb 25, 2019
fbe3d8d
fixed sync vs async
charbull Feb 25, 2019
5ec97e5
update readme
charbull Feb 25, 2019
0b3de03
bug fix on rollout
charbull Feb 25, 2019
059cd92
firestore init
charbull Feb 25, 2019
5856a5b
init firestore
charbull Feb 25, 2019
d949ac7
firestore integration
charbull Feb 26, 2019
50921ca
fixed NPE on cancel download
charbull Feb 26, 2019
c49f8a4
support for multi os/app upload
charbull Feb 27, 2019
38cd026
mergeOptions set to true when writing to firestore
charbull Feb 27, 2019
4a6b406
adding firebase admin
charbull Feb 27, 2019
fb6efff
firebase ID
charbull Feb 27, 2019
2560d4c
updating the following:
charbull Feb 28, 2019
9ed6204
update readme and adding an image
charbull Feb 28, 2019
3dfeea6
adding image
charbull Feb 28, 2019
2324b3f
fixed readme
charbull Feb 28, 2019
1986370
FIRESTORE Stuff
charbull Feb 28, 2019
1187e0e
trying with default credentials
charbull Feb 28, 2019
de2f9ff
update mvn
charbull Feb 28, 2019
a4c8088
update firestore testing
charbull Feb 28, 2019
ad16e47
new pullCompileRun script
charbull Feb 28, 2019
b7bd395
updating firestore
charbull Feb 28, 2019
a334b4b
update
charbull Feb 28, 2019
43bb634
added guava
charbull Feb 28, 2019
84505e9
guava update
charbull Feb 28, 2019
4857266
no firestore
charbull Feb 28, 2019
43ef796
no firestore
charbull Feb 28, 2019
196f1a7
updated google-api-client to version 1.28.0
charbull Feb 28, 2019
5ed1911
Merge branch 'master' of github.com:charbull/hawkbit-examples
charbull Feb 28, 2019
8be5daa
merge and update
charbull Feb 28, 2019
1ff6faf
firestore update
charbull Feb 28, 2019
cb69c48
FireBASe again
charbull Feb 28, 2019
f217e2c
add scripts
charbull Feb 28, 2019
d6e837b
script modifications
charbull Feb 28, 2019
05313fe
trying again with keys for firestore
charbull Feb 28, 2019
121b407
update extracting deviceId from pubsub message
charbull Feb 28, 2019
f9b96dd
update for Docker
charbull Apr 17, 2019
043af06
update
charbull Apr 17, 2019
0f41faf
renaming
charbull Apr 17, 2019
4a3e916
update from master
charbull Apr 17, 2019
350821d
sync
charbull Apr 17, 2019
0291ca7
removed unused files
charbull Apr 17, 2019
a4a7856
removed simulationProperties
charbull Apr 17, 2019
6e746da
update copyright
charbull Apr 17, 2019
8725fb5
added developer
charbull Apr 17, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions hawkbit-device-simulator/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
/bin/
/target/
/pom2.xml
/pom3.xml
/pom4.xml
13 changes: 0 additions & 13 deletions hawkbit-device-simulator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,6 @@ 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:
* username : admin
* passwd : admin

This can be configured/disabled by spring boot properties

## hawkBit APIs

The simulator supports `DDI` as well as the `DMF` integration APIs.
Expand Down
8 changes: 6 additions & 2 deletions hawkbit-device-simulator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,15 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2019 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 org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
* Spring security configuration to grant access for all incomming requests.
*/
@Configuration
public class AllowAllWebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(final HttpSecurity httpSec) throws Exception {
httpSec.csrf().disable().authorizeRequests().antMatchers("/**").permitAll().anyRequest().authenticated();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import org.eclipse.hawkbit.simulator.amqp.DmfSenderService;
import org.eclipse.hawkbit.simulator.http.GatewayTokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.hateoas.hal.Jackson2HalModule;
import org.springframework.stereotype.Service;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@
*/
package org.eclipse.hawkbit.simulator;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Optional;

import org.eclipse.hawkbit.simulator.AbstractSimulatedDevice.Protocol;
import org.eclipse.hawkbit.simulator.amqp.AmqpProperties;
import org.eclipse.hawkbit.simulator.amqp.DmfSenderService;
import org.eclipse.hawkbit.simulator.amqp.SimulatedUpdate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.net.MalformedURLException;
import java.net.URL;

/**
* REST endpoint for controlling the device simulator.
*/
Expand All @@ -33,8 +37,11 @@ public class SimulationController {

private final SimulationProperties simulationProperties;

private Optional<DmfSenderService> spSenderService = Optional.empty();

@Autowired
public SimulationController(final DeviceSimulatorRepository repository, final SimulatedDeviceFactory deviceFactory, final AmqpProperties amqpProperties, final SimulationProperties simulationProperties) {
public SimulationController(final DeviceSimulatorRepository repository, final SimulatedDeviceFactory deviceFactory,
final AmqpProperties amqpProperties, final SimulationProperties simulationProperties) {
this.repository = repository;
this.deviceFactory = deviceFactory;
this.amqpProperties = amqpProperties;
Expand All @@ -56,8 +63,7 @@ public SimulationController(final DeviceSimulatorRepository repository, final Si
* the URL endpoint to be used of the hawkbit-update-server for
* DDI devices
* @param pollDelay
* number of delay in seconds to delay polling of DDI
* devices
* number of delay in seconds to delay polling of DDI devices
* @param gatewayToken
* the hawkbit-update-server gatewaytoken in case authentication
* is enforced in hawkbit
Expand Down Expand Up @@ -87,9 +93,7 @@ ResponseEntity<String> start(@RequestParam(value = "name", defaultValue = "simul
}

if (protocol == Protocol.DMF_AMQP && isDmfDisabled()) {
return ResponseEntity.badRequest()
.body("The AMQP interface has been disabled, to use DMF protocol you need to enable the AMQP interface via '"
+ AmqpProperties.CONFIGURATION_PREFIX + ".enabled=true'");
return createAmqpDisabledResponse();
}

for (int i = 0; i < amount; i++) {
Expand All @@ -102,11 +106,17 @@ ResponseEntity<String> start(@RequestParam(value = "name", defaultValue = "simul
return ResponseEntity.ok("Updated " + amount + " " + protocol + " connected targets!");
}

private ResponseEntity<String> createAmqpDisabledResponse() {
return ResponseEntity.badRequest().body(
"The AMQP interface has been disabled, to use DMF protocol you need to enable the AMQP interface via '"
+ AmqpProperties.CONFIGURATION_PREFIX + ".enabled=true'");
}

/**
* Update an attribute of a device.
*
* NOTE: This represents not the expected client behaviour for DDI, since a
* DDI client shall only update its attributes if requested by hawkBit.
* DDI client shall only update its attributes if requested by hawkBit.
*
* @param tenant
* The tenant the device belongs to
Expand Down Expand Up @@ -150,7 +160,7 @@ ResponseEntity<String> update(@RequestParam(value = "tenant", required = false)
* if not found.
*/
@GetMapping("/remove")
ResponseEntity remove(@RequestParam(value = "tenant", required = false) final String tenant,
ResponseEntity<String> remove(@RequestParam(value = "tenant", required = false) final String tenant,
@RequestParam(value = "controllerid") final String controllerId) {

final AbstractSimulatedDevice controller = repository
Expand All @@ -176,6 +186,39 @@ ResponseEntity<String> reset() {
return ResponseEntity.ok("All simulated devices have been removed.");
}

/**
* Report action as FINISHED Sends an UpdateActionStatus event with value
* FINISHED
*
* @return A response string that the action_finished event was sent
*/
@GetMapping("/finishAction")
ResponseEntity<String> finishAction(@RequestParam(value = "actionId") final long actionId,
@RequestParam(value = "tenant", required = false) final String tenant,
@RequestParam(value = "controllerId") final String controllerId) {

if (!spSenderService.isPresent()) {
return createAmqpDisabledResponse();
}

final String theTenant = tenant != null && !tenant.isEmpty() ? tenant : simulationProperties.getDefaultTenant();
final AbstractSimulatedDevice device = repository.get(theTenant, controllerId);

if (device == null) {
return ResponseEntity.notFound().build();
}

spSenderService.get().finishUpdateProcess(new SimulatedUpdate(device.getTenant(), device.getId(), actionId),
Collections.singletonList("Simulation Finished."));

return ResponseEntity.ok(String.format("Action with id: [%d] reported as FINISHED", actionId));
}

@Autowired(required = false)
public void setSpSenderService(final DmfSenderService spSenderService) {
this.spSenderService = Optional.of(spSenderService);
}

private boolean isDmfDisabled() {
return !amqpProperties.isEnabled();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@ public UpdateStatus(final ResponseStatus responseStatus, final String message) {
}

/**
* Constructor including status message.
* Constructor including status messages.
*
* @param responseStatus
* of the update
* @param messages
* of the update status
* @param statusMessages
* list of status messages
*/
public UpdateStatus(final ResponseStatus responseStatus, final List<String> statusMessages) {
this(responseStatus);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,19 @@
import org.eclipse.hawkbit.simulator.DeviceSimulatorRepository;
import org.eclipse.hawkbit.simulator.DeviceSimulatorUpdater;
import org.eclipse.hawkbit.simulator.SimulationProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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;

import com.google.common.collect.Maps;

Expand All @@ -43,33 +39,6 @@
@ConditionalOnProperty(prefix = AmqpProperties.CONFIGURATION_PREFIX, name = "enabled")
public class AmqpConfiguration {

private static final Logger LOGGER = LoggerFactory.getLogger(AmqpConfiguration.class);

@Autowired
private AmqpProperties amqpProperties;

@Bean
RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
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;
}

@Bean
DmfReceiverService dmfReceiverService(final RabbitTemplate rabbitTemplate, final AmqpProperties amqpProperties,
final DmfSenderService spSenderService, final DeviceSimulatorUpdater deviceUpdater,
Expand All @@ -83,18 +52,38 @@ DmfSenderService dmfSenderService(final RabbitTemplate rabbitTemplate, final Amq
return new DmfSenderService(rabbitTemplate, amqpProperties, simulationProperties);
}

@Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);

// It is necessary to define rabbitTemplate as a Bean and set
// Jackson2JsonMessageConverter explicitly here in order to convert only
// OUTCOMING messages to json. In case of INCOMING messages,
// Jackson2JsonMessageConverter can not handle messages with NULL
// payload (e.g. REQUEST_ATTRIBUTES_UPDATE), so the
// SimpleMessageConverter is used instead per default.
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());

return rabbitTemplate;
}

/**
* Creates the receiver queue from update server for receiving message from
* update server.
*
* @return the queue
*/
@Bean
Queue receiverConnectorQueueFromHawkBit() {
final Map<String, Object> arguments = getTTLMaxArgs();

Queue receiverConnectorQueueFromHawkBit(final AmqpProperties amqpProperties) {
return QueueBuilder.nonDurable(amqpProperties.getReceiverConnectorQueueFromSp()).autoDelete()
.withArguments(arguments).build();
.withArguments(getTTLMaxArgs()).build();
}

private static Map<String, Object> getTTLMaxArgs() {
final Map<String, Object> args = Maps.newHashMapWithExpectedSize(2);
args.put("x-message-ttl", Duration.ofDays(1).toMillis());
args.put("x-max-length", 100_000);
return args;
}

/**
Expand All @@ -103,7 +92,7 @@ Queue receiverConnectorQueueFromHawkBit() {
* @return the exchange
*/
@Bean
FanoutExchange exchangeQueueToConnector() {
FanoutExchange exchangeQueueToConnector(final AmqpProperties amqpProperties) {
return new FanoutExchange(amqpProperties.getSenderForSpExchange(), false, true);
}

Expand All @@ -115,15 +104,18 @@ FanoutExchange exchangeQueueToConnector() {
* @return the binding and create the queue and exchange
*/
@Bean
Binding bindReceiverQueueToSpExchange() {
return BindingBuilder.bind(receiverConnectorQueueFromHawkBit()).to(exchangeQueueToConnector());
Binding bindReceiverQueueToSpExchange(final AmqpProperties amqpProperties) {
return BindingBuilder.bind(receiverConnectorQueueFromHawkBit(amqpProperties))
.to(exchangeQueueToConnector(amqpProperties));
}

private static Map<String, Object> getTTLMaxArgs() {
final Map<String, Object> args = Maps.newHashMapWithExpectedSize(2);
args.put("x-message-ttl", Duration.ofDays(1).toMillis());
args.put("x-max-length", 100_000);
return args;
}
@Configuration
@ConditionalOnProperty(prefix = AmqpProperties.CONFIGURATION_PREFIX, name = "customVhost")
protected static class CachingConnectionFactoryInitializer {

CachingConnectionFactoryInitializer(final CachingConnectionFactory connectionFactory,
final AmqpProperties amqpProperties) {
connectionFactory.setVirtualHost(amqpProperties.getCustomVhost());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ public class AmqpProperties {
*/
private int deadLetterTtl = 60_000;

private String customVhost;

public boolean isCheckDmfHealth() {
return checkDmfHealth;
}
Expand Down Expand Up @@ -90,4 +92,12 @@ public boolean isEnabled() {
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}

public String getCustomVhost() {
return customVhost;
}

public void setCustomVhost(final String customVhost) {
this.customVhost = customVhost;
}
}
Loading