SDK: Add unified Link download (using auth contexts) (#2270)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -35,14 +35,11 @@ public class DmfController {
|
||||
private static final String DEPLOYMENT_BASE_LINK = "deploymentBase";
|
||||
private static final String CONFIRMATION_BASE_LINK = "confirmationBase";
|
||||
|
||||
private final String tenantId;
|
||||
private final String controllerId;
|
||||
private final Tenant tenant;
|
||||
private final Controller controller;
|
||||
private final UpdateHandler updateHandler;
|
||||
private final DmfSender dmfSender;
|
||||
|
||||
// configuration
|
||||
private final boolean downloadAuthenticationEnabled;
|
||||
|
||||
@Getter
|
||||
private final Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
@@ -64,13 +61,20 @@ public class DmfController {
|
||||
final Tenant tenant, final Controller controller,
|
||||
final UpdateHandler updateHandler,
|
||||
final DmfSender dmfSender) {
|
||||
this.tenantId = tenant.getTenantId();
|
||||
downloadAuthenticationEnabled = tenant.isDownloadAuthenticationEnabled();
|
||||
this.controllerId = controller.getControllerId();
|
||||
this.tenant = tenant;
|
||||
this.controller = controller;
|
||||
this.updateHandler = updateHandler == null ? UpdateHandler.SKIP : updateHandler;
|
||||
this.dmfSender = dmfSender;
|
||||
}
|
||||
|
||||
public String getTenantId() {
|
||||
return tenant.getTenantId();
|
||||
}
|
||||
|
||||
public String getControllerId() {
|
||||
return controller.getControllerId();
|
||||
}
|
||||
|
||||
public void start(ScheduledExecutorService executorService) {
|
||||
stop();
|
||||
this.executorService = executorService;
|
||||
@@ -95,18 +99,18 @@ public class DmfController {
|
||||
}
|
||||
|
||||
public void processUpdate(final EventTopic actionType, final DmfDownloadAndUpdateRequest updateRequest) {
|
||||
log.info(LOG_PREFIX + "Processing update for action {} .", getTenantId(), controllerId, updateRequest.getActionId());
|
||||
log.info(LOG_PREFIX + "Processing update for action {} .", getTenantId(), getControllerId(), updateRequest.getActionId());
|
||||
executorService.submit(updateHandler.getUpdateProcessor(this, actionType, updateRequest));
|
||||
}
|
||||
|
||||
public void sendFeedback(final UpdateStatus updateStatus) {
|
||||
log.info(LOG_PREFIX + "Sending UPDATE_ACTION_STATUS for action : {}", getTenantId(), controllerId, currentActionId);
|
||||
dmfSender.sendFeedback(tenantId, currentActionId, updateStatus);
|
||||
log.info(LOG_PREFIX + "Sending UPDATE_ACTION_STATUS for action : {}", getTenantId(), getControllerId(), currentActionId);
|
||||
dmfSender.sendFeedback(getTenantId(), currentActionId, updateStatus);
|
||||
}
|
||||
|
||||
public void sendUpdateAttributes() {
|
||||
log.info(LOG_PREFIX + "Sending UPDATE_ATTRIBUTES", getTenantId(), controllerId);
|
||||
dmfSender.updateAttributes(tenantId, controllerId, DmfUpdateMode.MERGE, attributes);
|
||||
log.info(LOG_PREFIX + "Sending UPDATE_ATTRIBUTES", getTenantId(), getControllerId());
|
||||
dmfSender.updateAttributes(getTenantId(), getControllerId(), DmfUpdateMode.MERGE, attributes);
|
||||
}
|
||||
|
||||
public void setAttribute(final String key, final String value) {
|
||||
|
||||
@@ -13,8 +13,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
@@ -23,26 +21,20 @@ import java.util.HexFormat;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
|
||||
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.hc.core5.ssl.SSLContextBuilder;
|
||||
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfArtifact;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfArtifactHash;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule;
|
||||
import org.eclipse.hawkbit.sdk.HawkbitClient;
|
||||
import org.eclipse.hawkbit.sdk.spi.ArtifactHandler;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.hateoas.Link;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Update handler provide plug-in endpoint allowing for customization of the update processing.
|
||||
@@ -134,20 +126,12 @@ public interface UpdateHandler {
|
||||
" and hashes " + art.getHashes() + " ...")
|
||||
.toList()));
|
||||
|
||||
log.info(LOG_PREFIX + "Start download", dmfController.getTenantId(), dmfController.getControllerId());
|
||||
log.info(LOG_PREFIX + "Start download", dmfController.getTenant().getTenantId(), dmfController.getControllerId());
|
||||
|
||||
final List<UpdateStatus> updateStatusList = new ArrayList<>();
|
||||
modules.forEach(module -> module.getArtifacts().forEach(artifact -> {
|
||||
if (dmfController.isDownloadAuthenticationEnabled()) {
|
||||
handleArtifact(
|
||||
updateRequest.getTargetSecurityToken(),
|
||||
updateStatusList, artifact);
|
||||
} else {
|
||||
handleArtifact(null, updateStatusList, artifact);
|
||||
}
|
||||
}));
|
||||
modules.forEach(module -> module.getArtifacts().forEach(artifact -> handleArtifact(updateStatusList, artifact)));
|
||||
|
||||
log.info(LOG_PREFIX + "Download complete.", dmfController.getTenantId(), dmfController.getControllerId());
|
||||
log.info(LOG_PREFIX + "Download complete.", dmfController.getTenant().getTenantId(), dmfController.getControllerId());
|
||||
|
||||
final List<String> messages = new LinkedList<>();
|
||||
messages.add("Download complete.");
|
||||
@@ -163,7 +147,7 @@ public interface UpdateHandler {
|
||||
* may get the {@link #downloads} map and apply them
|
||||
*/
|
||||
protected UpdateStatus update() {
|
||||
log.info(LOG_PREFIX + "Updated", dmfController.getTenantId(), dmfController.getControllerId());
|
||||
log.info(LOG_PREFIX + "Updated", dmfController.getTenant().getTenantId(), dmfController.getControllerId());
|
||||
return new UpdateStatus(DmfActionStatus.FINISHED, List.of("Update complete."));
|
||||
}
|
||||
|
||||
@@ -177,11 +161,11 @@ public interface UpdateHandler {
|
||||
Files.delete(path);
|
||||
} catch (final IOException e) {
|
||||
log.warn(LOG_PREFIX + "Failed to cleanup {}",
|
||||
dmfController.getTenantId(), dmfController.getControllerId(),
|
||||
dmfController.getTenant().getTenantId(), dmfController.getControllerId(),
|
||||
path.toFile().getAbsolutePath(), e);
|
||||
}
|
||||
});
|
||||
log.debug(LOG_PREFIX + "Cleaned up", dmfController.getTenantId(), dmfController.getControllerId());
|
||||
log.debug(LOG_PREFIX + "Cleaned up", dmfController.getTenant().getTenantId(), dmfController.getControllerId());
|
||||
}
|
||||
|
||||
private static String hideTokenDetails(final String targetToken) {
|
||||
@@ -200,23 +184,6 @@ public interface UpdateHandler {
|
||||
return targetToken.substring(0, 2) + "***" + targetToken.substring(targetToken.length() - 2);
|
||||
}
|
||||
|
||||
private static CloseableHttpClient createHttpClientThatAcceptsAllServerCerts()
|
||||
throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||
return HttpClients
|
||||
.custom()
|
||||
.setConnectionManager(
|
||||
PoolingHttpClientConnectionManagerBuilder.create()
|
||||
.setSSLSocketFactory(
|
||||
new SSLConnectionSocketFactory(
|
||||
SSLContextBuilder
|
||||
.create()
|
||||
.loadTrustMaterial(null, (chain, authType) -> true)
|
||||
.build()))
|
||||
.build()
|
||||
)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Validator sizeValidator(final long size) {
|
||||
return new Validator() {
|
||||
|
||||
@@ -290,98 +257,70 @@ public interface UpdateHandler {
|
||||
}
|
||||
|
||||
private void handleArtifact(
|
||||
final String targetToken,
|
||||
final List<UpdateStatus> status, final DmfArtifact artifact) {
|
||||
if (artifact.getUrls().containsKey("HTTPS")) {
|
||||
status.add(downloadUrl(artifact.getUrls().get("HTTPS"), targetToken,
|
||||
artifact.getHashes(), artifact.getSize()));
|
||||
status.add(downloadUrl(Link.of(artifact.getUrls().get("HTTPS")), artifact.getHashes(), artifact.getSize()));
|
||||
} else if (artifact.getUrls().containsKey("HTTP")) {
|
||||
status.add(downloadUrl(artifact.getUrls().get("HTTP"), targetToken,
|
||||
artifact.getHashes(), artifact.getSize()));
|
||||
status.add(downloadUrl(Link.of(artifact.getUrls().get("HTTP")), artifact.getHashes(), artifact.getSize()));
|
||||
}
|
||||
}
|
||||
|
||||
private UpdateStatus downloadUrl(
|
||||
final String url, final String targetToken,
|
||||
final DmfArtifactHash hash, final long size) {
|
||||
private UpdateStatus downloadUrl(final Link link, final DmfArtifactHash hash, final long size) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(LOG_PREFIX + "Downloading {} with token {}, expected hash {} and size {}",
|
||||
dmfController.getTenantId(), dmfController.getControllerId(), url,
|
||||
hideTokenDetails(targetToken), hash, size);
|
||||
log.debug(LOG_PREFIX + "Downloading {}, expected hash {} and size {}",
|
||||
dmfController.getTenant().getTenantId(), dmfController.getControllerId(), link.getHref(), hash, size);
|
||||
}
|
||||
|
||||
try {
|
||||
return readAndCheckDownloadUrl(url, targetToken, hash, size);
|
||||
} catch (final IOException | KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
|
||||
log.error(LOG_PREFIX + "Failed to download {}",
|
||||
dmfController.getTenantId(), dmfController.getControllerId(), url, e);
|
||||
return readAndCheckDownloadUrl(link, hash, size);
|
||||
} catch (final NoSuchAlgorithmException | IOException e) {
|
||||
log.error(LOG_PREFIX + "Failed to download {}", dmfController.getTenant().getTenantId(), dmfController.getControllerId(), link.getHref(), e);
|
||||
return new UpdateStatus(
|
||||
DmfActionStatus.ERROR,
|
||||
List.of("Failed to download " + url + ": " + e.getMessage()));
|
||||
List.of("Failed to download " + link.getHref() + ": " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private UpdateStatus readAndCheckDownloadUrl(final String url,
|
||||
final String targetToken, final DmfArtifactHash hash, final long size)
|
||||
throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, IOException {
|
||||
private UpdateStatus readAndCheckDownloadUrl(final Link link, final DmfArtifactHash hash, final long size)
|
||||
throws NoSuchAlgorithmException, IOException {
|
||||
final Validator sizeValidator = sizeValidator(size);
|
||||
final Validator hashValidator = hashValidator(hash);
|
||||
final ArtifactHandler.DownloadHandler downloadHandler = artifactHandler.getDownloadHandler(url);
|
||||
final ArtifactHandler.DownloadHandler downloadHandler = artifactHandler.getDownloadHandler(link.getHref());
|
||||
|
||||
try (final CloseableHttpClient httpclient = createHttpClientThatAcceptsAllServerCerts()) {
|
||||
final HttpGet request = new HttpGet(url);
|
||||
if (StringUtils.hasLength(targetToken)) {
|
||||
request.addHeader(HttpHeaders.AUTHORIZATION, "TargetToken " + targetToken);
|
||||
}
|
||||
|
||||
return httpclient.execute(request, response -> {
|
||||
try {
|
||||
if (response.getCode() != HttpStatus.OK.value()) {
|
||||
throw new IllegalStateException("Unexpected status code: " + response.getCode());
|
||||
}
|
||||
|
||||
if (response.getEntity().getContentLength() != size) {
|
||||
throw new IllegalArgumentException(
|
||||
"Wrong content length " + EXPECTED + size + BUT_GOT_LOG_MESSAGE + response.getEntity()
|
||||
.getContentLength() + ")!");
|
||||
}
|
||||
|
||||
final byte[] buff = new byte[32 * 1024];
|
||||
try (final InputStream is = response.getEntity().getContent()) {
|
||||
for (int read; (read = is.read(buff)) != -1; ) {
|
||||
sizeValidator.read(buff, read);
|
||||
hashValidator.read(buff, read);
|
||||
downloadHandler.read(buff, 0, read);
|
||||
}
|
||||
}
|
||||
sizeValidator.validate();
|
||||
hashValidator.validate();
|
||||
|
||||
final String message = "Downloaded " + url + " (" + size + " bytes)";
|
||||
log.debug(LOG_PREFIX + message, dmfController.getTenantId(), dmfController.getControllerId());
|
||||
downloadHandler.finished(ArtifactHandler.DownloadHandler.Status.SUCCESS);
|
||||
downloadHandler.download().ifPresent(path -> downloads.put(url, path));
|
||||
return new UpdateStatus(DmfActionStatus.FINISHED, List.of(message));
|
||||
} catch (final Exception e) {
|
||||
final String message = e.getMessage();
|
||||
if (log.isTraceEnabled()) {
|
||||
log.error(LOG_PREFIX + DOWNLOAD_LOG_MESSAGE + url + " failed: " + message,
|
||||
dmfController.getTenantId(), dmfController.getControllerId(), e);
|
||||
} else {
|
||||
log.error(LOG_PREFIX + DOWNLOAD_LOG_MESSAGE + url + " failed: " + message,
|
||||
dmfController.getTenantId(), dmfController.getControllerId());
|
||||
}
|
||||
downloadHandler.finished(ArtifactHandler.DownloadHandler.Status.ERROR);
|
||||
return new UpdateStatus(DmfActionStatus.ERROR, List.of(message));
|
||||
try (final InputStream is = HawkbitClient.getLink(link, InputStream.class, dmfController.getTenant(), dmfController.getController())) {
|
||||
try {
|
||||
final byte[] buff = new byte[32 * 1024];
|
||||
for (int read; (read = is.read(buff)) != -1; ) {
|
||||
sizeValidator.read(buff, read);
|
||||
hashValidator.read(buff, read);
|
||||
downloadHandler.read(buff, 0, read);
|
||||
}
|
||||
});
|
||||
sizeValidator.validate();
|
||||
hashValidator.validate();
|
||||
|
||||
final String message = "Downloaded " + link + " (" + size + " bytes)";
|
||||
log.debug(LOG_PREFIX + message, dmfController.getTenant().getTenantId(), dmfController.getControllerId());
|
||||
downloadHandler.finished(ArtifactHandler.DownloadHandler.Status.SUCCESS);
|
||||
downloadHandler.download().ifPresent(path -> downloads.put(link.getHref(), path));
|
||||
return new UpdateStatus(DmfActionStatus.FINISHED, List.of(message));
|
||||
} catch (final Exception e) {
|
||||
final String message = e.getMessage();
|
||||
if (log.isTraceEnabled()) {
|
||||
log.error(LOG_PREFIX + DOWNLOAD_LOG_MESSAGE + link + " failed: " + message,
|
||||
dmfController.getTenant().getTenantId(), dmfController.getControllerId(), e);
|
||||
} else {
|
||||
log.error(LOG_PREFIX + DOWNLOAD_LOG_MESSAGE + link + " failed: " + message,
|
||||
dmfController.getTenant().getTenantId(), dmfController.getControllerId());
|
||||
}
|
||||
downloadHandler.finished(ArtifactHandler.DownloadHandler.Status.ERROR);
|
||||
return new UpdateStatus(DmfActionStatus.ERROR, List.of(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface Validator {
|
||||
|
||||
void read(final byte[] buff, final int len);
|
||||
|
||||
void validate();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user