Propose SDK Refactor (#1821)

* Propose SDK Refactor

* Added ExecutorService for DMF Devices

* After review, Created MgmtApi inside sdk-mgmt

* Removed direct dependency to halkbit-mgmt-api all mgmt related calls now goes through hawkbit-sdk-mgmt

* Added copyright header

* Removed redundant paramters for deleteController

* Fixed File Copyright Headers
This commit is contained in:
Vasil Ilchev
2024-08-19 15:34:29 +03:00
committed by GitHub
parent d958d8e82c
commit ac34b952d9
15 changed files with 215 additions and 131 deletions

View File

@@ -1,121 +0,0 @@
/**
* Copyright (c) 2023 Bosch.IO GmbH and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.sdk.demo;
import feign.FeignException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTargetRestApi;
import org.eclipse.hawkbit.mgmt.rest.api.MgmtTenantManagementRestApi;
import org.eclipse.hawkbit.sdk.HawkbitClient;
import org.eclipse.hawkbit.sdk.Tenant;
import org.springframework.util.ObjectUtils;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
/**
* Abstract class representing DDI device connecting directly to hawkVit.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j
public class SetupHelper {
private static final String AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY = "authentication.gatewaytoken.key";
private static final String AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED = "authentication.gatewaytoken.enabled";
private static final String AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED = "authentication.targettoken.enabled";
// if gateway toke is configured then the gateway auth is enabled key is set
// so all devices use gateway token authentication
// otherwise target token authentication is enabled. Then all devices shall be registerd
// and the target token shall be set to the one from the DDI controller instance
public static void setupTargetAuthentication(final HawkbitClient hawkbitClient, final Tenant tenant) {
final MgmtTenantManagementRestApi mgmtTenantManagementRestApi =
hawkbitClient.mgmtService(MgmtTenantManagementRestApi.class, tenant);
final String gatewayToken = tenant.getGatewayToken();
if (ObjectUtils.isEmpty(gatewayToken)) {
if (!((Boolean) Objects.requireNonNull(mgmtTenantManagementRestApi
.getTenantConfigurationValue(AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED)
.getBody()).getValue())) {
mgmtTenantManagementRestApi.updateTenantConfiguration(
Map.of(AUTHENTICATION_MODE_TARGET_SECURITY_TOKEN_ENABLED, true)
);
}
} else {
if (!((Boolean) Objects.requireNonNull(mgmtTenantManagementRestApi
.getTenantConfigurationValue(AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED)
.getBody()).getValue())) {
mgmtTenantManagementRestApi.updateTenantConfiguration(
Map.of(AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_ENABLED, true)
);
}
if (!gatewayToken.equals(
Objects.requireNonNull(mgmtTenantManagementRestApi
.getTenantConfigurationValue(AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY)
.getBody()).getValue())) {
mgmtTenantManagementRestApi.updateTenantConfiguration(
Map.of(AUTHENTICATION_MODE_GATEWAY_SECURITY_TOKEN_KEY, gatewayToken)
);
}
}
}
// returns target token
public static String setupTargetToken(
final String controllerId, String securityTargetToken,
final HawkbitClient hawkbitClient, final Tenant tenant) {
if (ObjectUtils.isEmpty(tenant.getGatewayToken())) {
final MgmtTargetRestApi mgmtTargetRestApi = hawkbitClient.mgmtService(MgmtTargetRestApi.class, tenant);
try {
// test if target exist, if not - throws 404
final MgmtTarget target = Objects.requireNonNull(mgmtTargetRestApi.getTarget(controllerId).getBody());
if (ObjectUtils.isEmpty(securityTargetToken)) {
if (ObjectUtils.isEmpty(target.getSecurityToken())) {
// generate random to set to tha existing target without configured security token
securityTargetToken = randomToken();
mgmtTargetRestApi.updateTarget(controllerId,
new MgmtTargetRequestBody().setSecurityToken(securityTargetToken));
} else {
securityTargetToken = target.getSecurityToken();
}
} else if (!securityTargetToken.equals(target.getSecurityToken())) {
// update target's with the security token (since it doesn't match)
mgmtTargetRestApi.updateTarget(controllerId,
new MgmtTargetRequestBody().setSecurityToken(securityTargetToken));
}
} catch (final FeignException.NotFound e) {
if (ObjectUtils.isEmpty(securityTargetToken)) {
securityTargetToken = randomToken();
}
// create target with the security token
mgmtTargetRestApi.createTargets(List.of(
new MgmtTargetRequestBody()
.setControllerId(controllerId)
.setSecurityToken(securityTargetToken)));
}
}
return securityTargetToken;
}
private static final Random RND = new SecureRandom();
public static String randomToken() {
final byte[] rnd = new byte[24];
RND.nextBytes(rnd);
return Base64.getEncoder().encodeToString(rnd);
}
}

View File

@@ -15,25 +15,22 @@ import feign.codec.Decoder;
import feign.codec.Encoder;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.sdk.Controller;
import org.eclipse.hawkbit.sdk.HawkbitServer;
import org.eclipse.hawkbit.sdk.HawkbitClient;
import org.eclipse.hawkbit.sdk.HawkbitSDKConfigurtion;
import org.eclipse.hawkbit.sdk.HawkbitServer;
import org.eclipse.hawkbit.sdk.Tenant;
import org.eclipse.hawkbit.sdk.demo.SetupHelper;
import org.eclipse.hawkbit.sdk.device.DdiController;
import org.eclipse.hawkbit.sdk.device.DdiTenant;
import org.eclipse.hawkbit.sdk.device.UpdateHandler;
import org.springframework.beans.factory.annotation.Value;
import org.eclipse.hawkbit.sdk.mgmt.MgmtApi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.util.ObjectUtils;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
* Abstract class representing DDI device connecting directly to hawkVit.
@@ -54,49 +51,49 @@ public class DeviceApp {
}
@Bean
DdiController device(
@Value("${hawkbit.device:controller-default}") final String controllerId,
@Value("${hawkbit.device.securityToken:}") final String securityToken,
final Tenant defaultTenant,
final Optional<UpdateHandler> updateHandler,
final HawkbitClient hawkbitClient) {
return new DdiController(
defaultTenant,
Controller.builder()
.controllerId(controllerId)
.securityToken(ObjectUtils.isEmpty(securityToken) ?
(ObjectUtils.isEmpty(defaultTenant.getGatewayToken()) ? SetupHelper.randomToken() : securityToken) :
securityToken)
.build(),
updateHandler.orElse(null),
hawkbitClient).setOverridePollMillis(10_000);
DdiTenant ddiTenant(final Tenant defaultTenant,
final HawkbitClient hawkbitClient) {
return new DdiTenant(defaultTenant, hawkbitClient);
}
@Bean
MgmtApi mgmtApi(final Tenant tenant, final HawkbitClient hawkbitClient) {
return new MgmtApi(tenant, hawkbitClient);
}
@ShellComponent
public static class Shell {
private final Tenant tenant;
private final DdiTenant ddiTenant;
private final DdiController device;
private final HawkbitClient hawkbitClient;
private final MgmtApi mgmtApi;
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
Shell(final Tenant tenant, final DdiController device, final HawkbitClient hawkbitClient) {
this.tenant = tenant;
this.device = device;
this.hawkbitClient = hawkbitClient;
Shell(final DdiTenant ddiTenant, final MgmtApi mgmtApi, final Optional<UpdateHandler> updateHandler) {
this.ddiTenant = ddiTenant;
this.mgmtApi = mgmtApi;
String controllerId = System.getProperty("demo.controller.id");
String securityToken = System.getProperty("demo.controller.securityToken");
this.device = this.ddiTenant.createController(Controller.builder()
.controllerId(controllerId)
.securityToken(ObjectUtils.isEmpty(securityToken) ?
(ObjectUtils.isEmpty(ddiTenant.getTenant().getGatewayToken()) ? MgmtApi.randomToken() : securityToken) :
securityToken)
.build(),
updateHandler.orElse(null)).setOverridePollMillis(10_000);
}
@ShellMethod(key = "setup")
public void setup() {
SetupHelper.setupTargetAuthentication(hawkbitClient, tenant);
SetupHelper.setupTargetToken(
device.getControllerId(), device.getTargetSecurityToken(), hawkbitClient, tenant);
mgmtApi.setupTargetAuthentication();
mgmtApi.setupTargetToken(device.getControllerId(),device.getTargetSecurityToken());
}
@ShellMethod(key = "start")
public void start() {
device.start(scheduler);
device.start(Executors.newSingleThreadScheduledExecutor());
}
@ShellMethod(key = "stop")

View File

@@ -27,6 +27,7 @@ import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import java.util.Optional;
import java.util.concurrent.Executors;
/**
* Abstract class representing DDI device connecting directly to hawkVit.
@@ -45,33 +46,35 @@ public class DmfApp {
return new Amqp(rabbitProperties, amqpProperties);
}
@Bean
DmfTenant dmfTenant(Tenant tenant, Amqp amqp) {
return new DmfTenant(tenant, amqp);
}
@ShellComponent
public static class Shell {
private final UpdateHandler updateHandler;
private final DmfTenant dmfTenant;
private final UpdateHandler updateHandler;
Shell(final Tenant tenant, final Optional<UpdateHandler> updateHandler, final Amqp amqp) {
Shell(final DmfTenant dmfTenant, final Optional<UpdateHandler> updateHandler) {
this.dmfTenant = dmfTenant;
this.updateHandler = updateHandler.orElse(null);
dmfTenant = new DmfTenant(tenant, amqp);
}
@ShellMethod(key = "start-one")
public void startOne(@ShellOption("--id") final String controllerId) {
if (dmfTenant.getController(controllerId).isEmpty()) {
dmfTenant.create(
Controller.builder().controllerId(controllerId).build(),
updateHandler).connect();
}
dmfTenant.getController(controllerId).ifPresentOrElse(
dmfController -> dmfController.start(Executors.newSingleThreadScheduledExecutor()),
() -> dmfTenant.createController(Controller.builder().controllerId(controllerId).build(), updateHandler)
.start(Executors.newSingleThreadScheduledExecutor()));
}
@ShellMethod(key = "stop-one")
public void stopOne(@ShellOption("--id") final String controllerId) {
dmfTenant.getController(controllerId).ifPresentOrElse(
DmfController::stop,
() -> {
throw new IllegalArgumentException("Controller with id " + controllerId + " not found!");
});
() -> log.error("Controller with id " + controllerId + " not found!"));
}
@ShellMethod(key = "start")

View File

@@ -15,26 +15,22 @@ import feign.codec.Decoder;
import feign.codec.Encoder;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.sdk.Controller;
import org.eclipse.hawkbit.sdk.HawkbitServer;
import org.eclipse.hawkbit.sdk.HawkbitClient;
import org.eclipse.hawkbit.sdk.HawkbitSDKConfigurtion;
import org.eclipse.hawkbit.sdk.HawkbitServer;
import org.eclipse.hawkbit.sdk.Tenant;
import org.eclipse.hawkbit.sdk.demo.SetupHelper;
import org.eclipse.hawkbit.sdk.device.DdiController;
import org.eclipse.hawkbit.sdk.device.DdiTenant;
import org.eclipse.hawkbit.sdk.device.UpdateHandler;
import org.eclipse.hawkbit.sdk.mgmt.MgmtApi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
* Abstract class representing DDI device connecting directly to hawkVit.
@@ -54,66 +50,66 @@ public class MultiDeviceApp {
return new HawkbitClient(hawkBitServer, client, encoder, decoder, contract);
}
@Bean
DdiTenant ddiTenant(final Tenant defaultTenant,
final HawkbitClient hawkbitClient) {
return new DdiTenant(defaultTenant, hawkbitClient);
}
@Bean
MgmtApi mgmtApi(final Tenant defaultTenant, final HawkbitClient hawkbitClient) {
return new MgmtApi(defaultTenant, hawkbitClient);
}
@ShellComponent
public static class Shell {
private final Tenant tenant;
private final DdiTenant ddiTenant;
private final MgmtApi mgmtApi;
private final UpdateHandler updateHandler;
private final HawkbitClient hawkbitClient;
private final Map<String, DdiController> devices = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private boolean setup;
Shell(final Tenant tenant, final Optional<UpdateHandler> updateHandler, final HawkbitClient hawkbitClient) {
this.tenant = tenant;
Shell(final DdiTenant ddiTenant, final MgmtApi mgmtApi, final Optional<UpdateHandler> updateHandler) {
this.ddiTenant = ddiTenant;
this.mgmtApi = mgmtApi;
this.updateHandler = updateHandler.orElse(null);
this.hawkbitClient = hawkbitClient;
}
@ShellMethod(key = "setup")
public void setup() {
SetupHelper.setupTargetAuthentication(hawkbitClient, tenant);
mgmtApi.setupTargetAuthentication();
setup = true;
}
@ShellMethod(key = "start-one")
public void startOne(@ShellOption("--id") final String controllerId) {
DdiController device = devices.get(controllerId);
final String securityTargetToken;
if (setup) {
securityTargetToken = SetupHelper.setupTargetToken(
controllerId, null, hawkbitClient, tenant);
securityTargetToken = mgmtApi.setupTargetToken(controllerId,null);
} else {
securityTargetToken = null;
}
if (device == null) {
device = new DdiController(
tenant,
Controller.builder()
// Create device with security token if not yet registered in this execution
// if already created in this execution of app, just start the poll
// for each new device - separate ThreadScheduler
ddiTenant.getController(controllerId).ifPresentOrElse(
ddiController -> ddiController.start(Executors.newSingleThreadScheduledExecutor()),
() -> ddiTenant.createController(Controller.builder()
.controllerId(controllerId)
.securityToken(securityTargetToken)
.build(),
updateHandler,
hawkbitClient).setOverridePollMillis(10_000);
final DdiController oldDevice = devices.putIfAbsent(controllerId, device);
if (oldDevice != null) {
device = oldDevice; // reuse existing
}
}
device.start(scheduler);
.build(),updateHandler)
.setOverridePollMillis(10_000)
.start(Executors.newSingleThreadScheduledExecutor())
);
}
@ShellMethod(key = "stop-one")
public void stopOne(@ShellOption("--id") final String controllerId) {
final DdiController device = devices.get(controllerId);
if (device == null) {
throw new IllegalArgumentException("Controller with id " + controllerId + " not found!");
} else {
device.stop();
}
ddiTenant.getController(controllerId).ifPresentOrElse(
DdiController::stop,
() -> log.error("Controller with id " + controllerId + " not found!"));
}
@ShellMethod(key = "start")